Last active
December 16, 2015 02:09
-
-
Save gerardpaapu/5360310 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
// The MIT License (MIT) | |
// Copyright (c) 2013 Gerard Paapu | |
// | |
// 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. | |
var Parsnip = (function () { | |
'use strict'; | |
var modules = {}; | |
function require(name) { | |
if (!(name in modules)) { | |
throw new Error('Module not found: ' + name); | |
} | |
return modules[name]; | |
} | |
(function (exports) { | |
var Location, Port; | |
Port = (function() { | |
function Port(source, _index) { | |
this.source = source; | |
this._index = _index != null ? _index : 0; | |
} | |
Port.from = function(str) { | |
if (str instanceof Port) { | |
return str; | |
} else { | |
return new Port(str); | |
} | |
}; | |
Port.prototype.take = function(n) { | |
return this.slice(0, n); | |
}; | |
Port.prototype.drop = function(n) { | |
return this.move(n); | |
}; | |
Port.prototype.isEmpty = function() { | |
return this.source.length <= this._index; | |
}; | |
Port.prototype.move = function(n) { | |
return new Port(this.source, this._index + n); | |
}; | |
Port.prototype.index = function(n) { | |
if (n < 0) { | |
return n; | |
} else { | |
return this._index + n; | |
} | |
}; | |
Port.prototype.slice = function(a, b) { | |
switch (arguments.length) { | |
case 0: | |
return this.source.slice(this.index(0)); | |
case 1: | |
return this.source.slice(this.index(a)); | |
default: | |
return this.source.slice(this.index(a), this.index(b)); | |
} | |
}; | |
Port.prototype.getLocation = function() { | |
var column, i, row; | |
row = 0; | |
column = 0; | |
i = 0; | |
while (i < this._index) { | |
if ((this.source.charAt(i++)) === '\n') { | |
column = 0; | |
row++; | |
} else { | |
column++; | |
} | |
} | |
return new Location(row, column); | |
}; | |
Port.prototype.toString = function() { | |
return this.slice(0); | |
}; | |
return Port; | |
})(); | |
exports.Port = Port; | |
Location = (function() { | |
function Location(row, column) { | |
this.row = row; | |
this.column = column; | |
} | |
Location.prototype.toString = function() { | |
return "row: " + this.row + ", col: " + this.column; | |
}; | |
return Location; | |
})(); | |
exports.Location = Location; | |
}.call(null, modules['./Port'] = {})); | |
(function (exports) { | |
var typeString; | |
typeString = function(obj) { | |
var full; | |
if (obj != null) { | |
full = Object.prototype.toString.call(obj); | |
return full.slice(8, -1); | |
} else { | |
return String(obj); | |
} | |
}; | |
/* | |
Adds the static methods `from` and `addConverter` to the | |
class to provide ways to easily coerce other javascript | |
values into that class | |
*/ | |
exports.addMethods = function(Klass) { | |
var converters; | |
converters = {}; | |
Klass.addConverter = function(type, fn) { | |
if (converters[type] != null) { | |
throw new Error("" + type + " is already defined"); | |
} | |
return converters[type] = fn; | |
}; | |
return Klass.from = function(obj) { | |
var instance, type; | |
type = typeString(obj); | |
if (obj instanceof Klass) { | |
return obj; | |
} else if (typeof converters[type] === 'function') { | |
instance = converters[type](obj); | |
if (!(instance instanceof Klass)) { | |
throw new TypeError("converted value is not a instance of " + Klass); | |
} | |
return instance; | |
} else { | |
throw new TypeError("No converter from " + type + " is defined"); | |
} | |
}; | |
}; | |
}.call(null, modules['./From'] = {})); | |
(function (exports) { | |
var Failure, Result, Success, | |
__hasProp = {}.hasOwnProperty, | |
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; | |
Result = (function() { | |
function Result() {} | |
Result.succeedWithValue = function(value) { | |
return new Success(value); | |
}; | |
Result.failWithMessage = function(message) { | |
return new Failure(message); | |
}; | |
Result.bind = function(m, f) { | |
return m.bind(f); | |
}; | |
Result.mzero = function() { | |
return Result.failWithMessage('failed'); | |
}; | |
Result.mreturn = function(value) { | |
return Result.succeedWithValue(value); | |
}; | |
return Result; | |
})(); | |
Success = (function(_super) { | |
__extends(Success, _super); | |
function Success(value) { | |
this.value = value; | |
} | |
Success.prototype.didSucceed = true; | |
Success.prototype.bind = function(fn) { | |
return fn(this.value); | |
}; | |
return Success; | |
})(Result); | |
Failure = (function(_super) { | |
__extends(Failure, _super); | |
function Failure(message) { | |
this.message = message; | |
} | |
Failure.prototype.didSucceed = false; | |
Failure.prototype.bind = function(_) { | |
return this; | |
}; | |
return Failure; | |
})(Result); | |
exports.Result = Result; | |
exports.Success = Success; | |
exports.Failure = Failure; | |
}.call(null, modules['./Result'] = {})); | |
(function (exports) { | |
/* | |
The Parser class exposes the method parse which attempts | |
to extract a value from an input `source`. | |
Parser::parse (source) -> Result<Continuation, Message> | |
`parse` on a given source, returns either a `Success` | |
which contains a Continuation or a Failure which contains | |
a Message communicating why and where it failed. | |
*/ | |
var Continuation, Failure, Message, Parser, Port, Result, Success, _ref, _ref1, | |
__hasProp = {}.hasOwnProperty, | |
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; | |
_ref = require('./Result'), Result = _ref.Result, Success = _ref.Success, Failure = _ref.Failure; | |
Parser = (function() { | |
function Parser(parse) { | |
this.parse = parse; | |
} | |
Parser.prototype.parse = function(source) { | |
throw new Error('Not Implemented'); | |
}; | |
return Parser; | |
})(); | |
/* | |
A Continuation contains the value that was successfully | |
parsed out of source, and the remainder of source that | |
was not consumed. | |
When a `parse` succeeds we may continue by parsing the | |
remaining source with another parser. | |
*/ | |
Continuation = (function() { | |
function Continuation(value, source) { | |
this.value = value; | |
this.source = source; | |
} | |
return Continuation; | |
})(); | |
/* | |
A Message contains human readable text explaining why the | |
parse failed and the source it failed on. | |
*/ | |
Message = (function() { | |
function Message(text, source) { | |
this.text = text; | |
this.source = source; | |
} | |
return Message; | |
})(); | |
_ref1 = require('./Result'), Result = _ref1.Result, Success = _ref1.Success, Failure = _ref1.Failure; | |
Parser.Succeed = (function(_super) { | |
__extends(Succeed, _super); | |
function Succeed(value) { | |
this.value = value; | |
} | |
Succeed.prototype.parse = function(source) { | |
var continuation; | |
continuation = new Continuation(this.value, source); | |
return new Success(continuation); | |
}; | |
return Succeed; | |
})(Parser); | |
Parser.Fail = (function(_super) { | |
__extends(Fail, _super); | |
function Fail(text) { | |
this.text = text; | |
} | |
Fail.prototype.parse = function(source) { | |
var message; | |
message = new Message(this.text, source); | |
return new Failure(message); | |
}; | |
return Fail; | |
})(Parser); | |
Port = require('./Port').Port; | |
Parser.Item = (function(_super) { | |
__extends(Item, _super); | |
function Item() {} | |
Item.prototype.parse = function(source) { | |
var continuation, rest, val; | |
source = Port.from(source); | |
if (source.isEmpty()) { | |
return new Failure('Source empty', source); | |
} else { | |
val = source.take(1); | |
rest = source.drop(1); | |
continuation = new Continuation(val, rest); | |
return new Success(continuation); | |
} | |
}; | |
return Item; | |
})(Parser); | |
Message = (function() { | |
function Message(text, source) { | |
this.text = text; | |
this.source = source; | |
} | |
return Message; | |
})(); | |
Continuation = (function() { | |
function Continuation(value, source) { | |
this.value = value; | |
this.source = source; | |
} | |
return Continuation; | |
})(); | |
/* | |
mzero, return and bind implement the monad operations | |
By implementing these methods we can exploit | |
existing composability patterns for monadic values | |
* I'm using mreturn as the name for return because | |
return is a keyword in javascript (and coffeescript) | |
*/ | |
Parser.mzero = function() { | |
return new Parser.Fail('zero'); | |
}; | |
Parser.mreturn = function(v) { | |
return new Parser.Succeed(v); | |
}; | |
Parser.bind = function(m, f) { | |
var _this = this; | |
return new Parser(function(source) { | |
return (m.parse(source)).bind(function(c) { | |
var parser; | |
parser = f(c.value); | |
return parser.parse(c.source); | |
}); | |
}); | |
}; | |
Parser.prototype.bind = function(f) { | |
return Parser.bind(this, f); | |
}; | |
Parser.lazy = function(makeParser) { | |
var parser; | |
parser = null; | |
return new Parser(function(input) { | |
if (parser == null) { | |
parser = makeParser(); | |
} | |
return parser.parse(input); | |
}); | |
}; | |
/* | |
Add the static methods `Parser.from` and `Parser.addConverter` | |
these allow other modules to define a conversion from | |
another type to a Parser. | |
*/ | |
(function() { | |
var addMethods; | |
addMethods = require('./From').addMethods; | |
return addMethods(Parser); | |
})(); | |
exports.Parser = Parser; | |
exports.Message = Message; | |
exports.Continuation = Continuation; | |
}.call(null, modules['./Core'] = {})); | |
(function (exports) { | |
var Continuation, Failure, Message, Parser, Port, Result, Success, cloneRegexp, _ref, _ref1, | |
__hasProp = {}.hasOwnProperty, | |
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; | |
_ref = require('./Core'), Parser = _ref.Parser, Continuation = _ref.Continuation, Message = _ref.Message; | |
_ref1 = require('./Result'), Result = _ref1.Result, Success = _ref1.Success, Failure = _ref1.Failure; | |
Port = require('./Port').Port; | |
Parser.Exactly = (function(_super) { | |
__extends(Exactly, _super); | |
function Exactly(string) { | |
this.string = string; | |
} | |
Exactly.prototype.parse = function(source) { | |
var cont, head, message, reason, rest; | |
source = Port.from(source); | |
head = source.slice(0, this.string.length); | |
if (head === this.string) { | |
rest = source.move(this.string.length); | |
cont = new Continuation(head, rest); | |
return new Success(cont); | |
} else { | |
reason = "Source didn't match: '" + this.string + "'"; | |
message = new Message(reason, source); | |
return new Failure(message); | |
} | |
}; | |
return Exactly; | |
})(Parser); | |
Parser.addConverter('String', function(str) { | |
return new Parser.Exactly(str); | |
}); | |
cloneRegexp = function(source) { | |
var destination, flags, src; | |
src = source.source; | |
if ((src.charAt(0)) !== '^') { | |
src = '^' + src; | |
} | |
flags = [source.global ? 'g' : '', source.ignoreCase ? 'i' : '', source.multiline ? 'm' : ''].join(''); | |
destination = new RegExp(src, flags); | |
return destination; | |
}; | |
Parser.RegExp = (function(_super) { | |
__extends(RegExp, _super); | |
function RegExp(pattern, index) { | |
this._pattern = pattern; | |
this.index = index != null ? index : 0; | |
} | |
RegExp.prototype.getPattern = function() { | |
return cloneRegexp(this._pattern); | |
}; | |
RegExp.prototype.parse = function(source) { | |
var cont, match, message, pattern, reason, rest, val; | |
source = Port.from(source); | |
pattern = this.getPattern(); | |
match = pattern.exec(source.slice()); | |
if (match != null) { | |
val = match[this.index]; | |
rest = source.move(match[0].length); | |
cont = new Continuation(val, rest); | |
return new Success(cont); | |
} else { | |
reason = "Source didn't match: /" + pattern.source + "/"; | |
message = new Message(reason, source); | |
return new Failure(message); | |
} | |
}; | |
return RegExp; | |
})(Parser); | |
Parser.addConverter('RegExp', function(str) { | |
return new Parser.RegExp(str); | |
}); | |
Parser.EOF = new Parser(function(source) { | |
source = Port.from(source); | |
if (source.isEmpty()) { | |
return new Success(new Continuation(null, source)); | |
} else { | |
return new Failure(new Message('Expected EOF', source)); | |
} | |
}); | |
}.call(null, modules['./Matchers'] = {})); | |
(function (exports) { | |
var Any, Continuation, Failure, Location, Message, Or, Parser, Port, Result, Seq, Success, bind, mreturn, mzero, _ref, _ref1, _ref2, | |
__slice = [].slice; | |
_ref = require('./Core'), Parser = _ref.Parser, Message = _ref.Message, Continuation = _ref.Continuation; | |
_ref1 = require('./Result'), Success = _ref1.Success, Failure = _ref1.Failure, Result = _ref1.Result; | |
bind = Parser.bind, mreturn = Parser.mreturn, mzero = Parser.mzero; | |
_ref2 = require('./Port'), Port = _ref2.Port, Location = _ref2.Location; | |
Parser.prototype.andThen = function(parser) { | |
return bind(this, function(v1) { | |
return bind(parser, function(v2) { | |
return mreturn([v1, v2]); | |
}); | |
}); | |
}; | |
Parser.prototype.convert = function(converter) { | |
return this.bind(function(v) { | |
return new Parser.Succeed(converter(v)); | |
}); | |
}; | |
Parser.prototype.convertTo = function(Klass) { | |
return this.convert(function(v) { | |
return new Klass(v); | |
}); | |
}; | |
Seq = Parser.Seq = function(parsers) { | |
var cons, first, rest; | |
cons = function(a, b) { | |
return [a].concat(b); | |
}; | |
if (parsers.length === 0) { | |
return new Parser.Succeed([]); | |
} else { | |
first = parsers[0], rest = 2 <= parsers.length ? __slice.call(parsers, 1) : []; | |
return (Parser.from(first)).bind(function(head) { | |
return (Seq(rest)).bind(function(tail) { | |
return new Parser.Succeed(cons(head, tail)); | |
}); | |
}); | |
} | |
}; | |
Parser.addConverter('Array', Seq); | |
Or = Parser.Or = function(a, b) { | |
a = Parser.from(a); | |
b = Parser.from(b); | |
return new Parser(function(source) { | |
var result; | |
result = a.parse(source); | |
if (result.didSucceed) { | |
return result; | |
} else { | |
return b.parse(source); | |
} | |
}); | |
}; | |
Parser.prototype.or = function(other) { | |
return Or(this, other); | |
}; | |
Parser.prototype.maybe = function(v) { | |
return Or(this, new Parser.Succeed(v)); | |
}; | |
Any = Parser.Any = function(arr) { | |
var first, rest; | |
if (arr.length === 0) { | |
return new Parser.Fail('no candidates'); | |
} else if (arr.length === 1) { | |
return Parser.from(arr[0]); | |
} else if (arr.length === 2) { | |
return Or(arr[0], arr[1]); | |
} else { | |
first = arr[0], rest = 2 <= arr.length ? __slice.call(arr, 1) : []; | |
return Or(first, Any(rest)); | |
} | |
}; | |
Parser.prototype.onceOrMore = function() { | |
var cons, | |
_this = this; | |
cons = function(a, b) { | |
return [a].concat(b); | |
}; | |
return this.bind(function(head) { | |
var parseRest; | |
parseRest = (_this.onceOrMore()).maybe([]); | |
return parseRest.bind(function(tail) { | |
return new Parser.Succeed(cons(head, tail)); | |
}); | |
}); | |
}; | |
Parser.prototype.zeroOrMore = function() { | |
var any; | |
any = this.onceOrMore(); | |
return any.maybe([]); | |
}; | |
Parser.prototype.precededBy = function(prefix) { | |
var _this = this; | |
return (Parser.from(prefix)).bind(function(_) { | |
return _this; | |
}); | |
}; | |
Parser.prototype.followedBy = function(suffix) { | |
return this.bind(function(v1) { | |
return (Parser.from(suffix)).bind(function(_) { | |
return mreturn(v1); | |
}); | |
}); | |
}; | |
Parser.prototype.surroundedBy = function(left, right) { | |
return (this.precededBy(left)).followedBy(right); | |
}; | |
Parser.prototype.is = function(test) { | |
return this.bind(function(v1) { | |
if (test(v1)) { | |
return mreturn(v1); | |
} else { | |
return new Parser.Fail('Failed predicate'); | |
} | |
}); | |
}; | |
Parser.prototype.isnt = function(test) { | |
return this.is(function(v) { | |
return !(test(v)); | |
}); | |
}; | |
Parser.prototype.separatedBy = function(comma) { | |
var squash, tail; | |
comma = Parser.from(comma); | |
squash = function(_arg) { | |
var a, b; | |
a = _arg[0], b = _arg[1]; | |
return [a].concat(b); | |
}; | |
tail = (this.precededBy(comma)).zeroOrMore(); | |
return this.andThen(tail).convert(squash); | |
}; | |
Parser.prototype.withLocation = function(fn) { | |
var _this = this; | |
return new Parser(function(_source) { | |
var start; | |
_source = Port.from(_source); | |
start = _source.getLocation(); | |
return (_this.parse(_source)).bind(function(cont) { | |
var end, next, source, value, _value; | |
source = cont.source, value = cont.value; | |
end = source.getLocation(); | |
_value = fn(value, start, end); | |
next = new Continuation(_value, source); | |
return new Success(next); | |
}); | |
}); | |
}; | |
Parser.prototype.tag = function(tag_str) { | |
return this.withLocation(function(value, start, end) { | |
return { | |
type: tag_str, | |
value: value, | |
start: start, | |
end: end | |
}; | |
}); | |
}; | |
Parser.prototype.toFunction = function(opts) { | |
var _this = this; | |
return function(source) { | |
var complete, cont, loc, result, text; | |
if (opts == null) { | |
opts = {}; | |
} | |
result = _this.parse(source); | |
if (result.didSucceed) { | |
cont = result.value; | |
complete = cont.source.isEmpty(); | |
if (opts.noTrailing && !complete) { | |
throw new Error("Trailing Characters " + cont.source); | |
} | |
return cont.value; | |
} else { | |
text = result.message.text; | |
loc = result.message.source.getLocation(); | |
throw new SyntaxError("" + text + " " + loc); | |
} | |
}; | |
}; | |
Parser.prototype.dontConsume = function() { | |
var _this = this; | |
return new Parser(function(source) { | |
return (_this.parse(source)).bind(function(c) { | |
return new Success(new Continuation(c.value, source)); | |
}); | |
}); | |
}; | |
Parser.prototype.lookAhead = function(p) { | |
p = Parser.from(p); | |
return this.followedBy(p.dontConsume()); | |
}; | |
}.call(null, modules['./Combinators'] = {})); | |
(function (exports) { | |
var Continuation, Failure, Location, Message, Parser, Port, Result, Success, _ref, _ref1, _ref2; | |
_ref = require('./Core'), Parser = _ref.Parser, Continuation = _ref.Continuation, Message = _ref.Message; | |
require('./Matchers'); | |
require('./Combinators'); | |
_ref1 = require('./Port'), Port = _ref1.Port, Location = _ref1.Location; | |
_ref2 = require('./Result'), Result = _ref2.Result, Success = _ref2.Success, Failure = _ref2.Failure; | |
exports.Parser = Parser; | |
exports.Continuation = Continuation; | |
exports.Message = Message; | |
exports.Result = Result; | |
exports.Success = Success; | |
exports.Failure = Failure; | |
exports.Port = Port; | |
exports.Location = Location; | |
}.call(null, modules['./Parser'] = {})); | |
return require('./Parser'); | |
}()); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment