# BASIX — review, formal BNF grammar and C++ transpiler design

**Source reviewed:** `BASIX_specifikace.md` (the conversation also contains a duplicate-looking `BASIX_specifikace(1).md`).  
**Purpose:** consolidate the language features described by the specification, add a missing formal grammar, and define a practical transpilation architecture from BASIX to C++.

> **Important:** BNF describes syntactic structure. A number of BASIX rules are semantic/static constraints (for example “variable must already be declared”, label-collision rules, type compatibility, and `break` target validity), so those rules are specified separately below.

---

## 1. Review summary

The source document already defines a surprisingly complete language. It is not merely a sketch: it specifies primitive types, deterministic arithmetic semantics, arrays, multidimensional arrays, structured objects, functions, control-flow constructs, input/output, and a number of deliberately non-C++ semantics.

The most important implementation characteristics are:

- BASIX is compiled/transpiled to C++.
- The reference compiler is intended to be implemented in Python.
- GCC and Clang are supported targets; generated code uses `__int128`, so MSVC is not a supported target.
- `int` maps conceptually to a signed 64-bit BASIX value, while `real` maps to `double`.
- BASIX deliberately defines deterministic behavior where direct C++ would otherwise have undefined behavior.
- Statements are line-oriented: one statement per line; semicolons are not used as statement terminators.
- Expressions are normally enclosed in `[...]`.
- Arrays are 1-based and out-of-range indices are saturated rather than producing UB.
- `obj` values are structured values with per-instance member initialization.
- Functions have explicit `ret(...)` and `par(...)` declarations and support recursion.
- `and` / `or` are short-circuiting and return `int` 0/1.
- The four `if` modes (`[expr]`, `first`, `every`, `dive`) are semantically different.
- `break` can target labelled blocks and can therefore escape several nested blocks.

The specification explicitly states that the language should not produce C++ UB and uses saturation/deterministic fallbacks where needed. fileciteturn4file11L543-L563

### Missing piece

The main missing compiler-oriented artifact is a **formal grammar**. The prose specification gives syntax examples and local syntax rules, but a parser implementation needs one central grammar.

A second missing piece is a **compiler contract**: which BASIX construct becomes which C++ construct, and which parts must be implemented by a BASIX runtime library rather than emitted as a direct C++ operator.

---

# 2. Extracted language features

## 2.1 Lexical conventions

### Identifiers

- `[A-Za-z_][A-Za-z0-9_]*`
- case-sensitive
- cannot be a reserved keyword
- `first`, `every`, and `dive` are context-sensitive words rather than globally reserved identifiers. fileciteturn6file0L11-L41

### Statement termination

A statement ends at a newline. No semicolon is required. fileciteturn3file0L41-L48

### Reserved words

Core reserved words:

`var set code while loop for dowhile if case break continue obj max min input print fun ret par end return true false int real`

`first`, `every`, and `dive` are syntactically reserved only in the corresponding `if` position. fileciteturn3file2L129-L153

---

## 2.2 Types

Primitive types:

| BASIX | Target representation | Notes |
|---|---|---|
| `int` | `int64_t` plus widened intermediates | range `[-2^53, 2^53]` |
| `real` | `double` | BASIX range is also `[-2^53, 2^53]` |

The specification explicitly selects `int64_t` and `double` as the conceptual C++ representations. fileciteturn4file11L567-L576

Compound types:

- one-dimensional array: `(array, int)` / `(array, real)`
- multidimensional array: also represented by `(array, type)`
- object type: `(obj, Name)`

Arrays have fixed size and 1-based indexing. Out-of-range indices saturate to the nearest valid index. fileciteturn3file14L631-L661

---

## 2.3 Expressions and operators

Operators, highest to lowest precedence:

1. unary `-`, `not`
2. `*`, `/`, `%`
3. binary `+`, `-`
4. `<`, `<=`, `>`, `>=`
5. `==`, `!=`
6. `and`
7. `or`

Parentheses override precedence. BASIX has no bitwise operators. fileciteturn4file0L11-L37

Boolean operators are short-circuiting and return `int` 0/1. fileciteturn3file4L209-L219

Division/modulo by zero are defined:

- `x / 0` → `x`
- `x % 0` → zero of the target type

This must **not** be emitted as a raw C++ `/` or `%` without a guard. fileciteturn3file6L297-L313

---

## 2.4 Variables and assignment

Declaration:

```basix
var(int, x, [10])
var(real, y, [3.14])
var((array, int), a, [10])
var((array, real), b, {1.0, 2.0, 3.0})
var((obj, Point), p)
```

Assignment:

```basix
set(x, [expr])
set(a[3], [expr])
set(p.x, [expr])
```

The source specification also permits whole-array assignment/copying with element-wise conversion and truncation/zero filling. fileciteturn5file6L257-L289

---

## 2.5 Control flow

### `code`

Named compound block:

```basix
code(label)
    ...
end code
```

It can be empty and may contain arbitrary BASIX statements. fileciteturn7file1L69-L93

### `while`

```basix
while([condition])
    ...
end while
```

or labelled:

```basix
while(label; [condition])
    ...
end while
```

### `loop`

Arithmetic progression:

```basix
loop(i, ([from], [to], [step]))
    ...
end loop
```

or:

```basix
loop(label; i, ([from], [to], [step]))
    ...
end loop
```

