This is the pattern I usually go for for dead simple parsers. Here is an
example for an extremely limited version of S-expressions with only lists,
integers and atoms:
type
SexpKind = enum
List, Integer, Atom
Sexp = object
case kind: SexpKind
of List: elements: seq[Sexp]
of Integer: integer: int
of Atom: atom: string
proc parseInteger(s: string, i: var int): Sexp =
result = Sexp(kind: Integer, integer: 0)
while i < s.len:
let c = s[i]
case c
of '0'..'9':
result.integer = result.integer * 10 + (c.int - '0'.int)
else:
dec i
return
inc i
proc parseAtom(s: string, i: var int): Sexp =
result = Sexp(kind: Atom, atom: "")
while i < s.len:
let c = s[i]
case c
of 'a'..'z', 'A'..'Z', '_', '-':
result.atom.add(c)
else:
dec i
return
inc i
proc parseList(s: string, i: var int): Sexp =
result = Sexp(kind: List, elements: @[])
# consume (
if s[i] != '(': assert false
inc i
while i < s.len:
let c = s[i]
case c
of '0'..'9':
result.elements.add(parseInteger(s, i))
of 'a'..'z', 'A'..'Z', '_', '-':
result.elements.add(parseAtom(s, i))
of '(':
result.elements.add(parseList(s, i))
of ')':
return
else: discard
inc i
proc parseSexp(s: string): seq[Sexp] =
result = @[]
var i = 0
while i < s.len:
let c = s[i]
case c
of '0'..'9':
result.add(parseInteger(s, i))
of 'a'..'z', 'A'..'Z', '_', '-':
result.add(parseAtom(s, i))
of '(':
result.add(parseList(s, i))
else: discard
inc i
echo parseSexp("(foo 1 (bar 2 (3)) 4 ())")
Run