Requirements
Requirements let classes declare what component capabilities they need. They are validated against the component configuration built from .marsc files.
Where Requirements Live
Requirements appear inside class definitions:
class Example {
requirements {
Motor(functions=spin());
optional Sensor(parameters=[range > 10]);
}
}
Syntax Overview
A requirement item is a requirement expression, terminated by a semicolon:
requirements {
Chassis(parameters=[trackWidth > 0]);
Arm(functions=[lift(), extend()]);
Drive && Sensors;
!DebugComponent;
optional Encoder;
}
The building blocks:
- Requirement spec:
TypeName(...) - Logical operators:
&&,||,! - Parentheses for grouping
optionalmodifier
Requirement Spec
The core form is TypeName(...) where TypeName is a component type. Inside the parentheses, you can add any of these keys:
parameters=...functions=...subcomponents=...
Example:
Chassis(
parameters=[trackWidth > 0, scrubFactor >= 0],
functions=[drive(), getVelocities()]
);
Parameters
parameters is a list of boolean expressions evaluated against a component instance’s parameters.
Example:
Motor(parameters=[maxRPM >= 10]);
Rules and behavior:
- Parameter names refer to the target component’s parameters.
- Expressions must be evaluatable from literals and simple operators.
- If a required parameter is missing or cannot be evaluated, the requirement fails.
- Unit tags are supported in expressions.
Functions
functions is a list of required function names (with empty parentheses).
Example:
Chassis(functions=[drive(), getVelocities()]);
Only function presence is checked, not parameter types.
Subcomponents
subcomponents is a list of nested requirement expressions that must be satisfied somewhere under the target component’s subtree.
Example:
RobotBase(subcomponents=[Motor(functions=spin())]);
Subcomponent requirements can use &&, ||, and ! just like top-level requirements.
Optional Requirements
optional can be applied in three places:
- Before a requirement item:
optional Sensor; - Before a spec in a list:
subcomponents=[optional Sensor] - Before a parameter or function requirement:
parameters=[optional range > 10],functions=[optional spin()]
Optional requirements do not fail the build. They are reported as flags.
How Requirements Are Evaluated
Requirements are checked against the component tree built from .marsc config:
- Only Robot-family roots are considered.
- A requirement spec matches any component instance of that type or any derived type in the subtree.
- If any matching instance satisfies all constraints, the requirement passes.
- If only optional constraints fail, the requirement passes with a flag.
- If no match satisfies the constraints, the requirement fails.
Logical operators:
A && Brequires both to pass.A || Bpasses if either passes.!Apasses only ifAdoes not pass.
optional turns a hard failure into a flag at the point it appears.
Interaction With Configuration
Requirements use data from the configuration:
- Parameter values come from defaults and bindings in
.marsc. - Missing required parameters in config are configuration errors, not requirement errors.
- Function presence is derived from component definitions and inheritance.
Inheritance matters:
- A requirement for
Chassisalso matchesDifferentialChassisor any derived component.
When Requirements Run
There are two validation passes:
- Config pass: requirements are checked against all Robot-family roots based purely on
.marsc. - Instantiated pass: requirements are checked against actual component arguments passed to class constructors.
The instantiated pass resolves component paths through:
- Direct component variables
- Member access (
robot.chassis) match()expressions
If a class has requirements but no component argument can be resolved, validation fails.
Outputs
Hard failures abort execution with a detailed message.
Optional failures are printed as flags (prefixed with [requirements]).