Boron User Manual

Version 2.1.0, 2023-12-11

1 Overview

Boron is a scripting & data exchange language. It is similar to Rebol but with a lower run-time footprint. Language features include:

1.1 A Brief Introduction

The basic Hello World program is:

print "Hello World!"

As all Boron code is data this program can be examined at run time. The program can be enclosed in a data block, assigned to a variable, and then queried:

)> program: [print "Hello World!"]
== [print "Hello World!"]

)> foreach value program [print type? value]
word!
string!

We see the program consists of two values. The program can be modified with poke and then run with do to see the results.

)> poke program 2 "Hello Mars!"
== [print "Hello Mars!"]

)> do program
Hello Mars!

There are no keywords in the language so any word can be redefined. The meaning of the word print in the program can be changed by creating a new context with an alternate implementation.

)> reverse-print: func [text] [print reverse copy text]
== func [text][print reverse copy text]

)> bind program context [print: :reverse-print]
== [print "Hello Mars!"]

)> do program
!sraM olleH

1.2 About This Document

This manual is largely incomplete.

There is a separate function reference and code documentation available online at http://urlan.sourceforge.net/boron.

2 Scripts

Scripts are UTF-8 encoded text files.

2.1 Comments

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
*/

2.2 Shell Invocation

The first line of a script may be a UNIX shell sha-bang (#!) command.

#!/usr/bin/boron

2.3 Command Line Usage

Usage:

boron [options] [script] [arguments]

2.3.1 Command Line Options

-e “exp Evaluate expression
-h Show help and exit
-p Disable prompt and exit on exception
-s Disable security

2.3.2 Command Line Arguments

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"]

3 Datatypes

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!

3.1 Unset!

Unset is used to indicate that a word has not been assigned a value.

3.2 Datatype!

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!

3.3 None!

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.

3.4 Logic!

A boolean value of true or false.

The words yes & no are defined as synonyms for true & false.

)> yes
== true

3.5 Char!

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)'

3.6 Int!

A 64-bit integer number. Integers can be specified in decimal, or if prefixed with 0x, as hexadecimal.

Example integers:

24
-403281
0x1e

3.7 Double!

A 64-bit floating point number.

Example double values:

-3.5685
24.
6.503e-8

3.8 Coord!

Integer coordinate that is handy for specifying screen positions, rectangles, colors, etc.

A coord! can hold from two to six 16-bit integers. The numbers are separated by commas and spaces may follow the commas.

640,480       ; Screen size
45,10, 45,18  ; Rectangle
255,10,0      ; RGB triplet

3.9 Vec3!

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

3.10 Word!

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
>

3.11 Lit-word!

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

3.12 Set-word!

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

3.13 Get-word!

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.

3.14 Option!

An option is a variant of a word! that begins with a slash (/) character.

/outside

An option is inert will evaluate to itself.

3.15 Binary!

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}

3.16 Bitset!

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

3.17 String!

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
}}

3.18 File!

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

3.19 Vector!

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

3.20 Block!

A block is a series of values within square brackets.

[1 one "one"]

[
    item1: [some sub-block]
    item2: [another sub-block]
]

3.21 Paren!

A paren is similar to a block!, but it will be automatically evaluated.

(1 two "three")

3.22 Path!

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

3.23 Context!

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]

3.24 Hash-Map!

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"

3.25 Func!

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.

3.26 Cfunc!

This type is for the built-in functions written in C. See the function reference for the available functions.

3.27 Port!

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.

3.27.1 Standard IO Ports

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
]

3.27.2 Network Ports

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

4 Parse Language

The parse function can operate on strings, blocks, and binary values. It returns true if the end of the input is reached.

4.1 Block Parse

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.

4.2 String Parse

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
    ]
]