From da4b526abb120bcc5b9d3a02486d74ba5f8264c6 Mon Sep 17 00:00:00 2001 From: giles Date: Fri, 1 May 2026 09:31:28 +0000 Subject: [PATCH] =?UTF-8?q?spec:=20sequence=20protocol=20Spec=20step=20?= =?UTF-8?q?=E2=80=94=20seq-to-list=20+=20ho=20polymorphic=20dispatch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - seq-to-list: coerce list/vector/string/nil to list - ho-setup-dispatch: apply seq-to-list to all collection args so map/filter/ reduce/for-each/some/every? work over vectors and strings natively - sequence->list, sequence->vector, sequence-length, sequence-ref, sequence-append: full polymorphic sequence helpers - in-range: list-returning range generator (eager, works with all HO forms) - Restore 3 accidentally-deleted make-cek-state/make-cek-value/make-cek-suspended - Fix 8 shorthand define forms (transpiler requires long form) - Add vector->list/list->vector to transpiler js-renames + platform aliases - JS: 2137 passing (+28 vs HEAD baseline) Co-Authored-By: Claude Sonnet 4.6 --- hosts/javascript/platform.py | 2 + hosts/javascript/transpiler.sx | 2 +- plans/agent-briefings/primitives-loop.md | 3 +- shared/static/scripts/sx-browser.js | 88 ++++- spec/evaluator.sx | 409 ++++++++++++++--------- 5 files changed, 332 insertions(+), 172 deletions(-) diff --git a/hosts/javascript/platform.py b/hosts/javascript/platform.py index 26625cea..e7d08f2d 100644 --- a/hosts/javascript/platform.py +++ b/hosts/javascript/platform.py @@ -1657,6 +1657,8 @@ PLATFORM_JS_POST = ''' var mod = PRIMITIVES["mod"]; var indexOf_ = PRIMITIVES["index-of"]; var hasKey = PRIMITIVES["has-key?"]; + var vectorToList = PRIMITIVES["vector->list"]; + var listToVector = PRIMITIVES["list->vector"]; function zip(a, b) { var r = []; for (var i = 0; i < Math.min(a.length, b.length); i++) r.push([a[i], b[i]]); return r; } function append_b(arr, x) { arr.push(x); return arr; } var apply = function(f, args) { diff --git a/hosts/javascript/transpiler.sx b/hosts/javascript/transpiler.sx index 6c6f8f1b..02c00da8 100644 --- a/hosts/javascript/transpiler.sx +++ b/hosts/javascript/transpiler.sx @@ -66,7 +66,7 @@ "with" "yield")) -(define js-renames {:ho-filter "hoFilter" :thunk-env "thunkEnv" :cek-run "cekRun" :*custom-special-forms* "_customSpecialForms" :with-island-scope "withIslandScope" :step-sf-if "stepSfIf" :dom-is-fragment? "domIsFragment" :process-bindings "processBindings" :call-thunk "callThunk" :fetch-streaming "fetchStreaming" :bind-inline-handlers "bindInlineHandlers" :set-interval "setInterval_" :number? "isNumber" :reactive-list "reactiveList" :expand-macro "expandMacro" :handle-history "handleHistory" :page-render-plan "pageRenderPlan" :make-let-frame "makeLetFrame" :parse-comp-params "parseCompParams" :next-retry-ms "nextRetryMs" :fetch-request "fetchRequest" :kont-push "kontPush" :macro-body "macroBody" :for-each-indexed "forEachIndexed" :step-ho-for-each "stepHoForEach" :set-render-active! "setRenderActiveB" :local-storage-set "localStorageSet" :dom-get-attr "domGetAttr" :parse-element-args "parseElementArgs" :process-emit-elements "processEmitElements" :build-request-body "buildRequestBody" :kont-top "kontTop" :event-detail "eventDetail" :match-route "matchRoute" :handle-popstate "handlePopstate" :event-source-listen "eventSourceListen" :select-from-container "selectFromContainer" :try-eval-content "tryEvalContent" :query-page-scripts "queryPageScripts" :scope-emit! "scopeEmit" :promise-delayed "promiseDelayed" :make-call-frame "makeCallFrame" :HTML_TAGS "HTML_TAGS" :macro-rest-param "macroRestParam" :env-has? "envHas" :make-raw-html "makeRawHtml" :dom-set-style "domSetStyle" :try-parse-json "tryParseJson" :host-call "hostCall" :VERB_SELECTOR "VERB_SELECTOR" :render-dom-element "renderDomElement" :escape-html "escapeHtml" :parse-sse-swap "parseSseSwap" :disable-elements "disableElements" :starts-with? "startsWith" :parse-env-attr "parseEnvAttr" :ho-some "hoSome" :eval-cond-scheme "evalCondScheme" :ends-with? "endsWith" :>= "gte_" :dom-dispatch "domDispatch" :preload-cache-set "preloadCacheSet" :signal-subscribers "signalSubscribers" :step-sf-provide "stepSfProvide" :signal-add-sub! "signalAddSub" :render-lambda-html "renderLambdaHtml" :dom-set-data "domSetData" :make-thread-frame "makeThreadFrame" :make-sx-expr "makeSxExpr" :pop-wind! "popWind" :dom-append-to-head "domAppendToHead" :hoist-head-elements "hoistHeadElements" :make-reset-frame "makeResetFrame" :flush-subscribers "flushSubscribers" :controller-signal "controllerSignal" :clear-interval "clearInterval_" :children-to-fragment "childrenToFragment" :sx-render-component "sxRenderComponent" :with-transition "withTransition" :scan-io-refs-walk "scanIoRefsWalk" :step-sf-scope "stepSfScope" :get-primitive "getPrimitive" :_preload-cache "_preloadCache" :select-html-from-doc "selectHtmlFromDoc" :browser-location-href "browserLocationHref" :sf-case-loop "sfCaseLoop" :sf-dynamic-wind "sfDynamicWind" :symbol-name "symbolName" :set-lambda-name! "setLambdaName" :host-get "hostGet" :aser-fragment "aserFragment" :render-dom-unknown-component "renderDomUnknownComponent" :!= "notEqual_" :SX_VERSION "SX_VERSION" :render-html-element "renderHtmlElement" :dom-first-child "domFirstChild" :bind-client-route-click "bindClientRouteClick" :sf-cond-clojure "sfCondClojure" :MATH_NS "MATH_NS" :default-trigger "defaultTrigger" :signal-remove-sub! "signalRemoveSub" :make-cek-state "makeCekState" :emit! "sxEmit" :sf-quote "sfQuote" :bind-boost-form "bindBoostForm" :component-params "componentParams" :do-preload "doPreload" :component-affinity "componentAffinity" :eval-case-aser "evalCaseAser" :sf-begin "sfBegin" :revert-optimistic "revertOptimistic" :whitespace? "isWhitespace" :host-typeof "hostTypeof" :dom-insert-adjacent-html "domInsertAdjacentHtml" :step-sf-set! "stepSfSet" :error-message "errorMessage" :schedule-idle "scheduleIdle" :find-matching-route "findMatchingRoute" :component-body "componentBody" :qq-expand "qqExpand" :provide-push! "providePush" :make-keyword "makeKeyword" :do-fetch "doFetch" :component-deps "componentDeps" :component-set-io-refs! "componentSetIoRefs" :escape-string "escapeString" :make-island "makeIsland" :nil "NIL" :log-parse-error "logParseError" :enable-cek-reactive! "enableCekReactive" :signal-set-value! "signalSetValue" :env-set! "envSet" :clear-timeout "clearTimeout_" :sf-defcomp "sfDefcomp" :step-ho-map "stepHoMap" :dom-parse-html "domParseHtml" :make-lambda "makeLambda" :sf-if "sfIf" :make-route-segment "makeRouteSegment" :lambda-closure "lambdaClosure" :render-target "renderTarget" :dom-attr-list "domAttrList" :log-warn "logWarn" :eval-call "evalCall" :sync-attrs "syncAttrs" :make-case-frame "makeCaseFrame" :render-dom-component "renderDomComponent" :dom-child-nodes "domChildNodes" :collect! "sxCollect" :use-store "useStore" :classify-trigger "classifyTrigger" :engine-init "engineInit" :list? "isList" :index-of "indexOf_" :component-io-refs "componentIoRefs" :dom-remove "domRemove" :set-document-title "setDocumentTitle" :primitive? "isPrimitive" :parse-trigger-spec "parseTriggerSpec" :local-storage-get "localStorageGet" :dom-get-data "domGetData" :scan-refs-walk "scanRefsWalk" :abort-previous-target "abortPreviousTarget" :thunk-expr "thunkExpr" :create-comment "createComment" :component-closure "componentClosure" :render-dom-form? "isRenderDomForm" :sx-render-with-env "sxRenderWithEnv" :cek-phase "cekPhase" :prevent-default "preventDefault_" :true "true" :definition-form? "isDefinitionForm" :make-map-frame "makeMapFrame" :scope-pop! "scopePop" :contains? "contains" :bind-preload-for "bindPreloadFor" :dom-focus "domFocus" :sf-thread-first "sfThreadFirst" :find-oob-swaps "findOobSwaps" :dom-query-by-id "domQueryById" :handle-sx-response "handleSxResponse" :page-css-classes "pageCssClasses" :odd? "isOdd" :compute-all-deps "computeAllDeps" :has-reactive-reset-frame? "hasReactiveResetFrame_p" :sx-expr-source "sxExprSource" :render-html-form? "isRenderHtmlForm" :lambda-name "lambdaName" :parse-number "parseNumber" :regex-find-all "regexFindAll" :step-sf-define "stepSfDefine" :resolve-mount-target "resolveMountTarget" :emitted "sxEmitted" :browser-push-state "browserPushState" :signal-value "signalValue" :sf-defmacro "sfDefmacro" :swap-dom-nodes "swapDomNodes" :scan-components-from-source "scanComponentsFromSource" :lambda-body "lambdaBody" :scope-peek "scopePeek" :signal-deps "signalDeps" :aser-call "aserCall" :bind-sse-swap "bindSseSwap" :make-for-each-frame "makeForEachFrame" :make-and-frame "makeAndFrame" :parse-macro-params "parseMacroParams" :dispatch-trigger-events "dispatchTriggerEvents" :event-source-connect "eventSourceConnect" :type-of "typeOf" :map-indexed "mapIndexed" :render-lambda-dom "renderLambdaDom" :boot-init "bootInit" :clear-collected! "sxClearCollected" :render-value-to-html "renderValueToHtml" :dispatch-html-form "dispatchHtmlForm" :should-boost-link? "shouldBoostLink" :step-eval "stepEval" :morph-node "morphNode" :track-controller "trackController" :cek-kont "cekKont" :dom-query-all "domQueryAll" :env-merge "envMerge" :raw-html-content "rawHtmlContent" :reactive-fragment "reactiveFragment" :ho-map "hoMap" :browser-scroll-to "browserScrollTo" :render-attrs "renderAttrs" :RENDER_HTML_FORMS "RENDER_HTML_FORMS" :make-reduce-frame "makeReduceFrame" :*batch-depth* "_batchDepth" :kf-name "kfName" :parse-retry-spec "parseRetrySpec" :dom-document "domDocument" :render-to-sx "renderToSx" :host-global "hostGlobal" :scan-refs "scanRefs" :dom-replace-child "domReplaceChild" :signal-set-deps! "signalSetDeps" :empty-dict? "isEmptyDict" :execute-request "executeRequest" :step-eval-list "stepEvalList" :zero? "isZero" :dom-remove-child "domRemoveChild" :compute-all-io-refs "computeAllIoRefs" :sx-render "sxRender" :components-needed "componentsNeeded" :host-set! "hostSet" :sf-case "sfCase" :make-cek-continuation "makeCekContinuation" :sf-let "sfLet" :cek-env "cekEnv" :step-sf-lambda "stepSfLambda" :notify-subscribers "notifySubscribers" :*render-check* "_renderCheck" :step-sf-deref "stepSfDeref" :browser-media-matches? "browserMediaMatches" :parse-time "parseTime" :process-elements "processElements" :try-catch "tryCatch" :filter-params "filterParams" :ident-start? "isIdentStart" :format-date "formatDate" :def-store "defStore" :post-swap "postSwap" :fetch-preload "fetchPreload" :is-processed? "isProcessed" :call-lambda "callLambda" :_page-routes "_pageRoutes" :continuation-data "continuationData" :try-client-route "tryClientRoute" :merge-spread-attrs "mergeSpreadAttrs" :*use-cek-reactive* "_useCekReactive" :cek-step "cekStep" :promise-resolve "promiseResolve" :clear-processed! "clearProcessed" :step-sf-and "stepSfAnd" :strip-component-scripts "stripComponentScripts" :split-path-segments "splitPathSegments" :<= "lte_" :dom-has-class? "domHasClass" :bind-event "bindEvent" :render-to-html "renderToHtml" :dom-add-class "domAddClass" :process-one "processOne" :sx-hydrate "sxHydrate" :render-active? "renderActiveP" :collected "sxCollected" :clear-stores "clearStores" :dom-get-prop "domGetProp" :empty? "isEmpty" :step-sf-when "stepSfWhen" :strip-tags "stripTags" :component-has-children? "componentHasChildren" :VOID_ELEMENTS "VOID_ELEMENTS" :promise-then "promiseThen" :parse-swap-spec "parseSwapSpec" :json-parse "jsonParse" :dom-parent "domParent" :process-oob-swaps "processOobSwaps" :signal? "isSignal" :local-storage-remove "localStorageRemove" :register-io-deps "registerIoDeps" :parse-route-pattern "parseRoutePattern" :process-sx-scripts "processSxScripts" :*store-registry* "_storeRegistry" :dom-ensure-element "domEnsureElement" :eval-expr "evalExpr" :transitive-deps "transitiveDeps" :make-set-frame "makeSetFrame" :get-render-env "getRenderEnv" :sf-named-let "sfNamedLet" :reactive-shift-deref "reactiveShiftDeref" :escape-attr "escapeAttr" :process-component-script "processComponentScript" :transitive-io-refs "transitiveIoRefs" :component-pure? "componentPure_p" :sf-and "sfAnd" :apply-optimistic "applyOptimistic" :ho-every "hoEvery" :dom-parse-html-document "domParseHtmlDocument" :island? "isIsland" :emit-event "emitEvent" :step-ho-reduce "stepHoReduce" :render-dom-raw "renderDomRaw" :clear-loading-state "clearLoadingState" :dom-clone "domClone" :fetch-and-restore "fetchAndRestore" :render-dom-island "renderDomIsland" :step-sf-begin "stepSfBegin" :to-kebab "toKebab" :replace "replace_" :mark-processed! "markProcessed" :insert-remaining-siblings "insertRemainingSiblings" :sx-update-element "sxUpdateElement" :env-extend "envExtend" :handle-html-response "handleHtmlResponse" :dict-delete! "dictDelete" :make-component "makeComponent" :make-cond-frame "makeCondFrame" :sx-load-components "sxLoadComponents" :sf-lambda "sfLambda" :abort-previous "abortPrevious" :step-eval-call "stepEvalCall" :store-env-attr "storeEnvAttr" :chunk-every "chunkEvery" :dom-append "domAppend" :eval-cond-clojure "evalCondClojure" :morph-children "morphChildren" :make-when-frame "makeWhenFrame" :frame-type "frameType" :dom-set-inner-html "domSetInnerHtml" :process-response-headers "processResponseHeaders" :dom-query "domQuery" :dom-remove-class "domRemoveClass" :thunk? "isThunk" :kont-pop "kontPop" :eval-list "evalList" :resolve-target "resolveTarget" :dom-is-child-of? "domIsChildOf" :lambda? "isLambda" :dom-insert-after "domInsertAfter" :make-dynamic-wind-frame "makeDynamicWindFrame" :promise-catch "promiseCatch" :host-new "hostNew" :kont-capture-to-reactive-reset "kontCaptureToReactiveReset" :serialize-island-state "serializeIslandState" :handle-retry "handleRetry" :step-sf-thread-first "stepSfThreadFirst" :make-reactive-reset-frame "makeReactiveResetFrame" :dom-listen "domListen" :even? "isEven" :get-verb-info "getVerbInfo" :dispose-island "disposeIsland" :dom-child-list "domChildList" :log-info "logInfo" :macro-closure "macroClosure" :dict-has? "dictHas" :browser-reload "browserReload" :cond-scheme? "condScheme_p" :make-scope-frame "makeScopeFrame" :sf-define "sfDefine" :ident-char? "isIdentChar" :sx-serialize "sxSerialize" :render-dom-fragment "renderDomFragment" :dom-has-attr? "domHasAttr" :dom-is-active-element? "domIsActiveElement" :dom-create-element "domCreateElement" :create-text-node "createTextNode" :lambda-params "lambdaParams" :host-await "hostAwait" :macro? "isMacro" :dom-text-content "domTextContent" :step-sf-case "stepSfCase" :request-animation-frame "requestAnimationFrame_" :sf-case-step-loop "sfCaseStepLoop" :process-boosted "processBoosted" :sf-cond "sfCond" :dom-head "domHead" :component-io-refs-cached "componentIoRefsCached" :bind-triggers "bindTriggers" :every? "isEvery" :dom-closest "domClosest" :component? "isComponent" :make-handler-def "makeHandlerDef" :should-boost-form? "shouldBoostForm" :parse-header-value "parseHeaderValue" :render-to-dom "renderToDom" :make-or-frame "makeOrFrame" :has-key? "dictHas" :dom-body-inner-html "domBodyInnerHtml" :process-css-response "processCssResponse" :url-pathname "urlPathname" :aser-special "aserSpecial" :create-script-clone "createScriptClone" :match-route-segments "matchRouteSegments" :cek-reactive-text "cekReactiveText" :PRELOAD_TTL "PRELOAD_TTL" :cek-control "cekControl" :bridge-event "bridgeEvent" :resolve-suspense "resolveSuspense" :dom-remove-children-after "domRemoveChildrenAfter" :track-controller-target "trackControllerTarget" :clear-sx-comp-cookie "clearSxCompCookie" :cross-origin? "isCrossOrigin" :extract-response-css "extractResponseCss" :bind-sse "bindSse" :show-indicator "showIndicator" :bind-client-route-link "bindClientRouteLink" :scope-push! "scopePush" :component-set-deps! "componentSetDeps" :element-value "elementValue" :cek-try "cekTry" :make-page-def "makePageDef" :render-html-component "renderHtmlComponent" :ENGINE_VERBS "ENGINE_VERBS" :process-sse "processSse" :loaded-component-names "loadedComponentNames" :browser-replace-state "browserReplaceState" :dom-next-sibling "domNextSibling" :sf-when "sfWhen" :sx-mount "sxMount" :make-query-def "makeQueryDef" :activate-scripts "activateScripts" :now-ms "nowMs" :bind-preload "bindPreload" :preload-cache-get "preloadCacheGet" :validate-for-request "validateForRequest" :BOOLEAN_ATTRS "BOOLEAN_ATTRS" :digit? "isDigit" :zip-pairs "zipPairs" :dom-set-text-content "domSetTextContent" :parse-keyword-args "parseKeywordArgs" :ho-map-indexed "hoMapIndexed" :cek-value "cekValue" :env-components "envComponents" :dict? "isDict" :is-else-clause? "isElseClause" :reactive-attr "reactiveAttr" :sf-quasiquote "sfQuasiquote" :create-fragment "createFragment" :is-render-expr? "isRenderExpr" :spread-attrs "spreadAttrs" :render-html-island "renderHtmlIsland" :aser-list "aserList" :provide-pop! "providePop" :swap-html-string "swapHtmlString" :render-expr "renderExpr" :dom-set-attr "domSetAttr" :boost-descendants "boostDescendants" :browser-prompt "browserPrompt" :HEAD_HOIST_SELECTOR "HEAD_HOIST_SELECTOR" :make-deref-frame "makeDerefFrame" :dom-tag-name "domTagName" :scope-emitted "sxEmitted" :query-sx-scripts "querySxScripts" :strip-prefix "stripPrefix" :scan-io-refs "scanIoRefs" :step-sf-cond "stepSfCond" :dom-id "domId" :dom-body "domBody" :make-macro "makeMacro" :identical? "isIdentical" :cek-reactive-attr "cekReactiveAttr" :step-sf-or "stepSfOr" :render-dom-list "renderDomList" :init-css-tracking "initCssTracking" :sx-serialize-dict "sxSerializeDict" :try-async-eval-content "tryAsyncEvalContent" :register-in-scope "registerInScope" :cek-terminal? "cekTerminal_p" :step-ho-filter "stepHoFilter" :sf-set! "sfSetBang" :false "false" :browser-navigate "browserNavigate" :dom-node-type "domNodeType" :bind-boost-link "bindBoostLink" :scan-css-classes "scanCssClasses" :dom-matches? "domMatches" :set-sx-comp-cookie "setSxCompCookie" :ho-reduce "hoReduce" :ho-form? "isHoForm" :macro-params "macroParams" :on-event "onEvent" :parse-int "parseInt_" :step-sf-reset "stepSfReset" :*render-fn* "_renderFn" :dom-outer-html "domOuterHtml" :special-form? "isSpecialForm" :observe-intersection "observeIntersection" :make-env "makeEnv" :make-signal "makeSignal" :push-wind! "pushWind" :dom-set-prop "domSetProp" :eval-expr-cek "evalExprCek" :callable? "isCallable" :sf-defisland "sfDefisland" :kont-capture-to-reset "kontCaptureToReset" :handle-fetch-success "handleFetchSuccess" :dom-get-style "domGetStyle" :sf-cond-scheme "sfCondScheme" :keyword-name "keywordName" :env-bind! "envBind" :map-dict "mapDict" :host-callback "hostCallback" :remove-head-element "removeHeadElement" :context "sxContext" :dom-is-input-element? "domIsInputElement" :spread? "isSpread" :make-cek-value "makeCekValue" :step-continue "stepContinue" :dom-window "domWindow" :hydrate-island "hydrateIsland" :make-action-def "makeActionDef" :kont-empty? "kontEmpty_p" :make-filter-frame "makeFilterFrame" :make-thunk "makeThunk" :make-symbol "makeSymbol" :dict-get "dictGet" :dispatch-render-form "dispatchRenderForm" :dom-prepend "domPrepend" :make-begin-frame "makeBeginFrame" :merge-envs "mergeEnvs" :continue-with-call "continueWithCall" :browser-confirm "browserConfirm" :make-spread "makeSpread" :register-special-form! "registerSpecialForm" :csrf-token "csrfToken" :for-each "forEach" :make-dict-frame "makeDictFrame" :trampoline-cek "trampolineCek" :sf-letrec "sfLetrec" :DEFAULT_SWAP "DEFAULT_SWAP" :component-name "componentName" :*batch-queue* "_batchQueue" :component-css-classes "componentCssClasses" :make-arg-frame "makeArgFrame" :dict-set! "dictSet" :step-sf-let "stepSfLet" :browser-same-origin? "browserSameOrigin" :sx-hydrate-islands "sxHydrateIslands" :make-define-frame "makeDefineFrame" :process-page-scripts "processPageScripts" :ho-for-each "hoForEach" :stop-propagation "stopPropagation_" :sx-process-scripts "sxProcessScripts" :make-if-frame "makeIfFrame" :sf-or "sfOr" :dom-insert-before "domInsertBefore" :step-sf-shift "stepSfShift" :format-decimal "formatDecimal" :json-serialize "jsonSerialize" :defcomp-kwarg "defcompKwarg" :reactive-text "reactiveText" :dom-remove-attr "domRemoveAttr" :eval-cond "evalCond" :_css-hash "_cssHash" :fetch-location "fetchLocation" :sx-hydrate-elements "sxHydrateElements" :dispose-computed "disposeComputed" :abort-error? "isAbortError" :set-timeout "setTimeout_" :new-abort-controller "newAbortController" :nil? "isNil" :env-get "envGet" :call-component "callComponent" :SVG_NS "SVG_NS" :RENDER_DOM_FORMS "RENDER_DOM_FORMS" :build-request-headers "buildRequestHeaders" :page-component-bundle "pageComponentBundle" :render-list-to-html "renderListToHtml" :string? "isString" :dom-node-name "domNodeName" :hoist-head-elements-full "hoistHeadElementsFull"}) +(define js-renames {:ho-filter "hoFilter" :thunk-env "thunkEnv" :cek-run "cekRun" :*custom-special-forms* "_customSpecialForms" :with-island-scope "withIslandScope" :step-sf-if "stepSfIf" :dom-is-fragment? "domIsFragment" :process-bindings "processBindings" :call-thunk "callThunk" :fetch-streaming "fetchStreaming" :bind-inline-handlers "bindInlineHandlers" :set-interval "setInterval_" :number? "isNumber" :reactive-list "reactiveList" :expand-macro "expandMacro" :handle-history "handleHistory" :page-render-plan "pageRenderPlan" :make-let-frame "makeLetFrame" :parse-comp-params "parseCompParams" :next-retry-ms "nextRetryMs" :fetch-request "fetchRequest" :kont-push "kontPush" :macro-body "macroBody" :for-each-indexed "forEachIndexed" :step-ho-for-each "stepHoForEach" :set-render-active! "setRenderActiveB" :local-storage-set "localStorageSet" :dom-get-attr "domGetAttr" :parse-element-args "parseElementArgs" :process-emit-elements "processEmitElements" :build-request-body "buildRequestBody" :kont-top "kontTop" :event-detail "eventDetail" :match-route "matchRoute" :handle-popstate "handlePopstate" :event-source-listen "eventSourceListen" :select-from-container "selectFromContainer" :try-eval-content "tryEvalContent" :query-page-scripts "queryPageScripts" :scope-emit! "scopeEmit" :promise-delayed "promiseDelayed" :make-call-frame "makeCallFrame" :HTML_TAGS "HTML_TAGS" :macro-rest-param "macroRestParam" :env-has? "envHas" :make-raw-html "makeRawHtml" :dom-set-style "domSetStyle" :try-parse-json "tryParseJson" :host-call "hostCall" :VERB_SELECTOR "VERB_SELECTOR" :render-dom-element "renderDomElement" :escape-html "escapeHtml" :parse-sse-swap "parseSseSwap" :disable-elements "disableElements" :starts-with? "startsWith" :parse-env-attr "parseEnvAttr" :ho-some "hoSome" :eval-cond-scheme "evalCondScheme" :ends-with? "endsWith" :>= "gte_" :dom-dispatch "domDispatch" :preload-cache-set "preloadCacheSet" :signal-subscribers "signalSubscribers" :step-sf-provide "stepSfProvide" :signal-add-sub! "signalAddSub" :render-lambda-html "renderLambdaHtml" :dom-set-data "domSetData" :make-thread-frame "makeThreadFrame" :make-sx-expr "makeSxExpr" :pop-wind! "popWind" :dom-append-to-head "domAppendToHead" :hoist-head-elements "hoistHeadElements" :make-reset-frame "makeResetFrame" :flush-subscribers "flushSubscribers" :controller-signal "controllerSignal" :clear-interval "clearInterval_" :children-to-fragment "childrenToFragment" :sx-render-component "sxRenderComponent" :with-transition "withTransition" :scan-io-refs-walk "scanIoRefsWalk" :step-sf-scope "stepSfScope" :get-primitive "getPrimitive" :_preload-cache "_preloadCache" :select-html-from-doc "selectHtmlFromDoc" :browser-location-href "browserLocationHref" :sf-case-loop "sfCaseLoop" :sf-dynamic-wind "sfDynamicWind" :symbol-name "symbolName" :set-lambda-name! "setLambdaName" :host-get "hostGet" :aser-fragment "aserFragment" :render-dom-unknown-component "renderDomUnknownComponent" :!= "notEqual_" :SX_VERSION "SX_VERSION" :render-html-element "renderHtmlElement" :dom-first-child "domFirstChild" :bind-client-route-click "bindClientRouteClick" :sf-cond-clojure "sfCondClojure" :MATH_NS "MATH_NS" :default-trigger "defaultTrigger" :signal-remove-sub! "signalRemoveSub" :make-cek-state "makeCekState" :emit! "sxEmit" :sf-quote "sfQuote" :bind-boost-form "bindBoostForm" :component-params "componentParams" :do-preload "doPreload" :component-affinity "componentAffinity" :eval-case-aser "evalCaseAser" :sf-begin "sfBegin" :revert-optimistic "revertOptimistic" :whitespace? "isWhitespace" :host-typeof "hostTypeof" :dom-insert-adjacent-html "domInsertAdjacentHtml" :step-sf-set! "stepSfSet" :error-message "errorMessage" :schedule-idle "scheduleIdle" :find-matching-route "findMatchingRoute" :component-body "componentBody" :qq-expand "qqExpand" :provide-push! "providePush" :make-keyword "makeKeyword" :do-fetch "doFetch" :component-deps "componentDeps" :component-set-io-refs! "componentSetIoRefs" :escape-string "escapeString" :make-island "makeIsland" :nil "NIL" :log-parse-error "logParseError" :enable-cek-reactive! "enableCekReactive" :signal-set-value! "signalSetValue" :env-set! "envSet" :clear-timeout "clearTimeout_" :sf-defcomp "sfDefcomp" :step-ho-map "stepHoMap" :dom-parse-html "domParseHtml" :make-lambda "makeLambda" :sf-if "sfIf" :make-route-segment "makeRouteSegment" :lambda-closure "lambdaClosure" :render-target "renderTarget" :dom-attr-list "domAttrList" :log-warn "logWarn" :eval-call "evalCall" :sync-attrs "syncAttrs" :make-case-frame "makeCaseFrame" :render-dom-component "renderDomComponent" :dom-child-nodes "domChildNodes" :collect! "sxCollect" :use-store "useStore" :classify-trigger "classifyTrigger" :engine-init "engineInit" :list? "isList" :index-of "indexOf_" :component-io-refs "componentIoRefs" :dom-remove "domRemove" :set-document-title "setDocumentTitle" :primitive? "isPrimitive" :parse-trigger-spec "parseTriggerSpec" :local-storage-get "localStorageGet" :dom-get-data "domGetData" :scan-refs-walk "scanRefsWalk" :abort-previous-target "abortPreviousTarget" :thunk-expr "thunkExpr" :create-comment "createComment" :component-closure "componentClosure" :render-dom-form? "isRenderDomForm" :sx-render-with-env "sxRenderWithEnv" :cek-phase "cekPhase" :prevent-default "preventDefault_" :true "true" :definition-form? "isDefinitionForm" :make-map-frame "makeMapFrame" :scope-pop! "scopePop" :contains? "contains" :bind-preload-for "bindPreloadFor" :dom-focus "domFocus" :sf-thread-first "sfThreadFirst" :find-oob-swaps "findOobSwaps" :dom-query-by-id "domQueryById" :handle-sx-response "handleSxResponse" :page-css-classes "pageCssClasses" :odd? "isOdd" :compute-all-deps "computeAllDeps" :has-reactive-reset-frame? "hasReactiveResetFrame_p" :sx-expr-source "sxExprSource" :render-html-form? "isRenderHtmlForm" :lambda-name "lambdaName" :parse-number "parseNumber" :regex-find-all "regexFindAll" :step-sf-define "stepSfDefine" :resolve-mount-target "resolveMountTarget" :emitted "sxEmitted" :browser-push-state "browserPushState" :signal-value "signalValue" :sf-defmacro "sfDefmacro" :swap-dom-nodes "swapDomNodes" :scan-components-from-source "scanComponentsFromSource" :lambda-body "lambdaBody" :scope-peek "scopePeek" :signal-deps "signalDeps" :aser-call "aserCall" :bind-sse-swap "bindSseSwap" :make-for-each-frame "makeForEachFrame" :make-and-frame "makeAndFrame" :parse-macro-params "parseMacroParams" :dispatch-trigger-events "dispatchTriggerEvents" :event-source-connect "eventSourceConnect" :type-of "typeOf" :map-indexed "mapIndexed" :render-lambda-dom "renderLambdaDom" :boot-init "bootInit" :clear-collected! "sxClearCollected" :render-value-to-html "renderValueToHtml" :dispatch-html-form "dispatchHtmlForm" :should-boost-link? "shouldBoostLink" :step-eval "stepEval" :morph-node "morphNode" :track-controller "trackController" :cek-kont "cekKont" :dom-query-all "domQueryAll" :env-merge "envMerge" :raw-html-content "rawHtmlContent" :reactive-fragment "reactiveFragment" :ho-map "hoMap" :browser-scroll-to "browserScrollTo" :render-attrs "renderAttrs" :RENDER_HTML_FORMS "RENDER_HTML_FORMS" :make-reduce-frame "makeReduceFrame" :*batch-depth* "_batchDepth" :kf-name "kfName" :parse-retry-spec "parseRetrySpec" :dom-document "domDocument" :render-to-sx "renderToSx" :host-global "hostGlobal" :scan-refs "scanRefs" :dom-replace-child "domReplaceChild" :signal-set-deps! "signalSetDeps" :empty-dict? "isEmptyDict" :execute-request "executeRequest" :step-eval-list "stepEvalList" :zero? "isZero" :dom-remove-child "domRemoveChild" :compute-all-io-refs "computeAllIoRefs" :sx-render "sxRender" :components-needed "componentsNeeded" :host-set! "hostSet" :sf-case "sfCase" :make-cek-continuation "makeCekContinuation" :sf-let "sfLet" :cek-env "cekEnv" :step-sf-lambda "stepSfLambda" :notify-subscribers "notifySubscribers" :*render-check* "_renderCheck" :step-sf-deref "stepSfDeref" :browser-media-matches? "browserMediaMatches" :parse-time "parseTime" :process-elements "processElements" :try-catch "tryCatch" :filter-params "filterParams" :ident-start? "isIdentStart" :format-date "formatDate" :def-store "defStore" :post-swap "postSwap" :fetch-preload "fetchPreload" :is-processed? "isProcessed" :call-lambda "callLambda" :_page-routes "_pageRoutes" :continuation-data "continuationData" :try-client-route "tryClientRoute" :merge-spread-attrs "mergeSpreadAttrs" :*use-cek-reactive* "_useCekReactive" :cek-step "cekStep" :promise-resolve "promiseResolve" :clear-processed! "clearProcessed" :step-sf-and "stepSfAnd" :strip-component-scripts "stripComponentScripts" :split-path-segments "splitPathSegments" :<= "lte_" :dom-has-class? "domHasClass" :bind-event "bindEvent" :render-to-html "renderToHtml" :dom-add-class "domAddClass" :process-one "processOne" :sx-hydrate "sxHydrate" :render-active? "renderActiveP" :collected "sxCollected" :clear-stores "clearStores" :dom-get-prop "domGetProp" :empty? "isEmpty" :step-sf-when "stepSfWhen" :strip-tags "stripTags" :component-has-children? "componentHasChildren" :VOID_ELEMENTS "VOID_ELEMENTS" :promise-then "promiseThen" :parse-swap-spec "parseSwapSpec" :json-parse "jsonParse" :dom-parent "domParent" :process-oob-swaps "processOobSwaps" :signal? "isSignal" :local-storage-remove "localStorageRemove" :register-io-deps "registerIoDeps" :parse-route-pattern "parseRoutePattern" :process-sx-scripts "processSxScripts" :*store-registry* "_storeRegistry" :dom-ensure-element "domEnsureElement" :eval-expr "evalExpr" :transitive-deps "transitiveDeps" :make-set-frame "makeSetFrame" :get-render-env "getRenderEnv" :sf-named-let "sfNamedLet" :reactive-shift-deref "reactiveShiftDeref" :escape-attr "escapeAttr" :process-component-script "processComponentScript" :transitive-io-refs "transitiveIoRefs" :component-pure? "componentPure_p" :sf-and "sfAnd" :apply-optimistic "applyOptimistic" :ho-every "hoEvery" :dom-parse-html-document "domParseHtmlDocument" :island? "isIsland" :emit-event "emitEvent" :step-ho-reduce "stepHoReduce" :render-dom-raw "renderDomRaw" :clear-loading-state "clearLoadingState" :dom-clone "domClone" :fetch-and-restore "fetchAndRestore" :render-dom-island "renderDomIsland" :step-sf-begin "stepSfBegin" :to-kebab "toKebab" :replace "replace_" :mark-processed! "markProcessed" :insert-remaining-siblings "insertRemainingSiblings" :sx-update-element "sxUpdateElement" :env-extend "envExtend" :handle-html-response "handleHtmlResponse" :dict-delete! "dictDelete" :make-component "makeComponent" :make-cond-frame "makeCondFrame" :sx-load-components "sxLoadComponents" :sf-lambda "sfLambda" :abort-previous "abortPrevious" :step-eval-call "stepEvalCall" :store-env-attr "storeEnvAttr" :chunk-every "chunkEvery" :dom-append "domAppend" :eval-cond-clojure "evalCondClojure" :morph-children "morphChildren" :make-when-frame "makeWhenFrame" :frame-type "frameType" :dom-set-inner-html "domSetInnerHtml" :process-response-headers "processResponseHeaders" :dom-query "domQuery" :dom-remove-class "domRemoveClass" :thunk? "isThunk" :kont-pop "kontPop" :eval-list "evalList" :resolve-target "resolveTarget" :dom-is-child-of? "domIsChildOf" :lambda? "isLambda" :dom-insert-after "domInsertAfter" :make-dynamic-wind-frame "makeDynamicWindFrame" :promise-catch "promiseCatch" :host-new "hostNew" :kont-capture-to-reactive-reset "kontCaptureToReactiveReset" :serialize-island-state "serializeIslandState" :handle-retry "handleRetry" :step-sf-thread-first "stepSfThreadFirst" :make-reactive-reset-frame "makeReactiveResetFrame" :dom-listen "domListen" :even? "isEven" :get-verb-info "getVerbInfo" :dispose-island "disposeIsland" :dom-child-list "domChildList" :log-info "logInfo" :macro-closure "macroClosure" :dict-has? "dictHas" :browser-reload "browserReload" :cond-scheme? "condScheme_p" :make-scope-frame "makeScopeFrame" :sf-define "sfDefine" :ident-char? "isIdentChar" :sx-serialize "sxSerialize" :render-dom-fragment "renderDomFragment" :dom-has-attr? "domHasAttr" :dom-is-active-element? "domIsActiveElement" :dom-create-element "domCreateElement" :create-text-node "createTextNode" :lambda-params "lambdaParams" :host-await "hostAwait" :macro? "isMacro" :dom-text-content "domTextContent" :step-sf-case "stepSfCase" :request-animation-frame "requestAnimationFrame_" :sf-case-step-loop "sfCaseStepLoop" :process-boosted "processBoosted" :sf-cond "sfCond" :dom-head "domHead" :component-io-refs-cached "componentIoRefsCached" :bind-triggers "bindTriggers" :every? "isEvery" :dom-closest "domClosest" :component? "isComponent" :make-handler-def "makeHandlerDef" :should-boost-form? "shouldBoostForm" :parse-header-value "parseHeaderValue" :render-to-dom "renderToDom" :make-or-frame "makeOrFrame" :has-key? "dictHas" :dom-body-inner-html "domBodyInnerHtml" :process-css-response "processCssResponse" :url-pathname "urlPathname" :aser-special "aserSpecial" :create-script-clone "createScriptClone" :match-route-segments "matchRouteSegments" :cek-reactive-text "cekReactiveText" :PRELOAD_TTL "PRELOAD_TTL" :cek-control "cekControl" :bridge-event "bridgeEvent" :resolve-suspense "resolveSuspense" :dom-remove-children-after "domRemoveChildrenAfter" :track-controller-target "trackControllerTarget" :clear-sx-comp-cookie "clearSxCompCookie" :cross-origin? "isCrossOrigin" :extract-response-css "extractResponseCss" :bind-sse "bindSse" :show-indicator "showIndicator" :bind-client-route-link "bindClientRouteLink" :scope-push! "scopePush" :component-set-deps! "componentSetDeps" :element-value "elementValue" :cek-try "cekTry" :make-page-def "makePageDef" :render-html-component "renderHtmlComponent" :ENGINE_VERBS "ENGINE_VERBS" :process-sse "processSse" :loaded-component-names "loadedComponentNames" :browser-replace-state "browserReplaceState" :dom-next-sibling "domNextSibling" :sf-when "sfWhen" :sx-mount "sxMount" :make-query-def "makeQueryDef" :activate-scripts "activateScripts" :now-ms "nowMs" :bind-preload "bindPreload" :preload-cache-get "preloadCacheGet" :validate-for-request "validateForRequest" :BOOLEAN_ATTRS "BOOLEAN_ATTRS" :digit? "isDigit" :zip-pairs "zipPairs" :dom-set-text-content "domSetTextContent" :parse-keyword-args "parseKeywordArgs" :ho-map-indexed "hoMapIndexed" :cek-value "cekValue" :env-components "envComponents" :dict? "isDict" :is-else-clause? "isElseClause" :reactive-attr "reactiveAttr" :sf-quasiquote "sfQuasiquote" :create-fragment "createFragment" :is-render-expr? "isRenderExpr" :spread-attrs "spreadAttrs" :render-html-island "renderHtmlIsland" :aser-list "aserList" :provide-pop! "providePop" :swap-html-string "swapHtmlString" :render-expr "renderExpr" :dom-set-attr "domSetAttr" :boost-descendants "boostDescendants" :browser-prompt "browserPrompt" :HEAD_HOIST_SELECTOR "HEAD_HOIST_SELECTOR" :make-deref-frame "makeDerefFrame" :dom-tag-name "domTagName" :scope-emitted "sxEmitted" :query-sx-scripts "querySxScripts" :strip-prefix "stripPrefix" :scan-io-refs "scanIoRefs" :step-sf-cond "stepSfCond" :dom-id "domId" :dom-body "domBody" :make-macro "makeMacro" :identical? "isIdentical" :cek-reactive-attr "cekReactiveAttr" :step-sf-or "stepSfOr" :render-dom-list "renderDomList" :init-css-tracking "initCssTracking" :sx-serialize-dict "sxSerializeDict" :try-async-eval-content "tryAsyncEvalContent" :register-in-scope "registerInScope" :cek-terminal? "cekTerminal_p" :step-ho-filter "stepHoFilter" :sf-set! "sfSetBang" :false "false" :browser-navigate "browserNavigate" :dom-node-type "domNodeType" :bind-boost-link "bindBoostLink" :scan-css-classes "scanCssClasses" :dom-matches? "domMatches" :set-sx-comp-cookie "setSxCompCookie" :ho-reduce "hoReduce" :ho-form? "isHoForm" :macro-params "macroParams" :on-event "onEvent" :parse-int "parseInt_" :step-sf-reset "stepSfReset" :*render-fn* "_renderFn" :dom-outer-html "domOuterHtml" :special-form? "isSpecialForm" :observe-intersection "observeIntersection" :make-env "makeEnv" :make-signal "makeSignal" :push-wind! "pushWind" :dom-set-prop "domSetProp" :eval-expr-cek "evalExprCek" :callable? "isCallable" :sf-defisland "sfDefisland" :kont-capture-to-reset "kontCaptureToReset" :handle-fetch-success "handleFetchSuccess" :dom-get-style "domGetStyle" :sf-cond-scheme "sfCondScheme" :keyword-name "keywordName" :env-bind! "envBind" :map-dict "mapDict" :host-callback "hostCallback" :remove-head-element "removeHeadElement" :context "sxContext" :dom-is-input-element? "domIsInputElement" :spread? "isSpread" :make-cek-value "makeCekValue" :step-continue "stepContinue" :dom-window "domWindow" :hydrate-island "hydrateIsland" :make-action-def "makeActionDef" :kont-empty? "kontEmpty_p" :make-filter-frame "makeFilterFrame" :make-thunk "makeThunk" :make-symbol "makeSymbol" :dict-get "dictGet" :dispatch-render-form "dispatchRenderForm" :dom-prepend "domPrepend" :make-begin-frame "makeBeginFrame" :merge-envs "mergeEnvs" :continue-with-call "continueWithCall" :browser-confirm "browserConfirm" :make-spread "makeSpread" :register-special-form! "registerSpecialForm" :csrf-token "csrfToken" :for-each "forEach" :make-dict-frame "makeDictFrame" :trampoline-cek "trampolineCek" :sf-letrec "sfLetrec" :DEFAULT_SWAP "DEFAULT_SWAP" :component-name "componentName" :*batch-queue* "_batchQueue" :component-css-classes "componentCssClasses" :make-arg-frame "makeArgFrame" :dict-set! "dictSet" :step-sf-let "stepSfLet" :browser-same-origin? "browserSameOrigin" :sx-hydrate-islands "sxHydrateIslands" :make-define-frame "makeDefineFrame" :process-page-scripts "processPageScripts" :ho-for-each "hoForEach" :stop-propagation "stopPropagation_" :sx-process-scripts "sxProcessScripts" :make-if-frame "makeIfFrame" :sf-or "sfOr" :dom-insert-before "domInsertBefore" :step-sf-shift "stepSfShift" :format-decimal "formatDecimal" :json-serialize "jsonSerialize" :defcomp-kwarg "defcompKwarg" :reactive-text "reactiveText" :dom-remove-attr "domRemoveAttr" :eval-cond "evalCond" :_css-hash "_cssHash" :fetch-location "fetchLocation" :sx-hydrate-elements "sxHydrateElements" :dispose-computed "disposeComputed" :abort-error? "isAbortError" :set-timeout "setTimeout_" :new-abort-controller "newAbortController" :nil? "isNil" :env-get "envGet" :call-component "callComponent" :SVG_NS "SVG_NS" :RENDER_DOM_FORMS "RENDER_DOM_FORMS" :build-request-headers "buildRequestHeaders" :page-component-bundle "pageComponentBundle" :render-list-to-html "renderListToHtml" :string? "isString" :dom-node-name "domNodeName" :hoist-head-elements-full "hoistHeadElementsFull" :vector->list "vectorToList" :list->vector "listToVector"}) (define js-mangle diff --git a/plans/agent-briefings/primitives-loop.md b/plans/agent-briefings/primitives-loop.md index 08793f42..ab54de62 100644 --- a/plans/agent-briefings/primitives-loop.md +++ b/plans/agent-briefings/primitives-loop.md @@ -329,7 +329,7 @@ on type (list → existing path, vector → index loop, string → char iteratio - `sequence-append` `s1` `s2` → concatenate two same-type sequences Steps: -- [ ] Spec: extend `map`/`filter`/`reduce`/`for-each`/`some`/`every?` in `spec/evaluator.sx` +- [x] Spec: extend `map`/`filter`/`reduce`/`for-each`/`some`/`every?` in `spec/evaluator.sx` to type-dispatch; add `in-range` lazy sequence type + helpers. - [ ] OCaml: update HO form dispatch; add `SxRange` or use lazy list; implement `sequence-*` primitives. @@ -732,3 +732,4 @@ _Newest first._ - 2026-04-25: Phase 1 spec step done — all 10 vector primitives in spec/primitives.sx have full :as type annotations, :returns, :doc; make-vector optional fill param added. - 2026-04-25: Phase 1 OCaml step done — bounds-checked vector-ref/set!, vector-copy now accepts optional start/end, spec/primitives.sx doc updated. 10/10 r7rs vector tests pass, 4747 total (394 pre-existing hs-upstream fails unchanged). - 2026-04-25: Phase 0 complete — stopped CL/APL/Ruby/Tcl loops (all 4 idle at shell); confirmed E38 (tokenizer :end/:line) and E39 (WebWorker stub) both have implementation commits. +- 2026-05-01: Phase 11 Spec step done — seq-to-list coercion helper; ho-setup-dispatch extended with seqToList on all collection args; sequence-to-list/vector/length/ref/append + in-range added to evaluator.sx. Restored 3 accidentally-deleted make-cek-state/value/suspended definitions. Fixed 8 shorthand define forms + added vector->list/list->vector transpiler renames. JS: 2137 passing (+28 vs HEAD baseline of 2109). diff --git a/shared/static/scripts/sx-browser.js b/shared/static/scripts/sx-browser.js index 92b0cf3d..11a157e2 100644 --- a/shared/static/scripts/sx-browser.js +++ b/shared/static/scripts/sx-browser.js @@ -31,7 +31,7 @@ // ========================================================================= var NIL = Object.freeze({ _nil: true, toString: function() { return "nil"; } }); - var SX_VERSION = "2026-05-01T08:18:20Z"; + var SX_VERSION = "2026-05-01T09:26:26Z"; function isNil(x) { return x === NIL || x === null || x === undefined; } function isSxTruthy(x) { return x !== false && !isNil(x); } @@ -170,6 +170,7 @@ if (x._sx_expr) return "sx-expr"; if (x._vector) return "vector"; if (x._string_buffer) return "string-buffer"; + if (x._hash_table) return "hash-table"; if (typeof Node !== "undefined" && x instanceof Node) return "dom-node"; if (Array.isArray(x)) return "list"; if (typeof x === "object") return "dict"; @@ -425,7 +426,7 @@ PRIMITIVES["inexact?"] = function(x) { return typeof x === "number" && !Number.isInteger(x); }; PRIMITIVES["string?"] = function(x) { return typeof x === "string"; }; PRIMITIVES["list?"] = Array.isArray; - PRIMITIVES["dict?"] = function(x) { return x !== null && typeof x === "object" && !Array.isArray(x) && !x._sym && !x._kw && !x._string_buffer && !x._vector; }; + PRIMITIVES["dict?"] = function(x) { return x !== null && typeof x === "object" && !Array.isArray(x) && !x._sym && !x._kw && !x._string_buffer && !x._vector && !x._hash_table; }; PRIMITIVES["empty?"] = function(c) { return isNil(c) || (Array.isArray(c) ? c.length === 0 : typeof c === "string" ? c.length === 0 : Object.keys(c).length === 0); }; PRIMITIVES["contains?"] = function(c, k) { if (typeof c === "string") return c.indexOf(String(k)) !== -1; @@ -717,6 +718,35 @@ }; + // stdlib.hash-table + function SxHashTable() { this.data = new Map(); this._hash_table = true; } + PRIMITIVES["make-hash-table"] = function() { return new SxHashTable(); }; + PRIMITIVES["hash-table?"] = function(x) { return x instanceof SxHashTable; }; + PRIMITIVES["hash-table-set!"] = function(ht, k, v) { ht.data.set(k, v); return null; }; + PRIMITIVES["hash-table-ref"] = function(ht, k, dflt) { + if (ht.data.has(k)) return ht.data.get(k); + if (arguments.length > 2) return dflt; + throw new Error("hash-table-ref: key not found"); + }; + PRIMITIVES["hash-table-delete!"] = function(ht, k) { ht.data.delete(k); return null; }; + PRIMITIVES["hash-table-size"] = function(ht) { return ht.data.size; }; + PRIMITIVES["hash-table-keys"] = function(ht) { return Array.from(ht.data.keys()); }; + PRIMITIVES["hash-table-values"] = function(ht) { return Array.from(ht.data.values()); }; + PRIMITIVES["hash-table->alist"] = function(ht) { + var result = []; + ht.data.forEach(function(v, k) { result.push([k, v]); }); + return result; + }; + PRIMITIVES["hash-table-for-each"] = function(ht, fn) { + ht.data.forEach(function(v, k) { apply(fn, [k, v]); }); + return null; + }; + PRIMITIVES["hash-table-merge!"] = function(dst, src) { + src.data.forEach(function(v, k) { dst.data.set(k, v); }); + return null; + }; + + function isPrimitive(name) { return name in PRIMITIVES; } function getPrimitive(name) { return PRIMITIVES[name]; } @@ -774,6 +804,8 @@ var mod = PRIMITIVES["mod"]; var indexOf_ = PRIMITIVES["index-of"]; var hasKey = PRIMITIVES["has-key?"]; + var vectorToList = PRIMITIVES["vector->list"]; + var listToVector = PRIMITIVES["list->vector"]; function zip(a, b) { var r = []; for (var i = 0; i < Math.min(a.length, b.length); i++) r.push([a[i], b[i]]); return r; } function append_b(arr, x) { arr.push(x); return arr; } var apply = function(f, args) { @@ -3012,6 +3044,15 @@ PRIMITIVES["ho-fn?"] = hoFn_p; })()); }; PRIMITIVES["ho-swap-args"] = hoSwapArgs; + // seq-to-list + var seqToList = function(x) { return (isSxTruthy(sxEq(x, NIL)) ? [] : (isSxTruthy(isList(x)) ? x : (isSxTruthy(vector_p(x)) ? vectorToList(x) : (isSxTruthy(isString(x)) ? (function() { + var n = len(x); + var loop = function(i, acc) { return (isSxTruthy((i < 0)) ? acc : loop((i - 1), cons(slice(x, i, (i + 1)), acc))); }; +PRIMITIVES["loop"] = loop; + return loop((n - 1), []); +})() : x)))); }; +PRIMITIVES["seq-to-list"] = seqToList; + // ho-setup-dispatch var hoSetupDispatch = function(hoType, evaled, env, kont) { return (function() { var ordered = hoSwapArgs(hoType, evaled); @@ -3025,32 +3066,61 @@ PRIMITIVES["ho-swap-args"] = hoSwapArgs; return continueWithCall(f, heads, env, [], kontPush(makeMultiMapFrame(f, tails, [], env), kont)); })()); })() : (function() { - var coll = nth(ordered, 1); + var coll = seqToList(nth(ordered, 1)); return (isSxTruthy(isEmpty(coll)) ? makeCekValue([], env, kont) : continueWithCall(f, [first(coll)], env, [], kontPush(makeMapFrame(f, rest(coll), [], env), kont))); })()); if (_m == "map-indexed") return (function() { - var coll = nth(ordered, 1); + var coll = seqToList(nth(ordered, 1)); return (isSxTruthy(isEmpty(coll)) ? makeCekValue([], env, kont) : continueWithCall(f, [0, first(coll)], env, [], kontPush(makeMapIndexedFrame(f, rest(coll), [], env), kont))); })(); if (_m == "filter") return (function() { - var coll = nth(ordered, 1); + var coll = seqToList(nth(ordered, 1)); return (isSxTruthy(isEmpty(coll)) ? makeCekValue([], env, kont) : continueWithCall(f, [first(coll)], env, [], kontPush(makeFilterFrame(f, rest(coll), [], first(coll), env), kont))); })(); if (_m == "reduce") return (function() { var init = nth(ordered, 1); - var coll = nth(ordered, 2); + var coll = seqToList(nth(ordered, 2)); return (isSxTruthy(isEmpty(coll)) ? makeCekValue(init, env, kont) : continueWithCall(f, [init, first(coll)], env, [], kontPush(makeReduceFrame(f, rest(coll), env), kont))); })(); if (_m == "some") return (function() { - var coll = nth(ordered, 1); + var coll = seqToList(nth(ordered, 1)); return (isSxTruthy(isEmpty(coll)) ? makeCekValue(false, env, kont) : continueWithCall(f, [first(coll)], env, [], kontPush(makeSomeFrame(f, rest(coll), env), kont))); })(); if (_m == "every") return (function() { - var coll = nth(ordered, 1); + var coll = seqToList(nth(ordered, 1)); return (isSxTruthy(isEmpty(coll)) ? makeCekValue(true, env, kont) : continueWithCall(f, [first(coll)], env, [], kontPush(makeEveryFrame(f, rest(coll), env), kont))); })(); if (_m == "for-each") return (function() { - var coll = nth(ordered, 1); + var coll = seqToList(nth(ordered, 1)); return (isSxTruthy(isEmpty(coll)) ? makeCekValue(NIL, env, kont) : continueWithCall(f, [first(coll)], env, [], kontPush(makeForEachFrame(f, rest(coll), env), kont))); })(); return error((String("Unknown HO type: ") + String(hoType))); })(); })(); })(); }; PRIMITIVES["ho-setup-dispatch"] = hoSetupDispatch; + // sequence-to-list + var sequenceToList = function(s) { return seqToList(s); }; +PRIMITIVES["sequence-to-list"] = sequenceToList; + + // sequence-to-vector + var sequenceToVector = function(s) { return listToVector(seqToList(s)); }; +PRIMITIVES["sequence-to-vector"] = sequenceToVector; + + // sequence-length + var sequenceLength = function(s) { return (isSxTruthy(sxOr(sxEq(s, NIL), isList(s))) ? len(s) : (isSxTruthy(vector_p(s)) ? vectorLength(s) : (isSxTruthy(isString(s)) ? len(s) : len(seqToList(s))))); }; +PRIMITIVES["sequence-length"] = sequenceLength; + + // sequence-ref + var sequenceRef = function(s, i) { return (isSxTruthy(sxOr(sxEq(s, NIL), isList(s))) ? nth(s, i) : (isSxTruthy(vector_p(s)) ? vectorRef(s, i) : (isSxTruthy(isString(s)) ? slice(s, i, (i + 1)) : nth(seqToList(s), i)))); }; +PRIMITIVES["sequence-ref"] = sequenceRef; + + // sequence-append + var sequenceAppend = function(s1, s2) { return (isSxTruthy((isSxTruthy(vector_p(s1)) && vector_p(s2))) ? listToVector(concat(vectorToList(s1), vectorToList(s2))) : (isSxTruthy((isSxTruthy(isString(s1)) && isString(s2))) ? (String(s1) + String(s2)) : concat(seqToList(s1), seqToList(s2)))); }; +PRIMITIVES["sequence-append"] = sequenceAppend; + + // in-range + var inRange = function(a) { var rest = Array.prototype.slice.call(arguments, 1); return (function() { + var end = (isSxTruthy(isEmpty(rest)) ? a : first(rest)); + var step = (isSxTruthy((len(rest) >= 2)) ? nth(rest, 1) : 1); + var realStart = (isSxTruthy(isEmpty(rest)) ? 0 : a); + return (isSxTruthy(sxEq(step, 0)) ? error("in-range: step cannot be zero") : (define(build, function(i, acc) { return (isSxTruthy((isSxTruthy((step > 0)) ? (i >= end) : (i <= end))) ? reverse(acc) : build((i + step), cons(i, acc))); }), build(realStart, []))); +})(); }; +PRIMITIVES["in-range"] = inRange; + // step-ho-map var stepHoMap = function(args, env, kont) { return makeCekState(first(args), env, kontPush(makeHoSetupFrame("map", rest(args), [], env), kont)); }; PRIMITIVES["step-ho-map"] = stepHoMap; diff --git a/spec/evaluator.sx b/spec/evaluator.sx index 4bc83401..35c142ce 100644 --- a/spec/evaluator.sx +++ b/spec/evaluator.sx @@ -25,6 +25,10 @@ (define cek-kont (fn (s) (get s "kont"))) +(define cek-phase (fn (s) (get s "phase"))) + +(define cek-io-request (fn (s) (get s "request"))) + ;; ═══════════════════════════════════════════════════════════════ ;; Part 2: Continuation Frames ;; @@ -32,10 +36,6 @@ ;; when the current sub-expression finishes evaluating. The kont ;; (continuation) is a list of frames, forming a reified call stack. ;; ═══════════════════════════════════════════════════════════════ -(define cek-phase (fn (s) (get s "phase"))) - -(define cek-io-request (fn (s) (get s "request"))) - (define cek-value (fn (s) (get s "value"))) (define make-if-frame (fn (then-expr else-expr env) {:else else-expr :env env :type "if" :then then-expr})) @@ -44,11 +44,11 @@ (define make-begin-frame (fn (remaining env) {:env env :type "begin" :remaining remaining})) -;; Function call frames: accumulate evaluated args, then dispatch (define make-let-frame (fn (name remaining body local) {:body body :env local :type "let" :remaining remaining :name name})) (define make-define-frame (fn (name env has-effects effect-list) {:env env :effect-list effect-list :has-effects has-effects :type "define" :name name})) +;; Function call frames: accumulate evaluated args, then dispatch (define make-define-foreign-frame (fn (name spec env) {:spec spec :env env :type "define-foreign" :name name})) (define make-set-frame (fn (name env) {:env env :type "set" :name name})) @@ -61,11 +61,11 @@ (define make-cond-frame (fn (remaining env scheme?) {:scheme scheme? :env env :type "cond" :remaining remaining})) -;; Higher-order iteration frames (define make-cond-arrow-frame (fn (test-value env) {:env env :match-val test-value :type "cond-arrow"})) (define make-case-frame (fn (match-val remaining env) {:match-val match-val :env env :type "case" :remaining remaining})) +;; Higher-order iteration frames (define make-thread-frame (fn (remaining env mode name) {:env env :type "thread" :extra mode :remaining remaining :name name})) (define @@ -94,44 +94,43 @@ (define make-multi-map-frame (fn (f remaining-lists results env) {:env env :results results :type "multi-map" :f f :remaining remaining-lists})) -;; Scope/provide/context — downward data passing without env threading (define make-filter-frame (fn (f remaining results current-item env) {:current-item current-item :env env :results results :type "filter" :f f :remaining remaining})) (define make-reduce-frame (fn (f remaining env) {:env env :type "reduce" :f f :remaining remaining})) +;; Scope/provide/context — downward data passing without env threading (define make-for-each-frame (fn (f remaining env) {:env env :type "for-each" :f f :remaining remaining})) -;; Delimited continuations (shift/reset) (define make-some-frame (fn (f remaining env) {:env env :type "some" :f f :remaining remaining})) (define make-every-frame (fn (f remaining env) {:env env :type "every" :f f :remaining remaining})) +;; Delimited continuations (shift/reset) (define make-scope-frame (fn (name remaining env) {:env env :type "scope" :remaining remaining :name name})) (define make-provide-frame (fn (name value remaining env) {:subscribers (list) :env env :value value :type "provide" :remaining remaining :name name})) -;; Dynamic wind + reactive signals (define make-bind-frame (fn (body env prev-tracking) {:body body :env env :type "bind" :prev-tracking prev-tracking})) (define make-provide-set-frame (fn (name env) {:env env :type "provide-set" :name name})) -;; Undelimited continuations (call/cc) +;; Dynamic wind + reactive signals (define make-scope-acc-frame (fn (name value remaining env) {:env env :value (or value nil) :type "scope-acc" :remaining remaining :emitted (list) :name name})) (define make-reset-frame (fn (env) {:env env :type "reset"})) -;; HO setup: staged argument evaluation for map/filter/etc. -;; Evaluates args one at a time, then dispatches to the correct -;; HO frame (map, filter, reduce) once all args are ready. +;; Undelimited continuations (call/cc) (define make-dict-frame (fn (remaining results env) {:env env :results results :type "dict" :remaining remaining})) (define make-and-frame (fn (remaining env) {:env env :type "and" :remaining remaining})) +;; HO setup: staged argument evaluation for map/filter/etc. +;; Evaluates args one at a time, then dispatches to the correct +;; HO frame (map, filter, reduce) once all args are ready. (define make-or-frame (fn (remaining env) {:env env :type "or" :remaining remaining})) -;; Condition system frames (handler-bind, restart-case, signal) (define make-dynamic-wind-frame (fn (phase body-thunk after-thunk env) {:env env :phase phase :after-thunk after-thunk :type "dynamic-wind" :body-thunk body-thunk})) @@ -140,31 +139,20 @@ make-reactive-reset-frame (fn (env update-fn first-render?) {:first-render first-render? :update-fn update-fn :env env :type "reactive-reset"})) +;; Condition system frames (handler-bind, restart-case, signal) (define make-callcc-frame (fn (env) {:env env :type "callcc"})) -(define - make-wind-after-frame - (fn (after-thunk winders-len env) - {:type "wind-after" :after-thunk after-thunk :winders-len winders-len :env env})) +(define make-wind-after-frame (fn (after-thunk winders-len env) {:winders-len winders-len :env env :after-thunk after-thunk :type "wind-after"})) -(define - make-wind-return-frame - (fn (body-result env) - {:type "wind-return" :body-result body-result :env env})) +(define make-wind-return-frame (fn (body-result env) {:body-result body-result :env env :type "wind-return"})) -;; R7RS exception frames (raise, guard) (define make-deref-frame (fn (env) {:env env :type "deref"})) (define make-ho-setup-frame (fn (ho-type remaining-args evaled-args env) {:ho-type ho-type :env env :evaled evaled-args :type "ho-setup" :remaining remaining-args})) -;; ═══════════════════════════════════════════════════════════════ -;; Part 3: Continuation Stack Operations -;; -;; Searching and manipulating the kont list — finding handlers, -;; restarts, scope accumulators, and capturing delimited slices. -;; ═══════════════════════════════════════════════════════════════ +;; R7RS exception frames (raise, guard) (define make-comp-trace-frame (fn (name file) {:env file :type "comp-trace" :name name})) (define @@ -181,28 +169,34 @@ (cons {:file (get frame "file") :name (get frame "name")} (kont-collect-comp-trace (rest kont))) (kont-collect-comp-trace (rest kont))))))) +;; ═══════════════════════════════════════════════════════════════ +;; Part 3: Continuation Stack Operations +;; +;; Searching and manipulating the kont list — finding handlers, +;; restarts, scope accumulators, and capturing delimited slices. +;; ═══════════════════════════════════════════════════════════════ (define make-handler-frame (fn (handlers remaining env) {:env env :type "handler" :f handlers :remaining remaining})) (define make-restart-frame (fn (restarts remaining env) {:env env :type "restart" :f restarts :remaining remaining})) -;; Basic kont operations (define make-signal-return-frame (fn (env saved-kont) {:env env :type "signal-return" :f saved-kont})) (define make-raise-eval-frame (fn (env continuable?) {:scheme continuable? :env env :type "raise-eval"})) +;; Basic kont operations (define make-raise-guard-frame (fn (env saved-kont) {:env env :type "raise-guard" :remaining saved-kont})) (define make-perform-frame (fn (env) {:env env :type "perform"})) (define make-vm-resume-frame (fn (resume-fn env) {:env env :type "vm-resume" :f resume-fn})) -;; Capture frames up to a reset boundary — used by shift (define make-import-frame (fn (import-set remaining-sets env) {:args import-set :env env :type "import" :remaining remaining-sets})) (define make-parameterize-frame (fn (remaining current-param results body env) {:env env :body body :results results :type "parameterize" :f current-param :remaining remaining})) +;; Capture frames up to a reset boundary — used by shift (define find-matching-handler (fn @@ -240,7 +234,8 @@ (define kont-unwind-to-handler - (fn (kont condition) + (fn + (kont condition) (if (empty? kont) {:handler nil :kont kont} @@ -261,8 +256,7 @@ (set! *winders* (rest *winders*))) (cek-call (get frame "after-thunk") (list)) (kont-unwind-to-handler rest-k condition)) - :else - (kont-unwind-to-handler rest-k condition)))))) + :else (kont-unwind-to-handler rest-k condition)))))) (define wind-escape-to @@ -290,12 +284,6 @@ entry (find-named-restart (rest restarts) name)))))) -;; ═══════════════════════════════════════════════════════════════ -;; Part 4: Extension Points & Mutable State -;; -;; Custom special forms registry, render hooks, strict mode. -;; Mutable globals use set! — the transpiler emits OCaml refs. -;; ═══════════════════════════════════════════════════════════════ (define kont-find-restart (fn @@ -317,6 +305,12 @@ (define frame-type (fn (f) (get f "type"))) +;; ═══════════════════════════════════════════════════════════════ +;; Part 4: Extension Points & Mutable State +;; +;; Custom special forms registry, render hooks, strict mode. +;; Mutable globals use set! — the transpiler emits OCaml refs. +;; ═══════════════════════════════════════════════════════════════ (define kont-push (fn (frame kont) (cons frame kont))) (define kont-top (fn (kont) (first kont))) @@ -359,7 +353,11 @@ (rest pairs) env (cons - (make-provide-frame (first pair) (nth pair 1) (list) env) + (make-provide-frame + (first pair) + (nth pair 1) + (list) + env) kont)))))) (define @@ -406,14 +404,6 @@ true (has-reactive-reset-frame? (rest kont)))))) -;; ═══════════════════════════════════════════════════════════════ -;; Part 5: Evaluation Utilities -;; -;; Forward-declared eval-expr, lambda/component calling, keyword -;; arg parsing, special form constructors (lambda, defcomp, -;; defmacro, quasiquote), and macro expansion. -;; ═══════════════════════════════════════════════════════════════ -;; Forward declaration — redefined at end of file as CEK entry point (define kont-capture-to-reactive-reset (fn @@ -433,31 +423,39 @@ (scan (rest k) (append captured (list frame)))))))) (scan kont (list)))) -;; Shared param binding for lambda/component calls. -;; Handles &rest collection — used by both call-lambda and continue-with-call. (define *custom-special-forms* (dict)) +;; ═══════════════════════════════════════════════════════════════ +;; Part 5: Evaluation Utilities +;; +;; Forward-declared eval-expr, lambda/component calling, keyword +;; arg parsing, special form constructors (lambda, defcomp, +;; defmacro, quasiquote), and macro expansion. +;; ═══════════════════════════════════════════════════════════════ +;; Forward declaration — redefined at end of file as CEK entry point (define register-special-form! (fn ((name :as string) handler) (dict-set! *custom-special-forms* name handler))) -;; Component calls: parse keyword args, bind params, TCO thunk +;; Shared param binding for lambda/component calls. +;; Handles &rest collection — used by both call-lambda and continue-with-call. (define *render-check* nil) (define *render-fn* nil) -;; Cond/case helpers +;; Component calls: parse keyword args, bind params, TCO thunk (define *bind-tracking* nil) (define *provide-batch-depth* 0) -;; Special form constructors — build state for CEK evaluation +;; Cond/case helpers (define *provide-batch-queue* (list)) (define *provide-subscribers* (dict)) +;; Special form constructors — build state for CEK evaluation (define *winders* (list)) (define *library-registry* (dict)) @@ -488,11 +486,11 @@ (define *io-registry* (dict)) -;; Quasiquote expansion (define io-register! (fn (name spec) (dict-set! *io-registry* name spec))) (define io-registered? (fn (name) (has-key? *io-registry* name))) +;; Quasiquote expansion (define io-lookup (fn (name) (get *io-registry* name))) (define io-names (fn () (keys *io-registry*))) @@ -503,9 +501,13 @@ foreign-register! (fn (name spec) (dict-set! *foreign-registry* name spec))) -;; Macro expansion — expand then re-evaluate the result (define foreign-registered? (fn (name) (has-key? *foreign-registry* name))) +(define foreign-lookup (fn (name) (get *foreign-registry* name))) + +;; Macro expansion — expand then re-evaluate the result +(define foreign-names (fn () (keys *foreign-registry*))) + ;; ═══════════════════════════════════════════════════════════════ ;; Part 6: CEK Machine Core ;; @@ -514,10 +516,6 @@ ;; step-eval: evaluates control expression, pushes frames. ;; step-continue: pops a frame, processes result. ;; ═══════════════════════════════════════════════════════════════ -(define foreign-lookup (fn (name) (get *foreign-registry* name))) - -(define foreign-names (fn () (keys *foreign-registry*))) - (define foreign-parse-params (fn @@ -528,12 +526,6 @@ (items (if (list? param-list) param-list (list)))) (foreign-parse-params-loop items result)))) -;; ═══════════════════════════════════════════════════════════════ -;; Part 7: Special Form Step Functions -;; -;; Each step-sf-* handles one special form in the eval phase. -;; They push frames and return new CEK states — never recurse. -;; ═══════════════════════════════════════════════════════════════ (define foreign-parse-kwargs! (fn @@ -551,7 +543,6 @@ (if (keyword? v) (keyword-name v) v))) (foreign-parse-kwargs! spec (rest (rest remaining)))))) -;; R7RS guard: desugars to call/cc + handler-bind with sentinel re-raise (define foreign-resolve-binding (fn @@ -566,9 +557,12 @@ (obj (join "." (reverse (rest (reverse parts)))))) {:method method :object obj}))))) -;; List evaluation — dispatches on head: special forms, macros, -;; higher-order forms, or function calls. This is the main -;; expression dispatcher for the CEK machine. +;; ═══════════════════════════════════════════════════════════════ +;; Part 7: Special Form Step Functions +;; +;; Each step-sf-* handles one special form in the eval phase. +;; They push frames and return new CEK states — never recurse. +;; ═══════════════════════════════════════════════════════════════ (define foreign-check-args (fn @@ -606,7 +600,7 @@ (type-of val)))))) (range 0 (min (len params) (len args)))))) -;; call/cc: capture entire kont as undelimited escape continuation +;; R7RS guard: desugars to call/cc + handler-bind with sentinel re-raise (define foreign-build-lambda (fn @@ -639,6 +633,9 @@ (list (quote quote) name) (quote __ffi-args__))))))) +;; List evaluation — dispatches on head: special forms, macros, +;; higher-order forms, or function calls. This is the main +;; expression dispatcher for the CEK machine. (define sf-define-foreign (fn @@ -653,6 +650,7 @@ (foreign-register! name spec) spec))) +;; call/cc: capture entire kont as undelimited escape continuation (define step-sf-define-foreign (fn @@ -670,7 +668,6 @@ env (kont-push (make-define-foreign-frame name spec env) kont))))) -;; Pattern matching (match form) (define foreign-dispatch (fn @@ -708,7 +705,6 @@ name ": host-call not available on this platform"))))))))) -;; Condition system special forms (define foreign-parse-params-loop (fn @@ -731,6 +727,7 @@ rest-items (append acc (list {:type "any" :name (if (symbol? item) (symbol-name item) (str item))})))))))) +;; Pattern matching (match form) (define step-sf-io (fn @@ -743,6 +740,7 @@ (str "io: unknown operation '" name "' — not in *io-registry*"))) (make-cek-state (cons (quote perform) (list {:args io-args :op name})) env kont)))) +;; Condition system special forms (define trampoline (fn @@ -786,7 +784,10 @@ (nil? val) (value-matches-type? val - (slice expected-type 0 (- (string-length expected-type) 1)))) + (slice + expected-type + 0 + (- (string-length expected-type) 1)))) true))))) (define @@ -985,7 +986,6 @@ (= (type-of test) "symbol") (or (= (symbol-name test) "else") (= (symbol-name test) ":else")))))) -;; Scope/provide/context — structured downward data passing (define sf-named-let (fn @@ -1018,7 +1018,9 @@ (append! params (if - (= (type-of (nth bindings (* pair-idx 2))) "symbol") + (= + (type-of (nth bindings (* pair-idx 2))) + "symbol") (symbol-name (nth bindings (* pair-idx 2))) (nth bindings (* pair-idx 2)))) (append! inits (nth bindings (inc (* pair-idx 2)))))) @@ -1062,6 +1064,7 @@ params-expr))) (make-lambda param-names body env)))) +;; Scope/provide/context — structured downward data passing (define sf-defcomp (fn @@ -1121,18 +1124,6 @@ (range 2 end 1)) result))) -;; ═══════════════════════════════════════════════════════════════ -;; R7RS syntax-rules / define-syntax -;; -;; syntax-rules creates a macro transformer via pattern matching. -;; define-syntax binds the transformer as a macro (reuses define). -;; Pattern language: _ (wildcard), literals (exact match), -;; pattern variables (bind), ... (ellipsis/repetition). -;; ═══════════════════════════════════════════════════════════════ - -;; Match a syntax-rules pattern against a form. -;; Returns a dict of bindings on success, nil on failure. -;; literals is a list of symbol name strings that must match exactly. (define parse-comp-params (fn @@ -1179,8 +1170,6 @@ params-expr) (list params has-children param-types)))) -;; Match a list pattern against a form list, handling ellipsis at any position. -;; pi = pattern index, fi = form index. (define sf-defisland (fn @@ -1206,8 +1195,18 @@ (env-bind! env (symbol-name name-sym) island) island)))) -;; Find which pattern variable in a template drives an ellipsis. -;; Returns the variable name (string) whose binding is a list, or nil. +;; ═══════════════════════════════════════════════════════════════ +;; R7RS syntax-rules / define-syntax +;; +;; syntax-rules creates a macro transformer via pattern matching. +;; define-syntax binds the transformer as a macro (reuses define). +;; Pattern language: _ (wildcard), literals (exact match), +;; pattern variables (bind), ... (ellipsis/repetition). +;; ═══════════════════════════════════════════════════════════════ + +;; Match a syntax-rules pattern against a form. +;; Returns a dict of bindings on success, nil on failure. +;; literals is a list of symbol name strings that must match exactly. (define defio-parse-kwargs! (fn @@ -1217,11 +1216,14 @@ (not (empty? remaining)) (>= (len remaining) 2) (keyword? (first remaining))) - (dict-set! spec (keyword-name (first remaining)) (nth remaining 1)) + (dict-set! + spec + (keyword-name (first remaining)) + (nth remaining 1)) (defio-parse-kwargs! spec (rest (rest remaining)))))) -;; Find ALL ellipsis-bound pattern variables in a template. -;; Returns a list of variable name strings. +;; Match a list pattern against a form list, handling ellipsis at any position. +;; pi = pattern index, fi = form index. (define sf-defio (fn @@ -1233,8 +1235,8 @@ (io-register! name spec) spec))) -;; Instantiate a template with pattern variable bindings. -;; Handles ellipsis repetition and recursive substitution. +;; Find which pattern variable in a template drives an ellipsis. +;; Returns the variable name (string) whose binding is a list, or nil. (define sf-defmacro (fn @@ -1251,9 +1253,8 @@ (env-bind! env (symbol-name name-sym) mac) mac)))) -;; Walk a template list, handling ellipsis at any position. -;; When element at i is followed by ... at i+1, expand the element -;; for each value of its ellipsis variables (all cycled in parallel). +;; Find ALL ellipsis-bound pattern variables in a template. +;; Returns a list of variable name strings. (define parse-macro-params (fn @@ -1282,10 +1283,8 @@ params-expr) (list params rest-param)))) -;; Try each syntax-rules clause against a form. -;; Returns the instantiated template for the first matching rule, or errors. -;; form is the raw args (without macro name). We prepend a dummy _ symbol -;; because syntax-rules patterns include the keyword as the first element. +;; Instantiate a template with pattern variable bindings. +;; Handles ellipsis repetition and recursive substitution. (define qq-expand (fn @@ -1325,6 +1324,9 @@ (list) template))))))) +;; Walk a template list, handling ellipsis at any position. +;; When element at i is followed by ... at i+1, expand the element +;; for each value of its ellipsis variables (all cycled in parallel). (define sf-letrec (fn @@ -1380,10 +1382,10 @@ (slice body 0 (dec (len body)))) (make-thunk (last body) local)))) -;; Special form: (syntax-rules (literal ...) (pattern template) ...) -;; Creates a Macro with rules/literals stored in closure env. -;; Body is a marker symbol; expand-macro detects it and calls -;; the pattern matcher directly. +;; Try each syntax-rules clause against a form. +;; Returns the instantiated template for the first matching rule, or errors. +;; form is the raw args (without macro name). We prepend a dummy _ symbol +;; because syntax-rules patterns include the keyword as the first element. (define call-with-values (fn @@ -1425,17 +1427,10 @@ body) last-val)))) -;; R7RS records (SRFI-9) -;; -;; (define-record-type -;; (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. +;; Special form: (syntax-rules (literal ...) (pattern template) ...) +;; Creates a Macro with rules/literals stored in closure env. +;; Body is a marker symbol; expand-macro detects it and calls +;; the pattern matcher directly. (define sf-define-values (fn @@ -1451,12 +1446,22 @@ names) nil))))) -;; Delimited continuations (register-special-form! "define-values" sf-define-values) +;; R7RS records (SRFI-9) +;; +;; (define-record-type +;; (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. (register-special-form! "let-values" sf-let-values) -;; Signal dereferencing with reactive dependency tracking +;; Delimited continuations (define step-sf-letrec (fn @@ -1465,13 +1470,6 @@ ((thk (sf-letrec args env))) (make-cek-state (thunk-expr thk) (thunk-env thk) kont)))) -;; ═══════════════════════════════════════════════════════════════ -;; Part 8: Call Dispatch -;; -;; cek-call: invoke a function from native code (runs a nested -;; trampoline). step-eval-call: CEK-native call dispatch for -;; lambda, component, native fn, and continuations. -;; ═══════════════════════════════════════════════════════════════ (define step-sf-dynamic-wind (fn @@ -1492,7 +1490,7 @@ (list) (kont-push (make-wind-after-frame after winders-len env) kont))))))) -;; Reactive signal tracking — captures dependency continuation for re-render +;; Signal dereferencing with reactive dependency tracking (define sf-scope (fn @@ -1520,6 +1518,13 @@ (scope-pop! name) result)))) +;; ═══════════════════════════════════════════════════════════════ +;; Part 8: Call Dispatch +;; +;; cek-call: invoke a function from native code (runs a nested +;; trampoline). step-eval-call: CEK-native call dispatch for +;; lambda, component, native fn, and continuations. +;; ═══════════════════════════════════════════════════════════════ (define sf-provide (fn @@ -1536,13 +1541,7 @@ (scope-pop! name) result))) -;; ═══════════════════════════════════════════════════════════════ -;; Part 9: Higher-Order Form Machinery -;; -;; Data-first HO forms: (map coll fn) and (map fn coll) both work. -;; ho-swap-args auto-detects argument order. HoSetupFrame stages -;; argument evaluation, then dispatches to the appropriate step-ho-*. -;; ═══════════════════════════════════════════════════════════════ +;; Reactive signal tracking — captures dependency continuation for re-render (define expand-macro (fn @@ -1587,6 +1586,13 @@ state (cek-step-loop (cek-step state))))) +;; ═══════════════════════════════════════════════════════════════ +;; Part 9: Higher-Order Form Machinery +;; +;; Data-first HO forms: (map coll fn) and (map fn coll) both work. +;; ho-swap-args auto-detects argument order. HoSetupFrame stages +;; argument evaluation, then dispatches to the appropriate step-ho-*. +;; ═══════════════════════════════════════════════════════════════ (define cek-run (fn @@ -1789,14 +1795,6 @@ env kont)))) -;; ═══════════════════════════════════════════════════════════════ -;; Part 10: Continue Phase — Frame Dispatch -;; -;; When phase="continue", pop the top frame and process the value. -;; Each frame type has its own handling: if frames check truthiness, -;; let frames bind the value, arg frames accumulate it, etc. -;; continue-with-call handles the final function/component dispatch. -;; ═══════════════════════════════════════════════════════════════ (define step-eval-list (fn @@ -1974,9 +1972,6 @@ :else (step-eval-call head args env kont))))) (step-eval-call head args env kont)))))) -;; Final call dispatch from arg frame — all args evaluated, invoke function. -;; Handles: lambda (bind params + TCO), component (keyword args + TCO), -;; native fn (direct call), continuation (resume), callcc continuation (escape). (define sf-define-type (fn @@ -2036,19 +2031,23 @@ ctor-specs) nil)))) +;; ═══════════════════════════════════════════════════════════════ +;; Part 10: Continue Phase — Frame Dispatch +;; +;; When phase="continue", pop the top frame and process the value. +;; Each frame type has its own handling: if frames check truthiness, +;; let frames bind the value, arg frames accumulate it, etc. +;; continue-with-call handles the final function/component dispatch. +;; ═══════════════════════════════════════════════════════════════ (define sf-delay (fn (args env) (let ((thunk (make-lambda (list) (first args) env))) {:forced false :value nil :thunk thunk :_promise true}))) -;; ═══════════════════════════════════════════════════════════════ -;; Part 11: Entry Points -;; -;; eval-expr-cek / trampoline-cek: CEK evaluation entry points. -;; eval-expr / trampoline: top-level bindings that override the -;; forward declarations from Part 5. -;; ═══════════════════════════════════════════════════════════════ +;; Final call dispatch from arg frame — all args evaluated, invoke function. +;; Handles: lambda (bind params + TCO), component (keyword args + TCO), +;; native fn (direct call), continuation (resume), callcc continuation (escape). (define sf-delay-force (fn @@ -2057,6 +2056,13 @@ (define promise? (fn (v) (and (dict? v) (get v :_promise false)))) +;; ═══════════════════════════════════════════════════════════════ +;; Part 11: Entry Points +;; +;; eval-expr-cek / trampoline-cek: CEK evaluation entry points. +;; eval-expr / trampoline: top-level bindings that override the +;; forward declarations from Part 5. +;; ═══════════════════════════════════════════════════════════════ (define make-promise (fn (v) {:forced true :value v :_promise true})) (define @@ -3484,6 +3490,30 @@ ((a (first evaled)) (b (nth evaled 1))) (if (and (not (ho-fn? a)) (ho-fn? b)) (list b a) evaled))))) +(define + seq-to-list + (fn + (x) + (cond + ((= x nil) (list)) + ((list? x) x) + ((vector? x) (vector->list x)) + ((string? x) + (let + ((n (len x))) + (define + loop + (fn + (i acc) + (if + (< i 0) + acc + (loop + (- i 1) + (cons (slice x i (+ i 1)) acc))))) + (loop (- n 1) (list)))) + (else x)))) + (define ho-setup-dispatch (fn @@ -3514,7 +3544,7 @@ (make-multi-map-frame f tails (list) env) kont))))) (let - ((coll (nth ordered 1))) + ((coll (seq-to-list (nth ordered 1)))) (if (empty? coll) (make-cek-value (list) env kont) @@ -3528,7 +3558,7 @@ kont)))))) ("map-indexed" (let - ((coll (nth ordered 1))) + ((coll (seq-to-list (nth ordered 1)))) (if (empty? coll) (make-cek-value (list) env kont) @@ -3542,7 +3572,7 @@ kont))))) ("filter" (let - ((coll (nth ordered 1))) + ((coll (seq-to-list (nth ordered 1)))) (if (empty? coll) (make-cek-value (list) env kont) @@ -3562,7 +3592,7 @@ ("reduce" (let ((init (nth ordered 1)) - (coll (nth ordered 2))) + (coll (seq-to-list (nth ordered 2)))) (if (empty? coll) (make-cek-value init env kont) @@ -3574,7 +3604,7 @@ (kont-push (make-reduce-frame f (rest coll) env) kont))))) ("some" (let - ((coll (nth ordered 1))) + ((coll (seq-to-list (nth ordered 1)))) (if (empty? coll) (make-cek-value false env kont) @@ -3586,7 +3616,7 @@ (kont-push (make-some-frame f (rest coll) env) kont))))) ("every" (let - ((coll (nth ordered 1))) + ((coll (seq-to-list (nth ordered 1)))) (if (empty? coll) (make-cek-value true env kont) @@ -3598,7 +3628,7 @@ (kont-push (make-every-frame f (rest coll) env) kont))))) ("for-each" (let - ((coll (nth ordered 1))) + ((coll (seq-to-list (nth ordered 1)))) (if (empty? coll) (make-cek-value nil env kont) @@ -3610,6 +3640,63 @@ (kont-push (make-for-each-frame f (rest coll) env) kont))))) (_ (error (str "Unknown HO type: " ho-type)))))))) +(define sequence-to-list (fn (s) (seq-to-list s))) + +(define sequence-to-vector (fn (s) (list->vector (seq-to-list s)))) + +(define + sequence-length + (fn + (s) + (cond + ((or (= s nil) (list? s)) (len s)) + ((vector? s) (vector-length s)) + ((string? s) (len s)) + (else (len (seq-to-list s)))))) + +(define + sequence-ref + (fn + (s i) + (cond + ((or (= s nil) (list? s)) (nth s i)) + ((vector? s) (vector-ref s i)) + ((string? s) (slice s i (+ i 1))) + (else (nth (seq-to-list s) i))))) + +(define + sequence-append + (fn + (s1 s2) + (cond + ((and (vector? s1) (vector? s2)) + (list->vector (concat (vector->list s1) (vector->list s2)))) + ((and (string? s1) (string? s2)) (str s1 s2)) + (else (concat (seq-to-list s1) (seq-to-list s2)))))) + +(define + in-range + (fn + (a &rest rest) + (let + ((end (if (empty? rest) a (first rest))) + (step + (if (>= (len rest) 2) (nth rest 1) 1)) + (real-start (if (empty? rest) 0 a))) + (if + (= step 0) + (error "in-range: step cannot be zero") + (do + (define + build + (fn + (i acc) + (if + (if (> step 0) (>= i end) (<= i end)) + (reverse acc) + (build (+ i step) (cons i acc))))) + (build real-start (list))))))) + (define step-ho-map (fn