Skip to content

Commit d3e27aa

Browse files
committed
Handles updates
1 parent 95f6e2e commit d3e27aa

1 file changed

Lines changed: 123 additions & 8 deletions

File tree

  • .agents/skills/grammar-sync-update

.agents/skills/grammar-sync-update/SKILL.md

Lines changed: 123 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,37 +2,53 @@
22
name: grammar-sync-update
33
description: >
44
Use this skill when a grammar sync PR has landed (updating src/parser/antlr/) and a follow-up PR
5-
is needed to wire a new ES|QL command into the AST layer. Covers all 7 files that need changes
6-
and the exact patterns to follow.
5+
is needed to wire a new ES|QL command or update an existing one in the AST layer. Covers all files
6+
that need changes and the exact patterns to follow for both new and existing commands.
77
---
88

99
# Grammar Sync Update
1010

1111
## When to use
1212

13-
After the automated grammar sync bot merges a PR that adds a **new command**, run this skill to produce the follow-up wiring PR.
13+
After the automated grammar sync bot merges a PR, this skill covers two scenarios:
14+
15+
**Scenario A — New command** (7 files to change): A brand-new `xxxCommand` rule appeared in the grammar with no handler anywhere in the TypeScript layer.
16+
17+
**Scenario B — Existing command update** (1–3 files to change): An existing command's grammar rule gained a new option, new labeled field, or new sub-rule (e.g. `CHANGE_POINT` gained `BY`, `WHERE IN` gained subquery support).
1418

1519
**You do NOT need this skill when:**
1620
- The grammar sync PR only updated ANTLR-generated files with no new grammar rules (`.ts`, `.interp`, `.tokens` only) — those are handled automatically.
1721

1822
**You DO need this skill when:**
19-
- A new `xxxCommand` rule appears in `src/parser/antlr/esql_parser.g4` with no corresponding `fromXxxCommand()` in `cst_to_ast_converter.ts`.
20-
- The `esql_parser_listener.ts` has new `enterXxxCommand` / `exitXxxCommand` entries with no converter handler.
23+
- A new `xxxCommand` rule appears in `src/parser/antlr/esql_parser.g4` with no corresponding `fromXxxCommand()` in `cst_to_ast_converter.ts`. → **Use Scenario A below.**
24+
- The `esql_parser_listener.ts` has new `enterXxxCommand` / `exitXxxCommand` entries with no converter handler. → **Use Scenario A below.**
25+
- An existing command's grammar rule changed (new context method, new labeled alternative, new keyword option) and the converter no longer covers it. → **Use Scenario B below.**
2126

2227
## How to detect what changed
2328

2429
```bash
25-
# Find new command rules added in the grammar sync commit
30+
# 1. See the full grammar diff
31+
git diff HEAD~1 -- src/parser/antlr/esql_parser.g4
32+
33+
# 2. Identify new command rules (Scenario A)
2634
git diff HEAD~1 -- src/parser/antlr/esql_parser.g4 | grep "^+.*[Cc]ommand\b"
2735

28-
# Cross-check: listener entries without a converter method
36+
# 3. Cross-check: listener entries without a converter handler (Scenario A)
2937
grep "enter.*Command" src/parser/antlr/esql_parser_listener.ts | \
3038
sed 's/.*enter\([A-Za-z]*\)Command.*/\1/' | \
3139
while read cmd; do
3240
grep -q "from${cmd}Command" src/parser/core/cst_to_ast_converter.ts || echo "MISSING: $cmd"
3341
done
42+
43+
# 4. Identify changed rules for existing commands (Scenario B)
44+
# Look for new labeled alternatives (_newField), new keyword tokens (BY, WITH, AS),
45+
# or new sub-rule methods (ctx.newSubRule()) added to an existing command rule.
46+
git diff HEAD~1 -- src/parser/antlr/esql_parser.g4 | grep "^[+-]" | grep -v "^---\|^+++"
3447
```
3548

49+
If step 2/3 shows an entirely new command → **Scenario A**.
50+
If step 4 shows additions inside an existing command rule → **Scenario B**.
51+
3652
---
3753

3854
## Files to change (all 7, in order)
@@ -245,7 +261,7 @@ Reference test files: `src/parser/__tests__/user_agent.test.ts`, `src/parser/__t
245261

246262
---
247263

248-
## Checklist
264+
## Checklist — Scenario A (new command)
249265

250266
- [ ] `src/types.ts` — new interface + union updated
251267
- [ ] `src/ast/visitor/contexts.ts``XxxCommandVisitorContext` added
@@ -258,6 +274,105 @@ Reference test files: `src/parser/__tests__/user_agent.test.ts`, `src/parser/__t
258274
- [ ] `yarn build` passes (no TypeScript errors)
259275
- [ ] `yarn lint` passes
260276

