Skip to content

Commit

Permalink
Fix offset calculation for parsed nodes.
Browse files Browse the repository at this point in the history
Closes #335.

This builds on #117 by ensuring that not only are line & column
information corrected to account for control characters inserted by the
preprocessor, but offset information is too. Unfortunately this expands
the hack that overrides the code generated by PEGjs, but it seemed the
cleanest and most performant way to solve the problem.
  • Loading branch information
eventualbuddha committed Jan 22, 2015
1 parent b31ccfb commit 9ac232b
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 7 deletions.
2 changes: 1 addition & 1 deletion lib/parser.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 16 additions & 2 deletions src/grammar.pegcoffee
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,19 @@
r = p = rp = id

`
// XXX: this overrides the function with the same name generated by PEGjs; see comment within
// XXX: The functions below override the default code generated by PEGjs.
// CoffeeScriptRedux has a preprocessor that adds control characters to
// mark indents/outdents/etc for PEGjs. These characters cause the line,
// column, and offset values to differ from the original input source code,
// so this section exists to properly hide those control characters when
// reporting location information. See #117 & #335.

var csr$controlCharacterCount = 0;

function offset() {
return peg$reportedPos - csr$controlCharacterCount;
}

function peg$computePosDetails() {
function advanceCachedReportedPos() {
var ch;
Expand All @@ -274,16 +286,18 @@
peg$cachedPosDetails.line++;
peg$cachedPosDetails.column = 1;
peg$cachedPosDetails.seenCR = true;
// XXX: strip control characters when calculating position information; see #117
} else if(!/[\uEFEF\uEFFE\uEFFF]/.test(ch)) {
peg$cachedPosDetails.column++;
peg$cachedPosDetails.seenCR = false;
} else {
csr$controlCharacterCount++;
}
}
}

if (peg$cachedPos !== peg$reportedPos) {
if (peg$cachedPos > peg$reportedPos) {
csr$controlCharacterCount = 0;
peg$cachedPos = 0;
peg$cachedPosDetails = { line: 1, column: 1, seenCR: false };
}
Expand Down
17 changes: 13 additions & 4 deletions test/parser.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@ suite 'Parser', ->
setup ->
@shouldParse = (input) -> doesNotThrow -> parse input
@shouldNotParse = (input) -> throws -> parse input
@checkNodeRaw = (node, source) =>
rawAtOffset = source[node.offset...(node.offset + node.raw.length)]
if node.raw isnt rawAtOffset
fail "expected #{node.className} raw to equal #{JSON.stringify(rawAtOffset)}, but was #{JSON.stringify(node.raw)}"
for own prop, child of node
if Array.isArray child
@checkNodeRaw element, source for element in child
else if child instanceof CoffeeScript.Nodes.Nodes
@checkNodeRaw child, source


test 'empty program', -> @shouldParse ''
Expand Down Expand Up @@ -172,9 +181,9 @@ suite 'Parser', ->
suite 'position/offset preservation', ->

test 'basic indentation', ->
ast = parse '''
source = '''
fn = ->
body
''', raw: yes
eq 3, ast.body.statements[0].expression.body.statements[0].column
eq 11, ast.body.statements[0].expression.body.statements[0].offset
'''
ast = parse source, raw: yes
@checkNodeRaw ast, source

0 comments on commit 9ac232b

Please sign in to comment.