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

Reply via email to