Salvaged from worktree-agent-* branches killed during sx-tree MCP outage: - lua: tokenizer + parser + phase-2 transpile (~157 tests) - prolog: tokenizer + parser + unification (72 tests, plan update lost to WIP) - forth: phase-1 reader/interpreter + phase-2 colon/VARIABLE (134 tests) - erlang: tokenizer + parser (114 tests) - haskell: tokenizer + parse tests (43 tests) Cherry-picked file contents only, not branch history, to avoid pulling in unrelated ocaml-vm merge commits that were in those branches' bases.
252 lines
7.6 KiB
Plaintext
252 lines
7.6 KiB
Plaintext
;; Haskell parser / tokenizer tests.
|
|
;;
|
|
;; Lightweight runner: each test checks actual vs expected with
|
|
;; structural (deep) equality and accumulates pass/fail counters.
|
|
;; Final value of this file is a summary dict with :pass :fail :fails.
|
|
|
|
(define
|
|
hk-deep=?
|
|
(fn
|
|
(a b)
|
|
(cond
|
|
((= a b) true)
|
|
((and (dict? a) (dict? b))
|
|
(let
|
|
((ak (keys a)) (bk (keys b)))
|
|
(if
|
|
(not (= (len ak) (len bk)))
|
|
false
|
|
(every?
|
|
(fn
|
|
(k)
|
|
(and (has-key? b k) (hk-deep=? (get a k) (get b k))))
|
|
ak))))
|
|
((and (list? a) (list? b))
|
|
(if
|
|
(not (= (len a) (len b)))
|
|
false
|
|
(let
|
|
((i 0) (ok true))
|
|
(define
|
|
hk-de-loop
|
|
(fn
|
|
()
|
|
(when
|
|
(and ok (< i (len a)))
|
|
(do
|
|
(when
|
|
(not (hk-deep=? (nth a i) (nth b i)))
|
|
(set! ok false))
|
|
(set! i (+ i 1))
|
|
(hk-de-loop)))))
|
|
(hk-de-loop)
|
|
ok)))
|
|
(:else false))))
|
|
|
|
(define hk-test-pass 0)
|
|
(define hk-test-fail 0)
|
|
(define hk-test-fails (list))
|
|
|
|
(define
|
|
hk-test
|
|
(fn
|
|
(name actual expected)
|
|
(if
|
|
(hk-deep=? actual expected)
|
|
(set! hk-test-pass (+ hk-test-pass 1))
|
|
(do
|
|
(set! hk-test-fail (+ hk-test-fail 1))
|
|
(append! hk-test-fails {:actual actual :expected expected :name name})))))
|
|
|
|
;; Convenience: tokenize and drop newline + eof tokens so tests focus
|
|
;; on meaningful content. Returns list of {:type :value} pairs.
|
|
(define
|
|
hk-toks
|
|
(fn
|
|
(src)
|
|
(map
|
|
(fn (tok) {:value (get tok "value") :type (get tok "type")})
|
|
(filter
|
|
(fn
|
|
(tok)
|
|
(let
|
|
((ty (get tok "type")))
|
|
(not (or (= ty "newline") (= ty "eof")))))
|
|
(hk-tokenize src)))))
|
|
|
|
;; ── 1. Identifiers & reserved words ──
|
|
(hk-test "varid simple" (hk-toks "foo") (list {:value "foo" :type "varid"}))
|
|
|
|
(hk-test
|
|
"varid with digits and prime"
|
|
(hk-toks "foo123' bar2")
|
|
(list {:value "foo123'" :type "varid"} {:value "bar2" :type "varid"}))
|
|
|
|
(hk-test "conid" (hk-toks "Maybe") (list {:value "Maybe" :type "conid"}))
|
|
|
|
(hk-test "reserved: where" (hk-toks "where") (list {:value "where" :type "reserved"}))
|
|
|
|
(hk-test
|
|
"reserved: case of"
|
|
(hk-toks "case of")
|
|
(list {:value "case" :type "reserved"} {:value "of" :type "reserved"}))
|
|
|
|
(hk-test "underscore is reserved" (hk-toks "_") (list {:value "_" :type "reserved"}))
|
|
|
|
;; ── 2. Qualified names ──
|
|
(hk-test "qvarid" (hk-toks "Data.Map.lookup") (list {:value "Data.Map.lookup" :type "qvarid"}))
|
|
|
|
(hk-test "qconid" (hk-toks "Data.Map") (list {:value "Data.Map" :type "qconid"}))
|
|
|
|
(hk-test "qualified operator" (hk-toks "Prelude.+") (list {:value "Prelude.+" :type "varsym"}))
|
|
|
|
;; ── 3. Numbers ──
|
|
(hk-test "integer" (hk-toks "42") (list {:value 42 :type "integer"}))
|
|
|
|
(hk-test "hex" (hk-toks "0x2A") (list {:value 42 :type "integer"}))
|
|
|
|
(hk-test "octal" (hk-toks "0o17") (list {:value 15 :type "integer"}))
|
|
|
|
(hk-test "float" (hk-toks "3.14") (list {:value 3.14 :type "float"}))
|
|
|
|
(hk-test "float with exp" (hk-toks "1.5e-3") (list {:value 0.0015 :type "float"}))
|
|
|
|
;; ── 4. Strings / chars ──
|
|
(hk-test "string" (hk-toks "\"hello\"") (list {:value "hello" :type "string"}))
|
|
|
|
(hk-test "char" (hk-toks "'a'") (list {:value "a" :type "char"}))
|
|
|
|
(hk-test "char escape newline" (hk-toks "'\\n'") (list {:value "\n" :type "char"}))
|
|
|
|
(hk-test "string escape" (hk-toks "\"a\\nb\"") (list {:value "a\nb" :type "string"}))
|
|
|
|
;; ── 5. Operators ──
|
|
(hk-test "operator +" (hk-toks "+") (list {:value "+" :type "varsym"}))
|
|
|
|
(hk-test "operator >>=" (hk-toks ">>=") (list {:value ">>=" :type "varsym"}))
|
|
|
|
(hk-test "consym" (hk-toks ":+:") (list {:value ":+:" :type "consym"}))
|
|
|
|
(hk-test "reservedop ->" (hk-toks "->") (list {:value "->" :type "reservedop"}))
|
|
|
|
(hk-test "reservedop =>" (hk-toks "=>") (list {:value "=>" :type "reservedop"}))
|
|
|
|
(hk-test "reservedop .. (range)" (hk-toks "..") (list {:value ".." :type "reservedop"}))
|
|
|
|
(hk-test "reservedop backslash" (hk-toks "\\") (list {:value "\\" :type "reservedop"}))
|
|
|
|
;; ── 6. Punctuation ──
|
|
(hk-test "parens" (hk-toks "( )") (list {:value "(" :type "lparen"} {:value ")" :type "rparen"}))
|
|
|
|
(hk-test "brackets" (hk-toks "[]") (list {:value "[" :type "lbracket"} {:value "]" :type "rbracket"}))
|
|
|
|
(hk-test "braces" (hk-toks "{}") (list {:value "{" :type "lbrace"} {:value "}" :type "rbrace"}))
|
|
|
|
(hk-test
|
|
"backtick"
|
|
(hk-toks "`mod`")
|
|
(list {:value "`" :type "backtick"} {:value "mod" :type "varid"} {:value "`" :type "backtick"}))
|
|
|
|
(hk-test "comma and semi" (hk-toks ",;") (list {:value "," :type "comma"} {:value ";" :type "semi"}))
|
|
|
|
;; ── 7. Comments ──
|
|
(hk-test "line comment stripped" (hk-toks "-- a comment") (list))
|
|
|
|
(hk-test "line comment before code" (hk-toks "-- c\nfoo") (list {:value "foo" :type "varid"}))
|
|
|
|
(hk-test
|
|
"block comment stripped"
|
|
(hk-toks "{- block -} foo")
|
|
(list {:value "foo" :type "varid"}))
|
|
|
|
(hk-test
|
|
"nested block comment"
|
|
(hk-toks "{- {- nested -} -} x")
|
|
(list {:value "x" :type "varid"}))
|
|
|
|
(hk-test
|
|
"-- inside operator is comment in Haskell"
|
|
(hk-toks "-->")
|
|
(list {:value "-->" :type "varsym"}))
|
|
|
|
;; ── 8. Mixed declarations ──
|
|
(hk-test
|
|
"type signature"
|
|
(hk-toks "main :: IO ()")
|
|
(list {:value "main" :type "varid"} {:value "::" :type "reservedop"} {:value "IO" :type "conid"} {:value "(" :type "lparen"} {:value ")" :type "rparen"}))
|
|
|
|
(hk-test
|
|
"data declaration"
|
|
(hk-toks "data Maybe a = Nothing | Just a")
|
|
(list
|
|
{:value "data" :type "reserved"}
|
|
{:value "Maybe" :type "conid"}
|
|
{:value "a" :type "varid"}
|
|
{:value "=" :type "reservedop"}
|
|
{:value "Nothing" :type "conid"}
|
|
{:value "|" :type "reservedop"}
|
|
{:value "Just" :type "conid"}
|
|
{:value "a" :type "varid"}))
|
|
|
|
(hk-test
|
|
"lambda"
|
|
(hk-toks "\\x -> x + 1")
|
|
(list {:value "\\" :type "reservedop"} {:value "x" :type "varid"} {:value "->" :type "reservedop"} {:value "x" :type "varid"} {:value "+" :type "varsym"} {:value 1 :type "integer"}))
|
|
|
|
(hk-test
|
|
"let expression"
|
|
(hk-toks "let x = 1 in x + x")
|
|
(list
|
|
{:value "let" :type "reserved"}
|
|
{:value "x" :type "varid"}
|
|
{:value "=" :type "reservedop"}
|
|
{:value 1 :type "integer"}
|
|
{:value "in" :type "reserved"}
|
|
{:value "x" :type "varid"}
|
|
{:value "+" :type "varsym"}
|
|
{:value "x" :type "varid"}))
|
|
|
|
(hk-test
|
|
"case expr"
|
|
(hk-toks "case x of Just y -> y")
|
|
(list
|
|
{:value "case" :type "reserved"}
|
|
{:value "x" :type "varid"}
|
|
{:value "of" :type "reserved"}
|
|
{:value "Just" :type "conid"}
|
|
{:value "y" :type "varid"}
|
|
{:value "->" :type "reservedop"}
|
|
{:value "y" :type "varid"}))
|
|
|
|
(hk-test
|
|
"list literal"
|
|
(hk-toks "[1, 2, 3]")
|
|
(list
|
|
{:value "[" :type "lbracket"}
|
|
{:value 1 :type "integer"}
|
|
{:value "," :type "comma"}
|
|
{:value 2 :type "integer"}
|
|
{:value "," :type "comma"}
|
|
{:value 3 :type "integer"}
|
|
{:value "]" :type "rbracket"}))
|
|
|
|
(hk-test
|
|
"range syntax"
|
|
(hk-toks "[1..10]")
|
|
(list {:value "[" :type "lbracket"} {:value 1 :type "integer"} {:value ".." :type "reservedop"} {:value 10 :type "integer"} {:value "]" :type "rbracket"}))
|
|
|
|
;; ── 9. Positions ──
|
|
(hk-test
|
|
"line/col positions"
|
|
(let
|
|
((toks (hk-tokenize "foo\n bar")))
|
|
(list
|
|
(get (nth toks 0) "line")
|
|
(get (nth toks 0) "col")
|
|
(get (nth toks 2) "line")
|
|
(get (nth toks 2) "col")))
|
|
(list 1 1 2 3))
|
|
|
|
;; ── Summary — final value of this file ──
|
|
{:fails hk-test-fails :pass hk-test-pass :fail hk-test-fail}
|