Comparing version 1.0.1 to 1.1.0
140
index.js
@@ -6,8 +6,8 @@ "use strict"; | ||
}); | ||
exports.toValue = exports.toPromise = exports.takeLeft = exports.takeRight = exports.recursiveParser = exports.whitespace = exports.skip = exports.possibly = exports.lookAhead = exports.anythingExcept = exports.everythingUntil = exports.between = exports.choice = exports.sepBy1 = exports.sepBy = exports.sequenceOf = exports.namedSequenceOf = exports.anyOfString = exports.letters = exports.letter = exports.digits = exports.digit = exports.regex = exports.str = exports.char = exports.mapTo = exports.many1 = exports.many = exports.succeedWith = exports.fail = exports.decide = exports.parse = exports.tapParser = exports.composeParsers = exports.pipeParsers = exports.Parser = void 0; | ||
exports.toValue = exports.toPromise = exports.takeLeft = exports.takeRight = exports.recursiveParser = exports.whitespace = exports.skip = exports.possibly = exports.lookAhead = exports.anythingExcept = exports.everythingUntil = exports.between = exports.choice = exports.sepBy1 = exports.sepBy = exports.sequenceOf = exports.namedSequenceOf = exports.anyOfString = exports.letters = exports.letter = exports.digits = exports.digit = exports.regex = exports.str = exports.char = exports.leftMapTo = exports.mapTo = exports.many1 = exports.many = exports.succeedWith = exports.fail = exports.decide = exports.parse = exports.tapParser = exports.composeParsers = exports.pipeParsers = exports.Parser = void 0; | ||
var _data = require("data.either"); | ||
const id = x => x; // type ParserState a = Either String (Int, String, a) | ||
// type Parser a b = () -> ParserState a -> ParserState b | ||
const id = x => x; // type ParserState e a = Either (Int, e) (Int, String, a) | ||
// type Parser e a b = () -> ParserState e a -> ParserState e b | ||
@@ -40,7 +40,10 @@ | ||
p.of = p['fantasy-land/of']; | ||
p.leftMap = fn => FL(() => state => p()(state).leftMap(([i, e]) => [i, fn(e, i)])); | ||
return p; | ||
}; // Parser :: Parser a a | ||
}; // Parser :: Parser e a a | ||
const Parser = FL(() => id); // pipeParsers :: [Parser * *] -> Parser * * | ||
const Parser = FL(() => id); // pipeParsers :: [Parser * * *] -> Parser * * * | ||
@@ -51,3 +54,3 @@ exports.Parser = Parser; | ||
return fns.slice(1).reduce((nextState, fn) => fn()(nextState), fns[0]()(state)); | ||
}); // composeParsers :: [Parser * *] -> Parser * * | ||
}); // composeParsers :: [Parser * * *] -> Parser * * * | ||
@@ -59,3 +62,3 @@ | ||
return pipeParsers([...fns].reverse())()(x); | ||
}; // tapParser :: (a => void) -> Parser a a | ||
}; // tapParser :: (a => void) -> Parser e a a | ||
@@ -68,3 +71,3 @@ | ||
return state; | ||
}); // parse :: Parser a b -> String -> Either String b | ||
}); // parse :: Parser e a b -> String -> Either e b | ||
@@ -77,3 +80,3 @@ | ||
return parser()(parserState).map(([_, __, result]) => result); | ||
}; // decide :: (a -> Parser b c) -> Parser b c | ||
}; // decide :: (a -> Parser e b c) -> Parser e b c | ||
@@ -88,3 +91,3 @@ | ||
}); | ||
}); // fail :: String -> Parser a b | ||
}); // fail :: String -> Parser String a b | ||
@@ -94,5 +97,5 @@ | ||
const fail = errorMessage => FL(() => _ => { | ||
return (0, _data.Left)(errorMessage); | ||
}); // succeedWith :: b -> Parser a b | ||
const fail = errorMessage => FL(() => state => { | ||
return state.chain(([i]) => (0, _data.Left)([i, errorMessage])); | ||
}); // succeedWith :: b -> Parser e a b | ||
@@ -104,3 +107,3 @@ | ||
return state.map(([i, s]) => [i, s, x]); | ||
}); // many :: Parser a b -> Parser a [b] | ||
}); // many :: Parser e a b -> Parser e a [b] | ||
@@ -136,3 +139,3 @@ | ||
}); | ||
}); // many1 :: Parser a b -> Parser a [b] | ||
}); // many1 :: Parser e a b -> Parser String a [b] | ||
@@ -146,3 +149,3 @@ | ||
if (value.length === 0) { | ||
return (0, _data.Left)(`ParseError 'many1' (position ${index}): Expecting to match at least one value`); | ||
return (0, _data.Left)([index, `ParseError 'many1' (position ${index}): Expecting to match at least one value`]); | ||
} | ||
@@ -152,3 +155,3 @@ | ||
}); | ||
}); // mapTo :: (a -> b) -> Parser a b | ||
}); // mapTo :: (a -> b) -> Parser e a b | ||
@@ -162,3 +165,3 @@ | ||
}); | ||
}); // char :: Char -> Parser a String | ||
}); // leftMapTo :: ((e, Int) -> f) -> Parser f a b | ||
@@ -168,2 +171,11 @@ | ||
const leftMapTo = fn => FL(() => state => { | ||
return state.leftMap(([index, errorString]) => { | ||
return [index, fn(errorString, index)]; | ||
}); | ||
}); // char :: Char -> Parser String a String | ||
exports.leftMapTo = leftMapTo; | ||
const char = c => FL(() => state => { | ||
@@ -181,9 +193,9 @@ if (!c || c.length !== 1) { | ||
} else { | ||
return (0, _data.Left)(`ParseError (position ${index}): Expecting character '${c}', got '${rest[0]}'`); | ||
return (0, _data.Left)([index, `ParseError (position ${index}): Expecting character '${c}', got '${rest[0]}'`]); | ||
} | ||
} | ||
return (0, _data.Left)(`ParseError (position ${index}): Expecting character '${c}', but got end of input.`); | ||
return (0, _data.Left)([index, `ParseError (position ${index}): Expecting character '${c}', but got end of input.`]); | ||
}); | ||
}); // str :: String -> Parser a String | ||
}); // str :: String -> Parser String a String | ||
@@ -205,9 +217,9 @@ | ||
} else { | ||
return (0, _data.Left)(`ParseError (position ${index}): Expecting string '${s}', got '${rest.slice(0, s.length)}...'`); | ||
return (0, _data.Left)([index, `ParseError (position ${index}): Expecting string '${s}', got '${rest.slice(0, s.length)}...'`]); | ||
} | ||
} | ||
return (0, _data.Left)(`ParseError (position ${index}): Expecting string '${s}', but got end of input.`); | ||
return (0, _data.Left)([index, `ParseError (position ${index}): Expecting string '${s}', but got end of input.`]); | ||
}); | ||
}); // regex :: RegExp -> Parser a String | ||
}); // regex :: RegExp -> Parser String a String | ||
@@ -238,10 +250,10 @@ | ||
} else { | ||
return (0, _data.Left)(`ParseError (position ${index}): Expecting string matching '${re}', got '${rest.slice(0, 5)}...'`); | ||
return (0, _data.Left)([index, `ParseError (position ${index}): Expecting string matching '${re}', got '${rest.slice(0, 5)}...'`]); | ||
} | ||
} | ||
return (0, _data.Left)(`ParseError (position ${index}): Expecting string matching '${re}', but got end of input.`); | ||
return (0, _data.Left)([index, `ParseError (position ${index}): Expecting string matching '${re}', but got end of input.`]); | ||
}); | ||
}); | ||
}; // digit :: Parser a String | ||
}; // digit :: Parser String a String | ||
@@ -258,12 +270,12 @@ | ||
} else { | ||
return (0, _data.Left)(`ParseError (position ${index}): Expecting digit, got '${rest[0]}'`); | ||
return (0, _data.Left)([index, `ParseError (position ${index}): Expecting digit, got '${rest[0]}'`]); | ||
} | ||
} | ||
return (0, _data.Left)(`ParseError (position ${index}): Expecting digit, but got end of input.`); | ||
return (0, _data.Left)([index, `ParseError (position ${index}): Expecting digit, but got end of input.`]); | ||
}); | ||
}); // digits :: Parser a String | ||
}); // digits :: Parser String a String | ||
exports.digit = digit; | ||
const digits = pipeParsers([many1(digit), mapTo(x => x.join(''))]); // letter :: Parser a String | ||
const digits = many1(digit).map(x => x.join('')); // letter :: Parser String a String | ||
@@ -279,12 +291,12 @@ exports.digits = digits; | ||
} else { | ||
return (0, _data.Left)(`ParseError (position ${index}): Expecting letter, got ${rest[0]}`); | ||
return (0, _data.Left)([index, `ParseError (position ${index}): Expecting letter, got ${rest[0]}`]); | ||
} | ||
} | ||
return (0, _data.Left)(`ParseError (position ${index}): Expecting letter, but got end of input.`); | ||
return (0, _data.Left)([index, `ParseError (position ${index}): Expecting letter, but got end of input.`]); | ||
}); | ||
}); // letters :: Parser a String | ||
}); // letters :: Parser String a String | ||
exports.letter = letter; | ||
const letters = pipeParsers([many1(letter), mapTo(x => x.join(''))]); // anyOfString :: String -> Parser a String | ||
const letters = many1(letter).map(x => x.join('')); // anyOfString :: String -> Parser String a String | ||
@@ -301,9 +313,9 @@ exports.letters = letters; | ||
} else { | ||
return (0, _data.Left)(`ParseError (position ${index}): Expecting any of the string "${s}", got ${rest[0]}`); | ||
return (0, _data.Left)([index, `ParseError (position ${index}): Expecting any of the string "${s}", got ${rest[0]}`]); | ||
} | ||
} | ||
return (0, _data.Left)(`ParseError (position ${index}): Expecting any of the string "${s}", but got end of input.`); | ||
return (0, _data.Left)([index, `ParseError (position ${index}): Expecting any of the string "${s}", but got end of input.`]); | ||
}); | ||
}); // namedSequenceOf :: [(String, Parser a b)] -> Parser a (StrMap b) | ||
}); // namedSequenceOf :: [(String, Parser e a b)] -> Parser e a (StrMap b) | ||
@@ -340,3 +352,3 @@ | ||
}); | ||
}); // sequenceOf :: [Parser a b] -> Parser a [b] | ||
}); // sequenceOf :: [Parser e a b] -> Parser e a [b] | ||
@@ -373,3 +385,3 @@ | ||
}); | ||
}); // sepBy :: Parser a c -> Parser a b -> Parser a [b] | ||
}); // sepBy :: Parser e a c -> Parser e a b -> Parser e a [b] | ||
@@ -422,3 +434,3 @@ | ||
}); | ||
}); // sepBy1 :: Parser a c -> Parser a b -> Parser a [b] | ||
}); // sepBy1 :: Parser e a c -> Parser f a b -> Parser String a [b] | ||
@@ -432,3 +444,3 @@ | ||
if (value.length === 0) { | ||
return (0, _data.Left)(`ParseError 'sepBy1' (position ${index}): Expecting to match at least one separated value`); | ||
return (0, _data.Left)([index, `ParseError 'sepBy1' (position ${index}): Expecting to match at least one separated value`]); | ||
} | ||
@@ -438,3 +450,3 @@ | ||
}); | ||
}); // choice :: [Parser a *] -> Parser a * | ||
}); // choice :: [Parser e a *] -> Parser e a * | ||
@@ -445,2 +457,3 @@ | ||
const choice = parsers => FL(() => state => { | ||
const lefts = []; | ||
return state.chain(([index]) => { | ||
@@ -453,3 +466,6 @@ let match = null; | ||
out.cata({ | ||
Left: id, | ||
Left: x => { | ||
lefts.push(x); | ||
return x; | ||
}, | ||
Right: x => { | ||
@@ -464,3 +480,5 @@ exit = true; | ||
if (!match) { | ||
return (0, _data.Left)(`ParseError 'choice' (position ${index}): Expecting to match at least parser`); | ||
const furthestLeft = lefts.reduce((acc, l) => l[0] > acc[0] ? l : acc, [-Infinity]); | ||
return (0, _data.Left)(furthestLeft); // console.log(furthestLeft); | ||
// return Left ([index, `ParseError 'choice' (position ${index}): Expecting to match at least parser`]); | ||
} | ||
@@ -470,3 +488,3 @@ | ||
}); | ||
}); // between :: Parser a b -> Parser a c -> Parser a d -> Parser a d | ||
}); // between :: Parser e a b -> Parser f a c -> Parser g a d -> Parser g a d | ||
@@ -476,3 +494,3 @@ | ||
const between = leftParser => rightParser => parser => pipeParsers([sequenceOf([leftParser, parser, rightParser]), mapTo(([_, x]) => x)]); // everythingUntil :: Parser a b -> Parser a c | ||
const between = leftParser => rightParser => parser => sequenceOf([leftParser, parser, rightParser]).map(([_, x]) => x); // everythingUntil :: Parser e a b -> Parser String a c | ||
@@ -512,3 +530,3 @@ | ||
if (eof) { | ||
return (0, _data.Left)(`ParseError 'everythingUntil' (position ${nextState[0]}): Unexpected end of input.`); | ||
return (0, _data.Left)([nextState[0], `ParseError 'everythingUntil' (position ${nextState[0]}): Unexpected end of input.`]); | ||
} | ||
@@ -519,3 +537,3 @@ | ||
}); | ||
}); // anythingExcept :: Parser a b -> Parser a c | ||
}); // anythingExcept :: Parser e a b -> Parser String a c | ||
@@ -530,6 +548,6 @@ | ||
Left: () => (0, _data.Right)([index + 1, targetString, targetString[index]]), | ||
Right: ([_, __, s]) => (0, _data.Left)(`ParseError 'anythingExcept' (position ${index}): Matched '${s}' from the exception parser`) | ||
Right: ([_, __, s]) => (0, _data.Left)([index, `ParseError 'anythingExcept' (position ${index}): Matched '${s}' from the exception parser`]) | ||
}); | ||
}); | ||
}); // lookAhead :: Parser a b -> Parser a b | ||
}); // lookAhead :: Parser e a b -> Parser e a b | ||
@@ -544,3 +562,3 @@ | ||
}); | ||
}); // possibly :: Parser a b -> Parser a (b|null) | ||
}); // possibly :: Parser e a b -> Parser e a (b|null) | ||
@@ -558,3 +576,3 @@ | ||
}); | ||
}); // skip :: Parser a b -> Parser a a | ||
}); // skip :: Parser e a b -> Parser e a a | ||
@@ -569,11 +587,11 @@ | ||
}); | ||
}); // whitespace :: Parser a String | ||
}); // whitespace :: Parser e a String | ||
exports.skip = skip; | ||
const whitespace = pipeParsers([many(anyOfString(' \n\t\r')), mapTo(x => x.join(''))]); // recursiveParser :: (() => Parser a b) -> Parser a b | ||
const whitespace = many(anyOfString(' \n\t\r')).map(x => x.join('')); // recursiveParser :: (() => Parser e a b) -> Parser e a b | ||
exports.whitespace = whitespace; | ||
const recursiveParser = parserThunk => FL(() => parserThunk()()); // takeRight :: Parser a b -> Parser b c -> Parser a c | ||
const recursiveParser = parserThunk => FL(() => parserThunk()()); // takeRight :: Parser e a b -> Parser f b c -> Parser f a c | ||
@@ -583,3 +601,3 @@ | ||
const takeRight = lParser => rParser => pipeParsers([lParser, rParser]); // takeLeft :: Parser a b -> Parser b c -> Parser a b | ||
const takeRight = lParser => rParser => pipeParsers([lParser, rParser]); // takeLeft :: Parser e a b -> Parser f b c -> Parser e a b | ||
@@ -589,3 +607,3 @@ | ||
const takeLeft = lParser => rParser => pipeParsers([sequenceOf([lParser, rParser]), mapTo(x => x[0])]); // toPromise :: Either a b -> Promise a b | ||
const takeLeft = lParser => rParser => sequenceOf([lParser, rParser]).map(x => x[0]); // toPromise :: Either a b -> Promise a b | ||
@@ -607,4 +625,6 @@ | ||
return result.cata({ | ||
Left: x => { | ||
throw new Error(x); | ||
Left: ([index, x]) => { | ||
const e = new Error(x); | ||
e.parseIndex = index; | ||
throw e; | ||
}, | ||
@@ -611,0 +631,0 @@ Right: x => x |
{ | ||
"name": "arcsecond", | ||
"version": "1.0.1", | ||
"version": "1.1.0", | ||
"description": "", | ||
@@ -5,0 +5,0 @@ "main": "index", |
@@ -341,13 +341,13 @@ # Arcsecond | ||
The two main "types" in arcsecond are `Parser a b` and `ParserState a`, which are defined as: | ||
The two main "types" in arcsecond are `Parser e a b` and `ParserState e a`, which are defined as: | ||
`type ParserState a = Either String (Int, String, a)` | ||
`type ParserState e a = Either (Int, e) (Int, String, a)` | ||
`type Parser a b = () -> ParserState a -> ParserState b` | ||
`type Parser e a b = () -> ParserState e a -> ParserState e b` | ||
Which is to say that a `Parser a b` is a type that describes taking a `ParserState a` to a `ParserState b`. | ||
Which is to say that a `Parser e a b` is a type that describes taking a `ParserState e a` to a `ParserState e b`, where `e` is the the error. | ||
### parse | ||
`parse :: Parser a b -> String -> Either String b` | ||
`parse :: Parser e a b -> String -> Either (Int, e) b` | ||
@@ -364,3 +364,3 @@ `parse` takes a parser function and a string, and returns the result of parsing the string using the parser. | ||
`char :: Char -> Parser a String` | ||
`char :: Char -> Parser String a String` | ||
@@ -377,3 +377,3 @@ `char` takes a character and returns a parser that matches that character **exactly one** time. | ||
`str :: String -> Parser a String` | ||
`str :: String -> Parser String a String` | ||
@@ -390,3 +390,3 @@ `str` takes a string and returns a parser that matches that string **exactly one** time. | ||
`digit :: Parser a String` | ||
`digit :: Parser String a String` | ||
@@ -403,3 +403,3 @@ `digit` is a parser that matches **exactly one** numerical digit `/[0-9]/`. | ||
`digits :: Parser a String` | ||
`digits :: Parser String a String` | ||
@@ -416,3 +416,3 @@ `digits` is a parser that matches **one or more** numerical digit `/[0-9]/`. | ||
`letter :: Parser a String` | ||
`letter :: Parser String a String` | ||
@@ -429,3 +429,3 @@ `letter` is a parser that matches **exactly one** alphabetical letter `/[a-zA-Z]/`. | ||
`letters :: Parser a String` | ||
`letters :: Parser String a String` | ||
@@ -442,3 +442,3 @@ `letters` is a parser that matches **one or more** alphabetical letter `/[a-zA-Z]/`. | ||
`whitespace :: Parser a String` | ||
`whitespace :: Parser e a String` | ||
@@ -464,3 +464,3 @@ `whitespace` is a parser that matches **zero or more** whitespace characters. | ||
`anyOfString :: String -> Parser a String` | ||
`anyOfString :: String -> Parser String a String` | ||
@@ -477,3 +477,3 @@ `anyOfString` takes a string and returns a parser that matches **exactly one** character from that string. | ||
`regex :: RegExp -> Parser a String` | ||
`regex :: RegExp -> Parser String a String` | ||
@@ -490,3 +490,3 @@ `regex` takes a RegExp and returns a parser that matches **as many characters** as the RegExp matches. | ||
`sequenceOf :: [Parser * *] -> Parser a [*]` | ||
`sequenceOf :: [Parser e a b] -> Parser e a [b]` | ||
@@ -510,3 +510,3 @@ `sequenceOf` takes an array of parsers, and returns a new parser that matches each of them sequentially, collecting up the results into an array. | ||
`namedSequenceOf :: [[String, Parser * *]] -> Parser a Object` | ||
`namedSequenceOf :: [(String, Parser e a b)] -> Parser e a (StrMap b)` | ||
@@ -537,5 +537,5 @@ `namedSequenceOf` takes an array of string/parser pairs, and returns a new parser that matches each of them sequentially, collecting up the results into an object where the key is the string in the pair. | ||
`choice :: [Parser a *] -> Parser a *` | ||
`choice :: [Parser * a *] -> Parser * a *` | ||
`choice` takes an array of parsers, and returns a new parser that tries to match each one of them sequentially, and returns the first match. | ||
`choice` takes an array of parsers, and returns a new parser that tries to match each one of them sequentially, and returns the first match. If `choice` fails, then it returns the error message of the parser that matched the most from the string. | ||
@@ -558,3 +558,3 @@ **Example** | ||
`lookAhead :: Parser a b -> Parser a b` | ||
`lookAhead :: Parser e a b -> Parser e a b` | ||
@@ -577,3 +577,3 @@ `lookAhead` takes *look ahead* parser, and returns a new parser that matches using the *look ahead* parser, but without consuming input. | ||
`sepBy :: Parser a c -> Parser a b -> Parser a [b]` | ||
`sepBy :: Parser e a c -> Parser e a b -> Parser e a [b]` | ||
@@ -598,3 +598,3 @@ `sepBy` takes two parsers - a *separator* parser and a *value* parser - and returns a new parser that matches **zero or more** values from the *value* parser that are separated by values of the *separator* parser. Because it will match zero or more values, this parser will always match, resulting in an empty array in the zero case. | ||
`sepBy1 :: Parser a c -> Parser a b -> Parser a [b]` | ||
`sepBy1 :: Parser e a c -> Parser e a b -> Parser e a [b]` | ||
@@ -616,3 +616,3 @@ `sepBy1` is the same as `sepBy`, except that it matches **one or more** occurence. | ||
`many :: Parser a b -> Parser a [b]` | ||
`many :: Parser e a b -> Parser e a [b]` | ||
@@ -637,3 +637,3 @@ `many` takes a parser and returns a new parser which matches that parser **zero or more** times. Because it will match zero or more values, this parser will always match, resulting in an empty array in the zero case. | ||
`many1 :: Parser a b -> Parser a [b]` | ||
`many1 :: Parser e a b -> Parser e a [b]` | ||
@@ -660,3 +660,3 @@ `many1` is the same as `many`, except that it matches **one or more** occurence. | ||
`between :: between :: Parser a b -> Parser a c -> Parser a d -> Parser a d` | ||
`between :: Parser e a b -> Parser f a c -> Parser g a d -> Parser g a d` | ||
@@ -683,3 +683,3 @@ `between` takes 3 parsers, a *left* parser, a *right* parser, and a *value* parser, returning a new parser that matches a value matched by the *value* parser, between values matched by the *left* parser and the *right* parser. | ||
`everythingUntil :: Parser a b -> Parser a c` | ||
`everythingUntil :: Parser e a b -> Parser String a c` | ||
@@ -706,3 +706,3 @@ `everythingUntil` takes a *termination* parser and returns a new parser which matches everything up until a value is matched by the *termination* parser. When a value is matched by the *termination* parser, it is not "consumed". | ||
`anythingExcept :: Parser a b -> Parser a c` | ||
`anythingExcept :: Parser e a b -> Parser String a c` | ||
@@ -723,3 +723,3 @@ `anythingExcept` takes a *exception* parser and returns a new parser which matches **exactly one** character, if it is not matched by the *exception* parser. | ||
`possibly :: Parser a b -> Parser a (b|null)` | ||
`possibly :: Parser e a b -> Parser e a (b|null)` | ||
@@ -741,3 +741,3 @@ `possibly` takes an *attempt* parser and returns a new parser which tries to match using the *attempt* parser. If it is unsuccessful, it returns a null value and does not "consume" any input. | ||
`skip :: Parser a b -> Parser a a` | ||
`skip :: Parser e a b -> Parser e a a` | ||
@@ -760,3 +760,3 @@ `skip` takes a *skip* parser and returns a new parser which matches using the *skip* parser, but doesn't return its value, but instead the value of whatever came before it. | ||
`pipeParsers :: [Parser * *] -> Parser * *` | ||
`pipeParsers :: [Parser * * *] -> Parser * * *` | ||
@@ -779,3 +779,3 @@ `pipeParsers` takes an array of parsers and composes them left to right, so each parsers return value is passed into the next one in the chain. The result is a new parser that, when run, yields the result of the final parser in the chain. | ||
`composeParsers :: [Parser * *] -> Parser * *` | ||
`pipeParsers :: [Parser * * *] -> Parser * * *` | ||
@@ -798,3 +798,3 @@ `composeParsers` takes an array of parsers and composes them right to left, so each parsers return value is passed into the next one in the chain. The result is a new parser that, when run, yields the result of the final parser in the chain. | ||
`takeRight :: Parser a b -> Parser b c -> Parser a c` | ||
`takeRight :: Parser e a b -> Parser f b c -> Parser f a c` | ||
@@ -813,3 +813,3 @@ `takeRight` takes two parsers, *left* and *right*, and returns a new parser that first matches the *left*, then the *right*, and keeps the value matched by the *right*. | ||
`takeLeft :: Parser a b -> Parser b c -> Parser a b` | ||
`takeLeft :: Parser e a b -> Parser f b c -> Parser e a b` | ||
@@ -828,3 +828,3 @@ `takeLeft` takes two parsers, *left* and *right*, and returns a new parser that first matches the *left*, then the *right*, and keeps the value matched by the *left*. | ||
`recursiveParser :: (() => Parser a b) -> Parser a b` | ||
`recursiveParser :: (() => Parser e a b) -> Parser e a b` | ||
@@ -857,3 +857,3 @@ `recursiveParser` takes a function that returns a parser (a thunk), and returns that same parser. This is needed in order to create *recursive parsers* because javascript is not a "lazy" language. | ||
`tapParser :: (a => void) -> Parser a a` | ||
`tapParser :: (a => void) -> Parser e a a` | ||
@@ -878,3 +878,3 @@ `tapParser` takes a function and returns a parser that does nothing and consumes no input, but runs the provided function on the last parsed value. This is intended as a debugging tool to see the state of parsing at any point in a sequential operation like `sequenceOf` or `pipeParsers`. | ||
`decide :: (a -> Parser b c) -> Parser b c` | ||
`decide :: (a -> Parser e b c) -> Parser e b c` | ||
@@ -910,3 +910,3 @@ `decide` takes a function that recieves the last matched value and returns a new parser. It's important that the function **always** returns a parser. If a valid one cannot be selected, you can always use [fail](#fail). | ||
`mapTo :: (a -> b) -> Parser a b` | ||
`mapTo :: (a -> b) -> Parser e a b` | ||
@@ -934,5 +934,25 @@ `mapTo` takes a function and returns a parser does not consume input, but instead runs the provided function on the last matched value, and set that as the new last matched value. This function can be used to apply structure or transform the values as they are being parsed. | ||
### leftMapTo | ||
`leftMapTo :: ((e, Int) -> f) -> Parser f a b` | ||
`leftMapTo` is like [mapTo](#mapto) but it transforms the error value. The function passed to `leftMapTo` gets the *current error message* as its first argument and the *index* that parsing stopped at as the second. | ||
**Example** | ||
```javascript | ||
const newParser = pipeParsers([ | ||
letters, | ||
leftMapTo((message, index) => `Old message was: [${message}] @ index ${index}`) | ||
]); | ||
parse (newParser) ('1234') | ||
// -> Either.Left([ | ||
// 0, | ||
// 'Old message was: [ParseError \'many1\' (position 0): Expecting to match at least on value] @ index 0' | ||
// ]) | ||
``` | ||
### fail | ||
`fail :: String -> Parser a b` | ||
`fail :: String -> Parser String a b` | ||
@@ -949,3 +969,3 @@ `fail` takes an *error message* string and returns a parser that always fails with the provided *error message*. | ||
`succeedWith :: b -> Parser a b` | ||
`succeedWith :: b -> Parser e a b` | ||
@@ -952,0 +972,0 @@ `succeedWith` takes an value and returns a parser that always matches that value and does not consume any input. |
@@ -37,3 +37,4 @@ const { | ||
toValue, | ||
succeedWith | ||
succeedWith, | ||
leftMapTo | ||
} = require('../index') | ||
@@ -330,3 +331,19 @@ | ||
'-bcd' | ||
) | ||
), | ||
() => { | ||
const parser = choice ([ | ||
sequenceOf ([ | ||
letters, | ||
char (' '), | ||
letters | ||
]), | ||
sequenceOf ([ | ||
digits, | ||
char (' '), | ||
digits | ||
]) | ||
]) | ||
const failResult = parse (parser) ('12345 hello') .value; | ||
expect (failResult).toEqual([6, `ParseError 'many1' (position 6): Expecting to match at least one value`]); | ||
} | ||
] | ||
@@ -415,2 +432,24 @@ ); | ||
test('leftMapTo', () => { | ||
const parser = pipeParsers ([ | ||
choice ([ | ||
sequenceOf ([ | ||
letters, | ||
char (' '), | ||
letters | ||
]), | ||
sequenceOf ([ | ||
digits, | ||
char (' '), | ||
digits | ||
]) | ||
]), | ||
leftMapTo ((_, index) => `Failed to parse structure @ ${index}`) | ||
]); | ||
const failResult = parse (parser) ('12345 hello') .value; | ||
expect (failResult).toEqual([6, 'Failed to parse structure @ 6']); | ||
}); | ||
testMany( | ||
@@ -590,3 +629,3 @@ 'anythingExcept', [ | ||
expect(wasCalled).toBe(true); | ||
expect(value).toEqual(`ParseError (position 0): Expecting character 'a', got 'x'`); | ||
expect(value).toEqual([0, `ParseError (position 0): Expecting character 'a', got 'x'`]); | ||
}); | ||
@@ -612,4 +651,4 @@ | ||
test('toValue', () => { | ||
const lv = Left('oh no'); | ||
const rv = Right('oh yes'); | ||
const lv = parse(str ('oh no'))('nope'); | ||
const rv = parse(str ('oh yes'))('oh yes'); | ||
@@ -620,3 +659,4 @@ try { | ||
} catch (ex) { | ||
expect(ex.message).toBe('oh no'); | ||
expect(ex.message).toBe(`ParseError (position 0): Expecting string 'oh no', got 'nope...'`); | ||
expect(ex.parseIndex).toBe(0); | ||
} | ||
@@ -655,2 +695,22 @@ | ||
testMany('leftMap (laws)', [ | ||
expectEquivalence( | ||
fail('nope').leftMap(x => x), | ||
fail('nope') | ||
), | ||
expectEquivalence( | ||
fail('nope').map(x => f(g(x))), | ||
fail('nope').map(g).map(f) | ||
), | ||
]); | ||
test('map (equivalence to mapTo)', () => { | ||
const fn = x => ({ value: x }) | ||
const failMap = fail('nope').leftMap(fn); | ||
const failMapTo = pipeParsers ([ fail('nope'), leftMapTo (fn) ]); | ||
expectEquivalence(failMap, failMapTo)(); | ||
}); | ||
test('chain (equivalence to decide)', () => { | ||
@@ -657,0 +717,0 @@ const testStr1 = 'num 42'; |
Sorry, the diff of this file is not supported yet
266550
2567
1082
22