Syntax
Arc language syntax reference for comments, identifiers, literals, and statement structure
Comments
Single-line comments start with //. Multi-line comments use /* */.
// This is a single-line comment
/* This is a
multi-line comment */
x := 42 // Comments can follow code Identifiers
Identifiers name variables, functions, sequences, stages, and channels.
Rules:
- Start with a letter (
a-z,A-Z) or underscore (_) - Contain letters, digits (
0-9), or underscores - Case-sensitive (
ox_pt_1andOx_Pt_1are different)
valid_name
_private
sensor1
ox_pt_1 Reserved keywords cannot be used as identifiers:
Literals
Numeric Literals
Integer literals default to i64. Float literals default to f64.
42 // i64
3.14 // f64
0.5 // f64
.25 // f64 (leading dot allowed) Numeric Literals with Units
Unit suffixes attach directly to numbers (no whitespace):
100ms // 100 milliseconds
5s // 5 seconds
1min // 1 minute
2h // 2 hours
10hz // 10 hertz
1khz // 1 kilohertz A suffix tags the literal with a dimensioned unit from the built-in registry. The
compiler tracks dimensions (time, frequency, length, pressure, etc.) but does not
implicitly convert between them. A 10hz value is a frequency and will not satisfy a
parameter that expects a time span.
String Literals
Two forms, distinguished by delimiter:
"..."— single-line, double-quoted. Cannot contain unescaped newlines.`...`— multi-line, backtick-delimited. Newlines and"characters are allowed verbatim in the body; the string ends at the next`.
"hello"
"tab\there"
`line1
line2` The delimiter escape is asymmetric: \" is interpreted only inside "...", and
\` only inside `...`. The other character is a literal everywhere it is not
the delimiter, so no escape is needed. Any other backslash sequence (e.g., \z) is
preserved as the two literal characters.
Raw Strings
Prefix with r to disable escape processing. Every character in the body is verbatim —
\n is the two characters \ and n, not a newline:
r"C:\Users\path"
r`line one
line two` In a raw string, the delimiter escape is still accepted by the lexer so the delimiter
character can appear in the body, but the backslash is preserved in the value (r"a\"b"
yields a\"b, four characters; r`a\`b` yields a\`b).
Format Strings
Prefix with f to enable {expr} placeholders, with an optional format spec after :.
Combine with r (as rf or fr) for raw + format.
f"Data: {pressure:.1f} psi" // "Data: 523.7 psi"
f"Code: {pump_err_code:#x}" // "Code: 0xff00"
f"Math: {(2.0 + 4.0)/2.0}" // "Math: 3"
rf"C:\logs\{name}.txt" // backslashes literal; {name} substituted Each placeholder is a regular Arc expression. Numeric values are converted to their default string form unless a format spec is given. Format specs follow printf-style conventions and are validated against the placeholder type at compile time.
Format spec reference
A format spec has the form:
[flags][width][.precision]verb Only the verb is required. Each bracketed part is optional. If precision is given, the
leading . must accompany it. Each part is described below.
Verb
The verb selects the conversion. Each verb accepts specific placeholder types.
Verbs x, e, and g have uppercase variants X, E, and G that uppercase their
output (e.g., X of 255 → FF). O is o with a leading 0o prefix (e.g., O of
8 → 0o10).
s outputs the string as-is, identical to the default {name} form. Its practical use
is as the anchor verb when applying width or padding modifiers to a string: {name:5s}
pads to width 5, {name:-10s} left-aligns within width 10. See the Examples section
below.
q wraps the string in double quotes and escapes special characters so the result is a
valid string literal: an embedded " becomes \", a newline becomes \n, and so on.
For example, q of say "hi" produces "say \"hi\"".
Flags
Flags are zero or more of the following characters, in any order, placed at the start of the spec.
Width
A digit count specifying the minimum output width. The value is padded with spaces (or
zeros, with the 0 flag) up to this width. For example, 5d of 42 → ␣␣␣42.
Precision
A . followed by a digit count specifying digits after the decimal point. For example,
.2f of 3.14159 → 3.14.
Examples
Flags, width, and precision compose in spec order:
Inside a format string, { opens a placeholder. To write a literal {, double it as
{{; to write a literal }, double it as }}. A single } outside a placeholder is
treated as plain text.
msg := f"progress: {{{50}}}" // "progress: {50}" The {{ / }} escape mechanism is independent of the r prefix, so backslashes never
interfere with placeholder parsing. rf"C:\logs\{name}.txt" interpolates {name} and
keeps every backslash literal.
Series Literals
Series (arrays) use square brackets:
[1, 2, 3] // series i64
[1.0, 2.0, 3.0] // series f64
[] // empty (requires type annotation) Empty series require an explicit type:
empty series f64 := [] Imports
Modules must be imported before their qualified members (time.now,
control.set_authority, status.set, …) can be used. A single import uses the bare
form; multiple imports go in an import ( … ) block.
Single Module
import time
trig -> time.now{} -> ts_out Aliased
Rename the qualifier with as:
import time as t
trig -> t.now{} -> ts_out Multiple Modules
List several modules in one block, separated by whitespace:
import (
control
status
time as t
) Authority Declarations
Authority declarations set the control authority
for channels written by the program. They must appear before any func, sequence, or
flow declarations.
Default Authority
Set a default authority for all channels:
authority 200 When omitted, the system default is 255 (maximum).
Per-Channel Authority
Set authority for specific channels using a grouped form:
authority (
200
valve_cmd 100
vent_cmd 150
) This sets a default of 200, then overrides valve_cmd to 100 and vent_cmd to 150.
You can also set per-channel authority without a default:
authority (
valve_cmd 100
vent_cmd 150
) Summary
Authority values are u8 integers in the range 0-255.
To change authority at runtime, use the
control.set_authority
standard library function inside stage bodies.
Blocks
Braces {} delimit blocks. Blocks contain statements (function bodies), flow statements
(stage bodies), or items (sequence bodies).
func example() {
// statements in function block
}
stage pressurize {
// flow statements in stage block
}
sequence prime {
// items in sequence block
} Stage bodies contain flow statements; newlines separate them.
stage pressurize {
1 -> valve
sensor -> controller{}
pressure > 500 => next
} Sequence bodies contain items that run in order: writes, waits, condition gates, stages, and nested sequences.
sequence prime {
1 -> press_vlv_cmd
time.wait{5s}
tank_pressure > 500
0 -> press_vlv_cmd
}