Skip to content

Commit

Permalink
Only move lookahead/context records when there are actual skipped nod…
Browse files Browse the repository at this point in the history
…es before them

FIX: Fix a tree corruption issue caused by lookahead records in some circumstances.

Closes lezer-parser/lezer#55
  • Loading branch information
marijnh committed Jul 27, 2024
1 parent 583e19e commit 4ce4abe
Showing 1 changed file with 19 additions and 12 deletions.
31 changes: 19 additions & 12 deletions src/stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ export class Stack {
let depth = action >> Action.ReduceDepthShift, type = action & Action.ValueMask
let {parser} = this.p

if (this.reducePos < this.pos - Lookahead.Margin) this.setLookAhead(this.pos)
let lookaheadRecord = this.reducePos < this.pos - Lookahead.Margin
if (lookaheadRecord) this.setLookAhead(this.pos)

let dPrec = parser.dynamicPrecedence(type)
if (dPrec) this.score += dPrec
Expand All @@ -100,7 +101,7 @@ export class Stack {
this.pushState(parser.getGoto(this.state, type, true), this.reducePos)
// Zero-depth reductions are a special case—they add stuff to
// the stack without popping anything off.
if (type < parser.minRepeatTerm) this.storeNode(type, this.reducePos, this.reducePos, 4, true)
if (type < parser.minRepeatTerm) this.storeNode(type, this.reducePos, this.reducePos, lookaheadRecord ? 8 : 4, true)
this.reduceContext(type, this.reducePos)
return
}
Expand Down Expand Up @@ -146,7 +147,7 @@ export class Stack {

// Shift a value into the buffer
/// @internal
storeNode(term: number, start: number, end: number, size = 4, isReduce = false) {
storeNode(term: number, start: number, end: number, size = 4, mustSink = false) {
if (term == Term.Err &&
(!this.stack.length || this.stack[this.stack.length - 1] < this.buffer.length + this.bufferBase)) {
// Try to omit/merge adjacent error nodes
Expand All @@ -161,18 +162,24 @@ export class Stack {
}
}

if (!isReduce || this.pos == end) { // Simple case, just append
if (!mustSink || this.pos == end) { // Simple case, just append
this.buffer.push(term, start, end, size)
} else { // There may be skipped nodes that have to be moved forward
let index = this.buffer.length
if (index > 0 && this.buffer[index - 4] != Term.Err) while (index > 0 && this.buffer[index - 2] > end) {
// Move this record forward
this.buffer[index] = this.buffer[index - 4]
this.buffer[index + 1] = this.buffer[index - 3]
this.buffer[index + 2] = this.buffer[index - 2]
this.buffer[index + 3] = this.buffer[index - 1]
index -= 4
if (size > 4) size -= 4
if (index > 0 && this.buffer[index - 4] != Term.Err) {
let mustMove = false
for (let scan = index; scan > 0 && this.buffer[scan - 2] > end; scan -= 4) {
if (this.buffer[scan - 1] >= 0) { mustMove = true; break }
}
if (mustMove) while (index > 0 && this.buffer[index - 2] > end) {
// Move this record forward
this.buffer[index] = this.buffer[index - 4]
this.buffer[index + 1] = this.buffer[index - 3]
this.buffer[index + 2] = this.buffer[index - 2]
this.buffer[index + 3] = this.buffer[index - 1]
index -= 4
if (size > 4) size -= 4
}
}
this.buffer[index] = term
this.buffer[index + 1] = start
Expand Down

0 comments on commit 4ce4abe

Please sign in to comment.