aboutsummaryrefslogtreecommitdiff
path: root/src/lang/lexer.clj
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/lang/lexer.clj60
1 files changed, 60 insertions, 0 deletions
diff --git a/src/lang/lexer.clj b/src/lang/lexer.clj
new file mode 100644
index 000000000..191686cb8
--- /dev/null
+++ b/src/lang/lexer.clj
@@ -0,0 +1,60 @@
+(ns lang.lexer
+ (:require [clojure.core.match :refer [match]]
+ [lang.util :as &util :refer [exec return* return fail fail*
+ repeat-m try-m try-all-m]]))
+
+(declare lex-form)
+
+;; [Utils]
+(defn ^:private lex-regex [regex]
+ (fn [text]
+ (if-let [[match] (re-find regex text)]
+ (return* (.substring text (.length match)) match)
+ (fail* "Pattern failed."))))
+
+(defn ^:private lex-str [str]
+ (fn [text]
+ (if (.startsWith text str)
+ (return* (.substring text (.length str)) str)
+ (fail* "String failed."))))
+
+;; [Lexers]
+(def ^:private lex-white-space (lex-regex #"^(\s+)"))
+
+(def ^:private lex-list
+ (exec [_ (lex-str "(")
+ members (repeat-m lex-form)
+ _ (lex-str ")")]
+ (return [::list members])))
+
+(def ^:private lex-ident
+ (exec [token (lex-regex #"^([a-zA-Z!@$%^&*<>\.,/\\\|][a-zA-Z0-9!@$%^&*<>\.,/\\\|]*)")]
+ (return [::ident token])))
+
+(def ^:private lex-int
+ (exec [token (lex-regex #"^([1-9][0-9]*)")]
+ (return [::int token])))
+
+(def ^:private lex-form
+ (exec [_ (try-m lex-white-space)
+ form (try-all-m [lex-list
+ lex-ident
+ lex-int
+ ])
+ _ (try-m lex-white-space)]
+ (return form)))
+
+;; [Interface]
+(defn lex [text]
+ (match ((exec [_ (try-m lex-white-space)
+ form lex-list
+ _ (try-m lex-white-space)]
+ (return form))
+ text)
+ [::&util/ok [?state ?datum]]
+ (if (empty? ?state)
+ ?datum
+ (assert false (str "Unconsumed input: " ?state)))
+
+ [::&util/failure ?message]
+ (assert false ?message)))