mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2024-11-23 10:07:19 +00:00
Introduce text/vnd.tiddlywiki.railroad
This commit is contained in:
parent
e5676d058a
commit
1591b43066
@ -236,9 +236,11 @@ var Root = function(content) {
|
||||
|
||||
Root.prototype = new Component();
|
||||
|
||||
Root.prototype.toSvg = function() {
|
||||
// Call Diagram(component1,component2,...)
|
||||
return railroad.Diagram.apply(null, this.getSvgOfChildren());
|
||||
Root.prototype.toSvg = function(options) {
|
||||
var args = this.getSvgOfChildren();
|
||||
args.unshift(options);
|
||||
// Call Diagram(options,component1,component2,...)
|
||||
return railroad.Diagram.apply(null,args);
|
||||
}
|
||||
|
||||
var Sequence = function(content) {
|
||||
@ -249,7 +251,7 @@ Sequence.prototype = new Component();
|
||||
|
||||
Sequence.prototype.toSvg = function() {
|
||||
// Call Sequence(component1,component2,...)
|
||||
return railroad.Sequence.apply(null, this.getSvgOfChildren());
|
||||
return railroad.Sequence.apply(null,this.getSvgOfChildren());
|
||||
}
|
||||
|
||||
var Choice = function(content,normal) {
|
||||
@ -266,7 +268,7 @@ Choice.prototype.toSvg = function() {
|
||||
// Call Choice(normal,component1,component2,...)
|
||||
var args = this.getSvgOfChildren();
|
||||
args.unshift(this.normal);
|
||||
return railroad.Choice.apply(null, args);
|
||||
return railroad.Choice.apply(null,args);
|
||||
}
|
||||
|
||||
/////////////////////////// Exports
|
||||
|
@ -1,9 +1,9 @@
|
||||
created: 20150103184022184
|
||||
modified: 20150103185522184
|
||||
modified: 20150119214125000
|
||||
tags:
|
||||
title: $:/plugins/tiddlywiki/railroad/example-source
|
||||
type: text/plain
|
||||
type: text/vnd.tiddlywiki.railroad
|
||||
|
||||
["+"]
|
||||
({ [[digit|GettingStarted]] } | "#" <'escape sequence'>)
|
||||
[{("@" name-char | :"--" )}]
|
||||
[{("@" name-char | :"--" )}]
|
||||
|
@ -1,16 +1,12 @@
|
||||
created: 20150102165032410
|
||||
modified: 20150102172010663
|
||||
modified: 20150119215720000
|
||||
tags:
|
||||
title: $:/plugins/tiddlywiki/railroad/example
|
||||
|
||||
<$railroad text={{$:/plugins/tiddlywiki/railroad/example-source}}/>
|
||||
Transcluding from [[$:/plugins/tiddlywiki/railroad/example-source]]:
|
||||
|
||||
```
|
||||
<$railroad text="""
|
||||
["+"]
|
||||
({ [[digit|GettingStarted]] } | "#" <'escape sequence'>)
|
||||
[{("@" name-char | :"--" )}]
|
||||
"""/>
|
||||
```
|
||||
{{$:/plugins/tiddlywiki/railroad/example-source}}
|
||||
|
||||
<$railroad mode="debug" text={{$:/plugins/tiddlywiki/railroad/example-source}}/>
|
||||
Debug mode:
|
||||
|
||||
<$railroad debug="yes" text={{$:/plugins/tiddlywiki/railroad/example-source}}/>
|
||||
|
@ -1,20 +1,20 @@
|
||||
created: 20150102163222184
|
||||
modified: 20150102172016663
|
||||
modified: 20150119220023000
|
||||
title: $:/plugins/tiddlywiki/railroad/readme
|
||||
|
||||
This plugin provides a `<$railroad>` widget for generating railroad syntax diagrams as SVG images. It is based on [[a library by Tab Atkins|https://github.com/tabatkins/railroad-diagrams]], and has been extended to allow components of a diagram to function as links.
|
||||
This plugin provides a `<$railroad>` widget for generating railroad diagrams as SVG images.
|
||||
|
||||
Alternatively, the [[diagram notation|$:/plugins/tiddlywiki/railroad/syntax]] can be stored in a dedicated tiddler with its `type` field set to `text/vnd.tiddlywiki.railroad`, and that tiddler can simply be transcluded to wherever it is needed.
|
||||
|
||||
The plugin is based on [[a library by Tab Atkins|https://github.com/tabatkins/railroad-diagrams]], and has been extended to make it more flexible, including allowing components of a diagram to function as links or be transcluded from other tiddlers.
|
||||
|
||||
The content of the `<$railroad>` widget is ignored.
|
||||
|
||||
|!Attribute |!Description |
|
||||
|text |Text in a special syntax that defines the diagram's layout |
|
||||
|arrow |If set to `no`, repeat paths do not have an arrow on them. The default is `yes` |
|
||||
|mode |If set to `debug`, the diagram will display its internal tree structure. The default mode is `svg` |
|
||||
|!Attribute |!Description |!Default |
|
||||
|text |Text in a special notation that defines the diagram's layout |-- |
|
||||
|arrow |If set to `no`, repeat paths do not have an arrow on them |`yes` |
|
||||
|start |Style of the startpoint: `single`, `double`, `none` |`single` |
|
||||
|end |Style of the endpoint: `single`, `double`, `none` |`single` |
|
||||
|debug |If set to `yes`, the diagram displays its parse tree |`no` |
|
||||
|
||||
The entire `text` can be transcluded from another tiddler:
|
||||
|
||||
```
|
||||
<$railroad tiddler={{diagram}}>
|
||||
```
|
||||
|
||||
Alternatively, the diagram syntax allows specific parts of the `text` to be transcluded from other tiddlers.
|
||||
These options can also be specified via pragmas in the diagram notation.
|
||||
|
@ -1,8 +1,8 @@
|
||||
created: 20150103184022184
|
||||
modified: 20150103184022184
|
||||
modified: 20150119220342000
|
||||
title: $:/plugins/tiddlywiki/railroad/syntax
|
||||
|
||||
The railroad widget uses a special ''diagram syntax'' to construct the components defined below.
|
||||
The railroad widget uses a special notation to construct the components defined below.
|
||||
|
||||
`x` and `y` here stand for any component.
|
||||
|
||||
@ -78,3 +78,22 @@ Names (as opposed to quoted strings) are available when a value starts with a le
|
||||
; transclusion
|
||||
: <$railroad text=""" "{{" (name|string) "}}" """/>
|
||||
* Treats the content of another tiddler as diagram syntax and transcludes it into the current diagram
|
||||
|
||||
---
|
||||
|
||||
; arrow pragma
|
||||
: <$railroad text=""" "\arrow" ("yes" | "no") """/>
|
||||
* Controls whether repeat paths have an arrow on them
|
||||
* Can be toggled on and off in mid-diagram, if desired
|
||||
|
||||
---
|
||||
|
||||
; debug pragma
|
||||
: <$railroad text=""" "\debug" """/>
|
||||
* Causes the diagram to display its parse tree
|
||||
|
||||
---
|
||||
|
||||
; start/end pragma
|
||||
: <$railroad text=""" ("\start" |: "\end") ("none" |: "single" | "double") """/>
|
||||
* Controls the style of the diagram's startpoint or endpoint
|
||||
|
@ -162,16 +162,17 @@ var temp = (function(options) {
|
||||
return this;
|
||||
}
|
||||
|
||||
function Diagram(items) {
|
||||
if(!(this instanceof Diagram)) return new Diagram([].slice.call(arguments));
|
||||
/* TiddlyWiki: added twOptions parameter, passing it to Start() and End() */
|
||||
function Diagram(twOptions, items) {
|
||||
if(!(this instanceof Diagram)) return new Diagram(twOptions, [].slice.call(arguments,1));
|
||||
FakeSVG.call(this, 'svg', {class: Diagram.DIAGRAM_CLASS});
|
||||
this.items = items.map(wrapString);
|
||||
this.items.unshift(new Start);
|
||||
this.items.push(new End);
|
||||
this.items.unshift(new Start(twOptions.start));
|
||||
this.items.push(new End(twOptions.end));
|
||||
this.width = this.items.reduce(function(sofar, el) { return sofar + el.width + (el.needsSpace?20:0)}, 0)+1;
|
||||
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;
|
||||
this.formatted = false;
|
||||
}
|
||||
subclassOf(Diagram, FakeSVG);
|
||||
for(var option in options) {
|
||||
@ -386,29 +387,47 @@ var temp = (function(options) {
|
||||
return Optional(OneOrMore(item, rep, wantArrow), skip);
|
||||
}
|
||||
|
||||
function Start() {
|
||||
if(!(this instanceof Start)) return new Start();
|
||||
/* TiddlyWiki: added type parameter */
|
||||
function Start(type) {
|
||||
if(!(this instanceof Start)) return new Start(type);
|
||||
FakeSVG.call(this, 'path');
|
||||
this.width = 20;
|
||||
this.type = type || 'single'
|
||||
this.width = (this.type === 'double') ? 20 : 10;
|
||||
this.up = 10;
|
||||
this.down = 10;
|
||||
}
|
||||
subclassOf(Start, FakeSVG);
|
||||
Start.prototype.format = function(x,y) {
|
||||
this.attrs.d = 'M '+x+' '+(y-10)+' v 20 m 10 -20 v 20 m -10 -10 h 20.5';
|
||||
/* TiddlyWiki: added types */
|
||||
if(this.type === 'single') {
|
||||
this.attrs.d = 'M '+x+' '+(y-10)+' v 20 m 0 -10 h 10.5';
|
||||
} else if(this.type === 'double') {
|
||||
this.attrs.d = 'M '+x+' '+(y-10)+' v 20 m 10 -20 v 20 m -10 -10 h 20.5';
|
||||
} else { // 'none'
|
||||
this.attrs.d = 'M '+x+' '+y+' h 10.5';
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
function End() {
|
||||
if(!(this instanceof End)) return new End();
|
||||
/* TiddlyWiki: added type parameter */
|
||||
function End(type) {
|
||||
if(!(this instanceof End)) return new End(type);
|
||||
FakeSVG.call(this, 'path');
|
||||
this.width = 20;
|
||||
this.type = type || 'double';
|
||||
this.width = (this.type === 'double') ? 20 : 10;
|
||||
this.up = 10;
|
||||
this.down = 10;
|
||||
}
|
||||
subclassOf(End, FakeSVG);
|
||||
End.prototype.format = function(x,y) {
|
||||
this.attrs.d = 'M '+x+' '+y+' h 20 m -10 -10 v 20 m 10 -20 v 20';
|
||||
/* TiddlyWiki: added types */
|
||||
if(this.type === 'single') {
|
||||
this.attrs.d = 'M '+x+' '+y+' h 10 m 0 -10 v 20';
|
||||
} else if(this.type === 'double') {
|
||||
this.attrs.d = 'M '+x+' '+y+' h 20 m -10 -10 v 20 m 10 -20 v 20';
|
||||
} else { // 'none'
|
||||
this.attrs.d = 'M '+x+' '+y+' h 10';
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,12 @@ x y z sequence
|
||||
|
||||
"x" can also be written 'x' or """x"""
|
||||
|
||||
pragmas:
|
||||
\arrow yes|no
|
||||
\debug yes|no
|
||||
\start single|double|none
|
||||
\end single|double|none
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
@ -35,10 +41,10 @@ x y z sequence
|
||||
|
||||
var components = require("$:/plugins/tiddlywiki/railroad/components.js").components;
|
||||
|
||||
var Parser = function(widget,source,wantArrow) {
|
||||
var Parser = function(widget,source,options) {
|
||||
this.widget = widget;
|
||||
this.source = source;
|
||||
this.wantArrow = wantArrow;
|
||||
this.options = options;
|
||||
this.tokens = this.tokenise(source);
|
||||
this.tokenPos = 0;
|
||||
this.advance();
|
||||
@ -57,7 +63,9 @@ Parser.prototype.parseContent = function() {
|
||||
if(!component) {
|
||||
break;
|
||||
}
|
||||
content.push(component);
|
||||
if(!component.isPragma) {
|
||||
content.push(component);
|
||||
}
|
||||
}
|
||||
return content;
|
||||
};
|
||||
@ -69,6 +77,8 @@ Parser.prototype.parseComponent = function() {
|
||||
component = this.parseTerminal();
|
||||
} else if(this.at("name")) {
|
||||
component = this.parseName();
|
||||
} else if(this.at("pragma")) {
|
||||
component = this.parsePragma();
|
||||
} else {
|
||||
switch(this.token.value) {
|
||||
case "[":
|
||||
@ -183,6 +193,7 @@ Parser.prototype.parseNonterminal = function() {
|
||||
};
|
||||
|
||||
Parser.prototype.parseOptional = function() {
|
||||
var wantArrow = this.options.arrow;
|
||||
// Consume the [
|
||||
this.advance();
|
||||
// Consume the { if there is one
|
||||
@ -202,11 +213,12 @@ Parser.prototype.parseOptional = function() {
|
||||
}
|
||||
this.close("]");
|
||||
// Create a component
|
||||
return repeated ? new components.OptionalRepeated(content,separator,normal,this.wantArrow)
|
||||
return repeated ? new components.OptionalRepeated(content,separator,normal,wantArrow)
|
||||
: new components.Optional(content,normal);
|
||||
};
|
||||
|
||||
Parser.prototype.parseRepeated = function() {
|
||||
var wantArrow = this.options.arrow;
|
||||
// Consume the {
|
||||
this.advance();
|
||||
// Parse the content
|
||||
@ -219,7 +231,7 @@ Parser.prototype.parseRepeated = function() {
|
||||
// Consume the closing bracket
|
||||
this.close("}");
|
||||
// Create a component
|
||||
return new components.Repeated(content,separator,this.wantArrow);
|
||||
return new components.Repeated(content,separator,wantArrow);
|
||||
};
|
||||
|
||||
Parser.prototype.parseSequence = function() {
|
||||
@ -255,6 +267,44 @@ Parser.prototype.parseTransclusion = function() {
|
||||
return new components.Transclusion(content);
|
||||
};
|
||||
|
||||
/////////////////////////// Pragmas
|
||||
|
||||
Parser.prototype.parsePragma = function() {
|
||||
// Create a dummy component
|
||||
var component = { isPragma: true };
|
||||
// Consume the pragma
|
||||
var pragma = this.token.value;
|
||||
this.advance();
|
||||
// Apply the setting
|
||||
if(pragma === "arrow") {
|
||||
this.options.arrow = this.parseYesNo(pragma);
|
||||
} else if(pragma === "debug") {
|
||||
this.options.debug = true;
|
||||
} else if(pragma === "start") {
|
||||
this.options.start = this.parseTerminusStyle(pragma);
|
||||
} else if(pragma === "end") {
|
||||
this.options.end = this.parseTerminusStyle(pragma);
|
||||
} else {
|
||||
throw "Invalid pragma";
|
||||
}
|
||||
return component;
|
||||
};
|
||||
|
||||
Parser.prototype.parseYesNo = function(pragma) {
|
||||
return this.parseSetting(["yes","no"],pragma) === "yes";
|
||||
}
|
||||
|
||||
Parser.prototype.parseTerminusStyle = function(pragma) {
|
||||
return this.parseSetting(["single","double","none"],pragma);
|
||||
}
|
||||
|
||||
Parser.prototype.parseSetting = function(options,pragma) {
|
||||
if(this.at("name") && options.indexOf(this.token.value) !== -1) {
|
||||
return this.tokenValueEaten();
|
||||
}
|
||||
throw options.join(" or ") + " expected after \\" + pragma;
|
||||
}
|
||||
|
||||
/////////////////////////// Token manipulation
|
||||
|
||||
Parser.prototype.advance = function() {
|
||||
@ -276,7 +326,7 @@ Parser.prototype.eat = function(token) {
|
||||
return at;
|
||||
};
|
||||
|
||||
Parser.prototype.tokenValue = function() {
|
||||
Parser.prototype.tokenValueEaten = function() {
|
||||
var output = this.token.value;
|
||||
this.advance();
|
||||
return output;
|
||||
@ -305,12 +355,12 @@ Parser.prototype.expectString = function(context,token) {
|
||||
token = token || "String";
|
||||
throw token + " expected " + context;
|
||||
}
|
||||
return this.tokenValue();
|
||||
return this.tokenValueEaten();
|
||||
};
|
||||
|
||||
Parser.prototype.expectNameOrString = function(context) {
|
||||
if(this.at("name")) {
|
||||
return this.tokenValue();
|
||||
return this.tokenValueEaten();
|
||||
}
|
||||
return this.expectString(context,"Name or string");
|
||||
};
|
||||
@ -353,6 +403,9 @@ Parser.prototype.tokenise = function(source) {
|
||||
} else if(c.match(/[a-zA-Z]/)) {
|
||||
// Name
|
||||
token = this.readName(source,pos);
|
||||
} else if(c.match(/\\/)) {
|
||||
// Pragma
|
||||
token = this.readPragma(source,pos);
|
||||
} else {
|
||||
throw "Syntax error at " + c;
|
||||
}
|
||||
@ -380,6 +433,18 @@ Parser.prototype.readName = function(source,pos) {
|
||||
}
|
||||
};
|
||||
|
||||
Parser.prototype.readPragma = function(source,pos) {
|
||||
var re = /([a-z]+)/g;
|
||||
pos++;
|
||||
re.lastIndex = pos;
|
||||
var match = re.exec(source);
|
||||
if(match && match.index === pos) {
|
||||
return {type: "pragma", value: match[1], start: pos, end: pos+match[1].length};
|
||||
} else {
|
||||
throw "Invalid pragma";
|
||||
}
|
||||
};
|
||||
|
||||
/////////////////////////// Exports
|
||||
|
||||
exports.parser = Parser;
|
||||
|
@ -33,18 +33,21 @@ RailroadWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
// Get the source text
|
||||
console.log('getAttribute(text)', this.getAttribute("text", 'not found'));
|
||||
$tw.utils.each(this.parseTreeNode,function(element,title,object) {
|
||||
console.log(':', element, title);
|
||||
});
|
||||
var source = this.getAttribute("text",this.parseTreeNode.text || "");
|
||||
// Create a div to contain the SVG or error message
|
||||
var div = this.document.createElement("div");
|
||||
try {
|
||||
// Initialise options from widget attributes
|
||||
var options = {
|
||||
arrow: this.getAttribute("arrow","yes") === "yes",
|
||||
debug: this.getAttribute("debug","no") === "yes",
|
||||
start: this.getAttribute("start","single"),
|
||||
end: this.getAttribute("end","single")
|
||||
};
|
||||
// Parse the source
|
||||
var parser = new Parser(this,source,this.getAttribute("arrow","yes") === "yes");
|
||||
var parser = new Parser(this,source,options);
|
||||
// Generate content into the div
|
||||
if(this.getAttribute("mode","svg") === "debug") {
|
||||
if(parser.options.debug) {
|
||||
this.renderDebug(parser,div);
|
||||
} else {
|
||||
this.renderSvg(parser,div);
|
||||
@ -67,7 +70,7 @@ RailroadWidget.prototype.renderDebug = function(parser,div) {
|
||||
|
||||
RailroadWidget.prototype.renderSvg = function(parser,div) {
|
||||
// Generate a model of the diagram
|
||||
var fakeSvg = parser.root.toSvg();
|
||||
var fakeSvg = parser.root.toSvg(parser.options);
|
||||
// Render the model into a tree of SVG DOM nodes
|
||||
var svg = fakeSvg.toSVG();
|
||||
// Fill in the remaining attributes of any link nodes
|
||||
@ -111,7 +114,7 @@ RailroadWidget.prototype.patchLinks = function(node) {
|
||||
|
||||
RailroadWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if(changedAttributes.text || changedAttributes.mode || changedAttributes.arrow) {
|
||||
if(changedAttributes.text) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user