Summary
Enhance the Forge language parser to extract and structure invariant expressions currently parsed as strings. This foundation enables invariant-based code generation in v0.2.
Motivation
Invariants are a critical semantic feature of Forge contracts but are currently captured only as string expressions. To enable:
- Runtime validation in Zod schemas
- Database constraints in SQL DDL
- Swagger documentation with constraints
...we need a structured representation of invariant expressions that captures operator, left/right operands, and type information.
Scope
In Scope
Out of Scope
- Complex expressions (AND, OR, NOT)
- Arithmetic operators
- Function calls in invariants
- Invariant evaluation/enforcement (Phase 1.1.2)
Technical Design
Current State
interface InvariantModel {
expression: string; // "valor > 0" stored as string
}
Proposed State
interface InvariantExpression {
left: string; // Field name
operator: ComparisonOp; // Type: ==|!=|<|>|<=|>=
right: InvariantValue; // Number | String | Field reference
location: SourceLocation;
}
interface InvariantModel {
expression: string; // Original text
parsed?: InvariantExpression; // NEW: Structured form
}
Parser Changes
-
Extend Langium grammar (forge.langium):
Invariant: 'invariant' left=ID operator=ComparisonOp right=InvariantValue
ComparisonOp: '==' | '!=' | '<' | '>' | '<=' | '>='
InvariantValue: NUMBER | STRING | ID
-
New validation in validateInvariant():
- Left operand must match existing field name
- Right operand type must be compatible with left operand type
- Report precise errors with file:line:column
Error Codes
FORGE_SEMANTIC_005: Invalid invariant syntax
FORGE_SEMANTIC_006: Type mismatch in invariant operands
FORGE_SEMANTIC_007: Unsupported operator in invariant
Acceptance Criteria
Testing Strategy
Unit Tests
// tests/invariants-parsing.test.mjs
test('parse comparison operators', () => {
const cases = [
'invariant valor > 0',
'invariant idade >= 18',
'invariant status != ""',
];
// Verify each parses into correct operator
})
test('detect invalid operator', () => {
const invalid = 'invariant valor ~= 0';
// Verify error: unknown operator
})
test('detect missing left operand', () => {
const invalid = 'invariant > 0';
// Verify syntax error
})
test('detect invalid right operand type', () => {
const invalid = 'invariant valor > invalidField';
// Verify: reference must exist or be literal
})
Integration Tests
- Compile contract with invariants → no errors
- Use contract in generator → invariant accessible
- Multiple invariants per field → all parsed
Files to Create/Modify
packages/language/src/forge.langium (MODIFY - 2 lines)
packages/language/src/index.ts (MODIFY - 50 lines)
packages/language/src/invariants/parser.ts (NEW - 80 lines)
packages/language/src/invariants/types.ts (NEW - 30 lines)
tests/invariants-parsing.test.mjs (NEW - 150 lines)
Non-Goals
- Multi-field complex boolean expressions
- Arithmetic or string operations in invariants
- Evaluation or enforcement of invariants
- GraphQL/SQL invariant generation (v0.2 later features)
Future Extensions
- Conditional invariants:
invariant if status == 'ACTIVE' then valor > 0
- Complex expressions:
invariant valor > 0 AND valor < 999999
- Cross-field validation:
invariant dateFim > dataInicio
- Custom invariant functions
- Pre/post condition assertions
PR Template Reminder
When implementing, create a PR with:
- Title:
feat(invariants): parse expressions into structured AST
- Branch:
feature/invariant-parser
- Tests: All passing, new coverage included
- Docs: README update with invariant examples
Summary
Enhance the Forge language parser to extract and structure invariant expressions currently parsed as strings. This foundation enables invariant-based code generation in v0.2.
Motivation
Invariants are a critical semantic feature of Forge contracts but are currently captured only as string expressions. To enable:
...we need a structured representation of invariant expressions that captures operator, left/right operands, and type information.
Scope
In Scope
invariant left operator right==,!=,<,>,<=,>=InvariantExpressiontypeOut of Scope
Technical Design
Current State
Proposed State
Parser Changes
Extend Langium grammar (
forge.langium):New validation in
validateInvariant():Error Codes
FORGE_SEMANTIC_005: Invalid invariant syntaxFORGE_SEMANTIC_006: Type mismatch in invariant operandsFORGE_SEMANTIC_007: Unsupported operator in invariantAcceptance Criteria
Testing Strategy
Unit Tests
Integration Tests
Files to Create/Modify
packages/language/src/forge.langium(MODIFY - 2 lines)packages/language/src/index.ts(MODIFY - 50 lines)packages/language/src/invariants/parser.ts(NEW - 80 lines)packages/language/src/invariants/types.ts(NEW - 30 lines)tests/invariants-parsing.test.mjs(NEW - 150 lines)Non-Goals
Future Extensions
invariant if status == 'ACTIVE' then valor > 0invariant valor > 0 AND valor < 999999invariant dateFim > dataInicioPR Template Reminder
When implementing, create a PR with:
feat(invariants): parse expressions into structured ASTfeature/invariant-parser