mirror of https://github.com/ghostfolio/ghostfolio
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
204 lines
6.6 KiB
204 lines
6.6 KiB
(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,
|
|
};
|
|
});
|
|
|