Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 70 additions & 65 deletions lib/combinators.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"use strict";

var M = require('./maybeerror.js');
var M = require('./maybeerror.js'),
F = require('./funcs.js');


function Parser(f) {
Expand Down Expand Up @@ -46,10 +47,6 @@ function good(value, rest, state) {
return M.pure(result(value, rest, state));
}

function compose(f, g) {
return function(x) { return f(g(x)); };
}


function fmap(g, parser) {
/*
Expand Down Expand Up @@ -98,10 +95,7 @@ function error(e) {
/*
e -> Parser e s (m t) a
*/
function f(_xs_, _s_) {
return M.error(e);
}
return new Parser(f);
return new Parser(F.constF(M.error(e)));
}

function catchError(f, parser) {
Expand All @@ -126,40 +120,66 @@ function mapError(f, parser) {
*/
checkFunction('mapError', f);
checkParser('mapError', parser);
return catchError(compose(error, f), parser);
return catchError(F.compose(error, f), parser);
}

function put(xs) {
function update(g) {
/*
m t -> Parser e s (m t) a
(m t -> m t) -> Parser e s (m t) (m t)
*/
function f(_xs_, s) {
return good(null, xs, s);
checkFunction('update', g);
function f(xs, s) {
return good(xs, g(xs), s);
}
return new Parser(f);
}

function putState(s) {
// m t -> Parser e s (m t) (m t)
var put = F.compose(update, F.constF),
// Parser e s (m t) (m t)
get = update(F.id);

function updateState(g) {
/*
s -> Parser e s (m t) a
(s -> s) -> Parser e s (m t) s
*/
function f(xs, _s_) {
return good(null, xs, s);
checkFunction('updateState', g);
function f(xs, s) {
return good(s, xs, g(s));
}
return new Parser(f);
}

function updateState(g) {
// s -> Parser e s (m t) s
var putState = F.compose(updateState, F.constF),
// Parser e s (m t) s
getState = updateState(F.id);

function updateStateForKey(key, g) {
/*
(s -> s) -> Parser e s (m t) a
x -> (y -> z) -> Parser e s (m t) z
*/
checkFunction('updateState', g);
checkFunction('updateStateForKey', g);
function f(xs, s) {
return good(null, xs, g(s));
return good(s[key], xs, F.fmapObject(s, key, g));
}
return new Parser(f);
}

function putStateForKey(key, newValue) {
/*
y -> z -> Parser e s (m t) z
*/
return updateStateForKey(key, F.constF(newValue));
}

function getStateForKey(key) {
/*
y -> Parser e s (m t) z
*/
return updateStateForKey(key, F.id);
}

function check(predicate, parser) {
/*
(a -> Bool) -> Parser e s (m t) a -> Parser e s (m t) a
Expand Down Expand Up @@ -212,15 +232,11 @@ function many1(parser) {
return check(function(x) {return x.length > 0;}, many0(parser));
}

function _get_args(args, ix) {
return Array.prototype.slice.call(args, ix);
}

function seq() {
/*
[Parser e s (m t) a] -> Parser e s (m t) [a]
*/
var parsers = _get_args(arguments, 0);
var parsers = F.getArgs(arguments, 0);
parsers.map(checkParser.bind(null, 'seq')); // can I use `forEach` here instead of `map`?
function f(xs, s) {
var vals = [],
Expand All @@ -246,7 +262,7 @@ function appP(p) {
/*
Parser e s (m t) (a -> ... -> z) -> Parser e s (m t) a -> ... -> Parser e s (m t) z
*/
var parsers = _get_args(arguments, 1);
var parsers = F.getArgs(arguments, 1);
checkParser('appP', p);
parsers.map(checkParser.bind(null, 'appP'));
return bind(p, function(f) {
Expand All @@ -261,25 +277,17 @@ function app(f) {
/*
(a -> ... -> z) -> Parser e s (m t) a -> ... -> Parser e s (m t) z
*/
var args = _get_args(arguments, 1);
var args = F.getArgs(arguments, 1);
return appP.apply(null, [pure(f)].concat(args));
}

function _first(x, _) {
return x;
}

function _second(_, y) {
return y;
}

function seq2L(p1, p2) {
/*
Parser e s (m t) a -> Parser e s (m t) b -> Parser e s (m t) a
*/
checkParser('seq2L', p1);
checkParser('seq2L', p2);
return app(_first, p1, p2);
return app(F.first, p1, p2);
}

function seq2R(p1, p2) {
Expand All @@ -288,7 +296,7 @@ function seq2R(p1, p2) {
*/
checkParser('seq2R', p1);
checkParser('seq2R', p2);
return app(_second, p1, p2);
return app(F.second, p1, p2);
}

function lookahead(parser) {
Expand Down Expand Up @@ -326,7 +334,7 @@ function alt() {
/*
[Parser e s (m t) a] -> Parser e s (m t) a
*/
var parsers = _get_args(arguments, 0);
var parsers = F.getArgs(arguments, 0);
parsers.map(checkParser.bind(null, 'alt')); // use `forEach` here, too?
function f(xs, s) {
var r = M.zero;
Expand Down Expand Up @@ -377,17 +385,13 @@ function _buildSepByValue(fst, pairs) {
};
}

function _pair(x, y) {
return [x, y];
}

function sepBy1(parser, separator) {
/*
Parser e s (m t) a -> Parser e s (m t) b -> Parser e s (m t) {'values': [a], 'separators': [b]}
*/
return app(_buildSepByValue,
parser,
many0(app(_pair, separator, parser)));
many0(app(F.pair, separator, parser)));
}

function sepBy0(parser, separator) {
Expand All @@ -398,13 +402,7 @@ function sepBy0(parser, separator) {
}

// Parser e s (m t) a
var zero = new Parser(function(_xs_, _s_) {return M.zero;});

// Parser e s (m t) (m t)
var get = new Parser(function(xs, s) {return good(xs, xs, s);});

// Parser e s (m t) s
var getState = new Parser(function(xs, s) {return good(s, xs, s);});
var zero = new Parser(F.constF(M.zero));


function _build_set(elems) {
Expand Down Expand Up @@ -484,23 +482,27 @@ function Itemizer(f) {
}


function _bump(char, position) {
function _bump(char, s) {
/*
only treats `\n` as newline
*/
var line = position[0],
col = position[1];
if ( char === '\n' ) {
return [line + 1, 1];
}
return [line, col + 1];
return F.fmapObject(s, 'position', function(position) {
var line = position[0],
col = position[1];
if ( char === '\n' ) {
return [line + 1, 1];
}
return [line, col + 1];
});
}

var basic = Itemizer(function(first, s) { return s; }),
var basic = Itemizer(F.second),
// assumes the state is a 2-tuple of integers (line, column)
position = Itemizer(_bump),
// assumes that state is an integer -- how many tokens have been consumed
count = Itemizer(function(first, s) { return s + 1; });
count = Itemizer(function(first, s) {
return F.fmapObject(s, 'count', F.inc);
});


function run(parser, input_string, state) {
Expand All @@ -521,9 +523,15 @@ module.exports = {
'error' : error,
'catchError' : catchError,
'mapError' : mapError,
'update' : update,
'put' : put,
'putState' : putState,
'get' : get,
'updateState': updateState,
'putState' : putState,
'getState' : getState,
'updateStateForKey' : updateStateForKey,
'putStateForKey' : putStateForKey,
'getStateForKey' : getStateForKey,
'check' : check,
'many0' : many0,
'many1' : many1,
Expand All @@ -538,8 +546,6 @@ module.exports = {
'commit' : commit,
'alt' : alt,
'zero' : zero,
'get' : get,
'getState' : getState,
'sepBy0' : sepBy0,
'sepBy1' : sepBy1,

Expand All @@ -552,4 +558,3 @@ module.exports = {
'checkParser' : checkParser,
'good' : good
};

60 changes: 60 additions & 0 deletions lib/funcs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
'use strict';

function compose(f, g) {
return function(x) { return f(g(x)); };
}

function id(x) {
return x;
}

function constF(x) {
return function() { return x; };
}

function fmapObject(obj, key, f) {
if ( !obj.hasOwnProperty(key) ) {
throw new Error('object does not have key ' + key);
}
var out = {};
Object.getOwnPropertyNames(obj).forEach(function(name) {
if ( name === key ) {
out[name] = f(obj[name]);
} else {
out[name] = obj[name];
}
});
return out;
}

function first(x, _) {
return x;
}

function second(_, y) {
return y;
}

function getArgs(args, ix) {
return Array.prototype.slice.call(args, ix);
}

function pair(x, y) {
return [x, y];
}

function inc(x) {
return x + 1;
}

module.exports = {
'compose' : compose,
'id' : id,
'constF' : constF,
'fmapObject': fmapObject,
'first' : first,
'second' : second,
'getArgs' : getArgs,
'pair' : pair,
'inc' : inc
};
Loading