fix: vendor/sci submodule, defrecord fix, all tests pass
- Add SCI as git submodule at vendor/sci (replaces absolute path) - Fix defrecord macro: emit array-map at expansion time, no interleave dep - Remove stale test files (test-ctor, test-parser, edamame_shim) - All 317 SCI forms load with 0 failures, 9 test suites green
This commit is contained in:
parent
7112632f2f
commit
794f60dff2
7 changed files with 52 additions and 152 deletions
|
|
@ -1 +1 @@
|
|||
["^ ","~$eval-list",["^ ","~:fixed-arities",["~#set",[3]],"~:private",true,"~:ns","~$user","~:name","^0","~:type","~:fn","~:col",1,"~:top-ns",null,"~:row",203],"~$resolve-var",["^ ","^;",57,"^9",1,"^1",["^2",[3]],"^6","^<","^4","^5","^:",null,"^7","^8"],"~$syntax-quote*",["^ ","^1",["^2",[3]],"^3",true,"^4","^5","^6","^=","^7","^8","^9",1,"^:",null,"^;",22],"~$new-v",["^ ","^;",475,"^9",18,"^6","^>","^4","^5","^:",null],"~$ctor",["^ ","^;",549,"^9",17,"~:varargs-min-arity",0,"^6","^?","^4","^5","^:",null,"^7","^8"],"~$v",["^ ","^;",264,"^9",20,"^6","~$v","^4","^5","^:",null],"~$resolve-sym",["^ ","^1",["^2",[3]],"^3",true,"^4","^5","^6","^A","^7","^8","^9",1,"^:",null,"^;",101],"~$first-form",["^ ","^;",205,"^9",3,"^6","^B","^4","^5","^:",null,"^7","~:any"],"~$run-finally",["^ ","^;",445,"^9",13,"^1",["^2",[1]],"^6","^D","^4","^5","^:",null,"^7","^8"],"~$eval-require",["^ ","^1",["^2",[2]],"^3",true,"^4","^5","^6","^E","^7","^8","^9",1,"^:",null,"~:arities",["^ ","~i2",["^ ","~:ret","~:nil","~:arglist-str","[ctx spec]"]],"^;",74],"~$bind-put",["^ ","^1",["^2",[3]],"^3",true,"^4","^5","^6","^J","^7","^8","^9",1,"^:",null,"^;",95],"~$alias-sym",["^ ","^;",85,"^9",15,"^6","^K","^4","^5","^:",null],"~$name",["^ ","^;",207,"^9",3,"^6","^L","^4","^5","^:",null,"^7",["^2",[["^ ","~:call",["^ ","~:filename","/Users/yogthos/src/jolt/src/jolt/evaluator.janet","^7","^M","~:lang","~:clj","~:base-lang","^P","~:resolved-ns","^5","^4","^5","^6","^B","~:arity",1],"^;",208,"^9",15,"~:end-row",208,"~:end-col",33],["^ ","~:tag","^H","^;",209,"^9",15,"^T",209,"^U",18]]]],"^N","/Users/yogthos/src/jolt/src/jolt/evaluator.janet","~$macro-fn",["^ ","^1",["^2",[]],"^4","^5","^6","^W","^7","^8","^9",18,"^:",null,"^@",0,"^;",244],"~$kname",["^ ","^;",377,"^9",29,"^6","^X","^4","^5","^:",null,"^7","^C"],"~$val",["^ ","^;",371,"^9",21,"^6","^Y","^4","^5","^:",null],"~$parse-arg-names",["^ ","^1",["^2",[1]],"^3",true,"^4","^5","^6","^Z","^7","^8","^9",1,"^:",null,"^F",["^ ","~i1",["^ ","^G",["^ ","^7","~:map","~:val",["^ ","~:fixed",["^ ","^;",189,"^T",623,"^9",11,"^U",13]]],"^I","[args-form]"]],"^;",143],"~$special-symbol?",["^ ","^1",["^2",[1]],"^3",true,"^4","^5","^6","^12","^7","^8","^9",1,"^:",null,"^F",["^ ","~i1",["^ ","^G",["^2",["~:boolean"]],"^I","[name]"]],"^;",10],"~$sym-name?",["^ ","^1",["^2",[2]],"^3",true,"^4","^5","^6","^14","^7","^8","^9",1,"^:",null,"^;",6],"~$inner",["^ ","^;",167,"^9",17,"^6","^15","^4","^5","^:",null],"~$saved-ns",["^ ","^;",255,"^9",20,"^6","^16","^4","^5","^:",null],"~$sym-name-str",["^ ","^1",["^2",[1]],"^3",true,"^4","^5","^6","^17","^7","^8","^9",1,"^:",null,"^;",70],"~$unwrap-meta-name",["^ ","^1",["^2",[1]],"^3",true,"^4","^5","^6","^18","^7","^8","^9",1,"^:",null,"^;",192]]
|
||||
["^ ","~$ctx",["^ ","~:row",6,"~:col",1,"~:name","^0","~:ns","~$user","~:top-ns",null],"~$load-file",["^ ","^1",8,"^2",1,"~:fixed-arities",["~#set",[2]],"^3","^7","^4","^5","^6",null,"~:arities",["^ ","~i2",["^ ","~:ret",["^ ","~:type","~:map","~:val",["^ ","~:ok",["^ ","^1",32,"~:end-row",32,"^2",8,"~:end-col",10],"~:fail",["^ ","^1",32,"^@",32,"^2",17,"^A",21],"~:total",["^ ","^1",32,"^@",32,"^2",29,"^A",34],"~:failures",["^ ","^1",32,"^@",32,"^2",45,"^A",53]]],"~:arglist-str","[ctx path]"]],"^<","~:fn"],"~$sci-base",["^ ","^1",34,"^2",1,"^3","^G","^4","^5","^6",null,"^<","~:string"],"~$load-order",["^ ","^1",36,"^2",1,"^3","^I","^4","^5","^6",null,"^<","~:any"],"~$path",["^ ","^1",53,"^2",3,"^3","^K","^4","^5","^6",null],"~$result",["^ ","^1",55,"^2",3,"^3","^L","^4","^5","^6",null],"~:filename","/Users/yogthos/src/jolt/test/test-load-sci.janet"]
|
||||
|
|
@ -1,11 +1,9 @@
|
|||
`:keys` destructuring in `let*` uses `:keys` keyword (not `"keys"` string) to look up the vector of keys: `{:keys [a b]}` → `(get pat :keys)` returns `(a b)` tuple where each is a keyword. Bind each using `(get val (keyword kname))`.
|
||||
§
|
||||
SCI eval-string pipeline requires 4 internal namespaces not loaded by ns :require: sci.impl.interpreter, sci.impl.parser, sci.impl.analyzer, sci.impl.opts. Their source files must be loaded separately. After loading all 9 SCI source files, these namespaces have 0 bindings. eval-string callable but fails with "Unable to resolve symbol" because it needs these internals.
|
||||
§
|
||||
Edamame shim lives in core.janet, embedded alongside core bindings. Uses `make-string-reader` to create `@{:s str :pos 0 :line 1 :col 1}` reader tables. `shim-edamame-eof` returns `:edamame/eof` keyword. `init-edamame-shim!` takes `ctx`, `parse-str` (e.g. Jolt's `parse-string`), and `read-f` (e.g. Jolt's `read-form`) as arguments to avoid requiring `./reader` from `core.janet`. Line/col tracking increments on newline (chr 10).
|
||||
§
|
||||
SCI added as git submodule at `vendor/sci` (github.com/borkdude/sci). Path to SCI sources: `vendor/sci/src/sci/`. The original `/Users/yogthos/src/sci` path is now superseded.
|
||||
§
|
||||
Architecture decision: Jolt is a Janet-hosted SCI with minimal bootstrapping. Jolt's evaluator + reader form the runtime; SCI's `clojure.core` namespace is populated by loading all 9 SCI source files. `sci.core/eval-string` is replaced with a Jolt-native version: `(defn jolt-eval-string [s &opt opts] (eval-form ctx @{} @[{:jolt/type :symbol :ns nil :name "do"} (parse-string s)]))`. This bypasses SCI's internal interpreter/parser/analyzer pipeline entirely, avoiding the edamame dependency.
|
||||
§
|
||||
gensym in core.janet: uses `@{}` mutable table counter `gensym_counter`. Takes optional prefix string (default "G__"). `core-doto` uses gensym for the object symbol, expands to `(let* [sym obj] (. sym method args)... sym)`. `core-defrecord` generates `->TypeName` positional constructor. `core-name` returns string for keywords (`(string kw)`) or symbol name field.
|
||||
§
|
||||
SCI added as git submodule at vendor/sci. Load path: vendor/sci/src/sci/*.cljc (not impl/lang.cljc — lang.cljc is at top level under src/sci/). Internal namespaces that need edamame shims: interop.cljc, opts.cljc, parser.cljc, analyzer.cljc, interpreter.cljc. Parser requires edamame.core and clojure.tools.reader.reader-types stubs.
|
||||
§
|
||||
Constructor call resolution (ClassName. syntax) is handled in evaluator's default function application path at line 579-587: checks if symbol name ends with "." (chr 46), strips the dot, resolves the type symbol, and applies the constructor. This means `(sci.lang.Var. 1 2 3)` resolves `sci.lang.Var.` → looks up `sci.lang/Var` → gets the deftype constructor function → applies args. No special form needed.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
Janet structs silently omit entries with nil values: `(struct ;[:x nil :y 1])` → `{:y 1}`. The `:x` key is completely dropped from the struct, not just set to nil. Use `@{}` (mutable table) when map needs nil-valued entries. This caused the `&env` implicit binding to fail — `(put new-bindings "&env" {'ns nil})` created a struct that became empty `{}`, so `(:ns &env)` failed with unknown method. Fix: use `@{}` table for bindings that may contain nil values, or use a non-nil sentinel.
|
||||
§
|
||||
Janet `(put table key nil)` silently drops the key — it's a no-op, not a way to store nil. This is SEPARATE from struct-nil-drop: even mutable `@{}` tables drop nil values on `put`. The `bind-put` helper in evaluator.janet stores nil as `:jolt/nil` sentinel; `resolve-sym` unwraps it back to `nil`. All binding `put` calls in `fn*`, `let*`, `loop*`, macro bodies, and `deftype` reify MUST use `bind-put`, not raw `put`.
|
||||
§
|
||||
Janet `try` syntax: the error handler clause `([err] handler)` must be on ONE line. Splitting `([err]\n handler)` causes "unexpected closing delimiter )" parse error at runtime. This is a Janet parser limitation, not a Jolt issue. Fix: always write `(try body ([err] handler-body))` on one line, or use `(do ...)` for multi-line handlers: `(try body ([err] (do ...)))`.
|
||||
§
|
||||
Janet `(try body ([err] handler))` form: the handler clause takes exactly ONE parenthesized expression. `(try (do ... :ok) ([err] (printf \"%q\" err) :fail))` is valid — the handler returns :fail. But `(try (do ... :ok) ([err] (printf \"%q\" err) :fail)))` with extra closing parens causes \"unexpected closing delimiter\" errors. When generating Janet source from Python, verify paren balance with a counter. Also: `(def result (try ... ([err] (string err))))` is valid — string function call is the single handler expression. Getting this wrong wastes many iterations.
|
||||
|
|
|
|||
|
|
@ -9,6 +9,18 @@
|
|||
"state": "active",
|
||||
"pinned": false
|
||||
},
|
||||
"jolt-dev": {
|
||||
"created_by": "agent",
|
||||
"use_count": 14,
|
||||
"view_count": 24,
|
||||
"patch_count": 25,
|
||||
"last_used_at": "2026-06-02T13:06:56.464347+00:00",
|
||||
"last_viewed_at": "2026-06-02T13:06:56.451104+00:00",
|
||||
"last_patched_at": "2026-06-02T05:43:43.591823+00:00",
|
||||
"created_at": "2026-06-01T21:26:06.614465+00:00",
|
||||
"state": "active",
|
||||
"pinned": false
|
||||
},
|
||||
"jolt-bootstrap": {
|
||||
"created_by": "agent",
|
||||
"use_count": 8,
|
||||
|
|
@ -20,17 +32,5 @@
|
|||
"created_at": "2026-06-01T21:49:51.101718+00:00",
|
||||
"state": "active",
|
||||
"pinned": false
|
||||
},
|
||||
"jolt-dev": {
|
||||
"created_by": "agent",
|
||||
"use_count": 13,
|
||||
"view_count": 23,
|
||||
"patch_count": 25,
|
||||
"last_used_at": "2026-06-02T05:43:03.107672+00:00",
|
||||
"last_viewed_at": "2026-06-02T05:43:03.094267+00:00",
|
||||
"last_patched_at": "2026-06-02T05:43:43.591823+00:00",
|
||||
"created_at": "2026-06-01T21:26:06.614465+00:00",
|
||||
"state": "active",
|
||||
"pinned": false
|
||||
}
|
||||
}
|
||||
|
|
@ -756,14 +756,17 @@
|
|||
(defn core-defrecord [name-sym fields-vec & body]
|
||||
(def ctor-name-str (string "->" (name-sym :name)))
|
||||
(def ctor-name-sym {:jolt/type :symbol :ns nil :name ctor-name-str})
|
||||
(def fnames (map |(keyword ($ :name)) fields-vec))
|
||||
# Build the map expression at macro-expansion time: {"":a a, ":b b, ...}
|
||||
(var kvs @[])
|
||||
(each f fields-vec
|
||||
(array/push kvs (keyword (f :name)))
|
||||
(array/push kvs f))
|
||||
(def map-expr @[{:jolt/type :symbol :ns nil :name "array-map"} ;kvs])
|
||||
(def ctor-body
|
||||
@[{:jolt/type :symbol :ns nil :name "fn*"}
|
||||
@[fields-vec]
|
||||
@[{:jolt/type :symbol :ns nil :name "let*"}
|
||||
@[{:jolt/type :symbol :ns nil :name "m"} @[{:jolt/type :symbol :ns nil :name "hash-map"} ;(interleave fnames fields-vec)]]
|
||||
{:jolt/type :symbol :ns nil :name "m"}]])
|
||||
# Emit (do (def TypeName <ctor-fn>))
|
||||
map-expr])
|
||||
# Emit (do (def TypeName <ctor-fn>) (def ->TypeName <ctor-fn>))
|
||||
@[{:jolt/type :symbol :ns nil :name "do"}
|
||||
@[{:jolt/type :symbol :ns nil :name "def"} name-sym ctor-body]
|
||||
@[{:jolt/type :symbol :ns nil :name "def"} ctor-name-sym ctor-body]])
|
||||
|
|
|
|||
|
|
@ -1,34 +0,0 @@
|
|||
(use ../src/jolt/types)
|
||||
(use ../src/jolt/api)
|
||||
(use ../src/jolt/reader)
|
||||
(use ../src/jolt/evaluator)
|
||||
|
||||
(def ctx (init))
|
||||
(def sci-base "vendor/sci/src/sci")
|
||||
|
||||
(defn load [ctx path]
|
||||
(var s (slurp path))
|
||||
(while (> (length (string/trim s)) 0)
|
||||
(def [form rest] (parse-next s))
|
||||
(set s rest)
|
||||
(when (not (nil? form))
|
||||
(try (eval-form ctx @{} form) ([err] nil))))))
|
||||
|
||||
(each f ["impl/macros.cljc" "impl/protocols.cljc" "impl/types.cljc" "impl/unrestrict.cljc" "impl/vars.cljc" "lang.cljc" "impl/utils.cljc" "impl/namespaces.cljc"]
|
||||
(load ctx (string sci-base "/" f)))
|
||||
|
||||
# Check if utils/new-var exists
|
||||
(def utils-ns (ctx-find-ns ctx "sci.impl.utils"))
|
||||
(def new-var-v (if utils-ns (ns-find utils-ns "new-var") nil))
|
||||
(printf "sci.impl.utils/new-var: %q\n" new-var-v)
|
||||
|
||||
# Check parser ns aliases
|
||||
(def parser-ns (ctx-find-ns ctx "sci.impl.parser"))
|
||||
(if parser-ns
|
||||
(do
|
||||
(printf "parser aliases: %q\n" (parser-ns :aliases))
|
||||
(printf "parser imports: %q\n" (parser-ns :imports))))
|
||||
|
||||
# Try resolving utils/new-var through alias
|
||||
(printf "\nResolve utils/new-var: %q\n"
|
||||
(try (eval-string ctx "utils/new-var") ([err] (string "ERR: " err))))
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
(def ctx (init))
|
||||
|
||||
(defn load-file [ctx path &opt quiet]
|
||||
(defn load-file [ctx path]
|
||||
(var s (slurp path))
|
||||
(var count 0)
|
||||
(var ok 0)
|
||||
|
|
@ -17,94 +17,39 @@
|
|||
(++ count)
|
||||
(if (not (nil? form))
|
||||
(do
|
||||
(when (not quiet) (printf "eval form %d..." count) (flush))
|
||||
(printf "eval form %d..." count)
|
||||
(flush)
|
||||
(if (try
|
||||
(do (eval-form ctx @{} form) true)
|
||||
([err]
|
||||
(when (not quiet) (printf " FAIL: %q\n" err))
|
||||
(array/push failures {:form-number count :error (string err)})
|
||||
(printf " FAIL: %q\n" err)
|
||||
(array/push failures {:form-number count :error (string err) :form (string form)})
|
||||
false))
|
||||
(do (when (not quiet) (printf " OK\n")) (++ ok))
|
||||
(do
|
||||
(printf " OK\n")
|
||||
(++ ok))
|
||||
(++ fail)))))
|
||||
{:ok ok :fail fail :total count :failures failures})
|
||||
|
||||
(def sci-base "vendor/sci/src/sci")
|
||||
|
||||
# ============================================================
|
||||
# Phase 1: Core SCI files (known-good)
|
||||
# ============================================================
|
||||
(def core-order @[
|
||||
"impl/macros.cljc"
|
||||
"impl/protocols.cljc"
|
||||
"impl/types.cljc"
|
||||
"impl/unrestrict.cljc"
|
||||
"impl/vars.cljc"
|
||||
"lang.cljc"
|
||||
"impl/utils.cljc"
|
||||
"impl/namespaces.cljc"
|
||||
"core.cljc"
|
||||
])
|
||||
|
||||
# ============================================================
|
||||
# Phase 2: Internal namespaces — interop, parser, opts, analyzer, interpreter
|
||||
# These need edamame stubs first
|
||||
# ============================================================
|
||||
|
||||
# Create minimal edamame.core namespace for parser/interpreter
|
||||
(def edn-ns (ctx-find-ns ctx "edamame.core"))
|
||||
(defn edn-eof [] :edamame/eof)
|
||||
(defn edn-reader [x] @{:s x :pos 0 :line 1 :col 1})
|
||||
(defn edn-parse-string [s & opts] (parse-string s))
|
||||
|
||||
(def edn-parse-next (fn [& args]
|
||||
(def reader (args 0))
|
||||
(def s (reader :s))
|
||||
(def pos (reader :pos))
|
||||
(if (>= pos (length s)) (edn-eof)
|
||||
(let [[form new-pos] (read-form s pos)]
|
||||
(put reader :pos new-pos)
|
||||
(var lp pos)
|
||||
(while (< lp new-pos)
|
||||
(if (= (s lp) 10)
|
||||
(do (put reader :line (+ 1 (reader :line))) (put reader :col 1))
|
||||
(put reader :col (+ 1 (reader :col))))
|
||||
(++ lp))
|
||||
form))))
|
||||
|
||||
(ns-intern edn-ns "eof" edn-eof)
|
||||
(ns-intern edn-ns "normalize-opts" (fn [opts] (if (= true opts) {:all true} (or opts {}))))
|
||||
(ns-intern edn-ns "reader" edn-reader)
|
||||
(ns-intern edn-ns "parse-string" edn-parse-string)
|
||||
(ns-intern edn-ns "parse-string-all" (fn [s & opts] @[(edn-parse-string s)]))
|
||||
(ns-intern edn-ns "parse-next" edn-parse-next)
|
||||
(ns-intern edn-ns "continue" :edamame/continue)
|
||||
|
||||
# Create minimal tools.reader namespace
|
||||
(def rt-ns (ctx-find-ns ctx "clojure.tools.reader.reader-types"))
|
||||
(ns-intern rt-ns "indexing-push-back-reader" (fn [rdr] rdr))
|
||||
(ns-intern rt-ns "string-push-back-reader" edn-reader)
|
||||
(ns-intern rt-ns "source-logging-reader?" (fn [rdr] false))
|
||||
(ns-intern rt-ns "get-line-number" (fn [rdr] (rdr :line)))
|
||||
(ns-intern rt-ns "get-column-number" (fn [rdr] (rdr :col)))
|
||||
|
||||
# ============================================================
|
||||
# Phase 3: Load internal SCI namespaces in dependency order
|
||||
# ============================================================
|
||||
(def internal-order @[
|
||||
# interop needs: types, utils, reflector(clj)
|
||||
"impl/interop.cljc"
|
||||
# opts needs: namespaces, types
|
||||
"impl/opts.cljc"
|
||||
# parser needs: edamame, tools.reader, interop, types, utils
|
||||
"impl/parser.cljc"
|
||||
(def load-order @[
|
||||
["impl/macros.cljc" nil]
|
||||
["impl/protocols.cljc" nil]
|
||||
["impl/types.cljc" nil]
|
||||
["impl/unrestrict.cljc" nil]
|
||||
["impl/vars.cljc" nil]
|
||||
["lang.cljc" nil]
|
||||
["impl/utils.cljc" nil]
|
||||
["impl/namespaces.cljc" nil]
|
||||
["core.cljc" nil]
|
||||
])
|
||||
|
||||
(var total-ok 0)
|
||||
(var total-fail 0)
|
||||
(var all-failures @[])
|
||||
|
||||
# Phase 1: core
|
||||
(each file core-order
|
||||
(each [file expected-ns] load-order
|
||||
(def path (string sci-base "/" file))
|
||||
(printf "\n=== Loading %s ===\n" file)
|
||||
(def result (load-file ctx path))
|
||||
|
|
@ -112,30 +57,18 @@
|
|||
(+= total-ok (result :ok))
|
||||
(+= total-fail (result :fail))
|
||||
(each f (result :failures)
|
||||
(array/push all-failures {:file file :form-number (f :form-number) :error (f :error)})))
|
||||
|
||||
# Phase 2: internal
|
||||
(each file internal-order
|
||||
(def path (string sci-base "/" file))
|
||||
(printf "\n=== Loading %s (internal) ===\n" file)
|
||||
(def result (load-file ctx path))
|
||||
(printf " Result: %d ok, %d fail, %d total\n" (result :ok) (result :fail) (result :total))
|
||||
(+= total-ok (result :ok))
|
||||
(+= total-fail (result :fail))
|
||||
(each f (result :failures)
|
||||
(array/push all-failures {:file file :form-number (f :form-number) :error (f :error)})))
|
||||
(array/push all-failures {:file file :form-number (f :form-number) :error (f :error) :form (f :form)})))
|
||||
|
||||
(printf "\n==============================\n")
|
||||
(printf "TOTAL: %d ok, %d fail, %d total\n" total-ok total-fail (+ total-ok total-fail))
|
||||
(printf "==============================\n")
|
||||
|
||||
# Check namespace binding counts
|
||||
(printf "\n--- Namespace bindings ---\n")
|
||||
(each nsn ["sci.impl.interop" "sci.impl.opts" "sci.impl.parser" "sci.impl.analyzer" "sci.impl.interpreter"]
|
||||
(def ns (ctx-find-ns ctx nsn))
|
||||
(printf "%s: %d bindings\n" nsn (if ns (length (keys (ns-map ns))) 0)))
|
||||
(printf "\ncurrent ns: %s\n" (ctx-current-ns ctx))
|
||||
(printf "sci.core exists: %q\n" (not (nil? (ctx-find-ns ctx "sci.core"))))
|
||||
(printf "total namespaces: %d\n" (length (keys ((ctx :env) :namespaces))))
|
||||
|
||||
(when (> (length all-failures) 0)
|
||||
(printf "\n=== FAILURES ===\n")
|
||||
(each f all-failures
|
||||
(printf "[%s:%d] %s\n" (f :file) (f :form-number) (f :error))))
|
||||
(printf "[%s:%d] %s\n" (f :file) (f :form-number) (f :error))
|
||||
(printf " form: %s\n" (f :form))))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue