Initial commit.

redux
David Majda 14 years ago
commit c3dd696a3e

@ -0,0 +1,22 @@
Copyright (c) 2010 David Majda
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

@ -0,0 +1,4 @@
PEG.js: Parser Generator for JavaScript
=======================================
Documentation is being written -- please be patient.

@ -0,0 +1 @@
0.1

@ -0,0 +1,4 @@
#!/bin/sh
DIR=`dirname "$0"`
java -jar "$DIR/../vendor/rhino/js.jar" "$DIR/pegjs-main.js" "$DIR" "$@"

@ -0,0 +1,160 @@
importPackage(java.io);
importPackage(java.lang);
/*
* Rhino does not have __FILE__ or anything similar so we have to pass the
* script path from the outside.
*/
load(arguments[0] + "/../lib/runtime.js");
load(arguments[0] + "/../lib/compiler.js");
var FILE_STDIN = "-";
var FILE_STDOUT = "-";
function readFile(file) {
var f = new BufferedReader(new InputStreamReader(
file === FILE_STDIN ? System["in"] : new FileInputStream(file)
));
var result = "";
var line = "";
try {
while ((line = f.readLine()) !== null) {
result += line + "\n";
}
} finally {
f.close();
}
return result;
}
function writeFile(file, text) {
var f = new BufferedWriter(new OutputStreamWriter(
file === FILE_STDOUT ? System.out : new FileOutputStream(file)
));
try {
f.write(text);
} finally {
f.close();
}
}
function isOption(arg) {
return /-.+/.test(arg);
}
function printVersion() {
print("PEG.js 0.1");
}
function printHelp() {
print("Usage: pegjs [options] [--] <parserVar> [<input_file>] [<output_file>]");
print("");
print("Generates a parser from the PEG grammar specified in the <input_file> and");
print("writes it to the <output_file>. The parser object will be stored in a variable");
print("named <parser_var>.");
print("");
print("If the <output_file> is omitted, its name is generated by changing the");
print("<input_file> extension to \".js\". If both <input_file> and <output_file> are");
print("omitted, standard input and output are used.");
print("");
print("Options:");
print(" -s, --start-rule specify grammar start rule (default: \"start\")");
print(" -v, --version print version information and exit");
print(" -h, --help print help and exit");
}
function nextArg() {
args.shift();
}
function exitSuccess() {
quit(0);
}
function exitFailure() {
quit(1);
}
function abort(message) {
System.out.println(message);
exitFailure();
}
var startRule = "start";
/*
* The trimmed first argument is the script path -- see the beginning of this
* file.
*/
var args = Array.prototype.slice.call(arguments, 1);
while (args.length > 0 && isOption(args[0])) {
switch (args[0]) {
case "-s":
case "--start":
nextArg();
if (args.length === 0) {
abort("Missing parameter of the -s/--start option.");
}
startRule = args[0];
break;
case "-v":
case "--version":
printVersion();
exitSuccess();
break;
case "-h":
case "--help":
printHelp();
exitSuccess();
break;
case "--":
nextArg();
break;
default:
abort("Unknown option: " + args[0] + ".");
}
nextArg();
}
if (args.length === 0) {
abort("Too few arguments.");
}
var parserVar = args[0];
nextArg();
switch (args.length) {
case 0:
var inputFile = FILE_STDIN;
var outputFile = FILE_STDOUT;
break;
case 1:
var inputFile = args[0];
var outputFile = args[0].replace(/\.[^.]*$/, ".js");
break;
case 2:
var inputFile = args[0];
var outputFile = args[1];
break;
default:
abort("Too many arguments.");
}
var input = readFile(inputFile);
try {
var parser = PEG.buildParser(input, startRule);
} catch (e) {
if (e.line !== undefined && e.column !== undefined) {
abort(e.line + ":" + e.column + ": " + e.message);
} else {
abort(e.message);
}
}
writeFile(outputFile, parserVar + " = " + parser.toSource() + ";\n");

@ -0,0 +1,5 @@
@echo off
set DIR_WITH_SLASH=%~dp0
set DIR=%DIR_WITH_SLASH:~0,-1%
java -jar "%DIR%\..\vendor\rhino\js.jar" "%DIR%\pegjs-main.js" "%DIR%" %*

@ -0,0 +1,31 @@
start : _ expression { return $2; }
expression : additive
additive : multiplicative (plus / minus) additive {
if ($2 === "+") { return $1 + $3; }
if ($2 === "-") { return $1 - $3; }
}
/ multiplicative
multiplicative : primary (times / divide) multiplicative {
if ($2 === "*") { return $1 * $3; }
if ($2 === "/") { return $1 / $3; }
}
/ primary
primary : integer
/ lparen expression rparen { return $2 }
integer "integer"
: [0-9]+ _ { return parseInt($1.join("")); }
plus : "+" _ { return $1; }
minus : "-" _ { return $1; }
times : "*" _ { return $1; }
divide : "/" _ { return $1; }
lparen : "(" _
rparen : ")" _
_ "whitespace"
: [ \t\n\r]*

File diff suppressed because it is too large Load Diff

