ba0b300917
- New TUI screens: Main Menu, Compile, Run, REPL, AI Generator, AI Settings, Help - AI configuration persisted in ~/.config/cljnim/config.json - Added illwill dependency for terminal UI - Updated experiments, examples, docs, and core modules
234 lines
5.6 KiB
Nim
234 lines
5.6 KiB
Nim
import unittest, strutils
|
|
import ../src/types
|
|
import ../src/reader
|
|
|
|
suite "Reader - Basic Types":
|
|
test "read nil":
|
|
let v = read("nil")
|
|
check v.kind == ckNil
|
|
|
|
test "read true":
|
|
let v = read("true")
|
|
check v.kind == ckBool
|
|
check v.boolVal == true
|
|
|
|
test "read false":
|
|
let v = read("false")
|
|
check v.kind == ckBool
|
|
check v.boolVal == false
|
|
|
|
test "read integer":
|
|
let v = read("42")
|
|
check v.kind == ckInt
|
|
check v.intVal == 42
|
|
|
|
test "read negative integer":
|
|
let v = read("-7")
|
|
check v.kind == ckInt
|
|
check v.intVal == -7
|
|
|
|
test "read float":
|
|
let v = read("3.14")
|
|
check v.kind == ckFloat
|
|
check v.floatVal == 3.14
|
|
|
|
test "read string":
|
|
let v = read("\"hello\"")
|
|
check v.kind == ckString
|
|
check v.strVal == "hello"
|
|
|
|
test "read string with escapes":
|
|
let v = read("\"hello\\nworld\"")
|
|
check v.kind == ckString
|
|
check v.strVal == "hello\nworld"
|
|
|
|
test "read keyword":
|
|
let v = read(":key")
|
|
check v.kind == ckKeyword
|
|
check v.kwName == "key"
|
|
|
|
test "read symbol":
|
|
let v = read("foo")
|
|
check v.kind == ckSymbol
|
|
check v.symName == "foo"
|
|
|
|
test "read symbol with special chars":
|
|
let v = read("my-fn?")
|
|
check v.kind == ckSymbol
|
|
check v.symName == "my-fn?"
|
|
|
|
suite "Reader - Collections":
|
|
test "read empty list":
|
|
let v = read("()")
|
|
check v.kind == ckList
|
|
check v.items.len == 0
|
|
|
|
test "read list":
|
|
let v = read("(+ 1 2)")
|
|
check v.kind == ckList
|
|
check v.items.len == 3
|
|
check v.items[0].kind == ckSymbol
|
|
check v.items[0].symName == "+"
|
|
check v.items[1].kind == ckInt
|
|
check v.items[1].intVal == 1
|
|
check v.items[2].kind == ckInt
|
|
check v.items[2].intVal == 2
|
|
|
|
test "read vector":
|
|
let v = read("[1 2 3]")
|
|
check v.kind == ckVector
|
|
check v.items.len == 3
|
|
check v.items[0].intVal == 1
|
|
check v.items[1].intVal == 2
|
|
check v.items[2].intVal == 3
|
|
|
|
test "read empty vector":
|
|
let v = read("[]")
|
|
check v.kind == ckVector
|
|
check v.items.len == 0
|
|
|
|
test "read map":
|
|
let v = read("{:a 1 :b 2}")
|
|
check v.kind == ckMap
|
|
check v.mapKeys.len == 2
|
|
check v.mapKeys[0].kind == ckKeyword
|
|
check v.mapKeys[0].kwName == "a"
|
|
check v.mapVals[0].kind == ckInt
|
|
check v.mapVals[0].intVal == 1
|
|
|
|
test "read empty map":
|
|
let v = read("{}")
|
|
check v.kind == ckMap
|
|
check v.mapKeys.len == 0
|
|
|
|
test "read nested list":
|
|
let v = read("(+ (- 3 1) 2)")
|
|
check v.kind == ckList
|
|
check v.items.len == 3
|
|
check v.items[1].kind == ckList
|
|
check v.items[1].items.len == 3
|
|
|
|
suite "Reader - Quote and Syntax":
|
|
test "read quote":
|
|
let v = read("'foo")
|
|
check v.kind == ckList
|
|
check v.items.len == 2
|
|
check v.items[0].kind == ckSymbol
|
|
check v.items[0].symName == "quote"
|
|
check v.items[1].kind == ckSymbol
|
|
check v.items[1].symName == "foo"
|
|
|
|
test "read deref":
|
|
let v = read("@atom")
|
|
check v.kind == ckList
|
|
check v.items[0].symName == "deref"
|
|
|
|
test "read unquote":
|
|
let v = read("~x")
|
|
check v.kind == ckList
|
|
check v.items[0].symName == "unquote"
|
|
|
|
test "read unquote-splicing":
|
|
let v = read("~@x")
|
|
check v.kind == ckList
|
|
check v.items[0].symName == "unquote-splicing"
|
|
|
|
suite "Reader - Comments":
|
|
test "skip comments":
|
|
let v = read("; comment\n42")
|
|
check v.kind == ckInt
|
|
check v.intVal == 42
|
|
|
|
test "inline comments":
|
|
let forms = readAll("(+ 1 2) ; addition\n(- 3 1)")
|
|
check forms.len == 2
|
|
|
|
suite "Reader - readAll":
|
|
test "read multiple forms":
|
|
let forms = readAll("1 2 3")
|
|
check forms.len == 3
|
|
check forms[0].intVal == 1
|
|
check forms[1].intVal == 2
|
|
check forms[2].intVal == 3
|
|
|
|
test "read empty string":
|
|
let forms = readAll("")
|
|
check forms.len == 0
|
|
|
|
test "read whitespace only":
|
|
let forms = readAll(" \n \t ")
|
|
check forms.len == 0
|
|
|
|
suite "Reader - Edge Cases":
|
|
test "read negative float":
|
|
let v = read("-3.14")
|
|
check v.kind == ckFloat
|
|
check v.floatVal == -3.14
|
|
|
|
test "read negative float without leading zero":
|
|
let v = read("-.5")
|
|
check v.kind == ckFloat
|
|
check v.floatVal == -0.5
|
|
|
|
test "read positive float without leading zero":
|
|
let v = read("+.25")
|
|
check v.kind == ckFloat
|
|
check v.floatVal == 0.25
|
|
|
|
test "read scientific notation 1e5":
|
|
let v = read("1e5")
|
|
check v.kind == ckFloat
|
|
check v.floatVal == 100000.0
|
|
|
|
test "read scientific notation with negative exponent":
|
|
let v = read("1.5e-3")
|
|
check v.kind == ckFloat
|
|
check v.floatVal == 0.0015
|
|
|
|
test "read negative scientific notation":
|
|
let v = read("-2.5e2")
|
|
check v.kind == ckFloat
|
|
check v.floatVal == -250.0
|
|
|
|
test "read negative float scientific without leading zero":
|
|
let v = read("-.5e-3")
|
|
check v.kind == ckFloat
|
|
check v.floatVal == -0.0005
|
|
|
|
test "read number with leading dot":
|
|
let v = read(".25")
|
|
check v.kind == ckFloat
|
|
check v.floatVal == 0.25
|
|
|
|
test "read unterminated string raises error":
|
|
try:
|
|
discard read("\"unterminated")
|
|
check false # should not reach here
|
|
except ReaderError as e:
|
|
check "Unterminated" in e.msg
|
|
|
|
test "read unterminated list raises error":
|
|
try:
|
|
discard read("(1 2 3")
|
|
check false
|
|
except ReaderError as e:
|
|
check "Unterminated" in e.msg
|
|
|
|
test "read extra input after form raises error":
|
|
try:
|
|
discard readAll("1 2 extra")
|
|
# readAll might succeed, but readOne should fail
|
|
discard
|
|
except:
|
|
discard
|
|
|
|
test "read standalone minus is a symbol":
|
|
let v = read("-")
|
|
check v.kind == ckSymbol
|
|
check v.symName == "-"
|
|
|
|
test "read standalone plus is a symbol":
|
|
let v = read("+")
|
|
check v.kind == ckSymbol
|
|
check v.symName == "+"
|