a programming language is only ever going to be as good as its standard library. try writing a c
program without <string.h>
, for instance . it’s not great.
amber’s standard library is not large — it’s only 24 commands, and some of those aren’t yet available in version 1.0 — but it provides a lot of the convenience functionality that we want when scripting. there’s string manipulation, array handling, some file access stuff. all of these functions are, under the hood, just bash commands, but they provide proven and convenient ways to do the things we want to do.
none of the standard library commands are covered in the official documentation. to find out what they do (or even that they exist), you need to read the source code. so, lets’ go over them here so we don’t have to do that.
posts in this series
importing commands from the standard library
amber’s standard commands are not loaded by default. if we want to use one, we first must import it. we do this with (no surprises), the import
command. for instance, to get access to exit
, we would write:
import { exit } from "std"
the import
command must happen before the command itself is called.
once we have imported exit
, we can use it directly:
exit(0)
string commands
the standard library contains most of the functionality we want when dealing with strings. the commands are:
len()
get the length of a string.
import { len } from "std" let somestring = "this is 26 characters long" len(somestring) // 26
note that this is the number of characters in the string (usually), not the number of bytes or the display width of the characters.
import { len } from "std" let japanesestring = "キャラクター"; len(japanesestring) // 6 let chinesestring = "長度" len(chinesestring) // 2 let emojistring = "❤️ 👍🎉" len(emojistring) // 5 because emojis are sometimes two characters
the exception here is with emojis. some emojis count as two characters. that’s just the way it is.
lower()
set all characters in a string to lowercase where possible.
import { lower } from "std" lower("I AM NOT SHOUTING") // i am not shouting lower("hAcKeR cAsE") // hacker case lower("❤️ 👍🎉") // ❤️ 👍🎉
under the hood, this is a call to tr '[:upper:]' '[:lower:]
.
replace()
replace all the characters in a string that meet a pattern with a replacement string.
replace(<original string>, <pattern to match>, <replacement text>)
the pattern can be a regular expression. it is case-sensitive.
import { replace } from "std" replace("to who it may concern", "who", "whom") // to whom it may concern replace("to who it may concern", "[aeiou]", "v") // tv whv vt mvy cvncvrn replace("Foo foo", "foo", "bar") // Foo bar replace("キャラクター", "ラ", "foo") // キャfooクター replace("❤️ 👍🎉", "👍", "thumbsup"); // ❤️ thumbsup🎉
replace_once()
identical to replace()
except that only the first occurrence in a string is replaced.
import { replace_once } from "std" replace_once("bar bar", "bar", "foo") // foo bar
replace_regex()
like replace
, but using sed
for pattern matching and replacement
import { replace_regex } from "std" replace_regex("line with many spaces", " \{1,\}", " ") // line with many space
trim()
remove all whitespace from the beginning and end of a string. handles both spaces and tabs.
import { trim } from "std" let a = trim(" padded line ") // padded line let b = trim("\ttab padded line\t") // padded line let c = trim("\t tab padded line\t ") // padded line echo "{a}END" echo "{b}END" echo "{c}END"
results in
padded lineEND tab padded lineEND tab padded lineEND
trim_left()
identical to trim()
except it only removes whitespace from the left side of the string.
import { trim_left } from "std" let a = trim_left(" padded line ") // padded line let b = trim_left("\ttab padded line\t") // padded line let c = trim_left("\t tab padded line\t ") // padded line echo "{a}END" echo "{b}END" echo "{c}END"
results in
padded line END tab padded line END tab padded line END
trim_right()
identical to trim()
except it only removes whitespace from the right side of the string.
import { trim_right } from "std" let a = trim_right(" padded line ") // padded line let b = trim_right("\ttab padded line\t") // padded line let c = trim_right("\t tab padded line\t ") // padded line echo "{a}END" echo "{b}END" echo "{c}END"
results in:
padded lineEND tab padded lineEND tab padded lineEND
upper()
set all characters in a string to uppercase where possible.
import { upper } from "std" upper("i am shouting") // I AM SHOUTING upper("hAcKeR cAsE") // HACKER CASE upper("❤️ 👍🎉") // ❤️ 👍🎉
under the hood, this is a call to tr '[:lower:]' '[:upper:]
.
user input commands
amber has one function to poll for and return user keyboard input.
input()
takes user input from keyboard.
import { input } from "std" let name = input("enter your name: ") echo name
file commands
amber has a limited number of functions for handling safe file read and write. some functions are defined but do not exist in version 1.0.
dir_exist()
this function is listed in the latest master branch as of 2024-06-26 but is not implemented in amber 1.0.
file_exist()
this function is listed in the latest master branch as of 2024-06-26 but is not implemented in amber 1.0.
file_read()
reads the content of a file and returns it as a string.
note that file_read()
requires either a failed
block or to be declared as unsafe
. in the event of failure to read the file, null is returned.
import { file_read } from "std" // successful file read silent let somecontents = file_read("/tmp/somefile.txt") failed { echo "fail case" } echo somecontents // contents of the file // unsuccessful file read with error handling. returned value is null silent let nocontents = file_read("/no/tmp/somefile.txt") failed { echo "fail case" } echo nocontents // null echo status // 1 // unsuccessful file read with errors suppressed. returned value is null silent unsafe let unsafenocontents = file_read("/no/tmp/somefile.txt") echo unsafenocontents // null echo status // 1
file_write()
writes a string to a file. this function overwrites the contents of the target file. to append a string to a file, use file_append()
.
note that file_write()
requires either a failed
block to be declared as unsafe
.
import { file_write } from "std" let contents = "some contents" file_write("/tmp/foo", contents) failed { echo "could not write to file" } unsafe file_write("/tmp/foo", contents)
file_append()
appends a string to the contents of a file.
import { file_append } from "std" let contents = "more contents" file_append("/tmp/foo", contents) failed { echo "could not write to file" } unsafe file_append("/tmp/foo", contents)
note that file_append()
requires either a failed
block to be declared as unsafe
.
array commands
the standard library has several functions for splitting strings into arrays and combining arrays into strings.
split()
splits a string on a delimiter and returns an array of the parts. delimiters must be one character. strings that do not contain the delimiter return an array of one element containing the original string. works with multibyte characters.
import { split } from "std" // split string by delimiter split("comma,separated,values", ",") // ["comma", "separated", "values"] // split string that does not contain the delimiter split("no commas in string", ",") // ["no commas in string"] // split multi-byte string by delimiter split("❤️ ,👍,🎉", ",") // ["❤️ ","👍","🎉"] // cannot succesfully split string on multi-character delimiter split("andANDseparatedANDstring", "AND") // ["and", null, null, "separated", null, null, "string"]
lines()
splits a string into an array of lines. the delimiter for the split is \n
, however in practice \r\n
will work. blank lines in the input string, ie \n\n
are ignored in the returned array.
import { lines } from "std" // multiple lines using \n let multi_line_string = "line one\nline two\nline three" lines(multi_line_string); // multiple lines using \r\n let multi_line_string = "line one\r\nline two\r\nline three" lines(multi_line_string); // single line string let single_line_string = "one line" lines(single_line_string); // blank lines are ignored let multi_line_string = "line one\n\n\nline two\n\n\nline three" lines(multi_line_string);
reading a file as an array of lines
the lines()
function can be combined with the output of file_read()
to get the contents of a text file as an array of lines. this process must be done in two steps; attempting to combine lines()
with file_read()
in one statement does not provide the expected results.
import { file_read } from "std" import { lines } from "std" // works as expected unsafe let some_contents = file_read("/tmp/somefile.txt"); let some_array = lines(some_contents); loop index, element in some_array { echo "{index} {element}" } // DOES NOT work as expected unsafe let some_array = lines(file_read("/tmp/somefile.txt")) loop index, element in some_array { echo "{index} {element}" }
words()
this function is listed in the latest master branch as of 2024-06-26 but is not implemented in amber 1.0.
join()
joins the elements of an array into a string using a delimiter. the array being joined must be an array of strings. if the delimiter is more than once character, only the first character of the delimter is used.
import { join } from "std" // join with one char delimiter let some_array = ["foo", "bar", "baz"] join(some_array, ",") // foo,bar,baz let some_array = ["❤️ ", "👍", "🎉"] join(some_array, ",") // ❤️ ,👍,🎉 // join with empty delimiter let some_array = ["foo", "bar", "baz"] join(some_array, "") // foobarbaz // DOES NOT work as expected // join with multi-char delimiter let some_array = ["foo", "bar", "baz"] join(some_array, "AND") // fooAbarAbaz // ERROR // join array of non-text items let some_array = [1, 2, 3] join(some_array, ",") // 1st argument 'list' of function 'join' expects type '[Text]', but '[Num]' was given
includes()
tests if an array includes an element.
import { includes } from "std" let some_array = ["foo", "bar", "baz"] echo includes(some_array, "bar") // true echo includes(some_array, "not bar") // false echo includes(some_array, 2) // false
chars()
splits a string into an array of characters.
import { chars } from "std" // string split into characters chars("aeiou") // ["a", "e", "i", "o", "u"] // spaces count as characters chars("ae iou") // ["a", "e", " ", "i", "o", "u"] // multi-byte safe chars("長度") // ["長", "度"] // emojis are sometimes two or more characters. be warned. chars("❤️ 👍🎉") // ["❤", "", "", "👍", "🎉"]
math commands
there are two convenience functions for math and numbers.
sum()
sums all the elements of an array. all array elements must be of type Num
, which includes both integers and floating-point numbers.
import { sum } from "std" // an array of integers let some_array = [1, 3, 5] echo sum(some_array) // 9 // an array of floats let some_array = [2.5, 6.5] echo sum(some_array) // 9 // results can be floats let some_array = [3.1, 6.5] echo sum(some_array) // 9.6 // floats and ints can be mixed because they are both of type Num let some_array = [1, 1.5, 6.5] echo sum(some_array) // 9 // strings not allowed let some_array = [1, "3", 5] echo sum(some_array) // ERROR: Expected array value of type 'Num'
parse()
attempts to convert a string of digits to a Num
value. requires either a failed
block or to be declared as unsafe
. in the event of failure to parse the string, null is returned.
import { parse } from "std" // parse a numeric string to a Num parse("1") failed { // 1 echo "coult not parse" } unsafe echo parse("1") // 1 // cannot parse non-numeric strings unsafe echo parse("a") // null // cannot parse floats unsafe echo parse("19.0") // null
system commands
commands for script and system management.
exit()
terminates the script and returns a status code. status 0 indicates success, all other numbers indicate an error. status must be an integer
import { exit } from "std" // exit successfully exit(0) // exit with an error code exit(2) // ERROR // exit requires text argument exit("custom error message") // 1st argument 'code' of function 'exit' expects type 'Num', but 'Text' was given // ERROR // must be an integer exit(3.4)
has_failed()
accepts a command, runs eval
on it and returns true if the status code of the command is 0, false otherwise.
for scripting, this function almost certainly serves no purpose and, most likely, is used by amber itself for implementing things like the failed
feature.
referencing the std
source
the official documentation currently does not cover the standard libary. to further investigate these commands and discover new ones as they are released, the best option is is refer to the std/main.ab
source file.
co-founder of fruitbat studios. cli-first linux snob, metric evangelist, unrepentant longhair. all the music i like is objectively horrible. he/him.