sigildocs

(sigil core)

(sigil core) - Core Language Primitives

The foundation of Sigil. This module is automatically imported into every module and provides essential procedures, macros, and syntax forms.

List Operations

(append '(1 2) '(3 4))           ; => (1 2 3 4)
(reverse '(1 2 3))               ; => (3 2 1)
(filter even? '(1 2 3 4 5))      ; => (2 4)
(map (lambda (x) (* x 2)) '(1 2 3)) ; => (2 4 6)
(fold + 0 '(1 2 3 4))            ; => 10

Control Flow

(cond
  ((< x 0) "negative")
  ((= x 0) "zero")
  (else "positive"))

(case color
  ((red green blue) "primary")
  ((cyan magenta yellow) "secondary")
  (else "other"))

(when (> x 0) (do-something))
(unless (null? lst) (process lst))

Records

(define-record <point> (x) (y))
(define p (point 10 20))
(point-x p)  ; => 10
(point? p)   ; => #t

Exception Handling

(error "Something went wrong: ~a" details)
(raise (make-exception 'not-found "Resource missing" '()))

Keyword Arguments

(define (greet name (keys: (greeting "Hello")))
  (format "~a, ~a!" greeting name))

(greet "World")                    ; => "Hello, World!"
(greet "World" greeting: "Hi")     ; => "Hi, World!"

This module uses bootstrap primitives (%make-module, %module-export!) instead of define-library for internal bootstrapping reasons.

Exports

assq-refprocedure

Look up a key in an association list using eq?, returning the value or a default.

Returns the cdr of the first pair whose car is eq? to KEY, or DEFAULT if no match is found. If DEFAULT is not provided, returns #f.

(assq-ref 'b '((a . 1) (b . 2) (c . 3)))      ; => 2
(assq-ref 'x '((a . 1) (b . 2)) 'not-found)   ; => not-found
(assq-ref 'x '((a . 1) (b . 2)))              ; => #f
assv-refprocedure

Look up a key in an association list using eqv?, returning the value or a default.

Returns the cdr of the first pair whose car is eqv? to KEY, or DEFAULT if no match is found. If DEFAULT is not provided, returns #f.

(assv-ref 1 '((1 . "one") (2 . "two") (3 . "three")))  ; => "one"
(assv-ref 4 '((1 . "one") (2 . "two")) 'not-found)     ; => not-found
(assv-ref 4 '((1 . "one") (2 . "two")))                ; => #f
zero?procedure

Check if a number is zero.

(zero? 0)    ; => #t
(zero? 1)    ; => #f
(zero? -1)   ; => #f
(zero? 0.0)  ; => #t
positive?procedure

Check if a number is positive (greater than zero).

(positive? 5)    ; => #t
(positive? 0)    ; => #f
(positive? -3)   ; => #f
(positive? 0.1)  ; => #t
negative?procedure

Check if a number is negative (less than zero).

(negative? -5)   ; => #t
(negative? 0)    ; => #f
(negative? 3)    ; => #f
(negative? -0.1) ; => #t
appendprocedure

Concatenate any number of lists into one.

Returns a new list with elements from all lists in order. The last list is shared (not copied).

(append '(1 2) '(3 4))        ; => (1 2 3 4)
(append '(a) '(b) '(c d))     ; => (a b c d)
(append '(1 2) '())           ; => (1 2)
reverseprocedure

Reverse the order of elements in a list.

Returns a new list with elements in reverse order.

(reverse '(1 2 3))    ; => (3 2 1)
(reverse '(a b c d))  ; => (d c b a)
(reverse '())         ; => ()

list-length-internal - Tail-recursive length

Check if a syntax object has local-binding metadata in its substs. Used for free-identifier=? comparison in literal matching.

Register captured locals for a macro instance. Returns the capture-id to use for retrieval.

Retrieve a captured value at runtime. capture-id is the unique ID for the macro instance. sym is the symbol name of the captured local.

syntax-rules transformer generator

In syntax-rules, the first element of each pattern is the macro keyword. It should be treated as a literal (matched exactly, not bound as a variable).

captured-locals is an alist of (symbol . value) for free identifiers that were local bindings at macro definition time. This enables proper hygiene for macros defined in local scopes.

caarprocedure

Get the car of the car: (car (car x)).

(caar '((1 2) 3))  ; => 1
cadrprocedure

Get the car of the cdr (second element): (car (cdr x)).

(cadr '(1 2 3))  ; => 2
cdarprocedure

Get the cdr of the car: (cdr (car x)).

(cdar '((1 2 3) 4))  ; => (2 3)
cddrprocedure

Get the cdr of the cdr (tail after second): (cdr (cdr x)).

(cddr '(1 2 3 4))  ; => (3 4)
caadrprocedure

Get (car (car (cdr x))).

cadarprocedure

Get (car (cdr (car x))) - useful for alist value access.

caddrprocedure

Get the third element: (car (cdr (cdr x))).

(caddr '(1 2 3 4))  ; => 3
cdadrprocedure

Get (cdr (car (cdr x))).

cdddrprocedure

Get the tail after third element: (cdr (cdr (cdr x))).

(cdddr '(1 2 3 4 5))  ; => (4 5)
cadddrprocedure

Get the fourth element: (car (cdr (cdr (cdr x)))).

(cadddr '(1 2 3 4 5))  ; => 4

Check if a syntax object is an identifier (syntax-wrapped symbol)

Pattern match on syntax objects, preserving syntax in bindings. Returns an alist of (pattern-var . syntax-value) or #f if no match. Unlike the plain %pattern-match, this keeps syntax wrappers on bindings.

Match ellipsis patterns on syntax, building lists of bindings

Collect pattern variables from a pattern (for ellipsis handling) Returns plain symbols, stripping syntax wrappers from pattern variable names.

Match multiple expressions against a sub-pattern, accumulating bindings

Continue pattern matching after ellipsis

Instantiate a template with syntax bindings. This is the core of hygienic template construction.

  • Pattern variables are substituted with their bound syntax values
  • Introduced bindings are renamed with gensyms for hygiene
  • Free identifiers with capture-id emit (%macro-capture capture-id 'sym) forms
  • Other free identifiers are wrapped with definition-site module name

Expand an ellipsis template

Hygienic version of ellipsis expansion that uses renames and capture-id

%matchvariable

Default binding for %match used outside syntax-case / with-syntax. syntax-case and with-syntax establish inner let bindings that shadow this top-level value with the actual match alist. When (syntax tmpl) or #'tmpl is used at top level (outside either form), the reference to %match resolves here, giving %syntax-template-instantiate an empty bindings alist -- no pattern-variable substitution occurs and the template is returned as a plain syntax object.

Post-process the result of %syntax-template-instantiate so that (syntax tmpl) always produces a syntax object. When the template has no pattern-variable or module-renamed identifiers to substitute, %syntax-template-instantiate returns the raw datum unchanged; wrap it so callers like (syntax? #'foo) see a syntax object.

Determine whether a template datum contains any symbols that could be pattern variables. Templates with no symbols at all (numbers, strings, booleans, empty lists, nested lists of the same) have no possible pattern-variable references and can be emitted as a plain syntax wrap without consulting %match.

Generate let-bindings for pattern variables from a match result Takes: pattern, literals list, and the binding alist variable name Returns: list of (var (cdr (assq 'var bindings-var))) Note: Variable names are stripped of syntax to work as proper binding names.

%qs-head-is?procedure

Head-symbol predicate -- is tmpl a pair whose car (possibly syntax-wrapped) is the symbol sym?

%qs-innerprocedure

Extract the single argument of a one-argument form (X arg). Works through syntax-wrapping because car/cdr unwrap one level on the register VM.

%qs-walkprocedure

Rewrite a quasisyntax list element-by-element. Returns a cons of (new-list . escape-bindings) where escape-bindings is a list of (kind gensym expr): kind is 'single or 'splice.

The only difference between %qs-walk-list and %qs-walk's pair arm is that at depth 0, a (unsyntax-splicing expr) ELEMENT of a list rewrites to two sibling entries (<gensym> ...) so the surrounding list instantiator sees <gensym> ... at the right nesting level.

dynamic-windprocedure

dynamic-wind - establish before/after thunks around body R7RS: (dynamic-wind before thunk after) Calls before, then thunk, then after. If a continuation captured inside thunk is invoked from outside, after is called on exit and before on re-entry.

%qq-unquote?procedure

The quasiquote macro (invoked with backtick `) allows building lists with some parts evaluated and some quoted.

Examples: (a b c) => '(a b c) (a ,x c) => (list 'a x 'c) (a ,@xs c) => (append (list 'a) xs (list 'c)) (a (b ,c) d) => (list 'a (list 'b c) 'd)

Implementation approach:

  • Walk the quasiquoted form
  • For atoms: quote them
  • For (unquote x): return x directly
  • For lists containing unquote-splicing: use append
  • For other lists: use cons or list Check if a form is (unquote x)

Check if a form is (unquote-splicing x)

Check if a list contains any unquote-splicing at the top level

%qq-expandprocedure

Expand a quasiquoted expression into list-building code

Expand a list without splicing using list

Expand a list that contains splicing using append

Collect segments for append: quoted items get wrapped in (list ...), spliced items are included directly

Collect one segment: either a single splice, or multiple non-spliced items Returns (segment . remaining-list)

Collect consecutive non-spliced items Returns (items . remaining-list)

mapprocedure

Apply a procedure to each element of a list, returning a new list.

(map (lambda (x) (* x 2)) '(1 2 3))  ; => (2 4 6)
(map symbol->string '(a b c))        ; => ("a" "b" "c")

For multi-list mapping, use list-map from (sigil list):

(import (sigil list))
(list-map + '(1 2 3) '(10 20 30))    ; => (11 22 33)
anyprocedure

Test if any element in a list satisfies a predicate.

Returns #t as soon as one matching element is found, #f otherwise.

(any even? '(1 3 4 5))    ; => #t
(any even? '(1 3 5 7))    ; => #f
(any null? '(() (1) (2))) ; => #t
everyprocedure

Test if all elements in a list satisfy a predicate.

Returns #t if the predicate is true for every element, #f otherwise.

(every number? '(1 2 3))  ; => #t
(every even? '(2 4 5))    ; => #f
(every symbol? '())       ; => #t (vacuously true)
for-eachprocedure

Apply a procedure to each element of a list for side effects.

Like map, but doesn't collect results—used for side effects like printing.

(for-each println '("a" "b" "c"))  ; prints a, b, c on separate lines
(for-each (lambda (x) (set! sum (+ sum x))) '(1 2 3))

For multi-list iteration, use list-for-each from (sigil list):

(import (sigil list))
(list-for-each (lambda (a b) (println "~a" (+ a b))) '(1 2) '(10 20))
filterprocedure

Return elements that satisfy a predicate.

Creates a new list containing only elements for which the predicate returns #t.

(filter even? '(1 2 3 4 5))       ; => (2 4)
(filter string? '(1 "a" 2 "b"))   ; => ("a" "b")
(filter (lambda (x) (> x 0)) '(-1 0 1 2))  ; => (1 2)
filter-mapprocedure

Map a procedure over a list, keeping only non-#f results.

Applies proc to each element and collects non-false results. This is equivalent to (filter values (map proc lst)) but more efficient.

(filter-map (lambda (x) (if (even? x) (* x 2) #f)) '(1 2 3 4))  ; => (4 8)
(filter-map (lambda (x) (and (> x 0) x)) '(-1 2 -3 4))          ; => (2 4)
partitionprocedure

Partition a list into two lists based on a predicate.

Returns two values: a list of elements satisfying the predicate, and a list of elements that don't.

(partition even? '(1 2 3 4 5 6))  ; => (2 4 6) (1 3 5)
(partition positive? '(-1 2 -3 4))  ; => (2 4) (-1 -3)
takeprocedure

Return the first n elements of a list.

If the list has fewer than n elements, returns the entire list.

(take '(1 2 3 4 5) 3)   ; => (1 2 3)
(take '(a b) 5)         ; => (a b)
(take '() 3)            ; => ()
removeprocedure

Remove all occurrences of an item from a list.

Uses equal? for comparison. Returns a new list with all elements equal to item removed.

(remove 2 '(1 2 3 2 4))    ; => (1 3 4)
(remove 'a '(a b a c a))   ; => (b c)
findprocedure

Find the first element satisfying a predicate.

Returns the first element for which the predicate returns #t, or #f if no element matches.

(find even? '(1 3 4 5))   ; => 4
(find even? '(1 3 5))     ; => #f
(find (lambda (x) (> x 10)) '(5 15 25))  ; => 15
assoc-refprocedure

Get the value for a key in an alist, or a default if not found.

Uses equal? for key comparison.

(assoc-ref 'b '((a . 1) (b . 2) (c . 3)))      ; => 2
(assoc-ref 'd '((a . 1) (b . 2)) 'not-found)   ; => not-found
(assoc-ref 'd '((a . 1) (b . 2)))              ; => #f
fold-leftprocedure

Reduce a list from the left.

Applies (proc accumulator element) for each element, threading the result as the accumulator for the next call.

(fold-left + 0 '(1 2 3 4))        ; => 10
(fold-left cons '() '(1 2 3))     ; => ((((() . 1) . 2) . 3)
(fold-left max 0 '(3 1 4 1 5))    ; => 5
fold-rightprocedure

Reduce a list from the right.

Applies (proc element accumulator) starting from the last element. Note: Not tail-recursive—may stack overflow on very long lists.

(fold-right cons '() '(1 2 3))    ; => (1 2 3)
(fold-right + 0 '(1 2 3 4))       ; => 10
(fold-right list '() '(a b c))    ; => (a (b (c ())))
foldvariable

Reduce a list (alias for fold-left).

The most common fold operation. See fold-left for details.

(fold + 0 '(1 2 3 4))  ; => 10
even?procedure

even? - Test if an integer is even (even? 4) => #t (even? 3) => #f

odd?procedure

odd? - Test if an integer is odd (odd? 3) => #t (odd? 4) => #f

iotaprocedure

iota - Generate a list of integers (iota 5) => (0 1 2 3 4) (iota 5 1) => (1 2 3 4 5) (iota 5 0 2) => (0 2 4 6 8)

make-listprocedure

Create a list of n copies of fill.

(make-list 3 'a)  ; => (a a a)
(make-list 5)     ; => (#f #f #f #f #f)
(make-list 0 'x)  ; => ()

If fill is omitted, #f is used.

make-stringprocedure

Create a string of n copies of char.

(make-string 3 #\a)  ; => "aaa"
(make-string 5)      ; => "     "
(make-string 0 #\x)  ; => ""

If char is omitted, space is used.

Apply procedure to each element of a vector for side effects. (vector-for-each display #(1 2 3)) displays 123

vector-mapprocedure

Apply procedure to each element, return vector of results. (vector-map (lambda (x) (* x 2)) #(1 2 3)) => #(2 4 6)

vector-filterprocedure

Return a new vector containing only elements that satisfy the predicate. (vector-filter even? #(1 2 3 4 5)) => #(2 4)

vector-foldprocedure

Fold a procedure over a vector from left to right. (vector-fold + 0 #(1 2 3 4 5)) => 15

Apply procedure to each character of a string for side effects. (string-for-each display "abc") displays abc

O(n): walks the string with a native char cursor (one forward UTF-8 decode per character) instead of per-index string-ref, which is O(index) on non-ASCII strings and made this loop O(n^2). The proc is called from bytecode (not from a C callback loop), so procs that suspend (await/yield in a fiber) keep full continuation support — see topics/sigil-native-reentry-fiber-amputation for why a native callback loop would not.

string-mapprocedure

Apply procedure to each character, return string of results. (string-map char-upcase "abc") => "ABC"

O(n) via a native char cursor (see string-for-each above).

error-object?procedure

Test if a value is an error object (exception).

Get the message from an error object.

Get the irritants from an error object.

Get the type/kind of an error object.

file-error?procedure

Test if an error is file-related.

read-error?procedure

Test if an error is read-related.

*reset-tag*variable

Default prompt tag for reset/shift Exported so that reset/shift macro expansions can reference it.

call-with-port - R7RS procedure for safe port handling Calls proc with port as argument, closes port when proc returns, and returns the result of proc.

Usage: (call-with-port (open-input-file "data.txt") (lambda (port) (read-line port)))

Exception type tag

Create an exception with the given kind, message, and irritants.

Create an exception with stack trace.

exception?procedure

Check if obj is an exception.

Get the kind of an exception (e.g., 'error, 'type-error).

Get the message from an exception.

Get the irritants (additional data) from an exception.

Get the stack trace from an exception.

Exception prompt tag

Default handler - prints error and returns #f

Current exception handler

Get the current exception handler.

raiseprocedure

Raise an exception (non-continuable). The compiler compiles direct calls to OP_RAISE (which handles stack trace attachment and searches the VM exception handler stack). This Scheme wrapper exists for first-class use (e.g. apply/map).

Raise a continuable exception. The handler can return a value to continue execution.

errorprocedure

Convenience function to raise an error exception. (error "message" irritant ...)

Install an exception handler and execute thunk (R6RS style). Handler is a procedure taking the exception. Thunk is the body to execute.

Run a thunk with a default error handler that prints and exits. Used by sigil-run to wrap the entry point so errors are visible. Applications can install their own handlers (like REPL does) to override.

string-refprocedure

Return the character at position k in the string.

(string-ref "hello" 0)  ; => #\h
(string-ref "hello" 4)  ; => #\o

The index k must be a non-negative integer less than the string length. Raises an error if the index is out of bounds.

string-lengthprocedure

Return the number of characters in the string.

(string-length "hello")  ; => 5
(string-length "")       ; => 0
(string-length "λ")      ; => 1  (counts characters, not bytes)
string-appendprocedure

Concatenate zero or more strings into a single string.

(string-append "hello" " " "world")  ; => "hello world"
(string-append)                       ; => ""
(string-append "only")                ; => "only"
substringprocedure

Extract a substring from start (inclusive) to end (exclusive).

(substring "hello" 1 4)   ; => "ell"
(substring "hello" 0 5)   ; => "hello"
(substring "hello" 2 2)   ; => ""

Both start and end must be valid indices where 0 <= start <= end <= length.

string=?procedure

Test if two strings are equal.

(string=? "hello" "hello")  ; => #t
(string=? "hello" "Hello")  ; => #f  (case-sensitive)
(string=? "" "")            ; => #t
char=?procedure

Test if two characters are equal.

(char=? #\a #\a)  ; => #t
(char=? #\a #\A)  ; => #f
char<?procedure

Test if the first character is less than the second.

(char<? #\a #\b)  ; => #t
(char<? #\b #\a)  ; => #f
(char<? #\A #\a)  ; => #t  (uppercase before lowercase in Unicode)
char<=?procedure

Test if the first character is less than or equal to the second.

(char<=? #\a #\a)  ; => #t
(char<=? #\a #\b)  ; => #t
(char<=? #\b #\a)  ; => #f
char>?procedure

Test if the first character is greater than the second.

(char>? #\b #\a)  ; => #t
(char>? #\a #\b)  ; => #f
char>=?procedure

Test if the first character is greater than or equal to the second.

(char>=? #\a #\a)  ; => #t
(char>=? #\b #\a)  ; => #t
(char>=? #\a #\b)  ; => #f
char->integerprocedure

Convert a character to its Unicode code point.

(char->integer #\a)    ; => 97
(char->integer #\A)    ; => 65
(char->integer #\λ)    ; => 955
(char->integer #\space) ; => 32
integer->charprocedure

Convert a Unicode code point to its character.

(integer->char 97)   ; => #\a
(integer->char 65)   ; => #\A
(integer->char 955)  ; => #\λ
stringprocedure

Create a string from zero or more character arguments.

(string #\h #\i)  ; => "hi"
(string)          ; => ""
(string #\λ)      ; => "λ"
carprocedure

Return the first element (car) of a pair.

(car '(1 2 3))      ; => 1
(car '(a . b))      ; => a
(car '((1 2) 3 4))  ; => (1 2)
cdrprocedure

Return the rest (cdr) of a pair.

(cdr '(1 2 3))      ; => (2 3)
(cdr '(a . b))      ; => b
(cdr '(1))          ; => ()
consprocedure

Construct a new pair with the given car and cdr.

(cons 1 2)           ; => (1 . 2)
(cons 1 '(2 3))      ; => (1 2 3)
(cons 'a '())        ; => (a)
set-car!procedure

Mutate the car of a pair.

(define p (cons 1 2))
(set-car! p 'a)
p  ; => (a . 2)
set-cdr!procedure

Mutate the cdr of a pair.

(define p (cons 1 2))
(set-cdr! p 'b)
p  ; => (1 . b)
vector-refprocedure

Return the element at position k in the vector.

(vector-ref #(a b c) 0)  ; => a
(vector-ref #(a b c) 2)  ; => c

The index must be a non-negative integer less than the vector length.

vector-set!procedure

Set the element at position k in the vector.

(define v #(1 2 3))
(vector-set! v 1 'x)
v  ; => #(1 x 3)
vector-lengthprocedure

Return the number of elements in the vector.

(vector-length #(a b c))  ; => 3
(vector-length #())       ; => 0
make-vectorprocedure

Create a vector of the given length, optionally filled with a value.

(make-vector 3)       ; => #(#<undefined> #<undefined> #<undefined>)
(make-vector 3 'x)    ; => #(x x x)
(make-vector 0)       ; => #()
list->vectorprocedure

Convert a list to a freshly-allocated vector.

(list->vector '(1 2 3))  ; => #(1 2 3)
(list->vector '())       ; => #()
vector->listprocedure

Convert a vector, or a sub-range of it, to a list.

With one argument, converts the whole vector. start (default 0) and end (default the vector length) select the half-open range [start, end).

(vector->list #(1 2 3))      ; => (1 2 3)
(vector->list #(1 2 3) 1)    ; => (2 3)
(vector->list #(1 2 3 4) 1 3); => (2 3)
vector-copy!procedure

Copy elements from one vector into another, in place.

Copies the elements of from in the half-open range [start, end) (default the whole vector) into to starting at index at. Overlap is handled correctly when to and from are the same vector.

(define v #(1 2 3 4 5))
(vector-copy! v 0 #(a b) 0 2)
v  ; => #(a b 3 4 5)
vector-fill!procedure

Fill a vector, or a sub-range of it, with a value, in place.

start (default 0) and end (default the vector length) select the half-open range [start, end) to fill.

(define v #(1 2 3 4))
(vector-fill! v 0)      ; v => #(0 0 0 0)
(vector-fill! v 9 1 3)  ; v => #(0 9 9 0)
vector-copyprocedure

Copy a vector, optionally a sub-range.

(vector-copy #(1 2 3))      ; => #(1 2 3)
(vector-copy #(1 2 3) 1)    ; => #(2 3)
(vector-copy #(1 2 3) 0 2)  ; => #(1 2)
displayprocedure

Display a value to a port (default: current output port).

(display "hello")      ; prints: hello
(display 42)           ; prints: 42
(display #\newline)    ; prints a newline

Strings are printed without quotes, characters without # notation.

newlineprocedure

Write a newline to a port (default: current output port).

(newline)  ; prints a newline character
writeprocedure

Write a value in machine-readable form to a port.

(write "hello")  ; prints: "hello"
(write #\a)      ; prints: #\a
(write '(1 2))   ; prints: (1 2)

Unlike display, strings include quotes and characters include # notation.

read-exprprocedure

Read an S-expression from a string.

(read-expr "(+ 1 2)")   ; => (+ 1 2)
(read-expr "42")        ; => 42
(read-expr "\"hello\"") ; => "hello"
loadprocedure

Load and evaluate a Scheme source file.

(load "myfile.sgl")

Reads and evaluates all expressions in the file sequentially.

evalprocedure

Evaluate an expression in the current environment.

(eval '(+ 1 2))          ; => 3
(eval '(list 'a 'b 'c))  ; => (a b c)

Convert a symbol to its string name.

(symbol->string 'hello)  ; => "hello"
(symbol->string '+)      ; => "+"

Convert a string to a symbol (interned).

(string->symbol "hello")  ; => hello
(eq? (string->symbol "x") (string->symbol "x"))  ; => #t

Convert a number to its string representation.

(number->string 42)       ; => "42"
(number->string 3.14)     ; => "3.14"
(number->string 255 16)   ; => "ff"  (with radix)

Parse a string as a number, returning #f if invalid.

(string->number "42")     ; => 42
(string->number "3.14")   ; => 3.14
(string->number "ff" 16)  ; => 255  (with radix)
(string->number "abc")    ; => #f
string->listprocedure

Convert a string to a list of characters.

(string->list "hello")  ; => (#\h #\e #\l #\l #\o)
(string->list "")       ; => ()
list->stringprocedure

Convert a list of characters to a string.

(list->string '(#\h #\i))  ; => "hi"
(list->string '())         ; => ""
string-copyprocedure

Copy a string, optionally a sub-range.

(string-copy "hello")      ; => "hello"
(string-copy "hello" 1)    ; => "ello"
(string-copy "hello" 1 4)  ; => "ell"
string-copy!procedure

Copy characters into an existing string, in place.

Copies the characters of from in [start, end) (default the whole string) into to starting at index at. Overlap is handled correctly when to and from are the same string. Each copied character must have the same UTF-8 byte width as the character it replaces (width-changing writes raise an error, as with string-set!).

(define s (string-copy "hello"))
(string-copy! s 0 "HE" 0 2)
s  ; => "HEllo"
string-fill!procedure

Fill a string range with a character, in place.

Fills str[start, end) (default the whole string) with char. The fill character must have the same UTF-8 byte width as each character it replaces (width-changing writes raise an error, as with string-set!).

(define s (string-copy "hello"))
(string-fill! s #\x 1 3)
s  ; => "hxxlo"
string-joinprocedure

Join a list of strings with an optional separator (default "").

Builds the result in a single pass.

(string-join '("a" "b" "c") "-")  ; => "a-b-c"
(string-join '("a" "b" "c"))      ; => "abc"
(string-join '())                 ; => ""

Convert a string (optionally a sub-range) to a vector of characters.

Decodes the string in a single O(n) pass. start (inclusive) and end (exclusive) are character indices.

(string->vector "abc")        ; => #(#\a #\b #\c)
(string->vector "hello" 1 4)  ; => #(#\e #\l #\l)

Convert a vector of characters (optionally a sub-range) to a string.

(vector->string #(#\a #\b #\c))    ; => "abc"
(vector->string #(#\a #\b #\c) 1)  ; => "bc"

Create a forward character cursor over a string.

A cursor iterates a string's characters in O(1) amortized time per step by tracking the byte offset alongside the character index — unlike indexed string-ref, which is O(index) on non-ASCII strings and makes per-char index loops O(n^2). Optional start/end character indices restrict iteration to a sub-range without copying.

(define cur (string->cursor "hi"))
(cursor-peek cur)   ; => #\h  (does not advance)
(cursor-next! cur)  ; => #\h  (advances)
(cursor-next! cur)  ; => #\i
(cursor-next! cur)  ; => #f   (end of string)
(cursor-eof? cur)   ; => #t

Test whether a value is a string cursor.

(string-cursor? (string->cursor "x"))  ; => #t
(string-cursor? "x")                   ; => #f
cursor-peekprocedure

Return the character at the cursor without advancing, or #f at end.

(define cur (string->cursor "ab"))
(cursor-peek cur)  ; => #\a
(cursor-peek cur)  ; => #\a  (unchanged)
cursor-next!procedure

Return the character at the cursor and advance past it, or #f at end.

The lookahead-1 lexer pattern: cursor-peek to classify, then cursor-next! to consume.

(define cur (string->cursor "ab"))
(cursor-next! cur)  ; => #\a
(cursor-next! cur)  ; => #\b
(cursor-next! cur)  ; => #f
cursor-eof?procedure

Test whether a cursor has reached its end.

(cursor-eof? (string->cursor ""))   ; => #t
(cursor-eof? (string->cursor "a"))  ; => #f
cursor-posprocedure

Return the cursor's current character index (absolute in the string).

(define cur (string->cursor "abc"))
(cursor-pos cur)    ; => 0
(cursor-next! cur)
(cursor-pos cur)    ; => 1
bytevector?procedure

Test if a value is a bytevector.

(bytevector? #u8(1 2 3))  ; => #t
(bytevector? "string")    ; => #f

Create a bytevector of the given size, optionally filled with a byte value.

(make-bytevector 3)     ; => #u8(0 0 0)
(make-bytevector 3 42)  ; => #u8(42 42 42)

Return the byte at position k in the bytevector.

(bytevector-u8-ref #u8(10 20 30) 1)  ; => 20

Set the byte at position k in the bytevector.

(define bv (make-bytevector 3 0))
(bytevector-u8-set! bv 1 255)
bv  ; => #u8(0 255 0)

Return the number of bytes in the bytevector.

(bytevector-length #u8(1 2 3))  ; => 3
bytevectorprocedure

Create a bytevector from byte arguments (each 0-255).

(bytevector 1 2 3)  ; => #u8(1 2 3)
(bytevector)        ; => #u8()

Copy a bytevector, optionally a sub-range.

(bytevector-copy #u8(1 2 3))      ; => #u8(1 2 3)
(bytevector-copy #u8(1 2 3) 1)    ; => #u8(2 3)
(bytevector-copy #u8(1 2 3) 0 2)  ; => #u8(1 2)

Copy bytes into an existing bytevector, in place.

Copies the bytes of from in [start, end) (default the whole bytevector) into to starting at index at. Overlap is handled correctly when to and from are the same bytevector.

(define bv (bytevector 1 2 3 4 5))
(bytevector-copy! bv 0 #u8(9 9) 0 2)
bv  ; => #u8(9 9 3 4 5)

Concatenate zero or more bytevectors.

(bytevector-append #u8(1 2) #u8(3 4))  ; => #u8(1 2 3 4)
(bytevector-append)                    ; => #u8()

Read an IEEE-754 double (8 bytes) at byte offset k.

The optional endianness is a symbol: little, big, or native (the default when omitted). The result is a flonum. Bounds-checked: k + 8 must not exceed the bytevector length.

A colliding NaN bit pattern read from arbitrary bytes is canonicalized to a safe quiet NaN (a Sigil-written value round-trips bit-exactly).

(define bv (make-bytevector 8 0))
(bytevector-ieee-double-set! bv 0 3.14 'little)
(bytevector-ieee-double-ref bv 0 'little)  ; => 3.14

Write an IEEE-754 double (8 bytes) at byte offset k.

The optional endianness is a symbol: little, big, or native (the default when omitted). Bounds-checked: k + 8 must not exceed the bytevector length.

(define bv (make-bytevector 8 0))
(bytevector-ieee-double-set! bv 0 -0.5)  ; native endianness

Read an IEEE-754 single (4 bytes) at byte offset k, returned as a flonum (promoted to double precision).

The optional endianness is a symbol: little, big, or native (the default when omitted). Bounds-checked: k + 4 must not exceed the bytevector length.

(define bv (make-bytevector 4 0))
(bytevector-ieee-single-set! bv 0 1.5 'big)
(bytevector-ieee-single-ref bv 0 'big)  ; => 1.5

Write an IEEE-754 single (4 bytes) at byte offset k. The value is rounded to single precision.

The optional endianness is a symbol: little, big, or native (the default when omitted). Bounds-checked: k + 4 must not exceed the bytevector length.

(define bv (make-bytevector 4 0))
(bytevector-ieee-single-set! bv 0 2.0)  ; native endianness
srcloc?procedure

Test if a value is a source location object.

srcloc-fileprocedure

Get the file path from a source location.

srcloc-lineprocedure

Get the line number (1-based) from a source location.

srcloc-columnprocedure

Get the column number (0-based) from a source location.

Get the byte position from a source location.

srcloc-spanprocedure

Get the byte span (length) from a source location.

make-srclocprocedure

Create a source location object.

(make-srcloc "file.sgl" 10 0 100 25)

Arguments: file, line, column, position, span.

Capture the current call stack as a stack trace object.

(capture-stack-trace 0)  ; capture full stack
(capture-stack-trace 1)  ; skip 1 frame
stack-trace?procedure

Test if a value is a stack trace object.

Return the number of frames in a stack trace.

Get a frame from a stack trace as a vector.

Returns #(proc-name srcloc) or #f if index is out of bounds.

foreign?procedure

Test if a value is a foreign (C) object.

foreign-typeprocedure

Get the type tag of a foreign object.

+procedure

Add zero or more numbers.

(+)           ; => 0
(+ 1)         ; => 1
(+ 1 2 3)     ; => 6
(+ 1.5 2.5)   ; => 4.0
-procedure

Subtract numbers or negate a single number.

(- 5)         ; => -5  (negation)
(- 5 3)       ; => 2
(- 10 3 2)    ; => 5
*procedure

Multiply zero or more numbers.

(*)           ; => 1
(* 2)         ; => 2
(* 2 3 4)     ; => 24
/procedure

Divide numbers or compute reciprocal.

(/ 2)         ; => 0.5  (reciprocal)
(/ 10 2)      ; => 5
(/ 24 2 3)    ; => 4
<procedure

Test if arguments are in strictly increasing order.

(< 1 2)       ; => #t
(< 1 2 3)     ; => #t
(< 1 1)       ; => #f
>procedure

Test if arguments are in strictly decreasing order.

(> 2 1)       ; => #t
(> 3 2 1)     ; => #t
(> 1 1)       ; => #f
<=procedure

Test if arguments are in non-decreasing order.

(<= 1 2)      ; => #t
(<= 1 1)      ; => #t
(<= 2 1)      ; => #f
>=procedure

Test if arguments are in non-increasing order.

(>= 2 1)      ; => #t
(>= 1 1)      ; => #t
(>= 1 2)      ; => #f
=procedure

Test if all arguments are numerically equal.

(= 1 1)       ; => #t
(= 1 1 1)     ; => #t
(= 1 2)       ; => #f
(= 1 1.0)     ; => #t
null?procedure

Test if a value is the empty list.

(null? '())   ; => #t
(null? '(1))  ; => #f
pair?procedure

Test if a value is a pair.

(pair? '(1 . 2))  ; => #t
(pair? '(1 2))    ; => #t
(pair? '())       ; => #f
number?procedure

Test if a value is a number.

(number? 42)    ; => #t
(number? 3.14)  ; => #t
(number? "42")  ; => #f
symbol?procedure

Test if a value is a symbol.

(symbol? 'foo)    ; => #t
(symbol? "foo")   ; => #f
string?procedure

Test if a value is a string.

(string? "hello")  ; => #t
(string? 'hello)   ; => #f
vector?procedure

Test if a value is a vector.

(vector? #(1 2 3))  ; => #t
(vector? '(1 2 3))  ; => #f
procedure?procedure

Test if a value is a procedure.

(procedure? +)              ; => #t
(procedure? (lambda (x) x)) ; => #t
(procedure? 42)             ; => #f
boolean?procedure

Test if a value is a boolean.

(boolean? #t)  ; => #t
(boolean? #f)  ; => #t
(boolean? 0)   ; => #f
char?procedure

Test if a value is a character.

(char? #\a)    ; => #t
(char? "a")    ; => #f
list?procedure

Test if a value is a proper list.

(list? '(1 2 3))  ; => #t
(list? '())       ; => #t
(list? '(1 . 2))  ; => #f  (improper list)
listprocedure

Create a list from the arguments.

(list 1 2 3)    ; => (1 2 3)
(list)          ; => ()
(list 'a 'b)    ; => (a b)
lengthprocedure

Return the length of a list.

(length '(1 2 3))  ; => 3
(length '())       ; => 0
integer?procedure

Test if a value is an exact integer.

(integer? 42)    ; => #t
(integer? 3.0)   ; => #f
(integer? 3.5)   ; => #f
eof-object?procedure

Test if a value is the end-of-file object.

(eof-object? (eof-object))  ; => #t
(eof-object? #f)            ; => #f
eq?procedure

Test object identity (same memory location).

(eq? 'a 'a)           ; => #t
(eq? '() '())         ; => #t
(eq? "a" "a")         ; => unspecified
(define x '(1 2))
(eq? x x)             ; => #t
eqv?procedure

Test equivalence (same type and value).

(eqv? 1 1)        ; => #t
(eqv? 1 1.0)      ; => #f
(eqv? #\a #\a)    ; => #t
equal?procedure

Test structural equality (deep comparison).

(equal? '(1 2 3) '(1 2 3))   ; => #t
(equal? "hello" "hello")     ; => #t
(equal? #(1 2) #(1 2))       ; => #t
notprocedure

Boolean negation.

(not #f)      ; => #t
(not #t)      ; => #f
(not '())     ; => #f  (only #f is false)
(not 0)       ; => #f
gensymprocedure

Generate a unique symbol, optionally with a prefix.

(gensym)          ; => g1234 (unique each time)
(gensym "temp")   ; => temp1235
applyprocedure

Apply a procedure to a list of arguments.

(apply + '(1 2 3))        ; => 6
(apply list 'a 'b '(c d)) ; => (a b c d)
valuesprocedure

Return multiple values.

(values 1 2 3)  ; returns 3 values
(call-with-values (lambda () (values 1 2))
                  (lambda (a b) (+ a b)))  ; => 3

Call producer with no args, pass its values to consumer.

(call-with-values
  (lambda () (values 1 2))
  (lambda (a b) (+ a b)))  ; => 3

Create a parameter object (dynamic variable).

(define my-param (make-parameter 10))
(my-param)        ; => 10
(my-param 20)     ; set to 20
(my-param)        ; => 20
parameter?procedure

Test if a value is a parameter object.

promise?procedure

Test if a value is a promise.

(promise? (delay (+ 1 2)))  ; => #t
(promise? 42)               ; => #f
make-promiseprocedure

Create an already-forced promise containing a value.

(force (make-promise 42))  ; => 42
forceprocedure

Force evaluation of a promise, caching the result.

(define p (delay (begin (display "computing...") (+ 1 2))))
(force p)  ; prints "computing...", returns 3
(force p)  ; returns 3 (cached, no print)
eval-stringprocedure

Evaluate a string as Sigil code.

(eval-string "(+ 1 2)")  ; => 3

Evaluate a string in a specific module's context.

(eval-string-in-module "(+ 1 2)" (find-module '(sigil core)))

Get the metadata alist of a procedure.

(procedure-metadata my-proc)  ; => ((doc . "...") (name . my-proc))

Set the metadata alist of a procedure.

(set-procedure-metadata! my-proc '((custom . value)))

Get the module a procedure was defined in.

Set the module attribution of a procedure.

Get the arity specification of a procedure.

(procedure-arity +)  ; => (-1 . 0)  (variadic, at least 0 args)

Get the name of a procedure (symbol or #f).

(procedure-name car)  ; => car
module?procedure

Test if a value is a module.

Get the current module context.

Set the current module context.

macroexpandprocedure

Expand macros in an expression (for debugging).

(macroexpand '(when #t (display "hi")))
disassembleprocedure

Disassemble a procedure's bytecode (for debugging).

(disassemble my-proc)
find-moduleprocedure

Find a module by its name, returning #f if not loaded.

(find-module '(sigil core))  ; => #<module (sigil core)>
load-moduleprocedure

Load a module by name, returning the module object.

(load-module '(sigil json))  ; => #<module (sigil json)>

Get the list of exported symbols from a module.

(module-exports (find-module '(sigil path)))  ; => (path-join path-dirname ...)
module-refprocedure

Look up a binding in a module by name.

(module-ref (find-module '(sigil core)) 'car)  ; => #<native car>
module-nameprocedure

Get the name of a module as a list of symbols.

(module-name (current-module))  ; => (sigil user)

Add a directory to the library search path.

(add-library-path! "/my/modules")
library-pathsprocedure

Return the list of library search paths.

(library-paths)  ; => ("/usr/lib/sigil" "/home/user/lib")

Get the current time in milliseconds since epoch.

(current-milliseconds)  ; => 1702834567890
keyword?procedure

Test if a value is a keyword.

(keyword? foo:)    ; => #t
(keyword? 'foo)    ; => #f

Convert a keyword to a string (without the colon).

(keyword->string foo:)  ; => "foo"

Convert a string to a keyword.

(string->keyword "foo")  ; => foo:

Convert a keyword to a symbol.

(keyword->symbol foo:)  ; => foo

Split an argument list into positional and keyword arguments Returns (positional-args . keyword-alist) where keyword-alist is ((kw . value) ...)

keyword-refprocedure

Get a keyword value from a keyword alist Returns default if keyword not found

Check for unknown keywords and signal error if any found known-keywords is a list of keyword objects

keys-form?procedure

Usage: (lambda ((keys: x (y 10))) (+ x y))

(define (add (keys: (x 0) (y 0))) (+ x y))

(add x: 1 y: 2) ; => 3 (add x: 5) ; => 5 (y defaults to 0)

The (keys: ...) form contains keyword parameter specs: name - required keyword (no default) (name default) - optional keyword with default value

When no (keys: ...) form is present, lambda/define work normally. Helper: Check if a form is (keys: ...)

opts-form?procedure

Helper: Check if a form is (opts: ...)

rest-form?procedure

Helper: Check if a form is (rest: ...)

Helper: Check if a form is any special parameter form

Helper: Parse keyword parameter specs from (keys: spec ...) Returns list of (name has-default default-expr keyword-obj)

Helper: Parse optional positional parameter specs from (opts: spec ...) Returns list of (name default-expr)

Helper: Extract required positional params (everything before opts:/keys:/rest:)

Helper: Find and extract the (keys: ...) form from formals

Helper: Find and extract the (opts: ...) form from formals

Helper: Find and extract the (rest: ...) form from formals

Helper: Build let bindings for keyword parameters parsed-specs: ((name has-default default kw-obj) ...)

Helper: Build let bindings to extract required positional params Returns (bindings . rest-var-name) where rest-var-name is the variable holding remaining args after required params are extracted

Helper: Build let bindings for optional positional params opt-specs: ((name default) ...) Returns (bindings . rest-var-name)

any-ofprocedure

Return a predicate accepting values satisfying any of the given predicates.

((any-of string? number?) "hi")   ; => #t
((any-of string? number?) 42)     ; => #t
((any-of string? number?) #t)     ; => #f
maybeprocedure

Return a predicate accepting #f or values satisfying pred.

((maybe string?) "hi")   ; => #t
((maybe string?) #f)     ; => #t
((maybe string?) 42)     ; => #f
list-ofprocedure

Return a predicate accepting lists where every element satisfies pred.

((list-of number?) '(1 2 3))    ; => #t
((list-of number?) '(1 "a" 3))  ; => #f
((list-of number?) '())         ; => #t
none-ofprocedure

Return a predicate rejecting values that satisfy any of the given predicates.

((none-of string? number?) #t)     ; => #t
((none-of string? number?) "hi")   ; => #f
((none-of string? number?) 42)     ; => #f
any?procedure

Always-true predicate for "any type" in specs.

lambda-transformer - The core implementation for extended lambda Used by both 'lambda' and 'fn' macros. Supports extended parameter forms: (opts: (name default) ...) - optional positional parameters (keys: name (name default) ...) - keyword parameters (rest: name) - rest parameter for remaining positional args If no special forms, expands to plain %lambda.

define-transformer - Extended define with function shorthand and keyword support

Handles both simple definitions and function shorthand: (define x 42) => (%define x 42) (define (f x) body...) => (%define (f x) body...) (define (f x (opts: ...)) body...) => (%define f (lambda ...))

Simple function shorthand is preserved so the C compiler can apply docstrings. Extended parameter syntax (opts:, keys:, rest:) requires lambda conversion. The input form's docstring is propagated to the output for doc attachment.

Get the spec annotation from a procedure, or #f if none.

Returns the spec list from a procedure's metadata, or #f if the procedure has no spec annotation.

(define (add a b)
  (: number? number? -> number?)
  (+ a b))
(procedure-spec add)  ; => (number? number? -> number?)