The loop variable must already exist. fileciteturn6file9L180-L204

### `for`

Iterates over array indices and writes them into already-existing variables:

```basix
for(i, array)
    ...
end for
```

For multidimensional arrays:

```basix
for((i, j), matrix)
    ...
end for
```

The last index changes fastest. fileciteturn6file4L191-L222

### `dowhile`

```basix
dowhile([condition])
    ...
end dowhile
```

The condition is checked after the body. fileciteturn5file12L533-L556

### `break` / `continue`

Both accept an optional label:

```basix
break()
break(label)

continue()
continue(label)
```

A labelled `break` can cross intermediate blocks; a labelled `continue` targets a loop (`while`, `loop`, `for`, `dowhile`), not a plain `code`, `if`, or `case`. fileciteturn6file12L631-L639

---

## 2.6 Conditional constructs

BASIX has four `if` modes:

| Form | Meaning |
|---|---|
| `if([expr])` | choose the true/false case |
| `if(first)` | execute the first case whose condition is true |
| `if(every)` | execute every case whose condition is true |
| `if(dive)` | execute cases from the beginning until the first false condition |

The distinction is explicitly defined by the specification. fileciteturn7file8L403-L413

Example of ordinary `if`:

```basix
if([x > 0])
    case(true)
        print("positive")
    end case
    case(false)
        print("non-positive")
    end case
end if
```

For `first`, `every`, and `dive`, cases contain their own `[condition]`; a final catch-all `case()` is permitted where defined by the variant. fileciteturn7file10L527-L556

---

## 2.7 Arrays

One-dimensional:

```basix
var((array, real), values, [4])
set(values[1], [21.5])
set(values[4], [values[1] + values[2]])
```

Multidimensional:

```basix
var((array, real), matrix, ([rows], [cols]))
set(matrix[1][2], [2.5])
```

Indexing is 1-based. C++ indexing therefore requires a `-1` conversion or a runtime accessor. fileciteturn3file14L652-L661

Array bounds are saturated, e.g. index `-5` becomes `1`, and index `100` on a length-3 array becomes `3`. fileciteturn6file15L796-L808

---

## 2.8 `max` / `min`

For arrays:

```basix
max(a)
min(a)
```

return the highest/lowest valid index. For BASIX arrays, `min(a)` is therefore `1`. For multidimensional arrays, an overload can select a dimension:

```basix
max((matrix, [dimension]))
min((matrix, [dimension]))
```

Both return `int`. fileciteturn5file2L95-L124

---

## 2.9 Input / output

`input`:

```basix
input(x)
input(a)
```

Input errors and EOF produce deterministic default values rather than runtime errors; numeric values outside the BASIX range are saturated. fileciteturn3file16L731-L754

`print` supports scalar expressions, string literals, arrays, and structured output forms. A multidimensional array passed in a context where only a one-dimensional output is defined is silently omitted rather than causing a runtime failure. fileciteturn5file10L465-L486

---

## 2.10 `obj`

Definition:

```basix
obj(Point)
    var(int, x, [0])
    var(int, y, [0])
end obj
```

Instance:

```basix
var((obj, Point), p)
```

Member access:

```basix
p.x
p.y
```

Member assignment:

```basix
set(p.x, [10])
```

Member initializers are evaluated independently for every instance, at instance creation time. fileciteturn4file4L217-L235

Objects do not implicitly convert to scalars, arrays, or other objects; invalid object conversions use the specified “silent omission” behavior. fileciteturn4file4L237-L257

---

## 2.11 Functions

Definition:

```basix
fun(name)
ret(...)
par(...)
par(...)
...
    ...
end fun
```

`ret` and `par` use the same type/name syntax as variable declarations. fileciteturn6file11L574-L600

Return:

```basix
return
```

There is no return expression. The current value of `ret` is the function result. Falling through to `end fun` has the same effect. fileciteturn6file11L607-L618

Parameter passing:

- `int` / `real`: by value
- arrays / objects: by reference

Every call, including recursive calls, gets an independent `ret` and parameter frame. fileciteturn6file16L840-L859

Functions exist at one global definition level and can be called independently of source order; recursion is supported. fileciteturn6file5L263-L280

---

# 3. Formal BNF grammar

## 3.1 Grammar conventions

The following grammar is intended for a parser implementation.

- `ε` means empty.
- Quoted strings are literal terminals.
- `<...>` denotes a non-terminal.
- `NL` is a logical newline.
- Whitespace inside constructs such as `var(...)` may be ignored by the lexer, but the statement-level `NL` is significant.
- Semantic constraints are intentionally not encoded in BNF where doing so would make the grammar unnecessarily context-sensitive.

## 3.2 Lexical grammar

```bnf
<letter> ::= "A" | "B" | ... | "Z" | "a" | "b" | ... | "z"

<digit> ::= "0" | "1" | ... | "9"

<underscore> ::= "_"

<identifier>
    ::= <letter> <identifier-tail>
     | <underscore> <identifier-tail>

<identifier-tail>
    ::= <letter> <identifier-tail>
     | <digit> <identifier-tail>
     | <underscore> <identifier-tail>
     | ε

<int-literal>
    ::= <digit>+

<real-literal>
    ::= <digit>+ "." <digit>+
     | <digit>+ "." <digit>* <exponent>
     | <digit>+ <exponent>

<exponent>
    ::= ("e" | "E") ["+" | "-"] <digit>+

<string-literal>
    ::= '"' <string-char>* '"'
```

