chevrotain
Version:
Chevrotain is a high performance fault tolerant javascript parsing DSL for building recursive decent parsers
205 lines (195 loc) • 6.75 kB
JavaScript
(function (root, factory) {
if (typeof define === "function" && define.amd) {
// AMD. Register as an anonymous module.
// TODO: remove dependency to Chevrotain
define(["../vendor/railroad-diagrams"], factory);
} else if (typeof module === "object" && module.exports) {
// Node. Does not work with strict CommonJS, but
// only CommonJS-like environments that support module.exports,
// like Node.
// TODO: remove dependency to Chevrotain
module.exports = factory(require("../vendor/railroad-diagrams"));
} else {
// Browser globals (root is window)
root.diagrams_builder = factory(root.railroad);
}
})(this, function (railroad) {
var Diagram = railroad.Diagram;
var Sequence = railroad.Sequence;
var Choice = railroad.Choice;
var Optional = railroad.Optional;
var OneOrMore = railroad.OneOrMore;
var ZeroOrMore = railroad.ZeroOrMore;
// var Terminal = railroad.Terminal
var NonTerminal = railroad.NonTerminal;
/**
* @param {chevrotain.gast.ISerializedGast} topRules
*
* @returns {string} - The htmlText that will render the diagrams
*/
function buildSyntaxDiagramsText(topRules) {
var diagramsHtml = "";
topRules.forEach(function (production) {
var currDiagramHtml = convertProductionToDiagram(
production,
production.name,
);
diagramsHtml +=
'<h2 class="diagramHeader">' +
production.name +
"</h2>" +
currDiagramHtml;
});
return diagramsHtml;
}
function definitionsToSubDiagrams(definitions, topRuleName) {
var subDiagrams = definitions.map(function (subProd) {
return convertProductionToDiagram(subProd, topRuleName);
});
return subDiagrams;
}
/**
* @param {chevrotain.gast.ISerializedTerminal} prod
* @param {string} topRuleName
* @param {string} dslRuleName
*
* @return {RailRoadDiagram.Terminal}
*/
function createTerminalFromSerializedGast(prod, topRuleName, dslRuleName) {
// PATTERN static property will not exist when using custom lexers (hand built or other lexer generators)
var toolTipTitle = undefined;
// avoid trying to use a custom token pattern as the title.
if (
typeof prod.pattern === "string" ||
Object.prototype.toString.call(prod.pattern) === "[object RegExp]"
) {
toolTipTitle = prod.pattern;
}
return railroad.Terminal(
prod.label,
undefined,
toolTipTitle,
prod.occurrenceInParent,
topRuleName,
dslRuleName,
prod.name,
);
}
/**
* @param prod
* @param topRuleName
*
* Converts a single Chevrotain Grammar production to a RailRoad Diagram.
* This is also exported to allow custom logic in the creation of the diagrams.
* @returns {*}
*/
function convertProductionToDiagram(prod, topRuleName) {
if (prod.type === "NonTerminal") {
// must handle NonTerminal separately from the other AbstractProductions as we do not want to expand the subDefinition
// of a reference and cause infinite loops
return NonTerminal(
getNonTerminalName(prod),
undefined,
prod.occurrenceInParent,
topRuleName,
);
} else if (prod.type !== "Terminal") {
var subDiagrams = definitionsToSubDiagrams(prod.definition, topRuleName);
if (prod.type === "Rule") {
return Diagram.apply(this, subDiagrams);
} else if (prod.type === "Alternative") {
return Sequence.apply(this, subDiagrams);
} else if (prod.type === "Option") {
if (subDiagrams.length > 1) {
return Optional(Sequence.apply(this, subDiagrams));
} else if (subDiagrams.length === 1) {
return Optional(subDiagrams[0]);
} else {
throw Error("Empty Optional production, OOPS!");
}
} else if (prod.type === "Repetition") {
if (subDiagrams.length > 1) {
return ZeroOrMore(Sequence.apply(this, subDiagrams));
} else if (subDiagrams.length === 1) {
return ZeroOrMore(subDiagrams[0]);
} else {
throw Error("Empty Optional production, OOPS!");
}
} else if (prod.type === "Alternation") {
// todo: what does the first argument of choice (the index 0 means?)
return Choice.apply(this, [0].concat(subDiagrams));
} else if (prod.type === "RepetitionMandatory") {
if (subDiagrams.length > 1) {
return OneOrMore(Sequence.apply(this, subDiagrams));
} else if (subDiagrams.length === 1) {
return OneOrMore(subDiagrams[0]);
} else {
throw Error("Empty Optional production, OOPS!");
}
} else if (prod.type === "RepetitionWithSeparator") {
if (subDiagrams.length > 0) {
// MANY_SEP(separator, definition) === (definition (separator definition)*)?
return Optional(
Sequence.apply(
this,
subDiagrams.concat([
ZeroOrMore(
Sequence.apply(
this,
[
createTerminalFromSerializedGast(
prod.separator,
topRuleName,
"many_sep",
),
].concat(subDiagrams),
),
),
]),
),
);
} else {
throw Error("Empty Optional production, OOPS!");
}
} else if (prod.type === "RepetitionMandatoryWithSeparator") {
if (subDiagrams.length > 0) {
// AT_LEAST_ONE_SEP(separator, definition) === definition (separator definition)*
return Sequence.apply(
this,
subDiagrams.concat([
ZeroOrMore(
Sequence.apply(
this,
[
createTerminalFromSerializedGast(
prod.separator,
topRuleName,
"at_least_one_sep",
),
].concat(subDiagrams),
),
),
]),
);
} else {
throw Error("Empty Optional production, OOPS!");
}
}
} else if (prod.type === "Terminal") {
return createTerminalFromSerializedGast(prod, topRuleName, "consume");
} else {
throw Error("non exhaustive match");
}
}
function getNonTerminalName(prod) {
if (prod.nonTerminalName !== undefined) {
return prod.nonTerminalName;
} else {
return prod.name;
}
}
return {
buildSyntaxDiagramsText: buildSyntaxDiagramsText,
convertProductionToDiagram: convertProductionToDiagram,
};
});