diff --git a/src/ast/reference.js b/src/ast/reference.js index b9393e5..6c466c6 100644 --- a/src/ast/reference.js +++ b/src/ast/reference.js @@ -15,8 +15,13 @@ util.assign(Reference.prototype, { return ['reference', this.refName]; }, - compile: function(builder, address) { + compile: function(builder, address, action) { builder.jump_(address, this.refName); + if (action) { + builder.ifNode_(address, function(builder){ + builder.assign_(address, builder.action_(address, action)); + }, this); + } } }); diff --git a/src/builders/java.js b/src/builders/java.js index 6def96d..5909cae 100644 --- a/src/builders/java.js +++ b/src/builders/java.js @@ -464,6 +464,12 @@ util.assign(Builder.prototype, { this.conditional_('while', expression + ' != ' + this.nullNode_(), block, context); }, + action_: function(address, action) { + var args = ['input', -1, -1, address]; + action = 'actions.' + action; + return action + '(' + args.join(', ') + ')'; + }, + stringMatch_: function(expression, string) { return expression + ' != null && ' + expression + '.equals(' + this._quote(string) + ')'; }, diff --git a/src/builders/javascript.js b/src/builders/javascript.js index 2088c98..1ae6126 100644 --- a/src/builders/javascript.js +++ b/src/builders/javascript.js @@ -350,6 +350,12 @@ util.assign(Builder.prototype, { this.conditional_('while', expression + ' !== ' + this.nullNode_(), block, context); }, + action_: function(address, action) { + var args = ['this._input', -1, -1, address]; + action = 'this._actions.' + action; + return action + '(' + args.join(', ') + ')'; + }, + stringMatch_: function(expression, string) { return expression + ' === ' + this._quote(string); }, diff --git a/src/builders/python.js b/src/builders/python.js index 35eb671..59eb4ac 100644 --- a/src/builders/python.js +++ b/src/builders/python.js @@ -322,6 +322,12 @@ util.assign(Builder.prototype, { this._indent(block, context); }, + action_: function(address, action) { + var args = ['self._input', -1, -1, address]; + action = 'self._actions.' + action; + return action + '(' + args.join(', ') + ')'; + }, + stringMatch_: function(expression, string) { return expression + ' == ' + this._quote(string); }, diff --git a/src/builders/ruby.js b/src/builders/ruby.js index e9b22eb..cf6f6a9 100644 --- a/src/builders/ruby.js +++ b/src/builders/ruby.js @@ -339,6 +339,12 @@ util.assign(Builder.prototype, { this._line('end'); }, + action_: function(address, action) { + var args = ['@input', -1, -1, address]; + action = '@actions.' + action; + return action + '(' + args.join(', ') + ')'; + }, + stringMatch_: function(expression, string) { return expression + ' == ' + this._quote(string); }, diff --git a/src/meta_grammar.js b/src/meta_grammar.js index 936aecd..10d0fb3 100644 --- a/src/meta_grammar.js +++ b/src/meta_grammar.js @@ -1173,88 +1173,95 @@ address0 = this._read_maybe_atom(); if (address0 === FAILURE) { this._offset = index1; - address0 = this._read_terminal(); + address0 = this._read_reference(); if (address0 === FAILURE) { this._offset = index1; - var index2 = this._offset, elements0 = new Array(5); - var address1 = FAILURE; - var chunk0 = null, max0 = this._offset + 1; - if (max0 <= this._inputSize) { - chunk0 = this._input.substring(this._offset, max0); - } - if (chunk0 === '(') { - address1 = new TreeNode(this._input.substring(this._offset, this._offset + 1), this._offset, []); - this._offset = this._offset + 1; - } else { - address1 = FAILURE; - if (this._offset > this._failure) { - this._failure = this._offset; - this._expected = []; - } - if (this._offset === this._failure) { - this._expected.push('"("'); - } - } - if (address1 !== FAILURE) { - elements0[0] = address1; - var address2 = FAILURE; - var remaining0 = 0, index3 = this._offset, elements1 = [], address3 = true; - while (address3 !== FAILURE) { - address3 = this._read__(); - if (address3 !== FAILURE) { - elements1.push(address3); - --remaining0; - } + address0 = this._read_terminal(); + if (address0 === FAILURE) { + this._offset = index1; + var index2 = this._offset, elements0 = new Array(5); + var address1 = FAILURE; + var chunk0 = null, max0 = this._offset + 1; + if (max0 <= this._inputSize) { + chunk0 = this._input.substring(this._offset, max0); } - if (remaining0 <= 0) { - address2 = new TreeNode(this._input.substring(index3, this._offset), index3, elements1); - this._offset = this._offset; + if (chunk0 === '(') { + address1 = new TreeNode(this._input.substring(this._offset, this._offset + 1), this._offset, []); + this._offset = this._offset + 1; } else { - address2 = FAILURE; + address1 = FAILURE; + if (this._offset > this._failure) { + this._failure = this._offset; + this._expected = []; + } + if (this._offset === this._failure) { + this._expected.push('"("'); + } } - if (address2 !== FAILURE) { - elements0[1] = address2; - var address4 = FAILURE; - address4 = this._read_actionable(); - if (address4 !== FAILURE) { - elements0[2] = address4; - var address5 = FAILURE; - var remaining1 = 0, index4 = this._offset, elements2 = [], address6 = true; - while (address6 !== FAILURE) { - address6 = this._read__(); - if (address6 !== FAILURE) { - elements2.push(address6); - --remaining1; - } - } - if (remaining1 <= 0) { - address5 = new TreeNode(this._input.substring(index4, this._offset), index4, elements2); - this._offset = this._offset; - } else { - address5 = FAILURE; + if (address1 !== FAILURE) { + elements0[0] = address1; + var address2 = FAILURE; + var remaining0 = 0, index3 = this._offset, elements1 = [], address3 = true; + while (address3 !== FAILURE) { + address3 = this._read__(); + if (address3 !== FAILURE) { + elements1.push(address3); + --remaining0; } - if (address5 !== FAILURE) { - elements0[3] = address5; - var address7 = FAILURE; - var chunk1 = null, max1 = this._offset + 1; - if (max1 <= this._inputSize) { - chunk1 = this._input.substring(this._offset, max1); + } + if (remaining0 <= 0) { + address2 = new TreeNode(this._input.substring(index3, this._offset), index3, elements1); + this._offset = this._offset; + } else { + address2 = FAILURE; + } + if (address2 !== FAILURE) { + elements0[1] = address2; + var address4 = FAILURE; + address4 = this._read_actionable(); + if (address4 !== FAILURE) { + elements0[2] = address4; + var address5 = FAILURE; + var remaining1 = 0, index4 = this._offset, elements2 = [], address6 = true; + while (address6 !== FAILURE) { + address6 = this._read__(); + if (address6 !== FAILURE) { + elements2.push(address6); + --remaining1; + } } - if (chunk1 === ')') { - address7 = new TreeNode(this._input.substring(this._offset, this._offset + 1), this._offset, []); - this._offset = this._offset + 1; + if (remaining1 <= 0) { + address5 = new TreeNode(this._input.substring(index4, this._offset), index4, elements2); + this._offset = this._offset; } else { - address7 = FAILURE; - if (this._offset > this._failure) { - this._failure = this._offset; - this._expected = []; + address5 = FAILURE; + } + if (address5 !== FAILURE) { + elements0[3] = address5; + var address7 = FAILURE; + var chunk1 = null, max1 = this._offset + 1; + if (max1 <= this._inputSize) { + chunk1 = this._input.substring(this._offset, max1); } - if (this._offset === this._failure) { - this._expected.push('")"'); + if (chunk1 === ')') { + address7 = new TreeNode(this._input.substring(this._offset, this._offset + 1), this._offset, []); + this._offset = this._offset + 1; + } else { + address7 = FAILURE; + if (this._offset > this._failure) { + this._failure = this._offset; + this._expected = []; + } + if (this._offset === this._failure) { + this._expected.push('")"'); + } + } + if (address7 !== FAILURE) { + elements0[4] = address7; + } else { + elements0 = null; + this._offset = index2; } - } - if (address7 !== FAILURE) { - elements0[4] = address7; } else { elements0 = null; this._offset = index2; @@ -1271,18 +1278,15 @@ elements0 = null; this._offset = index2; } - } else { - elements0 = null; - this._offset = index2; - } - if (elements0 === null) { - address0 = FAILURE; - } else { - address0 = this._actions.paren_expr(this._input, index2, this._offset, elements0); - this._offset = this._offset; - } - if (address0 === FAILURE) { - this._offset = index1; + if (elements0 === null) { + address0 = FAILURE; + } else { + address0 = this._actions.paren_expr(this._input, index2, this._offset, elements0); + this._offset = this._offset; + } + if (address0 === FAILURE) { + this._offset = index1; + } } } } diff --git a/src/meta_grammar.peg b/src/meta_grammar.peg index 5e177be..836e552 100644 --- a/src/meta_grammar.peg +++ b/src/meta_grammar.peg @@ -56,6 +56,7 @@ action_expression <- actionable _+ action_tag %action actionable <- sequence / repeated_atom / maybe_atom + / reference / terminal / "(" _* actionable _* ")" %paren_expr diff --git a/test/canopy/compiler/action_spec.js b/test/canopy/compiler/action_spec.js index 01f7b4f..94156b6 100644 --- a/test/canopy/compiler/action_spec.js +++ b/test/canopy/compiler/action_spec.js @@ -191,4 +191,39 @@ jstest.describe("Compiler.Action", function() { with(this) { assertEqual( 0, ChoiceActionTest.parse('0', {actions: actions}) ) }}) }}) + + describe('constructing a referenced node', function() { with(this) { + before(function() { with(this) { + compile('grammar global.RefActionTest \ + rule <- begin %override \ + begin <- "begin" %begin') + + this.actions = { + begin: function() { return {type: 'tBEGIN'} }, + override: function(input, start, end, elements) { return elements.type.toLowerCase(); } + } + }}) + + it('creates nodes using the named action', function() { with(this) { + assertEqual('tbegin', RefActionTest.parse('begin', {actions: actions}) ) + }}) + }}) + describe('constructing another referenced node', function() { with(this) { + before(function() { with(this) { + compile('grammar global.RefActionTest \ + rule <- (notbegin %override) / (begin %override) \ + notbegin <- "notbegin" %notbegin\ + begin <- "begin" %begin') + + this.actions = { + begin: function() { return {type: 'tBEGIN'} }, + notbegin: function() { return {type: 'tNOTBEGIN'} }, + override: function(input, start, end, elements) { return elements.type.toLowerCase(); } + } + }}) + + it('creates nodes using the named action', function() { with(this) { + assertEqual('tbegin', RefActionTest.parse('begin', {actions: actions}) ) + }}) + }}) }}) diff --git a/test/canopy/meta_grammar_spec.js b/test/canopy/meta_grammar_spec.js index 37ab3d6..c4dd7d6 100644 --- a/test/canopy/meta_grammar_spec.js +++ b/test/canopy/meta_grammar_spec.js @@ -290,6 +290,27 @@ jstest.describe("MetaGrammar", function() { with(this) { }}) }}) + describe('with an actionable referencing rule', function() { with(this) { + before(function() { with(this) { + this.compiler = new Compiler(' \ + grammar References \ + first <- second %make_rep \ + second <- "done" \ + ') + }}) + + it('compiles a referencing-rule parser', function() { with(this) { + assertEqual(['grammar', 'References', + ['rule', 'first', + ['action', 'make_rep', + ['reference', 'second']]], + ['rule', 'second', + ['string', 'done']]], + + compiler.toSexp() ) + }}) + }}) + describe('with comments', function() { with(this) { before(function() { with(this) { this.compiler = new Compiler(' \