Standard Library

July 24, 2020 ยท View on GitHub

Standard Library

The standard library is available to all sink scripts, and is native to sink itself for basic execution. These commands are available in all host environments, and always produce the same results.

SectionNamespace
Globals*
Numbernum.*
Integerint.*
Randomrand.*
Stringstr.*
UTF-8utf8.*
Structured Datastruct.*
Listlist.*
Picklepickle.*
Garbage Collectiongc.*

Globals

FunctionDescription
say a, ...Output arguments to stdout
warn a, ...Output arguments to stderr
ask a, ...Prompt the user for input from stdin
exit a, ...Output arguments to stdout and terminate execution in success
abort a, ...Terminate execution in failure using arguments as the error message
isnum aReturns true if a is a number; otherwise false
isstr aReturns true if a is a string; otherwise false
islist aReturns true if a is a list; otherwise false
isnative cmdReturns true if cmd is defined in the host environment
range [start,] stop[, step]Returns a list of numbers in the interval [start, stop)
order a, bCompare a with b according to the sorting precedence (-1, 0, 1)
pick cond, a, bIf cond is true, return a, otherwise return b (short-circuited)
embed 'file'At compile-time, load the contents of 'file' as a string
stacktraceReturn a list of strings with stacktrace information (if available)

Number

Number commands will operate on lists by performing the operation on each element, just like the built-in unary and binary operators.

FunctionDescription
num.abs aAbsolute value of a
num.sign aSign of a (-1, 0, or 1)
num.max a, b, ...Returns the maximum number from all arguments
num.min a, b, ...Returns the minimum number from all arguments
num.clamp a, b, cClamp a to be between b and c
num.floor aRound a down to the nearest integer
num.ceil aRound a up to the nearest integer
num.round aRound a to the nearest integer, 0.5 and above rounds up
num.trunc aRound a towards 0
num.nanNot-a-number value
num.infInfinity value
num.isnan aTests whether a is a NaN value
num.isfinite aTests whether a is a finite value (i.e., not NaN and not infinite)
num.ee (2.718282...)
num.pipi (3.141592...)
num.tautau (2 * pi = 6.283185...)
num.sin aSine of a (radians)
num.cos aCosine of a (radians)
num.tan aTangent of a (radians)
num.asin aArc-sine of a, returns radians
num.acos aArc-cosine of a, returns radians
num.atan aArc-tangent of a, returns radians
num.atan2 a, bArc-tangent of a / b, returns radians
num.log aNatural log of a
num.log2 aLog base 2 of a
num.log10 aLog base 10 of a
num.exp aea
num.lerp a, b, tLinear interpolation from a to b, by amount t
num.hex a, bConvert a to a sink hexadecimal string, 0-padded to b digits
num.oct a, bConvert a to a sink octal string, 0-padded to b digits
num.bin a, bConvert a to a sink binary string, 0-padded to b digits

Integer

Sink only operates on 64-bit floating point numbers, but it's possible to simulate operations on signed 32-bit integers using the int namespace, with appropriate two's-complement wrapping.

Integer commands will operate on lists by performing the operation on each element, just like the built-in unary and binary operators.

FunctionDescription
int.new aRound a to an integer
int.not aBitwise NOT of a
int.and a, b, ...Bitwise AND between all arguments
int.or a, b, ...Bitwise OR between all arguments
int.xor a, b, ...Bitwise XOR between all arguments
int.shl a, bBit-shift left a by b bits
int.shr a, bBit-shift right a by b bits (zero-fill shift)
int.sar a, bBit-shift right a by b bits (sign-extended shift)
int.add a, ba + b
int.sub a, ba - b
int.mul a, ba * b
int.div a, ba / b
int.mod a, ba % b
int.clz aCount leading zeros
int.pop aCount number of bits set (population count, Hamming weight)
int.bswap aByte swap (0x12345678 becomes 0x78563412)

Random

The random number generator is the same on all host environments, defined here. It is fast, simple, and passes many statistical tests. On startup, it is automatically seeded via rand.seedauto.

