Recompile all 26 .sxbc with define-library wrappers + fix eval/JIT
All 26 browser modules recompiled with define-library/import forms. Compilation works without vm-compile-adapter (JIT pre-compilation hangs with library wrappers in some JIT paths — skipped for now, CEK compilation is ~34s total). Key fixes: - eval command: import-aware loop that handles define-library/import locally without touching the Python bridge pipe (avoids deadlock) - compile-modules.js: skip vm-compile-adapter, bump timeout 2621/2621 OCaml tests passing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -50,6 +50,13 @@ let rec deep_equal a b =
|
||||
deep_equal
|
||||
(match Hashtbl.find_opt a k with Some v -> v | None -> Nil)
|
||||
(match Hashtbl.find_opt b k with Some v -> v | None -> Nil)) ka
|
||||
| Record a, Record b ->
|
||||
a.r_type.rt_uid = b.r_type.rt_uid &&
|
||||
Array.length a.r_fields = Array.length b.r_fields &&
|
||||
(let eq = ref true in
|
||||
for i = 0 to Array.length a.r_fields - 1 do
|
||||
if not (deep_equal a.r_fields.(i) b.r_fields.(i)) then eq := false
|
||||
done; !eq)
|
||||
| Lambda _, Lambda _ -> a == b (* identity *)
|
||||
| NativeFn _, NativeFn _ -> a == b
|
||||
| _ -> false
|
||||
|
||||
@@ -1030,9 +1030,29 @@ let rec dispatch env cmd =
|
||||
(try
|
||||
let exprs = Sx_parser.parse_all src in
|
||||
let result = List.fold_left (fun _acc expr ->
|
||||
(* Use IO-aware eval to handle import suspensions *)
|
||||
(* Use import-aware eval — handles define-library/import locally
|
||||
but does NOT send other IO to the Python bridge (would deadlock
|
||||
on stdin which carries batch commands). *)
|
||||
let state = Sx_ref.make_cek_state expr (Env env) (List []) in
|
||||
cek_run_with_io state
|
||||
let s = ref (Sx_ref.cek_step_loop state) in
|
||||
while Sx_types.sx_truthy (Sx_ref.cek_suspended_p !s) do
|
||||
let request = Sx_ref.cek_io_request !s in
|
||||
let op = match request with
|
||||
| Dict d -> (match Hashtbl.find_opt d "op" with Some (String o) -> o | _ -> "")
|
||||
| _ -> "" in
|
||||
let response = if op = "import" then begin
|
||||
let lib_spec = Sx_runtime.get_val request (String "library") in
|
||||
let key = Sx_ref.library_name_key lib_spec in
|
||||
if Sx_types.sx_truthy (Sx_ref.library_loaded_p key) then Nil
|
||||
else begin
|
||||
(match resolve_library_path lib_spec with
|
||||
| Some path -> load_library_file path | None -> ());
|
||||
Nil
|
||||
end
|
||||
end else Nil (* non-import IO: resume with nil *) in
|
||||
s := Sx_ref.cek_resume !s response
|
||||
done;
|
||||
Sx_ref.cek_value !s
|
||||
) Nil exprs in
|
||||
(* Use ok-raw with natural list serialization — no (list ...) wrapping.
|
||||
This preserves the SX structure for Python to parse back. *)
|
||||
|
||||
@@ -56,8 +56,10 @@ let script = '';
|
||||
// Load compiler
|
||||
script += `(epoch ${epoch++})\n(load "lib/compiler.sx")\n`;
|
||||
|
||||
// JIT pre-compile the compiler
|
||||
script += `(epoch ${epoch++})\n(vm-compile-adapter)\n`;
|
||||
// JIT pre-compile the compiler (skipped: vm-compile-adapter hangs with
|
||||
// define-library wrappers in some lambda JIT paths. Compilation still
|
||||
// works via CEK — just ~2x slower per file.)
|
||||
// script += `(epoch ${epoch++})\n(vm-compile-adapter)\n`;
|
||||
|
||||
// Load all modules into env
|
||||
for (const file of FILES) {
|
||||
|
||||
@@ -200,6 +200,14 @@ let () =
|
||||
(match Hashtbl.find_opt a "__host_handle", Hashtbl.find_opt b "__host_handle" with
|
||||
| Some (Number ha), Some (Number hb) -> ha = hb
|
||||
| _ -> false)
|
||||
(* Records: same type + structurally equal fields *)
|
||||
| Record a, Record b ->
|
||||
a.r_type.rt_uid = b.r_type.rt_uid &&
|
||||
Array.length a.r_fields = Array.length b.r_fields &&
|
||||
(let eq = ref true in
|
||||
for i = 0 to Array.length a.r_fields - 1 do
|
||||
if not (safe_eq a.r_fields.(i) b.r_fields.(i)) then eq := false
|
||||
done; !eq)
|
||||
(* Lambda/Component/Island/Signal/NativeFn: physical only *)
|
||||
| _ -> false
|
||||
in
|
||||
@@ -723,6 +731,7 @@ let () =
|
||||
| [Island i] ->
|
||||
String (Printf.sprintf "~%s" i.i_name)
|
||||
| [Lambda _] -> String "<lambda>"
|
||||
| [Record r] -> String (Printf.sprintf "#<%s>" r.r_type.rt_name)
|
||||
| [a] -> String (inspect a) (* used for dedup keys in compiler *)
|
||||
| _ -> raise (Eval_error "serialize: 1 arg"));
|
||||
register "make-symbol" (fun args ->
|
||||
@@ -912,6 +921,36 @@ let () =
|
||||
match args with [Lambda _] -> Bool true | _ -> Bool false);
|
||||
register "island?" (fun args ->
|
||||
match args with [Island _] -> Bool true | _ -> Bool false);
|
||||
|
||||
(* R7RS records *)
|
||||
register "record?" (fun args ->
|
||||
match args with [v] -> record_p v | _ -> Bool false);
|
||||
register "make-rtd" (fun args ->
|
||||
match args with [name; fields; ctor_params] -> make_rtd name fields ctor_params
|
||||
| _ -> raise (Eval_error "make-rtd: expected (name fields ctor-params)"));
|
||||
register "make-record" (fun args ->
|
||||
match args with [uid; arg_list] -> make_record uid arg_list
|
||||
| _ -> raise (Eval_error "make-record: expected (uid args-list)"));
|
||||
register "record-ref" (fun args ->
|
||||
match args with [v; idx] -> record_ref v idx
|
||||
| _ -> raise (Eval_error "record-ref: expected (record index)"));
|
||||
register "record-set!" (fun args ->
|
||||
match args with [v; idx; nv] -> record_set_b v idx nv
|
||||
| _ -> raise (Eval_error "record-set!: expected (record index value)"));
|
||||
register "record-type?" (fun args ->
|
||||
match args with [v; uid] -> record_type_p v uid | _ -> Bool false);
|
||||
register "make-record-constructor" (fun args ->
|
||||
match args with [uid] -> make_record_constructor uid
|
||||
| _ -> raise (Eval_error "make-record-constructor: expected (uid)"));
|
||||
register "make-record-predicate" (fun args ->
|
||||
match args with [uid] -> make_record_predicate uid
|
||||
| _ -> raise (Eval_error "make-record-predicate: expected (uid)"));
|
||||
register "make-record-accessor" (fun args ->
|
||||
match args with [idx] -> make_record_accessor idx
|
||||
| _ -> raise (Eval_error "make-record-accessor: expected (index)"));
|
||||
register "make-record-mutator" (fun args ->
|
||||
match args with [idx] -> make_record_mutator idx
|
||||
| _ -> raise (Eval_error "make-record-mutator: expected (index)"));
|
||||
register "is-else-clause?" (fun args ->
|
||||
match args with
|
||||
| [Keyword "else"] -> Bool true
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -67,6 +67,7 @@ and value =
|
||||
| CekState of cek_state (** Optimized CEK machine state — avoids Dict allocation. *)
|
||||
| CekFrame of cek_frame (** Optimized CEK continuation frame. *)
|
||||
| VmClosure of vm_closure (** VM-compiled closure — callable within the VM without allocating a new VM. *)
|
||||
| Record of record (** R7RS record — opaque, generative, field-indexed. *)
|
||||
|
||||
(** CEK machine state — record instead of Dict for performance.
|
||||
5 fields × 55K steps/sec = 275K Hashtbl allocations/sec eliminated. *)
|
||||
@@ -139,6 +140,21 @@ and signal = {
|
||||
mutable s_deps : signal list;
|
||||
}
|
||||
|
||||
(** R7RS record type descriptor — one per [define-record-type] call.
|
||||
Stored in [rtd_table]; closures capture only the integer uid. *)
|
||||
and record_type = {
|
||||
rt_name : string; (** e.g., "point" *)
|
||||
rt_uid : int; (** unique identity — generative *)
|
||||
rt_fields : string array; (** field names in declaration order *)
|
||||
rt_ctor_map : int array; (** ctor_map[i] = field index for ctor param i *)
|
||||
}
|
||||
|
||||
(** R7RS record instance — opaque, accessed only through generated functions. *)
|
||||
and record = {
|
||||
r_type : record_type;
|
||||
r_fields : value array; (** mutable via Array.set for record-set! *)
|
||||
}
|
||||
|
||||
(** {1 Bytecode VM types}
|
||||
|
||||
Defined here (not in sx_vm.ml) because [vm_code.constants] references
|
||||
@@ -180,6 +196,12 @@ exception Eval_error of string
|
||||
exception Parse_error of string
|
||||
|
||||
|
||||
(** {1 Record type descriptor table} *)
|
||||
|
||||
let rtd_table : (int, record_type) Hashtbl.t = Hashtbl.create 16
|
||||
let rtd_counter = ref 0
|
||||
|
||||
|
||||
(** {1 Environment operations} *)
|
||||
|
||||
let make_env () =
|
||||
@@ -347,6 +369,7 @@ let type_of = function
|
||||
| CekState _ -> "dict" (* CEK state behaves as a dict for type checks *)
|
||||
| CekFrame _ -> "dict"
|
||||
| VmClosure _ -> "function"
|
||||
| Record r -> r.r_type.rt_name
|
||||
|
||||
let is_nil = function Nil -> true | _ -> false
|
||||
let is_lambda = function Lambda _ -> true | _ -> false
|
||||
@@ -359,6 +382,8 @@ let is_signal = function
|
||||
| Dict d -> Hashtbl.mem d "__signal"
|
||||
| _ -> false
|
||||
|
||||
let is_record = function Record _ -> true | _ -> false
|
||||
|
||||
let is_callable = function
|
||||
| Lambda _ | NativeFn _ | Continuation (_, _) | CallccContinuation _ | VmClosure _ -> true
|
||||
| _ -> false
|
||||
@@ -470,6 +495,130 @@ let thunk_env = function
|
||||
| v -> raise (Eval_error ("Expected thunk, got " ^ type_of v))
|
||||
|
||||
|
||||
(** {1 Record operations} *)
|
||||
|
||||
let val_to_int = function
|
||||
| Number n -> int_of_float n
|
||||
| v -> raise (Eval_error ("Expected number, got " ^ type_of v))
|
||||
|
||||
(** [make_rtd name fields ctor_params] — create a record type descriptor.
|
||||
Called as [make-rtd] from transpiled evaluator. Takes 3 separate args. *)
|
||||
let make_rtd name fields ctor_params =
|
||||
let uid = !rtd_counter in
|
||||
incr rtd_counter;
|
||||
let field_names = List.map value_to_string (match fields with List l -> l | _ -> []) in
|
||||
let ctor_names = List.map value_to_string (match ctor_params with List l -> l | _ -> []) in
|
||||
let field_arr = Array.of_list field_names in
|
||||
let ctor_map = Array.of_list (List.map (fun cp ->
|
||||
let rec find j = function
|
||||
| [] -> raise (Eval_error (Printf.sprintf "make-rtd: ctor param %s not in fields" cp))
|
||||
| f :: _ when f = cp -> j
|
||||
| _ :: rest -> find (j + 1) rest
|
||||
in find 0 field_names
|
||||
) ctor_names) in
|
||||
let rt = { rt_name = value_to_string name; rt_uid = uid; rt_fields = field_arr; rt_ctor_map = ctor_map } in
|
||||
Hashtbl.add rtd_table uid rt;
|
||||
Number (float_of_int uid)
|
||||
|
||||
(** [make_record uid_val args_list] — create a record from uid + args list.
|
||||
2-arg direct call: (make-record rtd-uid ctor-args-list). *)
|
||||
let make_record uid_val args_list =
|
||||
let uid = val_to_int uid_val in
|
||||
let ctor_args = match args_list with List l -> l | _ -> [] in
|
||||
match Hashtbl.find_opt rtd_table uid with
|
||||
| None -> raise (Eval_error "make-record: unknown rtd")
|
||||
| Some rt ->
|
||||
let n_ctor = Array.length rt.rt_ctor_map in
|
||||
let n_args = List.length ctor_args in
|
||||
if n_args <> n_ctor then
|
||||
raise (Eval_error (Printf.sprintf "%s: expected %d args, got %d"
|
||||
rt.rt_name n_ctor n_args));
|
||||
let fields = Array.make (Array.length rt.rt_fields) Nil in
|
||||
List.iteri (fun i arg ->
|
||||
fields.(rt.rt_ctor_map.(i)) <- arg
|
||||
) ctor_args;
|
||||
Record { r_type = rt; r_fields = fields }
|
||||
|
||||
(** [record_ref v idx] — access field by index. 2-arg direct call. *)
|
||||
let record_ref v idx =
|
||||
match v with
|
||||
| Record r ->
|
||||
let i = val_to_int idx in
|
||||
if i < 0 || i >= Array.length r.r_fields then
|
||||
raise (Eval_error (Printf.sprintf "record-ref: index %d out of bounds for %s" i r.r_type.rt_name));
|
||||
r.r_fields.(i)
|
||||
| _ -> raise (Eval_error ("record-ref: not a record, got " ^ type_of v))
|
||||
|
||||
(** [record_set_b v idx new_val] — mutate field by index. 3-arg direct call.
|
||||
Named record_set_b because transpiler mangles record-set! to record_set_b. *)
|
||||
let record_set_b v idx new_val =
|
||||
match v with
|
||||
| Record r ->
|
||||
let i = val_to_int idx in
|
||||
if i < 0 || i >= Array.length r.r_fields then
|
||||
raise (Eval_error (Printf.sprintf "record-set!: index %d out of bounds for %s" i r.r_type.rt_name));
|
||||
r.r_fields.(i) <- new_val; Nil
|
||||
| _ -> raise (Eval_error ("record-set!: not a record, got " ^ type_of v))
|
||||
|
||||
(** [record_type_p v uid_val] — type predicate. 2-arg direct call.
|
||||
Named record_type_p because transpiler mangles record-type? to record_type_p. *)
|
||||
let record_type_p v uid_val =
|
||||
match v with
|
||||
| Record r -> Bool (r.r_type.rt_uid = val_to_int uid_val)
|
||||
| _ -> Bool false
|
||||
|
||||
(** [record_p v] — generic record predicate.
|
||||
Named record_p because transpiler mangles record? to record_p. *)
|
||||
let record_p v = Bool (is_record v)
|
||||
|
||||
(** [make_record_constructor rtd_uid] — returns a NativeFn that constructs records.
|
||||
Called from transpiled sf-define-record-type. *)
|
||||
let make_record_constructor uid_val =
|
||||
let uid = val_to_int uid_val in
|
||||
let rt = match Hashtbl.find_opt rtd_table uid with
|
||||
| Some rt -> rt | None -> raise (Eval_error "make-record-constructor: unknown rtd") in
|
||||
NativeFn (rt.rt_name, fun args ->
|
||||
let n_ctor = Array.length rt.rt_ctor_map in
|
||||
let n_args = List.length args in
|
||||
if n_args <> n_ctor then
|
||||
raise (Eval_error (Printf.sprintf "%s: expected %d args, got %d" rt.rt_name n_ctor n_args));
|
||||
let fields = Array.make (Array.length rt.rt_fields) Nil in
|
||||
List.iteri (fun i arg -> fields.(rt.rt_ctor_map.(i)) <- arg) args;
|
||||
Record { r_type = rt; r_fields = fields })
|
||||
|
||||
(** [make_record_predicate rtd_uid] — returns a NativeFn that tests record type. *)
|
||||
let make_record_predicate uid_val =
|
||||
let uid = val_to_int uid_val in
|
||||
NativeFn ("?", fun args ->
|
||||
match args with
|
||||
| [Record r] -> Bool (r.r_type.rt_uid = uid)
|
||||
| [_] -> Bool false
|
||||
| _ -> raise (Eval_error "record predicate: expected 1 arg"))
|
||||
|
||||
(** [make_record_accessor field_idx] — returns a NativeFn that reads a field. *)
|
||||
let make_record_accessor idx_val =
|
||||
let idx = val_to_int idx_val in
|
||||
NativeFn ("ref", fun args ->
|
||||
match args with
|
||||
| [Record r] ->
|
||||
if idx < 0 || idx >= Array.length r.r_fields then
|
||||
raise (Eval_error (Printf.sprintf "record accessor: index %d out of bounds" idx));
|
||||
r.r_fields.(idx)
|
||||
| [v] -> raise (Eval_error ("record accessor: not a record, got " ^ type_of v))
|
||||
| _ -> raise (Eval_error "record accessor: expected 1 arg"))
|
||||
|
||||
(** [make_record_mutator field_idx] — returns a NativeFn that sets a field. *)
|
||||
let make_record_mutator idx_val =
|
||||
let idx = val_to_int idx_val in
|
||||
NativeFn ("set!", fun args ->
|
||||
match args with
|
||||
| [Record r; new_val] ->
|
||||
if idx < 0 || idx >= Array.length r.r_fields then
|
||||
raise (Eval_error (Printf.sprintf "record mutator: index %d out of bounds" idx));
|
||||
r.r_fields.(idx) <- new_val; Nil
|
||||
| _ -> raise (Eval_error "record mutator: expected (record value)"))
|
||||
|
||||
|
||||
(** {1 Dict operations} *)
|
||||
|
||||
let make_dict () : dict = Hashtbl.create 8
|
||||
@@ -541,3 +690,8 @@ let rec inspect = function
|
||||
| CekState _ -> "<cek-state>"
|
||||
| CekFrame f -> Printf.sprintf "<frame:%s>" f.cf_type
|
||||
| VmClosure cl -> Printf.sprintf "<vm:%s>" (match cl.vm_name with Some n -> n | None -> "anon")
|
||||
| Record r ->
|
||||
let fields = Array.to_list (Array.mapi (fun i v ->
|
||||
Printf.sprintf "%s=%s" r.r_type.rt_fields.(i) (inspect v)
|
||||
) r.r_fields) in
|
||||
Printf.sprintf "<record:%s %s>" r.r_type.rt_name (String.concat " " fields)
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,3 +1,22 @@
|
||||
|
||||
|
||||
(define-library (web adapter-html)
|
||||
(export
|
||||
render-to-html
|
||||
render-value-to-html
|
||||
RENDER_HTML_FORMS
|
||||
render-html-form?
|
||||
render-list-to-html
|
||||
dispatch-html-form
|
||||
render-lambda-html
|
||||
render-html-component
|
||||
render-html-element
|
||||
render-html-lake
|
||||
render-html-marsh
|
||||
render-html-island
|
||||
serialize-island-state)
|
||||
(begin
|
||||
|
||||
(define
|
||||
render-to-html
|
||||
:effects (render)
|
||||
@@ -589,3 +608,9 @@
|
||||
(fn
|
||||
((kwargs :as dict))
|
||||
(if (empty-dict? kwargs) nil (sx-serialize kwargs))))
|
||||
|
||||
|
||||
)) ;; end define-library
|
||||
|
||||
;; Re-export to global namespace for backward compatibility
|
||||
(import (web adapter-html))
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,3 +1,22 @@
|
||||
|
||||
|
||||
(define-library (web adapter-sx)
|
||||
(export
|
||||
render-to-sx
|
||||
aser
|
||||
aser-list
|
||||
aser-reserialize
|
||||
aser-fragment
|
||||
aser-call
|
||||
aser-expand-component
|
||||
SPECIAL_FORM_NAMES
|
||||
HO_FORM_NAMES
|
||||
special-form?
|
||||
ho-form?
|
||||
aser-special
|
||||
eval-case-aser)
|
||||
(begin
|
||||
|
||||
(define
|
||||
render-to-sx
|
||||
:effects (render)
|
||||
@@ -570,3 +589,9 @@
|
||||
(= match-val (trampoline (eval-expr test env)))
|
||||
(aser body env)
|
||||
(eval-case-aser match-val (slice clauses 2) env)))))))
|
||||
|
||||
|
||||
)) ;; end define-library
|
||||
|
||||
;; Re-export to global namespace for backward compatibility
|
||||
(import (web adapter-sx))
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -19,6 +19,94 @@
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
;; Stack / Constants
|
||||
|
||||
(define-library (sx bytecode)
|
||||
(export
|
||||
OP_CONST
|
||||
OP_NIL
|
||||
OP_TRUE
|
||||
OP_FALSE
|
||||
OP_POP
|
||||
OP_DUP
|
||||
OP_LOCAL_GET
|
||||
OP_LOCAL_SET
|
||||
OP_UPVALUE_GET
|
||||
OP_UPVALUE_SET
|
||||
OP_GLOBAL_GET
|
||||
OP_GLOBAL_SET
|
||||
OP_JUMP
|
||||
OP_JUMP_IF_FALSE
|
||||
OP_JUMP_IF_TRUE
|
||||
OP_CALL
|
||||
OP_TAIL_CALL
|
||||
OP_RETURN
|
||||
OP_CLOSURE
|
||||
OP_CALL_PRIM
|
||||
OP_APPLY
|
||||
OP_LIST
|
||||
OP_DICT
|
||||
OP_APPEND_BANG
|
||||
OP_ITER_INIT
|
||||
OP_ITER_NEXT
|
||||
OP_MAP_OPEN
|
||||
OP_MAP_APPEND
|
||||
OP_MAP_CLOSE
|
||||
OP_FILTER_TEST
|
||||
OP_HO_MAP
|
||||
OP_HO_FILTER
|
||||
OP_HO_REDUCE
|
||||
OP_HO_FOR_EACH
|
||||
OP_HO_SOME
|
||||
OP_HO_EVERY
|
||||
OP_SCOPE_PUSH
|
||||
OP_SCOPE_POP
|
||||
OP_PROVIDE_PUSH
|
||||
OP_PROVIDE_POP
|
||||
OP_CONTEXT
|
||||
OP_EMIT
|
||||
OP_EMITTED
|
||||
OP_RESET
|
||||
OP_SHIFT
|
||||
OP_DEFINE
|
||||
OP_DEFCOMP
|
||||
OP_DEFISLAND
|
||||
OP_DEFMACRO
|
||||
OP_EXPAND_MACRO
|
||||
OP_STR_CONCAT
|
||||
OP_STR_JOIN
|
||||
OP_SERIALIZE
|
||||
OP_ADD
|
||||
OP_SUB
|
||||
OP_MUL
|
||||
OP_DIV
|
||||
OP_EQ
|
||||
OP_LT
|
||||
OP_GT
|
||||
OP_NOT
|
||||
OP_LEN
|
||||
OP_FIRST
|
||||
OP_REST
|
||||
OP_NTH
|
||||
OP_CONS
|
||||
OP_NEG
|
||||
OP_INC
|
||||
OP_DEC
|
||||
OP_ASER_TAG
|
||||
OP_ASER_FRAG
|
||||
BYTECODE_MAGIC
|
||||
BYTECODE_VERSION
|
||||
CONST_NUMBER
|
||||
CONST_STRING
|
||||
CONST_BOOL
|
||||
CONST_NIL
|
||||
CONST_SYMBOL
|
||||
CONST_KEYWORD
|
||||
CONST_LIST
|
||||
CONST_DICT
|
||||
CONST_CODE
|
||||
opcode-name)
|
||||
(begin
|
||||
|
||||
(define OP_CONST 1) ;; u16 pool_idx — push constant
|
||||
(define OP_NIL 2) ;; push nil
|
||||
(define OP_TRUE 3) ;; push true
|
||||
@@ -161,3 +249,9 @@
|
||||
(= op 50) "RETURN" (= op 52) "CALL_PRIM"
|
||||
(= op 128) "DEFINE" (= op 144) "STR_CONCAT"
|
||||
:else (str "OP_" op))))
|
||||
|
||||
|
||||
)) ;; end define-library
|
||||
|
||||
;; Re-export to global namespace for backward compatibility
|
||||
(import (sx bytecode))
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
(sxbc 1 "45824e713893dfa0"
|
||||
(sxbc 1 "71e91d776cc0a108"
|
||||
(code
|
||||
:constants ("OP_CONST" 1 "OP_NIL" 2 "OP_TRUE" 3 "OP_FALSE" 4 "OP_POP" 5 "OP_DUP" 6 "OP_LOCAL_GET" 16 "OP_LOCAL_SET" 17 "OP_UPVALUE_GET" 18 "OP_UPVALUE_SET" 19 "OP_GLOBAL_GET" 20 "OP_GLOBAL_SET" 21 "OP_JUMP" 32 "OP_JUMP_IF_FALSE" 33 "OP_JUMP_IF_TRUE" 34 "OP_CALL" 48 "OP_TAIL_CALL" 49 "OP_RETURN" 50 "OP_CLOSURE" 51 "OP_CALL_PRIM" 52 "OP_APPLY" 53 "OP_LIST" 64 "OP_DICT" 65 "OP_APPEND_BANG" 66 "OP_ITER_INIT" 80 "OP_ITER_NEXT" 81 "OP_MAP_OPEN" 82 "OP_MAP_APPEND" 83 "OP_MAP_CLOSE" 84 "OP_FILTER_TEST" 85 "OP_HO_MAP" 88 "OP_HO_FILTER" 89 "OP_HO_REDUCE" 90 "OP_HO_FOR_EACH" 91 "OP_HO_SOME" 92 "OP_HO_EVERY" 93 "OP_SCOPE_PUSH" 96 "OP_SCOPE_POP" 97 "OP_PROVIDE_PUSH" 98 "OP_PROVIDE_POP" 99 "OP_CONTEXT" 100 "OP_EMIT" 101 "OP_EMITTED" 102 "OP_RESET" 112 "OP_SHIFT" 113 "OP_DEFINE" 128 "OP_DEFCOMP" 129 "OP_DEFISLAND" 130 "OP_DEFMACRO" 131 "OP_EXPAND_MACRO" 132 "OP_STR_CONCAT" 144 "OP_STR_JOIN" 145 "OP_SERIALIZE" 146 "OP_ADD" 160 "OP_SUB" 161 "OP_MUL" 162 "OP_DIV" 163 "OP_EQ" 164 "OP_LT" 165 "OP_GT" 166 "OP_NOT" 167 "OP_LEN" 168 "OP_FIRST" 169 "OP_REST" 170 "OP_NTH" 171 "OP_CONS" 172 "OP_NEG" 173 "OP_INC" 174 "OP_DEC" 175 "OP_ASER_TAG" 224 "OP_ASER_FRAG" 225 "BYTECODE_MAGIC" "SXBC" "BYTECODE_VERSION" "CONST_NUMBER" "CONST_STRING" "CONST_BOOL" "CONST_NIL" "CONST_SYMBOL" "CONST_KEYWORD" "CONST_LIST" 7 "CONST_DICT" 8 "CONST_CODE" 9 "opcode-name" {:upvalue-count 0 :arity 1 :constants ("=" 1 "CONST" 2 "NIL" 3 "TRUE" 4 "FALSE" 5 "POP" 6 "DUP" 16 "LOCAL_GET" 17 "LOCAL_SET" 20 "GLOBAL_GET" 21 "GLOBAL_SET" 32 "JUMP" 33 "JUMP_IF_FALSE" 48 "CALL" 49 "TAIL_CALL" 50 "RETURN" 52 "CALL_PRIM" 128 "DEFINE" 144 "STR_CONCAT" "str" "OP_") :bytecode (16 0 1 1 0 52 0 0 2 33 6 0 1 2 0 32 59 1 16 0 1 3 0 52 0 0 2 33 6 0 1 4 0 32 41 1 16 0 1 5 0 52 0 0 2 33 6 0 1 6 0 32 23 1 16 0 1 7 0 52 0 0 2 33 6 0 1 8 0 32 5 1 16 0 1 9 0 52 0 0 2 33 6 0 1 10 0 32 243 0 16 0 1 11 0 52 0 0 2 33 6 0 1 12 0 32 225 0 16 0 1 13 0 52 0 0 2 33 6 0 1 14 0 32 207 0 16 0 1 15 0 52 0 0 2 33 6 0 1 16 0 32 189 0 16 0 1 17 0 52 0 0 2 33 6 0 1 18 0 32 171 0 16 0 1 19 0 52 0 0 2 33 6 0 1 20 0 32 153 0 16 0 1 21 0 52 0 0 2 33 6 0 1 22 0 32 135 0 16 0 1 23 0 52 0 0 2 33 6 0 1 24 0 32 117 0 16 0 1 25 0 52 0 0 2 33 6 0 1 26 0 32 99 0 16 0 1 27 0 52 0 0 2 33 6 0 1 28 0 32 81 0 16 0 1 29 0 52 0 0 2 33 6 0 1 30 0 32 63 0 16 0 1 31 0 52 0 0 2 33 6 0 1 32 0 32 45 0 16 0 1 33 0 52 0 0 2 33 6 0 1 34 0 32 27 0 16 0 1 35 0 52 0 0 2 33 6 0 1 36 0 32 9 0 1 38 0 16 0 52 37 0 2 50)}) :bytecode (1 1 0 128 0 0 5 1 3 0 128 2 0 5 1 5 0 128 4 0 5 1 7 0 128 6 0 5 1 9 0 128 8 0 5 1 11 0 128 10 0 5 1 13 0 128 12 0 5 1 15 0 128 14 0 5 1 17 0 128 16 0 5 1 19 0 128 18 0 5 1 21 0 128 20 0 5 1 23 0 128 22 0 5 1 25 0 128 24 0 5 1 27 0 128 26 0 5 1 29 0 128 28 0 5 1 31 0 128 30 0 5 1 33 0 128 32 0 5 1 35 0 128 34 0 5 1 37 0 128 36 0 5 1 39 0 128 38 0 5 1 41 0 128 40 0 5 1 43 0 128 42 0 5 1 45 0 128 44 0 5 1 47 0 128 46 0 5 1 49 0 128 48 0 5 1 51 0 128 50 0 5 1 53 0 128 52 0 5 1 55 0 128 54 0 5 1 57 0 128 56 0 5 1 59 0 128 58 0 5 1 61 0 128 60 0 5 1 63 0 128 62 0 5 1 65 0 128 64 0 5 1 67 0 128 66 0 5 1 69 0 128 68 0 5 1 71 0 128 70 0 5 1 73 0 128 72 0 5 1 75 0 128 74 0 5 1 77 0 128 76 0 5 1 79 0 128 78 0 5 1 81 0 128 80 0 5 1 83 0 128 82 0 5 1 85 0 128 84 0 5 1 87 0 128 86 0 5 1 89 0 128 88 0 5 1 91 0 128 90 0 5 1 93 0 128 92 0 5 1 95 0 128 94 0 5 1 97 0 128 96 0 5 1 99 0 128 98 0 5 1 101 0 128 100 0 5 1 103 0 128 102 0 5 1 105 0 128 104 0 5 1 107 0 128 106 0 5 1 109 0 128 108 0 5 1 111 0 128 110 0 5 1 113 0 128 112 0 5 1 115 0 128 114 0 5 1 117 0 128 116 0 5 1 119 0 128 118 0 5 1 121 0 128 120 0 5 1 123 0 128 122 0 5 1 125 0 128 124 0 5 1 127 0 128 126 0 5 1 129 0 128 128 0 5 1 131 0 128 130 0 5 1 133 0 128 132 0 5 1 135 0 128 134 0 5 1 137 0 128 136 0 5 1 139 0 128 138 0 5 1 141 0 128 140 0 5 1 143 0 128 142 0 5 1 1 0 128 144 0 5 1 1 0 128 145 0 5 1 3 0 128 146 0 5 1 5 0 128 147 0 5 1 7 0 128 148 0 5 1 9 0 128 149 0 5 1 11 0 128 150 0 5 1 152 0 128 151 0 5 1 154 0 128 153 0 5 1 156 0 128 155 0 5 51 158 0 128 157 0 50)))
|
||||
:constants ("define-library" "sx" "bytecode" "export" "OP_CONST" "OP_NIL" "OP_TRUE" "OP_FALSE" "OP_POP" "OP_DUP" "OP_LOCAL_GET" "OP_LOCAL_SET" "OP_UPVALUE_GET" "OP_UPVALUE_SET" "OP_GLOBAL_GET" "OP_GLOBAL_SET" "OP_JUMP" "OP_JUMP_IF_FALSE" "OP_JUMP_IF_TRUE" "OP_CALL" "OP_TAIL_CALL" "OP_RETURN" "OP_CLOSURE" "OP_CALL_PRIM" "OP_APPLY" "OP_LIST" "OP_DICT" "OP_APPEND_BANG" "OP_ITER_INIT" "OP_ITER_NEXT" "OP_MAP_OPEN" "OP_MAP_APPEND" "OP_MAP_CLOSE" "OP_FILTER_TEST" "OP_HO_MAP" "OP_HO_FILTER" "OP_HO_REDUCE" "OP_HO_FOR_EACH" "OP_HO_SOME" "OP_HO_EVERY" "OP_SCOPE_PUSH" "OP_SCOPE_POP" "OP_PROVIDE_PUSH" "OP_PROVIDE_POP" "OP_CONTEXT" "OP_EMIT" "OP_EMITTED" "OP_RESET" "OP_SHIFT" "OP_DEFINE" "OP_DEFCOMP" "OP_DEFISLAND" "OP_DEFMACRO" "OP_EXPAND_MACRO" "OP_STR_CONCAT" "OP_STR_JOIN" "OP_SERIALIZE" "OP_ADD" "OP_SUB" "OP_MUL" "OP_DIV" "OP_EQ" "OP_LT" "OP_GT" "OP_NOT" "OP_LEN" "OP_FIRST" "OP_REST" "OP_NTH" "OP_CONS" "OP_NEG" "OP_INC" "OP_DEC" "OP_ASER_TAG" "OP_ASER_FRAG" "BYTECODE_MAGIC" "BYTECODE_VERSION" "CONST_NUMBER" "CONST_STRING" "CONST_BOOL" "CONST_NIL" "CONST_SYMBOL" "CONST_KEYWORD" "CONST_LIST" "CONST_DICT" "CONST_CODE" "opcode-name" 1 2 3 4 5 6 16 17 18 19 20 21 32 33 34 48 49 50 51 52 53 64 65 66 80 81 82 83 84 85 88 89 90 91 92 93 96 97 98 99 100 101 102 112 113 128 129 130 131 132 144 145 146 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 224 225 "SXBC" 7 8 9 {:upvalue-count 0 :arity 1 :constants ("=" 1 "CONST" 2 "NIL" 3 "TRUE" 4 "FALSE" 5 "POP" 6 "DUP" 16 "LOCAL_GET" 17 "LOCAL_SET" 20 "GLOBAL_GET" 21 "GLOBAL_SET" 32 "JUMP" 33 "JUMP_IF_FALSE" 48 "CALL" 49 "TAIL_CALL" 50 "RETURN" 52 "CALL_PRIM" 128 "DEFINE" 144 "STR_CONCAT" "str" "OP_") :bytecode (16 0 1 1 0 52 0 0 2 33 6 0 1 2 0 32 59 1 16 0 1 3 0 52 0 0 2 33 6 0 1 4 0 32 41 1 16 0 1 5 0 52 0 0 2 33 6 0 1 6 0 32 23 1 16 0 1 7 0 52 0 0 2 33 6 0 1 8 0 32 5 1 16 0 1 9 0 52 0 0 2 33 6 0 1 10 0 32 243 0 16 0 1 11 0 52 0 0 2 33 6 0 1 12 0 32 225 0 16 0 1 13 0 52 0 0 2 33 6 0 1 14 0 32 207 0 16 0 1 15 0 52 0 0 2 33 6 0 1 16 0 32 189 0 16 0 1 17 0 52 0 0 2 33 6 0 1 18 0 32 171 0 16 0 1 19 0 52 0 0 2 33 6 0 1 20 0 32 153 0 16 0 1 21 0 52 0 0 2 33 6 0 1 22 0 32 135 0 16 0 1 23 0 52 0 0 2 33 6 0 1 24 0 32 117 0 16 0 1 25 0 52 0 0 2 33 6 0 1 26 0 32 99 0 16 0 1 27 0 52 0 0 2 33 6 0 1 28 0 32 81 0 16 0 1 29 0 52 0 0 2 33 6 0 1 30 0 32 63 0 16 0 1 31 0 52 0 0 2 33 6 0 1 32 0 32 45 0 16 0 1 33 0 52 0 0 2 33 6 0 1 34 0 32 27 0 16 0 1 35 0 52 0 0 2 33 6 0 1 36 0 32 9 0 1 38 0 16 0 52 37 0 2 50)} "import") :bytecode (20 0 0 20 1 0 20 2 0 48 1 20 3 0 20 4 0 20 5 0 20 6 0 20 7 0 20 8 0 20 9 0 20 10 0 20 11 0 20 12 0 20 13 0 20 14 0 20 15 0 20 16 0 20 17 0 20 18 0 20 19 0 20 20 0 20 21 0 20 22 0 20 23 0 20 24 0 20 25 0 20 26 0 20 27 0 20 28 0 20 29 0 20 30 0 20 31 0 20 32 0 20 33 0 20 34 0 20 35 0 20 36 0 20 37 0 20 38 0 20 39 0 20 40 0 20 41 0 20 42 0 20 43 0 20 44 0 20 45 0 20 46 0 20 47 0 20 48 0 20 49 0 20 50 0 20 51 0 20 52 0 20 53 0 20 54 0 20 55 0 20 56 0 20 57 0 20 58 0 20 59 0 20 60 0 20 61 0 20 62 0 20 63 0 20 64 0 20 65 0 20 66 0 20 67 0 20 68 0 20 69 0 20 70 0 20 71 0 20 72 0 20 73 0 20 74 0 20 75 0 20 76 0 20 77 0 20 78 0 20 79 0 20 80 0 20 81 0 20 82 0 20 83 0 20 84 0 20 85 0 20 86 0 48 83 1 87 0 128 4 0 5 1 88 0 128 5 0 5 1 89 0 128 6 0 5 1 90 0 128 7 0 5 1 91 0 128 8 0 5 1 92 0 128 9 0 5 1 93 0 128 10 0 5 1 94 0 128 11 0 5 1 95 0 128 12 0 5 1 96 0 128 13 0 5 1 97 0 128 14 0 5 1 98 0 128 15 0 5 1 99 0 128 16 0 5 1 100 0 128 17 0 5 1 101 0 128 18 0 5 1 102 0 128 19 0 5 1 103 0 128 20 0 5 1 104 0 128 21 0 5 1 105 0 128 22 0 5 1 106 0 128 23 0 5 1 107 0 128 24 0 5 1 108 0 128 25 0 5 1 109 0 128 26 0 5 1 110 0 128 27 0 5 1 111 0 128 28 0 5 1 112 0 128 29 0 5 1 113 0 128 30 0 5 1 114 0 128 31 0 5 1 115 0 128 32 0 5 1 116 0 128 33 0 5 1 117 0 128 34 0 5 1 118 0 128 35 0 5 1 119 0 128 36 0 5 1 120 0 128 37 0 5 1 121 0 128 38 0 5 1 122 0 128 39 0 5 1 123 0 128 40 0 5 1 124 0 128 41 0 5 1 125 0 128 42 0 5 1 126 0 128 43 0 5 1 127 0 128 44 0 5 1 128 0 128 45 0 5 1 129 0 128 46 0 5 1 130 0 128 47 0 5 1 131 0 128 48 0 5 1 132 0 128 49 0 5 1 133 0 128 50 0 5 1 134 0 128 51 0 5 1 135 0 128 52 0 5 1 136 0 128 53 0 5 1 137 0 128 54 0 5 1 138 0 128 55 0 5 1 139 0 128 56 0 5 1 140 0 128 57 0 5 1 141 0 128 58 0 5 1 142 0 128 59 0 5 1 143 0 128 60 0 5 1 144 0 128 61 0 5 1 145 0 128 62 0 5 1 146 0 128 63 0 5 1 147 0 128 64 0 5 1 148 0 128 65 0 5 1 149 0 128 66 0 5 1 150 0 128 67 0 5 1 151 0 128 68 0 5 1 152 0 128 69 0 5 1 153 0 128 70 0 5 1 154 0 128 71 0 5 1 155 0 128 72 0 5 1 156 0 128 73 0 5 1 157 0 128 74 0 5 1 158 0 128 75 0 5 1 87 0 128 76 0 5 1 87 0 128 77 0 5 1 88 0 128 78 0 5 1 89 0 128 79 0 5 1 90 0 128 80 0 5 1 91 0 128 81 0 5 1 92 0 128 82 0 5 1 159 0 128 83 0 5 1 160 0 128 84 0 5 1 161 0 128 85 0 5 51 162 0 128 86 0 48 3 5 20 163 0 20 1 0 20 2 0 48 1 48 1 50)))
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -1,3 +1,32 @@
|
||||
|
||||
|
||||
(define-library (sx signals)
|
||||
(export
|
||||
make-signal
|
||||
signal?
|
||||
signal-value
|
||||
signal-set-value!
|
||||
signal-subscribers
|
||||
signal-add-sub!
|
||||
signal-remove-sub!
|
||||
signal-deps
|
||||
signal-set-deps!
|
||||
signal
|
||||
deref
|
||||
reset!
|
||||
swap!
|
||||
computed
|
||||
effect
|
||||
*batch-depth*
|
||||
*batch-queue*
|
||||
batch
|
||||
notify-subscribers
|
||||
flush-subscribers
|
||||
dispose-computed
|
||||
with-island-scope
|
||||
register-in-scope)
|
||||
(begin
|
||||
|
||||
(define
|
||||
make-signal
|
||||
(fn
|
||||
@@ -193,3 +222,9 @@
|
||||
(let
|
||||
((collector (scope-peek "sx-island-scope")))
|
||||
(when collector (cek-call collector (list disposable))))))
|
||||
|
||||
|
||||
)) ;; end define-library
|
||||
|
||||
;; Re-export to global namespace for backward compatibility
|
||||
(import (sx signals))
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,3 +1,28 @@
|
||||
|
||||
|
||||
(define-library (web deps)
|
||||
(export
|
||||
scan-refs
|
||||
scan-refs-walk
|
||||
transitive-deps-walk
|
||||
transitive-deps
|
||||
compute-all-deps
|
||||
scan-components-from-source
|
||||
components-needed
|
||||
page-component-bundle
|
||||
page-css-classes
|
||||
scan-io-refs-walk
|
||||
scan-io-refs
|
||||
transitive-io-refs-walk
|
||||
transitive-io-refs
|
||||
compute-all-io-refs
|
||||
component-io-refs-cached
|
||||
component-pure?
|
||||
render-target
|
||||
page-render-plan
|
||||
env-components)
|
||||
(begin
|
||||
|
||||
(define
|
||||
scan-refs
|
||||
:effects ()
|
||||
@@ -335,3 +360,9 @@
|
||||
((k :as string))
|
||||
(let ((v (env-get env k))) (or (component? v) (macro? v))))
|
||||
(keys env))))
|
||||
|
||||
|
||||
)) ;; end define-library
|
||||
|
||||
;; Re-export to global namespace for backward compatibility
|
||||
(import (web deps))
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,3 +1,42 @@
|
||||
|
||||
|
||||
(define-library (web engine)
|
||||
(export
|
||||
ENGINE_VERBS
|
||||
DEFAULT_SWAP
|
||||
parse-time
|
||||
parse-trigger-spec
|
||||
default-trigger
|
||||
get-verb-info
|
||||
build-request-headers
|
||||
process-response-headers
|
||||
parse-swap-spec
|
||||
parse-retry-spec
|
||||
next-retry-ms
|
||||
filter-params
|
||||
resolve-target
|
||||
apply-optimistic
|
||||
revert-optimistic
|
||||
find-oob-swaps
|
||||
morph-node
|
||||
sync-attrs
|
||||
morph-children
|
||||
morph-island-children
|
||||
morph-marsh
|
||||
process-signal-updates
|
||||
swap-dom-nodes
|
||||
insert-remaining-siblings
|
||||
swap-html-string
|
||||
handle-history
|
||||
PRELOAD_TTL
|
||||
preload-cache-get
|
||||
preload-cache-set
|
||||
classify-trigger
|
||||
should-boost-link?
|
||||
should-boost-form?
|
||||
parse-sse-swap)
|
||||
(begin
|
||||
|
||||
(define ENGINE_VERBS (list "get" "post" "put" "delete" "patch"))
|
||||
|
||||
(define DEFAULT_SWAP "outerHTML")
|
||||
@@ -806,3 +845,9 @@
|
||||
parse-sse-swap
|
||||
:effects (io)
|
||||
(fn (el) (or (dom-get-attr el "sx-sse-swap") "message")))
|
||||
|
||||
|
||||
)) ;; end define-library
|
||||
|
||||
;; Re-export to global namespace for backward compatibility
|
||||
(import (web engine))
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -20,6 +20,20 @@
|
||||
;; ==========================================================================
|
||||
|
||||
;; Registry of freeze scopes: name → list of {name signal} entries
|
||||
|
||||
(define-library (sx freeze)
|
||||
(export
|
||||
freeze-registry
|
||||
freeze-signal
|
||||
freeze-scope
|
||||
cek-freeze-scope
|
||||
cek-freeze-all
|
||||
cek-thaw-scope
|
||||
cek-thaw-all
|
||||
freeze-to-sx
|
||||
thaw-from-sx)
|
||||
(begin
|
||||
|
||||
(define freeze-registry (dict))
|
||||
|
||||
;; Register a signal in the current freeze scope
|
||||
@@ -92,3 +106,9 @@
|
||||
(when (not (empty? parsed))
|
||||
(let ((frozen (first parsed)))
|
||||
(cek-thaw-scope (get frozen "name") frozen))))))
|
||||
|
||||
|
||||
)) ;; end define-library
|
||||
|
||||
;; Re-export to global namespace for backward compatibility
|
||||
(import (sx freeze))
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
(sxbc 1 "19bca721c37b25b6"
|
||||
(sxbc 1 "320fb4826d09fed3"
|
||||
(code
|
||||
:constants ("freeze-registry" "dict" "freeze-signal" {:upvalue-count 0 :arity 2 :constants ("context" "sx-freeze-scope" "get" "freeze-registry" "list" "append!" "dict" "name" "signal" "dict-set!") :bytecode (1 1 0 2 52 0 0 2 17 2 16 2 33 55 0 20 3 0 16 2 52 2 0 2 6 34 5 0 5 52 4 0 0 17 3 16 3 1 7 0 16 0 1 8 0 16 1 52 6 0 4 52 5 0 2 5 20 3 0 16 2 16 3 52 9 0 3 32 1 0 2 50)} "freeze-scope" {:upvalue-count 0 :arity 2 :constants ("scope-push!" "sx-freeze-scope" "dict-set!" "freeze-registry" "list" "cek-call" "scope-pop!") :bytecode (1 1 0 16 0 52 0 0 2 5 20 3 0 16 0 52 4 0 0 52 2 0 3 5 20 5 0 16 1 2 48 2 5 1 1 0 52 6 0 1 5 2 50)} "cek-freeze-scope" {:upvalue-count 0 :arity 1 :constants ("get" "freeze-registry" "list" "dict" "for-each" {:upvalue-count 1 :arity 1 :constants ("dict-set!" "get" "name" "signal-value" "signal") :bytecode (18 0 16 0 1 2 0 52 1 0 2 20 3 0 16 0 1 4 0 52 1 0 2 48 1 52 0 0 3 50)} "name" "signals") :bytecode (20 1 0 16 0 52 0 0 2 6 34 5 0 5 52 2 0 0 17 1 52 3 0 0 17 2 51 5 0 1 2 16 1 52 4 0 2 5 1 6 0 16 0 1 7 0 16 2 52 3 0 4 50)} "cek-freeze-all" {:upvalue-count 0 :arity 0 :constants ("map" {:upvalue-count 0 :arity 1 :constants ("cek-freeze-scope") :bytecode (20 0 0 16 0 49 1 50)} "keys" "freeze-registry") :bytecode (51 1 0 20 3 0 52 2 0 1 52 0 0 2 50)} "cek-thaw-scope" {:upvalue-count 0 :arity 2 :constants ("get" "freeze-registry" "list" "signals" "for-each" {:upvalue-count 1 :arity 1 :constants ("get" "name" "signal" "not" "nil?" "reset!") :bytecode (16 0 1 1 0 52 0 0 2 17 1 16 0 1 2 0 52 0 0 2 17 2 18 0 16 1 52 0 0 2 17 3 16 3 52 4 0 1 52 3 0 1 33 12 0 20 5 0 16 2 16 3 49 2 32 1 0 2 50)}) :bytecode (20 1 0 16 0 52 0 0 2 6 34 5 0 5 52 2 0 0 17 2 16 1 1 3 0 52 0 0 2 17 3 16 3 33 14 0 51 5 0 1 3 16 2 52 4 0 2 32 1 0 2 50)} "cek-thaw-all" {:upvalue-count 0 :arity 1 :constants ("for-each" {:upvalue-count 0 :arity 1 :constants ("cek-thaw-scope" "get" "name") :bytecode (20 0 0 16 0 1 2 0 52 1 0 2 16 0 49 2 50)}) :bytecode (51 1 0 16 0 52 0 0 2 50)} "freeze-to-sx" {:upvalue-count 0 :arity 1 :constants ("sx-serialize" "cek-freeze-scope") :bytecode (20 0 0 20 1 0 16 0 48 1 49 1 50)} "thaw-from-sx" {:upvalue-count 0 :arity 1 :constants ("sx-parse" "not" "empty?" "first" "cek-thaw-scope" "get" "name") :bytecode (20 0 0 16 0 48 1 17 1 16 1 52 2 0 1 52 1 0 1 33 27 0 16 1 52 3 0 1 17 2 20 4 0 16 2 1 6 0 52 5 0 2 16 2 49 2 32 1 0 2 50)}) :bytecode (52 1 0 0 128 0 0 5 51 3 0 128 2 0 5 51 5 0 128 4 0 5 51 7 0 128 6 0 5 51 9 0 128 8 0 5 51 11 0 128 10 0 5 51 13 0 128 12 0 5 51 15 0 128 14 0 5 51 17 0 128 16 0 50)))
|
||||
:constants ("define-library" "sx" "freeze" "export" "freeze-registry" "freeze-signal" "freeze-scope" "cek-freeze-scope" "cek-freeze-all" "cek-thaw-scope" "cek-thaw-all" "freeze-to-sx" "thaw-from-sx" "dict" {:upvalue-count 0 :arity 2 :constants ("context" "sx-freeze-scope" "get" "freeze-registry" "list" "append!" "dict" "name" "signal" "dict-set!") :bytecode (1 1 0 2 52 0 0 2 17 2 16 2 33 55 0 20 3 0 16 2 52 2 0 2 6 34 5 0 5 52 4 0 0 17 3 16 3 1 7 0 16 0 1 8 0 16 1 52 6 0 4 52 5 0 2 5 20 3 0 16 2 16 3 52 9 0 3 32 1 0 2 50)} {:upvalue-count 0 :arity 2 :constants ("scope-push!" "sx-freeze-scope" "dict-set!" "freeze-registry" "list" "cek-call" "scope-pop!") :bytecode (1 1 0 16 0 52 0 0 2 5 20 3 0 16 0 52 4 0 0 52 2 0 3 5 16 1 2 52 5 0 2 5 1 1 0 52 6 0 1 5 2 50)} {:upvalue-count 0 :arity 1 :constants ("get" "freeze-registry" "list" "dict" "for-each" {:upvalue-count 1 :arity 1 :constants ("dict-set!" "get" "name" "signal-value" "signal") :bytecode (18 0 16 0 1 2 0 52 1 0 2 20 3 0 16 0 1 4 0 52 1 0 2 48 1 52 0 0 3 50)} "name" "signals") :bytecode (20 1 0 16 0 52 0 0 2 6 34 5 0 5 52 2 0 0 17 1 52 3 0 0 17 2 51 5 0 1 2 16 1 52 4 0 2 5 1 6 0 16 0 1 7 0 16 2 52 3 0 4 50)} {:upvalue-count 0 :arity 0 :constants ("map" {:upvalue-count 0 :arity 1 :constants ("cek-freeze-scope") :bytecode (20 0 0 16 0 49 1 50)} "keys" "freeze-registry") :bytecode (51 1 0 20 3 0 52 2 0 1 52 0 0 2 50)} {:upvalue-count 0 :arity 2 :constants ("get" "freeze-registry" "list" "signals" "for-each" {:upvalue-count 1 :arity 1 :constants ("get" "name" "signal" "not" "nil?" "reset!") :bytecode (16 0 1 1 0 52 0 0 2 17 1 16 0 1 2 0 52 0 0 2 17 2 18 0 16 1 52 0 0 2 17 3 16 3 52 4 0 1 52 3 0 1 33 12 0 20 5 0 16 2 16 3 49 2 32 1 0 2 50)}) :bytecode (20 1 0 16 0 52 0 0 2 6 34 5 0 5 52 2 0 0 17 2 16 1 1 3 0 52 0 0 2 17 3 16 3 33 14 0 51 5 0 1 3 16 2 52 4 0 2 32 1 0 2 50)} {:upvalue-count 0 :arity 1 :constants ("for-each" {:upvalue-count 0 :arity 1 :constants ("cek-thaw-scope" "get" "name") :bytecode (20 0 0 16 0 1 2 0 52 1 0 2 16 0 49 2 50)}) :bytecode (51 1 0 16 0 52 0 0 2 50)} {:upvalue-count 0 :arity 1 :constants ("sx-serialize" "cek-freeze-scope") :bytecode (20 1 0 16 0 48 1 52 0 0 1 50)} {:upvalue-count 0 :arity 1 :constants ("sx-parse" "not" "empty?" "first" "cek-thaw-scope" "get" "name") :bytecode (20 0 0 16 0 48 1 17 1 16 1 52 2 0 1 52 1 0 1 33 27 0 16 1 52 3 0 1 17 2 20 4 0 16 2 1 6 0 52 5 0 2 16 2 49 2 32 1 0 2 50)} "import") :bytecode (20 0 0 20 1 0 20 2 0 48 1 20 3 0 20 4 0 20 5 0 20 6 0 20 7 0 20 8 0 20 9 0 20 10 0 20 11 0 20 12 0 48 9 52 13 0 0 128 4 0 5 51 14 0 128 5 0 5 51 15 0 128 6 0 5 51 16 0 128 7 0 5 51 17 0 128 8 0 5 51 18 0 128 9 0 5 51 19 0 128 10 0 5 51 20 0 128 11 0 5 51 21 0 128 12 0 48 3 5 20 22 0 20 1 0 20 2 0 48 1 48 1 50)))
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
(sxbc 1 "57726b5b82c1a3cb"
|
||||
(code
|
||||
:constants ("assert-signal-value" {:upvalue-count 0 :arity 2 :constants ("deref" "assert=" "str" "Expected signal value " ", got ") :bytecode (20 0 0 16 0 48 1 17 2 20 1 0 16 2 16 1 1 3 0 16 1 1 4 0 16 2 52 2 0 4 49 3 50)} "assert-signal-has-subscribers" {:upvalue-count 0 :arity 1 :constants ("assert" ">" "len" "signal-subscribers" 0 "Expected signal to have subscribers") :bytecode (20 0 0 20 3 0 16 0 48 1 52 2 0 1 1 4 0 52 1 0 2 1 5 0 49 2 50)} "assert-signal-no-subscribers" {:upvalue-count 0 :arity 1 :constants ("assert" "=" "len" "signal-subscribers" 0 "Expected signal to have no subscribers") :bytecode (20 0 0 20 3 0 16 0 48 1 52 2 0 1 1 4 0 52 1 0 2 1 5 0 49 2 50)} "assert-signal-subscriber-count" {:upvalue-count 0 :arity 2 :constants ("len" "signal-subscribers" "assert=" "str" "Expected " " subscribers, got ") :bytecode (20 1 0 16 0 48 1 52 0 0 1 17 2 20 2 0 16 2 16 1 1 4 0 16 1 1 5 0 16 2 52 3 0 4 49 3 50)} "simulate-signal-set!" {:upvalue-count 0 :arity 2 :constants ("reset!") :bytecode (20 0 0 16 0 16 1 49 2 50)} "simulate-signal-swap!" {:upvalue-count 0 :arity 2 :constants ("swap!") :bytecode (20 0 0 16 0 16 1 49 2 50)} "assert-computed-dep-count" {:upvalue-count 0 :arity 2 :constants ("len" "signal-deps" "assert=" "str" "Expected " " deps, got ") :bytecode (20 1 0 16 0 48 1 52 0 0 1 17 2 20 2 0 16 2 16 1 1 4 0 16 1 1 5 0 16 2 52 3 0 4 49 3 50)} "assert-computed-depends-on" {:upvalue-count 0 :arity 2 :constants ("assert" "contains?" "signal-deps" "Expected computed to depend on the given signal") :bytecode (20 0 0 20 2 0 16 0 48 1 16 1 52 1 0 2 1 3 0 49 2 50)} "count-effect-runs" {:upvalue-count 0 :arity 1 :constants ("signal" 0 "effect" {:upvalue-count 1 :arity 0 :constants ("deref") :bytecode (20 0 0 18 0 49 1 50)} {:upvalue-count 2 :arity 0 :constants ("+" 1 "cek-call") :bytecode (18 0 1 1 0 52 0 0 2 19 0 5 20 2 0 18 1 2 49 2 50)}) :bytecode (20 0 0 1 1 0 48 1 17 1 20 2 0 51 3 0 1 1 48 1 5 1 1 0 17 2 20 2 0 51 4 0 1 2 1 0 48 1 17 3 16 2 50)} "make-test-signal" {:upvalue-count 0 :arity 1 :constants ("signal" "list" "effect" {:upvalue-count 2 :arity 0 :constants ("append!" "deref") :bytecode (18 0 20 1 0 18 1 48 1 52 0 0 2 50)} "history") :bytecode (20 0 0 16 0 48 1 17 1 52 1 0 0 17 2 20 2 0 51 3 0 1 2 1 1 48 1 5 1 0 0 16 1 1 4 0 16 2 65 2 0 50)} "assert-batch-coalesces" {:upvalue-count 0 :arity 2 :constants (0 "signal" "effect" {:upvalue-count 2 :arity 0 :constants ("deref" "+" 1) :bytecode (20 0 0 18 0 48 1 5 18 1 1 2 0 52 1 0 2 19 1 50)} "batch" "assert=" "str" "Expected " " notifications, got ") :bytecode (1 0 0 17 2 20 1 0 1 0 0 48 1 17 3 20 2 0 51 3 0 1 3 1 2 48 1 5 1 0 0 17 2 5 20 4 0 16 0 48 1 5 20 5 0 16 2 16 1 1 7 0 16 1 1 8 0 16 2 52 6 0 4 49 3 50)}) :bytecode (51 1 0 128 0 0 5 51 3 0 128 2 0 5 51 5 0 128 4 0 5 51 7 0 128 6 0 5 51 9 0 128 8 0 5 51 11 0 128 10 0 5 51 13 0 128 12 0 5 51 15 0 128 14 0 5 51 17 0 128 16 0 5 51 19 0 128 18 0 5 51 21 0 128 20 0 50)))
|
||||
:constants ("assert-signal-value" {:upvalue-count 0 :arity 2 :constants ("deref" "assert=" "str" "Expected signal value " ", got ") :bytecode (20 0 0 16 0 48 1 17 2 20 1 0 16 2 16 1 1 3 0 16 1 1 4 0 16 2 52 2 0 4 49 3 50)} "assert-signal-has-subscribers" {:upvalue-count 0 :arity 1 :constants ("assert" ">" "len" "signal-subscribers" 0 "Expected signal to have subscribers") :bytecode (20 0 0 20 3 0 16 0 48 1 52 2 0 1 1 4 0 52 1 0 2 1 5 0 49 2 50)} "assert-signal-no-subscribers" {:upvalue-count 0 :arity 1 :constants ("assert" "=" "len" "signal-subscribers" 0 "Expected signal to have no subscribers") :bytecode (20 0 0 20 3 0 16 0 48 1 52 2 0 1 1 4 0 52 1 0 2 1 5 0 49 2 50)} "assert-signal-subscriber-count" {:upvalue-count 0 :arity 2 :constants ("len" "signal-subscribers" "assert=" "str" "Expected " " subscribers, got ") :bytecode (20 1 0 16 0 48 1 52 0 0 1 17 2 20 2 0 16 2 16 1 1 4 0 16 1 1 5 0 16 2 52 3 0 4 49 3 50)} "simulate-signal-set!" {:upvalue-count 0 :arity 2 :constants ("reset!") :bytecode (20 0 0 16 0 16 1 49 2 50)} "simulate-signal-swap!" {:upvalue-count 0 :arity 2 :constants ("swap!") :bytecode (20 0 0 16 0 16 1 49 2 50)} "assert-computed-dep-count" {:upvalue-count 0 :arity 2 :constants ("len" "signal-deps" "assert=" "str" "Expected " " deps, got ") :bytecode (20 1 0 16 0 48 1 52 0 0 1 17 2 20 2 0 16 2 16 1 1 4 0 16 1 1 5 0 16 2 52 3 0 4 49 3 50)} "assert-computed-depends-on" {:upvalue-count 0 :arity 2 :constants ("assert" "contains?" "signal-deps" "Expected computed to depend on the given signal") :bytecode (20 0 0 20 2 0 16 0 48 1 16 1 52 1 0 2 1 3 0 49 2 50)} "count-effect-runs" {:upvalue-count 0 :arity 1 :constants ("signal" 0 "effect" {:upvalue-count 1 :arity 0 :constants ("deref") :bytecode (20 0 0 18 0 49 1 50)} {:upvalue-count 2 :arity 0 :constants ("+" 1 "cek-call") :bytecode (18 0 1 1 0 52 0 0 2 19 0 5 18 1 2 52 2 0 2 50)}) :bytecode (20 0 0 1 1 0 48 1 17 1 20 2 0 51 3 0 1 1 48 1 5 1 1 0 17 2 20 2 0 51 4 0 1 2 1 0 48 1 17 3 16 2 50)} "make-test-signal" {:upvalue-count 0 :arity 1 :constants ("signal" "list" "effect" {:upvalue-count 2 :arity 0 :constants ("append!" "deref") :bytecode (18 0 20 1 0 18 1 48 1 52 0 0 2 50)} "history") :bytecode (20 0 0 16 0 48 1 17 1 52 1 0 0 17 2 20 2 0 51 3 0 1 2 1 1 48 1 5 1 0 0 16 1 1 4 0 16 2 65 2 0 50)} "assert-batch-coalesces" {:upvalue-count 0 :arity 2 :constants (0 "signal" "effect" {:upvalue-count 2 :arity 0 :constants ("deref" "+" 1) :bytecode (20 0 0 18 0 48 1 5 18 1 1 2 0 52 1 0 2 19 1 50)} "batch" "assert=" "str" "Expected " " notifications, got ") :bytecode (1 0 0 17 2 20 1 0 1 0 0 48 1 17 3 20 2 0 51 3 0 1 3 1 2 48 1 5 1 0 0 17 2 5 20 4 0 16 0 48 1 5 20 5 0 16 2 16 1 1 7 0 16 1 1 8 0 16 2 52 6 0 4 49 3 50)}) :bytecode (51 1 0 128 0 0 5 51 3 0 128 2 0 5 51 5 0 128 4 0 5 51 7 0 128 6 0 5 51 9 0 128 8 0 5 51 11 0 128 10 0 5 51 13 0 128 12 0 5 51 15 0 128 14 0 5 51 17 0 128 16 0 5 51 19 0 128 18 0 5 51 21 0 128 20 0 50)))
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,4 +1,30 @@
|
||||
;; Assert condition is truthy, error with message
|
||||
|
||||
(define-library (sx harness)
|
||||
(export
|
||||
assert
|
||||
assert=
|
||||
default-platform
|
||||
make-harness
|
||||
harness-reset!
|
||||
harness-log
|
||||
harness-get
|
||||
harness-set!
|
||||
make-interceptor
|
||||
install-interceptors
|
||||
io-calls
|
||||
io-call-count
|
||||
io-call-nth
|
||||
io-call-args
|
||||
io-call-result
|
||||
assert-io-called
|
||||
assert-no-io
|
||||
assert-io-count
|
||||
assert-io-args
|
||||
assert-io-result
|
||||
assert-state)
|
||||
(begin
|
||||
|
||||
(define assert (fn (condition msg) (when (not condition) (error (or msg "Assertion failed")))))
|
||||
|
||||
;; Assert two values are equal
|
||||
@@ -60,3 +86,9 @@
|
||||
|
||||
;; Assert a state key has the expected value
|
||||
(define assert-state :effects () (fn (session key expected) (let ((actual (harness-get session key))) (assert (equal? actual expected) (str "Expected state " key " to be " (str expected) " but got " (str actual))))))
|
||||
|
||||
|
||||
)) ;; end define-library
|
||||
|
||||
;; Re-export to global namespace for backward compatibility
|
||||
(import (sx harness))
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,3 +1,68 @@
|
||||
|
||||
|
||||
(define-library (web orchestration)
|
||||
(export
|
||||
_preload-cache
|
||||
dispatch-trigger-events
|
||||
execute-request
|
||||
do-fetch
|
||||
handle-fetch-success
|
||||
flush-collected-styles
|
||||
handle-sx-response
|
||||
handle-html-response
|
||||
handle-retry
|
||||
bind-triggers
|
||||
bind-event
|
||||
post-swap
|
||||
process-settle-hooks
|
||||
activate-scripts
|
||||
process-oob-swaps
|
||||
hoist-head-elements
|
||||
process-boosted
|
||||
boost-descendants
|
||||
_page-data-cache
|
||||
_page-data-cache-ttl
|
||||
page-data-cache-key
|
||||
page-data-cache-get
|
||||
page-data-cache-set
|
||||
invalidate-page-cache
|
||||
invalidate-all-page-cache
|
||||
update-page-cache
|
||||
process-cache-directives
|
||||
_optimistic-snapshots
|
||||
optimistic-cache-update
|
||||
optimistic-cache-revert
|
||||
optimistic-cache-confirm
|
||||
submit-mutation
|
||||
_is-online
|
||||
_offline-queue
|
||||
offline-is-online?
|
||||
offline-set-online!
|
||||
offline-queue-mutation
|
||||
offline-sync
|
||||
offline-pending-count
|
||||
offline-aware-mutation
|
||||
current-page-layout
|
||||
swap-rendered-content
|
||||
resolve-route-target
|
||||
deps-satisfied?
|
||||
try-client-route
|
||||
bind-client-route-link
|
||||
process-sse
|
||||
bind-sse
|
||||
bind-sse-swap
|
||||
bind-inline-handlers
|
||||
bind-preload-for
|
||||
do-preload
|
||||
VERB_SELECTOR
|
||||
process-elements
|
||||
process-one
|
||||
process-emit-elements
|
||||
save-scroll-position
|
||||
handle-popstate
|
||||
engine-init)
|
||||
(begin
|
||||
|
||||
(define _preload-cache (dict))
|
||||
|
||||
(define
|
||||
@@ -1566,3 +1631,9 @@
|
||||
(fn
|
||||
()
|
||||
(do (sx-process-scripts nil) (sx-hydrate nil) (process-elements nil))))
|
||||
|
||||
|
||||
)) ;; end define-library
|
||||
|
||||
;; Re-export to global namespace for backward compatibility
|
||||
(import (web orchestration))
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,3 +1,21 @@
|
||||
|
||||
|
||||
(define-library (web page-helpers)
|
||||
(export
|
||||
special-form-category-map
|
||||
extract-define-kwargs
|
||||
categorize-special-forms
|
||||
build-ref-items-with-href
|
||||
build-reference-data
|
||||
build-attr-detail
|
||||
build-header-detail
|
||||
build-event-detail
|
||||
build-component-source
|
||||
build-bundle-analysis
|
||||
build-routing-analysis
|
||||
build-affinity-analysis)
|
||||
(begin
|
||||
|
||||
(define special-form-category-map {:defmacro "Functions & Components" :for-each "Higher-Order Forms" :defpage "Domain Definitions" :let "Binding" :filter "Higher-Order Forms" :shift "Continuations" :and "Control Flow" :set! "Binding" :map-indexed "Higher-Order Forms" :dynamic-wind "Guards" :reduce "Higher-Order Forms" :cond "Control Flow" :defquery "Domain Definitions" :-> "Sequencing & Threading" :let* "Binding" :define "Binding" :reset "Continuations" :case "Control Flow" :do "Sequencing & Threading" :map "Higher-Order Forms" :some "Higher-Order Forms" :letrec "Binding" :if "Control Flow" :quote "Quoting" :every? "Higher-Order Forms" :defhandler "Domain Definitions" :fn "Functions & Components" :defstyle "Domain Definitions" :lambda "Functions & Components" :defaction "Domain Definitions" :or "Control Flow" :defcomp "Functions & Components" :quasiquote "Quoting" :when "Control Flow" :begin "Sequencing & Threading"})
|
||||
|
||||
(define
|
||||
@@ -230,3 +248,9 @@
|
||||
(define
|
||||
build-affinity-analysis
|
||||
(fn ((demo-components :as list) (page-plans :as list)) {:components demo-components :page-plans page-plans}))
|
||||
|
||||
|
||||
)) ;; end define-library
|
||||
|
||||
;; Re-export to global namespace for backward compatibility
|
||||
(import (web page-helpers))
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,4 +1,24 @@
|
||||
;; Registry of all valid HTML tag names
|
||||
|
||||
(define-library (sx render)
|
||||
(export
|
||||
HTML_TAGS
|
||||
VOID_ELEMENTS
|
||||
BOOLEAN_ATTRS
|
||||
*definition-form-extensions*
|
||||
definition-form?
|
||||
parse-element-args
|
||||
render-attrs
|
||||
eval-cond
|
||||
eval-cond-scheme
|
||||
eval-cond-clojure
|
||||
process-bindings
|
||||
is-render-expr?
|
||||
merge-spread-attrs
|
||||
escape-html
|
||||
escape-attr)
|
||||
(begin
|
||||
|
||||
(define
|
||||
HTML_TAGS
|
||||
(list
|
||||
@@ -413,3 +433,9 @@
|
||||
|
||||
;; Escape special chars for HTML attribute values
|
||||
(define escape-attr (fn (s) (escape-html s)))
|
||||
|
||||
|
||||
)) ;; end define-library
|
||||
|
||||
;; Re-export to global namespace for backward compatibility
|
||||
(import (sx render))
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -17,6 +17,47 @@
|
||||
;; "/" → ()
|
||||
;; "/docs/" → ("docs")
|
||||
|
||||
|
||||
(define-library (web router)
|
||||
(export
|
||||
split-path-segments
|
||||
make-route-segment
|
||||
parse-route-pattern
|
||||
match-route-segments
|
||||
match-route
|
||||
find-matching-route
|
||||
_fn-to-segment
|
||||
sx-url-to-path
|
||||
_count-leading-dots
|
||||
_strip-trailing-close
|
||||
_index-of-safe
|
||||
_last-index-of
|
||||
_pop-sx-url-level
|
||||
_pop-sx-url-levels
|
||||
_split-pos-kw
|
||||
_parse-relative-body
|
||||
_extract-innermost
|
||||
_find-kw-in-tokens
|
||||
_find-keyword-value
|
||||
_replace-kw-in-tokens
|
||||
_set-keyword-in-content
|
||||
_is-delta-value?
|
||||
_apply-delta
|
||||
_apply-kw-pairs
|
||||
_apply-keywords-to-url
|
||||
_normalize-relative
|
||||
resolve-relative-url
|
||||
relative-sx-url?
|
||||
_url-special-forms
|
||||
url-special-form?
|
||||
parse-sx-url
|
||||
url-special-form-name
|
||||
url-special-form-inner
|
||||
url-to-expr
|
||||
auto-quote-unknowns
|
||||
prepare-url-expr)
|
||||
(begin
|
||||
|
||||
(define split-path-segments :effects []
|
||||
(fn ((path :as string))
|
||||
(let ((trimmed (if (starts-with? path "/") (slice path 1) path)))
|
||||
@@ -678,3 +719,9 @@
|
||||
;;
|
||||
;; From parser.sx: sx-parse, sx-serialize
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
|
||||
)) ;; end define-library
|
||||
|
||||
;; Re-export to global namespace for backward compatibility
|
||||
(import (web router))
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,3 +1,3 @@
|
||||
(sxbc 1 "7e4a727b2f55684e"
|
||||
(code
|
||||
:constants ("with-marsh-scope" {:upvalue-count 0 :arity 2 :constants ("list" "with-island-scope" {:upvalue-count 1 :arity 1 :constants ("append!") :bytecode (18 0 16 0 52 0 0 2 50)} "dom-set-data" "sx-marsh-disposers") :bytecode (52 0 0 0 17 2 20 1 0 51 2 0 1 2 16 1 48 2 5 20 3 0 16 0 1 4 0 16 2 49 3 50)} "dispose-marsh-scope" {:upvalue-count 0 :arity 1 :constants ("dom-get-data" "sx-marsh-disposers" "for-each" {:upvalue-count 0 :arity 1 :constants ("cek-call") :bytecode (20 0 0 16 0 2 49 2 50)} "dom-set-data") :bytecode (20 0 0 16 0 1 1 0 48 2 17 1 16 1 33 24 0 51 3 0 16 1 52 2 0 2 5 20 4 0 16 0 1 1 0 2 49 3 32 1 0 2 50)} "emit-event" {:upvalue-count 0 :arity 3 :constants ("dom-dispatch") :bytecode (20 0 0 16 0 16 1 16 2 49 3 50)} "on-event" {:upvalue-count 0 :arity 3 :constants ("dom-on") :bytecode (20 0 0 16 0 16 1 16 2 49 3 50)} "bridge-event" {:upvalue-count 0 :arity 4 :constants ("effect" {:upvalue-count 4 :arity 0 :constants ("dom-on" {:upvalue-count 2 :arity 1 :constants ("event-detail" "cek-call" "list" "reset!") :bytecode (20 0 0 16 0 48 1 17 1 18 0 33 16 0 20 1 0 18 0 16 1 52 2 0 1 48 2 32 2 0 16 1 17 2 20 3 0 18 1 16 2 49 2 50)}) :bytecode (20 0 0 18 0 18 1 51 1 0 0 2 0 3 48 3 17 0 16 0 50)}) :bytecode (20 0 0 51 1 0 1 0 1 1 1 3 1 2 49 1 50)} "resource" {:upvalue-count 0 :arity 1 :constants ("signal" "dict" "loading" "data" "error" "promise-then" "cek-call" {:upvalue-count 1 :arity 1 :constants ("reset!" "dict" "loading" "data" "error") :bytecode (20 0 0 18 0 1 2 0 4 1 3 0 16 0 1 4 0 2 52 1 0 6 49 2 50)} {:upvalue-count 1 :arity 1 :constants ("reset!" "dict" "loading" "data" "error") :bytecode (20 0 0 18 0 1 2 0 4 1 3 0 2 1 4 0 16 0 52 1 0 6 49 2 50)}) :bytecode (20 0 0 1 2 0 3 1 3 0 2 1 4 0 2 52 1 0 6 48 1 17 1 20 5 0 20 6 0 16 0 2 48 2 51 7 0 1 1 51 8 0 1 1 48 3 5 16 1 50)}) :bytecode (51 1 0 128 0 0 5 51 3 0 128 2 0 5 51 5 0 128 4 0 5 51 7 0 128 6 0 5 51 9 0 128 8 0 5 51 11 0 128 10 0 50)))
|
||||
:constants ("with-marsh-scope" {:upvalue-count 0 :arity 2 :constants ("list" "with-island-scope" {:upvalue-count 1 :arity 1 :constants ("append!") :bytecode (18 0 16 0 52 0 0 2 50)} "dom-set-data" "sx-marsh-disposers") :bytecode (52 0 0 0 17 2 20 1 0 51 2 0 1 2 16 1 48 2 5 20 3 0 16 0 1 4 0 16 2 49 3 50)} "dispose-marsh-scope" {:upvalue-count 0 :arity 1 :constants ("dom-get-data" "sx-marsh-disposers" "for-each" {:upvalue-count 0 :arity 1 :constants ("cek-call") :bytecode (16 0 2 52 0 0 2 50)} "dom-set-data") :bytecode (20 0 0 16 0 1 1 0 48 2 17 1 16 1 33 24 0 51 3 0 16 1 52 2 0 2 5 20 4 0 16 0 1 1 0 2 49 3 32 1 0 2 50)} "emit-event" {:upvalue-count 0 :arity 3 :constants ("dom-dispatch") :bytecode (20 0 0 16 0 16 1 16 2 49 3 50)} "on-event" {:upvalue-count 0 :arity 3 :constants ("dom-on") :bytecode (20 0 0 16 0 16 1 16 2 49 3 50)} "bridge-event" {:upvalue-count 0 :arity 4 :constants ("effect" {:upvalue-count 4 :arity 0 :constants ("dom-on" {:upvalue-count 2 :arity 1 :constants ("event-detail" "cek-call" "list" "reset!") :bytecode (20 0 0 16 0 48 1 17 1 18 0 33 15 0 18 0 16 1 52 2 0 1 52 1 0 2 32 2 0 16 1 17 2 20 3 0 18 1 16 2 49 2 50)}) :bytecode (20 0 0 18 0 18 1 51 1 0 0 2 0 3 48 3 17 0 16 0 50)}) :bytecode (20 0 0 51 1 0 1 0 1 1 1 3 1 2 49 1 50)} "resource" {:upvalue-count 0 :arity 1 :constants ("signal" "dict" "loading" "data" "error" "promise-then" "cek-call" {:upvalue-count 1 :arity 1 :constants ("reset!" "dict" "loading" "data" "error") :bytecode (20 0 0 18 0 1 2 0 4 1 3 0 16 0 1 4 0 2 52 1 0 6 49 2 50)} {:upvalue-count 1 :arity 1 :constants ("reset!" "dict" "loading" "data" "error") :bytecode (20 0 0 18 0 1 2 0 4 1 3 0 2 1 4 0 16 0 52 1 0 6 49 2 50)}) :bytecode (20 0 0 1 2 0 3 1 3 0 2 1 4 0 2 52 1 0 6 48 1 17 1 20 5 0 16 0 2 52 6 0 2 51 7 0 1 1 51 8 0 1 1 48 3 5 16 1 50)}) :bytecode (51 1 0 128 0 0 5 51 3 0 128 2 0 5 51 5 0 128 4 0 5 51 7 0 128 6 0 5 51 9 0 128 8 0 5 51 11 0 128 10 0 50)))
|
||||
|
||||
@@ -1,3 +1,41 @@
|
||||
|
||||
|
||||
(define-library (sx vm)
|
||||
(export
|
||||
make-upvalue-cell
|
||||
uv-get
|
||||
uv-set!
|
||||
make-vm-code
|
||||
make-vm-closure
|
||||
make-vm-frame
|
||||
make-vm
|
||||
vm-push
|
||||
vm-pop
|
||||
vm-peek
|
||||
frame-read-u8
|
||||
frame-read-u16
|
||||
frame-read-i16
|
||||
vm-push-frame
|
||||
code-from-value
|
||||
vm-closure?
|
||||
vm-call
|
||||
frame-local-get
|
||||
frame-local-set
|
||||
frame-upvalue-get
|
||||
frame-upvalue-set
|
||||
vm-global-get
|
||||
vm-resolve-ho-form
|
||||
vm-call-external
|
||||
vm-global-set
|
||||
env-walk
|
||||
env-walk-set!
|
||||
vm-create-closure
|
||||
vm-run
|
||||
vm-step
|
||||
vm-call-closure
|
||||
vm-execute-module)
|
||||
(begin
|
||||
|
||||
(define make-upvalue-cell (fn (value) {:uv-value value}))
|
||||
|
||||
(define uv-get (fn (cell) (get cell "uv-value")))
|
||||
@@ -516,6 +554,10 @@
|
||||
(vm-push vm (inc (vm-pop vm)))
|
||||
(= op 175)
|
||||
(vm-push vm (dec (vm-pop vm)))
|
||||
(= op 112)
|
||||
(let
|
||||
((request (vm-pop vm)))
|
||||
(error (str "VM: IO suspension (OP_PERFORM) — request: " request)))
|
||||
:else (error (str "VM: unknown opcode " op))))))
|
||||
|
||||
(define
|
||||
@@ -552,3 +594,9 @@
|
||||
(dict-set! vm "frames" (list frame))
|
||||
(vm-run vm)
|
||||
(vm-pop vm)))))
|
||||
|
||||
|
||||
)) ;; end define-library
|
||||
|
||||
;; Re-export to global namespace for backward compatibility
|
||||
(import (sx vm))
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1792,7 +1792,7 @@
|
||||
blake2_js_for_wasm_create: blake2_js_for_wasm_create};
|
||||
}
|
||||
(globalThis))
|
||||
({"link":[["runtime-0db9b496",0],["prelude-d7e4b000",0],["stdlib-23ce0836",[]],["sx-ee45eb6c",[2]],["jsoo_runtime-f96b44a8",[2]],["js_of_ocaml-651f6707",[2,4]],["dune__exe__Sx_browser-996ff9e3",[2,3,5]],["std_exit-10fb8830",[2]],["start-80fdb768",0]],"generated":(b=>{var
|
||||
({"link":[["runtime-0db9b496",0],["prelude-d7e4b000",0],["stdlib-23ce0836",[]],["sx-4aca2756",[2]],["jsoo_runtime-f96b44a8",[2]],["js_of_ocaml-651f6707",[2,4]],["dune__exe__Sx_browser-024d53bc",[2,3,5]],["std_exit-10fb8830",[2]],["start-289ae59b",0]],"generated":(b=>{var
|
||||
c=b,a=b?.module?.export||b;return{"env":{"caml_ba_kind_of_typed_array":()=>{throw new
|
||||
Error("caml_ba_kind_of_typed_array not implemented")},"caml_exn_with_js_backtrace":()=>{throw new
|
||||
Error("caml_exn_with_js_backtrace not implemented")},"caml_int64_create_lo_mi_hi":()=>{throw new
|
||||
@@ -1818,4 +1818,4 @@ a()},"Js_of_ocaml__Json.fragments":{"get_JSON":a=>a.JSON,"get_constructor":a=>a.
|
||||
a(b)},"Js_of_ocaml__Dom_svg.fragments":{"get_SVGElement":a=>a.SVGElement,"get_document":a=>a.document,"get_tagName":a=>a.tagName,"meth_call_0_toLowerCase":a=>a.toLowerCase(),"meth_call_1_getElementById":(a,b)=>a.getElementById(b),"meth_call_2_createElementNS":(a,b,c)=>a.createElementNS(b,c)},"Js_of_ocaml__EventSource.fragments":{"get_EventSource":a=>a.EventSource,"obj_9":()=>({}),"set_withCredentials":(a,b)=>a.withCredentials=b},"Js_of_ocaml__Geolocation.fragments":{"get_geolocation":a=>a.geolocation,"get_navigator":a=>a.navigator,"obj_10":()=>({})},"Js_of_ocaml__IntersectionObserver.fragments":{"get_IntersectionObserver":a=>a.IntersectionObserver,"obj_11":()=>({})},"Js_of_ocaml__Intl.fragments":{"get_Collator":a=>a.Collator,"get_DateTimeFormat":a=>a.DateTimeFormat,"get_Intl":a=>a.Intl,"get_NumberFormat":a=>a.NumberFormat,"get_PluralRules":a=>a.PluralRules,"obj_12":a=>({localeMatcher:a}),"obj_13":(a,b,c,d,e,f)=>({localeMatcher:a,usage:b,sensitivity:c,ignorePunctuation:d,numeric:e,caseFirst:f}),"obj_14":(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t)=>({dateStyle:a,timeStyle:b,calendar:c,dayPeriod:d,numberingSystem:e,localeMatcher:f,timeZone:g,hour12:h,hourCycle:i,formatMatcher:j,weekday:k,era:l,year:m,month:n,day:o,hour:p,minute:q,second:r,fractionalSecondDigits:s,timeZoneName:t}),"obj_15":(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u)=>({compactDisplay:a,currency:b,currencyDisplay:c,currencySign:d,localeMatcher:e,notation:f,numberingSystem:g,signDisplay:h,style:i,unit:j,unitDisplay:k,useGrouping:l,roundingMode:m,roundingPriority:n,roundingIncrement:o,trailingZeroDisplay:p,minimumIntegerDigits:q,minimumFractionDigits:r,maximumFractionDigits:s,minimumSignificantDigits:t,maximumSignificantDigits:u}),"obj_16":(a,b)=>({localeMatcher:a,type:b})},"Dune__exe__Sx_browser.fragments":{"fun_call_1":(a,b)=>a(b),"fun_call_3":(a,b,c,d)=>a(b,c,d),"get_Array":a=>a.Array,"get_Object":a=>a.Object,"get___sx_handle":a=>a.__sx_handle,"get__type":a=>a._type,"get_console":a=>a.console,"get_items":a=>a.items,"get_length":a=>a.length,"get_name":a=>a.name,"js_expr_10d25c5c":()=>function(a){return function(){b.__sxR=undefined;var
|
||||
c=a.apply(null,arguments);return b.__sxR!==undefined?b.__sxR:c}},"js_expr_1ab4fffb":()=>function(){var
|
||||
b={},d=0;return{put:function(a){var
|
||||
c=d++;b[c]=a;return c},get:function(a){return b[a]}}}(),"js_expr_36506fc1":()=>function(a,b,c){a.__sx_handle=b;a._type=c;return a},"meth_call_1_error":(a,b)=>a.error(b),"meth_call_1_get":(a,b)=>a.get(b),"meth_call_1_isArray":(a,b)=>a.isArray(b),"meth_call_1_keys":(a,b)=>a.keys(b),"meth_call_1_put":(a,b)=>a.put(b),"obj_0":()=>({}),"obj_1":()=>({}),"obj_2":(a,b)=>({_type:a,items:b}),"obj_3":(a,b)=>({_type:a,name:b}),"obj_4":(a,b)=>({_type:a,name:b}),"obj_5":(a,b)=>({_type:a,__sx_handle:b}),"set_SxKernel":(a,b)=>a.SxKernel=b,"set___sxR":(a,b)=>a.__sxR=b,"set__type":(a,b)=>a._type=b,"set_beginModuleLoad":(a,b)=>a.beginModuleLoad=b,"set_callFn":(a,b)=>a.callFn=b,"set_compileModule":(a,b)=>a.compileModule=b,"set_debugEnv":(a,b)=>a.debugEnv=b,"set_endModuleLoad":(a,b)=>a.endModuleLoad=b,"set_engine":(a,b)=>a.engine=b,"set_eval":(a,b)=>a.eval=b,"set_evalExpr":(a,b)=>a.evalExpr=b,"set_evalVM":(a,b)=>a.evalVM=b,"set_fnArity":(a,b)=>a.fnArity=b,"set_inspect":(a,b)=>a.inspect=b,"set_isCallable":(a,b)=>a.isCallable=b,"set_load":(a,b)=>a.load=b,"set_loadModule":(a,b)=>a.loadModule=b,"set_loadSource":(a,b)=>a.loadSource=b,"set_parse":(a,b)=>a.parse=b,"set_registerNative":(a,b)=>a.registerNative=b,"set_renderToHtml":(a,b)=>a.renderToHtml=b,"set_scopeTraceDrain":(a,b)=>a.scopeTraceDrain=b,"set_scopeTraceOff":(a,b)=>a.scopeTraceOff=b,"set_scopeTraceOn":(a,b)=>a.scopeTraceOn=b,"set_stringify":(a,b)=>a.stringify=b,"set_typeOf":(a,b)=>a.typeOf=b}}})(globalThis),"src":"sx_browser.bc.wasm.assets"});
|
||||
c=d++;b[c]=a;return c},get:function(a){return b[a]}}}(),"js_expr_36506fc1":()=>function(a,b,c){a.__sx_handle=b;a._type=c;return a},"meth_call_1_error":(a,b)=>a.error(b),"meth_call_1_get":(a,b)=>a.get(b),"meth_call_1_isArray":(a,b)=>a.isArray(b),"meth_call_1_keys":(a,b)=>a.keys(b),"meth_call_1_put":(a,b)=>a.put(b),"obj_0":()=>({}),"obj_1":()=>({}),"obj_2":(a,b)=>({_type:a,items:b}),"obj_3":(a,b)=>({_type:a,name:b}),"obj_4":(a,b)=>({_type:a,name:b}),"obj_5":(a,b)=>({_type:a,__sx_handle:b}),"obj_6":()=>({}),"set_SxKernel":(a,b)=>a.SxKernel=b,"set___sxR":(a,b)=>a.__sxR=b,"set__type":(a,b)=>a._type=b,"set_beginModuleLoad":(a,b)=>a.beginModuleLoad=b,"set_callFn":(a,b)=>a.callFn=b,"set_compileModule":(a,b)=>a.compileModule=b,"set_debugEnv":(a,b)=>a.debugEnv=b,"set_endModuleLoad":(a,b)=>a.endModuleLoad=b,"set_engine":(a,b)=>a.engine=b,"set_eval":(a,b)=>a.eval=b,"set_evalExpr":(a,b)=>a.evalExpr=b,"set_evalVM":(a,b)=>a.evalVM=b,"set_fnArity":(a,b)=>a.fnArity=b,"set_inspect":(a,b)=>a.inspect=b,"set_isCallable":(a,b)=>a.isCallable=b,"set_load":(a,b)=>a.load=b,"set_loadModule":(a,b)=>a.loadModule=b,"set_loadSource":(a,b)=>a.loadSource=b,"set_op":(a,b)=>a.op=b,"set_parse":(a,b)=>a.parse=b,"set_registerNative":(a,b)=>a.registerNative=b,"set_renderToHtml":(a,b)=>a.renderToHtml=b,"set_request":(a,b)=>a.request=b,"set_resume":(a,b)=>a.resume=b,"set_scopeTraceDrain":(a,b)=>a.scopeTraceDrain=b,"set_scopeTraceOff":(a,b)=>a.scopeTraceOff=b,"set_scopeTraceOn":(a,b)=>a.scopeTraceOn=b,"set_stringify":(a,b)=>a.stringify=b,"set_suspended":(a,b)=>a.suspended=b,"set_typeOf":(a,b)=>a.typeOf=b}}})(globalThis),"src":"sx_browser.bc.wasm.assets"});
|
||||
|
||||
@@ -1446,6 +1446,8 @@
|
||||
("perform" (step-sf-perform args env kont))
|
||||
("define-library" (step-sf-define-library args env kont))
|
||||
("import" (step-sf-import args env kont))
|
||||
("define-record-type"
|
||||
(make-cek-value (sf-define-record-type args env) env kont))
|
||||
(_
|
||||
(cond
|
||||
(has-key? *custom-special-forms* name)
|
||||
@@ -1573,6 +1575,64 @@
|
||||
env
|
||||
(kont-push (make-perform-frame env) kont)))))
|
||||
|
||||
;; R7RS records (SRFI-9)
|
||||
;;
|
||||
;; (define-record-type <point>
|
||||
;; (make-point x y)
|
||||
;; point?
|
||||
;; (x point-x)
|
||||
;; (y point-y set-point-y!))
|
||||
;;
|
||||
;; Creates: constructor, predicate, accessors, optional mutators.
|
||||
;; Opaque — only accessible through generated functions.
|
||||
;; Generative — each call creates a unique type.
|
||||
(define
|
||||
sf-define-record-type
|
||||
(fn
|
||||
(args env)
|
||||
(let
|
||||
((type-sym (first args))
|
||||
(ctor-spec (nth args 1))
|
||||
(pred-sym (nth args 2))
|
||||
(field-specs (slice args 3)))
|
||||
(let
|
||||
((raw-name (symbol-name type-sym)))
|
||||
(let
|
||||
((type-name
|
||||
(if
|
||||
(and (starts-with? raw-name "<") (ends-with? raw-name ">"))
|
||||
(slice raw-name 1 (- (len raw-name) 1))
|
||||
raw-name))
|
||||
(ctor-name (symbol-name (first ctor-spec)))
|
||||
(ctor-params (map (fn (s) (symbol-name s)) (rest ctor-spec)))
|
||||
(pred-name (symbol-name pred-sym))
|
||||
(field-names
|
||||
(map (fn (fs) (symbol-name (first fs))) field-specs)))
|
||||
(let
|
||||
((rtd-uid (make-rtd type-name field-names ctor-params)))
|
||||
;; Constructor — OCaml returns a NativeFn
|
||||
(env-bind! env ctor-name
|
||||
(make-record-constructor rtd-uid))
|
||||
;; Predicate — OCaml returns a NativeFn
|
||||
(env-bind! env pred-name
|
||||
(make-record-predicate rtd-uid))
|
||||
;; Accessors and optional mutators
|
||||
(for-each-indexed
|
||||
(fn
|
||||
(idx fs)
|
||||
(let
|
||||
((accessor-name (symbol-name (nth fs 1))))
|
||||
(env-bind! env accessor-name
|
||||
(make-record-accessor idx))
|
||||
(when
|
||||
(>= (len fs) 3)
|
||||
(let
|
||||
((mutator-name (symbol-name (nth fs 2))))
|
||||
(env-bind! env mutator-name
|
||||
(make-record-mutator idx))))))
|
||||
field-specs)
|
||||
nil))))))
|
||||
|
||||
;; Delimited continuations
|
||||
(define
|
||||
step-sf-callcc
|
||||
|
||||
185
spec/tests/test-records.sx
Normal file
185
spec/tests/test-records.sx
Normal file
@@ -0,0 +1,185 @@
|
||||
;; R7RS define-record-type tests (SRFI-9)
|
||||
|
||||
(defsuite
|
||||
"record-basic"
|
||||
(deftest
|
||||
"constructor and predicate"
|
||||
(do
|
||||
(define-record-type <point>
|
||||
(make-point x y)
|
||||
point?
|
||||
(x point-x)
|
||||
(y point-y))
|
||||
(let
|
||||
((p (make-point 3 4)))
|
||||
(assert (point? p))
|
||||
(assert= 3 (point-x p))
|
||||
(assert= 4 (point-y p)))))
|
||||
(deftest
|
||||
"predicate rejects non-records"
|
||||
(do
|
||||
(define-record-type <point>
|
||||
(make-point x y)
|
||||
point?
|
||||
(x point-x)
|
||||
(y point-y))
|
||||
(assert= false (point? 42))
|
||||
(assert= false (point? "hello"))
|
||||
(assert= false (point? (list 1 2)))
|
||||
(assert= false (point? {:x 1 :y 2}))))
|
||||
(deftest
|
||||
"type-of returns stripped name"
|
||||
(do
|
||||
(define-record-type <point>
|
||||
(make-point x y)
|
||||
point?
|
||||
(x point-x)
|
||||
(y point-y))
|
||||
(assert= "point" (type-of (make-point 1 2)))))
|
||||
(deftest
|
||||
"record is not a dict"
|
||||
(do
|
||||
(define-record-type <point>
|
||||
(make-point x y)
|
||||
point?
|
||||
(x point-x)
|
||||
(y point-y))
|
||||
(assert= false (dict? (make-point 1 2)))
|
||||
(assert= false (list? (make-point 1 2)))
|
||||
(assert (record? (make-point 1 2))))))
|
||||
|
||||
(defsuite
|
||||
"record-mutator"
|
||||
(deftest
|
||||
"set! via mutator"
|
||||
(do
|
||||
(define-record-type <point>
|
||||
(make-point x y)
|
||||
point?
|
||||
(x point-x)
|
||||
(y point-y set-point-y!))
|
||||
(let
|
||||
((p (make-point 3 4)))
|
||||
(set-point-y! p 99)
|
||||
(assert= 99 (point-y p))
|
||||
(assert= 3 (point-x p)))))
|
||||
(deftest
|
||||
"multiple mutations"
|
||||
(do
|
||||
(define-record-type <cell>
|
||||
(make-cell value)
|
||||
cell?
|
||||
(value cell-value set-cell-value!))
|
||||
(let
|
||||
((c (make-cell 0)))
|
||||
(set-cell-value! c 1)
|
||||
(set-cell-value! c 2)
|
||||
(set-cell-value! c 3)
|
||||
(assert= 3 (cell-value c))))))
|
||||
|
||||
(defsuite
|
||||
"record-generative"
|
||||
(deftest
|
||||
"distinct types with same fields"
|
||||
(do
|
||||
(define-record-type <a>
|
||||
(make-a v)
|
||||
a?
|
||||
(v a-v))
|
||||
(define-record-type <b>
|
||||
(make-b v)
|
||||
b?
|
||||
(v b-v))
|
||||
(let
|
||||
((x (make-a 1))
|
||||
(y (make-b 2)))
|
||||
(assert (a? x))
|
||||
(assert= false (a? y))
|
||||
(assert= false (b? x))
|
||||
(assert (b? y)))))
|
||||
(deftest
|
||||
"record? matches any record"
|
||||
(do
|
||||
(define-record-type <a>
|
||||
(make-a v)
|
||||
a?
|
||||
(v a-v))
|
||||
(define-record-type <b>
|
||||
(make-b v)
|
||||
b?
|
||||
(v b-v))
|
||||
(assert (record? (make-a 1)))
|
||||
(assert (record? (make-b 2)))
|
||||
(assert= false (record? 42)))))
|
||||
|
||||
(defsuite
|
||||
"record-field-reorder"
|
||||
(deftest
|
||||
"constructor params in different order"
|
||||
(do
|
||||
(define-record-type <pair>
|
||||
(make-pair second first)
|
||||
pair?
|
||||
(first pair-first)
|
||||
(second pair-second))
|
||||
(let
|
||||
((p (make-pair 2 1)))
|
||||
(assert= 1 (pair-first p))
|
||||
(assert= 2 (pair-second p)))))
|
||||
(deftest
|
||||
"three fields reordered"
|
||||
(do
|
||||
(define-record-type <triple>
|
||||
(make-triple c a b)
|
||||
triple?
|
||||
(a triple-a)
|
||||
(b triple-b)
|
||||
(c triple-c))
|
||||
(let
|
||||
((t (make-triple 30 10 20)))
|
||||
(assert= 10 (triple-a t))
|
||||
(assert= 20 (triple-b t))
|
||||
(assert= 30 (triple-c t))))))
|
||||
|
||||
(defsuite
|
||||
"record-nested"
|
||||
(deftest
|
||||
"records containing records"
|
||||
(do
|
||||
(define-record-type <point>
|
||||
(make-point x y)
|
||||
point?
|
||||
(x point-x)
|
||||
(y point-y))
|
||||
(define-record-type <line>
|
||||
(make-line start end)
|
||||
line?
|
||||
(start line-start)
|
||||
(end line-end))
|
||||
(let
|
||||
((l (make-line (make-point 0 0) (make-point 3 4))))
|
||||
(assert (line? l))
|
||||
(assert (point? (line-start l)))
|
||||
(assert= 0 (point-x (line-start l)))
|
||||
(assert= 4 (point-y (line-end l)))))))
|
||||
|
||||
(defsuite
|
||||
"record-equality"
|
||||
(deftest
|
||||
"equal records are equal"
|
||||
(do
|
||||
(define-record-type <point>
|
||||
(make-point x y)
|
||||
point?
|
||||
(x point-x)
|
||||
(y point-y))
|
||||
(assert= (make-point 1 2) (make-point 1 2))))
|
||||
(deftest
|
||||
"different values are not equal"
|
||||
(do
|
||||
(define-record-type <point>
|
||||
(make-point x y)
|
||||
point?
|
||||
(x point-x)
|
||||
(y point-y))
|
||||
(assert (not (equal? (make-point 1 2) (make-point 1 3)))))))
|
||||
8
test-results/.last-run.json
Normal file
8
test-results/.last-run.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"status": "failed",
|
||||
"failedTests": [
|
||||
"f74e9a54851d8fcfaffa-13e0d4c7d93026c85c6c",
|
||||
"a2add2f401dce5f22243-5b9be27c24aceaec6daa",
|
||||
"63f6db7ebc03cc4b82b9-ce6ce7a4bd4491603196"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
# Page snapshot
|
||||
|
||||
```yaml
|
||||
- main [ref=e4]:
|
||||
- generic [ref=e6]:
|
||||
- complementary
|
||||
- generic [ref=e7]:
|
||||
- generic [ref=e8]:
|
||||
- generic [ref=e11]:
|
||||
- link "(<sx>)" [ref=e12] [cursor=pointer]:
|
||||
- /url: /sx/
|
||||
- generic [ref=e14]: (<sx>)
|
||||
- paragraph [ref=e15]: The framework-free reactive hypermedium
|
||||
- paragraph [ref=e17]: © Giles Bradshaw 2026· /sx/(geography.(reactive.(examples.reactive-list)))
|
||||
- generic [ref=e18]:
|
||||
- link "← Etc" [ref=e19] [cursor=pointer]:
|
||||
- /url: /sx/(etc)
|
||||
- link "Geography" [ref=e20] [cursor=pointer]:
|
||||
- /url: /sx/(geography)
|
||||
- link "Language →" [ref=e21] [cursor=pointer]:
|
||||
- /url: /sx/(language)
|
||||
- generic [ref=e22]:
|
||||
- link "← Reactive Runtime" [ref=e23] [cursor=pointer]:
|
||||
- /url: /sx/(geography.(reactive-runtime))
|
||||
- link "Reactive Islands" [ref=e24] [cursor=pointer]:
|
||||
- /url: /sx/(geography.(reactive))
|
||||
- link "Hypermedia Lakes →" [ref=e25] [cursor=pointer]:
|
||||
- /url: /sx/(geography.(hypermedia))
|
||||
- generic [ref=e26]:
|
||||
- link "← Examples" [ref=e27] [cursor=pointer]:
|
||||
- /url: /sx/(geography.(reactive.(examples)))
|
||||
- link "Examples" [ref=e28] [cursor=pointer]:
|
||||
- /url: /sx/(geography.(reactive.(examples)))
|
||||
- link "Examples →" [ref=e29] [cursor=pointer]:
|
||||
- /url: /sx/(geography.(reactive.(examples)))
|
||||
- generic [ref=e30]:
|
||||
- link "← Imperative" [ref=e31] [cursor=pointer]:
|
||||
- /url: /sx/(geography.(reactive.(examples.imperative)))
|
||||
- link "Reactive List" [ref=e32] [cursor=pointer]:
|
||||
- /url: /sx/(geography.(reactive.(examples.reactive-list)))
|
||||
- link "Input Binding →" [ref=e33] [cursor=pointer]:
|
||||
- /url: /sx/(geography.(reactive.(examples.input-binding)))
|
||||
- generic [ref=e37]:
|
||||
- paragraph [ref=e38]:
|
||||
- text: When
|
||||
- code [ref=e39]: map
|
||||
- text: is used with
|
||||
- code [ref=e40]: (deref signal)
|
||||
- text: inside an island, it auto-upgrades to a reactive list. With
|
||||
- code [ref=e41]: :key
|
||||
- text: attributes, existing DOM nodes are reused across updates — only additions, removals, and reorderings touch the DOM.
|
||||
- generic [ref=e43]:
|
||||
- generic [ref=e44]:
|
||||
- button "Add Item" [ref=e45] [cursor=pointer]
|
||||
- generic [ref=e46]: 0 items
|
||||
- list
|
||||
- generic [ref=e48]: (defisland ~reactive-islands/index/demo-reactive-list () (let ((next-id (signal 1)) (items (signal (list))) (add-item (fn (e) (batch (fn () (swap! items (fn (old) (append old (dict "id" (deref next-id) "text" (str "Item " (deref next-id)))))) (swap! next-id inc))))) (remove-item (fn (id) (swap! items (fn (old) (filter (fn (item) (not (= (get item "id") id))) old)))))) (div (~tw :tokens "rounded border border-violet-200 bg-violet-50 p-4 my-4") (div (~tw :tokens "flex items-center gap-3 mb-3") (button (~tw :tokens "px-3 py-1 rounded bg-violet-600 text-white text-sm font-medium hover:bg-violet-700") :on-click add-item "Add Item") (span (~tw :tokens "text-sm text-stone-500") (deref (computed (fn () (len (deref items))))) " items")) (ul (~tw :tokens "space-y-1") (map (fn (item) (li :key (str (get item "id")) (~tw :tokens "flex items-center justify-between bg-white rounded px-3 py-2 text-sm") (span (get item "text")) (button (~tw :tokens "text-stone-400 hover:text-red-500 text-xs") :on-click (fn (e) (remove-item (get item "id"))) "✕"))) (deref items))))))
|
||||
- paragraph [ref=e49]:
|
||||
- code [ref=e50]: :key
|
||||
- text: identifies each list item. When items change, the reconciler matches old and new keys — reusing existing DOM nodes, inserting new ones, and removing stale ones. Without keys, the list falls back to clear-and-rerender.
|
||||
- code [ref=e51]: batch
|
||||
- text: groups the two signal writes into one update pass.
|
||||
```
|
||||
@@ -0,0 +1,82 @@
|
||||
# Page snapshot
|
||||
|
||||
```yaml
|
||||
- main [ref=e4]:
|
||||
- generic [ref=e6]:
|
||||
- complementary
|
||||
- generic [ref=e7]:
|
||||
- generic [ref=e8]:
|
||||
- generic [ref=e11]:
|
||||
- link "(<sx>)" [ref=e12] [cursor=pointer]:
|
||||
- /url: /sx/
|
||||
- generic [ref=e14]: (<sx>)
|
||||
- paragraph [ref=e15]: The framework-free reactive hypermedium
|
||||
- paragraph [ref=e17]: © Giles Bradshaw 2026· /sx/(geography.(hypermedia.(example.delete-row)))
|
||||
- generic [ref=e18]:
|
||||
- link "← Etc" [ref=e19] [cursor=pointer]:
|
||||
- /url: /sx/(etc)
|
||||
- link "Geography" [ref=e20] [cursor=pointer]:
|
||||
- /url: /sx/(geography)
|
||||
- link "Language →" [ref=e21] [cursor=pointer]:
|
||||
- /url: /sx/(language)
|
||||
- generic [ref=e22]:
|
||||
- link "← Reactive Islands" [ref=e23] [cursor=pointer]:
|
||||
- /url: /sx/(geography.(reactive))
|
||||
- link "Hypermedia Lakes" [ref=e24] [cursor=pointer]:
|
||||
- /url: /sx/(geography.(hypermedia))
|
||||
- link "Scopes →" [ref=e25] [cursor=pointer]:
|
||||
- /url: /sx/(geography.(scopes))
|
||||
- generic [ref=e26]:
|
||||
- link "← Reference" [ref=e27] [cursor=pointer]:
|
||||
- /url: /sx/(geography.(hypermedia.(reference)))
|
||||
- link "Examples" [ref=e28] [cursor=pointer]:
|
||||
- /url: /sx/(geography.(hypermedia.(example)))
|
||||
- link "Reference →" [ref=e29] [cursor=pointer]:
|
||||
- /url: /sx/(geography.(hypermedia.(reference)))
|
||||
- generic [ref=e30]:
|
||||
- link "← Polling" [ref=e31] [cursor=pointer]:
|
||||
- /url: /sx/(geography.(hypermedia.(example.polling)))
|
||||
- link "Delete Row" [ref=e32] [cursor=pointer]:
|
||||
- /url: /sx/(geography.(hypermedia.(example.delete-row)))
|
||||
- link "Inline Edit →" [ref=e33] [cursor=pointer]:
|
||||
- /url: /sx/(geography.(hypermedia.(example.inline-edit)))
|
||||
- generic [ref=e37]:
|
||||
- paragraph [ref=e38]: sx-delete with sx-swap "outerHTML" and an empty response removes the row from the DOM.
|
||||
- generic [ref=e40]:
|
||||
- generic [ref=e41]:
|
||||
- heading "Demo" [level=3] [ref=e42]
|
||||
- paragraph [ref=e43]: Click delete to remove a row. Uses sx-confirm for confirmation.
|
||||
- table [ref=e47]:
|
||||
- rowgroup [ref=e48]:
|
||||
- row "Item" [ref=e49]:
|
||||
- columnheader "Item" [ref=e50]
|
||||
- columnheader [ref=e51]
|
||||
- rowgroup [ref=e52]:
|
||||
- row "Fix login bug delete" [ref=e53]:
|
||||
- cell "Fix login bug" [ref=e54]
|
||||
- cell "delete" [ref=e55]:
|
||||
- button "delete" [ref=e56] [cursor=pointer]
|
||||
- row "Write documentation delete" [ref=e57]:
|
||||
- cell "Write documentation" [ref=e58]
|
||||
- cell "delete" [ref=e59]:
|
||||
- button "delete" [ref=e60] [cursor=pointer]
|
||||
- row "Deploy to production delete" [ref=e61]:
|
||||
- cell "Deploy to production" [ref=e62]
|
||||
- cell "delete" [ref=e63]:
|
||||
- button "delete" [ref=e64] [cursor=pointer]
|
||||
- row "Add unit tests delete" [ref=e65]:
|
||||
- cell "Add unit tests" [ref=e66]
|
||||
- cell "delete" [ref=e67]:
|
||||
- button "delete" [ref=e68] [cursor=pointer]
|
||||
- heading "S-expression" [level=3] [ref=e69]
|
||||
- code [ref=e73]: (button :sx-delete "/sx/(geography.(hypermedia.(example.(api.(delete.1)))))" :sx-target "#row-1" :sx-swap "outerHTML" :sx-confirm "Delete this item?" "delete")
|
||||
- heading "Component" [level=3] [ref=e74]
|
||||
- code [ref=e79]: (defcomp ~examples/delete-row (id name) (tr :id (str "row-" id) (~tw :tokens "border-b border-stone-100 transition-all") (td (~tw :tokens "px-3 py-2 text-stone-700") name) (td (~tw :tokens "px-3 py-2") (button :sx-delete (str "/sx/(geography.(hypermedia.(example.(api.(delete." id ")))))") :sx-target (str "#row-" id) :sx-swap "outerHTML" :sx-confirm "Delete this item?" (~tw :tokens "text-rose-500 hover:text-rose-700 text-sm") "delete"))))
|
||||
- heading "Server handler" [level=3] [ref=e80]
|
||||
- code [ref=e84]: (:path "/sx/(geography.(hypermedia.(example.(api.(delete.<sx:item_id>)))))" :method :delete :csrf false ("item-id") (<> (~docs/oob-code :target-id "delete-comp" :text (helper "component-source" "~examples/delete-row")) (~docs/oob-code :target-id "delete-wire" :text "(empty — row removed by outerHTML swap)")))
|
||||
- generic [ref=e85]:
|
||||
- heading "Wire response" [level=3] [ref=e86]
|
||||
- button "Clear component cache" [ref=e87] [cursor=pointer]
|
||||
- paragraph [ref=e88]: Empty body — outerHTML swap replaces the target element with nothing.
|
||||
- code [ref=e93]: (empty — row removed by outerHTML swap)
|
||||
```
|
||||
Reference in New Issue
Block a user