feat: clojure-features demo runs end-to-end (Janet interop in §14, real exception demo in §11); fix syntax-quote (auto-gensym foo#, leave core/special unqualified) so macros work; (. obj sym) is a zero-arg method call; add long/double/float/num coercions; print Janet stack trace on errors (REPL/file/-e)
This commit is contained in:
parent
f20df44d6a
commit
7d490843e5
4 changed files with 81 additions and 26 deletions
|
|
@ -1277,6 +1277,10 @@
|
|||
# ============================================================
|
||||
|
||||
(def core-int (fn [x] (if (core-char? x) (x :ch) (math/trunc x))))
|
||||
(def core-long (fn [x] (if (core-char? x) (x :ch) (math/trunc x))))
|
||||
(def core-double (fn [x] (* 1.0 (if (core-char? x) (x :ch) x))))
|
||||
(def core-float core-double)
|
||||
(def core-num (fn [x] (if (core-char? x) (x :ch) x)))
|
||||
(defn core-char [x]
|
||||
"(char code-or-char) -> a character value."
|
||||
(cond
|
||||
|
|
@ -2617,6 +2621,10 @@
|
|||
"unsigned-bit-shift-right" core-unsigned-bit-shift-right
|
||||
# Integer coercion / unchecked math
|
||||
"int" core-int
|
||||
"long" core-long
|
||||
"double" core-double
|
||||
"float" core-float
|
||||
"num" core-num
|
||||
"char" core-char
|
||||
"char?" core-char?
|
||||
"unchecked-inc" core-unchecked-inc
|
||||
|
|
|
|||
|
|
@ -79,8 +79,28 @@
|
|||
(error (string "Cannot call " (type f) " as a function"))))
|
||||
(error (string "Cannot call " (type f) " as a function"))))
|
||||
|
||||
(defn- sq-symbol
|
||||
"Resolve a symbol inside syntax-quote. `foo#` becomes a stable auto-gensym
|
||||
(per-expansion, via gsmap); special forms and clojure.core names are left
|
||||
unqualified (they resolve via the core fallback); other symbols are qualified
|
||||
to the current namespace so they resolve when the macro is used elsewhere."
|
||||
[ctx form gsmap]
|
||||
(if (nil? (form :ns))
|
||||
(let [nm (form :name)]
|
||||
(cond
|
||||
(string/has-suffix? "#" nm)
|
||||
(or (get gsmap nm)
|
||||
(let [g {:jolt/type :symbol :ns nil
|
||||
:name (string (string/slice nm 0 -2) "__" (string (gensym)) "__auto")}]
|
||||
(put gsmap nm g) g))
|
||||
(special-symbol? nm) form
|
||||
(ns-find (ctx-find-ns ctx "clojure.core") nm) form
|
||||
{:jolt/type :symbol :ns (ctx-current-ns ctx) :name nm}))
|
||||
form))
|
||||
|
||||
(defn- syntax-quote*
|
||||
[ctx bindings form]
|
||||
[ctx bindings form &opt gsmap]
|
||||
(default gsmap @{})
|
||||
(cond
|
||||
(and (array? form) (> (length form) 0) (sym-name? (first form) "unquote"))
|
||||
(eval-form ctx bindings (in form 1))
|
||||
|
|
@ -89,29 +109,26 @@
|
|||
(or (number? form) (string? form) (keyword? form) (nil? form) (= true form) (= false form))
|
||||
form
|
||||
(and (struct? form) (= :symbol (form :jolt/type)))
|
||||
(if (nil? (form :ns))
|
||||
(if (special-symbol? (form :name)) form
|
||||
{:jolt/type :symbol :ns (ctx-current-ns ctx) :name (form :name)})
|
||||
form)
|
||||
(sq-symbol ctx form gsmap)
|
||||
(tuple? form)
|
||||
(do (var result @[]) (var i 0) (while (< i (length form))
|
||||
(let [item (in form i)]
|
||||
(if (and (array? item) (> (length item) 0) (sym-name? (first item) "unquote-splicing"))
|
||||
(each v (eval-form ctx bindings (in item 1)) (array/push result v))
|
||||
(array/push result (syntax-quote* ctx bindings item))))
|
||||
(array/push result (syntax-quote* ctx bindings item gsmap))))
|
||||
(++ i)) (tuple ;result))
|
||||
(array? form)
|
||||
(do (var result @[]) (var i 0) (while (< i (length form))
|
||||
(let [item (in form i)]
|
||||
(if (and (array? item) (> (length item) 0) (sym-name? (first item) "unquote-splicing"))
|
||||
(each v (eval-form ctx bindings (in item 1)) (array/push result v))
|
||||
(array/push result (syntax-quote* ctx bindings item))))
|
||||
(array/push result (syntax-quote* ctx bindings item gsmap))))
|
||||
(++ i)) result)
|
||||
(and (struct? form) (get form :jolt/type)) form
|
||||
(struct? form)
|
||||
(do (var kvs @[]) (each k (keys form)
|
||||
(array/push kvs (syntax-quote* ctx bindings k))
|
||||
(array/push kvs (syntax-quote* ctx bindings (get form k)))) (struct ;kvs))
|
||||
(array/push kvs (syntax-quote* ctx bindings k gsmap))
|
||||
(array/push kvs (syntax-quote* ctx bindings (get form k) gsmap))) (struct ;kvs))
|
||||
form))
|
||||
|
||||
(defn resolve-var
|
||||
|
|
@ -1097,8 +1114,19 @@
|
|||
(error (string "Cannot call non-function " field-name " on " (type target)))))
|
||||
(error (string "Cannot call non-function " field-name " on " (type target))))))
|
||||
(error (string "Cannot call method " field-name " on " (type target))))))
|
||||
# field access: (. obj field) — works on tables, structs, and deftypes
|
||||
(get target (keyword field-name))))
|
||||
# (. obj member) with no extra args: a symbol member naming a
|
||||
# function is a zero-arg method call (receiver passed as self);
|
||||
# a keyword or `-field` member is plain field access.
|
||||
(let [v (get target (keyword field-name))]
|
||||
(if (and (struct? member-raw) (= :symbol (member-raw :jolt/type))
|
||||
(not (string/has-prefix? "-" member-name)))
|
||||
(cond
|
||||
(or (function? v) (cfunction? v)) (v target)
|
||||
# value stored as an unevaluated fn* form: compile then call
|
||||
(array? v) (let [f (eval-form ctx bindings v)]
|
||||
(if (or (function? f) (cfunction? f)) (f target) f))
|
||||
v)
|
||||
v))))
|
||||
# default: function application — check for macros
|
||||
(if (and (struct? first-form) (= :symbol (first-form :jolt/type)))
|
||||
(let [sym-name (first-form :name)]
|
||||
|
|
|
|||
|
|
@ -163,6 +163,18 @@
|
|||
(write-value v buf)
|
||||
(print (string buf)))
|
||||
|
||||
(defn- err-message [err]
|
||||
(cond
|
||||
(string? err) err
|
||||
(and (or (table? err) (struct? err)) (= :jolt/exception (get err :jolt/type)))
|
||||
(let [v (get err :value)] (if (string? v) v (string/format "%q" v)))
|
||||
(string/format "%q" err)))
|
||||
|
||||
(defn- report-error [err fib]
|
||||
(eprint "Error: " (err-message err))
|
||||
# Janet-level stack trace of where evaluation failed
|
||||
(when fib (debug/stacktrace fib "")))
|
||||
|
||||
(defn- run-repl []
|
||||
(print "Jolt — Clojure on Janet")
|
||||
(print "Type (exit) to quit.\n")
|
||||
|
|
@ -188,7 +200,7 @@
|
|||
(set pending "")
|
||||
(try
|
||||
(print-value (eval-string ctx input))
|
||||
([err] (eprint "Error: " err))))))))))))
|
||||
([err fib] (report-error err fib))))))))))))
|
||||
|
||||
(defn- set-command-line-args [argv]
|
||||
# bind clojure.core/*command-line-args* to a vector of the remaining args
|
||||
|
|
@ -203,14 +215,14 @@
|
|||
(let [src (slurp path)]
|
||||
(try
|
||||
(load-string ctx src)
|
||||
([err] (eprint "Error: " err) (os/exit 1))))))
|
||||
([err fib] (report-error err fib) (os/exit 1))))))
|
||||
|
||||
(defn- run-eval [expr argv]
|
||||
(set-command-line-args argv)
|
||||
(try
|
||||
(let [v (load-string ctx expr)]
|
||||
(when (not (nil? v)) (print-value v)))
|
||||
([err] (eprint "Error: " err) (os/exit 1))))
|
||||
([err fib] (report-error err fib) (os/exit 1))))
|
||||
|
||||
(defn- print-help []
|
||||
(print "Jolt — a Clojure interpreter on Janet\n")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue