Syntax
BPL divides each statement into runs and tokens before it evaluates anything. The rules on this page don’t depend on what any name holds. Evaluation covers how the runs then bind. Types of brackets and the Language overview explain the same rules with more examples.
Hanzi spellings
Hanzi are ordinary name characters by default. Enable their glyph spellings with a leading declaration:
•prefs ["hanzi_aliases":$t]The setting persists across inputs and loaded files. When enabled, simplified, traditional and APL spellings can be mixed. +/, 加总, 加總 and 加/ all name the same function. Each alias retains the whole glyph’s meaning in both valences. The notation preference changes printed functions, not accepted input.
是 names assignment ←: mean是加总除数 defines the mean without spaces. 内 (內) names the inner-product operator .. Ordinary . remains the usual spelling for fields and indexing, as in record.field and a.[i]. Hanzi inside strings and comments remain ordinary text. Hanzi as notation and 汉字 BPL introduce the notation through examples.
Names
A name accepts characters not reserved for syntax, glyphs, numbers or whitespace, including hanzi and emoji. A digit ends a name, as it ends a glyph. f2 therefore applies f to 2, the same way ⍳2 applies ⍳ to 2.
_ adds the next non-whitespace character to a name, including glyphs and Hanzi aliases: _+ and _加_总 are names. Hanzi that aren’t aliases remain name characters when aliases are enabled. Digits straight after _ all stay in the name. A name can’t end in _, except the lone name _. A lone _ is a name when a space, ], ), ←, ;, ⋄ or the end of the line follows it. Assign to _ a value that nothing reads.
After a dot, digits stay in the key: T.q1 is the entry with key q1.
f←- ⋄ f2 ⍝ ¯2
x_1←10 ⋄ x_12←20 ⋄ x_1+x_12 ⍝ 30
x_+←3 ⋄ x_+ ⍝ 3
[k _ v]←1 2 3 ⋄ k+v ⍝ 4
T←["q1":5] ⋄ T.q1 ⍝ 5
😎←2 ⋄ 中文←3 ⋄ 😎+中文 ⍝ 5Literals and strands
Numbers, ∞, ¯∞, ⍬, characters, strings and the constants $t, $f, $n, $a and $d are literals. A constant is $ followed by one lowercase letter. $a is the uppercase Latin alphabet and $d the digits. $e names the error that an error guard’s handler caught. Numbers and Characters and strings give the written forms of numbers and text.
'' and "" are the same empty character vector. A double-quoted string can span lines. Every line ending inside it reads as one newline, and its indentation stays as written.
ₓ straight after a number, ⍬, or a ] or ) that closes literals makes every number it covers exact, as in 5ₓ, [1 2]ₓ and (0 1⋄2 3)ₓ. After anything else, ₓ floors to exact integers: nₓ is ⌊ₓn, and ÷ₓ divides and floors, as Superscripts and subscripts describes.
After a number, e, j or r, or its subscript ₑ, ⱼ or ᵣ, continues the number only when the rest of a number follows it, as in 1e5, 2ⱼ3 and 1r3. Otherwise a plain letter starts a name: 2edges is 2 beside the name edges. A subscript there, or between any two items, joins the items on each side into one term, as Numbers describes: 2ₑn is 2×10*n.
Literals separated by spaces form a strand, which is one vector. Literals that touch stay separate, as 3 and 'b' do in 'a',⍣3'b'. Names never join a literal strand. Inside brackets without ;, a space separates items and forms no strand.
⍝ starts a comment, which ends at the end of the line.
Runs
Outside brackets, spaces divide a statement into runs. A group in brackets, parentheses or braces counts as one token, even when it contains spaces. Apart from the two operator rules below, each run must give one value: a subject, a function, or a lone operator such as the / in + / x. A run that gives anything else is a SYNTAX error.
BPL reads a statement in three steps:
- Literals separated by spaces join into strands, such as
1 2or"ab" "cd". - Each remaining run is wrapped in parentheses.
- The result reads as ordinary APL, right to left.
So a+b × c+d is (a+b)(×)(c+d), and 1 2+3 is (1 2)+3, not (1)(2+3). A run that ends in a dyadic operator takes the next run as its right operand: ⌊@ 10× x is (⌊@)(10×)(x), where 10× is the operand of @. A run that starts with an operator takes the previous run as its left operand: ≢⊢ ⌸x is (≢⊢)(⌸x), where ≢⊢ is the operand of ⌸. Evaluation explains operands.
a←1 ⋄ b←2 ⋄ c←3 ⋄ d←4 ⋄ a+b × c+d ⍝ 21
(1)(+)(1) ⍝ 2
1 2+3 ⍝ 4 5
2×1 + 1 ⍝ 3
x←3 ⋄ x+1 ÷ 2 ⍝ 2
+ / 1 2 3 ⍝ 6
x←0 1 1 0 1 1 ⋄ ≢⊢ ⌸x ⍝ [2 4]ₓBrackets, parentheses and braces
A bracket list is a vector, whatever the number of items. Inside brackets, spaces separate items, not runs:
- Without
;, each space separates two items, and each item is one run. - With
;, only;separates items. Within each item, spaces divide runs as they do outside brackets. ⋄stacks rows into an array of higher rank, with each row as one major cell. Each row follows the first two rules. A row of one number has rank 0. Write a column with a one-item vector in each row.- Each item stays one item of the vector, whatever it holds, including an array or a function. Items evaluate left to right.
- Items written
"key":valuemake a record. An item writtenname:, with no value, is"name":name.
⍴[5] ⍝ [1]ₓ
≢[1 + 1] ⍝ 3ₓ
[1+2;3 4] ⍝ [3 [3 4]]
[1 + 1;2] ⍝ 2 2
⍴[1⋄2] ⍝ [2]ₓ
⍴[[1]⋄[2]] ⍝ [2 1]ₓ
≢[+ - ×] ⍝ 3ₓ
["a":1 "b":2].b ⍝ 2
a←1 ⋄ [a:].a ⍝ 1A line break means ⋄, inside every kind of bracket and at the top level. A line break just after [, ( or {, just before ], ) or }, or next to ;, ⋄ or ? separates nothing.
m←[
1 2 3
4 5 6
]
⍴m ⍝ [2 3]ₓParentheses group, whatever they hold. With ⋄ inside, parentheses are used to write a vector of rows instead. Each row is a bracket list, even a row with one item. A trailing ⋄ gives a vector that holds one row. Rows in brackets form a flat array. Rows in parentheses form a nested vector.
(1 2)≡[1 2] ⍝ $t
(1+2)×3 ⍝ 9
(-)3 ⍝ ¯3
(4⋄4 5)≡[[4] [4 5]] ⍝ $t
(1 2⋄)≡[[1 2]] ⍝ $tBraces define a function. Inside braces, ⋄ separates statements. ; separates bodies. ? ends a body’s predicate. Dfns describes what each part means.
Outside brackets, parentheses and braces, ⋄ separates statements. A ; outside brackets and braces is a SYNTAX error.
Superscripts and subscripts
A superscript or subscript written straight after a token applies to that token. After a subject, a superscript binds more tightly than anything else. After a function, a superscript takes the whole function to its left, as a monadic operator does. A superscript or subscript that stands alone after a space applies to the run before it.
A subscript selects one position on the leading axis: x₁ is (1⌷x).
2 3² ⍝ 2 9
2 3 ² ⍝ 4 9
x←2 ⋄ y←3 ⋄ x y ² ⍝ 2 9
+↢3 ⁻¹ 10 ⍝ 7
[2 3]² ⍝ 4 9
+\⁻¹ 1 3 6 ⍝ 1 2 3
v←10 20 30 ⋄ v₋₁ ⍝ 30
2 3 ₁ ⍝ 3Assignment
The target of ← is the run just before it. The value is everything to the right of ←, up to the end of the statement or group. A target is one of these:
- a name
- a selection, such as
(1⌷v),v₁,v.[1]orT.x - a list of names, such as
[a b] - any of these followed by a function, for modified assignment
A space before ← makes the whole run before it the target, as parentheses would: [[0 2]]⌷v ←0 assigns to the selection. So does a space before the function of a modified assignment, as in x +←1. A named function goes in parentheses, because in a f←3 the target would be f: a(f)←3 is a←a f 3, at top level and in dfns. With nothing after the arrow, a(f)← is a←f a. A value can itself contain an assignment. In a←1 b←2, the value of a is the strand 1 b←2, which assigns 2 to b and gives 1 2. Separate independent assignments with ⋄. Assignment describes each kind of target.
v←10 20 30 ⋄ v₁←7 ⋄ v ⍝ 10 7 30
[a b]←3 4 ⋄ a×b ⍝ 12
a←1 ⋄ f←+ ⋄ a(f)←3 ⋄ a ⍝ 4
a←1 2 3 ⋄ a⌽← ⋄ a ⍝ 3 2 1
a←1 b←2 ⋄ a ⍝ 1 2Precedence
From tightest to loosest:
- within a run, right to left
- across runs, right to left
- assignment
← - the error guard
:: ⋄, which separates statements, and?, which ends a predicate;, which separates a dfn’s bodies
Spaces change grouping only at the first two levels.
Syntax highlighting
Use bpl code fences for ordinary names and hpl for Hanzi glyph spellings. The BPL TextMate grammar and its BPL (Hanzi) overlay work with TextMate-compatible editors and Shiki. scripts/prep.py generates both from the Quarto XML and shared glyph table. The Python and npm packages include bpl.tmLanguage.json and hpl.tmLanguage.json. Load both grammars to use hpl.