(.require [library [lux (.except and not local) [abstract [monad (.only Monad do)]] [control ["//" parser] ["[0]" maybe] ["[0]" try (.only Try)] ["[0]" exception (.only Exception)]] [data ["/" text (.use "[1]#[0]" monoid) [char (.only Char)]] ["[0]" product] [collection ["[0]" list (.use "[1]#[0]" mix)]]] [math [number ["n" nat (.use "[1]#[0]" decimal)]]] [meta ["[0]" code] [macro ["^" pattern] ["[0]" template]]]]]) (type .public Offset Nat) (def beginning Offset 0) (exception.def .public cannot_parse) (exception.def .public cannot_slice) (type .public Parser (//.Parser [Offset Text])) (type .public Slice (Record [#basis Offset #distance Offset])) (def .public (slice parser) (-> (Parser Slice) (Parser Text)) (do //.monad [[basis distance] parser] (function (_ (^.let input [offset tape])) (when (/.clip basis distance tape) {.#Some output} {try.#Success [input output]} {.#None} (exception.except ..cannot_slice []))))) (def (left_over offset tape) (-> Offset Text Text) (|> tape (/.clip_since offset) maybe.trusted)) (exception.def .public (unconsumed_input [offset tape]) (Exception [Offset Text]) (exception.report (list ["Offset" (n#encoded offset)] ["Input size" (n#encoded (/.size tape))] ["Remaining input" (..left_over offset tape)]))) (exception.def .public (expected_to_fail [offset tape]) (Exception [Offset Text]) (exception.report (list ["Offset" (n#encoded offset)] ["Input" (..left_over offset tape)]))) (def .public (result parser input) (All (_ a) (-> (Parser a) Text (Try a))) (when (parser [..beginning input]) {try.#Failure msg} {try.#Failure msg} {try.#Success [[end_offset _] output]} (if (n.= end_offset (/.size input)) {try.#Success output} (exception.except ..unconsumed_input [end_offset input])))) (def .public offset (Parser Offset) (function (_ (^.let input [offset tape])) {try.#Success [input offset]})) (def (with_slices parser) (-> (Parser (List Slice)) (Parser Slice)) (do //.monad [offset ..offset slices parser] (in (list#mix (function (_ [slice::basis slice::distance] [total::basis total::distance]) [total::basis (.i64_+# slice::distance total::distance)]) [#basis offset #distance 0] slices)))) (def .public any (Parser Text) (function (_ [offset tape]) (when (/.char offset tape) {.#Some output} {try.#Success [[(.i64_+# 1 offset) tape] (/.of_char output)]} _ (exception.except ..cannot_parse [])))) (def .public any! (Parser Slice) (function (_ [offset tape]) (when (/.char offset tape) {.#Some _} {try.#Success [[(.i64_+# 1 offset) tape] [#basis offset #distance 1]]} _ (exception.except ..cannot_slice [])))) (with_template [ ] [(`` (def .public ( parser) (All (_ a) (-> (Parser a) (Parser ))) (function (_ input) (when (parser input) {try.#Failure msg} ( input) _ (exception.except ..expected_to_fail input)))))] [not Text ..any] [not! Slice ..any!] ) (exception.def .public (cannot_match reference) (Exception Text) (exception.report (list ["Reference" (/.format reference)]))) (def .public (this reference) (-> Text (Parser Any)) (function (_ [offset tape]) (when (/.index_since offset reference tape) {.#Some where} (if (n.= offset where) {try.#Success [[(.i64_+# (/.size reference) offset) tape] []]} (exception.except ..cannot_match [reference])) _ (exception.except ..cannot_match [reference])))) (def .public end (Parser Any) (function (_ (^.let input [offset tape])) (if (n.= offset (/.size tape)) {try.#Success [input []]} (exception.except ..unconsumed_input input)))) (def .public next (Parser Text) (function (_ (^.let input [offset tape])) (when (/.char offset tape) {.#Some output} {try.#Success [input (/.of_char output)]} _ (exception.except ..cannot_parse [])))) (def .public remaining (Parser Text) (function (_ (^.let input [offset tape])) {try.#Success [input (..left_over offset tape)]})) (def .public (range bottom top) (-> Nat Nat (Parser Text)) (do //.monad [char any .let [char' (maybe.trusted (/.char 0 char))] _ (//.assertion (all /#composite "Character is not within range: " (/.of_char bottom) "-" (/.of_char top)) (.and (n.>= bottom char') (n.<= top char')))] (in char))) (def .public (range! bottom top) (-> Nat Nat (Parser Slice)) (do //.monad [it ..any! char (..slice (in it)) .let [char' (maybe.trusted (/.char 0 char))] _ (//.assertion (all /#composite "Character is not within range: " (/.of_char bottom) "-" (/.of_char top)) (.and (n.>= bottom char') (n.<= top char')))] (in it))) (with_template [ ] [(def .public (Parser Text) (..range (char ) (char ))) (def .public (Parser Slice) (..range! (char ) (char )))] ["A" "Z" upper upper!] ["a" "z" lower lower!] ["0" "9" decimal decimal!] ["0" "7" octal octal!] ) (def .public alpha (Parser Text) (//.either ..lower ..upper)) (def .public alpha! (Parser Slice) (//.either ..lower! ..upper!)) (def .public alpha_num (Parser Text) (//.either ..alpha ..decimal)) (def .public alpha_num! (Parser Slice) (//.either ..alpha! ..decimal!)) (def .public hexadecimal (Parser Text) (all //.either ..decimal (..range (char "a") (char "f")) (..range (char "A") (char "F")))) (def .public hexadecimal! (Parser Slice) (all //.either ..decimal! (..range! (char "a") (char "f")) (..range! (char "A") (char "F")))) (with_template [] [(exception.def .public ( [options character]) (Exception [Text Char]) (exception.report (list ["Options" (/.format options)] ["Character" (/.format (/.of_char character))])))] [character_should_be] [character_should_not_be] ) (with_template [ ] [(def .public ( options) (-> Text (Parser Text)) (function (_ [offset tape]) (when (/.char offset tape) {.#Some output} (let [output' (/.of_char output)] (if ( (/.contains? output' options)) {try.#Success [[(.i64_+# 1 offset) tape] output']} (exception.except [options output]))) _ (exception.except ..cannot_parse []))))] [one_of |> ..character_should_be] [none_of .not ..character_should_not_be] ) (with_template [ ] [(def .public ( options) (-> Text (Parser Slice)) (function (_ [offset tape]) (when (/.char offset tape) {.#Some output} (let [output' (/.of_char output)] (if ( (/.contains? output' options)) {try.#Success [[(.i64_+# 1 offset) tape] [#basis offset #distance 1]]} (exception.except [options output]))) _ (exception.except ..cannot_slice []))))] [one_of! |> ..character_should_be] [none_of! .not ..character_should_not_be] ) (exception.def .public (character_does_not_satisfy_predicate character) (Exception Char) (exception.report (list ["Character" (/.format (/.of_char character))]))) (def .public (satisfies parser) (-> (-> Char Bit) (Parser Text)) (function (_ [offset tape]) (when (/.char offset tape) {.#Some output} (if (parser output) {try.#Success [[(.i64_+# 1 offset) tape] (/.of_char output)]} (exception.except ..character_does_not_satisfy_predicate [output])) _ (exception.except ..cannot_parse [])))) (def .public (satisfies! parser) (-> (-> Char Bit) (Parser Slice)) (function (_ [offset tape]) (when (/.char offset tape) {.#Some output} (if (parser output) {try.#Success [[(.i64_+# 1 offset) tape] [#basis offset #distance 1]]} (exception.except ..character_does_not_satisfy_predicate [output])) _ (exception.except ..cannot_parse [])))) (def .public space (Parser Text) (..satisfies /.space?)) (def .public space! (Parser Slice) (..satisfies! /.space?)) (def .public (and left right) (-> (Parser Text) (Parser Text) (Parser Text)) (do //.monad [=left left =right right] (in (all /#composite =left =right)))) (def .public (and! left right) (-> (Parser Slice) (Parser Slice) (Parser Slice)) (do //.monad [(open "left[0]") left (open "right[0]") right] (in [left#basis (.i64_+# left#distance right#distance)]))) (with_template [ ] [(def .public ( parser) (-> (Parser Text) (Parser Text)) (|> parser (at //.monad each /.together)))] [some //.some "some"] [many //.many "many"] ) (with_template [ ] [(def .public ( parser) (-> (Parser Slice) (Parser Slice)) (with_slices ( parser)))] [some! //.some "some"] [many! //.many "many"] ) (with_template [ ] [(def .public ( amount parser) (-> Nat (Parser Text) (Parser Text)) (|> parser ( amount) (at //.monad each /.together)))] [exactly //.exactly "exactly"] [at_most //.at_most "at most"] [at_least //.at_least "at least"] ) (with_template [ ] [(def .public ( amount parser) (-> Nat (Parser Slice) (Parser Slice)) (with_slices ( amount parser)))] [exactly! //.exactly "exactly"] [at_most! //.at_most "at most"] [at_least! //.at_least "at least"] ) (def .public (between minimum additional parser) (-> Nat Nat (Parser Text) (Parser Text)) (|> parser (//.between minimum additional) (at //.monad each /.together))) (def .public (between! minimum additional parser) (-> Nat Nat (Parser Slice) (Parser Slice)) (with_slices (//.between minimum additional parser))) (def .public (enclosed [start end] parser) (All (_ a) (-> [Text Text] (Parser a) (Parser a))) (|> parser (//.before (this end)) (//.after (this start)))) (def .public (local local_input parser) (All (_ a) (-> Text (Parser a) (Parser a))) (function (_ real_input) (when (..result parser local_input) {try.#Failure error} {try.#Failure error} {try.#Success value} {try.#Success [real_input value]}))) (def .public (then structured text) (All (_ s a) (-> (Parser a) (//.Parser s Text) (//.Parser s a))) (do //.monad [raw text] (//.of_try (..result structured raw))))