On this page:
7.1 Type predicates
7.2 General operations
7.3 Interpreter
7.4 Context
7.5 Debugging utilities
7.6 Definitions and builtins
7.7 Lists
7.8 Strings and symbols
7.9 Booleans
7.10 Numbers
7.11 Environment
7.12 Ports
7.13 Places
7.14 Subprocesses
7.15 Filesystem
7.16 Hash tables
7.17 Vectors
7.18 Byte vectors

7 More builtins

This (incomplete) library of builtins unashamedly takes inspiration from the equivalent concepts in Racket and Rust. There are a lot here – this section takes up a third of the entire source file – but not all of them need to be in every Worst implementation, nor must they have the same syntax or semantics. For instance, at the moment, the builtins listed here are quite inconsistent with whether they consume their input arguments or not.

So, if you’re writing your own Worst, feel free to just use these builtins for inspiration. You may like to skim this section.

7.1 Type predicates

Predicates for some of the basic data types.

(define-builtin (char?          s [a #t]) (cons (char? a) s))
(define-builtin (string?        s [a #t]) (cons (string? a) s))
(define-builtin (symbol?        s [a #t]) (cons (symbol? a) s))
(define-builtin (list?          s [a #t]) (cons (list? a) s))
(define-builtin (bool?          s [a #t]) (cons (boolean? a) s))
(define-builtin (number?        s [a #t]) (cons (number? a) s))
(define-builtin (integer?       s [a #t]) (cons (integer? a) s))
(define-builtin (exact?         s [a #t]) (cons (exact? a) s))
(define-builtin (rational?      s [a #t]) (cons (rational? a) s))
(define-builtin (float?         s [a #t]) (cons (double-flonum? a) s))
(define-builtin (eof-object?    s [a #t]) (cons (eof-object? a) s))

7.2 General operations

(define-builtin (clone s [a #t]) (cons a s))
(define-builtin (drop s [a #t]) (cdr s))
(define-builtin (swap s [a #t] [b #t]) (list* b a (cddr s)))
(define-builtin (rot s [a #t] [b #t] [c #t]) (list* b c a (cdddr s)))
(define-builtin (equal? s [a #t] [b #t]) (cons (equal? a b) s))
(define-builtin (identical? s [a #t] [b #t]) (cons (eq? a b) s))

7.3 Interpreter

(define-builtin (interpreter-stack s) (cons s s))
  (interpreter-exit ctx stack)
  (exit (stack-top stack byte?)))

7.4 Context

  (current-context-root? ctx stack)
  (values ctx (cons (not (context-parent ctx)) stack)))
  (current-context-clear ctx stack)
  (values (make-context #:parent (context-parent ctx)) stack))
  (current-context-has-definition? ctx stack)
  (values ctx
          (cons (hash-has-key? (context-definitions ctx)
                               (stack-top stack symbol?))
  (current-context-definitions ctx stack)
  (values ctx
          (cons (context-definitions ctx)
  (current-context-set-code ctx stack)
  (let ([v (stack-top stack list?)])
    (values (struct-copy context ctx [body v])
            (cdr stack))))
  (current-context-get-code ctx stack)
  (let ([v (stack-top stack list?)])
    (values (struct-copy context ctx [body v])
            (cdr stack))))
; getter for current context itself?

7.5 Debugging utilities

  (interpreter-dump-stack ctx stack)
  (eprintf "Stack:\n~S\n" stack)
  (values ctx stack))
(require racket/pretty)
  (interpreter-dump-context ctx stack)
  (pretty-print ctx)
  (values ctx stack))

7.6 Definitions and builtins

  (builtin-get ctx stack)
  (values ctx
          (cons (hash-ref (*builtins*) (stack-top stack symbol?) #f)
                (cdr stack))))
  (definition-get ctx stack)
  (values ctx
          (cons (hash-ref
                  (context-definitions ctx)
                  (stack-top stack symbol?) #f)
  (definition-resolve ctx stack)
  (let ([name (stack-top stack symbol?)])
    (values ctx (cons (context-resolve ctx name) stack))))
  (definition-add ctx stack)
  (let* ([name (stack-top stack symbol?)]
         [def (stack-top (cdr stack) function?)]
         [defs (hash-set (context-definitions ctx) name def)])
      (struct-copy context ctx [definitions defs])
      (cddr stack))))
;    definition-exists?
;    defined-names
;    all-builtins

7.7 Lists

(define-builtin (list-empty? s [a list?]) (cons (empty? a) s))
(define-builtin (list-length s [a list?]) (cons (length a) s))
(define-builtin (list-reverse s [a list?]) (cons (reverse a) (cdr s)))
(define-builtin (list-append s [b list?] [a list?]) (cons (append a b) (cddr s)))
(define-builtin (list-push s [v #t] [a list?]) (cons (cons v a) (cddr s)))
(define-builtin (list-pop s [a list?]) (list* (car a) (cdr a) (cdr s)))
(define-builtin (list-split s [v Nonnegative-Integer?] [l list?])
                (let-values ([(a b) (split-at l v)])
                  (list* a b s)))
(define-builtin (list-get s [n Nonnegative-Integer?] [a list?])
                (cons (list-ref a n) s))
(define-builtin (list-set s [v #t] [n Nonnegative-Integer?] [a list?])
                (cons (list-set a n v) (cdddr s)))

7.8 Strings and symbols

  (string-append s [a string?] [b string?])
  (cons (string-append b a)
        (cddr s)))
(define-builtin (string-length s [a string?]) (cons (string-length a) s))
(define-builtin (->string s [a #t]) (cons (~a a) (cdr s)))
; string-char-boundary? string-get string->list string-pop string-push
; string->symbol symbol? symbol->string

7.9 Booleans

(define-builtin (and s [a #t] [b #t]) (cons (and a b) s))
(define-builtin (or s [a #t] [b #t]) (cons (or a b) s))
(define-builtin (false? s [a #t]) (cons (false? a) s))
(define-builtin (not s [a #t]) (cons (false? a) (cdr s)))

7.10 Numbers

(define-builtin (add s [b number?] [a number?]) (cons (a . + . b) (cddr s)))
(define-builtin (sub s [b number?] [a number?]) (cons (a . - . b) (cddr s)))
(define-builtin (mul s [b number?] [a number?]) (cons (a . * . b) (cddr s)))
(define-builtin (div s [b number?] [a number?]) (cons (a . / . b) (cddr s)))
(define-builtin (abs s [a real?]) (cons (abs a) (cdr s)))
(define-builtin (negate s [a number?]) (cons (- a) (cdr s)))
; TODO comparison: gt?, lt?, gte?, lte?

7.11 Environment

  (command-line ctx stack)
  (values ctx (cons (vector->list (current-command-line-arguments)) stack)))
  (env-get ctx stack)
  (values ctx
            (getenv (stack-top stack string?))
; env-list : -> listof string (environment variable names)
  (env-set ctx stack)
  (let* ([v (stack-top stack string?)]
         [k (stack-top (cdr stack) string?)]
         [stack (cddr stack)])
    (putenv k v)
    (values ctx stack)))
  (system-resolve-path s [a string?])
  (cons (find-executable-path a) (cdr s)))

7.12 Ports

(define-builtin (port? s [a #t]) (cons (port? a) s))
(define-builtin (input-port? s [a #t]) (cons (input-port? a) s))
(define-builtin (output-port? s [a #t]) (cons (output-port? a) s))
(define-builtin (current-input-port s) (cons (current-input-port) s))
(define-builtin (current-output-port s) (cons (current-output-port) s))
(define-builtin (current-error-port s) (cons (current-error-port) s))
(define-builtin (port-read-value s [a input-port?]) (cons (read a) s))
(define-builtin (port-read-char s [a input-port?]) (cons (read-char a) s))
(define-builtin (port-peek-char s [a input-port?]) (cons (peek-char a) s))
(define-builtin (port-has-char? s [a input-port?]) (cons (char-ready? a) s))
(define-builtin (port-read-line s [a input-port?]) (cons (read-line a) s))
(define-builtin (port-write-value s [v #t] [p output-port?])
                (write v p)
                (cdr s))
(define-builtin (port-write-string s [v string?] [p output-port?])
                (display v p)
                (cdr s))
(define-builtin (port-copy-all s [o output-port?] [i input-port?])
                (copy-port i o)
(define-builtin (make-pipe-ports s)
                (let-values ([(i o) (make-pipe)])
                  (list* i o s)))
; port-read
; port-seekable?
; port-seek/end
; port-seek/relative
; port-seek/start
; port-unique?
; port-write
; output-port-flush

7.13 Places

A place is a mutable storage location capable of storing exactly one item. Multiple copies of a place all reference the same object.

; the name 'place' is taken
(struct mplace ([v : Any])
(define-builtin (place? s [a #t]) (cons (mplace? a) s))
(define-builtin (make-place s [a #t]) (cons (mplace a) (cdr s)))
(define-builtin (place-get s [p mplace?]) (cons (mplace-v p) s))
(define-builtin (place-swap s [v #t] [p mplace?])
                (let ([o (mplace-v p)])
                  (set-mplace-v! p v)
                  (cons o (cdr s))))
(define-builtin (place-set s [v #t] [p mplace?])
                (set-mplace-v! p v)
                (cdr s))

7.14 Subprocesses

Subprocess invocation happens in two parts: first, configure the command to run, including the program, its arguments, environment variables, and input, output, and error pipes. Then spawn the command as a process to run it.

TODO replace command with https://docs.racket-lang.org/reference/subprocess.html

; untested
(struct command
  ([program : Path-String]
   [arguments : (Listof (U Bytes Path-String))]
   ; TODO cd?
   [stdin-port : (Option Input-Port)]
   [stdout-port : (Option Output-Port)]
   [stderr-port : (Option Output-Port)]
   [environment : (HashTable String String)])
  #:type-name Command
(define-builtin (command? s [a #t]) (cons (command? a) s))
(define-builtin (make-command s [program (make-predicate (U Path String))])
                (cons (command program '() #f #f #f (hash)) (cdr s)))
  (command-set-arguments s
                         [args (make-predicate (Listof (U Bytes Path-String)))]
                         [cmd command?])
  (cons (struct-copy command cmd [arguments args]) (cddr s)))
  (command-set-env s [env hash?] [cmd command?])
  (let ([env (cast env (HashTable String String))])
    (cons (struct-copy command cmd [environment env]) (cddr s))))
  (command-set-stdin s [p input-port?] [cmd command?])
  (cons (struct-copy command cmd [stdin-port p]) (cddr s)))
  (command-set-stdout s [p output-port?] [cmd command?])
  (cons (struct-copy command cmd [stdout-port p]) (cddr s)))
  (command-set-stderr s [p output-port?] [cmd command?])
  (cons (struct-copy command cmd [stderr-port p]) (cddr s)))
  ; process is taken
  ([subprocess : Subprocess]
   [stdin : (Option Output-Port)]
   [stdout : (Option Input-Port)]
   [stderr : (Option Input-Port)])
  #:type-name Process)
(define-builtin (process? s [a #t]) (cons (proc? a) s))
(define-builtin (process-stdin-port s [p proc?])
                (cons (proc-stdin p) s))
(define-builtin (process-stdout-port s [p proc?])
                (cons (proc-stdout p) s))
(define-builtin (process-stdout-port s [p proc?])
                (cons (proc-stdout p) s))
(define-builtin (process-pid s [p proc?])
                (cons (subprocess-pid (proc-subprocess p)) s))
(define-builtin (process-running? s [p proc?])
                (cons (eq? (subprocess-status (proc-subprocess p))
                           'running) s))
(define-builtin (process-kill s [p proc?])
                (subprocess-kill (proc-subprocess p) #t)
                (cdr s))
(define-builtin (process-wait s [p proc?])
                (subprocess-wait (proc-subprocess p))
                (cons (subprocess-status (proc-subprocess p)) s))
  (command-spawn s [cmd command?])
  (let-values ([(subp stdout stdin stderr)
                (apply subprocess
                       (command-stdout-port cmd)
                       (command-stdin-port cmd)
                       (command-stderr-port cmd)
                       (command-program cmd)
                       (command-arguments cmd))])
      (proc subp stdin stdout stderr)
      (cdr s))))

7.15 Filesystem

; name tbc
(define-builtin (open-input-file s [f string?])
                (cons (open-input-file f) (cdr s)))

TODO. Doesn’t have to be as complicated as the Rust version. file? file-info file-info? file-info-directory? file-info-file? file-info-length file-info-readonly? file-info-symlink? file-port file-sync make-open-file-options open-file open-file-append open-file-create open-file-create-new open-file-read open-file-truncate open-file-write

7.16 Hash tables

Immutable dictionaries based on Racket’s hashtable. If you want a mutable hash-table, put it in a place.

(define-builtin (hash-table? s [h #t]) (cons (hash? h) s))
(define-builtin (hash-table-empty s) (cons (hash) s))
(define-builtin (hash-table-keys s [h hash?]) (cons (hash-keys h) s))
  (hash-table-first-key s [h hash?])
  (let* ([it (hash-iterate-first h)]
         [key (and it (hash-iterate-key h it))])
    (cons key s)))
(define-builtin (hash-table-exists s [k #t] [h hash?])
                (cons (hash-has-key? h k) s))
(define-builtin (hash-table-get s [k #t] [h hash?])
                (cons (hash-ref h k #f) s))
(define-builtin (hash-table-set s [v #t] [k #t] [h hash?])
                (let ([h (cast h (HashTable Any Any))])
                  (cons (hash-set h k v) (cdddr s))))
(define-builtin (hash-table-take s [k #t] [h hash?])
                (let ([v (hash-ref h k #f)]
                      [tbl (hash-remove h k)]
                      [s (cddr s)])
                  (list* v k tbl s)))

7.17 Vectors

TODO? Vectors are like lists, but mutable/double-ended/constant-sized?

7.18 Byte vectors

Bytes is a vector of... bytes. TODO rename from u8vector.

string->u8vector make-u8vector ; or bytes-empty u8vector? u8vector-append u8vector-extend u8vector-get u8vector-invalid-char-index u8vector-length u8vector-push u8vector-set u8vector->string u8vector-truncate