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