Reference
Scripting Language Reference
Overview
Gate Lab's scripting language lets you define arbitrary digital logic in custom components. The syntax is C-like: imperative, expression-based, and procedural. It is not a general-purpose language — there is no heap allocation, no floating-point, no recursion, and no I/O beyond reading input pins and writing output pins. Everything is a 64-bit signed integer.
A script has two parts, in order:
- Header section — keyword declarations that define the component's pin interface and internal memory. These lines have no semicolons and must all appear before any logic code.
- Logic body — statements that compute output values from input values. Every statement ends with a semicolon.
inputs: A, B
outputs: Y
Y = A & B; # AND gate
Open the editor: In Gate Lab, drag a Custom Component or Custom Sequential Component from the Programmable category onto the canvas, then right-click it and select Edit Script to open the script editor.
LAUNCH GATE LABComponent types
There are two programmable component types in the Programmable category of the component panel. Both use the same scripting language; they differ in execution model.
| Type | When the body runs | Persistent state | clock: header |
|---|---|---|---|
| Custom Component | Every time any input pin changes value | No | Not allowed |
| Custom Sequential Component | On the rising edge of the clock pin only | Yes — via state: |
Required |
state: variables. For pure combinational logic — gates,
MUXes, ALU operations — use Custom Component.
Header declarations
Header declarations define the component's pins and internal storage. Each keyword may appear at most once and must come before the logic body. No semicolons in the header.
inputs: and outputs:
Define input and output pins. Names are comma-separated. Append
[N] to a name for an N-bit bus pin.
| Declaration | Result |
|---|---|
inputs: A, B |
Two 1-bit input pins named A and B |
inputs: Data[8], Select[2] |
An 8-bit bus pin and a 2-bit bus pin |
outputs: Result[4], Carry |
A 4-bit bus output and a 1-bit output |
Accessing individual bits of a bus in the logic body uses zero-based
indexing: Data[0] is the least-significant bit;
Data[7] is the most-significant bit of an 8-bit bus.
Using the name without brackets reads or writes the full integer
value of the bus.
clock: and state:
clock: names the clock input pin for a Custom
Sequential Component. The logic body executes only on the
rising edge
of this pin (LOW-to-HIGH transition). It takes a single name:
clock: CLK
state: declares variables that persist between clock
cycles. Their values survive from one rising edge to the next. Valid
only when a clock: is also declared.
state: count[8], ram[16][8] # 8-bit counter, 16-row × 8-bit memory
State variables support single values, 1D arrays, and 2D arrays. All state variables are initialised to 0 when the simulation starts and when the simulation is reset.
vars:
vars: declares scratch variables. Unlike state
variables, vars: values are reset to 0 before each
script execution. They are useful for intermediate calculations
within a single clock cycle.
vars: temp, carry
Variables can also be declared inline in the logic body using the
var keyword:
var sum = A + B; # declare and initialise
var buf[4]; # declare a 4-element array
var arr[4] = 0xFF; # declare array with assignment (truncated to 4 bits)
var declarations are
effectively hoisted to script scope — they are accessible throughout
the entire logic body. However, the initialisation runs at the point
the statement appears.
Syntax and data types
Integers and buses
All values are 64-bit signed integers. There is no boolean type:
0 is LOW/false and any non-zero value is HIGH/true. The
keywords true and false are aliases for
1 and 0.
Number literals:
- Decimal:
123,-5 - Hexadecimal:
0xFF,0x1A
When an integer value is assigned to a declared array or bus (e.g.,
Out[4] = 255), the value is truncated
to fit — only the least-significant N bits are retained.
Comments
Comments start with # and run to the end of the line.
They can appear on a line of their own or after a statement.
# This entire line is a comment
Y = A & B; # inline comment
Reserved keywords
The following identifiers are reserved and cannot be used as variable or pin names:
if, else, for,
while, break, continue,
true, false, var,
inputs, outputs, state,
clock, vars, random,
abs, min, max,
popcount
Operators
Standard operator precedence applies: unary operators bind tightest, then multiplication/division, then addition/subtraction, then shifts, then bitwise AND/XOR/OR, then comparison, then logical AND/OR.
Arithmetic operators
| Operator | Description | Notes |
|---|---|---|
+ |
Addition | |
- |
Subtraction | Also unary negation |
* |
Multiplication | |
/ |
Integer division | Division by zero returns 0 |
% |
Modulo | Modulo by zero returns 0 |
val = (A + B) * 2;
remainder = count % 16;
Bitwise operators
| Operator | Description |
|---|---|
& |
Bitwise AND |
| |
Bitwise OR |
^ |
Bitwise XOR |
~ |
Bitwise NOT (ones complement) |
<< |
Left shift |
>> |
Right shift |
masked = Data & 0x0F; # keep bottom 4 bits
inverted = ~Data;
bit3 = 1 << 3; # result is 8
Comparison operators
All return 1 (true) or 0 (false).
| Operator | Description |
|---|---|
== |
Equal |
!= |
Not equal |
< |
Less than |
> |
Greater than |
<= |
Less than or equal |
>= |
Greater than or equal |
Logical operators
Operate on the boolean interpretation of values (non-zero = true). Short-circuit evaluation is applied.
| Operator | Description |
|---|---|
&& |
Logical AND |
|| |
Logical OR |
! |
Logical NOT |
if (Enable && !Reset) {
count = count + 1;
}
& and
| when you want to operate on individual bits of a bus.
Use && and || when you want
true/false logic on whole values. 1 & 2 is
0; 1 && 2 is 1.
Ternary operator
Inline if-else expression:
condition ? value_if_true : value_if_false
larger = (A > B) ? A : B;
IsZero = (Result == 0) ? 1 : 0;
Control flow
if / else
Curly braces are required around the block, even for a single statement.
if (condition) {
# statements
} else if (another_condition) {
# statements
} else {
# statements
}
for loop
All three parts of the for header are optional.
for (var i = 0; i < 8; i = i + 1) {
sum = sum + data[i];
}
# Infinite loop — exit with break
for (;;) {
if (done) { break; }
}
while loop
var bit = 0;
while ((value & 1) == 0 && bit < 8) {
value = value >> 1;
bit = bit + 1;
}
break and continue
break exits the innermost loop immediately.
continue skips the rest of the current iteration and
evaluates the loop condition again. Both affect only the innermost
enclosing loop.
# Sum odd numbers 1..9
var sum = 0;
for (var i = 0; i < 10; i = i + 1) {
if ((i & 1) == 0) { continue; } # skip even
sum = sum + i;
}
Built-in functions
abs(x)
Returns the absolute value of x.
distance = abs(A - B); # always non-negative
min(a, b) and max(a, b)
Return the smaller or larger of two values.
clamped = min(value, 255); # cap at 255
floored = max(value, 0); # floor at 0
random()
random() with no argument returns a random bit (0 or
1). random(max) returns a random integer in the range 0
to max - 1 inclusive. The generator is automatically
seeded each simulation run.
bit = random(); # 0 or 1
nibble = random(16); # 0 to 15
byte = random(256); # 0 to 255
popcount(x)
Returns the number of set bits (1s) in the integer x.
ones = popcount(0x0B); # returns 3 (binary 1011)
parity = popcount(data) & 1; # 1 if odd number of 1s
weight = popcount(value);
Examples
Each example below is a complete, ready-to-paste script. Copy it into the script editor of the matching component type.
Example 1 — Basic logic gates (AND, OR, NOT)
One component that exposes an AND output, an OR output, and a NOT output for inputs A and B. Use a Custom Component (combinational).
inputs: A, B
outputs: OutAND, OutOR, OutNOT
OutAND = A && B;
OutOR = A || B;
OutNOT = !A;
Example 2 — XOR gate
XOR using the built-in bitwise XOR operator. The commented-out line shows the equivalent construction from basic operators.
inputs: A, B
outputs: OutXOR
OutXOR = A ^ B;
# Equivalent: OutXOR = (A && !B) || (!A && B);
Example 3 — Half adder
Adds two single bits. Produces a Sum and a Carry out.
inputs: A, B
outputs: Sum, Carry
Sum = A ^ B;
Carry = A && B;
Example 4 — 4-to-1 multiplexer
Selects one of four 1-bit inputs using a 2-bit Select bus.
inputs: Select[2], In0, In1, In2, In3
outputs: OutMUX
if (Select == 0) {
OutMUX = In0;
} else if (Select == 1) {
OutMUX = In1;
} else if (Select == 2) {
OutMUX = In2;
} else {
OutMUX = In3;
}
Example 5 — Simple ALU
A 4-bit ALU supporting addition, subtraction, bitwise AND, and bitwise OR. OpCode selects the operation. Outputs the result and a zero flag. Combinational — no clock needed.
inputs: PacketA[4], PacketB[4], OpCode[2]
outputs: Result[4], IsZero
var val = 0;
if (OpCode == 0) {
val = PacketA + PacketB; # add
} else if (OpCode == 1) {
val = PacketA - PacketB; # subtract
} else if (OpCode == 2) {
val = PacketA & PacketB; # bitwise AND
} else {
val = PacketA | PacketB; # bitwise OR
}
Result = val;
IsZero = (Result == 0) ? 1 : 0;
Example 6 — 8-bit counter
Counts up on each rising clock edge when Enable is HIGH. Resets to 0 when Reset is HIGH. Uses Custom Sequential Component because the count must persist between clock edges.
clock: CLK
inputs: Reset, Enable
outputs: Count[8]
state: internal_count[8]
if (Reset) {
internal_count = 0;
} else if (Enable) {
internal_count = internal_count + 1;
}
Count = internal_count;
Example 7 — 4×4-bit RAM
A 4-address, 4-bit-wide RAM. Writes DataIn to Address when WriteEnable is HIGH on the rising clock edge. Reads the stored value when ReadEnable is HIGH.
clock: CLK
inputs: Address[2], DataIn[4], WriteEnable, ReadEnable
outputs: DataOut[4]
state: memory[4][4] # 4 rows × 4 bits
if (WriteEnable) {
memory[Address] = DataIn;
}
if (ReadEnable) {
DataOut = memory[Address];
} else {
DataOut = 0;
}
Example 8 — Random number generator
Outputs a random bit and a random N-bit value each time Enable is HIGH. Range controls the upper bound; if Range is 0 it defaults to 256. Combinational — values update whenever Enable or Range change.
inputs: Enable, Range[4]
outputs: RandomValue[8], RandomBit
if (Enable) {
RandomBit = random();
var bound = Range;
if (bound == 0) { bound = 256; }
RandomValue = random(bound);
} else {
RandomBit = 0;
RandomValue = 0;
}
Example 9 — ALU using built-in functions
Demonstrates abs, min, max,
and popcount. Operation selects between absolute
difference, minimum, maximum, and bitwise AND. Outputs a 4-bit Flags
bus: bit 0 is parity, bit 1 is overflow, bit 2 is zero, bit 3
signals that more than half the result bits are set.
inputs: A[8], B[8], Operation[2]
outputs: Result[8], Flags[4]
var output = 0;
if (Operation == 0) {
output = abs(A - B);
} else if (Operation == 1) {
output = min(A, B);
} else if (Operation == 2) {
output = max(A, B);
} else {
output = A & B;
}
var ones = popcount(output);
Result = output;
Flags[0] = ones & 1; # parity
Flags[1] = (output > 255) ? 1 : 0; # overflow
Flags[2] = (output == 0) ? 1 : 0; # zero
Flags[3] = (ones > 4) ? 1 : 0; # majority set
Ready to try? Open Gate Lab, add a Custom Component or Custom Sequential Component to the canvas, and paste one of the scripts above into the editor.
LAUNCH GATE LAB