12 KiB
12 KiB
MARTe Development Tools
Project goal
- A single portable executable named
mdt(MARTe Development Tools) written in Go - It should parse and index a custom configuration language (MARTe)
- It should provide an LSP server for this language
- It should provide a build tool for unifying multi-file projects into a single configuration output
CLI Commands
The executable should support the following subcommands:
lsp: Starts the Language Server Protocol server.build: Merges files with the same base namespace into a single output.check: Runs diagnostics and validations on the configuration files.fmt: Formats the configuration files.
LSP Features
The LSP server should provide the following capabilities:
- Diagnostics: Report syntax errors and validation issues.
- Hover Documentation:
- Objects: Display
CLASS::Nameand any associated docstrings. - Signals: Display
DataSource.Name TYPE (SIZE) [IN/OUT/INOUT]along with docstrings. - GAMs: Show the list of States where the GAM is referenced.
- Referenced Signals: Show the list of GAMs where the signal is referenced.
- Objects: Display
- Go to Definition: Jump to the definition of a reference, supporting navigation across any file in the current project.
- Go to References: Find usages of a node or field, supporting navigation across any file in the current project.
- Code Completion: Autocomplete fields, values, and references.
- Code Snippets: Provide snippets for common patterns.
- Formatting: Format the document using the same rules and engine as the
fmtcommand.
Build System & File Structure
- File Extension:
.marte - Project Structure: Files can be distributed across sub-folders.
- Namespaces: The
#packagemacro defines the namespace for the file.- Single File Context: If no
#packageis defined in a file, the LSP, build tool, and validator must consider only that file (no project-wide merging or referencing). - Semantic:
#package PROJECT_NAME.SUB_URIimplies that:PROJECT_NAMEis a namespace identifier used to group files from the same project. It does not create a node in the configuration tree.SUB_URIdefines the path of nodes where the file's definitions are placed. All definitions within the file are treated as children/fields of the node defined bySUB_URI.
- URI Symbols: The symbols
+and$used for object nodes are not written in the URI of the#packagemacro (e.g., usePROJECT.NODEeven if the node is defined as+NODE).
- Single File Context: If no
- Build Process:
- The build tool merges all files sharing the same base namespace into a single output configuration.
- Namespace Consistency: The build tool must verify that all input files belong to the same project namespace (the first segment of the
#packageURI). If multiple project namespaces are detected, the build must fail with an error. - Target: The build output is written to a single target file (e.g., provided via CLI or API).
- Multi-File Definitions: Nodes and objects can be defined across multiple files. The build tool, validator, and LSP must merge these definitions (including all fields and sub-nodes) from the entire project to create a unified view before processing or validating.
- Global References: References to nodes, signals, or objects can point to definitions located in any file within the project.
- Merging Order: For objects defined across multiple files, the first file to be considered is the one containing the
Classfield definition. - Field Order: Within a single file, the relative order of defined fields must be maintained.
- The LSP indexes only files belonging to the same project/namespace scope.
- Output: The output format is the same as the input configuration but without the
#packagemacro.
MARTe Configuration Language
Grammar
comment://.*configuration:definition+definition:field = value | node = subnodefield:[a-zA-Z][a-zA-Z0-9_\-]*node:[+$][a-zA-Z][a-zA-Z0-9_\-]*subnode:{ definition+ }value:string|int|float|bool|reference|arrayint:/-?[0-9]+|0b[01]+|0x[0-9a-fA-F]+float:-?[0-9]+\.[0-9]+|-?[0-9]+\.?[0-9]*e\-?[0-9]+bool:true|falsestring:".*"reference:string|.*array:{ value }
Extended grammar
package:#package URIURI:PROJECT | PROJECT.PRJ_SUB_URIPRJ_SUB_URI:NODE | NODE.PRJ_SUB_URIdocstring://#.*pragma://!.*
Semantics
- Nodes (
+/$): The prefixes+and$indicate that the node represents an object.- Constraint: These nodes must contain a field named
Classwithin their subnode definition (across all files where the node is defined).
- Constraint: These nodes must contain a field named
- Signals: Signals are considered nodes but not objects. They do not require a
Classfield. - Pragmas (
//!): Used to suppress specific diagnostics. The developer can use these to explain why a rule is being ignored. - Structure: A configuration is composed by one or more definitions.
Core MARTe Classes
MARTe configurations typically involve several main categories of objects:
- State Machine (
StateMachine): Defines state machines and transition logic. - Real-Time Application (
RealTimeApplication): Defines a real-time application, including its data sources, functions, states, and scheduler. - Data Source: Multiple classes used to define input and/or output signal sources.
- GAM (Generic Application Module): Multiple classes used to process signals.
- Constraint: A GAM node must contain at least one
InputSignalssub-node, oneOutputSignalssub-node, or both.
- Constraint: A GAM node must contain at least one
Signals and Data Flow
- Signal Definition:
- Explicit: Signals defined within the
DataSourcedefinition. - Implicit: Signals defined only within a
GAM, which are then automatically managed. - Requirements:
- All signal definitions must include a
Typefield with a valid value. - Size Information: Signals can optionally include
NumberOfDimensionsandNumberOfElementsfields. If not explicitly defined, these default to1. - Extensibility: Signal definitions can include additional fields as required by the specific application context.
- All signal definitions must include a
- Explicit: Signals defined within the
- Signal Reference Syntax:
- Signals are referenced or defined in
InputSignalsorOutputSignalssub-nodes using one of the following formats:- Direct Reference (Option 1):
In this case, the GAM signal name is the same as the DataSource signal name.
SIGNAL_NAME = { DataSource = DATASOURCE_NAME // Other fields if necessary } - Aliased Reference (Option 2):
In this case,
GAM_SIGNAL_NAME = { Alias = SIGNAL_NAME DataSource = DATASOURCE_NAME // ... }Aliaspoints to the DataSource signal name.
- Direct Reference (Option 1):
- Implicit Definition Constraint: If a signal is implicitly defined within a GAM, the
Typefield must be present in the reference block to define the signal's properties.
- Signals are referenced or defined in
- Directionality: DataSources and their signals are directional:
Input(IN): Only providing data. Signals can only be used inInputSignals.Output(OUT): Only receiving data. Signals can only be used inOutputSignals.Inout(INOUT): Bidirectional data flow. Signals can be used in bothInputSignalsandOutputSignals.- Validation: The tool must validate that signal usage in GAMs respects the direction of the referenced DataSource.
Object Indexing & References
The tool must build an index of the configuration to support LSP features and validations:
- GAMs: Referenced in
$APPLICATION.States.$STATE_NAME.Threads.$THREAD_NAME.Functions(where$APPLICATIONis aRealTimeApplicationnode). - Signals: Referenced within the
InputSignalsandOutputSignalssub-nodes of a GAM. - DataSources: Referenced within the
DataSourcefield of a signal reference/definition. - General References: Objects can also be referenced in other fields (e.g., as targets for messages).
Validation Rules
- Consistency: The
lsp,check, andbuildcommands must share the same validation engine to ensure consistent results across all tools. - Global Validation Context:
- All validation steps must operate on the aggregated view of the project.
- A node's validity is determined by the combination of all its fields and sub-nodes defined across all project files.
- Class Validation:
- For each known
Class, the validator checks:- Mandatory Fields: Verification that all required fields are present.
- Field Types: Verification that values assigned to fields match the expected types (e.g.,
int,string,bool). - Field Order: Verification that specific fields appear in a prescribed order when required by the class definition.
- Conditional Fields: Validation of fields whose presence or value depends on the values of other fields within the same node or context.
- Schema Definition:
- Class validation rules must be defined in a separate schema file.
- Project-Specific Classes: Developers can define their own project-specific classes and corresponding validation rules, expanding the validation capabilities for their specific needs.
- Schema Loading:
- Default Schema: The tool should look for a default schema file
marte_schema.jsonin standard system locations:/usr/share/mdt/marte_schema.json$HOME/.local/share/mdt/marte_schema.json
- Project Schema: If a file named
.marte_schema.jsonexists in the project root, it must be loaded. - Merging: The final schema is a merge of the built-in schema, the system default schema (if found), and the project-specific schema. Rules in later sources (Project > System > Built-in) append to or override earlier ones.
- Default Schema: The tool should look for a default schema file
- For each known
- Duplicate Fields:
- Constraint: A field must not be defined more than once within the same object/node scope, even if those definitions are spread across different files.
- Multi-File Consideration: Validation must account for nodes being defined across multiple files (merged) when checking for duplicates.
Formatting Rules
The fmt command must format the code according to the following rules:
- Indentation: 2 spaces per indentation level.
- Assignment: 1 space before and after the
=operator (e.g.,Field = Value). - Comments:
- 1 space after
//,//#, or//!. - Comments should "stick" to the next definition (no empty lines between the comment and the code it documents).
- Placement:
- Comments can be placed inline after a definition (e.g.,
field = value // comment). - Comments can be placed after a subnode opening bracket (e.g.,
node = { // comment) or after an object definition.
- Comments can be placed inline after a definition (e.g.,
- 1 space after
- Arrays: 1 space after the opening bracket
{and 1 space before the closing bracket}(e.g.,{ 1 2 3 }). - Strings: Quoted strings must preserve their quotes during formatting.
Diagnostic Messages
The LSP and check command should report the following:
-
Warnings:
- Unused GAM: A GAM is defined but not referenced in any thread or scheduler.
- Unused Signal: A signal is explicitly defined in a
DataSourcebut never referenced in anyGAM. - Implicitly Defined Signal: A signal is defined only within a
GAMand not in its parentDataSource.
-
Errors:
- Type Inconsistency: A signal is referenced with a type different from its definition.
- Size Inconsistency: A signal is referenced with a size (dimensions/elements) different from its definition.
- Duplicate Field Definition: A field is defined multiple times within the same node scope (including across multiple files).
- Validation Errors:
- Missing mandatory fields.
- Field type mismatches.
- Grammar errors (e.g., missing closing brackets).
Logging
- Requirement: All logs must be managed through a centralized logger.
- Output: Logs should be written to
stderrby default to avoid interfering withstdoutwhich might be used for CLI output (e.g., build artifacts or formatted text).