FunctionDescription
rand.seed aSet the seed of the RNG to a (interpreted as a 32-bit unsigned integer)
rand.seedautoSet the seed of the RNG automatically (likely based on current time)
rand.intRandom 32-bit unsigned integer ranging [0, 232 - 1]
rand.numRandom number ranging [0, 1) (contains 52 bits of randomness)
rand.range [start,] stop[, step]Random element from range defined by start, stop, step
rand.getstateReturns a two item list, each a 32-bit unsigned integer
rand.setstate aRestores a previous state (a should be a two item list of integers)
rand.pick lsPick a random item out of the list ls
rand.shuffle lsShuffle the contents of list ls in place

String

Strings are 8-bit clean, and interpreted as binary data.

FunctionDescription
str.new a, ...Convert arguments to a string (using space as a separator)
str.split a, bSplit a into an array of strings based on separator b
str.replace a, b, cReplace all occurrences of b in string a with c
str.begins a, bTrue if string a begins with string b; false otherwise
str.ends a, bTrue if string a ends with string b; false otherwise
str.pad a, bPads string a with space until it is length b (-b to pad left)
str.find a, b, cFind b in string a starting at c; returns nil if not found
str.rfind a, b, cFind b in string a starting at c and searching in reverse
str.lower aConvert a to lowercase
str.upper aConvert a to uppercase
str.trim aTrim surrounding whitespace from a
str.rev aReverse a
str.rep a, bRepeat string a b times
str.list aConvert a string to a list of bytes
str.byte a, bUnsigned byte from string a at index b (nil if out of range)
str.hash a, bHash string a with seed b (interpretted as 32-bit unsigned int)

Uppercase, Lowercase, Trim

Due to the fact strings are interpreted as binary data, and not unicode strings, the str.lower, str.upper, and str.trim commands are explicitly specified:

  • str.lower will only convert bytes A-Z to a-z (values 65-90 to 97-122).
  • str.upper will only convert bytes a-z to A-Z (values 97-122 to 65-90).
  • str.trim will only remove surrounding whitespace defined as bytes 9 (tab), 10 (newline), 11 (vertical tab), 12 (form feed), 13 (carriage return), and 32 (space).

UTF-8

The utf8 namespace operates on strings (bytes), and only provides some basic commands for encoding and decoding.

Codepoints U+0000 to U+10FFFF are considered valid, with the sole exception of the surrogate characters (U+D800 to U+DFFF). Overlong encodings are rejected as invalid.

FunctionDescription
utf8.valid aChecks whether a is valid UTF-8 (a is string or list of codepoints)
utf8.list aConverts string a (UTF-8 bytes) to a list of codepoints (integers)
utf8.str aConverts a list of codepoints (integers) a to a string (UTF-8 bytes)

Structured Data

FunctionDescription
struct.size tplCalculate the length of string the template specifies (nil for invalid)
struct.str ls, tplConvert data in list ls to a string using tpl as the template
struct.list a, tplConvert string a to a list of data using tpl as the template
struct.isLEReturns true if system is little endian; false otherwise

Structure Templates

Structure templates are lists of numbers, where each number is defined by symbols in the struct namespace, and describe the type of data:

CodeSizeSigned?EndianC Type
struct.U81UnsignedN/Auint8_t
struct.U162UnsignedNativeuint16_t
struct.UL162UnsignedLittleuint16_t
struct.UB162UnsignedBiguint16_t
struct.U324UnsignedNativeuint32_t
struct.UL324UnsignedLittleuint32_t
struct.UB324UnsignedBiguint32_t
struct.S81SignedN/Aint8_t
struct.S162SignedNativeint16_t
struct.SL162SignedLittleint16_t
struct.SB162SignedBigint16_t
struct.S324SignedNativeint32_t
struct.SL324SignedLittleint32_t
struct.SB324SignedBigint32_t
struct.F324N/ANativefloat
struct.FL324N/ALittlefloat
struct.FB324N/ABigfloat
struct.F648N/ANativedouble
struct.FL648N/ALittledouble
struct.FB648N/ABigdouble
struct.str {0x41, 0x42}, {struct.U8, struct.U8}  # => 'AB'
struct.list 'AAAB', {struct.UL32}                # => { 0x42414141 }
struct.list 'AAAB', {struct.UB32}                # => { 0x41414142 }
struct.size {struct.F32, struct.U8, struct.S16}  # => 7 (bytes)
struct.size {'hello'}                            # => nil; template is invalid

