Import source code for pegjs.org
This commit is contained in:
parent
7c93ffb22b
commit
dfbfdd350b
|
@ -3,3 +3,4 @@
|
|||
/coverage
|
||||
/node_modules
|
||||
/test/server/vendor
|
||||
/website
|
||||
|
|
|
@ -45,7 +45,8 @@ task( "lint", () => pump(
|
|||
"test/spec/**/*.js",
|
||||
"test/server/run",
|
||||
"src/*.js",
|
||||
"gulpfile.js"
|
||||
"gulpfile.js",
|
||||
"server.js",
|
||||
] ),
|
||||
eslint( { dotfiles: true } ),
|
||||
eslint.format(),
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
"build:parser": "gulp build:parser",
|
||||
"build:browser": "gulp build:browser",
|
||||
"clean": "gulp clean",
|
||||
"start": "node server.js",
|
||||
"test:impact": "node test/impact master",
|
||||
"test:server": "node test/server/run",
|
||||
"test": "nyc gulp",
|
||||
|
@ -42,14 +43,17 @@
|
|||
"@babel/core": "7.0.0",
|
||||
"@babel/preset-env": "7.0.0",
|
||||
"babelify": "10.0.0-beta.1",
|
||||
"body-parser": "1.18.3",
|
||||
"browserify": "16.2.2",
|
||||
"chai": "4.1.2",
|
||||
"chai-like": "1.1.1",
|
||||
"coveralls": "3.0.2",
|
||||
"dedent": "0.7.0",
|
||||
"del": "3.0.0",
|
||||
"ejs": "2.6.1",
|
||||
"eslint-config-futagozaryuu": "5",
|
||||
"express": "4.16.3",
|
||||
"express-layout": "0.1.0",
|
||||
"glob": "7.1.3",
|
||||
"gulp": "4.0.0",
|
||||
"gulp-eslint": "5.0.0",
|
||||
|
|
81
server.js
Normal file
81
server.js
Normal file
|
@ -0,0 +1,81 @@
|
|||
"use strict";
|
||||
|
||||
const bodyParser = require( "body-parser" );
|
||||
const express = require( "express" );
|
||||
const layout = require( "express-layout" );
|
||||
const logger = require( "morgan" );
|
||||
const { join } = require( "path" );
|
||||
|
||||
/* Setup */
|
||||
|
||||
const app = express();
|
||||
|
||||
app.set( "views", join( __dirname, "website", "views" ) );
|
||||
app.set( "view engine", "ejs" );
|
||||
|
||||
app.use( logger( "dev" ) );
|
||||
app.use( express.static( join( __dirname, "website" ) ) );
|
||||
|
||||
app.use( layout() );
|
||||
app.use( ( req, res, next ) => {
|
||||
|
||||
res.locals.req = req;
|
||||
next();
|
||||
|
||||
} );
|
||||
|
||||
app.locals.menuItem = ( req, id, title ) => {
|
||||
|
||||
const className = req.path === "/" + id ? " class=\"current\"" : "";
|
||||
|
||||
return `<a ${ className } href="/${ id }">${ title }</a>`;
|
||||
|
||||
};
|
||||
|
||||
/* Routes */
|
||||
|
||||
app.get( "/", ( req, res ) => {
|
||||
|
||||
res.render( "index", { title: "" } );
|
||||
|
||||
} );
|
||||
|
||||
app.get( "/online", ( req, res ) => {
|
||||
|
||||
res.render( "online", { title: "Online version", layout: "layout-online" } );
|
||||
|
||||
} );
|
||||
|
||||
app.post( "/online/download", bodyParser.urlencoded( { extended: false, limit: "1024kb" } ), ( req, res ) => {
|
||||
|
||||
res.set( "Content-Type", "application/javascript" );
|
||||
res.set( "Content-Disposition", "attachment;filename=parser.js" );
|
||||
res.send( req.body.source );
|
||||
|
||||
} );
|
||||
|
||||
app.get( "/documentation", ( req, res ) => {
|
||||
|
||||
res.render( "documentation", { title: "Documentation" } );
|
||||
|
||||
} );
|
||||
|
||||
app.get( "/development", ( req, res ) => {
|
||||
|
||||
res.render( "development", { title: "Development" } );
|
||||
|
||||
} );
|
||||
|
||||
app.get( "/download", ( req, res ) => {
|
||||
|
||||
res.redirect( 301, "/#download" );
|
||||
|
||||
} );
|
||||
|
||||
/* Main */
|
||||
|
||||
app.listen( 3000, () => {
|
||||
|
||||
console.log( "PEG.js website running at http://localhost:3000/ in %s mode...", app.get( "env" ) );
|
||||
|
||||
} );
|
8
website/css/common.css
Normal file
8
website/css/common.css
Normal file
|
@ -0,0 +1,8 @@
|
|||
body {
|
||||
margin: 0;
|
||||
font-family: "Trebuchet MS", lucida, sans-serif;
|
||||
line-height: 1.5;
|
||||
color: black; background-color: white;
|
||||
}
|
||||
aside, footer, header, nav { display: block; } /* HTML5 */
|
||||
a img { border: none; }
|
151
website/css/content.css
Normal file
151
website/css/content.css
Normal file
|
@ -0,0 +1,151 @@
|
|||
#content h1 { margin: 0 0 .75em 0; font-size: 200%; }
|
||||
#content h2 {
|
||||
margin: 1.5em 0 .75em 0; border-bottom: 2pt dotted silver;
|
||||
font-size: 150%;
|
||||
}
|
||||
#content h3 { margin: 1.5em 0 .5em 0; font-size: 125%; }
|
||||
#content li { margin: .5em 0; }
|
||||
#content dt { font-weight: bold; }
|
||||
#content dd { margin-top: 1em; margin-bottom: 1em; }
|
||||
#content aside.info { margin: 1em 0 1em 2em; color: gray; }
|
||||
#content pre {
|
||||
overflow: auto;
|
||||
padding: .5em 1em; border-left: 5px solid silver;
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
#content table { border-spacing: 0; }
|
||||
#content a, #content a:visited { color: #3d586c; }
|
||||
|
||||
#content .center { text-align: center; }
|
||||
|
||||
/* Home */
|
||||
|
||||
#content #sidebar {
|
||||
float: right; width: 17em;
|
||||
font-size: 80%; text-align: center;
|
||||
}
|
||||
#content #sidebar a {
|
||||
font-weight: bold; text-decoration: none;
|
||||
color: #006000;
|
||||
}
|
||||
#content #sidebar a:hover { text-decoration: underline; }
|
||||
#content #sidebar a.try {
|
||||
display: block;
|
||||
padding: .75em; border-radius: .6em; -moz-border-radius: .6em;
|
||||
font-size: 140%;
|
||||
color: #e0ffe0; background-color: #499149;
|
||||
}
|
||||
#content #sidebar a.try:hover {
|
||||
text-decoration: none; background-color: #006000;
|
||||
}
|
||||
#content #sidebar .install {
|
||||
padding: .75em; border: 1px solid #499149; border-radius: .7em; -moz-border-radius: .7em;
|
||||
font-family: "Lucida Console", fixed, monospace; font-size: 120%;
|
||||
color: #2c572c; background-color: #e0ffe0;
|
||||
}
|
||||
#content #sidebar .label { margin-left: 2.6em; text-align: left; color: #606060; }
|
||||
#content #sidebar #download { color: gray; font-size: 120%; }
|
||||
#content #sidebar .separator { color: gray; margin: 1.5em 0 1em 0; }
|
||||
|
||||
#content #left-column { margin-right: 17em; }
|
||||
|
||||
/* Online Version */
|
||||
|
||||
#content .message {
|
||||
border-radius: .5em; -moz-border-radius: .5em; padding: .5em 1em;
|
||||
}
|
||||
#content .message.info { background-color: #c0ffc0; }
|
||||
#content .message.info a.download { display: block; float: right; }
|
||||
#content .message.info .size-and-time { visibility: hidden; float: right; font-size: 70%; margin: .3em 0; color: #80c080; }
|
||||
#content .message.info:hover .size-and-time { visibility: visible; }
|
||||
#content .message.error { background-color: orange; }
|
||||
#content .message.progress {
|
||||
padding-left: 40px;
|
||||
/* Spinner image generated by http://www.loadinfo.net/. */
|
||||
background: #ffff80 url("../img/spinner-16x16-progress.gif") 14px center no-repeat;
|
||||
}
|
||||
#content .message.disabled { color: gray; background-color: #f0f0f0; }
|
||||
|
||||
#content table.online { table-layout: fixed; };
|
||||
|
||||
#content table.form { width: 100%; }
|
||||
#content table.form td, table.form th { padding: .5em 1em; }
|
||||
#content table.form td:first-child, table.form th:first-child { padding-left: 0; }
|
||||
#content table.form td:last-child, table.form th:last-child { padding-right: 0; }
|
||||
#content table.form th { text-align: left; font-weight: normal; }
|
||||
|
||||
#content h2.suggestion { border: none; }
|
||||
#content h2.suggestion.top { margin-top: 0; }
|
||||
#content h2.suggestion .step-number {
|
||||
display: block; float: left;
|
||||
width: 1.5em;
|
||||
border-radius: .4em; -moz-border-radius: .4em;
|
||||
text-align: center;
|
||||
color: white; background-color: black;
|
||||
}
|
||||
#content h2.suggestion .step-title { margin-left: 2.5em; }
|
||||
|
||||
#content textarea.code {
|
||||
width: 100%; height: 20em;
|
||||
border: 1px solid gray;
|
||||
font-family: "Lucida Console", fixed, monospace;
|
||||
min-height: 150pt;
|
||||
}
|
||||
|
||||
#content .textarea-wrapper { padding-right: 6px; }
|
||||
|
||||
#content #columns { width: 100%; height: 100%; border-spacing: 1em; }
|
||||
#content #columns td { width: 50%; }
|
||||
|
||||
#content table.column { width: 100%; height: 100%; }
|
||||
#content table.column td { vertical-align: top; }
|
||||
/* Browsers will enlarge the |.content-height| cells to fit the contents. */
|
||||
#content table.column td.content-height { height: 1px; }
|
||||
|
||||
#content .CodeMirror {
|
||||
border: 1px solid gray;
|
||||
}
|
||||
|
||||
#content .CodeMirror pre {
|
||||
overflow: visible;
|
||||
padding: 0px 0px 0px 2px; border: none;
|
||||
background-color: inherit;
|
||||
}
|
||||
|
||||
#content #output-header {
|
||||
margin: 1.25em 0 0 0; border: none; padding: .25em 1.2em .25em 1.2em;
|
||||
font-size: 80%;
|
||||
color: white; background-color: silver;
|
||||
}
|
||||
#content #output {
|
||||
overflow: auto;
|
||||
max-height: 20em;
|
||||
margin: 0; padding: .5em 1em; border: 2px solid silver; border-top: none;
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
#content #output.disabled { color: gray; }
|
||||
|
||||
#content #settings { padding: .5em 0; }
|
||||
#content #settings label { padding-right: 1em; }
|
||||
#content #settings label[for=option-optimize] { padding-left: 2em; }
|
||||
#content #parser-var { width: 15em; }
|
||||
#content #options { padding-top: 1em; }
|
||||
#content #parser-download {
|
||||
float: right;
|
||||
width: 10em;
|
||||
margin-top: 2em;
|
||||
border-width: 0;
|
||||
padding: .5em; border-radius: .4em; -moz-border-radius: .4em;
|
||||
font-family: "Trebuchet MS", lucida, sans-serif;
|
||||
font-size: 1em;
|
||||
line-height: 1.5;
|
||||
text-align: center; text-decoration: none;
|
||||
color: #e0ffe0; background-color: #499149;
|
||||
}
|
||||
#content #parser-download:hover { background-color: #006000; }
|
||||
#content #parser-download.disabled { color: #e0e0e0; background-color: gray; }
|
||||
|
||||
|
||||
#output {
|
||||
max-width: 50vw;
|
||||
}
|
45
website/css/layout-default.css
Normal file
45
website/css/layout-default.css
Normal file
|
@ -0,0 +1,45 @@
|
|||
#header { margin: 2em 0; text-align: center; }
|
||||
#header h1 {
|
||||
margin: 0; padding: .05em 0;
|
||||
border-top-left-radius: .2em; -moz-border-radius-topleft: .2em;
|
||||
border-top-right-radius: .2em; -moz-border-radius-topright: .2em;
|
||||
font-size: 400%; text-shadow: black 0 0 5px;
|
||||
background-color: #234d6b; color: white;
|
||||
}
|
||||
#header h1 a { display: block; text-decoration: none; color: white; }
|
||||
#header h2 {
|
||||
margin: 0; padding: .25em 0;
|
||||
border-bottom-left-radius: .666em; -moz-border-radius-bottomleft: .666em;
|
||||
border-bottom-right-radius: .666em; -moz-border-radius-bottomright: .666em;
|
||||
font-size: 120%; text-shadow: black 0 0 2px;
|
||||
background-color: #2c5f85; color: white;
|
||||
}
|
||||
#main { width: 50em; margin: 0 auto; }
|
||||
|
||||
#menu {
|
||||
border-radius: .8em; -moz-border-radius: .8em; padding: .5em 1em;
|
||||
text-align: center;
|
||||
background-color: #e5ecf2;
|
||||
}
|
||||
#menu a, #menu a:visited {
|
||||
padding: .2em 1em;
|
||||
text-decoration: none;
|
||||
color: #3d586c;
|
||||
}
|
||||
#menu a.current {
|
||||
border-radius: .4em; -moz-border-radius: .4em;
|
||||
color: #e5ecf2; background-color: #8da6b8;
|
||||
}
|
||||
#menu a:hover {
|
||||
border-radius: .4em; -moz-border-radius: .4em;
|
||||
color: #e5ecf2; background-color: #3d586c;
|
||||
}
|
||||
|
||||
#content { margin: 2em 0 4em 0; }
|
||||
|
||||
#footer {
|
||||
margin-bottom: 2em;
|
||||
text-align: center; font-size: 75%;
|
||||
color: silver;
|
||||
}
|
||||
#footer a, #footer a:visited { color: silver; }
|
60
website/css/layout-online.css
Normal file
60
website/css/layout-online.css
Normal file
|
@ -0,0 +1,60 @@
|
|||
#header {
|
||||
position: absolute;
|
||||
top: 1em; left: 1em;
|
||||
}
|
||||
#header h1 {
|
||||
float: left;
|
||||
width: 3em;
|
||||
margin: 0; padding: .5em 1.5em;
|
||||
border-top-left-radius: .5em; -moz-border-radius-topleft: .5em;
|
||||
border-bottom-left-radius: .5em; -moz-border-radius-bottomleft: .5em;
|
||||
font-size: 100%; text-shadow: black 0 0 1.25px;
|
||||
text-align: center;
|
||||
background-color: #234d6b; color: white;
|
||||
}
|
||||
#header h1 a {
|
||||
display: block;
|
||||
margin: -.5em -1.5em; padding: .5em 1.5em;
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
}
|
||||
#header h2 {
|
||||
margin: 0 0 0 6em; padding: .5em 1.5em;
|
||||
border-top-right-radius: .5em; -moz-border-radius-topright: .5em;
|
||||
border-bottom-right-radius: .5em; -moz-border-radius-bottomright: .5em;
|
||||
font-size: 100%; text-shadow: black 0 0 1.25px;
|
||||
text-align: center;
|
||||
background-color: #2c5f85; color: white;
|
||||
}
|
||||
|
||||
#menu {
|
||||
position: absolute;
|
||||
top: 1em; right: 1em;
|
||||
border-radius: .5em; -moz-border-radius: .5em; padding: .5em 1em;
|
||||
text-align: center;
|
||||
background-color: #e5ecf2;
|
||||
}
|
||||
#menu a, #menu a:visited {
|
||||
padding: .2em 1em;
|
||||
text-decoration: none;
|
||||
color: #3d586c;
|
||||
}
|
||||
#menu a.current {
|
||||
border-radius: .4em; -moz-border-radius: .4em;
|
||||
color: #e5ecf2; background-color: #8da6b8;
|
||||
}
|
||||
#menu a:hover {
|
||||
border-radius: .4em; -moz-border-radius: .4em;
|
||||
color: #e5ecf2; background-color: #3d586c;
|
||||
}
|
||||
|
||||
#loader { position: absolute; top: 45%; left: 0; right: 0; }
|
||||
#loader-inner {
|
||||
width: 4.5em;
|
||||
margin: auto; padding-left: 64px;
|
||||
font-size: 250%;
|
||||
/* Spinner image generated by http://www.loadinfo.net/. */
|
||||
background: url("../img/spinner-48x48.gif") left center no-repeat;
|
||||
}
|
||||
|
||||
#content { display: none; position: absolute; top: 4em; left: 0; right: 0; bottom: 0; }
|
BIN
website/favicon.ico
Normal file
BIN
website/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 318 B |
BIN
website/img/rule.png
Normal file
BIN
website/img/rule.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
BIN
website/img/spinner-16x16-progress.gif
Normal file
BIN
website/img/spinner-16x16-progress.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.2 KiB |
BIN
website/img/spinner-48x48.gif
Normal file
BIN
website/img/spinner-48x48.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.2 KiB |
215
website/js/online.js
Normal file
215
website/js/online.js
Normal file
|
@ -0,0 +1,215 @@
|
|||
$(document).ready(function() {
|
||||
var KB = 1024;
|
||||
var MS_IN_S = 1000;
|
||||
|
||||
var parser;
|
||||
|
||||
var buildAndParseTimer = null;
|
||||
var parseTimer = null;
|
||||
|
||||
var oldGrammar = null;
|
||||
var oldParserVar = null;
|
||||
var oldOptionCache = null;
|
||||
var oldOptionOptimize = null;
|
||||
var oldInput = null;
|
||||
|
||||
var editor = CodeMirror.fromTextArea($("#grammar").get(0), {
|
||||
lineNumbers: true,
|
||||
mode: "pegjs"
|
||||
});
|
||||
|
||||
function buildSizeAndTimeInfoHtml(title, size, time) {
|
||||
return $("<span/>", {
|
||||
"class": "size-and-time",
|
||||
title: title,
|
||||
html: (size / KB).toPrecision(2) + " kB, "
|
||||
+ time + " ms, "
|
||||
+ ((size / KB) / (time / MS_IN_S)).toPrecision(2) + " kB/s"
|
||||
});
|
||||
}
|
||||
|
||||
function buildErrorMessage(e) {
|
||||
return e.location !== undefined
|
||||
? "Line " + e.location.start.line + ", column " + e.location.start.column + ": " + e.message
|
||||
: e.message;
|
||||
}
|
||||
|
||||
function build() {
|
||||
oldGrammar = getGrammar();
|
||||
oldParserVar = $("#parser-var").val();
|
||||
oldOptionCache = $("#option-cache").is(":checked");
|
||||
oldOptionOptimize = $("#option-optimize").val();
|
||||
|
||||
$('#build-message').attr("class", "message progress").text("Building the parser...");
|
||||
$("#input").attr("disabled", "disabled");
|
||||
$("#parse-message").attr("class", "message disabled").text("Parser not available.");
|
||||
$("#output").addClass("disabled").text("Output not available.");
|
||||
$("#parser-var").attr("disabled", "disabled");
|
||||
$("#option-cache").attr("disabled", "disabled");
|
||||
$("#option-optimize").attr("disabled", "disabled");
|
||||
$("#parser-download").attr("disabled", "disabled");
|
||||
|
||||
try {
|
||||
var timeBefore = (new Date).getTime();
|
||||
var parserSource = peg.generate(getGrammar(), {
|
||||
cache: $("#option-cache").is(":checked"),
|
||||
optimize: $("#option-optimize").val(),
|
||||
output: "source"
|
||||
});
|
||||
var timeAfter = (new Date).getTime();
|
||||
|
||||
parser = eval(parserSource);
|
||||
|
||||
$("#build-message")
|
||||
.attr("class", "message info")
|
||||
.html("Parser built successfully.")
|
||||
.append(buildSizeAndTimeInfoHtml(
|
||||
"Parser build time and speed",
|
||||
getGrammar().length,
|
||||
timeAfter - timeBefore
|
||||
));
|
||||
$("#input").removeAttr("disabled");
|
||||
$("#parser-source").val($("#parser-var").val() + " = " + parserSource + ";\n");
|
||||
$("#parser-var").removeAttr("disabled");
|
||||
$("#option-cache").removeAttr("disabled");
|
||||
$("#option-optimize").removeAttr("disabled");
|
||||
$("#parser-download").removeAttr("disabled");
|
||||
|
||||
var result = true;
|
||||
} catch (e) {
|
||||
$("#build-message").attr("class", "message error").text(buildErrorMessage(e));
|
||||
|
||||
var result = false;
|
||||
}
|
||||
|
||||
doLayout();
|
||||
return result;
|
||||
}
|
||||
|
||||
function parse() {
|
||||
oldInput = $("#input").val();
|
||||
|
||||
$("#input").removeAttr("disabled");
|
||||
$("#parse-message").attr("class", "message progress").text("Parsing the input...");
|
||||
$("#output").addClass("disabled").text("Output not available.");
|
||||
|
||||
try {
|
||||
var timeBefore = (new Date).getTime();
|
||||
var output = parser.parse($("#input").val());
|
||||
var timeAfter = (new Date).getTime();
|
||||
|
||||
$("#parse-message")
|
||||
.attr("class", "message info")
|
||||
.text("Input parsed successfully.")
|
||||
.append(buildSizeAndTimeInfoHtml(
|
||||
"Parsing time and speed",
|
||||
$("#input").val().length,
|
||||
timeAfter - timeBefore
|
||||
));
|
||||
$("#output").removeClass("disabled").text(jsDump.parse(output));
|
||||
|
||||
var result = true;
|
||||
} catch (e) {
|
||||
$("#parse-message").attr("class", "message error").text(buildErrorMessage(e));
|
||||
|
||||
var result = false;
|
||||
}
|
||||
|
||||
doLayout();
|
||||
return result;
|
||||
}
|
||||
|
||||
function buildAndParse() {
|
||||
build() && parse();
|
||||
}
|
||||
|
||||
function scheduleBuildAndParse() {
|
||||
var nothingChanged = getGrammar() === oldGrammar
|
||||
&& $("#parser-var").val() === oldParserVar
|
||||
&& $("#option-cache").is(":checked") === oldOptionCache
|
||||
&& $("#option-optimize").val() === oldOptionOptimize;
|
||||
if (nothingChanged) { return; }
|
||||
|
||||
if (buildAndParseTimer !== null) {
|
||||
clearTimeout(buildAndParseTimer);
|
||||
buildAndParseTimer = null;
|
||||
}
|
||||
if (parseTimer !== null) {
|
||||
clearTimeout(parseTimer);
|
||||
parseTimer = null;
|
||||
}
|
||||
|
||||
buildAndParseTimer = setTimeout(function() {
|
||||
buildAndParse();
|
||||
buildAndParseTimer = null;
|
||||
}, 500);
|
||||
}
|
||||
|
||||
function scheduleParse() {
|
||||
if ($("#input").val() === oldInput) { return; }
|
||||
if (buildAndParseTimer !== null) { return; }
|
||||
|
||||
if (parseTimer !== null) {
|
||||
clearTimeout(parseTimer);
|
||||
parseTimer = null;
|
||||
}
|
||||
|
||||
parseTimer = setTimeout(function() {
|
||||
parse();
|
||||
parseTimer = null;
|
||||
}, 500);
|
||||
}
|
||||
|
||||
function doLayout() {
|
||||
/*
|
||||
* This forces layout of the page so that the |#columns| table gets a chance
|
||||
* make itself smaller when the browser window shrinks.
|
||||
*/
|
||||
$("#left-column").height("0px"); // needed for IE
|
||||
$("#right-column").height("0px"); // needed for IE
|
||||
$(".CodeMirror").height("0px");
|
||||
$("#input").height("0px");
|
||||
|
||||
$("#left-column").height(($("#left-column").parent().innerHeight() - 2) + "px"); // needed for IE
|
||||
$("#right-column").height(($("#right-column").parent().innerHeight() - 2) + "px"); // needed for IE
|
||||
$(".CodeMirror").height(($(".CodeMirror").parent().parent().innerHeight() - 14) + "px");
|
||||
$("#input").height(($("#input").parent().parent().innerHeight() - 14) + "px");
|
||||
}
|
||||
|
||||
function getGrammar() {
|
||||
return editor.getValue();
|
||||
}
|
||||
|
||||
editor.on("change", scheduleBuildAndParse);
|
||||
|
||||
$("#parser-var, #option-cache, #option-optimize")
|
||||
.change(scheduleBuildAndParse)
|
||||
.mousedown(scheduleBuildAndParse)
|
||||
.mouseup(scheduleBuildAndParse)
|
||||
.click(scheduleBuildAndParse)
|
||||
.keydown(scheduleBuildAndParse)
|
||||
.keyup(scheduleBuildAndParse)
|
||||
.keypress(scheduleBuildAndParse);
|
||||
|
||||
$("#input")
|
||||
.change(scheduleParse)
|
||||
.mousedown(scheduleParse)
|
||||
.mouseup(scheduleParse)
|
||||
.click(scheduleParse)
|
||||
.keydown(scheduleParse)
|
||||
.keyup(scheduleParse)
|
||||
.keypress(scheduleParse);
|
||||
|
||||
doLayout();
|
||||
$(window).resize(doLayout);
|
||||
|
||||
$("#loader").hide();
|
||||
$("#content").show();
|
||||
|
||||
$("#grammar, #parser-var, #option-cache, #option-optimize").removeAttr("disabled");
|
||||
|
||||
buildAndParse();
|
||||
|
||||
editor.refresh();
|
||||
editor.focus();
|
||||
});
|
1
website/vendor/codemirror/codemirror.css
vendored
Normal file
1
website/vendor/codemirror/codemirror.css
vendored
Normal file
File diff suppressed because one or more lines are too long
17
website/vendor/codemirror/codemirror.js
vendored
Normal file
17
website/vendor/codemirror/codemirror.js
vendored
Normal file
File diff suppressed because one or more lines are too long
5
website/vendor/jquery/jquery.js
vendored
Normal file
5
website/vendor/jquery/jquery.js
vendored
Normal file
File diff suppressed because one or more lines are too long
196
website/vendor/jsdump/jsDump.js
vendored
Normal file
196
website/vendor/jsdump/jsDump.js
vendored
Normal file
|
@ -0,0 +1,196 @@
|
|||
/**
|
||||
* jsDump
|
||||
* Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
|
||||
* Licensed under BSD (http://www.opensource.org/licenses/bsd-license.php)
|
||||
* Date: 5/15/2008
|
||||
* @projectDescription Advanced and extensible data dumping for Javascript.
|
||||
* @version 1.0.0
|
||||
* @author Ariel Flesler
|
||||
* @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html}
|
||||
*/
|
||||
var jsDump;
|
||||
|
||||
(function(){
|
||||
function quote( str ){
|
||||
return '"' + str.toString().replace(/"/g, '\\"') + '"';
|
||||
}
|
||||
function literal( o ){
|
||||
return o + '';
|
||||
}
|
||||
function join( pre, arr, post ){
|
||||
var s = jsDump.separator(),
|
||||
base = jsDump.indent(),
|
||||
inner = jsDump.indent(1);
|
||||
if( arr.join )
|
||||
arr = arr.join( ',' + s + inner );
|
||||
if( !arr )
|
||||
return pre + post;
|
||||
return [ pre, inner + arr, base + post ].join(s);
|
||||
}
|
||||
function array( arr ){
|
||||
var i = arr.length, ret = Array(i);
|
||||
this.up();
|
||||
while( i-- )
|
||||
ret[i] = this.parse( arr[i] );
|
||||
this.down();
|
||||
return join( '[', ret, ']' );
|
||||
}
|
||||
|
||||
var reName = /^function (\w+)/;
|
||||
|
||||
jsDump = {
|
||||
parse:function( obj, type ){//type is used mostly internally, you can fix a (custom)type in advance
|
||||
var parser = this.parsers[ type || this.typeOf(obj) ];
|
||||
type = typeof parser;
|
||||
|
||||
return type == 'function' ? parser.call( this, obj ) :
|
||||
type == 'string' ? parser :
|
||||
this.parsers.error;
|
||||
},
|
||||
typeOf:function( obj ){
|
||||
var type = typeof obj,
|
||||
kind;
|
||||
|
||||
if ( type == 'object' || type == 'function' ) {
|
||||
if ( obj === null )
|
||||
return 'null';
|
||||
|
||||
// Extract Stuff from [Object Stuff]
|
||||
kind = Object.prototype.toString.call(obj).slice(8, -1);
|
||||
switch ( kind ) {
|
||||
case 'Array':
|
||||
return 'array';
|
||||
|
||||
case 'Arguments':
|
||||
return 'arguments';
|
||||
|
||||
case 'Date':
|
||||
return 'date';
|
||||
|
||||
case 'RegExp':
|
||||
return 'regexp';
|
||||
|
||||
case 'Window': //Firefox, IE, Opera
|
||||
case 'DOMWindow': //WebKit
|
||||
case 'global':
|
||||
return 'window';
|
||||
|
||||
case 'HTMLDocument': //WebKit, Firefox, Opera
|
||||
case 'Document': // IE
|
||||
return 'document';
|
||||
|
||||
case 'NodeList':
|
||||
return 'nodelist';
|
||||
|
||||
default:
|
||||
if (window.jQuery && obj instanceof window.jQuery)
|
||||
return 'jquery';
|
||||
else if ( 'ownerDocument' in obj && 'defaultView' in obj.ownerDocument && obj instanceof obj.ownerDocument.defaultView.Node )
|
||||
return 'node';
|
||||
}
|
||||
}
|
||||
return type;
|
||||
},
|
||||
separator:function(){
|
||||
return this.multiline ? this.HTML ? '<br />' : '\n' : this.HTML ? ' ' : ' ';
|
||||
},
|
||||
indent:function( extra ){// extra can be a number, shortcut for increasing-calling-decreasing
|
||||
if( !this.multiline )
|
||||
return '';
|
||||
var chr = this.indentChar;
|
||||
if( this.HTML )
|
||||
chr = chr.replace(/\t/g,' ').replace(/ /g,' ');
|
||||
return Array( this._depth_ + (extra||0) ).join(chr);
|
||||
},
|
||||
up:function( a ){
|
||||
this._depth_ += a || 1;
|
||||
},
|
||||
down:function( a ){
|
||||
this._depth_ -= a || 1;
|
||||
},
|
||||
setParser:function( name, parser ){
|
||||
this.parsers[name] = parser;
|
||||
},
|
||||
// The next 3 are exposed so you can use them
|
||||
quote:quote,
|
||||
literal:literal,
|
||||
join:join,
|
||||
_depth_: 1,
|
||||
// This is the list of parsers, to modify them, use jsDump.setParser
|
||||
parsers:{
|
||||
window: '[Window]',
|
||||
document: '[Document]',
|
||||
error:'[ERROR]', //when no parser is found, shouldn't happen
|
||||
unknown: '[Unknown]',
|
||||
'null':'null',
|
||||
undefined:'undefined',
|
||||
'function':function( fn ){
|
||||
var ret = 'function',
|
||||
name = 'name' in fn ? fn.name : (reName.exec(fn)||[])[1];//functions never have name in IE
|
||||
if( name )
|
||||
ret += ' ' + name;
|
||||
ret += '(';
|
||||
ret = [ ret, this.parse( fn, 'functionArgs' ), '){'].join('');
|
||||
return join( ret, this.parse(fn,'functionCode'), '}' );
|
||||
},
|
||||
array: array,
|
||||
nodelist: array,
|
||||
arguments: array,
|
||||
jquery:array,
|
||||
object:function( map ){
|
||||
if (this._depth_ >= this.maxDepth) {
|
||||
this._depth_ = 1; // Reset for future use
|
||||
throw new Error("Object nesting exceeded jsDump.maxDepth (" + jsDump.maxDepth + ")");
|
||||
}
|
||||
var ret = [ ];
|
||||
this.up();
|
||||
for( var key in map )
|
||||
ret.push( this.parse(key,'key') + ': ' + this.parse(map[key]) );
|
||||
this.down();
|
||||
return join( '{', ret, '}' );
|
||||
},
|
||||
node:function( node ){
|
||||
var open = this.HTML ? '<' : '<',
|
||||
close = this.HTML ? '>' : '>';
|
||||
var tag = node.nodeName.toLowerCase(),
|
||||
ret = open + tag;
|
||||
for( var a in this.DOMAttrs ){
|
||||
var val = node[this.DOMAttrs[a]];
|
||||
if( val )
|
||||
ret += ' ' + a + '=' + this.parse( val, 'attribute' );
|
||||
}
|
||||
return ret + close + open + '/' + tag + close;
|
||||
},
|
||||
functionArgs:function( fn ){//function calls it internally, it's the arguments part of the function
|
||||
var l = fn.length;
|
||||
if( !l ) return '';
|
||||
var args = Array(l);
|
||||
while( l-- )
|
||||
args[l] = String.fromCharCode(97+l);//97 is 'a'
|
||||
return ' ' + args.join(', ') + ' ';
|
||||
},
|
||||
key:quote, //object calls it internally, the key part of an item in a map
|
||||
functionCode:'[code]', //function calls it internally, it's the content of the function
|
||||
attribute:quote, //node calls it internally, it's an html attribute value
|
||||
string:quote,
|
||||
date:quote,
|
||||
regexp:literal, //regex
|
||||
number:literal,
|
||||
'boolean':literal
|
||||
},
|
||||
DOMAttrs:{//attributes to dump from nodes, name=>realName
|
||||
id:'id',
|
||||
name:'name',
|
||||
'class':'className'
|
||||
},
|
||||
HTML:false,//if true, entities are escaped ( <, >, \t, space and \n )
|
||||
indentChar:' ',//indentation unit
|
||||
multiline:true, //if true, items in a collection, are separated by a \n, else just a space.
|
||||
maxDepth:100 //maximum depth of object nesting
|
||||
};
|
||||
|
||||
})();
|
||||
|
||||
if (typeof exports !== 'undefined') {
|
||||
module.exports = jsDump;
|
||||
}
|
12
website/vendor/pegjs/peg.js
vendored
Normal file
12
website/vendor/pegjs/peg.js
vendored
Normal file
File diff suppressed because one or more lines are too long
27
website/views/development.ejs
Normal file
27
website/views/development.ejs
Normal file
|
@ -0,0 +1,27 @@
|
|||
<h1>Development</h1>
|
||||
|
||||
<ul>
|
||||
<li><a href="https://pegjs.org/">Project website</a></li>
|
||||
<li><a href="https://github.com/pegjs/pegjs/wiki">Wiki</a></li>
|
||||
<li><a href="https://github.com/pegjs/pegjs">Source code</a></li>
|
||||
<li><a href="https://github.com/pegjs/pegjs/issues">Issue tracker</a></li>
|
||||
<li><a href="https://groups.google.com/group/pegjs">Google Group</a></li>
|
||||
<li><a href="https://twitter.com/peg_js">Twitter</a></li>
|
||||
</ul>
|
||||
|
||||
<p>PEG.js is currently maintained by <a href="https://github.com/futagoza">Futago-za Ryuu</a>.
|
||||
Since it's <a href="https://www.google.com/search?q=inception+meaning">inception</a> in 2010, PEG.js was
|
||||
maintained by <a href="https://majda.cz/">David Majda</a> (<a href="https://twitter.com/dmajda">@dmajda</a>),
|
||||
until <a href="https://github.com/pegjs/pegjs/issues/503">May 2017</a>.</p>
|
||||
|
||||
<p>The <a href="https://github.com/pegjs/bower">Bower package</a> is maintained by
|
||||
<a href="https://www.michel-kraemer.com/">Michel Krämer</a>
|
||||
(<a href="https://twitter.com/michelkraemer">@michelkraemer</a>).</p>
|
||||
|
||||
<p>You are welcome to contribute code. Unless your contribution is really
|
||||
trivial you should get in touch with me first — this can prevent wasted
|
||||
effort on both sides. You can send code both as a patch or a GitHub pull
|
||||
request.</p>
|
||||
|
||||
<p>Note that PEG.js is still very much work in progress. There are no
|
||||
compatibility guarantees until version 1.0.</p>
|
583
website/views/documentation.ejs
Normal file
583
website/views/documentation.ejs
Normal file
|
@ -0,0 +1,583 @@
|
|||
<h1>Documentation</h1>
|
||||
|
||||
<h2 id="table-of-contents">Table of Contents</h2>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#installation">Installation</a>
|
||||
<ul>
|
||||
<li><a href="#installation-node-js">Node.js</a></li>
|
||||
<li><a href="#installation-browser">Browser</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#generating-a-parser">Generating a Parser</a>
|
||||
<ul>
|
||||
<li><a href="#generating-a-parser-command-line">Command Line</a></li>
|
||||
<li><a href="#generating-a-parser-javascript-api">JavaScript API</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#using-the-parser">Using the Parser</a></li>
|
||||
<li>
|
||||
<a href="#grammar-syntax-and-semantics">Grammar Syntax and Semantics</a>
|
||||
<ul>
|
||||
<li><a href="#grammar-syntax-and-semantics-parsing-expression-types">Parsing Expression Types</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#compatibility">Compatibility</a></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="installation">Installation</h2>
|
||||
|
||||
<h3 id="installation-node-js">Node.js</h3>
|
||||
|
||||
<p>To use the <code>pegjs</code> command, install PEG.js globally:</p>
|
||||
|
||||
<pre><code>$ npm install -g pegjs</code></pre>
|
||||
|
||||
<p>To use the JavaScript API, install PEG.js locally:</p>
|
||||
|
||||
<pre><code>$ npm install pegjs</code></pre>
|
||||
|
||||
<p>If you need both the <code>pegjs</code> command and the JavaScript API,
|
||||
install PEG.js both ways.</p>
|
||||
|
||||
<h3 id="installation-browser">Browser</h3>
|
||||
|
||||
<p><a href="https://pegjs.org/#download">Download</a> the PEG.js library
|
||||
(regular or minified version) or install it using Bower:</p>
|
||||
|
||||
<pre><code>$ bower install pegjs</code></pre>
|
||||
|
||||
<h2 id="generating-a-parser">Generating a Parser</h2>
|
||||
|
||||
<p>PEG.js generates parser from a grammar that describes expected input and can
|
||||
specify what the parser returns (using semantic actions on matched parts of the
|
||||
input). Generated parser itself is a JavaScript object with a simple API.</p>
|
||||
|
||||
<h3 id="generating-a-parser-command-line">Command Line</h3>
|
||||
|
||||
<p>To generate a parser from your grammar, use the <code>pegjs</code>
|
||||
command:</p>
|
||||
|
||||
<pre><code>$ pegjs arithmetics.pegjs</code></pre>
|
||||
|
||||
<p>This writes parser source code into a file with the same name as the grammar
|
||||
file but with “.js” extension. You can also specify the output file
|
||||
explicitly:</p>
|
||||
|
||||
<pre><code>$ pegjs -o arithmetics-parser.js arithmetics.pegjs</code></pre>
|
||||
|
||||
<p>If you omit both input and output file, standard input and output are
|
||||
used.</p>
|
||||
|
||||
<p>By default, the generated parser is in the Node.js module format. You can
|
||||
override this using the <code>--format</code> option.</p>
|
||||
|
||||
<p>You can tweak the generated parser with several options:</p>
|
||||
|
||||
<dl>
|
||||
<dt><code>--allowed-start-rules</code></dt>
|
||||
<dd>Comma-separated list of rules the parser will be allowed to start parsing
|
||||
from (default: the first rule in the grammar).</dd>
|
||||
|
||||
<dt><code>--cache</code></dt>
|
||||
<dd>Makes the parser cache results, avoiding exponential parsing time in
|
||||
pathological cases but making the parser slower.</dd>
|
||||
|
||||
<dt><code>--dependency</code></dt>
|
||||
<dd>Makes the parser require a specified dependency (can be specified multiple
|
||||
times).</dd>
|
||||
|
||||
<dt><code>--export-var</code></dt>
|
||||
<dd>Name of a global variable into which the parser object is assigned to when
|
||||
no module loader is detected.</dd>
|
||||
|
||||
<dt><code>--extra-options</code></dt>
|
||||
<dd>Additional options (in JSON format) to pass to
|
||||
<code>peg.generate</code>.</dd>
|
||||
|
||||
<dt><code>--extra-options-file</code></dt>
|
||||
<dd>File with additional options (in JSON format) to pass to
|
||||
<code>peg.generate</code>.</dd>
|
||||
|
||||
<dt><code>--format</code></dt>
|
||||
<dd>Format of the generated parser: <code>amd</code>, <code>commonjs</code>,
|
||||
<code>globals</code>, <code>umd</code> (default: <code>commonjs</code>).</dd>
|
||||
|
||||
<dt><code>--optimize</code></dt>
|
||||
<dd>Selects between optimizing the generated parser for parsing speed
|
||||
(<code>speed</code>) or code size (<code>size</code>) (default:
|
||||
<code>speed</code>)</dd>
|
||||
|
||||
<dt><code>--plugin</code></dt>
|
||||
<dd>Makes PEG.js use a specified plugin (can be specified multiple
|
||||
times).</dd>
|
||||
|
||||
<dt><code>--trace</code></dt>
|
||||
<dd>Makes the parser trace its progress.</dd>
|
||||
</dl>
|
||||
|
||||
<h3 id="generating-a-parser-javascript-api">JavaScript API</h3>
|
||||
|
||||
<p>In Node.js, require the PEG.js parser generator module:</p>
|
||||
|
||||
<pre><code>var peg = require("pegjs");</code></pre>
|
||||
|
||||
<p>In browser, include the PEG.js library in your web page or application using
|
||||
the <code><script></code> tag. If PEG.js detects an AMD loader, it will
|
||||
define itself as a module, otherwise the API will be available in the
|
||||
<code>peg</code> global object.</p>
|
||||
|
||||
<p>To generate a parser, call the <code>peg.generate</code> method and pass your
|
||||
grammar as a parameter:</p>
|
||||
|
||||
<pre><code>var parser = peg.generate("start = ('a' / 'b')+");</code></pre>
|
||||
|
||||
<p>The method will return generated parser object or its source code as a string
|
||||
(depending on the value of the <code>output</code> option — see below). It will
|
||||
throw an exception if the grammar is invalid. The exception will contain
|
||||
<code>message</code> property with more details about the error.</p>
|
||||
|
||||
<p>You can tweak the generated parser by passing a second parameter with an
|
||||
options object to <code>peg.generate</code>. The following options are
|
||||
supported:</p>
|
||||
|
||||
<dl>
|
||||
<dt><code>allowedStartRules</code></dt>
|
||||
<dd>Rules the parser will be allowed to start parsing from (default: the first
|
||||
rule in the grammar).</dd>
|
||||
|
||||
<dt><code>cache</code></dt>
|
||||
<dd>If <code>true</code>, makes the parser cache results, avoiding exponential
|
||||
parsing time in pathological cases but making the parser slower (default:
|
||||
<code>false</code>).</dd>
|
||||
|
||||
<dt><code>dependencies</code></dt>
|
||||
<dd>Parser dependencies, the value is an object which maps variables used to
|
||||
access the dependencies in the parser to module IDs used to load them; valid
|
||||
only when <code>format</code> is set to <code>"amd"</code>,
|
||||
<code>"commonjs"</code>, or <code>"umd"</code> (default:
|
||||
<code>{}</code>).</dd>
|
||||
|
||||
<dt><code>exportVar</code></dt>
|
||||
<dd>Name of a global variable into which the parser object is assigned to when
|
||||
no module loader is detected; valid only when <code>format</code> is set to
|
||||
<code>"globals"</code> or <code>"umd"</code> (default:
|
||||
<code>null</code>).</dd>
|
||||
|
||||
<dt><code>format</code></dt>
|
||||
<dd>format of the genreated parser (<code>"amd"</code>, <code>"bare"</code>,
|
||||
<code>"commonjs"</code>, <code>"globals"</code>, or <code>"umd"</code>); valid
|
||||
only when <code>output</code> is set to <code>"source"</code> (default:
|
||||
<code>"bare"</code>).</dd>
|
||||
|
||||
<dt><code>optimize</code></dt>
|
||||
<dd>Selects between optimizing the generated parser for parsing speed
|
||||
(<code>"speed"</code>) or code size (<code>"size"</code>) (default:
|
||||
<code>"speed"</code>).</dd>
|
||||
|
||||
<dt><code>output</code></dt>
|
||||
<dd>If set to <code>"parser"</code>, the method will return generated parser
|
||||
object; if set to <opde>"source"</code>, it will return parser source code as
|
||||
a string (default: <code>"parser"</code>).</dd>
|
||||
|
||||
<dt><code>plugins</code></dt>
|
||||
<dd>Plugins to use.</dd>
|
||||
|
||||
<dt><code>trace</code></dt>
|
||||
<dd>Makes the parser trace its progress (default: <code>false</code>).</dd>
|
||||
</dl>
|
||||
|
||||
<h2 id="using-the-parser">Using the Parser</h2>
|
||||
|
||||
<p>Using the generated parser is simple — just call its <code>parse</code>
|
||||
method and pass an input string as a parameter. The method will return a parse
|
||||
result (the exact value depends on the grammar used to generate the parser) or
|
||||
throw an exception if the input is invalid. The exception will contain
|
||||
<code>location</code>, <code>expected</code>, <code>found</code> and
|
||||
<code>message</code> properties with more details about the error.</p>
|
||||
|
||||
<pre><code>parser.parse("abba"); // returns ["a", "b", "b", "a"]
|
||||
|
||||
parser.parse("abcd"); // throws an exception </code></pre>
|
||||
|
||||
<p>You can tweak parser behavior by passing a second parameter with an options
|
||||
object to the <code>parse</code> method. The following options are
|
||||
supported:</p>
|
||||
|
||||
<dl>
|
||||
<dt><code>startRule</code></dt>
|
||||
<dd>Name of the rule to start parsing from.</dd>
|
||||
|
||||
<dt><code>tracer</code></dt>
|
||||
<dd>Tracer to use.</dd>
|
||||
</dl>
|
||||
|
||||
<p>Parsers can also support their own custom options.</p>
|
||||
|
||||
<h2 id="grammar-syntax-and-semantics">Grammar Syntax and Semantics</h2>
|
||||
|
||||
<p>The grammar syntax is similar to JavaScript in that it is not line-oriented
|
||||
and ignores whitespace between tokens. You can also use JavaScript-style
|
||||
comments (<code>// ...</code> and <code>/* ... */</code>).</p>
|
||||
|
||||
<p>Let's look at example grammar that recognizes simple arithmetic expressions
|
||||
like <code>2*(3+4)</code>. A parser generated from this grammar computes their
|
||||
values.</p>
|
||||
|
||||
<pre><code>start
|
||||
= additive
|
||||
|
||||
additive
|
||||
= left:multiplicative "+" right:additive { return left + right; }
|
||||
/ multiplicative
|
||||
|
||||
multiplicative
|
||||
= left:primary "*" right:multiplicative { return left * right; }
|
||||
/ primary
|
||||
|
||||
primary
|
||||
= integer
|
||||
/ "(" additive:additive ")" { return additive; }
|
||||
|
||||
integer "integer"
|
||||
= digits:[0-9]+ { return parseInt(digits.join(""), 10); }</code></pre>
|
||||
|
||||
<p>On the top level, the grammar consists of <em>rules</em> (in our example,
|
||||
there are five of them). Each rule has a <em>name</em> (e.g.
|
||||
<code>integer</code>) that identifies the rule, and a <em>parsing
|
||||
expression</em> (e.g. <code>digits:[0-9]+ { return parseInt(digits.join(""),
|
||||
10); }</code>) that defines a pattern to match against the input text and
|
||||
possibly contains some JavaScript code that determines what happens when the
|
||||
pattern matches successfully. A rule can also contain <em>human-readable
|
||||
name</em> that is used in error messages (in our example, only the
|
||||
<code>integer</code> rule has a human-readable name). The parsing starts at the
|
||||
first rule, which is also called the <em>start rule</em>.</p>
|
||||
|
||||
<p>A rule name must be a JavaScript identifier. It is followed by an equality
|
||||
sign (“=”) and a parsing expression. If the rule has a human-readable name, it
|
||||
is written as a JavaScript string between the name and separating equality sign.
|
||||
Rules need to be separated only by whitespace (their beginning is easily
|
||||
recognizable), but a semicolon (“;”) after the parsing expression is
|
||||
allowed.</p>
|
||||
|
||||
<p>The first rule can be preceded by an <em>initializer</em> — a piece of
|
||||
JavaScript code in curly braces (“{” and “}”). This code is executed before the
|
||||
generated parser starts parsing. All variables and functions defined in the
|
||||
initializer are accessible in rule actions and semantic predicates. The code
|
||||
inside the initializer can access options passed to the parser using the
|
||||
<code>options</code> variable. Curly braces in the initializer code must be
|
||||
balanced. Let's look at the example grammar from above using a simple
|
||||
initializer.</p>
|
||||
|
||||
<pre><code>{
|
||||
function makeInteger(o) {
|
||||
return parseInt(o.join(""), 10);
|
||||
}
|
||||
}
|
||||
|
||||
start
|
||||
= additive
|
||||
|
||||
additive
|
||||
= left:multiplicative "+" right:additive { return left + right; }
|
||||
/ multiplicative
|
||||
|
||||
multiplicative
|
||||
= left:primary "*" right:multiplicative { return left * right; }
|
||||
/ primary
|
||||
|
||||
primary
|
||||
= integer
|
||||
/ "(" additive:additive ")" { return additive; }
|
||||
|
||||
integer "integer"
|
||||
= digits:[0-9]+ { return makeInteger(digits); }</code></pre>
|
||||
|
||||
<p>The parsing expressions of the rules are used to match the input text to the
|
||||
grammar. There are various types of expressions — matching characters or
|
||||
character classes, indicating optional parts and repetition, etc. Expressions
|
||||
can also contain references to other rules. See <a
|
||||
href="#grammar-syntax-and-semantics-parsing-expression-types">detailed
|
||||
description below</a>.</p>
|
||||