Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Has been cancelled
370 lines
10 KiB
Plaintext
370 lines
10 KiB
Plaintext
;; Smalltalk parser tests.
|
|
;;
|
|
;; Reuses helpers (st-test, st-deep=?) from tokenize.sx. Counters reset
|
|
;; here so this file's summary covers parse tests only.
|
|
|
|
(set! st-test-pass 0)
|
|
(set! st-test-fail 0)
|
|
(set! st-test-fails (list))
|
|
|
|
;; ── 1. Atoms ──
|
|
(st-test "int" (st-parse-expr "42") {:type "lit-int" :value 42})
|
|
(st-test "float" (st-parse-expr "3.14") {:type "lit-float" :value 3.14})
|
|
(st-test "string" (st-parse-expr "'hi'") {:type "lit-string" :value "hi"})
|
|
(st-test "char" (st-parse-expr "$x") {:type "lit-char" :value "x"})
|
|
(st-test "symbol" (st-parse-expr "#foo") {:type "lit-symbol" :value "foo"})
|
|
(st-test "binary symbol" (st-parse-expr "#+") {:type "lit-symbol" :value "+"})
|
|
(st-test "keyword symbol" (st-parse-expr "#at:put:") {:type "lit-symbol" :value "at:put:"})
|
|
(st-test "nil" (st-parse-expr "nil") {:type "lit-nil"})
|
|
(st-test "true" (st-parse-expr "true") {:type "lit-true"})
|
|
(st-test "false" (st-parse-expr "false") {:type "lit-false"})
|
|
(st-test "self" (st-parse-expr "self") {:type "self"})
|
|
(st-test "super" (st-parse-expr "super") {:type "super"})
|
|
(st-test "ident" (st-parse-expr "x") {:type "ident" :name "x"})
|
|
(st-test "negative int" (st-parse-expr "-3") {:type "lit-int" :value -3})
|
|
|
|
;; ── 2. Literal arrays ──
|
|
(st-test
|
|
"literal array of ints"
|
|
(st-parse-expr "#(1 2 3)")
|
|
{:type "lit-array"
|
|
:elements (list
|
|
{:type "lit-int" :value 1}
|
|
{:type "lit-int" :value 2}
|
|
{:type "lit-int" :value 3})})
|
|
|
|
(st-test
|
|
"literal array mixed"
|
|
(st-parse-expr "#(1 #foo 'x' true)")
|
|
{:type "lit-array"
|
|
:elements (list
|
|
{:type "lit-int" :value 1}
|
|
{:type "lit-symbol" :value "foo"}
|
|
{:type "lit-string" :value "x"}
|
|
{:type "lit-true"})})
|
|
|
|
(st-test
|
|
"literal array bare ident is symbol"
|
|
(st-parse-expr "#(foo bar)")
|
|
{:type "lit-array"
|
|
:elements (list
|
|
{:type "lit-symbol" :value "foo"}
|
|
{:type "lit-symbol" :value "bar"})})
|
|
|
|
(st-test
|
|
"nested literal array"
|
|
(st-parse-expr "#(1 (2 3) 4)")
|
|
{:type "lit-array"
|
|
:elements (list
|
|
{:type "lit-int" :value 1}
|
|
{:type "lit-array"
|
|
:elements (list
|
|
{:type "lit-int" :value 2}
|
|
{:type "lit-int" :value 3})}
|
|
{:type "lit-int" :value 4})})
|
|
|
|
(st-test
|
|
"byte array"
|
|
(st-parse-expr "#[1 2 3]")
|
|
{:type "lit-byte-array" :elements (list 1 2 3)})
|
|
|
|
;; ── 3. Unary messages ──
|
|
(st-test
|
|
"unary single"
|
|
(st-parse-expr "x foo")
|
|
{:type "send"
|
|
:receiver {:type "ident" :name "x"}
|
|
:selector "foo"
|
|
:args (list)})
|
|
|
|
(st-test
|
|
"unary chain"
|
|
(st-parse-expr "x foo bar baz")
|
|
{:type "send"
|
|
:receiver {:type "send"
|
|
:receiver {:type "send"
|
|
:receiver {:type "ident" :name "x"}
|
|
:selector "foo"
|
|
:args (list)}
|
|
:selector "bar"
|
|
:args (list)}
|
|
:selector "baz"
|
|
:args (list)})
|
|
|
|
(st-test
|
|
"unary on literal"
|
|
(st-parse-expr "42 printNl")
|
|
{:type "send"
|
|
:receiver {:type "lit-int" :value 42}
|
|
:selector "printNl"
|
|
:args (list)})
|
|
|
|
;; ── 4. Binary messages ──
|
|
(st-test
|
|
"binary single"
|
|
(st-parse-expr "1 + 2")
|
|
{:type "send"
|
|
:receiver {:type "lit-int" :value 1}
|
|
:selector "+"
|
|
:args (list {:type "lit-int" :value 2})})
|
|
|
|
(st-test
|
|
"binary left-assoc"
|
|
(st-parse-expr "1 + 2 + 3")
|
|
{:type "send"
|
|
:receiver {:type "send"
|
|
:receiver {:type "lit-int" :value 1}
|
|
:selector "+"
|
|
:args (list {:type "lit-int" :value 2})}
|
|
:selector "+"
|
|
:args (list {:type "lit-int" :value 3})})
|
|
|
|
(st-test
|
|
"binary same precedence l-to-r"
|
|
(st-parse-expr "1 + 2 * 3")
|
|
{:type "send"
|
|
:receiver {:type "send"
|
|
:receiver {:type "lit-int" :value 1}
|
|
:selector "+"
|
|
:args (list {:type "lit-int" :value 2})}
|
|
:selector "*"
|
|
:args (list {:type "lit-int" :value 3})})
|
|
|
|
;; ── 5. Precedence: unary binds tighter than binary ──
|
|
(st-test
|
|
"unary tighter than binary"
|
|
(st-parse-expr "3 + 4 factorial")
|
|
{:type "send"
|
|
:receiver {:type "lit-int" :value 3}
|
|
:selector "+"
|
|
:args (list
|
|
{:type "send"
|
|
:receiver {:type "lit-int" :value 4}
|
|
:selector "factorial"
|
|
:args (list)})})
|
|
|
|
;; ── 6. Keyword messages ──
|
|
(st-test
|
|
"keyword single"
|
|
(st-parse-expr "x at: 1")
|
|
{:type "send"
|
|
:receiver {:type "ident" :name "x"}
|
|
:selector "at:"
|
|
:args (list {:type "lit-int" :value 1})})
|
|
|
|
(st-test
|
|
"keyword chain"
|
|
(st-parse-expr "x at: 1 put: 'a'")
|
|
{:type "send"
|
|
:receiver {:type "ident" :name "x"}
|
|
:selector "at:put:"
|
|
:args (list {:type "lit-int" :value 1} {:type "lit-string" :value "a"})})
|
|
|
|
;; ── 7. Precedence: binary tighter than keyword ──
|
|
(st-test
|
|
"binary tighter than keyword"
|
|
(st-parse-expr "x at: 1 + 2")
|
|
{:type "send"
|
|
:receiver {:type "ident" :name "x"}
|
|
:selector "at:"
|
|
:args (list
|
|
{:type "send"
|
|
:receiver {:type "lit-int" :value 1}
|
|
:selector "+"
|
|
:args (list {:type "lit-int" :value 2})})})
|
|
|
|
(st-test
|
|
"keyword absorbs trailing unary"
|
|
(st-parse-expr "a foo: b bar")
|
|
{:type "send"
|
|
:receiver {:type "ident" :name "a"}
|
|
:selector "foo:"
|
|
:args (list
|
|
{:type "send"
|
|
:receiver {:type "ident" :name "b"}
|
|
:selector "bar"
|
|
:args (list)})})
|
|
|
|
;; ── 8. Parens override precedence ──
|
|
(st-test
|
|
"paren forces grouping"
|
|
(st-parse-expr "(1 + 2) * 3")
|
|
{:type "send"
|
|
:receiver {:type "send"
|
|
:receiver {:type "lit-int" :value 1}
|
|
:selector "+"
|
|
:args (list {:type "lit-int" :value 2})}
|
|
:selector "*"
|
|
:args (list {:type "lit-int" :value 3})})
|
|
|
|
;; ── 9. Cascade ──
|
|
(st-test
|
|
"simple cascade"
|
|
(st-parse-expr "x m1; m2")
|
|
{:type "cascade"
|
|
:receiver {:type "ident" :name "x"}
|
|
:messages (list
|
|
{:selector "m1" :args (list)}
|
|
{:selector "m2" :args (list)})})
|
|
|
|
(st-test
|
|
"cascade with binary and keyword"
|
|
(st-parse-expr "Stream new nl; tab; print: 1")
|
|
{:type "cascade"
|
|
:receiver {:type "send"
|
|
:receiver {:type "ident" :name "Stream"}
|
|
:selector "new"
|
|
:args (list)}
|
|
:messages (list
|
|
{:selector "nl" :args (list)}
|
|
{:selector "tab" :args (list)}
|
|
{:selector "print:" :args (list {:type "lit-int" :value 1})})})
|
|
|
|
;; ── 10. Blocks ──
|
|
(st-test
|
|
"empty block"
|
|
(st-parse-expr "[]")
|
|
{:type "block" :params (list) :temps (list) :body (list)})
|
|
|
|
(st-test
|
|
"block one expr"
|
|
(st-parse-expr "[1 + 2]")
|
|
{:type "block"
|
|
:params (list)
|
|
:temps (list)
|
|
:body (list
|
|
{:type "send"
|
|
:receiver {:type "lit-int" :value 1}
|
|
:selector "+"
|
|
:args (list {:type "lit-int" :value 2})})})
|
|
|
|
(st-test
|
|
"block with params"
|
|
(st-parse-expr "[:a :b | a + b]")
|
|
{:type "block"
|
|
:params (list "a" "b")
|
|
:temps (list)
|
|
:body (list
|
|
{:type "send"
|
|
:receiver {:type "ident" :name "a"}
|
|
:selector "+"
|
|
:args (list {:type "ident" :name "b"})})})
|
|
|
|
(st-test
|
|
"block with temps"
|
|
(st-parse-expr "[| t | t := 1. t]")
|
|
{:type "block"
|
|
:params (list)
|
|
:temps (list "t")
|
|
:body (list
|
|
{:type "assign" :name "t" :expr {:type "lit-int" :value 1}}
|
|
{:type "ident" :name "t"})})
|
|
|
|
(st-test
|
|
"block with params and temps"
|
|
(st-parse-expr "[:x | | t | t := x + 1. t]")
|
|
{:type "block"
|
|
:params (list "x")
|
|
:temps (list "t")
|
|
:body (list
|
|
{:type "assign"
|
|
:name "t"
|
|
:expr {:type "send"
|
|
:receiver {:type "ident" :name "x"}
|
|
:selector "+"
|
|
:args (list {:type "lit-int" :value 1})}}
|
|
{:type "ident" :name "t"})})
|
|
|
|
;; ── 11. Assignment / return / statements ──
|
|
(st-test
|
|
"assignment"
|
|
(st-parse-expr "x := 1")
|
|
{:type "assign" :name "x" :expr {:type "lit-int" :value 1}})
|
|
|
|
(st-test
|
|
"return"
|
|
(st-parse-expr "1")
|
|
{:type "lit-int" :value 1})
|
|
|
|
(st-test
|
|
"return statement at top level"
|
|
(st-parse "^ 1")
|
|
{:type "seq" :temps (list)
|
|
:exprs (list {:type "return" :expr {:type "lit-int" :value 1}})})
|
|
|
|
(st-test
|
|
"two statements"
|
|
(st-parse "x := 1. y := 2")
|
|
{:type "seq" :temps (list)
|
|
:exprs (list
|
|
{:type "assign" :name "x" :expr {:type "lit-int" :value 1}}
|
|
{:type "assign" :name "y" :expr {:type "lit-int" :value 2}})})
|
|
|
|
(st-test
|
|
"trailing dot allowed"
|
|
(st-parse "1. 2.")
|
|
{:type "seq" :temps (list)
|
|
:exprs (list {:type "lit-int" :value 1} {:type "lit-int" :value 2})})
|
|
|
|
;; ── 12. Method headers ──
|
|
(st-test
|
|
"unary method"
|
|
(st-parse-method "factorial ^ self * (self - 1) factorial")
|
|
{:type "method"
|
|
:selector "factorial"
|
|
:params (list)
|
|
:temps (list)
|
|
:pragmas (list)
|
|
:body (list
|
|
{:type "return"
|
|
:expr {:type "send"
|
|
:receiver {:type "self"}
|
|
:selector "*"
|
|
:args (list
|
|
{:type "send"
|
|
:receiver {:type "send"
|
|
:receiver {:type "self"}
|
|
:selector "-"
|
|
:args (list {:type "lit-int" :value 1})}
|
|
:selector "factorial"
|
|
:args (list)})}})})
|
|
|
|
(st-test
|
|
"binary method"
|
|
(st-parse-method "+ other ^ 'plus'")
|
|
{:type "method"
|
|
:selector "+"
|
|
:params (list "other")
|
|
:temps (list)
|
|
:pragmas (list)
|
|
:body (list {:type "return" :expr {:type "lit-string" :value "plus"}})})
|
|
|
|
(st-test
|
|
"keyword method"
|
|
(st-parse-method "at: i put: v ^ v")
|
|
{:type "method"
|
|
:selector "at:put:"
|
|
:params (list "i" "v")
|
|
:temps (list)
|
|
:pragmas (list)
|
|
:body (list {:type "return" :expr {:type "ident" :name "v"}})})
|
|
|
|
(st-test
|
|
"method with temps"
|
|
(st-parse-method "twice: x | t | t := x + x. ^ t")
|
|
{:type "method"
|
|
:selector "twice:"
|
|
:params (list "x")
|
|
:temps (list "t")
|
|
:pragmas (list)
|
|
:body (list
|
|
{:type "assign"
|
|
:name "t"
|
|
:expr {:type "send"
|
|
:receiver {:type "ident" :name "x"}
|
|
:selector "+"
|
|
:args (list {:type "ident" :name "x"})}}
|
|
{:type "return" :expr {:type "ident" :name "t"}})})
|
|
|
|
(list st-test-pass st-test-fail)
|