Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 11s
832 lines
35 KiB
Plaintext
832 lines
35 KiB
Plaintext
;; Ruby parser: token list → AST.
|
|
;; Entry: (rb-parse tokens) or (rb-parse-str src)
|
|
;; AST nodes: dicts with :type plus type-specific fields.
|
|
|
|
(define rb-parse
|
|
(fn (tokens)
|
|
(let ((pos 0) (tok-count (len tokens)))
|
|
|
|
(define rb-p-cur
|
|
(fn () (nth tokens pos)))
|
|
(define rb-p-peek
|
|
(fn (n)
|
|
(if (< (+ pos n) tok-count)
|
|
(nth tokens (+ pos n))
|
|
{:type "eof" :value nil :line 0 :col 0})))
|
|
(define rb-p-advance!
|
|
(fn () (set! pos (+ pos 1))))
|
|
(define rb-p-type
|
|
(fn () (get (rb-p-cur) :type)))
|
|
(define rb-p-val
|
|
(fn () (get (rb-p-cur) :value)))
|
|
(define rb-p-sep?
|
|
(fn () (or (= (rb-p-type) "newline") (= (rb-p-type) "semi"))))
|
|
(define rb-p-skip-seps!
|
|
(fn ()
|
|
(when (rb-p-sep?)
|
|
(do (rb-p-advance!) (rb-p-skip-seps!)))))
|
|
(define rb-p-skip-newlines!
|
|
(fn ()
|
|
(when (= (rb-p-type) "newline")
|
|
(do (rb-p-advance!) (rb-p-skip-newlines!)))))
|
|
(define rb-p-expect!
|
|
(fn (type)
|
|
(if (= (rb-p-type) type)
|
|
(let ((tok (rb-p-cur)))
|
|
(rb-p-advance!)
|
|
tok)
|
|
{:type "error"
|
|
:msg (join "" (list "expected " type " got " (rb-p-type)))})))
|
|
(define rb-p-expect-kw!
|
|
(fn (kw)
|
|
(when (and (= (rb-p-type) "keyword") (= (rb-p-val) kw))
|
|
(rb-p-advance!))))
|
|
|
|
;; Block: do |params| body end or { |params| body }
|
|
(define rb-p-parse-block-params
|
|
(fn ()
|
|
(if (= (rb-p-type) "pipe")
|
|
(do
|
|
(rb-p-advance!)
|
|
(let ((params (list)))
|
|
(define rb-p-bp-loop
|
|
(fn ()
|
|
(when (not (or (= (rb-p-type) "pipe") (= (rb-p-type) "eof")))
|
|
(do
|
|
(cond
|
|
((and (= (rb-p-type) "op") (= (rb-p-val) "**"))
|
|
(do
|
|
(rb-p-advance!)
|
|
(append! params {:type "param-kwrest" :name (rb-p-val)})
|
|
(rb-p-advance!)))
|
|
((and (= (rb-p-type) "op") (= (rb-p-val) "*"))
|
|
(do
|
|
(rb-p-advance!)
|
|
(if (= (rb-p-type) "ident")
|
|
(do
|
|
(append! params {:type "param-rest" :name (rb-p-val)})
|
|
(rb-p-advance!))
|
|
(append! params {:type "param-rest" :name nil}))))
|
|
(:else
|
|
(do
|
|
(append! params {:type "param-req" :name (rb-p-val)})
|
|
(rb-p-advance!))))
|
|
(when (= (rb-p-type) "comma") (rb-p-advance!))
|
|
(rb-p-bp-loop)))))
|
|
(rb-p-bp-loop)
|
|
(rb-p-expect! "pipe")
|
|
params))
|
|
(list))))
|
|
|
|
(define rb-p-parse-block
|
|
(fn ()
|
|
(cond
|
|
((and (= (rb-p-type) "keyword") (= (rb-p-val) "do"))
|
|
(do
|
|
(rb-p-advance!)
|
|
(let ((params (rb-p-parse-block-params)))
|
|
(rb-p-skip-seps!)
|
|
(let ((body (rb-p-parse-stmts (list "end"))))
|
|
(rb-p-expect-kw! "end")
|
|
{:type "block" :params params :body body}))))
|
|
((= (rb-p-type) "lbrace")
|
|
(do
|
|
(rb-p-advance!)
|
|
(let ((params (rb-p-parse-block-params)))
|
|
(rb-p-skip-seps!)
|
|
(let ((body (rb-p-parse-stmts (list "rbrace"))))
|
|
(rb-p-expect! "rbrace")
|
|
{:type "block" :params params :body body}))))
|
|
(:else nil))))
|
|
|
|
;; Method def params
|
|
(define rb-p-parse-def-params
|
|
(fn ()
|
|
(let ((params (list)))
|
|
(define rb-p-dp-one
|
|
(fn ()
|
|
(cond
|
|
((and (= (rb-p-type) "op") (= (rb-p-val) "&"))
|
|
(do
|
|
(rb-p-advance!)
|
|
(append! params {:type "param-block" :name (rb-p-val)})
|
|
(rb-p-advance!)))
|
|
((and (= (rb-p-type) "op") (= (rb-p-val) "**"))
|
|
(do
|
|
(rb-p-advance!)
|
|
(append! params {:type "param-kwrest" :name (rb-p-val)})
|
|
(rb-p-advance!)))
|
|
((and (= (rb-p-type) "op") (= (rb-p-val) "*"))
|
|
(do
|
|
(rb-p-advance!)
|
|
(if (= (rb-p-type) "ident")
|
|
(do
|
|
(append! params {:type "param-rest" :name (rb-p-val)})
|
|
(rb-p-advance!))
|
|
(append! params {:type "param-rest" :name nil}))))
|
|
((and (= (rb-p-type) "ident")
|
|
(= (get (rb-p-peek 1) :type) "colon"))
|
|
(do
|
|
(let ((name (rb-p-val)))
|
|
(rb-p-advance!)
|
|
(rb-p-advance!)
|
|
(if (or (rb-p-sep?) (= (rb-p-type) "comma")
|
|
(= (rb-p-type) "rparen") (= (rb-p-type) "eof"))
|
|
(append! params {:type "param-kw" :name name :default nil})
|
|
(append! params {:type "param-kw" :name name
|
|
:default (rb-p-parse-assign)})))))
|
|
(:else
|
|
(let ((name (rb-p-val)))
|
|
(rb-p-advance!)
|
|
(if (and (= (rb-p-type) "op") (= (rb-p-val) "="))
|
|
(do
|
|
(rb-p-advance!)
|
|
(append! params {:type "param-opt" :name name
|
|
:default (rb-p-parse-assign)}))
|
|
(append! params {:type "param-req" :name name})))))))
|
|
(define rb-p-dp-loop
|
|
(fn ()
|
|
(when (not (or (= (rb-p-type) "rparen") (rb-p-sep?)
|
|
(= (rb-p-type) "eof")))
|
|
(do
|
|
(rb-p-dp-one)
|
|
(when (= (rb-p-type) "comma")
|
|
(do (rb-p-advance!) (rb-p-skip-newlines!)))
|
|
(rb-p-dp-loop)))))
|
|
(rb-p-dp-loop)
|
|
params)))
|
|
|
|
;; def [recv.] name [(params)] body end
|
|
(define rb-p-parse-def
|
|
(fn ()
|
|
(rb-p-advance!)
|
|
(let ((recv nil) (name nil))
|
|
(cond
|
|
((and (= (rb-p-type) "keyword") (= (rb-p-val) "self")
|
|
(= (get (rb-p-peek 1) :type) "dot"))
|
|
(do
|
|
(set! recv {:type "self"})
|
|
(rb-p-advance!)
|
|
(rb-p-advance!)
|
|
(set! name (rb-p-val))
|
|
(rb-p-advance!)))
|
|
((and (= (rb-p-type) "ident")
|
|
(= (get (rb-p-peek 1) :type) "dot"))
|
|
(do
|
|
(set! recv {:type "lvar" :name (rb-p-val)})
|
|
(rb-p-advance!)
|
|
(rb-p-advance!)
|
|
(set! name (rb-p-val))
|
|
(rb-p-advance!)))
|
|
(:else
|
|
(do
|
|
(set! name (rb-p-val))
|
|
(rb-p-advance!))))
|
|
(let ((params (list)))
|
|
(cond
|
|
((= (rb-p-type) "lparen")
|
|
(do
|
|
(rb-p-advance!)
|
|
(rb-p-skip-newlines!)
|
|
(set! params (rb-p-parse-def-params))
|
|
(rb-p-expect! "rparen")))
|
|
((not (or (rb-p-sep?) (= (rb-p-type) "eof")))
|
|
(set! params (rb-p-parse-def-params)))
|
|
(:else nil))
|
|
(rb-p-skip-seps!)
|
|
(let ((body (rb-p-parse-stmts (list "end"))))
|
|
(rb-p-expect-kw! "end")
|
|
{:type "method-def" :recv recv :name name
|
|
:params params :body body})))))
|
|
|
|
;; class [<<obj | Name [<Super]] body end
|
|
(define rb-p-parse-class
|
|
(fn ()
|
|
(rb-p-advance!)
|
|
(if (and (= (rb-p-type) "op") (= (rb-p-val) "<<"))
|
|
(do
|
|
(rb-p-advance!)
|
|
(let ((obj (rb-p-parse-primary)))
|
|
(rb-p-skip-seps!)
|
|
(let ((body (rb-p-parse-stmts (list "end"))))
|
|
(rb-p-expect-kw! "end")
|
|
{:type "sclass" :obj obj :body body})))
|
|
(let ((name (rb-p-parse-const-path)))
|
|
(let ((super nil))
|
|
(when (and (= (rb-p-type) "op") (= (rb-p-val) "<"))
|
|
(do
|
|
(rb-p-advance!)
|
|
(set! super (rb-p-parse-const-path))))
|
|
(rb-p-skip-seps!)
|
|
(let ((body (rb-p-parse-stmts (list "end"))))
|
|
(rb-p-expect-kw! "end")
|
|
{:type "class-def" :name name :super super :body body}))))))
|
|
|
|
;; module Name body end
|
|
(define rb-p-parse-module
|
|
(fn ()
|
|
(rb-p-advance!)
|
|
(let ((name (rb-p-parse-const-path)))
|
|
(rb-p-skip-seps!)
|
|
(let ((body (rb-p-parse-stmts (list "end"))))
|
|
(rb-p-expect-kw! "end")
|
|
{:type "module-def" :name name :body body}))))
|
|
|
|
;; Const or Const::Const::...
|
|
(define rb-p-parse-const-path
|
|
(fn ()
|
|
(let ((node {:type "const" :name (rb-p-val)}))
|
|
(rb-p-advance!)
|
|
(define rb-p-cp-loop
|
|
(fn ()
|
|
(when (= (rb-p-type) "dcolon")
|
|
(do
|
|
(rb-p-advance!)
|
|
(let ((name (rb-p-val)))
|
|
(rb-p-advance!)
|
|
(set! node {:type "const-path" :left node :name name})
|
|
(rb-p-cp-loop))))))
|
|
(rb-p-cp-loop)
|
|
node)))
|
|
|
|
;; [e, *e, ...]
|
|
(define rb-p-parse-array
|
|
(fn ()
|
|
(rb-p-advance!)
|
|
(rb-p-skip-newlines!)
|
|
(let ((elems (list)))
|
|
(define rb-p-arr-loop
|
|
(fn ()
|
|
(when (not (or (= (rb-p-type) "rbracket") (= (rb-p-type) "eof")))
|
|
(do
|
|
(if (and (= (rb-p-type) "op") (= (rb-p-val) "*"))
|
|
(do
|
|
(rb-p-advance!)
|
|
(append! elems {:type "splat" :value (rb-p-parse-assign)}))
|
|
(append! elems (rb-p-parse-assign)))
|
|
(rb-p-skip-newlines!)
|
|
(when (= (rb-p-type) "comma")
|
|
(do (rb-p-advance!) (rb-p-skip-newlines!)))
|
|
(rb-p-arr-loop)))))
|
|
(rb-p-arr-loop)
|
|
(rb-p-expect! "rbracket")
|
|
{:type "array" :elems elems})))
|
|
|
|
;; {k: v, k => v, ...}
|
|
(define rb-p-parse-hash
|
|
(fn ()
|
|
(rb-p-advance!)
|
|
(rb-p-skip-newlines!)
|
|
(let ((pairs (list)))
|
|
(define rb-p-hash-loop
|
|
(fn ()
|
|
(when (not (or (= (rb-p-type) "rbrace") (= (rb-p-type) "eof")))
|
|
(do
|
|
(let ((key nil) (val nil) (style nil))
|
|
(cond
|
|
((and (or (= (rb-p-type) "ident") (= (rb-p-type) "const"))
|
|
(= (get (rb-p-peek 1) :type) "colon"))
|
|
(do
|
|
(set! key {:type "lit-sym" :value (rb-p-val)})
|
|
(set! style "colon")
|
|
(rb-p-advance!)
|
|
(rb-p-advance!)))
|
|
(:else
|
|
(do
|
|
(set! key (rb-p-parse-assign))
|
|
(set! style "rocket")
|
|
(when (and (= (rb-p-type) "op") (= (rb-p-val) "=>"))
|
|
(rb-p-advance!)))))
|
|
(rb-p-skip-newlines!)
|
|
(set! val (rb-p-parse-assign))
|
|
(append! pairs {:key key :val val :style style}))
|
|
(rb-p-skip-newlines!)
|
|
(when (= (rb-p-type) "comma")
|
|
(do (rb-p-advance!) (rb-p-skip-newlines!)))
|
|
(rb-p-hash-loop)))))
|
|
(rb-p-hash-loop)
|
|
(rb-p-expect! "rbrace")
|
|
{:type "hash" :pairs pairs})))
|
|
|
|
;; (a, *b, **c, &d)
|
|
(define rb-p-parse-args-parens
|
|
(fn ()
|
|
(rb-p-advance!)
|
|
(rb-p-skip-newlines!)
|
|
(let ((args (list)))
|
|
(define rb-p-ap-loop
|
|
(fn ()
|
|
(when (not (or (= (rb-p-type) "rparen") (= (rb-p-type) "eof")))
|
|
(do
|
|
(cond
|
|
((and (= (rb-p-type) "op") (= (rb-p-val) "**"))
|
|
(do (rb-p-advance!)
|
|
(append! args {:type "dsplat" :value (rb-p-parse-assign)})))
|
|
((and (= (rb-p-type) "op") (= (rb-p-val) "*"))
|
|
(do (rb-p-advance!)
|
|
(append! args {:type "splat" :value (rb-p-parse-assign)})))
|
|
((and (= (rb-p-type) "op") (= (rb-p-val) "&"))
|
|
(do (rb-p-advance!)
|
|
(append! args {:type "block-pass" :value (rb-p-parse-assign)})))
|
|
(:else (append! args (rb-p-parse-assign))))
|
|
(rb-p-skip-newlines!)
|
|
(when (= (rb-p-type) "comma")
|
|
(do (rb-p-advance!) (rb-p-skip-newlines!)))
|
|
(rb-p-ap-loop)))))
|
|
(rb-p-ap-loop)
|
|
(rb-p-expect! "rparen")
|
|
args)))
|
|
|
|
;; No-paren arg list up to sep/end-keyword
|
|
(define rb-p-parse-args-bare
|
|
(fn ()
|
|
(let ((args (list)) (going true))
|
|
(define rb-p-ab-loop
|
|
(fn ()
|
|
(when (and going
|
|
(not (rb-p-sep?))
|
|
(not (= (rb-p-type) "eof"))
|
|
(not (= (rb-p-type) "rparen"))
|
|
(not (= (rb-p-type) "rbracket"))
|
|
(not (= (rb-p-type) "rbrace"))
|
|
(not (and (= (rb-p-type) "keyword")
|
|
(contains? (list "end" "else" "elsif" "when"
|
|
"rescue" "ensure" "then" "do")
|
|
(rb-p-val)))))
|
|
(do
|
|
(cond
|
|
((and (= (rb-p-type) "op") (= (rb-p-val) "*"))
|
|
(do (rb-p-advance!)
|
|
(append! args {:type "splat" :value (rb-p-parse-assign)})))
|
|
((and (= (rb-p-type) "op") (= (rb-p-val) "**"))
|
|
(do (rb-p-advance!)
|
|
(append! args {:type "dsplat" :value (rb-p-parse-assign)})))
|
|
((and (= (rb-p-type) "op") (= (rb-p-val) "&"))
|
|
(do (rb-p-advance!)
|
|
(append! args {:type "block-pass" :value (rb-p-parse-assign)})))
|
|
(:else (append! args (rb-p-parse-assign))))
|
|
(if (= (rb-p-type) "comma")
|
|
(do (rb-p-advance!) (rb-p-skip-newlines!) (rb-p-ab-loop))
|
|
(set! going false))))))
|
|
(rb-p-ab-loop)
|
|
args)))
|
|
|
|
;; Primary expression
|
|
(define rb-p-parse-primary
|
|
(fn ()
|
|
(cond
|
|
((= (rb-p-type) "int")
|
|
(let ((v (rb-p-val))) (rb-p-advance!) {:type "lit-int" :value v}))
|
|
((= (rb-p-type) "float")
|
|
(let ((v (rb-p-val))) (rb-p-advance!) {:type "lit-float" :value v}))
|
|
((= (rb-p-type) "string")
|
|
(let ((v (rb-p-val))) (rb-p-advance!) {:type "lit-str" :value v}))
|
|
((= (rb-p-type) "symbol")
|
|
(let ((v (rb-p-val))) (rb-p-advance!) {:type "lit-sym" :value v}))
|
|
((= (rb-p-type) "words")
|
|
(let ((v (rb-p-val))) (rb-p-advance!) {:type "lit-words" :elems v}))
|
|
((= (rb-p-type) "isymbols")
|
|
(let ((v (rb-p-val))) (rb-p-advance!) {:type "lit-isyms" :elems v}))
|
|
((= (rb-p-type) "ivar")
|
|
(let ((v (rb-p-val))) (rb-p-advance!) {:type "ivar" :name v}))
|
|
((= (rb-p-type) "cvar")
|
|
(let ((v (rb-p-val))) (rb-p-advance!) {:type "cvar" :name v}))
|
|
((= (rb-p-type) "gvar")
|
|
(let ((v (rb-p-val))) (rb-p-advance!) {:type "gvar" :name v}))
|
|
((= (rb-p-type) "const")
|
|
(rb-p-parse-const-path))
|
|
((= (rb-p-type) "ident")
|
|
(let ((name (rb-p-val)))
|
|
(rb-p-advance!)
|
|
(if (= (rb-p-type) "lparen")
|
|
(let ((args (rb-p-parse-args-parens))
|
|
(blk (rb-p-parse-block)))
|
|
{:type "send" :name name :args args :block blk})
|
|
{:type "send" :name name :args (list) :block nil})))
|
|
((= (rb-p-type) "keyword")
|
|
(cond
|
|
((= (rb-p-val) "nil")
|
|
(do (rb-p-advance!) {:type "lit-nil"}))
|
|
((= (rb-p-val) "true")
|
|
(do (rb-p-advance!) {:type "lit-bool" :value true}))
|
|
((= (rb-p-val) "false")
|
|
(do (rb-p-advance!) {:type "lit-bool" :value false}))
|
|
((= (rb-p-val) "self")
|
|
(do (rb-p-advance!) {:type "self"}))
|
|
((= (rb-p-val) "super")
|
|
(do
|
|
(rb-p-advance!)
|
|
(let ((args (if (= (rb-p-type) "lparen")
|
|
(rb-p-parse-args-parens) (list)))
|
|
(blk (rb-p-parse-block)))
|
|
{:type "send" :name "super" :args args :block blk})))
|
|
(:else
|
|
{:type "error"
|
|
:msg (join "" (list "unexpected kw " (rb-p-val)))})))
|
|
((= (rb-p-type) "lbracket")
|
|
(rb-p-parse-array))
|
|
((= (rb-p-type) "lbrace")
|
|
(rb-p-parse-hash))
|
|
((= (rb-p-type) "lparen")
|
|
(do
|
|
(rb-p-advance!)
|
|
(rb-p-skip-seps!)
|
|
(let ((node (rb-p-parse-expr)))
|
|
(rb-p-skip-seps!)
|
|
(rb-p-expect! "rparen")
|
|
node)))
|
|
(:else
|
|
(do
|
|
(rb-p-advance!)
|
|
{:type "error"
|
|
:msg (join "" (list "unexpected " (rb-p-type)
|
|
" '" (or (rb-p-val) "") "'"))})))))
|
|
|
|
;; .method ::Const [index] chains
|
|
(define rb-p-parse-postfix
|
|
(fn ()
|
|
(let ((node (rb-p-parse-primary)))
|
|
(define rb-p-pf-loop
|
|
(fn ()
|
|
(cond
|
|
((= (rb-p-type) "dot")
|
|
(do
|
|
(rb-p-advance!)
|
|
(let ((method (rb-p-val)))
|
|
(rb-p-advance!)
|
|
(let ((args (if (= (rb-p-type) "lparen")
|
|
(rb-p-parse-args-parens) (list)))
|
|
(blk (rb-p-parse-block)))
|
|
(set! node {:type "call" :recv node :method method
|
|
:args args :block blk})
|
|
(rb-p-pf-loop)))))
|
|
((= (rb-p-type) "dcolon")
|
|
(do
|
|
(rb-p-advance!)
|
|
(let ((name (rb-p-val)))
|
|
(rb-p-advance!)
|
|
(if (= (rb-p-type) "lparen")
|
|
(let ((args (rb-p-parse-args-parens))
|
|
(blk (rb-p-parse-block)))
|
|
(set! node {:type "call" :recv node :method name
|
|
:args args :block blk}))
|
|
(set! node {:type "const-path" :left node :name name}))
|
|
(rb-p-pf-loop))))
|
|
((= (rb-p-type) "lbracket")
|
|
(do
|
|
(rb-p-advance!)
|
|
(rb-p-skip-newlines!)
|
|
(let ((idxargs (list)))
|
|
(define rb-p-idx-loop
|
|
(fn ()
|
|
(when (not (or (= (rb-p-type) "rbracket") (= (rb-p-type) "eof")))
|
|
(do
|
|
(append! idxargs (rb-p-parse-assign))
|
|
(when (= (rb-p-type) "comma")
|
|
(do (rb-p-advance!) (rb-p-skip-newlines!)))
|
|
(rb-p-idx-loop)))))
|
|
(rb-p-idx-loop)
|
|
(rb-p-expect! "rbracket")
|
|
(set! node {:type "index" :recv node :args idxargs})
|
|
(rb-p-pf-loop))))
|
|
(:else nil))))
|
|
(rb-p-pf-loop)
|
|
node)))
|
|
|
|
(define rb-p-parse-unary
|
|
(fn ()
|
|
(cond
|
|
((and (= (rb-p-type) "op") (= (rb-p-val) "!"))
|
|
(do (rb-p-advance!)
|
|
{:type "unop" :op "!" :value (rb-p-parse-unary)}))
|
|
((and (= (rb-p-type) "op") (= (rb-p-val) "~"))
|
|
(do (rb-p-advance!)
|
|
{:type "unop" :op "~" :value (rb-p-parse-unary)}))
|
|
((and (= (rb-p-type) "op") (= (rb-p-val) "-"))
|
|
(do (rb-p-advance!)
|
|
{:type "unop" :op "-" :value (rb-p-parse-unary)}))
|
|
((and (= (rb-p-type) "op") (= (rb-p-val) "+"))
|
|
(do (rb-p-advance!) (rb-p-parse-unary)))
|
|
(:else (rb-p-parse-postfix)))))
|
|
|
|
(define rb-p-parse-power
|
|
(fn ()
|
|
(let ((node (rb-p-parse-unary)))
|
|
(if (and (= (rb-p-type) "op") (= (rb-p-val) "**"))
|
|
(do (rb-p-advance!)
|
|
{:type "binop" :op "**" :left node :right (rb-p-parse-power)})
|
|
node))))
|
|
|
|
(define rb-p-parse-mul
|
|
(fn ()
|
|
(let ((node (rb-p-parse-power)))
|
|
(define rb-p-mul-loop
|
|
(fn ()
|
|
(if (and (= (rb-p-type) "op")
|
|
(or (= (rb-p-val) "*") (= (rb-p-val) "/") (= (rb-p-val) "%")))
|
|
(let ((op (rb-p-val)))
|
|
(rb-p-advance!)
|
|
(set! node {:type "binop" :op op :left node :right (rb-p-parse-power)})
|
|
(rb-p-mul-loop))
|
|
node)))
|
|
(rb-p-mul-loop))))
|
|
|
|
(define rb-p-parse-add
|
|
(fn ()
|
|
(let ((node (rb-p-parse-mul)))
|
|
(define rb-p-add-loop
|
|
(fn ()
|
|
(if (and (= (rb-p-type) "op")
|
|
(or (= (rb-p-val) "+") (= (rb-p-val) "-")))
|
|
(let ((op (rb-p-val)))
|
|
(rb-p-advance!)
|
|
(set! node {:type "binop" :op op :left node :right (rb-p-parse-mul)})
|
|
(rb-p-add-loop))
|
|
node)))
|
|
(rb-p-add-loop))))
|
|
|
|
(define rb-p-parse-shift
|
|
(fn ()
|
|
(let ((node (rb-p-parse-add)))
|
|
(define rb-p-sh-loop
|
|
(fn ()
|
|
(if (and (= (rb-p-type) "op")
|
|
(or (= (rb-p-val) "<<") (= (rb-p-val) ">>")))
|
|
(let ((op (rb-p-val)))
|
|
(rb-p-advance!)
|
|
(set! node {:type "binop" :op op :left node :right (rb-p-parse-add)})
|
|
(rb-p-sh-loop))
|
|
node)))
|
|
(rb-p-sh-loop))))
|
|
|
|
(define rb-p-parse-bitand
|
|
(fn ()
|
|
(let ((node (rb-p-parse-shift)))
|
|
(define rb-p-ba-loop
|
|
(fn ()
|
|
(if (and (= (rb-p-type) "op") (= (rb-p-val) "&"))
|
|
(do
|
|
(rb-p-advance!)
|
|
(set! node {:type "binop" :op "&" :left node :right (rb-p-parse-shift)})
|
|
(rb-p-ba-loop))
|
|
node)))
|
|
(rb-p-ba-loop))))
|
|
|
|
;; | is "pipe" token (not "op")
|
|
(define rb-p-parse-bitor
|
|
(fn ()
|
|
(let ((node (rb-p-parse-bitand)))
|
|
(define rb-p-bo-loop
|
|
(fn ()
|
|
(cond
|
|
((= (rb-p-type) "pipe")
|
|
(do
|
|
(rb-p-advance!)
|
|
(set! node {:type "binop" :op "|" :left node :right (rb-p-parse-bitand)})
|
|
(rb-p-bo-loop)))
|
|
((and (= (rb-p-type) "op") (= (rb-p-val) "^"))
|
|
(do
|
|
(rb-p-advance!)
|
|
(set! node {:type "binop" :op "^" :left node :right (rb-p-parse-bitand)})
|
|
(rb-p-bo-loop)))
|
|
(:else node))))
|
|
(rb-p-bo-loop))))
|
|
|
|
(define rb-p-parse-comparison
|
|
(fn ()
|
|
(let ((node (rb-p-parse-bitor)))
|
|
(if (and (= (rb-p-type) "op")
|
|
(contains? (list "==" "!=" "<" ">" "<=" ">="
|
|
"<=>" "===" "=~" "!~") (rb-p-val)))
|
|
(let ((op (rb-p-val)))
|
|
(rb-p-advance!)
|
|
{:type "binop" :op op :left node :right (rb-p-parse-bitor)})
|
|
node))))
|
|
|
|
(define rb-p-parse-not
|
|
(fn ()
|
|
(if (and (= (rb-p-type) "keyword") (= (rb-p-val) "not"))
|
|
(do (rb-p-advance!)
|
|
{:type "not" :value (rb-p-parse-not)})
|
|
(rb-p-parse-comparison))))
|
|
|
|
(define rb-p-parse-and
|
|
(fn ()
|
|
(let ((node (rb-p-parse-not)))
|
|
(define rb-p-and-loop
|
|
(fn ()
|
|
(cond
|
|
((and (= (rb-p-type) "op") (= (rb-p-val) "&&"))
|
|
(do
|
|
(rb-p-advance!)
|
|
(set! node {:type "binop" :op "&&" :left node :right (rb-p-parse-not)})
|
|
(rb-p-and-loop)))
|
|
((and (= (rb-p-type) "keyword") (= (rb-p-val) "and"))
|
|
(do
|
|
(rb-p-advance!)
|
|
(set! node {:type "binop" :op "and" :left node :right (rb-p-parse-not)})
|
|
(rb-p-and-loop)))
|
|
(:else node))))
|
|
(rb-p-and-loop))))
|
|
|
|
(define rb-p-parse-or
|
|
(fn ()
|
|
(let ((node (rb-p-parse-and)))
|
|
(define rb-p-or-loop
|
|
(fn ()
|
|
(cond
|
|
((and (= (rb-p-type) "op") (= (rb-p-val) "||"))
|
|
(do
|
|
(rb-p-advance!)
|
|
(set! node {:type "binop" :op "||" :left node :right (rb-p-parse-and)})
|
|
(rb-p-or-loop)))
|
|
((and (= (rb-p-type) "keyword") (= (rb-p-val) "or"))
|
|
(do
|
|
(rb-p-advance!)
|
|
(set! node {:type "binop" :op "or" :left node :right (rb-p-parse-and)})
|
|
(rb-p-or-loop)))
|
|
(:else node))))
|
|
(rb-p-or-loop))))
|
|
|
|
(define rb-p-parse-range
|
|
(fn ()
|
|
(let ((node (rb-p-parse-or)))
|
|
(cond
|
|
((= (rb-p-type) "dotdot")
|
|
(do (rb-p-advance!)
|
|
{:type "range" :from node :to (rb-p-parse-or) :exclusive false}))
|
|
((= (rb-p-type) "dotdotdot")
|
|
(do (rb-p-advance!)
|
|
{:type "range" :from node :to (rb-p-parse-or) :exclusive true}))
|
|
(:else node)))))
|
|
|
|
(define rb-p-parse-assign
|
|
(fn ()
|
|
(let ((node (rb-p-parse-range)))
|
|
(cond
|
|
((and (= (rb-p-type) "op") (= (rb-p-val) "="))
|
|
(do (rb-p-advance!)
|
|
{:type "assign" :target node :value (rb-p-parse-assign)}))
|
|
((and (= (rb-p-type) "op")
|
|
(contains? (list "+=" "-=" "*=" "/=" "%=" "**="
|
|
"<<=" ">>=" "&=" "|=" "^=" "&&=" "||=")
|
|
(rb-p-val)))
|
|
(let ((op (substring (rb-p-val) 0 (- (len (rb-p-val)) 1))))
|
|
(rb-p-advance!)
|
|
{:type "op-assign" :target node :op op :value (rb-p-parse-assign)}))
|
|
(:else node)))))
|
|
|
|
(define rb-p-parse-expr
|
|
(fn () (rb-p-parse-assign)))
|
|
|
|
;; e, e, ... → single node or array
|
|
(define rb-p-parse-multi-val
|
|
(fn ()
|
|
(let ((vals (list)))
|
|
(define rb-p-mv-loop
|
|
(fn ()
|
|
(append! vals (rb-p-parse-assign))
|
|
(when (= (rb-p-type) "comma")
|
|
(do (rb-p-advance!) (rb-p-skip-newlines!) (rb-p-mv-loop)))))
|
|
(rb-p-mv-loop)
|
|
(if (= (len vals) 1)
|
|
(nth vals 0)
|
|
{:type "array" :elems vals}))))
|
|
|
|
;; a, b, *c = rhs
|
|
(define rb-p-parse-massign
|
|
(fn ()
|
|
(let ((targets (list)))
|
|
(define rb-p-ma-loop
|
|
(fn ()
|
|
(cond
|
|
((and (= (rb-p-type) "op") (= (rb-p-val) "*"))
|
|
(do
|
|
(rb-p-advance!)
|
|
(if (= (rb-p-type) "ident")
|
|
(do
|
|
(append! targets {:type "splat-target" :name (rb-p-val)})
|
|
(rb-p-advance!))
|
|
(append! targets {:type "splat-target" :name nil}))))
|
|
((= (rb-p-type) "ident")
|
|
(do (append! targets {:type "lvar" :name (rb-p-val)}) (rb-p-advance!)))
|
|
((= (rb-p-type) "ivar")
|
|
(do (append! targets {:type "ivar" :name (rb-p-val)}) (rb-p-advance!)))
|
|
((= (rb-p-type) "cvar")
|
|
(do (append! targets {:type "cvar" :name (rb-p-val)}) (rb-p-advance!)))
|
|
((= (rb-p-type) "gvar")
|
|
(do (append! targets {:type "gvar" :name (rb-p-val)}) (rb-p-advance!)))
|
|
((= (rb-p-type) "const")
|
|
(do (append! targets {:type "const" :name (rb-p-val)}) (rb-p-advance!)))
|
|
(:else nil))
|
|
(when (= (rb-p-type) "comma")
|
|
(do (rb-p-advance!) (rb-p-skip-newlines!) (rb-p-ma-loop)))))
|
|
(rb-p-ma-loop)
|
|
(rb-p-advance!)
|
|
{:type "massign" :targets targets :value (rb-p-parse-multi-val)})))
|
|
|
|
(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
|
|
(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))))))
|
|
|
|
(define rb-p-parse-stmts
|
|
(fn (terminators)
|
|
(let ((stmts (list)))
|
|
(define rb-p-at-term?
|
|
(fn ()
|
|
(or (= (rb-p-type) "eof")
|
|
(and (= (rb-p-type) "keyword")
|
|
(contains? terminators (rb-p-val)))
|
|
(and (= (rb-p-type) "rbrace")
|
|
(contains? terminators "rbrace")))))
|
|
(define rb-p-ps-loop
|
|
(fn ()
|
|
(rb-p-skip-seps!)
|
|
(when (not (rb-p-at-term?))
|
|
(do
|
|
(append! stmts (rb-p-parse-stmt))
|
|
(rb-p-skip-seps!)
|
|
(rb-p-ps-loop)))))
|
|
(rb-p-ps-loop)
|
|
stmts)))
|
|
|
|
{:type "program" :stmts (rb-p-parse-stmts (list))})))
|
|
|
|
(define rb-parse-str
|
|
(fn (src) (rb-parse (rb-tokenize src))))
|