Single-Pass S-Expression Parser
Problem statement
Parse one simplified S-expression in a single sequential pass through text. Preserve atoms and nested lists, and return a normalized expression or the first error position. Do not use split or pre-tokenize the whole input.
For this exercise, use this bounded grammar:
- An atom is a nonempty run of ASCII letters, digits, or
_ + - * /. - A list is
(, followed by zero or more expressions, followed by). - ASCII spaces, tabs, carriage returns and newlines may separate expressions and surround the one root expression. Parentheses also delimit atoms, so whitespace around a parenthesis is optional.
Normalize by preserving every atom verbatim, removing surrounding whitespace, and placing exactly one space between consecutive list elements. Empty lists normalize to ().
Stop at the first syntax error and return ERROR@i, where i is the zero-based input character index. Errors include an unsupported character, an unmatched closing parenthesis, an extra root expression, or missing input at the end. An incomplete expression at end-of-input reports text.length. An empty or whitespace-only input also reports the end position.
The grammar, normalization and error encoding are practice conventions completing the reported atom, parenthesized and recursive parsing parts. Complete parseSExpression.
Function
parseSExpression(text: String) → StringExamples
Example 1
text = " (* 2 (+ 3 4))\n"return = "(* 2 (+ 3 4))"The nested lists are preserved; surrounding whitespace is removed.
Example 2
text = "(a @ b)"return = "ERROR@3"The unsupported @ at index 3 terminates parsing before later characters.
Constraints
0 <= text.length <= 10000.- Input characters are printable ASCII or tab, carriage return and newline; unsupported printable characters may appear and must produce an error.
- Parenthesis nesting in any valid prefix is at most
200. - The input may be malformed. Process the input cursor forward and terminate at the first error.