Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Modified ebnf-transform to allow symbol aliases. #3

Merged
merged 2 commits into from
Aug 10, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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];
}

Loading