Skip to content

Builtins

mar edited this page May 28, 2023 · 7 revisions

Builtins are divided into two categories:

  • Builtin functions, which are globally available functions.
  • Builtin methods, which are methods callable on instances of the harlock types.

Table of Contents

Builtins array methods map methods set methods Hex File methods Elf File methods Bytes File methods
hex array.push map.set set.add hex.record elf.has_section bytes.read_at
len array.pop map.pop set.remove hex.size elf.sections bytes.write_at
set array.slice hex.read_at elf.section_address
type array.map hex.write_at elf.section_size
open array.reduce hex.binary_size elf.read_section
save elf.write_section
print
as_bytes
contains
hash
int
error
as_array

Builtin Functions

The following is a list of the builtin function available within the harlock language, alongside with their signatures.

hex

hex(int|array) -> string

Converts an integer or a byte array to a hex-string. When using byte arrays, the hex-string will not have the '0x' prefix.

Usage:

>>> hex(255)
0xff
>>> hex(12000)
0x2ee0
>>> hex([0xca, 0xfe, 0x01, 0x02])
cafe0102

from_hex

from_hex(string) -> array

Converts a hex-string '0x' to an array of bytes

Usage:

>>> from_hex("beefcafe") 
[190, 239, 202, 254]
>>> from_hex("0xbeefcafe") 
>>> from_hex("0XBEEFCAFE") 
[190, 239, 202, 254]
>>> from_hex(hex([0xbe, 0xef, 0xca, 0xfe]))
[190, 239, 202, 254]

len

len(string|array|map|set) -> int

Returns the length of the passed collection type.

Usage:

>>> [len("test"), len([1,2]), len({"a": 1, "b": 2, "c": 3}), len(set(1, 2, 3))]
[4, 2, 3, 3]

set

set(...hashable|iterable) -> set

Builds a set starting from the passed elements.

If one of the elements is iterable, its elements are iterated instead of adding the iterable itself.

Usage:

>>> set(3, 4, [5, 7, "test"], {"c": 0})
set(test, c, 3, 4, 5, 7)

type

type(any) -> string

Returns the type of the object as a string.

Usage:

>>> [type(1), type(type), type(fun(x){}), type("test")]
[Int, Builtin Function, Function, String]

open

open(string, string) -> file

Attempts to open a file with the name of the first argument, with the file type specified by the second argument.

Valid file types are:

  • hex, which creates a new instance of Hex File type.
  • elf, which creates a new instance of Elf File type.
  • bytes, which creates a new instance of Bytes File type.

Usage:

>>> var h = open("small.hex", "hex")
>>> h
:100000000C9438000C9442000C9442000C94420072
:100010000C9442000C9442000C9442000C94420058
:100020000C9442000C9442000C9442000C94420048
:100030000C9442000C9442000C9442000C94420038
:100040000C9442000C9442000C9442000C94420028
:100050000C9442000C9442000C9442000C94420018
:100060000C9442000C9442000C9442000C94420008
:1000700011241FBECFEFD0E1DEBFCDBF0E944400F0
:0E0080000C9445000C940000FFCFF894FFCFC5
:00000001FF
>>>
>>> var e = open("small.elf", "elf")
>>> e
ElfFile(@small.elf) {
  Sections: [ .data .text .comment .note.gnu.avr.deviceinfo .debug_aranges .debug_info .debug_abbrev .debug_line .debug_str .debug_line_str .symtab .strtab .shstrtab ]
}
>>>
>>> var b = open("small.bin", "bytes")
>>> b
12, 148, 56, 0, 12, 148, 66, 0, 12, 148, 66, 0, 12, 148, 66, 0, 12, 148, 66, 0, 12, 148, 66, 0, 12, 148, 66, 0, 12, 148, 66, 0, 12, 148, 66, 0, 12, 148, 66, 0, 12, 148, 66, 0, 12, 148, 66, 0, 12, 148, 66, 0, 12, 148, 66, 0, 12, 148, 66, 0, 12, 148, 66, 0, 12, 148, 66, 0, 12, 148, 66, 0, 12, 148, 66, 0, 12, 148, 66, 0, 12, 148, 66, 0, 12, 148, 66, 0, 12, 148, 66, 0, 12, 148, 66, 0, 12, 148, 66, 0, 12, 148, 66, 0, 12, 148, 66, 0, 12, 148, 66, 0, 17, 36, 31, 190, 207, 239, 208, 225, 222, 191, 205, 191, 14, 148, 68, 0, 12, 148, 69, 0, 12, 148, 0, 0, 255, 207, 248, 148, 255, 207
>>> 

