;; 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.
;; The hk-test / hk-deep=? helpers live in lib/haskell/testlib.sx
;; and are preloaded by lib/haskell/test.sh.

;; 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}