277+
---
278+
279+
## Scenario B — Existing command update
280+
281+
The grammar changed an **existing** command's rule: it gained a new option keyword (`BY`, `WITH`, `AS`), a new labeled alternative (`_newField`), or a new sub-rule. The converter method already exists — you only need to extend it.
282+
283+
### Files to change
284+
285+
#### `src/parser/core/cst_to_ast_converter.ts`
286+
287+
Find the existing `fromXxxCommand()` method and add handling for what's new.
288+
289+
**New keyword option** (e.g. `CHANGE_POINT … BY field1, field2`):
290+
```ts
291+
if (ctx.BY()) {
292+
const args = (ctx._groupings ?? [])
293+
.filter((e) => !e.exception)
294+
.map((e) => this.fromBooleanExpressionToExpressionOrUnknown(e));
295+
296+
const byOption = this.toByOption(ctx, args);
297+
if (byOption) {
298+
command.args.push(byOption);
299+
command.incomplete ||= byOption.incomplete;
300+
}
301+
}
302+
```
303+
304+
**New labeled field** (e.g. `_targetField` added to an existing rule):
305+
```ts
306+
if (ctx._targetField) {
307+
const col = this.toColumn(ctx._targetField);
308+
command.targetField = col; // only if types.ts interface has this field
309+
command.args.push(col);
310+
}
311+
```
312+
313+
**New sub-rule variant** (e.g. `WHERE IN` gained a `LogicalInSubquery` alternative):
314+
```ts
315+
// In the existing expression handler, add a branch before the current logic:
316+
if (ctx instanceof cst.LogicalInSubqueryContext) {
317+
return this.fromLogicalInSubquery(ctx);
318+
}
319+
// then add the new private method:
320+
private fromLogicalInSubquery(ctx: cst.LogicalInSubqueryContext): ast.ESQLAstExpression {
321+
const left = this.fromLogicalInLeft(ctx.valueExpression());
322+
const right = this.fromSubquery(ctx.subquery());
323+
return this.toLogicalInFunction(ctx, left, right, ctx.stop?.stop ?? right.location.max);
324+
}
325+
```
326+
327+
#### `src/types.ts` — only if the command gains new named fields
328+
329+
If the existing interface (e.g. `ESQLAstChangePointCommand`) needs to expose the new option as a typed field, add it:
330+
```ts
331+
export interface ESQLAstChangePointCommand extends ESQLCommand<'change_point'> {
332+
value: ESQLColumn;
333+
key?: ESQLColumn;
334+
target?: { type: ESQLColumn; pvalue: ESQLColumn };
335+
// ← add new field only if consumers need typed access
336+
}
337+
```
338+
339+
Skip this if the new option is only accessible via generic `args` — that's fine for options that don't need first-class API access.
340+
341+
#### Tests
342+
343+
Always update or add tests. For an existing command update, add to the existing test file (`src/parser/__tests__/xxx.test.ts`) rather than creating a new one.
344+
345+
Cover:
346+
1. The new option/field parses correctly
347+
2. The AST shape is correct (`toMatchObject`)
348+
3. The pretty-printer round-trips the new syntax
349+
4. Walker traversal visits any new nodes (if the new option introduces new AST nodes)
350+
351+
**Real example — `CHANGE_POINT BY`** (PR #104):
352+
- Added 21 lines to `change_point.test.ts`
353+
- Added 14 lines to `fromChangePointCommand()` in `cst_to_ast_converter.ts`
354+
- No `types.ts` change (BY option fits in generic `args`)
355+
- No visitor changes
356+
357+
**Real example — `WHERE IN` subquery** (PR #107):
358+
- Refactored `fromLogicalIn()` in `cst_to_ast_converter.ts` into smaller methods + added subquery branch
359+
- Added tests to `src/parser/__tests__/function.test.ts` and `src/ast/walker/__tests__/walker.test.ts`
360+
- Added pretty-printer tests to 4 existing test files
361+
- No `types.ts` or visitor changes (subquery uses existing `ESQLParens` type)
362+
363+
### Checklist — Scenario B (existing command update)
364+
365+
- [ ] `src/parser/core/cst_to_ast_converter.ts` — existing handler extended
366+
- [ ] `src/types.ts` — interface updated only if new named fields are needed
367+
- [ ] Tests added/extended in existing test file
368+
- [ ] Pretty-printer tests added if new syntax affects printing
369+
- [ ] Walker tests added if new syntax introduces new traversable nodes
370+
- [ ] `yarn test` passes
371+
- [ ] `yarn build` passes
372+
- [ ] `yarn lint` passes
373+
374+
---
375+
261376
## Reference: simple vs complex existing commands
262377

263378
| Command | Interface | Visitor context | Test file |

0 commit comments

Comments
 (0)