-
Notifications
You must be signed in to change notification settings - Fork 0
Grammar
Reference for the artheia DSL. The grammar source of truth is the textX
grammar under artheia/grammar/*.tx; this page summarizes it with authentic
examples. When in doubt, read the .tx.
Code blocks use
scalahighlighting —.artisn't a known highlighter language, and Scala renders it cleanly.
Model:
('package' name=QualifiedName)?
imports*=Import
elements*=TopLevelElementA file is an optional package line, then import lines, then top-level
elements. Imports must precede every element — the grammar is
package? imports* elements*, so an import after any declaration is a syntax
error.
A package is split across two sibling files that the loader merges into one
model: package.art (the schema — messages, enums, interfaces, nodes) and
component.art (the wiring — compositions, clusters). They must declare the
same package. The merger hoists import lines from both files to the
top, so either file may carry imports.
File-name resolution priority for import X.Y.*: the resolver looks under the
directory mapped from the FQN for system.art → cluster.art → package.art
→ component.art.
package system.services.exec
import system.supervisor.* // imports first, always
message SupervisionEvent { } // then declarationsmessage · enum · interface · node · composition · cluster ·
bus · gateway_route.
message LogRecord {
string context
uint32 level
uint64 ts_ns
string text
}
enum FgState { IDLE = 0 STARTING = 1 RUNNING = 2 STOPPING = 3 }Scalar field types: int32 int64 sint32 sint64 uint32 uint64 fixed32 fixed64 sfixed32 sfixed64 float double bool string bytes. A field may also reference
another message / enum by name, and may be repeated.
message Name { } is its own forward declaration — a message is never
extern.
provides / requires on a port must match the interface flavor.
// pub/sub, fire-and-forget; one message type per `data`:
interface senderReceiver LogStream {
data LogRecord record
}
// RPC, request/reply; operations with in/out/inout params:
interface clientServer ExecCtl {
operation StartGroup(in r:StartGroupRequest) returns ExecEmpty
operation StopGroup(in r:StopGroupRequest) returns ExecEmpty
}NodeDecl:
extern?='extern'
'node' kind=NodeKind name=ID ('prototype' base=[NodeDecl|FQN])? '{'
(tipc=TipcAddress)?
(requires_timers?='requires_timers')?
('reporting' '=' reporting=BoolLit)?
('tag' '=' tag=STRING)?
('config' config=[MessageDecl|FQN])?
('ports' '{' ports*=PortDecl '}')?
('params' '{' params*=NodeParam '}')?
('statem' '{' statem=StateMBody '}')?
'}'-
kindisatomic(aGenServer, orGenStateMif astatemblock is present) orrunnable(aGenRunnablefree worker — nostatem). -
tipc type=0x… instance=0— the network address. Grammar-optional but loader-REQUIRED for a real (non-extern) node, unless aprototype <Base>supplies it via inheritance. -
requires_timers— the node usesprocess_timers()(send_after / cancel_timer); the flag letsmainpublish the processTimerService. -
tag = "LOG"— short context id for log lines (defaults to the node name when omitted). -
reporting = true|false— whether the supervisor watchdogs this node and can push heartbeat / trace / log-level config to it (defaults true). -
prototype Base— attribute-REPLACEMENT inheritance (deliberately NOTextends): the derived node inherits the base's ports / statem / config / params / requires_timers unless it re-declares them (no field-level merge). The common case is overriding onlytipc:node atomic FooZonal prototype Foo { tipc type=0x… instance=0 }. -
config Msg— the node's etcd-backed config message type.
There is no kick_off (a node's post-construction startup is the OTP
init(State&) callback) and no fallthrough (an unrouted inbound frame is
dropped with a CRITICAL log in TipcMux; cross-node traffic is exclusively
typed cast / call). Tracing has no annotation: every node is
runtime-traceable, flipped by the supervisor.
node atomic TraceCollector {
tipc type=0x80010013 instance=0
tag = "LOG"
ports {
server ctl_supdbg provides TraceControl
sender stream_out provides TraceRecordStream
receiver in_records requires TraceRecordSubmit
client to_supervisor requires SupervisorControlIf
sender to_log provides LogStream best_effort
}
}Four port kinds. provides / requires must match the interface flavor:
| port | interface | direction |
|---|---|---|
server X provides Iface |
clientServer | this node answers RPCs |
client X requires Iface |
clientServer | this node calls RPCs |
sender X provides Iface [reliable|best_effort] |
senderReceiver | this node publishes |
receiver X requires Iface [reliable|best_effort] |
senderReceiver | this node subscribes |
Reliability defaults to reliable; best_effort is fire-and-forget (used for
log/trace fan-in where a drop must never block the app).
params {
publish_period_ms : uint32 = 10
enabled : bool = true
source_name : string = "front-axle"
}A statem block on an atomic node makes it a GenStateM.
statem {
states [ Idle, Running, Stopping ]
initial Idle
data SmContext
on Idle:
event StartReq → Running
on Running:
event StopReq → Stopping
timeout → halt
}Transition arrows are the Unicode → (U+2192), not ->.
composition VehicleSystem {
prototype SpeedPublisher speed_pub
prototype TorqueController torque_ctrl
connect speed_pub.out to torque_ctrl.speed_in
connect torque_ctrl.torque_out to actuator.torque_in
}-
prototype NodeType nameinstantiates a node; an optionalon process Pis a process-affinity hint (consumed by the rig/manifest pipeline). -
composition CompType nameincludes another composition. -
connect a.port to b.portwires ports. A port ref is<prototype>.<port>; prototype names are globally unique in the model, so no member-prefix qualification is needed.
cluster Services {
composition Com com
composition Log log
composition Per per
composition Sm sm
composition Ucm ucm
}Each composition T name member is one installable package keyed by name.
connect lines inside a cluster are inter-process TIPC (vs the in-process
wiring inside a composition).
bus kcan kind = can
gateway_route SpeedPublisher {
can id=0x42 bus=kcan dlc=8
direction = in
}gateway_route maps a node's PDU to a CAN id / FlexRay slot, with
direction = in|out. These appear in the generated AUTOSAR mega-node specs
(produced by gen-autosar-system / import-dbc / import-fibex), not in
hand-written FC specs.
Cross-file references resolve through the import + extern forward-decl
pattern: declare the symbol locally with the extern keyword and an EMPTY body
so the file parses standalone, and the import-following scope provider
materializes the real definition from the imported package.
extern is the explicit forward-declaration marker (an empty body is not
magic). It prefixes a node, composition, cluster, or interface (a message is
never extern).
// system/system.art
package system
import system.services.*
import system.supervisor.*
extern cluster Services { } // real def in system.services
extern composition Supervisor { } // real def in system.supervisor
cluster Platform {
composition Supervisor sup
}The empty { } is still required (extern node atomic FlexRayIngress { },
extern interface senderReceiver EML_01_Iface { }).
Loader rules:
- An
externdecl MUST have an empty body — notipc/ ports on a node, no members on a composition / cluster / interface. - A non-
externnodeis loader-REQUIRED to have atipc, unless aprototype <Base>supplies it.
artheia parse <file.art>Resolves imports recursively and prints the merged tree, or exits non-zero on
the first error. Common errors: an import placed after a declaration; a
non-extern node missing its tipc; a non-empty extern body; → typed as
->; a provides / requires pointing at the wrong interface flavor. The LSP
gives the same diagnostics live, plus go-to-definition across the forward-decl
boundary — see Editor Support.
The structural keywords (as recognized by the LSP completion + grammar):
| group | keywords |
|---|---|
| file |
package import
|
| data |
message enum
|
| interface |
interface senderReceiver clientServer data operation returns in out inout
|
| node |
node atomic runnable tipc type instance ports params config tag reporting requires_timers extern
|
| ports |
sender receiver client server provides requires reliable best_effort
|
| composition |
composition prototype connect to on process
|
| cluster | cluster |
| statem |
statem states initial event timeout after halt
|
| bus / route |
bus gateway_route signal
|
| literals |
true false
|
| scalar types |
int32 int64 uint32 uint64 sint32 sint64 fixed32 fixed64 sfixed32 sfixed64 float double bool string bytes
|
pip install artheiaartheia · artheia-lsp · artheia-mcp
- Theia wiki — the runtime + system
- rf-theia wiki — the testing harness
- perotheia org