A production such as `<digit>+` is EBNF shorthand for one-or-more repetitions; a parser implementation can expand it to ordinary BNF if strict Chomsky-style notation is required.

---

## 3.3 Program structure

The language is line-oriented:

```bnf
<program>
    ::= <top-level-item>*

<top-level-item>
    ::= <statement> <NL>
     | <function-definition> <NL>*
     | <object-definition> <NL>*

<statement>
    ::= <simple-statement>
     | <compound-statement>
```

In an implementation, it is preferable for the lexer/parser to normalize CRLF/CR to `NL`.

---

## 3.4 Types

```bnf
<type>
    ::= "int"
     | "real"
     | "(" "array" "," <scalar-type> ")"
     | "(" "obj" "," <identifier> ")"

<scalar-type>
    ::= "int"
     | "real"
```

---

## 3.5 Expressions

```bnf
<expression>
    ::= <or-expression>

<or-expression>
    ::= <and-expression>
        ( "or" <and-expression> )*

<and-expression>
    ::= <equality-expression>
        ( "and" <equality-expression> )*

<equality-expression>
    ::= <relational-expression>
        ( ("==" | "!=") <relational-expression> )*

<relational-expression>
    ::= <additive-expression>
        ( ("<" | "<=" | ">" | ">=") <additive-expression> )*

<additive-expression>
    ::= <multiplicative-expression>
        ( ("+" | "-") <multiplicative-expression> )*

<multiplicative-expression>
    ::= <unary-expression>
        ( ("*" | "/" | "%") <unary-expression> )*

<unary-expression>
    ::= ("-" | "not") <unary-expression>
     | <primary-expression>

<primary-expression>
    ::= <literal>
     | <reference>
     | "(" <expression> ")"

<literal>
    ::= <int-literal>
     | <real-literal>
     | "true"
     | "false"
     | <string-literal>

<reference>
    ::= <identifier> <reference-suffix>*

<reference-suffix>
    ::= "[" <expression> "]"
     | "." <identifier>
```

This grammar follows the precedence documented by the specification. fileciteturn4file0L11-L33

---

## 3.6 Bracketed expressions and casts

```bnf
<bracket-expression>
    ::= "[" <expression> "]"

<cast-expression>
    ::= "[" <type> ";" <expression> "]"

<value-expression>
    ::= <bracket-expression>
     | <cast-expression>
```

Expressions are normally enclosed in `[]`. fileciteturn7file5L276-L289

---

## 3.7 Variable declarations

```bnf
<var-statement>
    ::= "var" "(" <type> "," <identifier> ")"
     | "var" "(" <type> "," <identifier> "," <value-expression> ")"
     | "var" "(" <array-type> "," <identifier> "," <array-size-list> ")"
     | "var" "(" <array-type> "," <identifier> "," <array-initializer> ")"

<array-type>
    ::= "(" "array" "," <scalar-type> ")"

<array-size-list>
    ::= "[" <expression> "]"
     | "(" <value-expression> "," <value-expression-list> ")"

<value-expression-list>
    ::= <value-expression>
     | <value-expression> "," <value-expression-list>

<array-initializer>
    ::= "{" <initializer-list>? "}"

<initializer-list>
    ::= <initializer>
     | <initializer> "," <initializer-list>

<initializer>
    ::= <literal>
     | <value-expression>
```

The semantic checker must distinguish the one-dimensional and multidimensional array declaration forms described in the specification.

---

## 3.8 Assignment

```bnf
<set-statement>
    ::= "set" "(" <lvalue> "," <value-expression> ")"

<lvalue>
    ::= <identifier> <lvalue-suffix>*

<lvalue-suffix>
    ::= "[" <expression> "]"
     | "." <identifier>
```

The semantic checker determines whether the target is a scalar, array element, whole array, or object member and selects the appropriate assignment semantics.

---

## 3.9 Compound statements

```bnf
<compound-statement>
    ::= <code-statement>
     | <while-statement>
     | <loop-statement>
     | <for-statement>
     | <dowhile-statement>
     | <if-statement>

<block-label>
    ::= <identifier>

<optional-label-prefix>
    ::= ε
     | <identifier> ";"

<statement-list>
    ::= <statement>*

<code-statement>
    ::= "code" "(" <identifier> ")" <NL>
        <statement-list>
        "end" "code"

<while-statement>
    ::= "while" "(" <optional-label-prefix> <value-expression> ")" <NL>
        <statement-list>
        "end" "while"

<loop-statement>
    ::= "loop" "(" <optional-label-prefix> <identifier> ","
        "(" <value-expression> "," <value-expression> "," <value-expression> ")" ")" <NL>
        <statement-list>
        "end" "loop"

<for-statement>
    ::= "for" "(" <for-index-list> "," <identifier> ")" <NL>
        <statement-list>
        "end" "for"

<for-index-list>
    ::= <identifier>
     | "(" <identifier> "," <identifier-list> ")"

<identifier-list>
    ::= <identifier>
     | <identifier> "," <identifier-list>

<dowhile-statement>
    ::= "dowhile" "(" <optional-label-prefix> <value-expression> ")" <NL>
        <statement-list>
        "end" "dowhile"
```

