@smile-digital-health/crl 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/README.md +502 -0
  2. package/dist/ast/builder.d.ts +45 -0
  3. package/dist/ast/builder.js +389 -0
  4. package/dist/ast/builder.js.map +1 -0
  5. package/dist/ast/types.d.ts +223 -0
  6. package/dist/ast/types.js +73 -0
  7. package/dist/ast/types.js.map +1 -0
  8. package/dist/ast/utils.d.ts +14 -0
  9. package/dist/ast/utils.js +65 -0
  10. package/dist/ast/utils.js.map +1 -0
  11. package/dist/cli/run-ast.d.ts +1 -0
  12. package/dist/cli/run-ast.js +24 -0
  13. package/dist/cli/run-ast.js.map +1 -0
  14. package/dist/cli/run-lexer.d.ts +1 -0
  15. package/dist/cli/run-lexer.js +41 -0
  16. package/dist/cli/run-lexer.js.map +1 -0
  17. package/dist/cli/run-parser.d.ts +1 -0
  18. package/dist/cli/run-parser.js +33 -0
  19. package/dist/cli/run-parser.js.map +1 -0
  20. package/dist/cli/run-transformer-fsh-to-crl.d.ts +1 -0
  21. package/dist/cli/run-transformer-fsh-to-crl.js +20 -0
  22. package/dist/cli/run-transformer-fsh-to-crl.js.map +1 -0
  23. package/dist/cli/run-validator.d.ts +1 -0
  24. package/dist/cli/run-validator.js +37 -0
  25. package/dist/cli/run-validator.js.map +1 -0
  26. package/dist/grammar/activityTypes.d.ts +2 -0
  27. package/dist/grammar/activityTypes.js +9 -0
  28. package/dist/grammar/activityTypes.js.map +1 -0
  29. package/dist/grammar/conceptTypes.d.ts +2 -0
  30. package/dist/grammar/conceptTypes.js +9 -0
  31. package/dist/grammar/conceptTypes.js.map +1 -0
  32. package/dist/grammar/conceptValueTypes.d.ts +2 -0
  33. package/dist/grammar/conceptValueTypes.js +9 -0
  34. package/dist/grammar/conceptValueTypes.js.map +1 -0
  35. package/dist/grammar/generated/antlr/CRLLexer.d.ts +84 -0
  36. package/dist/grammar/generated/antlr/CRLLexer.js +505 -0
  37. package/dist/grammar/generated/antlr/CRLLexer.js.map +1 -0
  38. package/dist/grammar/generated/antlr/CRLParser.d.ts +653 -0
  39. package/dist/grammar/generated/antlr/CRLParser.js +3466 -0
  40. package/dist/grammar/generated/antlr/CRLParser.js.map +1 -0
  41. package/dist/grammar/generated/antlr/CRLParserListener.d.ts +162 -0
  42. package/dist/grammar/generated/antlr/CRLParserListener.js +3 -0
  43. package/dist/grammar/generated/antlr/CRLParserListener.js.map +1 -0
  44. package/dist/grammar/generated/antlr/CRLParserVisitor.d.ts +109 -0
  45. package/dist/grammar/generated/antlr/CRLParserVisitor.js +3 -0
  46. package/dist/grammar/generated/antlr/CRLParserVisitor.js.map +1 -0
  47. package/dist/grammar/generated/types/activityTypes.json +16 -0
  48. package/dist/grammar/generated/types/conceptTypes.json +26 -0
  49. package/dist/grammar/generated/types/conceptValueTypes.json +14 -0
  50. package/dist/index.d.ts +18 -0
  51. package/dist/index.js +147 -0
  52. package/dist/index.js.map +1 -0
  53. package/dist/lexer/CRLLexerErrorListener.d.ts +16 -0
  54. package/dist/lexer/CRLLexerErrorListener.js +168 -0
  55. package/dist/lexer/CRLLexerErrorListener.js.map +1 -0
  56. package/dist/lexer/createLexer.d.ts +6 -0
  57. package/dist/lexer/createLexer.js +14 -0
  58. package/dist/lexer/createLexer.js.map +1 -0
  59. package/dist/lexer/index.d.ts +1 -0
  60. package/dist/lexer/index.js +6 -0
  61. package/dist/lexer/index.js.map +1 -0
  62. package/dist/parser/CustomParserErrorListener.d.ts +8 -0
  63. package/dist/parser/CustomParserErrorListener.js +37 -0
  64. package/dist/parser/CustomParserErrorListener.js.map +1 -0
  65. package/dist/parser/createParser.d.ts +7 -0
  66. package/dist/parser/createParser.js +17 -0
  67. package/dist/parser/createParser.js.map +1 -0
  68. package/dist/transformer/fsh-to-crl/mapping/activityDefinition.d.ts +41 -0
  69. package/dist/transformer/fsh-to-crl/mapping/activityDefinition.js +229 -0
  70. package/dist/transformer/fsh-to-crl/mapping/activityDefinition.js.map +1 -0
  71. package/dist/transformer/fsh-to-crl/mapping/concept.d.ts +1 -0
  72. package/dist/transformer/fsh-to-crl/mapping/concept.js +14 -0
  73. package/dist/transformer/fsh-to-crl/mapping/concept.js.map +1 -0
  74. package/dist/transformer/fsh-to-crl/mapping/planDefinition.d.ts +14 -0
  75. package/dist/transformer/fsh-to-crl/mapping/planDefinition.js +235 -0
  76. package/dist/transformer/fsh-to-crl/mapping/planDefinition.js.map +1 -0
  77. package/dist/transformer/fsh-to-crl/mapping/terminology.d.ts +1 -0
  78. package/dist/transformer/fsh-to-crl/mapping/terminology.js +10 -0
  79. package/dist/transformer/fsh-to-crl/mapping/terminology.js.map +1 -0
  80. package/dist/transformer/fsh-to-crl/transformer.d.ts +1 -0
  81. package/dist/transformer/fsh-to-crl/transformer.js +172 -0
  82. package/dist/transformer/fsh-to-crl/transformer.js.map +1 -0
  83. package/dist/transformer/fsh-to-crl/utils/activityDeduplication.d.ts +11 -0
  84. package/dist/transformer/fsh-to-crl/utils/activityDeduplication.js +23 -0
  85. package/dist/transformer/fsh-to-crl/utils/activityDeduplication.js.map +1 -0
  86. package/dist/transformer/fsh-to-crl/utils/fshPathFunctions.d.ts +12 -0
  87. package/dist/transformer/fsh-to-crl/utils/fshPathFunctions.js +73 -0
  88. package/dist/transformer/fsh-to-crl/utils/fshPathFunctions.js.map +1 -0
  89. package/dist/types/errors.d.ts +8 -0
  90. package/dist/types/errors.js +3 -0
  91. package/dist/types/errors.js.map +1 -0
  92. package/dist/validator/actionUniquenessValidator.d.ts +20 -0
  93. package/dist/validator/actionUniquenessValidator.js +280 -0
  94. package/dist/validator/actionUniquenessValidator.js.map +1 -0
  95. package/dist/validator/cycleDetector.d.ts +13 -0
  96. package/dist/validator/cycleDetector.js +79 -0
  97. package/dist/validator/cycleDetector.js.map +1 -0
  98. package/dist/validator/nameUniquenessValidator.d.ts +5 -0
  99. package/dist/validator/nameUniquenessValidator.js +87 -0
  100. package/dist/validator/nameUniquenessValidator.js.map +1 -0
  101. package/dist/validator/unusedDeclarationsValidator.d.ts +21 -0
  102. package/dist/validator/unusedDeclarationsValidator.js +190 -0
  103. package/dist/validator/unusedDeclarationsValidator.js.map +1 -0
  104. package/dist/validator/validator.d.ts +27 -0
  105. package/dist/validator/validator.js +30 -0
  106. package/dist/validator/validator.js.map +1 -0
  107. package/package.json +92 -0