The data can be an array of structures, as long as the data length is a multiple of the template size:

struct.str {0x41, 0x42, 0x43}, {struct.U8}         # => 'ABC'
struct.str {1, 2, 3, 4}, {struct.U8, struct.UL16}  # => "\x01\x02\x00\x03\x04\x00"
struct.list 'ABC', {struct.U8}                     # => { 0x41, 0x42, 0x43 }
struct.list 'ABCD', {struct.UL16}                  # => { 0x4241, 0x4443 }
struct.list 'ABCDEF', {struct.U8, struct.UL16}     # => { 0x41, 0x4342, 0x44, 0x4645 }

List

FunctionDescription
list.new a, bCreate a new list of size a, with each element set to b
list.shift lsRemove and return the value at the start of ls
list.pop lsRemove and return the value at the end of ls
list.push ls, bPush b at end of list ls (returns ls)
list.unshift ls, bUnshift b at the start of list ls (returns ls)
list.append ls, ls2Append ls2 at the end of list ls (returns ls)
list.prepend ls, ls2Prepend ls2 at the start of list ls (returns ls)
list.find ls, a, bFind a in list ls starting at b; returns nil if not found
list.rfind ls, a, bFind a in list ls starting at b and searching in reverse
list.join ls, aConvert list ls to a string by joining elements with string a
list.rev lsReverse list ls (returns ls)
list.str lsConvert a list of bytes to a string
list.sort lsSorts the list ls in place using order (returns ls)
list.rsort lsReverse sorts the list ls in place using order (returns ls)

Pickle

The pickle namespace implements serialization and deserialization commands for sink values.

There are two serialization formats: JSON and binary.

The JSON format is possible by mapping lists to arrays and nil to null, but cannot handle referencing lists and is slightly inefficient.

The binary format is compact, fast, and restores list references -- therefore it can safely serialize any sink value, and is the recommended format for serialization. See: Pickle Binary Format.

Note: Pickling completely ignores host user data attached to lists and cannot be used to copy or marshal user objects in the host environment.

Note: pickle.valid does not validate all possible JSON, just the subset that can be correctly deserialized to a sink value.

FunctionDescription
pickle.json aConverts a non-circular sink value a to a serialized string in JSON
pickle.bin aConverts any sink value a to a binary serialized string
pickle.val aConverts a serialized value a (JSON or binary) back to a sink value
pickle.valid aReturns nil if a is invalid, 1 if JSON format, and 2 if binary
pickle.sibling aTrue if a has sibling references
pickle.circular aTrue if a has circular references
pickle.copy aPerforms a deep copy of a (i.e., binary pickles then unpickles)
pickle.json {1, nil}     # => '[1,null]'
pickle.val '[[-1],5]'    # => {{-1}, 5}
pickle.valid '{}'        # => nil, not all of JSON can be converted to sink
pickle.valid '"\u1000"'  # => nil, only bytes in strings are supported ("\u0000" to "\u00FF")
pickle.valid 'null'      # => 1, JSON formatted serialized sink value (`null` maps to `nil`)

Garbage Collection

Note: garbage collection manipulation is only available in certain environments, but the commands always exist and execute without error.

FunctionDescription
gc.getlevelGet the garbage collection level (see below)
gc.setlevel aSet the garbage collection level (see below)
gc.runRun a full cycle of garbage collection right now

GC Level

The garbage collection (GC) level is one of the constants:

  • gc.NONE - Do not perform automatic GC
  • gc.DEFAULT - Perform automatic GC at a reasonable frequency for most applications
  • gc.LOWMEM - Perform automatic GC at a high frequency for lower memory applications

No matter what the level is set to, running gc.run will always perform a full collection.