ruby: WIP — parser Phase 1 in-progress changes (save before session close)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 46s

228 insertions, 75 deletions on lib/ruby/parser.sx. Work mid-flight
when the tmux session was wrapped up. Saving here so it isn't lost;
not validated, not under test. Pick up from here when the Ruby loop
resumes.
This commit is contained in:
2026-06-06 15:20:09 +00:00
parent fa3274c394
commit f6dfc034d1

View File

@@ -725,84 +725,237 @@
(rb-p-advance!)
{:type "massign" :targets targets :value (rb-p-parse-multi-val)})))
;; if cond [then] body [elsif cond [then] body ...] [else body] end
(define rb-p-parse-if
(fn ()
(rb-p-advance!)
(let ((cond-val (rb-p-parse-expr)))
(when (and (= (rb-p-type) "keyword") (= (rb-p-val) "then"))
(rb-p-advance!))
(rb-p-skip-seps!)
(let ((then-body (rb-p-parse-stmts (list "end" "else" "elsif"))))
(let ((elsifs (list)) (else-body nil))
(define rb-p-elsif-loop
(fn ()
(when (and (= (rb-p-type) "keyword") (= (rb-p-val) "elsif"))
(do
(rb-p-advance!)
(let ((ec (rb-p-parse-expr)))
(when (and (= (rb-p-type) "keyword") (= (rb-p-val) "then"))
(rb-p-advance!))
(rb-p-skip-seps!)
(let ((eb (rb-p-parse-stmts (list "end" "else" "elsif"))))
(append! elsifs {:cond ec :then eb})
(rb-p-elsif-loop)))))))
(rb-p-elsif-loop)
(when (and (= (rb-p-type) "keyword") (= (rb-p-val) "else"))
(do
(rb-p-advance!)
(rb-p-skip-seps!)
(set! else-body (rb-p-parse-stmts (list "end")))))
(rb-p-expect-kw! "end")
{:type "if" :cond cond-val :then then-body
:elsif elsifs :else else-body})))))
;; unless cond [then] body [else body] end
(define rb-p-parse-unless
(fn ()
(rb-p-advance!)
(let ((cond-val (rb-p-parse-expr)))
(when (and (= (rb-p-type) "keyword") (= (rb-p-val) "then"))
(rb-p-advance!))
(rb-p-skip-seps!)
(let ((then-body (rb-p-parse-stmts (list "end" "else"))))
(let ((else-body nil))
(when (and (= (rb-p-type) "keyword") (= (rb-p-val) "else"))
(do
(rb-p-advance!)
(rb-p-skip-seps!)
(set! else-body (rb-p-parse-stmts (list "end")))))
(rb-p-expect-kw! "end")
{:type "unless" :cond cond-val :then then-body :else else-body})))))
;; while cond [do] body end
(define rb-p-parse-while
(fn ()
(rb-p-advance!)
(let ((cond-val (rb-p-parse-expr)))
(when (and (= (rb-p-type) "keyword") (= (rb-p-val) "do"))
(rb-p-advance!))
(rb-p-skip-seps!)
(let ((body (rb-p-parse-stmts (list "end"))))
(rb-p-expect-kw! "end")
{:type "while" :cond cond-val :body body}))))
;; until cond [do] body end
(define rb-p-parse-until
(fn ()
(rb-p-advance!)
(let ((cond-val (rb-p-parse-expr)))
(when (and (= (rb-p-type) "keyword") (= (rb-p-val) "do"))
(rb-p-advance!))
(rb-p-skip-seps!)
(let ((body (rb-p-parse-stmts (list "end"))))
(rb-p-expect-kw! "end")
{:type "until" :cond cond-val :body body}))))
;; for var in iter [do] body end
(define rb-p-parse-for
(fn ()
(rb-p-advance!)
(let ((var (rb-p-val)))
(rb-p-advance!)
(rb-p-expect-kw! "in")
(let ((iter (rb-p-parse-expr)))
(when (and (= (rb-p-type) "keyword") (= (rb-p-val) "do"))
(rb-p-advance!))
(rb-p-skip-seps!)
(let ((body (rb-p-parse-stmts (list "end"))))
(rb-p-expect-kw! "end")
{:type "for" :var var :iter iter :body body})))))
;; case [expr] when val[, val] [then] body... [else body] end
(define rb-p-parse-case
(fn ()
(rb-p-advance!)
(let ((case-val (if (rb-p-sep?) nil (rb-p-parse-expr))))
(rb-p-skip-seps!)
(let ((whens (list)) (else-body nil))
(define rb-p-when-loop
(fn ()
(when (and (= (rb-p-type) "keyword") (= (rb-p-val) "when"))
(do
(rb-p-advance!)
(let ((wvals (list)))
(define rb-p-wval-loop
(fn ()
(append! wvals (rb-p-parse-assign))
(when (= (rb-p-type) "comma")
(do (rb-p-advance!) (rb-p-skip-newlines!) (rb-p-wval-loop)))))
(rb-p-wval-loop)
(when (and (= (rb-p-type) "keyword") (= (rb-p-val) "then"))
(rb-p-advance!))
(rb-p-skip-seps!)
(let ((wb (rb-p-parse-stmts (list "end" "else" "when"))))
(append! whens {:values wvals :body wb})
(rb-p-when-loop)))))))
(rb-p-when-loop)
(when (and (= (rb-p-type) "keyword") (= (rb-p-val) "else"))
(do
(rb-p-advance!)
(rb-p-skip-seps!)
(set! else-body (rb-p-parse-stmts (list "end")))))
(rb-p-expect-kw! "end")
{:type "case" :value case-val :whens whens :else else-body}))))
(define rb-p-parse-stmt
(fn ()
(cond
((and (= (rb-p-type) "keyword") (= (rb-p-val) "def"))
(rb-p-parse-def))
((and (= (rb-p-type) "keyword") (= (rb-p-val) "class"))
(rb-p-parse-class))
((and (= (rb-p-type) "keyword") (= (rb-p-val) "module"))
(rb-p-parse-module))
((and (= (rb-p-type) "keyword") (= (rb-p-val) "return"))
(do (rb-p-advance!)
{:type "return"
:value (if (or (rb-p-sep?) (= (rb-p-type) "eof"))
nil (rb-p-parse-multi-val))}))
((and (= (rb-p-type) "keyword") (= (rb-p-val) "yield"))
(do (rb-p-advance!)
{:type "yield"
:args (cond
((= (rb-p-type) "lparen") (rb-p-parse-args-parens))
((or (rb-p-sep?) (= (rb-p-type) "eof")) (list))
(:else (rb-p-parse-args-bare)))}))
((and (= (rb-p-type) "keyword") (= (rb-p-val) "break"))
(do (rb-p-advance!)
{:type "break"
:value (if (or (rb-p-sep?) (= (rb-p-type) "eof"))
nil (rb-p-parse-expr))}))
((and (= (rb-p-type) "keyword") (= (rb-p-val) "next"))
(do (rb-p-advance!)
{:type "next"
:value (if (or (rb-p-sep?) (= (rb-p-type) "eof"))
nil (rb-p-parse-expr))}))
((and (= (rb-p-type) "keyword") (= (rb-p-val) "redo"))
(do (rb-p-advance!) {:type "redo"}))
((and (= (rb-p-type) "keyword") (= (rb-p-val) "raise"))
(do (rb-p-advance!)
{:type "raise"
:value (if (or (rb-p-sep?) (= (rb-p-type) "eof"))
nil (rb-p-parse-expr))}))
;; Massign: token followed by comma
((and (or (= (rb-p-type) "ident") (= (rb-p-type) "ivar")
(= (rb-p-type) "cvar") (= (rb-p-type) "gvar")
(= (rb-p-type) "const"))
(= (get (rb-p-peek 1) :type) "comma"))
(rb-p-parse-massign))
(:else
(let ((node (rb-p-parse-assign)))
(if (and (= (get node :type) "send")
(= (len (get node :args)) 0)
(nil? (get node :block)))
;; Bare send: check for block or no-paren args
(let ((node
(cond
;; Block immediately follows (do or {)
((or (and (= (rb-p-type) "keyword") (= (rb-p-val) "do"))
(= (rb-p-type) "lbrace"))
(let ((blk (rb-p-parse-block)))
{:type "send" :name (get node :name) :args (list) :block blk}))
;; No-paren args (stop before block/sep/end keywords)
((and (not (rb-p-sep?))
(not (= (rb-p-type) "eof"))
(not (= (rb-p-type) "op"))
(not (= (rb-p-type) "dot"))
(not (= (rb-p-type) "dcolon"))
(not (= (rb-p-type) "rparen"))
(not (= (rb-p-type) "rbracket"))
(not (= (rb-p-type) "rbrace"))
(not (= (rb-p-type) "lbrace"))
(not (and (= (rb-p-type) "keyword")
(contains? (list "end" "else" "elsif" "when"
"rescue" "ensure" "then" "do"
"and" "or" "not")
(rb-p-val)))))
(let ((args (rb-p-parse-args-bare))
(blk (rb-p-parse-block)))
(if (> (len args) 0)
{:type "send" :name (get node :name) :args args :block blk}
node)))
(:else node))
node))))))
((and (= (rb-p-type) "keyword") (= (rb-p-val) "def"))
(rb-p-parse-def))
((and (= (rb-p-type) "keyword") (= (rb-p-val) "class"))
(rb-p-parse-class))
((and (= (rb-p-type) "keyword") (= (rb-p-val) "module"))
(rb-p-parse-module))
((and (= (rb-p-type) "keyword") (= (rb-p-val) "if"))
(rb-p-parse-if))
((and (= (rb-p-type) "keyword") (= (rb-p-val) "unless"))
(rb-p-parse-unless))
((and (= (rb-p-type) "keyword") (= (rb-p-val) "while"))
(rb-p-parse-while))
((and (= (rb-p-type) "keyword") (= (rb-p-val) "until"))
(rb-p-parse-until))
((and (= (rb-p-type) "keyword") (= (rb-p-val) "case"))
(rb-p-parse-case))
((and (= (rb-p-type) "keyword") (= (rb-p-val) "for"))
(rb-p-parse-for))
((and (= (rb-p-type) "keyword") (= (rb-p-val) "return"))
(do (rb-p-advance!)
{:type "return"
:value (if (or (rb-p-sep?) (= (rb-p-type) "eof"))
nil (rb-p-parse-multi-val))}))
((and (= (rb-p-type) "keyword") (= (rb-p-val) "yield"))
(do (rb-p-advance!)
{:type "yield"
:args (cond
((= (rb-p-type) "lparen") (rb-p-parse-args-parens))
((or (rb-p-sep?) (= (rb-p-type) "eof")) (list))
(:else (rb-p-parse-args-bare)))}))
((and (= (rb-p-type) "keyword") (= (rb-p-val) "break"))
(do (rb-p-advance!)
{:type "break"
:value (if (or (rb-p-sep?) (= (rb-p-type) "eof"))
nil (rb-p-parse-expr))}))
((and (= (rb-p-type) "keyword") (= (rb-p-val) "next"))
(do (rb-p-advance!)
{:type "next"
:value (if (or (rb-p-sep?) (= (rb-p-type) "eof"))
nil (rb-p-parse-expr))}))
((and (= (rb-p-type) "keyword") (= (rb-p-val) "redo"))
(do (rb-p-advance!) {:type "redo"}))
((and (= (rb-p-type) "keyword") (= (rb-p-val) "raise"))
(do (rb-p-advance!)
{:type "raise"
:value (if (or (rb-p-sep?) (= (rb-p-type) "eof"))
nil (rb-p-parse-expr))}))
;; Massign: token followed by comma
((and (or (= (rb-p-type) "ident") (= (rb-p-type) "ivar")
(= (rb-p-type) "cvar") (= (rb-p-type) "gvar")
(= (rb-p-type) "const"))
(= (get (rb-p-peek 1) :type) "comma"))
(rb-p-parse-massign))
(:else
(let ((nd (rb-p-parse-assign)))
(if (and (= (get nd :type) "send")
(= (len (get nd :args)) 0)
(nil? (get nd :block)))
(cond
((or (and (= (rb-p-type) "keyword") (= (rb-p-val) "do"))
(= (rb-p-type) "lbrace"))
(let ((blk (rb-p-parse-block)))
{:type "send" :name (get nd :name)
:args (list) :block blk}))
((and (not (rb-p-sep?))
(not (= (rb-p-type) "eof"))
(not (= (rb-p-type) "op"))
(not (= (rb-p-type) "dot"))
(not (= (rb-p-type) "dcolon"))
(not (= (rb-p-type) "rparen"))
(not (= (rb-p-type) "rbracket"))
(not (= (rb-p-type) "rbrace"))
(not (= (rb-p-type) "lbrace"))
(not (and (= (rb-p-type) "keyword")
(contains? (list "end" "else" "elsif"
"when" "rescue" "ensure"
"then" "do" "and" "or"
"not" "if" "unless"
"while" "until")
(rb-p-val)))))
(let ((args (rb-p-parse-args-bare))
(blk (rb-p-parse-block)))
(if (> (len args) 0)
{:type "send" :name (get nd :name)
:args args :block blk}
nd)))
(:else nd))
nd))))))
;; Postfix modifiers
(cond
((and (= (rb-p-type) "keyword") (= (rb-p-val) "if"))
(do (rb-p-advance!)
{:type "postfix-if" :cond (rb-p-parse-expr) :body node}))
((and (= (rb-p-type) "keyword") (= (rb-p-val) "unless"))
(do (rb-p-advance!)
{:type "postfix-unless" :cond (rb-p-parse-expr) :body node}))
((and (= (rb-p-type) "keyword") (= (rb-p-val) "while"))
(do (rb-p-advance!)
{:type "postfix-while" :cond (rb-p-parse-expr) :body node}))
((and (= (rb-p-type) "keyword") (= (rb-p-val) "until"))
(do (rb-p-advance!)
{:type "postfix-until" :cond (rb-p-parse-expr) :body node}))
(:else node)))))
(define rb-p-parse-stmts
(fn (terminators)