The parser may represent all these constructs using a common `Block` AST node with an optional label.

---

## 3.10 `break`, `continue`, `return`

```bnf
<break-statement>
    ::= "break" "(" ")"
     | "break" "(" <identifier> ")"

<continue-statement>
    ::= "continue" "(" ")"
     | "continue" "(" <identifier> ")"

<return-statement>
    ::= "return"
```

`return` has no argument. fileciteturn6file11L607-L618

---

## 3.11 Conditional grammar

### Ordinary `if`

```bnf
<if-statement>
    ::= <ordinary-if>
     | <first-if>
     | <every-if>
     | <dive-if>

<ordinary-if>
    ::= "if" "(" <optional-label-prefix> <value-expression> ")" <NL>
        <boolean-case> <boolean-case>
        "end" "if"

<boolean-case>
    ::= "case" "(" <optional-label> ";" <boolean-value> ")" <NL>
        <statement-list>
        "end" "case"

<boolean-value>
    ::= "true"
     | "false"

<optional-label>
    ::= ε
     | <identifier>
```

### `first`

```bnf
<first-if>
    ::= "if" "(" <optional-label-prefix> "first" ")" <NL>
        <conditional-case>+
        [ <catch-all-case> ]
        "end" "if"
```

### `every`

```bnf
<every-if>
    ::= "if" "(" <optional-label-prefix> "every" ")" <NL>
        <conditional-case>+
        [ <catch-all-case> ]
        "end" "if"
```

### `dive`

```bnf
<dive-if>
    ::= "if" "(" <optional-label-prefix> "dive" ")" <NL>
        <conditional-case>+
        [ <catch-all-case> ]
        "end" "if"
```

Cases:

```bnf
<conditional-case>
    ::= "case" "(" <optional-label> ";" <value-expression> ")" <NL>
        <statement-list>
        "end" "case"

<catch-all-case>
    ::= "case" "(" <optional-label> ")" <NL>
        <statement-list>
        "end" "case"
```

The `catch-all` case must be last and there may be at most one. The `dive` form requires at least one case. fileciteturn7file9L492-L516

---

## 3.12 Functions

```bnf
<function-definition>
    ::= "fun" "(" <identifier> ")" <NL>
        <return-declaration>*
        <parameter-declaration>*
        <statement-list>
        "end" "fun"

<return-declaration>
    ::= "ret" "(" <type> "," <identifier> ")"
     | "ret" "(" <type> "," <identifier> "," <value-expression> ")"

<parameter-declaration>
    ::= "par" "(" <type> "," <identifier> ")"
     | "par" "(" <type> "," <identifier> "," <value-expression> ")"
```

The specification permits the same declaration form for `ret` and `par` as for `var`. fileciteturn6file11L574-L600

Function calls are statement-level constructs:

```bnf
<call-statement>
    ::= <identifier> "(" <argument-list>? ")"

<argument-list>
    ::= <argument>
     | <argument> "," <argument-list>

<argument>
    ::= <value-expression>
     | "[" <identifier> "]"
```

The distinction between a value argument and an explicit reference argument is a semantic rule. Arrays/objects are passed by reference. fileciteturn6file5L263-L280

---

## 3.13 Objects

```bnf
<object-definition>
    ::= "obj" "(" <identifier> ")" <NL>
        <object-member-declaration>*
        "end" "obj"

<object-member-declaration>
    ::= <var-statement>
```

An object member may itself be another object type.

---

## 3.14 Built-ins

```bnf
<input-statement>
    ::= "input" "(" <identifier> ")"

<print-statement>
    ::= "print" "(" <print-argument-list>? ")"

<print-argument-list>
    ::= <print-argument>
     | <print-argument> "," <print-argument-list>

<print-argument>
    ::= <string-literal>
     | <value-expression>
     | <identifier>

<max-expression>
    ::= "max" "(" <identifier> ")"
     | "max" "(" "(" <identifier> "," <value-expression> ")" ")"

<min-expression>
    ::= "min" "(" <identifier> ")"
     | "min" "(" "(" <identifier> "," <value-expression> ")" ")"
```

`max`/`min` have array-specific semantics rather than being ordinary C++ `std::max`/`std::min`. fileciteturn5file15L684-L710

---

# 4. Static semantic rules

The parser should not attempt to encode these entirely in BNF.

## 4.1 Name resolution

Maintain separate namespaces for:

- variables / parameters / members
- functions
- object types

Reject identifiers that collide with reserved words.

Function names are globally scoped and function definitions are independent of source order. fileciteturn6file5L274-L280

## 4.2 Declaration-before-use

A variable used as:

- assignment target
- loop variable
- array
- object
- function parameter reference

must be known to the current semantic environment.

## 4.3 Type checking

The semantic analyzer should compute a type for every expression:

```text
int
real
array<int>
array<real>
array<int, D>
array<real, D>
obj<T>
```

and insert implicit conversions where BASIX permits them.

## 4.4 Label checking

For every nested block, maintain a stack of active labels.

Reject a label if it collides with an active enclosing block label. Sibling blocks may reuse a label. The specification explicitly defines a common collision domain for `code`, `while`, `loop`, `for`, `dowhile`, `if`, and `case`. fileciteturn5file12L560-L573

## 4.5 `break` target resolution

