Language Reference
Complete reference for the Soma scripting language — declarations, types, operators, control flow, and indexing semantics. Soma is a line-oriented language where all values are either a scalar or a Float64Array time-series. Comments start with #.
Declarations
Declarations must appear at the top of the script, before any executable statements. The parser collects them to determine how many input handles to render on the node widget and which output channels to expose downstream.
inputinput <name>Declares a named input channel. The name must match the edge handle that wires an upstream series into this node. At runtime the value is a Float64Array of bar values in chronological order (index 0 = oldest bar).
input pricesoutputoutput <name>Declares a named output array. The runtime pre-allocates a Float64Array of the same length as the primary input. Assign values inside loops via output[i] = expr. Unassigned positions remain NaN. The node renders one output handle per declared output.
output upperparamparam <name> as <type> [= default]Declares a user-configurable parameter. Type must be int, float, or bool. The optional default is a numeric literal. Params are exposed in the node's property panel and passed to the script as read-only scalars.
param period as int = 20letlet <name> [as <type>] = <expr>Declares a local variable in the current scope. Type is inferred from the right-hand expression unless an explicit type annotation is given. Variables declared inside a block (for/if/while) are scoped to that block. Re-assignment is allowed.
let alpha = 2.0 / (period + 1)Type System
Soma has a minimal type system. All time-series data is stored as Float64Array. Scalars are JS numbers. The type annotation on let and param controls how the compiler treats the variable — it does not change the underlying JS representation.
| Type | Context | JS storage | Notes |
|---|---|---|---|
| num | input / output | Float64Array | Default type for input/output channels. |
| num[] | input / output | Float64Array | Alias for num. |
| int | param / let | number (truncated) | Integer. Values are Math.trunc() at runtime. |
| float | param / let | number | Floating-point scalar. |
| bool | param / let | number (0 or 1) | Boolean. true = 1, false = 0. |
| int[] | let | Float64Array | Typed local array. Elements treated as integers. |
| float[] | let | Float64Array | Typed local float array. |
| bool[] | let | Float64Array | Typed local boolean array (0/1 values). |
sma() are represented as NaN. Use prune(x) to strip NaN values, or isnan(x) to test element-wise.Operators
All binary operators work element-wise when either operand is a Float64Array. Scalar-array broadcasting is supported: the scalar is applied to every element. The result length is the shorter of the two operand lengths.
| + | Addition |
| - | Subtraction (also unary negation: -x) |
| * | Multiplication |
| / | Division |
| % | Modulo |
| ^ | Exponentiation (right-associative) |
| > | Greater than |
| < | Less than |
| >= | Greater than or equal |
| <= | Less than or equal |
| == | Equal |
| != | Not equal |
| && / and | Logical AND |
| || / or | Logical OR |
| ! / not | Logical NOT (unary) |
| & | Bitwise AND |
| | | Bitwise OR |
| ~ | Bitwise NOT (unary) |
| >> | Right shift |
| << | Left shift |
| = | Simple assignment |
| += | Add and assign |
| -= | Subtract and assign |
| *= | Multiply and assign |
| /= | Divide and assign |
| %= | Modulo and assign |
| cond ? a : b | Ternary conditional |
| a ?? b | Null-coalesce: b when a is NaN or null |
| arr[n] | Array index (see Indexing below) |
Indexing Semantics
Array indexing uses a look-back convention inside a for loop body:
prices[0]The current bar's value (same as v in for (i, v) in prices).
prices[1]The previous bar's value.
prices[n]The value n bars back from the current bar.
Outside a loop, arr[n] uses ordinary 0-based indexing from the start of the array. Output arrays are indexed by absolute bar position: output[i] = value writes to bar i.
Control Flow
forIterates over a Float64Array. Two forms: with index (for (i, v) in arr) or value-only (for v in arr). The loop body executes once per element in chronological order. Supports break and continue.
# With index and value
for (i, v) in prices {
output[i] = v * 2
}
# Value-only (no index needed)
for v in prices {
print(v)
}whileLoops while the condition is truthy (non-zero). Useful for manual rolling calculations inside a for body. Supports break and continue.
let j = 0
while j < period {
let diff = prices[j] - mean
sum = sum + diff * diff
j = j + 1
}if / elseConditional branch. Both branches are optional — you may write a bare if without else. The condition is truthy when non-zero. Blocks are delimited by { }.
if i < period - 1 { continue }
if value > upper_band {
signal[i] = 1
} else {
signal[i] = 0
}break / continuebreak exits the innermost for or while loop immediately. continue skips the remainder of the current iteration and advances to the next.