arcthird v1.1.6
Arcthird
A modern and more abstract version of the Arcsecond library made with Coffeescript.
Arcsecond is a simple parsing library inspired by Haskell's Parsec, made from scratch by francisrstrokes who runs the LLJS YouTube channel.
Arcthird makes it more abstract by introducing a new class called PStream, from which your parsers are gonna parse from. This allows for example to make a parser of tokens, represented as JS objects.
The documentation is still very empty. It will get more full once I have more free time. If you'd like to contribute, don't hesitate to create pull requests!
Minor changes in functions
I've decided to change some of the functions from Arcsecond. Overall:
- The
ParserStateis stored in its own class. Therefore:createParserStateis replaced by theParserStateconstructor;updateErroris replaced by theerrorifymethod;updateResultis replaced by theresultifymethod;updateDatais replaced by thedataifymethod;updateParserStateis replaced by theupdatemethod.
- The
Parser'srunmethod is calledparse, and has to take aPStream. Thus the curriedparsefunction also takes aPSteamas an argument. - For string / buffer / typed array specific parsers from Arcsecond, I created a
StringPStreamclass instance ofPSteam. So you would write for examplechar('h').parse(new StringPStream("hello world")) parse(str("hello"))(new StringPStream("hello world")) - Because writing
new StringPStreameverytime would be kinda annoying, I created thestrparsefunction to directly take a string instead:strparse(str("hello"))("hello world") - There is no
many1function. Instead, there isatLeastandatLeast1, which is more general.
Classes
PStream
PStream is an abstract class which is gonna be the input stream of every parser you're gonna use and create.
-- This is just for type annotation
class PStream
constructor :: t -> PStream t
length :: PStream t ~> () -> Int
elementAt :: PStream t ~> Int -> a
next :: PStream t ~> () -> a
nexts :: PStream t ~> Int -> [a]next: gives the next element.nexts: gives an array of the next n elements.
The next and nexts methods are automatically defined in terms of elementAt. Although they aren't used anywhere as I didn't realize that I had used the index property of the ParserState class all this time. Replacing that with the index property of the PStream is an improvement to make in the future.
Because it is an abstract class, you cannot use it as is, you have to create your own extension and implement the length and elementAt functions yourself. For example:
class NumberStream extends PStream {
constructor(target) {
if (!(target instanceof Array))
throw new TypeError("Target must be an Array");
super(target);
}
length() {
return this.structure.length;
}
elementAt(i) {
try {
return this.structure[i];
} catch (err) {
return null;
}
}
}
const nstream = new NumberStream([1, 2, 3, 42, 69]);
console.log(nstream.next()); // 1
console.log(nstream.nexts(2)); // [2, 3]
console.log(nstream.elementAt(0)); // 1
console.log(nstream.nexts(3)); // [42, 69, null]
console.log(nstream.next()); // null
console.log(nstream.length()); // 5Parser
The type of a parser as used in the documentation is different from Arcsecond. Because of the new abstraction, there is now a new generic type to take into account: t as a PStream.
-- This is just for type annotation
class Parser
constructor :: PStream t => (ParserState t * d -> ParserState t a d) -> Parser t a d
parse :: PStream t => Parser t a d ~> t -> ParserState t a d
fork :: PStream t => Parser t a d ~> (t, (ParserState t a d -> x), (ParserState t a d -> y)) -> (Either x y)
map :: PStream t => Parser t a d ~> (a -> b) -> Parser t b d
chain :: PStream t => Parser t a d ~> (a -> Parser t b d) -> Parser t b d
ap :: PStream t => Parser t a d ~> Parser t (a -> b) d -> Parser t b d
errorMap :: PStream t => Parser t a d ~> ({d, String, Int} -> String) -> Parser t a d
errorChain :: PStream t => Parser t a d ~> ({d, String, Int} -> Parser t a d) -> Parser t a d
mapFromData :: PStream t => Parser t a d ~> ({a, d} -> b) -> Parser t b d
chainFromData :: PStream t => Parser t a d ~> ({a, d} -> Parser t b e) -> Parser t b e
mapData :: PStream t => Parser t a d ~> (d -> e) -> Parser t a e
of :: PStream t => x -> Parser t x dAll the errors returned in parser states are strings. Now this doesn't change anything from Arcsecond, although Arcsecond explicitly allowed for different error types. After verification, I realized that my code does also allow for different error types; however, this isn't reflected in the documentation where errors are described as strings even for parser combinators.
constructor: constructs a new parser by taking aParserStatetransformer as an argument, which is a function that takes aParserStateand returns a newParserState.const consume = new Parser(state => new ParserState({ ...state.props(), index: state.target.length() }));parse: directly parses aPStreaminto aParserState.char('a').parse(new StringPStream("abc"));
Parser Generators
char
char :: StringPStream t => Char -> Parser t Char dParses a single character.
strparse(char('h'))("hello world")
// -> ParserState result='h' index=1anyChar
anyChar :: StringPStream t => Parser t Char dParses any character.
strparse(anyChar)("bruh")
// -> ParserState result='b' index=1peek
peek :: PStream t => Parser t a dReturns the element at the current index as a result without moving forward.
strparse(peek)("something")
// -> ParserState result='s' index=0str
str :: StringPStream t => String -> Parser t String dregex
regex :: StringPStream t => RegExp -> Parser t String ddigit
digit :: StringPStream t => Parser t String ddigits
digits :: StringPStream t => Parser t String dletter
letter :: StringPStream t => Parser t String dletters
letters :: StringPStream t => Parser t String danyOfString
anyOfString :: StringPStream t => String -> Parser t Char dendOfInput
endOfInput :: PStream t => Parser t Null dwhitespace
whitespace :: StringPStream t => Parser t String doptionalWhitespace
optionalWhitespace :: StringPStream t => Parser t String dParser Combinators
getData
getData :: PStream t => Parser t a dsetData
setData :: PStream t => d -> Parser t a dmapData
mapData :: PStream t => (d -> e) -> Parser t a ewithData
withData :: PStream t => Parser t a d -> e -> Parser t a epipe
pipe :: PStream t => [Parser t * *] -> Parser t * *compose
compose :: PStream t => [Parser t * *] -> Parser t * *tap
tap :: PStream t => (ParserState t a d -> IO ()) -> Parser t a dparse
parse :: PStream t => Parser t a d -> t -> Either String astrparse
strparse :: StringPStream t => Parser t a d -> String -> Either String adecide
decide :: PStream t => (a -> Parser t b d) -> Parser t b dfail
fail :: PStream t => String -> Parser t a dsucceedWith
succeedWith :: PStream t => a -> Parser t a deither
either :: PStream t => Parser t a d -> Parser t (Either String a) dcoroutine
coroutine :: PStream t => (() -> Iterator (Parser t a d)) -> Parser t a dexactly
exactly :: PStream t => Int -> Parser t a d -> Parser t [a] dmany
many :: PStream t => Parser t a d -> Parser t [a] datLeast
atLeast :: PStream t => Int -> Parser t a d -> Parser t [a] datLeast1
atLeast1 :: PStream t => Parser t a d -> Parser t [a] dmapTo
mapTo :: PStream t => (a -> b) -> Parser t b derrorMapTo
errorMapTo :: PStream t => ({d, String, Int} -> String) -> Parser t a dnamedSequenceOf
namedSequenceOf :: PStream t => [(String, Parser t * *)] -> Parser t (StrMap *) dsequenceOf
sequenceOf :: PStream t => [Parser t * *] -> Parser t [*] *sepBy
sepBy :: PStream t => Parser t a d -> Parser t b d -> Parser t [b] dsepBy1
sepBy1 :: PStream t => Parser t a d -> Parser t b d -> Parser t [b] dchoice
choice :: PStream t => [Parser t * *] -> Parser t * *between
between :: PStream t => Parser t a d -> Parser t b d -> Parser t c d -> Parser t b deverythingUntil
everythingUntil :: StringPStream t => Parser t a d -> Parser t String deveryCharUntil
everyCharUntil :: StringPStream t => Parser t a d -> Parser t String danythingExcept
anythingExcept :: StringPStream t => Parser t a d -> Parser t Char danyCharExcept
anyCharExcept :: StringPStream t => Parser t a d -> Parser t Char dlookAhead
lookAhead :: PStream t => Parser t a d -> Parser t a dpossibly
possibly :: PStream t => Parser t a d -> Parser t (a | Null) dskip
skip :: PStream t => Parser t a d -> Parser t a drecursiveParser
recursiveParser :: PStream t => (() -> Parser t a d) -> Parser t a dtakeRight
takeRight :: PStream t => Parser t a d -> Parser t b d -> Parser t b dtakeLeft
takeLeft :: PStream t => Parser t a d -> Parser t b d -> Parser t a dtoPromise
toPromise :: PStream t => ParserState t a d -> Promise (String, Integer, d) atoValue
toValue :: PStream t => ParserState t a d -> a