(defn ->kw [kw-str] (keyword (slice kw-str 1))) (defn word [str] [:word str]) (defn num [num] [:number (scan-number num)]) (defn kw [_ kw] [:keyword (->kw kw)]) (defn str [str] [:string str]) (defn bool [bool] (if (= bool "true") [:boolean true] [:boolean false])) (defn nill [_] [:nil nil]) (defn not-empty? [x] (not (empty? x))) (defn ifl [args] [:if ;args]) (defn letl [args] [:let ;args]) (defn block [args] [:block args]) (def errors @[]) (defn ? [x default] (if (nil? x) default x)) (defn panic [& args] (def info (filter |(not= "" $) args)) (def [msg line col source] info) (def error {:msg msg :line line :col col :source (? source "")}) (array/push errors error) [:error error] ) (do (def ludus-grammar ~{:nil (cmt (<- "nil") ,nill) :true (cmt (<- "true") ,bool) :false (cmt (<- "false") ,bool) :comment (* "&" (any (if-not (+ "\n" -1) 1))) :wordchars (+ :w (set "_-/*?!")) :reserved (+ "if" "then" "else" "let") :word (cmt (<- (if-not :reserved (* (range "az") (any :wordchars)))) ,word) :keyword (cmt (<- (* ":" :word)) ,kw) :hex (range "09" "af" "AF") :escape (* "\\" (+ (set `"'0?\abefnrtvz`) (* "x" :hex :hex) (* "u" [4 :hex]) (* "U" [6 :hex]) (error (constant "bad escape")))) :string (cmt (* "\"" (<- (any (+ :escape (if-not "\"" 1)))) "\"") ,str) :int (* (? "-") (range "19") (any (+ "_" (range "09")))) :float (* (? "-") (+ :int "0") "." (any (+ "_" (range "09")))) :zero "0" :number (cmt (<- (+ :float :int :zero)) ,num) :comment (* "&" (any (if-not (+ "\n" -1) 1))) :atom (+ :nil :true :false :keyword :number :word) :newline "\n" :terminator (* (any :ws) (some (+ :newline ";"))) :separator (* (any :ws) (some (+ :newline ","))) :ws (any (+ :comment (set " \t"))) :simple (+ :atom :string :word (* (constant "expected simple" :err-msg) :panic)) :then (+ (* (+ (some :ws) (any :newline)) (any :ws) "then" (some :ws)) (* (constant "expected then" :err-msg) :panic)) :else (+ (* (+ (some :ws) (any :newline)) (any :ws) "else" (some :ws)) (* (constant "expected else" :err-msg) :panic)) :panic (cmt (* (<- (-> :err-msg)) (<- (line)) (<- (column)) (<- (* (any (if-not (+ "\n" -1) 1))))) ,panic) :non-binding (+ :atom :word :string :if :block (* (constant "expected non-binding" :err-msg) :panic)) :tuple-term (* :number (some :separator)) :tuple (* "(" (any :separator) (any :tuple-term) ")") :block (cmt (group (* "{" (some :line) (? :expression) "}")) ,block) :if (cmt (group (* "if" (some :ws) :simple :then :non-binding :else :non-binding)) ,ifl) :pattern (+ :atom :string (* (constant "expected pattern") :panic)) :equals (* (any :ws) "=" (any :ws)) :let (cmt (group (* "let" (some :ws) :pattern :equals :non-binding)) ,letl) :expression (+ :atom :string :block :if :let (* (constant "expected expression" :err-msg) :panic)) :empty (* :ws :terminator) :line (* :ws :expression :ws :terminator) :main :tuple}) (def simple-tuple ~{ :value (<- (range "az")) :separator (+ "," "\n") :ws (any (set " \t")) :term (* :ws :value :ws :separator) :tuple (group (* "(" (any :term) (? :value) :ws ")")) :main :tuple }) # :empty/:line/:block are still giving me grief # also, somehow line & column numbers ar (def source `` ( a, b ,c,d ) `` ) (peg/match simple-tuple source) )