ReferenceControlArcReferenceSyntax

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_1 and Ox_Pt_1 are different)
valid_name
_private
sensor1
ox_pt_1

Reserved keywords cannot be used as identifiers:

Keywords
funcifelsereturn
forbreakcontinuechan
sequencestagenextseries
andornotauthority
i8 i16i32 i64u8 u16 u32 u64f32 f64 str

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.

Temporal UnitsMeaning
nsnanoseconds
usmicroseconds
msmilliseconds
sseconds
minminutes
hhours
Frequency UnitsMeaning
hzhertz
khzkilohertz
mhzmegahertz

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`
EscapeMeaning
\bbackspace
\ttab
\nnewline
\fform feed
\rcarriage return
\"double quote (single-line only)
\`backtick (multi-line only)
\\backslash
\uXXXX4-digit Unicode escape

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.

FormMeaning
{expr}Insert expr using its default string form
{expr:spec}Format expr using a printf-style spec (e.g., .2f)

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.

VerbTypesUseExample
dintDecimal4242
bintBinary5101
ointOctal810
xintHex255ff
cintUnicode character65A
ffloatDecimal3.143.140000
efloatScientific3.143.140000e+00
gfloatCompact3.143.14
sstrStringhellohello
qstrQuoted stringhello"hello"

Verbs x, e, and g have uppercase variants X, E, and G that uppercase their output (e.g., X of 255FF). O is o with a leading 0o prefix (e.g., O of 80o10).

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.

FlagUse
#Alternate form: adds 0b / 0 / 0x prefix for b / o / x
+Force sign on positive numbers
Leading space for positive numbers
-Left-align within width
0Zero-pad to width

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.141593.14.

Examples

Flags, width, and precision compose in spec order:

SpecValueOutput
#b50b101
#o8010
#x2550xff
X255FF
E3.143.140000E+00
G3.143.14
+d42+42
d42␣42
5d42␣␣␣42
-5d4242␣␣␣
05d4200042
5s"ok"␣␣␣ok
-5s"ok"ok␣␣␣
.2f3.141593.14
+f3.14+3.140000
6.2f3.14␣␣3.14
+08.2f3.14+0003.14
#06x2550x0000ff

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.

EscapeMeaning
{{Literal {
}}Literal }
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

FormSyntaxDescription
Default onlyauthority 200Sets authority for all channels
Grouped with defaultauthority (200 valve_cmd 100)Default plus per-channel overrides
Per-channel onlyauthority (valve_cmd 100 vent_cmd 150)Only named channels

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
}