@ -76,19 +76,8 @@ PEG.compiler.emitter = function(ast) {
return indentMultilineParts ( interpolateVariablesInParts ( args ) ) . join ( "\n" ) ;
} ;
/* Unique ID generator. */
var UID = {
_counters : { } ,
next : function ( prefix ) {
this . _counters [ prefix ] = this . _counters [ prefix ] || 0 ;
return prefix + this . _counters [ prefix ] ++ ;
} ,
reset : function ( ) {
this . _counters = { } ;
}
} ;
function resultVar ( index ) { return "result" + index ; }
function posVar ( index ) { return "pos" + index ; }
var emit = buildNodeVisitor ( {
grammar : function ( node ) {
@ -332,16 +321,16 @@ PEG.compiler.emitter = function(ast) {
} ,
rule : function ( node ) {
/ *
* We want to reset variable names at the beginning of every function so
* that a little change in the source grammar does not change variables in
* all the generated code . This is desired especially when one has the
* generated grammar stored in a VCS ( this is true e . g . for our
* metagrammar ) .
* /
UID . reset ( ) ;
var context = {
resultIndex : 0 ,
posIndex : 0
} ;
var resultVars = map ( range ( node . resultStackDepth ) , resultVar ) ;
var posVars = map ( range ( node . posStackDepth ) , posVar ) ;
var resultVar = UID . next ( "result" ) ;
var resultVarsCode = resultVars . length > 0 ? "var " + resultVars . join ( ", " ) + ";" : "" ;
var posVarsCode = posVars . length > 0 ? "var " + posVars . join ( ", " ) + ";" : "" ;
if ( node . displayName !== null ) {
var setReportFailuresCode = formatCode (
@ -356,7 +345,7 @@ PEG.compiler.emitter = function(ast) {
"}" ,
{
displayName : node . displayName ,
resultVar : resultVar
resultVar : resultVar ( context . resultIndex )
}
) ;
} else {
@ -374,6 +363,9 @@ PEG.compiler.emitter = function(ast) {
" return cachedResult.result;" ,
" }" ,
" " ,
" ${resultVarsCode}" ,
" ${posVarsCode}" ,
" " ,
" ${setReportFailuresCode}" ,
" ${code}" ,
" ${restoreReportFailuresCode}" ,
@ -387,51 +379,68 @@ PEG.compiler.emitter = function(ast) {
"}" ,
{
name : node . name ,
resultVarsCode : resultVarsCode ,
posVarsCode : posVarsCode ,
setReportFailuresCode : setReportFailuresCode ,
restoreReportFailuresCode : restoreReportFailuresCode ,
reportFailureCode : reportFailureCode ,
code : emit ( node . expression , resultVar ) ,
resultVar : resultVar
code : emit ( node . expression , context ) ,
resultVar : resultVar ( context . resultIndex )
}
) ;
} ,
/ *
* The contract for all code fragments generated by the following functions
* is as follows :
* is as follows .
*
* The code fragment tries to match a part of the input starting with the
* position indicated in | pos | . That position may point past the end of the
* input .
*
* * If the code fragment matches the input , it advances | pos | to point to
* the first chracter following the matched part of the input and sets
* variable with a name computed by calling
* | resultVar ( context . resultIndex ) | to an appropriate value . This value is
* always non - | null | .
*
* * If the code fragment does not match the input , it returns with | pos |
* set to the original value and it sets a variable with a name computed
* by calling | resultVar ( context . resultIndex ) | to | null | .
*
* * The code fragment should try to match a part of the input starting with
* the position indicated in | pos | . That position may point past the end of
* the input .
* The code can use variables with names computed by calling
*
* * If the code fragment matches the input , it advances | pos | after the
* matched part of the input and sets variable with a name stored in
* | resultVar | to appropriate value , which is always non - null .
* | resultVar ( context . resultIndex + i ) |
*
* * If the code fragment does not match the input , it does not change | pos |
* and it sets a variable with a name stored in | resultVar | to | null | .
* and
*
* | posVar ( context . posIndex + i ) |
*
* where | i | >= 1 to store necessary data ( return values and positions ) . It
* won ' t use any other variables .
* /
choice : function ( node , resultVar ) {
var code = formatCode (
"var ${resultVar} = null;" ,
{ resultVar : resultVar }
) ;
choice : function ( node , context ) {
var code , nextAlternativesCode ;
for ( var i = node . alternatives . length - 1 ; i >= 0 ; i -- ) {
var alternativeResultVar = UID . next ( "result" ) ;
nextAlternativesCode = i !== node . alternatives . length - 1
? formatCode (
"if (${resultVar} === null) {" ,
" ${code}" ,
"}" ,
{
code : code ,
resultVar : resultVar ( context . resultIndex )
}
)
: "" ;
code = formatCode (
"${alternativeCode}" ,
"if (${alternativeResultVar} !== null) {" ,
" var ${resultVar} = ${alternativeResultVar};" ,
"} else {" ,
" ${code};" ,
"}" ,
"${currentAlternativeCode}" ,
"${nextAlternativesCode}" ,
{
alternativeCode : emit ( node . alternatives [ i ] , alternativeResultVar ) ,
alternativeResultVar : alternativeResultVar ,
code : code ,
resultVar : resultVar
currentAlternativeCode : emit ( node . alternatives [ i ] , context ) ,
nextAlternativesCode : nextAlternativesCode
}
) ;
}
@ -439,177 +448,185 @@ PEG.compiler.emitter = function(ast) {
return code ;
} ,
sequence : function ( node , resultVar ) {
var savedPosVar = UID . next ( "savedPos" ) ;
var elementResultVars = map ( node . elements , function ( ) {
return UID . next ( "result" )
sequence : function ( node , context ) {
var elementResultVars = map ( node . elements , function ( element , i ) {
return resultVar ( context . resultIndex + i ) ;
} ) ;
var code = formatCode (
" var ${resultVar} = ${elementResultVarArray};",
" ${resultVar} = ${elementResultVarArray};",
{
resultVar : resultVar ,
resultVar : resultVar (context . resultIndex ) ,
elementResultVarArray : "[" + elementResultVars . join ( ", " ) + "]"
}
) ;
var elementContext ;
for ( var i = node . elements . length - 1 ; i >= 0 ; i -- ) {
elementContext = {
resultIndex : context . resultIndex + i ,
posIndex : context . posIndex + 1
} ;
code = formatCode (
"${elementCode}" ,
"if (${elementResultVar} !== null) {" ,
" ${code}" ,
"} else {" ,
" var ${resultVar} = null;",
" pos = ${ savedP osVar};",
" ${resultVar} = null;",
" pos = ${ p osVar};",
"}" ,
{
elementCode : emit ( node . elements [ i ] , element ResultVars[ i ] ) ,
elementCode : emit ( node . elements [ i ] , element Context ) ,
elementResultVar : elementResultVars [ i ] ,
code : code ,
savedPosVar: savedPosVar ,
resultVar : resultVar
posVar: posVar ( context . posIndex ) ,
resultVar : resultVar ( context . resultIndex )
}
) ;
}
return formatCode (
" var ${savedP osVar} = pos;",
" ${p osVar} = pos;",
"${code}" ,
{
code : code ,
savedPosVar: savedPosVar
code : code ,
posVar: posVar ( context . posIndex )
}
) ;
} ,
labeled : function ( node , resultVar ) {
return emit ( node . expression , resultVar ) ;
labeled : function ( node , context ) {
return emit ( node . expression , context ) ;
} ,
simple _and : function ( node , resultVar ) {
var savedPosVar = UID . next ( "savedPos" ) ;
var expressionResultVar = UID . next ( "result" ) ;
simple _and : function ( node , context ) {
var expressionContext = {
resultIndex : context . resultIndex ,
posIndex : context . posIndex + 1
} ;
return formatCode (
" var ${savedP osVar} = pos;",
" ${p osVar} = pos;",
"reportFailures++;" ,
"${expressionCode}" ,
"reportFailures--;" ,
"if (${ exp ressionR esultVar} !== null) {",
" var ${resultVar} = '';",
" pos = ${ savedP osVar};",
"if (${ resultVar} !== null) {",
" ${resultVar} = '';",
" pos = ${ p osVar};",
"} else {" ,
" var ${resultVar} = null;",
" ${resultVar} = null;",
"}" ,
{
expressionCode : emit ( node . expression , expressionResultVar ) ,
expressionResultVar : expressionResultVar ,
savedPosVar : savedPosVar ,
resultVar : resultVar
expressionCode : emit ( node . expression , expressionContext ) ,
posVar : posVar ( context . posIndex ) ,
resultVar : resultVar ( context . resultIndex )
}
) ;
} ,
simple _not : function ( node , resultVar ) {
var savedPosVar = UID . next ( "savedPos" ) ;
var expressionResultVar = UID . next ( "result" ) ;
simple _not : function ( node , context ) {
var expressionContext = {
resultIndex : context . resultIndex ,
posIndex : context . posIndex + 1
} ;
return formatCode (
" var ${savedP osVar} = pos;",
" ${p osVar} = pos;",
"reportFailures++;" ,
"${expressionCode}" ,
"reportFailures--;" ,
"if (${ exp ressionR esultVar} === null) {",
" var ${resultVar} = '';",
"if (${ resultVar} === null) {",
" ${resultVar} = '';",
"} else {" ,
" var ${resultVar} = null;",
" pos = ${ savedP osVar};",
" ${resultVar} = null;",
" pos = ${ p osVar};",
"}" ,
{
expressionCode : emit ( node . expression , expressionResultVar ) ,
expressionResultVar : expressionResultVar ,
savedPosVar : savedPosVar ,
resultVar : resultVar
expressionCode : emit ( node . expression , expressionContext ) ,
posVar : posVar ( context . posIndex ) ,
resultVar : resultVar ( context . resultIndex )
}
) ;
} ,
semantic _and : function ( node , resultVar ) {
semantic _and : function ( node , context ) {
return formatCode (
" var ${resultVar} = (function() {${actionCode}})() ? '' : null;",
" ${resultVar} = (function() {${actionCode}})() ? '' : null;",
{
actionCode : node . code ,
resultVar : resultVar
actionCode : node . code ,
resultVar : resultVar ( context . resultIndex )
}
) ;
} ,
semantic _not : function ( node , resultVar ) {
semantic _not : function ( node , context ) {
return formatCode (
" var ${resultVar} = (function() {${actionCode}})() ? null : '';",
" ${resultVar} = (function() {${actionCode}})() ? null : '';",
{
actionCode : node . code ,
resultVar : resultVar
actionCode : node . code ,
resultVar : resultVar ( context . resultIndex )
}
) ;
} ,
optional : function ( node , resultVar ) {
var expressionResultVar = UID . next ( "result" ) ;
optional : function ( node , context ) {
return formatCode (
"${expressionCode}" ,
" var ${resultVar} = ${exp ressionRes ultVar} !== null ? ${exp ressionR esultVar} : '';",
" ${resultVar} = ${resultVar} !== null ? ${resultVar} : '';",
{
expressionCode : emit ( node . expression , expressionResultVar ) ,
expressionResultVar : expressionResultVar ,
resultVar : resultVar
expressionCode : emit ( node . expression , context ) ,
resultVar : resultVar ( context . resultIndex )
}
) ;
} ,
zero _or _more : function ( node , resultVar ) {
var expressionResultVar = UID . next ( "result" ) ;
zero _or _more : function ( node , context ) {
var expressionContext = {
resultIndex : context . resultIndex + 1 ,
posIndex : context . posIndex
} ;
return formatCode (
"var ${resultVar} = [];" ,
" ${resultVar} = [];",
"${expressionCode}" ,
"while (${expressionResultVar} !== null) {" ,
" ${resultVar}.push(${expressionResultVar});" ,
" ${expressionCode}" ,
"}" ,
{
expressionCode : emit ( node . expression , expression ResultVar ) ,
expressionResultVar : expressionResultVar ,
resultVar : resultVar
expressionCode : emit ( node . expression , expression Context ) ,
expressionResultVar : resultVar( context . resultIndex + 1 ) ,
resultVar : resultVar ( context . resultIndex )
}
) ;
} ,
one _or _more : function ( node , resultVar ) {
var expressionResultVar = UID . next ( "result" ) ;
one _or _more : function ( node , context ) {
var expressionContext = {
resultIndex : context . resultIndex + 1 ,
posIndex : context . posIndex
} ;
return formatCode (
"${expressionCode}" ,
"if (${expressionResultVar} !== null) {" ,
" var ${resultVar} = [];",
" ${resultVar} = [];",
" while (${expressionResultVar} !== null) {" ,
" ${resultVar}.push(${expressionResultVar});" ,
" ${expressionCode}" ,
" }" ,
"} else {" ,
" var ${resultVar} = null;",
" ${resultVar} = null;",
"}" ,
{
expressionCode : emit ( node . expression , expression ResultVar ) ,
expressionResultVar : expressionResultVar ,
resultVar : resultVar
expressionCode : emit ( node . expression , expression Context ) ,
expressionResultVar : resultVar( context . resultIndex + 1 ) ,
resultVar : resultVar ( context . resultIndex )
}
) ;
} ,
action : function ( node , resultVar ) {
action : function ( node , context ) {
/ *
* In case of sequences , we splat their elements into function arguments
* one by one . Example :
@ -619,9 +636,10 @@ PEG.compiler.emitter = function(ast) {
* This behavior is reflected in this function .
* /
var expressionResultVar = UID . next ( "result" ) ;
var actionResultVar = UID . next ( "result" ) ;
var savedPosVar = UID . next ( "savedPos" ) ;
var expressionContext = {
resultIndex : context . resultIndex ,
posIndex : context . posIndex + 1
} ;
if ( node . expression . type === "sequence" ) {
var formalParams = [ ] ;
@ -632,59 +650,54 @@ PEG.compiler.emitter = function(ast) {
for ( var i = 0 ; i < elementsLength ; i ++ ) {
if ( elements [ i ] . type === "labeled" ) {
formalParams . push ( elements [ i ] . label ) ;
actualParams . push ( expressionResultVar + "[" + i + "]" ) ;
actualParams . push ( resultVar( context . resultIndex ) + "[" + i + "]" ) ;
}
}
} else if ( node . expression . type === "labeled" ) {
var formalParams = [ node . expression . label ] ;
var actualParams = [ expressionResultVar ] ;
var actualParams = [ resultVar( context . resultIndex ) ] ;
} else {
var formalParams = [ ] ;
var actualParams = [ ] ;
}
return formatCode (
" var ${savedP osVar} = pos;",
" ${p osVar} = pos;",
"${expressionCode}" ,
"var ${actionResultVar} = ${expressionResultVar} !== null" ,
" ? (function(${formalParams}) {${actionCode}})(${actualParams})" ,
" : null;" ,
"if (${actionResultVar} !== null) {" ,
" var ${resultVar} = ${actionResultVar};" ,
"} else {" ,
" var ${resultVar} = null;" ,
" pos = ${savedPosVar};" ,
"if (${resultVar} !== null) {" ,
" ${resultVar} = (function(${formalParams}) {${actionCode}})(${actualParams});" ,
"}" ,
"if (${resultVar} === null) {" ,
" pos = ${posVar};" ,
"}" ,
{
expressionCode : emit ( node . expression , expressionResultVar ) ,
expressionResultVar : expressionResultVar ,
actionCode : node . code ,
actionResultVar : actionResultVar ,
formalParams : formalParams . join ( ", " ) ,
actualParams : actualParams . join ( ", " ) ,
savedPosVar : savedPosVar ,
resultVar : resultVar
expressionCode : emit ( node . expression , expressionContext ) ,
actionCode : node . code ,
formalParams : formalParams . join ( ", " ) ,
actualParams : actualParams . join ( ", " ) ,
posVar : posVar ( context . posIndex ) ,
resultVar : resultVar ( context . resultIndex )
}
) ;
} ,
rule _ref : function ( node , resultVar ) {
rule _ref : function ( node , context ) {
return formatCode (
" var ${resultVar} = ${ruleMethod}();",
" ${resultVar} = ${ruleMethod}();",
{
ruleMethod : "parse_" + node . name ,
resultVar : resultVar
resultVar : resultVar ( context . resultIndex )
}
) ;
} ,
literal : function ( node , resultVar ) {
literal : function ( node , context ) {
return formatCode (
"if (input.substr(pos, ${length}) === ${value|string}) {" ,
" var ${resultVar} = ${value|string};",
" ${resultVar} = ${value|string};",
" pos += ${length};" ,
"} else {" ,
" var ${resultVar} = null;",
" ${resultVar} = null;",
" if (reportFailures === 0) {" ,
" matchFailed(${valueQuoted|string});" ,
" }" ,
@ -693,27 +706,27 @@ PEG.compiler.emitter = function(ast) {
value : node . value ,
valueQuoted : quote ( node . value ) ,
length : node . value . length ,
resultVar : resultVar
resultVar : resultVar ( context . resultIndex )
}
) ;
} ,
any : function ( node , resultVar ) {
any : function ( node , context ) {
return formatCode (
"if (input.length > pos) {" ,
" var ${resultVar} = input.charAt(pos);",
" ${resultVar} = input.charAt(pos);",
" pos++;" ,
"} else {" ,
" var ${resultVar} = null;",
" ${resultVar} = null;",
" if (reportFailures === 0) {" ,
" matchFailed('any character');" ,
" }" ,
"}" ,
{ resultVar : resultVar }
{ resultVar : resultVar ( context . resultIndex ) }
) ;
} ,
"class" : function ( node , resultVar ) {
"class" : function ( node , context ) {
if ( node . parts . length > 0 ) {
var regexp = "/^["
+ ( node . inverted ? "^" : "" )
@ -735,10 +748,10 @@ PEG.compiler.emitter = function(ast) {
return formatCode (
"if (input.substr(pos).match(${regexp}) !== null) {" ,
" var ${resultVar} = input.charAt(pos);",
" ${resultVar} = input.charAt(pos);",
" pos++;" ,
"} else {" ,
" var ${resultVar} = null;",
" ${resultVar} = null;",
" if (reportFailures === 0) {" ,
" matchFailed(${rawText|string});" ,
" }" ,
@ -746,7 +759,7 @@ PEG.compiler.emitter = function(ast) {
{
regexp : regexp ,
rawText : node . rawText ,
resultVar : resultVar
resultVar : resultVar ( context . resultIndex )
}
) ;
}