save

save(hex_file|elf_file|bytes_file) -> no return

Saves a previously opened file's contents unto the original file.

Usage:

>>> var b = open("small.bin", "bytes")
>>> save(b)

print

print(...) -> no return

Prints every passed object as a string separated by a space, with a newline character at the end.

Usage:

>>> print("Hello world!")
Hello world!

as_bytes

as_bytes(hex_file|elf_file|bytes_file) -> array

Returns an array containing the passed file as a stream of bytes.

Usage:

>>> var h = open("small.hex", "hex")
>>> as_bytes(h)
[58, 49, 48, 48, 48, 48, 48, 48, 48, 48, 67, 57, 52, 51, 56, 48, 48, 48, 67, 57, 52, 52, 50, 48, 48, 48, 67, 57, 52, 52, 50, 48, 48, 48, 67, 57, 52, 52, 50, 48, 48, 55, 50, 13, 10, 58, 49, 48, 48, 48, 49, 48, 48, 48, 48, 67, 57, 52, 52, 50, 48, 48, 48, 67, 57, 52, 52, 50, 48, 48, 48, 67, 57, 52, 52, 50, 48, 48, 48, 67, 57, 52, 52, 50, 48, 48, 53, 56, 13, 10, 58, 49, 48, 48, 48, 50, 48, 48, 48, 48, 67, 57, 52, 52, 50, 48, 48, 48, 67, 57, 52, 52, 50, 48, 48, 48, 67, 57, 52, 52, 50, 48, 48, 48, 67, 57, 52, 52, 50, 48, 48, 52, 56, 13, 10, 58, 49, 48, 48, 48, 51, 48, 48, 48, 48, 67, 57, 52, 52, 50, 48, 48, 48, 67, 57, 52, 52, 50, 48, 48, 48, 67, 57, 52, 52, 50, 48, 48, 48, 67, 57, 52, 52, 50, 48, 48, 51, 56, 13, 10, 58, 49, 48, 48, 48, 52, 48, 48, 48, 48, 67, 57, 52, 52, 50, 48, 48, 48, 67, 57, 52, 52, 50, 48, 48, 48, 67, 57, 52, 52, 50, 48, 48, 48, 67, 57, 52, 52, 50, 48, 48, 50, 56, 13, 10, 58, 49, 48, 48, 48, 53, 48, 48, 48, 48, 67, 57, 52, 52, 50, 48, 48, 48, 67, 57, 52, 52, 50, 48, 48, 48, 67, 57, 52, 52, 50, 48, 48, 48, 67, 57, 52, 52, 50, 48, 48, 49, 56, 13, 10, 58, 49, 48, 48, 48, 54, 48, 48, 48, 48, 67, 57, 52, 52, 50, 48, 48, 48, 67, 57, 52, 52, 50, 48, 48, 48, 67, 57, 52, 52, 50, 48, 48, 48, 67, 57, 52, 52, 50, 48, 48, 48, 56, 13, 10, 58, 49, 48, 48, 48, 55, 48, 48, 48, 49, 49, 50, 52, 49, 70, 66, 69, 67, 70, 69, 70, 68, 48, 69, 49, 68, 69, 66, 70, 67, 68, 66, 70, 48, 69, 57, 52, 52, 52, 48, 48, 70, 48, 13, 10, 58, 48, 69, 48, 48, 56, 48, 48, 48, 48, 67, 57, 52, 52, 53, 48, 48, 48, 67, 57, 52, 48, 48, 48, 48, 70, 70, 67, 70, 70, 56, 57, 52, 70, 70, 67, 70, 67, 53, 13, 10, 58, 48, 48, 48, 48, 48, 48, 48, 49, 70, 70, 13, 10]

contains

contains(any, array|map|set) -> bool