package/README.md ADDED
@@ -0,0 +1,502 @@
1
+ # @smile-digital-health/crl
2
+
3
+ Clinical Reasoning Language (CRL) parser and validator
4
+
5
+ Clinical Reasoning Language (CRL) provides a structured way to model clinical decision logic, concept derivations, clinical activities, measurements, and summaries — while remaining simple, readable, and guideline-compatible.
6
+
7
+ ## Overview
8
+
9
+ See the [User Guide](./USER_GUIDE.md) for a comprehensive introduction to the CRL language, syntax, and authoring best practices.
10
+
11
+ CRL is a domain-specific language designed for expressing clinical practice guidelines in a structured and machine-readable format. The language is implemented in TypeScript and provides a comprehensive set of tools for processing CRL documents.
12
+
13
+ ## Installation
14
+
15
+ This is a private package. To install it, you need:
16
+
17
+ 1. An npm account with access to `@smile-digital-health` packages
18
+ - Contact your team lead to request access
19
+ - You will receive an invitation to join the organization
20
+ - Each developer should use their own npm account
21
+
22
+ 2. Set up authentication:
23
+ ```bash
24
+ # Log in to npm - recommended for individual developers
25
+ npm login
26
+
27
+ # Or create/edit ~/.npmrc with your personal access token
28
+ //registry.npmjs.org/:_authToken=YOUR_NPM_TOKEN
29
+ ```
30
+
31
+ > **Security Note:** Always use your personal npm token. Do not share tokens between team members.
32
+
33
+ Once authenticated, install the package:
34
+
35
+ ```bash
36
+ npm install @smile-digital-health/crl
37
+ ```
38
+
39
+ Or add it to your `package.json`:
40
+
41
+ ```json
42
+ "dependencies": {
43
+ "@smile-digital-health/crl": "^0.6.1"
44
+ }
45
+ ```
46
+
47
+ ### For Package Maintainers
48
+
49
+ #### Publishing to npm
50
+
51
+ The package is automatically published to npm when a new GitHub release is created (non-draft, non-prerelease). To verify what will be published:
52
+
53
+ ```bash
54
+ # See what files would be included in the package
55
+ npm publish --dry-run
56
+
57
+ # Create the package locally without publishing
58
+ npm pack
59
+
60
+ # Examine contents of the generated .tgz file
61
+ tar -ztvf <package-name>.tgz
62
+ ```
63
+
64
+ ## Features
65
+
66
+ ### Language Features
67
+
68
+ * Decision blocks with nested conditions
69
+ * Concept definitions with type and value specifications
70
+ * Activity statements with perform types
71
+ * Terminology statements with valueset, system/code, and unknown definitions
72
+ * FHIR resource type support
73
+ * String literals with proper escaping
74
+ * Comments (single-line and block)
75
+
76
+ ### Processing Features
77
+
78
+ * Lexical analysis with detailed token information
79
+ * Syntax parsing with error recovery
80
+ * AST generation with type safety
81
+ * Semantic validation with comprehensive error reporting
82
+ * Cross-platform compatibility (Windows, Mac, Linux)
83
+
84
+ ### Development Features
85
+
86
+ * TypeScript implementation for type safety
87
+ * Comprehensive test suite
88
+ * Detailed documentation
89
+ * Example implementations
90
+ * Development tools and utilities
91
+
92
+ ## CLI Usage
93
+
94
+ The CRL package includes command-line tools for processing CRL files. Each tool can be run with the `--raw` flag to output raw JSON data instead of formatted output.
95
+
96
+ ### Lexer Tool
97
+
98
+ ```bash
99
+ npx ts-node src/cli/run-lexer.ts
100
+ npx ts-node src/cli/run-lexer.ts --raw
101
+ ```
102
+
103
+ ### Parser Tool
104
+
105
+ ```bash
106
+ npx ts-node src/cli/run-parser.ts
107
+ npx ts-node src/cli/run-parser.ts --raw
108
+ ```
109
+
110
+ ### AST Tool
111
+
112
+ ```bash
113
+ npx ts-node src/cli/run-ast.ts
114
+ npx ts-node src/cli/run-ast.ts --raw
115
+ ```
116
+
117
+ ### Validator Tool
118
+
119
+ ```bash
120
+ npx ts-node src/cli/run-validator.ts
121
+ npx ts-node src/cli/run-validator.ts --raw
122
+ ```
123
+
124
+ ### FSH-to-CRL Transformer Tool
125
+
126
+ The FSH-to-CRL transformer converts FHIR Shorthand (FSH) files into Clinical Reasoning Language (CRL) files. It supports advanced mapping and deduplication logic for activities, concepts, and terminology blocks.
127
+
128
+ #### Activity Mapping Enhancements
129
+
130
+ - **Conditional `do not perform`**: If an activity in FSH has `doNotPerform = true`, the generated CRL will emit `do not perform` instead of `perform` for that activity.
131
+ - **Activity Terminology Block Emission**: For activities with a `medicationCodeableConcept`, the terminology block uses the `system` and `code` properties directly from the FSH object. For activities using `dynamicValue.expression.expression` with `path = "code.coding"`, the code and system are extracted from the CQL code expression.
132
+ - **Deduplication and Suffixing**: Terminology blocks are unique by identifier and body. If a duplicate identifier is encountered with a different body, a numeric suffix (e.g., `_2`) is added to the identifier. If both identifier and body are the same, the block is not duplicated.
133
+ - **Extraction Logic**: For `medicationCodeableConcept`, the transformer uses `system`, `code`, and `identifier` from the FSH object. For `dynamicValue.expression.expression` (where `path = "code.coding"`), the transformer uses `system` and `code` from the CQL code expression string and `identifier` from the corresponding description.
134
+
135
+ For more details, see the [User Guide](./USER_GUIDE.md) and the technical mapping documentation.
136
+
137
+ ## API Usage & Reference
138
+
139
+ **All API functions return a `ParseResult` object. If there are any lexical or syntax errors, these are collected and returned in the `errors` array (not just printed to the console). You should always check `result.success` and handle errors accordingly.**
140
+
141
+ The package provides four main functions for processing CRL code:
142
+
143
+ ### 1. Tokenization
144
+
145
+ ```typescript
146
+ import { tokenizeCRL } from '@smile-digital-health/crl';
147
+
148
+
149
+ const result = tokenizeCRL(`
150
+ decision "Test":
151
+ when "Condition" then do "Action".
152
+ done
153
+ `);
154
+
155
+ if (result.success) {
156
+ // Access the tokens
157
+ console.log(result.result);
158
+ } else {
159
+ // Handle errors
160
+ console.error(result.errors);
161
+ }
162
+ ```
163
+
164
+ ### 2. Parsing
165
+
166
+ ```typescript
167
+ import { parseCRL } from '@smile-digital-health/crl';
168
+
169
+ const result = parseCRL(`
170
+ decision "Test":
171
+ when "Condition" then do "Action".
172
+ done
173
+ `);
174
+
175
+ if (result.success) {
176
+ // Access the parse tree
177
+ console.log(result.result);
178
+ } else {
179
+ // Handle errors
180
+ console.error(result.errors);
181
+ }
182
+ ```
183
+
184
+ ### 3. AST Building
185
+
186
+ ```typescript
187
+ import { buildCRL } from '@smile-digital-health/crl';
188
+
189
+ const result = buildCRL(`
190
+ decision "Test":
191
+ when "Condition" then do "Action".
192
+ done
193
+ `);
194
+
195
+ if (result.success) {
196
+ // Access the AST
197
+ console.log(result.result);
198
+ } else {
199
+ // Handle errors
200
+ console.error(result.errors);
201
+ }
202
+ ```
203
+
204
+ ### 4. Validation
205
+
206
+ > ⚠️ **Note:** Validation functionality is not yet implemented. The `validateCRL` function is a placeholder and will likely throw or return an error if used.
207
+
208
+ ```typescript
209
+ import { validateCRL } from '@smile-digital-health/crl';
210
+
211
+ const result = validateCRL(`
212
+ decision "Test":
213
+ when "Condition" then do "Action".
214
+ done
215
+ `);
216
+
217
+ if (result.success) {
218
+ // Access the validated AST
219
+ console.log(result.result);
220
+ } else {
221
+ // Handle validation errors
222
+ console.error(result.errors);
223
+ }
224
+ ```
225
+
226
+ ### Core Functions & Types
227
+
228
+ ```typescript
229
+ interface Token {
230
+ line: number;
231
+ column: number;
232
+ type: string;
233
+ text: string;
234
+ }
235
+
236
+ interface ParseResult<T> {
237
+ success: boolean;
238
+ result?: T;
239
+ errors?: string[];
240
+ }
241
+ ```
242
+
243
+ ## Examples
244
+
245
+ ### Example AST Comparison
246
+
247
+ To compare the generated AST with the expected AST, you can run:
248
+
249
+ ```bash
250
+ npx ts-node --log-error src/examples/compare-ast.ts
251
+ ```
252
+
253
+ This will:
254
+
255
+ 1. Parse the example CRL file (`docs/Measles Immunization Decision.crl`)
256
+ 2. Generate an AST from the parsed input
257
+ 3. Compare it with the expected AST (`docs/Expected AST.ast`)
258
+ 4. Display any differences between the two ASTs
259
+
260
+ The comparison includes:
261
+
262
+ * Line count matching
263
+ * Whitespace-normalized matching
264
+ * Structure matching
265
+ * Detailed line-by-line comparison of differences
266
+
267
+ ### FSH-to-CRL Example Data
268
+
269
+ The example FSH files and CRL outputs in `src/examples/fsh/who/smart-example-immz/` and `src/examples/crl/who/smart-example-immz/` are derived from the [WHO SMART Guidelines - Example IG for Measles Immunization](https://github.com/WorldHealthOrganization/smart-example-immz).
270
+
271
+ - **Source repository:** [WorldHealthOrganization/smart-example-immz](https://github.com/WorldHealthOrganization/smart-example-immz)
272
+ - **License:** [CC BY-IGO 3.0](https://github.com/WorldHealthOrganization/smart-example-immz/blob/main/LICENSE.md)
273
+
274
+ These examples are used for development, testing, and demonstration of the transformer.
275
+
276
+ ## Updating Grammar & Generated Files
277
+
278
+ ### Updating grammar files
279
+
280
+ ```bash
281
+ npm run generate
282
+ ```
283
+
284
+ This will extract all grammar-driven types and regenerate the lexer and parser.
285
+
286
+ ### Grammar-Driven Types
287
+
288
+ * The lists of valid activity types, concept types, and concept value types are defined in the `validTypes` arrays in the `ACTIVITY_TYPE`, `CONCEPT_TYPE`, and `CONCEPT_VALUE_TYPE` rules of `src/grammar/CRLLexer.g4`.
289
+ * Scripts (`scripts/extractActivityTypes.js`, `scripts/extractConceptTypes.js`, `scripts/extractConceptValueTypes.js`) automatically extract these lists and write them to `src/grammar/activityTypes.json`, `src/grammar/conceptTypes.json`, and `src/grammar/conceptValueTypes.json`.
290
+ * TypeScript modules (`src/grammar/activityTypes.ts`, `src/grammar/conceptTypes.ts`, `src/grammar/conceptValueTypes.ts`) import these JSON files and export both the arrays and type-safe union types.
291
+ * **All code (lexer, AST, error listener, etc.) should import from these modules to avoid drift.**
292
+
293
+ ### ANTLR-Generated Files
294
+
295
+ The following files are generated by ANTLR and should not be edited manually or tracked in git:
296
+
297
+ * `src/grammar/generated/antlr/CRLLexer.ts`
298
+ * `src/grammar/generated/antlr/CRLParser.ts`
299
+ * `src/grammar/generated/antlr/CRLParserVisitor.ts`
300
+ * `src/grammar/generated/antlr/CRLParserListener.ts`
301
+ * `src/grammar/generated/antlr/CRLLexer.tokens`
302
+ * `src/grammar/generated/antlr/CRLLexer.interp`
303
+ * `src/grammar/generated/antlr/CRLParser.tokens`
304
+ * `src/grammar/generated/antlr/CRLParser.interp`
305
+
306
+ To regenerate these files, run:
307
+
308
+ ```bash
309
+ npm run generate
310
+ ```
311
+
312
+ These files are ignored by git via `.gitignore` and will be recreated as needed.
313
+
314
+ ## Development
315
+
316
+ ### Building
317
+
318
+ ```bash
319
+ npm run build
320
+ ```
321
+
322
+ ### Test Documentation
323
+
324
+ The project includes a comprehensive test suite to ensure the correctness of the lexer and parser. Here's how to run and work with the tests:
325
+
326
+ #### Running Tests
327
+
328
+ ```bash
329
+ # Run all tests
330
+ npm test
331
+
332
+ # Run tests with coverage
333
+ npm run test:coverage
334
+
335
+ # Run specific test files
336
+ npx jest src/ast/tests/decision-structure.test.ts --verbose --no-cache --colors --forceExit --detectOpenHandles --watchAll=false
337
+
338
+ # Run tests in watch mode (useful during development)
339
+ npm run test:watch
340
+ ```
341
+
342
+ #### Test Categories
343
+
344
+ The test suite is organized into several categories:
345
+
346
+ 1. **Basic Tokens** (`basic-tokens.test.ts`)
347
+ * Tests for fundamental language tokens
348
+ * Keywords, punctuation, and basic syntax
349
+
350
+ 2. **FHIR Types** (`fhir-types.test.ts`)
351
+ * Tests for FHIR-specific activity types
352
+ * Validation of CPG-prefixed activity types
353
+ * Error handling for invalid types
354
+
355
+ 3. **Grammar Example** (`grammar-example.test.ts`)
356
+ * Tests the lexer against the complete grammar example
357
+ * Validates real-world usage scenarios
358
+
359
+ 4. **Error Handling** (`error-handling.test.ts`)
360
+ * Tests for invalid input handling
361
+ * Error message validation
362
+ * Recovery from syntax errors
363
+
364
+ 5. **Comments** (`comments.test.ts`)
365
+ * Tests for comment handling
366
+ * Single-line and block comments
367
+ * Comment placement and nesting
368
+
369
+ #### Writing Tests
370
+
371
+ When adding new tests:
372
+
373
+ 1. Place test files in the appropriate directory, for example `src/lexer/tests/`
374
+ 2. Follow the existing test patterns
375
+ 3. Include both positive and negative test cases
376
+ 4. Use descriptive test names
377
+ 5. Add comments explaining complex test scenarios
378
+
379
+ Example test structure:
380
+
381
+ ```typescript
382
+ describe('Feature Name', () => {
383
+ it('should handle valid input', () => {
384
+ // Test valid cases
385
+ });
386
+
387
+ it('should handle invalid input', () => {
388
+ // Test error cases
389
+ });
390
+ });
391
+ ```
392
+
393
+ #### Test Utilities
394
+
395
+ The test suite includes several utility functions:
396
+
397
+ * `getAllTokens`: Retrieves all tokens from a lexer
398
+ * `verifyTokenSequence`: Validates token sequences
399
+ * `getActionTokenSequence`: Helper for activity type tests
400
+ * `getCaseFeatureTokenSequence`: Helper for concept type tests
401
+ * `getValueTypeTokenSequence`: Helper for value type tests
402
+
403
+ ### Linting
404
+
405
+ The project uses ESLint for code quality checks. To run the linter:
406
+
407
+ ```bash
408
+ # Lint all TypeScript files
409
+ npx eslint . --ext .ts
410
+
411
+ # Lint and automatically fix issues where possible
412
+ npx eslint . --ext .ts --fix
413
+ ```
414
+
415
+ ### Running Examples
416
+
417
+ The project includes several example files that demonstrate different features and validation scenarios:
418
+
419
+ ```bash
420
+ # Run the basic usage example
421
+ npm run example
422
+ ```
423
+
424
+ ### Regenerating ANTLR Files
425
+
426
+ If you modify the grammar in `src/grammar/CRL.g4`, you'll need to regenerate the lexer and parser:
427
+
428
+ ```bash
429
+ cd src/grammar
430
+ antlr4ts -Xforce-atn -o src/grammar/generated src/grammar/CRLLexer.g4 && antlr4ts -Xforce-atn -o src/grammar/generated src/grammar/CRLParser.g4
431
+ ```
432
+
433
+ Note: This project uses a custom AST implementation that uses ANTLR's visitor pattern. The generated files are used only for lexing and parsing directly, while semantic analysis and interpretation are handled by our custom AST implementation.
434
+
435
+ ## 📦 Release Process & Checklist
436
+
437
+ > **Release Process:**
438
+ >
439
+ > The authoritative release checklist and instructions are maintained in a single location:
440
+ >
441
+ > **[.github/PULL_REQUEST_TEMPLATE/release.md](.github/PULL_REQUEST_TEMPLATE/release.md)**
442
+ >
443
+ > Always follow the steps in this file when preparing a new release. This ensures consistency and reduces maintenance overhead. If you need to update the release process, update the PR template only.
444
+
445
+ ## Developer Notes & Error Handling
446
+
447
+ ### Developer Notes: Release Checklist & GitHub Automation
448
+
449
+ - **Pull Request Templates:** The project uses GitHub's PR template system to standardize release PRs and ensure all required steps are followed.
450
+ - **Release Automation:** The release process is automated via scripts and GitHub Actions. Always ensure your working directory is clean and up-to-date before running release scripts.
451
+ - **CI/CD:** All tests and builds are run in GitHub Actions before a release is published. If any step fails, the release will be blocked.
452
+ - **Generated Files:** Auto-generated files (e.g., ANTLR outputs, grammar-driven types) are ignored by git and should never be edited manually. Always use the provided scripts to regenerate them.
453
+ - **Documentation:** Keep all documentation referencing the release checklist up-to-date by linking to the PR template.
454
+
455
+ For more details, see the [release PR template](.github/PULL_REQUEST_TEMPLATE/release.md) and the automation scripts in `.github/scripts/`.
456
+
457
+ ### Error Handling Strategy
458
+
459
+ This project implements robust, layered error handling across all stages of the CRL pipeline:
460
+
461
+ | Layer | Responsibility | Error Type | How Errors Are Reported |
462
+ |-----------|-------------------------------------------------|--------------------|----------------------------------------|
463
+ | Lexer | Tokenization, valid characters/tokens | LexicalError | Collected by error listener, JSON |
464
+ | Parser | Syntax, structure, required/optional elements | ParserError | Collected by error listener, JSON |
465
+ | AST | Parse tree → AST, structural integrity | AstError | Collected by AST builder, JSON |
466
+ | Validator | Domain/semantic rules, cross-references, logic | ValidationError | Collected by validator, plain text |
467
+
468
+ - **Lexical and parser errors** are collected by their respective error listeners and reported as JSON strings with a `type` property (e.g., `"LexicalError"`, `"ParserError"`).
469
+ - **AST errors** are reported as `"AstError"` if the AST builder encounters a structural problem not caught by the parser.
470
+ - **Validation errors** are reported by the validator after AST construction.
471
+
472
+ #### Example Error Object
473
+
474
+ ```json
475
+ {
476
+ "type": "ParserError",
477
+ "line": 1,
478
+ "column": 51,
479
+ "message": "Syntax error: missing 'done' at '<EOF>'",
480
+ "details": {
481
+ "offendingSymbol": { "text": "<EOF>", ... }
482
+ }
483
+ }
484
+ ```
485
+
486
+ #### Clean Test Output: Suppressing Console Errors in Jest
487
+
488
+ To keep test output clean and focused on actual test failures, all `console.error` output is globally suppressed during tests. This is achieved by adding the following to `jest.setup.js`:
489
+
490
+ ```js
491
+ beforeAll(() => {
492
+ jest.spyOn(console, 'error').mockImplementation(() => {});
493
+ });
494
+ ```
495
+
496
+ And registering it in `jest.config.js`:
497
+
498
+ ```js
499
+ setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
500
+ ```
501
+
502
+ This ensures that error logs are visible during development and CLI runs, but do not clutter test output.
@@ -0,0 +1,45 @@
1
+ import { ParserRuleContext } from "antlr4ts/ParserRuleContext";
2
+ import { AbstractParseTreeVisitor } from "antlr4ts/tree/AbstractParseTreeVisitor";
3
+ import { CrlContext, DecisionStatementContext, DecisionBodyContext, WhenWithBodyContext, WhenSingleActionContext, NestedWhenBlockContext, BlockActionContext, BlockBodyContext, SingleActionStatementContext, DoStatementContext, UseStatementContext, TerminologyStatementContext, TerminologyValuesetContext, TerminologySystemCodeContext, ActivityStatementContext, ConceptStatementContext, InferredFromLineContext, DefinitionConceptContext, DefinitionLogicContext, InferredFromExpressionContext, InformalOrContext, InformalAndContext, InformalNotContext, ConceptAtomContext, GroupExpressionContext } from "../grammar/generated/antlr/CRLParser";
4
+ import { CRLParserVisitor } from "../grammar/generated/antlr/CRLParserVisitor";
5
+ import { CRLError } from "../types/errors";
6
+ import { ASTNode, CRL, Decision, DecisionBody, WhenBlock, BlockBody, SingleAction, ActionStatement, DoActivity, UseDecision, Terminology, TerminologyValueset, TerminologySystemCode, Activity, Concept, InferredFromDefinition, ConceptReference, InformalAnd, InformalOr, NotExpression, GroupExpression, InferredFromConcept, InferredFromExpression } from "./types";
7
+ type InformalNode = GroupExpression | ConceptReference | InformalAnd | NotExpression | InformalOr;
8
+ export declare class CRLAstBuilder extends AbstractParseTreeVisitor<ASTNode> implements CRLParserVisitor<ASTNode> {
9
+ private readonly errors;
10
+ protected defaultResult(): ASTNode;
11
+ getErrors(): CRLError[];
12
+ protected reportError(message: string, ctx: ParserRuleContext, details?: Record<string, unknown>): void;
13
+ visitCrl(ctx: CrlContext): CRL;
14
+ visitDecisionStatement(ctx: DecisionStatementContext): Decision;
15
+ visitDecisionBody(ctx: DecisionBodyContext): DecisionBody;
16
+ visitWhenWithBody(ctx: WhenWithBodyContext): WhenBlock;
17
+ visitWhenSingleAction(ctx: WhenSingleActionContext): WhenBlock;
18
+ visitNestedWhenBlock(ctx: NestedWhenBlockContext): WhenBlock;
19
+ visitBlockAction(ctx: BlockActionContext): ActionStatement;
20
+ visitBlockBody(ctx: BlockBodyContext): BlockBody;
21
+ visitSingleActionStatement(ctx: SingleActionStatementContext): SingleAction;
22
+ visitActionStatement(ctx: import("../grammar/generated/antlr/CRLParser").ActionStatementContext): ActionStatement;
23
+ visitDoStatement(ctx: DoStatementContext): DoActivity;
24
+ visitUseStatement(ctx: UseStatementContext): UseDecision;
25
+ visitTerminologyStatement(ctx: TerminologyStatementContext): Terminology;
26
+ visitTerminologyValueset(ctx: TerminologyValuesetContext): TerminologyValueset;
27
+ visitTerminologySystemCode(ctx: TerminologySystemCodeContext): TerminologySystemCode;
28
+ private parseWithClause;
29
+ private parseRationaleClause;
30
+ visitActivityStatement(ctx: ActivityStatementContext): Activity;
31
+ private parseConceptTypes;
32
+ private parseEvidence;
33
+ private parseConceptDefinition;
34
+ visitConceptStatement(ctx: ConceptStatementContext): Concept;
35
+ visitInferredFromLine(ctx: InferredFromLineContext): InferredFromDefinition;
36
+ visitDefinitionConcept(ctx: DefinitionConceptContext): InferredFromConcept;
37
+ visitDefinitionLogic(ctx: DefinitionLogicContext): GroupExpression;
38
+ visitInferredFromExpression(ctx: InferredFromExpressionContext): InformalNode;
39
+ visitInformalOr(ctx: InformalOrContext): InformalOr;
40
+ visitInformalAnd(ctx: InformalAndContext): InformalAnd | NotExpression | GroupExpression | ConceptReference;
41
+ visitInformalNot(ctx: InformalNotContext): InferredFromExpression;
42
+ visitConceptAtom(ctx: ConceptAtomContext): ConceptReference;
43
+ visitGroupExpression(ctx: GroupExpressionContext): GroupExpression;
44
+ }
45
+ export {};