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.fhir.ucum; 013 014public class ExpressionParser { 015 016 private UcumModel model; 017 018 /** 019 * @param model 020 */ 021 public ExpressionParser(UcumModel model) { 022 super(); 023 this.model = model; 024 } 025 026 public Term parse(String code) throws UcumException { 027 Lexer lexer = new Lexer(code); 028 Term res = parseTerm(lexer, true); 029 if (!lexer.finished()) 030 throw new UcumException("Expression was not parsed completely. Syntax Error?"); 031 return res; 032 } 033 034 private Term parseTerm(Lexer lexer, boolean first) throws UcumException { 035 Term res = new Term(); 036 if (first && lexer.getType() == TokenType.NONE) { 037 res.setComp(new Factor(1)); 038 } else if (lexer.getType() == TokenType.SOLIDUS) { 039 res.setOp(Operator.DIVISION); 040 lexer.consume(); 041 res.setTerm(parseTerm(lexer, false)); 042 } else { 043 if (lexer.getType() == TokenType.ANNOTATION) { 044 res.setComp(new Factor(1)); // still lose the annotation 045 lexer.consume(); 046 } else 047 res.setComp(parseComp(lexer)); 048 if (lexer.getType() != TokenType.NONE && lexer.getType() != TokenType.CLOSE) { 049 if (lexer.getType() == TokenType.SOLIDUS) { 050 res.setOp(Operator.DIVISION); 051 lexer.consume(); 052 } else if (lexer.getType() == TokenType.PERIOD) { 053 res.setOp(Operator.MULTIPLICATION); 054 lexer.consume(); 055 } else if (lexer.getType() == TokenType.ANNOTATION) 056 res.setOp(Operator.MULTIPLICATION); // implicit 057 else 058 lexer.error("Expected '/' or '.'"); 059 res.setTerm(parseTerm(lexer, false)); 060 } 061 } 062 return res; 063 } 064 065 private Component parseComp(Lexer lexer) throws UcumException { 066 if (lexer.getType() == TokenType.NUMBER) { 067 Factor fact = new Factor(lexer.getTokenAsInt()); 068 lexer.consume(); 069 return fact; 070 } else if (lexer.getType() == TokenType.SYMBOL) 071 return parseSymbol(lexer); 072 else if (lexer.getType() == TokenType.NONE) 073 lexer.error("unexpected end of expression looking for a symbol or a number"); 074 else if (lexer.getType() == TokenType.OPEN) { 075 lexer.consume(); 076 Term res = parseTerm(lexer, true); 077 if (lexer.getType() == TokenType.CLOSE) 078 lexer.consume(); 079 else 080 lexer.error("Unexpected Token Type '"+lexer.getType().toString()+"' looking for a close bracket"); 081 return res; 082 } else 083 lexer.error("unexpected token looking for a symbol or a number"); 084 return null; // we never get to here 085 } 086 087 private Component parseSymbol(Lexer lexer) throws UcumException { 088 Symbol symbol = new Symbol(); 089 String sym = lexer.getToken(); 090 091 // now, can we pick a prefix that leaves behind a metric unit? 092 Prefix selected = null; 093 Unit unit = null; 094 for (Prefix prefix : model.getPrefixes()) { 095 if (sym.startsWith(prefix.getCode())) { 096 unit = model.getUnit(sym.substring(prefix.getCode().length())); 097 if (unit != null && (unit.getKind() == ConceptKind.BASEUNIT || ((DefinedUnit) unit).isMetric())) { 098 selected = prefix; 099 break; 100 }; 101 } 102 } 103 104 if (selected != null) { 105 symbol.setPrefix(selected); 106 symbol.setUnit(unit); 107 } else { 108 unit = model.getUnit(sym); 109 if (unit != null) 110 symbol.setUnit(unit); 111 else if (!sym.equals("1")) 112 lexer.error("The unit '"+sym+"' is unknown"); 113 } 114 115 lexer.consume(); 116 if (lexer.getType() == TokenType.NUMBER) { 117 symbol.setExponent(lexer.getTokenAsInt()); 118 lexer.consume(); 119 } else 120 symbol.setExponent(1); 121 122 return symbol; 123 } 124}