// Copyright 2026 moonpddl contributors
//
// SPDX-License-Identifier: MIT
///|
/// Maximum S-expression nesting accepted by the generic reader.
const MAX_SEXP_DEPTH : Int32 = 1024
///|
/// Generic S-expression node. The semantic parser consumes this tree, which
/// keeps parenthesis handling and line/column bookkeeping in one place.
priv enum Sexp {
SAtom(String, Position)
SVar(String, Position)
SList(Array[Sexp], Position)
}
///|
fn parse_sexps(tokens : Array[Token]) -> Array[Sexp] raise PddlError {
let result : Array[Sexp] = []
let mut i = 0
while i < tokens.length() {
let (node, next) = parse_sexp_at(tokens, i, 0)
result.push(node)
i = next
}
result
}
///|
fn parse_sexp_at(
tokens : Array[Token],
start : Int32,
depth : Int32,
) -> (Sexp, Int32) raise PddlError {
let tok = tokens[start]
match tok.kind {
LParen => {
if depth >= MAX_SEXP_DEPTH {
raise syntax_error(
tok.pos,
"S-expression nesting exceeds the limit of \{MAX_SEXP_DEPTH}",
)
}
let items : Array[Sexp] = []
let mut i = start + 1
let mut closed = false
while i < tokens.length() {
match tokens[i].kind {
RParen => {
closed = true
i += 1
break
}
_ => {
let (child, next) = parse_sexp_at(tokens, i, depth + 1)
items.push(child)
i = next
}
}
}
if !closed {
raise syntax_error(tok.pos, "unclosed '('")
}
(SList(items, tok.pos), i)
}
RParen => raise syntax_error(tok.pos, "unexpected ')'")
Sym => (SAtom(tok.text, tok.pos), start + 1)
VarTok => (SVar(tok.text, tok.pos), start + 1)
}
}