Skip to content

Commit

Permalink
A template html to generate diagrams for grammars.
Browse files Browse the repository at this point in the history
See included diagrams/readme.md for details.

fixes #138
  • Loading branch information
Shahar Soel committed Mar 12, 2016
1 parent 8bea15d commit 0c9817a
Show file tree
Hide file tree
Showing 13 changed files with 1,397 additions and 5 deletions.
1 change: 1 addition & 0 deletions bower.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"ignore": [
"**/*",
"!release/*.*",
"!diagrams/*.*",
"!readme.md",
"!LICENSE.txt"
],
Expand Down
85 changes: 85 additions & 0 deletions diagrams/diagrams.css
Original file line number Diff line number Diff line change
@@ -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;
}
49 changes: 49 additions & 0 deletions diagrams/diagrams.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
background-color: hsl(30, 20%, 95%)
}
</style>

<!-- references when running the sample html directly-->
<link rel='stylesheet' href='diagrams.css'>
<script src='vendor/railroad-diagrams.js'></script>
<script src='../bin/chevrotain.js'></script>
<script src='src/diagrams_builder.js'></script>
<script src='src/diagrams_behavior.js'></script>
<script src='src/main.js'></script>


<!-- references when using npm and chevrotain is installed node_modules/chevrotain-->
<!--<link rel='stylesheet' href='node_modules/chevrotain/diagrams/diagrams.css'>-->
<!--<script src='node_modules/chevrotain/diagrams/vendor/railroad-diagrams.js'></script>-->
<!--<script src='node_modules/chevrotain/bin/chevrotain.js'></script>-->
<!--<script src='node_modules/chevrotain/diagrams/src/diagrams_builder.js'></script>-->
<!--<script src='node_modules/chevrotain/diagrams/src/diagrams_behavior.js'></script>-->
<!--<script src='node_modules/chevrotain/diagrams/src/main.js'></script>-->


<!-- references when using bower and chevrotain is installed in bower_components/chevrotain-->
<!--<link rel='stylesheet' href='bower_components/chevrotain/diagrams/diagrams.css'>-->
<!--<script src='bower_components/chevrotain/diagrams/vendor/railroad-diagrams.js'></script>-->
<!--<script src='bower_components/chevrotain/release/chevrotain.js'></script>-->
<!--<script src='bower_components/chevrotain/diagrams/src/diagrams_builder.js'></script>-->
<!--<script src='bower_components/chevrotain/diagrams/src/diagrams_behavior.js'></script>-->
<!--<script src='bower_components/chevrotain/diagrams/src/main.js'></script>-->

<body>
<div id="diagrams" align="center"></div>

<!-- TODO: The DUMMY_SAMPLE script should be replaced with your custom grammar script.-->
<script src='sample/DUMMY_SAMPLE.js'></script>

<script>
// TODO: replace DUMMY_SAMPLE_PARSER initialization with your parser implementation
var parserInstanceToDraw = new DUMMY_SAMPLE_PARSER([]);

var diagramsDiv = document.getElementById("diagrams");

main.drawDiagramsFromParserInstance(parserInstanceToDraw, diagramsDiv)
</script>
</body>
50 changes: 50 additions & 0 deletions diagrams/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
## Template for Generating Syntax Diagrams for a Grammar.

It is often useful to visually inspect a grammar's syntax diagrams during development
or for documentation purposes.

