Improve location info available in action code

Replace |line|, |column|, and |offset| functions with the |location|
function. It returns an object like this:

  {
    start: { offset: 23, line: 5, column: 6 },
    end:   { offset: 25, line: 5, column: 8 }
  }

In actions, |start| refers to the position at the beginning of action's
expression and |end| refers to the position after the end of action's
expression. This allows one to easily add location info e.g. to AST
nodes created in actions.

In predicates, both |start| and |end| refer to the current position.

Fixes #246.
redux
David Majda 9 years ago
parent e75f21dc8f
commit 4f7145e360

@ -321,10 +321,17 @@ otherwise consider the match failed.
The code inside the predicate can access all variables and functions defined in
the initializer at the beginning of the grammar.
The code inside the predicate can also access the current parse position using
the `offset` function. It returns a zero-based character index into the input
string. The code can also access the current line and column using the `line`
and `column` functions. Both return one-based indexes.
The code inside the predicate can also access location information using the
`location` function. It returns an object like this:
{
start: { offset: 23, line: 5, column: 6 },
end: { offset: 23, line: 5, column: 6 }
}
The `start` and `end` properties both refer to the current parse position. The
`offset` property contains an offset as a zero-based index and `line` and
`column` properties contain a line and a column as one-based indices.
The code inside the predicate can also access the parser object using the
`parser` variable and options passed to the parser using the `options` variable.
@ -343,10 +350,17 @@ otherwise consider the match failed.
The code inside the predicate can access all variables and functions defined in
the initializer at the beginning of the grammar.
The code inside the predicate can also access the current parse position using
the `offset` function. It returns a zero-based character index into the input
string. The code can also access the current line and column using the `line`
and `column` functions. Both return one-based indexes.
The code inside the predicate can also access location information using the
`location` function. It returns an object like this:
{
start: { offset: 23, line: 5, column: 6 },
end: { offset: 23, line: 5, column: 6 }
}
The `start` and `end` properties both refer to the current parse position. The
`offset` property contains an offset as a zero-based index and `line` and
`column` properties contain a line and a column as one-based indices.
The code inside the predicate can also access the parser object using the
`parser` variable and options passed to the parser using the `options` variable.
@ -397,11 +411,19 @@ must be balanced.
The code inside the action can also access the string matched by the expression
using the `text` function.
The code inside the action can also access the parse position at the beginning
of the action's expression using the `offset` function. It returns a zero-based
character index into the input string. The code can also access the line and
column at the beginning of the action's expression using the `line` and `column`
functions. Both return one-based indexes.
The code inside the action can also access location information using the
`location` function. It returns an object like this:
{
start: { offset: 23, line: 5, column: 6 },
end: { offset: 25, line: 5, column: 8 }
}
The `start` property refers to the position at the beginning of the expression,
the `end` property refers to position after the end of the expression. The
`offset` property contains an offset as a zero-based index and `line` and
`column` properties contain a line and a column as one-based indices.
The code inside the action can also access the parser object using the `parser`
variable and options passed to the parser using the `options` variable.

