Reactive tracking (deref/computed/effect dep discovery) and island lifecycle now use the general scoped effects system instead of parallel infrastructure. Two scope names: "sx-reactive" for tracking context, "sx-island-scope" for island disposable collection. Eliminates ~98 net lines: _TrackingContext class, 7 tracking context platform functions (Python + JS), *island-scope* global, and corresponding RENAME_MAP entries. All 20 signal tests pass (17 original + 3 new scope integration tests), plus CEK/continuation/type tests clean. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1213 lines
48 KiB
Plaintext
1213 lines
48 KiB
Plaintext
;; ==========================================================================
|
|
;; py.sx — SX-to-Python translator, written in SX
|
|
;;
|
|
;; Translates (define ...) forms from .sx spec files into Python source.
|
|
;; This is the self-hosting bootstrapper: the Python evaluator executes
|
|
;; this file against the spec to produce sx_ref.py — identical output to
|
|
;; the hand-written bootstrap_py.py.
|
|
;;
|
|
;; Usage (from SX):
|
|
;; (py-expr expr) — translate one expression to Python
|
|
;; (py-statement expr indent) — translate to Python statement
|
|
;; (py-translate-file defines) — translate a list of (name . define-expr) pairs
|
|
;;
|
|
;; Usage (as reader macro):
|
|
;; #py(define foo (fn (x) (+ x 1)))
|
|
;; → "foo = lambda x: (x + 1)"
|
|
;; ==========================================================================
|
|
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; Python reserved words — names that get _ suffix
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(define py-reserved
|
|
(list "False" "None" "True" "and" "as" "assert" "async" "await"
|
|
"break" "class" "continue" "def" "del" "elif" "else" "except"
|
|
"finally" "for" "from" "global" "if" "import" "in" "is"
|
|
"lambda" "nonlocal" "not" "or" "pass" "raise" "return" "try"
|
|
"while" "with" "yield"
|
|
"default" "type" "id" "input" "open" "print" "set" "super"))
|
|
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; RENAMES table — explicit SX name → Python name mappings
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(define py-renames {
|
|
:nil "NIL"
|
|
:true "True"
|
|
:false "False"
|
|
"nil?" "is_nil"
|
|
"type-of" "type_of"
|
|
"symbol-name" "symbol_name"
|
|
"keyword-name" "keyword_name"
|
|
"make-lambda" "make_lambda"
|
|
"make-component" "make_component"
|
|
"make-macro" "make_macro"
|
|
"make-thunk" "make_thunk"
|
|
"make-handler-def" "make_handler_def"
|
|
"make-query-def" "make_query_def"
|
|
"make-action-def" "make_action_def"
|
|
"make-page-def" "make_page_def"
|
|
"make-symbol" "make_symbol"
|
|
"make-keyword" "make_keyword"
|
|
"lambda-params" "lambda_params"
|
|
"lambda-body" "lambda_body"
|
|
"lambda-closure" "lambda_closure"
|
|
"lambda-name" "lambda_name"
|
|
"set-lambda-name!" "set_lambda_name"
|
|
"component-params" "component_params"
|
|
"component-body" "component_body"
|
|
"component-closure" "component_closure"
|
|
"component-has-children?" "component_has_children"
|
|
"component-name" "component_name"
|
|
"component-affinity" "component_affinity"
|
|
"macro-params" "macro_params"
|
|
"macro-rest-param" "macro_rest_param"
|
|
"macro-body" "macro_body"
|
|
"macro-closure" "macro_closure"
|
|
"thunk?" "is_thunk"
|
|
"thunk-expr" "thunk_expr"
|
|
"thunk-env" "thunk_env"
|
|
"callable?" "is_callable"
|
|
"lambda?" "is_lambda"
|
|
"component?" "is_component"
|
|
"island?" "is_island"
|
|
"make-island" "make_island"
|
|
"make-signal" "make_signal"
|
|
"signal?" "is_signal"
|
|
"signal-value" "signal_value"
|
|
"signal-set-value!" "signal_set_value"
|
|
"signal-subscribers" "signal_subscribers"
|
|
"signal-add-sub!" "signal_add_sub"
|
|
"signal-remove-sub!" "signal_remove_sub"
|
|
"signal-deps" "signal_deps"
|
|
"signal-set-deps!" "signal_set_deps"
|
|
"identical?" "is_identical"
|
|
"notify-subscribers" "notify_subscribers"
|
|
"flush-subscribers" "flush_subscribers"
|
|
"dispose-computed" "dispose_computed"
|
|
"with-island-scope" "with_island_scope"
|
|
"register-in-scope" "register_in_scope"
|
|
"*batch-depth*" "_batch_depth"
|
|
"*batch-queue*" "_batch_queue"
|
|
"*store-registry*" "_store_registry"
|
|
"def-store" "def_store"
|
|
"use-store" "use_store"
|
|
"clear-stores" "clear_stores"
|
|
"emit-event" "emit_event"
|
|
"on-event" "on_event"
|
|
"bridge-event" "bridge_event"
|
|
"dom-listen" "dom_listen"
|
|
"dom-dispatch" "dom_dispatch"
|
|
"event-detail" "event_detail"
|
|
"macro?" "is_macro"
|
|
"primitive?" "is_primitive"
|
|
"get-primitive" "get_primitive"
|
|
"env-has?" "env_has"
|
|
"env-get" "env_get"
|
|
"env-set!" "env_set"
|
|
"env-extend" "env_extend"
|
|
"env-merge" "env_merge"
|
|
"dict-set!" "dict_set"
|
|
"dict-get" "dict_get"
|
|
"dict-has?" "dict_has"
|
|
"dict-delete!" "dict_delete"
|
|
"eval-expr" "eval_expr"
|
|
"eval-list" "eval_list"
|
|
"eval-call" "eval_call"
|
|
"is-render-expr?" "is_render_expr"
|
|
"render-expr" "render_expr"
|
|
"call-lambda" "call_lambda"
|
|
"call-component" "call_component"
|
|
"parse-keyword-args" "parse_keyword_args"
|
|
"parse-comp-params" "parse_comp_params"
|
|
"parse-macro-params" "parse_macro_params"
|
|
"expand-macro" "expand_macro"
|
|
"render-to-html" "render_to_html"
|
|
"render-to-sx" "render_to_sx"
|
|
"render-value-to-html" "render_value_to_html"
|
|
"render-list-to-html" "render_list_to_html"
|
|
"render-html-element" "render_html_element"
|
|
"render-html-component" "render_html_component"
|
|
"parse-element-args" "parse_element_args"
|
|
"render-attrs" "render_attrs"
|
|
"aser-list" "aser_list"
|
|
"aser-fragment" "aser_fragment"
|
|
"aser-call" "aser_call"
|
|
"aser-special" "aser_special"
|
|
"sf-if" "sf_if"
|
|
"sf-when" "sf_when"
|
|
"sf-cond" "sf_cond"
|
|
"sf-cond-scheme" "sf_cond_scheme"
|
|
"sf-cond-clojure" "sf_cond_clojure"
|
|
"sf-case" "sf_case"
|
|
"sf-case-loop" "sf_case_loop"
|
|
"sf-and" "sf_and"
|
|
"sf-or" "sf_or"
|
|
"sf-let" "sf_let"
|
|
"sf-lambda" "sf_lambda"
|
|
"sf-define" "sf_define"
|
|
"sf-defcomp" "sf_defcomp"
|
|
"defcomp-kwarg" "defcomp_kwarg"
|
|
"sf-defmacro" "sf_defmacro"
|
|
"sf-begin" "sf_begin"
|
|
"sf-quote" "sf_quote"
|
|
"sf-quasiquote" "sf_quasiquote"
|
|
"sf-thread-first" "sf_thread_first"
|
|
"sf-set!" "sf_set_bang"
|
|
"sf-reset" "sf_reset"
|
|
"sf-shift" "sf_shift"
|
|
"qq-expand" "qq_expand"
|
|
"ho-map" "ho_map"
|
|
"ho-map-indexed" "ho_map_indexed"
|
|
"ho-filter" "ho_filter"
|
|
"ho-reduce" "ho_reduce"
|
|
"ho-some" "ho_some"
|
|
"ho-every" "ho_every"
|
|
"ho-for-each" "ho_for_each"
|
|
"sf-defstyle" "sf_defstyle"
|
|
"special-form?" "is_special_form"
|
|
"ho-form?" "is_ho_form"
|
|
"strip-prefix" "strip_prefix"
|
|
"escape-html" "escape_html"
|
|
"escape-attr" "escape_attr"
|
|
"escape-string" "escape_string"
|
|
"raw-html-content" "raw_html_content"
|
|
"HTML_TAGS" "HTML_TAGS"
|
|
"VOID_ELEMENTS" "VOID_ELEMENTS"
|
|
"BOOLEAN_ATTRS" "BOOLEAN_ATTRS"
|
|
"definition-form?" "is_definition_form"
|
|
"RENDER_HTML_FORMS" "RENDER_HTML_FORMS"
|
|
"render-html-form?" "is_render_html_form"
|
|
"dispatch-html-form" "dispatch_html_form"
|
|
"render-lambda-html" "render_lambda_html"
|
|
"make-raw-html" "make_raw_html"
|
|
"render-html-island" "render_html_island"
|
|
"serialize-island-state" "serialize_island_state"
|
|
"json-serialize" "json_serialize"
|
|
"empty-dict?" "is_empty_dict"
|
|
"sf-defisland" "sf_defisland"
|
|
"render-to-sx" "render_to_sx"
|
|
"aser" "aser"
|
|
"eval-case-aser" "eval_case_aser"
|
|
"sx-serialize" "sx_serialize"
|
|
"sx-serialize-dict" "sx_serialize_dict"
|
|
"sx-expr-source" "sx_expr_source"
|
|
"contains?" "contains_p"
|
|
"starts-with?" "starts_with_p"
|
|
"ends-with?" "ends_with_p"
|
|
"empty?" "empty_p"
|
|
"every?" "every_p"
|
|
"for-each" "for_each"
|
|
"for-each-indexed" "for_each_indexed"
|
|
"map-indexed" "map_indexed"
|
|
"map-dict" "map_dict"
|
|
"eval-cond" "eval_cond"
|
|
"eval-cond-scheme" "eval_cond_scheme"
|
|
"eval-cond-clojure" "eval_cond_clojure"
|
|
"process-bindings" "process_bindings"
|
|
"scan-refs" "scan_refs"
|
|
"scan-refs-walk" "scan_refs_walk"
|
|
"transitive-deps" "transitive_deps"
|
|
"compute-all-deps" "compute_all_deps"
|
|
"scan-components-from-source" "scan_components_from_source"
|
|
"components-needed" "components_needed"
|
|
"page-component-bundle" "page_component_bundle"
|
|
"page-css-classes" "page_css_classes"
|
|
"component-deps" "component_deps"
|
|
"component-set-deps!" "component_set_deps"
|
|
"component-css-classes" "component_css_classes"
|
|
"component-io-refs" "component_io_refs"
|
|
"component-set-io-refs!" "component_set_io_refs"
|
|
"env-components" "env_components"
|
|
"regex-find-all" "regex_find_all"
|
|
"scan-css-classes" "scan_css_classes"
|
|
"scan-io-refs" "scan_io_refs"
|
|
"scan-io-refs-walk" "scan_io_refs_walk"
|
|
"transitive-io-refs" "transitive_io_refs"
|
|
"compute-all-io-refs" "compute_all_io_refs"
|
|
"component-io-refs-cached" "component_io_refs_cached"
|
|
"component-pure?" "component_pure_p"
|
|
"render-target" "render_target"
|
|
"page-render-plan" "page_render_plan"
|
|
"split-path-segments" "split_path_segments"
|
|
"make-route-segment" "make_route_segment"
|
|
"parse-route-pattern" "parse_route_pattern"
|
|
"match-route-segments" "match_route_segments"
|
|
"match-route" "match_route"
|
|
"find-matching-route" "find_matching_route"
|
|
"make-spread" "make_spread"
|
|
"spread?" "is_spread"
|
|
"spread-attrs" "spread_attrs"
|
|
"merge-spread-attrs" "merge_spread_attrs"
|
|
"collect!" "sx_collect"
|
|
"collected" "sx_collected"
|
|
"clear-collected!" "sx_clear_collected"
|
|
"scope-push!" "scope_push"
|
|
"scope-pop!" "scope_pop"
|
|
"provide-push!" "provide_push"
|
|
"provide-pop!" "provide_pop"
|
|
"context" "sx_context"
|
|
"emit!" "sx_emit"
|
|
"emitted" "sx_emitted"
|
|
})
|
|
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; Name mangling: SX identifier → valid Python identifier
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(define py-mangle
|
|
(fn ((name :as string))
|
|
(let ((renamed (get py-renames name)))
|
|
(if (not (nil? renamed))
|
|
renamed
|
|
;; General mangling rules
|
|
(let ((result name))
|
|
;; Handle trailing ? and !
|
|
(let ((result (cond
|
|
(ends-with? result "?")
|
|
(str (slice result 0 (- (string-length result) 1)) "_p")
|
|
(ends-with? result "!")
|
|
(str (slice result 0 (- (string-length result) 1)) "_b")
|
|
:else result)))
|
|
;; Kebab to snake_case
|
|
(let ((result (replace result "-" "_")))
|
|
;; Escape Python reserved words
|
|
(if (some (fn (r) (= r result)) py-reserved)
|
|
(str result "_")
|
|
result))))))))
|
|
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; String quoting for Python
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(define py-quote-string
|
|
(fn ((s :as string))
|
|
;; Produce a Python repr-style string literal
|
|
(str "'" (replace (replace (replace (replace s "\\" "\\\\") "'" "\\'") "\n" "\\n") "\t" "\\t") "'")))
|
|
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; Infix operators
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(define py-infix-ops
|
|
(list "+" "-" "*" "/" "=" "!=" "<" ">" "<=" ">=" "mod"))
|
|
|
|
(define py-infix?
|
|
(fn ((op :as string))
|
|
(some (fn (x) (= x op)) py-infix-ops)))
|
|
|
|
(define py-op-symbol
|
|
(fn ((op :as string))
|
|
(case op
|
|
"=" "=="
|
|
"!=" "!="
|
|
"mod" "%"
|
|
:else op)))
|
|
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; Cell variable detection: find set! targets crossing lambda boundaries
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(define py-find-nested-set-vars
|
|
(fn ((body :as list))
|
|
;; Returns a list of mangled variable names that are set! from within
|
|
;; nested fn/lambda bodies
|
|
(let ((result (list)))
|
|
(begin
|
|
(for-each (fn (b) (py-scan-set-vars b false result)) body)
|
|
result))))
|
|
|
|
(define py-scan-set-vars
|
|
(fn (node (in-nested :as boolean) (result :as list))
|
|
(when (and (list? node) (not (empty? node)))
|
|
(let ((head (first node)))
|
|
(cond
|
|
;; fn/lambda — mark as nested and scan body
|
|
(and (= (type-of head) "symbol")
|
|
(or (= (symbol-name head) "fn")
|
|
(= (symbol-name head) "lambda")))
|
|
(for-each (fn (child) (py-scan-set-vars child true result))
|
|
(rest (rest node)))
|
|
;; set! inside nested fn — record the variable
|
|
(and (= (type-of head) "symbol")
|
|
(= (symbol-name head) "set!")
|
|
in-nested
|
|
(>= (len node) 2))
|
|
(let ((var-name (if (= (type-of (nth node 1)) "symbol")
|
|
(symbol-name (nth node 1))
|
|
(str (nth node 1)))))
|
|
(let ((mangled (py-mangle var-name)))
|
|
(when (not (some (fn (x) (= x mangled)) result))
|
|
(append! result mangled))))
|
|
;; Otherwise scan children
|
|
:else
|
|
(for-each (fn (child)
|
|
(when (list? child)
|
|
(py-scan-set-vars child in-nested result)))
|
|
node))))))
|
|
|
|
;; Check if a fn expression's body uses set! anywhere
|
|
(define py-body-uses-set?
|
|
(fn (fn-expr)
|
|
(let ((body (rest (rest fn-expr))))
|
|
(py-has-set? body))))
|
|
|
|
(define py-has-set?
|
|
(fn ((nodes :as list))
|
|
(some (fn (node)
|
|
(and (list? node)
|
|
(not (empty? node))
|
|
(or (and (= (type-of (first node)) "symbol")
|
|
(= (symbol-name (first node)) "set!"))
|
|
(py-has-set? (filter (fn (x) (list? x)) node)))))
|
|
nodes)))
|
|
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; Expression translator: SX AST → Python expression string
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(define py-expr
|
|
(fn (expr)
|
|
(py-expr-with-cells expr (list))))
|
|
|
|
(define py-expr-with-cells
|
|
(fn (expr (cell-vars :as list))
|
|
(cond
|
|
;; Bool MUST come before number check (Python: bool is subclass of int)
|
|
(= (type-of expr) "boolean")
|
|
(if expr "True" "False")
|
|
|
|
;; Nil
|
|
(nil? expr) "NIL"
|
|
|
|
;; Numbers
|
|
(number? expr) (str expr)
|
|
|
|
;; Strings
|
|
(string? expr) (py-quote-string expr)
|
|
|
|
;; Symbols
|
|
(= (type-of expr) "symbol")
|
|
(let ((mangled (py-mangle (symbol-name expr))))
|
|
(if (some (fn (c) (= c mangled)) cell-vars)
|
|
(str "_cells[" (py-quote-string mangled) "]")
|
|
mangled))
|
|
|
|
;; Keywords → string
|
|
(= (type-of expr) "keyword")
|
|
(py-quote-string (keyword-name expr))
|
|
|
|
;; Dicts (native {:key val} syntax)
|
|
(= (type-of expr) "dict")
|
|
(py-emit-native-dict expr cell-vars)
|
|
|
|
;; Lists (function calls / special forms)
|
|
(list? expr)
|
|
(if (empty? expr)
|
|
"[]"
|
|
(py-emit-list expr cell-vars))
|
|
|
|
;; Fallback
|
|
:else (str expr))))
|
|
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; Dict emission
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(define py-emit-native-dict
|
|
(fn ((d :as dict) (cell-vars :as list))
|
|
(let ((items (keys d)))
|
|
(str "{" (join ", " (map (fn (k)
|
|
(str (py-quote-string k) ": " (py-expr-with-cells (get d k) cell-vars)))
|
|
items)) "}"))))
|
|
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; List/call emission — the main dispatch
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(define py-emit-list
|
|
(fn (expr (cell-vars :as list))
|
|
(let ((head (first expr))
|
|
(args (rest expr)))
|
|
(if (not (= (type-of head) "symbol"))
|
|
;; Data list — not a function call
|
|
(str "[" (join ", " (map (fn (x) (py-expr-with-cells x cell-vars)) expr)) "]")
|
|
(let ((op (symbol-name head)))
|
|
(cond
|
|
;; fn/lambda
|
|
(or (= op "fn") (= op "lambda"))
|
|
(py-emit-fn expr cell-vars)
|
|
|
|
;; let/let*
|
|
(or (= op "let") (= op "let*"))
|
|
(py-emit-let expr cell-vars)
|
|
|
|
;; if
|
|
(= op "if")
|
|
(let ((cond-e (py-expr-with-cells (nth args 0) cell-vars))
|
|
(then-e (py-expr-with-cells (nth args 1) cell-vars))
|
|
(else-e (if (>= (len args) 3)
|
|
(py-expr-with-cells (nth args 2) cell-vars)
|
|
"NIL")))
|
|
(str "(" then-e " if sx_truthy(" cond-e ") else " else-e ")"))
|
|
|
|
;; when
|
|
(= op "when")
|
|
(py-emit-when expr cell-vars)
|
|
|
|
;; cond
|
|
(= op "cond")
|
|
(py-emit-cond args cell-vars)
|
|
|
|
;; case
|
|
(= op "case")
|
|
(py-emit-case args cell-vars)
|
|
|
|
;; and
|
|
(= op "and")
|
|
(py-emit-and args cell-vars)
|
|
|
|
;; or
|
|
(= op "or")
|
|
(py-emit-or args cell-vars)
|
|
|
|
;; not
|
|
(= op "not")
|
|
(str "(not sx_truthy(" (py-expr-with-cells (first args) cell-vars) "))")
|
|
|
|
;; do/begin
|
|
(or (= op "do") (= op "begin"))
|
|
(py-emit-do args cell-vars)
|
|
|
|
;; list literal
|
|
(= op "list")
|
|
(str "[" (join ", " (map (fn (x) (py-expr-with-cells x cell-vars)) args)) "]")
|
|
|
|
;; dict literal
|
|
(= op "dict")
|
|
(py-emit-dict-literal args cell-vars)
|
|
|
|
;; quote
|
|
(= op "quote")
|
|
(py-emit-quote (first args))
|
|
|
|
;; set!
|
|
(= op "set!")
|
|
(let ((var-name (if (= (type-of (first args)) "symbol")
|
|
(symbol-name (first args))
|
|
(str (first args)))))
|
|
(let ((mangled (py-mangle var-name)))
|
|
(str "_sx_cell_set(_cells, " (py-quote-string mangled)
|
|
", " (py-expr-with-cells (nth args 1) cell-vars) ")")))
|
|
|
|
;; str
|
|
(= op "str")
|
|
(str "sx_str(" (join ", " (map (fn (x) (py-expr-with-cells x cell-vars)) args)) ")")
|
|
|
|
;; Mutation forms in expression context
|
|
(= op "append!")
|
|
(str "_sx_append(" (py-expr-with-cells (nth args 0) cell-vars)
|
|
", " (py-expr-with-cells (nth args 1) cell-vars) ")")
|
|
|
|
(= op "dict-set!")
|
|
(str "_sx_dict_set(" (py-expr-with-cells (nth args 0) cell-vars)
|
|
", " (py-expr-with-cells (nth args 1) cell-vars)
|
|
", " (py-expr-with-cells (nth args 2) cell-vars) ")")
|
|
|
|
(= op "env-set!")
|
|
(str "_sx_dict_set(" (py-expr-with-cells (nth args 0) cell-vars)
|
|
", " (py-expr-with-cells (nth args 1) cell-vars)
|
|
", " (py-expr-with-cells (nth args 2) cell-vars) ")")
|
|
|
|
(= op "set-lambda-name!")
|
|
(str "_sx_set_attr(" (py-expr-with-cells (nth args 0) cell-vars)
|
|
", 'name', " (py-expr-with-cells (nth args 1) cell-vars) ")")
|
|
|
|
;; Infix operators
|
|
(py-infix? op)
|
|
(py-emit-infix op args cell-vars)
|
|
|
|
;; inc/dec
|
|
(= op "inc")
|
|
(str "(" (py-expr-with-cells (first args) cell-vars) " + 1)")
|
|
(= op "dec")
|
|
(str "(" (py-expr-with-cells (first args) cell-vars) " - 1)")
|
|
|
|
;; Regular function call
|
|
:else
|
|
(str (py-mangle op) "("
|
|
(join ", " (map (fn (x) (py-expr-with-cells x cell-vars)) args))
|
|
")")))))))
|
|
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; fn/lambda → Python lambda
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(define py-emit-fn
|
|
(fn (expr (cell-vars :as list))
|
|
(let ((params (nth expr 1))
|
|
(body (rest (rest expr)))
|
|
(param-strs (py-collect-params params)))
|
|
(let ((params-str (join ", " param-strs)))
|
|
(if (= (len body) 1)
|
|
(str "lambda " params-str ": " (py-expr-with-cells (first body) cell-vars))
|
|
;; Multi-expression body
|
|
(str "_sx_fn(lambda " params-str ": (\n"
|
|
(join ",\n" (map (fn (b) (str " " (py-expr-with-cells b cell-vars))) (slice body 0 (- (len body) 1))))
|
|
",\n " (py-expr-with-cells (last body) cell-vars)
|
|
"\n)[-1])"))))))
|
|
|
|
(define py-collect-params
|
|
(fn ((params :as list))
|
|
(py-collect-params-loop params 0 (list))))
|
|
|
|
(define py-collect-params-loop
|
|
(fn ((params :as list) (i :as number) (result :as list))
|
|
(if (>= i (len params))
|
|
result
|
|
(let ((p (nth params i)))
|
|
(cond
|
|
;; &rest marker
|
|
(and (= (type-of p) "symbol") (= (symbol-name p) "&rest"))
|
|
(if (< (+ i 1) (len params))
|
|
(let ((rp (nth params (+ i 1))))
|
|
(py-collect-params-loop params (+ i 2)
|
|
(append result (str "*" (py-mangle
|
|
(if (and (= (type-of rp) "list") (= (len rp) 3)
|
|
(= (type-of (nth rp 1)) "keyword")
|
|
(= (keyword-name (nth rp 1)) "as"))
|
|
(symbol-name (first rp))
|
|
(if (= (type-of rp) "symbol") (symbol-name rp) (str rp))))))))
|
|
(py-collect-params-loop params (+ i 1) result))
|
|
;; Normal param
|
|
(= (type-of p) "symbol")
|
|
(py-collect-params-loop params (+ i 1)
|
|
(append result (py-mangle (symbol-name p))))
|
|
;; Annotated param: (name :as type) → extract name
|
|
(and (= (type-of p) "list") (= (len p) 3)
|
|
(= (type-of (nth p 1)) "keyword")
|
|
(= (keyword-name (nth p 1)) "as"))
|
|
(py-collect-params-loop params (+ i 1)
|
|
(append result (py-mangle (symbol-name (first p)))))
|
|
;; Something else
|
|
:else
|
|
(py-collect-params-loop params (+ i 1)
|
|
(append result (str p))))))))
|
|
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; let → nested IIFE lambdas
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(define py-emit-let
|
|
(fn (expr (cell-vars :as list))
|
|
(let ((bindings (nth expr 1))
|
|
(body (rest (rest expr))))
|
|
(let ((assignments (py-parse-bindings bindings cell-vars)))
|
|
(let ((body-str (if (= (len body) 1)
|
|
(py-expr-with-cells (first body) cell-vars)
|
|
(str "_sx_begin(" (join ", " (map (fn (b) (py-expr-with-cells b cell-vars)) body)) ")"))))
|
|
;; Build from inside out
|
|
(py-wrap-let-bindings assignments body-str cell-vars))))))
|
|
|
|
(define py-parse-bindings
|
|
(fn (bindings (cell-vars :as list))
|
|
(if (and (list? bindings) (not (empty? bindings)))
|
|
(if (list? (first bindings))
|
|
;; Scheme-style: ((name val) ...)
|
|
(map (fn (b)
|
|
(let ((vname (if (= (type-of (first b)) "symbol")
|
|
(symbol-name (first b))
|
|
(str (first b)))))
|
|
(list (py-mangle vname) (py-expr-with-cells (nth b 1) cell-vars))))
|
|
bindings)
|
|
;; Clojure-style: (name val name val ...)
|
|
(py-parse-clojure-bindings bindings 0 (list) cell-vars))
|
|
(list))))
|
|
|
|
(define py-parse-clojure-bindings
|
|
(fn (bindings (i :as number) (result :as list) (cell-vars :as list))
|
|
(if (>= i (- (len bindings) 1))
|
|
result
|
|
(let ((vname (if (= (type-of (nth bindings i)) "symbol")
|
|
(symbol-name (nth bindings i))
|
|
(str (nth bindings i)))))
|
|
(py-parse-clojure-bindings bindings (+ i 2)
|
|
(append result (list (py-mangle vname) (py-expr-with-cells (nth bindings (+ i 1)) cell-vars)))
|
|
cell-vars)))))
|
|
|
|
(define py-wrap-let-bindings
|
|
(fn ((assignments :as list) (body-str :as string) (cell-vars :as list))
|
|
(if (empty? assignments)
|
|
body-str
|
|
(let ((binding (last assignments))
|
|
(rest-bindings (slice assignments 0 (- (len assignments) 1)))
|
|
(name (first binding))
|
|
(val (nth binding 1)))
|
|
(let ((inner (if (some (fn (c) (= c name)) cell-vars)
|
|
;; Cell var: initialize in _cells dict
|
|
(str "_sx_begin(_sx_cell_set(_cells, " (py-quote-string name) ", " val "), " body-str ")")
|
|
;; Normal: lambda wrapper
|
|
(str "(lambda " name ": " body-str ")(" val ")"))))
|
|
(py-wrap-let-bindings rest-bindings inner cell-vars))))))
|
|
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; when → ternary
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(define py-emit-when
|
|
(fn (expr (cell-vars :as list))
|
|
(let ((cond-e (py-expr-with-cells (nth expr 1) cell-vars))
|
|
(body-parts (rest (rest expr))))
|
|
(if (= (len body-parts) 1)
|
|
(str "(" (py-expr-with-cells (first body-parts) cell-vars) " if sx_truthy(" cond-e ") else NIL)")
|
|
(str "(_sx_begin(" (join ", " (map (fn (b) (py-expr-with-cells b cell-vars)) body-parts))
|
|
") if sx_truthy(" cond-e ") else NIL)")))))
|
|
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; cond → chained ternaries
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(define py-emit-cond
|
|
(fn ((clauses :as list) (cell-vars :as list))
|
|
(if (empty? clauses)
|
|
"NIL"
|
|
;; Detect scheme vs clojure style
|
|
(let ((is-scheme (and
|
|
(every? (fn (c) (and (list? c) (= (len c) 2))) clauses)
|
|
(not (some (fn (c) (= (type-of c) "keyword")) clauses)))))
|
|
(if is-scheme
|
|
(py-cond-scheme clauses cell-vars)
|
|
(py-cond-clojure clauses cell-vars))))))
|
|
|
|
(define py-is-else?
|
|
(fn (test)
|
|
(or (and (= (type-of test) "symbol")
|
|
(or (= (symbol-name test) "else") (= (symbol-name test) ":else")))
|
|
(and (= (type-of test) "keyword") (= (keyword-name test) "else")))))
|
|
|
|
(define py-cond-scheme
|
|
(fn ((clauses :as list) (cell-vars :as list))
|
|
(if (empty? clauses)
|
|
"NIL"
|
|
(let ((clause (first clauses))
|
|
(test (first clause))
|
|
(body (nth clause 1)))
|
|
(if (py-is-else? test)
|
|
(py-expr-with-cells body cell-vars)
|
|
(str "(" (py-expr-with-cells body cell-vars)
|
|
" if sx_truthy(" (py-expr-with-cells test cell-vars)
|
|
") else " (py-cond-scheme (rest clauses) cell-vars) ")"))))))
|
|
|
|
(define py-cond-clojure
|
|
(fn ((clauses :as list) (cell-vars :as list))
|
|
(if (< (len clauses) 2)
|
|
"NIL"
|
|
(let ((test (first clauses))
|
|
(body (nth clauses 1)))
|
|
(if (py-is-else? test)
|
|
(py-expr-with-cells body cell-vars)
|
|
(str "(" (py-expr-with-cells body cell-vars)
|
|
" if sx_truthy(" (py-expr-with-cells test cell-vars)
|
|
") else " (py-cond-clojure (rest (rest clauses)) cell-vars) ")"))))))
|
|
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; case → _sx_case
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(define py-emit-case
|
|
(fn ((args :as list) (cell-vars :as list))
|
|
(let ((match-expr (py-expr-with-cells (first args) cell-vars))
|
|
(clauses (rest args)))
|
|
(str "_sx_case(" match-expr ", [" (py-case-pairs clauses cell-vars) "])"))))
|
|
|
|
(define py-case-pairs
|
|
(fn ((clauses :as list) (cell-vars :as list))
|
|
(py-case-pairs-loop clauses 0 (list) cell-vars)))
|
|
|
|
(define py-case-pairs-loop
|
|
(fn ((clauses :as list) (i :as number) (result :as list) (cell-vars :as list))
|
|
(if (>= i (- (len clauses) 1))
|
|
(join ", " result)
|
|
(let ((test (nth clauses i))
|
|
(body (nth clauses (+ i 1))))
|
|
(let ((pair (if (py-is-else? test)
|
|
(str "(None, lambda: " (py-expr-with-cells body cell-vars) ")")
|
|
(str "(" (py-expr-with-cells test cell-vars)
|
|
", lambda: " (py-expr-with-cells body cell-vars) ")"))))
|
|
(py-case-pairs-loop clauses (+ i 2) (append result pair) cell-vars))))))
|
|
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; and/or → short-circuit ternaries
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(define py-emit-and
|
|
(fn ((args :as list) (cell-vars :as list))
|
|
(let ((parts (map (fn (x) (py-expr-with-cells x cell-vars)) args)))
|
|
(if (= (len parts) 1)
|
|
(first parts)
|
|
(py-and-chain parts)))))
|
|
|
|
(define py-and-chain
|
|
(fn ((parts :as list))
|
|
(if (= (len parts) 1)
|
|
(first parts)
|
|
(let ((p (first parts)))
|
|
(str "(" p " if not sx_truthy(" p ") else " (py-and-chain (rest parts)) ")")))))
|
|
|
|
(define py-emit-or
|
|
(fn ((args :as list) (cell-vars :as list))
|
|
(if (= (len args) 1)
|
|
(py-expr-with-cells (first args) cell-vars)
|
|
(let ((parts (map (fn (x) (py-expr-with-cells x cell-vars)) args)))
|
|
(py-or-chain parts)))))
|
|
|
|
(define py-or-chain
|
|
(fn ((parts :as list))
|
|
(if (= (len parts) 1)
|
|
(first parts)
|
|
(let ((p (first parts)))
|
|
(str "(" p " if sx_truthy(" p ") else " (py-or-chain (rest parts)) ")")))))
|
|
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; do/begin → _sx_begin
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(define py-emit-do
|
|
(fn ((args :as list) (cell-vars :as list))
|
|
(if (= (len args) 1)
|
|
(py-expr-with-cells (first args) cell-vars)
|
|
(str "_sx_begin(" (join ", " (map (fn (e) (py-expr-with-cells e cell-vars)) args)) ")"))))
|
|
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; dict literal → Python dict
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(define py-emit-dict-literal
|
|
(fn ((pairs :as list) (cell-vars :as list))
|
|
(str "{" (py-dict-pairs-str pairs 0 (list) cell-vars) "}")))
|
|
|
|
(define py-dict-pairs-str
|
|
(fn ((pairs :as list) (i :as number) (result :as list) (cell-vars :as list))
|
|
(if (>= i (- (len pairs) 1))
|
|
(join ", " result)
|
|
(let ((key (nth pairs i))
|
|
(val (nth pairs (+ i 1))))
|
|
(let ((key-str (if (= (type-of key) "keyword")
|
|
(py-quote-string (keyword-name key))
|
|
(py-expr-with-cells key cell-vars)))
|
|
(val-str (py-expr-with-cells val cell-vars)))
|
|
(py-dict-pairs-str pairs (+ i 2)
|
|
(append result (str key-str ": " val-str))
|
|
cell-vars))))))
|
|
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; Infix operators
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(define py-emit-infix
|
|
(fn ((op :as string) (args :as list) (cell-vars :as list))
|
|
(let ((py-op (py-op-symbol op))
|
|
(n (len args)))
|
|
(cond
|
|
(and (= n 1) (= op "-"))
|
|
(str "(-" (py-expr-with-cells (first args) cell-vars) ")")
|
|
(= n 2)
|
|
(str "(" (py-expr-with-cells (first args) cell-vars)
|
|
" " py-op " " (py-expr-with-cells (nth args 1) cell-vars) ")")
|
|
;; Variadic: left-fold (a op b op c op d ...)
|
|
:else
|
|
(let ((result (py-expr-with-cells (first args) cell-vars)))
|
|
(for-each (fn (arg)
|
|
(set! result (str "(" result " " py-op " " (py-expr-with-cells arg cell-vars) ")")))
|
|
(rest args))
|
|
result)))))
|
|
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; quote → Python AST literals
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(define py-emit-quote
|
|
(fn (expr)
|
|
(cond
|
|
(= (type-of expr) "boolean")
|
|
(if expr "True" "False")
|
|
(number? expr) (str expr)
|
|
(string? expr) (py-quote-string expr)
|
|
(nil? expr) "NIL"
|
|
(= (type-of expr) "symbol")
|
|
(str "Symbol(" (py-quote-string (symbol-name expr)) ")")
|
|
(= (type-of expr) "keyword")
|
|
(str "Keyword(" (py-quote-string (keyword-name expr)) ")")
|
|
(list? expr)
|
|
(str "[" (join ", " (map py-emit-quote expr)) "]")
|
|
:else (str expr))))
|
|
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; Statement translator: SX AST → Python statement string
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(define py-pad
|
|
(fn ((indent :as number))
|
|
(join "" (map (fn (i) " ") (range 0 indent)))))
|
|
|
|
(define py-statement
|
|
(fn (expr (indent :as number))
|
|
(py-statement-with-cells expr indent (list))))
|
|
|
|
(define py-statement-with-cells
|
|
(fn (expr (indent :as number) (cell-vars :as list))
|
|
(let ((pad (py-pad indent)))
|
|
(if (and (list? expr) (not (empty? expr))
|
|
(= (type-of (first expr)) "symbol"))
|
|
(let ((name (symbol-name (first expr))))
|
|
(cond
|
|
(= name "define")
|
|
(py-emit-define expr indent cell-vars)
|
|
(= name "set!")
|
|
(str pad (py-mangle (symbol-name (nth expr 1))) " = "
|
|
(py-expr-with-cells (nth expr 2) cell-vars))
|
|
(= name "when")
|
|
(py-emit-when-stmt expr indent cell-vars)
|
|
(or (= name "do") (= name "begin"))
|
|
(join "\n" (map (fn (e) (py-statement-with-cells e indent cell-vars))
|
|
(rest expr)))
|
|
(= name "for-each")
|
|
(py-emit-for-each-stmt expr indent cell-vars)
|
|
(= name "dict-set!")
|
|
(str pad (py-expr-with-cells (nth expr 1) cell-vars)
|
|
"[" (py-expr-with-cells (nth expr 2) cell-vars)
|
|
"] = " (py-expr-with-cells (nth expr 3) cell-vars))
|
|
(= name "append!")
|
|
(str pad (py-expr-with-cells (nth expr 1) cell-vars)
|
|
".append(" (py-expr-with-cells (nth expr 2) cell-vars) ")")
|
|
(= name "env-set!")
|
|
(str pad (py-expr-with-cells (nth expr 1) cell-vars)
|
|
"[" (py-expr-with-cells (nth expr 2) cell-vars)
|
|
"] = " (py-expr-with-cells (nth expr 3) cell-vars))
|
|
(= name "set-lambda-name!")
|
|
(str pad (py-expr-with-cells (nth expr 1) cell-vars)
|
|
".name = " (py-expr-with-cells (nth expr 2) cell-vars))
|
|
:else
|
|
(str pad (py-expr-with-cells expr cell-vars))))
|
|
(str pad (py-expr-with-cells expr cell-vars))))))
|
|
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; define → assignment or def statement
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(define py-emit-define
|
|
(fn (expr (indent :as number) (cell-vars :as list))
|
|
(let ((pad (py-pad indent))
|
|
(name (if (= (type-of (nth expr 1)) "symbol")
|
|
(symbol-name (nth expr 1))
|
|
(str (nth expr 1))))
|
|
(val-expr (nth expr 2)))
|
|
;; If value is fn/lambda with set! in body, emit as def
|
|
(if (and (list? val-expr)
|
|
(not (empty? val-expr))
|
|
(= (type-of (first val-expr)) "symbol")
|
|
(or (= (symbol-name (first val-expr)) "fn")
|
|
(= (symbol-name (first val-expr)) "lambda"))
|
|
(py-body-uses-set? val-expr))
|
|
(py-emit-define-as-def name val-expr indent)
|
|
(str pad (py-mangle name) " = " (py-expr-with-cells val-expr cell-vars))))))
|
|
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; define-as-def: function with set! → proper def + _cells
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(define py-emit-define-as-def
|
|
(fn ((name :as string) fn-expr (indent :as number))
|
|
(let ((pad (py-pad indent))
|
|
(params (nth fn-expr 1))
|
|
(body (rest (rest fn-expr)))
|
|
(param-strs (py-collect-params params))
|
|
(params-str (join ", " param-strs))
|
|
(py-name (py-mangle name))
|
|
(nested-set-vars (py-find-nested-set-vars body)))
|
|
(let ((lines (list (str pad "def " py-name "(" params-str "):"))))
|
|
(begin
|
|
(when (not (empty? nested-set-vars))
|
|
(append! lines (str pad " _cells = {}")))
|
|
(py-emit-body-stmts body lines (+ indent 1) nested-set-vars)
|
|
(join "\n" lines))))))
|
|
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; Body statement emission (for def bodies)
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(define py-emit-body-stmts
|
|
(fn ((body :as list) (lines :as list) (indent :as number) (cell-vars :as list))
|
|
(let ((pad (py-pad indent))
|
|
(total (len body)))
|
|
(py-emit-body-stmts-loop body lines indent cell-vars 0 total pad))))
|
|
|
|
(define py-emit-body-stmts-loop
|
|
(fn ((body :as list) (lines :as list) (indent :as number) (cell-vars :as list) (i :as number) (total :as number) (pad :as string))
|
|
(when (< i total)
|
|
(let ((expr (nth body i))
|
|
(is-last (= i (- total 1))))
|
|
(begin
|
|
(if (and (list? expr) (not (empty? expr)) (= (type-of (first expr)) "symbol"))
|
|
(let ((name (symbol-name (first expr))))
|
|
(cond
|
|
(or (= name "let") (= name "let*"))
|
|
(py-emit-let-as-stmts expr lines indent is-last cell-vars)
|
|
(or (= name "do") (= name "begin"))
|
|
(if is-last
|
|
(py-emit-body-stmts (rest expr) lines indent cell-vars)
|
|
(for-each (fn (sub) (py-emit-stmt-recursive sub lines indent cell-vars))
|
|
(rest expr)))
|
|
:else
|
|
(if is-last
|
|
(append! lines (str pad "return " (py-expr-with-cells expr cell-vars)))
|
|
(append! lines (py-statement-with-cells expr indent cell-vars)))))
|
|
(if is-last
|
|
(append! lines (str pad "return " (py-expr-with-cells expr cell-vars)))
|
|
(append! lines (py-statement-with-cells expr indent cell-vars))))
|
|
(py-emit-body-stmts-loop body lines indent cell-vars (+ i 1) total pad))))))
|
|
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; let as statements (inside def bodies)
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(define py-emit-let-as-stmts
|
|
(fn (expr (lines :as list) (indent :as number) (is-last :as boolean) (cell-vars :as list))
|
|
(let ((pad (py-pad indent))
|
|
(bindings (nth expr 1))
|
|
(body (rest (rest expr))))
|
|
(begin
|
|
;; Emit bindings as assignments
|
|
(py-emit-binding-assignments bindings lines indent cell-vars)
|
|
;; Emit body
|
|
(if is-last
|
|
(py-emit-body-stmts body lines indent cell-vars)
|
|
(for-each (fn (b) (py-emit-stmt-recursive b lines indent cell-vars)) body))))))
|
|
|
|
(define py-emit-binding-assignments
|
|
(fn (bindings (lines :as list) (indent :as number) (cell-vars :as list))
|
|
(let ((pad (py-pad indent)))
|
|
(when (and (list? bindings) (not (empty? bindings)))
|
|
(if (list? (first bindings))
|
|
;; Scheme-style
|
|
(for-each (fn (b)
|
|
(let ((vname (if (= (type-of (first b)) "symbol")
|
|
(symbol-name (first b))
|
|
(str (first b))))
|
|
(mangled (py-mangle (if (= (type-of (first b)) "symbol")
|
|
(symbol-name (first b))
|
|
(str (first b))))))
|
|
(if (some (fn (c) (= c mangled)) cell-vars)
|
|
(append! lines (str pad "_cells[" (py-quote-string mangled) "] = "
|
|
(py-expr-with-cells (nth b 1) cell-vars)))
|
|
(append! lines (str pad mangled " = " (py-expr-with-cells (nth b 1) cell-vars))))))
|
|
bindings)
|
|
;; Clojure-style
|
|
(py-emit-clojure-binding-assignments bindings lines indent 0 cell-vars))))))
|
|
|
|
(define py-emit-clojure-binding-assignments
|
|
(fn (bindings (lines :as list) (indent :as number) (i :as number) (cell-vars :as list))
|
|
(let ((pad (py-pad indent)))
|
|
(when (< i (- (len bindings) 1))
|
|
(let ((vname (if (= (type-of (nth bindings i)) "symbol")
|
|
(symbol-name (nth bindings i))
|
|
(str (nth bindings i))))
|
|
(mangled (py-mangle (if (= (type-of (nth bindings i)) "symbol")
|
|
(symbol-name (nth bindings i))
|
|
(str (nth bindings i))))))
|
|
(begin
|
|
(if (some (fn (c) (= c mangled)) cell-vars)
|
|
(append! lines (str pad "_cells[" (py-quote-string mangled) "] = "
|
|
(py-expr-with-cells (nth bindings (+ i 1)) cell-vars)))
|
|
(append! lines (str pad mangled " = " (py-expr-with-cells (nth bindings (+ i 1)) cell-vars))))
|
|
(py-emit-clojure-binding-assignments bindings lines indent (+ i 2) cell-vars)))))))
|
|
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; Recursive statement emission (for loop/control flow bodies)
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(define py-emit-stmt-recursive
|
|
(fn (expr (lines :as list) (indent :as number) (cell-vars :as list))
|
|
(let ((pad (py-pad indent)))
|
|
(if (not (and (list? expr) (not (empty? expr))))
|
|
(append! lines (py-statement-with-cells expr indent cell-vars))
|
|
(if (not (= (type-of (first expr)) "symbol"))
|
|
(append! lines (py-statement-with-cells expr indent cell-vars))
|
|
(let ((name (symbol-name (first expr))))
|
|
(cond
|
|
(= name "set!")
|
|
(let ((var-name (if (= (type-of (nth expr 1)) "symbol")
|
|
(symbol-name (nth expr 1))
|
|
(str (nth expr 1))))
|
|
(mangled (py-mangle (if (= (type-of (nth expr 1)) "symbol")
|
|
(symbol-name (nth expr 1))
|
|
(str (nth expr 1))))))
|
|
(if (some (fn (c) (= c mangled)) cell-vars)
|
|
(append! lines (str pad "_cells[" (py-quote-string mangled) "] = "
|
|
(py-expr-with-cells (nth expr 2) cell-vars)))
|
|
(append! lines (str pad mangled " = " (py-expr-with-cells (nth expr 2) cell-vars)))))
|
|
(or (= name "let") (= name "let*"))
|
|
(py-emit-let-as-stmts expr lines indent false cell-vars)
|
|
(= name "when")
|
|
(begin
|
|
(append! lines (str pad "if sx_truthy(" (py-expr-with-cells (nth expr 1) cell-vars) "):"))
|
|
(for-each (fn (b) (py-emit-stmt-recursive b lines (+ indent 1) cell-vars))
|
|
(rest (rest expr))))
|
|
(= name "cond")
|
|
(py-emit-cond-stmt expr lines indent cell-vars)
|
|
(or (= name "do") (= name "begin"))
|
|
(for-each (fn (b) (py-emit-stmt-recursive b lines indent cell-vars))
|
|
(rest expr))
|
|
(= name "if")
|
|
(begin
|
|
(append! lines (str pad "if sx_truthy(" (py-expr-with-cells (nth expr 1) cell-vars) "):"))
|
|
(py-emit-stmt-recursive (nth expr 2) lines (+ indent 1) cell-vars)
|
|
(when (>= (len expr) 4)
|
|
(append! lines (str pad "else:"))
|
|
(py-emit-stmt-recursive (nth expr 3) lines (+ indent 1) cell-vars)))
|
|
(= name "append!")
|
|
(append! lines (str pad (py-expr-with-cells (nth expr 1) cell-vars)
|
|
".append(" (py-expr-with-cells (nth expr 2) cell-vars) ")"))
|
|
(= name "dict-set!")
|
|
(append! lines (str pad (py-expr-with-cells (nth expr 1) cell-vars)
|
|
"[" (py-expr-with-cells (nth expr 2) cell-vars)
|
|
"] = " (py-expr-with-cells (nth expr 3) cell-vars)))
|
|
(= name "env-set!")
|
|
(append! lines (str pad (py-expr-with-cells (nth expr 1) cell-vars)
|
|
"[" (py-expr-with-cells (nth expr 2) cell-vars)
|
|
"] = " (py-expr-with-cells (nth expr 3) cell-vars)))
|
|
:else
|
|
(append! lines (py-statement-with-cells expr indent cell-vars)))))))))
|
|
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; cond as statement (if/elif/else chain)
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(define py-emit-cond-stmt
|
|
(fn (expr (lines :as list) (indent :as number) (cell-vars :as list))
|
|
(let ((pad (py-pad indent))
|
|
(clauses (rest expr)))
|
|
;; Detect scheme vs clojure
|
|
(let ((is-scheme (and
|
|
(every? (fn (c) (and (list? c) (= (len c) 2))) clauses)
|
|
(not (some (fn (c) (= (type-of c) "keyword")) clauses)))))
|
|
(if is-scheme
|
|
(py-cond-stmt-scheme clauses lines indent true cell-vars)
|
|
(py-cond-stmt-clojure clauses lines indent 0 true cell-vars))))))
|
|
|
|
(define py-cond-stmt-scheme
|
|
(fn ((clauses :as list) (lines :as list) (indent :as number) (first-clause :as boolean) (cell-vars :as list))
|
|
(let ((pad (py-pad indent)))
|
|
(when (not (empty? clauses))
|
|
(let ((clause (first clauses))
|
|
(test (first clause))
|
|
(body (nth clause 1)))
|
|
(begin
|
|
(if (py-is-else? test)
|
|
(append! lines (str pad "else:"))
|
|
(begin
|
|
(append! lines (str pad (if first-clause "if" "elif")
|
|
" sx_truthy(" (py-expr-with-cells test cell-vars) "):"))
|
|
nil))
|
|
(py-emit-stmt-recursive body lines (+ indent 1) cell-vars)
|
|
(py-cond-stmt-scheme (rest clauses) lines indent false cell-vars)))))))
|
|
|
|
(define py-cond-stmt-clojure
|
|
(fn ((clauses :as list) (lines :as list) (indent :as number) (i :as number) (first-clause :as boolean) (cell-vars :as list))
|
|
(let ((pad (py-pad indent)))
|
|
(when (< i (- (len clauses) 1))
|
|
(let ((test (nth clauses i))
|
|
(body (nth clauses (+ i 1))))
|
|
(begin
|
|
(if (py-is-else? test)
|
|
(append! lines (str pad "else:"))
|
|
(begin
|
|
(append! lines (str pad (if first-clause "if" "elif")
|
|
" sx_truthy(" (py-expr-with-cells test cell-vars) "):"))
|
|
nil))
|
|
(py-emit-stmt-recursive body lines (+ indent 1) cell-vars)
|
|
(py-cond-stmt-clojure clauses lines indent (+ i 2) false cell-vars)))))))
|
|
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; when as statement
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(define py-emit-when-stmt
|
|
(fn (expr (indent :as number) (cell-vars :as list))
|
|
(let ((pad (py-pad indent))
|
|
(cond-e (py-expr-with-cells (nth expr 1) cell-vars))
|
|
(body-parts (rest (rest expr))))
|
|
(str pad "if sx_truthy(" cond-e "):\n"
|
|
(join "\n" (map (fn (b) (py-statement-with-cells b (+ indent 1) cell-vars))
|
|
body-parts))))))
|
|
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; for-each as statement
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(define py-emit-for-each-stmt
|
|
(fn (expr (indent :as number) (cell-vars :as list))
|
|
(let ((pad (py-pad indent))
|
|
(fn-expr (nth expr 1))
|
|
(coll-expr (nth expr 2))
|
|
(coll (py-expr-with-cells coll-expr cell-vars)))
|
|
(if (and (list? fn-expr)
|
|
(= (type-of (first fn-expr)) "symbol")
|
|
(= (symbol-name (first fn-expr)) "fn"))
|
|
;; Inline lambda → for loop
|
|
(let ((params (nth fn-expr 1))
|
|
(body (rest (rest fn-expr)))
|
|
(p (if (= (type-of (first params)) "symbol")
|
|
(symbol-name (first params))
|
|
(str (first params))))
|
|
(p-py (py-mangle p)))
|
|
(let ((lines (list (str pad "for " p-py " in " coll ":"))))
|
|
(begin
|
|
(for-each (fn (b) (py-emit-stmt-recursive b lines (+ indent 1) cell-vars)) body)
|
|
(join "\n" lines))))
|
|
;; Non-inline → for _item
|
|
(str pad "for _item in " coll ":\n"
|
|
pad " " (py-expr-with-cells fn-expr cell-vars) "(_item)")))))
|
|
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; File translation: process a list of (name, define-expr) pairs
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(define py-translate-file
|
|
(fn ((defines :as list))
|
|
(join "\n" (map (fn (pair)
|
|
(let ((name (first pair))
|
|
(expr (nth pair 1)))
|
|
(str "# " name "\n" (py-statement expr 0) "\n")))
|
|
defines))))
|