Created
May 7, 2015 00:22
-
-
Save fwg/c4cf735923504dbac591 to your computer and use it in GitHub Desktop.
s-expression PEG.js grammar
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var S = require('./peg-gen8'); | |
function P(stream) { | |
try { | |
return S.parse(stream); | |
} catch (e) { | |
if (!(e instanceof S.SyntaxError)) throw e; | |
if (e.message == 'Expected "\\"", "\\\\", "\\\\\\"" or any character but end of input found.') { | |
e.message = 'Syntax error: Unterminated string literal'; | |
} | |
e.col = e.column; | |
return e; | |
} | |
} | |
P.SyntaxError = S.SyntaxError; | |
module.exports = P; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"name": "s-expression", | |
"version": "3.0.1-PEG", | |
"description": "s-expression parser with (un|quasi|)quoting support", | |
"main": "index-peg.js", | |
"scripts": { | |
"test": "node test.js" | |
}, | |
"author": { | |
"name": "Friedemann Altrock", | |
"email": "frodenius@gmail.com" | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module.exports = (function() { | |
/* | |
* Generated by PEG.js 0.8.0. | |
* | |
* http://pegjs.majda.cz/ | |
*/ | |
function peg$subclass(child, parent) { | |
function ctor() { this.constructor = child; } | |
ctor.prototype = parent.prototype; | |
child.prototype = new ctor(); | |
} | |
function SyntaxError(message, expected, found, offset, line, column) { | |
this.message = message; | |
this.expected = expected; | |
this.found = found; | |
this.offset = offset; | |
this.line = line; | |
this.column = column; | |
this.name = "SyntaxError"; | |
} | |
peg$subclass(SyntaxError, Error); | |
function parse(input) { | |
var options = arguments.length > 1 ? arguments[1] : {}, | |
peg$FAILED = {}, | |
peg$startRuleFunctions = { Expr0: peg$parseExpr0 }, | |
peg$startRuleFunction = peg$parseExpr0, | |
peg$c0 = peg$FAILED, | |
peg$c1 = [], | |
peg$c2 = null, | |
peg$c3 = function(x) {return x[1]?x[1]:''}, | |
peg$c4 = function(x) {return x[1]}, | |
peg$c5 = function(quoted) {quoted[0]={"'":"quote","`":"quasiquote",",@":"unquote-splicing",",":"unquote"}[quoted[0]];return quoted}, | |
peg$c6 = "'", | |
peg$c7 = { type: "literal", value: "'", description: "\"'\"" }, | |
peg$c8 = "`", | |
peg$c9 = { type: "literal", value: "`", description: "\"`\"" }, | |
peg$c10 = ",@", | |
peg$c11 = { type: "literal", value: ",@", description: "\",@\"" }, | |
peg$c12 = ",", | |
peg$c13 = { type: "literal", value: ",", description: "\",\"" }, | |
peg$c14 = "(", | |
peg$c15 = { type: "literal", value: "(", description: "\"(\"" }, | |
peg$c16 = ")", | |
peg$c17 = { type: "literal", value: ")", description: "\")\"" }, | |
peg$c18 = function(xs) {return xs[1]}, | |
peg$c19 = "\"", | |
peg$c20 = { type: "literal", value: "\"", description: "\"\\\"\"" }, | |
peg$c21 = function(xs) {return xs.join('')}, | |
peg$c22 = function(parts) {return new String(parts.slice(1,-1)[0])}, | |
peg$c23 = "\\", | |
peg$c24 = { type: "literal", value: "\\", description: "\"\\\\\"" }, | |
peg$c25 = function(xs) {return {"\\":"\\","t":"\t","n":"\n","r":"\r","v":"\v",}[xs[1]] || xs[1]}, | |
peg$c26 = "\\\"", | |
peg$c27 = { type: "literal", value: "\\\"", description: "\"\\\\\\\"\"" }, | |
peg$c28 = function(y) {return y[1]}, | |
peg$c29 = void 0, | |
peg$c30 = function(chars) { return chars.map(function(x){return x[0]===undefined?x[1]:x}).join('') }, | |
peg$c31 = { type: "any", description: "any character" }, | |
peg$c32 = /^[ \t\n\r\x0B]/, | |
peg$c33 = { type: "class", value: "[ \\t\\n\\r\\x0B]", description: "[ \\t\\n\\r\\x0B]" }, | |
peg$currPos = 0, | |
peg$reportedPos = 0, | |
peg$cachedPos = 0, | |
peg$cachedPosDetails = { line: 1, column: 1, seenCR: false }, | |
peg$maxFailPos = 0, | |
peg$maxFailExpected = [], | |
peg$silentFails = 0, | |
peg$result; | |
if ("startRule" in options) { | |
if (!(options.startRule in peg$startRuleFunctions)) { | |
throw new Error("Can't start parsing from rule \"" + options.startRule + "\"."); | |
} | |
peg$startRuleFunction = peg$startRuleFunctions[options.startRule]; | |
} | |
function text() { | |
return input.substring(peg$reportedPos, peg$currPos); | |
} | |
function offset() { | |
return peg$reportedPos; | |
} | |
function line() { | |
return peg$computePosDetails(peg$reportedPos).line; | |
} | |
function column() { | |
return peg$computePosDetails(peg$reportedPos).column; | |
} | |
function expected(description) { | |
throw peg$buildException( | |
null, | |
[{ type: "other", description: description }], | |
peg$reportedPos | |
); | |
} | |
function error(message) { | |
throw peg$buildException(message, null, peg$reportedPos); | |
} | |
function peg$computePosDetails(pos) { | |
function advance(details, startPos, endPos) { | |
var p, ch; | |
for (p = startPos; p < endPos; p++) { | |
ch = input.charAt(p); | |
if (ch === "\n") { | |
if (!details.seenCR) { details.line++; } | |
details.column = 1; | |
details.seenCR = false; | |
} else if (ch === "\r" || ch === "\u2028" || ch === "\u2029") { | |
details.line++; | |
details.column = 1; | |
details.seenCR = true; | |
} else { | |
details.column++; | |
details.seenCR = false; | |
} | |
} | |
} | |
if (peg$cachedPos !== pos) { | |
if (peg$cachedPos > pos) { | |
peg$cachedPos = 0; | |
peg$cachedPosDetails = { line: 1, column: 1, seenCR: false }; | |
} | |
advance(peg$cachedPosDetails, peg$cachedPos, pos); | |
peg$cachedPos = pos; | |
} | |
return peg$cachedPosDetails; | |
} | |
function peg$fail(expected) { | |
if (peg$currPos < peg$maxFailPos) { return; } | |
if (peg$currPos > peg$maxFailPos) { | |
peg$maxFailPos = peg$currPos; | |
peg$maxFailExpected = []; | |
} | |
peg$maxFailExpected.push(expected); | |
} | |
function peg$buildException(message, expected, pos) { | |
function cleanupExpected(expected) { | |
var i = 1; | |
expected.sort(function(a, b) { | |
if (a.description < b.description) { | |
return -1; | |
} else if (a.description > b.description) { | |
return 1; | |
} else { | |
return 0; | |
} | |
}); | |
while (i < expected.length) { | |
if (expected[i - 1] === expected[i]) { | |
expected.splice(i, 1); | |
} else { | |
i++; | |
} | |
} | |
} | |
function buildMessage(expected, found) { | |
function stringEscape(s) { | |
function hex(ch) { return ch.charCodeAt(0).toString(16).toUpperCase(); } | |
return s | |
.replace(/\\/g, '\\\\') | |
.replace(/"/g, '\\"') | |
.replace(/\x08/g, '\\b') | |
.replace(/\t/g, '\\t') | |
.replace(/\n/g, '\\n') | |
.replace(/\f/g, '\\f') | |
.replace(/\r/g, '\\r') | |
.replace(/[\x00-\x07\x0B\x0E\x0F]/g, function(ch) { return '\\x0' + hex(ch); }) | |
.replace(/[\x10-\x1F\x80-\xFF]/g, function(ch) { return '\\x' + hex(ch); }) | |
.replace(/[\u0180-\u0FFF]/g, function(ch) { return '\\u0' + hex(ch); }) | |
.replace(/[\u1080-\uFFFF]/g, function(ch) { return '\\u' + hex(ch); }); | |
} | |
var expectedDescs = new Array(expected.length), | |
expectedDesc, foundDesc, i; | |
for (i = 0; i < expected.length; i++) { | |
expectedDescs[i] = expected[i].description; | |
} | |
expectedDesc = expected.length > 1 | |
? expectedDescs.slice(0, -1).join(", ") | |
+ " or " | |
+ expectedDescs[expected.length - 1] | |
: expectedDescs[0]; | |
foundDesc = found ? "\"" + stringEscape(found) + "\"" : "end of input"; | |
return "Expected " + expectedDesc + " but " + foundDesc + " found."; | |
} | |
var posDetails = peg$computePosDetails(pos), | |
found = pos < input.length ? input.charAt(pos) : null; | |
if (expected !== null) { | |
cleanupExpected(expected); | |
} | |
return new SyntaxError( | |
message !== null ? message : buildMessage(expected, found), | |
expected, | |
found, | |
pos, | |
posDetails.line, | |
posDetails.column | |
); | |
} | |
function peg$parseExpr0() { | |
var s0, s1, s2, s3, s4, s5; | |
s0 = peg$currPos; | |
s1 = peg$currPos; | |
s2 = []; | |
s3 = peg$parseSpace(); | |
while (s3 !== peg$FAILED) { | |
s2.push(s3); | |
s3 = peg$parseSpace(); | |
} | |
if (s2 !== peg$FAILED) { | |
s3 = peg$parseExpr(); | |
if (s3 === peg$FAILED) { | |
s3 = peg$c2; | |
} | |
if (s3 !== peg$FAILED) { | |
s4 = []; | |
s5 = peg$parseSpace(); | |
while (s5 !== peg$FAILED) { | |
s4.push(s5); | |
s5 = peg$parseSpace(); | |
} | |
if (s4 !== peg$FAILED) { | |
s2 = [s2, s3, s4]; | |
s1 = s2; | |
} else { | |
peg$currPos = s1; | |
s1 = peg$c0; | |
} | |
} else { | |
peg$currPos = s1; | |
s1 = peg$c0; | |
} | |
} else { | |
peg$currPos = s1; | |
s1 = peg$c0; | |
} | |
if (s1 !== peg$FAILED) { | |
peg$reportedPos = s0; | |
s1 = peg$c3(s1); | |
} | |
s0 = s1; | |
return s0; | |
} | |
function peg$parseExpr() { | |
var s0, s1, s2, s3, s4, s5; | |
s0 = peg$currPos; | |
s1 = peg$currPos; | |
s2 = []; | |
s3 = peg$parseSpace(); | |
while (s3 !== peg$FAILED) { | |
s2.push(s3); | |
s3 = peg$parseSpace(); | |
} | |
if (s2 !== peg$FAILED) { | |
s3 = peg$parseExpr1(); | |
if (s3 !== peg$FAILED) { | |
s4 = []; | |
s5 = peg$parseSpace(); | |
while (s5 !== peg$FAILED) { | |
s4.push(s5); | |
s5 = peg$parseSpace(); | |
} | |
if (s4 !== peg$FAILED) { | |
s2 = [s2, s3, s4]; | |
s1 = s2; | |
} else { | |
peg$currPos = s1; | |
s1 = peg$c0; | |
} | |
} else { | |
peg$currPos = s1; | |
s1 = peg$c0; | |
} | |
} else { | |
peg$currPos = s1; | |
s1 = peg$c0; | |
} | |
if (s1 !== peg$FAILED) { | |
peg$reportedPos = s0; | |
s1 = peg$c4(s1); | |
} | |
s0 = s1; | |
if (s0 === peg$FAILED) { | |
s0 = peg$parseQuoted(); | |
if (s0 === peg$FAILED) { | |
s0 = peg$parseAtom(); | |
if (s0 === peg$FAILED) { | |
s0 = peg$parseList(); | |
} | |
} | |
} | |
return s0; | |
} | |
function peg$parseQuoted() { | |
var s0, s1, s2, s3; | |
s0 = peg$currPos; | |
s1 = peg$currPos; | |
s2 = peg$parseQuote(); | |
if (s2 !== peg$FAILED) { | |
s3 = peg$parseExpr(); | |
if (s3 !== peg$FAILED) { | |
s2 = [s2, s3]; | |
s1 = s2; | |
} else { | |
peg$currPos = s1; | |
s1 = peg$c0; | |
} | |
} else { | |
peg$currPos = s1; | |
s1 = peg$c0; | |
} | |
if (s1 !== peg$FAILED) { | |
peg$reportedPos = s0; | |
s1 = peg$c5(s1); | |
} | |
s0 = s1; | |
return s0; | |
} | |
function peg$parseQuote() { | |
var s0; | |
if (input.charCodeAt(peg$currPos) === 39) { | |
s0 = peg$c6; | |
peg$currPos++; | |
} else { | |
s0 = peg$FAILED; | |
if (peg$silentFails === 0) { peg$fail(peg$c7); } | |
} | |
if (s0 === peg$FAILED) { | |
if (input.charCodeAt(peg$currPos) === 96) { | |
s0 = peg$c8; | |
peg$currPos++; | |
} else { | |
s0 = peg$FAILED; | |
if (peg$silentFails === 0) { peg$fail(peg$c9); } | |
} | |
if (s0 === peg$FAILED) { | |
if (input.substr(peg$currPos, 2) === peg$c10) { | |
s0 = peg$c10; | |
peg$currPos += 2; | |
} else { | |
s0 = peg$FAILED; | |
if (peg$silentFails === 0) { peg$fail(peg$c11); } | |
} | |
if (s0 === peg$FAILED) { | |
if (input.charCodeAt(peg$currPos) === 44) { | |
s0 = peg$c12; | |
peg$currPos++; | |
} else { | |
s0 = peg$FAILED; | |
if (peg$silentFails === 0) { peg$fail(peg$c13); } | |
} | |
} | |
} | |
} | |
return s0; | |
} | |
function peg$parseExpr1() { | |
var s0; | |
s0 = peg$parseQuoted(); | |
if (s0 === peg$FAILED) { | |
s0 = peg$parseAtom(); | |
if (s0 === peg$FAILED) { | |
s0 = peg$parseList(); | |
} | |
} | |
return s0; | |
} | |
function peg$parseAtom() { | |
var s0; | |
s0 = peg$parseString(); | |
if (s0 === peg$FAILED) { | |
s0 = peg$parseSymbol(); | |
} | |
return s0; | |
} | |
function peg$parseList() { | |
var s0, s1, s2, s3, s4; | |
s0 = peg$currPos; | |
s1 = peg$currPos; | |
if (input.charCodeAt(peg$currPos) === 40) { | |
s2 = peg$c14; | |
peg$currPos++; | |
} else { | |
s2 = peg$FAILED; | |
if (peg$silentFails === 0) { peg$fail(peg$c15); } | |
} | |
if (s2 !== peg$FAILED) { | |
s3 = []; | |
s4 = peg$parseExpr(); | |
while (s4 !== peg$FAILED) { | |
s3.push(s4); | |
s4 = peg$parseExpr(); | |
} | |
if (s3 !== peg$FAILED) { | |
if (input.charCodeAt(peg$currPos) === 41) { | |
s4 = peg$c16; | |
peg$currPos++; | |
} else { | |
s4 = peg$FAILED; | |
if (peg$silentFails === 0) { peg$fail(peg$c17); } | |
} | |
if (s4 !== peg$FAILED) { | |
s2 = [s2, s3, s4]; | |
s1 = s2; | |
} else { | |
peg$currPos = s1; | |
s1 = peg$c0; | |
} | |
} else { | |
peg$currPos = s1; | |
s1 = peg$c0; | |
} | |
} else { | |
peg$currPos = s1; | |
s1 = peg$c0; | |
} | |
if (s1 !== peg$FAILED) { | |
peg$reportedPos = s0; | |
s1 = peg$c18(s1); | |
} | |
s0 = s1; | |
return s0; | |
} | |
function peg$parseString() { | |
var s0, s1, s2, s3, s4, s5; | |
s0 = peg$currPos; | |
s1 = peg$currPos; | |
if (input.charCodeAt(peg$currPos) === 34) { | |
s2 = peg$c19; | |
peg$currPos++; | |
} else { | |
s2 = peg$FAILED; | |
if (peg$silentFails === 0) { peg$fail(peg$c20); } | |
} | |
if (s2 !== peg$FAILED) { | |
s3 = peg$currPos; | |
s4 = []; | |
s5 = peg$parseStringChar(); | |
while (s5 !== peg$FAILED) { | |
s4.push(s5); | |
s5 = peg$parseStringChar(); | |
} | |
if (s4 !== peg$FAILED) { | |
peg$reportedPos = s3; | |
s4 = peg$c21(s4); | |
} | |
s3 = s4; | |
if (s3 !== peg$FAILED) { | |
if (input.charCodeAt(peg$currPos) === 34) { | |
s4 = peg$c19; | |
peg$currPos++; | |
} else { | |
s4 = peg$FAILED; | |
if (peg$silentFails === 0) { peg$fail(peg$c20); } | |
} | |
if (s4 !== peg$FAILED) { | |
s2 = [s2, s3, s4]; | |
s1 = s2; | |
} else { | |
peg$currPos = s1; | |
s1 = peg$c0; | |
} | |
} else { | |
peg$currPos = s1; | |
s1 = peg$c0; | |
} | |
} else { | |
peg$currPos = s1; | |
s1 = peg$c0; | |
} | |
if (s1 !== peg$FAILED) { | |
peg$reportedPos = s0; | |
s1 = peg$c22(s1); | |
} | |
s0 = s1; | |
return s0; | |
} | |
function peg$parseStringChar() { | |
var s0, s1, s2, s3; | |
s0 = peg$currPos; | |
s1 = peg$currPos; | |
if (input.charCodeAt(peg$currPos) === 92) { | |
s2 = peg$c23; | |
peg$currPos++; | |
} else { | |
s2 = peg$FAILED; | |
if (peg$silentFails === 0) { peg$fail(peg$c24); } | |
} | |
if (s2 !== peg$FAILED) { | |
s3 = peg$parseChar(); | |
if (s3 !== peg$FAILED) { | |
s2 = [s2, s3]; | |
s1 = s2; | |
} else { | |
peg$currPos = s1; | |
s1 = peg$c0; | |
} | |
} else { | |
peg$currPos = s1; | |
s1 = peg$c0; | |
} | |
if (s1 !== peg$FAILED) { | |
peg$reportedPos = s0; | |
s1 = peg$c25(s1); | |
} | |
s0 = s1; | |
if (s0 === peg$FAILED) { | |
s0 = peg$currPos; | |
if (input.substr(peg$currPos, 2) === peg$c26) { | |
s1 = peg$c26; | |
peg$currPos += 2; | |
} else { | |
s1 = peg$FAILED; | |
if (peg$silentFails === 0) { peg$fail(peg$c27); } | |
} | |
if (s1 !== peg$FAILED) { | |
peg$reportedPos = s0; | |
s1 = peg$c28(s1); | |
} | |
s0 = s1; | |
if (s0 === peg$FAILED) { | |
s0 = peg$currPos; | |
s1 = peg$currPos; | |
s2 = peg$currPos; | |
peg$silentFails++; | |
if (input.charCodeAt(peg$currPos) === 34) { | |
s3 = peg$c19; | |
peg$currPos++; | |
} else { | |
s3 = peg$FAILED; | |
if (peg$silentFails === 0) { peg$fail(peg$c20); } | |
} | |
peg$silentFails--; | |
if (s3 === peg$FAILED) { | |
s2 = peg$c29; | |
} else { | |
peg$currPos = s2; | |
s2 = peg$c0; | |
} | |
if (s2 !== peg$FAILED) { | |
s3 = peg$parseChar(); | |
if (s3 !== peg$FAILED) { | |
s2 = [s2, s3]; | |
s1 = s2; | |
} else { | |
peg$currPos = s1; | |
s1 = peg$c0; | |
} | |
} else { | |
peg$currPos = s1; | |
s1 = peg$c0; | |
} | |
if (s1 !== peg$FAILED) { | |
peg$reportedPos = s0; | |
s1 = peg$c4(s1); | |
} | |
s0 = s1; | |
} | |
} | |
return s0; | |
} | |
function peg$parseSymbol() { | |
var s0, s1, s2; | |
s0 = peg$currPos; | |
s1 = []; | |
s2 = peg$parseSymbolChar(); | |
if (s2 !== peg$FAILED) { | |
while (s2 !== peg$FAILED) { | |
s1.push(s2); | |
s2 = peg$parseSymbolChar(); | |
} | |
} else { | |
s1 = peg$c0; | |
} | |
if (s1 !== peg$FAILED) { | |
peg$reportedPos = s0; | |
s1 = peg$c30(s1); | |
} | |
s0 = s1; | |
return s0; | |
} | |
function peg$parseSymbolChar() { | |
var s0, s1, s2, s3; | |
s0 = peg$currPos; | |
s1 = peg$currPos; | |
if (input.charCodeAt(peg$currPos) === 92) { | |
s2 = peg$c23; | |
peg$currPos++; | |
} else { | |
s2 = peg$FAILED; | |
if (peg$silentFails === 0) { peg$fail(peg$c24); } | |
} | |
if (s2 !== peg$FAILED) { | |
s3 = peg$parseChar(); | |
if (s3 !== peg$FAILED) { | |
s2 = [s2, s3]; | |
s1 = s2; | |
} else { | |
peg$currPos = s1; | |
s1 = peg$c0; | |
} | |
} else { | |
peg$currPos = s1; | |
s1 = peg$c0; | |
} | |
if (s1 !== peg$FAILED) { | |
peg$reportedPos = s0; | |
s1 = peg$c4(s1); | |
} | |
s0 = s1; | |
if (s0 === peg$FAILED) { | |
s0 = peg$currPos; | |
s1 = peg$currPos; | |
peg$silentFails++; | |
s2 = peg$parseSpace(); | |
if (s2 === peg$FAILED) { | |
s2 = peg$parseQuote(); | |
if (s2 === peg$FAILED) { | |
if (input.charCodeAt(peg$currPos) === 40) { | |
s2 = peg$c14; | |
peg$currPos++; | |
} else { | |
s2 = peg$FAILED; | |
if (peg$silentFails === 0) { peg$fail(peg$c15); } | |
} | |
if (s2 === peg$FAILED) { | |
if (input.charCodeAt(peg$currPos) === 41) { | |
s2 = peg$c16; | |
peg$currPos++; | |
} else { | |
s2 = peg$FAILED; | |
if (peg$silentFails === 0) { peg$fail(peg$c17); } | |
} | |
if (s2 === peg$FAILED) { | |
if (input.charCodeAt(peg$currPos) === 39) { | |
s2 = peg$c6; | |
peg$currPos++; | |
} else { | |
s2 = peg$FAILED; | |
if (peg$silentFails === 0) { peg$fail(peg$c7); } | |
} | |
if (s2 === peg$FAILED) { | |
if (input.charCodeAt(peg$currPos) === 34) { | |
s2 = peg$c19; | |
peg$currPos++; | |
} else { | |
s2 = peg$FAILED; | |
if (peg$silentFails === 0) { peg$fail(peg$c20); } | |
} | |
} | |
} | |
} | |
} | |
} | |
peg$silentFails--; | |
if (s2 === peg$FAILED) { | |
s1 = peg$c29; | |
} else { | |
peg$currPos = s1; | |
s1 = peg$c0; | |
} | |
if (s1 !== peg$FAILED) { | |
s2 = peg$parseChar(); | |
if (s2 !== peg$FAILED) { | |
s1 = [s1, s2]; | |
s0 = s1; | |
} else { | |
peg$currPos = s0; | |
s0 = peg$c0; | |
} | |
} else { | |
peg$currPos = s0; | |
s0 = peg$c0; | |
} | |
} | |
return s0; | |
} | |
function peg$parseChar() { | |
var s0; | |
if (input.length > peg$currPos) { | |
s0 = input.charAt(peg$currPos); | |
peg$currPos++; | |
} else { | |
s0 = peg$FAILED; | |
if (peg$silentFails === 0) { peg$fail(peg$c31); } | |
} | |
return s0; | |
} | |
function peg$parseSpace() { | |
var s0; | |
if (peg$c32.test(input.charAt(peg$currPos))) { | |
s0 = input.charAt(peg$currPos); | |
peg$currPos++; | |
} else { | |
s0 = peg$FAILED; | |
if (peg$silentFails === 0) { peg$fail(peg$c33); } | |
} | |
return s0; | |
} | |
peg$result = peg$startRuleFunction(); | |
if (peg$result !== peg$FAILED && peg$currPos === input.length) { | |
return peg$result; | |
} else { | |
if (peg$result !== peg$FAILED && peg$currPos < input.length) { | |
peg$fail({ type: "end", description: "end of input" }); | |
} | |
throw peg$buildException(null, peg$maxFailExpected, peg$maxFailPos); | |
} | |
} | |
return { | |
SyntaxError: SyntaxError, | |
parse: parse | |
}; | |
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Grammar used for http://pegjs.org/online to generate peg-gen8.js */ | |
Expr0 = x:(Space* Expr? Space*) {return x[1]?x[1]:''} | |
Expr = x:(Space* Expr1 Space*) {return x[1]} / Quoted / Atom / List | |
Quoted = quoted:(Quote Expr) {quoted[0]={"'":"quote","`":"quasiquote",",@":"unquote-splicing",",":"unquote"}[quoted[0]];return quoted} | |
Quote = ("'" / "`" / ",@" / ",") | |
Expr1 = Quoted / Atom / List | |
Atom = String / Symbol | |
List = xs:("(" Expr* ")") {return xs[1]} | |
String = parts: ("\"" (xs:StringChar* {return xs.join('')}) "\"") {return new String(parts.slice(1,-1)[0])} | |
StringChar = (xs:("\\" Char) {return {"\\":"\\","t":"\t","n":"\n","r":"\r","v":"\v",}[xs[1]] || xs[1]}) / (y:"\\\"" {return y[1]}) / (x:(! "\"" Char) {return x[1]}) | |
Symbol = chars:SymbolChar+ { return chars.map(function(x){return x[0]===undefined?x[1]:x}).join('') } | |
SymbolChar = (x:("\\" (Char)) {return x[1]}) / (! (Space / Quote / "(" / ")" / "'" / "\"") Char) | |
Char = . | |
Space = [ \t\n\r\v] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var assert = require('assert'); | |
var SParse = require(process.cwd() + '/'); | |
var SyntaxError = SParse.SyntaxError; | |
assert.deepEqual(SParse('((a b c)(()()))'), [['a','b','c'],[[],[]]]); | |
assert.deepEqual(SParse('((a b c) (() ()))'), [['a','b','c'],[[],[]]]); | |
assert.deepEqual(SParse("((a 'b 'c))"), [['a',['quote','b'],['quote','c']]]); | |
assert.deepEqual(SParse("(a '(a b c))"), ['a', ['quote', ['a', 'b', 'c']]]); | |
assert.deepEqual(SParse("(a ' (a b c))"), ['a', ['quote', ['a', 'b', 'c']]]); | |
assert.deepEqual(SParse("(a '' (a b c))"), ['a', ['quote', ['quote', ['a', 'b', 'c']]]], 'Multiple quotes should not be flattened'); | |
assert.deepEqual(SParse("((a `b `c))"), [['a',['quasiquote','b'],['quasiquote','c']]]); | |
assert.deepEqual(SParse("(a `(a b c))"), ['a', ['quasiquote', ['a', 'b', 'c']]]); | |
assert.deepEqual(SParse("(a ` (a b c))"), ['a', ['quasiquote', ['a', 'b', 'c']]]); | |
assert.deepEqual(SParse("(a `` (a b c))"), ['a', ['quasiquote', ['quasiquote', ['a', 'b', 'c']]]], 'Multiple quasiquotes should not be flattened'); | |
assert.deepEqual(SParse("((a ,b ,c))"), [['a',['unquote','b'],['unquote','c']]]); | |
assert.deepEqual(SParse("(a ,(a b c))"), ['a', ['unquote', ['a', 'b', 'c']]]); | |
assert.deepEqual(SParse("(a , (a b c))"), ['a', ['unquote', ['a', 'b', 'c']]]); | |
assert.deepEqual(SParse("(a ,, (a b c))"), ['a', ['unquote', ['unquote', ['a', 'b', 'c']]]], 'Multiple unquotes should not be flattened'); | |
assert.deepEqual(SParse("((a ,@b ,@c))"), [['a',['unquote-splicing','b'],['unquote-splicing','c']]]); | |
assert.deepEqual(SParse("(a ,@(a b c))"), ['a', ['unquote-splicing', ['a', 'b', 'c']]]); | |
assert.deepEqual(SParse("(a ,@ (a b c))"), ['a', ['unquote-splicing', ['a', 'b', 'c']]]); | |
assert.deepEqual(SParse("(a ,@,@ (a b c))"), ['a', ['unquote-splicing', ['unquote-splicing', ['a', 'b', 'c']]]], 'Multiple unquote-splicings should not be flattened'); | |
assert(SParse("()()") instanceof SyntaxError, 'Any character after a complete expression should be an error'); | |
assert(SParse("((a) b))") instanceof SyntaxError, 'Any character after a complete expression should be an error'); | |
assert(SParse("((a))abc") instanceof SyntaxError, 'Any character after a complete expression should be an error'); | |
assert(SParse("(')") instanceof SyntaxError, 'A \' without anything to quote should be an error'); | |
assert.deepEqual(SParse("'()"), ['quote', []], 'A quoted empty list should parse'); | |
assert.deepEqual(SParse("()"), [], 'An empty list should parse'); | |
assert.deepEqual(SParse("'a"), ['quote', 'a'], 'A quoted atom should parse'); | |
assert.deepEqual(SParse("'(a)"), ['quote', ['a']], 'A quoted atom in a list should parse'); | |
assert.deepEqual(SParse("a"), 'a', 'An atom should parse'); | |
assert.deepEqual(SParse("(a'b)"), ['a', ['quote', 'b']], 'Quote should act symbol delimiting'); | |
assert.deepEqual(SParse("(a`b)"), ['a', ['quasiquote', 'b']], 'Quasiquote should act symbol delimiting'); | |
assert.deepEqual(SParse("(a,b)"), [ 'a', ['unquote', 'b']], 'Unquote should act symbol delimiting'); | |
assert.deepEqual(SParse("(a,@b)"), ['a', ['unquote-splicing', 'b']], 'Unquote-splicing should act symbol delimiting'); | |
assert.deepEqual(SParse("(a\\'b)"), ['a\'b'], 'Escaped quotes in symbols should parse'); | |
assert.deepEqual(SParse("(a\\\"b)"), ['a\"b'], 'Escaped quotes in symbols should parse'); | |
assert.deepEqual(SParse("(a\\\\b)"), ['a\\b'], 'Escaped \\ in symbols should parse as \\'); | |
assert.deepEqual(SParse("(a\\b)"), ['ab'], 'Escaped normal characters in symbols should parse as normal'); | |
var error = SParse("(\n'"); | |
assert(error instanceof SyntaxError, "Parsing (\\n' Should be an error"); | |
assert(error.line == 2, "line should be 2"); | |
assert(error.col == 2, "col should be 2"); | |
error = SParse("(\r\n'"); | |
assert(error instanceof SyntaxError, "Parsing (\\r\\n' Should be an error"); | |
assert(error.line == 2, "line should be 2"); | |
assert(error.col == 2, "col should be 1"); | |
assert.deepEqual(SParse('(a "a")'), ['a', new String('a')], 'Strings should parse as String objects'); | |
assert.deepEqual(SParse('(a"s"b)'), ['a', new String('s'), 'b'], 'Strings should act symbol delimiting'); | |
assert.deepEqual(SParse('(a\\"s\\"b)'), ['a"s"b'], 'Escaped double quotes in symbols should parse'); | |
assert.deepEqual(SParse('(a "\\"\n")'), ['a', new String('"\n')], 'Escaped double quotes \\" should work in Strings'); | |
assert.deepEqual(SParse('(a "\\\\")'), ['a', new String('\\')], 'Escaped \\ should work in Strings'); | |
assert.deepEqual(SParse('(a "\\a")'), ['a', new String('a')], 'Escaped characters should work in Strings'); | |
assert(SParse('(a "string)') instanceof SyntaxError, 'Prematurely ending strings should produce an error'); | |
assert(SParse('\'"string"', ['quote', new String('string')], 'A quoted string should parse')); | |
error = SParse("(\"a)"); | |
assert(error instanceof SyntaxError); | |
assert(error.message == "Syntax error: Unterminated string literal", error.message); | |
assert.deepEqual(SParse(' a '), 'a', 'Whitespace should be ignored'); | |
assert.deepEqual(SParse(' '), '', 'The empty expression should parse'); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment