Some checks are pending
Test, Build, and Deploy / test-build-deploy (push) Waiting to run
lib/common-lisp/eval.sx: cl-eval-ast implementing quote, if, progn, let/let*, flet, labels, setq/setf, function, lambda, the, locally, eval-when, defun, defvar/defparameter/defconstant, built-in arithmetic (+/-/*//, min/max/abs/evenp/oddp), comparisons, predicates, list ops, string ops, funcall/apply/mapcar. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
205 lines
6.3 KiB
Plaintext
205 lines
6.3 KiB
Plaintext
;; Lambda list parser tests
|
|
|
|
(define cl-test-pass 0)
|
|
(define cl-test-fail 0)
|
|
(define cl-test-fails (list))
|
|
|
|
;; Deep structural equality for dicts and lists
|
|
(define
|
|
cl-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) (cl-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
|
|
chk
|
|
(fn
|
|
()
|
|
(when
|
|
(and ok (< i (len a)))
|
|
(do
|
|
(when
|
|
(not (cl-deep= (nth a i) (nth b i)))
|
|
(set! ok false))
|
|
(set! i (+ i 1))
|
|
(chk)))))
|
|
(chk)
|
|
ok)))
|
|
(:else false))))
|
|
|
|
(define
|
|
cl-test
|
|
(fn
|
|
(name actual expected)
|
|
(if
|
|
(cl-deep= actual expected)
|
|
(set! cl-test-pass (+ cl-test-pass 1))
|
|
(do
|
|
(set! cl-test-fail (+ cl-test-fail 1))
|
|
(append! cl-test-fails {:name name :expected expected :actual actual})))))
|
|
|
|
;; Helper: parse lambda list from string "(x y ...)"
|
|
(define ll (fn (src) (cl-parse-lambda-list-str src)))
|
|
(define ll-req (fn (src) (get (ll src) "required")))
|
|
(define ll-opt (fn (src) (get (ll src) "optional")))
|
|
(define ll-rest (fn (src) (get (ll src) "rest")))
|
|
(define ll-key (fn (src) (get (ll src) "key")))
|
|
(define ll-aok (fn (src) (get (ll src) "allow-other-keys")))
|
|
(define ll-aux (fn (src) (get (ll src) "aux")))
|
|
|
|
;; ── required parameters ───────────────────────────────────────────
|
|
|
|
(cl-test "required: empty" (ll-req "()") (list))
|
|
(cl-test "required: one" (ll-req "(x)") (list "X"))
|
|
(cl-test "required: two" (ll-req "(x y)") (list "X" "Y"))
|
|
(cl-test "required: three" (ll-req "(a b c)") (list "A" "B" "C"))
|
|
(cl-test "required: upcased" (ll-req "(foo bar)") (list "FOO" "BAR"))
|
|
|
|
;; ── &optional ─────────────────────────────────────────────────────
|
|
|
|
(cl-test "optional: none" (ll-opt "(x)") (list))
|
|
|
|
(cl-test
|
|
"optional: bare symbol"
|
|
(ll-opt "(x &optional z)")
|
|
(list {:name "Z" :default nil :supplied nil}))
|
|
|
|
(cl-test
|
|
"optional: with default"
|
|
(ll-opt "(x &optional (z 0))")
|
|
(list {:name "Z" :default 0 :supplied nil}))
|
|
|
|
(cl-test
|
|
"optional: with supplied-p"
|
|
(ll-opt "(x &optional (z 0 z-p))")
|
|
(list {:name "Z" :default 0 :supplied "Z-P"}))
|
|
|
|
(cl-test
|
|
"optional: two params"
|
|
(ll-opt "(&optional a (b 1))")
|
|
(list {:name "A" :default nil :supplied nil} {:name "B" :default 1 :supplied nil}))
|
|
|
|
(cl-test
|
|
"optional: string default"
|
|
(ll-opt "(&optional (name \"world\"))")
|
|
(list {:name "NAME" :default {:cl-type "string" :value "world"} :supplied nil}))
|
|
|
|
;; ── &rest ─────────────────────────────────────────────────────────
|
|
|
|
(cl-test "rest: none" (ll-rest "(x)") nil)
|
|
(cl-test "rest: present" (ll-rest "(x &rest args)") "ARGS")
|
|
(cl-test "rest: with required" (ll-rest "(a b &rest tail)") "TAIL")
|
|
|
|
;; &body is an alias for &rest
|
|
(cl-test "body: alias for rest" (ll-rest "(&body forms)") "FORMS")
|
|
|
|
;; rest doesn't consume required params
|
|
(cl-test "rest: required still there" (ll-req "(a b &rest rest)") (list "A" "B"))
|
|
|
|
;; ── &key ──────────────────────────────────────────────────────────
|
|
|
|
(cl-test "key: none" (ll-key "(x)") (list))
|
|
|
|
(cl-test
|
|
"key: bare symbol"
|
|
(ll-key "(&key x)")
|
|
(list {:name "X" :keyword "X" :default nil :supplied nil}))
|
|
|
|
(cl-test
|
|
"key: with default"
|
|
(ll-key "(&key (x 42))")
|
|
(list {:name "X" :keyword "X" :default 42 :supplied nil}))
|
|
|
|
(cl-test
|
|
"key: with supplied-p"
|
|
(ll-key "(&key (x 42 x-p))")
|
|
(list {:name "X" :keyword "X" :default 42 :supplied "X-P"}))
|
|
|
|
(cl-test
|
|
"key: two params"
|
|
(ll-key "(&key a b)")
|
|
(list
|
|
{:name "A" :keyword "A" :default nil :supplied nil}
|
|
{:name "B" :keyword "B" :default nil :supplied nil}))
|
|
|
|
;; ── &allow-other-keys ─────────────────────────────────────────────
|
|
|
|
(cl-test "aok: absent" (ll-aok "(x)") false)
|
|
(cl-test "aok: present" (ll-aok "(&key x &allow-other-keys)") true)
|
|
|
|
;; ── &aux ──────────────────────────────────────────────────────────
|
|
|
|
(cl-test "aux: none" (ll-aux "(x)") (list))
|
|
|
|
(cl-test
|
|
"aux: bare symbol"
|
|
(ll-aux "(&aux temp)")
|
|
(list {:name "TEMP" :init nil}))
|
|
|
|
(cl-test
|
|
"aux: with init"
|
|
(ll-aux "(&aux (count 0))")
|
|
(list {:name "COUNT" :init 0}))
|
|
|
|
(cl-test
|
|
"aux: two vars"
|
|
(ll-aux "(&aux a (b 1))")
|
|
(list {:name "A" :init nil} {:name "B" :init 1}))
|
|
|
|
;; ── combined ──────────────────────────────────────────────────────
|
|
|
|
(cl-test
|
|
"combined: full lambda list"
|
|
(let
|
|
((parsed (ll "(x y &optional (z 0 z-p) &rest args &key a (b nil b-p) &aux temp)")))
|
|
(list
|
|
(get parsed "required")
|
|
(get (nth (get parsed "optional") 0) "name")
|
|
(get (nth (get parsed "optional") 0) "default")
|
|
(get (nth (get parsed "optional") 0) "supplied")
|
|
(get parsed "rest")
|
|
(get (nth (get parsed "key") 0) "name")
|
|
(get (nth (get parsed "key") 1) "supplied")
|
|
(get (nth (get parsed "aux") 0) "name")))
|
|
(list
|
|
(list "X" "Y")
|
|
"Z"
|
|
0
|
|
"Z-P"
|
|
"ARGS"
|
|
"A"
|
|
"B-P"
|
|
"TEMP"))
|
|
|
|
(cl-test
|
|
"combined: required only stops before &"
|
|
(ll-req "(a b &optional c)")
|
|
(list "A" "B"))
|
|
|
|
(cl-test
|
|
"combined: required only with &key"
|
|
(ll-req "(x &key y)")
|
|
(list "X"))
|
|
|
|
(cl-test
|
|
"combined: &rest and &key together"
|
|
(let
|
|
((parsed (ll "(&rest args &key verbose)")))
|
|
(list (get parsed "rest") (get (nth (get parsed "key") 0) "name")))
|
|
(list "ARGS" "VERBOSE"))
|