`break()` targets the nearest breakable enclosing block.

`break(label)` searches the active labelled-block stack and unwinds all intermediate blocks.

## 4.6 `continue` target resolution

`continue()` targets the nearest loop.

`continue(label)` must resolve to an enclosing `while`, `loop`, `for`, or `dowhile`.

## 4.7 Function return rules

Every function has one `ret` value. `return` exits the current function. A function that reaches `end fun` returns the current `ret`.

Each runtime call needs its own storage for `ret` and parameters.

---

# 5. Proposed transpiler architecture

## 5.1 Pipeline

Recommended architecture:

```text
                 BASIX source
                      |
                      v
              +---------------+
              | Lexer         |
              +---------------+
                      |
                      v
              +---------------+
              | Parser        |
              | -> AST        |
              +---------------+
                      |
                      v
              +---------------+
              | Name resolver |
              | + type checker|
              +---------------+
                      |
                      v
              +---------------+
              | Lowering      |
              | / desugaring  |
              +---------------+
                      |
                      v
              +---------------+
              | C++ emitter    |
              +---------------+
                      |
                      v
               generated .cpp
                      |
                      v
                 g++ / clang++
```

A **runtime support library** should sit underneath the generated C++:

```text
generated BASIX C++
        |
        +---- basix_runtime.hpp
        |
        +---- basix_runtime.cpp
        |
        +---- standard C++ library
```

This is preferable to generating every safety rule inline.

---

# 6. C++ runtime representation

## 6.1 Scalar values

Suggested aliases:

```cpp
using basix_int = std::int64_t;
using basix_real = double;
```

But arithmetic should not directly use `int64_t` because BASIX semantics require saturation and widened intermediate arithmetic.

Use helpers:

```cpp
basix_int basix_add(basix_int a, basix_int b);
basix_int basix_sub(basix_int a, basix_int b);
basix_int basix_mul(basix_int a, basix_int b);
basix_int basix_div(basix_int a, basix_int b);
basix_int basix_mod(basix_int a, basix_int b);
```

and corresponding mixed/real helpers.

The specification explicitly calls for widened arithmetic and deterministic saturation, so `__int128` is appropriate for integer intermediates. GCC and Clang support it and are the intended targets. fileciteturn5file0L11-L19

## 6.2 Saturation

Centralize saturation:

```cpp
constexpr long double BASIX_MIN = -(1LL << 53);
constexpr long double BASIX_MAX =  (1LL << 53);

basix_int saturate_int(__int128 x);
basix_real saturate_real(double x);
```

Do not scatter range checks throughout the emitter.

---

# 7. Array runtime

Recommended abstraction:

```cpp
template<class T>
class BasixArray {
public:
    std::vector<T> data;

    T& at_basix(basix_int index);
    const T& at_basix(basix_int index) const;

    basix_int min_index() const; // always 1
    basix_int max_index() const; // size
};
```

Index conversion:

```cpp
cpp_index = clamp(basix_index, 1, size) - 1;
```

This directly implements BASIX's 1-based saturated indexing. fileciteturn3file14L652-L661

For multidimensional arrays:

```cpp
template<class T>
class BasixNDArray {
    std::vector<T> data;
    std::vector<basix_int> dimensions;
};
```

Linearization:

```text
(i1, i2, ..., iD)
    -> (((i1-1) * d2 + (i2-1)) * d3 + ... + (iD-1))
```

with the last dimension changing fastest, matching the documented `for` traversal. fileciteturn6file4L217-L222

---

# 8. Object representation

Each BASIX object type can become a C++ `struct`.

BASIX:

```basix
obj(Point)
    var(int, x, [0])
    var(int, y, [0])
end obj
```

Generated C++ conceptually:

```cpp
struct Point {
    basix_int x;
    basix_int y;

    Point()
        : x(basix_int(0)),
          y(basix_int(0))
    {}
};
```

If a member initializer references another variable, the initializer must be emitted in the constructor body so it is evaluated **for every instance**, at construction time.

For example, the documented behavior where two instances observe different values of an external variable requires constructor-time evaluation, not a C++ class-member constant. fileciteturn4file4L229-L235

---

# 9. Variable translation

| BASIX | C++ |
|---|---|
| `var(int, x, [expr])` | `basix_int x = basix_eval_int(expr);` |
| `var(real, x, [expr])` | `basix_real x = basix_eval_real(expr);` |
| `var((array,int), a, [n])` | `BasixArray<basix_int> a(n);` |
| `var((array,real), a, [n])` | `BasixArray<basix_real> a(n);` |
| `var((obj,T), x)` | `T x{};` |

For arrays, initialization should call runtime helpers rather than raw `std::vector` operations where BASIX's zero-fill/truncation semantics matter.

---

# 10. Assignment translation

## Scalar

```basix
set(x, [a + b])
```

becomes conceptually:

```cpp
x = basix_assign_int(basix_add(...));
```

## Array element

```basix
set(a[i], [x])
```

becomes:

```cpp
a.at_basix(i) = basix_convert<element_type>(x);
```

## Object member

```basix
set(p.x, [value])
```

becomes:

```cpp
p.x = ...;
```

## Whole-array assignment

```basix
set(a, [b])
```

must use a runtime copy/conversion helper because BASIX allows different lengths and different element types:

```cpp
basix_copy_array(a, b);
```

