Compile Codie templates only once

redux
David Majda 13 years ago
parent a5af9611a2
commit da12c2f5d4

@ -226,37 +226,11 @@ PEG.compiler.emitter = function(ast) {
return Codie;
})();
function formatCode() {
var args = Array.prototype.slice.call(arguments);
var vars = args[args.length - 1] instanceof Object ? args.pop() : {};
vars.string = quote;
return Codie.template(args.join('\n'))(vars);
}
function resultVar(index) { return "result" + index; }
function posVar(index) { return "pos" + index; }
var emit = buildNodeVisitor({
grammar: function(node) {
var initializerCode = node.initializer !== null
? emit(node.initializer)
: "";
var name;
var parseFunctionTableItems = [];
for (name in node.rules) {
parseFunctionTableItems.push(quote(name) + ": parse_" + name);
}
parseFunctionTableItems.sort();
var parseFunctionDefinitions = [];
for (name in node.rules) {
parseFunctionDefinitions.push(emit(node.rules[name]));
}
return formatCode(
var templates = (function() {
var name,
templates = {},
sources = {
grammar: [
'(function(){',
' /* Generated by PEG.js @VERSION (http://pegjs.majda.cz/). */',
' ',
@ -479,34 +453,9 @@ PEG.compiler.emitter = function(ast) {
' result.SyntaxError.prototype = Error.prototype;',
' ',
' return result;',
'})()',
{
initializerCode: initializerCode,
parseFunctionTableItems: parseFunctionTableItems,
parseFunctionDefinitions: parseFunctionDefinitions,
startRule: node.startRule
}
);
},
initializer: function(node) {
return node.code;
},
rule: function(node) {
var context = {
resultIndex: 0,
posIndex: 0,
delta: function(resultIndexDelta, posIndexDelta) {
return {
resultIndex: this.resultIndex + resultIndexDelta,
posIndex: this.posIndex + posIndexDelta,
delta: this.delta
};
}
};
return formatCode(
'})()'
],
rule: [
'function parse_#{node.name}() {',
' var cacheKey = "#{node.name}@" + pos;',
' var cachedResult = cache[cacheKey];',
@ -538,15 +487,209 @@ PEG.compiler.emitter = function(ast) {
' result: #{resultVar}',
' };',
' return #{resultVar};',
'}'
],
choice: [
'#block currentAlternativeCode',
'#block nextAlternativesCode'
],
"choice.next": [
'if (#{resultVar} === null) {',
' #block code',
'}'
],
sequence: [
'#{posVar} = pos;',
'#block code'
],
"sequence.iteration": [
'#block elementCode',
'if (#{elementResultVar} !== null) {',
' #block code',
'} else {',
' #{resultVar} = null;',
' pos = #{posVar};',
'}'
],
"sequence.inner": [
'#{resultVar} = [#{elementResultVars.join(", ")}];'
],
simple_and: [
'#{posVar} = pos;',
'reportFailures++;',
'#block expressionCode',
'reportFailures--;',
'if (#{resultVar} !== null) {',
' #{resultVar} = "";',
' pos = #{posVar};',
'} else {',
' #{resultVar} = null;',
'}'
],
simple_not: [
'#{posVar} = pos;',
'reportFailures++;',
'#block expressionCode',
'reportFailures--;',
'if (#{resultVar} === null) {',
' #{resultVar} = "";',
'} else {',
' #{resultVar} = null;',
' pos = #{posVar};',
'}'
],
semantic_and: [
'#{resultVar} = (function() {#{node.code}})() ? "" : null;'
],
semantic_not: [
'#{resultVar} = (function() {#{node.code}})() ? null : "";'
],
optional: [
'#block expressionCode',
'#{resultVar} = #{resultVar} !== null ? #{resultVar} : "";'
],
zero_or_more: [
'#{resultVar} = [];',
'#block expressionCode',
'while (#{expressionResultVar} !== null) {',
' #{resultVar}.push(#{expressionResultVar});',
' #block expressionCode',
'}'
],
one_or_more: [
'#block expressionCode',
'if (#{expressionResultVar} !== null) {',
' #{resultVar} = [];',
' while (#{expressionResultVar} !== null) {',
' #{resultVar}.push(#{expressionResultVar});',
' #block expressionCode',
' }',
'} else {',
' #{resultVar} = null;',
'}'
],
action: [
'#{posVar} = pos;',
'#block expressionCode',
'if (#{resultVar} !== null) {',
' #{resultVar} = (function(#{formalParams.join(", ")}) {#{node.code}})(#{actualParams.join(", ")});',
'}',
'if (#{resultVar} === null) {',
' pos = #{posVar};',
'}'
],
rule_ref: [
'#{resultVar} = parse_#{node.name}();'
],
literal: [
'#if node.value.length === 0',
' #{resultVar} = "";',
'#else',
' #if node.value.length === 1',
' if (input.charCodeAt(pos) === #{node.value.charCodeAt(0)}) {',
' #else',
' if (input.substr(pos, #{node.value.length}) === #{string(node.value)}) {',
' #end',
' #{resultVar} = #{string(node.value)};',
' pos += #{node.value.length};',
' } else {',
' #{resultVar} = null;',
' if (reportFailures === 0) {',
' matchFailed(#{string(string(node.value))});',
' }',
' }',
'#end'
],
any: [
'if (input.length > pos) {',
' #{resultVar} = input.charAt(pos);',
' pos++;',
'} else {',
' #{resultVar} = null;',
' if (reportFailures === 0) {',
' matchFailed("any character");',
' }',
{
'}'
],
"class": [
'if (#{regexp}.test(input.charAt(pos))) {',
' #{resultVar} = input.charAt(pos);',
' pos++;',
'} else {',
' #{resultVar} = null;',
' if (reportFailures === 0) {',
' matchFailed(#{string(node.rawText)});',
' }',
'}'
]
};
for (name in sources) {
templates[name] = Codie.template(sources[name].join('\n'));
}
return templates;
})();
function fill(name, vars) {
vars.string = quote;
return templates[name](vars);
}
function resultVar(index) { return "result" + index; }
function posVar(index) { return "pos" + index; }
var emit = buildNodeVisitor({
grammar: function(node) {
var initializerCode = node.initializer !== null
? emit(node.initializer)
: "";
var name;
var parseFunctionTableItems = [];
for (name in node.rules) {
parseFunctionTableItems.push(quote(name) + ": parse_" + name);
}
parseFunctionTableItems.sort();
var parseFunctionDefinitions = [];
for (name in node.rules) {
parseFunctionDefinitions.push(emit(node.rules[name]));
}
return fill("grammar", {
initializerCode: initializerCode,
parseFunctionTableItems: parseFunctionTableItems,
parseFunctionDefinitions: parseFunctionDefinitions,
startRule: node.startRule
});
},
initializer: function(node) {
return node.code;
},
rule: function(node) {
var context = {
resultIndex: 0,
posIndex: 0,
delta: function(resultIndexDelta, posIndexDelta) {
return {
resultIndex: this.resultIndex + resultIndexDelta,
posIndex: this.posIndex + posIndexDelta,
delta: this.delta
};
}
};
return fill("rule", {
node: node,
resultVars: map(range(node.resultStackDepth), resultVar),
posVars: map(range(node.posStackDepth), posVar),
code: emit(node.expression, context),
resultVar: resultVar(context.resultIndex)
}
);
});
},
/*
@ -584,24 +727,15 @@ PEG.compiler.emitter = function(ast) {
for (var i = node.alternatives.length - 1; i >= 0; i--) {
nextAlternativesCode = i !== node.alternatives.length - 1
? formatCode(
'if (#{resultVar} === null) {',
' #block code',
'}',
{
? fill("choice.next", {
code: code,
resultVar: resultVar(context.resultIndex)
}
)
})
: '';
code = formatCode(
'#block currentAlternativeCode',
'#block nextAlternativesCode',
{
code = fill("choice", {
currentAlternativeCode: emit(node.alternatives[i], context),
nextAlternativesCode: nextAlternativesCode
}
);
});
}
return code;
@ -612,41 +746,22 @@ PEG.compiler.emitter = function(ast) {
return resultVar(context.resultIndex + i);
});
var code = formatCode(
'#{resultVar} = [#{elementResultVars.join(", ")}];',
{
var code = fill("sequence.inner", {
resultVar: resultVar(context.resultIndex),
elementResultVars: elementResultVars
}
);
});
for (var i = node.elements.length - 1; i >= 0; i--) {
code = formatCode(
'#block elementCode',
'if (#{elementResultVar} !== null) {',
' #block code',
'} else {',
' #{resultVar} = null;',
' pos = #{posVar};',
'}',
{
code = fill("sequence.iteration", {
elementCode: emit(node.elements[i], context.delta(i, 1)),
elementResultVar: elementResultVars[i],
code: code,
posVar: posVar(context.posIndex),
resultVar: resultVar(context.resultIndex)
}
);
});
}
return formatCode(
'#{posVar} = pos;',
'#block code',
{
code: code,
posVar: posVar(context.posIndex)
}
);
return fill("sequence", { code: code, posVar: posVar(context.posIndex) });
},
labeled: function(node, context) {
@ -654,110 +769,56 @@ PEG.compiler.emitter = function(ast) {
},
simple_and: function(node, context) {
return formatCode(
'#{posVar} = pos;',
'reportFailures++;',
'#block expressionCode',
'reportFailures--;',
'if (#{resultVar} !== null) {',
' #{resultVar} = "";',
' pos = #{posVar};',
'} else {',
' #{resultVar} = null;',
'}',
{
return fill("simple_and", {
expressionCode: emit(node.expression, context.delta(0, 1)),
posVar: posVar(context.posIndex),
resultVar: resultVar(context.resultIndex)
}
);
});
},
simple_not: function(node, context) {
return formatCode(
'#{posVar} = pos;',
'reportFailures++;',
'#block expressionCode',
'reportFailures--;',
'if (#{resultVar} === null) {',
' #{resultVar} = "";',
'} else {',
' #{resultVar} = null;',
' pos = #{posVar};',
'}',
{
return fill("simple_not", {
expressionCode: emit(node.expression, context.delta(0, 1)),
posVar: posVar(context.posIndex),
resultVar: resultVar(context.resultIndex)
}
);
});
},
semantic_and: function(node, context) {
return formatCode(
'#{resultVar} = (function() {#{node.code}})() ? "" : null;',
{
return fill("semantic_and", {
node: node,
resultVar: resultVar(context.resultIndex)
}
);
});
},
semantic_not: function(node, context) {
return formatCode(
'#{resultVar} = (function() {#{node.code}})() ? null : "";',
{
return fill("semantic_not", {
node: node,
resultVar: resultVar(context.resultIndex)
}
);
});
},
optional: function(node, context) {
return formatCode(
'#block expressionCode',
'#{resultVar} = #{resultVar} !== null ? #{resultVar} : "";',
{
return fill("optional", {
expressionCode: emit(node.expression, context),
resultVar: resultVar(context.resultIndex)
}
);
});
},
zero_or_more: function(node, context) {
return formatCode(
'#{resultVar} = [];',
'#block expressionCode',
'while (#{expressionResultVar} !== null) {',
' #{resultVar}.push(#{expressionResultVar});',
' #block expressionCode',
'}',
{
return fill("zero_or_more", {
expressionCode: emit(node.expression, context.delta(1, 0)),
expressionResultVar: resultVar(context.resultIndex + 1),
resultVar: resultVar(context.resultIndex)
}
);
});
},
one_or_more: function(node, context) {
return formatCode(
'#block expressionCode',
'if (#{expressionResultVar} !== null) {',
' #{resultVar} = [];',
' while (#{expressionResultVar} !== null) {',
' #{resultVar}.push(#{expressionResultVar});',
' #block expressionCode',
' }',
'} else {',
' #{resultVar} = null;',
'}',
{
return fill("one_or_more", {
expressionCode: emit(node.expression, context.delta(1, 0)),
expressionResultVar: resultVar(context.resultIndex + 1),
resultVar: resultVar(context.resultIndex)
}
);
});
},
action: function(node, context) {
@ -793,75 +854,32 @@ PEG.compiler.emitter = function(ast) {
actualParams = [];
}
return formatCode(
'#{posVar} = pos;',
'#block expressionCode',
'if (#{resultVar} !== null) {',
' #{resultVar} = (function(#{formalParams.join(", ")}) {#{node.code}})(#{actualParams.join(", ")});',
'}',
'if (#{resultVar} === null) {',
' pos = #{posVar};',
'}',
{
return fill("action", {
node: node,
expressionCode: emit(node.expression, context.delta(0, 1)),
formalParams: formalParams,
actualParams: actualParams,
posVar: posVar(context.posIndex),
resultVar: resultVar(context.resultIndex)
}
);
});
},
rule_ref: function(node, context) {
return formatCode(
'#{resultVar} = parse_#{node.name}();',
{
return fill("rule_ref", {
node: node,
resultVar: resultVar(context.resultIndex)
}
);
});
},
literal: function(node, context) {
return formatCode(
'#if node.value.length === 0',
' #{resultVar} = "";',
'#else',
' #if node.value.length === 1',
' if (input.charCodeAt(pos) === #{node.value.charCodeAt(0)}) {',
' #else',
' if (input.substr(pos, #{node.value.length}) === #{string(node.value)}) {',
' #end',
' #{resultVar} = #{string(node.value)};',
' pos += #{node.value.length};',
' } else {',
' #{resultVar} = null;',
' if (reportFailures === 0) {',
' matchFailed(#{string(string(node.value))});',
' }',
' }',
'#end',
{
return fill("literal", {
node: node,
resultVar: resultVar(context.resultIndex)
}
);
});
},
any: function(node, context) {
return formatCode(
'if (input.length > pos) {',
' #{resultVar} = input.charAt(pos);',
' pos++;',
'} else {',
' #{resultVar} = null;',
' if (reportFailures === 0) {',
' matchFailed("any character");',
' }',
'}',
{ resultVar: resultVar(context.resultIndex) }
);
return fill("any", { resultVar: resultVar(context.resultIndex) });
},
"class": function(node, context) {
@ -886,22 +904,11 @@ PEG.compiler.emitter = function(ast) {
regexp = node.inverted ? '/^[\\S\\s]/' : '/^(?!)/';
}
return formatCode(
'if (#{regexp}.test(input.charAt(pos))) {',
' #{resultVar} = input.charAt(pos);',
' pos++;',
'} else {',
' #{resultVar} = null;',
' if (reportFailures === 0) {',
' matchFailed(#{string(node.rawText)});',
' }',
'}',
{
return fill("class", {
node: node,
regexp: regexp,
resultVar: resultVar(context.resultIndex)
}
);
});
}
});

Loading…
Cancel
Save