I/O
Contains simple I/O primitives.
input(prompt?)
Read a line from standard input, printing the optional argument prompt
if
provided.
var name = io.input("Enter your name> ") io.println("Hello, {name}!")
$ bs demo.bs
Enter your name> Batman
Hello, Batman!
print(...)
Print all the arguments to standard output.
io.print("Hello", "world", 69)
$ bs demo.bs
Hello world 69⏎
The arguments are separated with spaces.
println(...)
Print all the arguments to standard output, with a following newline.
eprint(...)
Print all the arguments to standard error.
eprintln(...)
Print all the arguments to standard error, with a following newline.
readdir(path)
Read a directory into an array of DirEntry
.
Returns nil
if failed.
var entries = assert(io.readdir(".")) for _, e in entries { io.println(e.name(), e.isdir()) }
$ bs demo.bs
.git true
include true
demo.bs false
compile_flags.txt false
thirdparty true
lib true
tests true
.gitattributes false
README.md false
bin true
docs true
build true
.gitignore false
examples true
src true
LICENSE false
.github true
readfile(path, binary?)
Read a file into a string.
If the argument binary
is provided to be true
, then the file is opened in
binary mode.
Returns nil
if failed.
var contents = io.readfile("input.txt") if !contents { io.eprintln("Error: could not read file 'input.txt'") os.exit(1) } io.print(contents)
$ bs demo.bs
Just a test file
Nothing to see here
Foo
Bar
Baz
People's dreams have no end! ~ Blackbeard
Reader(path, binary?)
Native C class that opens path
in readable mode.
If the argument binary
is provided to be true
, then the file is opened in
binary mode.
Returns nil
if failed.
var f = io.Reader("input.txt") if !f { io.eprintln("Error: could not read file!") os.exit(1) } var contents = f.read() io.print(contents)
Create a file input.txt
with some sample text and run it.
$ bs demo.bs
Just a test file
Nothing to see here
Foo
Bar
Baz
People's dreams have no end! ~ Blackbeard
Reader.close()
Close the file.
This is done automatically by the garbage collector, so can be omitted for short programs.
Reader.read(count?)
Read count
bytes from the current position. If the argument count
is not
provided, it reads all of the available bytes.
Returns nil
if failed.
var f = io.Reader("input.txt") if !f { io.eprintln("Error: could not read file!") os.exit(1) } io.print("The first 16 bytes: [{f.read(16)}]\n") io.print("The rest:", f.read())
$ bs demo.bs
The first 16 bytes: [Just a test file]
The rest:
Nothing to see here
Foo
Bar
Baz
People's dreams have no end! ~ Blackbeard
Reader.readln()
Read a line.
var f = io.Reader("input.txt") if !f { io.eprintln("Error: could not read file!") os.exit(1) } while !f.eof() { var line = f.readln() io.println("Line:", line) }
$ bs demo.bs
Line: Just a test file
Line: Nothing to see here
Line: Foo
Line: Bar
Line: Baz
Line: People's dreams have no end! ~ Blackbeard
Line:
Reader.eof()
Return whether the end of file has been reached.
Reader.seek(offset, whence)
Change the read position of the file.
Any of the following values can be used for whence
.
- SEEK_SET
- Seek from beginning of file
- SEEK_CUR
- Seek from current position
- SEEK_END
- Seek from end of file
This function only works if the file was opened in binary mode.
Returns true
if succeeded and false
if failed.
var f = io.Reader("input.txt", true) if !f { io.eprintln("Error: could not read file!") os.exit(1) } io.print("The first 16 bytes: [{f.read(16)}]\n") f.seek(5, io.SEEK_SET) io.println("The full content offset by 5:") io.print(f.read())
$ bs demo.bs
The first 16 bytes: [Just a test file]
The full content offset by 5:
a test file
Nothing to see here
Foo
Bar
Baz
People's dreams have no end! ~ Blackbeard
Reader.tell()
Get the current position of the file.
This function only works if the file was opened in binary mode.
Returns nil
if failed.
var f = io.Reader("input.txt", true) if !f { io.eprintln("Error: could not read file!") os.exit(1) } io.println("The first 3 lines:") for i in 0..3 { io.println(f.readln()) } io.println() io.println("Read {f.tell()} bytes so far.")
$ bs demo.bs
The first 3 lines:
Just a test file
Nothing to see here
Foo
Read 41 bytes so far.
Writer(path, binary?)
Native C class that opens path
in writeable mode.
If the argument binary
is provided to be true
, then the file is opened in
binary mode.
Returns nil
if failed.
var f = io.Writer("output.txt") if !f { io.eprintln("Error: could not create file!") os.exit(1) } f.writeln("Hello there!") f.writeln("General Kenobi!") f.writeln(69, "Nice!")
$ bs demo.bs
$ cat output.txt # type output.txt
on windows
Hello there!
General Kenobi!
69 Nice!
Writer.close()
Close the file.
This is done automatically by the garbage collector, so can be omitted for short programs.
Writer.flush()
Flush the contents of the file, since I/O is buffered in C, which is the language BS is written in.
Writer.write(...)
Write all the arguments into the file.
Returns false
if any errors were encountered, else true
.
Writer.writeln(...)
Write all the arguments into the file, with a following newline.
Returns false
if any errors were encountered, else true
.
DirEntry()
Native C class that wraps over a directory entry. This doesn't really do
anything, and only serves as an implementation detail for readdir()
. Attempt
to call this constructor directly will throw a runtime error.
Example usecase:
var entries = assert(io.readdir(".")) for _, e in entries { io.println(e.name(), e.isdir()) }
$ bs demo.bs
.git true
include true
demo.bs false
compile_flags.txt false
thirdparty true
lib true
tests true
.gitattributes false
README.md false
bin true
docs true
build true
.gitignore false
examples true
src true
LICENSE false
.github true
DirEntry.name()
Get the name of the entry.
DirEntry.isdir()
Get whether the entry is a directory.
stdin
Reader
for standard input.
stdout
Writer
for standard output.
stderr
Writer
for standard error.
OS
Contains simple primitives for OS functions.
exit(code)
Halt the BS runtime with exit code code
.
This doesn't actually exit the process itself in embedded usecase. It just halts the BS interpreter, and the caller of the virtual machine can decide what to do.
os.exit(69)
$ bs demo.bs
$ echo $? # echo %errorlevel%
on windows
69
clock()
Get the monotonic time passed since boot.
This function provides time with nanosecond level of precision but in the unit of seconds.
fn fib(n) { if n < 2 { return n } return fib(n - 1) + fib(n - 2) } var start = os.clock() io.println(fib(30)) var elapsed = os.clock() - start io.println("Elapsed: {elapsed}")
$ bs demo.bs
832040
Elapsed: 0.137143968999226
sleep(seconds)
Sleep for seconds
interval, with nanosecond level of precision.
var start = os.clock() os.sleep(0.69) var elapsed = os.clock() - start io.println("Elapsed: {elapsed}")
$ bs demo.bs
Elapsed: 0.690292477000185
getenv(name)
Get the environment variable name
.
Returns nil
if it doesn't exist.
io.println(os.getenv("FOO")) io.println(os.getenv("BAR"))
$ export FOO=lmao # set FOO=lmao
on windows
$ bs demo.bs
lmao
nil
setenv(name, value)
Set the environment variable name
to value
.
Returns true
if successful, else false
.
os.setenv("FOO", "lmao") io.println(os.getenv("FOO"))
$ bs demo.bs
lmao
getcwd()
Get the current working directory.
io.println(os.getcwd())
$ bs demo.bs
/home/sk/Git/bs
setcwd(dir)
Set the current working directory to dir
.
Returns true
if successful, else false
.
os.setcwd("/usr") io.println(os.getcwd())
$ bs demo.bs
/usr
Process(args, capture_stdout?, capture_stderr?, capture_stdin?, binary?)
Native C class that spawns a process. Expects args
to be an
array of strings that represent the command line arguments.
If any of the optional capture arguments are provided to be true
, then that
corresponding file of the created process will be captured.
If the argument binary
is provided to be true
, then the captured files are
opened in binary mode.
Returns nil
if failed.
var p = os.Process(["ls", "-l"]) if !p { io.eprintln("ERROR: could not start process") os.exit(1) } p.wait()
$ bs demo.bs
total 4
-rw-r--r-- 1 shoumodip shoumodip 122 Oct 6 15:11 demo.bs
Process.stdout()
Get the standard output of the process as an io.Reader
instance.
Returns nil
if the process was spawned without capturing stdout.
var p = os.Process(["ls", "-l"], true) if !p { io.eprintln("ERROR: could not start process") os.exit(1) } var stdout = p.stdout() while !stdout.eof() { var line = stdout.readln() io.println("Line:", line) } p.wait()
$ bs demo.bs
Line: total 4
Line: -rw-r--r-- 1 shoumodip shoumodip 241 Oct 6 15:44 demo.bs
Line:
Process.stderr()
Get the standard error of the process as an io.Reader
instance.
Returns nil
if the process was spawned without capturing stderr.
Process.stdin()
Get the standard input of the process as an io.Writer
instance.
Returns nil
if the process was spawned without capturing stdin.
var p = os.Process(["grep", "foobar"], false, false, true) if !p { io.eprintln("ERROR: could not start process") os.exit(1) } var stdin = p.stdin() stdin.writeln("First line foobar lmao") stdin.writeln("Second line lmao") stdin.writeln("Third line lets goooo foobar") stdin.close() p.wait()
$ bs demo.bs
First line foobar lmao
Third line lets goooo foobar
Process.kill(signal)
Kill the process with signal
.
On windows the process is just killed, since there is no concept of kill signals there.
Returns false
if any errors were encountered, else true
.
Process.wait()
Wait for the process to exit, and return its exit code.
Returns nil
if failed.
args
Array of command line arguments. First element is the program name.
io.println(os.args)
$ bs demo.bs foo bar baz
["demo.bs", "foo", "bar", "baz"]
name
The name of the OS.
io.println(os.name)
$ bs demo.bs
Linux # On linux
macOS # On macOS
Windows # On windows
Unknown # On unknown
The output depends on the OS.
arch
The CPU architecture.
io.println(os.arch)
$ bs demo.bs
x86_64 # On x86_64
ARM64 # On arm64
Unknown # On unknown
The output depends on the architecture.
Regex(pattern)
POSIX compatible regular expressions.
Returns nil
if failed.
var r = Regex("([0-9]+) ([a-z]+)") io.println("69 apples, 420 oranges".replace(r, "\{fruit: '\\2', count: \\1}"))
$ bs demo.bs
{fruit: 'apples', count: 69}, {fruit: 'oranges', count: 420}
String
Methods for the builtin string value.
string.slice(start, end?)
Slice a string from start
(inclusive) to end
(exclusive).
io.println("deez nuts".slice(0, 4)) io.println("deez nuts".slice(5, 9)) io.println("deez nuts".slice(5)) // No end defaults to string end
$ bs demo.bs
deez
nuts
nuts
string.reverse()
Reverse a string.
io.println("sllab amgil".reverse())
$ bs demo.bs
ligma balls
string.repeat(count)
Repeat a string count
times.
io.println("foo".repeat(6))
$ bs demo.bs
foofoofoofoofoofoo
string.toupper()
Convert a string to uppercase.
io.println("Urmom".toupper())
$ bs demo.bs
URMOM
string.tolower()
Convert a string to lowercase.
io.println("Urmom".tolower())
$ bs demo.bs
urmom
string.tonumber()
Convert a string to a number.
io.println("69".tonumber()) io.println("420.69".tonumber()) io.println("0xff".tonumber()) io.println("69e3".tonumber()) io.println("nah".tonumber())
$ bs demo.bs
69
420.69
255
69000
nil
string.find(pattern, start?)
Find pattern
within a string starting from position start
(which defaults
to 0
).
Returns the position if found, else nil
.
io.println("foo bar baz".find("ba")) io.println("foo bar baz".find("ba", 5)) io.println("foo bar baz".find("ba", 9)) io.println("foo bar baz".find("lmao")) var r = Regex("[0-9]") io.println("69a".find(r)) io.println("69a".find(r, 1)) io.println("69a".find(r, 2)) io.println("yolol".find(r))
$ bs demo.bs
4
8
nil
nil
0
1
nil
nil
string.split(pattern)
Split string by pattern
.
io.println("foo bar baz".split("")) io.println("foo bar baz".split(" ")) io.println("foo bar baz".split(" ")) var r = Regex(" +") io.println("foobar".split(r)) io.println("foo bar".split(r)) io.println("foo bar baz".split(r))
$ bs demo.bs
["foo bar baz"]
["foo", "bar", "baz"]
["foo bar baz"]
["foobar"]
["foo", "bar"]
["foo", "bar", "baz"]
string.replace(pattern, replacement)
Replace pattern
with replacement
.
io.println("foo bar baz".replace("", "-")) io.println("foo bar baz".replace(" ", "---")) io.println("foo bar baz".replace(" ", "-")) var r = Regex("([0-9]+) ([a-z]+)") io.println("69 apples, 420 oranges".replace(r, "\{type: \\2, count: \\1}")) io.println("69 apples, 420 oranges".replace(r, "\{type: \\2, count: \\1}")) io.println("ayo noice!".replace(r, "\{type: \\2, count: \\1}"))
$ bs demo.bs
foo bar baz
foo---bar---baz
foo bar ba
{type: apples, count: 69}, {type: oranges, count: 420}
{type: apples, count: 69}, 420 oranges
ayo noice!
string.compare(other)
Compare two strings together.
- 0
means the string is equal to the other string
- 1
means the string is "greater than" the other string
- -1
means the string is "less than" the other string
io.println("foo".compare("bar")) io.println("foo".compare("lol")) io.println("foo".compare("foo")) io.println("foo".compare("food")) io.println("food".compare("foo"))
$ bs demo.bs
1
-1
0
-1
1
string.ltrim(pattern)
Trim pattern
from the left of a string.
io.println("[" $ " foo bar baz ".ltrim(" ") $ "]")
$ bs demo.bs
[foo bar baz ]
string.rtrim(pattern)
Trim pattern
from the right of a string.
io.println("[" $ " foo bar baz ".rtrim(" ") $ "]")
$ bs demo.bs
[ foo bar baz]
string.trim(pattern)
Trim pattern
from both sides of a string.
io.println("[" $ " foo bar baz ".trim(" ") $ "]")
$ bs demo.bs
[foo bar baz]
string.lpad(pattern, count)
Pad string with pattern
on the left side, till the total length reaches
count
.
io.println("foo".lpad("69", 0)) io.println("foo".lpad("69", 3)) io.println("foo".lpad("69", 4)) io.println("foo".lpad("69", 5)) io.println("foo".lpad("69", 6))
$ bs demo.bs
foo
foo
6foo
69foo
696foo
string.rpad(pattern, count)
Pad string with pattern
on the right side, till the total length reaches
count
.
io.println("foo".rpad("69", 0)) io.println("foo".rpad("69", 3)) io.println("foo".rpad("69", 4)) io.println("foo".rpad("69", 5)) io.println("foo".rpad("69", 6))
$ bs demo.bs
foo
foo
foo6
foo69
foo696
string.prefix(pattern)
Check whether string starts with pattern
.
io.println("foobar".prefix("foo")) io.println("foobar".prefix("Foo")) io.println("foobar".prefix("deez nuts"))
$ bs demo.bs
true
false
false
string.suffix(pattern)
Check whether string ends with pattern
.
io.println("foobar".suffix("bar")) io.println("foobar".suffix("Bar")) io.println("foobar".suffix("deez nuts"))
$ bs demo.bs
true
false
false
Bit
Contains bitwise operations which are not frequent enough to warrant a dedicated operator, but which still finds occasional uses.
ceil(n)
Return the bitwise ceiling of a number.
io.println(bit.ceil(7)) io.println(bit.ceil(8)) io.println(bit.ceil(9))
$ bs demo.bs
8
8
16
floor(n)
Return the bitwise floor of a number.
io.println(bit.floor(7)) io.println(bit.floor(8)) io.println(bit.floor(9))
$ bs demo.bs
4
8
8
ASCII
Contains functions for dealing with ASCII codes and characters.
char(code)
Return the character associated with an ASCII code.
code(char)
Return the ASCII code associated with a character.
Expects char
to be a string of length 1
.
isalnum(char)
Return whether a character is an alphabet or a digit.
Expects char
to be a string of length 1
.
isalpha(char)
Return whether a character is an alphabet.
Expects char
to be a string of length 1
.
iscntrl(char)
Return whether a character is a control character.
Expects char
to be a string of length 1
.
isdigit(char)
Return whether a character is a digit.
Expects char
to be a string of length 1
.
islower(char)
Return whether a character is lowercase.
Expects char
to be a string of length 1
.
isupper(char)
Return whether a character is uppercase.
Expects char
to be a string of length 1
.
isgraph(char)
Return whether a character is graphable.
Expects char
to be a string of length 1
.
isprint(char)
Return whether a character is printable.
Expects char
to be a string of length 1
.
ispunct(char)
Return whether a character is a punctuation.
Expects char
to be a string of length 1
.
isspace(char)
Return whether a character is whitespace.
Expects char
to be a string of length 1
.
Bytes(str?)
Strings are immutable in BS. The native class Bytes
provides a mutable string
builder for optimized string modification operations.
var b = Bytes() b.push("Hello, ") b.push("world!") io.println(b) var nice = Bytes("69 Hehe") io.println(nice)
$ bs demo.bs
Hello, world!
69 Hehe
Bytes.count()
Return the current number of bytes written.
var b = Bytes() b.push("Hello") b.push(" world!") io.println(b) var n = b.count() io.println("{n} bytes written.")
$ bs demo.bs
Hello world!
12 bytes written.
Bytes.reset(position)
Move back the writer head to position
.
var b = Bytes() b.push("Hello") var p = b.count() b.push(" world!") io.println(b) b.reset(p) io.println(b)
$ bs demo.bs
Hello world!
Hello
Bytes.slice(start?, end?)
Return a slice from start
(inclusive) to end
(exclusive).
If no arguments are provided to this function, the whole builder is returned as a string.
var b = Bytes() b.push("Hello world!") io.println(b.slice()) io.println(b.slice(0, 5)) io.println(b.slice(6, 12))
$ bs demo.bs
Hello world!
Hello
world!
Bytes.push(value)
Push value
to the end.
var buffer = Bytes() // Operations can be chained buffer .push("Hello") // A String .push(Bytes(", world!")) // Another Bytes instance .push(32) // An ASCII code, in this case ' ' .push($69) // To push the string representation, a string must be provided io.println(buffer)
$ bs demo.bs
Hello, world! 69
Bytes.insert(position, value)
Insert value
at position
.
var buffer = Bytes("world!") // Operations can be chained, just like Bytes.push() buffer .insert(0, "Hell") // A String .insert(4, Bytes(", ")) // Another Bytes instance .insert(4, 111) // An ASCII code, in this case 'o' io.println(buffer)
$ bs demo.bs
Hello, world!
Bytes.get(position)
Get the byte at position
as a number.
var b = Bytes() b.push("Hello") for i in 0..b.count() { var c = b.get(i) io.println(ascii.char(c), c) }
$ bs demo.bs
H 72
e 101
l 108
l 108
o 111
Bytes.set(position, value)
Set the byte at position
to value
.
The argument value
has to be a number.
var b = Bytes() b.push("Cello") io.println(b) b.set(0, ascii.code("H")) io.println(b)
$ bs demo.bs
Cello
Hello
Array
Methods for the builtin array value.
array.map(f)
Functional map.
The provided function f
must take a single argument.
var xs = [1, 2, 3, 4, 5] var ys = xs.map(fn (x) -> x * 2) io.println(xs) io.println(ys)
$ bs demo.bs
[1, 2, 3, 4, 5]
[2, 4, 6, 8, 10]
array.filter(f)
Functional filter.
The provided function f
must take a single argument.
var xs = [1, 2, 3, 4, 5] var ys = xs.filter(fn (x) -> x % 2 == 0) io.println(xs) io.println(ys)
$ bs demo.bs
[1, 2, 3, 4, 5]
[2, 4]
array.reduce(f, accumulator?)
Functional reduce.
The provided function f
must take two arguments. The first
argument shall be the accumulator, and the second shall be the
current value.
var xs = [1, 2, 3, 4, 5] var a = xs.reduce(fn (x, y) -> x + y) io.println(a) var b = xs.reduce(fn (x, y) -> x + y, 10) io.println(b)
$ bs demo.bs
15
25
array.join(separator)
Join the elements of an array, separated by separator
into a single string.
io.println([1, 2, 3, 4, 5].join(" -> "))
$ bs demo.bs
1 -> 2 -> 3 -> 4 -> 5
array.find(value, start?)
Find value
within an array starting from position start
(which defaults to
0
).
Returns the position if found, else nil
.
var xs = [1, 2, 3, 4, 5, 3] io.println(xs.find(3)) io.println(xs.find(3, 3)) io.println(xs.find(3, 6)) io.println(xs.find(true, 6))
$ bs demo.bs
2
5
nil
nil
If you just want to check if a value exists in an array, you can use the in
operator.
var xs = [1, 2, 3, 4, 5, 3] io.println(3 in xs) io.println(6 in xs)
$ bs demo.bs
true
false
array.push(value)
Push value
into an array.
This modifies the array.
var xs = [] for i in 0..5 { xs.push(i * 2) } io.println(xs)
$ bs demo.bs
[0, 2, 4, 6, 8]
Operations can be chained.
io.println( ["Nice!"] .push(69) .push(420))
$ bs demo.bs
["Nice!", 69, 420]
array.insert(position, value)
Insert value
into an array at position
.
This modifies the array.
var xs = [] for i in 0..5 { if i == 2 { continue } xs.push(i * 2) } io.println(xs) xs.insert(2, 4) io.println(xs)
$ bs demo.bs
[0, 2, 6, 8]
[0, 2, 4, 6, 6]
Operations can be chained, like array.push()
io.println( ["Nice!"] .insert(0, 69) .insert(1, 420))
$ bs demo.bs
[69, 420, "Nice!"]
array.pop()
Pop the last element from an array. If the array is empty, an error is thrown.
This modifies the array.
var xs = [1, 2, 3, 4, 5] io.println(xs.pop()) io.println(xs)
$ bs demo.bs
5
[1, 2, 3, 4]
array.sort(compare)
Sort an array inplace with compare
, and return itself.
var xs = [4, 2, 5, 1, 3] xs.sort(fn (x, y) -> x < y) // This also returns the array so you can chain operations io.println(xs)
$ bs demo.bs
[1, 2, 3, 4, 5]
The compare function must take two arguments. If it returns true
, then the
left argument shall be considered "less than", and vice versa for false
.
array.resize(size)
Resize an array to one having size
elements, and return itself.
If size
is larger than the original size, the extra elements shall default to
nil
.
This modifies the array.
var xs = [1, 2, 3, 4, 5] io.println(xs) io.println(xs.resize(3)) io.println(xs)
$ bs demo.bs
[1, 2, 3, 4, 5]
[1, 2, 3]
[1, 2, 3]
array.reverse()
Reverse an array.
This modifies the array.
var xs = [1, 2, 3, 4, 5] io.println(xs) xs.reverse() // This also returns the array so you can chain operations io.println(xs)
$ bs demo.bs
[1, 2, 3, 4, 5]
[5, 4, 3, 2, 1]
array.fill(value)
Fill an array with value
.
This modifies the array.
var xs = [1, 2, 3, 4, 5] io.println(xs) xs.fill(69) // This also returns the array so you can chain operations io.println(xs)
$ bs demo.bs
[1, 2, 3, 4, 5]
[69, 69, 69, 69, 69]
Use this with array.resize()
to create an array with a preset size and value.
var xs = [].resize(5).fill("foo") io.println(xs)
$ bs demo.bs
["foo", "foo", "foo", "foo", "foo"]
Here's a 2D version.
var width = 5 var height = 6 var board = [] .resize(height) .map(fn (x) -> [].resize(width).fill(0)) board[2][2] = 1 io.println(board)
$ bs demo.bs
[
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 1, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0]
]
array.slice(start, end?)
Slice an array from start
(inclusive) to end
(exclusive).
var xs = [1, 2, 3, 4, 5] io.println(xs) io.println(xs.slice(2)) io.println(xs.slice(1, 3))
$ bs demo.bs
[1, 2, 3, 4, 5]
[3, 4, 5]
[2, 3]
array.append(other)
Append an array.
This modifies the array.
var xs = [1, 2, 3, 4, 5] var ys = [6, 7, 8, 9, 10] io.println(xs) io.println(xs.append(ys))
$ bs demo.bs
[1, 2, 3, 4, 5]
[
1,
2,
3,
4,
5,
6,
7,
8,
9,
10
]
Table
Methods for the builtin table value.
table.extend(src, overwrite)
Extend a table.
var xs = { foo = 69, bar = 420 } var ys = { bar = 1337, baz = 42 } var zs = { bar = 420, lol = 420 } io.println(xs) xs.extend(ys, true) // This also returns the table so you can chain operations io.println(xs) xs.extend(zs, false) io.println(xs)
$ bs demo.bs
{
bar = 420,
foo = 69
}
{
baz = 42,
bar = 1337,
foo = 69
}
{
lol = 420,
baz = 42,
bar = 1337,
foo = 69
}
Math
Contains simple mathematical primitives.
number.sin()
Sine in radians.
var theta = 0.5 io.println(theta.sin())
$ bs demo.bs
0.479425538604203
Note that the precision of the mathematical functions may vary depending on the compiler and the platform. This is inherent to computing in general.
number.cos()
Cosine in radians.
number.tan()
Tangent in radians.
number.asin()
Inverse sine in radians.
number.acos()
Inverse cosine in radians.
number.atan()
Inverse tangent in radians.
number.exp()
Return the exponential function of the number.
io.println(2.exp()) // Basically e^2
$ bs demo.bs
7.38905609893065
number.log()
Return the natural logarithm (base e
) of the number.
number.log10()
Return the common logarithm (base 10
) of the number.
number.pow(exponent)
Raise the number to exponent
.
number.sqrt()
Square root.
number.ceil()
Ceiling.
number.floor()
Floor.
number.round()
Return the nearest integer.
number.abs()
Return the absolute value (Basically make a number positive).
number.sign()
Return the sign of the number.
io.println((0).sign()) io.println((69).sign()) io.println((-420).sign())
$ bs demo.bs
0
1
-1
number.max(...)
Return the maximum between the method receiver and the provided arguments.
io.println(1.max(2, 3)) io.println(3.max(0, 1, 2))
$ bs demo.bs
3
3
number.min(...)
Return the minimum between the method receiver and the provided arguments.
io.println(1.min(2, 3)) io.println(3.min(1, 2, 3))
$ bs demo.bs
1
1
number.clamp(low, high)
Clamp the number between low
and high
.
number.lerp(a, b, t)
Linear interpolation.
number.precise(level)
Set the precision (number of decimal digits).
var n = 69.1337 io.println(n) io.println(n.precise(0)) io.println(n.precise(3))
$ bs demo.bs
69.1337
69
69.134
number.tohex()
Format an integer as a hexadecimal string.
io.println(69.tohex()) io.println((-420).tohex())
$ bs demo.bs
45
-1a4
Random(seed?)
Random number generator using the xoroshiro128+
algorithm.
If the seed
argument is not provided, then a random seed is chosen at
runtime.
var a = math.Random() var b = math.Random(1337) io.println(a.number()) io.println(a.number()) io.println(a.number(69, 420)) io.println(a.number(69, 420)) io.println() io.println("==== Constant ====") io.println() io.println(b.number()) io.println(b.number()) io.println(b.number(69, 420)) io.println(b.number(69, 420))
$ bs demo.bs
0.279107155961776
0.73815524476827
225.323320231653
148.438554063034
==== Constant ====
0.0725452046400308
0.811773795162954
416.950161924657
182.365867621578
Random.number(min?, max?)
Return a random number between low
and high
.
If no arguments are provided, a number between 0
and 1
is returned.
var r = math.Random() io.println(r.number()) io.println(r.number()) io.println(r.number(69, 420)) io.println(r.number(69, 420))
$ bs demo.bs
0.279107155961776
0.73815524476827
225.323320231653
148.438554063034
Random.bytes(count)
Return a random sequence of bytes.
var seq = math.Random().bytes(9) io.println(seq.slice())
$ bs demo.bs
{03ah
"7f
range(begin, end, step?)
Return an array containing a range.
If step
is not provided, it is automatically selected.
io.println(math.range(1, 6)) io.println(math.range(6, 1)) io.println(math.range(1, 6, 2)) io.println(math.range(6, 1, -2))
$ bs demo.bs
[1, 2, 3, 4, 5]
[6, 5, 4, 3, 2]
[1, 3, 5]
[6, 4, 2]
If step
is provided such that it would run indefinitely, an error will be
raised.
io.println(math.range(1, 6, -1))
$ bs demo.bs
demo.bs:1:29: error: a step of -1 in an ascending range would run indefinitely
1 | io.println(math.range(1, 6, -1))
| ^
E
Euler's constant.
PI
PI.
Meta
Contains simple metaprogramming primitives.
Error()
Native C class that wraps over a metaprogram error. This doesn't really do
anything, and only serves as an implementation detail for the meta
functions.
Attempt to call this constructor directly will throw a runtime error.
Example usecase:
var f = meta.compile("Oops@") if f is meta.Error { io.println("Row:", f.row()) io.println("Col:", f.col()) io.println("Path:", f.path()) io.println("Line:", f.line()) io.println("Message:", f.message()) io.println("Explanation:", f.explanation()) io.println("Example:", f.example()) }
$ bs demo.bs
Row: 1
Col: 5
Path: <meta>
Line: Oops@
Message: invalid character '@' (64)
Explanation: nil
Example: nil
Error.row()
Return the row in which the error occured.
Returns nil
if the error occured in native code.
Error.col()
Return the column in which the error occured.
Returns nil
if the error occured in native code.
Error.path()
Return the path in which the error occured.
Returns nil
if the error occured in native code.
Error.line()
Return the line of meta source code in which the error occured as a string.
Returns nil
if the error occured in native code.
Error.message()
Return the error message.
Error.explanation()
Return the explanation associated with the error.
Returns nil
if there is no explanation associated with the error.
Error.example()
Return the example associated with the error.
Returns nil
if there is no example associated with the error.
compile(str)
Compile a string into a function.
var f = meta.compile("34 + 35") io.println(f())
$ bs demo.bs
69
If any errors were encountered while compiling the string, an Error
instance
is returned instead of a function.
var f = meta.compile("Oops@"); assert(f is meta.Error) io.println("Row:", f.row()) io.println("Col:", f.col()) io.println("Path:", f.path()) io.println("Line:", f.line()) io.println("Message:", f.message()) io.println("Explanation:", f.explanation()) io.println("Example:", f.example())
$ bs demo.bs
Row: 1
Col: 5
Path: <meta>
Line: Oops@
Message: invalid character '@' (64)
Explanation: nil
Example: nil
So the usage becomes as straight forward as:
var f = meta.compile(...) if f is meta.Error { panic(f.message()) // Error handling... } f() // Or whatever you want to do
The function is compiled such that the last expression in the body is returned,
otherwise defaulting to nil
.
var f = meta.compile({{ for i in 0..5 { io.println('Nice!') } }}) assert(f !is meta.Error) io.println(f()) var g = meta.compile({{ for i in 0..5 { io.println('Hehe!') } 69 }}) assert(g !is meta.Error) io.println(g()) var h = meta.compile({{ for i in 0..5 { io.println('Bruh!') } return 420 }}) assert(h !is meta.Error) io.println(h())
$ bs demo.bs
Nice!
Nice!
Nice!
Nice!
Nice!
nil
Hehe!
Hehe!
Hehe!
Hehe!
Hehe!
69
Bruh!
Bruh!
Bruh!
Bruh!
Bruh!
420
call(fn, ...args)
Basically a protected call.
fn handle(result) { if result is meta.Error { io.println("ERROR!") io.println("Row:", result.row()) io.println("Col:", result.col()) io.println("Path:", result.path()) io.println("Line:", result.line()) io.println("Message:", result.message()) io.println("Explanation:", result.explanation()) io.println("Example:", result.example()) } else { io.println("OK!") io.println(result) } } // Ok handle(meta.call(fn (a, b) -> a + b, 34, 35)) io.println() // Error handle(meta.call(fn () -> -nil))
$ bs demo.bs
OK!
69
ERROR!
Row: 22
Col: 27
Path: demo.bs
Line: handle(meta.call(fn () -> -nil))
Message: invalid operand to unary (-): nil
Explanation: nil
Example: nil
eval(str)
Evaluate a string.
io.println(meta.eval("34 + 35")) io.println(meta.eval({{ for i in 0..5 { io.println('Nice!') } }}))
$ bs demo.bs
69
Nice!
Nice!
Nice!
Nice!
Nice!
nil
Errors are thrown as it is.
meta.eval("-nil")
$ bs demo.bs
<meta>:1:1: error: invalid operand to unary (-): nil
1 | -nil
| ^
demo.bs:1:10: in eval()
1 | meta.eval("-nil")
| ^