import nre, optional_t, strutils, types let tokenRE = re"""[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"|;.*|[^\s\[\]{}('"`,;)]*)""" intRE = re"-?[0-9]+$" type Blank* = object of Exception Reader = object tokens: seq[string] position: int proc next(r: var Reader): string = if r.position >= r.tokens.len: result = nil else: result = r.tokens[r.position] inc r.position proc peek(r: Reader): string = if r.position >= r.tokens.len: nil else: r.tokens[r.position] proc tokenize(str: string): seq[string] = result = @[] for match in str.findIter(tokenRE): if match.captures[0][0] != ';': result.add match.captures[0] proc read_form(r: var Reader): MalType proc read_seq(r: var Reader, fr, to: string): seq[MalType] = result = @[] var t = r.next if t != fr: raise newException(ValueError, "expected '" & fr & "'") t = r.peek while t != to: if t == nil: raise newException(ValueError, "expected '" & to & "', got EOF") result.add r.read_form t = r.peek discard r.next proc read_list(r: var Reader): MalType = result = list r.read_seq("(", ")") proc read_vector(r: var Reader): MalType = result = vector r.read_seq("[", "]") proc read_hash_map(r: var Reader): MalType = result = hash_map r.read_seq("{", "}") proc read_atom(r: var Reader): MalType = let t = r.next if t.match(intRE): number t.parseInt elif t[0] == '"': str t[1 ..