@ -5,7 +5,6 @@ module.exports = function(ast, options) {
options = utils . clone ( options ) ;
utils . defaults ( options , {
cache : false ,
trackLineAndColumn : false ,
allowedStartRules : [ ast . startRule ]
} ) ;
@ -333,10 +332,10 @@ module.exports = function(ast, options) {
' startRule = #{string(options.allowedStartRules[0])};' ,
' }' ,
' ' ,
' #{posInit("pos")} ;',
' #{posInit("reportedPos")} ;',
' var pos = 0 ;',
' var reportedPos = 0 ;',
' var reportFailures = 0;' , // 0 = report, anything > 0 = do not report
' #{posInit("rightmostFailuresPos")} ;',
' var rightmostFailuresPos = 0 ;',
' var rightmostFailuresExpected = [];' ,
' #if options.cache' ,
' var cache = {};' ,
@ -372,56 +371,24 @@ module.exports = function(ast, options) {
' }' ,
' ' ,
' function offset() {' ,
' return #{posOffset("reportedPos")};' ,
' return reportedPos;' ,
' }' ,
' ' ,
' function line() {' ,
' return computePosDetails(reportedPos).line;' ,
' }' ,
' ' ,
' function column() {' ,
' return computePosDetails(reportedPos).column;' ,
' }' ,
' ' ,
' #if options.trackLineAndColumn' ,
' function line() {' ,
' return reportedPos.line;' ,
' }' ,
' ' ,
' function column() {' ,
' return reportedPos.column;' ,
' }' ,
' ' ,
' function clone(object) {' ,
' var result = {};' ,
' for (var key in object) {' ,
' result[key] = object[key];' ,
' }' ,
' return result;' ,
' }' ,
' ' ,
' function advance(pos, n) {' ,
' var endOffset = pos.offset + n;' ,
' ' ,
' for (var offset = pos.offset; offset < endOffset; offset++) {' ,
' var ch = input.charAt(offset);' ,
' if (ch === "\\n") {' ,
' if (!pos.seenCR) { pos.line++; }' ,
' pos.column = 1;' ,
' pos.seenCR = false;' ,
' } else if (ch === "\\r" || ch === "\\u2028" || ch === "\\u2029") {' ,
' pos.line++;' ,
' pos.column = 1;' ,
' pos.seenCR = true;' ,
' } else {' ,
' pos.column++;' ,
' pos.seenCR = false;' ,
' }' ,
' }' ,
' ' ,
' pos.offset += n;' ,
' }' ,
' ' ,
' #end' ,
' function matchFailed(failure) {' ,
' if ( #{ posOffset("pos")} < #{posOffset(" rightmostFailuresPos")} ) {',
' if (pos < rightmostFailuresPos) {' ,
' return;' ,
' }' ,
' ' ,
' if ( #{ posOffset("pos")} > #{posOffset(" rightmostFailuresPos")} ) {',
' rightmostFailuresPos = #{ posClone("pos")} ;',
' if (pos > rightmostFailuresPos) {' ,
' rightmostFailuresPos = pos;' ,
' rightmostFailuresExpected = [];' ,
' }' ,
' ' ,
@ -447,38 +414,36 @@ module.exports = function(ast, options) {
' return cleanExpected;' ,
' }' ,
' ' ,
' #if !options.trackLineAndColumn' ,
' function computeErrorPosition() {' ,
' /*' ,
' * The first idea was to use |String.split| to break the input up to the' ,
' * error position along newlines and derive the line and column from' ,
' * there. However IE\'s |split| implementation is so broken that it was' ,
' * enough to prevent it.' ,
' */' ,
' ' ,
' var line = 1;' ,
' var column = 1;' ,
' var seenCR = false;' ,
' ' ,
' for (var i = 0; i < Math.max(pos, rightmostFailuresPos); i++) {' ,
' var ch = input.charAt(i);' ,
' if (ch === "\\n") {' ,
' if (!seenCR) { line++; }' ,
' column = 1;' ,
' seenCR = false;' ,
' } else if (ch === "\\r" || ch === "\\u2028" || ch === "\\u2029") {' ,
' line++;' ,
' column = 1;' ,
' seenCR = true;' ,
' } else {' ,
' column++;' ,
' seenCR = false;' ,
' }' ,
' function computePosDetails(pos) {' ,
' /*' ,
' * The first idea was to use |String.split| to break the input up to the' ,
' * error position along newlines and derive the line and column from' ,
' * there. However IE\'s |split| implementation is so broken that it was' ,
' * enough to prevent it.' ,
' */' ,
' ' ,
' var line = 1;' ,
' var column = 1;' ,
' var seenCR = false;' ,
' ' ,
' for (var i = 0; i < pos; i++) {' ,
' var ch = input.charAt(i);' ,
' if (ch === "\\n") {' ,
' if (!seenCR) { line++; }' ,
' column = 1;' ,
' seenCR = false;' ,
' } else if (ch === "\\r" || ch === "\\u2028" || ch === "\\u2029") {' ,
' line++;' ,
' column = 1;' ,
' seenCR = true;' ,
' } else {' ,
' column++;' ,
' seenCR = false;' ,
' }' ,
' ' ,
' return { line: line, column: column };' ,
' }' ,
' #end' ,
' ' ,
' return { line: line, column: column };' ,
' }' ,
' ' ,
' #if node.initializer' ,
' #block emit(node.initializer)' ,
@ -492,32 +457,28 @@ module.exports = function(ast, options) {
' * 1. The parser successfully parsed the whole input.' ,
' *' ,
' * - |result !== null|' ,
' * - | #{ posOffset("pos")} === input.length|',
' * - | pos === input.length|',
' * - |rightmostFailuresExpected| may or may not contain something' ,
' *' ,
' * 2. The parser successfully parsed only a part of the input.' ,
' *' ,
' * - |result !== null|' ,
' * - | #{ posOffset("pos")} < input.length|',
' * - | pos < input.length|',
' * - |rightmostFailuresExpected| may or may not contain something' ,
' *' ,
' * 3. The parser did not successfully parse any part of the input.' ,
' *' ,
' * - |result === null|' ,
' * - | #{ posOffset("pos")} === 0|',
' * - | pos === 0|',
' * - |rightmostFailuresExpected| contains at least one failure' ,
' *' ,
' * All code following this comment (including called functions) must' ,
' * handle these states.' ,
' */' ,
' if (result === null || #{ posOffset("pos")} !== input.length) {',
' var offset = Math.max( #{ posOffset("pos")} , #{posOffset(" rightmostFailuresPos")} );',
' if (result === null || pos !== input.length) {',
' var offset = Math.max( pos, rightmostFailuresPos);',
' var found = offset < input.length ? input.charAt(offset) : null;' ,
' #if options.trackLineAndColumn' ,
' var errorPosition = #{posOffset("pos")} > #{posOffset("rightmostFailuresPos")} ? pos : rightmostFailuresPos;' ,
' #else' ,
' var errorPosition = computeErrorPosition();' ,
' #end' ,
' var errorPosition = computePosDetails(Math.max(pos, rightmostFailuresPos));' ,
' ' ,
' throw new this.SyntaxError(' ,
' cleanupExpected(rightmostFailuresExpected),' ,
@ -573,10 +534,10 @@ module.exports = function(ast, options) {
rule : [
'function parse_#{node.name}() {' ,
' #if options.cache' ,
' var cacheKey = "#{node.name}@" + #{ posOffset("pos")} ;',
' var cacheKey = "#{node.name}@" + pos;',
' var cachedResult = cache[cacheKey];' ,
' if (cachedResult) {' ,
' pos = #{posClone(" cachedResult.nextPos")} ;',
' pos = cachedResult.nextPos;',
' return cachedResult.result;' ,
' }' ,
' ' ,
@ -589,7 +550,7 @@ module.exports = function(ast, options) {
' #if options.cache' ,
' ' ,
' cache[cacheKey] = {' ,
' nextPos: #{ posClone("pos")} ,',
' nextPos: pos,',
' result: #{r(node.expression.resultIndex)}' ,
' };' ,
' #end' ,
@ -614,18 +575,18 @@ module.exports = function(ast, options) {
'}'
] ,
action : [
'#{ posSave(node)} ;',
'#{ r(node.posIndex)} = pos ;',
'#block emit(node.expression)' ,
'if (#{r(node.resultIndex)} !== null) {' ,
' reportedPos = #{ posClone( r(node.posIndex) )};',
' reportedPos = #{ r(node.posIndex)};',
' #{r(node.resultIndex)} = (function(#{keys(node.params).join(", ")}) {#{node.code}})(#{map(values(node.params), r).join(", ")});' ,
'}' ,
'if (#{r(node.resultIndex)} === null) {' ,
' #{posRestore(node )};',
' pos = #{r(node.posIndex )};',
'}'
] ,
sequence : [
'#{ posSave(node)} ;',
'#{ r(node.posIndex)} = pos ;',
'#block code'
] ,
"sequence.iteration" : [
@ -634,26 +595,26 @@ module.exports = function(ast, options) {
' #block code' ,
'} else {' ,
' #{r(node.resultIndex)} = null;' ,
' #{posRestore(node )};',
' pos = #{r(node.posIndex )};',
'}'
] ,
"sequence.inner" : [
'#{r(node.resultIndex)} = [#{map(pluck(node.elements, "resultIndex"), r).join(", ")}];'
] ,
simple _and : [
'#{ posSave(node)} ;',
'#{ r(node.posIndex)} = pos ;',
'reportFailures++;' ,
'#block emit(node.expression)' ,
'reportFailures--;' ,
'if (#{r(node.resultIndex)} !== null) {' ,
' #{r(node.resultIndex)} = "";' ,
' #{posRestore(node )};',
' pos = #{r(node.posIndex )};',
'} else {' ,
' #{r(node.resultIndex)} = null;' ,
'}'
] ,
simple _not : [
'#{ posSave(node)} ;',
'#{ r(node.posIndex)} = pos ;',
'reportFailures++;' ,
'#block emit(node.expression)' ,
'reportFailures--;' ,
@ -661,15 +622,15 @@ module.exports = function(ast, options) {
' #{r(node.resultIndex)} = "";' ,
'} else {' ,
' #{r(node.resultIndex)} = null;' ,
' #{posRestore(node )};',
' pos = #{r(node.posIndex )};',
'}'
] ,
semantic _and : [
'reportedPos = #{ posClone("pos")} ;',
'reportedPos = pos;',
'#{r(node.resultIndex)} = (function(#{keys(node.params).join(", ")}) {#{node.code}})(#{map(values(node.params), r).join(", ")}) ? "" : null;'
] ,
semantic _not : [
'reportedPos = #{ posClone("pos")} ;',
'reportedPos = pos;',
'#{r(node.resultIndex)} = (function(#{keys(node.params).join(", ")}) {#{node.code}})(#{map(values(node.params), r).join(", ")}) ? null : "";'
] ,
optional : [
@ -705,9 +666,9 @@ module.exports = function(ast, options) {
'#else' ,
' #if !node.ignoreCase' ,
' #if node.value.length === 1' ,
' if (input.charCodeAt( #{ posOffset("pos")} ) === #{node.value.charCodeAt(0)}) {',
' if (input.charCodeAt( pos) === #{node.value.charCodeAt(0)}) {',
' #else' ,
' if (input.substr( #{ posOffset("pos")} , #{node.value.length}) === #{string(node.value)}) {',
' if (input.substr( pos, #{node.value.length}) === #{string(node.value)}) {',
' #end' ,
' #else' ,
/ *
@ -718,14 +679,14 @@ module.exports = function(ast, options) {
* meaning the result of lowercasing a character can be more
* characters .
* /
' if (input.substr( #{ posOffset("pos")} , #{node.value.length}).toLowerCase() === #{string(node.value.toLowerCase())}) {',
' if (input.substr( pos, #{node.value.length}).toLowerCase() === #{string(node.value.toLowerCase())}) {',
' #end' ,
' #if !node.ignoreCase' ,
' #{r(node.resultIndex)} = #{string(node.value)};' ,
' #else' ,
' #{r(node.resultIndex)} = input.substr( #{ posOffset("pos")} , #{node.value.length});',
' #{r(node.resultIndex)} = input.substr( pos, #{node.value.length});',
' #end' ,
' #{ posAdvance(node.value.length) };',
' #{ node.value.length > 1 ? "pos += " + node.value.length : "pos++" };',
' } else {' ,
' #{r(node.resultIndex)} = null;' ,
' if (reportFailures === 0) {' ,
@ -735,9 +696,9 @@ module.exports = function(ast, options) {
'#end'
] ,
"class" : [
'if (#{regexp}.test(input.charAt( #{ posOffset("pos")} ))) {',
' #{r(node.resultIndex)} = input.charAt( #{ posOffset("pos")} );',
' #{posAdvance(1)} ;',
'if (#{regexp}.test(input.charAt( pos))) {',
' #{r(node.resultIndex)} = input.charAt( pos);',
' pos++ ;',
'} else {' ,
' #{r(node.resultIndex)} = null;' ,
' if (reportFailures === 0) {' ,
@ -746,9 +707,9 @@ module.exports = function(ast, options) {
'}'
] ,
any : [
'if (input.length > #{ posOffset("pos")} ) {',
' #{r(node.resultIndex)} = input.charAt( #{ posOffset("pos")} );',
' #{posAdvance(1)} ;',
'if (input.length > pos) {',
' #{r(node.resultIndex)} = input.charAt( pos);',
' pos++ ;',
'} else {' ,
' #{r(node.resultIndex)} = null;' ,
' if (reportFailures === 0) {' ,
@ -777,34 +738,6 @@ module.exports = function(ast, options) {
vars . r = function ( index ) { return "r" + index ; } ;
/* Position-handling macros */
if ( options . trackLineAndColumn ) {
vars . posInit = function ( name ) {
return "var "
+ name
+ " = "
+ "{ offset: 0, line: 1, column: 1, seenCR: false }" ;
} ;
vars . posClone = function ( name ) { return "clone(" + name + ")" ; } ;
vars . posOffset = function ( name ) { return name + ".offset" ; } ;
vars . posAdvance = function ( n ) { return "advance(pos, " + n + ")" ; } ;
} else {
vars . posInit = function ( name ) { return "var " + name + " = 0" ; } ;
vars . posClone = function ( name ) { return name ; } ;
vars . posOffset = function ( name ) { return name ; } ;
vars . posAdvance = function ( n ) {
return n === 1 ? "pos++" : "pos += " + n ;
} ;
}
vars . posSave = function ( node ) {
return vars . r ( node . posIndex ) + " = " + vars . posClone ( "pos" ) ;
} ;
vars . posRestore = function ( node ) {
return "pos" + " = " + vars . posClone ( vars . r ( node . posIndex ) ) ;
} ;
return templates [ name ] ( vars ) ;
}