fix(parser): bound folding depth and macro expansion work

Assisted-by: GLM 5.3
This commit is contained in:
2026-10-02 00:40:20 +02:00
parent 6570709226
commit ca887d3927
4 changed files with 343 additions and 78 deletions
+29 -18
View File
@@ -16,12 +16,21 @@ import (
"sourcedock.dev/petrbalvin/gasm-sdk/token"
)
// maxFoldDepth bounds the nesting the folder follows, counting parenthesis
// groups and unary signs alike. Real expressions nest a handful deep, and
// beyond the bound the expression simply fails to fold, which lands it in
// the ordinary operand paths. Without a bound, a hostile file parks the
// folder millions of frames down the mutual recursion of foldAdd, foldMul
// and foldFactor, and the goroutine stack overflows with a fatal error no
// caller can recover from.
const maxFoldDepth = 1000
// foldExpr evaluates the constant integer expression at the head of ts and
// returns its value together with the unconsumed tokens. ok is false when
// the tokens do not form an expression, which is the callers' signal to use
// the ordinary parsing paths.
func foldExpr(ts []token.Token) (val int64, rest []token.Token, ok bool) {
v, rest, ok := foldAdd(ts)
v, rest, ok := foldAdd(ts, 0)
if !ok {
return 0, ts, false
}
@@ -30,8 +39,8 @@ func foldExpr(ts []token.Token) (val int64, rest []token.Token, ok bool) {
// foldAdd parses addition-level expressions: +, - and | bind loosest, the
// Plan 9 convention that makes x<<1|3 read as (x<<1)|3.
func foldAdd(ts []token.Token) (int64, []token.Token, bool) {
v, rest, ok := foldMul(ts)
func foldAdd(ts []token.Token, depth int) (int64, []token.Token, bool) {
v, rest, ok := foldMul(ts, depth)
if !ok {
return 0, ts, false
}
@@ -40,7 +49,7 @@ func foldAdd(ts []token.Token) (int64, []token.Token, bool) {
if kind != token.Plus && kind != token.Minus && kind != token.Pipe {
return v, rest, true
}
w, r2, ok := foldMul(rest[1:])
w, r2, ok := foldMul(rest[1:], depth)
if !ok {
return v, rest, true
}
@@ -59,43 +68,43 @@ func foldAdd(ts []token.Token) (int64, []token.Token, bool) {
// foldMul parses multiplication-level expressions: *, / and the bit
// operators &, << and >>.
func foldMul(ts []token.Token) (int64, []token.Token, bool) {
v, rest, ok := foldFactor(ts)
func foldMul(ts []token.Token, depth int) (int64, []token.Token, bool) {
v, rest, ok := foldFactor(ts, depth)
if !ok {
return 0, ts, false
}
for len(rest) > 0 {
switch rest[0].Kind {
case token.Star:
w, r2, ok := foldFactor(rest[1:])
w, r2, ok := foldFactor(rest[1:], depth)
if !ok {
return v, rest, true
}
v *= w
rest = r2
case token.Slash:
w, r2, ok := foldFactor(rest[1:])
w, r2, ok := foldFactor(rest[1:], depth)
if !ok || w == 0 {
return v, rest, true
}
v /= w
rest = r2
case token.Ampersand:
w, r2, ok := foldFactor(rest[1:])
w, r2, ok := foldFactor(rest[1:], depth)
if !ok {
return v, rest, true
}
v &= w
rest = r2
case token.LShift:
w, r2, ok := foldFactor(rest[1:])
w, r2, ok := foldFactor(rest[1:], depth)
if !ok || w < 0 || w >= 64 {
return v, rest, true
}
v <<= uint(w)
rest = r2
case token.RShift:
w, r2, ok := foldFactor(rest[1:])
w, r2, ok := foldFactor(rest[1:], depth)
if !ok || w < 0 || w >= 64 {
return v, rest, true
}
@@ -109,9 +118,11 @@ func foldMul(ts []token.Token) (int64, []token.Token, bool) {
}
// foldFactor parses a number, a parenthesised expression, or a unary sign
// or complement.
func foldFactor(ts []token.Token) (int64, []token.Token, bool) {
if len(ts) == 0 {
// or complement. Each parenthesis group and each unary operator costs one
// depth level, so nesting beyond maxFoldDepth fails here instead of growing
// the stack without end.
func foldFactor(ts []token.Token, depth int) (int64, []token.Token, bool) {
if len(ts) == 0 || depth > maxFoldDepth {
return 0, ts, false
}
switch ts[0].Kind {
@@ -122,21 +133,21 @@ func foldFactor(ts []token.Token) (int64, []token.Token, bool) {
}
return v, ts[1:], true
case token.LParen:
v, rest, ok := foldAdd(ts[1:])
v, rest, ok := foldAdd(ts[1:], depth+1)
if !ok || len(rest) == 0 || rest[0].Kind != token.RParen {
return 0, ts, false
}
return v, rest[1:], true
case token.Minus:
v, rest, ok := foldFactor(ts[1:])
v, rest, ok := foldFactor(ts[1:], depth+1)
if !ok {
return 0, ts, false
}
return -v, rest, true
case token.Plus:
return foldFactor(ts[1:])
return foldFactor(ts[1:], depth+1)
case token.Tilde:
v, rest, ok := foldFactor(ts[1:])
v, rest, ok := foldFactor(ts[1:], depth+1)
if !ok {
return 0, ts, false
}