1. Extra closing paren in ex-tabs handler 2. tab-content dict values contained (div ...) HTML tags which crash during register_components since HTML primitives aren't in env Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
603 lines
17 KiB
Plaintext
603 lines
17 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 ...))
|
|
;;
|
|
;; Typed params use (name :as type) syntax:
|
|
;; (define-primitive "+"
|
|
;; :params (&rest (args :as number))
|
|
;; :returns "number"
|
|
;; :doc "Sum all arguments.")
|
|
;;
|
|
;; Untyped params default to `any`. Typed params enable the gradual
|
|
;; type checker (types.sx) to catch mistyped primitive calls.
|
|
;;
|
|
;; 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 :as number))
|
|
:returns "number"
|
|
:doc "Sum all arguments."
|
|
:body (reduce (fn (a b) (native-add a b)) 0 args))
|
|
|
|
(define-primitive "-"
|
|
:params ((a :as number) &rest (b :as number))
|
|
: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 :as number))
|
|
:returns "number"
|
|
:doc "Multiply all arguments."
|
|
:body (reduce (fn (a b) (native-mul a b)) 1 args))
|
|
|
|
(define-primitive "/"
|
|
:params ((a :as number) (b :as number))
|
|
:returns "number"
|
|
:doc "Divide a by b."
|
|
:body (native-div a b))
|
|
|
|
(define-primitive "mod"
|
|
:params ((a :as number) (b :as number))
|
|
:returns "number"
|
|
:doc "Modulo a % b."
|
|
:body (native-mod a b))
|
|
|
|
(define-primitive "random-int"
|
|
:params ((low :as number) (high :as number))
|
|
:returns "number"
|
|
:doc "Random integer in [low, high] inclusive."
|
|
:body (native-random-int low high))
|
|
|
|
(define-primitive "json-encode"
|
|
:params (value)
|
|
:returns "string"
|
|
:doc "Encode value as JSON string with indentation.")
|
|
|
|
(define-primitive "sqrt"
|
|
:params ((x :as number))
|
|
:returns "number"
|
|
:doc "Square root.")
|
|
|
|
(define-primitive "pow"
|
|
:params ((x :as number) (n :as number))
|
|
:returns "number"
|
|
:doc "x raised to power n.")
|
|
|
|
(define-primitive "abs"
|
|
:params ((x :as number))
|
|
:returns "number"
|
|
:doc "Absolute value.")
|
|
|
|
(define-primitive "floor"
|
|
:params ((x :as number))
|
|
:returns "number"
|
|
:doc "Floor to integer.")
|
|
|
|
(define-primitive "ceil"
|
|
:params ((x :as number))
|
|
:returns "number"
|
|
:doc "Ceiling to integer.")
|
|
|
|
(define-primitive "round"
|
|
:params ((x :as number) &rest (ndigits :as number))
|
|
:returns "number"
|
|
:doc "Round to ndigits decimal places (default 0).")
|
|
|
|
(define-primitive "min"
|
|
:params (&rest (args :as number))
|
|
:returns "number"
|
|
:doc "Minimum. Single list arg or variadic.")
|
|
|
|
(define-primitive "max"
|
|
:params (&rest (args :as number))
|
|
:returns "number"
|
|
:doc "Maximum. Single list arg or variadic.")
|
|
|
|
(define-primitive "clamp"
|
|
:params ((x :as number) (lo :as number) (hi :as number))
|
|
:returns "number"
|
|
:doc "Clamp x to range [lo, hi]."
|
|
:body (max lo (min hi x)))
|
|
|
|
(define-primitive "inc"
|
|
:params ((n :as number))
|
|
:returns "number"
|
|
:doc "Increment by 1."
|
|
:body (+ n 1))
|
|
|
|
(define-primitive "dec"
|
|
:params ((n :as number))
|
|
:returns "number"
|
|
:doc "Decrement by 1."
|
|
:body (- n 1))
|
|
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; Core — Comparison
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(define-module :core.comparison)
|
|
|
|
(define-primitive "="
|
|
:params (a b)
|
|
:returns "boolean"
|
|
:doc "Deep structural equality. Alias for equal?.")
|
|
|
|
(define-primitive "!="
|
|
:params (a b)
|
|
:returns "boolean"
|
|
:doc "Inequality."
|
|
:body (not (= a b)))
|
|
|
|
(define-primitive "eq?"
|
|
:params (a b)
|
|
:returns "boolean"
|
|
:doc "Identity equality. True only if a and b are the exact same object.
|
|
For immutable atoms (numbers, strings, booleans, nil) this may or
|
|
may not match — use eqv? for reliable atom comparison.")
|
|
|
|
(define-primitive "eqv?"
|
|
:params (a b)
|
|
:returns "boolean"
|
|
:doc "Equivalent value for atoms, identity for compound objects.
|
|
Returns true for identical objects (eq?), and also for numbers,
|
|
strings, booleans, and nil with the same value. For lists, dicts,
|
|
lambdas, and components, only true if same identity.")
|
|
|
|
(define-primitive "equal?"
|
|
:params (a b)
|
|
:returns "boolean"
|
|
:doc "Deep structural equality. Recursively compares lists and dicts.
|
|
Same semantics as = but explicit Scheme name.")
|
|
|
|
(define-primitive "<"
|
|
:params ((a :as number) (b :as number))
|
|
:returns "boolean"
|
|
:doc "Less than.")
|
|
|
|
(define-primitive ">"
|
|
:params ((a :as number) (b :as number))
|
|
:returns "boolean"
|
|
:doc "Greater than.")
|
|
|
|
(define-primitive "<="
|
|
:params ((a :as number) (b :as number))
|
|
:returns "boolean"
|
|
:doc "Less than or equal.")
|
|
|
|
(define-primitive ">="
|
|
:params ((a :as number) (b :as number))
|
|
:returns "boolean"
|
|
:doc "Greater than or equal.")
|
|
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; Core — Predicates
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(define-module :core.predicates)
|
|
|
|
(define-primitive "odd?"
|
|
:params ((n :as number))
|
|
:returns "boolean"
|
|
:doc "True if n is odd."
|
|
:body (= (mod n 2) 1))
|
|
|
|
(define-primitive "even?"
|
|
:params ((n :as number))
|
|
:returns "boolean"
|
|
:doc "True if n is even."
|
|
:body (= (mod n 2) 0))
|
|
|
|
(define-primitive "zero?"
|
|
:params ((n :as number))
|
|
: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 "boolean?"
|
|
:params (x)
|
|
:returns "boolean"
|
|
:doc "True if x is a boolean (true or false). Must be checked before
|
|
number? on platforms where booleans are numeric subtypes.")
|
|
|
|
(define-primitive "number?"
|
|
:params (x)
|
|
:returns "boolean"
|
|
:doc "True if x is a number (int or float). Excludes booleans.")
|
|
|
|
(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 "continuation?"
|
|
:params (x)
|
|
:returns "boolean"
|
|
:doc "True if x is a captured continuation.")
|
|
|
|
(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 :as list))
|
|
:returns "list"
|
|
:doc "Concatenate multiple lists into one. Skips nil values.")
|
|
|
|
(define-primitive "upper"
|
|
:params ((s :as string))
|
|
:returns "string"
|
|
:doc "Uppercase string.")
|
|
|
|
(define-primitive "upcase"
|
|
:params ((s :as string))
|
|
:returns "string"
|
|
:doc "Alias for upper. Uppercase string.")
|
|
|
|
(define-primitive "lower"
|
|
:params ((s :as string))
|
|
:returns "string"
|
|
:doc "Lowercase string.")
|
|
|
|
(define-primitive "downcase"
|
|
:params ((s :as string))
|
|
:returns "string"
|
|
:doc "Alias for lower. Lowercase string.")
|
|
|
|
(define-primitive "string-length"
|
|
:params ((s :as string))
|
|
:returns "number"
|
|
:doc "Length of string in characters.")
|
|
|
|
(define-primitive "substring"
|
|
:params ((s :as string) (start :as number) (end :as number))
|
|
:returns "string"
|
|
:doc "Extract substring from start (inclusive) to end (exclusive).")
|
|
|
|
(define-primitive "string-contains?"
|
|
:params ((s :as string) (needle :as string))
|
|
:returns "boolean"
|
|
:doc "True if string s contains substring needle.")
|
|
|
|
(define-primitive "trim"
|
|
:params ((s :as string))
|
|
:returns "string"
|
|
:doc "Strip leading/trailing whitespace.")
|
|
|
|
(define-primitive "split"
|
|
:params ((s :as string) &rest (sep :as string))
|
|
:returns "list"
|
|
:doc "Split string by separator (default space).")
|
|
|
|
(define-primitive "join"
|
|
:params ((sep :as string) (coll :as list))
|
|
:returns "string"
|
|
:doc "Join collection items with separator string.")
|
|
|
|
(define-primitive "replace"
|
|
:params ((s :as string) (old :as string) (new :as string))
|
|
:returns "string"
|
|
:doc "Replace all occurrences of old with new in s.")
|
|
|
|
(define-primitive "slice"
|
|
:params (coll (start :as number) &rest (end :as number))
|
|
:returns "any"
|
|
:doc "Slice a string or list from start to end (exclusive). End is optional.")
|
|
|
|
(define-primitive "index-of"
|
|
:params ((s :as string) (needle :as string) &rest (from :as number))
|
|
:returns "number"
|
|
:doc "Index of first occurrence of needle in s, or -1 if not found. Optional start index.")
|
|
|
|
(define-primitive "starts-with?"
|
|
:params ((s :as string) (prefix :as string))
|
|
:returns "boolean"
|
|
:doc "True if string s starts with prefix.")
|
|
|
|
(define-primitive "ends-with?"
|
|
:params ((s :as string) (suffix :as string))
|
|
: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 :as number) (end :as number) &rest (step :as number))
|
|
: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 :as list))
|
|
:returns "any"
|
|
:doc "First element, or nil if empty.")
|
|
|
|
(define-primitive "last"
|
|
:params ((coll :as list))
|
|
:returns "any"
|
|
:doc "Last element, or nil if empty.")
|
|
|
|
(define-primitive "rest"
|
|
:params ((coll :as list))
|
|
:returns "list"
|
|
:doc "All elements except the first.")
|
|
|
|
(define-primitive "nth"
|
|
:params ((coll :as list) (n :as number))
|
|
:returns "any"
|
|
:doc "Element at index n, or nil if out of bounds.")
|
|
|
|
(define-primitive "cons"
|
|
:params (x (coll :as list))
|
|
:returns "list"
|
|
:doc "Prepend x to coll.")
|
|
|
|
(define-primitive "append"
|
|
:params ((coll :as list) x)
|
|
:returns "list"
|
|
:doc "If x is a list, concatenate. Otherwise append x as single element.")
|
|
|
|
(define-primitive "append!"
|
|
:params ((coll :as list) x)
|
|
:returns "list"
|
|
:doc "Mutate coll by appending x in-place. Returns coll.")
|
|
|
|
(define-primitive "reverse"
|
|
:params ((coll :as list))
|
|
:returns "list"
|
|
:doc "Return coll in reverse order.")
|
|
|
|
(define-primitive "flatten"
|
|
:params ((coll :as list))
|
|
:returns "list"
|
|
:doc "Flatten one level of nesting. Nested lists become top-level elements.")
|
|
|
|
(define-primitive "chunk-every"
|
|
:params ((coll :as list) (n :as number))
|
|
:returns "list"
|
|
:doc "Split coll into sub-lists of size n.")
|
|
|
|
(define-primitive "zip-pairs"
|
|
:params ((coll :as list))
|
|
: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 :as dict))
|
|
:returns "list"
|
|
:doc "List of dict keys.")
|
|
|
|
(define-primitive "vals"
|
|
:params ((d :as dict))
|
|
:returns "list"
|
|
:doc "List of dict values.")
|
|
|
|
(define-primitive "merge"
|
|
:params (&rest (dicts :as dict))
|
|
:returns "dict"
|
|
:doc "Merge dicts left to right. Later keys win. Skips nil.")
|
|
|
|
(define-primitive "has-key?"
|
|
:params ((d :as dict) key)
|
|
:returns "boolean"
|
|
:doc "True if dict d contains key.")
|
|
|
|
(define-primitive "assoc"
|
|
:params ((d :as dict) &rest pairs)
|
|
:returns "dict"
|
|
:doc "Return new dict with key/value pairs added/overwritten.")
|
|
|
|
(define-primitive "dissoc"
|
|
:params ((d :as dict) &rest keys)
|
|
:returns "dict"
|
|
:doc "Return new dict with keys removed.")
|
|
|
|
(define-primitive "dict-set!"
|
|
:params ((d :as dict) key val)
|
|
:returns "any"
|
|
:doc "Mutate dict d by setting key to val in-place. Returns val.")
|
|
|
|
(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 :as string) (fmt :as string))
|
|
:returns "string"
|
|
:doc "Parse ISO date string and format with strftime-style format.")
|
|
|
|
(define-primitive "format-decimal"
|
|
:params ((val :as number) &rest (places :as number))
|
|
: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 :as string))
|
|
:returns "string"
|
|
:doc "Parse datetime string — identity passthrough (returns string or nil).")
|
|
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; Stdlib — Text
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(define-module :stdlib.text)
|
|
|
|
(define-primitive "pluralize"
|
|
:params ((count :as number) &rest (forms :as string))
|
|
:returns "string"
|
|
:doc "Pluralize: (pluralize 1) → \"\", (pluralize 2) → \"s\". Or (pluralize n \"item\" \"items\").")
|
|
|
|
(define-primitive "escape"
|
|
:params ((s :as string))
|
|
:returns "string"
|
|
:doc "HTML-escape a string (&, <, >, \", ').")
|
|
|
|
(define-primitive "strip-tags"
|
|
:params ((s :as string))
|
|
:returns "string"
|
|
:doc "Remove HTML tags from string.")
|
|
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; Stdlib — Style
|
|
;; --------------------------------------------------------------------------
|
|
|
|
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; 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.")
|
|
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; Type introspection — platform primitives
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(define-module :stdlib.types)
|
|
|
|
(define-primitive "type-of"
|
|
:params (x)
|
|
:returns "string"
|
|
:doc "Return type name: number, string, boolean, nil, symbol, keyword, list, dict, lambda, component, island, macro.")
|
|
|
|
(define-primitive "symbol-name"
|
|
:params ((sym :as symbol))
|
|
:returns "string"
|
|
:doc "Return the name string of a symbol.")
|
|
|
|
(define-primitive "keyword-name"
|
|
:params ((kw :as keyword))
|
|
:returns "string"
|
|
:doc "Return the name string of a keyword.")
|
|
|
|
(define-primitive "sx-parse"
|
|
:params ((source :as string))
|
|
:returns "list"
|
|
:doc "Parse SX source string into a list of AST expressions.")
|