From f6dfc034d1668123a5556ba42ca2f98f55607898 Mon Sep 17 00:00:00 2001 From: giles Date: Sat, 6 Jun 2026 15:20:09 +0000 Subject: [PATCH] =?UTF-8?q?ruby:=20WIP=20=E2=80=94=20parser=20Phase=201=20?= =?UTF-8?q?in-progress=20changes=20(save=20before=20session=20close)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- lib/ruby/parser.sx | 303 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 228 insertions(+), 75 deletions(-) diff --git a/lib/ruby/parser.sx b/lib/ruby/parser.sx index 49a3b197..81b3ea27 100644 --- a/lib/ruby/parser.sx +++ b/lib/ruby/parser.sx @@ -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)