Skip to content

Commit 9ae3155

Browse files
emit-portable: record token spans on CST nodes (S7a, #57)
Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 9ca4178 commit 9ae3155

4 files changed

Lines changed: 225 additions & 65 deletions

File tree

src/target-go.ts

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,7 @@ function predAltBody(branches: Step[][], firsts?: FirstSig[]): string {
408408

409409
function rdRule(r: RdRule): string {
410410
if (r.predictive) {
411-
const arm = (steps: Step[], i: number) => `\t${i === 0 ? 'if' : 'else if'} ${firstCond(r.altFirst[i], 't')} { if ${steps.map(stepCond).join(' && ')} { return finish(${J(r.cstName)}, sb, offAt(save)) } }`;
411+
const arm = (steps: Step[], i: number) => `\t${i === 0 ? 'if' : 'else if'} ${firstCond(r.altFirst[i], 't')} { if ${steps.map(stepCond).join(' && ')} { return finish(${J(r.cstName)}, sb, offAt(save), save) } }`;
412412
return `func parse${r.name}() int32 {
413413
\tsave := pos; sb := len(scratch); nb := len(nodes); kb := len(kids)
414414
\tt := peek(); if t == nil { return -1 }
@@ -418,7 +418,7 @@ ${r.alts.map(arm).join(' ')}
418418
}`;
419419
}
420420
const alt = (steps: Step[]) =>
421-
`\tif ${steps.map(stepCond).join(' && ')} { return finish(${J(r.cstName)}, sb, offAt(save)) }
421+
`\tif ${steps.map(stepCond).join(' && ')} { return finish(${J(r.cstName)}, sb, offAt(save), save) }
422422
\tpos = save; scratch = scratch[:sb]; nodes = nodes[:nb]; kids = kids[:kb]`;
423423
return `func parse${r.name}() int32 {
424424
\tsave := pos; sb := len(scratch); nb := len(nodes); kb := len(kids)
@@ -433,32 +433,32 @@ function prattRule(r: PrattRule, tpl: TplCfg | null): string {
433433
\t\tnode := matchTemplate()
434434
\t\tif node < 0 { return -1 }
435435
\t\tsb := len(scratch); scratch = append(scratch, node)
436-
\t\treturn finish(${J(r.cstName)}, sb, nodes[node].Offset)
436+
\t\treturn finish(${J(r.cstName)}, sb, nodes[node].Offset, nodes[node].TokStart)
437437
\t}\n`
438438
: '';
439439
const bin = r.binary.map((b) => `${J(b.op)}: {${b.lbp}, ${b.rbp}}`).join(', ');
440440
const pre = r.prefix.map((p) => `${J(p.op)}: ${p.rbp}`).join(', ');
441441
const atoms = r.nudToks.map((k) => `${J(k)}: true`).join(', ');
442442
const bracketNud = (b: Bracket) => `\tif t.Text == ${J(b.first)} {
443443
\t\tsave := pos; sb := len(scratch); nb := len(nodes); kb := len(kids)
444-
\t\tif ${b.steps.map(stepCond).join(' && ')} { return finish(${J(r.cstName)}, sb, t.Off) }
444+
\t\tif ${b.steps.map(stepCond).join(' && ')} { return finish(${J(r.cstName)}, sb, t.Off, save) }
445445
\t\tpos = save; scratch = scratch[:sb]; nodes = nodes[:nb]; kids = kids[:kb]
446446
\t}`;
447447
const ledArm = (b: Bracket, accessTail: boolean, lbp: number | null, sameLine: boolean, nll: string[] | null) => `\t\tif ${accessTail ? '!tailClosed && ' : ''}${lbp !== null ? `${lbp} > minBp && ` : ''}${sameLine ? '!t.Nl && ' : ''}${nll ? `!_inW([]string{${nll.map(J).join(', ')}}, headLeafText(left)) && ` : ''}!_suppressCur[${J(b.first)}] && t.Text == ${J(b.first)} {
448448
\t\t\tledSave := pos; sb := len(scratch); nb := len(nodes); kb := len(kids)
449449
\t\t\tscratch = append(scratch, left)
450-
\t\t\tif ${b.steps.map(stepCond).join(' && ')} { left = finish(${J(r.cstName)}, sb, nodes[left].Offset); continue }
450+
\t\t\tif ${b.steps.map(stepCond).join(' && ')} { left = finish(${J(r.cstName)}, sb, nodes[left].Offset, nodes[left].TokStart); continue }
451451
\t\t\tpos = ledSave; scratch = scratch[:sb]; nodes = nodes[:nb]; kids = kids[:kb]; break
452452
\t\t}`;
453453
const postfixArm = (tok: string) => {
454454
const tplPart = tpl && tok === tpl.token ? `
455455
\t\tif !tailClosed && t.Kind == "$templateHead" {
456456
\t\t\tnode := matchTemplate()
457-
\t\t\tif node >= 0 { sb := len(scratch); scratch = append(scratch, left, node); left = finish(${J(r.cstName)}, sb, nodes[left].Offset); continue }
457+
\t\t\tif node >= 0 { sb := len(scratch); scratch = append(scratch, left, node); left = finish(${J(r.cstName)}, sb, nodes[left].Offset, nodes[left].TokStart); continue }
458458
\t\t}` : '';
459459
return `\t\tif !tailClosed && t.Kind == ${J(tok)} {
460460
\t\t\tsb := len(scratch); scratch = append(scratch, left, mkLeaf(t.Kind, t.Off, t.End)); pos++
461-
\t\t\tleft = finish(${J(r.cstName)}, sb, nodes[left].Offset); continue
461+
\t\t\tleft = finish(${J(r.cstName)}, sb, nodes[left].Offset, nodes[left].TokStart); continue
462462
\t\t}${tplPart}`;
463463
};
464464
const post = r.postfix.map((p) => `${J(p.op)}: ${p.lbp}`).join(', ');
@@ -486,7 +486,7 @@ ${r.leds.map((b, i) => ledArm(b, r.ledAccessTail[i], r.ledLbp[i], r.ledSameLine[
486486
${r.postfixToks.map(postfixArm).join('\n')}
487487
\t\tif post, ok := ${r.name}POST[t.Text]; ok && !tailClosed && post > minBp {
488488
\t\t\tsb := len(scratch); scratch = append(scratch, left, mkLeaf("$operator", t.Off, t.End)); pos++; tailClosed = true
489-
\t\t\tleft = finish(${J(r.cstName)}, sb, nodes[left].Offset); continue
489+
\t\t\tleft = finish(${J(r.cstName)}, sb, nodes[left].Offset, nodes[left].TokStart); continue
490490
\t\t}
491491
\t\tinfo, ok := ${r.name}BIN[t.Text]
492492
\t\tif !ok || info.lbp <= minBp { break }
@@ -496,19 +496,19 @@ ${r.postfixToks.map(postfixArm).join('\n')}
496496
\t\trhs := ${r.name}bp(info.rbp)
497497
\t\tif rhs < 0 { pos = ledSave; scratch = scratch[:sb]; break }
498498
\t\tscratch = append(scratch, rhs)
499-
\t\tleft = finish(${J(r.cstName)}, sb, nodes[left].Offset)
499+
\t\tleft = finish(${J(r.cstName)}, sb, nodes[left].Offset, nodes[left].TokStart)
500500
\t}
501501
\treturn left
502502
}
503503
func ${r.name}nud(minBp int) int32 {
504504
\t_capped = false
505505
\tt := peek()
506506
\tif t == nil { return -1 }
507-
${r.nudCapped.map((c) => `\tif minBp < ${c.capBp} { save := pos; sb := len(scratch); nb := len(nodes); kb := len(kids); if ${c.steps.length ? c.steps.map(stepCond).join(' && ') : 'true'} { _capped = true; return finish(${J(r.cstName)}, sb, offAt(save)) }; pos = save; scratch = scratch[:sb]; nodes = nodes[:nb]; kids = kids[:kb] }`).join('\n')}
507+
${r.nudCapped.map((c) => `\tif minBp < ${c.capBp} { save := pos; sb := len(scratch); nb := len(nodes); kb := len(kids); if ${c.steps.length ? c.steps.map(stepCond).join(' && ') : 'true'} { _capped = true; return finish(${J(r.cstName)}, sb, offAt(save), save) }; pos = save; scratch = scratch[:sb]; nodes = nodes[:nb]; kids = kids[:kb] }`).join('\n')}
508508
\t_r := func() int32 { // non-capped: a sub-parse may leave _capped set; force it false after
509509
${tplNud}\tif ${r.name}ATOM[t.Kind] {
510-
\t\tsb := len(scratch); scratch = append(scratch, mkLeaf(t.Kind, t.Off, t.End)); pos++
511-
\t\treturn finish(${J(r.cstName)}, sb, t.Off)
510+
\t\tsb := len(scratch); ts := pos; scratch = append(scratch, mkLeaf(t.Kind, t.Off, t.End)); pos++
511+
\t\treturn finish(${J(r.cstName)}, sb, t.Off, ts)
512512
\t}
513513
${r.nudBrackets.map(bracketNud).join('\n')}
514514
\tif pbp, ok := ${r.name}PRE[t.Text]; ok {
@@ -517,9 +517,9 @@ ${r.nudBrackets.map(bracketNud).join('\n')}
517517
\t\toperand := ${r.name}bp(pbp)
518518
\t\tif operand < 0 { pos = save; scratch = scratch[:sb]; nodes = nodes[:nb]; kids = kids[:kb]; return -1 }
519519
\t\tscratch = append(scratch, operand)
520-
\t\treturn finish(${J(r.cstName)}, sb, t.Off)
520+
\t\treturn finish(${J(r.cstName)}, sb, t.Off, save)
521521
\t}
522-
${r.nudSeqs.map((seq) => `\t{ save := pos; sb := len(scratch); nb := len(nodes); kb := len(kids); if ${seq.length ? seq.map(stepCond).join(' && ') : 'true'} { return finish(${J(r.cstName)}, sb, offAt(save)) }; pos = save; scratch = scratch[:sb]; nodes = nodes[:nb]; kids = kids[:kb] }`).join('\n')}
522+
${r.nudSeqs.map((seq) => `\t{ save := pos; sb := len(scratch); nb := len(nodes); kb := len(kids); if ${seq.length ? seq.map(stepCond).join(' && ') : 'true'} { return finish(${J(r.cstName)}, sb, offAt(save), save) }; pos = save; scratch = scratch[:sb]; nodes = nodes[:nb]; kids = kids[:kb] }`).join('\n')}
523523
\treturn -1
524524
\t}()
525525
\t_capped = false
@@ -1104,7 +1104,7 @@ func Tokenize(src string) []Tok { return lex(src) }
11041104
\t\tif next.Kind == "$templateTail" { scratch = append(scratch, mkLeaf("$templateTail", next.Off, next.End)); pos++; break }
11051105
\t\tpos = save; scratch = scratch[:sb]; nodes = nodes[:nb]; kids = kids[:kb]; return -1
11061106
\t}
1107-
\treturn finish("$template", sb, t.Off)
1107+
\treturn finish("$template", sb, t.Off, save)
11081108
}
11091109
` : '';
11101110
return `// GENERATED by emit-portable.ts (goTarget) — parser LIBRARY for grammar "${ir.grammarName}".
@@ -1127,7 +1127,7 @@ type Tok struct {
11271127
type Node struct {
11281128
\tRule, TokenType string
11291129
\tIsLeaf bool
1130-
\tKidStart, KidCount, Offset, End int
1130+
\tKidStart, KidCount, Offset, End, TokStart, TokEnd int
11311131
}
11321132
type bp struct{ lbp, rbp int }
11331133
@@ -1149,18 +1149,19 @@ func peek() *Tok {
11491149
}
11501150
func offAt(i int) int { if i < len(toks) { return toks[i].Off }; return 0 }
11511151
func mkLeaf(ttype string, off, end int) int32 {
1152-
\tnodes = append(nodes, Node{TokenType: ttype, IsLeaf: true, Offset: off, End: end})
1152+
\tnodes = append(nodes, Node{TokenType: ttype, IsLeaf: true, Offset: off, End: end, TokStart: pos, TokEnd: pos + 1})
11531153
\treturn int32(len(nodes) - 1)
11541154
}
11551155
// Wrap the scratch entries [sb:] as one node's children (flattened into kids); truncate scratch.
1156-
func finish(rule string, sb, fallbackOff int) int32 {
1156+
// tokStart is the parse-consumption start (entry pos / leftmost operand), tokEnd is pos at finish.
1157+
func finish(rule string, sb, fallbackOff, tokStart int) int32 {
11571158
\tnn := len(scratch)
11581159
\tkidStart := len(kids)
11591160
\toff, end := fallbackOff, fallbackOff
11601161
\tif nn > sb { off = nodes[scratch[sb]].Offset; end = nodes[scratch[nn-1]].End }
11611162
\tkids = append(kids, scratch[sb:nn]...)
11621163
\tscratch = scratch[:sb]
1163-
\tnodes = append(nodes, Node{Rule: rule, KidStart: kidStart, KidCount: nn - sb, Offset: off, End: end})
1164+
\tnodes = append(nodes, Node{Rule: rule, KidStart: kidStart, KidCount: nn - sb, Offset: off, End: end, TokStart: tokStart, TokEnd: pos})
11641165
\treturn int32(len(nodes) - 1)
11651166
}
11661167
func matchLit(value, ttype string) bool {
@@ -1287,6 +1288,22 @@ func main() {
12871288
\t\tos.Stdout.WriteString(b.String())
12881289
\t\treturn
12891290
\t}
1291+
\tif len(os.Args) > 1 && os.Args[1] == "tok-spans" {
1292+
\t\troot := parse(tokenize(src))
1293+
\t\tif root < 0 || pos != len(toks) {
1294+
\t\t\tfmt.Fprintf(os.Stderr, "parse error (pos %d/%d)\\n", pos, len(toks))
1295+
\t\t\tos.Exit(1)
1296+
\t\t}
1297+
\t\tnd := &nodes[root]
1298+
\t\tfor i := 0; i < nd.KidCount; i++ {
1299+
\t\t\tk := &nodes[kids[nd.KidStart+i]]
1300+
\t\t\tname := k.Rule
1301+
\t\t\tif k.IsLeaf { name = k.TokenType }
1302+
\t\t\tfmt.Printf("%s\\t%d\\t%d\\n", name, k.TokStart, k.TokEnd)
1303+
\t\t}
1304+
\t\tfmt.Printf("total\\t0\\t%d\\n", pos)
1305+
\t\treturn
1306+
\t}
12901307
\troot := parse(tokenize(src))
12911308
\tif root < 0 || pos != len(toks) {
12921309
\t\tfmt.Fprintf(os.Stderr, "parse error (pos %d/%d)\\n", pos, len(toks))

0 commit comments

Comments
 (0)