Compare commits
No commits in common. "ff1e1345b87c97c72613e1aac768292587792c94" and "6cf3fdd5f22dd997718878ecc3e3a43250fc195d" have entirely different histories.
ff1e1345b8
...
6cf3fdd5f2
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -28,4 +28,3 @@ node_modules/
|
||||||
.shadow-cljs
|
.shadow-cljs
|
||||||
.cljs_node_repl/
|
.cljs_node_repl/
|
||||||
.helix/
|
.helix/
|
||||||
target/repl-port
|
|
||||||
|
|
|
@ -73,11 +73,12 @@
|
||||||
|
|
||||||
(defn- stringify-args [arglist]
|
(defn- stringify-args [arglist]
|
||||||
(apply str (interpose " " (into [] (map print-show) (rest arglist)))))
|
(apply str (interpose " " (into [] (map print-show) (rest arglist)))))
|
||||||
; (def panic! {:name "panic!"
|
|
||||||
; ::data/type ::data/clj
|
(def panic! {:name "panic!"
|
||||||
; :body (fn panic-inner
|
::data/type ::data/clj
|
||||||
; ([] (panic-inner [::data/list]))
|
:body (fn panic-inner
|
||||||
; ([args] (throw (ex-info (stringify-args args) {}))))})
|
([] (panic-inner [::data/list]))
|
||||||
|
([args] (throw (ex-info (stringify-args args) {}))))})
|
||||||
|
|
||||||
(def print- {:name "print"
|
(def print- {:name "print"
|
||||||
::data/type ::data/clj
|
::data/type ::data/clj
|
||||||
|
@ -409,7 +410,7 @@
|
||||||
:to_vec to_vec
|
:to_vec to_vec
|
||||||
:fold fold
|
:fold fold
|
||||||
:map map
|
:map map
|
||||||
; :panic! panic!
|
:panic! panic!
|
||||||
:prn prn-
|
:prn prn-
|
||||||
:concat concat-
|
:concat concat-
|
||||||
:str str-
|
:str str-
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
(ns ludus.error
|
(ns ludus.error
|
||||||
(:require [clojure.string :as string]))
|
(:require [clojure.string :as string]))
|
||||||
|
|
||||||
(defn get-line [source line]
|
(defn get-line [source {:keys [line]}]
|
||||||
(let [lines (string/split-lines source)
|
(let [lines (string/split-lines source)
|
||||||
the_line (nth lines (dec line))]
|
the_line (nth lines (dec line))]
|
||||||
the_line))
|
the_line))
|
||||||
|
@ -21,26 +21,22 @@
|
||||||
|
|
||||||
(defn scan-error [] :TODO)
|
(defn scan-error [] :TODO)
|
||||||
|
|
||||||
(defn parse-error [{:keys [trace token]}]
|
(defn parse-error [source {:keys [trace token]}]
|
||||||
(let [source (:source token)
|
(let [line (get-line source token)
|
||||||
input (:input token)
|
|
||||||
line-num (:line token)
|
|
||||||
line (get-line source line-num)
|
|
||||||
line-num (:line token)
|
line-num (:line token)
|
||||||
prefix (str line-num ": ")
|
prefix (str line-num ": ")
|
||||||
underline (get-underline source token (count prefix))
|
underline (get-underline source token (count prefix))
|
||||||
expected (first trace)
|
expected (first trace)
|
||||||
got (:type token)
|
got (:type token)
|
||||||
message (str "Ludus found a parsing error on line " line-num " in " input ".\nExpected: " expected "\nGot: " got "\n")
|
message (str "Ludus found a parsing error on line " line-num ".\nExpected: " expected "\nGot: " got "\n")
|
||||||
]
|
]
|
||||||
(str message "\n" prefix line "\n" underline)
|
(str message "\n" prefix line "\n" underline)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
(defn run-error [{:keys [message token line]}]
|
(defn run-error [source {:keys [line message]}]
|
||||||
(let [source (:source token) input (:input token)]
|
(if line
|
||||||
(if line
|
(str "Ludus panicked on line " line ":\n" (get-line source {:line line}) "\n" message)
|
||||||
(str "Ludus panicked!: " message "\nOn line " line " in " input "\n" (get-line source line))
|
(str "Ludus panicked!\n" message)
|
||||||
(str "Ludus panicked!\n" message)
|
))
|
||||||
)))
|
|
||||||
|
|
||||||
|
|
|
@ -234,9 +234,7 @@
|
||||||
(defp collection flat choice [;struct-literal
|
(defp collection flat choice [;struct-literal
|
||||||
dict list-literal set-literal tuple])
|
dict list-literal set-literal tuple])
|
||||||
|
|
||||||
(defp panic group order-1 [(quiet :panic) expression])
|
(defp simple flat choice [literal collection synthetic recur-call lambda])
|
||||||
|
|
||||||
(defp simple flat choice [literal collection synthetic recur-call lambda panic])
|
|
||||||
|
|
||||||
(defp compound flat choice [match loop-expr if-expr when-expr do-expr block repeat-expr])
|
(defp compound flat choice [match loop-expr if-expr when-expr do-expr block repeat-expr])
|
||||||
|
|
||||||
|
@ -270,4 +268,3 @@
|
||||||
(defp script order-0 [nls?
|
(defp script order-0 [nls?
|
||||||
(one+ script-line)
|
(one+ script-line)
|
||||||
(quiet :eof)])
|
(quiet :eof)])
|
||||||
|
|
||||||
|
|
|
@ -817,18 +817,11 @@
|
||||||
|
|
||||||
(defn- interpret-literal [ast] (-> ast :data first))
|
(defn- interpret-literal [ast] (-> ast :data first))
|
||||||
|
|
||||||
(defn- interpret-panic [ast ctx]
|
|
||||||
(let [msg-value (interpret-ast (-> ast :data first) ctx)
|
|
||||||
msg-string (show/show msg-value)]
|
|
||||||
(throw (ex-info msg-string {:ast ast}))))
|
|
||||||
|
|
||||||
(defn interpret-ast [ast ctx]
|
(defn interpret-ast [ast ctx]
|
||||||
(case (:type ast)
|
(case (:type ast)
|
||||||
|
|
||||||
(:nil :true :false :number :string :keyword) (interpret-literal ast)
|
(:nil :true :false :number :string :keyword) (interpret-literal ast)
|
||||||
|
|
||||||
:panic (interpret-panic ast ctx)
|
|
||||||
|
|
||||||
:let-expr (interpret-let ast ctx)
|
:let-expr (interpret-let ast ctx)
|
||||||
|
|
||||||
:if-expr (interpret-if ast ctx)
|
:if-expr (interpret-if ast ctx)
|
||||||
|
@ -893,7 +886,7 @@
|
||||||
; :struct-literal
|
; :struct-literal
|
||||||
; (interpret-struct ast ctx)
|
; (interpret-struct ast ctx)
|
||||||
|
|
||||||
(throw (ex-info (str "Unknown AST node type " (get ast :type :none) " on line " (get-in ast [:token :line])) {:ast ast}))))
|
(throw (ex-info (str "Unknown AST node type " (get ast :type :err) " on line " (get-in ast [:token :line])) {:ast ast}))))
|
||||||
|
|
||||||
(defn get-line [source line]
|
(defn get-line [source line]
|
||||||
(if line
|
(if line
|
||||||
|
@ -915,11 +908,11 @@
|
||||||
(into {} (map-keys kw->str) ns))
|
(into {} (map-keys kw->str) ns))
|
||||||
|
|
||||||
(def ludus-prelude
|
(def ludus-prelude
|
||||||
(let [scanned (scanner/scan prelude/prelude "prelude")
|
(let [scanned (scanner/scan prelude/prelude)
|
||||||
parsed (p/apply-parser g/script (:tokens scanned))
|
parsed (p/apply-parser g/script (:tokens scanned))
|
||||||
; _ (println "Parse status: " (:status parsed))
|
; _ (println "Parse status: " (:status parsed))
|
||||||
; _ (if (= :err (:status parsed))
|
; _ (if (= :err (:status parsed))
|
||||||
; (throw (ex-info (error/parse-error parsed) {})))
|
; (throw (ex-info (error/parse-error prelude/prelude parsed) {})))
|
||||||
base-ctx (volatile! {::parent (volatile! {"base" base/base})})
|
base-ctx (volatile! {::parent (volatile! {"base" base/base})})
|
||||||
interpreted (interpret-ast parsed base-ctx)
|
interpreted (interpret-ast parsed base-ctx)
|
||||||
namespace (dissoc interpreted ::data/type ::data/name ::data/struct)
|
namespace (dissoc interpreted ::data/type ::data/name ::data/struct)
|
||||||
|
@ -980,41 +973,28 @@
|
||||||
;(pp/pprint (ex-data e))
|
;(pp/pprint (ex-data e))
|
||||||
;(throw e)
|
;(throw e)
|
||||||
{::data/error true
|
{::data/error true
|
||||||
:token (get-in (ex-data e) [:ast :token])
|
|
||||||
:line (get-in (ex-data e) [:ast :token :line])
|
:line (get-in (ex-data e) [:ast :token :line])
|
||||||
:message (ex-message e)}
|
:message (ex-message e)}
|
||||||
))))
|
))))
|
||||||
|
|
||||||
; No prelude; helps when there are errors in the prelude
|
|
||||||
(defn interpret-bare [source parsed ctx]
|
|
||||||
(let [base-ctx (volatile! {::parent (volatile! ctx)})]
|
|
||||||
(try
|
|
||||||
(interpret-ast parsed base-ctx)
|
|
||||||
(catch Throwable e
|
|
||||||
{::data/error true
|
|
||||||
:token (get-in (ex-data e) [:ast :token])
|
|
||||||
:line (get-in (ex-data e) [:ast :token :line])
|
|
||||||
:message (ex-message e)}))))
|
|
||||||
|
|
||||||
|
|
||||||
;; repl
|
;; repl
|
||||||
(comment
|
(comment
|
||||||
|
|
||||||
(println "***********")
|
(println "***********")
|
||||||
|
|
||||||
(def source "
|
(def source "
|
||||||
panic! (:oh, :no)
|
let times = 1.3
|
||||||
|
repeat times { print! (:foo) }
|
||||||
")
|
")
|
||||||
|
|
||||||
(def tokens (-> source (scanner/scan "test input") :tokens))
|
(def tokens (-> source scanner/scan :tokens))
|
||||||
|
|
||||||
(def ast (p/apply-parser g/script tokens))
|
(def ast (p/apply-parser g/script tokens))
|
||||||
|
|
||||||
(def result (interpret-bare source ast {}))
|
(def result (interpret-safe source ast {}))
|
||||||
|
|
||||||
(println tokens)
|
;(-> ast prettify-ast println)
|
||||||
|
|
||||||
(-> ast prettify-ast println)
|
|
||||||
|
|
||||||
(println result)
|
(println result)
|
||||||
|
|
||||||
|
|
|
@ -31,12 +31,14 @@
|
||||||
#?(:clj value :cljs (clj->js value)))
|
#?(:clj value :cljs (clj->js value)))
|
||||||
|
|
||||||
(defn run [source]
|
(defn run [source]
|
||||||
(let [user_scanned (s/scan source "user input")
|
(let [user_scanned (s/scan source)
|
||||||
user_tokens (:tokens user_scanned)
|
user_tokens (:tokens user_scanned)
|
||||||
|
_ (println "Tokens: " user_tokens)
|
||||||
user_parsed (p/apply-parser g/script user_tokens)
|
user_parsed (p/apply-parser g/script user_tokens)
|
||||||
|
_ (println "Ast: " (i/prettify-ast user_parsed))
|
||||||
user_result (i/interpret-safe source user_parsed {})
|
user_result (i/interpret-safe source user_parsed {})
|
||||||
result_str (show/show user_result)
|
result_str (show/show user_result)
|
||||||
post_scanned (s/scan pre/postlude "postlude")
|
post_scanned (s/scan pre/postlude)
|
||||||
post_tokens (:tokens post_scanned)
|
post_tokens (:tokens post_scanned)
|
||||||
post_parsed (p/apply-parser g/script post_tokens)
|
post_parsed (p/apply-parser g/script post_tokens)
|
||||||
post_result (i/interpret-safe source post_parsed {})
|
post_result (i/interpret-safe source post_parsed {})
|
||||||
|
@ -48,24 +50,13 @@
|
||||||
(clean-out {:errors (:errors user_tokens)})
|
(clean-out {:errors (:errors user_tokens)})
|
||||||
|
|
||||||
(= :err (:status user_parsed))
|
(= :err (:status user_parsed))
|
||||||
(clean-out {:errors [(error/parse-error user_parsed)]})
|
(clean-out {:errors [(error/parse-error source user_parsed)]})
|
||||||
|
|
||||||
(::data/error user_result)
|
(::data/error user_result)
|
||||||
(clean-out (assoc (ld->clj post_result) :errors [(error/run-error user_result)]))
|
(clean-out (assoc (ld->clj post_result) :errors [(error/run-error source user_result)]))
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(clean-out clj_result)
|
(clean-out clj_result)
|
||||||
)
|
)
|
||||||
))
|
))
|
||||||
|
|
||||||
(comment
|
|
||||||
(def source "
|
|
||||||
|
|
||||||
a b c
|
|
||||||
|
|
||||||
")
|
|
||||||
|
|
||||||
(-> source run :errors println)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -276,6 +276,19 @@ fn report! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn panic! {
|
||||||
|
"Causes Ludus to panic, outputting any arguments as messages."
|
||||||
|
() -> {
|
||||||
|
add_msg! ("Ludus panicked!")
|
||||||
|
base :panic! ()
|
||||||
|
}
|
||||||
|
(...args) -> {
|
||||||
|
add_msg! ("Ludus panicked!")
|
||||||
|
add_msg! (args)
|
||||||
|
base :panic! (args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn doc! {
|
fn doc! {
|
||||||
"Prints the documentation of a function to the console."
|
"Prints the documentation of a function to the console."
|
||||||
(f as :fn) -> do f > base :doc > print!
|
(f as :fn) -> do f > base :doc > print!
|
||||||
|
@ -383,7 +396,7 @@ fn mult {
|
||||||
fn div {
|
fn div {
|
||||||
"Divides numbers. Panics on division by zero."
|
"Divides numbers. Panics on division by zero."
|
||||||
(x as :number) -> x
|
(x as :number) -> x
|
||||||
(_, 0) -> panic! "Division by zero."
|
(_, 0) -> panic! ("Division by zero.")
|
||||||
(x as :number, y as :number) -> base :div (x, y)
|
(x as :number, y as :number) -> base :div (x, y)
|
||||||
(x, y, ...zs) -> {
|
(x, y, ...zs) -> {
|
||||||
let divisor = fold (mult, zs, y)
|
let divisor = fold (mult, zs, y)
|
||||||
|
@ -885,8 +898,8 @@ fn err? {
|
||||||
fn unwrap! {
|
fn unwrap! {
|
||||||
"Takes a result tuple. If it's :ok, then returns the value. If it's not :ok, then it panics. If it's not a result tuple, it also panics."
|
"Takes a result tuple. If it's :ok, then returns the value. If it's not :ok, then it panics. If it's not a result tuple, it also panics."
|
||||||
((:ok, value)) -> value
|
((:ok, value)) -> value
|
||||||
((:err, msg)) -> panic! string ("Unwrapped :err! ", msg)
|
((:err, msg)) -> panic! ("Unwrapped :err", msg)
|
||||||
(_) -> panic! "Cannot unwrap something that's not an error tuple."
|
(_) -> panic! ("Cannot unwrap something that's not an error tuple.")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unwrap_or {
|
fn unwrap_or {
|
||||||
|
@ -897,10 +910,10 @@ fn unwrap_or {
|
||||||
|
|
||||||
fn assert! {
|
fn assert! {
|
||||||
"Asserts a condition: returns the value if the value is truthy, panics if the value is falsy. Takes an optional message."
|
"Asserts a condition: returns the value if the value is truthy, panics if the value is falsy. Takes an optional message."
|
||||||
(value) -> if value then value else panic! string ("Assert failed:", value)
|
(value) -> if value then value else panic! ("Assert failed", value)
|
||||||
(msg, value) -> if value
|
(value, message) -> if value
|
||||||
then value
|
then value
|
||||||
else panic! string ("Assert failed: ", msg, " with ", value)
|
else panic! ("Assert failed:", message, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
&&& Turtle & other graphics
|
&&& Turtle & other graphics
|
||||||
|
@ -1204,6 +1217,7 @@ ns prelude {
|
||||||
show
|
show
|
||||||
prn!
|
prn!
|
||||||
report!
|
report!
|
||||||
|
panic!
|
||||||
doc!
|
doc!
|
||||||
concat
|
concat
|
||||||
ref?
|
ref?
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
"match" :match ;; impl
|
"match" :match ;; impl
|
||||||
"nil" :nil ;; impl -> literal word
|
"nil" :nil ;; impl -> literal word
|
||||||
"ns" :ns ;; impl
|
"ns" :ns ;; impl
|
||||||
"panic!" :panic ;; impl (should _not_ be a function)
|
;; "panic!" :panic ;; impl (should be a function)
|
||||||
"recur" :recur ;; impl
|
"recur" :recur ;; impl
|
||||||
"ref" :ref ;; impl
|
"ref" :ref ;; impl
|
||||||
"then" :then ;; impl
|
"then" :then ;; impl
|
||||||
|
@ -51,9 +51,8 @@
|
||||||
|
|
||||||
(defn- new-scanner
|
(defn- new-scanner
|
||||||
"Creates a new scanner."
|
"Creates a new scanner."
|
||||||
[source input]
|
[source]
|
||||||
{:source source
|
{:source source
|
||||||
:input input
|
|
||||||
:length (count source)
|
:length (count source)
|
||||||
:errors []
|
:errors []
|
||||||
:start 0
|
:start 0
|
||||||
|
@ -136,9 +135,7 @@
|
||||||
(current-lexeme scanner)
|
(current-lexeme scanner)
|
||||||
literal
|
literal
|
||||||
(:line scanner)
|
(:line scanner)
|
||||||
(:start scanner)
|
(:start scanner)))))
|
||||||
(:source scanner)
|
|
||||||
(:input scanner)))))
|
|
||||||
|
|
||||||
;; TODO: errors should also be in the vector of tokens
|
;; TODO: errors should also be in the vector of tokens
|
||||||
;; The goal is to be able to be able to hand this to an LSP?
|
;; The goal is to be able to be able to hand this to an LSP?
|
||||||
|
@ -149,9 +146,7 @@
|
||||||
(current-lexeme scanner)
|
(current-lexeme scanner)
|
||||||
nil
|
nil
|
||||||
(:line scanner)
|
(:line scanner)
|
||||||
(:start scanner)
|
(:start scanner))
|
||||||
(:source scanner)
|
|
||||||
(:input scanner))
|
|
||||||
err-token (assoc token :message msg)]
|
err-token (assoc token :message msg)]
|
||||||
(-> scanner
|
(-> scanner
|
||||||
(update :errors conj err-token)
|
(update :errors conj err-token)
|
||||||
|
@ -245,7 +240,7 @@
|
||||||
comm (str char)]
|
comm (str char)]
|
||||||
(let [char (current-char scanner)]
|
(let [char (current-char scanner)]
|
||||||
(if (= \newline char)
|
(if (= \newline char)
|
||||||
scanner
|
(update scanner :line inc)
|
||||||
(recur (advance scanner) (str comm char))))))
|
(recur (advance scanner) (str comm char))))))
|
||||||
|
|
||||||
(defn- scan-token [scanner]
|
(defn- scan-token [scanner]
|
||||||
|
@ -327,10 +322,11 @@
|
||||||
(defn- next-token [scanner]
|
(defn- next-token [scanner]
|
||||||
(assoc scanner :start (:current scanner)))
|
(assoc scanner :start (:current scanner)))
|
||||||
|
|
||||||
(defn scan [source input]
|
(defn scan [source]
|
||||||
(loop [scanner (new-scanner source input)]
|
(loop [scanner (new-scanner source)]
|
||||||
(if (at-end? scanner)
|
(if (at-end? scanner)
|
||||||
(let [scanner (add-token (add-token scanner :break) :eof)]
|
(let [scanner (add-token (add-token scanner :break) :eof)]
|
||||||
{:tokens (:tokens scanner)
|
{:tokens (:tokens scanner)
|
||||||
:errors (:errors scanner)})
|
:errors (:errors scanner)})
|
||||||
(recur (-> scanner (scan-token) (next-token))))))
|
(recur (-> scanner (scan-token) (next-token))))))
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
(ns ludus.token)
|
(ns ludus.token)
|
||||||
|
|
||||||
(defn token
|
(defn token
|
||||||
[type text literal line start source input]
|
[type text literal line start]
|
||||||
{:type type
|
{:type type
|
||||||
:lexeme text
|
:lexeme text
|
||||||
:literal literal
|
:literal literal
|
||||||
:line line
|
:line line
|
||||||
:source source
|
|
||||||
:input input
|
|
||||||
:start start})
|
:start start})
|
||||||
|
|
1
target/repl-port
Normal file
1
target/repl-port
Normal file
|
@ -0,0 +1 @@
|
||||||
|
51500
|
Loading…
Reference in New Issue
Block a user