assert
assert(value: any) -> any
panics if value is falsy
⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⡶⢦⢀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⡔⢫⣮⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⡰⠑⠌⠄⡀⡆⠀⠀⠀⠀⠀⠀⠀⠀⢠⡞⣕⣱⣛⡻⠻⣧⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⣧⠐⠃⡀⢃⡙⡀⠀⠀⠀⠀⠀⠀⡰⣫⡞⢩⡞⠁⡀⠀⣟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⢹⣦⠁⡐⠠⠹⣄⠀⠀⠀⠀⠀⡔⢵⡟⠠⠋⡄⠀⠀⡘⠸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⢻⡀⡇⢢⠀⠘⣇⠀⠀⠀⣰⡫⣻⠀⠀⡸⠀⠀⢘⡒⢼⠠⠒⠚⠻⡍⠋⠛⠒⣶⢤⣀⣀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⡿⠡⡱⡙⢄⠌⢆⠀⢠⠟⡏⢸⡆⠀⠇⠀⣠⣪⠖⣿⠀⠀⠀⡆⠀⠀⠀⠀⠀⠅⢀⡙⢋⠙⠲⡦⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢻⡆⠃⠼⡀⡄⣞⠄⣼⣾⣯⠢⣏⣰⠀⡰⣻⠅⣸⡿⠀⠀⡰⠀⣴⠁⢠⠂⡘⠐⠀⠀⠀⠀⠀⠀⠈⠲⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡆⠄⡖⠅⢳⠎⢹⣿⣿⡾⡢⢻⣾⡸⢠⠇⠤⢫⣧⣇⡖⣷⡺⡫⡆⣡⠎⢀⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⠶⣄⡀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢾⢦⠃⠀⢃⣾⡛⢿⢻⠇⡨⣻⣷⣏⣠⠊⠍⡼⣿⡇⡘⢀⡕⡱⠀⡠⠂⣈⠔⢁⠔⠀⠀⠀⠀⠀⠀⠀⠀⠀⠹⣢⡀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⡶⢀⠄⠔⠀⠘⢻⣧⡟⠇⠘⠳⢨⡟⠁⠠⠊⣸⣿⡏⣰⡧⠛⠀⣤⠏⣠⠞⠁⠔⠀⠀⠀⠈⠀⠀⠀⠀⠀⠀⠀⠀⠀⢣⡄⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⣠⣾⢏⡐⠋⡰⢁⠞⡀⣼⠺⣧⡎⡔⠀⠈⠉⠀⠀⣰⡿⣿⣷⣿⣧⡾⢾⢛⣍⠌⣠⣤⡠⠄⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠱⡄⠀ ⠀⠀⠀⠀⠀⢀⣠⣾⢫⢯⣿⢴⣽⣷⢆⣼⠟⡗⡀⡠⠟⠀⢀⠄⠀⠀⠀⣹⣾⣿⢏⣾⣩⣷⣟⣋⡞⠖⠋⠀⠀⠀⠂⠀⠐⠂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢱⡀ ⠀⠀⠀⠀⢠⡞⣣⣿⢣⣫⡿⣽⠎⢢⠿⢃⠎⠈⡁⠊⠀⡨⠃⠀⠠⢀⡨⠛⣩⣷⣷⠿⣫⡱⠟⠃⠀⠀⣀⡀⠄⠠⠤⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠓ ⠀⠀⠀⢰⡏⣰⢃⠃⣼⡿⣿⠋⠀⠀⠀⠁⠀⠀⠀⢀⠜⠀⠠⠖⠂⠁⠀⠀⠘⣹⣿⡉⠋⣡⠔⠀⠀⠀⡀⠀⠀⠒⠐⠂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⣸⢴⢳⢹⣧⡟⠁⠃⢀⣔⣶⣦⡤⠀⠈⠀⠁⠀⠀⠀⠀⠀⠀⠀⠀⢀⣳⣿⠛⠒⠓⢀⠀⡀⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⢠⡿⢸⣿⡖⣇⣇⠀⢴⣿⣿⣿⣿⣿⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⢯⣾⠟⠃⠀⠔⠁⠈⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⣾⢁⣟⣷⣇⠻⠉⢇⠘⠯⣿⣿⡿⠛⠀⠀⠀⠀⣀⠀⠀⠀⠀⠀⢀⠼⣗⡊⠉⠉⣄⠂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⢸⡿⢟⣟⠫⢏⡆⠀⠨⠟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣷⡞⠶⠶⠟⠛⠄⠐⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⢸⣷⣹⠐⠁⠀⠀⠀⠀⠀⠀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⠞⠀⠌⠐⠑⠦⠀⠀⠂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠈⢾⣿⣿⣴⣤⣧⡶⣦⣤⠐⠂⠤⠄⠀⠄⠀⠀⠀⠀⠀⠀⢀⡋⠉⠉⢤⣌⣈⠢⠀⠠⠤⢤⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠘⣿⣿⣿⣿⣾⣷⣯⢧⠼⡢⠐⠂⠀⠀⠀⠀⠀⠀⠀⢠⠀⠀⠀⠰⠦⣿⠁⠒⠗⣤⡀⠀⢢⡀⠀⠀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠂⠀⠀⠀⠀⠀⠀ ⠀⠀⠈⣿⣿⣆⣻⡿⣹⡀⣦⡚⢦⠁⠁⠀⠀⢠⠀⠐⠀⢐⠀⠀⠈⠂⢄⠙⢧⡍⠑⠠⡉⣷⣤⡠⢭⣆⠱⣕⡖⠂⠀⠀⠀⠀⠀⣤⣿⣷⡯⢍⡓⣤⠀⠀⠀⠀ ⠀⠀⠀⡘⠫⠝⠿⣿⣜⣧⣰⢎⢄⢡⠀⠐⠂⠀⠀⠀⠄⠠⢀⠦⣀⡐⠳⣦⣈⡌⣗⣄⣿⣮⡿⣿⡷⢿⢷⠕⡤⢧⡀⠀⠀⠀⣶⣿⣯⣈⡇⢷⠨⣢⡁⣦⡀⠀ ⠀⠀⠈⠀⠀⠀⠀⠀⠉⣧⣧⢥⣡⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢩⠏⠻⣮⡹⠦⣵⣼⣿⣿⣿⢿⡜⢾⣜⣦⣿⡜⢿⠢⢵⣤⣷⣿⣿⣏⠻⠿⠦⣄⣟⠁⠀⠈⢁ ⠀⠀⠀⠀⠀⠰⠀⠀⠘⠛⠛⠚⠛⣳⣶⣶⣄⠀⠢⡀⠠⠀⢠⠀⢀⢰⣿⣌⣧⣿⡿⠿⠟⠉⠖⢳⠈⠄⡀⢩⢿⣆⣻⣳⣿⣿⣿⣿⣿⢿⡀⣀⣸⡇⢀⠠⠖⠁ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢿⣿⣿⣿⣿⣷⣿⣦⣶⣿⣤⣾⣾⣿⣿⠟⢁⠀⠀⠀⣰⡆⢀⠰⡏⢱⣾⡶⠿⠛⠋⠁⠀⠀⠀⠈⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⠛⠛⠛⠟⠿⠿⠿⠿⠿⠿⠿⠿⠟⠒⠻⣀⡶⣳⡿⡇⢀⣿⣣⠟⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠐⠂⠤⠏⠀⠄⠒⠋⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
auto-generated from source
you'll see some metamethods here (functions prepended with `__`)
`number.__call("42")` is `number(42)`
`string.__index("qwer", 2)` is `string[2]`
assert(value: any) -> any
panics if value is falsy
assert_eq(a: any, b: any) -> any
panics if values are not equal
debug_info() -> {flags: {dump: bool, trace: bool, instr: bool, stack: bool}, fiber_id: number, pc: number, stack_depth: number, frame_depth: number, program_len: number, has_debug_info: bool, source_name: string | :nil, source: string | :nil, span_count: number, panic_message: string | :nil}
returns debug info as a table
expect(value: any) -> !any
used in tests, returns value or {:err, :ExpectFailed} tuple
expect_eq(a: any, b: any) -> !any
returns value or {:err, :NotEqual} tuple if values are not equal
fmt(format: string, args: any...) -> string
formats a string with format specifiers ~ %v is value (plain) ~ %? is debug (quoted strings, multilined tables) ~ %p is pretty (debug with colors)
string literals also support `#{expr}`, `#{expr:?}`, and `#{expr:p}` interpolation
you likely want to use them instead
get_meta(value: any) -> table | :undef
returns metatable of value or :undef
hash(val: any) -> number
hashes any value
input(?opts: os.InputOpts...) -> !string
reads one line from stdin, or until a custom delimiter (?opts: { delimiter: string|:nil|:eof })
inspect(value: any) -> any
prints one value and returns it back
len(value: any) -> number
returns length of string or table
panic(args: any...) -> never
panics with given message
print(args: any...) -> :ok
prints values to stdout with space separator
set_debug(flags: {dump: bool?, trace: bool?, instr: bool?, stack: bool?}) -> :ok
sets debug flags from a table (all fields default to :false)
set_meta(value: any, meta: table | :undef) -> any
sets metatable of value
sleep(ms: number) -> :parked
sleeps current fiber for given milliseconds
system(args: table<string>) -> !{string, string}
runs a subprocess and returns (stdout, stderr)
to_iter(obj: any) -> function
wraps any iterable in a zero-arg callable built-in types (string, table) get a position-based iterator functions return as-is (already callable) tables with __iter metamethod call __iter(obj)
typeof(value: any) -> atom
returns type of value as atom: nil, number, string, atom, function, table, opaque, resource
command-line arguments parsing
(value)
alias for ```revo {err: argparse.ResultMsg?, flags: table<string, string | bool> | :nil, commands: table<string, bool> | :nil, positionals: table<string, string> | :nil, leftover: table<string> | :nil} ```
(value)
alias for ```revo {token: string, message: string} ```
a parse error, token plus its message
argparse.parse(callback: function, argv: table) -> argparse.Opts
Usage: argparse.parse BUILDER ARGS Parse CLI args with flags, options, commands, and positionals. Subcommands: p:flag(name: atom, opts: table) p:option(name: atom, opts: table) p:command(name: atom, opts: table) p:positional(name: atom, opts: table) All arguments to opts are optional: short string the shorthand for the argument, like -v description string description to be displayed in the usage terminal bool stop parsing after this arg is filled passthrough bool pass remaining args to child prefix bool treat the option as a prefix Examples: ./args.rv ./args.rv -v -o out.txt main.rv ./args.rv compile -v ./args.rv --unknown Output status: result.err: nil | { token = string, message = string } result.flags: table of flag names to values result.commands: table of command names to booleans result.positionals: table of positional names to values result.leftover: table of unconsumed args Report bugs to <https://github.com/if-not-nil/revo/issues>
argparse.usage(result: argparse.Opts) -> string
format a usage string from a parse result
channels! `chan(5)` makes one with a buffer of 5 and `chan()` makes an unbuffered one
chan.__call(capacity: number?) -> resource
creates a channel, an unbuffered one waits for a `recv` on every `send`
chan.recv(self: resource) -> any
receives a value from the channel, parks when it's empty
chan.send(self: resource, value: any) -> :ok
sends value to the channel, parks when it's full
dataframe functions
dataframe.filter(input: table<string, table<any>>, fn: function) -> table<string, table<any>>
filters a dataframe by mapping a predicate function over the rows of the dataframe and keeping only the rows which return :true
dataframe.len(input: table<string, table<any>>) -> number
returns the length of the dataframe, ensuring column lengths are equal
dataframe.map(input: table<string, table<any>>, new_col: string, fn: function) -> table<string, table<any>>
maps a predicate function over the rows of the dataframe to a new column with the given column name
dataframe.row(input: table<string, table<any>>, idx: number) -> table<string, any>
returns the dataframe row at the given index
dataframe.select(input: table<string, table<any>>, columns: table<string>) -> table<string, table<any>>
selects only the given columns for a new dataframe, in the input order
dataframe.sort(input: table<string, table<any>>, fn: function) -> table<string, table<any>>
sorts a dataframe using the result of a given function mapped over the dataframe's rows
you already know what it is (might merge into time, idk)
datetime.today() -> string
returns current date in UTC using ISO 8601 (YYYY-MM-DD) date format
dynamic c calls, posix only
ffi.errno() -> number
errno from the last ffi call on this vm
ffi.func(lib: any, name: string, ret: atom, args: table) -> any
declare a c function, call the handle: return type + arg types as atoms
ffi.load(path: string) -> any
open a shared library, returns a handle
ffi.varfunc(lib: any, name: string, ret: atom, fixed: table, total: number) -> any
declare a variadic c function: fixed prefix types + total count
what you get from `spawn`
fiber.join(self: resource) -> any
parks until the fiber is done and gives you what it returned
file handles
file.close(self: table) -> !atom
closes a file handle table this is currently a logical close for wrapper handles
file.open(path: string, mode: string?) -> !table
wraps a path in a file handle table mode is optional, defaults to "r": "r" opens an existing path, "w" creates or truncates a file, "a" creates or keeps a file use `file.close()` when you're done with the handle
file.read(self: table) -> !string
reads the full file contents as a string
file.stat(self: table, follow: bool?) -> !os.Stat
get file metadata as a table; follow optional, defaults to :true: :false leaves symlinks alone
file.write(self: table, data: string, append: bool?, permissions: number?) -> !number
overwrites the file with the provided string, or appends when append is :true optional permissions default to the platform file default
http
(value)
alias for ```revo {?headers: table<string, http.HeaderValue>, ?body: string, ?max_redirects: number} ```
fetch options
http.fetch(method: :get | :post | :put | :delete | :head | :options | :patch | :trace, url: any, ?opts: http.FetchOpts) -> !string
fetch an http request the default Content-Type is json fetch options are `headers` (key-value table), `body`, and `max_redirects` any fetch option can be `:nil` setting `max_redirects` to a number will follow redirects for the specified count. `:nil` leaves redirection up to the caller. examples:
revo
http.fetch(:get, "https://example.com")
http.fetch(:post, "https://example.com", {
body = "{\"foo\":\"bar\"}",
max_redirects = 3
})
lazy iterators: mapping, filtering, folding and chaining
iter.all?(collection: any, pred: function) -> bool
returns true if function returns true for all elements iter.all?({1, 2, 3}, fn(x) x > 0)
iter.any?(collection: any, pred: function) -> bool
returns true if function returns true for any element iter.any?({1, 2, 3}, fn(x) x > 2)
iter.chunk(collection: any, n: number) -> function
returns a lazy iterator of n-element table chunks iter.collect(iter.chunk({1, 2, 3, 4, 5}, 2)) # {{1, 2}, {3, 4}, {5}}
iter.collect(iterable: any) -> table
collects all values from an iterable into a table iter.collect(iterable)
iter.collect_string(iterable: any) -> string
collects string/number elements from an iterable into a string numbers are clamped to byte values iter.collect_string(iter.filter("hello", fn(c) c != "l"))
iter.count(collection: any, ?pred: function) -> number
returns the number of elements, or of those where pred is truthy iter.count({1, 2, 3, 4}) iter.count({1, 2, 3, 4}, fn(x) x > 2)
iter.drop(collection: any, n: number) -> function
returns a lazy iterator without the first n elements iter.collect(iter.drop({1, 2, 3, 4}, 2))
iter.each(collection: any, fn: function) -> :ok
iterates over elements, calling function for side effects, returns :ok an arity-2 fn receives (value, index/key) iter.each({a = 1}, fn(v, k) print(k, v))
iter.enumerate(collection: any) -> function
returns a lazy iterator of (index, value) pairs iter.collect(iter.enumerate({5, 7})) # {{0, 5}, {1, 7}}
iter.filter(collection: any, fn: function) -> function
returns a lazy iterator that only yields values where fn is truthy an arity-2 fn receives (value, index/key) iter.collect(iter.filter({1, 2, 3, 4}, fn(x) x > 2))
iter.find(collection: any, fn: function) -> any | :nil
returns first element where function returns true, or nil iter.find({1, 2, 3, 4}, fn(x) x > 2)
iter.flat_map(collection: any, fn: function) -> function
maps each element to an iterable and yields its elements lazily iter.collect(iter.flat_map({1, 2}, fn(x) {x, x * 10})) # {1, 10, 2, 20}
iter.flatten(collection: any) -> function
returns a lazy iterator that goes one level into nested tables, other elements pass thru iter.collect(iter.flatten({1, {2, 3}, 4})) # {1, 2, 3, 4}
iter.fold(collection: any, fn: function) -> any | :nil
like reduce but without an initial value; first element seeds the fold returns nil for empty collections iter.fold({1, 2, 3, 4}, fn(acc, x) acc + x)
iter.index_of(collection: any, value: any) -> number | :undef
gives the index of the first element equal to value, or :undef when it's not there iter.index_of({5, 7}, 7) # 1
iter.join(collection: any, ?sep: string) -> string
joins the elements into a string, with the default formatting (same as just `print`) iter.join({1, 2, 3}, ", ") # "1, 2, 3"
iter.map(collection: any, fn: function) -> function
returns a lazy iterator that transforms each element an arity-2 fn receives (value, index/key); materialize with iter.collect() iter.collect(iter.map({1, 2, 3}, fn(x) x * 2))
iter.reduce(collection: any, fn: function, init: any) -> any
folds/accumulates elements using function and initial value iter.reduce({1, 2, 3, 4}, fn(acc, x) acc + x, 0) iter.reduce(iter.map({1, 2}, fn(x) x * 2), fn(acc, x) acc + x, 0)
iter.sum(collection: any) -> number
sums numeric elements, skipping non-numbers iter.sum({1, 2, "x", 3})
iter.take(collection: any, n: number) -> function
returns a lazy iterator of the first n elements iter.collect(iter.take({1, 2, 3, 4}, 2))
iter.unique(collection: any) -> function
returns a lazy iterator that skips elements it already yielded iter.collect(iter.unique({1, 2, 1, 3})) # {1, 2, 3}
iter.zip(first: any, second: any, rest: any...) -> function
returns a lazy iterator of pairs, one element from each iterable takes two or more, stops at the shortest iter.collect(iter.zip({1, 2}, {3, 4})) # {{1, 3}, {2, 4}}
encode values as json and decode json back into revo values
json.decode(source: string) -> !any
decodes json string into revo value
json.encode(value: any, ?pretty: bool) -> !string
encodes value as json string; enabling `pretty` will emit indented json
numeric math
math.abs(x: number) -> number
absolute value
math.acos(x: number) -> number
arc-cosine of x (x in radians)
math.acosh(x: number) -> number
hyperbolic arc-cosine of x (x in radians)
math.asin(x: number) -> number
arc-sine of x (x in radians)
math.asinh(x: number) -> number
hyperbolic arc-sine of x (x in radians)
math.atan(x: number) -> number
arc-tangent of x (x in radians)
math.atan2(y: number, x: number) -> number
arc-tangent of y and x (y and x in radians)
math.atanh(x: number) -> number
hyperbolic arc-tangent of x (x in radians)
math.ceil(x: number) -> number
ceiling of x
math.close?(x: number, y: number, places: number) -> bool
test whether two values are close within the given number of decimal places
math.cos(x: number) -> number
cosine of x (x in radians)
math.cosh(x: number) -> number
hyperbolic cosine of x (x in radians)
math.exp(x: number) -> number
e raised to x
math.floor(x: number) -> number
floor of x
math.hypot(x: number, y: number) -> number
hypotenuse of sides x and y
math.log(x: number) -> number
natural logarithm, panics if x <= 0
math.max(first: number, args: number...) -> number
max of all arguments, at least one required
math.min(first: number, args: number...) -> number
min of all arguments, at least one required
math.pi() -> number
pi, 3.141592653589793
math.pow(base: number, exponent: number) -> number
base raised to exponent
math.sign(value: number) -> number
sign of value (-1, 0, 1)
math.sin(x: number) -> number
sine of x (x in radians)
math.sinh(x: number) -> number
hyperbolic sine of x (x in radians)
math.sqrt(x: number) -> number
square root, errors if x is negative
math.tan(x: number) -> number
tangent of x (x in radians)
math.tanh(x: number) -> number
hyperbolic tangent of x (x in radians)
tcp sockets: connect, listen, accept, send and receive
net.connect(host: string, port: number) -> !table
connects to a remote host and port, returns a socket handle
net.listen(port: number, ?backlog: number...) -> !table
listens for incoming connections on the given port, returns server socket
global number helpers, calllable as `1337:nan?` and `number.nan?(1337)`
number.__call(value: string) -> number
converts value to number: number("12") => 12
number.abs(self: number) -> number
absolute value
number.ceil(self: number) -> number
smallest integer >= self
number.finite?(self: number) -> bool
checks if number is finite
number.floor(self: number) -> number
largest integer <= self
number.inf?(self: number) -> bool
checks if number is infinite
number.nan?(self: number) -> bool
checks if number is NaN
number.round(self: number) -> number
rounds to nearest integer
the machine you're running on: the process, the environment, the clocks and the filesystem
(value)
alias for ```revo {?delimiter: string | :nil | :eof} ```
read input options
(value)
alias for ```revo {size: number, kind: atom, permissions: number, mtime: number, atime: number, ctime: number} ```
file metadata as returned by file.stat and os.stat
os.args() -> table
the command line arguments after the script name, a new table every call
os.chdir(path: string) -> !atom
changes cwd to the given path
os.clock() -> number
returns monotonic clock in milliseconds
os.copy(src: string, dst: string) -> !atom
copies a file to a new path, overwriting the destination
os.cwd() -> string
returns current working directory path
os.env(?name: string) -> any
the value of an environment variable or :nil when it isn't set. without a name it's a table of all of them, so `os.env().HOME`
os.exists?(path: string) -> bool
does path exist? answers :false when missing, raises on unexpected io errors
os.exit(status: number) -> never
exits the program with a status
os.glob(pattern: string, recursive: bool?, depth_limit: number?) -> !table
returns a list of files using pattern matching rules similar to the UNIX shell, if no matches are found then the table will be empty. recursive optional, defaults to :false: :true causes "**" to match all directories and subdirectories until it reaches an endpoint, this can be quite cpu heavy on large directories depth_limit optional, defaults to 100: this is used to set an endpoint where glob must stop matching files after a certain recursion depth.
os.mkdir(path: string, parents: bool?, permissions: number?) -> !atom
creates a directory; parents optional, defaults to :false: :true creates missing parents like `mkdir -p`. permissions optional, platform default when omitted
os.now() -> number
returns current wall-clock time in milliseconds
os.readdir(path: string) -> !table<{name: string, kind: atom}>
returns table of directory entries
os.remove(path: string, recursive: bool?) -> !atom
removes a file or directory at path recursive is optional, defaults to :false: removes a file or empty directory only, :true deletes the whole tree
os.rename(old_path: string, new_path: string) -> !atom
renames a file or directory
os.setenv(name: string, value: string) -> :ok
sets an environment variable, returns :ok
os.stat(path: string, follow: bool?) -> !os.Stat
get file metadata as a table from a path; follow optional, defaults to :true
os.touch(path: string) -> !atom
ensures a file exists, creating it empty when missing and leaving existing entries alone
the revo metaprogramming lib
(value)
alias for ```revo {message: string, phase: atom, code: string | :nil, line: number | :nil, column: number | :nil, source: string | :nil, rendered: string, kind: string | :nil, trace: table} ```
structured diagnostic returned in `{:err, ...}` by functions that run revo code
```
match revo.eval("let x: number = 'hi'") | {:ok, v} => v | {:err, e} => e.message
```
(value)
alias for ```revo {type: string, class: string, text: string, line: number, column: number, start: number, end: number} ```
revo.compile(code: string) -> !string
builds it as a module, gives you back its bytecode in a string the string is only useful for writing to a file or executing build failures come back as {:err, revo.EvalError}
revo.dload(path: string) -> table
opens a dll and gives you back its functions in a table `./x` and `../x` resolve from the calling file, anything else goes to the os
you're meant to type it with a binding
revo
const lib: {add: fn(number, number) -> number} = revo.dload("./lib.so")
revo.eval(code: string) -> !any
evaluates it as a module, gives you back its return value you can treat it as a function's body static and runtime failures come back as {:err, revo.EvalError}
revo.lex(code: string) -> !table<revo.LexToken>
lex source into an array of revo.LexToken tables
revo.load(path: string, opts: table?) -> !any
dynamic `import`! loads the module at `path` and gives you back its value the path is data, so it resolves from the cwd
`name:x` collections work
shares the module table with `import`, `{fresh = true}` reruns it
revo.threads() -> number
worker thread count (see --threads)
revo.version() -> string
version of your revo installation, like "revo v1.2.3"
pseudo-random numbers with a settable seed
rng.choice<T>(input: table<T>) -> T | :nil
Returns a random element from a table, or :nil if empty (note: this function *ignores* key value pairs.)
rng.rand(upper_bound: number) -> number
Returns a random integer in the range [0, upper_bound]
rng.rand_float() -> number
Returns a random float in the range [0, 1]
rng.range(lower_bound: number, upper_bound: number) -> number
Returns a random integer in the range [lower_bound, upper_bound]
rng.revert_seed()
Reverts seed back to using the current time at the time of generation.
rng.set_seed(seed: number)
Statically sets the seed used for generation of pseudo-random numbers.
tcp sockets: connect, listen, accept, send and receive
(value)
alias for ```revo {?mode: :read_some | :read_all | :read_line, ?max_bytes: number, ?delimiter: string} ```
recv options
socket.accept(self: table) -> !table
accepts an incoming client connection on a server socket
socket.close(self: table) -> !atom
closes the socket
socket.recv(self: table, opts: socket.RecvOpts) -> !string
receives data according to opts
socket.send(self: table, data: string) -> !number
sends data over the socket, returns number of bytes sent
statistics functions
stats.frequencies(input: table<any>) -> table<any, number>
counts frequencies of elements in an array table and returns a mapping of elements to their counts
stats.median(input: table<number>) -> number
middle value of input data
stats.mode(input: table<any>) -> any
most frequent occuring value of input data
stats.regression(input1: table<number>, input2: table<number>) -> {n: number, sum_of_products: number, slope: number, intercept: number, correlation: number, covariance: number, sample_covariance: number}
table of regression statistics of the input data
stats.statistics(input: table<number>) -> {n: number, frequencies: table, mean: number, median: number, mode: number, geomean: number, minimum: number, maximum: number, sum: number, sum_of_squares: number, product: number, stdev: number, sample_stdev: number, variance: number, sample_variance: number, skewness: number, sample_skewness: number, kurtosis: number, sample_kurtosis: number}
table of all statistics of the input data
global string helpers, calllable as `"hello world":split(" ")` and `string.split("hello world", " ")`
string.__call(value: any) -> string
converts value to string: string(x)
string.alnum?(self: string) -> bool
checks if all characters in the string are alphanumeric
string.alpha?(self: string) -> bool
checks if all characters in the string are alphabetic
string.ascii(self: string) -> number
returns ASCII code of first character
"a":ascii() => 97
string.concat(self: string, other: string) -> string
concatenates two strings
string.contains?(self: string, substr: string) -> bool
checks if string contains substring
string.ends_with?(self: string, suffix: string) -> bool
checks if string ends with suffix
string.index_of(self: string, substr: string) -> number | :nil
ret 0-based index of substring or nil
string.join(table: table<string>, sep: string) -> string
joins table elements into string with separator
string.len(self: string) -> number
returns length of string
string.lower(self: string) -> string
converts string to lowercase
string.lower?(self: string) -> bool
checks if all characters in the string are lower case
string.of_ascii(code: number) -> string
creates string from ASCII code(s)
of_ascii(97) => "a"
string.punct?(self: string) -> bool
checks if all characters in the string are punctuation
string.repeat(self: string, n: number) -> string
repeats string n times
string.replace(self: string, old: string, new: string) -> string
replaces all occurrences of old with new
string.reverse(self: string) -> string
reverses the string
string.split(self: string, delim: string) -> table<string>
splits string by delimiter into table
string.starts_with?(self: string, prefix: string) -> bool
checks if string starts with prefix
string.table(self: string) -> table<string>
converts string to table of characters
"asdf":table() => {"a", "s", "d", "f"}
string.trim(self: string) -> string
trims whitespace from both ends
string.upper(self: string) -> string
converts string to uppercase
string.upper?(self: string) -> bool
checks if all characters in the string are upper case
string.whitespace?(self: string) -> bool
checks if all characters in the string are whitespace
global table helpers, calllable as `{1, 2}:at(0)` and `table.at({1, 2}, 0)`
table.alen(self: table) -> number
returns length of table array part only
table.at<T>(self: table<T>, index: number) -> T | :undef
gets array element at 0-based index, metatable-ignoring returns :undef when out of range
table.at?(self: table, index: number) -> bool
whether a 0-based index holds an array element, metatable-ignoring
table.contains?(self: table, value: any) -> bool
checks if table contains value
table.copy<T>(self: table<T>) -> table<T>
creates shallow copy of table
table.deep_copy<T>(self: table<T>) -> table<T>
creates a deep copy of the table, duplicating nested tables (cycles map to their in-progress copy)
table.empty?(self: table) -> bool
reports whether the table holds no entries at all
table.freeze<T>(self: T) -> T
makes the table read-only. you probably won't need it, it's mostly for pub records
table.get_meta(self: table) -> table | :undef
returns metatable of value or :undef
table.insert<T>(self: table<T>, pos: number, value: T) -> :ok
inserts value at position, shifting elements right
table.key<V>(self: table<V>, key: any) -> V | :undef
gets map value, metatable-ignoring returns :undef if key missing
table.key?(self: table, key: any) -> bool
whether a key exists, metatable-ignoring
table.keys<K, V>(self: table<K, V>) -> table<K>
returns all keys as table (array indices + hash keys)
table.klen(self: table) -> number
returns length of table keyed entries only
table.last(self: table) -> any
the array part's last element
table.len(self: table) -> number
returns total length of table (array part + keyed entries)
table.merge<K, V>(self: table<K, V>, other: table<K, V>) -> table<K, V>
merges second table into first later values overwrite earlier ones
table.pop<T>(self: table<T>) -> T | :undef
removes and returns the last array element, or :undef if empty
table.push(self: table, values: any...) -> table
inserts elements as last
table.rawget(self: table, key: any) -> any | :undef
gets value without metamethods returns :undef if key missing
table.rawset(self: table, key: any, value: any) -> table
sets value without metamethods
table.remove<T>(self: table<T>, key: any) -> T
removes element at position or key, returns removed value
table.repeat<T>(self: table<T>, n: number) -> table<T>
repeats table array part n times
table.reverse<T>(self: table<T>) -> table<T>
reverses table array part in place
table.set_meta(self: table, meta: :undef | table) -> table
sets the metatable of the table, `:undef` clears it
table.sort<T>(self: table<T>) -> table<T>
sorts table array part in ascending order (numbers < strings)
table.sort_by(self: table, fn: function) -> table
sorts table array part using comparison function fn(a, b) -> bool (true if a < b)
table.unwrap<T>(self: {:ok, T} | table) -> T
unwraps result, panics if not :ok
table.unwrap_err<T>(self: {:err, T} | table) -> T
extracts error from result, panics if not :err
table.update<T>(self: table<T>, key: any, fn: function) -> table<T>
applies fn to the value at key (missing reads as :nil) and stores the result back, returns self
table.values<K, V>(self: table<K, V>) -> table<V>
returns all values as table