Returns true if the collection contains the passed object.

Usage:

>>> [contains([1, 2], 1), contains({"k1": 0, "k2": 1}, "k1"), contains(set(4, 5, "t"), "t")]
[true, true, true]

hash

hash(array, string) -> array

Returns an array containing the computed hash of the passed array, using the specified algorithm.

Currently supported algorightms are:

  • sha1
  • sha256
  • md5

Usage:

>>> hash([1, 2, 3], "sha1")
[112, 55, 128, 113, 152, 194, 42, 125, 43, 8, 7, 55, 29, 118, 55, 121, 168, 79, 223, 207]
>>> hash([1, 2, 3], "sha256")
[3, 144, 88, 198, 242, 192, 203, 73, 44, 83, 59, 10, 77, 20, 239, 119, 204, 15, 120, 171, 204, 206, 213, 40, 125, 132, 161, 162, 1, 28, 251, 129]
>>> hash([1, 2, 3], "md5")
[82, 137, 223, 115, 125, 245, 115, 38, 252, 221, 34, 89, 122, 251, 31, 172]

int

int(string) -> int Converts a string representing an integer to an actual integer.

Usage:

>>> [int("12"), int("0xca"), int("0XCA")]
[12, 202, 202]

error

error(...any) -> error

Creates a custom error that can be used in code.

Usage:

>>> error("custom error!")
Runtime Error: custom error! on line 1

as_array

as_array(int, int, string) -> array

Converts an integer to its representation as an array of bytes of specific size and endianness.

Usage:

>>> as_array(1234, 4, "little")
[210, 4, 0, 0]
>>> as_array(0xff, 8, "big")
[0, 0, 0, 0, 0, 0, 0, 255]

array methods

The following is a list of the builtin methods that can be called on instances of the array builtin type, alongside with their signatures. A new array instance can be constructed by using the square braces to list a series of elements, like so:

>>> var a = [1, 2, 3, 4]
>>> a
[1, 2, 3, 4]

array.push

array.push(any) -> array

Adds an element to the tail of the array and returns the new array. The original array remains unchanged.

Usage:

>>> var arr = [1, 9]
>>> arr.push(12)
[1, 9, 12]
>>> arr
[1, 9]
>>> [1, 9].push(12)
[1, 9, 12]

array.pop

array.pop() -> array Removes the last element from the array and returns a copy of the new array.

Usage:

>>> var arr = [10, 2, "test"]
>>> arr.pop()
[10, 2]
>>> arr
[10, 2, test]
>>> [10, 2, "test"].pop()
[10, 2]

array.slice

array.slice(int, int) -> array

Returns a sub-array slicing the original array in the [args[0]:args[1]) interval. This returns a new array and copies each element in the new array. Lists/Maps/Sets/Files are copied as references.

Usage:

>>> var arr = [1, 2, 3, 4]
>>> arr.slice(0, 2)
[1, 2]
>>> arr      
[1, 2, 3, 4]
>>> [1, 2, 3, 4, 5].slice(1, 3)   
[2, 3]

array.map

array.map(function) -> array

Applies the passed function to each element of the array and returns a new array with the modified values.

I.e.: output[n] = function(input[n])

Usage:

>>> [1, 2, 3, 4].map(fun(x) { ret x*2 })
[2, 4, 6, 8]

array.reduce

array.reduce(function [, any]) -> any

Applies the passed function to each element of the array; the first argument gets used as the result of the previous iteration. An accumulator init value can be passed as an optional final argument.

Usage:

>>> [1, 2, 3, 4].reduce(fun(x, y) { ret x+y })
10
>>> [1, 2, 3, 4].reduce(fun(x, y) { ret x+y }, 5)
15

map methods

The following is a list of the builtin methods that can be called on instances of the map builtin type, alongside with their signatures. A new map instance can be constructed by using the curly braces to list a series of comma-separated key-value pairs, expressed through a colon, like so:

>>> var m = {"key": "value", 2: 3, "test": 14}
>>> m
{2: 3, test: 14, key: value}

map.set

map.set(any, any) -> no return

Adds the (arg[0], arg[1]) key value couple to the map. This mutates the map.

Usage:

>>> var m = {"key1": "val1", "key2": 42}
>>> m
{key1: val1, key2: 42}
>>> m.set("key3", 3)
>>> m
{key1: val1, key2: 42, key3: 3}

map.pop

map.pop(any) -> no return

Removes the passed key from the map if it exists. This mutates the map.

Usage:

>>> var m = {"key1": "val1", "key2": 42}
>>> m
{key1: val1, key2: 42}
>>> m.pop("key2")
>>> m
{key1: val1}

set methods

The following is a list of the builtin methods that can be called on instances of the set builtin type, alongside with their signatures. A new set instance can be constructed by using the builtin set function, like so:

>>> var s = set(1, 2, 3)
>>> s
set(1, 2, 3)

set.add

set.add(any) -> no return

Adds the element to the set if it is not already in. This mutates the set.

Usage:

>>> var s = set(1, 2, 3)
>>> s
set(1, 2, 3)
>>> s.add(1)
>>> s
set(1, 2, 3)
>>> s.add(4)
>>> s
set(1, 2, 3, 4)

set.remove

set.remove(any) -> no return

Removes the passed element from the set if it is there. This mutates the set.

Usage:

>>> var s = set(1, 2, 3)
>>> s.remove(4)
>>> s
set(1, 2, 3)
>>> s.remove(3)
>>> s
set(1, 2)

Hex File methods

The following is a list of the builtin methods that can be called on instances of the Hex File builtin type, alongside with their signatures. All the examples listed in this section are based on the output of the avr-gcc compiler, getting fed the following C program:

int main(void) {
  for(;;) {
  }
}

The complete compilation command is the following:

avr-gcc -o build/small.elf build/src/main.c -std=c11 -Wall -Wextra -Werror -mmcu=atmega644 -DF_CPU=1000000 -Os -DNDEBUG  src/main.c
avr-objcopy -j .text -j .data -O ihex build/small.elf build/small.hex

A new Hex File instance can be constructed by using the builtin open function, with the hex modifier.

>>> var h = open("small.hex", "hex")
>>> h
:100000000C9438000C9442000C9442000C94420072
:100010000C9442000C9442000C9442000C94420058
:100020000C9442000C9442000C9442000C94420048
:100030000C9442000C9442000C9442000C94420038
:100040000C9442000C9442000C9442000C94420028
:100050000C9442000C9442000C9442000C94420018
:100060000C9442000C9442000C9442000C94420008
:1000700011241FBECFEFD0E1DEBFCDBF0E944400F0
:0E0080000C9445000C940000FFCFF894FFCFC5
:00000001FF

hex.record

hex.record(int) -> string

Returns the nth record as a string, if it exists and is a valid index, or an error.

Usage:

>>> var h = open("small.hex", "hex")
>>> var rec = h.record(0)
>>> rec
:100000000C9438000C9442000C9442000C94420072
>>> type(rec)
String

hex.size

hex.size(int) -> int

Returns the size of the file as a number of records it contains.

Usage:

>>> var h = open("small.hex", "hex")
>>> h.size()
10

hex.read_at

hex.read_at(int, int) -> array

Attempts to read arg[1] number of bytes starting from arg[0] position. This returns an array containing the data that would be found in the corresponding .bin file obtained from the hex file as a byte stream.

Usage:

>>> var h = open("small.hex", "hex")
>>> h.read_at(1, 3)
[148, 56, 0]
>>> hex(h.read_at(1, 3))
943800
>>> h.read_at(1, 3).map(fun(x) { ret hex(x) })
[0x94, 0x38, 0x00]

hex.write_at

hex.write_at(int, array) -> no return

Attempts to write the contents of the arg[1] byte array to the arg[0] position. This mutates the hex file object but not the copy on disk.

Call the save() function to make the changes persistent.

Usage:

>>> var h = open("small.hex", "hex")
>>> h.read_at(1, 3).map(fun(x) { ret hex(x) })
[0x94, 0x38, 0x00]
>>> h.write_at(1, from_hex("cafe01"))
>>> h.read_at(1, 3).map(fun(x) { ret hex(x) })
[0xca, 0xfe, 0x01]
>>> save(h)

hex.binary_size

hex.binary_size(int) -> int

