help creating peg.js parser
Asked Answered
P

1

6

I am wanting to create the parser, and expression syntax in peg.js that would allow me to do these things

basically I want to pass in a mask and have a number output.

the mask has these abilities.

1) generate a random number between 0-9 (character n for expression? )
2) generate a random number between x and y( (x,y) for expression?)
3) literal numbers are valid (hopefully nothing needed for expession?)
4) repeat previous expression x times({x} for expression?)
5) repeat previous expression between x and y times({x,y} for expression?)

so an example expression could be

027n(5,9){4}n12{2,8}(2,4)

the proposed expression syntax above is only an example, it can change.

can anyone provide help in creating the parser for this in peg.js?

Pew answered 9/3, 2011 at 4:20 Comment(0)
C
17

The idea is to make it generate a JavaScript function, which when executed, will return a random string according to the mask.

A literal number is any character between 0 to 9, so make it generate a function that returns itself.

literal_number
 = num:[0-9]
 { return function() {
     return num;
 }; }

Then n is for a random number. Again, this generates a function to return a random number. I added + '' to convert it to a string before returning.

random_number
 = "n"
 { return function() {
     return Math.floor(Math.random() * 10) + '';
 }; }

In the (a,b) syntax, a and b are numbers, so we need to make it parse and return a number. Using the declaration from the calculator example:

number
 = digits:[0-9]+ { return parseInt(digits.join(""), 10); }

Then we can move on to create a rule for (a,b) syntax.

random_number_between
 = "(" a:number "," b:number ")"
 { return function() {
     return a + Math.floor(Math.random() * (b - a + 1)) + ''
 }; }

So, these 3 things (literal_number, random_number, random_number_between) combine into a single expression that generates a valid function.

single_expression
 = random_number
 / random_number_between
 / literal_number

A single expression, followed by {n} or {a,b} forms a repeated expression. A single expression is also a repeated expression, repeated one time.

The idea of a repeated expression is, given a function, return a function that calls the input function N times, collect the result, and return it.

repeated_expression
 = ex:single_expression "{" n:number "}" {
       return function() {
           var result = '';
           for (var i = 0; i < n; i ++) {
               result += ex();
           }
           return result;
       };
   }
 / ex:single_expression "{" a:number "," b:number "}" {
       return function() {
           var result = '';
           var n = a + Math.floor(Math.random() * (b - a + 1))
           for (var i = 0; i < n; i ++) {
               result += ex();
           }
           return result;
       };
   }
 / ex:single_expression

Finally, repeated expressions can be put next to each other to concatenate.

expression
 = list:repeated_expression* {
       return function() {
           var result = '';
           for (var i = 0; i < list.length; i ++) {
               result += list[i]();
           }
           return result;
       };
   }

Finally, you need a starting point, which defines a mask. This final bit scans the expression which returns a generates a function, and call it. Put the following at the top, and when you try it online, it will generate a string of numbers according to your mask.

mask
 = ex:expression
 { return ex() }

Example run: 027n(5,9){4}n12{2,8}(2,4) gives 0271568891222224.

Charlettecharley answered 9/3, 2011 at 7:34 Comment(6)
Wow, This should actually be in the documentation for peg.js It's a better tutorial than what they have! very much appreciated.Pew
btw, I think you have a couple of rogue 11's in your example answer.Pew
Hmm, im still confused a bit, I am trying to make a rule that say, if it doesnt match any other rules just return itself. how could I do that?Pew
Oh, yes, I accidently put 11 in it.Charlettecharley
You can also have another rule which matches any character, like literal_char = char:. and then return function like in literal_number, and append literal_char to single_expression rule to make it accept literal_char.Charlettecharley
ah! the . is what I was missing! I had it working by modifying the literal_number to accept [0-9a-z+,.....]Pew

© 2022 - 2024 — McMap. All rights reserved.