diff --git a/README.md b/README.md index b8c63f7..05452b8 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ Imagine a microservice with Python code and a Dockerfile. You want to ensure: ```hielements element orders_service: - # Define scopes with language annotations (V2 syntax) + # Define scopes with language annotations scope python_module = python.module_selector('orders') scope dockerfile = docker.file_selector('orders_service.dockerfile') @@ -76,18 +76,18 @@ Define architectural patterns once and reuse them across your codebase: ```hielements pattern compiler { element lexer { - scope module # Unbounded scope (V2) + scope module # Unbounded scope in pattern ref tokens: TokenStream } element parser { - scope module # Unbounded scope (V2) + scope module # Unbounded scope in pattern ref ast: AbstractSyntaxTree } check compiler.lexer.tokens.compatible_with(compiler.parser.input) } element python_compiler implements compiler { - # Bind pattern scopes using V2 binds keyword + # Bind pattern scopes using binds keyword scope lexer_mod binds compiler.lexer.module = rust.module_selector('pycompiler::lexer') ref lexer_tokens: TokenStream binds compiler.lexer.tokens = rust.function_selector(lexer_mod, 'tokenize') @@ -270,7 +270,7 @@ Reject PRs that violate architectural rules. ### 1. Define Elements (Descriptive) Elements represent logical components with: -- **Scope**: What code/artifacts belong to this element (with V2 language annotations like ``) +- **Scope**: What code/artifacts belong to this element (with language annotations like ``) - **Connection Points**: APIs, interfaces, or dependencies the element exposes - **Children**: Sub-elements for hierarchical composition @@ -504,7 +504,7 @@ Check out our [Contributing Guide](CONTRIBUTING.md) (coming soon). - **Multi-level**: From high-level system design to low-level module structure - **Flexible**: Support both description (documenting what exists) and prescription (enforcing what should be) -**Hielements V2 makes this possible** through: +**Hielements makes this possible** through: - **Descriptive mode**: Document your architecture without enforcement - **Prescriptive mode**: Use patterns and checks to enforce architectural rules - **Hybrid approach**: Mix both modes as needed for different parts of your system diff --git a/USAGE.md b/USAGE.md index 70aef55..ff5c47e 100644 --- a/USAGE.md +++ b/USAGE.md @@ -177,7 +177,7 @@ element containerized_service: ## Using Patterns -Patterns are reusable architectural blueprints that can be instantiated with concrete implementations. Patterns (declared with the `template` keyword) help enforce consistent structure across similar components. +Patterns are reusable architectural blueprints that can be instantiated with concrete implementations. Patterns (declared with the `pattern` keyword) help enforce consistent structure across similar components. > 📚 **See the [Pattern Catalog](doc/patterns_catalog.md)** for an extensive collection of common software engineering patterns with their Hielements implementations. diff --git a/agent-changelog/remove-backwards-compatibility.md b/agent-changelog/remove-backwards-compatibility.md new file mode 100644 index 0000000..71efc45 --- /dev/null +++ b/agent-changelog/remove-backwards-compatibility.md @@ -0,0 +1,65 @@ +# Remove Backwards Compatibility and Version References + +## Summary + +Removed all backwards compatible elements of the language, all version references (V1, V2, V3), and all migration guides. The repository now has a single, clean language definition with no version history. + +## Changes Made + +### Source Code (Rust) + +**`crates/hielements-core/src/lexer.rs`**: +- Removed `Template` token kind (`template` keyword no longer recognized) +- Removed `ConnectionPoint` token kind (`connection_point` keyword no longer recognized) +- Updated tests to use `pattern` instead of `template`, and no longer reference `connection_point` + +**`crates/hielements-core/src/parser.rs`**: +- Removed handling of `Template` token (only `pattern` keyword accepted) +- Removed handling of `ConnectionPoint` token (only `ref` keyword accepted) +- Removed legacy colon syntax for scope language annotation (`scope src : python` no longer valid) +- Updated all error messages to say "patterns" instead of "templates" +- Updated all tests to use current language syntax + +**`crates/hielements-core/src/ast.rs`**: +- Removed `ConnectionPointDeclaration` type alias +- Removed backward-compat comments mentioning V2 or `connection_point` + +**`crates/hielements-core/src/interpreter.rs`**: +- Removed V2 references from inline comments + +### Documentation + +- **Deleted** `doc/language_v2.md` (V2 language documentation no longer needed) +- **Updated** `doc/language_reference.md`: Removed all V1/V2/V3 labels, version header, migration appendix (Appendix D), and backward compatibility notes throughout +- **Updated** `doc/implementation_status.md`: Removed V2/V3 labels from feature table, removed `template` and `connection_point` backward compat rows + +### Examples + +- **Deleted** `examples/v2_syntax_example.hie` (used deprecated `connection_point` keyword) +- **Deleted** `examples/v3_syntax_example.hie` (referenced V3 version) +- **Created** `examples/syntax_example.hie` (clean demonstration of current language features) +- **Updated** `examples/language_example.hie`: Removed V2 annotation comments + +### Self-Description + +- **Updated** `hielements.hie`: Removed V3 syntax comment, updated example file checks, updated internal comments + +### Other Files + +- **Updated** `README.md`: Removed V2 references in code examples +- **Updated** `USAGE.md`: Changed `template` keyword reference to `pattern` +- **Updated** `doc/scope_management.md`: Changed `template` keyword reference to `pattern` +- **Updated** `specs/core.hie`: Removed V3 syntax comment + +## Language Changes (Breaking) + +The following keywords are **no longer supported**: +- `template` - use `pattern` instead +- `connection_point` - use `ref` instead + +The following syntax is **no longer supported**: +- `scope name : language` (colon syntax for language annotation) - use `scope name` instead + +## Test Results + +All 92 tests pass. All 121 self-check passes. diff --git a/crates/hielements-core/src/ast.rs b/crates/hielements-core/src/ast.rs index e297dbc..8162765 100644 --- a/crates/hielements-core/src/ast.rs +++ b/crates/hielements-core/src/ast.rs @@ -49,7 +49,7 @@ pub struct Template { pub name: Identifier, /// Scope declarations pub scopes: Vec, - /// Ref declarations (formerly connection_point) + /// Ref declarations pub refs: Vec, /// Check declarations pub checks: Vec, @@ -92,7 +92,7 @@ pub struct Element { pub implements: Vec, /// Scope declarations pub scopes: Vec, - /// Ref declarations (formerly connection_point, renamed to avoid confusion with uses) + /// Ref declarations pub refs: Vec, /// Uses declarations - dependencies on other elements or scopes pub uses: Vec, @@ -108,7 +108,7 @@ pub struct Element { pub span: Span, } -/// A scope declaration (V2 supports unbounded scopes and bindings). +/// A scope declaration (supports unbounded scopes and bindings). #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ScopeDeclaration { /// Scope name @@ -123,8 +123,7 @@ pub struct ScopeDeclaration { pub span: Span, } -/// A ref declaration (V2 supports unbounded refs and bindings). -/// Renamed from connection_point to avoid confusion with 'uses' declarations. +/// A ref declaration (supports unbounded refs and bindings). #[derive(Debug, Clone, Serialize, Deserialize)] pub struct RefDeclaration { /// Ref name @@ -139,9 +138,6 @@ pub struct RefDeclaration { pub span: Span, } -/// Type alias for backward compatibility -pub type ConnectionPointDeclaration = RefDeclaration; - /// A uses declaration - declares that this element/scope uses another element or scope. /// This represents a dependency relationship (calling/importing). #[derive(Debug, Clone, Serialize, Deserialize)] @@ -278,10 +274,10 @@ impl BooleanLiteral { // New Unified Syntax Types // ============================================================================ -/// Unified component requirement that supports the new syntax: +/// Unified component requirement that supports the syntax: /// `requires [descendant] element name [: Type] [implements template]` /// `allows [descendant] connection to pattern` -/// `forbids [descendant] connection_point name: Type` +/// `forbids [descendant] ref name: Type` #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ComponentRequirement { /// The action: requires, allows, or forbids @@ -325,7 +321,7 @@ pub enum ComponentSpec { }, /// Connection requirement: `connection to pattern` (deprecated, use UsesDeclaration) Connection(ConnectionPattern), - /// Ref requirement: `ref name: Type [= expr]` (formerly connection_point) + /// Ref requirement: `ref name: Type [= expr]` Ref { /// Ref name name: Identifier, diff --git a/crates/hielements-core/src/interpreter.rs b/crates/hielements-core/src/interpreter.rs index 7a190d3..e1ed887 100644 --- a/crates/hielements-core/src/interpreter.rs +++ b/crates/hielements-core/src/interpreter.rs @@ -123,14 +123,14 @@ impl Interpreter { file_path: &str, diagnostics: &mut Diagnostics, ) { - // Validate scopes (expression is optional for unbounded scopes in V2) + // Validate scopes (expression is optional for unbounded scopes in patterns) for scope in &template.scopes { if let Some(ref expr) = scope.expression { self.validate_expression(expr, file_path, diagnostics); } } - // Validate refs (expression is optional for unbounded in V2) + // Validate refs (expression is optional for unbounded in patterns) for r in &template.refs { if let Some(ref expr) = r.expression { self.validate_expression(expr, file_path, diagnostics); @@ -172,14 +172,14 @@ impl Interpreter { let _ = template_impl; // Acknowledge the field } - // Validate scopes (expression is optional for unbounded scopes in V2) + // Validate scopes (expression is optional for unbounded scopes in patterns) for scope in &element.scopes { if let Some(ref expr) = scope.expression { self.validate_expression(expr, file_path, diagnostics); } } - // Validate refs (expression is optional for unbounded in V2) + // Validate refs (expression is optional for unbounded in patterns) for r in &element.refs { if let Some(ref expr) = r.expression { self.validate_expression(expr, file_path, diagnostics); @@ -326,7 +326,7 @@ impl Interpreter { if options.verbose { eprintln!("[verbose] Evaluating scope: {}", scope_name); } - // V2: expression is optional for unbounded scopes + // Expression is optional for unbounded scopes in patterns if let Some(ref expr) = scope.expression { match self.evaluate_expression(expr) { Ok(value) => { diff --git a/crates/hielements-core/src/lexer.rs b/crates/hielements-core/src/lexer.rs index 7adf68c..f09027a 100644 --- a/crates/hielements-core/src/lexer.rs +++ b/crates/hielements-core/src/lexer.rs @@ -14,9 +14,6 @@ pub enum TokenKind { #[token("element")] Element, - #[token("template")] - Template, - #[token("pattern")] Pattern, @@ -29,9 +26,6 @@ pub enum TokenKind { #[token("scope")] Scope, - #[token("connection_point")] - ConnectionPoint, - #[token("ref")] Ref, @@ -418,16 +412,16 @@ mod tests { #[test] fn test_keywords() { - let source = "element template implements scope connection_point check import from as true false"; + let source = "element pattern implements scope ref check import from as true false"; let mut lexer = Lexer::new(source); let tokens = lexer.tokenize(); let kinds: Vec<_> = tokens.iter().map(|t| &t.kind).collect(); assert!(kinds.contains(&&TokenKind::Element)); - assert!(kinds.contains(&&TokenKind::Template)); + assert!(kinds.contains(&&TokenKind::Pattern)); assert!(kinds.contains(&&TokenKind::Implements)); assert!(kinds.contains(&&TokenKind::Scope)); - assert!(kinds.contains(&&TokenKind::ConnectionPoint)); + assert!(kinds.contains(&&TokenKind::Ref)); assert!(kinds.contains(&&TokenKind::Check)); assert!(kinds.contains(&&TokenKind::Import)); assert!(kinds.contains(&&TokenKind::From)); @@ -437,13 +431,13 @@ mod tests { } #[test] - fn test_template_keyword() { - let source = "template compiler:\n element lexer"; + fn test_pattern_keyword() { + let source = "pattern compiler:\n element lexer"; let mut lexer = Lexer::new(source); let tokens = lexer.tokenize(); let kinds: Vec<_> = tokens.iter().map(|t| &t.kind).collect(); - assert!(kinds.contains(&&TokenKind::Template)); + assert!(kinds.contains(&&TokenKind::Pattern)); assert!(kinds.contains(&&TokenKind::Identifier)); assert!(kinds.contains(&&TokenKind::Colon)); assert!(kinds.contains(&&TokenKind::Element)); @@ -544,7 +538,7 @@ mod tests { } #[test] - fn test_v2_scope_with_binds() { + fn test_scope_with_binds() { let source = "scope main_module binds observable.metrics.module = rust.module_selector('api')"; let mut lexer = Lexer::new(source); let tokens = lexer.tokenize(); diff --git a/crates/hielements-core/src/parser.rs b/crates/hielements-core/src/parser.rs index 9900811..19325f7 100644 --- a/crates/hielements-core/src/parser.rs +++ b/crates/hielements-core/src/parser.rs @@ -9,7 +9,7 @@ use crate::span::Span; /// Note: 'requires', 'allows', 'forbids' are only allowed in templates, not elements. const EXPECTED_ELEMENT_BODY_TOKENS: &str = "'scope', 'ref', 'uses', 'check', or 'element'"; -/// Expected tokens in template body for error messages. +/// Expected tokens in pattern body for error messages. const EXPECTED_TEMPLATE_BODY_TOKENS: &str = "'scope', 'ref', 'check', 'element', 'requires', 'allows', or 'forbids'"; /// Parser for the Hielements language. @@ -70,7 +70,7 @@ impl<'a> Parser<'a> { // Skip doc comments before templates/elements let doc_comment = self.parse_doc_comment(); - if self.check(TokenKind::Template) || self.check(TokenKind::Pattern) { + if self.check(TokenKind::Pattern) { match self.parse_template(doc_comment) { Ok(template) => templates.push(template), Err(diag) => { @@ -97,7 +97,7 @@ impl<'a> Parser<'a> { } else if !self.is_at_end() { let token = self.current(); self.diagnostics.push( - Diagnostic::error("E001", format!("Expected 'template', 'element', or 'language', found {:?}", token.kind)) + Diagnostic::error("E001", format!("Expected 'pattern', 'element', or 'language', found {:?}", token.kind)) .with_file(&self.file_path) .with_span(token.span) .build(), @@ -325,14 +325,14 @@ impl<'a> Parser<'a> { } /// Parse an element declaration. - /// `in_template` indicates whether this element is inside a template (allows unbounded scopes). + /// `in_template` indicates whether this element is inside a pattern (allows unbounded scopes). /// Supports both curly bracket syntax `element name { ... }` and indentation syntax `element name:\n ...` fn parse_element_with_context(&mut self, doc_comment: Option, in_template: bool) -> Result { let start_span = self.current_span(); self.expect(TokenKind::Element)?; let name = self.parse_identifier()?; - // Parse optional template implementation + // Parse optional pattern implementation let mut implements = Vec::new(); if self.check(TokenKind::Implements) { self.advance(); @@ -393,8 +393,8 @@ impl<'a> Parser<'a> { if self.check(TokenKind::Scope) { scopes.push(self.parse_scope()?); - } else if self.check(TokenKind::Ref) || self.check(TokenKind::ConnectionPoint) { - // Support both 'ref' and 'connection_point' keywords + } else if self.check(TokenKind::Ref) { + // Parse ref declaration refs.push(self.parse_ref()?); } else if self.check(TokenKind::Uses) { // Parse uses declaration: `source uses target` @@ -410,7 +410,7 @@ impl<'a> Parser<'a> { return Err(Diagnostic::error( "E012", format!( - "'{}' is only allowed in templates, not in regular elements. Define a template with this constraint and have the element implement it.", + "'{}' is only allowed in patterns, not in regular elements. Define a pattern with this constraint and have the element implement it.", token.text ), ) @@ -420,7 +420,7 @@ impl<'a> Parser<'a> { } else if self.check(TokenKind::Identifier) { // Could be: // 1. A uses declaration: `identifier uses target` - // 2. A template binding: `template.element.scope = ...` + // 2. A pattern binding: `pattern.element.scope = ...` let pos = self.pos; self.advance(); // consume identifier @@ -429,17 +429,17 @@ impl<'a> Parser<'a> { self.pos = pos; // restore position uses.push(self.parse_uses()?); } else if self.check(TokenKind::Dot) { - // Looks like a template binding, restore and try to parse it + // Looks like a pattern binding, restore and try to parse it self.pos = pos; match self.try_parse_template_binding() { Ok(binding) => template_bindings.push(binding), Err(err) => { - // Failed to parse as template binding + // Failed to parse as pattern binding return Err(err); } } } else { - // Not a template binding or uses (no dot or uses keyword after identifier) + // Not a pattern binding or uses (no dot or uses keyword after identifier) self.pos = pos; // restore let token = self.current(); return Err(Diagnostic::error( @@ -488,15 +488,15 @@ impl<'a> Parser<'a> { let end_span = self.previous_span(); // Validate: unbounded scopes (no expression) are NOT allowed in regular elements - // They are only allowed in templates. Provide helpful error message. - // Skip validation if we're inside a template (in_template = true) + // They are only allowed in patterns. Provide helpful error message. + // Skip validation if we're inside a pattern (in_template = true) if !in_template { for scope in &scopes { if scope.expression.is_none() { return Err(Diagnostic::error( "E014", format!( - "Unbounded scope '{}' is only allowed in templates, not in regular elements. \ + "Unbounded scope '{}' is only allowed in patterns, not in regular elements. \ Provide an expression: `scope {} = `", scope.name.name, scope.name.name ), @@ -513,7 +513,7 @@ impl<'a> Parser<'a> { return Err(Diagnostic::error( "E015", format!( - "Unbounded ref '{}' is only allowed in templates, not in regular elements. \ + "Unbounded ref '{}' is only allowed in patterns, not in regular elements. \ Provide an expression: `ref {}: {} = `", r.name.name, r.name.name, r.type_annotation.type_name.name ), @@ -534,32 +534,30 @@ impl<'a> Parser<'a> { uses, checks, template_bindings, - component_requirements: Vec::new(), // Always empty - requires/allows/forbids only allowed in templates + component_requirements: Vec::new(), // Always empty - requires/allows/forbids only allowed in patterns children, span: start_span.merge(&end_span), }) } - /// Convenience wrapper to parse element at top level (not in template) + /// Convenience wrapper to parse element at top level (not in a pattern) fn parse_element(&mut self, doc_comment: Option) -> Result { self.parse_element_with_context(doc_comment, false) } - /// Parse a template declaration. + /// Parse a pattern declaration. /// Supports both curly bracket syntax `pattern name { ... }` and indentation syntax `pattern name:\n ...` - /// Also accepts `template` for backward compatibility. fn parse_template(&mut self, doc_comment: Option) -> Result { let start_span = self.current_span(); - // Accept both 'pattern' (preferred) and 'template' (backward compatibility) - if !self.check(TokenKind::Pattern) && !self.check(TokenKind::Template) { + if !self.check(TokenKind::Pattern) { return Err(Diagnostic::error( "E001", - format!("Expected 'pattern' or 'template' keyword, found {:?}", self.current().kind) + format!("Expected 'pattern' keyword, found {:?}", self.current().kind) ).with_file(&self.file_path) .with_span(start_span) .build()); } - self.advance(); // consume 'pattern' or 'template' + self.advance(); // consume 'pattern' let name = self.parse_identifier()?; // Support both curly bracket syntax `{ ... }` and colon/indent syntax `: ...` @@ -603,8 +601,8 @@ impl<'a> Parser<'a> { if self.check(TokenKind::Scope) { scopes.push(self.parse_scope()?); - } else if self.check(TokenKind::Ref) || self.check(TokenKind::ConnectionPoint) { - // Support both 'ref' and 'connection_point' keywords + } else if self.check(TokenKind::Ref) { + // Parse ref declaration refs.push(self.parse_ref()?); } else if self.check(TokenKind::Check) { checks.push(self.parse_check()?); @@ -627,7 +625,7 @@ impl<'a> Parser<'a> { return Err(Diagnostic::error( "E002", format!( - "Expected {} in template, found {:?}", + "Expected {} in pattern, found {:?}", EXPECTED_TEMPLATE_BODY_TOKENS, token.kind ), @@ -663,19 +661,19 @@ impl<'a> Parser<'a> { }) } - /// Try to parse a template binding (e.g., template.element.scope = expression). + /// Try to parse a pattern binding (e.g., pattern.element.scope = expression). fn try_parse_template_binding(&mut self) -> Result { let start_span = self.current_span(); let start_pos = self.pos; - // Parse the qualified path (template.element.property) + // Parse the qualified path (pattern.element.property) let mut path = vec![self.parse_identifier()?]; - // Must have at least one dot for it to be a template binding + // Must have at least one dot for it to be a pattern binding if !self.check(TokenKind::Dot) { // Restore position and fail self.pos = start_pos; - return Err(Diagnostic::error("E003", "Not a template binding") + return Err(Diagnostic::error("E003", "Not a pattern binding") .with_file(&self.file_path) .with_span(start_span) .build()); @@ -686,11 +684,11 @@ impl<'a> Parser<'a> { path.push(self.parse_identifier()?); } - // Template bindings must have at least 2 parts (template.property) and use = + // Pattern bindings must have at least 2 parts (pattern.property) and use = if path.len() < 2 || !self.check(TokenKind::Equals) { // Restore position and fail self.pos = start_pos; - return Err(Diagnostic::error("E003", "Not a template binding") + return Err(Diagnostic::error("E003", "Not a pattern binding") .with_file(&self.file_path) .with_span(start_span) .build()); @@ -708,9 +706,9 @@ impl<'a> Parser<'a> { }) } - /// Parse a scope declaration (V2 syntax). + /// Parse a scope declaration. /// Syntax: `scope [] [binds ] [= ]` - /// Unbounded scopes (no `=`) are allowed in templates. + /// Unbounded scopes (no `=`) are allowed in patterns. fn parse_scope(&mut self) -> Result { let start_span = self.current_span(); self.expect(TokenKind::Scope)?; @@ -722,15 +720,11 @@ impl<'a> Parser<'a> { let lang = self.parse_identifier()?; self.expect(TokenKind::RAngle)?; // consume '>' Some(lang) - } else if self.check(TokenKind::Colon) { - // Also support legacy colon syntax for backward compatibility during migration - self.advance(); // consume ':' - Some(self.parse_identifier()?) } else { None }; - // Check for optional binds clause: `binds template.element.scope` + // Check for optional binds clause: `binds pattern.element.scope` let binds = if self.check(TokenKind::Binds) { self.advance(); // consume 'binds' Some(self.parse_qualified_path()?) @@ -738,7 +732,7 @@ impl<'a> Parser<'a> { None }; - // Expression is optional for unbounded scopes in templates + // Expression is optional for unbounded scopes in patterns let expression = if self.check(TokenKind::Equals) { self.expect(TokenKind::Equals)?; Some(self.parse_expression()?) @@ -758,25 +752,19 @@ impl<'a> Parser<'a> { }) } - /// Parse a ref declaration (V2 syntax - renamed from connection_point). + /// Parse a ref declaration. /// Syntax: `ref : [binds ] [= ]` - /// Also accepts `connection_point` for backward compatibility. - /// Unbounded refs (no `=`) are allowed in templates. + /// Unbounded refs (no `=`) are allowed in patterns. fn parse_ref(&mut self) -> Result { let start_span = self.current_span(); - // Accept both 'ref' and 'connection_point' keywords - if self.check(TokenKind::Ref) { - self.advance(); - } else { - self.expect(TokenKind::ConnectionPoint)?; - } + self.expect(TokenKind::Ref)?; let name = self.parse_identifier()?; // Parse mandatory type annotation: `: ` self.expect(TokenKind::Colon)?; let type_annotation = self.parse_type_annotation()?; - // Check for optional binds clause: `binds template.element.ref` + // Check for optional binds clause: `binds pattern.element.ref` let binds = if self.check(TokenKind::Binds) { self.advance(); // consume 'binds' Some(self.parse_qualified_path()?) @@ -784,7 +772,7 @@ impl<'a> Parser<'a> { None }; - // Expression is optional for unbounded refs in templates + // Expression is optional for unbounded refs in patterns let expression = if self.check(TokenKind::Equals) { self.expect(TokenKind::Equals)?; Some(self.parse_expression()?) @@ -840,7 +828,7 @@ impl<'a> Parser<'a> { }) } - /// Parse a qualified path for binds clauses: `template.element.scope` + /// Parse a qualified path for binds clauses: `pattern.element.scope` fn parse_qualified_path(&mut self) -> Result, Diagnostic> { let mut path = vec![self.parse_identifier()?]; while self.check(TokenKind::Dot) { @@ -895,7 +883,7 @@ impl<'a> Parser<'a> { } /// Parse a component requirement. - /// Syntax: (requires | allows | forbids) [descendant] (scope | check | element | connection | connection_point) + /// Syntax: (requires | allows | forbids) [descendant] (scope | check | element | connection | ref) fn parse_component_requirement(&mut self, action: RequirementAction) -> Result { let start_span = self.current_span(); @@ -926,8 +914,7 @@ impl<'a> Parser<'a> { let pattern = self.parse_connection_pattern()?; self.expect_newline()?; ComponentSpec::Connection(pattern) - } else if self.check(TokenKind::Ref) || self.check(TokenKind::ConnectionPoint) { - // Support both 'ref' and 'connection_point' keywords + } else if self.check(TokenKind::Ref) { self.parse_ref_component_spec()? } else if self.check(TokenKind::Implements) { // Support for shorthand: `requires descendant implements template_name` @@ -977,7 +964,7 @@ impl<'a> Parser<'a> { } /// Parse an element component specification. - /// Syntax: element name [: Type] [implements template] [: body] + /// Syntax: element name [: Type] [implements pattern] [: body] fn parse_element_component_spec(&mut self) -> Result { self.advance(); // consume 'element' @@ -1055,7 +1042,7 @@ impl<'a> Parser<'a> { if self.check(TokenKind::Scope) { scopes.push(self.parse_scope()?); - } else if self.check(TokenKind::Ref) || self.check(TokenKind::ConnectionPoint) { + } else if self.check(TokenKind::Ref) { refs.push(self.parse_ref()?); } else if self.check(TokenKind::Check) { checks.push(self.parse_check()?); @@ -1119,11 +1106,10 @@ impl<'a> Parser<'a> { }) } - /// Parse a ref component specification (formerly connection_point). + /// Parse a ref component specification. /// Syntax: ref name: Type [= expression] - /// Also accepts connection_point for backward compatibility. fn parse_ref_component_spec(&mut self) -> Result { - self.advance(); // consume 'ref' or 'connection_point' + self.advance(); // consume 'ref' let name = self.parse_identifier()?; self.expect(TokenKind::Colon)?; @@ -1262,13 +1248,13 @@ impl<'a> Parser<'a> { let token = self.advance(); Ok(Identifier::new(token.text, token.span)) } else { - // In some contexts (like template binding paths), keywords can be used as identifiers + // In some contexts (like pattern binding paths), keywords can be used as identifiers // Allow certain keywords to be treated as identifiers let token = self.current(); match token.kind { TokenKind::Scope | TokenKind::Element | TokenKind::Check | - TokenKind::ConnectionPoint | TokenKind::Ref | TokenKind::Uses | - TokenKind::Template | TokenKind::Pattern | TokenKind::Implements | + TokenKind::Ref | TokenKind::Uses | + TokenKind::Pattern | TokenKind::Implements | TokenKind::Binds | TokenKind::To | // Unified keywords can also be used as identifiers in some contexts TokenKind::Requires | TokenKind::Allows | TokenKind::Forbids | @@ -1508,12 +1494,12 @@ element service: } #[test] - fn test_parse_template_declaration() { - let source = r#"template compiler: + fn test_parse_pattern_declaration() { + let source = r#"pattern compiler: element lexer: - connection_point tokens: TokenStream = rust.function_selector('tokenize') + ref tokens: TokenStream = rust.function_selector('tokenize') element parser: - connection_point ast: AbstractSyntaxTree = rust.function_selector('parse') + ref ast: AbstractSyntaxTree = rust.function_selector('parse') check compiler.lexer.tokens.compatible_with(compiler.parser.input) "#; let parser = Parser::new(source, "test.hie"); @@ -1582,12 +1568,12 @@ element service: } #[test] - fn test_parse_template_with_scopes_and_checks() { - let source = r#"template microservice: + fn test_parse_pattern_with_scopes_and_checks() { + let source = r#"pattern microservice: scope config = files.file_selector('config.yaml') element api: - connection_point endpoint: HttpHandler = rust.function_selector('api_handler') + ref endpoint: HttpHandler = rust.function_selector('api_handler') check microservice.api.endpoint.is_valid() "#; @@ -1603,11 +1589,11 @@ element service: } #[test] - fn test_parse_connection_point_with_type() { + fn test_parse_ref_with_type() { let source = r#"element api_service: - connection_point port: integer = docker.exposed_port(dockerfile) - connection_point url: string = config.get_api_url() - connection_point enabled: boolean = config.get_flag('api_enabled') + ref port: integer = docker.exposed_port(dockerfile) + ref url: string = config.get_api_url() + ref enabled: boolean = config.get_flag('api_enabled') "#; let parser = Parser::new(source, "test.hie"); let (program, diagnostics) = parser.parse(); @@ -1618,23 +1604,24 @@ element service: let element = &program.elements[0]; assert_eq!(element.refs.len(), 3); - // Check first connection point with type + // Check first ref with type assert_eq!(element.refs[0].name.name, "port"); assert_eq!(element.refs[0].type_annotation.type_name.name, "integer"); - // Check second connection point with type + // Check second ref with type assert_eq!(element.refs[1].name.name, "url"); assert_eq!(element.refs[1].type_annotation.type_name.name, "string"); - // Check third connection point with type + // Check third ref with type assert_eq!(element.refs[2].name.name, "enabled"); assert_eq!(element.refs[2].type_annotation.type_name.name, "boolean"); } #[test] - fn test_parse_connection_point_without_type_fails() { + fn test_parse_ref_without_type_fails() { + // ref without type annotation should fail - the parser expects `: type` after name let source = r#"element api_service: - connection_point endpoint = python.public_functions(module) + ref endpoint "#; let parser = Parser::new(source, "test.hie"); let (_program, diagnostics) = parser.parse(); @@ -1644,12 +1631,12 @@ element service: } #[test] - fn test_parse_connection_point_with_custom_type() { - let source = r#"template compiler: + fn test_parse_ref_with_custom_type() { + let source = r#"pattern compiler: element lexer: - connection_point tokens: TokenStream = rust.struct_selector('Token') + ref tokens: TokenStream = rust.struct_selector('Token') element parser: - connection_point ast: AbstractSyntaxTree = rust.struct_selector('Program') + ref ast: AbstractSyntaxTree = rust.struct_selector('Program') "#; let parser = Parser::new(source, "test.hie"); let (program, diagnostics) = parser.parse(); @@ -1660,12 +1647,12 @@ element service: let template = &program.templates[0]; assert_eq!(template.elements.len(), 2); - // Check lexer connection point with custom type + // Check lexer ref with custom type let lexer = &template.elements[0]; assert_eq!(lexer.refs[0].name.name, "tokens"); assert_eq!(lexer.refs[0].type_annotation.type_name.name, "TokenStream"); - // Check parser connection point with custom type + // Check parser ref with custom type let parser_elem = &template.elements[1]; assert_eq!(parser_elem.refs[0].name.name, "ast"); assert_eq!(parser_elem.refs[0].type_annotation.type_name.name, "AbstractSyntaxTree"); @@ -1677,7 +1664,7 @@ element service: #[test] fn test_parse_requires_descendant_scope() { - let source = r#"template dockerized: + let source = r#"pattern dockerized: requires descendant scope dockerfile = docker.file_selector('Dockerfile') "#; let parser = Parser::new(source, "test.hie"); @@ -1703,7 +1690,7 @@ element service: #[test] fn test_parse_requires_descendant_element() { - let source = r#"template observable: + let source = r#"pattern observable: requires descendant element metrics_service implements metrics_provider "#; let parser = Parser::new(source, "test.hie"); @@ -1729,9 +1716,9 @@ element service: } #[test] - fn test_parse_forbids_descendant_connection_point() { - let source = r#"template secure_zone: - forbids descendant connection_point external_api: HttpHandler + fn test_parse_forbids_descendant_ref() { + let source = r#"pattern secure_zone: + forbids descendant ref external_api: HttpHandler "#; let parser = Parser::new(source, "test.hie"); let (program, diagnostics) = parser.parse(); @@ -1751,13 +1738,13 @@ element service: assert_eq!(name.name, "external_api"); assert_eq!(type_annotation.type_name.name, "HttpHandler"); } - _ => panic!("Expected connection_point component"), + _ => panic!("Expected ref component"), } } #[test] fn test_parse_allows_connection() { - let source = r#"template frontend_zone: + let source = r#"pattern frontend_zone: allows connection to api_gateway.public_api "#; let parser = Parser::new(source, "test.hie"); @@ -1786,7 +1773,7 @@ element service: #[test] fn test_parse_forbids_connection_with_wildcard() { - let source = r#"template secure_zone: + let source = r#"pattern secure_zone: forbids connection to external.* "#; let parser = Parser::new(source, "test.hie"); @@ -1815,7 +1802,7 @@ element service: #[test] fn test_parse_requires_element_immediate() { // Without 'descendant' modifier - requires immediate child - let source = r#"template microservice: + let source = r#"pattern microservice: requires element api implements api_handler "#; let parser = Parser::new(source, "test.hie"); @@ -1842,8 +1829,8 @@ element service: #[test] fn test_parse_requires_descendant_implements_shorthand() { - // Shorthand: requires descendant implements template_name - let source = r#"template production_ready: + // Shorthand: requires descendant implements pattern_name + let source = r#"pattern production_ready: requires descendant implements dockerized "#; let parser = Parser::new(source, "test.hie"); @@ -1869,7 +1856,7 @@ element service: #[test] fn test_parse_all_requirement_types() { - let source = r#"template complete: + let source = r#"pattern complete: requires descendant scope config = files.file_selector('config.yaml') requires descendant check files.exists(config, 'required.txt') allows connection to api.* @@ -1888,10 +1875,10 @@ element service: #[test] fn test_parse_element_with_body() { - let source = r#"template observable: + let source = r#"pattern observable: requires descendant element metrics: scope module = rust.module_selector('metrics') - connection_point handler: MetricsHandler = rust.function_selector(module, 'handler') + ref handler: MetricsHandler = rust.function_selector(module, 'handler') "#; let parser = Parser::new(source, "test.hie"); let (program, diagnostics) = parser.parse(); @@ -1918,7 +1905,7 @@ element service: #[test] fn test_parse_requires_check() { - let source = r#"template validated: + let source = r#"pattern validated: requires descendant check files.exists(src, 'README.md') "#; let parser = Parser::new(source, "test.hie"); @@ -1942,7 +1929,7 @@ element service: #[test] fn test_parse_requires_in_element_fails() { - // requires/allows/forbids should only be allowed in templates, not elements + // requires/allows/forbids should only be allowed in patterns, not elements let source = r#"element test_element: scope src = files.folder_selector('src') requires connection to logging.* @@ -1952,14 +1939,14 @@ element service: // Should have errors because requires is not allowed in elements assert!(diagnostics.has_errors(), "Expected error for 'requires' in element"); - // Check that the error message mentions templates + // Check that the error message mentions patterns let error_msg = diagnostics.iter().next().unwrap().message.clone(); - assert!(error_msg.contains("template"), "Error message should mention templates: {}", error_msg); + assert!(error_msg.contains("pattern"), "Error message should mention patterns: {}", error_msg); } #[test] fn test_parse_allows_in_element_fails() { - // requires/allows/forbids should only be allowed in templates, not elements + // requires/allows/forbids should only be allowed in patterns, not elements let source = r#"element test_element: allows connection to api.* "#; @@ -1972,7 +1959,7 @@ element service: #[test] fn test_parse_forbids_in_element_fails() { - // requires/allows/forbids should only be allowed in templates, not elements + // requires/allows/forbids should only be allowed in patterns, not elements let source = r#"element test_element: forbids connection to external.* "#; @@ -2043,7 +2030,7 @@ language java #[test] fn test_parse_scope_with_language() { let source = r#"element test: - scope src : python = python.module_selector('test') + scope src = python.module_selector('test') "#; let parser = Parser::new(source, "test.hie"); let (program, diagnostics) = parser.parse(); @@ -2080,7 +2067,7 @@ language java #[test] fn test_parse_requires_language() { - let source = r#"template python_only: + let source = r#"pattern python_only: requires language python "#; let parser = Parser::new(source, "test.hie"); @@ -2106,7 +2093,7 @@ language java #[test] fn test_parse_forbids_language() { - let source = r#"template no_rust: + let source = r#"pattern no_rust: forbids language rust "#; let parser = Parser::new(source, "test.hie"); @@ -2131,7 +2118,7 @@ language java #[test] fn test_parse_allows_language() { - let source = r#"template multilingual: + let source = r#"pattern multilingual: allows language python allows language rust "#; @@ -2167,12 +2154,12 @@ language java connection_check no_circular(scopes: scope[]): python.no_circular_imports(scopes) -template python_service: +pattern python_service: requires language python forbids language rust element my_api implements python_service: - scope src : python = python.module_selector('my_api') + scope src = python.module_selector('my_api') check python.has_docstrings(src) "#; let parser = Parser::new(source, "test.hie"); @@ -2197,11 +2184,11 @@ element my_api implements python_service: } // ======================================================================== - // Tests for V2 syntax: angular brackets, binds keyword, unbounded scopes + // Tests for: angular brackets, binds keyword, unbounded scopes // ======================================================================== #[test] - fn test_parse_v2_angular_bracket_language() { + fn test_parse_angular_bracket_language() { let source = r#"element test: scope src = rust.module_selector('test') "#; @@ -2221,11 +2208,11 @@ element my_api implements python_service: } #[test] - fn test_parse_v2_unbounded_scope_in_template() { - let source = r#"template observable: + fn test_parse_unbounded_scope_in_pattern() { + let source = r#"pattern observable: element metrics: scope module - connection_point prometheus: MetricsHandler + ref prometheus: MetricsHandler "#; let parser = Parser::new(source, "test.hie"); let (program, diagnostics) = parser.parse(); @@ -2246,16 +2233,16 @@ element my_api implements python_service: // Unbounded scope - no expression assert!(scope.expression.is_none()); - // Connection point - unbounded + // Unbounded ref assert_eq!(metrics.refs.len(), 1); - let cp = &metrics.refs[0]; - assert_eq!(cp.name.name, "prometheus"); - assert_eq!(cp.type_annotation.type_name.name, "MetricsHandler"); - assert!(cp.expression.is_none()); + let r = &metrics.refs[0]; + assert_eq!(r.name.name, "prometheus"); + assert_eq!(r.type_annotation.type_name.name, "MetricsHandler"); + assert!(r.expression.is_none()); } #[test] - fn test_parse_v2_scope_with_binds() { + fn test_parse_scope_with_binds() { let source = r#"element component implements observable: scope main_module binds observable.metrics.module = rust.module_selector('api') "#; @@ -2287,9 +2274,9 @@ element my_api implements python_service: } #[test] - fn test_parse_v2_connection_point_with_binds() { + fn test_parse_ref_with_binds() { let source = r#"element component implements observable: - connection_point handler: MetricsHandler binds observable.metrics.prometheus = rust.function_selector(module, 'handler') + ref handler: MetricsHandler binds observable.metrics.prometheus = rust.function_selector(module, 'handler') "#; let parser = Parser::new(source, "test.hie"); let (program, diagnostics) = parser.parse(); @@ -2318,16 +2305,16 @@ element my_api implements python_service: } #[test] - fn test_parse_v2_complete_template_and_implementation() { - let source = r#"template observable: + fn test_parse_complete_pattern_and_implementation() { + let source = r#"pattern observable: allows language rust element metrics: scope module - connection_point prometheus: MetricsHandler + ref prometheus: MetricsHandler element observable_component implements observable: scope main_module binds observable.metrics.module = rust.module_selector('payments::api') - connection_point main_handler: MetricsHandler binds observable.metrics.prometheus = rust.function_selector(main_module, 'handler') + ref main_handler: MetricsHandler binds observable.metrics.prometheus = rust.function_selector(main_module, 'handler') "#; let parser = Parser::new(source, "test.hie"); let (program, diagnostics) = parser.parse(); @@ -2335,14 +2322,14 @@ element observable_component implements observable: assert!(!diagnostics.has_errors(), "Errors: {:?}", diagnostics); let program = program.unwrap(); - // Check template + // Check pattern assert_eq!(program.templates.len(), 1); let template = &program.templates[0]; assert_eq!(template.name.name, "observable"); assert_eq!(template.elements.len(), 1); assert_eq!(template.component_requirements.len(), 1); // allows language rust - // Check template element has unbounded scope + // Check pattern element has unbounded scope let template_metrics = &template.elements[0]; assert!(template_metrics.scopes[0].expression.is_none()); assert!(template_metrics.refs[0].expression.is_none()); @@ -2360,16 +2347,16 @@ element observable_component implements observable: assert!(scope.binds.is_some()); assert!(scope.expression.is_some()); - // Check element has bound connection point + // Check element has bound ref assert_eq!(element.refs.len(), 1); - let cp = &element.refs[0]; - assert!(cp.binds.is_some()); - assert!(cp.expression.is_some()); + let r = &element.refs[0]; + assert!(r.binds.is_some()); + assert!(r.expression.is_some()); } #[test] - fn test_parse_v2_unbounded_scope_in_element_fails() { - // Unbounded scopes are only allowed in templates, not in regular elements + fn test_parse_unbounded_scope_in_element_fails() { + // Unbounded scopes are only allowed in patterns, not in regular elements let source = r#"element test: scope src "#; @@ -2379,26 +2366,26 @@ element observable_component implements observable: // Should have errors because unbounded scope is not allowed in elements assert!(diagnostics.has_errors(), "Expected error for unbounded scope in element"); let error_msg = diagnostics.iter().next().unwrap().message.clone(); - assert!(error_msg.contains("only allowed in templates"), "Error message should mention templates: {}", error_msg); + assert!(error_msg.contains("only allowed in patterns"), "Error message should mention patterns: {}", error_msg); } #[test] - fn test_parse_v2_unbounded_connection_point_in_element_fails() { - // Unbounded connection points are only allowed in templates, not in regular elements + fn test_parse_unbounded_ref_in_element_fails() { + // Unbounded refs are only allowed in patterns, not in regular elements let source = r#"element test: - connection_point api: HttpHandler + ref api: HttpHandler "#; let parser = Parser::new(source, "test.hie"); let (_program, diagnostics) = parser.parse(); - // Should have errors because unbounded connection point is not allowed in elements - assert!(diagnostics.has_errors(), "Expected error for unbounded connection point in element"); + // Should have errors because unbounded ref is not allowed in elements + assert!(diagnostics.has_errors(), "Expected error for unbounded ref in element"); let error_msg = diagnostics.iter().next().unwrap().message.clone(); - assert!(error_msg.contains("only allowed in templates"), "Error message should mention templates: {}", error_msg); + assert!(error_msg.contains("only allowed in patterns"), "Error message should mention patterns: {}", error_msg); } // ======================================================================== - // Tests for V3 syntax: curly brackets, ref keyword, uses keyword + // Tests for: curly brackets, ref keyword, uses keyword // ======================================================================== #[test] @@ -2420,8 +2407,8 @@ element observable_component implements observable: } #[test] - fn test_parse_curly_brackets_template() { - let source = r#"template observable { + fn test_parse_curly_brackets_pattern() { + let source = r#"pattern observable { element metrics { scope module ref prometheus: MetricsHandler diff --git a/doc/implementation_status.md b/doc/implementation_status.md index 9e1342a..2cfc50b 100644 --- a/doc/implementation_status.md +++ b/doc/implementation_status.md @@ -15,19 +15,17 @@ This document tracks the implementation status of each Hielements language featu | `scope` declarations (bound) | ✅ Implemented | With selector expressions | | `scope` declarations (unbounded, in patterns) | ✅ Implemented | Without `=` expression, placeholder for `binds` | | Language annotations `` | ✅ Implemented | Stored in AST, used in connection checks | -| `ref` declarations | ✅ Implemented | V3 preferred keyword (alias for `connection_point`) | -| `connection_point` declarations | ✅ Implemented | V2/backward compat, parsed as `ref` | +| `ref` declarations | ✅ Implemented | Declares reference points with type annotations | | `check` declarations | ✅ Implemented | Evaluated at runtime via library calls | | `uses` declarations (syntax) | ✅ Parsed | AST node stored, dependency not validated | | `import` statements | ✅ Implemented | Loads built-in and external libraries | | Doc comments (`##`) | ✅ Implemented | Stored in AST for tooling | -### Pattern / Template Features +### Pattern Features | Feature | Status | Notes | |---------|--------|-------| -| `pattern` keyword | ✅ Implemented | V3 preferred keyword | -| `template` keyword | ✅ Implemented | V2/backward compat, equivalent to `pattern` | +| `pattern` keyword | ✅ Implemented | Declares reusable architectural blueprints | | `implements` keyword (syntax) | ✅ Parsed | Stored in AST; binding completeness **not enforced** | | `binds` keyword (syntax) | ✅ Parsed | Stored in AST; path resolution **not enforced** | | Pattern-level `requires` | ✅ Implemented | Hierarchical requirements evaluated | @@ -45,8 +43,8 @@ This document tracks the implementation status of each Hielements language featu | Feature | Status | Notes | |---------|--------|-------| -| Indentation-based blocks | ✅ Implemented | V2/V3 compatible | -| Curly bracket blocks `{}` | ✅ Implemented | V3 feature | +| Indentation-based blocks | ✅ Implemented | Supported alongside curly bracket syntax | +| Curly bracket blocks `{}` | ✅ Implemented | Supported alongside indentation syntax | | Both syntaxes mixed | ✅ Implemented | Can be used in the same file | ### Built-in Libraries diff --git a/doc/language_reference.md b/doc/language_reference.md index 5fd7b71..e694c90 100644 --- a/doc/language_reference.md +++ b/doc/language_reference.md @@ -1,2209 +1,1960 @@ -# Hielements Language Reference (V3) - -This document provides a complete reference for the Hielements V3 language syntax and semantics. Hielements is a declarative language for describing and enforcing software architecture. - -**Version**: 3.0 (This version adds curly bracket syntax, `ref` keyword, and `uses` declarations) - -## Design Philosophy - -Hielements V3 introduces several improvements over V2: - -- **Curly brackets `{}`**: Alternative to indentation-based blocks for clearer scope delimiters -- **`ref` keyword**: Renamed from `connection_point` to avoid confusion with `uses` declarations -- **`uses` keyword**: Explicit dependency declarations between elements/scopes - -It is possible to use only the descriptive part without the prescriptive one; in this case, no enforcement/checks are performed. - ---- - -## Table of Contents - -1. [Lexical Structure](#1-lexical-structure) -2. [Program Structure](#2-program-structure) -3. [Elements](#3-elements) -4. [Scopes](#4-scopes) -5. [Refs](#5-refs-formerly-connection-points) -6. [Uses Declarations](#6-uses-declarations-v3) -7. [Rules (Checks)](#7-rules-checks) -8. [Children Elements](#8-children-elements) -9. [Patterns](#9-patterns) -10. [Language Declarations](#10-language-declarations) -11. [Imports and Modules](#11-imports-and-modules) -12. [Expressions](#12-expressions) -13. [Built-in Libraries](#13-built-in-libraries) -14. [Comments](#14-comments) -15. [Complete Grammar](#15-complete-grammar) -16. [Examples](#16-examples) -17. [Appendix A: Error Messages](#appendix-a-error-messages) -18. [Appendix B: CLI Reference](#appendix-b-cli-reference) -19. [Appendix C: Best Practices](#appendix-c-best-practices) -20. [Appendix D: Migration Guide (V1/V2 → V3)](#appendix-d-migration-guide-v1v2--v3) - ---- - -## 1. Lexical Structure - -### 1.1 Character Set - -Hielements source files are UTF-8 encoded text files. The recommended file extension is `.hie`. - -### 1.2 Identifiers - -Identifiers name elements, scopes, connection points, and other entities. - -``` -identifier ::= letter (letter | digit | '_')* -letter ::= 'a'..'z' | 'A'..'Z' | '_' -digit ::= '0'..'9' -``` - -**Valid identifiers:** -``` -orders_module -MyService -_internal -api2 -``` - -**Invalid identifiers:** -``` -2fast # Cannot start with digit -my-service # Hyphens not allowed (use underscores) -``` - -### 1.3 Keywords - -The following are reserved keywords: - -| Keyword | Description | -|---------|-------------| -| `element` | Declares an element | -| `pattern` | Declares a pattern (reusable architectural blueprint, preferred in V3) | -| `template` | Declares a pattern (supported for backward compatibility, prefer `pattern`) | -| `implements` | Declares that an element implements pattern(s) | -| `binds` | Binds a scope/ref to a pattern declaration | -| `scope` | Declares a scope selector | -| `ref` | Declares a reference point (V3, preferred over `connection_point`) | -| `connection_point` | Declares a connection point (supported for backward compatibility, prefer `ref`) | -| `uses` | Declares a dependency on another element/scope (V3) | -| `check` | Declares a rule/check | -| `import` | Imports a library or module | -| `as` | Alias for imports | -| `from` | Selective import | -| `true` | Boolean literal | -| `false` | Boolean literal | -| `requires` | Declares a required component (patterns only) | -| `allows` | Declares an allowed component (patterns only) | -| `forbids` | Declares a forbidden component (patterns only) | -| `descendant` | Modifier for hierarchical requirements (applies to descendants) | -| `connection` | Specifies a connection requirement | -| `to` | Specifies connection target | -| `language` | Declares a language with optional connection checks | -| `connection_check` | Defines a connection verification check for a language | - -### 1.4 Literals - -#### String Literals - -Strings are enclosed in single or double quotes: - -```hielements -'single quoted string' -"double quoted string" -``` - -Escape sequences: -| Sequence | Meaning | -|----------|---------| -| `\\` | Backslash | -| `\'` | Single quote | -| `\"` | Double quote | -| `\n` | Newline | -| `\t` | Tab | - -#### Numeric Literals - -```hielements -42 # Integer -3.14 # Float -8080 # Port number (integer) -``` - -#### Boolean Literals - -```hielements -true -false -``` - -### 1.5 Operators and Punctuation - -| Symbol | Usage | -|--------|-------| -| `=` | Assignment | -| `.` | Member access | -| `,` | Argument separator | -| `:` | Block start, type annotation | -| `{` `}` | Block delimiters (V3 alternative to indentation) | -| `(` `)` | Function call, grouping | -| `[` `]` | List literals | -| `<` `>` | Language specification in scopes | - ---- - -## 2. Program Structure - -A Hielements program (specification) consists of: -1. Optional import statements -2. One or more top-level element declarations - -```hielements -# Imports (optional) -import python -import docker - -# Top-level elements -element my_service: - # ... element body -``` - -### 2.1 File Organization - -A typical Hielements project structure: - -``` -project/ -├── architecture.hie # Main specification -├── modules/ -│ ├── backend.hie # Backend module specs -│ └── frontend.hie # Frontend module specs -└── hielements.config # Configuration (optional) -``` - ---- - -## 3. Elements - -Elements are the fundamental building blocks of Hielements. An element represents a logical component of your software system. - -### 3.1 Syntax - -V3 supports two styles for block delimiters: curly brackets `{}` or colon and indentation: - -``` -# Curly bracket syntax (V3) -element_declaration ::= 'element' identifier '{' element_body '}' - -# Indentation syntax (V2/V3) -element_declaration ::= 'element' identifier ':' element_body - -element_body ::= (scope_declaration - | ref_declaration - | uses_declaration - | check_declaration - | nested_element)* -``` - -### 3.2 Basic Element - -Curly bracket syntax (V3): -```hielements -element orders_service { - scope src = files.folder_selector('src/orders') - check files.contains(src, 'main.py') -} -``` - -Indentation syntax (V2/V3): -```hielements -element orders_service: - scope src = files.folder_selector('src/orders') - check files.contains(src, 'main.py') -``` - -### 3.3 Element with All Components - -```hielements -element payment_gateway: - # Scopes define what code belongs to this element - scope python_module = python.module_selector('payments') - scope config = files.file_selector('config/payments.yaml') - scope dockerfile = docker.file_selector('payments.dockerfile') - - # Connection points expose interfaces to other elements - ref api = python.public_functions(python_module) - ref port = docker.exposed_port(dockerfile) - - # Checks enforce rules - check docker.exposes_port(dockerfile, 8080) - check python.no_circular_imports(python_module) - - # Nested elements for hierarchical structure - element validation_submodule: - scope src = python.module_selector('payments.validation') -``` - -### 3.4 Element Semantics - -- Each element defines a **boundary** around a logical component -- Elements can be **nested** to create hierarchies -- Element names must be **unique** within their scope -- Elements are evaluated **lazily** - scopes are resolved when checks execute - ---- - -## 4. Scopes - -Scopes define what code, files, or artifacts belong to an element. Scopes are specified using **selectors** from libraries. - -### 4.1 Syntax (V2) - -In V2, scopes can be either **bound** (in elements) or **unbounded** (in templates). Language is specified using angular brackets: - -``` -# Bound scope (in elements) -scope_declaration ::= 'scope' identifier ['<' language_name '>'] ['binds' binding_path] '=' selector_expression - -# Unbounded scope (in templates) -scope_declaration ::= 'scope' identifier ['<' language_name '>'] -``` - -### 4.2 Scope Selectors - -Selectors are library functions that identify parts of your codebase: - -```hielements -# File and folder selectors -scope src_folder = files.folder_selector('src/') -scope config_file = files.file_selector('config.yaml') -scope all_python = files.glob_selector('**/*.py') - -# Language-specific selectors with V2 angular bracket syntax -scope orders = python.module_selector('orders') -scope backend = rust.module_selector('backend') - -# Docker selectors -scope dockerfile = docker.file_selector('Dockerfile') -scope compose = docker.compose_selector('docker-compose.yml') -``` - -### 4.3 Scopes with Language Annotations (V2) - -Scopes can optionally include a language annotation using **angular brackets** to explicitly declare which programming language the scope belongs to: - -```hielements -element my_service: - # Scope with explicit language annotation (V2 syntax) - scope src = python.module_selector('my_service') - scope backend = rust.module_selector('backend') - - # Scope without language annotation (inferred from library) - scope config = files.file_selector('config.yaml') -``` - -The language annotation is specified in angular brackets immediately after the scope name (``). - -### 4.4 Unbounded Scopes in Patterns - -In patterns (declared with `template`), scopes are **unbounded** (declared without a selector expression). They serve as placeholders to be bound by implementing elements: - -```hielements -pattern observable: - element metrics: - # Unbounded scope - no '=' expression - scope module - ref prometheus: MetricsHandler -``` - -### 4.5 Binding Scopes with `binds` (V2) - -When an element implements a pattern, it uses the `binds` keyword to connect its scopes to the pattern's unbounded scopes: - -```hielements -element observable_component implements observable: - # Bind this scope to the pattern's unbounded scope - scope main_module binds observable.metrics.module = rust.module_selector('payments::api') - - # Bind a connection point to the pattern's connection point - ref main_handler: MetricsHandler binds observable.metrics.prometheus = rust.function_selector(main_module, 'handler') -``` - -The `binds` clause specifies which pattern scope/ref this declaration satisfies. - -### 4.6 Multiple Scopes - -An element can have multiple scopes, representing different aspects: - -```hielements -element full_stack_feature: - scope frontend = typescript.module_selector('components/OrderForm') - scope backend = python.module_selector('api/orders') - scope database = sql.migration_selector('migrations/001_orders.sql') - scope container = docker.file_selector('orders.dockerfile') -``` - -### 4.7 Scope Composition - -Scopes can be combined using set operations (library-dependent): - -```hielements -element api_layer: - scope handlers = python.package_selector('api.handlers') - scope models = python.package_selector('api.models') - - # Combine scopes - scope all_api = scopes.union(handlers, models) -``` - -### 4.8 Scope Semantics - -- Scopes are **lazy** - they don't scan the filesystem until needed -- Scopes can **overlap** between elements (a file can belong to multiple elements) -- Scope resolution may **fail** if the target doesn't exist (configurable: error vs warning) -- Scopes provide **source locations** for error reporting -- Scopes with **language annotations** enable language-specific connection verification - ---- - -## 5. Refs (formerly Connection Points) - -Refs (reference points) expose interfaces, APIs, or dependencies that other elements can reference. They make inter-element relationships explicit and verifiable. - -**Note**: In V3, `ref` is the preferred keyword. `connection_point` is deprecated but still supported for backward compatibility. - -### 5.1 Syntax - -``` -ref_declaration ::= 'ref' identifier ':' type_name '=' expression -``` - -Type annotations are **mandatory** for all refs. - -### 5.2 Basic Refs - -```hielements -element api_server { - scope module = python.module_selector('api') - - # All refs must have type annotations - ref rest_api: HttpHandler = python.public_functions(module) - - # Expose the main entry point - ref main: Function = python.function_selector(module, 'main') -} -``` - -### 5.3 Ref Type Annotations - -Refs **must** have explicit type annotations to ensure type safety across libraries and languages: - -```hielements -element api_server { - scope module = python.module_selector('api') - scope dockerfile = docker.file_selector('Dockerfile') - - # Basic type annotations (mandatory) - ref port: integer = docker.exposed_port(dockerfile) - ref api_url: string = python.get_api_url(module) - ref ssl_enabled: boolean = config.get_flag('ssl') - ref timeout: float = config.get_timeout() - - # Custom type annotations - ref rest_api: HttpHandler = python.public_functions(module) - ref db_conn: DatabaseConnection = python.class_selector(module, 'Database') -} - -#### Basic Types - -| Type | Description | Example Values | -|------|-------------|----------------| -| `string` | Text data | `"api/v1"`, `"localhost"` | -| `integer` | Whole numbers | `8080`, `443`, `-1` | -| `float` | Decimal numbers | `3.14`, `0.5`, `-2.718` | -| `boolean` | True/false | `true`, `false` | - -#### Custom Types - -Custom types are user-defined type names that can represent: -- Type aliases for basic types (e.g., `Port`, `Url`) -- Complex structures from code (e.g., `TokenStream`, `HttpHandler`) -- Library-defined types specific to a domain - -```hielements -# Custom type example -pattern compiler: - element lexer: - ref tokens: TokenStream = rust.struct_selector('Token') - - element parser: - ref ast: AbstractSyntaxTree = rust.struct_selector('Program') -``` - -### 5.4 Using Connection Points - -Connection points are used in checks to verify relationships: - -```hielements -element orders_service: - element api: - scope module = python.module_selector('orders.api') - ref handlers: HttpHandler = python.public_functions(module) - - element docker: - scope dockerfile = docker.file_selector('orders.dockerfile') - - # Verify Docker uses the correct entry point - check docker.entry_point(dockerfile, api.handlers) -``` - -### 5.5 Connection Point Types - -Different libraries expose different types of connection points: - -| Library | Connection Point | Description | -|---------|-----------------|-------------| -| `python` | `public_functions` | All public functions in a module | -| `python` | `exported_classes` | All exported classes | -| `python` | `main_module` | The `__main__` entry point | -| `docker` | `exposed_ports` | Ports exposed by the container | -| `docker` | `volumes` | Mounted volumes | -| `files` | `path` | Filesystem path | - -### 5.6 Connection Point Semantics - -- Connection points are **computed** from scopes -- They can be **referenced** across element boundaries using dot notation -- Connection points enable **dependency checking** between elements -- They provide **documentation** of element interfaces -- **Type annotations** are mandatory and provide type safety -- **Type checking** occurs at specification validation time (when implemented) - ---- - -## 6. Uses Declarations (V3) - -Uses declarations explicitly declare dependencies between elements or scopes. This makes architectural dependencies visible and verifiable. - -### 6.1 Syntax - -``` -uses_declaration ::= identifier 'uses' qualified_identifier -``` - -Where `qualified_identifier` is a path like `lexer` or `core.lexer`. - -### 6.2 Basic Uses Declarations - -```hielements -element core { - element lexer { - scope module = rust.module_selector('lexer') - } - - element parser { - scope module = rust.module_selector('parser') - scope lexer_module = rust.module_selector('lexer') - - # Declare that lexer_module depends on lexer element - lexer_module uses lexer - } -} -``` - -### 6.3 Qualified Target Paths - -Uses declarations can reference elements using qualified paths: - -```hielements -element parser { - scope module = rust.module_selector('parser') - - # Reference an element in parent scope using qualified name - module uses core.lexer -} -``` - -### 6.4 Uses Semantics - -- Uses declarations are **explicit** - they document architectural dependencies -- The source identifier must be a **scope** defined in the same element -- The target can be an **element** or **scope** reference -- Name resolution first looks at the **current element**, then **parent scope** -- Uses declarations enable **dependency validation** and **architecture enforcement** - ---- - -## 7. Rules (Checks) - -Checks enforce architectural rules. They are the mechanism by which Hielements validates your codebase against specifications. - -### 7.1 Syntax - -``` -check_declaration ::= 'check' function_call -``` - -### 7.2 Basic Checks - -```hielements -element my_service: - scope dockerfile = docker.file_selector('Dockerfile') - scope src = python.module_selector('my_service') - - # Check that port 8080 is exposed - check docker.exposes_port(dockerfile, 8080) - - # Check that a specific function exists - check python.function_exists(src, 'handle_request') - - # Check for no circular dependencies - check python.no_circular_imports(src) -``` - -### 7.3 Check Categories - -#### Existence Checks -Verify that something exists: - -```hielements -check files.exists(src, 'README.md') -check python.function_exists(module, 'main') -check docker.stage_exists(dockerfile, 'builder') -``` - -#### Property Checks -Verify properties of artifacts: - -```hielements -check docker.base_image(dockerfile, 'python:3.11-slim') -check python.has_docstring(function) -check files.max_size(file, 1048576) # 1MB max -``` - -#### Relationship Checks -Verify relationships between components: - -```hielements -check docker.entry_point(dockerfile, python_module.main) -check python.imports(module_a, module_b) -check python.no_dependency(module_a, module_b) -``` - -#### Negative Checks -Verify that something does NOT exist or is NOT true: - -```hielements -check files.no_files_matching(src, '*.tmp') -check python.no_circular_imports(module) -check docker.no_root_user(dockerfile) -``` - -### 7.4 Check Results - -Checks produce one of three results: - -| Result | Meaning | -|--------|---------| -| **Pass** | The check succeeded | -| **Fail** | The check failed (architectural violation) | -| **Error** | The check could not be evaluated (e.g., file not found) | - -### 7.5 Check Semantics - -- Checks are evaluated **after** all scopes are resolved -- Checks are **independent** - one failing check doesn't prevent others from running -- Check results include **source locations** for diagnostics -- Checks can be **parallelized** when they have no dependencies - ---- - -## 8. Children Elements - -Elements can contain nested (children) elements to create hierarchical structures. - -### 8.1 Syntax - -Nested elements are declared inside a parent element: - -```hielements -element parent: - element child_a: - # child_a body - - element child_b: - # child_b body -``` - -### 8.2 Hierarchical Example - -```hielements -element ecommerce_platform: - - element orders_service: - scope module = python.module_selector('services.orders') - ref api = python.public_functions(module) - - element orders_db: - scope migrations = sql.migration_selector('db/orders') - - element payments_service: - scope module = python.module_selector('services.payments') - ref api = python.public_functions(module) - - # Cross-service check: orders can call payments - check python.can_import(orders_service.module, payments_service.module) -``` - -### 8.3 Referencing Children - -Children elements are referenced using dot notation: - -```hielements -element system: - element service_a: - ref api = python.public_functions(module) - - element service_b: - scope module = python.module_selector('service_b') - - # Reference sibling's connection point - check python.imports(module, service_a.api) -``` - -### 8.4 Scope Inheritance - -Children elements inherit the context of their parent but have their own scope: - -```hielements -element microservices: - # Shared configuration for all children - scope shared_config = files.file_selector('shared/config.yaml') - - element service_a: - scope module = python.module_selector('service_a') - # Can reference parent's scope - check files.references(module, shared_config) -``` - ---- - -## 9. Patterns - -Patterns (declared with the `pattern` keyword) allow creating reusable architectural blueprints that define conformance requirements. Patterns establish structural constraints that elements can implement with concrete scopes and checks. - -> **Note**: The `pattern` keyword is preferred in V3. The `template` keyword is still supported for backward compatibility. See the [Pattern Catalog](patterns_catalog.md) for an extensive collection of common software engineering patterns. - -Patterns define **unbounded** scopes that serve as placeholders, while implementing elements use the **`binds`** keyword to provide concrete bindings. - -### 9.1 Pattern Declaration - -Patterns are declared using the `pattern` keyword (or `template` for backward compatibility) and define a structure with **unbounded scopes**: - -```hielements -pattern observable { - element metrics implements measurable { - allows language rust - - # Unbounded scope - angular brackets specify language - scope module - ref prometheus: MetricsHandler - - check files.exists(module, 'Cargo.toml') - } -} -``` - -**Key features:** -- Scopes in patterns are **unbounded** (no `=` expression) -- Language is specified via **angular brackets** (``) -- Patterns can include `allows`/`requires`/`forbids` constraints -- Both `pattern` and `template` keywords are accepted (`pattern` is preferred in V3) - -### 9.2 Implementing Patterns with `binds` - -Elements implement patterns using the `implements` keyword, then use **`binds`** to connect their scopes: - -```hielements -element observable_component implements observable: - # Bind scope to pattern's unbounded scope - scope main_module binds observable.metrics.module = rust.module_selector('payments::api') - - # Bind connection point - ref main_handler: MetricsHandler binds observable.metrics.prometheus = rust.function_selector(main_module, 'handler') -``` - -The `binds` keyword creates an explicit connection between the element's scope and the pattern's placeholder. - -### 9.3 Descriptive-Only Mode - -The `implements` and `binds` keywords are **optional**. When omitted, Hielements operates in "descriptive-only" mode without prescriptive enforcement: - -```hielements -# Descriptive only - no pattern implementation -element simple_component: - scope src = rust.module_selector('mymodule') - check rust.function_exists(src, 'main') -``` - -### 9.4 Absolute References - -Pattern properties are referenced using absolute paths prefixed with the pattern name (e.g., `observable.metrics`). This prevents name clashes when implementing multiple patterns: - -```hielements -pattern microservice: - element api: - scope module - ref rest_endpoint: HttpHandler - -pattern observable: - element api: - scope module - ref metrics_endpoint: MetricsHandler - -# No name clash - each 'api' is explicitly qualified -element my_service implements microservice, observable: - scope api_mod binds microservice.api.module = rust.module_selector('service::api') - scope metrics_mod binds observable.api.module = rust.module_selector('service::metrics') - - # Reference both in checks - check microservice.api.rest_endpoint.port != observable.api.metrics_endpoint.port -``` - -### 9.5 Multiple Pattern Implementation - -An element can implement multiple patterns: - -```hielements -pattern resilient: - element circuit_breaker: - scope module - -pattern secured: - element authentication: - scope module - -element production_service implements microservice, resilient, secured: - # Microservice bindings - scope api binds microservice.api.module = rust.module_selector('api') - - # Resilient bindings - scope resilience binds resilient.circuit_breaker.module = rust.module_selector('resilience') - - # Secured bindings - scope auth binds secured.authentication.module = rust.module_selector('auth') -``` - -### 9.6 Pattern Requirements - -When implementing a pattern, all unbounded scopes must be bound: - -```hielements -pattern web_service: - element frontend: - scope src - ref static_files: StaticAssets - - element backend: - scope src - ref api: HttpHandler - -# Valid - all required bindings provided -element complete_service implements web_service: - scope frontend_src binds web_service.frontend.src = typescript.module_selector('frontend') - scope backend_src binds web_service.backend.src = python.module_selector('backend') - ref static: StaticAssets binds web_service.frontend.static_files = files.glob_selector('frontend/dist/*') - ref api: HttpHandler binds web_service.backend.api = python.public_functions(backend_src) - -# Invalid - missing bindings (would produce validation error) -element incomplete_service implements web_service: - scope frontend_src binds web_service.frontend.src = typescript.module_selector('frontend') - # ERROR: web_service.backend bindings missing -``` - -### 9.7 Pattern Checks - -Checks defined in patterns are automatically included when the pattern is implemented. The checks use absolute references and are evaluated with the concrete bindings: - -```hielements -pattern microservice: - element api: - scope module - element database: - scope db - element container: - scope dockerfile - - # Pattern checks - check microservice.container.exposes_port(8080) - check microservice.api.connects_to(microservice.database) - -element orders_service implements microservice: - scope api_mod binds microservice.api.module = python.module_selector('orders.api') - scope db binds microservice.database.db = postgres.database_selector('orders_db') - scope dockerfile binds microservice.container.dockerfile = docker.file_selector('orders.dockerfile') - - # The pattern checks are automatically evaluated with bound scopes -``` - -### 9.8 Library-Defined Patterns - -Patterns can be defined in external libraries and imported for use: - -```hielements -import architecture_patterns - -element my_service implements architecture_patterns.hexagonal: - # Bind the hexagonal architecture pattern elements - scope domain binds hexagonal.domain.src = python.package_selector('myapp.domain') - scope app binds hexagonal.application.src = python.package_selector('myapp.application') - scope adapters binds hexagonal.adapters.src = python.package_selector('myapp.adapters') -``` - -External libraries can provide patterns via the library protocol. See the [External Library Plugin Guide](external_libraries.md) for details. - -### 9.9 Pattern Semantics - -- Patterns define **structure** with **unbounded scopes** -- Elements implementing patterns use **`binds`** to connect scopes -- **Angular brackets** specify language (``, ``) -- `implements` and `binds` are **optional** (for prescriptive features) -- Pattern checks are **inherited** by implementing elements -- Absolute references **prevent name clashes** between multiple patterns -- Patterns **cannot be nested** (a pattern cannot implement another pattern) -- Pattern names must be **unique** within their scope - -### 9.10 Pattern-Level Connection Points - -Patterns can declare connection points at the pattern level (not just within child elements). These connection points can be used in pattern checks and must be bound when implementing the pattern. - -**Example:** - -```hielements -pattern microservice: - element api: - scope module - - element container: - scope dockerfile - - ## Pattern-level unbounded connection point - ref port: integer - - ## Pattern checks can reference the pattern-level connection point - check files.exists(container.dockerfile, 'Dockerfile') - check rust.function_exists(api.module, 'start_server') - -## When implementing, bind the pattern-level connection point -element orders_service implements microservice: - scope api_mod binds microservice.api.module = rust.module_selector('orders::api') - scope dockerfile binds microservice.container.dockerfile = files.file_selector('orders.dockerfile') - - ## Bind the pattern-level port - ref service_port: integer binds microservice.port = rust.const_selector('ORDERS_PORT') - -element payments_service implements microservice: - scope api_mod binds microservice.api.module = rust.module_selector('payments::api') - scope dockerfile binds microservice.container.dockerfile = files.file_selector('payments.dockerfile') - - ## Different service, different port - ref service_port: integer binds microservice.port = rust.const_selector('PAYMENTS_PORT') -``` - -**Benefits:** -- **Parameterization**: Patterns can be parameterized without hardcoding values -- **Reusability**: Same pattern structure with different concrete values -- **Type Safety**: Connection points have type annotations ensuring correctness -- **Clarity**: Makes pattern dependencies and parameters explicit - -### 9.11 Hierarchical Checks - -Hierarchical checks allow parent elements to prescribe requirements that must be satisfied by at least one of their descendants (children, grandchildren, etc.). This is useful for expressing architectural constraints that span multiple levels of the hierarchy. - -#### Hierarchical Requirements - -The unified syntax uses `requires`, `allows`, and `forbids` keywords with an optional `descendant` modifier: - -```hielements -pattern dockerized: - ## At least one descendant must have a docker scope (V2 syntax with unbounded scope) - requires descendant scope dockerfile - - ## At least one descendant must satisfy this check - requires descendant check docker.has_healthcheck(dockerfile) - -pattern observable: - ## At least one descendant must have a metrics element with implements - requires descendant element metrics_service implements metrics_provider - -pattern production_ready: - ## At least one descendant must implement the dockerized template - requires descendant implements dockerized - - ## At least one descendant must implement the observable template - requires descendant implements observable -``` - -#### Satisfying Hierarchical Requirements - -When an element implements a pattern with hierarchical requirements, at least one of its descendants must satisfy each requirement: - -```hielements -element my_app implements dockerized: - ## Frontend - not dockerized - element frontend: - scope src = files.folder_selector('frontend') - - ## Backend - satisfies the hierarchical requirement! - element backend: - scope src = files.folder_selector('backend') - scope dockerfile binds dockerized.dockerfile = docker.file_selector('Dockerfile') ## Matches! - check docker.has_healthcheck(dockerfile) ## Matches! - -## Pattern implementation requirements -element ecommerce_platform implements production_ready: - ## Frontend - neither dockerized nor observable - element frontend: - scope src = files.folder_selector('frontend') - - ## Orders - implements dockerized (satisfies first requirement) - element orders implements dockerized: - scope dockerfile binds dockerized.dockerfile = files.file_selector('orders/Dockerfile') - - ## Monitoring - implements observable (satisfies second requirement) - element monitoring implements observable: - scope metrics_mod binds observable.metrics.module = rust.module_selector('monitoring::metrics') - ref prometheus: MetricsHandler binds observable.metrics.prometheus = rust.function_selector(metrics_mod, 'handler') -``` - -#### Hierarchical Requirement Kinds - -| Kind | Syntax | Description | -|------|--------|-------------| -| Scope | `requires descendant scope name[]` | A descendant must have a matching scope | -| Check | `requires descendant check expr` | A descendant must satisfy this check | -| Element | `requires descendant element name [implements pattern]` | A descendant must have an element with this structure | -| Pattern Implementation | `requires descendant implements pattern_name` | A descendant must implement the specified pattern | -| Connection Point | `requires descendant ref name: Type` | A descendant must have a connection point | - -#### Immediate vs Descendant Requirements - -The `descendant` modifier determines whether the requirement applies to any descendant in the hierarchy or only to immediate children: - -```hielements -pattern microservice: - ## Immediate child requirement (no descendant modifier) - requires element api implements api_handler - - ## Any descendant requirement (with descendant modifier) - requires descendant element metrics implements observable -``` - -### 9.12 Connection Boundaries - -Connection boundaries allow specifying constraints on architectural dependencies (imports/dependencies) between elements. These boundaries are inherited by all descendants. **Note**: "Connections" refer to logical/architectural dependencies like module imports, not network connections. - -**Important**: Connection boundaries (`allows connection`, `forbids connection`, `requires connection`) are **only allowed in patterns**, not in regular elements. Elements can inherit these constraints by implementing patterns that define them. - -#### Allowing Connections - -Use `allows connection to` in patterns to whitelist specific connection targets: - -```hielements -pattern frontend_zone: - ## Code in this zone may only import from api_gateway - allows connection to api_gateway.public_api - -element my_frontend implements frontend_zone: - element web_app: - scope src = files.folder_selector('frontend/web') - ## Any imports from this scope are checked against the boundary - - element mobile_api: - scope src = files.folder_selector('frontend/mobile') -``` - -#### Forbidding Connections - -Use `forbids connection to` in patterns to blacklist specific connection targets: - -```hielements -pattern secure_zone: - ## Code in this zone cannot import from external modules - forbids connection to external.* - forbids connection to public_network.* - -element internal_service implements secure_zone: - scope src = files.folder_selector('internal') - ## This element and all its children inherit the constraint -``` - -#### Requiring Connections - -Use `requires connection to` in patterns to mandate that code MUST have a dependency: - -```hielements -pattern service_mesh_zone: - ## All services in this mesh must import from logging module - requires connection to logging.* - -element service_mesh implements service_mesh_zone: - element user_service: - scope src = files.folder_selector('services/user') - # This service must import from logging.* to satisfy the constraint -``` - -#### Wildcard Patterns - -Connection patterns support wildcards with `.*` to match any sub-path: - -```hielements -## In patterns: -forbids connection to database.* ## Matches database.connection, database.pool, etc. -forbids connection to external.* ## Matches anything under external -allows connection to api.public.* ## Matches api.public.users, api.public.orders, etc. -``` - -**Note**: Wildcard interpretation is library-specific. For example, a Python library might expand `logging.*` to all modules in the logging package. - -#### Combined Boundaries - -Multiple boundaries can be combined - allows create a whitelist, forbids create a blacklist, requires create mandatory dependencies: - -```hielements -pattern secure_service: - allows connection to api.endpoint - allows connection to logging.output - forbids connection to database.* - forbids connection to external.network - requires connection to audit.* - -element my_secure_service implements secure_service: - ## Children inherit ALL of these boundaries - element child_service: - scope src = files.folder_selector('child') -``` - -#### Hierarchical Dependency Composition - -When A is allowed to connect to B, and B is allowed to connect to C: -- A→B→C is **allowed** (each hop respects its own boundary) -- This enables construction of complex layered architectures - -#### Boundary Semantics - -- `allows connection` boundaries create a **whitelist** - only listed targets are permitted -- `forbids connection` boundaries create a **blacklist** - listed targets are prohibited -- `requires connection` boundaries create a **mandate** - dependencies MUST exist -- **Connection boundaries are only allowed in patterns** - elements inherit them via `implements` -- Boundaries are **inherited** by all descendants within the parent/child hierarchy -- Multiple boundaries are **combined** (allows AND forbids AND requires apply) -- Wildcards (`.*`) match **any path suffix** (library-specific interpretation) -- Actual verification is **language-specific** - libraries check imports/dependencies - -### 9.13 Language Constraints - -Patterns can constrain which programming languages elements may use through `requires`, `allows`, and `forbids` with the `language` keyword: - -```hielements -pattern python_only: - requires language python - forbids language rust - -pattern multilingual: - allows language python - allows language rust - allows language java -``` - -When an element implements a pattern with language constraints: -- `requires language X` - The element must have at least one scope with language X -- `allows language X` - Only languages X are permitted (whitelist) -- `forbids language X` - Language X is prohibited (blacklist) - ---- - -## 10. Language Declarations - -Language declarations define supported languages and their connection verification checks. - -### 10.1 Simple Language Declaration - -A simple language declaration just registers a language name: - -```hielements -language python -language rust -language java -``` - -### 10.2 Language with Connection Checks - -Languages can define connection checks that verify connections between scopes: - -```hielements -language python: - connection_check can_import(source: scope[], target: scope[]): - python.imports_allowed(source, target) - - connection_check no_circular(scopes: scope[]): - python.no_circular_imports(scopes) - -language rust: - connection_check depends_on(source: scope[], target: scope[]): - rust.dependency_exists(source, target) -``` - -### 10.3 Connection Check Semantics - -Connection checks: -- Accept `scope[]` parameters representing arrays of scopes -- Return `True` (connection valid) or `False` (connection invalid) -- Are automatically applied recursively along the parent-children hierarchy -- Are language-specific - only applied to scopes with matching language annotation - -### 10.4 Connection Verification Process - -When verifying element connections: -1. For each language used by an element, gather all scopes of that language -2. For each child element, gather its scopes of the same language -3. Apply all `connection_check` functions defined for that language -4. Recursively verify all descendants - -**Example:** - -```hielements -language python: - connection_check can_import(source: scope[], target: scope[]): - python.imports_allowed(source, target) - -element system: - element frontend: - scope src : python = python.module_selector('frontend') - - element backend: - scope src : python = python.module_selector('backend') - - element api: - scope src : python = python.module_selector('backend.api') -``` - -The `can_import` check will verify that: -- `frontend` can import from `backend` -- `backend.api` can import from its parent `backend` -- And so on through the hierarchy - ---- - -## 11. Imports and Modules - -Imports bring libraries and other Hielements specifications into scope. - -### 11.1 Library Imports - -```hielements -# Import entire library -import python -import docker -import files - -# Import with alias -import kubernetes as k8s - -# Selective import -from python import module_selector, function_exists -``` - -### 11.2 File Imports - -Import other Hielements files: - -```hielements -# Import another spec file -import './modules/backend.hie' - -# Import with alias -import './shared/common.hie' as common -``` - -### 11.3 Import Resolution - -Import paths are resolved: -1. **Bare imports** (`import python`) - Look up in library registry -2. **Relative paths** (`import './foo.hie'`) - Relative to current file -3. **Absolute paths** (`import '/path/to/foo.hie'`) - Absolute filesystem path - -### 11.4 Built-in Libraries - -The following libraries are built-in: - -| Library | Description | -|---------|-------------| -| `files` | File and folder operations | -| `rust` | Rust code analysis | - -### 11.5 External Libraries (Plugins) - -Hielements supports user-defined libraries through external processes. External libraries are configured in a `hielements.toml` file in your workspace root. - -#### Configuration - -Create a `hielements.toml` file: - -```toml -[libraries] -mylibrary = { executable = "path/to/my-plugin", args = [] } -python_checks = { executable = "python3", args = ["scripts/python_checks.py"] } -``` - -Then use in your .hie files: - -```hielements -import mylibrary - -element mycomponent: - scope src = mylibrary.custom_selector('src') - check mylibrary.custom_check(src) -``` - -#### Protocol - -External libraries communicate via JSON-RPC 2.0 over stdin/stdout. See the [External Library Plugin Guide](external_libraries.md) for details on implementing custom plugins. - ---- - -## 12. Expressions - -Expressions compute values for scopes, connection points, and check arguments. - -### 12.1 Literal Expressions - -```hielements -"string literal" -'another string' -42 -3.14 -true -false -['list', 'of', 'items'] -``` - -### 12.2 Identifier References - -```hielements -my_scope # Reference a scope -parent.child.connection_pt # Qualified reference -``` - -### 12.3 Function Calls - -```hielements -python.module_selector('orders') -docker.exposes_port(dockerfile, 8080) -files.glob_selector('**/*.py') -``` - -### 12.4 Member Access - -```hielements -element.connection_point -library.function -parent.child.scope -``` - -### 12.5 List Expressions - -```hielements -check docker.exposes_ports(dockerfile, [80, 443, 8080]) -``` - ---- - -## 13. Built-in Libraries - -### 13.1 `files` Library - -The `files` library provides selectors and checks for files and folders. - -#### Selectors - -| Function | Description | -|----------|-------------| -| `files.file_selector(path)` | Select a specific file | -| `files.folder_selector(path)` | Select a folder | -| `files.glob_selector(pattern)` | Select files matching glob pattern | - -#### Checks - -| Function | Description | -|----------|-------------| -| `files.exists(scope, filename)` | File exists in scope | -| `files.contains(scope, filename)` | Scope contains file | -| `files.no_files_matching(scope, pattern)` | No files match pattern | -| `files.max_size(file, bytes)` | File size limit | -| `files.matches_pattern(file, pattern)` | File matches pattern | - -#### Examples - -```hielements -element source_code: - scope src = files.folder_selector('src/') - scope tests = files.folder_selector('tests/') - scope all_py = files.glob_selector('**/*.py') - - check files.exists(src, '__init__.py') - check files.no_files_matching(src, '*.pyc') - check files.no_files_matching(src, '__pycache__') -``` - -### 13.2 `python` Library - -The `python` library provides analysis for Python code. - -#### Selectors - -| Function | Description | -|----------|-------------| -| `python.module_selector(name)` | Select Python module by import name | -| `python.package_selector(name)` | Select Python package | -| `python.function_selector(module, name)` | Select specific function | -| `python.class_selector(module, name)` | Select specific class | - -#### Connection Point Functions - -| Function | Description | -|----------|-------------| -| `python.public_functions(module)` | All public functions | -| `python.exported_classes(module)` | All exported classes | -| `python.get_main_module(module)` | The `__main__` entry | - -#### Checks - -| Function | Description | -|----------|-------------| -| `python.function_exists(module, name)` | Function exists | -| `python.class_exists(module, name)` | Class exists | -| `python.imports(module_a, module_b)` | A imports B | -| `python.no_circular_imports(module)` | No circular dependencies | -| `python.has_docstring(item)` | Has documentation | -| `python.type_annotated(function)` | Has type annotations | - -#### Examples - -```hielements -element api_module: - scope module = python.module_selector('myapp.api') - scope handlers = python.package_selector('myapp.api.handlers') - - ref public_api = python.public_functions(module) - - check python.function_exists(module, 'create_app') - check python.no_circular_imports(module) - check python.has_docstring(public_api) -``` - -### 13.3 `docker` Library - -The `docker` library provides analysis for Dockerfiles. - -#### Selectors - -| Function | Description | -|----------|-------------| -| `docker.file_selector(path)` | Select a Dockerfile | -| `docker.compose_selector(path)` | Select docker-compose file | -| `docker.stage_selector(file, name)` | Select build stage | - -#### Connection Point Functions - -| Function | Description | -|----------|-------------| -| `docker.exposed_ports(dockerfile)` | All exposed ports | -| `docker.volumes(dockerfile)` | All volumes | -| `docker.entry_point(dockerfile)` | Container entry point | - -#### Checks - -| Function | Description | -|----------|-------------| -| `docker.exposes_port(dockerfile, port)` | Port is exposed | -| `docker.base_image(dockerfile, image)` | Uses specific base image | -| `docker.no_root_user(dockerfile)` | Doesn't run as root | -| `docker.stage_exists(dockerfile, name)` | Build stage exists | -| `docker.entry_point(dockerfile, module)` | Entry point matches | -| `docker.has_healthcheck(dockerfile)` | Has HEALTHCHECK instruction | - -#### Examples - -```hielements -element containerized_service: - scope dockerfile = docker.file_selector('Dockerfile') - scope compose = docker.compose_selector('docker-compose.yml') - - ref ports = docker.exposed_ports(dockerfile) - - check docker.exposes_port(dockerfile, 8080) - check docker.base_image(dockerfile, 'python:3.11-slim') - check docker.no_root_user(dockerfile) - check docker.has_healthcheck(dockerfile) -``` - ---- - -## 14. Comments - -### 14.1 Single-line Comments - -```hielements -# This is a comment -element my_service: # Inline comment - scope src = files.folder_selector('src/') # Another comment -``` - -### 14.2 Multi-line Comments - -```hielements -### -This is a multi-line comment. -It can span multiple lines. -### -element my_service: - scope src = files.folder_selector('src/') -``` - -### 14.3 Documentation Comments - -Documentation comments (doc comments) provide descriptions for elements: - -```hielements -## Orders Service -## Handles all order-related operations including creation, -## modification, and fulfillment. -element orders_service: - scope module = python.module_selector('orders') -``` - ---- - -## 15. Complete Grammar (V3) - -The following is the complete EBNF grammar for Hielements V2: - -```ebnf -(* Program structure *) -program ::= import_statement* language_declaration* (pattern_declaration | element_declaration)+ - -(* Imports *) -import_statement ::= 'import' import_path ('as' identifier)? - | 'from' import_path 'import' identifier_list -import_path ::= string_literal | identifier ('.' identifier)* -identifier_list ::= identifier (',' identifier)* - -(* Language Declarations *) -language_declaration ::= 'language' identifier NEWLINE - | 'language' identifier ':' NEWLINE INDENT connection_check+ DEDENT -connection_check ::= 'connection_check' identifier '(' parameter_list ')' ':' NEWLINE INDENT expression DEDENT -parameter_list ::= parameter (',' parameter)* -parameter ::= identifier ':' 'scope' '[' ']' - -(* Patterns - with unbounded scopes *) -(* Note: The 'pattern' keyword is preferred, 'template' is supported for backward compatibility *) -pattern_declaration ::= doc_comment? ('pattern' | 'template') identifier ':' NEWLINE INDENT pattern_body DEDENT - | doc_comment? ('pattern' | 'template') identifier '{' pattern_body '}' -pattern_body ::= pattern_item+ -pattern_item ::= scope_declaration_pattern - | ref_declaration_pattern - | check_declaration - | element_declaration - | component_requirement - -(* Scope in patterns - can be unbounded (no '=' expression) *) -scope_declaration_pattern ::= 'scope' identifier language_annotation? NEWLINE - | 'scope' identifier language_annotation? '=' expression NEWLINE - -(* Ref (connection point) in patterns - can be unbounded (no '=' expression) *) -ref_declaration_pattern ::= ('ref' | 'connection_point') identifier ':' type_name NEWLINE - | ('ref' | 'connection_point') identifier ':' type_name '=' expression NEWLINE - -(* Elements *) -(* Note: Elements do NOT support component_requirement - requires/allows/forbids are only in patterns *) -element_declaration ::= doc_comment? 'element' identifier pattern_implementation? ':' NEWLINE INDENT element_body DEDENT - | doc_comment? 'element' identifier pattern_implementation? '{' element_body '}' -pattern_implementation ::= 'implements' identifier (',' identifier)* -element_body ::= element_item+ -element_item ::= scope_declaration - | ref_declaration - | check_declaration - | uses_declaration - | element_declaration - -(* Component Requirements - unified syntax - ONLY allowed in patterns *) -component_requirement ::= ('requires' | 'allows' | 'forbids') ['descendant'] component_spec -component_spec ::= scope_declaration_pattern - | check_declaration - | element_spec - | connection_spec - | ref_spec - | language_spec - -element_spec ::= 'element' identifier [':' type_name] ['implements' identifier] [':' NEWLINE INDENT element_body DEDENT] -connection_spec ::= 'connection' ['to'] connection_pattern NEWLINE -ref_spec ::= ('ref' | 'connection_point') identifier ':' type_name ['=' expression] NEWLINE -connection_pattern ::= identifier ('.' identifier)* ('.' '*')? -language_spec ::= 'language' identifier NEWLINE - -(* Declarations - V2/V3 syntax with angular brackets, binds, ref, and uses *) -language_annotation ::= '<' identifier '>' -binds_clause ::= 'binds' qualified_identifier - -scope_declaration ::= 'scope' identifier language_annotation? binds_clause? '=' expression NEWLINE -ref_declaration ::= ('ref' | 'connection_point') identifier ':' type_name binds_clause? '=' expression NEWLINE -check_declaration ::= 'check' function_call NEWLINE -uses_declaration ::= identifier 'uses' qualified_identifier NEWLINE - -(* Qualified identifiers for binds references *) -qualified_identifier ::= identifier ('.' identifier)+ - -(* Type annotations *) -type_name ::= identifier (* Basic types: string, integer, float, boolean; or custom types *) - -(* Expressions *) -expression ::= function_call - | member_access - | identifier - | literal -member_access ::= expression '.' identifier -function_call ::= member_access '(' argument_list? ')' -argument_list ::= expression (',' expression)* - -(* Literals *) -literal ::= string_literal - | number_literal - | boolean_literal - | list_literal -string_literal ::= '"' character* '"' | "'" character* "'" -number_literal ::= digit+ ('.' digit+)? -boolean_literal ::= 'true' | 'false' -list_literal ::= '[' (expression (',' expression)*)? ']' - -(* Comments *) -comment ::= '#' character* NEWLINE -doc_comment ::= '##' character* NEWLINE -multiline_comment ::= '###' character* '###' - -(* Lexical elements *) -identifier ::= letter (letter | digit | '_')* -letter ::= 'a'..'z' | 'A'..'Z' | '_' -digit ::= '0'..'9' -character ::= -NEWLINE ::= '\n' | '\r\n' -INDENT ::= -DEDENT ::= -``` - ---- - -## 16. Examples - -### 16.1 Simple Service - -```hielements -import files -import rust - -## Order Management Service -## Handles order creation, updates, and fulfillment. -element orders_service: - # Source code scope with V2 language annotation - scope rust_module = rust.module_selector('orders') - scope tests = files.folder_selector('tests/orders') - - # Container scope - scope dockerfile = files.file_selector('orders.dockerfile') - - # Connection points with type annotations - ref api: HttpHandler = rust.public_functions(rust_module) - ref main: Function = rust.function_selector(rust_module, 'main') - - # Architectural rules - check rust.function_exists(rust_module, 'create_order') - check rust.function_exists(rust_module, 'get_order') - check rust.no_circular_imports(rust_module) - - check files.exists(dockerfile, 'Dockerfile') -``` - -### 16.2 Pattern with Unbounded Scopes - -```hielements -import files -import rust - -## Observable Pattern -## Defines a component that exposes metrics -pattern observable: - element metrics: - allows language rust - - # Unbounded scope - will be bound by implementing element - scope module - ref prometheus: MetricsHandler - - check files.exists(module, 'Cargo.toml') - -## Metrics Service implementing the observable pattern -element metrics_service implements observable: - # Bind the scope to the pattern's unbounded scope - scope metrics_mod binds observable.metrics.module = rust.module_selector('metrics::api') - - # Bind the connection point - ref handler: MetricsHandler binds observable.metrics.prometheus = rust.function_selector(metrics_mod, 'handler') -``` - -### 16.3 Microservices Architecture - -```hielements -import files -import rust - -## Microservice Pattern -pattern microservice: - element api: - scope module - ref endpoint: HttpHandler - - element container: - scope dockerfile - - check files.exists(container.dockerfile, 'Dockerfile') - -## E-Commerce Platform -## Main platform containing all microservices. -element ecommerce_platform: - - ## Orders Service - element orders_service implements microservice: - scope api_mod binds microservice.api.module = rust.module_selector('services::orders') - scope dockerfile binds microservice.container.dockerfile = files.file_selector('services/orders/Dockerfile') - ref api: HttpHandler binds microservice.api.endpoint = rust.public_functions(api_mod) - - ## Inventory Service - element inventory_service implements microservice: - scope api_mod binds microservice.api.module = rust.module_selector('services::inventory') - scope dockerfile binds microservice.container.dockerfile = files.file_selector('services/inventory/Dockerfile') - ref api: HttpHandler binds microservice.api.endpoint = rust.public_functions(api_mod) - - ## Payments Service - element payments_service implements microservice: - scope api_mod binds microservice.api.module = rust.module_selector('services::payments') - scope dockerfile binds microservice.container.dockerfile = files.file_selector('services/payments/Dockerfile') - ref api: HttpHandler binds microservice.api.endpoint = rust.public_functions(api_mod) - - # Cross-service rules - check rust.can_import(orders_service.api_mod, inventory_service.api) - check rust.can_import(orders_service.api_mod, payments_service.api) - check rust.no_dependency(payments_service.api_mod, orders_service.api_mod) -``` - -### 16.4 Hexagonal Architecture - -```hielements -import files -import rust - -## Application with Hexagonal Architecture -element hexagonal_app: - - ## Core Domain - ## Contains business logic, no external dependencies. - element domain: - scope module = rust.package_selector('myapp::domain') - - ref entities: Entities = rust.struct_selector(module, '*Entity') - ref services: Services = rust.struct_selector(module, '*Service') - - # Domain must not import adapters - check rust.no_dependency(module, adapters.module) - - ## Application Layer - ## Use cases and application services. - element application: - scope module = rust.package_selector('myapp::application') - - ref use_cases: UseCases = rust.public_functions(module) - - # Application can only depend on domain - check rust.imports_only(module, [domain.module]) - - ## Adapters Layer - ## External integrations (DB, API, etc.) - element adapters: - scope module = rust.package_selector('myapp::adapters') - - element database_adapter: - scope module = rust.module_selector('myapp::adapters::database') - ref repositories: Repositories = rust.struct_selector(module, '*Repository') - - element api_adapter: - scope module = rust.module_selector('myapp::adapters::api') - ref routes: Routes = rust.function_selector(module, 'setup_routes') - - # Adapters depend on application and domain - check rust.imports(module, application.module) - check rust.imports(module, domain.module) -``` - -### 16.5 Infrastructure Validation - -```hielements -import files - -## Infrastructure as Code -element infrastructure: - - ## Docker Configuration - element docker_config: - scope compose = files.file_selector('docker-compose.yml') - scope dockerfile_app = files.file_selector('Dockerfile') - scope dockerfile_worker = files.file_selector('worker.dockerfile') - - # All containers should have health checks - check files.exists(dockerfile_app, 'HEALTHCHECK') - check files.exists(dockerfile_worker, 'HEALTHCHECK') - - ## Configuration Files - element config: - scope env_example = files.file_selector('.env.example') - scope config_dir = files.folder_selector('config/') - - # Required configuration files - check files.exists(config_dir, 'production.yaml') - check files.exists(config_dir, 'development.yaml') - check files.exists(config_dir, 'testing.yaml') - - # No secrets in config files - check files.no_files_matching(config_dir, '*.secret') - check files.no_files_matching(config_dir, '*password*') -``` - -### 16.6 Testing Requirements - -```hielements -import files -import rust - -## Testing Standards -element testing_standards: - scope src = files.folder_selector('src/') - scope tests = files.folder_selector('tests/') - - # Mirror structure: tests should mirror src - element test_coverage: - scope unit_tests = files.folder_selector('tests/unit/') - scope integration_tests = files.folder_selector('tests/integration/') - - # Required test files - check files.exists(unit_tests, 'mod.rs') - check files.exists(integration_tests, 'mod.rs') - - # Each module should have tests - element orders_tests: - scope module = rust.module_selector('orders') - scope tests = rust.module_selector('tests::test_orders') - - check rust.module_exists(tests) - check rust.function_exists(tests, 'test_create_order') -``` - -### 16.7 Patterns - -```hielements -import files -import rust - -## Compiler Pattern with unbounded scopes -## Defines the structure of a compiler with lexer and parser components. -pattern compiler: - ## Lexer - tokenizes source code - element lexer: - scope module - ref tokens: TokenStream - - ## Parser - produces abstract syntax tree - element parser: - scope module - ref ast: AbstractSyntaxTree - - ## Verify lexer output is compatible with parser input - check compiler.lexer.tokens.compatible_with(compiler.parser.input) - -## Rust Compiler Implementation -## Implements the compiler pattern for Rust code. -element rust_compiler implements compiler: - # Bind lexer with V2 binds syntax - scope lexer_mod binds compiler.lexer.module = rust.module_selector('rustcompiler::lexer') - ref tokens: TokenStream binds compiler.lexer.tokens = rust.function_selector(lexer_mod, 'tokenize') - - # Bind parser - scope parser_mod binds compiler.parser.module = rust.module_selector('rustcompiler::parser') - ref ast: AbstractSyntaxTree binds compiler.parser.ast = rust.function_selector(parser_mod, 'parse') - - # Add compiler-specific elements - element optimizer: - scope module = rust.module_selector('rustcompiler::optimizer') - check rust.function_exists(module, 'optimize_ast') - -## Microservice Pattern with unbounded scopes -pattern microservice: - element api: - scope module - ref rest_endpoint: HttpHandler - - element database: - scope module - ref connection: DbConnection - - element container: - scope dockerfile - ref ports: integer - - check microservice.container.exposes_port(8080) - check microservice.api.connects_to(microservice.database) - -## Observable Pattern -pattern observable: - element metrics: - scope module - ref prometheus_endpoint: MetricsHandler - -## Resilient Pattern -pattern resilient: - element circuit_breaker: - scope module - ref breaker_config: BreakerConfig - -## Production Service with Multiple Patterns (V2 syntax) -element production_service implements microservice, observable, resilient: - # Microservice bindings with V2 binds syntax - scope api_mod binds microservice.api.module = rust.module_selector('service::api') - scope db_mod binds microservice.database.module = rust.module_selector('service::db') - scope dockerfile binds microservice.container.dockerfile = files.file_selector('service.dockerfile') - - ref api: HttpHandler binds microservice.api.rest_endpoint = rust.public_functions(api_mod) - ref db: DbConnection binds microservice.database.connection = rust.struct_selector(db_mod, 'DbConnection') - ref ports: integer binds microservice.container.ports = rust.const_selector(api_mod, 'PORT') - - # Observable bindings - scope metrics_mod binds observable.metrics.module = rust.module_selector('service::metrics') - ref prometheus: MetricsHandler binds observable.metrics.prometheus_endpoint = rust.function_selector(metrics_mod, 'metrics_handler') - - # Resilient bindings - scope resilience_mod binds resilient.circuit_breaker.module = rust.module_selector('service::resilience') - ref breaker: BreakerConfig binds resilient.circuit_breaker.breaker_config = rust.struct_selector(resilience_mod, 'CircuitBreakerConfig') -``` - ---- - -## Appendix A: Error Messages - -Common error messages and their meanings: - -| Error Code | Message | Meaning | -|------------|---------|---------| -| E001 | Undefined element '{name}' | Referenced element doesn't exist | -| E002 | Undefined scope '{name}' | Referenced scope doesn't exist | -| E003 | Undefined connection point '{name}' | Referenced connection point doesn't exist | -| E004 | Library '{name}' not found | Import references unknown library | -| E005 | Duplicate element '{name}' | Element name already defined in scope | -| E006 | Check failed: {message} | Architectural rule violation | -| E007 | Scope resolution failed | Selector couldn't find target | -| E008 | Invalid argument type | Wrong type passed to function | -| E009 | Syntax error | Invalid Hielements syntax | -| E010 | Cyclic element reference | Elements reference each other cyclically | - ---- - -## Appendix B: CLI Reference - -### Basic Commands - -```bash -# Validate specification syntax (no execution) -hielements check architecture.hie - -# Run all checks -hielements run architecture.hie - -# Dry run (show what would be checked) -hielements run --dry-run architecture.hie - -# Output formats -hielements check --format json architecture.hie -hielements check --format sarif architecture.hie -``` - -### Exit Codes - -| Code | Meaning | -|------|---------| -| 0 | Success (all checks passed) | -| 1 | Check failures (architectural violations) | -| 2 | Errors (syntax errors, missing files, etc.) | - ---- - -## Appendix C: Best Practices - -### Naming Conventions - -- Use `snake_case` for identifiers -- Use descriptive names that reflect the logical component -- Prefix private/internal elements with `_` - -### Organization - -- Keep one logical system per file -- Use nested elements for hierarchical structure -- Group related checks together - -### Documentation - -- Add doc comments (`##`) to all top-level elements -- Document connection points that are used by other elements -- Keep comments up-to-date with code changes - -### Performance - -- Use specific selectors over broad glob patterns -- Leverage caching in CI/CD pipelines -- Split large specifications into multiple files - ---- - -## Appendix D: Migration Guide (V1/V2 → V3) - -This section helps migrate existing Hielements code to V3 syntax. - -### D.1 Overview of Changes (V1 → V2/V3) - -| Feature | V1 Syntax | V3 Syntax | -|---------|-----------|-----------| -| Language annotation | `scope name : lang = expr` | `scope name = expr` | -| Template scopes | `scope name = expr` | `scope name` (unbounded) | -| Binding scopes | `template.element.scope = expr` | `scope name binds template.element.scope = expr` | -| Connection points | `ref name: Type = expr` | `ref name: Type binds path = expr` (for bindings) | - -### D.2 Language Annotation Changes - -**V1 (Deprecated):** -```hielements -element my_service: - scope src : python = python.module_selector('my_service') - scope backend : rust = rust.module_selector('backend') -``` - -**V2 (Current):** -```hielements -element my_service: - scope src = python.module_selector('my_service') - scope backend = rust.module_selector('backend') -``` - -**Migration**: Replace `: language` with `` after the scope name. - -### D.3 Pattern Unbounded Scopes - -**V1 (Deprecated):** -```hielements -pattern compiler: - element lexer: - scope module = rust.module_selector('lexer') # Bound in pattern - ref tokens: TokenStream = rust.function_selector(module, 'tokenize') -``` - -**V2 (Current):** -```hielements -pattern compiler: - element lexer: - scope module # Unbounded - no '=' expression - ref tokens: TokenStream -``` - -**Migration**: Remove the `= expression` part from pattern scopes. They become placeholders. - -### D.4 Element Bindings with `binds` - -**V1 (Deprecated):** -```hielements -element my_compiler implements compiler: - compiler.lexer.scope = rust.module_selector('mycompiler::lexer') - compiler.lexer.tokens = rust.function_selector(compiler.lexer.scope, 'tokenize') -``` - -**V2 (Current):** -```hielements -element my_compiler implements compiler: - scope lexer_mod binds compiler.lexer.module = rust.module_selector('mycompiler::lexer') - ref tokens: TokenStream binds compiler.lexer.tokens = rust.function_selector(lexer_mod, 'tokenize') -``` - -**Migration**: -1. Change `pattern.element.scope = expr` to `scope name binds pattern.element.scope = expr` -2. Change `pattern.element.ref = expr` to `ref name: Type binds pattern.element.ref = expr` - -### D.5 Descriptive-Only Mode - -V2 supports using the language without patterns or bindings. If you're not using prescriptive features, you can write V2 code that looks similar to V1: - -```hielements -# V2 descriptive-only (no patterns, no binds) -element my_service: - scope src = rust.module_selector('my_service') - ref api: HttpHandler = rust.public_functions(src) - check rust.function_exists(src, 'main') -``` - -### D.6 Complete Migration Example - -**V1 Code:** -```hielements -import python -import docker - -pattern microservice: - element api: - scope module = python.module_selector('api') - ref endpoint = python.public_functions(module) - - element container: - scope dockerfile = docker.file_selector('Dockerfile') - - check docker.exposes_port(dockerfile, 8080) - -element orders implements microservice: - microservice.api.scope = python.module_selector('orders.api') - microservice.api.endpoint = python.public_functions(microservice.api.scope) - microservice.container.dockerfile = docker.file_selector('orders.dockerfile') -``` - -**V2 Code:** -```hielements -import files -import rust - -pattern microservice: - element api: - scope module # Unbounded - ref endpoint: HttpHandler - - element container: - scope dockerfile - - check files.exists(container.dockerfile, 'Dockerfile') - -element orders implements microservice: - scope api_mod binds microservice.api.module = rust.module_selector('orders::api') - ref endpoint: HttpHandler binds microservice.api.endpoint = rust.public_functions(api_mod) - scope dockerfile binds microservice.container.dockerfile = files.file_selector('orders.dockerfile') -``` - -### D.7 Migration Checklist - -- [ ] Update all language annotations from `: lang` to `` -- [ ] Remove `= expression` from pattern scopes (make them unbounded) -- [ ] Remove `= expression` from pattern connection points (make them unbounded) -- [ ] Add `binds pattern.path` clause to element scopes that bind to patterns -- [ ] Add `binds pattern.path` clause to element connection points that bind to patterns -- [ ] Update any references to use the new local scope names -- [ ] Test that all checks pass with the new syntax - -### D.8 Backward Compatibility Note - -**Hielements V2 syntax is NOT backward compatible with V1.** The V1 syntax is deprecated and no longer supported. All existing V1 code must be migrated. - -The key philosophy changes (V1 → V3): -- **Patterns are prescriptive** - they define structure without implementation -- **Elements are descriptive** - they bind to actual code -- **`binds` makes connections explicit** - clearer separation of concerns -- **Angular brackets for language** - more consistent with type syntax conventions - ---- - -### D.9 V2 → V3 Migration - -V3 introduces new syntactic features while remaining backward compatible with V2. V2 code continues to work in V3. - -#### New Features in V3 - -| Feature | Description | -|---------|-------------| -| Curly bracket syntax `{}` | Alternative to indentation-based blocks | -| `ref` keyword | Preferred over `connection_point` for declaring reference points | -| `uses` declarations | Explicit dependency declarations between elements/scopes | -| `pattern` keyword | Preferred over `template` for declaring patterns | - -#### Updating `connection_point` to `ref` - -**V2:** -```hielements -element api_server: - scope module = python.module_selector('api') - connection_point rest_api: HttpHandler = python.public_functions(module) -``` - -**V3 (preferred):** -```hielements -element api_server { - scope module = python.module_selector('api') - ref rest_api: HttpHandler = python.public_functions(module) -} -``` - -**Migration**: Replace `connection_point` with `ref`. Both keywords are still supported. - -#### Updating `template` to `pattern` - -**V2:** -```hielements -template microservice: - element api: - scope module -``` - -**V3 (preferred):** -```hielements -pattern microservice { - element api { - scope module - } -} -``` - -**Migration**: Replace `template` with `pattern`. Both keywords are still supported. - -#### Adding `uses` Declarations - -V3 allows explicit dependency declarations to make architectural relationships visible: - -```hielements -element core { - element lexer { - scope module = rust.module_selector('lexer') - } - - element parser { - scope module = rust.module_selector('parser') - scope lexer_module = rust.module_selector('lexer') - - ## Explicit dependency: parser uses lexer - lexer_module uses lexer - } -} -``` - -#### V2 → V3 Migration Checklist - -- [ ] (Optional) Replace `connection_point` with `ref` -- [ ] (Optional) Replace `template` with `pattern` -- [ ] (Optional) Convert indentation-based blocks to curly brackets `{}` -- [ ] (Optional) Add `uses` declarations to document explicit dependencies -- [ ] Test that all checks pass after migration +# Hielements Language Reference + +This document provides a complete reference for the Hielements language syntax and semantics. Hielements is a declarative language for describing and enforcing software architecture. + +It is possible to use only the descriptive part without the prescriptive one; in this case, no enforcement/checks are performed. + +--- + +## Table of Contents + +1. [Lexical Structure](#1-lexical-structure) +2. [Program Structure](#2-program-structure) +3. [Elements](#3-elements) +4. [Scopes](#4-scopes) +5. [Refs](#5-refs) +6. [Uses Declarations](#6-uses-declarations) +7. [Rules (Checks)](#7-rules-checks) +8. [Children Elements](#8-children-elements) +9. [Patterns](#9-patterns) +10. [Language Declarations](#10-language-declarations) +11. [Imports and Modules](#11-imports-and-modules) +12. [Expressions](#12-expressions) +13. [Built-in Libraries](#13-built-in-libraries) +14. [Comments](#14-comments) +15. [Complete Grammar](#15-complete-grammar) +16. [Examples](#16-examples) +17. [Appendix A: Error Messages](#appendix-a-error-messages) +18. [Appendix B: CLI Reference](#appendix-b-cli-reference) +19. [Appendix C: Best Practices](#appendix-c-best-practices) + +--- + +## 1. Lexical Structure + +### 1.1 Character Set + +Hielements source files are UTF-8 encoded text files. The recommended file extension is `.hie`. + +### 1.2 Identifiers + +Identifiers name elements, scopes, connection points, and other entities. + +``` +identifier ::= letter (letter | digit | '_')* +letter ::= 'a'..'z' | 'A'..'Z' | '_' +digit ::= '0'..'9' +``` + +**Valid identifiers:** +``` +orders_module +MyService +_internal +api2 +``` + +**Invalid identifiers:** +``` +2fast # Cannot start with digit +my-service # Hyphens not allowed (use underscores) +``` + +### 1.3 Keywords + +The following are reserved keywords: + +| Keyword | Description | +|---------|-------------| +| `element` | Declares an element | +| `pattern` | Declares a pattern (reusable architectural blueprint) | +| `implements` | Declares that an element implements pattern(s) | +| `binds` | Binds a scope/ref to a pattern declaration | +| `scope` | Declares a scope selector | +| `ref` | Declares a reference point | +| `uses` | Declares a dependency on another element/scope | +| `check` | Declares a rule/check | +| `import` | Imports a library or module | +| `as` | Alias for imports | +| `from` | Selective import | +| `true` | Boolean literal | +| `false` | Boolean literal | +| `requires` | Declares a required component (patterns only) | +| `allows` | Declares an allowed component (patterns only) | +| `forbids` | Declares a forbidden component (patterns only) | +| `descendant` | Modifier for hierarchical requirements (applies to descendants) | +| `connection` | Specifies a connection requirement | +| `to` | Specifies connection target | +| `language` | Declares a language with optional connection checks | +| `connection_check` | Defines a connection verification check for a language | + +### 1.4 Literals + +#### String Literals + +Strings are enclosed in single or double quotes: + +```hielements +'single quoted string' +"double quoted string" +``` + +Escape sequences: +| Sequence | Meaning | +|----------|---------| +| `\\` | Backslash | +| `\'` | Single quote | +| `\"` | Double quote | +| `\n` | Newline | +| `\t` | Tab | + +#### Numeric Literals + +```hielements +42 # Integer +3.14 # Float +8080 # Port number (integer) +``` + +#### Boolean Literals + +```hielements +true +false +``` + +### 1.5 Operators and Punctuation + +| Symbol | Usage | +|--------|-------| +| `=` | Assignment | +| `.` | Member access | +| `,` | Argument separator | +| `:` | Block start, type annotation | +| `{` `}` | Block delimiters | +| `(` `)` | Function call, grouping | +| `[` `]` | List literals | +| `<` `>` | Language specification in scopes | + +--- + +## 2. Program Structure + +A Hielements program (specification) consists of: +1. Optional import statements +2. One or more top-level element declarations + +```hielements +# Imports (optional) +import python +import docker + +# Top-level elements +element my_service: + # ... element body +``` + +### 2.1 File Organization + +A typical Hielements project structure: + +``` +project/ +├── architecture.hie # Main specification +├── modules/ +│ ├── backend.hie # Backend module specs +│ └── frontend.hie # Frontend module specs +└── hielements.config # Configuration (optional) +``` + +--- + +## 3. Elements + +Elements are the fundamental building blocks of Hielements. An element represents a logical component of your software system. + +### 3.1 Syntax + +Hielements supports two styles for block delimiters: curly brackets `{}` or colon and indentation: + +``` +# Curly bracket syntax +element_declaration ::= 'element' identifier '{' element_body '}' + +# Indentation syntax +element_declaration ::= 'element' identifier ':' element_body + +element_body ::= (scope_declaration + | ref_declaration + | uses_declaration + | check_declaration + | nested_element)* +``` + +### 3.2 Basic Element + +Curly bracket syntax: +```hielements +element orders_service { + scope src = files.folder_selector('src/orders') + check files.contains(src, 'main.py') +} +``` + +Indentation syntax: +```hielements +element orders_service: + scope src = files.folder_selector('src/orders') + check files.contains(src, 'main.py') +``` + +### 3.3 Element with All Components + +```hielements +element payment_gateway: + # Scopes define what code belongs to this element + scope python_module = python.module_selector('payments') + scope config = files.file_selector('config/payments.yaml') + scope dockerfile = docker.file_selector('payments.dockerfile') + + # Connection points expose interfaces to other elements + ref api = python.public_functions(python_module) + ref port = docker.exposed_port(dockerfile) + + # Checks enforce rules + check docker.exposes_port(dockerfile, 8080) + check python.no_circular_imports(python_module) + + # Nested elements for hierarchical structure + element validation_submodule: + scope src = python.module_selector('payments.validation') +``` + +### 3.4 Element Semantics + +- Each element defines a **boundary** around a logical component +- Elements can be **nested** to create hierarchies +- Element names must be **unique** within their scope +- Elements are evaluated **lazily** - scopes are resolved when checks execute + +--- + +## 4. Scopes + +Scopes define what code, files, or artifacts belong to an element. Scopes are specified using **selectors** from libraries. + +### 4.1 Syntax + +Scopes can be either **bound** (in elements) or **unbounded** (in patterns). Language is specified using angular brackets: + +``` +# Bound scope (in elements) +scope_declaration ::= 'scope' identifier ['<' language_name '>'] ['binds' binding_path] '=' selector_expression + +# Unbounded scope (in templates) +scope_declaration ::= 'scope' identifier ['<' language_name '>'] +``` + +### 4.2 Scope Selectors + +Selectors are library functions that identify parts of your codebase: + +```hielements +# File and folder selectors +scope src_folder = files.folder_selector('src/') +scope config_file = files.file_selector('config.yaml') +scope all_python = files.glob_selector('**/*.py') + +# Language-specific selectors with angular bracket syntax +scope orders = python.module_selector('orders') +scope backend = rust.module_selector('backend') + +# Docker selectors +scope dockerfile = docker.file_selector('Dockerfile') +scope compose = docker.compose_selector('docker-compose.yml') +``` + +### 4.3 Scopes with Language Annotations + +Scopes can optionally include a language annotation using **angular brackets** to explicitly declare which programming language the scope belongs to: + +```hielements +element my_service: + # Scope with explicit language annotation + scope src = python.module_selector('my_service') + scope backend = rust.module_selector('backend') + + # Scope without language annotation (inferred from library) + scope config = files.file_selector('config.yaml') +``` + +The language annotation is specified in angular brackets immediately after the scope name (``). + +### 4.4 Unbounded Scopes in Patterns + +In patterns (declared with `pattern`), scopes are **unbounded** (declared without a selector expression). They serve as placeholders to be bound by implementing elements: + +```hielements +pattern observable: + element metrics: + # Unbounded scope - no '=' expression + scope module + ref prometheus: MetricsHandler +``` + +### 4.5 Binding Scopes with `binds` + +When an element implements a pattern, it uses the `binds` keyword to connect its scopes to the pattern's unbounded scopes: + +```hielements +element observable_component implements observable: + # Bind this scope to the pattern's unbounded scope + scope main_module binds observable.metrics.module = rust.module_selector('payments::api') + + # Bind a connection point to the pattern's connection point + ref main_handler: MetricsHandler binds observable.metrics.prometheus = rust.function_selector(main_module, 'handler') +``` + +The `binds` clause specifies which pattern scope/ref this declaration satisfies. + +### 4.6 Multiple Scopes + +An element can have multiple scopes, representing different aspects: + +```hielements +element full_stack_feature: + scope frontend = typescript.module_selector('components/OrderForm') + scope backend = python.module_selector('api/orders') + scope database = sql.migration_selector('migrations/001_orders.sql') + scope container = docker.file_selector('orders.dockerfile') +``` + +### 4.7 Scope Composition + +Scopes can be combined using set operations (library-dependent): + +```hielements +element api_layer: + scope handlers = python.package_selector('api.handlers') + scope models = python.package_selector('api.models') + + # Combine scopes + scope all_api = scopes.union(handlers, models) +``` + +### 4.8 Scope Semantics + +- Scopes are **lazy** - they don't scan the filesystem until needed +- Scopes can **overlap** between elements (a file can belong to multiple elements) +- Scope resolution may **fail** if the target doesn't exist (configurable: error vs warning) +- Scopes provide **source locations** for error reporting +- Scopes with **language annotations** enable language-specific connection verification + +--- + +## 5. Refs + +Refs (reference points) expose interfaces, APIs, or dependencies that other elements can reference. They make inter-element relationships explicit and verifiable. + +### 5.1 Syntax + +``` +ref_declaration ::= 'ref' identifier ':' type_name '=' expression +``` + +Type annotations are **mandatory** for all refs. + +### 5.2 Basic Refs + +```hielements +element api_server { + scope module = python.module_selector('api') + + # All refs must have type annotations + ref rest_api: HttpHandler = python.public_functions(module) + + # Expose the main entry point + ref main: Function = python.function_selector(module, 'main') +} +``` + +### 5.3 Ref Type Annotations + +Refs **must** have explicit type annotations to ensure type safety across libraries and languages: + +```hielements +element api_server { + scope module = python.module_selector('api') + scope dockerfile = docker.file_selector('Dockerfile') + + # Basic type annotations (mandatory) + ref port: integer = docker.exposed_port(dockerfile) + ref api_url: string = python.get_api_url(module) + ref ssl_enabled: boolean = config.get_flag('ssl') + ref timeout: float = config.get_timeout() + + # Custom type annotations + ref rest_api: HttpHandler = python.public_functions(module) + ref db_conn: DatabaseConnection = python.class_selector(module, 'Database') +} + +#### Basic Types + +| Type | Description | Example Values | +|------|-------------|----------------| +| `string` | Text data | `"api/v1"`, `"localhost"` | +| `integer` | Whole numbers | `8080`, `443`, `-1` | +| `float` | Decimal numbers | `3.14`, `0.5`, `-2.718` | +| `boolean` | True/false | `true`, `false` | + +#### Custom Types + +Custom types are user-defined type names that can represent: +- Type aliases for basic types (e.g., `Port`, `Url`) +- Complex structures from code (e.g., `TokenStream`, `HttpHandler`) +- Library-defined types specific to a domain + +```hielements +# Custom type example +pattern compiler: + element lexer: + ref tokens: TokenStream = rust.struct_selector('Token') + + element parser: + ref ast: AbstractSyntaxTree = rust.struct_selector('Program') +``` + +### 5.4 Using Connection Points + +Connection points are used in checks to verify relationships: + +```hielements +element orders_service: + element api: + scope module = python.module_selector('orders.api') + ref handlers: HttpHandler = python.public_functions(module) + + element docker: + scope dockerfile = docker.file_selector('orders.dockerfile') + + # Verify Docker uses the correct entry point + check docker.entry_point(dockerfile, api.handlers) +``` + +### 5.5 Connection Point Types + +Different libraries expose different types of connection points: + +| Library | Connection Point | Description | +|---------|-----------------|-------------| +| `python` | `public_functions` | All public functions in a module | +| `python` | `exported_classes` | All exported classes | +| `python` | `main_module` | The `__main__` entry point | +| `docker` | `exposed_ports` | Ports exposed by the container | +| `docker` | `volumes` | Mounted volumes | +| `files` | `path` | Filesystem path | + +### 5.6 Connection Point Semantics + +- Connection points are **computed** from scopes +- They can be **referenced** across element boundaries using dot notation +- Connection points enable **dependency checking** between elements +- They provide **documentation** of element interfaces +- **Type annotations** are mandatory and provide type safety +- **Type checking** occurs at specification validation time (when implemented) + +--- + +## 6. Uses Declarations + +Uses declarations explicitly declare dependencies between elements or scopes. This makes architectural dependencies visible and verifiable. + +### 6.1 Syntax + +``` +uses_declaration ::= identifier 'uses' qualified_identifier +``` + +Where `qualified_identifier` is a path like `lexer` or `core.lexer`. + +### 6.2 Basic Uses Declarations + +```hielements +element core { + element lexer { + scope module = rust.module_selector('lexer') + } + + element parser { + scope module = rust.module_selector('parser') + scope lexer_module = rust.module_selector('lexer') + + # Declare that lexer_module depends on lexer element + lexer_module uses lexer + } +} +``` + +### 6.3 Qualified Target Paths + +Uses declarations can reference elements using qualified paths: + +```hielements +element parser { + scope module = rust.module_selector('parser') + + # Reference an element in parent scope using qualified name + module uses core.lexer +} +``` + +### 6.4 Uses Semantics + +- Uses declarations are **explicit** - they document architectural dependencies +- The source identifier must be a **scope** defined in the same element +- The target can be an **element** or **scope** reference +- Name resolution first looks at the **current element**, then **parent scope** +- Uses declarations enable **dependency validation** and **architecture enforcement** + +--- + +## 7. Rules (Checks) + +Checks enforce architectural rules. They are the mechanism by which Hielements validates your codebase against specifications. + +### 7.1 Syntax + +``` +check_declaration ::= 'check' function_call +``` + +### 7.2 Basic Checks + +```hielements +element my_service: + scope dockerfile = docker.file_selector('Dockerfile') + scope src = python.module_selector('my_service') + + # Check that port 8080 is exposed + check docker.exposes_port(dockerfile, 8080) + + # Check that a specific function exists + check python.function_exists(src, 'handle_request') + + # Check for no circular dependencies + check python.no_circular_imports(src) +``` + +### 7.3 Check Categories + +#### Existence Checks +Verify that something exists: + +```hielements +check files.exists(src, 'README.md') +check python.function_exists(module, 'main') +check docker.stage_exists(dockerfile, 'builder') +``` + +#### Property Checks +Verify properties of artifacts: + +```hielements +check docker.base_image(dockerfile, 'python:3.11-slim') +check python.has_docstring(function) +check files.max_size(file, 1048576) # 1MB max +``` + +#### Relationship Checks +Verify relationships between components: + +```hielements +check docker.entry_point(dockerfile, python_module.main) +check python.imports(module_a, module_b) +check python.no_dependency(module_a, module_b) +``` + +#### Negative Checks +Verify that something does NOT exist or is NOT true: + +```hielements +check files.no_files_matching(src, '*.tmp') +check python.no_circular_imports(module) +check docker.no_root_user(dockerfile) +``` + +### 7.4 Check Results + +Checks produce one of three results: + +| Result | Meaning | +|--------|---------| +| **Pass** | The check succeeded | +| **Fail** | The check failed (architectural violation) | +| **Error** | The check could not be evaluated (e.g., file not found) | + +### 7.5 Check Semantics + +- Checks are evaluated **after** all scopes are resolved +- Checks are **independent** - one failing check doesn't prevent others from running +- Check results include **source locations** for diagnostics +- Checks can be **parallelized** when they have no dependencies + +--- + +## 8. Children Elements + +Elements can contain nested (children) elements to create hierarchical structures. + +### 8.1 Syntax + +Nested elements are declared inside a parent element: + +```hielements +element parent: + element child_a: + # child_a body + + element child_b: + # child_b body +``` + +### 8.2 Hierarchical Example + +```hielements +element ecommerce_platform: + + element orders_service: + scope module = python.module_selector('services.orders') + ref api = python.public_functions(module) + + element orders_db: + scope migrations = sql.migration_selector('db/orders') + + element payments_service: + scope module = python.module_selector('services.payments') + ref api = python.public_functions(module) + + # Cross-service check: orders can call payments + check python.can_import(orders_service.module, payments_service.module) +``` + +### 8.3 Referencing Children + +Children elements are referenced using dot notation: + +```hielements +element system: + element service_a: + ref api = python.public_functions(module) + + element service_b: + scope module = python.module_selector('service_b') + + # Reference sibling's connection point + check python.imports(module, service_a.api) +``` + +### 8.4 Scope Inheritance + +Children elements inherit the context of their parent but have their own scope: + +```hielements +element microservices: + # Shared configuration for all children + scope shared_config = files.file_selector('shared/config.yaml') + + element service_a: + scope module = python.module_selector('service_a') + # Can reference parent's scope + check files.references(module, shared_config) +``` + +--- + +## 9. Patterns + +Patterns (declared with the `pattern` keyword) allow creating reusable architectural blueprints that define conformance requirements. Patterns establish structural constraints that elements can implement with concrete scopes and checks. + +> **Note**: See the [Pattern Catalog](patterns_catalog.md) for an extensive collection of common software engineering patterns. + +Patterns define **unbounded** scopes that serve as placeholders, while implementing elements use the **`binds`** keyword to provide concrete bindings. + +### 9.1 Pattern Declaration + +Patterns are declared using the `pattern` keyword and define a structure with **unbounded scopes**: + +```hielements +pattern observable { + element metrics implements measurable { + allows language rust + + # Unbounded scope - angular brackets specify language + scope module + ref prometheus: MetricsHandler + + check files.exists(module, 'Cargo.toml') + } +} +``` + +**Key features:** +- Scopes in patterns are **unbounded** (no `=` expression) +- Language is specified via **angular brackets** (``) +- Patterns can include `allows`/`requires`/`forbids` constraints + +### 9.2 Implementing Patterns with `binds` + +Elements implement patterns using the `implements` keyword, then use **`binds`** to connect their scopes: + +```hielements +element observable_component implements observable: + # Bind scope to pattern's unbounded scope + scope main_module binds observable.metrics.module = rust.module_selector('payments::api') + + # Bind connection point + ref main_handler: MetricsHandler binds observable.metrics.prometheus = rust.function_selector(main_module, 'handler') +``` + +The `binds` keyword creates an explicit connection between the element's scope and the pattern's placeholder. + +### 9.3 Descriptive-Only Mode + +The `implements` and `binds` keywords are **optional**. When omitted, Hielements operates in "descriptive-only" mode without prescriptive enforcement: + +```hielements +# Descriptive only - no pattern implementation +element simple_component: + scope src = rust.module_selector('mymodule') + check rust.function_exists(src, 'main') +``` + +### 9.4 Absolute References + +Pattern properties are referenced using absolute paths prefixed with the pattern name (e.g., `observable.metrics`). This prevents name clashes when implementing multiple patterns: + +```hielements +pattern microservice: + element api: + scope module + ref rest_endpoint: HttpHandler + +pattern observable: + element api: + scope module + ref metrics_endpoint: MetricsHandler + +# No name clash - each 'api' is explicitly qualified +element my_service implements microservice, observable: + scope api_mod binds microservice.api.module = rust.module_selector('service::api') + scope metrics_mod binds observable.api.module = rust.module_selector('service::metrics') + + # Reference both in checks + check microservice.api.rest_endpoint.port != observable.api.metrics_endpoint.port +``` + +### 9.5 Multiple Pattern Implementation + +An element can implement multiple patterns: + +```hielements +pattern resilient: + element circuit_breaker: + scope module + +pattern secured: + element authentication: + scope module + +element production_service implements microservice, resilient, secured: + # Microservice bindings + scope api binds microservice.api.module = rust.module_selector('api') + + # Resilient bindings + scope resilience binds resilient.circuit_breaker.module = rust.module_selector('resilience') + + # Secured bindings + scope auth binds secured.authentication.module = rust.module_selector('auth') +``` + +### 9.6 Pattern Requirements + +When implementing a pattern, all unbounded scopes must be bound: + +```hielements +pattern web_service: + element frontend: + scope src + ref static_files: StaticAssets + + element backend: + scope src + ref api: HttpHandler + +# Valid - all required bindings provided +element complete_service implements web_service: + scope frontend_src binds web_service.frontend.src = typescript.module_selector('frontend') + scope backend_src binds web_service.backend.src = python.module_selector('backend') + ref static: StaticAssets binds web_service.frontend.static_files = files.glob_selector('frontend/dist/*') + ref api: HttpHandler binds web_service.backend.api = python.public_functions(backend_src) + +# Invalid - missing bindings (would produce validation error) +element incomplete_service implements web_service: + scope frontend_src binds web_service.frontend.src = typescript.module_selector('frontend') + # ERROR: web_service.backend bindings missing +``` + +### 9.7 Pattern Checks + +Checks defined in patterns are automatically included when the pattern is implemented. The checks use absolute references and are evaluated with the concrete bindings: + +```hielements +pattern microservice: + element api: + scope module + element database: + scope db + element container: + scope dockerfile + + # Pattern checks + check microservice.container.exposes_port(8080) + check microservice.api.connects_to(microservice.database) + +element orders_service implements microservice: + scope api_mod binds microservice.api.module = python.module_selector('orders.api') + scope db binds microservice.database.db = postgres.database_selector('orders_db') + scope dockerfile binds microservice.container.dockerfile = docker.file_selector('orders.dockerfile') + + # The pattern checks are automatically evaluated with bound scopes +``` + +### 9.8 Library-Defined Patterns + +Patterns can be defined in external libraries and imported for use: + +```hielements +import architecture_patterns + +element my_service implements architecture_patterns.hexagonal: + # Bind the hexagonal architecture pattern elements + scope domain binds hexagonal.domain.src = python.package_selector('myapp.domain') + scope app binds hexagonal.application.src = python.package_selector('myapp.application') + scope adapters binds hexagonal.adapters.src = python.package_selector('myapp.adapters') +``` + +External libraries can provide patterns via the library protocol. See the [External Library Plugin Guide](external_libraries.md) for details. + +### 9.9 Pattern Semantics + +- Patterns define **structure** with **unbounded scopes** +- Elements implementing patterns use **`binds`** to connect scopes +- **Angular brackets** specify language (``, ``) +- `implements` and `binds` are **optional** (for prescriptive features) +- Pattern checks are **inherited** by implementing elements +- Absolute references **prevent name clashes** between multiple patterns +- Patterns **cannot be nested** (a pattern cannot implement another pattern) +- Pattern names must be **unique** within their scope + +### 9.10 Pattern-Level Connection Points + +Patterns can declare connection points at the pattern level (not just within child elements). These connection points can be used in pattern checks and must be bound when implementing the pattern. + +**Example:** + +```hielements +pattern microservice: + element api: + scope module + + element container: + scope dockerfile + + ## Pattern-level unbounded connection point + ref port: integer + + ## Pattern checks can reference the pattern-level connection point + check files.exists(container.dockerfile, 'Dockerfile') + check rust.function_exists(api.module, 'start_server') + +## When implementing, bind the pattern-level connection point +element orders_service implements microservice: + scope api_mod binds microservice.api.module = rust.module_selector('orders::api') + scope dockerfile binds microservice.container.dockerfile = files.file_selector('orders.dockerfile') + + ## Bind the pattern-level port + ref service_port: integer binds microservice.port = rust.const_selector('ORDERS_PORT') + +element payments_service implements microservice: + scope api_mod binds microservice.api.module = rust.module_selector('payments::api') + scope dockerfile binds microservice.container.dockerfile = files.file_selector('payments.dockerfile') + + ## Different service, different port + ref service_port: integer binds microservice.port = rust.const_selector('PAYMENTS_PORT') +``` + +**Benefits:** +- **Parameterization**: Patterns can be parameterized without hardcoding values +- **Reusability**: Same pattern structure with different concrete values +- **Type Safety**: Connection points have type annotations ensuring correctness +- **Clarity**: Makes pattern dependencies and parameters explicit + +### 9.11 Hierarchical Checks + +Hierarchical checks allow parent elements to prescribe requirements that must be satisfied by at least one of their descendants (children, grandchildren, etc.). This is useful for expressing architectural constraints that span multiple levels of the hierarchy. + +#### Hierarchical Requirements + +The unified syntax uses `requires`, `allows`, and `forbids` keywords with an optional `descendant` modifier: + +```hielements +pattern dockerized: + ## At least one descendant must have a docker scope + requires descendant scope dockerfile + + ## At least one descendant must satisfy this check + requires descendant check docker.has_healthcheck(dockerfile) + +pattern observable: + ## At least one descendant must have a metrics element with implements + requires descendant element metrics_service implements metrics_provider + +pattern production_ready: + ## At least one descendant must implement the dockerized template + requires descendant implements dockerized + + ## At least one descendant must implement the observable template + requires descendant implements observable +``` + +#### Satisfying Hierarchical Requirements + +When an element implements a pattern with hierarchical requirements, at least one of its descendants must satisfy each requirement: + +```hielements +element my_app implements dockerized: + ## Frontend - not dockerized + element frontend: + scope src = files.folder_selector('frontend') + + ## Backend - satisfies the hierarchical requirement! + element backend: + scope src = files.folder_selector('backend') + scope dockerfile binds dockerized.dockerfile = docker.file_selector('Dockerfile') ## Matches! + check docker.has_healthcheck(dockerfile) ## Matches! + +## Pattern implementation requirements +element ecommerce_platform implements production_ready: + ## Frontend - neither dockerized nor observable + element frontend: + scope src = files.folder_selector('frontend') + + ## Orders - implements dockerized (satisfies first requirement) + element orders implements dockerized: + scope dockerfile binds dockerized.dockerfile = files.file_selector('orders/Dockerfile') + + ## Monitoring - implements observable (satisfies second requirement) + element monitoring implements observable: + scope metrics_mod binds observable.metrics.module = rust.module_selector('monitoring::metrics') + ref prometheus: MetricsHandler binds observable.metrics.prometheus = rust.function_selector(metrics_mod, 'handler') +``` + +#### Hierarchical Requirement Kinds + +| Kind | Syntax | Description | +|------|--------|-------------| +| Scope | `requires descendant scope name[]` | A descendant must have a matching scope | +| Check | `requires descendant check expr` | A descendant must satisfy this check | +| Element | `requires descendant element name [implements pattern]` | A descendant must have an element with this structure | +| Pattern Implementation | `requires descendant implements pattern_name` | A descendant must implement the specified pattern | +| Connection Point | `requires descendant ref name: Type` | A descendant must have a connection point | + +#### Immediate vs Descendant Requirements + +The `descendant` modifier determines whether the requirement applies to any descendant in the hierarchy or only to immediate children: + +```hielements +pattern microservice: + ## Immediate child requirement (no descendant modifier) + requires element api implements api_handler + + ## Any descendant requirement (with descendant modifier) + requires descendant element metrics implements observable +``` + +### 9.12 Connection Boundaries + +Connection boundaries allow specifying constraints on architectural dependencies (imports/dependencies) between elements. These boundaries are inherited by all descendants. **Note**: "Connections" refer to logical/architectural dependencies like module imports, not network connections. + +**Important**: Connection boundaries (`allows connection`, `forbids connection`, `requires connection`) are **only allowed in patterns**, not in regular elements. Elements can inherit these constraints by implementing patterns that define them. + +#### Allowing Connections + +Use `allows connection to` in patterns to whitelist specific connection targets: + +```hielements +pattern frontend_zone: + ## Code in this zone may only import from api_gateway + allows connection to api_gateway.public_api + +element my_frontend implements frontend_zone: + element web_app: + scope src = files.folder_selector('frontend/web') + ## Any imports from this scope are checked against the boundary + + element mobile_api: + scope src = files.folder_selector('frontend/mobile') +``` + +#### Forbidding Connections + +Use `forbids connection to` in patterns to blacklist specific connection targets: + +```hielements +pattern secure_zone: + ## Code in this zone cannot import from external modules + forbids connection to external.* + forbids connection to public_network.* + +element internal_service implements secure_zone: + scope src = files.folder_selector('internal') + ## This element and all its children inherit the constraint +``` + +#### Requiring Connections + +Use `requires connection to` in patterns to mandate that code MUST have a dependency: + +```hielements +pattern service_mesh_zone: + ## All services in this mesh must import from logging module + requires connection to logging.* + +element service_mesh implements service_mesh_zone: + element user_service: + scope src = files.folder_selector('services/user') + # This service must import from logging.* to satisfy the constraint +``` + +#### Wildcard Patterns + +Connection patterns support wildcards with `.*` to match any sub-path: + +```hielements +## In patterns: +forbids connection to database.* ## Matches database.connection, database.pool, etc. +forbids connection to external.* ## Matches anything under external +allows connection to api.public.* ## Matches api.public.users, api.public.orders, etc. +``` + +**Note**: Wildcard interpretation is library-specific. For example, a Python library might expand `logging.*` to all modules in the logging package. + +#### Combined Boundaries + +Multiple boundaries can be combined - allows create a whitelist, forbids create a blacklist, requires create mandatory dependencies: + +```hielements +pattern secure_service: + allows connection to api.endpoint + allows connection to logging.output + forbids connection to database.* + forbids connection to external.network + requires connection to audit.* + +element my_secure_service implements secure_service: + ## Children inherit ALL of these boundaries + element child_service: + scope src = files.folder_selector('child') +``` + +#### Hierarchical Dependency Composition + +When A is allowed to connect to B, and B is allowed to connect to C: +- A→B→C is **allowed** (each hop respects its own boundary) +- This enables construction of complex layered architectures + +#### Boundary Semantics + +- `allows connection` boundaries create a **whitelist** - only listed targets are permitted +- `forbids connection` boundaries create a **blacklist** - listed targets are prohibited +- `requires connection` boundaries create a **mandate** - dependencies MUST exist +- **Connection boundaries are only allowed in patterns** - elements inherit them via `implements` +- Boundaries are **inherited** by all descendants within the parent/child hierarchy +- Multiple boundaries are **combined** (allows AND forbids AND requires apply) +- Wildcards (`.*`) match **any path suffix** (library-specific interpretation) +- Actual verification is **language-specific** - libraries check imports/dependencies + +### 9.13 Language Constraints + +Patterns can constrain which programming languages elements may use through `requires`, `allows`, and `forbids` with the `language` keyword: + +```hielements +pattern python_only: + requires language python + forbids language rust + +pattern multilingual: + allows language python + allows language rust + allows language java +``` + +When an element implements a pattern with language constraints: +- `requires language X` - The element must have at least one scope with language X +- `allows language X` - Only languages X are permitted (whitelist) +- `forbids language X` - Language X is prohibited (blacklist) + +--- + +## 10. Language Declarations + +Language declarations define supported languages and their connection verification checks. + +### 10.1 Simple Language Declaration + +A simple language declaration just registers a language name: + +```hielements +language python +language rust +language java +``` + +### 10.2 Language with Connection Checks + +Languages can define connection checks that verify connections between scopes: + +```hielements +language python: + connection_check can_import(source: scope[], target: scope[]): + python.imports_allowed(source, target) + + connection_check no_circular(scopes: scope[]): + python.no_circular_imports(scopes) + +language rust: + connection_check depends_on(source: scope[], target: scope[]): + rust.dependency_exists(source, target) +``` + +### 10.3 Connection Check Semantics + +Connection checks: +- Accept `scope[]` parameters representing arrays of scopes +- Return `True` (connection valid) or `False` (connection invalid) +- Are automatically applied recursively along the parent-children hierarchy +- Are language-specific - only applied to scopes with matching language annotation + +### 10.4 Connection Verification Process + +When verifying element connections: +1. For each language used by an element, gather all scopes of that language +2. For each child element, gather its scopes of the same language +3. Apply all `connection_check` functions defined for that language +4. Recursively verify all descendants + +**Example:** + +```hielements +language python: + connection_check can_import(source: scope[], target: scope[]): + python.imports_allowed(source, target) + +element system: + element frontend: + scope src : python = python.module_selector('frontend') + + element backend: + scope src : python = python.module_selector('backend') + + element api: + scope src : python = python.module_selector('backend.api') +``` + +The `can_import` check will verify that: +- `frontend` can import from `backend` +- `backend.api` can import from its parent `backend` +- And so on through the hierarchy + +--- + +## 11. Imports and Modules + +Imports bring libraries and other Hielements specifications into scope. + +### 11.1 Library Imports + +```hielements +# Import entire library +import python +import docker +import files + +# Import with alias +import kubernetes as k8s + +# Selective import +from python import module_selector, function_exists +``` + +### 11.2 File Imports + +Import other Hielements files: + +```hielements +# Import another spec file +import './modules/backend.hie' + +# Import with alias +import './shared/common.hie' as common +``` + +### 11.3 Import Resolution + +Import paths are resolved: +1. **Bare imports** (`import python`) - Look up in library registry +2. **Relative paths** (`import './foo.hie'`) - Relative to current file +3. **Absolute paths** (`import '/path/to/foo.hie'`) - Absolute filesystem path + +### 11.4 Built-in Libraries + +The following libraries are built-in: + +| Library | Description | +|---------|-------------| +| `files` | File and folder operations | +| `rust` | Rust code analysis | + +### 11.5 External Libraries (Plugins) + +Hielements supports user-defined libraries through external processes. External libraries are configured in a `hielements.toml` file in your workspace root. + +#### Configuration + +Create a `hielements.toml` file: + +```toml +[libraries] +mylibrary = { executable = "path/to/my-plugin", args = [] } +python_checks = { executable = "python3", args = ["scripts/python_checks.py"] } +``` + +Then use in your .hie files: + +```hielements +import mylibrary + +element mycomponent: + scope src = mylibrary.custom_selector('src') + check mylibrary.custom_check(src) +``` + +#### Protocol + +External libraries communicate via JSON-RPC 2.0 over stdin/stdout. See the [External Library Plugin Guide](external_libraries.md) for details on implementing custom plugins. + +--- + +## 12. Expressions + +Expressions compute values for scopes, connection points, and check arguments. + +### 12.1 Literal Expressions + +```hielements +"string literal" +'another string' +42 +3.14 +true +false +['list', 'of', 'items'] +``` + +### 12.2 Identifier References + +```hielements +my_scope # Reference a scope +parent.child.ref_point # Qualified reference +``` + +### 12.3 Function Calls + +```hielements +python.module_selector('orders') +docker.exposes_port(dockerfile, 8080) +files.glob_selector('**/*.py') +``` + +### 12.4 Member Access + +```hielements +element.ref_point +library.function +parent.child.scope +``` + +### 12.5 List Expressions + +```hielements +check docker.exposes_ports(dockerfile, [80, 443, 8080]) +``` + +--- + +## 13. Built-in Libraries + +### 13.1 `files` Library + +The `files` library provides selectors and checks for files and folders. + +#### Selectors + +| Function | Description | +|----------|-------------| +| `files.file_selector(path)` | Select a specific file | +| `files.folder_selector(path)` | Select a folder | +| `files.glob_selector(pattern)` | Select files matching glob pattern | + +#### Checks + +| Function | Description | +|----------|-------------| +| `files.exists(scope, filename)` | File exists in scope | +| `files.contains(scope, filename)` | Scope contains file | +| `files.no_files_matching(scope, pattern)` | No files match pattern | +| `files.max_size(file, bytes)` | File size limit | +| `files.matches_pattern(file, pattern)` | File matches pattern | + +#### Examples + +```hielements +element source_code: + scope src = files.folder_selector('src/') + scope tests = files.folder_selector('tests/') + scope all_py = files.glob_selector('**/*.py') + + check files.exists(src, '__init__.py') + check files.no_files_matching(src, '*.pyc') + check files.no_files_matching(src, '__pycache__') +``` + +### 13.2 `python` Library + +The `python` library provides analysis for Python code. + +#### Selectors + +| Function | Description | +|----------|-------------| +| `python.module_selector(name)` | Select Python module by import name | +| `python.package_selector(name)` | Select Python package | +| `python.function_selector(module, name)` | Select specific function | +| `python.class_selector(module, name)` | Select specific class | + +#### Connection Point Functions + +| Function | Description | +|----------|-------------| +| `python.public_functions(module)` | All public functions | +| `python.exported_classes(module)` | All exported classes | +| `python.get_main_module(module)` | The `__main__` entry | + +#### Checks + +| Function | Description | +|----------|-------------| +| `python.function_exists(module, name)` | Function exists | +| `python.class_exists(module, name)` | Class exists | +| `python.imports(module_a, module_b)` | A imports B | +| `python.no_circular_imports(module)` | No circular dependencies | +| `python.has_docstring(item)` | Has documentation | +| `python.type_annotated(function)` | Has type annotations | + +#### Examples + +```hielements +element api_module: + scope module = python.module_selector('myapp.api') + scope handlers = python.package_selector('myapp.api.handlers') + + ref public_api = python.public_functions(module) + + check python.function_exists(module, 'create_app') + check python.no_circular_imports(module) + check python.has_docstring(public_api) +``` + +### 13.3 `docker` Library + +The `docker` library provides analysis for Dockerfiles. + +#### Selectors + +| Function | Description | +|----------|-------------| +| `docker.file_selector(path)` | Select a Dockerfile | +| `docker.compose_selector(path)` | Select docker-compose file | +| `docker.stage_selector(file, name)` | Select build stage | + +#### Connection Point Functions + +| Function | Description | +|----------|-------------| +| `docker.exposed_ports(dockerfile)` | All exposed ports | +| `docker.volumes(dockerfile)` | All volumes | +| `docker.entry_point(dockerfile)` | Container entry point | + +#### Checks + +| Function | Description | +|----------|-------------| +| `docker.exposes_port(dockerfile, port)` | Port is exposed | +| `docker.base_image(dockerfile, image)` | Uses specific base image | +| `docker.no_root_user(dockerfile)` | Doesn't run as root | +| `docker.stage_exists(dockerfile, name)` | Build stage exists | +| `docker.entry_point(dockerfile, module)` | Entry point matches | +| `docker.has_healthcheck(dockerfile)` | Has HEALTHCHECK instruction | + +#### Examples + +```hielements +element containerized_service: + scope dockerfile = docker.file_selector('Dockerfile') + scope compose = docker.compose_selector('docker-compose.yml') + + ref ports = docker.exposed_ports(dockerfile) + + check docker.exposes_port(dockerfile, 8080) + check docker.base_image(dockerfile, 'python:3.11-slim') + check docker.no_root_user(dockerfile) + check docker.has_healthcheck(dockerfile) +``` + +--- + +## 14. Comments + +### 14.1 Single-line Comments + +```hielements +# This is a comment +element my_service: # Inline comment + scope src = files.folder_selector('src/') # Another comment +``` + +### 14.2 Multi-line Comments + +```hielements +### +This is a multi-line comment. +It can span multiple lines. +### +element my_service: + scope src = files.folder_selector('src/') +``` + +### 14.3 Documentation Comments + +Documentation comments (doc comments) provide descriptions for elements: + +```hielements +## Orders Service +## Handles all order-related operations including creation, +## modification, and fulfillment. +element orders_service: + scope module = python.module_selector('orders') +``` + +--- + +## 15. Complete Grammar + +The following is the complete EBNF grammar for Hielements: + +```ebnf +(* Program structure *) +program ::= import_statement* language_declaration* (pattern_declaration | element_declaration)+ + +(* Imports *) +import_statement ::= 'import' import_path ('as' identifier)? + | 'from' import_path 'import' identifier_list +import_path ::= string_literal | identifier ('.' identifier)* +identifier_list ::= identifier (',' identifier)* + +(* Language Declarations *) +language_declaration ::= 'language' identifier NEWLINE + | 'language' identifier ':' NEWLINE INDENT connection_check+ DEDENT +connection_check ::= 'connection_check' identifier '(' parameter_list ')' ':' NEWLINE INDENT expression DEDENT +parameter_list ::= parameter (',' parameter)* +parameter ::= identifier ':' 'scope' '[' ']' + +(* Patterns - with unbounded scopes *) +pattern_declaration ::= doc_comment? 'pattern' identifier ':' NEWLINE INDENT pattern_body DEDENT + | doc_comment? 'pattern' identifier '{' pattern_body '}' +pattern_body ::= pattern_item+ +pattern_item ::= scope_declaration_pattern + | ref_declaration_pattern + | check_declaration + | element_declaration + | component_requirement + +(* Scope in patterns - can be unbounded (no '=' expression) *) +scope_declaration_pattern ::= 'scope' identifier language_annotation? NEWLINE + | 'scope' identifier language_annotation? '=' expression NEWLINE + +(* Ref (connection point) in patterns - can be unbounded (no '=' expression) *) +ref_declaration_pattern ::= 'ref' identifier ':' type_name NEWLINE + | 'ref' identifier ':' type_name '=' expression NEWLINE + +(* Elements *) +(* Note: Elements do NOT support component_requirement - requires/allows/forbids are only in patterns *) +element_declaration ::= doc_comment? 'element' identifier pattern_implementation? ':' NEWLINE INDENT element_body DEDENT + | doc_comment? 'element' identifier pattern_implementation? '{' element_body '}' +pattern_implementation ::= 'implements' identifier (',' identifier)* +element_body ::= element_item+ +element_item ::= scope_declaration + | ref_declaration + | check_declaration + | uses_declaration + | element_declaration + +(* Component Requirements - unified syntax - ONLY allowed in patterns *) +component_requirement ::= ('requires' | 'allows' | 'forbids') ['descendant'] component_spec +component_spec ::= scope_declaration_pattern + | check_declaration + | element_spec + | connection_spec + | ref_spec + | language_spec + +element_spec ::= 'element' identifier [':' type_name] ['implements' identifier] [':' NEWLINE INDENT element_body DEDENT] +connection_spec ::= 'connection' ['to'] connection_pattern NEWLINE +ref_spec ::= 'ref' identifier ':' type_name ['=' expression] NEWLINE +connection_pattern ::= identifier ('.' identifier)* ('.' '*')? +language_spec ::= 'language' identifier NEWLINE + +(* Declarations *) +language_annotation ::= '<' identifier '>' +binds_clause ::= 'binds' qualified_identifier + +scope_declaration ::= 'scope' identifier language_annotation? binds_clause? '=' expression NEWLINE +ref_declaration ::= 'ref' identifier ':' type_name binds_clause? '=' expression NEWLINE +check_declaration ::= 'check' function_call NEWLINE +uses_declaration ::= identifier 'uses' qualified_identifier NEWLINE + +(* Qualified identifiers for binds references *) +qualified_identifier ::= identifier ('.' identifier)+ + +(* Type annotations *) +type_name ::= identifier (* Basic types: string, integer, float, boolean; or custom types *) + +(* Expressions *) +expression ::= function_call + | member_access + | identifier + | literal +member_access ::= expression '.' identifier +function_call ::= member_access '(' argument_list? ')' +argument_list ::= expression (',' expression)* + +(* Literals *) +literal ::= string_literal + | number_literal + | boolean_literal + | list_literal +string_literal ::= '"' character* '"' | "'" character* "'" +number_literal ::= digit+ ('.' digit+)? +boolean_literal ::= 'true' | 'false' +list_literal ::= '[' (expression (',' expression)*)? ']' + +(* Comments *) +comment ::= '#' character* NEWLINE +doc_comment ::= '##' character* NEWLINE +multiline_comment ::= '###' character* '###' + +(* Lexical elements *) +identifier ::= letter (letter | digit | '_')* +letter ::= 'a'..'z' | 'A'..'Z' | '_' +digit ::= '0'..'9' +character ::= +NEWLINE ::= '\n' | '\r\n' +INDENT ::= +DEDENT ::= +``` + +--- + +## 16. Examples + +### 16.1 Simple Service + +```hielements +import files +import rust + +## Order Management Service +## Handles order creation, updates, and fulfillment. +element orders_service: + scope rust_module = rust.module_selector('orders') + scope tests = files.folder_selector('tests/orders') + + # Container scope + scope dockerfile = files.file_selector('orders.dockerfile') + + # Connection points with type annotations + ref api: HttpHandler = rust.public_functions(rust_module) + ref main: Function = rust.function_selector(rust_module, 'main') + + # Architectural rules + check rust.function_exists(rust_module, 'create_order') + check rust.function_exists(rust_module, 'get_order') + check rust.no_circular_imports(rust_module) + + check files.exists(dockerfile, 'Dockerfile') +``` + +### 16.2 Pattern with Unbounded Scopes + +```hielements +import files +import rust + +## Observable Pattern +## Defines a component that exposes metrics +pattern observable: + element metrics: + allows language rust + + # Unbounded scope - will be bound by implementing element + scope module + ref prometheus: MetricsHandler + + check files.exists(module, 'Cargo.toml') + +## Metrics Service implementing the observable pattern +element metrics_service implements observable: + # Bind the scope to the pattern's unbounded scope + scope metrics_mod binds observable.metrics.module = rust.module_selector('metrics::api') + + # Bind the connection point + ref handler: MetricsHandler binds observable.metrics.prometheus = rust.function_selector(metrics_mod, 'handler') +``` + +### 16.3 Microservices Architecture + +```hielements +import files +import rust + +## Microservice Pattern +pattern microservice: + element api: + scope module + ref endpoint: HttpHandler + + element container: + scope dockerfile + + check files.exists(container.dockerfile, 'Dockerfile') + +## E-Commerce Platform +## Main platform containing all microservices. +element ecommerce_platform: + + ## Orders Service + element orders_service implements microservice: + scope api_mod binds microservice.api.module = rust.module_selector('services::orders') + scope dockerfile binds microservice.container.dockerfile = files.file_selector('services/orders/Dockerfile') + ref api: HttpHandler binds microservice.api.endpoint = rust.public_functions(api_mod) + + ## Inventory Service + element inventory_service implements microservice: + scope api_mod binds microservice.api.module = rust.module_selector('services::inventory') + scope dockerfile binds microservice.container.dockerfile = files.file_selector('services/inventory/Dockerfile') + ref api: HttpHandler binds microservice.api.endpoint = rust.public_functions(api_mod) + + ## Payments Service + element payments_service implements microservice: + scope api_mod binds microservice.api.module = rust.module_selector('services::payments') + scope dockerfile binds microservice.container.dockerfile = files.file_selector('services/payments/Dockerfile') + ref api: HttpHandler binds microservice.api.endpoint = rust.public_functions(api_mod) + + # Cross-service rules + check rust.can_import(orders_service.api_mod, inventory_service.api) + check rust.can_import(orders_service.api_mod, payments_service.api) + check rust.no_dependency(payments_service.api_mod, orders_service.api_mod) +``` + +### 16.4 Hexagonal Architecture + +```hielements +import files +import rust + +## Application with Hexagonal Architecture +element hexagonal_app: + + ## Core Domain + ## Contains business logic, no external dependencies. + element domain: + scope module = rust.package_selector('myapp::domain') + + ref entities: Entities = rust.struct_selector(module, '*Entity') + ref services: Services = rust.struct_selector(module, '*Service') + + # Domain must not import adapters + check rust.no_dependency(module, adapters.module) + + ## Application Layer + ## Use cases and application services. + element application: + scope module = rust.package_selector('myapp::application') + + ref use_cases: UseCases = rust.public_functions(module) + + # Application can only depend on domain + check rust.imports_only(module, [domain.module]) + + ## Adapters Layer + ## External integrations (DB, API, etc.) + element adapters: + scope module = rust.package_selector('myapp::adapters') + + element database_adapter: + scope module = rust.module_selector('myapp::adapters::database') + ref repositories: Repositories = rust.struct_selector(module, '*Repository') + + element api_adapter: + scope module = rust.module_selector('myapp::adapters::api') + ref routes: Routes = rust.function_selector(module, 'setup_routes') + + # Adapters depend on application and domain + check rust.imports(module, application.module) + check rust.imports(module, domain.module) +``` + +### 16.5 Infrastructure Validation + +```hielements +import files + +## Infrastructure as Code +element infrastructure: + + ## Docker Configuration + element docker_config: + scope compose = files.file_selector('docker-compose.yml') + scope dockerfile_app = files.file_selector('Dockerfile') + scope dockerfile_worker = files.file_selector('worker.dockerfile') + + # All containers should have health checks + check files.exists(dockerfile_app, 'HEALTHCHECK') + check files.exists(dockerfile_worker, 'HEALTHCHECK') + + ## Configuration Files + element config: + scope env_example = files.file_selector('.env.example') + scope config_dir = files.folder_selector('config/') + + # Required configuration files + check files.exists(config_dir, 'production.yaml') + check files.exists(config_dir, 'development.yaml') + check files.exists(config_dir, 'testing.yaml') + + # No secrets in config files + check files.no_files_matching(config_dir, '*.secret') + check files.no_files_matching(config_dir, '*password*') +``` + +### 16.6 Testing Requirements + +```hielements +import files +import rust + +## Testing Standards +element testing_standards: + scope src = files.folder_selector('src/') + scope tests = files.folder_selector('tests/') + + # Mirror structure: tests should mirror src + element test_coverage: + scope unit_tests = files.folder_selector('tests/unit/') + scope integration_tests = files.folder_selector('tests/integration/') + + # Required test files + check files.exists(unit_tests, 'mod.rs') + check files.exists(integration_tests, 'mod.rs') + + # Each module should have tests + element orders_tests: + scope module = rust.module_selector('orders') + scope tests = rust.module_selector('tests::test_orders') + + check rust.module_exists(tests) + check rust.function_exists(tests, 'test_create_order') +``` + +### 16.7 Patterns + +```hielements +import files +import rust + +## Compiler Pattern with unbounded scopes +## Defines the structure of a compiler with lexer and parser components. +pattern compiler: + ## Lexer - tokenizes source code + element lexer: + scope module + ref tokens: TokenStream + + ## Parser - produces abstract syntax tree + element parser: + scope module + ref ast: AbstractSyntaxTree + + ## Verify lexer output is compatible with parser input + check compiler.lexer.tokens.compatible_with(compiler.parser.input) + +## Rust Compiler Implementation +## Implements the compiler pattern for Rust code. +element rust_compiler implements compiler: + # Bind lexer + scope lexer_mod binds compiler.lexer.module = rust.module_selector('rustcompiler::lexer') + ref tokens: TokenStream binds compiler.lexer.tokens = rust.function_selector(lexer_mod, 'tokenize') + + # Bind parser + scope parser_mod binds compiler.parser.module = rust.module_selector('rustcompiler::parser') + ref ast: AbstractSyntaxTree binds compiler.parser.ast = rust.function_selector(parser_mod, 'parse') + + # Add compiler-specific elements + element optimizer: + scope module = rust.module_selector('rustcompiler::optimizer') + check rust.function_exists(module, 'optimize_ast') + +## Microservice Pattern with unbounded scopes +pattern microservice: + element api: + scope module + ref rest_endpoint: HttpHandler + + element database: + scope module + ref connection: DbConnection + + element container: + scope dockerfile + ref ports: integer + + check microservice.container.exposes_port(8080) + check microservice.api.connects_to(microservice.database) + +## Observable Pattern +pattern observable: + element metrics: + scope module + ref prometheus_endpoint: MetricsHandler + +## Resilient Pattern +pattern resilient: + element circuit_breaker: + scope module + ref breaker_config: BreakerConfig + +## Production Service with Multiple Patterns +element production_service implements microservice, observable, resilient: + # Microservice bindings + scope api_mod binds microservice.api.module = rust.module_selector('service::api') + scope db_mod binds microservice.database.module = rust.module_selector('service::db') + scope dockerfile binds microservice.container.dockerfile = files.file_selector('service.dockerfile') + + ref api: HttpHandler binds microservice.api.rest_endpoint = rust.public_functions(api_mod) + ref db: DbConnection binds microservice.database.connection = rust.struct_selector(db_mod, 'DbConnection') + ref ports: integer binds microservice.container.ports = rust.const_selector(api_mod, 'PORT') + + # Observable bindings + scope metrics_mod binds observable.metrics.module = rust.module_selector('service::metrics') + ref prometheus: MetricsHandler binds observable.metrics.prometheus_endpoint = rust.function_selector(metrics_mod, 'metrics_handler') + + # Resilient bindings + scope resilience_mod binds resilient.circuit_breaker.module = rust.module_selector('service::resilience') + ref breaker: BreakerConfig binds resilient.circuit_breaker.breaker_config = rust.struct_selector(resilience_mod, 'CircuitBreakerConfig') +``` + +--- + +## Appendix A: Error Messages + +Common error messages and their meanings: + +| Error Code | Message | Meaning | +|------------|---------|---------| +| E001 | Undefined element '{name}' | Referenced element doesn't exist | +| E002 | Undefined scope '{name}' | Referenced scope doesn't exist | +| E003 | Undefined connection point '{name}' | Referenced connection point doesn't exist | +| E004 | Library '{name}' not found | Import references unknown library | +| E005 | Duplicate element '{name}' | Element name already defined in scope | +| E006 | Check failed: {message} | Architectural rule violation | +| E007 | Scope resolution failed | Selector couldn't find target | +| E008 | Invalid argument type | Wrong type passed to function | +| E009 | Syntax error | Invalid Hielements syntax | +| E010 | Cyclic element reference | Elements reference each other cyclically | + +--- + +## Appendix B: CLI Reference + +### Basic Commands + +```bash +# Validate specification syntax (no execution) +hielements check architecture.hie + +# Run all checks +hielements run architecture.hie + +# Dry run (show what would be checked) +hielements run --dry-run architecture.hie + +# Output formats +hielements check --format json architecture.hie +hielements check --format sarif architecture.hie +``` + +### Exit Codes + +| Code | Meaning | +|------|---------| +| 0 | Success (all checks passed) | +| 1 | Check failures (architectural violations) | +| 2 | Errors (syntax errors, missing files, etc.) | + +--- + +## Appendix C: Best Practices + +### Naming Conventions + +- Use `snake_case` for identifiers +- Use descriptive names that reflect the logical component +- Prefix private/internal elements with `_` + +### Organization + +- Keep one logical system per file +- Use nested elements for hierarchical structure +- Group related checks together + +### Documentation + +- Add doc comments (`##`) to all top-level elements +- Document connection points that are used by other elements +- Keep comments up-to-date with code changes + +### Performance + +- Use specific selectors over broad glob patterns +- Leverage caching in CI/CD pipelines +- Split large specifications into multiple files diff --git a/doc/language_v2.md b/doc/language_v2.md deleted file mode 100644 index 3b0319e..0000000 --- a/doc/language_v2.md +++ /dev/null @@ -1,40 +0,0 @@ -# Hielements V2 - -Hielements V2 is a second generation of the language. This second generation is incompatible with the original hielements language. The original hielements language is deprecated and unsupported. - -The goal of hielements v2 is having a clearer separation of responsibilities between *descriptive* and *prescriptive* parts of the language. - -Specifically: -- The *prescriptive* part is made of *patterns* (declared with the `template` keyword), the requires / forbids / allows keywords, and checks -- The *descriptive* part is made of elements that *implement* patterns and scopes that bind to actual code. - -It is possible to use the descriptive part without the prescriptive one; in this case, no enforcement / checks are performed - -Patterns declare scopes and rules: - -``` -pattern observable: - element metrics implements measurable: - allows language rust - - scope module # angular brackets specify the language; in original hielements, language was specified after columns - ref prometheus: MetricsHandler - - check files.exists(module, 'Cargo.toml') - -``` -scope in patterns is always unbounded. - -Elements *binds* these: - -``` -element observable_component implements observable: - scope main_module binds observable.metrics.module = rust.module_selector('payments::api') - ref main_handler: MetricsHandler binds observable.metrics.prometheus = rust.function_selector(main_module, 'handler') -``` - -The implements and binds keywords, and the language specification via angular brackets are optional, and only used when the prescriptive features of the language are used. - -See the [Pattern Catalog](patterns_catalog.md) for an extensive collection of common software engineering patterns expressed in Hielements. - - diff --git a/doc/scope_management.md b/doc/scope_management.md index d72cc4d..25373da 100644 --- a/doc/scope_management.md +++ b/doc/scope_management.md @@ -114,7 +114,7 @@ element system: ### Pattern Placeholders -Patterns (declared with `template`) define scope placeholders that implementations must bind: +Patterns (declared with `pattern`) define scope placeholders that implementations must bind: ```hielements pattern microservice: diff --git a/examples/language_example.hie b/examples/language_example.hie index 591b7b4..85ddf98 100644 --- a/examples/language_example.hie +++ b/examples/language_example.hie @@ -1,6 +1,6 @@ -# Example: Language Declarations and Connection Checks (V2 Syntax) +# Example: Language Declarations and Connection Checks +# Demonstrates the language keyword and connection verification # Demonstrates the language keyword and connection verification -# Updated to V2 syntax with angular brackets for language annotation import files import rust @@ -32,7 +32,7 @@ pattern multilingual_app { ## A Python-only service implementing the python_service pattern element api_service implements python_service { - # V2 syntax: angular brackets for language annotation + # Angular brackets for language annotation scope src = files.folder_selector('src/api') scope tests = files.folder_selector('tests/api') diff --git a/examples/v3_syntax_example.hie b/examples/syntax_example.hie similarity index 59% rename from examples/v3_syntax_example.hie rename to examples/syntax_example.hie index 48b2218..febe11f 100644 --- a/examples/v3_syntax_example.hie +++ b/examples/syntax_example.hie @@ -1,22 +1,25 @@ -# Example: Hielements V3 Syntax -# Demonstrates the new V3 language features: -# - Curly brackets { } for block delimiters instead of colons and indentation -# - 'ref' keyword instead of 'connection_point' -# - 'uses' keyword for declaring dependencies between elements/scopes +# Example: Hielements Syntax +# Demonstrates key language features: +# - Curly brackets { } for block delimiters +# - Indentation-based blocks (both styles are supported) +# - 'ref' keyword for reference points +# - 'uses' keyword for declaring dependencies +# - Angular brackets for language annotations: scope module +# - 'binds' keyword for connecting scopes to patterns import files import rust ### Compiler Core -Demonstrates the new V3 syntax with curly brackets and uses declarations. +Demonstrates curly bracket syntax and uses declarations. ### element core { ## Lexer - tokenizes .hie source files element lexer { scope module = rust.module_selector('lexer') - # Refs (formerly connection_point) - what the lexer produces + ## Refs - what the lexer produces ref token_output: Token = rust.struct_selector('Token') ref token_kinds: TokenKind = rust.enum_selector('TokenKind') @@ -29,7 +32,7 @@ element core { scope module = rust.module_selector('parser') scope lexer_module = rust.module_selector('lexer') - # The lexer_module scope uses (depends on) the lexer element + ## The lexer_module scope uses (depends on) the lexer element lexer_module uses lexer ref ast_output: Program = rust.struct_selector('Program') @@ -42,7 +45,7 @@ element core { element interpreter { scope module = rust.module_selector('interpreter') - # Interpreter uses parser (for AST types) + ## Interpreter uses parser (for AST types) module uses parser ref check_output: CheckResult = rust.enum_selector('CheckResult') @@ -52,16 +55,16 @@ element core { } ### -Pattern with V3 syntax -Shows that patterns also work with curly brackets +Observable Pattern +Shows patterns using curly brackets, with unbounded scopes and refs. ### -template observable { +pattern observable { allows language rust element metrics { - # Unbounded scope in pattern + ## Unbounded scope in pattern - must be bound by implementing elements scope module - # Unbounded ref in pattern + ## Unbounded ref in pattern - must be bound by implementing elements ref prometheus: MetricsHandler check rust.function_exists(module, 'metrics_handler') @@ -69,23 +72,30 @@ template observable { } ### -Element implementing pattern with V3 syntax +Payments Service implementing the observable pattern ### element payments_service implements observable { scope root = files.folder_selector('src/payments') - # Bind scope using new syntax + ## Bind scope using angular bracket language annotation and binds keyword scope metrics_mod binds observable.metrics.module = rust.module_selector('payments::metrics') - # Bind ref (not connection_point) + ## Bind ref ref handler: MetricsHandler binds observable.metrics.prometheus = rust.function_selector(metrics_mod, 'handler') element api { scope module = rust.module_selector('payments::api') - # API uses the metrics module + ## API uses the metrics module module uses metrics_mod check rust.function_exists(module, 'process_payment') } } + +## Simple element using indentation syntax (both styles are supported) +element simple_service: + scope src = rust.module_selector('simple_service') + ref api: HttpHandler = rust.public_functions(src) + + check rust.function_exists(src, 'handle_request') diff --git a/examples/v2_syntax_example.hie b/examples/v2_syntax_example.hie deleted file mode 100644 index 8491f15..0000000 --- a/examples/v2_syntax_example.hie +++ /dev/null @@ -1,71 +0,0 @@ -# Example: Hielements V2 Syntax -# Demonstrates the new V2 language features: -# - Angular brackets for language specification: scope module -# - Unbounded scopes in patterns (no = expression) -# - binds keyword for connecting scopes to patterns - -import files -import rust - -### -Observable Pattern (V2 Syntax) -Defines a component that must expose metrics. -Uses unbounded scopes that will be bound by implementing elements. -### -pattern observable { - allows language rust - - ## Metrics component - unbounded scope - element metrics { - # Unbounded scope - no '=' expression - scope module - # Unbounded connection point - ref prometheus: MetricsHandler - - check rust.function_exists(module, 'metrics_handler') - } -} - -## Resilient Pattern -## Adds resilience patterns (circuit breaker, retry) -pattern resilient { - element circuit_breaker { - scope module - ref breaker_config: BreakerConfig - } -} - -### -Payments Service -A concrete implementation demonstrating V2 binds syntax. -### -element payments_service implements observable, resilient { - scope root = files.folder_selector('src/payments') - - # Bind scope to observable pattern's metrics.module using V2 syntax - scope metrics_mod binds observable.metrics.module = rust.module_selector('payments::metrics') - - # Bind connection point - ref handler: MetricsHandler binds observable.metrics.prometheus = rust.function_selector(metrics_mod, 'handler') - - # Bind resilient pattern's circuit_breaker - scope breaker_mod binds resilient.circuit_breaker.module = rust.module_selector('payments::breaker') - ref breaker: BreakerConfig binds resilient.circuit_breaker.breaker_config = rust.struct_selector(breaker_mod, 'BreakerConfig') - - ## API module - element api { - scope module = rust.module_selector('payments::api') - - check rust.function_exists(module, 'process_payment') - check rust.has_tests(module) - } -} - -## Simple element without patterns (descriptive only mode) -## In V2, implements and binds are optional -element simple_service { - scope src = rust.module_selector('simple_service') - ref api: HttpHandler = rust.public_functions(src) - - check rust.function_exists(src, 'handle_request') -} diff --git a/hielements.hie b/hielements.hie index f88ffdd..b6f5e1f 100644 --- a/hielements.hie +++ b/hielements.hie @@ -1,9 +1,7 @@ -## Hielements Self-Description (V3 Syntax) +## Hielements Self-Description ## This file describes the structure of the Hielements project itself, ## demonstrating the language's ability to model hierarchical software systems. ## -## V3 syntax: curly brackets, ref, and uses keywords -## ## Hielements is primarily a CLI tool. The project structure is: ## - crates/hielements-core - Core library (lexer, parser, AST, interpreter, stdlib) ## - crates/hielements-cli - CLI binary (the main entry point) @@ -75,7 +73,7 @@ element hielements { check rust.struct_exists('Element') check rust.enum_exists('Expression') - ## Pattern support structures (patterns are declared with `template` keyword) + ## Pattern support structures (patterns are declared with `pattern` keyword) check rust.struct_exists('Template') check rust.struct_exists('TemplateImplementation') check rust.struct_exists('TemplateBinding') @@ -86,7 +84,7 @@ element hielements { check rust.enum_exists('ComponentSpec') check rust.struct_exists('ConnectionPattern') - ## V3 ref and uses declaration structures + ## Ref and uses declaration structures check rust.struct_exists('RefDeclaration') check rust.struct_exists('UsesDeclaration') @@ -368,8 +366,7 @@ element hielements { check files.exists(examples, 'hierarchical.hie') check files.exists(examples, 'language_example.hie') check files.exists(examples, 'python_example.hie') - check files.exists(examples, 'v2_syntax_example.hie') - check files.exists(examples, 'v3_syntax_example.hie') + check files.exists(examples, 'syntax_example.hie') check files.exists(examples, 'hielements.toml') check files.exists(examples, 'hielements_hybrid.toml') } diff --git a/specs/core.hie b/specs/core.hie index e059d8b..31d1898 100644 --- a/specs/core.hie +++ b/specs/core.hie @@ -1,6 +1,5 @@ ## Core Library Specification ## Defines the structure of the hielements-core crate. -## Updated to use V3 syntax: curly brackets, ref, and uses keywords import files import rust diff --git a/wasm_plugins/fastapi_auth/target/.rustc_info.json b/wasm_plugins/fastapi_auth/target/.rustc_info.json index fe1c14d..e1962da 100644 --- a/wasm_plugins/fastapi_auth/target/.rustc_info.json +++ b/wasm_plugins/fastapi_auth/target/.rustc_info.json @@ -1 +1 @@ -{"rustc_fingerprint":12749266168559913572,"outputs":{"17747080675513052775":{"success":true,"status":"","code":0,"stdout":"rustc 1.92.0 (ded5c06cf 2025-12-08)\nbinary: rustc\ncommit-hash: ded5c06cf21d2b93bffd5d884aa6e96934ee4234\ncommit-date: 2025-12-08\nhost: x86_64-unknown-linux-gnu\nrelease: 1.92.0\nLLVM version: 21.1.3\n","stderr":""},"7971740275564407648":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.so\nlib___.so\nlib___.a\nlib___.so\n/home/runner/.rustup/toolchains/stable-x86_64-unknown-linux-gnu\noff\npacked\nunpacked\n___\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"gnu\"\ntarget_family=\"unix\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"linux\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"unknown\"\nunix\n","stderr":""},"11652014622397750202":{"success":true,"status":"","code":0,"stdout":"___.wasm\nlib___.rlib\n___.wasm\nlib___.a\n/home/runner/.rustup/toolchains/stable-x86_64-unknown-linux-gnu\noff\n___\ndebug_assertions\npanic=\"abort\"\nproc_macro\ntarget_abi=\"\"\ntarget_arch=\"wasm32\"\ntarget_endian=\"little\"\ntarget_env=\"\"\ntarget_family=\"wasm\"\ntarget_feature=\"bulk-memory\"\ntarget_feature=\"multivalue\"\ntarget_feature=\"mutable-globals\"\ntarget_feature=\"nontrapping-fptoint\"\ntarget_feature=\"reference-types\"\ntarget_feature=\"sign-ext\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"unknown\"\ntarget_pointer_width=\"32\"\ntarget_vendor=\"unknown\"\n","stderr":"warning: dropping unsupported crate type `dylib` for target `wasm32-unknown-unknown`\n\nwarning: dropping unsupported crate type `proc-macro` for target `wasm32-unknown-unknown`\n\nwarning: 2 warnings emitted\n\n"}},"successes":{}} \ No newline at end of file +{"rustc_fingerprint":9228007664341697165,"outputs":{"11857020428658561806":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.so\nlib___.so\nlib___.a\nlib___.so\n/home/runner/.rustup/toolchains/stable-x86_64-unknown-linux-gnu\noff\npacked\nunpacked\n___\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"gnu\"\ntarget_family=\"unix\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"linux\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"unknown\"\nunix\n","stderr":""},"17747080675513052775":{"success":true,"status":"","code":0,"stdout":"rustc 1.94.0 (4a4ef493e 2026-03-02)\nbinary: rustc\ncommit-hash: 4a4ef493e3a1488c6e321570238084b38948f6db\ncommit-date: 2026-03-02\nhost: x86_64-unknown-linux-gnu\nrelease: 1.94.0\nLLVM version: 21.1.8\n","stderr":""},"7971740275564407648":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.so\nlib___.so\nlib___.a\nlib___.so\n/home/runner/.rustup/toolchains/stable-x86_64-unknown-linux-gnu\noff\npacked\nunpacked\n___\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"gnu\"\ntarget_family=\"unix\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"linux\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"unknown\"\nunix\n","stderr":""}},"successes":{}} \ No newline at end of file