title | date |
---|---|
Boron User Manual |
Version 2.0.8, 2022-04-25 |
Boron is an interpreted, prototype-based, scripting language.
Language features include:
- Garbage collected datatype system with prototype based objects.
- Written in C to work well as an embedded scripting language.
- Small (but not tiny) binary & run-time environment.
This manual is largely incomplete.
There is a separate function reference and code documentation available online at http://urlan.sourceforge.net/boron.
Scripts are UTF-8 encoded text files.
Single line comments begin with a semi-colon.
Block comments are the same as C block comments. They begin with '/*
' and
continue through '*/
'. Block comments cannot be nested.
Comment examples:
; line comment
add 2 4 ; result is 6
/*
Block comment
*/
The first line of a script may be a UNIX shell sha-bang (#!
) command.
#!/usr/bin/boron
Usage:
boron [options] [script] [arguments]
-e "exp" Evaluate expression -h Show help and exit -p Disable prompt and exit on exception -s Disable security
If the interpreter is invoked with a script then the args word will be set to either a block of strings, or none if no script arguments were given.
So this Boron command:
boron -e "probe args" file1 -p 2
Will print this:
["file1" "-p" "2"]
Datatype Examples
unset! datatype! logic! int!/double! none! none logic! true false word! hello focal-len .s lit-word! 'hello 'focal-len '.s set-word! hello: focal-len: .s: get-word! :hello :focal-len :.s option! /hello /focal-len /.s char! 'a' '^-' '^(01f3)' int! 1 455 -22 double! 3.05 -4. 6.503e-8 coord! 0,255,100 -1, 0, 0 vec3! 0.0,255.0,100.0 -1.0, 0, 0 string! "hello" {hello} file! %main.c %"/mnt/Project Backup/" binary! #{01afed} #{00 33 ff a0} 2#{00010010} bitset! make bitset! "abc" time! 10:02 -0:0:32.08 vector! #[1 2 3] #[-85.33 2 44.8] i16#[10 0 -4 0] block! [] [a b c] paren! () (a b c) path! obj/x my-block/2 lit-path! 'obj/x 'my-block/2 set-path! obj/x: my-block/2: context! context [area: 4,5 color: red] hash-map! make hash-map! [area 4,5 "color" red] error! func! inc2: func [n] [add n 2] cfunc! port!
Unset is used to indicate that a word has not been assigned a value.
A value which represents a type or set of types. To declare a type use it's
type word, which have names ending with an exclamation (!
) character.
To declare a set use a series of type words separated by slashes (/
).
none!
char!/int!/double!
None is a type used to denote nothing. This is often a return value of functions which search a series but find no matches.
)> select [a b] 'c
== none
Most conditional functions treat none as a false logic! value.
A boolean value of true or false.
The words yes & no are defined as synonyms for true & false.
)> yes
== true
A Unicode character. A char! can be specified with either a UTF-8 character
between two single quotes, or an ASCII caret (^
) sequence between two single
quotes.
The following caret sequences can be used:
Sequence Character Value
^-
Tab, 0x09
^/
New line, 0x0A
^^
Caret, 0x5E
^0
- ^F
Hexidecimal nibble, 0x00 - 0x0F
^(xxxx)
Hexidecimal number, 0x0000 - 0xFFFF
For example, a new line character could be declared in any of the following ways:
'^/' '^a' '^(0A)'
A 64-bit integer number.
Integers can be specified in decimal, or if prefixed with 0x
, as hexadecimal.
Example integers:
24
0x1e
A 64-bit floating point number.
Example double values:
-3.5685
24.
6.503e-8
Integer coordinate that is handy for specifying screen positions, rectangles, colors, etc.
A coord! can hold up to six 16-bit integers.
640,480 ; Screen size
45,10, 45,18 ; Rectangle
255,10,0 ; RGB triplet
Vec3 is a simple value that stores three floating point values.
A Vec3 is specified as two or three decimal numbers separated by commas. If none of the numbers has a decimal point then the value will be a coord!.
0.0, 1.0 ; Third component will be 0.0
1.0,0,100
A word is a series of ASCII characters which does not contain white space. The first character must not be a digit. All other characters may be alpha-numeric, mathematical symbols, or punctuation. Case is ignored in words.
Example words:
app_version
_60kHz_flag
MTP-3
>
A literal word is a variant of a word! that begins with a single quote ('
)
character.
It evaluates to a word! value rather than the value from any bound context.
)> 'sleep
== sleep
A set-word value is a variant of a word! that ends with a colon (:
)
character.
It is used to assign a value to a word.
)> a: 42
== 42
)> a
== 42
A get-word value is a variant of a word! that begins with a colon (:
)
character.
:my-function
It is used to obtain the value of a word without evaluating it further. For many datatypes this will be the same as using the word itself, but for func! values it will suppress invocation of the function.
An option is a variant of a word! that begins with a slash (/
) character.
/outside
An option is inert will evaluate to itself.
A binary value references a series of bytes.
Binary data is specified with hexadecimal values following a hash and
opening brace (#{
) and is terminated with a closing brace (}
).
White space is allowed and ignored inside the braces.
#{0000ff01}
#{0000ff01 0000f000
03ad4480 d17e0021}
)> to-binary "hello"
== #{68656C6C6F}
Alternative encodings for base 2 and base 64 can be used by putting the
base number before the initial hash (#
) character.
)> print to-string 2#{01101000 01100101 01101100 01101100 01101111}
hello
)> print to-string 64#{aGVsbG8=}
hello
Partial base 64 triplets will automatically be padded with equal (=
)
characters.
)> print 64#{aGVsbG8} ; "hello"
64#{aGVsbG8=}
)> print 64#{ZG9vcg} ; "door"
64#{ZG9vcg==}
Use the encode function to change the encoding base.
)> encode 16 2#{11001010 10110010}
== #{CAB2}
Bitset is an array of bits padded out to a multiple of eight bits.
The pick & poke functions can be used to get and set individual bits. Poking a value of none!, false logic!, 0, or 0.0 will clear a bit, while any other value will set it.
)> b: make bitset! 32
== make bitset! #{00000000}
)> poke b 12 true
== make bitset! #{00080000}
The construct and charset functions can also be used to create a bitset!.
Unlike make, these functions will interpret a dash (-
) between characters
in a string! as a range.
c: charset "0-9A-F"
== make bitset! #{000000000000FF037E0000...}
)> pick c 'B'
== true
)> pick c 'G'
== false
Strings are UTF-8 text enclosed with either double quotes ("
) or braces
({}
). They can include the same caret character sequences as char!
values.
Double quoted strings cannot contain a new line character or double quote unless it is in escaped form.
"Alpha Centari"
"First line with ^"quotes^".^/Second line.^/"
There are two brace formats, both of which can span multiple lines in the script.
A single left brace will track pairs of left/right braces before terminating with a single right brace. Other brace combinations must use escape sequences.
Two or more left braces followed by a new line will start the string on the next line and terminate it on the line prior to a line ending with a matching number of right braces. The enclosed text will be automatically unindented.
{This string
has three lines and
will preserve all whitespace.}
{Braces allow "quoting" without escape sequences.}
{{
This is four lines that will be unindented.
Item 1
- Subitem A
- Subitem B
}}
A file value is a string which names a file or directory on the local
filesystem. They begin with a percent (%
) character. If any spaces are
present in the path then it must be enclosed in double quotes.
File examples:
%/tmp/dump.out
%"../Input Files/test42"
%C:\windows\system32.exe
Vectors hold a series of numbers using less memory than a block!. All numbers in a vector are integers or floating point values of the same size.
A 32-bit vector! is specified with numbers following a hash and opening square
bracket (#[
) and are terminated with a closing square bracket (]
).
If the first number contains a decimal point, all numbers will be floating
point, otherwise they will all be integers.
Other types of numbers can be specified with the form name immediately before the hash.
Name C Type
i16 int16_t u16 uint16_t i32 int32_t u32 uint32_t f32 float f64 double
Some vector! examples:
)> a: #[1 2 3 4]
== #[1 2 3 4]
)> b: #[1.0 2 3 4]
== #[1.0 2.0 3.0 4.0]
)> c: i16#[1 2 3 4]
== i16#[1 2 3 4]
)> foreach w [a b c] [v: get w print [w type? last v size? to-binary v]]
a int! 16
b double! 16
c int! 8
A block is a series of values within square brackets.
[1 one "one"]
[
item1: [some sub-block]
item2: [another sub-block]
]
A paren is similar to a block!, but it will be automatically evaluated.
(1 two "three")
A path is a word! followed by one or more word!/get-word!/int! values
separated by slash (/
) characters.
Example paths:
object/order/1
list/:variable
A context holds word/value pairs.
Example context:
entry: make context! [
name: "John"
age: 44
job: 'farmer
]
Contexts can be created from existing ones. So given the previous entry context a new farmer could be created using make again.
joe: make entry [name: "Joe" age: 32]
The set-word! values in the outermost specification block become members of the context, but not those in any inner blocks.
The context word is normally used to make a new context instead of make context!.
unit: context [type: 'hybrid level: 2]
A hash-map holds key/value pairs.
Use make to create a hash-map from a block containing the key & value pairs.
level-map: make hash-map! [
0.0 "Minimum"
0.5 "Average"
1.0 "Maximum"
]
Use pick & poke to get and set values. If the key does not exist pick will return none!.
)> pick level-map 0.0
== "Minimum"
)> pick level-map 0.1
== none
)> poke level-map 0.1 "Slight"
== make hash-map! [
0.0 "Minimum"
0.5 "Average"
1.0 "Maximum"
0.1 "Slight"
]
)> pick level-map 0.1
== "Slight"
Functions can be defined with or without arguments. The return value of a function is the last evaluated expression.
The does word can be used to create a function from a block of code when no arguments or local variables are needed. In the following example 'name is bound to an external context.
hello: does [print ["Hello" name]]
The func word is used when arguments or local variables are required. It is followed by the signature block and code block. Required arguments, optional arguments, local values, and external values are declared in the signature block. Any local values and unused optional arguments are initialized to none!.
; Here is a function with two arguments and one local variable.
my-function: func [arg1 arg2 /local var] [
; var is none! here.
foreach var arg1 [print add var arg2] ; var is set by foreach.
]
The required arguments are word! values at the start of the signature block. Optional arguments are specified after this with option! values followed by zero or more word! values. The /local and /extern options will be last, each followed by one or more word! values.
Any variable assigned in the function with a set-word! value is automatically made a local value. To prevent this and keep the original binding from when the function was created use the /extern option.
append-item: func [item /extern list-size] [
was-empty: empty? obj-list ; was-empty is local.
append obj-list item
list-size: size? obj-list ; list-size is external.
was-empty
]
Arguments can be limited to certain types by following the argument name with a datatype in the signature block. This example includes an optional argument and both the required and optional arguments are type checked.
play-music: func [path file! /volume loudness int!/double!] [
if volume [
set-audio-volume loudness
]
play-audio-file path
]
Optional arguments are used by calling the function with a path!. If there are multiple options the order in the path does not matter, but this path order does indicate the order in which any option arguments must appear. The play-music function above can be called two ways:
play-music %/data/interlude.ogg ; Invoke without option.
play-music/volume %/data/interlude.ogg 0.5 ; Invoke with /volume option.
This type is for the built-in functions written in C. See the function reference for the available functions.
Ports are a general interface for various input/ouput devices.
The open and close functions create and destroy ports. The read and write functions are used to recieve and send data.
To use stdin
, stdout
, and stderr
streams use open with the
integer 0, 1, or 2.
To read commands from stdin:
t: open 0
cmd: ""
forever [
wait t
read/into t cmd
if eq? cmd "quit^/" [break]
print cmd
]
Here is a simple TCP server which sends clients a message:
s: open "tcp://:6044"
forever [
con: read wait s
write con "Hello, client.^/"
close con
]
And the client:
s: open "tcp://localhost:6044"
print to-string read s
close s
The parse function can operate on strings, blocks, and binary values. It returns true if the end of the input is reached.
Rule-Statement Operation
| Start an alternate rule. any <val> Match the value zero or more times. break Stop the current sub-rule as a successful match. into <rules> Parse block at current input position with a new set of rules. opt <val> Match the value zero or one time. place <ser> Set the current input position to the given series position. set <word> Set the specified word to the current input value. skip Skip a single value. some <val> Match the value one or more times. thru <val> Skip input until the value is found, then continue through it. to <val> Skip input until the value is found. int! <val> Match a value an exact number of times. int! int! <val> Match a value a variable number of times. int! skip Skip a number of values. block! Sub-rules. datatype! Match a single value of the given type. paren! Evaluate Boron code. set-word! Set word to the current input position. get-word! Set slice end to the current input position. lit-word! Match the word in the input.
Rule-Statement Operation
| Start an alternate rule. any <val> Match the value zero or more times. bits block! Parse bit fields. break Stop the current sub-rule as a successful match. opt <val> Match the value zero or one time. place <ser> Set the current input position to the given series position. skip Skip a single character. some <val> Match the value one or more times. thru <val> Skip input until the value is found, then continue through it. to <val> Skip input until the value is found. int! <val> Match a value an exact number of times. int! int! <val> Match a value a variable number of times. int! skip Skip a number of characters. paren! Evaluate Boron code. set-word! Set word to the current input position. get-word! Set slice end to the current input position.
Value
bitset! Match any character in the set. block! Sub-rules. char! Match a single character. string! Match a string. word! Match value of word.
The bits specification uses the following rules to extract fields of various bit lengths and endianess:
Rule-Statement Operation
big-endian Interpret following unsigned integers as big endian. little-endian Interpret following unsigned integers as little endian. u8 Unsigned 8-bit integer. u16 Unsigned 16-bit integer. u32 Unsigned 32-bit integer. u64 Unsigned 64-bit integer. int! Integer bit field of 1 to 64 bits. set-word! Set word to the next integer or bit field.
The default endianness is little-endian.
The following example parses the 10-byte GZIP header and places the individual file flag bits into separate words:
parse read %some.gz [
'^(1f)' '^(8b)'
bits [
method: u8
3 fcomment:1 fname:1 fextra:1 fcrc:1 ftext:1
timestamp: u32
cflags: u8
os: u8
]
]