The Swiss Army knife for SysML v2 — parse, validate, query, and modify models from the command line. Written in C with Clang-style diagnostics.
- 🚀 PackCC PEG parser - Generated from
grammar/sysml.peg, fast and maintainable - 🧠 Arena memory allocation - Fast allocation/deallocation, cache-friendly
- 🎯 Clang-style diagnostics - Clear error messages with source context and suggestions
- 📦 KerML and SysML v2 support - Parses both language layers
- 📤 JSON/SysML output - Semantic graph output for visualization and round-trip processing
- 🔍 Semantic analysis - Detects undefined references, duplicates, type errors
- 🔎 Query API -
--selectfor pattern-based element selection - ✏️ Modification API -
--deleteand--set --atfor CRUD operations - 👀 Dry-run mode -
--dry-runfor safe previewing of modifications
Requirements:
- CMake 3.16+
- C11 compiler (GCC, Clang, or MSVC)
mkdir build && cd build
cmake ..
makeFor debug builds:
cmake -DCMAKE_BUILD_TYPE=Debug ..sysml2 - SysML v2 CLI
Usage: sysml2 [options] <file>...
Options:
-o, --output <file> Write output to file
-f, --format <fmt> Output format: json, xml, sysml (default: none)
-I <path> Add library search path for imports
--fix Format and rewrite files in place
-P, --parse-only Parse only, skip semantic validation
--no-validate Same as --parse-only
--no-resolve Disable automatic import resolution
-s, --select <pattern> Filter output to matching elements (repeatable)
--set <file> --at <scope> Insert elements from file into scope
--delete <pattern> Delete elements matching pattern (repeatable)
--dry-run Preview modifications without writing
--create-scope Auto-create target scope if missing
--replace-scope Clear target scope before inserting (preserves fragment order)
--color[=when] Colorize output (auto, always, never)
--max-errors <n> Stop after n errors (default: 20)
-W<warning> Enable warning (e.g., -Werror)
--dump-tokens Dump lexer tokens
--dump-ast Dump parsed AST
-v, --verbose Verbose output
-h, --help Show help
--version Show version
Environment:
SYSML2_LIBRARY_PATH Colon-separated list of library search paths
Validate a KerML file:
./sysml2 model.kermlParse and output JSON semantic graph:
./sysml2 -f json model.sysml > model.jsonThe JSON output includes elements (packages, definitions, usages) and relationships:
{
"meta": { "version": "1.0", "source": "model.sysml" },
"elements": [
{ "id": "Pkg::Part", "name": "Part", "type": "PartDef", "parent": "Pkg" }
],
"relationships": []
}Show lexer tokens (for debugging):
./sysml2 --dump-tokens file.kermlSelect specific element:
./sysml2 --select 'Package::Element' -f json model.sysmlSelect all direct children:
./sysml2 --select 'Package::*' -f json model.sysmlSelect all descendants recursively:
./sysml2 --select 'Package::**' -f json model.sysmlDelete an element (with --fix to write back):
./sysml2 --delete 'Pkg::OldElement' model.sysml --fixInsert elements from a fragment file:
./sysml2 --set fragment.sysml --at 'Pkg' model.sysml --fixInsert from stdin:
echo 'part def Car;' | sysml2 --set - --at 'Vehicles' model.sysml --fixPreview changes without writing:
./sysml2 --delete 'Legacy::**' --dry-run model.sysmlReplace scope contents (preserves fragment element order):
# Useful for fixing E3002 ordering errors - declarations must come before redefinitions
./sysml2 --set reordered.sysml --at 'Package' --replace-scope model.sysml --fixCreate scope if missing and replace its contents:
./sysml2 --set data.sysml --at 'Root::NewScope' --create-scope --replace-scope model.sysml --fixThe parser provides Clang-style error messages with source context:
model.kerml:15:23: error[E2001]: expected ';' after feature declaration
|
15 | feature engine : Engine
| ^ expected ';'
|
= help: add ';' to complete the declaration
model.kerml:20:17: error[E3001]: undefined type 'Engin'
|
20 | feature x : Engin;
| ^^^^^ not found
|
= help: did you mean 'Engine'?
The parser performs semantic validation to catch errors beyond syntax:
| Code | Description |
|---|---|
| E3001 | Undefined type reference |
| E3002 | Undefined feature in redefines |
| E3003 | Undefined namespace in imports |
| E3004 | Duplicate definition in same scope |
| E3005 | Circular specialization chain |
| E3006 | Type compatibility mismatch |
| E3007 | Invalid multiplicity bounds |
| E3008 | Redefinition compatibility error |
The parser supports two modes of cross-file import resolution:
Use the -I flag to specify library search paths. The parser will automatically find and parse imported files:
# Specify library paths with -I
./sysml2 -I /path/to/library model.sysml
# Multiple library paths
./sysml2 -I /path/to/kernel -I /path/to/domain model.sysml
# Using environment variable
export SYSML2_LIBRARY_PATH="/path/to/kernel:/path/to/domain"
./sysml2 model.sysmlExample with verbose output:
$ ./sysml2 -v -I ./my-library test.sysml
note: added library path: /home/user/my-library
Processing: test.sysml
note: resolving import 'MyTypes' -> /home/user/my-library/MyTypes.sysmlThe resolver searches library paths for files matching the package name:
- For
import Foo::*;, searches forFoo.sysmlorFoo.kerml - Searches recursively in subdirectories (up to 5 levels deep)
- Caches parsed files to avoid re-parsing
- Automatically adds directories of input files to search paths (for cross-file imports within a project)
- Detects and handles circular imports
Use --no-resolve to disable automatic resolution:
./sysml2 --no-resolve model.sysml # Like the old behaviorAlternatively, provide all files explicitly on the command line:
# File A defines types, File B imports them
./sysml2 package_a.sysml package_b.sysml// package_a.sysml
package A {
part def Engine;
datatype Real;
}
// package_b.sysml
package B {
import A::*;
part car : Engine; // Resolves via import
attribute weight : Real; // Resolves via import
}
Supported import patterns:
import A::Engine;- Direct element importimport A::*;- Namespace import (all direct members)import A::**;- Recursive import (all nested members)
./sysml2 model.sysml # Full validation (default)
./sysml2 --parse-only model.sysml # Syntax check only, skip validation
./sysml2 --no-validate model.sysml # Same as --parse-only- Namespaces and packages
- Types, classifiers, classes, datatypes, structs
- Features with direction (in/out/inout)
- Specialization (
:>), subsetting (::>), redefinition (:>>) - Multiplicity (
[0..1],[*]) - Associations, behaviors, functions, predicates
- Comments and documentation
- Part definitions and usages
- Action definitions and usages
- State definitions and usages
- Requirement and constraint definitions
- Port definitions
- Interface definitions
- Item definitions
- Attribute definitions
- Enumeration definitions
- Datatype definitions (KerML primitive types)
Run the test suite:
cd build
ctest --output-on-failureOr use the check target:
ninja check # or: make checkRun individual test groups:
./test_lexer # Lexer unit tests
./test_ast # AST/builder/JSON unit tests
./test_validator # Validator unit tests
./test_query # Query unit tests
./test_modify # Modification unit tests
./test_memory # Memory tests
ctest -R json_output # JSON fixture tests
ctest -R validation # Validation fixture tests
ctest -R crud # CRUD integration testssysml2/
├── include/sysml2/ # Header files
│ ├── common.h # Common types and macros
│ ├── arena.h # Arena allocator
│ ├── intern.h # String interning
│ ├── token.h # Token definitions
│ ├── lexer.h # Lexer interface
│ ├── diagnostic.h # Error reporting
│ ├── cli.h # CLI options
│ ├── ast.h # AST node types
│ ├── ast_builder.h # AST builder context
│ ├── json_writer.h # JSON serialization
│ ├── sysml_writer.h # SysML/KerML output
│ ├── import_resolver.h # Automatic import resolution
│ ├── validator.h # Semantic validator
│ ├── symtab.h # Symbol table
│ ├── query.h # Query API
│ ├── modify.h # Modification API
│ ├── pipeline.h # Processing pipeline
│ ├── sysml_parser.h # Parser interface
│ └── utils.h # Utility functions
├── src/
│ ├── arena.c # Arena allocator implementation
│ ├── intern.c # String interning implementation
│ ├── keywords.c # Keyword recognition
│ ├── lexer.c # Lexer implementation
│ ├── diagnostic.c # Diagnostic reporting
│ ├── ast.c # AST utilities
│ ├── ast_builder.c # AST builder implementation
│ ├── json_writer.c # JSON writer implementation
│ ├── sysml_writer.c # SysML writer implementation
│ ├── import_resolver.c # Import resolution implementation
│ ├── validator.c # Semantic validation
│ ├── query.c # Query implementation
│ ├── modify.c # Modification implementation
│ ├── pipeline.c # Pipeline implementation
│ ├── main.c # CLI entry point
│ └── sysml_parser.c # PackCC-generated parser
├── grammar/
│ └── sysml.peg # PEG grammar (source of truth)
├── tests/
│ ├── test_lexer.c # Lexer unit tests
│ ├── test_ast.c # AST/builder/JSON unit tests
│ ├── test_validator.c # Validator unit tests
│ ├── test_packcc_parser.c # Parser integration tests
│ ├── test_query.c # Query unit tests
│ ├── test_modify.c # Modification unit tests
│ ├── test_memory.c # Memory/arena tests
│ ├── test_diagnostic.c # Diagnostic tests
│ ├── test_import_resolver.c # Import resolver tests
│ ├── test_json_writer.c # JSON writer tests
│ ├── test_sysml_writer.c # SysML writer tests
│ ├── test_json_output.sh # JSON output fixture tests
│ ├── test_validation.sh # Validation fixture tests
│ ├── test_crud.sh # CLI CRUD integration tests
│ └── fixtures/ # Test fixtures
│ ├── json/ # JSON output test pairs
│ ├── validation/ # Validation test cases
│ ├── official/ # Official SysML v2 examples
│ └── errors/ # Error case tests
└── CMakeLists.txt
MIT License