;; Erlang tokenizer tests

(define er-test-count 0)
(define er-test-pass 0)
(define er-test-fails (list))

(define tok-type (fn (t) (get t :type)))
(define tok-value (fn (t) (get t :value)))

(define tok-types (fn (src) (map tok-type (er-tokenize src))))

(define tok-values (fn (src) (map tok-value (er-tokenize src))))

(define
  er-test
  (fn
    (name actual expected)
    (set! er-test-count (+ er-test-count 1))
    (if
      (= actual expected)
      (set! er-test-pass (+ er-test-pass 1))
      (append! er-test-fails {:actual actual :expected expected :name name}))))

;; ── atoms ─────────────────────────────────────────────────────────
(er-test "atom: bare" (tok-values "foo") (list "foo" nil))

(er-test
  "atom: snake_case"
  (tok-values "hello_world")
  (list "hello_world" nil))

(er-test
  "atom: quoted"
  (tok-values "'Hello World'")
  (list "Hello World" nil))

(er-test
  "atom: quoted with special chars"
  (tok-values "'foo-bar'")
  (list "foo-bar" nil))

(er-test "atom: with @" (tok-values "node@host") (list "node@host" nil))

(er-test
  "atom: type is atom"
  (tok-types "foo bar baz")
  (list "atom" "atom" "atom" "eof"))

;; ── variables ─────────────────────────────────────────────────────
(er-test "var: uppercase" (tok-values "X") (list "X" nil))

(er-test "var: camelcase" (tok-values "FooBar") (list "FooBar" nil))

(er-test "var: underscore" (tok-values "_") (list "_" nil))

(er-test "var: _prefixed" (tok-values "_ignored") (list "_ignored" nil))

(er-test "var: type" (tok-types "X Y _") (list "var" "var" "var" "eof"))

;; ── integers ──────────────────────────────────────────────────────
(er-test "integer: zero" (tok-values "0") (list "0" nil))

(er-test "integer: positive" (tok-values "42") (list "42" nil))

(er-test "integer: big" (tok-values "12345678") (list "12345678" nil))

(er-test "integer: hex" (tok-values "16#FF") (list "16#FF" nil))

(er-test
  "integer: type"
  (tok-types "1 2 3")
  (list "integer" "integer" "integer" "eof"))

(er-test "integer: char literal" (tok-types "$a") (list "integer" "eof"))

(er-test
  "integer: char literal escape"
  (tok-types "$\\n")
  (list "integer" "eof"))

;; ── floats ────────────────────────────────────────────────────────
(er-test "float: simple" (tok-values "3.14") (list "3.14" nil))

(er-test "float: exponent" (tok-values "1.0e10") (list "1.0e10" nil))

(er-test "float: neg exponent" (tok-values "1.5e-3") (list "1.5e-3" nil))

(er-test "float: type" (tok-types "3.14") (list "float" "eof"))

;; ── strings ───────────────────────────────────────────────────────
(er-test "string: simple" (tok-values "\"hello\"") (list "hello" nil))

(er-test "string: empty" (tok-values "\"\"") (list "" nil))

(er-test "string: escape newline" (tok-values "\"a\\nb\"") (list "a\nb" nil))

(er-test "string: type" (tok-types "\"hello\"") (list "string" "eof"))

;; ── keywords ──────────────────────────────────────────────────────
(er-test "keyword: case" (tok-types "case") (list "keyword" "eof"))

(er-test
  "keyword: of end when"
  (tok-types "of end when")
  (list "keyword" "keyword" "keyword" "eof"))

(er-test
  "keyword: receive after"
  (tok-types "receive after")
  (list "keyword" "keyword" "eof"))

(er-test
  "keyword: fun try catch"
  (tok-types "fun try catch")
  (list "keyword" "keyword" "keyword" "eof"))

(er-test
  "keyword: andalso orelse not"
  (tok-types "andalso orelse not")
  (list "keyword" "keyword" "keyword" "eof"))

