Lexical structure
The building blocks of BT source text: files, comments, names, keywords and literals.
object Character { int health; int max_health; }
/// Heals `target` by `amount`, capped at its maximum.
infallible fn heal(Character target, int amount) -> int {
// Statements end with a semicolon; line breaks don't matter.
int healed = target.health + amount;
if (healed > target.max_health) { healed = target.max_health; }
target.health = healed;
return healed;
}
Source files
- BT source files use the extension
.bt. Write them in UTF-8. - BT is case-sensitive:
scoreandScoreare different names. - Statements end with
;. Spaces, tabs and line breaks only separate tokens; LF and CRLF line endings are equivalent. - A
#!line at the very start of a file is ignored, so a file can be run as a script on Linux.#anywhere else is an error. - These characters are an
unexpected charactererror outside strings and comments:#,`,@,\,^, and a single&or|.
Comments
| Form | Rule |
|---|---|
// text | Runs to the end of the line. |
/* text */ | May span lines. Block comments nest: each /* needs its own */. An unclosed block comment is an error. |
/// text | A doc comment: editors show it when you hover over the declaration. |
Doc comments
Write /// lines directly above a function, tree, object, interface, field,
method, query or access declaration. The text is Markdown.
/// Heals `target` by `amount`, capped at its maximum.
///
/// Fails when the target is already dead.
fn heal(Character target,
/// Hit points to restore.
int amount) -> int { ... }
- The comment must be on the line right above the declaration. A blank line,
an ordinary
//comment or code in between detaches it, and it documents nothing. - A parameter can have its own doc comment if it starts its own line.
////lines (four slashes, often used for banners) are not doc comments.///after code on the same line is an ordinary comment.
Names
A name (identifier) starts with a letter or _, followed by letters, digits
or _. Non-ASCII letters work too, so größe is a valid name. Names are
compared byte for byte, so two visually identical names written in different
Unicode forms are different names.
Reserved words
These words are keywords and cannot be used as names:
include object interface fn infallible const tree
if else while repeat for in select
break continue return throw discard
branch join new typeof unless
true false null
enum switch is
NaNandInfinityare float literals, so they cannot be names either.selectcan still be a method or function name (tabs.select(0),fn select(int i)).discardis deprecated and gives a warning; just write the expression as a statement.
Some words are special only in one position and remain usable as ordinary names elsewhere:
| Word | Where it is special |
|---|---|
query, access | At the start of a top-level declaration. |
as | Inside a query body, and after an include path. |
read, write | Inside an access body. |
void, bool, byte, int, int32, float, float32, string, error, type | As built-in type names. |
task, array, slice, map, channel, component, rows, view, borrow | As built-in type names with type arguments. |
this | The object inside a method. A parameter cannot be named this. |
err | The error inside a !! handler. |
_ | As a whole switch pattern it matches anything; as a type pattern's name (Circle _ =>) it binds nothing. |
Words from other languages such as try, catch, var, let, auto,
class and struct mean nothing in BT and are ordinary names.
Integer literals
0 42 1000000 -5
- Decimal digits only. There are no hexadecimal, octal or binary literals, digit separators or suffixes.
- The type is
int(signed 64-bit). A leading-is the negation operator, not part of the literal. - The largest literal is
9223372036854775807. A larger one is the compile errorinteger literal ... is out of range. The minimumintcan be written as-9223372036854775808.
Float literals
0.25 2e3 1.5e-2 6.02E+23 NaN Infinity
- The type is
float(64-bit double precision). - A decimal point needs digits on both sides: write
1.0, not1., and0.5, not.5. - An exponent needs digits:
1eis an error. NaNis a quiet not-a-number andInfinityis positive infinity. Write negative infinity as-Infinity. Both are reserved and adapt tofloat32like any float literal.- A literal too large for a double, such as
1e400, becomes infinity without a warning.
String literals
"Hello" "line one\nline two" "she said \"hi\""
-
A string literal must close on the same line (
unterminated string literal). -
Escapes:
Escape Meaning \nnewline \rcarriage return \ttab \""\\\\$$(so\${is literal text, not an interpolation)A backslash before any other character is that character (
\qisq). There are no numeric or Unicode (\u) escapes, and a string can never contain a NUL byte. UTF-8 text in the source is kept as written.
Interpolated strings
"hp ${hp} of ${max}" "${name}: ${score * 2}" "cost: \$${price}"
${inside a string starts an embedded expression and the matching}ends it. A$not followed by{is ordinary text.- The embedded expression is ordinary source: it may contain braces (map and object literals), other strings, and other interpolated strings. Only the string's literal text must stay on one line.
- A
${with no matching}is an error (unterminated '${' in string literal). A;outside braces inside the expression also ends it with that error, since no expression contains one. - What can be embedded and how each value is printed is described under String interpolation.
Character literals
'a' '\n' '\'' 'é' '\u{1F600}'
-
A character literal is one Unicode code point in single quotes. Its type is
char. -
UTF-8 text between the quotes counts as one character when it is one code point:
'é'and'😀'are valid,'ab'is an error. -
Escapes:
Escape Meaning \nnewline \rcarriage return \ttab \0the zero character, U+0000 \''\""\\\\u{1F600}the code point with that hex value (1 to 6 digits) -
Any other escape is an error, unlike in strings.
-
These are errors: an empty literal
'', more than one code point, a missing closing quote, malformed UTF-8, a UTF-16 surrogate (\u{D800}to\u{DFFF}), and a value above\u{10FFFF}.
Other literals
trueandfalsearebool.nullfits only where a nullable type is expected (see Nullability).- Array, map and object literals (
[1, 2, 3],{"hp": 10},Point { x: 1.0, y: 2.0 }) are described under Construction.
Operators and punctuation
( ) { } [ ] , ; : . ? ?? -> => ~
= + - * / % ++ -- ! !! == != < <= > >= && ||
+= -= *= /= %=
array<array<int>>needs no space between the closing>s.~is not an operator. It marks a declaration or field as private to its package (see Private declarations).&& {and!! {start guard blocks (see Guard blocks).
Nesting limit
Parentheses, blocks and operator chains can nest at most 256 levels deep.
Deeper code is the error nesting is too deep (limit 256); split it into
helper functions.
See also
- Guide: Language basics, Syntax reference