The helper performs:

1. linearized traversal;
2. element-wise `int`/`real` conversion;
3. zero-fill if source is shorter;
4. truncation if source is longer.

This matches the documented semantics. fileciteturn5file13L585-L617

---

# 11. Expression translation

## 11.1 Arithmetic

Do **not** blindly map:

```text
a + b -> a + b
```

because BASIX arithmetic has saturation and defined division/modulo semantics.

Instead:

```text
BASIX expression       C++ emission

a + b                   basix_add(a, b)
a - b                   basix_sub(a, b)
a * b                   basix_mul(a, b)
a / b                   basix_div(a, b)
a % b                   basix_mod(a, b)
```

The optimizer may later inline these helpers.

## 11.2 Comparisons

Comparisons can generally map to C++ after operands have been converted according to BASIX's type hierarchy:

```text
a < b   -> (a < b)
a == b  -> (a == b)
```

The result should be represented as BASIX `int` 0/1.

## 11.3 Logical operators

Never emit:

```cpp
basix_and(a, b)
```

if that would eagerly evaluate both arguments.

Instead emit native C++ short-circuiting:

```cpp
(a_truth(a) && b_truth(b)) ? 1 : 0
```

and:

```cpp
(a_truth(a) || b_truth(b)) ? 1 : 0
```

For `not`:

```cpp
(!basix_truth(a)) ? 1 : 0
```

This preserves the documented short-circuit semantics. fileciteturn3file4L209-L219

---

# 12. Control-flow translation

## 12.1 `while`

BASIX:

```basix
while([i < 10])
    set(i, [i + 1])
end while
```

C++:

```cpp
while (basix_truth(basix_lt(i, 10))) {
    i = basix_add(i, 1);
}
```

## 12.2 `dowhile`

Direct mapping:

```cpp
do {
    ...
} while (condition);
```

This is safe because the condition is evaluated after the body.

## 12.3 `loop`

Do not rely on C++ integer overflow.

Conceptually:

```cpp
for (i = start;
     basix_loop_continue(i, end, step);
     i = basix_add(i, step)) {
    ...
}
```

A runtime helper should define the exact positive/negative step and endpoint behavior.

## 12.4 `for`

BASIX:

```basix
for((i, j), matrix)
    ...
end for
```

Can lower to nested loops:

```cpp
for (i = 1; i <= matrix.dim(0); ++i) {
    for (j = 1; j <= matrix.dim(1); ++j) {
        ...
    }
}
```

For arbitrary dimensions, the lowering pass can generate nested loops dynamically based on the statically known array rank.

---

# 13. Labelled `break` / `continue`

Plain C++ labels are not a good direct representation because BASIX labels are associated with structured blocks and may target multiple levels.

Recommended approach: **lower labelled control flow into generated state variables**, or generate a small control-flow state machine for functions containing labelled exits.

For example:

```text
break(outer)
```

can lower to:

```cpp
_control = Control::BreakOuter;
goto end_outer;
```

However, `goto` must be generated carefully because C++ forbids jumping across initialization boundaries.

A safer design is:

```cpp
enum class BasixControl {
    Normal,
    BreakOuter,
    ContinueOuter,
    ...
};
```

with each lowered block checking/propagating the state:

```cpp
if (_control != BasixControl::Normal)
    break;
```

For the first implementation, the state-machine approach is recommended because it preserves nested BASIX block semantics without relying on fragile C++ `goto` placement.

---

# 14. Conditional translation

## Ordinary `if`

BASIX:

```basix
if([condition])
    case(true)
        ...
    end case
    case(false)
        ...
    end case
end if
```

C++:

```cpp
if (basix_truth(condition)) {
    ...
} else {
    ...
}
```

## `first`

```basix
if(first)
    case([a])
        A
    end case
    case([b])
        B
    end case
    case()
        C
    end case
end if
```

C++:

```cpp
if (truth(a)) {
    A
} else if (truth(b)) {
    B
} else {
    C
}
```

## `every`

```cpp
if (truth(a)) { A; }
if (truth(b)) { B; }
if (truth(c)) { C; }
```

Only matching cases execute.

## `dive`

```cpp
bool _dive = true;

if (_dive && truth(a)) {
    A;
} else {
    _dive = false;
}

if (_dive && truth(b)) {
    B;
} else {
    _dive = false;
}

if (_dive && truth(c)) {
    C;
} else {
    _dive = false;
}
```

A simpler and clearer lowering is:

```cpp
do {
    if (!truth(a)) break;
    A;

    if (!truth(b)) break;
    B;

    if (!truth(c)) break;
    C;
} while (false);
```

The latter closely matches “execute from the beginning until the first false condition”. fileciteturn7file8L408-L413

---

# 15. Function translation

BASIX:

```basix
fun(add)
ret(int, result, [0])
par(int, a)
par(int, b)

set(result, [a + b])
end fun
```

Generated C++ can be:

```cpp
basix_int add(basix_int a, basix_int b) {
    basix_int result = 0;
    result = basix_add(a, b);
    return result;
}
```

For an array/object reference parameter:

```cpp
basix_int f(BasixArray<basix_int>& a)
```

or:

```cpp
Point& f(Point& p)
```

as appropriate.

The exact generated signature should be determined by semantic type information, not by textual pattern matching.

---

# 16. Function calls

