Monad/Parser.bruijn


# MIT License, Copyright (c) 2024 Marvin Borner
# see samples/fun/minibruijn for example usage
# TODO: also support line/char offset

:import std/List .
:import std/Combinator .
:import std/Char C
:import std/Result R

# TODO: don't just use strings for errors

error-unexpected ["unexpected symbol " ++ 0] ⧗ Error

error-end-of-input "end of input" ⧗ Error

error-expected-end "expected end of input" ⧗ Error

error-custom [0] ⧗ Error

error-compose [[C.?eq? 1 0 0 (1 ++ " or " ++ 0)]] ⧗ Error → Error → Error

satisfy [[0 [[[go]]] end]] ⧗ (a → Boolean) → (Parser a)
	go 4 2 (R.ok (2 : 1)) (R.err (error-unexpected {}2))
	end R.err error-end-of-input

char [satisfy (C.eq? 0)] ⧗ Char → (Parser a)

:test (char 'a' "abc") (R.ok ('a' : "bc"))
:test (char 'b' "abc") (R.err (error-unexpected "a"))
:test (char 'a' [[0]]) (R.err error-end-of-input)

map [[[R.map ok (1 0)]]] ⧗ (a → b) → (Parser a) → (Parser b)
	ok &[[(4 1) : 0]]

…<$>… map

fail [[R.err 1]] ⧗ a → (Parser a)

pure [[R.ok (1 : 0)]] ⧗ a → (Parser a)

ap [[[R.bind (2 0) ok]]] ⧗ (Parser (a → b)) → (Parser a) → (Parser b)
	ok &[[R.map ok (3 0)]]
		ok &[[(3 1) : 0]]

…<*>… ap

string y [[0 [[[go]]] (pure [[0]])]] ⧗ String → (Parser a)
	go cons <$> (char 2) <*> (4 1)

:test (string "ac" "abc") (R.err (error-unexpected "b"))
:test (string "ab" "abcd") (R.ok ("ab" : "cd"))

return pure ⧗ a → (Parser a)

bind [[[R.bind (2 0) ok]]] ⧗ (Parser a) → (a → (Parser b)) → (Parser a)
	ok &[[3 1 0]]

…>>=… bind

alt [[[2 0 R.ok err]]] ⧗ (Parser a) → (Parser a) → (Parser a)
	err [2 1 R.ok err]
		err R.err ∘ (error-compose 0)

…<|>… alt

:test ((string "ab") <|> (string "cd") "abc") (R.ok ("ab" : "c"))
:test ((string "ab") <|> (string "cd") "cde") (R.ok ("cd" : "e"))
:test ((string "ab") <|> (string "cd") "acd") (R.err (error-compose (error-unexpected "c") (error-unexpected "a")))
:test ((string "ab") <|> (string "cd") "cbe") (R.err (error-compose (error-unexpected "c") (error-unexpected "b")))

eof [0 [[[go]]] end] ⧗ (Parser a)
	go R.err error-expected-end
	end R.ok ([[0]] : [[0]])

lift-result [0 pure fail] ⧗ (Result a) → (Parser a)

:test (lift-result (R.ok "ok") "rst") (R.ok ("ok" : "rst"))
:test (lift-result (R.err "oh") "rst") (R.err "oh")

# =========================================================================== #
# most relevant functions are defined - we can now derive from Generic/Monad! #
# =========================================================================== #

:input std/Generic/Monad

:test (k <$ (string "ab") "abc") (R.ok (k : "c"))
:test ((char '{') *> (string "wow") <* (char '}') "{wow}{owo}") (R.ok ("wow" : "{owo}"))