Files
rose-ash/lib/erlang/parser.sx
giles 99753580b4 Recover agent-loop progress: lua/prolog/forth/erlang/haskell phases 1-2
Salvaged from worktree-agent-* branches killed during sx-tree MCP outage:
- lua: tokenizer + parser + phase-2 transpile (~157 tests)
- prolog: tokenizer + parser + unification (72 tests, plan update lost to WIP)
- forth: phase-1 reader/interpreter + phase-2 colon/VARIABLE (134 tests)
- erlang: tokenizer + parser (114 tests)
- haskell: tokenizer + parse tests (43 tests)

Cherry-picked file contents only, not branch history, to avoid pulling in
unrelated ocaml-vm merge commits that were in those branches' bases.
2026-04-24 16:03:00 +00:00

112 lines
3.4 KiB
Plaintext

;; Erlang parser — turns a token list into an AST.
;;
;; Shared state lives in the surrounding `let` of `er-parse-*`.
;; All helpers use recursion (no `while` in SX).
;;
;; AST node shapes:
;; {:type "atom" :value "foo"}
;; {:type "integer" :value "42"} ; value kept as string
;; {:type "float" :value "3.14"}
;; {:type "string" :value "hi"}
;; {:type "var" :name "X"} ; "_" is wildcard
;; {:type "nil"}
;; {:type "tuple" :elements [...]}
;; {:type "cons" :head E :tail E}
;; {:type "call" :fun E :args [...]}
;; {:type "remote" :mod E :fun E}
;; {:type "op" :op OP :args [L R]}
;; {:type "unop" :op OP :arg E}
;; {:type "match" :lhs P :rhs E}
;; {:type "send" :to E :msg E}
;; {:type "if" :clauses [{:guards [...] :body [...]} ...]}
;; {:type "case" :expr E :clauses [{:pattern P :guards [...] :body [...]} ...]}
;; {:type "receive" :clauses [...] :after-ms E-or-nil :after-body [...]}
;; {:type "fun" :clauses [...]}
;; {:type "block" :exprs [...]}
;; {:type "try" :exprs [...] :of-clauses [...] :catch-clauses [...] :after [...]}
;; Top-level: {:type "module" :name A :attrs [{:name A :args [...]} ...] :functions [...]}
;; {:type "function" :name A :arity N :clauses [{:name :patterns :guards :body}]}
(define
er-is-binop?
(fn
(tok prec)
(let
((ty (get tok :type)) (v (get tok :value)))
(cond
(= prec 0)
(and (= ty "op") (= v "="))
(= prec 1)
(and (= ty "op") (= v "!"))
(= prec 2)
(or
(and (= ty "keyword") (= v "orelse"))
(and (= ty "keyword") (= v "or"))
(and (= ty "keyword") (= v "xor")))
(= prec 3)
(or
(and (= ty "keyword") (= v "andalso"))
(and (= ty "keyword") (= v "and")))
(= prec 4)
(and
(= ty "op")
(or
(= v "==")
(= v "/=")
(= v "=:=")
(= v "=/=")
(= v "<")
(= v ">")
(= v "=<")
(= v ">=")))
(= prec 5)
(and (= ty "op") (or (= v "++") (= v "--")))
(= prec 6)
(and (= ty "op") (or (= v "+") (= v "-")))
(= prec 7)
(or
(and (= ty "op") (or (= v "*") (= v "/")))
(and
(= ty "keyword")
(or
(= v "div")
(= v "rem")
(= v "band")
(= v "bor")
(= v "bxor")
(= v "bsl")
(= v "bsr"))))
:else false))))
(define
er-any-binop?
(fn
(tok min-prec)
(or
(and (>= 0 min-prec) (er-is-binop? tok 0))
(and (>= 1 min-prec) (er-is-binop? tok 1))
(and (>= 2 min-prec) (er-is-binop? tok 2))
(and (>= 3 min-prec) (er-is-binop? tok 3))
(and (>= 4 min-prec) (er-is-binop? tok 4))
(and (>= 5 min-prec) (er-is-binop? tok 5))
(and (>= 6 min-prec) (er-is-binop? tok 6))
(and (>= 7 min-prec) (er-is-binop? tok 7)))))
(define
er-slice-list
(fn
(xs from)
(if
(>= from (len xs))
(list)
(let
((out (list)))
(for-each
(fn (i) (append! out (nth xs i)))
(range from (len xs)))
out))))
(define
er-build-cons
(fn (elems tail) (if (= (len elems) 0) tail {:head (nth elems 0) :tail (er-build-cons (er-slice-list elems 1) tail) :type "cons"})))