--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2010 Association for Decentralized Information Management in\r
+ * Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ * VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.databoard.units.internal.parser;
+
+import org.simantics.databoard.units.internal.UnitParseException;\r
+
+public abstract class UnitParser {
+
+ String string;
+ int pos;
+
+ int nextChar;
+ int sign;
+
+ public UnitParser() {
+ }
+
+ void next() {
+ ++pos;
+ if(pos >= string.length())
+ nextChar = -1;
+ else
+ nextChar = string.charAt(pos);
+ }
+
+ public void unit(String string, int sign) throws UnitParseException {
+ this.string = string;
+ this.pos = -1;
+ this.sign = sign;
+ next();
+
+ expression();
+ expect(-1);
+ }
+
+ /* Grammar
+ *
+ * unit_expression: unit_numerator [ "/" unit_denominator ]
+ * unit_numerator: "1" | unit_factors | "(" unit_expression ")"
+ * unit_denominator: unit_factor | "(" unit_expression ")"
+ * unit_factors: unit_factor [ "." unit_factors ]
+ * unit_factor: unit_operand [ unit_exponent ]
+ * unit_exponent: [ "+" | "-" ] integer
+ * unit_operand: unit_symbol | unit_prefix unit_symbol
+ */
+
+ void expression() throws UnitParseException {
+ numerator();
+ if(nextChar == '/') {
+ next();
+ denominator();
+ }
+ }
+
+ void numerator() throws UnitParseException {
+ if(nextChar == '1')
+ next();
+ else if(nextChar == '(') {
+ next();
+ expression();
+ skip(')');
+ }
+ else
+ factors();
+ }
+
+ void denominator() throws UnitParseException {
+ sign = -sign;
+ if(nextChar == '(') {
+ next();
+ expression();
+ skip(')');
+ }
+ else
+ factor();
+ sign = -sign;
+ }
+
+ void factors() throws UnitParseException {
+ while(true) {
+ factor();
+ if(nextChar == '.')
+ next();
+ else
+ break;
+ }
+ }
+
+ void factor() throws UnitParseException {
+ // Base
+ int factorBegin = pos;
+ while((nextChar >= 'a' && nextChar <= 'z') || (nextChar >= 'A' && nextChar <= 'Z') || (nextChar=='ยต') || nextChar=='(' || nextChar==')' || nextChar=='%')
+ next();
+ int exponentBegin = pos;
+ if(factorBegin == exponentBegin)
+ throw new UnitParseException(string,
+ "Expected base unit name, but got " + charName(nextChar) + " at position " + pos + ".");
+ String baseUnit = string.substring(factorBegin, exponentBegin);
+
+ // Exponent
+ if(nextChar == '-' || nextChar == '+' || (nextChar >= '0' && nextChar <= '9')) {
+ next();
+ while(nextChar >= '0' && nextChar <= '9')
+ next();
+ }
+ int exponentEnd = pos;
+ if(exponentEnd > exponentBegin) {
+ String exponentString = string.substring(exponentBegin, exponentEnd);
+ try {
+ visit(baseUnit, sign * Integer.parseInt(exponentString));
+ } catch(NumberFormatException e) {
+ throw new UnitParseException(string,
+ "Invalid exponent \"" + exponentString + "\" at position " + pos + ".");
+ }
+ }
+ else
+ visit(baseUnit, sign);
+ }
+
+ private void skip(char c) throws UnitParseException {
+ expect(c);
+ next();
+ }
+
+ private static String charName(int c) {
+ if(c >= 0)
+ return "'" + Character.toString((char)c) + "'";
+ else
+ return "end of unit";
+ }
+
+ private void expect(int c) throws UnitParseException {
+ if(nextChar != c)
+ throw new UnitParseException(string,
+ "Expected " + charName(c) + ", but got " + charName(nextChar) + " at position " + pos + ".");
+ }
+
+ public abstract void visit(String baseUnit, int exponent);
+
+}