Skip to content

Commit 4686f22

Browse files
reidspencerclaude
andcommitted
Add BAST implementation plan document
Documents the design decisions and implementation steps for integrating BAST caching into the RIDDL parser. Most of this plan has been executed. Key decisions documented: - Move BAST code into language module (done) - Integrate BAST check into TopLevelParser (done) - BASTWriter as Pass vs direct call - Fallback behavior for corrupt BAST files Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent c393dae commit 4686f22

1 file changed

Lines changed: 128 additions & 0 deletions

File tree

bast/IMPLEMENTATION_PLAN.md

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
# Revised BAST Implementation Plan
2+
3+
## Goal
4+
Make BAST caching fundamental to RIDDL parsing, like Python's .pyc files. Users shouldn't need to know about BAST - it should "just work".
5+
6+
## Key Design Decisions (For Review)
7+
8+
### 1. Move BAST Code into Language Module
9+
- Move `BASTReader.scala`, `BASTWriter.scala`, `ByteBufferReader.scala`, `ByteBufferWriter.scala`, `VarIntCodec.scala`, `BinaryFormat.scala`, and `package.scala` constants into `language/shared/src/main/scala/com/ossuminc/riddl/language/bast/`
10+
- Keep `bast/` directory only for `NOTEBOOK.md` documentation
11+
- This eliminates circular dependency concerns since everything is in one module
12+
13+
### 2. Integrate BAST Check into TopLevelParser
14+
When `TopLevelParser.parseInput()` or `TopLevelParser.parseURL()` is called:
15+
1. If URL is a `.bast` file → load directly via BASTReader
16+
2. If URL is a `.riddl` file:
17+
- Check for sibling `.bast` file (same name, different extension)
18+
- If `.bast` exists AND is newer than `.riddl` → load from `.bast`
19+
- Otherwise → parse `.riddl` normally
20+
3. Optionally generate `.bast` after successful parse (controlled by `CommonOptions.autoGenerateBAST`)
21+
22+
### 3. Where to Add the Check
23+
**Option A**: In `TopLevelParser.parseInput()` (recommended)
24+
- Single entry point for all parsing
25+
- Transparent to all callers
26+
- Cleanest integration
27+
28+
**Option B**: In `RiddlParserInput.fromURL()`
29+
- Earlier in the pipeline
30+
- Would need to return different type for BAST vs text input
31+
32+
**Recommendation**: Option A - modify TopLevelParser
33+
34+
### 4. BASTWriter as Pass vs Direct Call
35+
**Option A**: Keep BASTWriter as a Pass (current)
36+
- Runs after validation passes
37+
- More heavyweight but ensures AST is valid before caching
38+
39+
**Option B**: Direct serialization after parsing
40+
- Lighter weight
41+
- Cache even if validation fails (faster iteration)
42+
43+
**Recommendation**: Option B for auto-generation (cache parse result, not validation result)
44+
45+
## Implementation Steps
46+
47+
### Step 1: Move Files
48+
Move from `bast/shared/src/main/scala/com/ossuminc/riddl/bast/` to `language/shared/src/main/scala/com/ossuminc/riddl/language/bast/`:
49+
- `package.scala` (constants, node type tags)
50+
- `BinaryFormat.scala`
51+
- `VarIntCodec.scala`
52+
- `ByteBufferReader.scala`
53+
- `ByteBufferWriter.scala`
54+
- `BASTReader.scala`
55+
- `BASTWriter.scala`
56+
57+
Update package declarations from `com.ossuminc.riddl.bast` to `com.ossuminc.riddl.language.bast`
58+
59+
### Step 2: Update build.sbt
60+
- Remove `bast` module from cross-project definitions
61+
- Remove `bast` dependencies from other modules
62+
- Keep `bast/` directory but don't compile it as a module
63+
64+
### Step 3: Modify TopLevelParser
65+
Add BAST-aware parsing logic:
66+
```scala
67+
def parseInput(input: RiddlParserInput, options: CommonOptions): Either[Messages, Root] = {
68+
// Check for BAST cache first
69+
input.origin match {
70+
case url if url.path.endsWith(".bast") =>
71+
// Direct BAST load
72+
loadFromBAST(url)
73+
case url if url.path.endsWith(".riddl") =>
74+
// Check for sibling .bast
75+
val bastUrl = url.withExtension(".bast")
76+
if shouldUseBastCache(url, bastUrl) then
77+
loadFromBAST(bastUrl)
78+
else
79+
val result = parseRiddl(input)
80+
if options.autoGenerateBAST then
81+
generateBAST(result, bastUrl)
82+
result
83+
case _ =>
84+
parseRiddl(input)
85+
}
86+
}
87+
```
88+
89+
### Step 4: Move Tests
90+
Move from `bast/jvm/src/test/scala/` to `language/jvm-native/src/test/scala/com/ossuminc/riddl/language/bast/`:
91+
- `BASTWriterSpec.scala`
92+
- `BASTRoundTripTest.scala`
93+
- `BASTPerformanceTest.scala`
94+
- `BASTLoaderTest.scala`
95+
96+
### Step 5: Update Commands Module
97+
- Update imports in `BastGenCommand.scala` to use new package location
98+
- `bast-gen` command still works but uses `language.bast` package
99+
100+
### Step 6: Clean Up
101+
- Remove empty `bast/shared/`, `bast/jvm/`, etc. directories
102+
- Keep only `bast/NOTEBOOK.md`
103+
- Update `CLAUDE.md` to reflect new structure
104+
105+
## Decisions Made
106+
107+
**Include files**: NO - includes are always .riddl files and can occur in any container (not just Root/Domain), so BAST caching them would lead to ambiguities. Only top-level files get BAST caching.
108+
109+
## Remaining Questions
110+
111+
1. **Where to add the BAST check?**
112+
- **Option A (my recommendation)**: `TopLevelParser.parseInput()` - single entry point, transparent to all callers
113+
- **Option B**: `RiddlParserInput.fromURL()` - earlier in pipeline
114+
115+
2. **BAST generation timing** - when `autoGenerateBAST` is true:
116+
- **Option A**: After validation passes (ensures valid AST before caching)
117+
- **Option B (my recommendation)**: After parsing only (lighter weight, cache even invalid AST for faster iteration)
118+
119+
3. **Fallback behavior** - If BAST load fails (corrupt file):
120+
- **a) (my recommendation)**: Silently fall back to parsing .riddl
121+
- b) Warn and fall back
122+
- c) Error out
123+
124+
## Files to Keep in bast/ Directory
125+
- `NOTEBOOK.md` - Engineering notebook documentation
126+
- `KNOWN_ISSUES.md` - If it exists
127+
128+
Everything else moves to `language/shared/.../language/bast/`

0 commit comments

Comments
 (0)