Update AST location info to match SpiderMonkey

Part of #889
This commit is contained in:
kpdecker
2014-11-26 09:01:03 -06:00
parent 3a9440f954
commit 61dd721ca2
9 changed files with 94 additions and 93 deletions
+14 -22
View File
@@ -1,16 +1,8 @@
import Exception from "../exception";
function LocationInfo(locInfo) {
locInfo = locInfo || {};
this.firstLine = locInfo.first_line;
this.firstColumn = locInfo.first_column;
this.lastColumn = locInfo.last_column;
this.lastLine = locInfo.last_line;
}
var AST = {
ProgramNode: function(statements, blockParams, strip, locInfo) {
LocationInfo.call(this, locInfo);
this.loc = locInfo;
this.type = "program";
this.statements = statements;
this.blockParams = blockParams;
@@ -18,7 +10,7 @@ var AST = {
},
MustacheNode: function(rawParams, hash, open, strip, locInfo) {
LocationInfo.call(this, locInfo);
this.loc = locInfo;
this.type = "mustache";
this.strip = strip;
@@ -47,7 +39,7 @@ var AST = {
},
SexprNode: function(rawParams, hash, locInfo) {
LocationInfo.call(this, locInfo);
this.loc = locInfo;
this.type = "sexpr";
this.hash = hash;
@@ -70,7 +62,7 @@ var AST = {
},
PartialNode: function(partialName, context, hash, strip, locInfo) {
LocationInfo.call(this, locInfo);
this.loc = locInfo;
this.type = "partial";
this.partialName = partialName;
this.context = context;
@@ -81,7 +73,7 @@ var AST = {
},
BlockNode: function(sexpr, program, inverse, strip, locInfo) {
LocationInfo.call(this, locInfo);
this.loc = locInfo;
this.type = 'block';
this.sexpr = sexpr;
@@ -95,19 +87,19 @@ var AST = {
},
ContentNode: function(string, locInfo) {
LocationInfo.call(this, locInfo);
this.loc = locInfo;
this.type = "content";
this.original = this.string = string;
},
HashNode: function(pairs, locInfo) {
LocationInfo.call(this, locInfo);
this.loc = locInfo;
this.type = "hash";
this.pairs = pairs;
},
IdNode: function(parts, locInfo) {
LocationInfo.call(this, locInfo);
this.loc = locInfo;
this.type = "ID";
var original = "",
@@ -147,13 +139,13 @@ var AST = {
},
PartialNameNode: function(name, locInfo) {
LocationInfo.call(this, locInfo);
this.loc = locInfo;
this.type = "PARTIAL_NAME";
this.name = name.original;
},
DataNode: function(id, locInfo) {
LocationInfo.call(this, locInfo);
this.loc = locInfo;
this.type = "DATA";
this.id = id;
this.stringModeValue = id.stringModeValue;
@@ -161,7 +153,7 @@ var AST = {
},
StringNode: function(string, locInfo) {
LocationInfo.call(this, locInfo);
this.loc = locInfo;
this.type = "STRING";
this.original =
this.string =
@@ -169,7 +161,7 @@ var AST = {
},
NumberNode: function(number, locInfo) {
LocationInfo.call(this, locInfo);
this.loc = locInfo;
this.type = "NUMBER";
this.original =
this.number = number;
@@ -177,14 +169,14 @@ var AST = {
},
BooleanNode: function(bool, locInfo) {
LocationInfo.call(this, locInfo);
this.loc = locInfo;
this.type = "BOOLEAN";
this.bool = bool;
this.stringModeValue = bool === "true";
},
CommentNode: function(comment, strip, locInfo) {
LocationInfo.call(this, locInfo);
this.loc = locInfo;
this.type = "comment";
this.comment = comment;
+6 -1
View File
@@ -8,11 +8,16 @@ export { parser };
var yy = {};
extend(yy, Helpers, AST);
export function parse(input) {
export function parse(input, options) {
// Just return if an already-compile AST was passed in.
if (input.constructor === AST.ProgramNode) { return input; }
parser.yy = yy;
// Altering the shared object here, but this is ok as parser is a sync operation
yy.locInfo = function(locInfo) {
return new yy.SourceLocation(options && options.srcName, locInfo);
};
return parser.parse(input);
}
+4 -4
View File
@@ -79,18 +79,18 @@ CodeGen.prototype = {
},
empty: function(loc) {
loc = loc || this.currentLocation || {};
return new SourceNode(loc.firstLine, loc.firstColumn, this.srcFile);
loc = loc || this.currentLocation || {start:{}};
return new SourceNode(loc.start.line, loc.start.column, this.srcFile);
},
wrap: function(chunk, loc) {
if (chunk instanceof SourceNode) {
return chunk;
}
loc = loc || this.currentLocation || {};
loc = loc || this.currentLocation || {start:{}};
chunk = castChunk(chunk, this, loc);
return new SourceNode(loc.firstLine, loc.firstColumn, this.srcFile, chunk);
return new SourceNode(loc.start.line, loc.start.column, this.srcFile, chunk);
},
functionCall: function(fn, type, params) {
+3 -7
View File
@@ -298,11 +298,7 @@ Compiler.prototype = {
// HELPERS
opcode: function(name, node) {
var loc = {
firstLine: node.firstLine, firstColumn: node.firstColumn,
lastLine: node.lastLine, lastColumn: node.lastColumn
};
this.opcodes.push({ opcode: name, args: slice.call(arguments, 2), loc: loc });
this.opcodes.push({ opcode: name, args: slice.call(arguments, 2), loc: node.loc });
},
addDepth: function(depth) {
@@ -393,7 +389,7 @@ export function precompile(input, options, env) {
options.useDepths = true;
}
var ast = env.parse(input);
var ast = env.parse(input, options);
var environment = new env.Compiler().compile(ast, options);
return new env.JavaScriptCompiler().compile(environment, options);
}
@@ -415,7 +411,7 @@ export function compile(input, options, env) {
var compiled;
function compileInput() {
var ast = env.parse(input);
var ast = env.parse(input, options);
var environment = new env.Compiler().compile(ast, options);
var templateSpec = new env.JavaScriptCompiler().compile(environment, options, undefined, true);
return env.template(templateSpec);
+14 -8
View File
@@ -1,5 +1,17 @@
import Exception from "../exception";
export function SourceLocation(source, locInfo) {
this.source = source;
this.start = {
line: locInfo.first_line,
column: locInfo.first_column
};
this.end = {
line: locInfo.last_line,
column: locInfo.last_column
};
}
export function stripFlags(open, close) {
return {
left: open.charAt(2) === '~',
@@ -15,10 +27,7 @@ export function stripComment(comment) {
export function prepareRawBlock(openRawBlock, content, close, locInfo) {
/*jshint -W040 */
if (openRawBlock.sexpr.id.original !== close) {
var errorNode = {
firstLine: openRawBlock.sexpr.firstLine,
firstColumn: openRawBlock.sexpr.firstColumn
};
var errorNode = {loc: openRawBlock.sexpr.loc};
throw new Exception(openRawBlock.sexpr.id.original + " doesn't match " + close, errorNode);
}
@@ -32,10 +41,7 @@ export function prepareBlock(openBlock, program, inverseAndProgram, close, inver
/*jshint -W040 */
// When we are chaining inverse calls, we will not have a close path
if (close && close.path && openBlock.sexpr.id.original !== close.path.original) {
var errorNode = {
firstLine: openBlock.sexpr.firstLine,
firstColumn: openBlock.sexpr.firstColumn
};
var errorNode = {loc: openBlock.sexpr.loc};
throw new Exception(openBlock.sexpr.id.original + ' doesn\'t match ' + close.path.original, errorNode);
}
@@ -136,7 +136,7 @@ JavaScriptCompiler.prototype = {
if (!asObject) {
ret.compiler = JSON.stringify(ret.compiler);
this.source.currentLocation = {firstLine: 1, firstColumn: 0};
this.source.currentLocation = {start: {line: 1, column: 0}};
ret = this.objectLiteral(ret);
if (options.srcName) {
+9 -6
View File
@@ -2,11 +2,14 @@
var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack'];
function Exception(message, node) {
var line;
if (node && node.firstLine) {
line = node.firstLine;
var loc = node && node.loc,
line,
column;
if (loc) {
line = loc.start.line;
column = loc.start.column;
message += ' - ' + line + ':' + node.firstColumn;
message += ' - ' + line + ':' + column;
}
var tmp = Error.prototype.constructor.call(this, message);
@@ -16,9 +19,9 @@ function Exception(message, node) {
this[errorProps[idx]] = tmp[errorProps[idx]];
}
if (line) {
if (loc) {
this.lineNumber = line;
this.column = node.firstColumn;
this.column = column;
}
}
+19 -20
View File
@@ -5,22 +5,21 @@ describe('ast', function() {
}
var LOCATION_INFO = {
last_line: 0,
first_line: 0,
first_column: 0,
last_column: 0
start: {
line: 1,
column: 1
},
end: {
line: 1,
column: 1
}
};
function testLocationInfoStorage(node){
var properties = [ 'firstLine', 'lastLine', 'firstColumn', 'lastColumn' ],
property,
propertiesLen = properties.length,
i;
for (i = 0; i < propertiesLen; i++){
property = properties[0];
equals(node[property], 0);
}
equals(node.loc.start.line, 1);
equals(node.loc.start.column, 1);
equals(node.loc.end.line, 1);
equals(node.loc.end.column, 1);
}
describe('MustacheNode', function() {
@@ -94,21 +93,21 @@ describe('ast', function() {
{part: 'foo'},
{part: '..'},
{part: 'bar'}
], {first_line: 1, first_column: 1});
], {start: {line: 1, column: 1}});
}, Handlebars.Exception, "Invalid path: foo.. - 1:1");
shouldThrow(function() {
new handlebarsEnv.AST.IdNode([
{part: 'foo'},
{part: '.'},
{part: 'bar'}
], {first_line: 1, first_column: 1});
], {start: {line: 1, column: 1}});
}, Handlebars.Exception, "Invalid path: foo. - 1:1");
shouldThrow(function() {
new handlebarsEnv.AST.IdNode([
{part: 'foo'},
{part: 'this'},
{part: 'bar'}
], {first_line: 1, first_column: 1});
], {start: {line: 1, column: 1}});
}, Handlebars.Exception, "Invalid path: foothis - 1:1");
});
@@ -201,10 +200,10 @@ describe('ast', function() {
var ast, statements;
function testColumns(node, firstLine, lastLine, firstColumn, lastColumn){
equals(node.firstLine, firstLine);
equals(node.lastLine, lastLine);
equals(node.firstColumn, firstColumn);
equals(node.lastColumn, lastColumn);
equals(node.loc.start.line, firstLine);
equals(node.loc.start.column, firstColumn);
equals(node.loc.end.line, lastLine);
equals(node.loc.end.column, lastColumn);
}
ast = Handlebars.parse("line 1 {{line1Token}}\n line 2 {{line2token}}\n line 3 {{#blockHelperOnLine3}}\nline 4{{line4token}}\n" +
+24 -24
View File
@@ -9,7 +9,7 @@ root
;
program
: statement* -> new yy.ProgramNode(yy.prepareProgram($1), null, {}, @$)
: statement* -> new yy.ProgramNode(yy.prepareProgram($1), null, {}, yy.locInfo(@$))
;
statement
@@ -18,15 +18,15 @@ statement
| rawBlock -> $1
| partial -> $1
| content -> $1
| COMMENT -> new yy.CommentNode(yy.stripComment($1), yy.stripFlags($1, $1), @$)
| COMMENT -> new yy.CommentNode(yy.stripComment($1), yy.stripFlags($1, $1), yy.locInfo(@$))
;
content
: CONTENT -> new yy.ContentNode($1, @$)
: CONTENT -> new yy.ContentNode($1, yy.locInfo(@$))
;
rawBlock
: openRawBlock content END_RAW_BLOCK -> yy.prepareRawBlock($1, $2, $3, @$)
: openRawBlock content END_RAW_BLOCK -> yy.prepareRawBlock($1, $2, $3, yy.locInfo(@$))
;
openRawBlock
@@ -34,8 +34,8 @@ openRawBlock
;
block
: openBlock program inverseChain? closeBlock -> yy.prepareBlock($1, $2, $3, $4, false, @$)
| openInverse program inverseAndProgram? closeBlock -> yy.prepareBlock($1, $2, $3, $4, true, @$)
: openBlock program inverseChain? closeBlock -> yy.prepareBlock($1, $2, $3, $4, false, yy.locInfo(@$))
| openInverse program inverseAndProgram? closeBlock -> yy.prepareBlock($1, $2, $3, $4, true, yy.locInfo(@$))
;
openBlock
@@ -47,7 +47,7 @@ openInverse
;
openInverseChain
: OPEN_INVERSE_CHAIN sexpr CLOSE -> new yy.MustacheNode($2, null, $1, yy.stripFlags($1, $3), @$)
: OPEN_INVERSE_CHAIN sexpr CLOSE -> new yy.MustacheNode($2, null, $1, yy.stripFlags($1, $3), yy.locInfo(@$))
;
inverseAndProgram
@@ -56,8 +56,8 @@ inverseAndProgram
inverseChain
: openInverseChain program inverseChain? {
var inverse = yy.prepareBlock($1, $2, $3, $3, false, @$),
program = new yy.ProgramNode(yy.prepareProgram([inverse]), null, {}, @$);
var inverse = yy.prepareBlock($1, $2, $3, $3, false, yy.locInfo(@$)),
program = new yy.ProgramNode(yy.prepareProgram([inverse]), null, {}, yy.locInfo(@$));
program.inverse = inverse;
@@ -73,31 +73,31 @@ closeBlock
mustache
// Parsing out the '&' escape token at AST level saves ~500 bytes after min due to the removal of one parser node.
// This also allows for handler unification as all mustache node instances can utilize the same handler
: OPEN sexpr CLOSE -> new yy.MustacheNode($2, null, $1, yy.stripFlags($1, $3), @$)
| OPEN_UNESCAPED sexpr CLOSE_UNESCAPED -> new yy.MustacheNode($2, null, $1, yy.stripFlags($1, $3), @$)
: OPEN sexpr CLOSE -> new yy.MustacheNode($2, null, $1, yy.stripFlags($1, $3), yy.locInfo(@$))
| OPEN_UNESCAPED sexpr CLOSE_UNESCAPED -> new yy.MustacheNode($2, null, $1, yy.stripFlags($1, $3), yy.locInfo(@$))
;
partial
: OPEN_PARTIAL partialName param hash? CLOSE -> new yy.PartialNode($2, $3, $4, yy.stripFlags($1, $5), @$)
| OPEN_PARTIAL partialName hash? CLOSE -> new yy.PartialNode($2, undefined, $3, yy.stripFlags($1, $4), @$)
: OPEN_PARTIAL partialName param hash? CLOSE -> new yy.PartialNode($2, $3, $4, yy.stripFlags($1, $5), yy.locInfo(@$))
| OPEN_PARTIAL partialName hash? CLOSE -> new yy.PartialNode($2, undefined, $3, yy.stripFlags($1, $4), yy.locInfo(@$))
;
sexpr
: path param* hash? -> new yy.SexprNode([$1].concat($2), $3, @$)
| dataName -> new yy.SexprNode([$1], null, @$)
: path param* hash? -> new yy.SexprNode([$1].concat($2), $3, yy.locInfo(@$))
| dataName -> new yy.SexprNode([$1], null, yy.locInfo(@$))
;
param
: path -> $1
| STRING -> new yy.StringNode($1, @$)
| NUMBER -> new yy.NumberNode($1, @$)
| BOOLEAN -> new yy.BooleanNode($1, @$)
| STRING -> new yy.StringNode($1, yy.locInfo(@$))
| NUMBER -> new yy.NumberNode($1, yy.locInfo(@$))
| BOOLEAN -> new yy.BooleanNode($1, yy.locInfo(@$))
| dataName -> $1
| OPEN_SEXPR sexpr CLOSE_SEXPR {$2.isHelper = true; $$ = $2;}
;
hash
: hashSegment+ -> new yy.HashNode($1, @$)
: hashSegment+ -> new yy.HashNode($1, yy.locInfo(@$))
;
hashSegment
@@ -109,17 +109,17 @@ blockParams
;
partialName
: path -> new yy.PartialNameNode($1, @$)
| STRING -> new yy.PartialNameNode(new yy.StringNode($1, @$), @$)
| NUMBER -> new yy.PartialNameNode(new yy.NumberNode($1, @$))
: path -> new yy.PartialNameNode($1, yy.locInfo(@$))
| STRING -> new yy.PartialNameNode(new yy.StringNode($1, yy.locInfo(@$)), yy.locInfo(@$))
| NUMBER -> new yy.PartialNameNode(new yy.NumberNode($1, yy.locInfo(@$)))
;
dataName
: DATA path -> new yy.DataNode($2, @$)
: DATA path -> new yy.DataNode($2, yy.locInfo(@$))
;
path
: pathSegments -> new yy.IdNode($1, @$)
: pathSegments -> new yy.IdNode($1, yy.locInfo(@$))
;
pathSegments