Vernac
A simple DSL for creating parsing expression grammars
Usage
Build up parsing expressions using the functions
eg:
let sum = Terminal('\\d').then('\\+').then(Terminal('\\d'));sum.listen(() => { console.log('matched');} sum.parse("1+2");// stdout >> 'matched'
API
Terminal
Matches a basic symbol, eg a number is:
let num = new Terminal('\\d')
or more complicated regex:
let ident = new Terminal('[A-Za-z]+\\w*');
let a = new Terminal('a'); a.parse('a'); // succeedsa.parse('b'); // failsa.parse('ba'); // succeeds at index 1
Empty
Matches explicitly nothing, a synonymous with new Terminal('');
let e = new Empty(); e.parse(''); // succeedse.parse('b'); // succeeds with b still to be parsed
Associated actions
We can add listeners for when a grammar matches.
let a = new Terminal('a');a.listen((result_obj, input) => { console.log(result_obj); // Result {matched: true, result: 'a', remaining: ''}});a.parse('a');
Ordered Choice
Noted as a | b
. Tries for a first and then b.
Usage
let a = new Terminal('a');let b = new Terminal('b'); let choice = a.or(b); choice.parse('a'); // matches achoice.parse('b'); // matches bchoice.parse('ab'); // matches a
Sequence
Notes as ab
. Needs to find a and then b.
Usage
let a = new Terminal('a');let b = new Terminal('b'); let seq = a.then(b); seq.parse('a'); // failsseq.parse('b'); // failsseq.parse('ab'); // succeeds
Lookahead
Noted as a&b
. Tries to find an a
followed by a b
. Only remembers the a
.
Similar to sequence but only remembers the first.
Usage
let a = new Terminal('a');let b = new Terminal('b'); let lookahead = a.before(b); lookahead.parse('a') // failslookahead.parse('b') // failslookahead.parse('ab') // succeeds and returns 'a' with 'b' still to be parsed
Negated Lookahead
Noted as a!b
. Tries to find an a
explicitly not followed by a b
. Only
remembers the a
.
Similar to lookahead but inverted.
Usage
let a = new Terminal('a');let b = new Terminal('b'); let negated = a.notBefore(b); negated.parse('a'); // succeedsnegated.parse('b'); // failsnegated.parse('ab'); // failsnegated.parse('ac'); // succeeds and returns 'a' with 'c' still to be parsed
0 or more
Noted as a*
. Tries to match a
0 or more times.
Usage
let a = new Terminal('a'); let zero_more = a.times(0); zero_more.parse('a'); // succeeds and returns 'a'zero_more.parse('ab'); // succeeds and returns 'a' with 'b' still to be parsedzero_more.parse('aaac'); // succeeds and returns 'aaa' with 'c' still to be parsedzero_more.parse(''); // succeeds
1 or more
Noted as a+
. Tries to match a
1 or more times.
Usage
let a = new Terminal('a'); let one_more = a.times(1); one_more.parse('a'); // succeeds and returns 'a'one_more.parse('ab'); // succeeds and returns 'a' with 'b' still to be parsedone_more.parse('aaac'); // succeeds and returns 'aaa' with 'c' still to be parsedone_more.parse(''); // fails
Optional
Noted as a?
. Tries to match a
0 or 1 times.
Usage
let a = new Terminal('a'); let optional = a.optionally(); optional.parse('a'); // succeedsoptional.parse(''); // succeedsoptional.parse('aa'); // succeeds and returns 'a' with 'a' still to be parsedoptional.parse('ab'); // succeeds and returns 'a' with 'b' still to be parsedoptional.parse('b'); //succeeds and returns '' with 'b' still to be parsed
TODO
- Add tests to verify the result field.
- Add stronger tests for listeners and ast building
- Add semantic error return possibilities for the listeners
- Add syntactic sugar for creating it from a list of tuples