diff --git a/janet/recursive.janet b/janet/recursive.janet index 5d38a2a..f01b433 100644 --- a/janet/recursive.janet +++ b/janet/recursive.janet @@ -31,7 +31,12 @@ }) ### and some helper functions for interfacing with that data structure -(defn- current [parser] (get (parser :tokens) (parser :current))) +(defn- current [parser] + (def curr (get (parser :tokens) (parser :current))) + (if (not curr) + (error "no more tokens") + curr)) + (defn- peek [parser] (get (parser :tokens) (inc (parser :current)))) @@ -39,46 +44,110 @@ (defn- type [token] (get token :type)) -(defn- expect [parser type] +(defn- check [parser type] (def current-type (-> parser current (get :type))) (= type current-type)) ### Parsing functions -(declare expr) +(declare nonbinding binding) + +# errors +(def terminators [:break :newline :semicolon :eof]) + +(defn- terminates? [parser] + (def curr (current parser)) + (def ttype (type curr)) + (has-value? terminators ttype)) + +(defn- panic [parser message] + (print "Panic in the parser: " message) + (def origin (current parser)) + (advance parser) + (def skipped @[origin]) + (while (not (terminates? parser)) + (array/push skipped (current parser)) + (advance parser)) + (array/push skipped (current parser)) + (advance parser) + (error {:type :error :data skipped :token origin})) + +(defn- expected [parser ttype] + (panic parser (string "expected " ttype ", got " (-> parser current type)))) + +(defn- expect [parser type] + (if-not (check parser type) (expected parser type))) # atoms -(defn- bool [parser] +(defn- bool [parser] + (expect parser :bool) (def curr (-> parser current)) (def ttype (type curr)) (def value (if (= ttype :true) true false)) - (def ast {:type :bool :value value :token curr}) - (update parser :ast array/push ast) - (advance parser)) + (advance parser) + {:type :bool :data value :token curr} + ) (defn- num [parser] + (expect parser :number) (def curr (-> parser current)) - (def ast {:type :number :value (curr :literal) :token curr}) - (update parser :ast array/push ast) - (advance parser)) + (advance parser) + {:type :number :data (curr :literal) :token curr} + ) (defn- kw [parser] + (expect parser :keyword) (def curr (-> parser current)) - (def ast {:type :keyword :value (curr :literal) :token curr}) - (update parser :ast array/push ast) - (advance parser)) + (advance parser) + {:type :keyword :data (curr :literal) :token curr} + ) (defn- nill [parser] - (def ast {:type :nil :token (current parser)}) - (update parser :ast array/push ast) - (advance parser)) + (expect parser :nil) + (advance parser) + {:type :nil :token (current parser)} + ) (defn- str [parser] + (expect parser :string) (def curr (-> parser current)) - (def ast {:type :string :value (curr :literal) :token curr}) - (update parser :ast array/push ast) - (advance parser)) + (advance parser) + {:type :string :data (curr :literal) :token curr} + ) -(defrec expr [parser] +(def separates [:break :newline :comma]) + +(defn- separates? [parser] + (def curr (current parser)) + (def ttype (type curr)) + (has-value? separates ttype)) + +(defn- separators [parser] + (if-not (separates? parser) + (panic parser (string "expected separator, got " (-> parser current type)))) + (while (separates? parser) (advance parser))) + +(defn- tup-term [parser] + (def term (nonbinding parser)) + (if (separates? parser) + (do + (while (separates? parser) (advance parser)) + term) + (panic parser (string "expected separator, got " (type (current parser)))))) + +(defn- tup [parser] + (def origin (current parser)) + (advance parser) # consume the :lparen + (def ast {:type :tuple :data @[] :token origin}) + (while (separates? parser) (advance parser)) # consume any separators + (while (not (check parser :rparen)) + (def term (try (nonbinding parser) ([e] e))) + (array/push (ast :data) term) + (try (separators parser) + ([e] (pp e) (array/push (ast :data) e)))) + (advance parser) + ast) + +(defrec nonbinding [parser] (def curr (current parser)) (case (type curr) # atoms @@ -92,18 +161,30 @@ :string (str parser) ### TODO: interpolated strings + # tuples + :lparen (tup parser) + + (panic parser (string "expected nonbinding expression, got " (type curr))) + ) +) + +(defrec binding [parser] + (def curr (current parser)) + (case (type curr) + :let nil + :fn nil ) ) -(do -#(comment (os/cd "janet") # For repl to do relative imports (import ./scanner :as s) +(do +#(comment -(def scanned (s/scan "\"foo bar baz\"")) +(def scanned (s/scan "(1, (2 3), 3)")) (def a-parser (new-parser scanned)) -(def parsed (expr a-parser)) -(-> parsed (get :ast) (get 0) (get :value)) +(def parsed (nonbinding a-parser)) +(-> parsed (get :data) ) )