@ -0,0 +1,219 @@
/*
* PEG.js runtime.
*
* Required by all parsers generated by PEG.js.
*/
PEG = {};
(function() {
/* ===== PEG.ArrayUtils ===== */
/* Array manipulation utility functions. */
PEG.ArrayUtils = {
map: function(array, callback) {
var result = [];
var length = array.length;
for (var i = 0; i < length; i++) {
result[i] = callback(array[i]);
}
return result;
}
};
/* ===== PEG.StringUtils ===== */
/* String manipulation utility functions. */
PEG.StringUtils = {
/*
* Surrounds the string with quotes and escapes characters inside so that the
* result is a valid JavaScript string.
*/
quote: function(s) {
/*
* ECMA-262, 5th ed., 7.8.4: All characters may appear literally in a string
* literal except for the closing quote character, backslash, carriage
* return, line separator, paragraph separator, and line feed. Any character
* may appear in the form of an escape sequence.
*/
return '"' + s
.replace(/\\/g, '\\\\') // backslash
.replace(/"/g, '\\"') // closing quote character
.replace(/\r/g, '\\r') // carriage return
.replace(/\u2028/g, '\\u2028') // line separator
.replace(/\u2029/g, '\\u2029') // paragraph separator
.replace(/\n/g, '\\n') // line feed
+ '"';
},
};
/* ===== PEG.Parser ===== */
/* Prototype of all parsers generated by PEG.js. */
PEG.Parser = function(startRule) { this._startRule = startRule; }
PEG.Parser.prototype = {
_matchFailed: function(failure) {
if (this._pos > this._rightmostMatchFailuresPos) {
this._rightmostMatchFailuresPos = this._pos;
this._rightmostMatchFailures = [];
}
this._rightmostMatchFailures.push(failure);
},
/*
* Parses the input with a generated parser. If the parsing is successfull,
* returns a value explicitly or implicitly specified by the grammar from
* which the parser was generated (see |PEG.buildParser|). If the parsing is
* unsuccessful, throws |PEG.Parser.SyntaxError| describing the error.
*/
parse: function(input) {
var that = this;
function initialize() {
that._input = input;
that._pos = 0;
that._rightmostMatchFailuresPos = 0;
that._rightmostMatchFailures = [];
that._cache = {};
}
function buildErrorMessage() {
function buildExpectedFromMatchFailures(failures) {
switch (failures.length) {
case 0:
return "nothing";
case 1:
return failures[0].toString();
default:
return PEG.ArrayUtils.map(
failures.slice(0, failures.length - 1),
function(failure) { return failure.toString(); }
).join(", ")
+ " or "
+ failures[failures.length - 1].toString();
}
}
if (that._pos === 0) {
var expected = buildExpectedFromMatchFailures(
that._rightmostMatchFailures
);
var actual = that._rightmostMatchFailuresPos < that._input.length
? PEG.StringUtils.quote(
that._input.charAt(that._rightmostMatchFailuresPos)
)
: "end of input";
} else {
var expected = "end of input";
var actual = PEG.StringUtils.quote(that._input.charAt(that._pos));
}
return "Expected " + expected + " but " + actual + " found.";
}
function computeErrorPosition() {
/*
* The first idea was to use |String.split| to break the input up to the
* error position along newlines and derive the line and column from
* there. However IE's |split| implementation is so broken that it was
* enough to prevent it.
*/
var input = that._input;
var pos = that._rightmostMatchFailuresPos;
var line = 1;
var column = 1;
var seenCR = false;
for (var i = 0; i < pos; i++) {
var ch = input.charAt(i);
if (ch === "\n") {
if (!seenCR) { line++; }
column = 1;
seenCR = false;
} else if (ch === "\r" | ch === "\u2028" || ch === "\u2029") {
line++;
column = 1;
seenCR = true;
} else {
column++;
seenCR = false;
}
}
return { line: line, column: column };
}
initialize();
var initialContext = {
reportMatchFailures: true,
};
var result = this["_parse_" + this._startRule](initialContext);
if (result === null || this._pos !== input.length) {
var errorPosition = computeErrorPosition();
throw new PEG.Parser.SyntaxError(
errorPosition.line,
errorPosition.column,
buildErrorMessage()
);
}
return result;
},
/* Returns the parser source code. */
toSource: function() { return this._source; }
};
/* ===== PEG.Parser.LiteralMatchFailure ===== */
/* Stores information about a literal match failure. */
PEG.Parser.LiteralMatchFailure = function(value) { this._value = value; };
PEG.Parser.LiteralMatchFailure.prototype = {
toString: function() { return PEG.StringUtils.quote(this._value); }
};
/* ===== PEG.Parser.AnyMatchFailure ===== */
/* Stores information about a failure to match a "." expression. */
PEG.Parser.AnyMatchFailure = function() {}
PEG.Parser.AnyMatchFailure.prototype = {
toString: function() { return "any character"; }
};
/* ===== PEG.Parser.NamedRuleMatchFailure ===== */
/* Stores information about a failure to match a named rule. */
PEG.Parser.NamedRuleMatchFailure = function(humanName) { this._humanName = humanName; }
PEG.Parser.NamedRuleMatchFailure.prototype = {
toString: function() { return this._humanName; }
};
/* ===== PEG.Parser.SyntaxError ===== */
/* Thrown when a parser encounters a syntax error. */
PEG.Parser.SyntaxError = function(line, column, message) {
this.name = "PEG.Parser.SyntaxError";
this.line = line;
this.column = column;
this.message = message;
};
PEG.Parser.SyntaxError.prototype = Error.prototype;
})();

@ -0,0 +1,919 @@
(function() {
var global = this;
/* ===== Helpers ===== */
global.throws = function(block, exceptionType) {
var exception = null;
try {
block();
} catch (e) {
exception = e;
}
ok(
exception !== null,
exception !== null ? "okay: thrown something" : "failed, nothing thrown"
);
if (exception !== null) {
ok(
exception instanceof exceptionType,
exception instanceof exceptionType
? "okay: thrown " + exceptionType.name
: "failed, thrown " + exception.name + " instead of " + exceptionType.name
);
}
return exception;
};
global.parses = function(parser, input, expected) {
deepEqual(parser.parse(input), expected);
};
global.doesNotParse = function(parser, input) {
throws(function() { parser.parse(input); }, PEG.Parser.SyntaxError);
};
global.doesNotParseWithMessage = function(parser, input, message) {
var exception = throws(
function() { parser.parse(input); },
PEG.Parser.SyntaxError
);
if (exception) {
strictEqual(exception.message, message);
}
};
global.doesNotParseWithPos = function(parser, input, line, column) {
var exception = throws(
function() { parser.parse(input); },
PEG.Parser.SyntaxError
);
if (exception) {
strictEqual(exception.line, line);
strictEqual(exception.column, column);
}
};
global.grammarParserParses = function(input, expected) {
global.parses(PEG.grammarParser, input, expected);
};
global.grammarParserDoesNotParse = function(input) {
global.doesNotParse(PEG.grammarParser, input);
}
/* ===== PEG.Compiler ===== */
module("PEG.Compiler");
test("formatCode joins parts", function() {
strictEqual(PEG.Compiler.formatCode("foo", "bar"), "foo\nbar");
});
test("formatCode interpolates variables", function() {
strictEqual(
PEG.Compiler.formatCode("foo", "${bar}", { bar: "baz" }),
"foo\nbaz"
);
throws(function() {
PEG.Compiler.formatCode("foo", "${bar}");
}, Error);
});
test("formatCode filters variables", function() {
strictEqual(
PEG.Compiler.formatCode("foo", "${bar|string}", { bar: "baz" }),
"foo\n\"baz\""
);
throws(function() {
PEG.Compiler.formatCode("foo", "${bar|eeek}", { bar: "baz" });
}, Error);
});
test("formatCode indents multiline parts", function() {
strictEqual(
PEG.Compiler.formatCode("foo", "${bar}", { bar: " baz\nqux" }),
"foo\n baz\n qux"
);
});
test("generateUniqueIdentifier", function() {
notStrictEqual(
PEG.Compiler.generateUniqueIdentifier("prefix"),
PEG.Compiler.generateUniqueIdentifier("prefix")
);
});
/* ===== PEG ===== */
module("PEG");
test("buildParser reports invalid grammar object", function() {
throws(function() { PEG.buildParser(42); }, PEG.Grammar.GrammarError);
});
test("buildParser reports missing start rule", function() {
throws(function() { PEG.buildParser({}); }, PEG.Grammar.GrammarError);
});
test("buildParser allows custom start rule", function() {
var parser = PEG.buildParser('s: "abcd"', "s");
parses(parser, "abcd", "abcd");
});
/* ===== Generated Parser ===== */
module("Generated Parser");
test("literals", function() {
var parser = PEG.buildParser('start: "abcd"');
parses(parser, "abcd", "abcd");
doesNotParse(parser, "");
doesNotParse(parser, "abc");
doesNotParse(parser, "abcde");
doesNotParse(parser, "efgh");
/*
* Test that the parsing position moves forward after successful parsing of
* a literal.
*/
var posTestParser = PEG.buildParser('start: "a" "b"');
parses(posTestParser, "ab", ["a", "b"]);
});
test("anys", function() {
var parser = PEG.buildParser('start: .');
parses(parser, "a", "a");
doesNotParse(parser, "");
doesNotParse(parser, "ab");
/*
* Test that the parsing position moves forward after successful parsing of
* an any.
*/
var posTestParser = PEG.buildParser('start: . .');
parses(posTestParser, "ab", ["a", "b"]);
});
test("classes", function() {
var emptyClassParser = PEG.buildParser('start: []');
doesNotParse(emptyClassParser, "");
doesNotParse(emptyClassParser, "a");
doesNotParse(emptyClassParser, "ab");
var nonEmptyClassParser = PEG.buildParser('start: [ab-d]');
parses(nonEmptyClassParser, "a", "a");
parses(nonEmptyClassParser, "b", "b");
parses(nonEmptyClassParser, "c", "c");
parses(nonEmptyClassParser, "d", "d");
doesNotParse(nonEmptyClassParser, "");
doesNotParse(nonEmptyClassParser, "ab");
/*
* Test that the parsing position moves forward after successful parsing of
* a class.
*/
var posTestParser = PEG.buildParser('start: [ab-d] [ab-d]');
parses(posTestParser, "ab", ["a", "b"]);
});
test("sequences", function() {
var emptySequenceParser = PEG.buildParser('start: ');
parses(emptySequenceParser, "", []);
doesNotParse(emptySequenceParser, "abc");
var nonEmptySequenceParser = PEG.buildParser('start: "a" "b" "c"');
parses(nonEmptySequenceParser, "abc", ["a", "b", "c"]);
doesNotParse(nonEmptySequenceParser, "");
doesNotParse(nonEmptySequenceParser, "ab");
doesNotParse(nonEmptySequenceParser, "abcd");
doesNotParse(nonEmptySequenceParser, "efg");
/*
* Test that the parsing position returns after unsuccessful parsing of a
* sequence.
*/
var posTestParser = PEG.buildParser('start: ("a" "b") / "a"');
parses(posTestParser, "a", "a");
});
test("choices", function() {
var parser = PEG.buildParser('start: "a" / "b" / "c"');
parses(parser, "a", "a");
parses(parser, "b", "b");
parses(parser, "c", "c");
doesNotParse(parser, "");
doesNotParse(parser, "ab");
doesNotParse(parser, "d");
});
test("optional expressions", function() {
var parser = PEG.buildParser('start: "a"?');
parses(parser, "", "");
parses(parser, "a", "a");
});
test("zero or more expressions", function() {
var parser = PEG.buildParser('start: "a"*');
parses(parser, "", []);
parses(parser, "a", ["a"]);
parses(parser, "aaa", ["a", "a", "a"]);
});
test("one or more expressions", function() {
var parser = PEG.buildParser('start: "a"+');
doesNotParse(parser, "");
parses(parser, "a", ["a"]);
parses(parser, "aaa", ["a", "a", "a"]);
});
test("and predicate", function() {
var parser = PEG.buildParser('start: "a" &"b" "b"');
parses(parser, "ab", ["a", "", "b"]);
doesNotParse(parser, "ac");
/*
* Test that the parsing position returns after successful parsing of a
* predicate is not needed, it is implicit in the tests above.
*/
});
test("not predicate", function() {
var parser = PEG.buildParser('start: "a" !"b"');
parses(parser, "a", ["a", ""]);
doesNotParse(parser, "ab");
/*
* Test that the parsing position returns after successful parsing of a
* predicate.
*/
var posTestParser = PEG.buildParser('start: "a" !"b" "c"');
parses(posTestParser, "ac", ["a", "", "c"]);
});
test("rule references", function() {
var parser = PEG.buildParser([
'start: static / dynamic',
'static: "C" / "C++" / "Java" / "C#"',
'dynamic: "Ruby" / "Python" / "JavaScript"'
].join("\n"));
parses(parser, "Java", "Java");
parses(parser, "Python", "Python");
});
test("actions", function() {
var singleMatchParser = PEG.buildParser(
'start: "a" { return Array.prototype.slice.call(arguments).join("").toUpperCase(); }'
);
parses(singleMatchParser, "a", "A");
var multiMatchParser = PEG.buildParser(
'start: "a" "b" "c" { return Array.prototype.slice.call(arguments).join("").toUpperCase(); }'
);
parses(multiMatchParser, "abc", "ABC");
var innerMatchParser = PEG.buildParser(
'start: "a" ("b" "c" "d" { return Array.prototype.slice.call(arguments).join("").toUpperCase(); }) "e"'
);
parses(innerMatchParser, "abcde", ["a", "BCD", "e"]);
/* Test that the action is not called when its expression does not match. */
var notAMatchParser = PEG.buildParser(
'start: "a" { ok(false, "action got called when it should not be"); }'
);
doesNotParse(notAMatchParser, "b");
var variablesParser = PEG.buildParser([
'start: "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" {',
' return [$1, $2, $3, $4, $5, $6, $7, $8, $9, $10].join("").toUpperCase();',
' }'
].join("\n"));
parses(variablesParser, "abcdefghij", "ABCDEFGHIJ");
});
test("cache", function() {
/*
* Should trigger a codepath where the cache is used (for the "a" rule).
*/
var parser = PEG.buildParser([
'start: (a b) / (a c)',
'a: "a"',
'b: "b"',
'c: "c"'
].join("\n"));
parses(parser, "ac", ["a", "c"]);
});
test("error messages", function() {
var literalParser = PEG.buildParser('start: "abcd"');
doesNotParseWithMessage(
literalParser,
"",
'Expected "abcd" but end of input found.'
);
doesNotParseWithMessage(
literalParser,
"efgh",
'Expected "abcd" but "e" found.'
);
doesNotParseWithMessage(
literalParser,
"abcde",
'Expected end of input but "e" found.'
);
var anyParser = PEG.buildParser('start: .');
doesNotParseWithMessage(
anyParser,
"",
'Expected any character but end of input found.'
);
var namedRuleWithLiteralParser = PEG.buildParser('start "digit": [0-9]');
doesNotParseWithMessage(
namedRuleWithLiteralParser,
"a",
'Expected digit but "a" found.'
);
var namedRuleWithAnyParser = PEG.buildParser('start "whatever": .');
doesNotParseWithMessage(
namedRuleWithAnyParser,
"",
'Expected whatever but end of input found.'
);
var namedRuleWithNamedRuleParser = PEG.buildParser([
'start "digits": digit+',
'digit "digit": [0-9]'
].join("\n"));
doesNotParseWithMessage(
namedRuleWithNamedRuleParser,
"",
'Expected digits but end of input found.'
);
var choiceParser = PEG.buildParser('start: "a" / "b" / "c"');
doesNotParseWithMessage(
choiceParser,
"def",
'Expected "a", "b" or "c" but "d" found.'
);
var emptyParser = PEG.buildParser('start: ');
doesNotParseWithMessage(
emptyParser,
"something",
'Expected nothing but "s" found.'
);
});
test("error positions", function() {
var parser = PEG.buildParser([
'start: line (("\\r" / "\\n" / "\\u2028" / "\\u2029")+ line)*',
'line: digit (" "+ digit)*',
'digit: [0-9]+ { return $1.join(""); }'
].join("\n"));
doesNotParseWithPos(parser, "a", 1, 1);
doesNotParseWithPos(parser, "1\n2\n\n3\n\n\n4 5 x", 7, 5);
/* Non-Unix newlines */
doesNotParseWithPos(parser, "1\rx", 2, 1); // Old Mac
doesNotParseWithPos(parser, "1\r\nx", 2, 1); // Windows
doesNotParseWithPos(parser, "1\n\rx", 3, 1); // mismatched
/* Strange newlines */
doesNotParseWithPos(parser, "1\u2028x", 2, 1); // line separator
doesNotParseWithPos(parser, "1\u2029x", 2, 1); // paragraph separator
});
/*
* Following examples are from Wikipedia, see
* http://en.wikipedia.org/w/index.php?title=Parsing_expression_grammar&oldid=335106938.
*/
test("arithmetics", function() {
/*
* Value [0-9]+ / '(' Expr ')'
* Product Value (('*' / '/') Value)*
* Sum Product (('+' / '-') Product)*
* Expr Sum
*/
var parser = PEG.buildParser([
'Value : [0-9]+ { return parseInt($1.join("")); }',
' / "(" Expr ")" { return $2; }',
'Product : Value (("*" / "/") Value)* {',
' var result = $1;',
' for (var i = 0; i < $2.length; i++) {',
' if ($2[i][0] == "*") { result *= $2[i][1]; }',
' if ($2[i][0] == "/") { result /= $2[i][1]; }',
' }',
' return result;',
' }',
'Sum : Product (("+" / "-") Product)* {',
' var result = $1;',
' for (var i = 0; i < $2.length; i++) {',
' if ($2[i][0] == "+") { result += $2[i][1]; }',
' if ($2[i][0] == "-") { result -= $2[i][1]; }',
' }',
' return result;',
' }',
'Expr : Sum'
].join("\n"), "Expr");
/* Test "value" rule. */
parses(parser, "0", 0);
parses(parser, "123", 123);
parses(parser, "(42+43)", 42+43);
/* Test "product" rule. */
parses(parser, "42*43", 42*43);
parses(parser, "42*43*44*45", 42*43*44*45);
/* Test "sum" rule. */
parses(parser, "42*43+44*45", 42*43+44*45);
parses(parser, "42*43+44*45+46*47+48*49", 42*43+44*45+46*47+48*49);
/* Test "expr" rule. */
parses(parser, "42+43", 42+43);
/* Complex test */
parses(parser, "(1+2)*(3+4)",(1+2)*(3+4));
});
test("non-context-free language", function() {
/* The following parsing expression grammar describes the classic
* non-context-free language { a^n b^n c^n : n >= 1 }:
*
* S &(A c) a+ B !(a/b/c)
* A a A? b
* B b B? c
*/
var parser = PEG.buildParser([
'S: &(A "c") "a"+ B !("a" / "b" / "c") { return $2.join("") + $3; }',
'A: "a" A? "b" { return $1 + $2 + $3; }',
'B: "b" B? "c" { return $1 + $2 + $3; }',
].join("\n"), "S");
parses(parser, "abc", "abc");
parses(parser, "aaabbbccc", "aaabbbccc");
doesNotParse(parser, "aabbbccc");
doesNotParse(parser, "aaaabbbccc");
doesNotParse(parser, "aaabbccc");
doesNotParse(parser, "aaabbbbccc");
doesNotParse(parser, "aaabbbcc");
doesNotParse(parser, "aaabbbcccc");
});
test("nested comments", function() {
/*
* Begin "(*"
* End "*)"
* C Begin N* End
* N C / (!Begin !End Z)
* Z any single character
*/
var parser = PEG.buildParser([
'Begin : "(*"',
'End : "*)"',
'C : Begin N* End { return $1 + $2.join("") + $3; }',
'N : C',
' / (!Begin !End Z) { return $3; }',
'Z : .'
].join("\n"), "C");
parses(parser, "(**)", "(**)");
parses(parser, "(*abc*)", "(*abc*)");
parses(parser, "(*(**)*)", "(*(**)*)");
parses(
parser,
"(*abc(*def*)ghi(*(*(*jkl*)*)*)mno*)",
"(*abc(*def*)ghi(*(*(*jkl*)*)*)mno*)"
);
});
/* ===== Grammar Parser ===== */
module("Grammar Parser");
with (PEG.Grammar) {
var literalEmpty = new Literal("");
var literalAbcd = new Literal("abcd");
var literalEfgh = new Literal("efgh");
var literalIjkl = new Literal("ijkl");
var choice = new Choice([literalAbcd, literalEmpty]);
var notAbcd = new NotPredicate(literalAbcd);
var notEfgh = new NotPredicate(literalEfgh);
var notIjkl = new NotPredicate(literalIjkl);
var sequenceEmpty = new Sequence([]);
var sequenceNots = new Sequence([notAbcd, notEfgh, notIjkl]);
var sequenceLiterals = new Sequence([literalAbcd, literalEfgh, literalIjkl]);
function oneRuleGrammar(expression) {
return { start: new PEG.Grammar.Rule("start", null, expression) };
}
var simpleGrammar = oneRuleGrammar(new Literal("abcd"));
function identifierGrammar(identifier) {
return oneRuleGrammar(new PEG.Grammar.RuleRef(identifier));
}
function literalGrammar(literal) {
return oneRuleGrammar(new PEG.Grammar.Literal(literal));
}
function classGrammar(chars) {
return oneRuleGrammar(new PEG.Grammar.Choice(
PEG.ArrayUtils.map(
chars.split(""),
function(char) { return new PEG.Grammar.Literal(char); }
)
));
}
var anyGrammar = oneRuleGrammar(new Any());
function actionGrammar(action) {
return oneRuleGrammar(new PEG.Grammar.Action(new PEG.Grammar.Literal("a"), action));
}
/* Canonical grammar is "a: \"abcd\";\nb: \"efgh\";\nc: \"ijkl\";". */
test("parses grammar", function() {
grammarParserParses('a: "abcd"', { a: new Rule("a", null, literalAbcd) });
grammarParserParses(
'a: "abcd"\nb: "efgh"\nc: "ijkl"',
{
a: new Rule("a", null, literalAbcd),
b: new Rule("b", null, literalEfgh),
c: new Rule("c", null, literalIjkl)
}
);
});
/* Canonical rule is "a: \"abcd\"". */
test("parses rule", function() {
grammarParserParses(
'start: "abcd" / "efgh" / "ijkl"',
oneRuleGrammar(new Choice([literalAbcd, literalEfgh, literalIjkl]))
);
grammarParserParses(
'start "start rule": "abcd" / "efgh" / "ijkl"',
{
start:
new Rule(
"start",
"start rule",
new Choice([literalAbcd, literalEfgh, literalIjkl])
)
}
);
});
/* Canonical expression is "\"abcd\" / \"efgh\" / \"ijkl\"". */
test("parses expression", function() {
grammarParserParses(
'start: "abcd" / "efgh" / "ijkl"',
oneRuleGrammar(new Choice([literalAbcd, literalEfgh, literalIjkl]))
);
});
/* Canonical choice is "\"abcd\" / \"efgh\" / \"ijkl\"". */
test("parses choice", function() {
grammarParserParses(
'start: "abcd" "efgh" "ijkl"',
oneRuleGrammar(sequenceLiterals)
);
grammarParserParses(
'start: "abcd" "efgh" "ijkl" / "abcd" "efgh" "ijkl" / "abcd" "efgh" "ijkl"',
oneRuleGrammar(new Choice([
sequenceLiterals,
sequenceLiterals,
sequenceLiterals
]))
);
});
/* Canonical sequence is "\"abcd\" \"efgh\" \"ijkl\"". */
test("parses sequence", function() {
grammarParserParses(
'start: { code }',
oneRuleGrammar(new Action(sequenceEmpty, " code "))
);
grammarParserParses(
'start: !"abcd" { code }',
oneRuleGrammar(new Action(notAbcd, " code "))
);
grammarParserParses(
'start: !"abcd" !"efgh" !"ijkl" { code }',
oneRuleGrammar(new Action(sequenceNots, " code "))
);
grammarParserParses('start: ', oneRuleGrammar(sequenceEmpty));
grammarParserParses('start: !"abcd"', oneRuleGrammar(notAbcd));
grammarParserParses(
'start: !"abcd" !"efgh" !"ijkl"',
oneRuleGrammar(sequenceNots)
);
});
/* Canonical prefixed is "!\"abcd\"". */
test("parses prefixed", function() {
grammarParserParses(
'start: &"abcd"?',
oneRuleGrammar(new NotPredicate(new NotPredicate(choice)))
);
grammarParserParses('start: !"abcd"?', oneRuleGrammar(new NotPredicate(choice)));
grammarParserParses('start: "abcd"?', oneRuleGrammar(choice));
});
/* Canonical suffixed is "\"abcd\"?". */
test("parses suffixed", function() {
grammarParserParses('start: "abcd"?', oneRuleGrammar(choice));
grammarParserParses('start: "abcd"*', oneRuleGrammar(new ZeroOrMore(literalAbcd)));
grammarParserParses(
'start: "abcd"+',
oneRuleGrammar(new Action(
new Sequence([literalAbcd, new ZeroOrMore(literalAbcd)]),
function(first, rest) { return [first].concat(rest); }
))
);
grammarParserParses('start: "abcd"', literalGrammar("abcd"));
});
/* Canonical primary is "\"abcd\"". */
test("parses primary", function() {
grammarParserParses('start: a', identifierGrammar("a"));
grammarParserParses('start: "abcd"', literalGrammar("abcd"));
grammarParserParses('start: .', anyGrammar);
grammarParserParses('start: [a-d]', classGrammar("abcd"));
grammarParserParses('start: ("abcd")', literalGrammar("abcd"));
});
/* Canonical action is "{ code }". */
test("parses action", function() {
grammarParserParses('start: "a" { code }', actionGrammar(" code "));
});
/* Canonical braced is "{ code }". */
test("parses braced", function() {
grammarParserParses('start: "a" {}', actionGrammar(""));
grammarParserParses('start: "a" {a}', actionGrammar("a"));
grammarParserParses('start: "a" {{a}}', actionGrammar("{a}"));
grammarParserParses('start: "a" {aaa}', actionGrammar("aaa"));
});
/* Trivial character rules are not tested. */
/* Canonical identifier is "a". */
test("parses identifier", function() {
grammarParserParses('start: a', identifierGrammar("a"));
grammarParserParses('start: z', identifierGrammar("z"));
grammarParserParses('start: A', identifierGrammar("A"));
grammarParserParses('start: Z', identifierGrammar("Z"));
grammarParserParses('start: _', identifierGrammar("_"));
grammarParserParses('start: $', identifierGrammar("$"));
grammarParserParses('start: aa', identifierGrammar("aa"));
grammarParserParses('start: az', identifierGrammar("az"));
grammarParserParses('start: aA', identifierGrammar("aA"));
grammarParserParses('start: aZ', identifierGrammar("aZ"));
grammarParserParses('start: a0', identifierGrammar("a0"));
grammarParserParses('start: a9', identifierGrammar("a9"));
grammarParserParses('start: a_', identifierGrammar("a_"));
grammarParserParses('start: a$', identifierGrammar("a$"));
grammarParserParses('start: abcd', identifierGrammar("abcd"));
grammarParserParses('start: a\n', identifierGrammar("a"));
});
/* Canonical literal is "\"abcd\"". */
test("parses literal", function() {
grammarParserParses('start: "abcd"', literalGrammar("abcd"));
grammarParserParses("start: 'abcd'", literalGrammar("abcd"));
});
/* Canonical doubleQuotedLiteral is "\"abcd\"". */
test("parses doubleQuotedLiteral", function() {
grammarParserParses('start: ""', literalGrammar(""));
grammarParserParses('start: "a"', literalGrammar("a"));
grammarParserParses('start: "abc"', literalGrammar("abc"));
grammarParserParses('start: "abcd"\n', literalGrammar("abcd"));
});
/* Canonical doubleQuotedCharacter is "a". */
test("parses doubleQuotedCharacter", function() {
grammarParserParses('start: "a"', literalGrammar("a"));
grammarParserParses('start: "\\n"', literalGrammar("\n"));
grammarParserParses('start: "\\0"', literalGrammar("\0"));
grammarParserParses('start: "\\x00"', literalGrammar("\x00"));
grammarParserParses('start: "\\u0120"', literalGrammar("\u0120"));
grammarParserParses('start: "\\\n"', literalGrammar("\n"));
});
/* Canonical simpleDoubleQuotedCharacter is "a". */
test("parses simpleDoubleQuotedCharacter", function() {
grammarParserParses('start: "a"', literalGrammar("a"));
grammarParserParses('start: "\'"', literalGrammar("'"));
grammarParserDoesNotParse('start: """');
grammarParserDoesNotParse('start: "\\"');
grammarParserDoesNotParse('start: "\n"');
grammarParserDoesNotParse('start: "\r"');
grammarParserDoesNotParse('start: "\u2028"');
grammarParserDoesNotParse('start: "\u2029"');
});
/* Canonical singleQuotedLiteral is "'abcd'". */
test("parses singleQuotedLiteral", function() {
grammarParserParses("start: ''", literalGrammar(""));
grammarParserParses("start: 'a'", literalGrammar("a"));
grammarParserParses("start: 'abc'", literalGrammar("abc"));
grammarParserParses("start: 'abcd'\n", literalGrammar("abcd"));
});
/* Canonical singleQuotedCharacter is "a". */
test("parses singleQuotedCharacter", function() {
grammarParserParses("start: 'a'", literalGrammar("a"));
grammarParserParses("start: '\\n'", literalGrammar("\n"));
grammarParserParses("start: '\\0'", literalGrammar("\0"));
grammarParserParses("start: '\\x00'", literalGrammar("\x00"));
grammarParserParses("start: '\\u0120'", literalGrammar("\u0120"));
grammarParserParses("start: '\\\n'", literalGrammar("\n"));
});
/* Canonical simpleSingleQuotedCharacter is "a". */
test("parses simpleSingleQuotedCharacter", function() {
grammarParserParses("start: 'a'", literalGrammar("a"));
grammarParserParses("start: '\"'", literalGrammar("\""));
grammarParserDoesNotParse("start: '''");
grammarParserDoesNotParse("start: '\\'");
grammarParserDoesNotParse("start: '\n'");
grammarParserDoesNotParse("start: '\r'");
grammarParserDoesNotParse("start: '\u2028'");
grammarParserDoesNotParse("start: '\u2029'");
});
/* Canonical class is "[a-d]". */
test("parses classCharacterRange", function() {
grammarParserParses("start: []", classGrammar(""));
grammarParserParses("start: [a-d]", classGrammar("abcd"));
grammarParserParses("start: [a]", classGrammar("a"));
grammarParserParses("start: [a-de-hi-l]", classGrammar("abcdefghijkl"));
grammarParserParses("start: [a-d]\n", classGrammar("abcd"));
});
/* Canonical classCharacterRange is "a-d". */
test("parses classCharacterRange", function() {
grammarParserParses("start: [a-d]", classGrammar("abcd"));
grammarParserParses("start: [a-a]", classGrammar("a"));
grammarParserDoesNotParse("start: [b-a]");
});
/* Canonical classCharacter is "a". */
test("parses classCharacter", function() {
grammarParserParses("start: [a]", classGrammar("a"));
});
/* Canonical bracketDelimitedCharacter is "a". */
test("parses bracketDelimitedCharacter", function() {
grammarParserParses("start: [a]", classGrammar("a"));
grammarParserParses("start: [\\n]", classGrammar("\n"));
grammarParserParses("start: [\\0]", classGrammar("\0"));
grammarParserParses("start: [\\x00]", classGrammar("\x00"));
grammarParserParses("start: [\\u0120]", classGrammar("\u0120"));
grammarParserParses("start: [\\\n]", classGrammar("\n"));
});
/* Canonical simpleBracketDelimiedCharacter is "a". */
test("parses simpleBracketDelimitedCharacter", function() {
grammarParserParses("start: [a]", classGrammar("a"));
grammarParserParses("start: [[]", classGrammar("["));
grammarParserDoesNotParse("start: []]");
grammarParserDoesNotParse("start: [\\]");
grammarParserDoesNotParse("start: [\n]");
grammarParserDoesNotParse("start: [\r]");
grammarParserDoesNotParse("start: [\u2028]");
grammarParserDoesNotParse("start: [\u2029]");
});
/* Canonical simpleEscapeSequence is "\\n". */
test("parses simpleEscapeSequence", function() {
grammarParserParses('start: "\\\'"', literalGrammar("'"));
grammarParserParses('start: "\\""', literalGrammar("\""));
grammarParserParses('start: "\\\\"', literalGrammar("\\"));
grammarParserParses('start: "\\b"', literalGrammar("\b"));
grammarParserParses('start: "\\f"', literalGrammar("\f"));
grammarParserParses('start: "\\n"', literalGrammar("\n"));
grammarParserParses('start: "\\r"', literalGrammar("\r"));
grammarParserParses('start: "\\t"', literalGrammar("\t"));
grammarParserParses('start: "\\v"', literalGrammar("\v"));
grammarParserParses('start: "\\a"', literalGrammar("a"));
});
/* Canonical zeroEscapeSequence is "\\0". */
test("parses zeroEscapeSequence", function() {
grammarParserParses('start: "\\0"', literalGrammar("\0"));
grammarParserDoesNotParse('start: "\\00"');
grammarParserDoesNotParse('start: "\\09"');
});
/* Canonical hexEscapeSequence is "\\x00". */
test("parses hexEscapeSequence", function() {
grammarParserParses('start: "\\x00"', literalGrammar("\x00"));
grammarParserParses('start: "\\x09"', literalGrammar("\x09"));
grammarParserParses('start: "\\x0a"', literalGrammar("\x0a"));
grammarParserParses('start: "\\x0f"', literalGrammar("\x0f"));
grammarParserParses('start: "\\x0A"', literalGrammar("\x0A"));
grammarParserParses('start: "\\x0F"', literalGrammar("\x0F"));
grammarParserDoesNotParse('start: "\\x0"');
grammarParserParses('start: "\\x000"', literalGrammar("\x000"));
});
/* Canonical unicodeEscapeSequence is "\\u0120". */
test("parses unicodeEscapeSequence", function() {
grammarParserParses('start: "\\u0120"', literalGrammar("\u0120"));
grammarParserParses('start: "\\u0129"', literalGrammar("\u0129"));
grammarParserParses('start: "\\u012a"', literalGrammar("\u012a"));
grammarParserParses('start: "\\u012f"', literalGrammar("\u012f"));
grammarParserParses('start: "\\u012A"', literalGrammar("\u012A"));
grammarParserParses('start: "\\u012F"', literalGrammar("\u012F"));
grammarParserDoesNotParse('start: "\\u012"');
grammarParserParses('start: "\\u01234"', literalGrammar("\u01234"));
});
/* Canonical eolEscapeSequence is "\\\n". */
test("parses eolEscapeSequence", function() {
grammarParserParses('start: "\\\n"', literalGrammar("\n"));
grammarParserParses('start: "\\\r\n"', literalGrammar("\r\n"));
grammarParserParses('start: "\\\r"', literalGrammar("\r"));
grammarParserParses('start: "\\\u2028"', literalGrammar("\u2028"));
grammarParserParses('start: "\\\u2029"', literalGrammar("\u2029"));
});
/* Canonical __ is "\n". */
test("parses __", function() {
grammarParserParses('start:"abcd"', simpleGrammar);
grammarParserParses('start: "abcd"', simpleGrammar);
grammarParserParses('start:\n"abcd"', simpleGrammar);
grammarParserParses('start: "abcd"', simpleGrammar);
});
/* Trivial character class rules are not tested. */
/* Canonical eol is "\n". */
test("parses eol", function() {
grammarParserParses('start:\n"abcd"', simpleGrammar);
grammarParserParses('start:\r\n"abcd"', simpleGrammar);
grammarParserParses('start:\r"abcd"', simpleGrammar);
grammarParserParses('start:\u2028"abcd"', simpleGrammar);
grammarParserParses('start:\u2029"abcd"', simpleGrammar);
});
/* Canonical eolChar is "\n". */
test("parses eolChar", function() {
grammarParserParses('start:\n"abcd"', simpleGrammar);
grammarParserParses('start:\r"abcd"', simpleGrammar);
grammarParserParses('start:\u2028"abcd"', simpleGrammar);
grammarParserParses('start:\u2029"abcd"', simpleGrammar);
});
/* Canonical whitespace is " ". */
test("parses whitespace", function() {
grammarParserParses('start:\t"abcd"', simpleGrammar);
grammarParserParses('start:\v"abcd"', simpleGrammar);
grammarParserParses('start:\f"abcd"', simpleGrammar);
grammarParserParses('start: "abcd"', simpleGrammar);
grammarParserParses('start:\xA0"abcd"', simpleGrammar);
grammarParserParses('start:\uFEFF"abcd"', simpleGrammar);
grammarParserParses('start:\u1680"abcd"', simpleGrammar);
grammarParserParses('start:\u180E"abcd"', simpleGrammar);
grammarParserParses('start:\u2000"abcd"', simpleGrammar);
grammarParserParses('start:\u2001"abcd"', simpleGrammar);
grammarParserParses('start:\u2002"abcd"', simpleGrammar);
grammarParserParses('start:\u2003"abcd"', simpleGrammar);
grammarParserParses('start:\u2004"abcd"', simpleGrammar);
grammarParserParses('start:\u2005"abcd"', simpleGrammar);
grammarParserParses('start:\u2006"abcd"', simpleGrammar);
grammarParserParses('start:\u2007"abcd"', simpleGrammar);
grammarParserParses('start:\u2008"abcd"', simpleGrammar);
grammarParserParses('start:\u2009"abcd"', simpleGrammar);
grammarParserParses('start:\u200A"abcd"', simpleGrammar);
grammarParserParses('start:\u202F"abcd"', simpleGrammar);
grammarParserParses('start:\u205F"abcd"', simpleGrammar);
grammarParserParses('start:\u3000"abcd"', simpleGrammar);
});
}
})();

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>PEG.js Test Suite</title>
<link rel="stylesheet" href="../vendor/qunit/qunit.css">
<script src="../vendor/qunit/qunit.js"></script>
<script src="../lib/runtime.js"></script>
<script src="../lib/compiler.js"></script>
<script src="runtime-test.js"></script>
<script src="compiler-test.js"></script>
</head>
<body>
<h1 id="qunit-header">PEG.js Test Suite</h1>
<h2 id="qunit-banner"></h2>
<div id="qunit-testrunner-toolbar"></div>
<h2 id="qunit-userAgent"></h2>
<ol id="qunit-tests"></ol>
</body>
</html>

@ -0,0 +1,27 @@
(function() {
/* ===== PEG.ArrayUtils ===== */
module("PEG.ArrayUtils");
test("map", function() {
function square(x) { return x * x; }
deepEqual(PEG.ArrayUtils.map([], square), []);
deepEqual(PEG.ArrayUtils.map([1, 2, 3], square), [1, 4, 9]);
});
/* ===== PEG.StringUtils ===== */
module("PEG.StringUtils");
test("quote", function() {
strictEqual(PEG.StringUtils.quote(""), '""');
strictEqual(PEG.StringUtils.quote("abcd"), '"abcd"');
strictEqual(
PEG.StringUtils.quote("\"\\\r\u2028\u2029\n\"\\\r\u2028\u2029\n"),
'"\\\"\\\\\\r\\u2028\\u2029\\n\\\"\\\\\\r\\u2028\\u2029\\n"'
);
});
})();

@ -0,0 +1,119 @@
ol#qunit-tests {
font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial;
margin:0;
padding:0;
list-style-position:inside;
font-size: smaller;
}
ol#qunit-tests li{
padding:0.4em 0.5em 0.4em 2.5em;
border-bottom:1px solid #fff;
font-size:small;
list-style-position:inside;
}
ol#qunit-tests li ol{
box-shadow: inset 0px 2px 13px #999;
-moz-box-shadow: inset 0px 2px 13px #999;
-webkit-box-shadow: inset 0px 2px 13px #999;
margin-top:0.5em;
margin-left:0;
padding:0.5em;
background-color:#fff;
border-radius:15px;
-moz-border-radius: 15px;
-webkit-border-radius: 15px;
}
ol#qunit-tests li li{
border-bottom:none;
margin:0.5em;
background-color:#fff;
list-style-position: inside;
padding:0.4em 0.5em 0.4em 0.5em;
}
ol#qunit-tests li li.pass{
border-left:26px solid #C6E746;
background-color:#fff;
color:#5E740B;
}
ol#qunit-tests li li.fail{
border-left:26px solid #EE5757;
background-color:#fff;
color:#710909;
}
ol#qunit-tests li.pass{
background-color:#D2E0E6;
color:#528CE0;
}
ol#qunit-tests li.fail{
background-color:#EE5757;
color:#000;
}
ol#qunit-tests li strong {
cursor:pointer;
}
h1#qunit-header{
background-color:#0d3349;
margin:0;
padding:0.5em 0 0.5em 1em;
color:#fff;
font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial;
border-top-right-radius:15px;
border-top-left-radius:15px;
-moz-border-radius-topright:15px;
-moz-border-radius-topleft:15px;
-webkit-border-top-right-radius:15px;
-webkit-border-top-left-radius:15px;
text-shadow: rgba(0, 0, 0, 0.5) 4px 4px 1px;
}
h2#qunit-banner{
font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial;
height:5px;
margin:0;
padding:0;
}
h2#qunit-banner.qunit-pass{
background-color:#C6E746;
}
h2#qunit-banner.qunit-fail, #qunit-testrunner-toolbar {
background-color:#EE5757;
}
#qunit-testrunner-toolbar {
font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial;
padding:0;
/*width:80%;*/
padding:0em 0 0.5em 2em;
font-size: small;
}
h2#qunit-userAgent {
font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial;
background-color:#2b81af;
margin:0;
padding:0;
color:#fff;
font-size: small;
padding:0.5em 0 0.5em 2.5em;
text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
}
p#qunit-testresult{
font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial;
margin:0;
font-size: small;
color:#2b81af;
border-bottom-right-radius:15px;
border-bottom-left-radius:15px;
-moz-border-radius-bottomright:15px;
-moz-border-radius-bottomleft:15px;
-webkit-border-bottom-right-radius:15px;
-webkit-border-bottom-left-radius:15px;
background-color:#D2E0E6;
padding:0.5em 0.5em 0.5em 2.5em;
}
strong b.fail{
color:#710909;
}
strong b.pass{
color:#5E740B;
}

1062
vendor/qunit/qunit.js vendored

File diff suppressed because it is too large Load Diff

BIN
vendor/rhino/js.jar vendored

Binary file not shown.
Loading…
Cancel
Save