A BASIX call is a statement:

```basix
foo([x], [y])
```

becomes:

```cpp
foo(x, y);
```

If a function result is assigned through the special BASIX mechanism, emit the result into the target after applying the appropriate BASIX conversion.

Do not treat function calls as generic expression nodes unless the language specification explicitly permits them in the relevant expression position. The specification states that calls are normally standalone statements and that the return value is either discarded or captured by the specified `set` form. fileciteturn6file5L263-L273

---

# 17. Input translation

The runtime should own parsing:

```cpp
basix_input(x);
basix_input_array(a);
```

Do not emit raw:

```cpp
std::cin >> x;
```

because BASIX has different behavior for:

- invalid numeric input,
- EOF,
- out-of-range values,
- arrays,
- default zero filling.

The runtime parser can read a line/token stream and then apply BASIX conversion/saturation rules. fileciteturn3file16L731-L754

---

# 18. Output translation

Use:

```cpp
basix_print(...);
```

rather than raw `std::cout`.

The runtime should implement:

- scalar formatting;
- strings;
- one-dimensional arrays;
- supported multidimensional output forms;
- newline behavior;
- silent omission rules.

This keeps the emitter simple and makes the runtime semantics testable independently.

---

# 19. `max` / `min` translation

Do **not** map these to `std::max` / `std::min`.

Examples:

```basix
max(a)
min(a)
```

should become:

```cpp
basix_array_max_index(a);
basix_array_min_index(a);
```

For multidimensional arrays:

```basix
max((matrix, [2]))
```

should become:

```cpp
basix_array_dim_max(matrix, 2);
```

The helpers return BASIX `int`.

---

# 20. AST design

Recommended core nodes:

```text
Program
FunctionDef
ObjectDef

VarDecl
Assignment
FunctionCall
Input
Print

Block
While
Loop
For
DoWhile

If
Case

Break
Continue
Return

BinaryExpr
UnaryExpr
Literal
VariableRef
ArrayAccess
MemberAccess
Cast
MaxExpr
MinExpr
```

Each expression node should carry semantic type information after type checking.

Example:

```text
BinaryExpr("+",
    VariableRef("x"): int,
    Literal(3): int
): int
```

After lowering:

```text
BasixAdd(
    x,
    3
): int
```

This separation is valuable because it prevents the C++ emitter from having to rediscover BASIX semantics.

---

# 21. Recommended compiler passes

## Pass 1 — Lexing

Input:

```text
var(int, x, [10])
```

Output tokens:

```text
VAR LPAREN INT COMMA IDENT LPAREN? ...
```

The lexer must preserve `NL`.

## Pass 2 — Parsing

Build an AST.

At this stage:

- syntax errors
- missing `end`
- malformed parentheses
- malformed expressions
- malformed cases

are diagnosed.

## Pass 3 — Symbol collection

Collect:

- object type definitions
- function definitions
- global symbols

This allows functions to be referenced independently of source order.

## Pass 4 — Name resolution

Resolve:

- variables
- members
- functions
- object types
- labels

## Pass 5 — Type checking

Check:

- scalar conversions
- array element types
- object member access
- function argument types
- `max`/`min`
- array rank
- valid `for` variables
- `break`/`continue` context

## Pass 6 — Lowering

Convert high-level BASIX constructs into a smaller internal IR:

```text
BASIX AST
   |
   +-- first/every/dive -> ordinary conditional control flow
   +-- for              -> loop IR
   +-- loop             -> counted loop IR
   +-- array access     -> runtime accessor
   +-- labelled exits   -> control-flow IR
   +-- BASIX arithmetic -> runtime arithmetic nodes
```

## Pass 7 — C++ code generation

Generate:

1. runtime include;
2. object structs;
3. function declarations;
4. function definitions;
5. main/program body;
6. helper metadata if needed.

## Pass 8 — C++ compilation

Invoke:

```text
g++ -std=c++20 generated.cpp -o program
```

or:

```text
clang++ -std=c++20 generated.cpp -o program
```

The exact minimum C++ standard should be fixed by the implementation project; C++20 is a reasonable baseline for the proposed implementation.

---

# 22. Suggested generated-file structure

```cpp
#include <cstdint>
#include <cmath>
#include <iostream>
#include <vector>
#include <string>
#include "basix_runtime.hpp"

// generated object types
struct Point {
    ...
};

// function prototypes
basix_int add(...);
...

// function definitions
basix_int add(...) {
    ...
}

// BASIX top-level program
int main() {
    ...
    return 0;
}
```

---

# 23. Runtime API proposal

A first runtime header could expose:

```cpp
namespace basix {

using int_t = std::int64_t;
using real_t = double;

int_t add(int_t, int_t);
int_t sub(int_t, int_t);
int_t mul(int_t, int_t);
int_t div(int_t, int_t);
int_t mod(int_t, int_t);

real_t add(real_t, real_t);
real_t sub(real_t, real_t);
real_t mul(real_t, real_t);
real_t div(real_t, real_t);
real_t mod(real_t, real_t);

int_t to_int(real_t);
real_t to_real(int_t);

bool truth(int_t);
bool truth(real_t);

template<class T>
class Array;

template<class T>
class NDArray;

template<class T>
void input(T&);

template<class T>
void print(const T&);

}
```

The actual API can be refined after the AST/type system is implemented.

