UNPKG

chevrotain

Version:

Chevrotain is a high performance fault tolerant javascript parsing DSL for building recursive decent parsers

205 lines (195 loc) 6.75 kB
(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, }; });