/* (c) https://github.com/MontiCore/monticore */ package de.monticore.siunit; /* Beta-version: This is intended to become a MontiCore stable grammar. */ /** * This grammar defines SI units and other derived units such as * 'm', 'km', 'km^2' or 'mm^2/kVA^2h'. Spaces in a unit are * prevented by semantic predicates. * * The definitions are fully compliant to the definitions given in * International Bureau of Weights and Measures (20 May 2019), * SI Brochure: The International System of Units (SI) (9th ed.). * * SI units are declared as independent Nonterminal and can then * be used as part of a * * value definition, such as "5kg", or * * type definition, such as "km/h" * * The grammar extends the MontiCore common literals, because it uses * natural numbers e.g. as exponent. */ grammar SIUnits extends de.monticore.literals.MCCommonLiterals { /** * The SIUnit is either a SIUnitPrimitive, e.g. "km" * the division of two SIUnitPrimitives, e.g. "km/h" * or the division of "1" and a SIUnitPrimitive, e.g. "1/h" * */ SIUnit = // The lookahead is needed for the parser to // decide which alternative to take. { isSIOneDiv() }? one:NatLiteral "/" denominator:SIUnitPrimitive | { isSIDiv() }? numerator:SIUnitPrimitive "/" denominator:SIUnitPrimitive | { !isSIDiv() }? SIUnitPrimitive; /** * The SIUnitPrimitives are the primitives of the SIUnit * * SIUnitPrimitives contains the basic SI units without prefixes * such as 'm', 's' or 'kg' * as well as the basic SI units with prefixes * such as 'km', 'mm' or 'ms'. * * Other derived or officialy accepted units are also contained * ('h', 'day', 'Ohm', ...) * * Do not take a SIUnitWith(out)Prefix if it is in fact a * SIUnitKindGroupWithExponent. */ SIUnitPrimitive = { !isSIUnitKindGroupWithExponent(1) }? SIUnitWithPrefix | { !isSIUnitKindGroupWithExponent(1) }? SIUnitWithoutPrefix | SIUnitDimensionless | CelsiusFahrenheit | { isSIUnitKindGroupWithExponent(1) }? SIUnitKindGroupWithExponent; /** * SIUnitWithPrefix * * The regular expression is defined according to: * * https://en.wikipedia.org/wiki/Metric_prefix * * https://en.wikipedia.org/wiki/SI_base_unit * * https://en.wikipedia.org/wiki/SI_derived_unit * * https://en.wikipedia.org/wiki/Non-SI_units_mentioned_in_the_SI * * The expression matches an SI unit starting * with a prefix. An SI unit that can have a * prefix is one of the following: * 'm,g,s,A,K,mol,cd,Hz,N,Pa,J,W,C,V,F,Ohm, * Ω,S,Wb,T,H,lm,lx,Bq,Gy,Sv,kat,l,L' * * Alternatively the SIUnitWithPrefix is * followed by any other SIUnitWithPrefix or * SIUnitWithoutPrefix (see below) for a * SI unit group, e.g. 'kVAh'. * * The regular expression is needed, because SI * units shall not be defined as keywords * because they would not be usable e.g. as * variable names in other places anymore. * See also functions available to handle the * stored unit. */ SIUnitWithPrefix = { isSIUnitWithPrefix(1) }? (Name | NonNameUnit); /** * SIUnitWithoutPrefix * * The regular expression is defined according to: * * https://en.wikipedia.org/wiki/Metric_prefix * * https://en.wikipedia.org/wiki/SI_base_unit * * https://en.wikipedia.org/wiki/SI_derived_unit * * https://en.wikipedia.org/wiki/Non-SI_units_mentioned_in_the_SI * * The expression matches an SI unit not starting * with a prefix. An SI unit that does not need a * prefix is one of the following: * 'm,g,s,A,K,mol,cd,Hz,N,Pa,J,W,C,V,F,Ohm, * Ω,S,Wb,T,H,lm,lx,Bq,Gy,Sv,kat,l,L' * and * 'min,h,d,ha,t,au,Np,B,dB,eV,Da,u' * * Alternatively the SIUnitWithPrefix is * followed by any other SIUnitWithPrefix (see * above) or SIUnitWithoutPrefix for a * SI unit group, e.g. 'VAh'. * * The regular expression is needed, because SI * units shall not be defined as keywords * because they would not be usable e.g. as * variable names in other places anymore. * See also functions available to handle the * stored unit. */ SIUnitWithoutPrefix = { isSIUnitWithoutPrefix(1) }? (Name | NonNameUnit); /** * CelsiusFahrenheit matches "°C" and "°F" * * Lookahead needed at the beginning to * distinguish with other alternatives */ CelsiusFahrenheit = { isCelsiusFahrenheit(1) }? "°" unit:Name; /** * SIUnitDimensionless matches "°" and "deg|rad|sr" * according to https://en.wikipedia.org/wiki/SI_derived_unit * * Lookahead needed at the beginning to * distinguish with other alternatives */ SIUnitDimensionless = "°" | { isDimensionless(1) }? unit:Name; /** * The SIUnitKindGroupWithExponent combines * several SIUnitWithPrefix and SIUnitWithoutPrefix * with exponents as one SI unit group, such as * 'kV^2Ah' and 's^2m' */ SIUnitKindGroupWithExponent = { isSIUnitKindGroupWithExponent(1) }? (SIUnitGroupPrimitive "^" exponent:SignedNatLiteral)+ SIUnitGroupPrimitive?; /** * The SIUnitGroupPrimitive is either a * SIUnitWithPrefix or SIUnitWithoutPrefix */ SIUnitGroupPrimitive = SIUnitWithPrefix | SIUnitWithoutPrefix; /** This Token is needed because the Name-Token * does not cover greek Ohm 'Ω' and greek mu 'µ'. * The Token contains at least one of those * symbols. */ token NonNameUnit = 'µ' UnitChar+ | UnitChar* 'Ω' | 'µ' 'Ω' ; fragment token UnitChar = 'a'..'z' | 'A'..'Z' ; // Defining semantic predicates concept antlr { parserjava { public static final String prefix = "(Y|Z|E|P|T|G|M|k|h|da|d|c|m|u|µ|n|p|f|a|z|y)"; public static final String unitWithPrefix = "(m|g|s|A|K|mol|cd|Hz|N|Pa|J|W|C|V|F|Ohm|Ω|S|Wb|T|H|lm|lx|Bq|Gy|Sv|kat|l|L)"; public static final String unitWithoutPrefix = "(min|h|d|ha|t|au|Np|B|dB|eV|Da|u)"; public static final String units = "(m|g|s|A|K|mol|cd|Hz|N|Pa|J|W|C|V|F|Ohm|Ω|S|Wb|T|H|lm|lx|Bq|Gy|Sv|kat|l|L|min|h|d|ha|t|au|Np|B|dB|eV|Da|u)"; /* returns true iff the next token matches a * SI unit (group) starting with a prefix */ public boolean isSIUnitWithPrefix(int i) { String regex = prefix + unitWithPrefix + "(" + prefix + unitWithPrefix + "|" + units + ")" + "*"; return cmpTokenRegEx(i, regex); } /* returns true iff the next token matches a * SI unit (group) starting without a prefix */ public boolean isSIUnitWithoutPrefix(int i) { String regex = units + "(" + prefix + unitWithPrefix + "|" + units + ")" + "*"; return cmpTokenRegEx(i, regex); } /* returns true iff the next token matches a * dimensionless SI unit */ public boolean isDimensionless(int i) { return cmpToken(i,"°","deg","rad","sr"); } /* returns true iff the next token matches a * °C or °F */ public boolean isCelsiusFahrenheit(int i) { return cmpToken(1,"°") && cmpToken(2,"C","F") && noSpace(2); } /* returns true iff the next token matches a * SI unit (group) starting with or without * a prefix */ public boolean isSIUnitGroupPrimitive(int i) { return isSIUnitWithPrefix(i) || isSIUnitWithoutPrefix(i); } /* counts the tokens used for a UnitGroup with * exponents, e.g. kV^2Ah * returns -1, iff ht next tokens do not match */ public int countSIUnitKindGroupWithExponent(int i) { if (!isSIUnitGroupPrimitive(i)) return -1; /* A SI unit group with exponents cannot * be a simple primitive and must be * followed by an exponent */ if (!cmpToken(i + 1, "^") || !( cmpTokenRegEx(i + 2, "\\d+") && noSpace(i + 1, i + 2) || cmpToken(i + 2, "-") && cmpTokenRegEx(i + 3, "\\d+") && noSpace(i + 1, i + 2, i + 3))) return -1; boolean loop = true; int counter = 0; while (loop) { // have seen a primitive counter ++; // exponent ^2 if (cmpToken(i + counter, "^") && cmpTokenRegEx(i + counter + 1, "\\d+") && noSpace(i + counter, i + counter + 1)) counter += 2; // exponent ^-2 else if (cmpToken(i + counter, "^") && cmpToken(i + counter + 1, "-") && cmpTokenRegEx(i + counter + 2, "\\d+") && noSpace(i + counter, i + counter + 1, i + counter + 2)) counter += 3; /* break if there are spaces between the tokens * or the next tokens do not match an exponent */ else loop = false; /* break if the next token is not a SIUnitGroup * or there are spaces between the tokens */ if (!isSIUnitGroupPrimitive(i + counter) || !noSpace(i + counter)) loop = false; // break } return counter; } /* returns true iff the next tokens match a * SI unit (group) with exponents */ public boolean isSIUnitKindGroupWithExponent(int i) { return countSIUnitKindGroupWithExponent(i) > 0; } /* counts the tokens used for a SI unit primitive * returns -1, iff ht next tokens do not match */ public int countPrimitive(int i) { int j = countSIUnitKindGroupWithExponent(i); if (j > 0) return j; if (isSIUnitWithPrefix(i)) return 1; if (isSIUnitWithoutPrefix(i)) return 1; if (isDimensionless(i)) return 1; if (isCelsiusFahrenheit(i)) return 2; return -1; } /* returns true iff the next token(s) matches a * SI unit primitive */ public boolean isPrimitive(int i) { return countPrimitive(i) > 0; } /* returns true iff the next tokens match a * SI unit Div */ public boolean isSIDiv() { int j = countPrimitive(1); if (j > 0 && cmpToken(1 + j, "/") && isPrimitive(2 + j) && noSpace(1 + j, 2 + j)) return true; return false; } /* returns true iff the next tokens match a * 1 divided by a SI unit primitive */ public boolean isSIOneDiv() { return cmpToken(1, "1") && isPrimitive(3) && noSpace(2,3); } } } }