The Baron reference
The tables. For the friendly walk-through, start with the guide; for zero-page allocation in depth, see Zero page allocation.
Contents
Command line reference
baron [-v | -vv] [--check] [-D <sym>=<expr>] [-p <path>] [--inf] [-o <image.ssd>] [--title <t>]
[--opt <0-3>] [--cycle <0-99>] [--pad] [-log<n> <file>] [--symbols <file>] [--warn <n>]
<source files>
| Switch | What it does |
|---|---|
-v |
Print an assembly listing to stdout: emitted bytes against their
source line (up to eight, then ...), each label's address
alongside it, assignments as their evaluated values (source expression
bracketed when it differs; list values cut after eight elements), macro
expansions and includes followed through, PRINT output
interleaved. |
-vv |
The uncut -v: every emitted byte, wrapped eight per
line with the address in the margin, and list values rendered
whole. |
-D <sym>=<expr> |
Predefine a symbol before assembly, e.g.
-D DEBUG=TRUE -D version="1.0" (no spaces inside the
assignment; repeat the switch for more). The expression gets the full
evaluator and may forward-reference symbols the source defines. Applies
to every source file on the line. See Predefined symbols. |
--check |
Assemble and validate everything, write nothing - no binaries, no image, no logs. |
-p <path> |
Write every saved section as a raw binary into the directory
path (which must already exist; . for the current
one). Nothing is written without this or -o. |
--inf |
Write a .inf sidecar beside each raw binary, carrying
its DFS name and load/exec addresses. Needs -p. |
-o <image.ssd> |
Gather every saved section onto a DFS single-sided disc image. Only
.ssd for now (.dsd, .adf and
.uef are spoken for, later). May be combined with
-p. |
--title <t> |
The disc title, up to 12 characters. Needs -o. |
--opt <0-3> |
The *OPT4 boot option: 0 none, 1 *LOAD, 2
*RUN, 3 *EXEC !BOOT. Needs
-o. |
--cycle <0-99> |
The catalogue cycle number. Needs -o. |
--pad |
Pad the image out to a full 80-track disc (200K); every unused
sector is filled with &E5, the single-density format
filler. Without it the image is truncated after the last used sector.
Needs -o. |
-log<n> <file> |
Redirect PRINT channel n (0-9) to a file.
Channel 0 otherwise goes to stdout; channels 1-9 are otherwise
discarded. |
--symbols <file> |
Write every source file's sections and resolved symbols to one JSON
file: an entry per source file listing the files it read and its
sections (name, parent, size, attributes as written), each section
holding the symbols defined in it, grouped by kind (labels,
assignments, defines, za_autos,
loop_vars, params) with each one's value,
source and line. Sorted, one symbol per line. See The symbol dump. |
--warn <n> |
Show warnings up to level n. The default 1 shows the
ordinary warnings; --warn 2 adds the opt-in audits (a store
into the ZA_POOL at a fixed address); --warn 3
adds branches that cross a page boundary and so cost a cycle more when
taken (plentiful in ordinary code, so they get a level of their own);
--warn 0 shows errors only. Warnings never affect the exit
code. |
--help |
Print the switch summary. |
--version |
Print the version and author information. |
-p and -o are the two output gates, and
neither is implied: a run given neither still assembles, reports and
prints (-v, PRINT, -log<n>
are unaffected), but the assembled bytes go nowhere and a warning says
so. --check asks for exactly that on purpose, and stays
quiet.
Anything that is not a switch is a source file, and each assembles
independently - a fresh symbol table per file,
diagnostics reported per file as it finishes. Diagnostics go to stderr
in the file:line:col: error: message shape; success is
silent.
The exit code is 0 exactly when every file assembled (warnings alone stay 0). Every file is assembled even after a failure - one run reports everything - but nothing is written (no binaries, no image, no logs) unless the whole batch succeeded.
Development builds also accept baron --test [group],
which runs the unit test suite.
Keyword reference
Everything is case-insensitive. Statements are separated by newlines
or :; comments run from ; or \ to
the end of the line.
Labels, symbols and scopes
| Syntax | Meaning |
|---|---|
.name |
Bind a label at the current address. |
name = expr |
Bind a symbol to a value. Symbols are immutable: one name, one
value, per scope. Prefix the name with @
(@next = 5) if the name clashes with a keyword. |
{ ... } |
A scope. A label immediately before the brace names it; its symbols
are reachable from outside as name.symbol. Anonymous scopes
are truly private. Both braces double as statement separators, so
LDX #8 {.loop DEX : BNE loop } RTS needs no extra
colons. |
.@ |
Bind an anonymous local label here. Referenced by @- /
@+ (nearest behind / ahead, within the current scope). |
Data
| Syntax | Meaning |
|---|---|
EQUB v [, v...] |
Emit bytes. Numbers emit one byte (-255..255 accepted); strings emit
their characters; lists and ranges flatten. EQUS is an
alias - the two are interchangeable. |
EQUW v [, v...] |
As EQUB, but each value is a 16-bit little-endian
word. |
EQUD v [, v...] |
As EQUB, but 32-bit. |
SKIP n |
Reserve n bytes of zeroes. n can be at
most &10000. |
SKIPTO addr |
Pad with zeroes up to addr (the address only ever moves
forwards). addr can be at most &10000, the
top of memory. |
ALIGN n |
Pad with zeroes to the next multiple of n, which must
be between 1 and &10000. |
INCBIN "file" |
Splice a binary file's bytes into the output. Path is relative to the including source file. |
BITABS |
Emit a lone &2C (a BIT abs opcode): at
run time its operand fetch swallows the next 2 bytes, so fall-through
resumes past them while a branch straight to the next instruction
executes it - the classic skip trick. The zero-page allocator models it
(the swallowed instruction counts only on the branch-taken path);
without the allocator it is trusted raw emission, exactly like
EQUB &2C. |
BITZP |
The 1-byte flavour: emit &24 (BIT zp),
swallowing the next single byte. |
BASIC ... ENDBASIC |
Emit an inline BBC BASIC program, tokenised exactly as BASIC 4
stores it (uppercase keywords only, &8D line-number
encoding, &0D &FF terminator). Each line starts
with a decimal line number 0-32767 and runs to the end of the physical
line; between lines only blanks and comments are allowed. A
{ ending a line splices Baron statements into it up to a
}, which resumes the same line: the spliced bytes go in
untokenised, the tokeniser carries on as if they weren't there, and the
splice opens no scope. Lines emit in written order and a record past 255
bytes (splices included) is refused. |
Structure and control
| Syntax | Meaning |
|---|---|
IF e / ELIF e / ELSE /
ENDIF |
Conditional assembly. A dead branch emits nothing, binds nothing and
computes nothing (no call, builtin or RND runs in it).
Nests freely. |
FOR v = seq ... NEXT |
Run the body once per element of a range or list, v
bound to each element, each iteration in its own scope. An empty
sequence runs zero times. |
INCLUDE "file" |
Splice another source file in textually: its code emits here, its symbols bind here. Paths resolve relative to the including file. |
MACRO name sig ... ENDMACRO |
Define a macro. The signature is parameter names, commas, and quoted literal tokens matched verbatim; one name may carry several overloads, matched by shape. An empty body forward-declares. Define before use. |
FUNCTION name(params) = expr |
A pure function usable in any expression - one-liner form, or a
multi-line body of local assignments, IFs,
FOR ... NEXT loops and ASSERTs ending in a
top-level = expr return. A local (parameters included)
takes a new value at each assignment and can be read only once assigned;
assigning a global's name makes a local that hides it, so a body changes
nothing outside itself. A body FOR walks a list or bounded
range as the statement does, its variable an ordinary local; there is no
WHILE or BREAK, and the return is never inside
a loop or an IF. Overloads by parameter count. Define
before use. Recursion may nest 128 calls deep; loops have no limit. |
PRINT [#n,] v [, v...] |
Write text during assembly (final pass only): values concatenated,
one newline per statement, to channel n (default 0). |
ERROR [v [, v...]] |
Report the message as an assembly error and fail the build; assembly carries on to find more. |
ASSERT e [, v [, v...]] |
IF NOT(e) : ERROR v... : ENDIF in one statement. The
message is evaluated only on failure; without one the error is
"Assertion failed". Also a FUNCTION body statement, where a
failure becomes the call's result. |
Sections and output
| Syntax | Meaning |
|---|---|
SECTION name [, attr = e ...] ...
ENDSECTION |
A uniquely-named block of output owning its own bytes. Nesting is
containment: at ENDSECTION a child's bytes are copied into
its parent and the parent's address moves past them; a child with an
explicit org is rephased (its labels resolve at the runtime
address while its bytes land in the parent - the relocation workflow). A
top-level section stands alone: the default section's address follows
only its own bytes. Attributes are never inherited. |
SECTION name ... ENDSECTION (again) |
Reopens a closed section, carrying on from its address so far; the
piece's bytes join the section's own, wherever the piece is written.
Top-level sections can always be reopened; a nested one only if it took
no room in its parent (a virtual child of a real section, or a child
with its own org inside a virtual one). A reopening takes
no attributes and keeps the first line's, so that line must come first.
A section cannot be reopened from inside itself. |
Attributes Baron acts on: org (assembly address;
default: continue the enclosing section - the default section starts at
0), filename (the request to save; an empty filename is a
config error), load (default org),
exec (default load), cmos (truthy
enables the 65C02 instruction set for this section - see the note under
Reserved names), guard (an address the section's emission
must not reach; overrunning it is a non-halting error reporting the
overshoot - checked at each ENDSECTION, so per piece of a
reopened section), transform (the name of a
FUNCTION, as a string: at ENDSECTION the
section's bytes, as a list of numbers, are replaced by what it returns -
a list of bytes, or a string - and that is what the parent receives; the
section's labels keep their untransformed addresses. It runs once every
byte is final, and a change of size sends the assembly round again until
the sizes settle. Not on a virtual section, and a transformed section
cannot be reopened), and virtual (truthy makes the section
assign addresses but keep no bytes: it cannot be saved, and its code is
ignored by the zero-page allocator. Inside a real section it takes no
room, so it needs an org of its own. As a parent it is a
memory map: it steps past each child that continues its address, while
the children keep their own bytes and can be saved; a child with its own
org takes no room in it). Other keys are carried through
untouched.
The address attributes take the BBC's full 32-bit host addresses
(&FFFFxxxx = the I/O processor,
&0000xxxx = the second processor): load
and exec keep the whole value for the catalogue, while
org and guard apply their low 16 bits - the
6502's actual address - to the assembly.
The last byte of memory is &FFFF. A section may fill
right up to it, but a byte placed past it is a non-halting error
("Program counter runs past &FFFF by N bytes"), reported once per
section at the statement that went over.
A filename destined for a disc image follows DFS rules: an optional
single-character directory prefix ("D.NAME"; default
$), then a name of up to 7 characters.
Zero-page allocation
| Syntax | Meaning |
|---|---|
ZA_POOL addrs |
Hand the allocator a pool of zero-page bytes: a comma-separated list
of addresses and .. ranges. Its presence switches the
feature on. |
ZA_AUTO1 names |
Declare one-byte auto-allocated variables. |
ZA_AUTO2 names |
Declare two-byte variables (pointer pairs). |
ZA_AUTO n, names |
Declare n-byte variables (1-256). |
ZA_UNREACHABLE |
Assert control cannot fall through to this point - a branch's dead
arm, or the continuation of a never-returning JSR; that
path is pruned. |
ZA_CANCALL targets |
After a computed/self-modified JSR: the routines it may
reach. Targets flatten like EQUB data, so a list-valued
symbol works. Name your own code by label (an alias or
label+n works too); a plain number landing on assembled
code is refused. |
ZA_CANJUMP targets |
After a computed/indirect JMP, a self-modified branch,
or an RTS-dispatch: the labels it may land on, replacing any literal
operand. Targets flatten like EQUB data, and follow
ZA_CANCALL's label rule. |
ZA_RETURN |
Declare the preceding jump/branch a return to this routine's caller (the inline-data trick's computed exit). |
ZA_RETURNTO targets |
Declare where the preceding JSR resumes, replacing its
fall-through (a data-consuming callee that resumes somewhere other than
just past the data). |
ZA_DISCARD vars |
Promise the named ZA_AUTO variables' current values are
dead - later reads see only later writes. For arrays rebuilt via
STA arr,X, whose writes prove nothing. Binds to the path it
is written on; below a label it applies to every path through that
point. |
ZA_WIPE |
Promise that the sweep just before it has written the whole pool
(the boot-time zero-page wipe): place it directly after the wipe loop.
Every variable counts as freshly written from the marker, and the
sweep's own fixed-base stores are not warned about. Binds like
ZA_DISCARD: written inside a branch arm it stays on that
arm. |
ZA_INDEXEDBY values |
Declare the index values the register can hold at the indexed
ZA_AUTO access just before it (var,X /
var,Y / (var,X)). Ranges and lists flatten
like EQUB data; each value 0-255. The access is then
bounds-checked against the variable's width (refused if any declared
index reaches outside it - the base itself may sit below the variable,
as in var-4,X : ZA_INDEXEDBY 4..7) and the default-level
unchecked-index warning goes quiet. |
ZA_ENTRY |
Mark the routine it opens as an externally-called entry point (a
reachability root). Any ZA_ENTRY replaces the default "each
section starts a routine" presumption. |
ZA_INTERRUPT |
Mark the routine it opens as an interrupt handler: a root whose variables are kept apart from the rest of the program. |
The full story - liveness, footprints, sections, every error - is in Zero page allocation.
Reserved names
TRUE, FALSE and PI are
reserved outright - they cannot name a label or symbol anywhere. Any
other spelling is fair game wherever a name is defined: labels
(.next, .clr), FOR variables,
macro and FUNCTION parameters, ZA_AUTO names
and -D defines may all share a spelling with a mnemonic,
directive or macro, because references are read where an operand is
expected, and no mnemonic, directive or macro name means anything there.
The one exception is a plain assignment, whose name starts the statement
- there the keywords win, so next = 5 will not parse.
Prefix the name with @ to force it to read as a name:
@next = 5 binds next, referenced bare (the C#
idiom). Inside a FUNCTION body, a local's assignment starts
its statement too, so the body keywords (IF,
ELIF, ELSE, ENDIF,
ASSERT, FOR, NEXT) cannot name
locals. Macro and FUNCTION names stay restricted -
they become keywords themselves, and a FUNCTION cannot take
a builtin function's name (LO, REPLACE,
WHERE, ...). All the NMOS 6502 mnemonics are recognised,
plus the 65C02 extras (BRA, STZ,
PHX, ...) - usable inside a section carrying
cmos = TRUE; elsewhere the target is plain NMOS and a
CMOS-only encoding is refused with its own message.
Expression reference
Values
| Kind | Examples |
|---|---|
| Number | 42, 1.5, &FF or
$FF (hex), %1010 (binary), 'A' (a
character literal: 65). All numbers
are one type. |
| Boolean | TRUE, FALSE - what comparisons and the
predicates return. Coerces to 1 / 0 in any numeric context
((x>5)*10, EQUB flag), so a boolean goes
anywhere a number does; only
AND/OR/EOR/NOT care
about the difference (logical on booleans, bitwise on numbers, a mixed
pair refused). Note: This is a breaking change from BeebAsm,
which represents TRUE as -1, not 1. The switch
--beebasm-true can be used to restore TRUE as -1. |
| String | "hello" - write "" for a literal quote. A
string is one value, like a number: broadcasting, list building
(CONCAT, FLATTEN) and shape
(RANK("abc") is 0) all treat it whole, and FOR
won't walk one. Only the functions that work along a sequence -
subscripts, LEN, REVERSE, FIND
and LZ_PARSE's data - read its characters, giving back a
string (or a position in one). There is no character type: one character
is a one-character string. |
| List | {1, 2, 3} - nestable, mixed types welcome, newlines
allowed inside the braces. Nested equal-length lists act as higher-rank
arrays; SHAPE/RANK report the axes all
elements agree on (ragged lists keep only their uniform leading
axes). |
| Range | A compact list of consecutive integers - see Range forms. |
Character literals
A character literal is exactly one character between single quotes,
and it is simply a number: the character's code. It goes anywhere a
number does - LDA #'A' loads 65, 'z'-'a' is
25, and EQUB 'h', 'i' emits two bytes.
Any character except a newline or the single quote itself may sit
between the quotes, ';' and ':' included.
There are no escape sequences, so write 39 for a quote. For
the codes of a whole string, see CODES under Element-wise functions.
Range forms
Endpoints must be whole numbers, at most 2^53 either way (where
numbers stop holding every integer); either or both may be any constant
expression. .. binds looser than arithmetic and
comparisons, so 0..n-1 needs no parentheses.
| Form | Meaning |
|---|---|
a..b |
Inclusive at both ends. Direction is inferred: 5..1 is
{5, 4, 3, 2, 1}. |
a..<b |
Excludes the end. Ascending only; a..<b with
a >= b (e.g. 5..<5) is the legal empty
list {}. |
a..b..c |
Stepped: b is the second element, fixing the
step at b-a; c is a limit, not a promise
(1..3..10 is {1, 3, 5, 7, 9}), though it must
reach b (0..2..2 is {0, 2};
0..2..1 is an error). Steps down too: 10..8..1
is {10, 8, 6, 4, 2}. |
a..b..<c |
As above, excluding c. The < goes on
the final separator only. |
a.. / ..b / a..b.. /
.. |
Unbounded: one or both ends left off. Not enumerable as data - used
as subscript selectors, where the axis
supplies the missing end (..b runs from the start,
a.. to the end). |
Operators, tightest first
| Operators | Meaning |
|---|---|
x[...] |
Subscript - see Subscripts. |
^ |
Power (right-associative). |
-x +x |
Unary minus / plus. |
* / DIV MOD
<< >> |
Multiply, divide, integer divide, modulo, shifts. |
+ - |
Add, subtract. + also concatenates strings. |
= == != <>
< > <=
>= |
Comparisons, yielding TRUE or FALSE.
=/== and !=/<>
are synonyms; strings compare too. A boolean compares against a number
as 1 / 0, so 1 = TRUE holds. |
AND |
Logical AND on two booleans (yielding a boolean); bitwise AND (32-bit) on two numbers. A mixed boolean/number pair is an error. |
OR EOR |
Logical / bitwise OR and exclusive-OR, overloaded exactly as
AND. |
.. ..< |
Range construction. |
<x >x ~x |
6502-style low / high byte of the whole following expression:
<start+1 is LO(start+1). ~x is
the same shape, giving x as an uppercase hex
string - 2, 4 or 8 digits, the narrowest that holds it
(~10 is "0A", ~&123 is
"0123", ~&123456 is
"00123456"). No & prefix; the 32-bit
pattern is what is formatted, so ~-1 is
"FFFFFFFF". For decimal text, see
AS_STRING. |
Subscripts
x[sel, sel, ...] - one selector per axis, outermost
first; fewer selectors than axes leaves the trailing axes whole
(M[1] is a row; M[1][2] and
M[1, 2] are the same element). Three selector kinds:
| Selector | Behaviour |
|---|---|
| Integer | Picks one element and drops the axis. Out of range is an error. |
| Range | Slices, keeping the axis. Forgiving: clipped to the axis,
may come back empty. Unbounded ends take the axis bounds
(L[..] is the whole list, L[3..] from index 3
on, L[0..2..] every other element); a descending range
reverses (L[4..0]). |
| List | Gathers exactly the named indices, in order, keeping the axis
(L[{0, 4}]; L[{1, 1, 1}] repeats). Out of
range is an error. |
Ranges subscript via their elements ((100..200)[10] is
110). Strings are rank 1 and always yield a string:
"hello"[1..3] is "ell",
"hello"[4..0] is "olleh".
Broadcasting
Operators and element-wise functions map across lists and ranges, NumPy-fashion. The rules, in order:
- A range becomes its list of elements.
- A single value (a number, or a string) pairs with everything in the
other side, however deep:
{{1, 2}, {3, 4}} * 10is{{10, 20}, {30, 40}}. - Two lists of the same depth pair off element-wise - lengths must
match (
{1, 2} + {1, 2, 3}is a shape error), except that a length-1 list repeats:{1, 2, 3} + {10}is{11, 12, 13}. - The shallower side is held whole and applied to each element of the
deeper: a rank-1 list against a rank-2 one applies per row
(
{{1, 2, 3}, {4, 5, 6}} * {1, 0, 0}is{{1, 0, 0}, {4, 0, 0}}), and a column against a row is the outer product ({{1}, {2}} + {10, 20}is{{11, 21}, {12, 22}}).
The rules recurse, so nesting and ragged tails work:
{1, {2, 3}} * 2 is {2, {4, 6}}. A string is a
single value here, like a number: {"a", "b"} + "!" is
{"a!", "b!"}.
Only operators and the element-wise
functions broadcast. Every other function takes its arguments whole:
LEN({{1, 2}, {3}}) is 2, not {2, 1}. The one
exception is FIND's second argument, which maps. A user
FUNCTION takes its arguments whole too, and maps over a
list only when everything in its body does: sqr(0..15) is a
16-element list because x*x maps, but an IF
needs one number or boolean, so a list condition is an error.
WHERE is the element-wise choice that maps instead.
Element-wise functions
All broadcast; trigonometry is in radians.
| Function | Meaning |
|---|---|
LO(n), HI(n) |
The low byte (bits 0-7) / the high byte (bits 8-15). |
ABS(n) |
Absolute value. |
INT(n), FLOOR(n) |
Round down (the two are synonyms). |
ROUND(n) |
Round to nearest; halves go away from zero (ROUND(2.5)
is 3, ROUND(-2.5) is -3). |
TRUNC(n) |
Round toward zero. |
CEIL(n) |
Round up. |
SQRT(n) |
Square root. |
SIN(n), COS(n), TAN(n),
ASIN(n), ACOS(n), ATAN(n) |
Trigonometry. |
LOG(n), LN(n), EXP(n) |
Log base 10, natural log, e^n. |
NOT(x) |
Logical NOT on a boolean (NOT(TRUE) is
FALSE); bitwise complement (32-bit) on a number. |
RND(n) |
A random integer in 0..n-1. RND(REPEATED(k, n)) makes k
draws. Deterministically reseeded each pass, so it converges. |
CODES(s) |
A string's character codes as a rank-1 list:
CODES("AB") is {65, 66},
CODES("") is {}. The bridge from text to
arithmetic (a lone character is simpler as a character literal, 'A'). The
result broadcasts like any list: CODES(s) - ' ' offsets
every code. |
AS_STRING(n) |
A number as decimal text, exactly as PRINT shows it:
AS_STRING(42) is "42",
AS_STRING(1.5) is "1.5",
AS_STRING(TRUE) is "1". For hex, use
~n. A ZA_AUTO address is refused, since its
length as text isn't known until it's allocated (PRINT v
shows it). |
WHERE(mask, a, b) |
The element-wise IF: for each value in
mask (a boolean, or a number with non-zero for true), the
matching value of a where it is true and of b
where it is false. The three arguments broadcast together, as an
operator's two do: WHERE({1, 300} > 255, 255, {1, 300})
is {1, 255}. Only picked values matter - an error in one it
passes over (a division by zero, a name not yet defined) is dropped, as
in an IF arm not taken - so
WHERE(x = 0, 0, 1/x) is safe. |
AS_NUMBER(s) |
The number a string spells, written as a number literal is written
in source: an optional - or +, then decimal,
&/$ hex, % binary or a
character literal - AS_NUMBER("42") is 42,
AS_NUMBER("-&FF") is -255. The whole string must be
that one number, with no blanks around it; anything else is an error
naming the string (Not a number: '12x'). So hex from
~ needs its & back:
AS_NUMBER("&" + ~n) is n. |
String, search and type functions
| Function | Meaning |
|---|---|
CHR(x) |
The inverse of CODES: every numeric leaf of
x (flattened; ranges enumerate) becomes one character of a
single string - CHR(72) is "H",
CHR({72, 73}) is "HI",
CHR(CODES(s)) is s. Codes must land in 0..255;
fractions truncate. Doubles as the way to join a list of codes into one
string. |
FIND(x, v [, otherwise]) |
The index of v's first occurrence in x (a
list, range, or string - a string is searched for a substring:
FIND("hello world", "world") is 6). The needle broadcasts -
the one argument of a whole-value function that does - so
FIND(from, CODES(s)) is a same-shape list of indices; a
needle can therefore never itself be a list-valued element, nor a run of
elements. A miss is a hard error naming the needle, unless
otherwise is given: then a miss gives
otherwise (per needle, when the needle maps).
otherwise only matters on a miss, so
ERROR("...") there makes a custom message. |
REPLACE(x, old, new) |
x with every occurrence of old replaced by
new, left to right, never overlapping, and never looking
inside what it has put in: REPLACE("aaa", "aa", "b") is
"ba". In a string, old and new
are strings. In a list (or range) they are runs of elements, read as
CONCAT reads its arguments - a list gives its elements, any
other value is one element - so
REPLACE(bytes, &0D, {&0D, &0A}) adds a line
feed after every carriage return; wrap a list to mean one list-valued
element ({{1, 2}}). Nothing found leaves x
unchanged; an empty old is an error. |
IS_STRING(x), IS_NUMBER(x) |
TRUE or FALSE for the value as a whole - a
list is neither (test list-ness with SHAPE(x) != {} or
RANK(x) > 0). A boolean counts as a number (it coerces
to one). A still-undefined symbol defers rather than answering. |
ERROR(v, ...) |
The ERROR
statement as a value: an error carrying the concatenated message
(strings raw, everything else as PRINT shows it), reported
wherever the value ends up used. Made for guarding FUNCTION
bodies: r = ERROR("bad width: ", w) behind an
IF. |
List functions
| Function | Meaning |
|---|---|
LEN(x) |
Length of the outermost axis (elements, characters, or range count). |
SHAPE(x) |
The axis lengths as a list, outermost first; the axes all elements
agree on (SHAPE({{1,2},{3,4,5}}) is {2}).
{} for a scalar; a range counts as the rank-1 list it
stands for (SHAPE(0..9) is {10}; unbounded
errors); empty axes are real lengths (SHAPE({}) is
{0}, SHAPE({{}}) is {1, 0}). |
RANK(x) |
The number of axes (0 for a scalar; 1 for any range, unbounded included). |
REPEATED(n, v), FULL(n, v) |
A list of n copies of v (two names, one
function). |
FLATTEN(x) |
Every leaf, in order, as one flat list. |
CONCAT(a, b, ...) |
Join along the outermost axis: each list (or range) argument
contributes its top-level elements, each scalar (strings included)
itself - so a scalar and a one-element list are interchangeable. Not a
flatten: nesting inside elements survives
(CONCAT({1,{2,3}}, 4) is {1,{2,3},4}). To
append a list as a single element, wrap it:
CONCAT(xs, {{3,4}}). |
ZIP(a, b, ...) |
Equal-length lists into a list of tuples:
ZIP({1,2},{3,4}) is {{1,3},{2,4}}. |
REVERSE(x) |
The outermost axis reversed (works on strings too). |
SORT(L [, key...]) |
Sort ascending by key, the keys all numbers or all strings (strings
in character-code order, as < orders them); extra
arguments subscript each element to find its key
(SORT(L, 0) sorts on first items). |
SUM(x [, axis]), PRODUCT(x [, axis]) |
With no axis, fold every leaf to one scalar
(SUM({{1,2,3},{4,5,6}}) is 21; SUM({}) is 0).
With an axis (0 = outermost), collapse just that one: axis 0 folds rows
together ({5, 7, 9}), axis 1 folds within each row
({6, 15}). SUM is + folded, so it
joins strings: SUM({"ab", "cd"}) is "abcd",
and SUM(rows, 1) joins each row. Strings and numbers don't
mix. |
MIN(x [, axis]), MAX(x [, axis]) |
Smallest / largest, same axis rules; strings compare as
< compares them (MIN({"pear", "apple"}) is
"apple"). MIN({}) errors - no identity. |
DEFINED(name) |
TRUE if the symbol resolves (yet), FALSE
if not - the one function happy to receive an undefined name. |
Bit and compression functions
The building blocks of a section transform that
compresses. A field of n bits holding v is
written as the single number 2^n + v (the top set bit marks
the width).
| Function | Meaning |
|---|---|
BITLEN(x) |
How many bits x's integer part needs:
floor(log2 x) + 1, exactly, and 0 below 1. Broadcasts. A
field's width is BITLEN(field) - 1. |
BITPACK(fields) |
The fields (flattened, as for EQUB) concatenated into a
list of bytes, each field most significant bit first and each byte
filled from its top bit; the last byte is padded with zero bits. A field
is a whole number from 1 (no bits) to 2^53 - 1 (52 bits). |
LZ_PARSE(data, literal_run, literal, match_run, length, offset [, repeat]) |
The cheapest way to write data (a list of bytes, or a
string) as runs alternating literals and matches - literals first, each
run's count before its contents, a match copying length
bytes from offset back. The other arguments are cost
tables: lists of bits by value written (a run's count, a literal byte, a
match run's count, a length, an offset), a negative entry meaning
impossible. offset may be a list of tables, one per match
length from 1, the last serving every longer match. The optional
repeat table prices a repeat match by its length: the first
match of a match run may copy from the previous match's offset again.
repeat may instead be a pair {lengths, after},
after giving by the count of the literal run before what
any match there pays, negative where no repeat may follow (Exomizer:
{-1, 1}). Returns {bits, runs}, each run
{0, bytes} or {1, {offsets, lengths}}, a
repeat having offset 0. Exact, given: when match runs' counts all cost
the same, nothing more (every offset is tried); otherwise, counts and
offsets never cheaper as they grow; and, with repeats, literal-run
counts (with what a match after them pays) and repeat lengths
likewise. |
Constants
| Constant | Meaning |
|---|---|
TRUE, FALSE |
The boolean values. Coerce to 1 and 0 in numeric contexts. |
PI |
3.14159... |
*, P% |
The current assembly address - of this statement, so
JMP * is jump-to-self and EQUB *,*,* advances
per byte. Read-only. |
@-, @+ |
The nearest .@ local label behind / ahead, within the
current scope. |
Your own FUNCTION names join the table as they are
defined, callable as name(args).
Version history
See the GitHub releases page for what changed in each version.