/******************************************************************************* * Copyright (c) 2010 Association for Decentralized Information Management in * Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.databoard.units.internal.parser; import org.simantics.databoard.units.internal.UnitParseException; 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); }