grant horwood
Posted on June 28, 2024
a programming language is only ever going to be as good as its standard library. try writing a c
program without, for instance, <string.h>
. 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.
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 patter 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.
https://github.com/Ph0enixKM/Amber/blob/master/src/std/main.ab
🔎 this post was originally written in the grant horwood technical blog
Posted on June 28, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 28, 2024
November 20, 2024