(er-test
  "keyword: div rem"
  (tok-types "div rem")
  (list "keyword" "keyword" "eof"))

;; ── punct ─────────────────────────────────────────────────────────
(er-test "punct: parens" (tok-values "()") (list "(" ")" nil))

(er-test "punct: braces" (tok-values "{}") (list "{" "}" nil))

(er-test "punct: brackets" (tok-values "[]") (list "[" "]" nil))

(er-test
  "punct: commas"
  (tok-types "a,b")
  (list "atom" "punct" "atom" "eof"))

(er-test
  "punct: semicolon"
  (tok-types "a;b")
  (list "atom" "punct" "atom" "eof"))

(er-test "punct: period" (tok-types "a.") (list "atom" "punct" "eof"))

(er-test "punct: arrow" (tok-values "->") (list "->" nil))

(er-test "punct: backarrow" (tok-values "<-") (list "<-" nil))

(er-test "punct: binary brackets" (tok-values "<<>>") (list "<<" ">>" nil))

(er-test
  "punct: cons bar"
  (tok-values "[a|b]")
  (list "[" "a" "|" "b" "]" nil))

(er-test "punct: double-bar (list comp)" (tok-values "||") (list "||" nil))

(er-test "punct: double-colon" (tok-values "::") (list "::" nil))

(er-test
  "punct: module-colon"
  (tok-values "lists:map")
  (list "lists" ":" "map" nil))

;; ── operators ─────────────────────────────────────────────────────
(er-test
  "op: plus minus times div"
  (tok-values "+ - * /")
  (list "+" "-" "*" "/" nil))

(er-test
  "op: eq/neq"
  (tok-values "== /= =:= =/=")
  (list "==" "/=" "=:=" "=/=" nil))

(er-test "op: compare" (tok-values "< > =< >=") (list "<" ">" "=<" ">=" nil))

(er-test "op: list ops" (tok-values "++ --") (list "++" "--" nil))

(er-test "op: send" (tok-values "!") (list "!" nil))

(er-test "op: match" (tok-values "=") (list "=" nil))

;; ── comments ──────────────────────────────────────────────────────
(er-test
  "comment: ignored"
  (tok-values "x % this is a comment\ny")
  (list "x" "y" nil))

(er-test
  "comment: end-of-file"
  (tok-values "x % comment to eof")
  (list "x" nil))

;; ── combined ──────────────────────────────────────────────────────
(er-test
  "combined: function head"
  (tok-values "foo(X, Y) -> X + Y.")
  (list "foo" "(" "X" "," "Y" ")" "->" "X" "+" "Y" "." nil))

(er-test
  "combined: case expression"
  (tok-values "case X of 1 -> ok; _ -> err end")
  (list "case" "X" "of" "1" "->" "ok" ";" "_" "->" "err" "end" nil))

(er-test
  "combined: tuple"
  (tok-values "{ok, 42}")
  (list "{" "ok" "," "42" "}" nil))

(er-test
  "combined: list cons"
  (tok-values "[H|T]")
  (list "[" "H" "|" "T" "]" nil))

(er-test
  "combined: receive"
  (tok-values "receive X -> X end")
  (list "receive" "X" "->" "X" "end" nil))

(er-test
  "combined: guard"
  (tok-values "when is_integer(X)")
  (list "when" "is_integer" "(" "X" ")" nil))

(er-test
  "combined: module attr"
  (tok-values "-module(foo).")
  (list "-" "module" "(" "foo" ")" "." nil))

(er-test
  "combined: send"
  (tok-values "Pid ! {self(), hello}")
  (list "Pid" "!" "{" "self" "(" ")" "," "hello" "}" nil))

(er-test
  "combined: whitespace skip"
  (tok-values "  a  \n  b  \t  c  ")
  (list "a" "b" "c" nil))

;; ── report ────────────────────────────────────────────────────────
(define
  er-tokenize-test-summary
  (str "tokenizer " er-test-pass "/" er-test-count))
