Pure Rust implementation of Object Constraint Language (OCL) 2.4 with Eclipse OCL compatible APIs.
crates/tree-sitter-ocl: tree-sitter grammar and bindingscrates/ocl: core parsing and evaluation library
- The tree-sitter parser sources (
src/parser.c) are generated bytree-sitter generate. - Run
tree-sitter generateinsidecrates/tree-sitter-oclbefore building if the parser is missing. - Use
./scripts/tree-sitter-test.shto run corpus tests without writing to your home directory. - External tooling locations are documented in
tools/README.md. - Enum literals are supported in expressions using
TypeName::LITERAL.
# Generate the parser once (requires tree-sitter CLI)
./scripts/generate-tree-sitter.sh
# Build workspace
cargo build[dependencies]
ocl = { package = "ocl-lang", version = "0.1" }use ocl::{Metamodel, Ocl, OclBindable, OclValue};
struct EmptyMetamodel;
impl Metamodel for EmptyMetamodel {
type Classifier = String;
type Operation = String;
type Property = String;
fn get_classifier(&self, name: &str) -> Option<Self::Classifier> {
Some(name.to_string())
}
fn get_properties(&self, _classifier: &Self::Classifier) -> Vec<Self::Property> {
Vec::new()
}
fn get_operations(&self, _classifier: &Self::Classifier) -> Vec<Self::Operation> {
Vec::new()
}
fn conforms_to(&self, subtype: &Self::Classifier, supertype: &Self::Classifier) -> bool {
subtype == supertype
}
fn property_type(&self, _property: &Self::Property) -> Self::Classifier {
"OclAny".to_string()
}
fn operation_return_type(&self, _operation: &Self::Operation) -> Self::Classifier {
"OclAny".to_string()
}
}
struct EmptyContext;
impl OclBindable for EmptyContext {
fn to_ocl_value(&self) -> OclValue {
OclValue::Null
}
fn get_property(&self, _name: &str) -> Option<OclValue> {
None
}
fn ocl_type_name(&self) -> &str {
"OclVoid"
}
}
let ocl = Ocl::new(EmptyMetamodel);
let result = ocl.evaluate("1 + 2", &EmptyContext).unwrap();
assert!(matches!(result, OclValue::Integer(3)));Level 1: Core OCL 2.4 support (expressions, collections/iterators, stdlib, constraints/defs, null/invalid propagation, metamodel-backed property/operation resolution).
Level 2: Model integration parity (EMF/UML/Pivot + Complete OCL environments).
Level 3: Eclipse tooling parity (IDE/validation/codegen + full upstream suite and perf gates).
Current status: Level 1 is implemented. Extracted Eclipse OCL evaluation fixtures
(tests/conformance/eclipse) pass 100% (1,983 tests after the latest import).
Use cargo run -p ocl-lang --bin conformance_report -- --dir tests/conformance/eclipse
to recompute counts and pass rate.
let mut ocl = Ocl::new(EmptyMetamodel);
let expr = ocl.parse_expression("1 + 2").unwrap();
let query = ocl.create_query(&expr);
let result = query.evaluate(&EmptyContext).unwrap();
assert!(matches!(result, OclValue::Integer(3)));
let query = ocl.create_query_from_str("1 + 2").unwrap();
let result = query.evaluate(&EmptyContext).unwrap();
assert!(matches!(result, OclValue::Integer(3)));
let env = ocl.create_helper().into_environment();
let query = ocl
.create_query_from_str_with_environment("1 + 2", env)
.unwrap();
let result = query.evaluate(&EmptyContext).unwrap();
assert!(matches!(result, OclValue::Integer(3)));
let document = ocl
.parse_document("context Person def: label: String = 'person'")
.unwrap();
let mut helper = ocl.create_helper();
helper.apply_document(&document);
// Or apply definitions directly to the facade.
ocl.apply_document(&document);Use OclHelper::set_constraint_context to attach context metadata when building
constraints with helper methods.
Use OclHelper::clear_context to reset definition scoping between helper definitions.
Use OclHelper::clear_operation_context to reset stored operation context metadata.
Use OclHelper::clear_constraint_context to reset stored constraint metadata.
Assuming the EmptyMetamodel and EmptyContext from above:
use ocl::OclValue;
let ocl = Ocl::new(EmptyMetamodel);
let helper = ocl.create_helper();
let expr = helper.create_query("x + 1").unwrap();
let mut query = ocl.create_query(&expr);
query
.evaluation_environment()
.add("x", OclValue::Integer(41));
let result = query.evaluate(&EmptyContext).unwrap();
assert!(matches!(result, OclValue::Integer(42)));Example sources live under crates/ocl/examples.
cargo run -p ocl-lang --example basiccargo run -p ocl-lang --example collectionscargo run -p ocl-lang --example enum_literalscargo run -p ocl-lang --example documentcargo run -p ocl-lang --example definitionscargo run -p ocl-lang --example querycargo run -p ocl-lang --example constraints
- Rust tests:
cargo test -p ocl-lang(unit + integration undercrates/ocl/tests, setCONFORMANCE_DIRto run against external fixtures,CONFORMANCE_SUITEto filter suites). - Tree-sitter corpus tests:
./scripts/tree-sitter-test.sh(setTREE_SITTER_BINto override the CLI path). - Conformance tests live in
tests/conformance. - Conformance summary:
./scripts/conformance-report.sh(requirespython3, add--suite NAMEor--dir PATHto filter/override, or setCONFORMANCE_SUITE). - Conformance fixture validation:
./scripts/validate-conformance.sh(requirespython3, add--suite NAME,--dir PATH,--fail-duplicates,--no-warn-duplicates, or setCONFORMANCE_DIR/CONFORMANCE_SUITE). - Conformance pass rate:
cargo run -p ocl-lang --bin conformance_report(add--suite NAME,--dir PATH, or setCONFORMANCE_DIR/CONFORMANCE_SUITE). - Release smoke checks:
./scripts/release-check.sh(add--suite NAME,--dir PATH,CONFORMANCE_DIR,CONFORMANCE_SUITE,--skip-validation,--skip-conformance,--skip-conformance-tests,--skip-tree-sitter,--fail-duplicates, or--no-warn-duplicates). - Prereq check:
./scripts/check-prereqs.sh(add--strictto fail on missing tools,--versionsto print versions). - Criterion bench:
OCL_BENCH_FILTER=collect OCL_BENCH_LIMIT=10 OCL_BENCH_FILE=benchmarks/expressions.txt cargo bench -p ocl-lang --bench eval_bench. - Benchmark workflow details:
benchmarks/README.md. - Script reference:
scripts/README.md. - Crate docs:
crates/ocl/README.mdandcrates/tree-sitter-ocl/README.md. - Import Eclipse OCL JSON fixtures with
./scripts/import-eclipse-tests.sh(requirespython3, add--source/--output/--cleanas needed). - Bootstrap Eclipse OCL sources with
./scripts/setup-eclipse-ocl.sh(use--importor--all; override repo withECLIPSE_OCL_TESTS_REPO). - Install Eclipse OCL plugins into an Eclipse app:
./scripts/install-eclipse-ocl-plugins.sh(defaults totools/Eclipse.app, override withECLIPSE_APP/ECLIPSE_HOME/ECLIPSE_BIN). - Optional env helper:
source ./scripts/eclipse-ocl-env.sh. - CLI eval helper:
cargo run -p ocl-lang --bin ocl_eval -- "1 + 2"(or--file path/to/expr.ocl). - Standalone app via Eclipse plugins:
./scripts/run-eclipse-ocl-cli.sh --plugins /path/to/eclipse/plugins --query "1 + 2"(optionally setECLIPSE_OCL_CLASSPATHvia./scripts/eclipse-ocl-classpath.sh). - Benchmark comparison script:
./scripts/benchmark-compare.sh(requires Java and an Eclipse plugins directory; supports env overrides likeOCL_EVAL_BIN,EXPRESSIONS_FILE,ITERATIONS,WARMUP,PERF_TOLERANCE,REPEATS,SLEEP_BETWEEN,SLEEP_BETWEEN_EXPR,EXPR_FILTER,EXPR_LIMIT,TIME_BIN(auto-detected),ECLIPSE_OCL_PLUGINS,ECLIPSE_OCL_CLASSPATH,ECLIPSE_OCL_OUTPUT,ECLIPSE_HOME,ECLIPSE_OCL_HOME,JAVA_BIN,JAVA_OPTS, and matching CLI flags; use--listto print the selected expressions; run with--helpfor the full list). - Both helpers default to
tools/Eclipse.app/Contents/Eclipsewhen present, soECLIPSE_HOMEis optional.
- Publishing checklist:
RELEASE.md
This repository is under active development. See AGENTS.md for the implementation plan and milestones.