Files
rose-ash/shared/sx/ref/primitives.sx
giles f77d7350dd Refactor SX primitives: modular, isomorphic, general-purpose
Spec modularization:
- Add (define-module :name) markers to primitives.sx creating 11 modules
  (7 core, 4 stdlib). Bootstrappers can now selectively include modules.
- Add parse_primitives_by_module() to boundary_parser.py.
- Remove split-ids primitive; inline at 4 call sites in blog/market queries.

Python file split:
- primitives.py: slimmed to registry + core primitives only (~350 lines)
- primitives_stdlib.py: NEW — stdlib primitives (format, text, style, debug)
- primitives_ctx.py: NEW — extracted 12 page context builders from IO
- primitives_io.py: add register_io_handler decorator, auto-derive
  IO_PRIMITIVES from registry, move sync IO bridges here

JS parity fixes:
- = uses === (strict equality), != uses !==
- round supports optional ndigits parameter
- concat uses nil-check not falsy-check (preserves 0, "", false)
- escape adds single quote entity (') matching Python/markupsafe
- assert added (was missing from JS entirely)

Bootstrapper modularization:
- PRIMITIVES_JS_MODULES / PRIMITIVES_PY_MODULES dicts keyed by module
- --modules CLI flag for selective inclusion (core.* always included)
- Regenerated sx-ref.js and sx_ref.py with all fixes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 01:45:29 +00:00

481 lines
12 KiB
Plaintext

;; ==========================================================================
;; primitives.sx — Specification of all SX built-in pure functions
;;
;; Each entry declares: name, parameter signature, and semantics.
;; Bootstrap compilers implement these natively per target.
;;
;; This file is a SPECIFICATION, not executable code. The define-primitive
;; form is a declarative macro that bootstrap compilers consume to generate
;; native primitive registrations.
;;
;; Format:
;; (define-primitive "name"
;; :params (param1 param2 &rest rest)
;; :returns "type"
;; :doc "description"
;; :body (reference-implementation ...))
;;
;; The :body is optional — when provided, it gives a reference
;; implementation in SX that bootstrap compilers MAY use for testing
;; or as a fallback. Most targets will implement natively for performance.
;;
;; Modules: (define-module :name) scopes subsequent define-primitive
;; entries until the next define-module. Bootstrappers use this to
;; selectively include primitive groups.
;; ==========================================================================
;; --------------------------------------------------------------------------
;; Core — Arithmetic
;; --------------------------------------------------------------------------
(define-module :core.arithmetic)
(define-primitive "+"
:params (&rest args)
:returns "number"
:doc "Sum all arguments."
:body (reduce (fn (a b) (native-add a b)) 0 args))
(define-primitive "-"
:params (a &rest b)
:returns "number"
:doc "Subtract. Unary: negate. Binary: a - b."
:body (if (empty? b) (native-neg a) (native-sub a (first b))))
(define-primitive "*"
:params (&rest args)
:returns "number"
:doc "Multiply all arguments."
:body (reduce (fn (a b) (native-mul a b)) 1 args))
(define-primitive "/"
:params (a b)
:returns "number"
:doc "Divide a by b."
:body (native-div a b))
(define-primitive "mod"
:params (a b)
:returns "number"
:doc "Modulo a % b."
:body (native-mod a b))
(define-primitive "sqrt"
:params (x)
:returns "number"
:doc "Square root.")
(define-primitive "pow"
:params (x n)
:returns "number"
:doc "x raised to power n.")
(define-primitive "abs"
:params (x)
:returns "number"
:doc "Absolute value.")
(define-primitive "floor"
:params (x)
:returns "number"
:doc "Floor to integer.")
(define-primitive "ceil"
:params (x)
:returns "number"
:doc "Ceiling to integer.")
(define-primitive "round"
:params (x &rest ndigits)
:returns "number"
:doc "Round to ndigits decimal places (default 0).")
(define-primitive "min"
:params (&rest args)
:returns "number"
:doc "Minimum. Single list arg or variadic.")
(define-primitive "max"
:params (&rest args)
:returns "number"
:doc "Maximum. Single list arg or variadic.")
(define-primitive "clamp"
:params (x lo hi)
:returns "number"
:doc "Clamp x to range [lo, hi]."
:body (max lo (min hi x)))
(define-primitive "inc"
:params (n)
:returns "number"
:doc "Increment by 1."
:body (+ n 1))
(define-primitive "dec"
:params (n)
:returns "number"
:doc "Decrement by 1."
:body (- n 1))
;; --------------------------------------------------------------------------
;; Core — Comparison
;; --------------------------------------------------------------------------
(define-module :core.comparison)
(define-primitive "="
:params (a b)
:returns "boolean"
:doc "Equality (value equality, not identity).")
(define-primitive "!="
:params (a b)
:returns "boolean"
:doc "Inequality."
:body (not (= a b)))
(define-primitive "<"
:params (a b)
:returns "boolean"
:doc "Less than.")
(define-primitive ">"
:params (a b)
:returns "boolean"
:doc "Greater than.")
(define-primitive "<="
:params (a b)
:returns "boolean"
:doc "Less than or equal.")
(define-primitive ">="
:params (a b)
:returns "boolean"
:doc "Greater than or equal.")
;; --------------------------------------------------------------------------
;; Core — Predicates
;; --------------------------------------------------------------------------
(define-module :core.predicates)
(define-primitive "odd?"
:params (n)
:returns "boolean"
:doc "True if n is odd."
:body (= (mod n 2) 1))
(define-primitive "even?"
:params (n)
:returns "boolean"
:doc "True if n is even."
:body (= (mod n 2) 0))
(define-primitive "zero?"
:params (n)
:returns "boolean"
:doc "True if n is zero."
:body (= n 0))
(define-primitive "nil?"
:params (x)
:returns "boolean"
:doc "True if x is nil/null/None.")
(define-primitive "number?"
:params (x)
:returns "boolean"
:doc "True if x is a number (int or float).")
(define-primitive "string?"
:params (x)
:returns "boolean"
:doc "True if x is a string.")
(define-primitive "list?"
:params (x)
:returns "boolean"
:doc "True if x is a list/array.")
(define-primitive "dict?"
:params (x)
:returns "boolean"
:doc "True if x is a dict/map.")
(define-primitive "empty?"
:params (coll)
:returns "boolean"
:doc "True if coll is nil or has length 0.")
(define-primitive "contains?"
:params (coll key)
:returns "boolean"
:doc "True if coll contains key. Strings: substring check. Dicts: key check. Lists: membership.")
;; --------------------------------------------------------------------------
;; Core — Logic
;; --------------------------------------------------------------------------
(define-module :core.logic)
(define-primitive "not"
:params (x)
:returns "boolean"
:doc "Logical negation. Note: and/or are special forms (short-circuit).")
;; --------------------------------------------------------------------------
;; Core — Strings
;; --------------------------------------------------------------------------
(define-module :core.strings)
(define-primitive "str"
:params (&rest args)
:returns "string"
:doc "Concatenate all args as strings. nil → empty string, bool → true/false.")
(define-primitive "concat"
:params (&rest colls)
:returns "list"
:doc "Concatenate multiple lists into one. Skips nil values.")
(define-primitive "upper"
:params (s)
:returns "string"
:doc "Uppercase string.")
(define-primitive "lower"
:params (s)
:returns "string"
:doc "Lowercase string.")
(define-primitive "trim"
:params (s)
:returns "string"
:doc "Strip leading/trailing whitespace.")
(define-primitive "split"
:params (s &rest sep)
:returns "list"
:doc "Split string by separator (default space).")
(define-primitive "join"
:params (sep coll)
:returns "string"
:doc "Join collection items with separator string.")
(define-primitive "replace"
:params (s old new)
:returns "string"
:doc "Replace all occurrences of old with new in s.")
(define-primitive "slice"
:params (coll start &rest end)
:returns "any"
:doc "Slice a string or list from start to end (exclusive). End is optional.")
(define-primitive "starts-with?"
:params (s prefix)
:returns "boolean"
:doc "True if string s starts with prefix.")
(define-primitive "ends-with?"
:params (s suffix)
:returns "boolean"
:doc "True if string s ends with suffix.")
;; --------------------------------------------------------------------------
;; Core — Collections
;; --------------------------------------------------------------------------
(define-module :core.collections)
(define-primitive "list"
:params (&rest args)
:returns "list"
:doc "Create a list from arguments.")
(define-primitive "dict"
:params (&rest pairs)
:returns "dict"
:doc "Create a dict from key/value pairs: (dict :a 1 :b 2).")
(define-primitive "range"
:params (start end &rest step)
:returns "list"
:doc "Integer range [start, end) with optional step.")
(define-primitive "get"
:params (coll key &rest default)
:returns "any"
:doc "Get value from dict by key, or list by index. Optional default.")
(define-primitive "len"
:params (coll)
:returns "number"
:doc "Length of string, list, or dict.")
(define-primitive "first"
:params (coll)
:returns "any"
:doc "First element, or nil if empty.")
(define-primitive "last"
:params (coll)
:returns "any"
:doc "Last element, or nil if empty.")
(define-primitive "rest"
:params (coll)
:returns "list"
:doc "All elements except the first.")
(define-primitive "nth"
:params (coll n)
:returns "any"
:doc "Element at index n, or nil if out of bounds.")
(define-primitive "cons"
:params (x coll)
:returns "list"
:doc "Prepend x to coll.")
(define-primitive "append"
:params (coll x)
:returns "list"
:doc "Append x to end of coll (returns new list).")
(define-primitive "chunk-every"
:params (coll n)
:returns "list"
:doc "Split coll into sub-lists of size n.")
(define-primitive "zip-pairs"
:params (coll)
:returns "list"
:doc "Consecutive pairs: (1 2 3 4) → ((1 2) (2 3) (3 4)).")
;; --------------------------------------------------------------------------
;; Core — Dict operations
;; --------------------------------------------------------------------------
(define-module :core.dict)
(define-primitive "keys"
:params (d)
:returns "list"
:doc "List of dict keys.")
(define-primitive "vals"
:params (d)
:returns "list"
:doc "List of dict values.")
(define-primitive "merge"
:params (&rest dicts)
:returns "dict"
:doc "Merge dicts left to right. Later keys win. Skips nil.")
(define-primitive "assoc"
:params (d &rest pairs)
:returns "dict"
:doc "Return new dict with key/value pairs added/overwritten.")
(define-primitive "dissoc"
:params (d &rest keys)
:returns "dict"
:doc "Return new dict with keys removed.")
(define-primitive "into"
:params (target coll)
:returns "any"
:doc "Pour coll into target. List target: convert to list. Dict target: convert pairs to dict.")
;; --------------------------------------------------------------------------
;; Stdlib — Format
;; --------------------------------------------------------------------------
(define-module :stdlib.format)
(define-primitive "format-date"
:params (date-str fmt)
:returns "string"
:doc "Parse ISO date string and format with strftime-style format.")
(define-primitive "format-decimal"
:params (val &rest places)
:returns "string"
:doc "Format number with fixed decimal places (default 2).")
(define-primitive "parse-int"
:params (val &rest default)
:returns "number"
:doc "Parse string to integer with optional default on failure.")
(define-primitive "parse-datetime"
:params (s)
:returns "string"
:doc "Parse datetime string — identity passthrough (returns string or nil).")
;; --------------------------------------------------------------------------
;; Stdlib — Text
;; --------------------------------------------------------------------------
(define-module :stdlib.text)
(define-primitive "pluralize"
:params (count &rest forms)
:returns "string"
:doc "Pluralize: (pluralize 1) → \"\", (pluralize 2) → \"s\". Or (pluralize n \"item\" \"items\").")
(define-primitive "escape"
:params (s)
:returns "string"
:doc "HTML-escape a string (&, <, >, \", ').")
(define-primitive "strip-tags"
:params (s)
:returns "string"
:doc "Remove HTML tags from string.")
;; --------------------------------------------------------------------------
;; Stdlib — Style
;; --------------------------------------------------------------------------
(define-module :stdlib.style)
(define-primitive "css"
:params (&rest atoms)
:returns "style-value"
:doc "Resolve style atoms to a StyleValue with className and CSS declarations.
Atoms are keywords or strings: (css :flex :gap-4 :hover:bg-sky-200).")
(define-primitive "merge-styles"
:params (&rest styles)
:returns "style-value"
:doc "Merge multiple StyleValues into one combined StyleValue.")
;; --------------------------------------------------------------------------
;; Stdlib — Debug
;; --------------------------------------------------------------------------
(define-module :stdlib.debug)
(define-primitive "assert"
:params (condition &rest message)
:returns "boolean"
:doc "Assert condition is truthy; raise error with message if not.")