;; 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"})))