UNPKG

chevrotain

Version:

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

212 lines (202 loc) 8.2 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 === "Flat") { 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 } })