001/******************************************************************************* 002 * Crown Copyright (c) 2006 - 2014, Copyright (c) 2006 - 2014 Kestral Computing P/L. 003 * All rights reserved. This program and the accompanying materials 004 * are made available under the terms of the Eclipse Public License v1.0 005 * which accompanies this distribution, and is available at 006 * http://www.eclipse.org/legal/epl-v10.html 007 * 008 * Contributors: 009 * Kestral Computing P/L - initial implementation 010 *******************************************************************************/ 011 012package org.hl7.fhir.utilities.ucum; 013 014import org.hl7.fhir.exceptions.UcumException; 015 016public class ExpressionParser { 017 018 private UcumModel model; 019 020 /** 021 * @param model 022 */ 023 public ExpressionParser(UcumModel model) { 024 super(); 025 this.model = model; 026 } 027 028 public Term parse(String code) throws UcumException { 029 Lexer lexer = new Lexer(code); 030 Term res = parseTerm(lexer, true); 031 if (!lexer.finished()) 032 throw new UcumException("Expression was not parsed completely. Syntax Error?"); 033 return res; 034 } 035 036 private Term parseTerm(Lexer lexer, boolean first) throws UcumException { 037 Term res = new Term(); 038 if (first && lexer.getType() == TokenType.NONE) { 039 res.setComp(new Factor(1)); 040 } else if (lexer.getType() == TokenType.SOLIDUS) { 041 res.setOp(Operator.DIVISION); 042 lexer.consume(); 043 res.setTerm(parseTerm(lexer, false)); 044 } else { 045 if (lexer.getType() == TokenType.ANNOTATION) { 046 res.setComp(new Factor(1)); // still lose the annotation 047 lexer.consume(); 048 } else 049 res.setComp(parseComp(lexer)); 050 if (lexer.getType() != TokenType.NONE && lexer.getType() != TokenType.CLOSE) { 051 if (lexer.getType() == TokenType.SOLIDUS) { 052 res.setOp(Operator.DIVISION); 053 lexer.consume(); 054 } else if (lexer.getType() == TokenType.PERIOD) { 055 res.setOp(Operator.MULTIPLICATION); 056 lexer.consume(); 057 } else if (lexer.getType() == TokenType.ANNOTATION) 058 res.setOp(Operator.MULTIPLICATION); // implicit 059 else 060 lexer.error("Expected '/' or '.'"); 061 res.setTerm(parseTerm(lexer, false)); 062 } 063 } 064 return res; 065 } 066 067 private Component parseComp(Lexer lexer) throws UcumException { 068 if (lexer.getType() == TokenType.NUMBER) { 069 Factor fact = new Factor(lexer.getTokenAsInt()); 070 lexer.consume(); 071 return fact; 072 } else if (lexer.getType() == TokenType.SYMBOL) 073 return parseSymbol(lexer); 074 else if (lexer.getType() == TokenType.NONE) 075 lexer.error("unexpected end of expression looking for a symbol or a number"); 076 else if (lexer.getType() == TokenType.OPEN) { 077 lexer.consume(); 078 Term res = parseTerm(lexer, true); 079 if (lexer.getType() == TokenType.CLOSE) 080 lexer.consume(); 081 else 082 lexer.error("Unexpected Token Type '"+lexer.getType().toString()+"' looking for a close bracket"); 083 return res; 084 } else 085 lexer.error("unexpected token looking for a symbol or a number"); 086 return null; // we never get to here 087 } 088 089 private Component parseSymbol(Lexer lexer) throws UcumException { 090 Symbol symbol = new Symbol(); 091 String sym = lexer.getToken(); 092 093 // now, can we pick a prefix that leaves behind a metric unit? 094 Prefix selected = null; 095 Unit unit = null; 096 for (Prefix prefix : model.getPrefixes()) { 097 if (sym.startsWith(prefix.getCode())) { 098 unit = model.getUnit(sym.substring(prefix.getCode().length())); 099 if (unit != null && (unit.getKind() == ConceptKind.BASEUNIT || ((DefinedUnit) unit).isMetric())) { 100 selected = prefix; 101 break; 102 }; 103 } 104 } 105 106 if (selected != null) { 107 symbol.setPrefix(selected); 108 symbol.setUnit(unit); 109 } else { 110 unit = model.getUnit(sym); 111 if (unit != null) 112 symbol.setUnit(unit); 113 else if (!sym.equals("1")) 114 lexer.error("The unit '"+sym+"' is unknown"); 115 } 116 117 lexer.consume(); 118 if (lexer.getType() == TokenType.NUMBER) { 119 symbol.setExponent(lexer.getTokenAsInt()); 120 lexer.consume(); 121 } else 122 symbol.setExponent(1); 123 124 return symbol; 125 } 126}