Returns the size of the file as the actual number of bytes contained in the data section of the data records found within the hex file.

Usage:

>>> var h = open("small.hex", "hex")
>>> h.binary_size()
142

Elf File methods

The following is a list of the builtin methods that can be called on instances of the Elf File builtin type, alongside with their signatures. All the examples listed in this section are based on the output of the avr-gcc compiler, getting fed the following C program:

int data[32] __attribute__((section(".metadata")));

int main(void) {
  for(;;) {
  }
}

The complete compilation command is the following:

avr-gcc -o build/small.elf build/src/main.o -I ./inc -std=c11 -Wall -Wextra -Werror -mmcu=atmega644 -DF_CPU=1000000 -O0 -DDEBUG

A new Hex File instance can be constructed by using the builtin open function, with the elf modifier.

>>> var e = open("small.elf", "elf")
>>> e
ElfFile(@small.elf) {
  Sections: [ .data .metadata .text .comment .note.gnu.avr.deviceinfo .debug_aranges .debug_info .debug_abbrev .debug_line .debug_str .debug_line_str .symtab .strtab .shstrtab ]
}

elf.has_section

elf.has_section(string) -> bool

Returns whether the elf file contains a section with the passed name or not.

Usage:

>>> var e = open("small.elf", "elf")
>>> e.has_section(".metadata")
true
>>> e.has_section(".wrong_name")
false

elf.sections

elf.sections() -> array

Returns an array containing the section header names as strings.

Usage:

>>> var e = open("small.elf", "elf")
>>> e.sections()
[, .data, .metadata, .text, .comment, .note.gnu.avr.deviceinfo, .debug_aranges, .debug_info, .debug_abbrev, .debug_line, .debug_str, .debug_line_str, .symtab, .strtab, .shstrtab]

elf.section_address

elf.section_address(string) -> int Returns the address of the specified section, if it exists.

Usage:

>>> var e = open("small.elf", "elf")
>>> var addr = e.section_address(".metadata")
>>> [addr, hex(addr)]
[8388864, 0x800100]

elf.section_size

elf.section_size(string) -> int

Returns the size of the specified section, if it exists.

Usage:

>>> // Note that an int is 16-bit wide in this example, size = 32 * sizeof(int)
>>> var e = open("small.elf", "elf")
>>> e.section_size(".metadata")
64

elf.read_section

elf.read_section(string) -> array

Attempts to read the contents of the specified section, if it exists, and returns it as a byte array.

Usage:

>>> var e = open("small.elf", "elf")
>>> e.read_section(".metadata")
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

elf.write_section

elf.write_section(string, array, int) -> no return Attempts to write the contents of the arg[1] byte array to the arg[0] section with arg[2] offset. This mutates the elf file object but not the copy on disk.

Call the save() function to make the changes persistent.

Usage:

>>> var e = open("small.elf", "elf")
>>> e.write_section(".metadata", from_hex("cafe01020304"), 0)
>>> e.read_section(".metadata")
[202, 254, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
>>> hex(e.read_section(".metadata"))
cafe0102030400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
>>> save(e)

Bytes File methods

The following is a list of the builtin methods that can be called on instances of the Bytes File builtin type, alongside with their signatures.

bytes.read_at

bytes.read_at(int, int) -> array

Attempts to read arg[1] number of bytes starting from arg[0] position.

This returns an array containing the data that would be found in the corresponding .bin file obtained from the bytes file as a byte stream.

Usage:

>>> var b = open("small.bin", "bytes")
>>> var data = b.read_at(0, 5)
>>> [data, hex(data)]
[[148, 66, 0, 12, 148], 9442000c94]

bytes.write_at

bytes.write_at(int, array) -> no return

Attempts to write the contents of the arg[1] byte array to the arg[0] position. This mutates the bytes file object but not the copy on disk.

Call the save() function to make the changes persistent.

Usage:

>>> var b = open("small.bin", "bytes")
>>> b.read_at(0, 5)
[12, 148, 56, 0, 12]
>>> b.write_at(0, from_hex("cafe010203"))
>>> b.read_at(0, 5)
[202, 254, 1, 2, 3]
>>> hex(b.read_at(0, 5))
cafe010203