Skip to content

Commit

Permalink
Merge pull request #3 from cdibbs/master
Browse files Browse the repository at this point in the history
Modified ebnf-transform to allow symbol aliases.
  • Loading branch information
zaach committed Aug 10, 2013
2 parents 706a5c4 + 982d9b6 commit ea638f6
Show file tree
Hide file tree
Showing 7 changed files with 506 additions and 122 deletions.
3 changes: 3 additions & 0 deletions Makefile
Expand Up @@ -8,6 +8,9 @@ build:
node ./node_modules/.bin/jison bnf.y bnf.l
mv bnf.js parser.js

node ./node_modules/.bin/jison ebnf.y
mv ebnf.js transform-parser.js

test:
node tests/all-tests.js

Expand Down
5 changes: 4 additions & 1 deletion bnf.l
@@ -1,3 +1,5 @@
id [a-zA-Z][a-zA-Z0-9_-]*

%x action code
%s bnf ebnf

Expand All @@ -14,7 +16,8 @@
\s+ /* skip whitespace */
"//".* /* skip comment */
"/*"(.|\n|\r)*?"*/" /* skip comment */
[a-zA-Z][a-zA-Z0-9_-]* return 'ID';
"["{id}"]" yytext = yytext.substr(1, yyleng-2); return 'ALIAS';
{id} return 'ID';
'"'[^"]+'"' yytext = yytext.substr(1, yyleng-2); return 'STRING';
"'"[^']+"'" yytext = yytext.substr(1, yyleng-2); return 'STRING';
":" return ':';
Expand Down
4 changes: 3 additions & 1 deletion bnf.y
Expand Up @@ -122,7 +122,9 @@ handle_sublist
;

expression_suffix
: expression suffix
: expression suffix ALIAS
{$$ = $expression + $suffix + "[" + $ALIAS + "]"; }
| expression suffix
{$$ = $expression + $suffix; }
;

Expand Down
39 changes: 31 additions & 8 deletions ebnf-transform.js
Expand Up @@ -2,14 +2,31 @@ var EBNF = (function(){
var parser = require('./transform-parser.js');

var transformExpression = function(e, opts, emit) {
var type = e[0], value = e[1], name;
var type = e[0], value = e[1], name = false;

if (type === 'xalias') {
type = e[1];
value = e[2]
name = e[3];
if (type) {
e = e.slice(1,2);
} else {
e = value;
type = e[0];
value = e[1];
}
}

if (type === 'symbol') {
if (e[1][0] === '\\') emit (e[1][1]);
else if (e[1][0] === '\'') emit (e[1].substring(1, e[1].length-1));
else emit (e[1]);
var n;
if (e[1][0] === '\\') n = e[1][1];
else if (e[1][0] === '\'') n = e[1].substring(1, e[1].length-1);
else n = e[1];
emit(n + (name ? "["+name+"]" : ""));
} else if (type === "+") {
name = opts.production + "_repetition_plus" + opts.repid++;
if (!name) {
name = opts.production + "_repetition_plus" + opts.repid++;
}
emit(name);

opts = optsForProduction(name, opts.grammar);
Expand All @@ -22,7 +39,9 @@ var EBNF = (function(){
]
];
} else if (type === "*") {
name = opts.production + "_repetition" + opts.repid++;
if (!name) {
name = opts.production + "_repetition" + opts.repid++;
}
emit(name);

opts = optsForProduction(name, opts.grammar);
Expand All @@ -34,7 +53,9 @@ var EBNF = (function(){
]
];
} else if (type ==="?") {
name = opts.production + "_option" + opts.optid++;
if (!name) {
name = opts.production + "_option" + opts.optid++;
}
emit(name);

opts = optsForProduction(name, opts.grammar);
Expand All @@ -45,7 +66,9 @@ var EBNF = (function(){
if (value.length == 1) {
emit(transformExpressionList(value[0], opts));
} else {
name = opts.production + "_group" + opts.groupid++;
if (!name) {
name = opts.production + "_group" + opts.groupid++;
}
emit(name);

opts = optsForProduction(name, opts.grammar);
Expand Down
66 changes: 66 additions & 0 deletions ebnf.y
@@ -0,0 +1,66 @@
/* EBNF grammar spec */

%lex

id [a-zA-Z][a-zA-Z0-9_-]*

%%
\s+ /* skip whitespace */
{id} return 'symbol';
"["{id}"]" yytext = yytext.substr(1, yyleng-2); return 'ALIAS';
"'"[^']*"'" return 'symbol';
"." return 'symbol';

bar return 'bar';
"(" return '(';
")" return ')';
"*" return '*';
"?" return '?';
"|" return '|';
"+" return '+';
<<EOF>> return 'EOF';
/lex

%start production

%%

production
: handle EOF
{ return $handle; }
;

handle_list
: handle
{ $$ = [$handle]; }
| handle_list '|' handle
{ $handle_list.push($handle); }
;

handle
:
{ $$ = []; }
| handle expression_suffix
{ $handle.push($expression_suffix); }
;

expression_suffix
: expression suffix ALIAS
{ $$ = ['xalias', $suffix, $expression, $ALIAS]; }
| expression suffix
{ if ($suffix) $$ = [$suffix, $expression]; else $$ = $expression; }
;

expression
: symbol
{ $$ = ['symbol', $symbol]; }
| '(' handle_list ')'
{ $$ = ['()', $handle_list]; }
;

suffix
:
| '*'
| '?'
| '+'
;
37 changes: 35 additions & 2 deletions tests/ebnf.js
Expand Up @@ -44,6 +44,25 @@ function testBadParse(top, strings) {
};
}

function testAlias(top, obj, str) {
return function() {
var grammar = {
"lex": {
"rules": [
["\\s+", ''],
["[A-Za-z]+", "return 'word';"],
[",", "return ',';"],
["$", "return 'EOF';"]
]
},
"start": "top",
"bnf": ebnf.transform({"top": [top]})
};
assert.deepEqual(grammar['bnf'], obj);
assert.ok(new Parser(grammar).parse(str));
};
}

var tests = {
"test idempotent transform": function() {
var first = {
Expand All @@ -63,10 +82,24 @@ var tests = {
"test group () on simple phrase": testParse("(word word) EOF", "two words"),
"test group () with multiple options on first option": testParse("((word word) | word) EOF", "hi there"),
"test group () with multiple options on second option": testParse("((word word) | word) EOF", "hi"),
"test complex expression ( *, ?, () )": testParse("(word (',' word)*)? EOF ", ["", "hi", "hi, there"])
"test complex expression ( *, ?, () )": testParse("(word (',' word)*)? EOF ", ["", "hi", "hi, there"]),
"test named repeat (*)": testAlias("word*[bob] EOF",
{ top: [ 'bob EOF' ],
bob: [ [ '', '$$ = [];' ], [ 'bob word', '$1.push($2);' ] ] }, "word"),
"test named repeat (+)": testAlias("word+[bob] EOF",
{ top: [ 'bob EOF' ],
bob: [ [ 'word', '$$ = [$1];' ], [ 'bob word', '$1.push($2);' ] ] }, "wordy word"),
"test named group ()": testAlias("word[alice] (',' word)*[bob] EOF",
{"top":["word[alice] bob EOF"],"bob":[["","$$ = [];"],["bob , word","$1.push($2);"]]},
"one, two"),
"test named option (?)": testAlias("word[alex] word?[bob] EOF", { top: [ 'word[alex] bob EOF' ], bob: [ '', 'word' ] }, "oneor two"),
"test named complex expression (())": testAlias("word[alpha] (word[alex] (word[bob] word[carol] ',')+[david] word ',')*[enoch] EOF",
{"top":["word[alpha] enoch EOF"],"david":[["word[bob] word[carol] ,","$$ = [$1];"],["david word[bob] word[carol] ,","$1.push($2);"]],
"enoch":[["","$$ = [];"],["enoch word[alex] david word ,","$1.push($2);"]]},
"one two three four, five,"
)
};

for (var test in tests) {
exports[test] = tests[test];
}

0 comments on commit ea638f6

Please sign in to comment.