Aktueller Stand

This commit is contained in:
2026-01-23 01:33:35 +01:00
parent 082dc5e110
commit 2766dd12c5
10109 changed files with 1578841 additions and 77685 deletions

1
backend/node_modules/chevrotain/diagrams/README.md generated vendored Normal file
View File

@@ -0,0 +1 @@
See [online docs](https://chevrotain.io/docs/guide/generating_syntax_diagrams.html).

85
backend/node_modules/chevrotain/diagrams/diagrams.css generated vendored Normal file
View File

@@ -0,0 +1,85 @@
svg.railroad-diagram path {
stroke-width: 3;
stroke: black;
fill: rgba(0, 0, 0, 0);
}
svg.railroad-diagram text {
font: bold 14px monospace;
text-anchor: middle;
}
svg.railroad-diagram text.label {
text-anchor: start;
}
svg.railroad-diagram text.comment {
font: italic 12px monospace;
}
svg.railroad-diagram g.non-terminal rect {
fill: hsl(223, 100%, 83%);
}
svg.railroad-diagram rect {
stroke-width: 3;
stroke: black;
fill: hsl(190, 100%, 83%);
}
.diagramHeader {
display: inline-block;
-webkit-touch-callout: default;
-webkit-user-select: text;
-khtml-user-select: text;
-moz-user-select: text;
-ms-user-select: text;
user-select: text;
font-weight: bold;
font-family: monospace;
font-size: 18px;
margin-bottom: -8px;
text-align: center;
}
.diagramHeaderDef {
background-color: lightgreen;
}
svg.railroad-diagram text {
-webkit-touch-callout: default;
-webkit-user-select: text;
-khtml-user-select: text;
-moz-user-select: text;
-ms-user-select: text;
user-select: text;
}
svg.railroad-diagram g.non-terminal rect.diagramRectUsage {
color: green;
fill: yellow;
stroke: 5;
}
svg.railroad-diagram g.terminal rect.diagramRectUsage {
color: green;
fill: yellow;
stroke: 5;
}
div {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
svg {
width: 100%;
}
svg.railroad-diagram g.non-terminal text {
cursor: pointer;
}

View File

@@ -0,0 +1,221 @@
;(function (root, factory) {
if (typeof define === "function" && define.amd) {
// AMD. Register as an anonymous module.
define([], 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.
module.exports = factory()
} else {
// Browser globals (root is window)
root.diagrams_behavior = factory()
}
})(this, function () {
/**
* @param [scrollingEnabled=true] {boolean} - Is the scrolling from a non-terminal usage to it's definition
* enabled. it is enabled by default, but this flow is not relevant in all use cases (playground) and thus
* it is parametrized.
*/
function initDiagramsBehavior(scrollingEnabled) {
if (scrollingEnabled === undefined) {
scrollingEnabled = true
}
var diagramHeaders = toArr(document.getElementsByClassName("diagramHeader"))
diagramHeaders.forEach(function (header) {
header.addEventListener(
"mouseover",
toggleNonTerminalUsageAndDef_fromHeader
)
header.addEventListener(
"mouseout",
toggleNonTerminalUsageAndDef_fromHeader
)
})
var noneTerminals = toArr(document.getElementsByClassName("non-terminal"))
var noneTerminalsText = findDomChildrenByTagName(noneTerminals, "text")
noneTerminalsText.forEach(function (nonTerminal) {
nonTerminal.addEventListener(
"mouseover",
toggleNonTerminalUsageAndDef_fromNoneTerminal
)
nonTerminal.addEventListener(
"mouseout",
toggleNonTerminalUsageAndDef_fromNoneTerminal
)
if (scrollingEnabled) {
nonTerminal.addEventListener("click", jumpToNoneTerminalDef)
}
})
var terminals = toArr(document.getElementsByClassName("terminal"))
var terminalsText = findDomChildrenByTagName(terminals, "text")
terminalsText.forEach(function (terminal) {
terminal.addEventListener("mouseover", toggleTerminalUsage)
terminal.addEventListener("mouseout", toggleTerminalUsage)
})
}
function toggleTerminalUsage(mouseEvent) {
var terminalName = mouseEvent.target.getAttribute("label")
var rects = getUsageSvgRect(terminalName, "terminal", "label")
toggleClassForNodes(rects, "diagramRectUsage")
}
function toggleNonTerminalUsageAndDef_fromNoneTerminal(mouseEvent) {
var rectsHeaderAndRuleName = getUsageRectAndDefHeader(mouseEvent.target)
toggleClassForNodes(rectsHeaderAndRuleName.rects, "diagramRectUsage")
toggleClass(rectsHeaderAndRuleName.header, "diagramHeaderDef")
}
function jumpToNoneTerminalDef(mouseEvent) {
var header = findHeader(mouseEvent.target.getAttribute("rulename"))
scrollToY(header.offsetTop, 666, "easeInOutQuint")
}
function toggleNonTerminalUsageAndDef_fromHeader(mouseEvent) {
toggleClass(mouseEvent.target, "diagramHeaderDef")
// this does not work on an svg DOM element so its ok to use innerHTML.
var definitionName = mouseEvent.target.innerHTML
var rects = getUsageSvgRect(definitionName, "non-terminal", "rulename")
toggleClassForNodes(rects, "diagramRectUsage")
}
function getUsageSvgRect(definitionName, className, attributeName) {
var classDomElements = toArr(document.getElementsByClassName(className))
var rects = findDomChildrenByTagName(classDomElements, "rect")
return rects.filter(function (currRect) {
var textNode = currRect.parentNode.getElementsByTagName("text")[0]
return textNode.getAttribute(attributeName) === definitionName
})
}
function findHeader(headerName) {
var headers = toArr(document.getElementsByClassName("diagramHeader"))
var header = headers.find(function (currHeader) {
// this works on H2 dom elements and not SVG elements so innerHTML usage is safe.
return currHeader.innerHTML === headerName
})
return header
}
function getUsageRectAndDefHeader(target) {
var headerName = target.getAttribute("rulename")
var rects = getUsageSvgRect(headerName, "non-terminal", "rulename")
var header = findHeader(headerName)
return {
rects: rects,
header: header,
ruleName: headerName
}
}
// utils
// IE 10/11 does not support this on svg elements.
// I'm uncertain I really care... :)
// https://developer.mozilla.org/en-US/docs/Web/API/Element/classList
function toggleClass(domNode, className) {
if (domNode.classList.contains(className)) {
domNode.classList.remove(className)
} else {
domNode.classList.add(className)
}
}
function toggleClassForNodes(domNodes, className) {
domNodes.forEach(function (currDomNode) {
toggleClass(currDomNode, className)
})
}
function toArr(htmlCollection) {
return Array.prototype.slice.call(htmlCollection)
}
// first add raf shim
// http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
var requestAnimFrame = (function () {
return (
window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function (callback) {
window.setTimeout(callback, 1000 / 60)
}
)
})()
// https://stackoverflow.com/questions/8917921/cross-browser-javascript-not-jquery-scroll-to-top-animation
function scrollToY(scrollTargetY, speed, easing) {
// scrollTargetY: the target scrollY property of the window
// speed: time in pixels per second
// easing: easing equation to use
var scrollY = window.scrollY,
scrollTargetY = scrollTargetY || 0,
speed = speed || 2000,
easing = easing || "easeOutSine",
currentTime = 0
// min time .1, max time .8 seconds
var time = Math.max(
0.1,
Math.min(Math.abs(scrollY - scrollTargetY) / speed, 0.8)
)
// easing equations from https://github.com/danro/easing-js/blob/master/easing.js
var PI_D2 = Math.PI / 2,
easingEquations = {
easeOutSine: function (pos) {
return Math.sin(pos * (Math.PI / 2))
},
easeInOutSine: function (pos) {
return -0.5 * (Math.cos(Math.PI * pos) - 1)
},
easeInOutQuint: function (pos) {
if ((pos /= 0.5) < 1) {
return 0.5 * Math.pow(pos, 5)
}
return 0.5 * (Math.pow(pos - 2, 5) + 2)
}
}
// add animation loop
function tick() {
currentTime += 1 / 60
var p = currentTime / time
var t = easingEquations[easing](p)
if (p < 1) {
requestAnimFrame(tick)
window.scrollTo(0, scrollY + (scrollTargetY - scrollY) * t)
} else {
window.scrollTo(0, scrollTargetY)
}
}
// call it once to get started
tick()
}
function findDomChildrenByTagName(domElements, tagName) {
var elemsFound = []
domElements.forEach(function (currDomNode) {
toArr(currDomNode.children).forEach(function (currChild) {
if (currChild.tagName === tagName) {
elemsFound.push(currChild)
}
})
})
return elemsFound
}
return {
initDiagramsBehavior: initDiagramsBehavior
}
})

View File

@@ -0,0 +1,204 @@
;(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
}
})

View File

@@ -0,0 +1,20 @@
/**
* @param {string} targetFilePath - The path and file name to serialize to.
* @param {string} varName - The name of the global variable to expose the serialized contents/
* @param {chevrotain.Parser} parserInstance - A parser instance whose grammar will be serialized.
*/
function serializeGrammarToFile(targetFilePath, varName, parserInstance) {
var fs = require("fs")
var serializedGrammar = parserInstance.getSerializedGastProductions()
var serializedGrammarText = JSON.stringify(serializedGrammar, null, "\t")
// generated a JavaScript file which exports the serialized grammar on the global scope (Window)
fs.writeFileSync(
targetFilePath,
"var " + varName + " = " + serializedGrammarText
)
}
module.exports = {
serializeGrammarToFile: serializeGrammarToFile
}

30
backend/node_modules/chevrotain/diagrams/src/main.js generated vendored Normal file
View File

@@ -0,0 +1,30 @@
;(function (root, factory) {
if (typeof define === "function" && define.amd) {
// AMD. Register as an anonymous module.
define(["./diagrams_builder", "./diagrams_behavior"], 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.
module.exports = factory(
require("./diagrams_builder"),
require("./diagrams_behavior")
)
} else {
// Browser globals (root is window)
root.main = factory(root.diagrams_builder, root.diagrams_behavior)
}
})(this, function (builder, behavior) {
return {
drawDiagramsFromParserInstance: function (parserInstanceToDraw, targetDiv) {
var topRules = parserInstanceToDraw.getSerializedGastProductions()
targetDiv.innerHTML = builder.buildSyntaxDiagramsText(topRules)
behavior.initDiagramsBehavior()
},
drawDiagramsFromSerializedGrammar: function (serializedGrammar, targetDiv) {
targetDiv.innerHTML = builder.buildSyntaxDiagramsText(serializedGrammar)
behavior.initDiagramsBehavior()
}
}
})

View File

@@ -0,0 +1,965 @@
/*
Railroad Diagrams
by Tab Atkins Jr. (and others)
http://xanthir.com
http://twitter.com/tabatkins
http://github.com/tabatkins/railroad-diagrams
This document and all associated files in the github project are licensed under CC0: http://creativecommons.org/publicdomain/zero/1.0/
This means you can reuse, remix, or otherwise appropriate this project for your own use WITHOUT RESTRICTION.
(The actual legal meaning can be found at the above link.)
Don't ask me for permission to use any part of this project, JUST USE IT.
I would appreciate attribution, but that is not required by the license.
*/
/*
This file uses a module pattern to avoid leaking names into the global scope.
The only accidental leakage is the name "temp".
The exported names can be found at the bottom of this file;
simply change the names in the array of strings to change what they are called in your application.
As well, several configuration constants are passed into the module function at the bottom of this file.
At runtime, these constants can be found on the Diagram class.
*/
;(function (options) {
function subclassOf(baseClass, superClass) {
baseClass.prototype = Object.create(superClass.prototype)
baseClass.prototype.$super = superClass.prototype
}
function unnull(/* children */) {
return [].slice.call(arguments).reduce(function (sofar, x) {
return sofar !== undefined ? sofar : x
})
}
function determineGaps(outer, inner) {
var diff = outer - inner
switch (Diagram.INTERNAL_ALIGNMENT) {
case "left":
return [0, diff]
break
case "right":
return [diff, 0]
break
case "center":
default:
return [diff / 2, diff / 2]
break
}
}
function wrapString(value) {
return typeof value == "string" ? new Terminal(value) : value
}
function stackAtIllegalPosition(items) {
/* The height of the last line of the Stack is determined by the last child and
therefore any element outside the Stack could overlap with other elements.
If the Stack is the last element no overlap can occur. */
for (var i = 0; i < items.length; i++) {
if (items[i] instanceof Stack && i !== items.length - 1) {
return true
}
}
return false
}
function SVG(name, attrs, text) {
attrs = attrs || {}
text = text || ""
var el = document.createElementNS("http://www.w3.org/2000/svg", name)
for (var attr in attrs) {
if (attr === "xlink:href")
el.setAttributeNS("http://www.w3.org/1999/xlink", "href", attrs[attr])
else el.setAttribute(attr, attrs[attr])
}
el.textContent = text
return el
}
function FakeSVG(tagName, attrs, text) {
if (!(this instanceof FakeSVG)) return new FakeSVG(tagName, attrs, text)
if (text) this.children = text
else this.children = []
this.tagName = tagName
this.attrs = unnull(attrs, {})
return this
}
FakeSVG.prototype.format = function (x, y, width) {
// Virtual
}
FakeSVG.prototype.addTo = function (parent) {
if (parent instanceof FakeSVG) {
parent.children.push(this)
return this
} else {
var svg = this.toSVG()
parent.appendChild(svg)
return svg
}
}
FakeSVG.prototype.escapeString = function (string) {
// Escape markdown and HTML special characters
return string.replace(/[*_\`\[\]<&]/g, function (charString) {
return "&#" + charString.charCodeAt(0) + ";"
})
}
FakeSVG.prototype.toSVG = function () {
var el = SVG(this.tagName, this.attrs)
if (typeof this.children == "string") {
el.textContent = this.children
} else {
this.children.forEach(function (e) {
el.appendChild(e.toSVG())
})
}
return el
}
FakeSVG.prototype.toString = function () {
var str = "<" + this.tagName
var group = this.tagName == "g" || this.tagName == "svg"
for (var attr in this.attrs) {
str +=
" " +
attr +
'="' +
(this.attrs[attr] + "").replace(/&/g, "&amp;").replace(/"/g, "&quot;") +
'"'
}
str += ">"
if (group) str += "\n"
if (typeof this.children == "string") {
str += FakeSVG.prototype.escapeString(this.children)
} else {
this.children.forEach(function (e) {
str += e
})
}
str += "</" + this.tagName + ">\n"
return str
}
function Path(x, y) {
if (!(this instanceof Path)) return new Path(x, y)
FakeSVG.call(this, "path")
this.attrs.d = "M" + x + " " + y
}
subclassOf(Path, FakeSVG)
Path.prototype.m = function (x, y) {
this.attrs.d += "m" + x + " " + y
return this
}
Path.prototype.h = function (val) {
this.attrs.d += "h" + val
return this
}
Path.prototype.right = Path.prototype.h
Path.prototype.left = function (val) {
return this.h(-val)
}
Path.prototype.v = function (val) {
this.attrs.d += "v" + val
return this
}
Path.prototype.down = Path.prototype.v
Path.prototype.up = function (val) {
return this.v(-val)
}
Path.prototype.arc = function (sweep) {
var x = Diagram.ARC_RADIUS
var y = Diagram.ARC_RADIUS
if (sweep[0] == "e" || sweep[1] == "w") {
x *= -1
}
if (sweep[0] == "s" || sweep[1] == "n") {
y *= -1
}
if (sweep == "ne" || sweep == "es" || sweep == "sw" || sweep == "wn") {
var cw = 1
} else {
var cw = 0
}
this.attrs.d +=
"a" +
Diagram.ARC_RADIUS +
" " +
Diagram.ARC_RADIUS +
" 0 0 " +
cw +
" " +
x +
" " +
y
return this
}
Path.prototype.format = function () {
// All paths in this library start/end horizontally.
// The extra .5 ensures a minor overlap, so there's no seams in bad rasterizers.
this.attrs.d += "h.5"
return this
}
function Diagram(items) {
if (!(this instanceof Diagram)) return new Diagram([].slice.call(arguments))
FakeSVG.call(this, "svg", { class: Diagram.DIAGRAM_CLASS })
if (stackAtIllegalPosition(items)) {
throw new RangeError(
"Stack() must only occur at the very last position of Diagram()."
)
}
this.items = items.map(wrapString)
this.items.unshift(new Start())
this.items.push(new End())
this.width =
this.items.reduce(function (sofar, el) {
return sofar + el.width + (el.needsSpace ? 20 : 0)
}, 0) + 1
this.height = this.items.reduce(function (sofar, el) {
return sofar + el.height
}, 0)
this.up = Math.max.apply(
null,
this.items.map(function (x) {
return x.up
})
)
this.down = Math.max.apply(
null,
this.items.map(function (x) {
return x.down
})
)
this.formatted = false
}
subclassOf(Diagram, FakeSVG)
for (var option in options) {
Diagram[option] = options[option]
}
Diagram.prototype.format = function (paddingt, paddingr, paddingb, paddingl) {
paddingt = unnull(paddingt, 20)
paddingr = unnull(paddingr, paddingt, 20)
paddingb = unnull(paddingb, paddingt, 20)
paddingl = unnull(paddingl, paddingr, 20)
var x = paddingl
var y = paddingt
y += this.up
var g = FakeSVG(
"g",
Diagram.STROKE_ODD_PIXEL_LENGTH ? { transform: "translate(.5 .5)" } : {}
)
for (var i = 0; i < this.items.length; i++) {
var item = this.items[i]
if (item.needsSpace) {
Path(x, y).h(10).addTo(g)
x += 10
}
item.format(x, y, item.width + item.offsetX).addTo(g)
x += item.width + item.offsetX
y += item.height
if (item.needsSpace) {
Path(x, y).h(10).addTo(g)
x += 10
}
}
this.attrs.width = this.width + paddingl + paddingr
this.attrs.height = this.up + this.height + this.down + paddingt + paddingb
this.attrs.viewBox = "0 0 " + this.attrs.width + " " + this.attrs.height
g.addTo(this)
this.formatted = true
return this
}
Diagram.prototype.addTo = function (parent) {
var scriptTag = document.getElementsByTagName("script")
scriptTag = scriptTag[scriptTag.length - 1]
var parentTag = scriptTag.parentNode
parent = parent || parentTag
return this.$super.addTo.call(this, parent)
}
Diagram.prototype.toSVG = function () {
if (!this.formatted) {
this.format()
}
return this.$super.toSVG.call(this)
}
Diagram.prototype.toString = function () {
if (!this.formatted) {
this.format()
}
return this.$super.toString.call(this)
}
function ComplexDiagram() {
var diagram = new Diagram([].slice.call(arguments))
var items = diagram.items
items.shift()
items.pop()
items.unshift(new Start(false))
items.push(new End(false))
diagram.items = items
return diagram
}
function Sequence(items) {
if (!(this instanceof Sequence))
return new Sequence([].slice.call(arguments))
FakeSVG.call(this, "g")
if (stackAtIllegalPosition(items)) {
throw new RangeError(
"Stack() must only occur at the very last position of Sequence()."
)
}
this.items = items.map(wrapString)
this.width = this.items.reduce(function (sofar, el) {
return sofar + el.width + (el.needsSpace ? 20 : 0)
}, 0)
this.offsetX = 0
this.height = this.items.reduce(function (sofar, el) {
return sofar + el.height
}, 0)
this.up = this.items.reduce(function (sofar, el) {
return Math.max(sofar, el.up)
}, 0)
this.down = this.items.reduce(function (sofar, el) {
return Math.max(sofar, el.down)
}, 0)
}
subclassOf(Sequence, FakeSVG)
Sequence.prototype.format = function (x, y, width) {
// Hook up the two sides if this is narrower than its stated width.
var gaps = determineGaps(width, this.width)
Path(x, y).h(gaps[0]).addTo(this)
Path(x + gaps[0] + this.width, y + this.height)
.h(gaps[1])
.addTo(this)
x += gaps[0]
for (var i = 0; i < this.items.length; i++) {
var item = this.items[i]
if (item.needsSpace) {
Path(x, y).h(10).addTo(this)
x += 10
}
item.format(x, y, item.width).addTo(this)
x += item.width
y += item.height
if (item.needsSpace) {
Path(x, y).h(10).addTo(this)
x += 10
}
}
return this
}
function Stack(items) {
if (!(this instanceof Stack)) return new Stack([].slice.call(arguments))
FakeSVG.call(this, "g")
if (stackAtIllegalPosition(items)) {
throw new RangeError(
"Stack() must only occur at the very last position of Stack()."
)
}
if (items.length === 0) {
throw new RangeError("Stack() must have at least one child.")
}
this.items = items.map(wrapString)
this.width = this.items.reduce(function (sofar, el) {
return Math.max(sofar, el.width + (el.needsSpace ? 20 : 0))
}, 0)
if (this.items.length > 1) {
this.width += Diagram.ARC_RADIUS * 2
}
this.up = this.items[0].up
this.down = this.items[this.items.length - 1].down
this.height = 0
for (var i = 0; i < this.items.length; i++) {
this.height += this.items[i].height
if (i !== this.items.length - 1) {
this.height +=
Math.max(this.items[i].down, Diagram.VERTICAL_SEPARATION) +
Math.max(this.items[i + 1].up, Diagram.VERTICAL_SEPARATION) +
Diagram.ARC_RADIUS * 4
}
}
if (this.items.length === 0) {
this.offsetX = 0
} else {
// the value is usually negative because the linebreak resets the x value for the next element
this.offsetX = -(
this.width -
this.items[this.items.length - 1].width -
this.items[this.items.length - 1].offsetX -
(this.items[this.items.length - 1].needsSpace ? 20 : 0)
)
if (this.items.length > 1) {
this.offsetX += Diagram.ARC_RADIUS * 2
}
}
}
subclassOf(Stack, FakeSVG)
Stack.prototype.format = function (x, y, width) {
var xIntitial = x
for (var i = 0; i < this.items.length; i++) {
var item = this.items[i]
if (item.needsSpace) {
Path(x, y).h(10).addTo(this)
x += 10
}
item
.format(
x,
y,
Math.max(item.width + item.offsetX, Diagram.ARC_RADIUS * 2)
)
.addTo(this)
x += Math.max(item.width + item.offsetX, Diagram.ARC_RADIUS * 2)
y += item.height
if (item.needsSpace) {
Path(x, y).h(10).addTo(this)
x += 10
}
if (i !== this.items.length - 1) {
Path(x, y)
.arc("ne")
.down(Math.max(item.down, Diagram.VERTICAL_SEPARATION))
.arc("es")
.left(x - xIntitial - Diagram.ARC_RADIUS * 2)
.arc("nw")
.down(Math.max(this.items[i + 1].up, Diagram.VERTICAL_SEPARATION))
.arc("ws")
.addTo(this)
y +=
Math.max(item.down, Diagram.VERTICAL_SEPARATION) +
Math.max(this.items[i + 1].up, Diagram.VERTICAL_SEPARATION) +
Diagram.ARC_RADIUS * 4
x = xIntitial + Diagram.ARC_RADIUS * 2
}
}
Path(x, y)
.h(width - (this.width + this.offsetX))
.addTo(this)
return this
}
function Choice(normal, items) {
if (!(this instanceof Choice))
return new Choice(normal, [].slice.call(arguments, 1))
FakeSVG.call(this, "g")
if (typeof normal !== "number" || normal !== Math.floor(normal)) {
throw new TypeError("The first argument of Choice() must be an integer.")
} else if (normal < 0 || normal >= items.length) {
throw new RangeError(
"The first argument of Choice() must be an index for one of the items."
)
} else {
this.normal = normal
}
this.items = items.map(wrapString)
this.width =
this.items.reduce(function (sofar, el) {
return Math.max(sofar, el.width)
}, 0) +
Diagram.ARC_RADIUS * 4
this.offsetX = 0
this.height = this.items[normal].height
this.up = this.down = 0
for (var i = 0; i < this.items.length; i++) {
var item = this.items[i]
if (i < normal) {
this.up += Math.max(
Diagram.ARC_RADIUS,
item.up + item.height + item.down + Diagram.VERTICAL_SEPARATION
)
}
if (i == normal) {
this.up += Math.max(Diagram.ARC_RADIUS, item.up)
this.down += Math.max(Diagram.ARC_RADIUS, item.down)
}
if (i > normal) {
this.down += Math.max(
Diagram.ARC_RADIUS,
Diagram.VERTICAL_SEPARATION + item.up + item.down + item.height
)
}
}
}
subclassOf(Choice, FakeSVG)
Choice.prototype.format = function (x, y, width) {
// Hook up the two sides if this is narrower than its stated width.
var gaps = determineGaps(width, this.width)
Path(x, y).h(gaps[0]).addTo(this)
Path(x + gaps[0] + this.width, y + this.height)
.h(gaps[1])
.addTo(this)
x += gaps[0]
var last = this.items.length - 1
var innerWidth = this.width - Diagram.ARC_RADIUS * 4
// Do the elements that curve above
for (var i = this.normal - 1; i >= 0; i--) {
var item = this.items[i]
if (i == this.normal - 1) {
var distanceFromY = Math.max(
Diagram.ARC_RADIUS * 2,
this.items[i + 1].up +
Diagram.VERTICAL_SEPARATION +
item.height +
item.down
)
}
Path(x, y)
.arc("se")
.up(distanceFromY - Diagram.ARC_RADIUS * 2)
.arc("wn")
.addTo(this)
item
.format(x + Diagram.ARC_RADIUS * 2, y - distanceFromY, innerWidth)
.addTo(this)
Path(
x + Diagram.ARC_RADIUS * 2 + innerWidth,
y - distanceFromY + item.height
)
.arc("ne")
.down(
distanceFromY -
item.height +
this.items[this.normal].height -
Diagram.ARC_RADIUS * 2
)
.arc("ws")
.addTo(this)
distanceFromY += Math.max(
Diagram.ARC_RADIUS,
item.up +
Diagram.VERTICAL_SEPARATION +
(i == 0 ? 0 : this.items[i - 1].down + this.items[i - 1].height)
)
}
// Do the straight-line path.
Path(x, y)
.right(Diagram.ARC_RADIUS * 2)
.addTo(this)
this.items[this.normal]
.format(x + Diagram.ARC_RADIUS * 2, y, innerWidth)
.addTo(this)
Path(x + Diagram.ARC_RADIUS * 2 + innerWidth, y + this.height)
.right(Diagram.ARC_RADIUS * 2)
.addTo(this)
// Do the elements that curve below
for (var i = this.normal + 1; i <= last; i++) {
var item = this.items[i]
if (i == this.normal + 1) {
var distanceFromY = Math.max(
Diagram.ARC_RADIUS * 2,
this.items[i - 1].height +
this.items[i - 1].down +
Diagram.VERTICAL_SEPARATION +
item.up
)
}
Path(x, y)
.arc("ne")
.down(distanceFromY - Diagram.ARC_RADIUS * 2)
.arc("ws")
.addTo(this)
item
.format(x + Diagram.ARC_RADIUS * 2, y + distanceFromY, innerWidth)
.addTo(this)
Path(
x + Diagram.ARC_RADIUS * 2 + innerWidth,
y + distanceFromY + item.height
)
.arc("se")
.up(
distanceFromY -
Diagram.ARC_RADIUS * 2 +
item.height -
this.items[this.normal].height
)
.arc("wn")
.addTo(this)
distanceFromY += Math.max(
Diagram.ARC_RADIUS,
item.height +
item.down +
Diagram.VERTICAL_SEPARATION +
(i == last ? 0 : this.items[i + 1].up)
)
}
return this
}
function Optional(item, skip) {
if (skip === undefined) return Choice(1, Skip(), item)
else if (skip === "skip") return Choice(0, Skip(), item)
else throw "Unknown value for Optional()'s 'skip' argument."
}
function OneOrMore(item, rep) {
if (!(this instanceof OneOrMore)) return new OneOrMore(item, rep)
FakeSVG.call(this, "g")
rep = rep || new Skip()
this.item = wrapString(item)
this.rep = wrapString(rep)
this.width =
Math.max(this.item.width, this.rep.width) + Diagram.ARC_RADIUS * 2
this.offsetX = 0
this.height = this.item.height
this.up = this.item.up
this.down = Math.max(
Diagram.ARC_RADIUS * 2,
this.item.down +
Diagram.VERTICAL_SEPARATION +
this.rep.up +
this.rep.height +
this.rep.down
)
}
subclassOf(OneOrMore, FakeSVG)
OneOrMore.prototype.needsSpace = true
OneOrMore.prototype.format = function (x, y, width) {
// Hook up the two sides if this is narrower than its stated width.
var gaps = determineGaps(width, this.width)
Path(x, y).h(gaps[0]).addTo(this)
Path(x + gaps[0] + this.width, y + this.height)
.h(gaps[1])
.addTo(this)
x += gaps[0]
// Draw item
Path(x, y).right(Diagram.ARC_RADIUS).addTo(this)
this.item
.format(x + Diagram.ARC_RADIUS, y, this.width - Diagram.ARC_RADIUS * 2)
.addTo(this)
Path(x + this.width - Diagram.ARC_RADIUS, y + this.height)
.right(Diagram.ARC_RADIUS)
.addTo(this)
// Draw repeat arc
var distanceFromY = Math.max(
Diagram.ARC_RADIUS * 2,
this.item.height +
this.item.down +
Diagram.VERTICAL_SEPARATION +
this.rep.up
)
Path(x + Diagram.ARC_RADIUS, y)
.arc("nw")
.down(distanceFromY - Diagram.ARC_RADIUS * 2)
.arc("ws")
.addTo(this)
this.rep
.format(
x + Diagram.ARC_RADIUS,
y + distanceFromY,
this.width - Diagram.ARC_RADIUS * 2
)
.addTo(this)
Path(
x + this.width - Diagram.ARC_RADIUS,
y + distanceFromY + this.rep.height
)
.arc("se")
.up(
distanceFromY -
Diagram.ARC_RADIUS * 2 +
this.rep.height -
this.item.height
)
.arc("en")
.addTo(this)
return this
}
function ZeroOrMore(item, rep, skip) {
return Optional(OneOrMore(item, rep), skip)
}
function Start(simpleType) {
if (!(this instanceof Start)) return new Start()
FakeSVG.call(this, "path")
this.width = 20
this.height = 0
this.offsetX = 0
this.up = 10
this.down = 10
this.simpleType = simpleType
}
subclassOf(Start, FakeSVG)
Start.prototype.format = function (x, y) {
if (this.simpleType === false) {
this.attrs.d = "M " + x + " " + (y - 10) + " v 20 m 0 -10 h 20.5"
} else {
this.attrs.d =
"M " + x + " " + (y - 10) + " v 20 m 10 -20 v 20 m -10 -10 h 20.5"
}
return this
}
function End(simpleType) {
if (!(this instanceof End)) return new End()
FakeSVG.call(this, "path")
this.width = 20
this.height = 0
this.offsetX = 0
this.up = 10
this.down = 10
this.simpleType = simpleType
}
subclassOf(End, FakeSVG)
End.prototype.format = function (x, y) {
if (this.simpleType === false) {
this.attrs.d = "M " + x + " " + y + " h 20 m 0 -10 v 20"
} else {
this.attrs.d = "M " + x + " " + y + " h 20 m -10 -10 v 20 m 10 -20 v 20"
}
return this
}
function Terminal(
text,
href,
title,
occurrenceIdx,
topRuleName,
dslRuleName,
tokenName
) {
if (!(this instanceof Terminal))
return new Terminal(
text,
href,
title,
occurrenceIdx,
topRuleName,
dslRuleName,
tokenName
)
FakeSVG.call(this, "g", { class: "terminal" })
this.text = text
this.label = text
this.href = href
this.title = title
this.occurrenceIdx = occurrenceIdx
this.topRuleName = topRuleName
this.dslRuleName = dslRuleName
this.tokenName = tokenName
this.width =
text.length * 8 +
20 /* Assume that each char is .5em, and that the em is 16px */
this.height = 0
this.offsetX = 0
this.up = 11
this.down = 11
}
subclassOf(Terminal, FakeSVG)
Terminal.prototype.needsSpace = true
Terminal.prototype.format = function (x, y, width) {
// Hook up the two sides if this is narrower than its stated width.
var gaps = determineGaps(width, this.width)
Path(x, y).h(gaps[0]).addTo(this)
Path(x + gaps[0] + this.width, y)
.h(gaps[1])
.addTo(this)
x += gaps[0]
FakeSVG("rect", {
x: x,
y: y - 11,
width: this.width,
height: this.up + this.down,
rx: 10,
ry: 10
}).addTo(this)
var text = FakeSVG(
"text",
{
x: x + this.width / 2,
y: y + 4,
occurrenceIdx: this.occurrenceIdx,
topRuleName: this.topRuleName,
dslRuleName: this.dslRuleName,
tokenName: this.tokenName,
label: this.label
},
this.text
)
var title = FakeSVG("title", {}, this.title)
if (this.href) FakeSVG("a", { "xlink:href": this.href }, [text]).addTo(this)
else {
text.addTo(this)
if (this.title !== undefined) {
title.addTo(this)
}
}
return this
}
function NonTerminal(text, href, occurrenceIdx, topRuleName) {
if (!(this instanceof NonTerminal))
return new NonTerminal(text, href, occurrenceIdx, topRuleName)
FakeSVG.call(this, "g", { class: "non-terminal" })
this.text = text
this.ruleName = text
this.href = href
this.occurrenceIdx = occurrenceIdx
this.topRuleName = topRuleName
this.width = text.length * 8 + 20
this.height = 0
this.offsetX = 0
this.up = 11
this.down = 11
}
subclassOf(NonTerminal, FakeSVG)
NonTerminal.prototype.needsSpace = true
NonTerminal.prototype.format = function (x, y, width) {
// Hook up the two sides if this is narrower than its stated width.
var gaps = determineGaps(width, this.width)
Path(x, y).h(gaps[0]).addTo(this)
Path(x + gaps[0] + this.width, y)
.h(gaps[1])
.addTo(this)
x += gaps[0]
FakeSVG("rect", {
x: x,
y: y - 11,
width: this.width,
height: this.up + this.down
}).addTo(this)
var text = FakeSVG(
"text",
{
x: x + this.width / 2,
y: y + 4,
occurrenceIdx: this.occurrenceIdx,
topRuleName: this.topRuleName,
ruleName: this.ruleName
},
this.text
)
if (this.href) FakeSVG("a", { "xlink:href": this.href }, [text]).addTo(this)
else text.addTo(this)
return this
}
function Comment(text) {
if (!(this instanceof Comment)) return new Comment(text)
FakeSVG.call(this, "g")
this.text = text
this.width = text.length * 7 + 10
this.height = 0
this.offsetX = 0
this.up = 11
this.down = 11
}
subclassOf(Comment, FakeSVG)
Comment.prototype.needsSpace = true
Comment.prototype.format = function (x, y, width) {
// Hook up the two sides if this is narrower than its stated width.
var gaps = determineGaps(width, this.width)
Path(x, y).h(gaps[0]).addTo(this)
Path(x + gaps[0] + this.width, y + this.height)
.h(gaps[1])
.addTo(this)
x += gaps[0]
FakeSVG(
"text",
{
x: x + this.width / 2,
y: y + 5,
class: "comment"
},
this.text
).addTo(this)
return this
}
function Skip() {
if (!(this instanceof Skip)) return new Skip()
FakeSVG.call(this, "g")
this.width = 0
this.height = 0
this.offsetX = 0
this.up = 0
this.down = 0
}
subclassOf(Skip, FakeSVG)
Skip.prototype.format = function (x, y, width) {
Path(x, y).right(width).addTo(this)
return this
}
var root
if (typeof define === "function" && define.amd) {
// AMD. Register as an anonymous module.
root = {}
define([], function () {
return root
})
} else if (typeof exports === "object") {
// CommonJS for node
root = exports
} else {
// Browser globals (root is window.railroad)
this.railroad = {}
root = this.railroad
}
var temp = [
Diagram,
ComplexDiagram,
Sequence,
Stack,
Choice,
Optional,
OneOrMore,
ZeroOrMore,
Terminal,
NonTerminal,
Comment,
Skip
]
/*
These are the names that the internal classes are exported as.
If you would like different names, adjust them here.
*/
;[
"Diagram",
"ComplexDiagram",
"Sequence",
"Stack",
"Choice",
"Optional",
"OneOrMore",
"ZeroOrMore",
"Terminal",
"NonTerminal",
"Comment",
"Skip"
].forEach(function (e, i) {
root[e] = temp[i]
})
}.call(this, {
VERTICAL_SEPARATION: 8,
ARC_RADIUS: 10,
DIAGRAM_CLASS: "railroad-diagram",
STROKE_ODD_PIXEL_LENGTH: true,
INTERNAL_ALIGNMENT: "center"
}))