From e69d7b1787c7db7e3ed5c0d34605d04b559635a3 Mon Sep 17 00:00:00 2001 From: Peter Burns Date: Sat, 11 Feb 2012 13:58:47 -0800 Subject: [PATCH 1/8] stopped jsparse from polluting the toplevel namespace --- jsparse.js | 315 +++++++++++++++++++++++++++++------------------------ tests.js | 259 ++++++++++++++++++++++--------------------- 2 files changed, 307 insertions(+), 267 deletions(-) diff --git a/jsparse.js b/jsparse.js index d62f22b..5d16e81 100644 --- a/jsparse.js +++ b/jsparse.js @@ -22,89 +22,99 @@ // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -function foldl(f, initial, seq) { +var jsparse; +try { + jsparse = exports; +} catch(e) { + jsparse = {}; +} + +jsparse.foldl = function foldl(f, initial, seq) { for(var i=0; i< seq.length; ++i) initial = f(initial, seq[i]); return initial; } -var memoize = true; +jsparse.memoize = true; -function ParseState(input, index) { - this.input = input; - this.index = index || 0; - this.length = input.length - this.index; - this.cache = { }; - return this; -} +jsparse.ParseState = (function() { + function ParseState(input, index) { + this.input = input; + this.index = index || 0; + this.length = input.length - this.index; + this.cache = { }; + return this; + } -ParseState.prototype.from = function(index) { - var r = new ParseState(this.input, this.index + index); - r.cache = this.cache; - r.length = this.length - index; - return r; -} + ParseState.prototype.from = function(index) { + var r = new ParseState(this.input, this.index + index); + r.cache = this.cache; + r.length = this.length - index; + return r; + } -ParseState.prototype.substring = function(start, end) { - return this.input.substring(start + this.index, (end || this.length) + this.index); -} + ParseState.prototype.substring = function(start, end) { + return this.input.substring(start + this.index, (end || this.length) + this.index); + } -ParseState.prototype.trimLeft = function() { - var s = this.substring(0); - var m = s.match(/^\s+/); - return m ? this.from(m[0].length) : this; -} + ParseState.prototype.trimLeft = function() { + var s = this.substring(0); + var m = s.match(/^\s+/); + return m ? this.from(m[0].length) : this; + } -ParseState.prototype.at = function(index) { - return this.input.charAt(this.index + index); -} + ParseState.prototype.at = function(index) { + return this.input.charAt(this.index + index); + } -ParseState.prototype.toString = function() { - return 'PS"' + this.substring(0) + '"'; -} + ParseState.prototype.toString = function() { + return 'PS"' + this.substring(0) + '"'; + } -ParseState.prototype.getCached = function(pid) { - if(!memoize) - return false; + ParseState.prototype.getCached = function(pid) { + if(!jsparse.memoize) + return false; - var p = this.cache[pid]; - if(p) - return p[this.index]; - else - return false; -} + var p = this.cache[pid]; + if(p) + return p[this.index]; + else + return false; + } -ParseState.prototype.putCached = function(pid, cached) { - if(!memoize) - return false; + ParseState.prototype.putCached = function(pid, cached) { + if(!jsparse.memoize) + return false; - var p = this.cache[pid]; - if(p) - p[this.index] = cached; - else { - p = this.cache[pid] = { }; - p[this.index] = cached; + var p = this.cache[pid]; + if(p) + p[this.index] = cached; + else { + p = this.cache[pid] = { }; + p[this.index] = cached; + } } -} + return ParseState; +})() -function ps(str) { - return new ParseState(str); +jsparse.ps = function ps(str) { + return new jsparse.ParseState(str); } // 'r' is the remaining string to be parsed. // 'matched' is the portion of the string that // was successfully matched by the parser. // 'ast' is the AST returned by the successfull parse. -function make_result(r, matched, ast) { - return { remaining: r, matched: matched, ast: ast }; +jsparse.make_result = function make_result(r, matched, ast) { + return { remaining: r, matched: matched, ast: ast }; } -var parser_id = 0; +jsparse.parser_id = 0; // 'token' is a parser combinator that given a string, returns a parser // that parses that string value. The AST contains the string that was parsed. -function token(s) { - var pid = parser_id++; +jsparse.token = function token(s) { + var pid = jsparse.parser_id++; return function(state) { var savedState = state; var cached = savedState.getCached(pid); @@ -123,8 +133,8 @@ function token(s) { // Like 'token' but for a single character. Returns a parser that given a string // containing a single character, parses that character value. -function ch(c) { - var pid = parser_id++; +jsparse.ch = function ch(c) { + var pid = jsparse.parser_id++; return function(state) { var savedState = state; var cached = savedState.getCached(pid); @@ -143,8 +153,8 @@ function ch(c) { // 'range' is a parser combinator that returns a single character parser // (similar to 'ch'). It parses single characters that are in the inclusive // range of the 'lower' and 'upper' bounds ("a" to "z" for example). -function range(lower, upper) { - var pid = parser_id++; +jsparse.range = function range(lower, upper) { + var pid = jsparse.parser_id++; return function(state) { var savedState = state; var cached = savedState.getCached(pid); @@ -167,15 +177,15 @@ function range(lower, upper) { // Helper function to convert string literals to token parsers // and perform other implicit parser conversions. -function toParser(p) { - return (typeof(p) == "string") ? token(p) : p; +jsparse.toParser = function toParser(p) { + return (typeof(p) == "string") ? jsparse.token(p) : p; } // Parser combinator that returns a parser that // skips whitespace before applying parser. -function whitespace(p) { - var p = toParser(p); - var pid = parser_id++; +jsparse.whitespace = function whitespace(p) { + var p = jsparse.toParser(p); + var pid = jsparse.parser_id++; return function(state) { var savedState = state; var cached = savedState.getCached(pid); @@ -190,9 +200,9 @@ function whitespace(p) { // Parser combinator that passes the AST generated from the parser 'p' // to the function 'f'. The result of 'f' is used as the AST in the result. -function action(p, f) { - var p = toParser(p); - var pid = parser_id++; +jsparse.action = function action(p, f) { + var p = jsparse.toParser(p); + var pid = jsparse.parser_id++; return function(state) { var savedState = state; var cached = savedState.getCached(pid); @@ -214,8 +224,8 @@ function action(p, f) { // Given a parser that produces an array as an ast, returns a // parser that produces an ast with the array joined by a separator. -function join_action(p, sep) { - return action(p, function(ast) { return ast.join(sep); }); +jsparse.join_action = function join_action(p, sep) { + return jsparse.action(p, function(ast) { return ast.join(sep); }); } // Given an ast of the form [ Expression, [ a, b, ...] ], convert to @@ -227,8 +237,8 @@ function join_action(p, sep) { // MemberExpression [ Expression ] // MemberExpression . Identifier // new MemberExpression Arguments -function left_factor(ast) { - return foldl(function(v, action) { +jsparse.left_factor = function left_factor(ast) { + return jsparse.foldl(function(v, action) { return [ v, action ]; }, ast[0], @@ -237,16 +247,16 @@ function left_factor(ast) { // Return a parser that left factors the ast result of the original // parser. -function left_factor_action(p) { - return action(p, left_factor); +jsparse.left_factor_action = function left_factor_action(p) { + return jsparse.action(p, jsparse.left_factor); } // 'negate' will negate a single character parser. So given 'ch("a")' it will successfully // parse any character except for 'a'. Or 'negate(range("a", "z"))' will successfully parse // anything except the lowercase characters a-z. -function negate(p) { - var p = toParser(p); - var pid = parser_id++; +jsparse.negate = function negate(p) { + var p = jsparse.toParser(p); + var pid = jsparse.parser_id++; return function(state) { var savedState = state; var cached = savedState.getCached(pid); @@ -256,7 +266,7 @@ function negate(p) { if(state.length >= 1) { var r = p(state); if(!r) - cached = make_result(state.from(1), state.at(0), state.at(0)); + cached = jsparse.make_result(state.from(1), state.at(0), state.at(0)); else cached = false; } @@ -268,27 +278,27 @@ function negate(p) { }; } -// 'end_p' is a parser that is successful if the input string is empty (ie. end of parse). -function end_p(state) { +// 'end' is a parser that is successful if the input string is empty (ie. end of parse). +jsparse.end = function end_p(state) { if(state.length == 0) - return make_result(state, undefined, undefined); + return jsparse.make_result(state, undefined, undefined); else return false; } -// 'nothing_p' is a parser that always fails. -function nothing_p(state) { +// 'nothing' is a parser that always fails. +jsparse.nothing = function nothing_p(state) { return false; } // 'sequence' is a parser combinator that processes a number of parsers in sequence. // It can take any number of arguments, each one being a parser. The parser that 'sequence' // returns succeeds if all the parsers in the sequence succeeds. It fails if any of them fail. -function sequence() { +jsparse.sequence = function sequence() { var parsers = []; for(var i = 0; i < arguments.length; ++i) - parsers.push(toParser(arguments[i])); - var pid = parser_id++; + parsers.push(jsparse.toParser(arguments[i])); + var pid = jsparse.parser_id++; return function(state) { var savedState = state; var cached = savedState.getCached(pid); @@ -314,7 +324,7 @@ function sequence() { } } if(i == parsers.length) { - cached = make_result(state, matched, ast); + cached = jsparse.make_result(state, matched, ast); } else cached = false; @@ -324,23 +334,23 @@ function sequence() { } // Like sequence, but ignores whitespace between individual parsers. -function wsequence() { +jsparse.wsequence = function wsequence() { var parsers = []; for(var i=0; i < arguments.length; ++i) { - parsers.push(whitespace(toParser(arguments[i]))); + parsers.push(jsparse.whitespace(jsparse.toParser(arguments[i]))); } - return sequence.apply(null, parsers); + return jsparse.sequence.apply(null, parsers); } // 'choice' is a parser combinator that provides a choice between other parsers. // It takes any number of parsers as arguments and returns a parser that will try // each of the given parsers in order. The first one that succeeds results in a // successfull parse. It fails if all parsers fail. -function choice() { +jsparse.choice = function choice() { var parsers = []; for(var i = 0; i < arguments.length; ++i) - parsers.push(toParser(arguments[i])); - var pid = parser_id++; + parsers.push(jsparse.toParser(arguments[i])); + var pid = jsparse.parser_id++; return function(state) { var savedState = state; var cached = savedState.getCached(pid); @@ -368,10 +378,10 @@ function choice() { // It returns a parser that succeeds if 'p1' matches and 'p2' does not, or // 'p1' matches and the matched text is longer that p2's. // Useful for things like: butnot(IdentifierName, ReservedWord) -function butnot(p1,p2) { - var p1 = toParser(p1); - var p2 = toParser(p2); - var pid = parser_id++; +jsparse.butnot = function butnot(p1,p2) { + var p1 = jsparse.toParser(p1); + var p2 = jsparse.toParser(p2); + var pid = jsparse.parser_id++; // match a but not b. if both match and b's matched text is shorter // than a's, a failed match is made @@ -405,10 +415,10 @@ function butnot(p1,p2) { // 'difference' is a parser combinator that takes two parsers, 'p1' and 'p2'. // It returns a parser that succeeds if 'p1' matches and 'p2' does not. If // both match then if p2's matched text is shorter than p1's it is successfull. -function difference(p1,p2) { - var p1 = toParser(p1); - var p2 = toParser(p2); - var pid = parser_id++; +jsparse.difference = function difference(p1,p2) { + var p1 = jsparse.toParser(p1); + var p2 = jsparse.toParser(p2); + var pid = jsparse.parser_id++; // match a but not b. if both match and b's matched text is shorter // than a's, a successfull match is made @@ -437,10 +447,10 @@ function difference(p1,p2) { // 'xor' is a parser combinator that takes two parsers, 'p1' and 'p2'. // It returns a parser that succeeds if 'p1' or 'p2' match but fails if // they both match. -function xor(p1, p2) { - var p1 = toParser(p1); - var p2 = toParser(p2); - var pid = parser_id++; +jsparse.xor = function xor(p1, p2) { + var p1 = jsparse.toParser(p1); + var p2 = jsparse.toParser(p2); + var pid = jsparse.parser_id++; // match a or b but not both return function(state) { @@ -462,9 +472,9 @@ function xor(p1, p2) { // A parser combinator that takes one parser. It returns a parser that // looks for zero or more matches of the original parser. -function repeat0(p) { - var p = toParser(p); - var pid = parser_id++; +jsparse.repeat0 = function repeat0(p) { + var p = jsparse.toParser(p); + var pid = jsparse.parser_id++; return function(state) { var savedState = state; @@ -483,7 +493,7 @@ function repeat0(p) { break; state = result.remaining; } - cached = make_result(state, matched, ast); + cached = jsparse.make_result(state, matched, ast); savedState.putCached(pid, cached); return cached; } @@ -491,9 +501,9 @@ function repeat0(p) { // A parser combinator that takes one parser. It returns a parser that // looks for one or more matches of the original parser. -function repeat1(p) { - var p = toParser(p); - var pid = parser_id++; +jsparse.repeat1 = function repeat1(p) { + var p = jsparse.toParser(p); + var pid = jsparse.parser_id++; return function(state) { var savedState = state; @@ -515,7 +525,7 @@ function repeat1(p) { state = result.remaining; result = p(state); } - cached = make_result(state, matched, ast); + cached = jsparse.make_result(state, matched, ast); } savedState.putCached(pid, cached); return cached; @@ -524,16 +534,16 @@ function repeat1(p) { // A parser combinator that takes one parser. It returns a parser that // matches zero or one matches of the original parser. -function optional(p) { - var p = toParser(p); - var pid = parser_id++; +jsparse.optional = function optional(p) { + var p = jsparse.toParser(p); + var pid = jsparse.parser_id++; return function(state) { var savedState = state; var cached = savedState.getCached(pid); if(cached) return cached; var r = p(state); - cached = r || make_result(state, "", false); + cached = r || jsparse.make_result(state, "", false); savedState.putCached(pid, cached); return cached; } @@ -543,14 +553,14 @@ function optional(p) { // ignores its result. This can be useful for parsing literals that you // don't want to appear in the ast. eg: // sequence(expect("("), Number, expect(")")) => ast: Number -function expect(p) { - return action(p, function(ast) { return undefined; }); +jsparse.expect = function expect(p) { + return jsparse.action(p, function(ast) { return undefined; }); } -function chain(p, s, f) { - var p = toParser(p); +jsparse.chain = function chain(p, s, f) { + var p = jsparse.toParser(p); - return action(sequence(p, repeat0(action(sequence(s, p), f))), + return jsparse.action(jsparse.sequence(p, jsparse.repeat0(jsparse.action(jsparse.sequence(s, p), f))), function(ast) { return [ast[0]].concat(ast[1]); }); } @@ -559,46 +569,46 @@ function chain(p, s, f) { // of the form: function(lhs,rhs) { return x; } // Where 'x' is the result of applying some operation to the lhs and rhs AST's from the item // parser. -function chainl(p, s) { - var p = toParser(p); - return action(sequence(p, repeat0(sequence(s, p))), +jsparse.chainl = function chainl(p, s) { + var p = jsparse.toParser(p); + return jsparse.action(jsparse.sequence(p, jsparse.repeat0(jsparse.sequence(s, p))), function(ast) { - return foldl(function(v, action) { return action[0](v, action[1]); }, ast[0], ast[1]); + return jsparse.foldl(function(v, action) { return action[0](v, action[1]); }, ast[0], ast[1]); }); } // A parser combinator that returns a parser that matches lists of things. The parser to // match the list item and the parser to match the seperator need to // be provided. The AST is the array of matched items. -function list(p, s) { - return chain(p, s, function(ast) { return ast[1]; }); +jsparse.list = function list(p, s) { + return jsparse.chain(p, s, function(ast) { return ast[1]; }); } // Like list, but ignores whitespace between individual parsers. -function wlist() { +jsparse.wlist = function wlist() { var parsers = []; for(var i=0; i < arguments.length; ++i) { - parsers.push(whitespace(arguments[i])); + parsers.push(jsparse.whitespace(arguments[i])); } - return list.apply(null, parsers); + return jsparse.list.apply(null, parsers); } // A parser that always returns a zero length match -function epsilon_p(state) { - return make_result(state, "", undefined); +jsparse.epsilon_p = function epsilon_p(state) { + return jsparse.make_result(state, "", undefined); } // Allows attaching of a function anywhere in the grammer. If the function returns // true then parse succeeds otherwise it fails. Can be used for testing if a symbol // is in the symbol table, etc. -function semantic(f) { - var pid = parser_id++; +jsparse.semantic = function semantic(f) { + var pid = jsparse.parser_id++; return function(state) { var savedState = state; var cached = savedState.getCached(pid); if(cached) return cached; - cached = f() ? make_result(state, "", undefined) : false; + cached = f() ? jsparse.make_result(state, "", undefined) : false; savedState.putCached(pid, cached); return cached; } @@ -611,16 +621,16 @@ function semantic(f) { // It succeeds if 'p' succeeds and fails if 'p' fails. It never // consume any input however, and doesn't put anything in the resulting // AST. -function and(p) { - var p = toParser(p); - var pid = parser_id++; +jsparse.and = function and(p) { + var p = jsparse.toParser(p); + var pid = jsparse.parser_id++; return function(state) { var savedState = state; var cached = savedState.getCached(pid); if(cached) return cached; var r = p(state); - cached = r ? make_result(state, "", undefined) : false; + cached = r ? jsparse.make_result(state, "", undefined) : false; savedState.putCached(pid, cached); return cached; } @@ -640,18 +650,37 @@ function and(p) { // parses a+b // parses a++b // -function not(p) { - var p = toParser(p); - var pid = parser_id++; +jsparse.not = function not(p) { + var p = jsparse.toParser(p); + var pid = jsparse.parser_id++; return function(state) { var savedState = state; var cached = savedState.getCached(pid); if(cached) return cached; - cached = p(state) ? false : make_result(state, "", undefined); + cached = p(state) ? false : jsparse.make_result(state, "", undefined); savedState.putCached(pid, cached); return cached; } } +// For ease of use, it's sometimes nice to be able to not have to prefix all +// of the jsparse functions with `jsparse.` and since the original version of +// this library put everything in the toplevel namespace, this makes it easy +// to use this version with old code. +// +// The only caveat there is that changing `memoize` MUST be done on +// jsparse.memoize +// +// Typical usage: +// jsparse.inject_into(window) +// +jsparse.inject_into = function inject_into(into) { + for (var key in jsparse) { + if (typeof jsparse[key] === 'function') { + into[key] = jsparse[key]; + } + } +} + diff --git a/tests.js b/tests.js index 84eb706..3a49b62 100644 --- a/tests.js +++ b/tests.js @@ -1,15 +1,15 @@ // Copyright (C) 2007 Chris Double. -// +// // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: -// +// // 1. Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. -// +// // 2. Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. -// +// // THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE @@ -22,8 +22,15 @@ // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // +var jsparse = require("./jsparse"); + var passed = []; var failed = []; + +function print(str) { + console.log(str); +} + function assertTrue(msg, test) { if(test) passed.push(msg); @@ -46,14 +53,14 @@ function assertFalse(msg, test) { } function assertEqual(msg, value1, value2) { - if(value1 == value2) + if(value1 == value2) passed.push(msg); else failed.push(msg); } function assertNotEqual(msg, value1, value2) { - if(value1 != value2) + if(value1 != value2) passed.push(msg); else failed.push(msg); @@ -61,50 +68,52 @@ function assertNotEqual(msg, value1, value2) { function assertFullyParsed(parser, string) { var msg = parser + " did not fully parse: " + string; - try { - var result = eval(parser)(ps(string)); - if(result && result.remaining.length == 0) - passed.push(msg); - else - failed.push(msg); - } - catch(e) { - failed.push(msg); - } + // try { + var result = eval(parser)(jsparse.ps(string)); + if(result && result.remaining.length == 0) + passed.push(msg); + else + failed.push(msg); + // } + // catch(e) { + // console.log(e); + // failed.push(msg); + // } } function assertParseFailed(parser, string) { var msg = parser + " succeeded but should have failed: " + string; - try { - var result = eval(parser)(ps(string)); - if(!result) - passed.push(msg); - else - failed.push(msg); - } - catch(e) { - failed.push(msg); - } + // try { + var result = eval(parser)(jsparse.ps(string)); + if(!result) + passed.push(msg); + else + failed.push(msg); + // } + // catch(e) { + // console.log(e); + // failed.push(msg); + // } } function assertParseMatched(parser, string, expected) { var msg = parser + " parse did not match: " + string; - try { - var result = eval(parser)(ps(string)); - if(result && result.matched == expected) - passed.push(msg); - else - failed.push(msg + " got [" + result.matched + "] expected [" + expected + "]"); - } - catch(e) { - failed.push(msg); - } + // try { + var result = eval(parser)(jsparse.ps(string)); + if(result && result.matched == expected) + passed.push(msg); + else + failed.push(msg + " got [" + result.matched + "] expected [" + expected + "]"); + // } + // catch(e) { + // failed.push(msg); + // } } function time(func) { - var start = java.lang.System.currentTimeMillis(); + var start = +new Date(); var r = func(); - var end = java.lang.System.currentTimeMillis(); + var end = +new Date(); print("Time: " + (end-start) + "ms"); return r; } @@ -114,104 +123,106 @@ function runTests(func) { failed = []; func(); var total = passed.length + failed.length; - for(var i=0; i < failed.length; ++i) + for(var i=0; i < failed.length; ++i) print(failed[i]); print(total + " tests: " + passed.length + " passed, " + failed.length + " failed"); } function ParserTests() { // Token - assertFullyParsed("token('a')", "a"); - assertFullyParsed("token('abcd')", "abcd"); - assertParseMatched("token('abcd')", "abcdef", "abcd"); - assertParseFailed("token('a')", "b"); + assertFullyParsed("jsparse.token('a')", "a"); + assertFullyParsed("jsparse.token('abcd')", "abcd"); + assertParseMatched("jsparse.token('abcd')", "abcdef", "abcd"); + assertParseFailed("jsparse.token('a')", "b"); // ch - assertParseMatched("ch('a')", "abcd", "a"); - assertParseFailed("ch('a')", "bcd"); + assertParseMatched("jsparse.ch('a')", "abcd", "a"); + assertParseFailed("jsparse.ch('a')", "bcd"); - // range + // // range for(var i=0; i < 10; ++i) { - assertParseMatched("range('0','9')", "" + i, i); + assertParseMatched("jsparse.range('0','9')", "" + i, i); } - assertParseFailed("range('0','9')", "a"); - - // whitespace - assertFullyParsed("whitespace(token('ab'))", "ab"); - assertFullyParsed("whitespace(token('ab'))", " ab"); - assertFullyParsed("whitespace(token('ab'))", " ab"); - assertFullyParsed("whitespace(token('ab'))", " ab"); - - // negate - assertFullyParsed("negate(ch('a'))", "b"); - assertParseFailed("negate(ch('a'))", "a"); - - // end_p - assertParseFailed("end_p", "ab"); - assertFullyParsed("end_p", ""); - - // nothing_p - assertParseFailed("nothing_p", "abcd"); - assertParseFailed("nothing_p", ""); - - // sequence - assertFullyParsed("sequence('a', 'b')", "ab"); - assertParseFailed("sequence('a', 'b')", "b"); - assertParseFailed("sequence('a', 'b')", "a"); - assertParseMatched("sequence('a', whitespace('b'))", "a b", "ab"); - assertParseMatched("sequence('a', whitespace('b'))", "a b", "ab"); - assertParseMatched("sequence('a', whitespace('b'))", "ab", "ab"); - - // choice - assertFullyParsed("choice('a', 'b')", "a"); - assertFullyParsed("choice('a', 'b')", "b"); - assertParseMatched("choice('a', 'b')", "ab", "a"); - assertParseMatched("choice('a', 'b')", "bc", "b"); - - // repeat0 - assertParseMatched("repeat0(choice('a','b'))", "adef", "a"); - assertParseMatched("repeat0(choice('a','b'))", "bdef", "b"); - assertParseMatched("repeat0(choice('a','b'))", "aabbabadef", "aabbaba"); - assertParseMatched("repeat0(choice('a','b'))", "daabbabadef", ""); - - // repeat1 - assertParseMatched("repeat1(choice('a','b'))", "adef", "a"); - assertParseMatched("repeat1(choice('a','b'))", "bdef", "b"); - assertParseMatched("repeat1(choice('a','b'))", "aabbabadef", "aabbaba"); - assertParseFailed("repeat1(choice('a','b'))", "daabbabadef"); - - // optional - assertParseMatched("sequence('a', optional(choice('b','c')), 'd')", "abd", "abd"); - assertParseMatched("sequence('a', optional(choice('b','c')), 'd')", "acd", "acd"); - assertParseMatched("sequence('a', optional(choice('b','c')), 'd')", "ad", "ad"); - assertParseFailed("sequence('a', optional(choice('b','c')), 'd')", "aed"); - assertParseFailed("sequence('a', optional(choice('b','c')), 'd')", "ab"); - assertParseFailed("sequence('a', optional(choice('b','c')), 'd')", "ac"); - - // list - assertParseMatched("list(choice('1','2','3'),',')", "1,2,3", "1,2,3"); - assertParseMatched("list(choice('1','2','3'),',')", "1,3,2", "1,3,2"); - assertParseMatched("list(choice('1','2','3'),',')", "1,3", "1,3"); - assertParseMatched("list(choice('1','2','3'),',')", "3", "3"); - assertParseFailed("list(choice('1','2','3'),',')", "5,6,7"); - - // and - assertParseMatched("sequence(and('0'), '0')", "0", "0"); - assertParseFailed("sequence(and('0'), '1')", "0"); - assertParseMatched("sequence('1',and('2'))", "12", "1"); - - // not - assertParseMatched("sequence('a',choice('+','++'),'b')", "a+b", "a+b"); - assertParseFailed("sequence('a',choice('+','++'),'b')", "a++b"); - assertParseMatched("sequence('a',choice(sequence('+',not('+')),'++'),'b')", "a+b", "a+b"); - assertParseMatched("sequence('a',choice(sequence('+',not('+')),'++'),'b')", "a++b", "a++b"); - - // butnot - assertFullyParsed("butnot(range('0','9'), '6')", "1"); - assertParseFailed("butnot(range('0','9'), '6')", "6"); - assertParseFailed("butnot(range('0','9'), 'x')", "x"); - assertParseFailed("butnot(range('0','9'), 'y')", "x"); + assertParseFailed("jsparse.range('0','9')", "a"); + + // // whitespace + assertFullyParsed("jsparse.whitespace(jsparse.token('ab'))", "ab"); + assertFullyParsed("jsparse.whitespace(jsparse.token('ab'))", " ab"); + assertFullyParsed("jsparse.whitespace(jsparse.token('ab'))", " ab"); + assertFullyParsed("jsparse.whitespace(jsparse.token('ab'))", " ab"); + + // // negate + assertFullyParsed("jsparse.negate(jsparse.ch('a'))", "b"); + assertParseFailed("jsparse.negate(jsparse.ch('a'))", "a"); + + // // end_p + assertParseFailed("jsparse.end", "ab"); + assertFullyParsed("jsparse.end", ""); + + // // nothing_p + assertParseFailed("jsparse.nothing", "abcd"); + assertParseFailed("jsparse.nothing", ""); + + // // sequence + assertFullyParsed("jsparse.sequence('a', 'b')", "ab"); + assertParseFailed("jsparse.sequence('a', 'b')", "b"); + assertParseFailed("jsparse.sequence('a', 'b')", "a"); + assertParseMatched("jsparse.sequence('a', jsparse.whitespace('b'))", "a b", "ab"); + assertParseMatched("jsparse.sequence('a', jsparse.whitespace('b'))", "a b", "ab"); + assertParseMatched("jsparse.sequence('a', jsparse.whitespace('b'))", "ab", "ab"); + + // // choice + assertFullyParsed("jsparse.choice('a', 'b')", "a"); + assertFullyParsed("jsparse.choice('a', 'b')", "b"); + assertParseMatched("jsparse.choice('a', 'b')", "ab", "a"); + assertParseMatched("jsparse.choice('a', 'b')", "bc", "b"); + + // // repeat0 + assertParseMatched("jsparse.repeat0(jsparse.choice('a','b'))", "adef", "a"); + assertParseMatched("jsparse.repeat0(jsparse.choice('a','b'))", "bdef", "b"); + assertParseMatched("jsparse.repeat0(jsparse.choice('a','b'))", "aabbabadef", "aabbaba"); + assertParseMatched("jsparse.repeat0(jsparse.choice('a','b'))", "daabbabadef", ""); + + // // repeat1 + assertParseMatched("jsparse.repeat1(jsparse.choice('a','b'))", "adef", "a"); + assertParseMatched("jsparse.repeat1(jsparse.choice('a','b'))", "bdef", "b"); + assertParseMatched("jsparse.repeat1(jsparse.choice('a','b'))", "aabbabadef", "aabbaba"); + assertParseFailed("jsparse.repeat1(jsparse.choice('a','b'))", "daabbabadef"); + + // // optional + assertParseMatched("jsparse.sequence('a', jsparse.optional(jsparse.choice('b','c')), 'd')", "abd", "abd"); + assertParseMatched("jsparse.sequence('a', jsparse.optional(jsparse.choice('b','c')), 'd')", "acd", "acd"); + assertParseMatched("jsparse.sequence('a', jsparse.optional(jsparse.choice('b','c')), 'd')", "ad", "ad"); + assertParseFailed("jsparse.sequence('a', jsparse.optional(jsparse.choice('b','c')), 'd')", "aed"); + assertParseFailed("jsparse.sequence('a', jsparse.optional(jsparse.choice('b','c')), 'd')", "ab"); + assertParseFailed("jsparse.sequence('a', jsparse.optional(jsparse.choice('b','c')), 'd')", "ac"); + + // // list + assertParseMatched("jsparse.list(jsparse.choice('1','2','3'),',')", "1,2,3", "1,2,3"); + assertParseMatched("jsparse.list(jsparse.choice('1','2','3'),',')", "1,3,2", "1,3,2"); + assertParseMatched("jsparse.list(jsparse.choice('1','2','3'),',')", "1,3", "1,3"); + assertParseMatched("jsparse.list(jsparse.choice('1','2','3'),',')", "3", "3"); + assertParseFailed("jsparse.list(jsparse.choice('1','2','3'),',')", "5,6,7"); + + // // and + assertParseMatched("jsparse.sequence(jsparse.and('0'), '0')", "0", "0"); + assertParseFailed("jsparse.sequence(jsparse.and('0'), '1')", "0"); + assertParseMatched("jsparse.sequence('1',jsparse.and('2'))", "12", "1"); + + // // not + assertParseMatched("jsparse.sequence('a',jsparse.choice('+','++'),'b')", "a+b", "a+b"); + assertParseFailed("jsparse.sequence('a',jsparse.choice('+','++'),'b')", "a++b"); + assertParseMatched("jsparse.sequence('a',jsparse.choice(jsparse.sequence('+',jsparse.not('+')),'++'),'b')", "a+b", "a+b"); + assertParseMatched("jsparse.sequence('a',jsparse.choice(jsparse.sequence('+',jsparse.not('+')),'++'),'b')", "a++b", "a++b"); + + // // butnot + assertFullyParsed("jsparse.butnot(jsparse.range('0','9'), '6')", "1"); + assertParseFailed("jsparse.butnot(jsparse.range('0','9'), '6')", "6"); + assertParseFailed("jsparse.butnot(jsparse.range('0','9'), 'x')", "x"); + assertParseFailed("jsparse.butnot(jsparse.range('0','9'), 'y')", "x"); } time(function() { runTests(ParserTests); }); + +process.exit(failed.length) From 27ba923d3016fdcb3f7ca1f323b75421848f400b Mon Sep 17 00:00:00 2001 From: Peter Burns Date: Sat, 11 Feb 2012 14:02:52 -0800 Subject: [PATCH 2/8] moved examples to their own dir --- es3.js => examples/es3.js | 120 +++++++++++++------------- es3_tests.js => examples/es3_tests.js | 19 ++-- example1.js => examples/example1.js | 0 example2.js => examples/example2.js | 0 example3.js => examples/example3.js | 0 5 files changed, 72 insertions(+), 67 deletions(-) rename es3.js => examples/es3.js (89%) rename es3_tests.js => examples/es3_tests.js (98%) rename example1.js => examples/example1.js (100%) rename example2.js => examples/example2.js (100%) rename example3.js => examples/example3.js (100%) diff --git a/es3.js b/examples/es3.js similarity index 89% rename from es3.js rename to examples/es3.js index 6065322..4d953a3 100644 --- a/es3.js +++ b/examples/es3.js @@ -1,15 +1,15 @@ // Copyright (C) 2007 Chris Double. -// +// // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: -// +// // 1. Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. -// +// // 2. Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. -// +// // THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE @@ -22,16 +22,18 @@ // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // +//This has not been updated to use jsparse.* + // Forward Declarations -var SourceElement = +var SourceElement = function(input) { return SourceElement(input); } -var AssignmentExpression = +var AssignmentExpression = function(input) { return AssignmentExpression(input); } -var Expression = +var Expression = function(input) { return Expression(input); } -var Statement = +var Statement = function(input) { return Statement(input); } -var LeftHandSideExpression = +var LeftHandSideExpression = function(input) { return LeftHandSideExpression(input); } var Whitespace = choice("\t", " "); @@ -48,7 +50,7 @@ var BooleanLiteral = choice("true", "false"); var Zero = action("0", function(ast) { return 0; }); var DecimalDigit = action(range("0", "9"), function(ast) { return parseInt(ast); }); var NonZeroDigit = action(range("1", "9"), function(ast) { return parseInt(ast); }); -var DecimalDigits = repeat1(DecimalDigit); +var DecimalDigits = repeat1(DecimalDigit); var DecimalIntegerLiteral = choice(Zero, sequence(NonZeroDigit, optional(DecimalDigits))); var SignedInteger = choice(DecimalDigits, sequence("+", DecimalDigits), sequence("-", DecimalDigits)); var ExponentIndicator = choice("e", "E"); @@ -77,9 +79,9 @@ var DoubleStringCharacters = repeat1(DoubleStringCharacter); var StringLiteral = choice(sequence("\"", optional(DoubleStringCharacters), "\""), sequence("'", optional(SingleStringCharacters), "'")); -var Literal = choice(NullLiteral, BooleanLiteral, NumericLiteral, StringLiteral); +var Literal = choice(NullLiteral, BooleanLiteral, NumericLiteral, StringLiteral); -var Keyword = +var Keyword = choice("break", "case", "catch", @@ -112,10 +114,10 @@ var HexDigit = choice(range("0", "9"), range("a", "f"), range("A", "F")); var IdentifierLetter = choice(range("a", "z"), range("A", "Z")); var IdentifierStart = choice(IdentifierLetter, "$", "_"); var IdentifierPart = choice(IdentifierStart, range("0-9")); -var IdentifierName = +var IdentifierName = action(sequence(IdentifierStart, join_action(repeat0(IdentifierPart), "")), - function(ast) { - return ast[0].concat(ast[1]); + function(ast) { + return ast[0].concat(ast[1]); }); var Identifier = butnot(IdentifierName, ReservedWord); @@ -123,13 +125,13 @@ var StatementList = repeat1(Statement); var Block = wsequence("{", optional(StatementList), "}"); var Initialiser = wsequence("=", AssignmentExpression); var VariableDeclaration = wsequence(Identifier, optional(Initialiser)); -var VariableDeclarationList = wlist(VariableDeclaration, ","); -var VariableStatement = +var VariableDeclarationList = wlist(VariableDeclaration, ","); +var VariableStatement = wsequence("var", VariableDeclarationList); var EmptyStatement = token(";"); -var IfStatement = +var IfStatement = choice(wsequence("if", "(", Expression, ")", Statement, "else", Statement), wsequence("if", "(", Expression, ")", Statement)); @@ -161,15 +163,15 @@ var ThrowStatement = wsequence("throw", Expression, ";"); var Catch = wsequence("catch", "(", Identifier, ")", Block); var Finally = wsequence("finally", Block); -var TryStatement = +var TryStatement = choice(wsequence("try", Block, Catch), wsequence("try", Block, Finally), wsequence("try", Block, Catch, Finally)); -var ExpressionStatement = +var ExpressionStatement = choice(sequence(choice("{", "function"), nothing_p), Expression); -var Statement = +var Statement = choice(Block, VariableStatement, EmptyStatement, @@ -185,27 +187,27 @@ var Statement = ThrowStatement, TryStatement); -var FunctionDeclaration = +var FunctionDeclaration = function(input) { return FunctionDeclaration(input); } var FunctionBody = repeat0(SourceElement); -var FormalParameterList = wlist(Identifier, ","); -var FunctionExpression = +var FormalParameterList = wlist(Identifier, ","); +var FunctionExpression = wsequence("function", optional(Identifier), "(", optional(FormalParameterList), ")", "{", FunctionBody, "}"); -var FunctionDeclaration = +var FunctionDeclaration = wsequence("function", Identifier, "(", optional(FormalParameterList), ")", "{", FunctionBody, "}"); -var PrimaryExpression = +var PrimaryExpression = function(input) { return PrimaryExpression(input); } -var ArgumentList = list(AssignmentExpression, ","); -var Arguments = +var ArgumentList = list(AssignmentExpression, ","); +var Arguments = choice(wsequence("(", ")"), wsequence("(", ArgumentList, ")")); -var MemberExpression = function(input) { return MemberExpression(input); } +var MemberExpression = function(input) { return MemberExpression(input); } var MemberExpression = left_factor_action(sequence(choice(wsequence("new", MemberExpression, Arguments), PrimaryExpression, @@ -213,18 +215,18 @@ var MemberExpression = repeat0(choice(wsequence("[", Expression, "]"), wsequence(".", Identifier))))); -var NewExpression = +var NewExpression = choice(MemberExpression, wsequence("new", NewExpression)); -var CallExpression = +var CallExpression = left_factor_action(wsequence(wsequence(MemberExpression, Arguments), repeat0(choice(Arguments, wsequence("[", Expression, "]"), wsequence(".", Identifier))))); - + var LeftHandSideExpression = choice(CallExpression, NewExpression); -var AssignmentOperator = +var AssignmentOperator = choice("=", "*=", "/=", @@ -238,29 +240,29 @@ var AssignmentOperator = "^=", "|="); -var LogicalORExpression = +var LogicalORExpression = function(input) { return LogicalORExpression(input); } -var LogicalANDExpression = +var LogicalANDExpression = function(input) { return LogicalANDExpression(input); } -var BitwiseORExpression = +var BitwiseORExpression = function(input) { return BitwiseORExpression(input); } -var BitwiseXORExpression = +var BitwiseXORExpression = function(input) { return BitwiseXORExpression(input); } -var BitwiseANDExpression = +var BitwiseANDExpression = function(input) { return BitwiseANDExpression(input); } -var EqualityExpression = +var EqualityExpression = function(input) { return EqualityExpression(input); } -var RelationalExpression = +var RelationalExpression = function(input) { return RelationalExpression(input); } -var ShiftExpression = +var ShiftExpression = function(input) { return ShiftExpression(input); } -var AdditiveExpression = +var AdditiveExpression = function(input) { return AdditiveExpression(input); } -var MultiplicativeExpression = +var MultiplicativeExpression = function(input) { return MultiplicativeExpression(input); } -var UnaryExpression = +var UnaryExpression = function(input) { return UnaryExpression(input); } -var PostfixExpression = +var PostfixExpression = function(input) { return PostfixExpression(input); } var PostfixExpression = @@ -290,8 +292,8 @@ var AdditiveExpression = wsequence(MultiplicativeExpression, repeat0(choice(wsequence("+", MultiplicativeExpression), wsequence("-", MultiplicativeExpression)))); - -var ShiftExpression = + +var ShiftExpression = wsequence(AdditiveExpression, repeat0(choice(wsequence("<<", AdditiveExpression), wsequence(">>", AdditiveExpression), @@ -306,37 +308,37 @@ var RelationalExpression = wsequence("instanceof", ShiftExpression)))); var EqualityExpression = - wsequence(RelationalExpression, + wsequence(RelationalExpression, repeat0(choice(wsequence("==", RelationalExpression), wsequence("!==", RelationalExpression), wsequence("===", RelationalExpression), wsequence("!==", RelationalExpression)))); -var BitwiseANDExpression = +var BitwiseANDExpression = wsequence(EqualityExpression, repeat0(wsequence("&", EqualityExpression))); -var BitwiseXORExpression = +var BitwiseXORExpression = wsequence(BitwiseANDExpression, repeat0(wsequence("^", BitwiseANDExpression))); -var BitwiseORExpression = +var BitwiseORExpression = wsequence(BitwiseXORExpression, repeat0(wsequence("|", BitwiseXORExpression))); -var LogicalANDExpression = +var LogicalANDExpression = wsequence(BitwiseORExpression, repeat0(wsequence("&&", BitwiseORExpression))); -var LogicalORExpression = +var LogicalORExpression = wsequence(LogicalANDExpression, repeat0(wsequence("||", LogicalANDExpression))); -var ConditionalExpression = +var ConditionalExpression = choice(LogicalORExpression, wsequence(LogicalORExpression, "?", AssignmentExpression, ":", AssignmentExpression)); -var AssignmentExpression = +var AssignmentExpression = choice(wsequence(LeftHandSideExpression, AssignmentOperator, AssignmentExpression), ConditionalExpression); var Expression = list(AssignmentExpression, ","); -var Elision = repeat1(","); +var Elision = repeat1(","); var ElementList = list(wsequence(optional(Elision), AssignmentExpression), ","); -var ArrayLiteral = +var ArrayLiteral = choice(wsequence("[", optional(Elision), "]"), wsequence("[", ElementList, "]"), wsequence("[", ElementList, optional(Elision), "]")); @@ -344,11 +346,11 @@ var ArrayLiteral = var PropertyName = choice(Identifier, StringLiteral, NumericLiteral); var PropertyNameAndValueList = list(wsequence(PropertyName, ":", AssignmentExpression), ","); -var ObjectLiteral = +var ObjectLiteral = choice(wsequence("{", "}"), wsequence("{", PropertyNameAndValueList, "}")); -var PrimaryExpression = +var PrimaryExpression = choice("this", wsequence("(", Expression, ")"), Identifier, diff --git a/es3_tests.js b/examples/es3_tests.js similarity index 98% rename from es3_tests.js rename to examples/es3_tests.js index 03fdb59..758cf30 100644 --- a/es3_tests.js +++ b/examples/es3_tests.js @@ -1,15 +1,15 @@ // Copyright (C) 2007 Chris Double. -// +// // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: -// +// // 1. Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. -// +// // 2. Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. -// +// // THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE @@ -21,6 +21,9 @@ // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // + +//This has NOT been updated to use jsparse.* + load("jsparse.js"); load("es3.js"); load("tests.js"); @@ -36,7 +39,7 @@ function LineTerminatorTest() { assertTrue("LineTerminator failed to parse newline", LineTerminator(ps("\n"))); assertFalse("LineTerminator parsed incorrect data", LineTerminator(ps("abcd"))); } - + function SingleLineCommentTest() { assertTrue("SingleLineComment failed to parse comment with no space", SingleLineComment(ps("//foo\n"))); assertTrue("SingleLineComment failed to parse comment with space", SingleLineComment(ps("// foo\n"))); @@ -86,7 +89,7 @@ function NonZeroDigitTest() { function IdentifierTest() { assertFullyParsed("Identifier", "abcd"); assertFalse("Identifier('while')", Identifier(ps('while'))); - assertTrue("Identifier('abcd').ast=='abcd'", Identifier(ps('abcd')).ast=='abcd'); + assertTrue("Identifier('abcd').ast=='abcd'", Identifier(ps('abcd')).ast=='abcd'); } function DecimalDigitsTest() { @@ -167,7 +170,7 @@ function FunctionDeclarationTest() { assertFullyParsed("FunctionBody", "return 123;"); assertFullyParsed("FunctionBody", "return function() { };"); } - + function allTests() { WhitespaceTest(); LineTerminatorTest(); @@ -189,4 +192,4 @@ function allTests() { FunctionDeclarationTest(); } -time(function() { runTests(allTests); }); \ No newline at end of file +time(function() { runTests(allTests); }); diff --git a/example1.js b/examples/example1.js similarity index 100% rename from example1.js rename to examples/example1.js diff --git a/example2.js b/examples/example2.js similarity index 100% rename from example2.js rename to examples/example2.js diff --git a/example3.js b/examples/example3.js similarity index 100% rename from example3.js rename to examples/example3.js From 25899c4bb7dfffaaf97063e4a4658609e110642d Mon Sep 17 00:00:00 2001 From: Peter Burns Date: Sat, 11 Feb 2012 14:14:53 -0800 Subject: [PATCH 3/8] a touch more documentation and a backwards compatability fix --- jsparse.js | 6 ++++-- readme.txt | 15 +++++++-------- tests.js | 4 ++-- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/jsparse.js b/jsparse.js index 5d16e81..e50efac 100644 --- a/jsparse.js +++ b/jsparse.js @@ -279,17 +279,19 @@ jsparse.negate = function negate(p) { } // 'end' is a parser that is successful if the input string is empty (ie. end of parse). -jsparse.end = function end_p(state) { +jsparse.end = function end(state) { if(state.length == 0) return jsparse.make_result(state, undefined, undefined); else return false; } +jsparse.end_p = jsparse.end; // 'nothing' is a parser that always fails. -jsparse.nothing = function nothing_p(state) { +jsparse.nothing = function nothing(state) { return false; } +jsparse.nothing_p = jsparse.nothing; // 'sequence' is a parser combinator that processes a number of parsers in sequence. // It can take any number of arguments, each one being a parser. The parser that 'sequence' diff --git a/readme.txt b/readme.txt index cffc187..58c43e6 100644 --- a/readme.txt +++ b/readme.txt @@ -2,7 +2,7 @@ jsparse ======= This is a simple library of parser combinators for Javascript based on -Packrat parsers [1] and Parsing expression grammars [2]. +Packrat parsers [1] and Parsing expression grammars [2]. [1] http://pdos.csail.mit.edu/~baford/packrat/ [2] http://en.wikipedia.org/wiki/Parsing_expression_grammar @@ -17,20 +17,19 @@ Examples: tests.js Various tests to ensure things are working -example1.js +examples/example1.js Simple expression example from wikipedia article on PEGs. -example2.js +examples/example2.js Expression example with actions used to produce AST. -example3.js +examples/example3.js Expression example with actions used to evaluate as it parses. -es3.js +examples/es3.js Incomplete/work-in-progress ECMAScript 3 parser -es3_tests.js +examples/es3_tests.js Tests for ECMAScript 3 parser -I use it from within the Mozilla Rhino environment but it also works -in the browser. +It has been updated to work in nodejs as well as in the browser. diff --git a/tests.js b/tests.js index 3a49b62..b9820d9 100644 --- a/tests.js +++ b/tests.js @@ -155,11 +155,11 @@ function ParserTests() { assertFullyParsed("jsparse.negate(jsparse.ch('a'))", "b"); assertParseFailed("jsparse.negate(jsparse.ch('a'))", "a"); - // // end_p + // // end assertParseFailed("jsparse.end", "ab"); assertFullyParsed("jsparse.end", ""); - // // nothing_p + // // nothing assertParseFailed("jsparse.nothing", "abcd"); assertParseFailed("jsparse.nothing", ""); From 2ba8982306eeb9009df2288c78cb80964fa8b515 Mon Sep 17 00:00:00 2001 From: Peter Burns Date: Sat, 11 Feb 2012 14:25:23 -0800 Subject: [PATCH 4/8] add basic browser test as well --- tests.html | 16 ++++++++++++++++ tests.js | 12 +++++++++--- 2 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 tests.html diff --git a/tests.html b/tests.html new file mode 100644 index 0000000..13520c3 --- /dev/null +++ b/tests.html @@ -0,0 +1,16 @@ + + + Tests + + +

