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.
114 lines
3.0 KiB
Plaintext
114 lines
3.0 KiB
Plaintext
;; Erlang module parser — reads top-level forms and builds a module AST.
|
|
;;
|
|
;; Depends on parser-core.sx, parser.sx, parser-expr.sx.
|
|
|
|
(define
|
|
er-parse-module
|
|
(fn
|
|
(src)
|
|
(let
|
|
((st (er-state-make (er-tokenize src)))
|
|
(mod-ref (list nil))
|
|
(attrs (list))
|
|
(functions (list)))
|
|
(er-parse-module-loop st mod-ref attrs functions)
|
|
{:functions functions :type "module" :attrs attrs :name (nth mod-ref 0)})))
|
|
|
|
(define
|
|
er-parse-module-loop
|
|
(fn
|
|
(st mod-ref attrs functions)
|
|
(when
|
|
(not (er-at-eof? st))
|
|
(er-parse-top-form st mod-ref attrs functions)
|
|
(er-parse-module-loop st mod-ref attrs functions))))
|
|
|
|
(define
|
|
er-parse-top-form
|
|
(fn
|
|
(st mod-ref attrs functions)
|
|
(cond
|
|
(er-is? st "op" "-")
|
|
(do
|
|
(er-advance! st)
|
|
(let
|
|
((attr-name (er-cur-value st)))
|
|
(er-advance! st)
|
|
(let
|
|
((args (er-parse-attr-args st)))
|
|
(er-expect! st "punct" ".")
|
|
(cond
|
|
(= attr-name "module")
|
|
(set-nth! mod-ref 0 (get (nth args 0) :value))
|
|
:else (append! attrs {:args args :name attr-name})))))
|
|
(= (er-cur-type st) "atom")
|
|
(append! functions (er-parse-function st))
|
|
:else (error
|
|
(str
|
|
"Erlang parse (top): unexpected "
|
|
(er-cur-type st)
|
|
" '"
|
|
(er-cur-value st)
|
|
"' at pos "
|
|
(get (er-cur st) :pos))))))
|
|
|
|
(define
|
|
er-parse-attr-args
|
|
(fn
|
|
(st)
|
|
(er-expect! st "punct" "(")
|
|
(if
|
|
(er-is? st "punct" ")")
|
|
(do (er-advance! st) (list))
|
|
(let
|
|
((args (list (er-parse-attr-arg st))))
|
|
(er-parse-attr-args-tail st args)))))
|
|
|
|
(define
|
|
er-parse-attr-args-tail
|
|
(fn
|
|
(st args)
|
|
(cond
|
|
(er-is? st "punct" ",")
|
|
(do
|
|
(er-advance! st)
|
|
(append! args (er-parse-attr-arg st))
|
|
(er-parse-attr-args-tail st args))
|
|
(er-is? st "punct" ")")
|
|
(do (er-advance! st) args)
|
|
:else (error (str "Erlang parse attr: got '" (er-cur-value st) "'")))))
|
|
|
|
;; Attribute args often contain `Name/Arity` pairs — parse as a
|
|
;; general expression so the caller can interpret the shape.
|
|
(define er-parse-attr-arg (fn (st) (er-parse-expr-prec st 0)))
|
|
|
|
(define
|
|
er-parse-function
|
|
(fn
|
|
(st)
|
|
(let
|
|
((name (er-cur-value st)))
|
|
(er-advance! st)
|
|
(let
|
|
((clauses (list (er-parse-fun-clause st name))))
|
|
(er-parse-function-tail st name clauses)
|
|
(er-expect! st "punct" ".")
|
|
(let ((arity (len (get (nth clauses 0) :patterns)))) {:arity arity :clauses clauses :type "function" :name name})))))
|
|
|
|
(define
|
|
er-parse-function-tail
|
|
(fn
|
|
(st name clauses)
|
|
(when
|
|
(er-is? st "punct" ";")
|
|
(let
|
|
((save (get st :idx)))
|
|
(er-advance! st)
|
|
(if
|
|
(and (= (er-cur-type st) "atom") (= (er-cur-value st) name))
|
|
(do
|
|
(er-advance! st)
|
|
(append! clauses (er-parse-fun-clause st name))
|
|
(er-parse-function-tail st name clauses))
|
|
(dict-set! st :idx save))))))
|