(.module: [lux #* [control ["M" monad (#+ do)] [equivalence (#+ Equivalence)] ["&" parser]] [data ["." error (#+ Error)]] [math ["r" random]] ["." macro ["." code] ["s" syntax (#+ syntax:)]]] lux/test) ## [Utils] (def: (should-fail input) (All [a] (-> (Error a) Bit)) (case input (#error.Failure _) #1 _ #0)) (def: (enforced? parser input) (All [s] (-> (&.Parser s Any) s Bit)) (case (&.run input parser) (#error.Success [_ []]) #1 _ #0)) (def: (found? parser input) (All [s] (-> (&.Parser s Bit) s Bit)) (case (&.run input parser) (#error.Success [_ #1]) #1 _ #0)) (def: (fails? input) (All [a] (-> (Error a) Bit)) (case input (#error.Failure _) #1 _ #0)) (syntax: (match pattern input) (wrap (list (` (case (~ input) (^ (#error.Success [(~' _) (~ pattern)])) #1 (~' _) #0))))) ## [Tests] (context: "Assertions" (test "Can make assertions while parsing." (and (match [] (&.run (list (code.bit #1) (code.int +123)) (&.assert "yolo" #1))) (fails? (&.run (list (code.bit #1) (code.int +123)) (&.assert "yolo" #0)))))) (context: "Combinators [Part 1]" ($_ seq (test "Can optionally succeed with some parser." (and (match (#.Some 123) (&.run (list (code.nat 123)) (&.maybe s.nat))) (match #.None (&.run (list (code.int -123)) (&.maybe s.nat))))) (test "Can apply a parser 0 or more times." (and (match (list 123 456 789) (&.run (list (code.nat 123) (code.nat 456) (code.nat 789)) (&.some s.nat))) (match (list) (&.run (list (code.int -123)) (&.some s.nat))))) (test "Can apply a parser 1 or more times." (and (match (list 123 456 789) (&.run (list (code.nat 123) (code.nat 456) (code.nat 789)) (&.many s.nat))) (match (list 123) (&.run (list (code.nat 123)) (&.many s.nat))) (fails? (&.run (list (code.int -123)) (&.many s.nat))))) (test "Can use either parser." (let [positive (: (s.Syntax Int) (do &.monad [value s.int _ (&.assert "" (i/> +0 value))] (wrap value)))] (and (match +123 (&.run (list (code.int +123) (code.int +456) (code.int +789)) (&.either positive s.int))) (match -123 (&.run (list (code.int -123) (code.int +456) (code.int +789)) (&.either positive s.int))) (fails? (&.run (list (code.bit #1) (code.int +456) (code.int +789)) (&.either positive s.int)))))) (test "Can create the opposite/negation of any parser." (and (fails? (&.run (list (code.int +123) (code.int +456) (code.int +789)) (&.not s.int))) (match [] (&.run (list (code.bit #1) (code.int +456) (code.int +789)) (&.not s.int))))) )) (context: "Combinators Part [2]" ($_ seq (test "Can fail at will." (should-fail (&.run (list) (&.fail "Well, it really SHOULD fail...")))) (test "Can apply a parser N times." (and (match (list +123 +456 +789) (&.run (list (code.int +123) (code.int +456) (code.int +789)) (&.exactly 3 s.int))) (match (list +123 +456) (&.run (list (code.int +123) (code.int +456) (code.int +789)) (&.exactly 2 s.int))) (fails? (&.run (list (code.int +123) (code.int +456) (code.int +789)) (&.exactly 4 s.int))))) (test "Can apply a parser at-least N times." (and (match (list +123 +456 +789) (&.run (list (code.int +123) (code.int +456) (code.int +789)) (&.at-least 3 s.int))) (match (list +123 +456 +789) (&.run (list (code.int +123) (code.int +456) (code.int +789)) (&.at-least 2 s.int))) (fails? (&.run (list (code.int +123) (code.int +456) (code.int +789)) (&.at-least 4 s.int))))) (test "Can apply a parser at-most N times." (and (match (list +123 +456 +789) (&.run (list (code.int +123) (code.int +456) (code.int +789)) (&.at-most 3 s.int))) (match (list +123 +456) (&.run (list (code.int +123) (code.int +456) (code.int +789)) (&.at-most 2 s.int))) (match (list +123 +456 +789) (&.run (list (code.int +123) (code.int +456) (code.int +789)) (&.at-most 4 s.int))))) (test "Can apply a parser between N and M times." (and (match (list +123 +456 +789) (&.run (list (code.int +123) (code.int +456) (code.int +789)) (&.between 3 10 s.int))) (fails? (&.run (list (code.int +123) (code.int +456) (code.int +789)) (&.between 4 10 s.int))))) (test "Can parse while taking separators into account." (and (match (list +123 +456 +789) (&.run (list (code.int +123) (code.text "YOLO") (code.int +456) (code.text "YOLO") (code.int +789)) (&.sep-by (s.this (' "YOLO")) s.int))) (match (list +123 +456) (&.run (list (code.int +123) (code.text "YOLO") (code.int +456) (code.int +789)) (&.sep-by (s.this (' "YOLO")) s.int))))) (test "Can obtain the whole of the remaining input." (|> &.remaining (&.run (list (code.int +123) (code.int +456) (code.int +789))) (match (list [_ (#.Int +123)] [_ (#.Int +456)] [_ (#.Int +789)])))) ))