diff options
Diffstat (limited to '')
-rw-r--r-- | src/lang/parser.clj | 76 |
1 files changed, 76 insertions, 0 deletions
diff --git a/src/lang/parser.clj b/src/lang/parser.clj new file mode 100644 index 000000000..289453999 --- /dev/null +++ b/src/lang/parser.clj @@ -0,0 +1,76 @@ +(ns lang.parser + (:require [clojure.core.match :refer [match]] + (lang [util :as &util :refer [exec return* return fail fail* + repeat-m try-m try-all-m map-m + apply-m]] + [lexer :as &lexer]))) + +(declare parse-form) + +;; [Utils] +(defmacro ^:private defparser [name match return] + `(def ~name + (fn [[token# & left#]] + (match token# + ~match + (~return left#) + _# + (fail* "Unmatched token."))))) + +;; [Parsers] +(defparser ^:private parse-ident + [::&lexer/ident ?ident] + (return [::ident ?ident])) + +(defparser ^:private parse-int + [::&lexer/int ?int] + (return [::int (Long/parseLong ?int)])) + +(defparser ^:private parse-def + [::&lexer/list ([[::&lexer/ident "def"] ?name ?body] :seq)] + (exec [=name (apply-m parse-form (list ?name)) + =body (apply-m parse-form (list ?body))] + (return [::def =name =body]))) + +(defparser ^:private parse-fn-call + [::&lexer/list ([?f & ?args] :seq)] + (exec [=f (apply-m parse-form (list ?f)) + =args (map-m (fn [arg] (apply-m parse-form (list arg))) + ?args)] + (return [::fn-call =f =args]))) + +(def ^:private parse-form + (try-all-m [parse-ident + parse-int + parse-def + parse-fn-call])) + +;; [Interface] +(defn parse [tokens] + (match (parse-form tokens) + [::&util/ok [?state ?datum]] + (if (empty? ?state) + ?datum + (assert false (str "Unconsumed input: " ?state))) + + [::&util/failure ?message] + (assert false ?message))) + +(comment + ((comp parse list &lexer/lex) (slurp "src/example/test1.lang")) + + (&lexer/lex (slurp "src/example/test1.lang")) + "\n(def (** base exp)\n (reduce * 1 (repeat exp base)))\n" + + [::list ([::ident "def"] + [::list ([::ident "**"] [::ident "base"] [::ident "exp"])] + [::list ([::ident "reduce"] + [::ident "*"] + [::int "1"] + [::list ([::ident "repeat"] + [::ident "exp"] + [::ident "base"])])])] + + (re-find #"^([a-zA-Z!@$%^&*<>\.,/\\\|][a-zA-Z0-9!@$%^&*<>\.,/\\\|]*)" "a9") + (re-find #"^([1-9][0-9]*)" "9") + ) |