This folder contains a template which can be easily modified to render and display
A grammar as railroad syntax diagrams using the [railroad-diagrams](https://github.com/tabatkins/railroad-diagrams)
library by @tabatkins.

An example of the railroad diagrams can be found on the chevrotain [Playground](http://sap.github.io/chevrotain/playground/).


### Features:
* Highlight usages and definitions on mouse hover.
* Scroll to definition of non-terminal on mouse click.


### Usage Instructions:
The template will runs as is, but it will render a sample grammar instead of your custom grammar.

There are **only** three steps needed to render a custom grammar:
1. Copy diagrams.html into a source controlled folder (root of your project is recommended).
2. Modify the references to the resources in **root/diagrams.html** so they will still be valid.
* For example, assuming chevrotain is installed to **node_modules/chevrotain**:
```<script src='src/diagrams_builder.js'></script>``` should be change to:
```<script src='node_modules/chevrotain/diagrams/src/diagrams_builder.js'></script>```
* For convenience the diagrams.html contains three blocks of references, comment/uncomment the relevant one for your needs.
* Modifying the references is not mandatory, instead the whole diagrams directory may be copied.
However this will automatic updates to those resources(npm update/bower update)
3. Replace the two references to **DUMMY_SAMPLE** with script tags/logic that will load and initialize an instance of
the custom Parser whose grammar should be rendered.

[Example](https://github.com/SAP/chevrotain/blob/master/examples/typescript_ecma5/ecma5_diagrams.html) of a modified html with a custom grammar.
Setup instructions to run that example can be found [here](https://github.com/SAP/chevrotain/blob/master/examples/typescript_ecma5/README.md).


#### What about grammars written with commonjs (node.js) modules.
Because The diagrams are rendered in a browser, it's implementation must be runnable in a browser.
This means the commonjs code must be wrapped / transformed to be browser compatible.
Some options to accomplish this:
* [UMD](https://github.com/umdjs/umd)
* [browserify](http://browserify.org/)
* [webpack](https://webpack.github.io/)
* [systemjs](https://github.com/systemjs/systemjs)


#### What about grammars written with AMD (require.js) modules.
All the sources used in the template are wrapped using the [UMD](https://github.com/umdjs/umd) pattern.
Thus they are compatible with AMD modules. In such a case the html can be modified to load require.js and perform
the grammar rendering in the require.js **data-main** script.
98 changes: 98 additions & 0 deletions diagrams/sample/DUMMY_SAMPLE.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// TODO: umd ?

// ----------------- lexer -----------------
var extendToken = chevrotain.extendToken;
var Lexer = chevrotain.Lexer;
var Parser = chevrotain.Parser;

// In ES6, custom inheritance implementation (such as 'extendToken(...)') can be replaced with simple "class X extends Y"...
var True = extendToken("True", /true/);
var False = extendToken("False", /false/);
var Null = extendToken("Null", /null/);
var LCurly = extendToken("LCurly", /{/);
var RCurly = extendToken("RCurly", /}/);
var LSquare = extendToken("LSquare", /\[/);
var RSquare = extendToken("RSquare", /]/);
var Comma = extendToken("Comma", /,/);
var Colon = extendToken("Colon", /:/);
var StringLiteral = extendToken("StringLiteral", /"(?:[^\\"]+|\\(?:[bfnrtv"\\/]|u[0-9a-fA-F]{4}))*"/);
var NumberLiteral = extendToken("NumberLiteral", /-?(0|[1-9]\d*)(\.\d+)?([eE][+-]?\d+)?/);
var WhiteSpace = extendToken("WhiteSpace", /\s+/);
WhiteSpace.GROUP = Lexer.SKIPPED; // marking WhiteSpace as 'SKIPPED' makes the lexer skip it.

var allTokens = [WhiteSpace, NumberLiteral, StringLiteral, LCurly, RCurly, LSquare, RSquare, Comma, Colon, True, False, Null];
var JsonLexer = new Lexer(allTokens);


// ----------------- parser -----------------

function DUMMY_SAMPLE_PARSER(input) {
// invoke super constructor
Parser.call(this, input, allTokens);

// not mandatory, using <$> (or any other sign) to reduce verbosity (this. this. this. this. .......)
var $ = this;

this.json = this.RULE("json", function () {
// @formatter:off
$.OR([
{ ALT: function () { $.SUBRULE($.object) }},
{ ALT: function () { $.SUBRULE($.array) }}
]);
// @formatter:on
});

this.object = this.RULE("object", function () {
$.CONSUME(LCurly);
$.OPTION(function () {
$.SUBRULE($.objectItem);
$.MANY(function () {
$.CONSUME(Comma);
$.SUBRULE2($.objectItem);
});
});
$.CONSUME(RCurly);
});

this.objectItem = this.RULE("objectItem", function () {
$.CONSUME(StringLiteral);
$.CONSUME(Colon);
$.SUBRULE($.value);
});

this.array = this.RULE("array", function () {
$.CONSUME(LSquare);
$.OPTION(function () {
$.SUBRULE($.value);
$.MANY(function () {
$.CONSUME(Comma);
$.SUBRULE2($.value);
});
});
$.CONSUME(RSquare);
});

// @formatter:off
this.value = this.RULE("value", function () {
$.OR([
{ ALT: function () { $.CONSUME(StringLiteral) }},
{ ALT: function () { $.CONSUME(NumberLiteral) }},
{ ALT: function () { $.SUBRULE($.object) }},
{ ALT: function () { $.SUBRULE($.array) }},
{ ALT: function () { $.CONSUME(True) }},
{ ALT: function () { $.CONSUME(False) }},
{ ALT: function () { $.CONSUME(Null) }}
], "a value");
});
// @formatter:on

// very important to call this after all the rules have been defined.
// otherwise the parser may not work correctly as it will lack information
// derived during the self analysis phase.
Parser.performSelfAnalysis(this);
}

// inheritance as implemented in javascript in the previous decade... :(
DUMMY_SAMPLE_PARSER.prototype = Object.create(Parser.prototype);
DUMMY_SAMPLE_PARSER.prototype.constructor = DUMMY_SAMPLE_PARSER;

Loading

0 comments on commit 0c9817a

Please sign in to comment.