---

# 24. Testing strategy

The compiler should use three layers of tests.

## 24.1 Parser tests

Each grammar construct gets:

- valid example;
- invalid example;
- boundary syntax example.

Examples:

```text
var(int, x, [1])
var(real, x, [1.5])
while([x < 5]) ... end while
for((i,j), matrix) ... end for
if(first) ... end if
```

## 24.2 Semantic tests

Examples:

- use of undeclared variable;
- duplicate active label;
- invalid `break(label)`;
- invalid `continue(label)`;
- object/scalar conversion;
- invalid array rank;
- wrong function parameter type.

## 24.3 Golden transpilation tests

For every BASIX input:

```text
source.basix
```

store expected generated:

```text
source.cpp
```

and expected program output.

This is especially important for the deliberately non-C++ semantics.

---

# 25. Critical semantic tests

The following should be mandatory because direct C++ translation can be wrong.

### Division by zero

```basix
var(int, x, [10])
var(int, y, [x / 0])
```

Expected:

```text
y == 10
```

### Modulo by zero

```text
10 % 0 == 0
```

### Array indexing

```basix
var((array, int), a, {10, 20, 30})
var(int, x, [a[-5]])
var(int, y, [a[100]])
```

Expected:

```text
x == 10
y == 30
```

### Short-circuit

```basix
false and ...
true or ...
```

The right side must not execute.

### Function recursion

Every recursive call must have independent `ret` and `par` storage. fileciteturn6file16L840-L851

### Object initialization

An object's member initializer must execute at instance construction time.

### Array copy

A shorter source must zero-fill the destination; a longer source must be truncated. fileciteturn5file13L591-L610

---

# 26. Implementation priorities

A practical implementation order is:

### Phase 1 — Core language

1. lexer
2. expression parser
3. `var`
4. `set`
5. `print`
6. `input`
7. scalar arithmetic
8. `while`
9. `if`

### Phase 2 — Arrays

10. one-dimensional arrays
11. multidimensional arrays
12. saturated indexing
13. `for`
14. `max` / `min`

### Phase 3 — Advanced control flow

15. `loop`
16. `dowhile`
17. `code`
18. labelled `break`
19. labelled `continue`
20. `first`
21. `every`
22. `dive`

### Phase 4 — Data abstraction

23. `obj`
24. nested objects
25. object member access

### Phase 5 — Functions

26. `fun`
27. `ret`
28. `par`
29. by-value/by-reference calling
30. recursion
31. function declarations independent of source order

### Phase 6 — Robustness

32. semantic diagnostics
33. golden tests
34. compiler optimization
35. generated-code formatting
36. GCC/Clang CI

---

# 27. Review conclusions

The source specification is sufficiently detailed to serve as the semantic reference for a first BASIX compiler. The main missing artifacts are not new language features but **formalization and implementation structure**.

The recommended division is:

```text
BASIX specification
        |
        +--> BNF grammar
        |
        +--> static semantic rules
        |
        +--> runtime semantic rules
        |
        v
   Python compiler
        |
        +--> AST
        +--> type checker
        +--> lowering
        +--> C++ emitter
        |
        v
 generated C++
        |
        +--> basix_runtime.hpp/.cpp
        |
        v
     GCC / Clang
```

The most important design decision is **not to translate BASIX mechanically into equivalent-looking C++**. Many BASIX rules intentionally differ from C++: saturated numeric behavior, defined division by zero, 1-based saturated array indexing, special array copying, short-circuit boolean result typing, object initialization timing, and labelled control-flow semantics.

Those differences should be centralized in a small runtime library and a lowering pass. This makes the compiler easier to verify and ensures that the generated C++ remains ordinary, readable C++ while retaining BASIX semantics.

---

## Appendix A — compact grammar checklist

A parser implementation should cover at least:

- [x] identifiers
- [x] `int`, `real`
- [x] array/object types
- [x] integer/real/boolean/string literals
- [x] unary operators
- [x] arithmetic operators
- [x] comparisons
- [x] `and` / `or`
- [x] parentheses
- [x] casts
- [x] variable declaration
- [x] scalar assignment
- [x] array indexing
- [x] multidimensional indexing
- [x] member access
- [x] `code`
- [x] `while`
- [x] `loop`
- [x] `for`
- [x] `dowhile`
- [x] `if`
- [x] `first`
- [x] `every`
- [x] `dive`
- [x] `case`
- [x] `break`
- [x] `continue`
- [x] `obj`
- [x] `fun`
- [x] `ret`
- [x] `par`
- [x] `return`
- [x] `input`
- [x] `print`
- [x] `max`
- [x] `min`

## Appendix B — source traceability

This document is derived from the supplied BASIX specification, including:

- overview, target toolchain, and type model fileciteturn5file0L11-L19
- lexical rules and keywords fileciteturn3file2L123-L153
- expression precedence and operators fileciteturn4file0L11-L37
- arrays and 1-based indexing fileciteturn3file14L631-L661
- control flow and loop semantics fileciteturn6file9L180-L204
- conditional variants fileciteturn7file8L403-L413
- object semantics fileciteturn4file4L217-L257
- function semantics fileciteturn6file11L574-L618
- recursive-call storage semantics fileciteturn6file16L840-L859
- input/output semantics fileciteturn3file16L731-L754
