1

I'm learning the PEGjs grammar and I request help or guide on the following:

I have functions like, PRODUCT(), SUM(), DIVIDE()

PRODUCT can take number/PRODUCT()/SUM()/DIVIDE() as parameters (any number but comma separated)

ex: PRODUCT(2, 5, SUM(5, 6, 7), DIVIDE(5, 2), PRODUCT(4, 6, 20, 12))

Same way the SUM can take any number of parameters separated by comma.

Ex: SUM(4, 5, 10, DIVIDE(SUM(2, 5, 6), 3))

DIVIDE will take two parameters (2 must), number's or another function

Ex: DIVIDE(3, PRODUCT(3, 4, SUM(2, 3)))

Could some one help or guide me how I can achieve?

The rules I have so far

start = sum
  /multiply

multiply = "PRODUCT("a:digit "," __ b:digit ")" {return a * b}

sum = "SUM("a:digit "," b:digit ")"  {return a + b}

digit = [0-9]

__ = WhiteSpace*

WhiteSpace "whitespace"
  = [ \t\r\n]

The above rule only support product/sum of two number's. How can I achieve the above?

Thanks in Advance Manjunath Reddy

Reddy
  • 1,403
  • 3
  • 14
  • 27

3 Answers3

2

Good start but you needed to figure out how to parse lists of arguments and do more recursion.

I found the best way to learn is by examples (https://github.com/pegjs/pegjs/tree/master/examples).

Try this in the online editor:

start
  = sum
  / multiply
  / divide

multiply
  = "PRODUCT(" _ args:arguments _ ")" { return args.reduce(function(a,b) {return a*b;}); }

sum
  = "SUM(" _ args:arguments _ ")"  { return args.reduce(function(a,b) {return a+b;}); }

divide
  = "DIVIDE(" _ dividend:argument _ "," _ divisor:argument _ ")" { return dividend / divisor; }

arguments
  = first:argument rest:(_ ',' _ arg:argument { return arg; })* { return [first].concat(rest); }

argument
  = multiply
  / sum
  / divide
  / number

number
  = digits:(digit)+ { return parseInt(digits.join(''), 10); }

digit = [0-9]

_ "optionalWhitespace"
  = whitespace *

whitespace
  = [ \t\n\r]+

Examples:

PRODUCT(2, 5, SUM(5, 6, 7), DIVIDE(5, 2), PRODUCT(4, 6, 20, 12))
2592000
SUM(4, 5, 10, DIVIDE(SUM(2, 5, 6), 3))
23.333333333333332
DIVIDE(3, PRODUCT(3, 4, SUM(2, 3)))
0.05
dan
  • 2,378
  • 18
  • 17
1

Generally, if you want to parse an unbounded list such as

1, 2, 3, 4

you can create a recursive rule as follows:

args = head:Integer rest:(_ "," _ r:args{ return r; })? {
    return rest != null ? [head].concat(rest) : [head];
}

Integer = [0-9]+ { return parseInt(text(), 10); }

_ = [ \t\n\r]*
spender
  • 117,338
  • 33
  • 229
  • 351
0

One more smaller split example that parse

1,a , 3.14;

into [ 1, "a", 3.14 ]

Try this in the online editor:

start = (_ r:(expression) (_"," /_)? _? {return r})*

expression = word / number
number = [0-9\.]+ { return parseFloat(text()) }
word = [a-z]+ { return text() }
_ = [ \t\n\r]*
Neppo
  • 1
  • 1