diff --git a/.gitignore b/.gitignore index 00d4096..7739033 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ build *.log -mdt *.out diff --git a/README.md b/README.md index 461bcf4..9c6f5e3 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,13 @@ Few additional features have been added to the standard MARTe configuration lang - Doc-strings support - Pragmas for warning suppression / documentation +## Documentation + +- [Step-by-Step Tutorial](docs/TUTORIAL.md) +- [Editor Integration Guide](docs/EDITOR_INTEGRATION.md) +- [Configuration Guide](docs/CONFIGURATION_GUIDE.md) +- [Examples Readme](/examples/README.md) + ## Installation ### From Source @@ -34,13 +41,17 @@ go install github.com/marte-community/marte-dev-tools/cmd/mdt@latest ### CLI Commands +- **Init**: Initialize a MARTe project. + ```bash + mdt init project_name + ``` - **Check**: Run validation on a file or project. ```bash mdt check path/to/project ``` - **Build**: Merge project files into a single output. ```bash - mdt build -o output.marte main.marte + mdt build [-o output.marte] main.marte ... ``` - **Format**: Format configuration files. ```bash @@ -92,10 +103,10 @@ package schema Use comments starting with `//!` to control validation behavior: -- `//!unused: Reason` - Suppress "Unused GAM" or "Unused Signal" warnings. -- `//!implicit: Reason` - Suppress "Implicitly Defined Signal" warnings. -- `//!cast(DefinedType, UsageType)` - Allow type mismatch between definition and usage (e.g. `//!cast(uint32, int32)`). -- `//!allow(unused)` - Global suppression for the file. +- `//! unused: Reason` - Suppress "Unused GAM" or "Unused Signal" warnings. +- `//! implicit: Reason` - Suppress "Implicitly Defined Signal" warnings. +- `//! cast(DefinedType, UsageType)` - Allow type mismatch between definition and usage (e.g. `//!cast(uint32, int32)`). +- `//! allow(unused)` - Global suppression for the file. ## Development diff --git a/cmd/mdt/main.go b/cmd/mdt/main.go index 74b46d9..1f3b6c3 100644 --- a/cmd/mdt/main.go +++ b/cmd/mdt/main.go @@ -3,6 +3,7 @@ package main import ( "bytes" "os" + "path/filepath" "github.com/marte-community/marte-dev-tools/internal/builder" "github.com/marte-community/marte-dev-tools/internal/formatter" @@ -16,7 +17,11 @@ import ( func main() { if len(os.Args) < 2 { logger.Println("Usage: mdt [arguments]") - logger.Println("Commands: lsp, build, check, fmt") + logger.Println("Commands: lsp, build, check, fmt, init") + logger.Println(" build [-o output_file] ") + logger.Println(" check ") + logger.Println(" fmt ") + logger.Println(" init ") os.Exit(1) } @@ -30,6 +35,8 @@ func main() { runCheck(os.Args[2:]) case "fmt": runFmt(os.Args[2:]) + case "init": + runInit(os.Args[2:]) default: logger.Printf("Unknown command: %s\n", command) os.Exit(1) @@ -42,12 +49,45 @@ func runLSP() { func runBuild(args []string) { if len(args) < 1 { - logger.Println("Usage: mdt build ") + logger.Println("Usage: mdt build [-o output_file] ") os.Exit(1) } - b := builder.NewBuilder(args) - err := b.Build(os.Stdout) + var outputFilePath string + var inputFiles []string + + for i := 0; i < len(args); i++ { + if args[i] == "-o" { + if i+1 < len(args) { + outputFilePath = args[i+1] + i++ + } else { + logger.Println("Error: -o requires a file path") + os.Exit(1) + } + } else { + inputFiles = append(inputFiles, args[i]) + } + } + + if len(inputFiles) < 1 { + logger.Println("Usage: mdt build [-o output_file] ") + os.Exit(1) + } + + output := os.Stdout + if outputFilePath != "" { + f, err := os.Create(outputFilePath) + if err != nil { + logger.Printf("Error creating output file %s: %v\n", outputFilePath, err) + os.Exit(1) + } + defer f.Close() + output = f + } + + b := builder.NewBuilder(inputFiles) + err := b.Build(output) if err != nil { logger.Printf("Build failed: %v\n", err) os.Exit(1) @@ -61,7 +101,6 @@ func runCheck(args []string) { } tree := index.NewProjectTree() - // configs := make(map[string]*parser.Configuration) // We don't strictly need this map if we just build the tree for _, file := range args { content, err := os.ReadFile(file) @@ -80,13 +119,9 @@ func runCheck(args []string) { tree.AddFile(file, config) } - // idx.ResolveReferences() // Not implemented in new tree yet, but Validator uses Tree directly v := validator.NewValidator(tree, ".") v.ValidateProject() - // Legacy loop removed as ValidateProject covers it via recursion - - for _, diag := range v.Diagnostics { level := "ERROR" if diag.Level == validator.LevelWarning { @@ -133,3 +168,32 @@ func runFmt(args []string) { logger.Printf("Formatted %s\n", file) } } + +func runInit(args []string) { + if len(args) < 1 { + logger.Println("Usage: mdt init ") + os.Exit(1) + } + + projectName := args[0] + if err := os.MkdirAll(filepath.Join(projectName, "src"), 0755); err != nil { + logger.Fatalf("Error creating project directories: %v", err) + } + + files := map[string]string{ + "Makefile": "MDT=mdt\n\nall: check build\n\ncheck:\n\t$(MDT) check src/*.marte\n\nbuild:\n\t$(MDT) build -o app.marte src/*.marte\n\nfmt:\n\t$(MDT) fmt src/*.marte\n", + ".marte_schema.cue": "package schema\n\n#Classes: {\n // Add your project-specific classes here\n}\n", + "src/app.marte": "#package App\n\n+Main = {\n Class = RealTimeApplication\n +States = {\n Class = ReferenceContainer\n +Run = {\n Class = RealTimeState\n +MainThread = {\n Class = RealTimeThread\n Functions = {}\n }\n }\n }\n +Data = {\n Class = ReferenceContainer\n }\n}\n", + "src/components.marte": "#package App.Data\n\n// Define your DataSources here\n", + } + + for path, content := range files { + fullPath := filepath.Join(projectName, path) + if err := os.WriteFile(fullPath, []byte(content), 0644); err != nil { + logger.Fatalf("Error creating file %s: %v", fullPath, err) + } + logger.Printf("Created %s\n", fullPath) + } + + logger.Printf("Project '%s' initialized successfully.\n", projectName) +} \ No newline at end of file diff --git a/docs/CODE_DOCUMENTATION.md b/docs/CODE_DOCUMENTATION.md new file mode 100644 index 0000000..47d0432 --- /dev/null +++ b/docs/CODE_DOCUMENTATION.md @@ -0,0 +1,106 @@ +# mdt Internal Code Documentation + +This document provides a detailed overview of the `mdt` codebase architecture and internal components. + +## Architecture Overview + +`mdt` is built as a modular system where core functionalities are separated into internal packages. The data flow typically follows this pattern: + +1. **Parsing**: Source code is parsed into an Abstract Syntax Tree (AST). +2. **Indexing**: ASTs from multiple files are aggregated into a unified `ProjectTree`. +3. **Processing**: The `ProjectTree` is used by the Validator, Builder, and LSP server to perform their respective tasks. + +## Package Structure + +``` +cmd/ + mdt/ # Application entry point (CLI) +internal/ + builder/ # Logic for merging and building configurations + formatter/ # Code formatting engine + index/ # Symbol table and project structure management + logger/ # Centralized logging + lsp/ # Language Server Protocol implementation + parser/ # Lexer, Parser, and AST definitions + schema/ # CUE schema loading and integration + validator/ # Semantic analysis and validation logic +``` + +## Core Packages + +### 1. `internal/parser` + +Responsible for converting MARTe configuration text into structured data. + +* **Lexer (`lexer.go`)**: Tokenizes the input stream. Handles MARTe specific syntax like `#package`, `//!` pragmas, and `//#` docstrings. Supports standard identifiers and `#`-prefixed identifiers. +* **Parser (`parser.go`)**: Recursive descent parser. Converts tokens into a `Configuration` object containing definitions, comments, and pragmas. +* **AST (`ast.go`)**: Defines the node types (`ObjectNode`, `Field`, `Value`, etc.). All nodes implement the `Node` interface providing position information. + +### 2. `internal/index` + +The brain of the system. It maintains a holistic view of the project. + +* **ProjectTree**: The central data structure. It holds the root of the configuration hierarchy (`Root`), references, and isolated files. +* **ProjectNode**: Represents a logical node in the configuration. Since a node can be defined across multiple files (fragments), `ProjectNode` aggregates these fragments. +* **NodeMap**: A hash map index (`map[string][]*ProjectNode`) for $O(1)$ symbol lookups, optimizing `FindNode` operations. +* **Reference Resolution**: The `ResolveReferences` method links `Reference` objects to their target `ProjectNode` using the `NodeMap`. + +### 3. `internal/validator` + +Ensures configuration correctness. + +* **Validator**: Iterates over the `ProjectTree` to check rules. +* **Checks**: + * **Structure**: Duplicate fields, invalid content. + * **Schema**: Unifies nodes with CUE schemas (loaded via `internal/schema`) to validate types and mandatory fields. + * **Signals**: Verifies that signals referenced in GAMs exist in DataSources and match types. + * **Threading**: Checks `checkDataSourceThreading` to ensure non-multithreaded DataSources are not shared across threads in the same state. + * **Unused**: Detects unused GAMs and Signals (suppressible via pragmas). + +### 4. `internal/lsp` + +Implements the Language Server Protocol. + +* **Server (`server.go`)**: Handles JSON-RPC messages over stdio. +* **Incremental Sync**: Supports `textDocumentSync: 2`. `HandleDidChange` applies patches to the in-memory document buffers using `offsetAt` logic. +* **Features**: + * `HandleCompletion`: Context-aware suggestions (Schema fields, Signal references, Class names). + * `HandleHover`: Shows documentation, signal types, and usage analysis (e.g., "Used in GAMs: Controller (Input)"). + * `HandleDefinition` / `HandleReferences`: specific lookup using the `index`. + +### 5. `internal/builder` + +Merges multiple MARTe files into a single output. + +* **Logic**: It parses all input files, builds a temporary `ProjectTree`, and then reconstructs the source code. +* **Merging**: It interleaves fields and subnodes from different file fragments to produce a coherent single-file configuration, respecting the `#package` hierarchy. + +### 6. `internal/schema` + +Manages CUE schemas. + +* **Loading**: Loads the embedded default schema (`marte.cue`) and merges it with any user-provided `.marte_schema.cue`. +* **Metadata**: Handles the `#meta` field in schemas to extract properties like `direction` and `multithreaded` support for the validator. + +## Key Data Flows + +### Reference Resolution +1. **Scan**: Files are parsed and added to the `ProjectTree`. +2. **Index**: `RebuildIndex` populates `NodeMap`. +3. **Resolve**: `ResolveReferences` iterates all recorded references (values) and calls `FindNode`. +4. **Link**: If found, `ref.Target` is set to the `ProjectNode`. + +### Validation Lifecycle +1. `mdt check` or LSP `didChange` triggers validation. +2. A new `Validator` is created with the current `Tree`. +3. `ValidateProject` is called. +4. It walks the tree, runs checks, and populates `Diagnostics`. +5. Diagnostics are printed (CLI) or published via `textDocument/publishDiagnostics` (LSP). + +### Threading Check Logic +1. Finds the `RealTimeApplication` node. +2. Iterates through `States` and `Threads`. +3. For each Thread, resolves the `Functions` (GAMs). +4. For each GAM, resolves connected `DataSources` via Input/Output signals. +5. Maps `DataSource -> Thread` within the context of a State. +6. If a DataSource is seen in >1 Thread, it checks the `#meta.multithreaded` property. If false (default), an error is raised. diff --git a/docs/CONFIGURATION_GUIDE.md b/docs/CONFIGURATION_GUIDE.md new file mode 100644 index 0000000..73f6387 --- /dev/null +++ b/docs/CONFIGURATION_GUIDE.md @@ -0,0 +1,162 @@ +# MARTe Configuration Guide + +This guide explains the syntax, features, and best practices for writing MARTe configurations using `mdt`. + +## 1. Syntax Overview + +MARTe configurations use a hierarchical object-oriented syntax. + +### Objects (Nodes) +Objects are defined using `+` (public/instantiated) or `$` (template/class-like) prefixes. Every object **must** have a `Class` field. + +```marte ++MyObject = { + Class = MyClass + Field1 = 100 + Field2 = "Hello" +} +``` + +### Fields and Values +- **Fields**: Alphanumeric identifiers (e.g., `Timeout`, `CycleTime`). +- **Values**: + - Integers: `10`, `-5`, `0xFA` + - Floats: `3.14`, `1e-3` + - Strings: `"Text"` + - Booleans: `true`, `false` + - References: `MyObject`, `MyObject.SubNode` + - Arrays: `{ 1 2 3 }` or `{ "A" "B" }` + +### Comments and Documentation +- Line comments: `// This is a comment` +- Docstrings: `//# This documents the following node`. These appear in hover tooltips. + +```marte +//# This is the main application ++App = { ... } +``` + +## 2. Signals and Data Flow + +Signals define how data moves between DataSources (drivers) and GAMs (algorithms). + +### Defining Signals +Signals are typically defined in a `DataSource`. They must have a `Type`. + +```marte ++MyDataSource = { + Class = GAMDataSource + Signals = { + Signal1 = { Type = uint32 } + Signal2 = { Type = float32 } + } +} +``` + +### Using Signals in GAMs +GAMs declare inputs and outputs. You can refer to signals directly or alias them. + +```marte ++MyGAM = { + Class = IOGAM + InputSignals = { + Signal1 = { + DataSource = MyDataSource + Type = uint32 // Must match DataSource definition + } + MyAlias = { + Alias = Signal2 + DataSource = MyDataSource + Type = float32 + } + } +} +``` + +### Threading Rules +**Validation Rule**: A DataSource that is **not** marked as multithreaded (default) cannot be used by GAMs running in different threads within the same State. + +To allow sharing, the DataSource class in the schema must have `#meta: multithreaded: true`. + +## 3. Schemas and Validation + +`mdt` validates your configuration against CUE schemas. + +### Built-in Schema +Common classes (`RealTimeApplication`, `StateMachine`, `IOGAM`, etc.) are built-in. + +### Custom Schemas +You can extend the schema by creating a `.marte_schema.cue` file in your project root. + +**Example: Adding a custom GAM** + +```cue +package schema + +#Classes: { + MyCustomGAM: { + // Metadata for Validator/LSP + #meta: { + direction: "INOUT" // "IN", "OUT", "INOUT" + multithreaded: false + } + + // Fields + Gain: float + Offset?: float // Optional + InputSignals: {...} + OutputSignals: {...} + } +} +``` + +## 4. Multi-file Projects + +You can split your configuration into multiple files. + +### Namespaces +Use `#package` to define where the file's content fits in the hierarchy. + +**file1.marte** +```marte +#package MyApp.Controller ++MyController = { ... } +``` + +This places `MyController` under `MyApp.Controller`. + +### Building +The `build` command merges all files. + +```bash +mdt build -o final.marte src/*.marte +``` + +## 5. Pragmas (Suppressing Warnings) + +If validation is too strict, you can suppress warnings using pragmas (`//!`). + +- **Suppress Unused Warning**: + ```marte + +MyGAM = { + Class = IOGAM + //! ignore(unused): This GAM is triggered externally + } + ``` + +- **Suppress Implicit Signal Warning**: + ```marte + InputSignals = { + //! ignore(implicit) + ImplicitSig = { Type = uint32 } + } + ``` + +- **Type Casting**: + ```marte + Sig1 = { + //! cast(uint32, int32): Intentional mismatch + DataSource = DS + Type = int32 + } + ``` diff --git a/docs/EDITOR_INTEGRATION.md b/docs/EDITOR_INTEGRATION.md new file mode 100644 index 0000000..33bf89a --- /dev/null +++ b/docs/EDITOR_INTEGRATION.md @@ -0,0 +1,158 @@ +# Editor Integration Guide + +`mdt` includes a Language Server Protocol (LSP) implementation that provides features like: + +- Syntax highlighting and error reporting +- Auto-completion +- Go to Definition / References +- Hover documentation +- Symbol renaming + +The LSP server is started via the command: + +```bash +mdt lsp +``` + +It communicates via **stdio**. + +## VS Code + +You can use a generic LSP extension like [Generic LSP Client](https://marketplace.visualstudio.com/items?itemName=summne.vscode-generic-lsp-client) or configure a custom task. + +**Using "Run on Save" or similar extensions is an option, but for true LSP support:** + +1. Install the **"glspc"** (Generic LSP Client) extension or similar. +2. Configure it in your `settings.json`: + +```json +"glspc.languageServer configurations": [ + { + "languageId": "marte", + "command": "mdt", + "args": ["lsp"], + "rootUri": "${workspaceFolder}" + } +] +``` + +3. Associate `.marte` files with the language ID: + +```json +"files.associations": { + "*.marte": "marte" +} +``` + +## Neovim (Native LSP) + +Add the following to your `init.lua` or `init.vim` (using `nvim-lspconfig`): + +```lua +local lspconfig = require'lspconfig' +local configs = require'lspconfig.configs' + +if not configs.marte then + configs.marte = { + default_config = { + cmd = {'mdt', 'lsp'}, + filetypes = {'marte'}, + root_dir = lspconfig.util.root_pattern('.git', 'go.mod', '.marte_schema.cue'), + settings = {}, + }, + } +end + +lspconfig.marte.setup{} + +-- Add filetype detection +vim.cmd([[ + autocmd BufNewFile,BufRead *.marte setfiletype marte +]]) +``` + +## Helix + +Add this to your `languages.toml` (usually in `~/.config/helix/languages.toml`): + +```toml +[[language]] +name = "marte" +scope = "source.marte" +injection-regex = "marte" +file-types = ["marte"] +roots = [".git", ".marte_schema.cue"] +comment-token = "//" +indent = { tab-width = 2, unit = " " } +language-servers = [ "mdt-lsp" ] + +[language-server.mdt-lsp] +command = "mdt" +args = ["lsp"] +``` + +## Vim + +### Using `vim-lsp` + +```vim +if executable('mdt') + au User lsp_setup call lsp#register_server({ + \ 'name': 'mdt-lsp', + \ 'cmd': {server_info->['mdt', 'lsp']}, + \ 'whitelist': ['marte'], + \ }) +endif + +au BufRead,BufNewFile *.marte set filetype=marte +``` + +### Using `ALE` + +```vim +call ale#linter#define('marte', { +\ 'name': 'mdt', +\ 'lsp': 'stdio', +\ 'executable': 'mdt', +\ 'command': '%e lsp', +\ 'project_root': function('ale#handlers#python#FindProjectRoot'), +\}) +``` + +## Zed + +Add to your `settings.json`: + +```json +"lsp": { + "marte": { + "binary": { + "path": "mdt", + "arguments": ["lsp"] + } + } +} +``` + +## Kakoune (kak-lsp) + +In your `kak-lsp.toml`: + +```toml +[language.marte] +filetypes = ["marte"] +roots = [".git", ".marte_schema.cue"] +command = "mdt" +args = ["lsp"] +``` + +## Eclipse + +1. Install **LSP4E** plugin. +2. Go to **Preferences > Language Servers**. +3. Add a new Language Server: + - **Content Type**: Text / Custom (Associate `*.marte` with a content type). + - **Launch configuration**: Program. + - **Command**: `mdt` + - **Arguments**: `lsp` + - **Input/Output**: Standard Input/Output. diff --git a/docs/TUTORIAL.md b/docs/TUTORIAL.md new file mode 100644 index 0000000..1f761df --- /dev/null +++ b/docs/TUTORIAL.md @@ -0,0 +1,173 @@ +# Creating a MARTe Application with mdt + +This tutorial will guide you through creating, building, and validating a complete MARTe application using the `mdt` toolset. + +## Prerequisites + +- `mdt` installed and available in your PATH. +- `make` (optional but recommended). + +## Step 1: Initialize the Project + +Start by creating a new project named `MyControlApp`. + +```bash +mdt init MyControlApp +cd MyControlApp +``` + +This command creates a standard project structure: + +- `Makefile`: For building and checking the project. +- `.marte_schema.cue`: For defining custom schemas (if needed). +- `src/app.marte`: The main application definition. +- `src/components.marte`: A placeholder for defining components (DataSources). + +## Step 2: Define Components + +Open `src/components.marte`. This file uses the `#package App.Data` namespace, meaning all definitions here will be children of `App.Data`. + +Let's define a **Timer** (input source) and a **Logger** (output destination). + +```marte +#package MyContollApp.App.Data + ++DDB = { + Class = GAMDataSource +} ++TimingDataSource = { + Class = TimingDataSource +} ++Timer = { + Class = LinuxTimer + Signals = { + Counter = { + Type = uint32 + } + Time = { + Type = uint32 + } + } +} + ++Logger = { + Class = LoggerDataSource + Signals = { + LogValue = { + Type = float32 + } + } +} +``` + +## Step 3: Implement Logic (GAM) + +Open `src/app.marte`. This file defines the `App` node. + +We will add a GAM that takes the time from the Timer, converts it, and logs it. + +Add the GAM definition inside the `+Main` object (or as a separate object if you prefer modularity). Let's modify `src/app.marte`: + +```marte +#package MyContollApp ++App = { + Class = RealTimeApplication + +Functions = { + Class = RefenceContainer + // Define the GAM + +Converter = { + Class = IOGAM + InputSignals = { + TimeIn = { + DataSource = Timer + Type = uint32 + Frequency = 100 //Hz + Alias = Time // Refers to 'Time' signal in Timer + } + } + OutputSignals = { + LogOut = { + DataSource = Logger + Type = float32 + Alias = LogValue + } + } + } + } + +States = { + Class = ReferenceContainer + +Run = { + Class = RealTimeState + +MainThread = { + Class = RealTimeThread + Functions = { Converter } // Run our GAM + } + } + } + + +Data = { + Class = ReferenceContainer + DefaultDataSource = DDB + } + +Scheduler = { + Class = GAMScheduler + TimingDataSource = TimingDataSource + } +} +``` + +## Step 4: Validate + +Run the validation check to ensure everything is correct (types match, references are valid). + +```bash +mdt check src/*.marte +``` + +Or using Make: + +```bash +make check +``` + +If you made a mistake (e.g., mismatched types), `mdt` will report an error. + +## Step 5: Build + +Merge all files into a single configuration file. + +```bash +mdt build -o final_app.marte src/*.marte +``` + +Or using Make: + +```bash +make build +``` + +This produces `app.marte` (or `final_app.marte`), which contains the flattened, merged configuration ready for the MARTe framework. + +## Step 6: Advanced - Custom Schema + +Suppose you want to enforce that your DataSources support multithreading. You can modify `.marte_schema.cue`. + +```cue +package schema + +#Classes: { + // Enforce that LinuxTimer must be multithreaded (example) + LinuxTimer: { + #meta: { + multithreaded: true + } + ... + } +} +``` + +Now, if you use `LinuxTimer` in multiple threads, `mdt check` will allow it (because of `#meta.multithreaded: true`). By default, it would disallow it. + +## Conclusion + +You have successfully initialized, implemented, validated, and built a MARTe application using `mdt`. diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..a29f07f --- /dev/null +++ b/examples/README.md @@ -0,0 +1,44 @@ +# Examples + +This directory contains example projects demonstrating different features and usage patterns of `mdt`. + +## Directory Structure + +``` +examples/ + simple/ # A basic, single-file application + complex/ # A multi-file project with custom schema + README.md # This file +``` + +## Running Examples + +Prerequisite: `mdt` must be built (or installed). The Makefiles in the examples assume `mdt` is available at `../../build/mdt`. + +### Simple Project + +Demonstrates a minimal setup: +- Single `main.marte` file. +- Basic Thread and GAM definition. + +**Run:** +```bash +cd simple +make check +make build +``` + +### Complex Project + +Demonstrates advanced features: +- **Multi-file Structure**: `src/app.marte` (Logic) and `src/components.marte` (Data). +- **Namespaces**: Use of `#package` to organize nodes. +- **Custom Schema**: `.marte_schema.cue` defines a custom class (`CustomController`) with specific metadata (`#meta.multithreaded`). +- **Validation**: Enforces strict typing and custom rules. + +**Run:** +```bash +cd complex +make check +make build +``` diff --git a/examples/complex/.marte_schema.cue b/examples/complex/.marte_schema.cue new file mode 100644 index 0000000..ce88b39 --- /dev/null +++ b/examples/complex/.marte_schema.cue @@ -0,0 +1,12 @@ +package schema + +#Classes: { + CustomController: { + #meta: { + multithreaded: false + } + Gain: float + InputSignals: {...} + OutputSignals: {...} + } +} diff --git a/examples/complex/Makefile b/examples/complex/Makefile new file mode 100644 index 0000000..252db9f --- /dev/null +++ b/examples/complex/Makefile @@ -0,0 +1,12 @@ +MDT=../../build/mdt + +all: check build + +check: + $(MDT) check src/*.marte + +build: + $(MDT) build -o app_full.marte src/*.marte + +fmt: + $(MDT) fmt src/*.marte diff --git a/examples/complex/src/app.marte b/examples/complex/src/app.marte new file mode 100644 index 0000000..26f3df5 --- /dev/null +++ b/examples/complex/src/app.marte @@ -0,0 +1,42 @@ +#package complex_ex + ++App = { + Class = RealTimeApplication + +States = { + Class = ReferenceContainer + +Run = { + Class = RealTimeState + +ControlThread = { + Class = RealTimeThread + Functions = { Controller } + } + } + } + +Functions = { + Class = ReferenceContainer + +Controller = { + Class = CustomController // Defined in .marte_schema.cue + Gain = 10.5 + InputSignals = { + Ref = { + DataSource = App.Data.References + Type = float32 + } + } + OutputSignals = { + Actuation = { + DataSource = App.Data.Actuators + Type = float32 + } + } + } + } + +Data = { + Class = ReferenceContainer + DefaultDataSource = DDB1 + } + +Scheduler = { + Class = GAMScheduler + TimingDataSource = TimingDS + } +} diff --git a/examples/complex/src/components.marte b/examples/complex/src/components.marte new file mode 100644 index 0000000..9fdd230 --- /dev/null +++ b/examples/complex/src/components.marte @@ -0,0 +1,24 @@ +#package complex_ex.App.Data + ++References = { + Class = GAMDataSource + Signals = { + Ref = { + Type = float32 + } + } +} ++Actuators = { + Class = GAMDataSource + Signals = { + Actuation = { + Type = float32 + } + } +} ++TimingDS = { + Class = TimingDataSource +} ++DDB1 = { + Class = GAMDataSource +} diff --git a/examples/simple/Makefile b/examples/simple/Makefile new file mode 100644 index 0000000..f95b6fc --- /dev/null +++ b/examples/simple/Makefile @@ -0,0 +1,12 @@ +MDT=../../build/mdt + +all: check build + +check: + $(MDT) check main.marte + +build: + $(MDT) build -o output.marte main.marte + +fmt: + $(MDT) fmt main.marte diff --git a/examples/simple/main.marte b/examples/simple/main.marte new file mode 100644 index 0000000..e28c86f --- /dev/null +++ b/examples/simple/main.marte @@ -0,0 +1,60 @@ +//# Main Application ++App = { + Class = RealTimeApplication + +Data = { + Class = ReferenceContainer + DefaultDataSource = DDB1 + +Timer = { + Class = LinuxTimer + Signals = { + Counter = { + Type = uint32 + } + //! unused: Time variable is not used + Time = { + Type = uint32 + } + } + } + +Logger = { + Class = LoggerDataSource + } + +DDB1 = { + Class = GAMDataSource + } + } + +States = { + Class = ReferenceContainer + +Idle = { + Class = RealTimeState + +Thread1 = { + Class = RealTimeThread + CPUs = 0x1 + Functions = { MyGAM } + } + } + } + +Functions = { + Class = ReferenceContainer + +MyGAM = { + Class = IOGAM + InputSignals = { + Counter = { + DataSource = Timer + Type = uint32 + Frequency = 100 //Hz + } + } + OutputSignals = { + CounterCopy = { + DataSource = Logger + Type = uint32 + } + } + } + } + +Scheduler = { + Class = GAMScheduler + TimingDataSource = Timer + } +} diff --git a/specification.md b/specification.md index eac44ac..f6a1dc8 100644 --- a/specification.md +++ b/specification.md @@ -58,7 +58,7 @@ The LSP server should provide the following capabilities: - **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 `#package` URI). 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). + - **Target**: The build output is written to standard output (`stdout`) by default. It can be written to a target file if the `-o` (or `--output`) argument is provided via CLI. - **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. Support for dot-separated paths (e.g., `Node.SubNode`) is required. - **Merging Order**: For objects defined across multiple files, definitions are merged. The build tool must preserve the relative order of fields and sub-nodes as they appear in the source files, interleaving them correctly in the final output.