Question

I'm using ParseKit to parse units of measure. In order to do this I have to supply a grammar. I tried googling but that didn't get me far. While it is a fun exercise on my own I want to make sure I get it right. ParseKit is expecting a BNF Grammar like this:

@start  = number units;
units = unit+ | unit+ / unit+;
unit = prefix baseUnit | baseUnit;
prefix = '' | 'milli' | 'micro' | 'pico';
baseUnit = 'm' | 'meter' | 'g' | 'gram'

I'm looking to support input like:

25 m²
25 m^-3
25 m**-5/kg**-2
25 m/s squared
25 mm² per second
25 m/s
5 kg meters per second squared
3 m-kg/s^2
3 m kilograms
Was it helpful?

Solution

Developer of ParseKit here.

I have not looked closely at your example input to determine whether your grammar is semantically correct.

However, I do see two significant syntactical problems with your existing grammar.


First, this line contains Left Recursion (and also a syntax error with the unquoted /):

units = unit+ | unit+ / unit+;  // Incorrect. Will not work.

You must change this line to remove the Left Recursion to something like this:

units = unit ('/' unit)*;

See my prior answer for more information on eliminating Left Recursion in your ParseKit grammars.


Second, I believe this line is attempting to allow an "empty" match by using '':

prefix = '' | 'milli' | 'micro' | 'pico';   // Incorrect. Will not work.

That is not supported syntax in ParseKit grammars. This feature is fully supported but the syntax is Empty, like:

prefix = Empty | 'milli' | 'micro' | 'pico';

Hope that helps.

OTHER TIPS

This grammar I found on unidata.ucar.edu looks pretty official though unwieldily and doesn't contain the prefixes or units.

Unit-Spec: one of nothing Shift-Spec

 Shift-Spec: one of
         Product-Spec
         Product-Spec SHIFT REAL
         Product-Spec SHIFT INT
         Product-Spec SHIFT Timestamp

 Product-Spec: one of
         Power-Spec
         Product-Spec Power-Spec
         Product-Spec MULTIPLY Power-Spec
         Product-Spec DIVIDE Power-Spec

 Power-Spec: one of
         Basic-Spec
         Basic-Spec INT
         Basic-Spec EXPONENT
         Basic-Spec RAISE INT

 Basic-Spec: one of
         ID
         "(" Shift-Spec ")"
         LOGREF Product_Spec ")"
         Number

 Number: one of
         INT
         REAL

 Timestamp: one of
         DATE
         DATE CLOCK
         DATE CLOCK CLOCK
         DATE CLOCK INT
         DATE CLOCK ID
         TIMESTAMP
         TIMESTAMP INT
         TIMESTAMP ID

 SHIFT:
         <space>* <shift_op> <space>*

 <shift_op>: one of
         "@"
         "after"
         "from"
         "since"
         "ref"

 REAL:
         the usual floating-point format

 INT:
         the usual integer format

 MULTIPLY: one of
         "-"
         "."
         "*"
         <space>+
         <centered middot>

 DIVIDE:
         <space>* <divide_op> <space>*

 <divide_op>: one of
         per
         PER
         "/"

 EXPONENT:
         ISO-8859-9 or UTF-8 encoded exponent characters

 RAISE: one of
         "^"
         "**"

 ID: one of
         <id>
         "%"
         "'"
         "\""
         degree sign
         greek mu character

 <id>:
         <alpha> <alphanum>*

 <alpha>:
         [A-Za-z_]
         ISO-8859-1 alphabetic characters
         non-breaking space

 <alphanum>: one of
         <alpha>
         <digit>

 <digit>:
         [0-9]

 LOGREF:
         <log> <space>* <logref>

 <log>: one of
         "log"
         "lg"
         "ln"
         "lb"

 <logref>:
         "(" <space>* <re> ":"? <space>*

 DATE:
         <year> "-" <month> ("-" <day>)?

 <year>:
         [+-]?[0-9]{1,4}

 <month>:
         "0"?[1-9]|1[0-2]

 <day>:
         "0"?[1-9]|[1-2][0-9]|"30"|"31"

 CLOCK:
         <hour> ":" <minute> (":" <second>)?

 TIMSTAMP:
         <year> (<month> <day>?)? "T" <hour> (<minute> <second>?)?

 <hour>:
         [+-]?[0-1]?[0-9]|2[0-3]

 <minute>:
         [0-5]?[0-9]

 <second>:
         (<minute>|60) (\.[0-9]*)?
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top