+  
+  
+  
+
+
diff --git a/tests.js b/tests.js
index b9820d9..f4a6139 100644
--- a/tests.js
+++ b/tests.js
@@ -22,12 +22,15 @@
 // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 //
 
-var jsparse = require("./jsparse");
+try {
+    var jsparse = require("./jsparse");
+} catch (e) {}
+
 
 var passed = [];
 var failed = [];
 
-function print(str) {
+var print = print || function print(str) {
     console.log(str);
 }
 
@@ -225,4 +228,7 @@ function ParserTests() {
 
 time(function() { runTests(ParserTests); });
 
-process.exit(failed.length)
+try {
+  process.exit(failed.length)
+} catch(e) {}
+

From 9ad8a16a00c19b18cb21a90d836586f819b0d95e Mon Sep 17 00:00:00 2001
From: Peter Burns 
Date: Sat, 11 Feb 2012 14:30:06 -0800
Subject: [PATCH 5/8] incredibly basic test for inject_into

---
 tests.html | 12 ++++++++++++
 tests.js   | 34 +++++++++++++++++-----------------
 2 files changed, 29 insertions(+), 17 deletions(-)

diff --git a/tests.html b/tests.html
index 13520c3..53f61a1 100644
--- a/tests.html
+++ b/tests.html
@@ -12,5 +12,17 @@
   
   
   
+  
 
 
diff --git a/tests.js b/tests.js
index f4a6139..c0da643 100644
--- a/tests.js
+++ b/tests.js
@@ -71,46 +71,46 @@ function assertNotEqual(msg, value1, value2) {
 
 function assertFullyParsed(parser, string) {
     var msg = parser + " did not fully parse: " + string;
-    // try {
+    try {
     	var result = eval(parser)(jsparse.ps(string));
     	if(result && result.remaining.length == 0)
     	    passed.push(msg);
     	else
     	    failed.push(msg);
-    // }
-    // catch(e) {
-    //     console.log(e);
-    // 	failed.push(msg);
-    // }
+    }
+    catch(e) {
+        console.log(e);
+    	failed.push(msg);
+    }
 }
 
 function assertParseFailed(parser, string) {
     var msg = parser + " succeeded but should have failed: " + string;
-    // try {
+    try {
     	var result = eval(parser)(jsparse.ps(string));
     	if(!result)
     	    passed.push(msg);
     	else
     	    failed.push(msg);
-        // }
-    // catch(e) {
-    //     console.log(e);
-    // 	failed.push(msg);
-    // }
+        }
+    catch(e) {
+        console.log(e);
+    	failed.push(msg);
+    }
 }
 
 function assertParseMatched(parser, string, expected) {
     var msg = parser + " parse did not match: " + string;
-    // try {
+    try {
     	var result = eval(parser)(jsparse.ps(string));
     	if(result && result.matched == expected)
     	    passed.push(msg);
     	else
     	    failed.push(msg + " got [" + result.matched + "] expected [" + expected + "]");
-    // }
-    // catch(e) {
-    // 	failed.push(msg);
-    // }
+    }
+    catch(e) {
+    	failed.push(msg);
+    }
 }
 
 function time(func) {

From 48b54e86652830a035ae3ee947b23c6dee23f52c Mon Sep 17 00:00:00 2001
From: Peter Burns 
Date: Sat, 11 Feb 2012 17:08:52 -0800
Subject: [PATCH 6/8] fix up examples

---
 examples/example1.js | 29 +++++++++++++++---------
 examples/example2.js | 52 ++++++++++++++++++++++++++++++++------------
 examples/example3.js | 38 ++++++++++++++++++++------------
 3 files changed, 81 insertions(+), 38 deletions(-)

diff --git a/examples/example1.js b/examples/example1.js
index 8feda80..f3ffae0 100644
--- a/examples/example1.js
+++ b/examples/example1.js
@@ -1,15 +1,15 @@
 // Copyright (C) 2007 Chris Double.
-// 
+//
 // Redistribution and use in source and binary forms, with or without
 // modification, are permitted provided that the following conditions are met:
-// 
+//
 // 1. Redistributions of source code must retain the above copyright notice,
 //    this list of conditions and the following disclaimer.
-// 
+//
 // 2. Redistributions in binary form must reproduce the above copyright notice,
 //    this list of conditions and the following disclaimer in the documentation
 //    and/or other materials provided with the distribution.
-// 
+//
 // THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
 // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
@@ -27,14 +27,23 @@
 // Value   := [0-9]+ / '(' Expr ')'
 // Product := Value (('*' / '/') Value)*
 // Sum     := Product (('+' / '-') Product)*
-// Expr    := Sum 
+// Expr    := Sum
 //
-// Forward definitions required due to lack of laziness in JS 
+
+//to run in both node and browser
+try {
+  var jp = require("../jsparse")
+} catch(e) {
+  var jp = jsparse;
+}
+
+// Forward definitions required due to lack of laziness in JS
 var Expr = function(state) { return Expr(state); }
 
-var Value = choice(repeat1(range('0','9')), Expr);
-var Product = sequence(Value, repeat0(sequence(choice('*', '/'), Value)));
-var Sum = sequence(Product, repeat0(sequence(choice('+', '-'), Product)));
+var Value = jp.choice(jp.repeat1(jp.range('0','9')), Expr);
+var Product = jp.sequence(Value, jp.repeat0(jp.sequence(jp.choice('*', '/'), Value)));
+var Sum = jp.sequence(Product, jp.repeat0(jp.sequence(jp.choice('+', '-'), Product)));
 var Expr = Sum;
 
-// Usage: Expr(ps("1+2*3-4"))
\ No newline at end of file
+console.log(JSON.stringify(Expr(jp.ps("1+2*3-4")).ast))
+//Yields: [[["1"],[]],[["+",[["2"],[["*",["3"]]]]],["-",[["4"],[]]]]]
diff --git a/examples/example2.js b/examples/example2.js
index 455efd1..6df5559 100644
--- a/examples/example2.js
+++ b/examples/example2.js
@@ -1,15 +1,15 @@
 // Copyright (C) 2007 Chris Double.
-// 
+//
 // Redistribution and use in source and binary forms, with or without
 // modification, are permitted provided that the following conditions are met:
-// 
+//
 // 1. Redistributions of source code must retain the above copyright notice,
 //    this list of conditions and the following disclaimer.
-// 
+//
 // 2. Redistributions in binary form must reproduce the above copyright notice,
 //    this list of conditions and the following disclaimer in the documentation
 //    and/or other materials provided with the distribution.
-// 
+//
 // THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
 // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
@@ -28,9 +28,18 @@
 // Value   := [0-9]+ / '(' Expr ')'
 // Product := Value (('*' / '/') Value)*
 // Sum     := Product (('+' / '-') Product)*
-// Expr    := Sum 
+// Expr    := Sum
 //
-// Forward definitions required due to lack of laziness in JS 
+
+//to run in both node and browser
+try {
+  var jp = require("../jsparse")
+} catch(e) {
+  var jp = jsparse;
+}
+
+
+// Forward definitions required due to lack of laziness in JS
 var Expr = function(state) { return Expr(state); }
 
 // AST objects
@@ -44,22 +53,37 @@ Operator.prototype.toString = function() {
     return uneval(this);
 }
 
-var Number = 
-    action(repeat1(range('0','9')), 
+var Number =
+    jp.action(jp.repeat1(jp.range('0','9')),
 	   function(ast) {
 	       return parseInt(ast.join(""));
 	   });
-var Value = choice(Number, Expr);
+var Value = jp.choice(Number, Expr);
 function operator_action(p) {
-    return action(p, 
-		  function(ast) { 
+    return jp.action(p,
+		  function(ast) {
 		      return function(lhs,rhs) {
 			  return new Operator(ast, lhs, rhs);
 		      };
 		  });
 }
-var Product = chainl(Value, operator_action(choice('*', '/')));
-var Sum = chainl(Product, operator_action(choice('+', '-')));
+var Product = jp.chainl(Value, operator_action(jp.choice('*', '/')));
+var Sum = jp.chainl(Product, operator_action(jp.choice('+', '-')));
 var Expr = Sum;
 
-// Usage: Expr(ps("1+2*3-4"))
\ No newline at end of file
+var example_result = Expr(jp.ps("1+2*3-4")).ast;
+console.log(JSON.stringify(example_result, null, 2))
+//Yields:
+// {
+//   "symbol": "-",
+//   "lhs": {
+//     "symbol": "+",
+//     "lhs": 1,
+//     "rhs": {
+//       "symbol": "*",
+//       "lhs": 2,
+//       "rhs": 3
+//     }
+//   },
+//   "rhs": 4
+// }
diff --git a/examples/example3.js b/examples/example3.js
index a2912c1..c122400 100644
--- a/examples/example3.js
+++ b/examples/example3.js
@@ -1,15 +1,15 @@
 // Copyright (C) 2007 Chris Double.
-// 
+//
 // Redistribution and use in source and binary forms, with or without
 // modification, are permitted provided that the following conditions are met:
-// 
+//
 // 1. Redistributions of source code must retain the above copyright notice,
 //    this list of conditions and the following disclaimer.
-// 
+//
 // 2. Redistributions in binary form must reproduce the above copyright notice,
 //    this list of conditions and the following disclaimer in the documentation
 //    and/or other materials provided with the distribution.
-// 
+//
 // THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
 // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
@@ -28,21 +28,30 @@
 // Value   := [0-9]+ / '(' Expr ')'
 // Product := Value (('*' / '/') Value)*
 // Sum     := Product (('+' / '-') Product)*
-// Expr    := Sum 
+// Expr    := Sum
 //
-// Forward definitions required due to lack of laziness in JS 
+
+//to run in both node and browser
+try {
+  var jp = require("../jsparse")
+} catch(e) {
+  var jp = jsparse;
+}
+
+
+// Forward definitions required due to lack of laziness in JS
 var Expr = function(state) { return Expr(state); }
 
-var Number = 
-    action(repeat1(range('0','9')), 
+var Number =
+    jp.action(jp.repeat1(jp.range('0','9')),
 	   function(ast) {
 	       return parseInt(ast.join(""));
 	   });
-var Value = choice(Number, Expr);
+var Value = jp.choice(Number, Expr);
 
-function operator_action(p, func) 
+function operator_action(p, func)
 {
-    return action(p, function(ast) { return func; });
+    return jp.action(p, function(ast) { return func; });
 }
 
 var Times = operator_action('*', function(lhs,rhs) { return lhs*rhs; });
@@ -50,8 +59,9 @@ var Divides = operator_action('/', function(lhs,rhs) { return lhs/rhs; });
 var Plus = operator_action('+', function(lhs,rhs) { return lhs+rhs; });
 var Minus = operator_action('-', function(lhs,rhs) { return lhs-rhs; });
 
-var Product = chainl(Value, choice(Times, Divides));
-var Sum = chainl(Product, choice(Plus, Minus));
+var Product = jp.chainl(Value, jp.choice(Times, Divides));
+var Sum = jp.chainl(Product, jp.choice(Plus, Minus));
 var Expr = Sum;
 
-// Usage: Expr(ps("1+2*3-4"))
\ No newline at end of file
+console.log(Expr(jp.ps("1+2*3-4")).ast)
+//Yields: 3

From 77a4c0e0fc15853657d13e4ab98e13d2dac9185a Mon Sep 17 00:00:00 2001
From: Peter Burns 
Date: Sat, 11 Feb 2012 17:10:24 -0800
Subject: [PATCH 7/8] mention that es3.js is currently broken

---
 readme.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/readme.txt b/readme.txt
index 58c43e6..a86d541 100644
--- a/readme.txt
+++ b/readme.txt
@@ -27,7 +27,7 @@ examples/example3.js
   Expression example with actions used to evaluate as it parses.
 
 examples/es3.js
-  Incomplete/work-in-progress ECMAScript 3 parser
+  Incomplete/work-in-progress ECMAScript 3 parser (currently broken)
 
 examples/es3_tests.js
   Tests for ECMAScript 3 parser

From da96d00056b25298ab1d74bf2b1ed28095cead8a Mon Sep 17 00:00:00 2001
From: Peter Burns 
Date: Sat, 11 Feb 2012 17:10:42 -0800
Subject: [PATCH 8/8] add AUTHORS and package file

---
 AUTHORS      |  6 ++++++
 package.json | 20 ++++++++++++++++++++
 2 files changed, 26 insertions(+)
 create mode 100644 AUTHORS
 create mode 100644 package.json

diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..8e2e6a6
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,6 @@
+# actually wrote jsparse.
+Chris Double 
+
+# just this random guy who came along and packaged it for node.
+Peter Burns 
+
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..1b565e6
--- /dev/null
+++ b/package.json
@@ -0,0 +1,20 @@
+{
+  "name": "jsparse",
+  "description": "A parser combinator library",
+  "version": "0.1.0",
+  "homepage": "https://github.com/rictic/jsparse",
+  "repository": {
+    "type": "git",
+    "url": "git://github.com/rictic/jsparse.git"
+  },
+  "main": "jsparse.js",
+  "scripts": {
+    "test": "node tests.js"
+  },
+  "engines": {
+    "node": ">0.1.0"
+  },
+  "dependencies": {},
+  "devDependencies": {},
+  "optionalDependencies": {}
+}