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.
112 lines
3.4 KiB
Plaintext
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"})))
|