@ -928,16 +928,22 @@ function generateJavascript(ast, options) {
' return input.substring(peg$reportedPos, peg$currPos);',
' }',
'',
' function offset() {',
' return peg$reportedPos;',
' }',
'',
' function line() {',
' return peg$computePosDetails(peg$reportedPos).line;',
' }',
' function location() {',
' var reportedPosDetails = peg$computePosDetails(peg$reportedPos),',
' currPosDetails = peg$computePosDetails(peg$currPos);',
'',
' function column() {',
' return peg$computePosDetails(peg$reportedPos).column;',
' return {',
' start: {',
' offset: peg$reportedPos,',
' line: reportedPosDetails.line,',
' column: reportedPosDetails.column',
' },',
' end: {',
' offset: peg$currPos,',
' line: currPosDetails.line,',
' column: currPosDetails.column',
' }',
' };',
' }',
'',
' function expected(description) {',
@ -982,7 +988,11 @@ function generateJavascript(ast, options) {
' peg$cachedPos = pos;',
' }',
'',
' return peg$cachedPosDetails;',
' return {',
' line: peg$cachedPosDetails.line,',
' column: peg$cachedPosDetails.column,',
' seenCR: peg$cachedPosDetails.seenCR',
' };',
' }',
'',
' function peg$fail(expected) {',

@ -356,16 +356,22 @@ module.exports = (function() {
return input.substring(peg$reportedPos, peg$currPos);
}
function offset() {
return peg$reportedPos;
}
function line() {
return peg$computePosDetails(peg$reportedPos).line;
}
function column() {
return peg$computePosDetails(peg$reportedPos).column;
function location() {
var reportedPosDetails = peg$computePosDetails(peg$reportedPos),
currPosDetails = peg$computePosDetails(peg$currPos);
return {
start: {
offset: peg$reportedPos,
line: reportedPosDetails.line,
column: reportedPosDetails.column
},
end: {
offset: peg$currPos,
line: currPosDetails.line,
column: currPosDetails.column
}
};
}
function expected(description) {
@ -410,7 +416,11 @@ module.exports = (function() {
peg$cachedPos = pos;
}
return peg$cachedPosDetails;
return {
line: peg$cachedPosDetails.line,
column: peg$cachedPosDetails.column,
seenCR: peg$cachedPosDetails.seenCR
};
}
function peg$fail(expected) {

@ -580,27 +580,45 @@ describe("generated parser behavior", function() {
expect(parser).toParse("", { a: 42 }, { a: 42 });
});
it("|offset|, |line|, and |column| return current parse position, line, and column", function() {
it("|location| returns current location info", function() {
var parser = PEG.buildParser([
'{ var result; }',
'start = line (nl+ line)* { return result; }',
'line = thing (" "+ thing)*',
'thing = digit / mark',
'digit = [0-9]',
'mark = &{ result = [offset(), line(), column()]; return true; } "x"',
'mark = &{ result = location(); return true; } "x"',
'nl = [\\r"\\n\\u2028\\u2029]'
].join("\n"), options);
expect(parser).toParse("1\n2\n\n3\n\n\n4 5 x", [13, 7, 5]);
expect(parser).toParse("1\n2\n\n3\n\n\n4 5 x", {
start: { offset: 13, line: 7, column: 5 },
end: { offset: 13, line: 7, column: 5 },
});
/* Non-Unix newlines */
expect(parser).toParse("1\rx", [2, 2, 1]); // Old Mac
expect(parser).toParse("1\r\nx", [3, 2, 1]); // Windows
expect(parser).toParse("1\n\rx", [3, 3, 1]); // mismatched
expect(parser).toParse("1\rx", { // Old Mac
start: { offset: 2, line: 2, column: 1 },
end: { offset: 2, line: 2, column: 1 },
});
expect(parser).toParse("1\r\nx", { // Windows
start: { offset: 3, line: 2, column: 1 },
end: { offset: 3, line: 2, column: 1 },
});
expect(parser).toParse("1\n\rx", { // mismatched
start: { offset: 3, line: 3, column: 1 },
end: { offset: 3, line: 3, column: 1 },
});
/* Strange newlines */
expect(parser).toParse("1\u2028x", [2, 2, 1]); // line separator
expect(parser).toParse("1\u2029x", [2, 2, 1]); // paragraph separator
expect(parser).toParse("1\u2028x", { // line separator
start: { offset: 2, line: 2, column: 1 },
end: { offset: 2, line: 2, column: 1 },
});
expect(parser).toParse("1\u2029x", { // paragraph separator
start: { offset: 2, line: 2, column: 1 },
end: { offset: 2, line: 2, column: 1 },
});
});
});
});
@ -762,27 +780,45 @@ describe("generated parser behavior", function() {
expect(parser).toParse("", { a: 42 }, { a: 42 });
});
it("|offset|, |line|, and |column| return current parse position, line, and column", function() {
it("|location| returns current location info", function() {
var parser = PEG.buildParser([
'{ var result; }',
'start = line (nl+ line)* { return result; }',
'line = thing (" "+ thing)*',
'thing = digit / mark',
'digit = [0-9]',
'mark = !{ result = [offset(), line(), column()]; return false; } "x"',
'mark = !{ result = location(); return false; } "x"',
'nl = [\\r"\\n\\u2028\\u2029]'
].join("\n"), options);
expect(parser).toParse("1\n2\n\n3\n\n\n4 5 x", [13, 7, 5]);
expect(parser).toParse("1\n2\n\n3\n\n\n4 5 x", {
start: { offset: 13, line: 7, column: 5 },
end: { offset: 13, line: 7, column: 5 },
});
/* Non-Unix newlines */
expect(parser).toParse("1\rx", [2, 2, 1]); // Old Mac
expect(parser).toParse("1\r\nx", [3, 2, 1]); // Windows
expect(parser).toParse("1\n\rx", [3, 3, 1]); // mismatched
expect(parser).toParse("1\rx", { // Old Mac
start: { offset: 2, line: 2, column: 1 },
end: { offset: 2, line: 2, column: 1 },
});
expect(parser).toParse("1\r\nx", { // Windows
start: { offset: 3, line: 2, column: 1 },
end: { offset: 3, line: 2, column: 1 },
});
expect(parser).toParse("1\n\rx", { // mismatched
start: { offset: 3, line: 3, column: 1 },
end: { offset: 3, line: 3, column: 1 },
});
/* Strange newlines */
expect(parser).toParse("1\u2028x", [2, 2, 1]); // line separator
expect(parser).toParse("1\u2029x", [2, 2, 1]); // paragraph separator
expect(parser).toParse("1\u2028x", { // line separator
start: { offset: 2, line: 2, column: 1 },
end: { offset: 2, line: 2, column: 1 },
});
expect(parser).toParse("1\u2029x", { // paragraph separator
start: { offset: 2, line: 2, column: 1 },
end: { offset: 2, line: 2, column: 1 },
});
});
});
});
@ -1158,27 +1194,45 @@ describe("generated parser behavior", function() {
expect(parser).toParse("a", "a");
});
it("|offset|, |line|, and |column| return parse position, line, and column at the beginning of the expression", function() {
it("|location| returns location info of the expression", function() {
var parser = PEG.buildParser([
'{ var result; }',
'start = line (nl+ line)* { return result; }',
'line = thing (" "+ thing)*',
'thing = digit / mark',
'digit = [0-9]',
'mark = "x" { result = [offset(), line(), column()]; }',
'mark = "x" { result = location(); }',
'nl = [\\r\\n\\u2028\\u2029]'
].join("\n"), options);
expect(parser).toParse("1\n2\n\n3\n\n\n4 5 x", [13, 7, 5]);
expect(parser).toParse("1\n2\n\n3\n\n\n4 5 x", {
start: { offset: 13, line: 7, column: 5 },
end: { offset: 14, line: 7, column: 6 },
});
/* Non-Unix newlines */
expect(parser).toParse("1\rx", [2, 2, 1]); // Old Mac
expect(parser).toParse("1\r\nx", [3, 2, 1]); // Windows
expect(parser).toParse("1\n\rx", [3, 3, 1]); // mismatched
expect(parser).toParse("1\rx", { // Old Mac
start: { offset: 2, line: 2, column: 1 },
end: { offset: 3, line: 2, column: 2 },
});
expect(parser).toParse("1\r\nx", { // Windows
start: { offset: 3, line: 2, column: 1 },
end: { offset: 4, line: 2, column: 2 },
});
expect(parser).toParse("1\n\rx", { // mismatched
start: { offset: 3, line: 3, column: 1 },
end: { offset: 4, line: 3, column: 2 },
});
/* Strange newlines */
expect(parser).toParse("1\u2028x", [2, 2, 1]); // line separator
expect(parser).toParse("1\u2029x", [2, 2, 1]); // paragraph separator
expect(parser).toParse("1\u2028x", { // line separator
start: { offset: 2, line: 2, column: 1 },
end: { offset: 3, line: 2, column: 2 },
});
expect(parser).toParse("1\u2029x", { // paragraph separator
start: { offset: 2, line: 2, column: 1 },
end: { offset: 3, line: 2, column: 2 },
});
});
it("|expected| terminates parsing and throws an exception", function() {

Loading…
Cancel
Save