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

Loading…
Cancel
Save