aboutsummaryrefslogtreecommitdiff
path: root/coffee/reader.coffee
blob: 83d24d235dc761d0bd59fca58a386f0a063d5e7e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
types = require "./types.coffee"
_symbol = types._symbol


class Reader
  constructor: (@tokens) -> @position = 0
  next: -> @tokens[@position++]
  peek: -> @tokens[@position]
  skip: ->
    @position++
    @

tokenize = (str) ->
    re = /[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"|;.*|[^\s\[\]{}('"`,;)]*)/g
    results = []
    while (match = re.exec(str)[1]) != ""
      continue if match[0] == ';'
      results.push(match)
    results

read_atom = (rdr) ->
  token = rdr.next()
  if token.match /^-?[0-9]+$/ then parseInt token,10
  else if token.match /^-?[0-9][0-9.]*$/ then parseFloat token,10
  else if token[0] == '"'
    token.slice(1, token.length-1)
      .replace(/\\"/g, '"')
      .replace(/\\n/g, "\n")
  else if token[0] == ':' then types._keyword(token[1..])
  else if token == "nil" then null
  else if token == "true" then true
  else if token == "false" then false
  else _symbol(token)

read_list = (rdr, start='(', end=')') ->
  ast = []
  token = rdr.next()
  throw new Error "expected '" + start + "'" if token != start
  while (token = rdr.peek()) != end
    throw new Error "expected '" + end + "', got EOF" if !token
    ast.push read_form rdr
  rdr.next()
  ast

read_vector = (rdr) ->
  types._vector(read_list(rdr, '[', ']')...)

read_hash_map = (rdr) ->
  types._hash_map(read_list(rdr, '{', '}')...)

read_form = (rdr) ->
  token = rdr.peek()
  switch token
    when '\'' then [_symbol('quote'), read_form(rdr.skip())]
    when '`'  then [_symbol('quasiquote'), read_form(rdr.skip())]
    when '~'  then [_symbol('unquote'), read_form(rdr.skip())]
    when '~@' then [_symbol('splice-unquote'), read_form(rdr.skip())]
    when '^'
      meta = read_form(rdr.skip())
      [_symbol('with-meta'), read_form(rdr), meta]
    when '@' then [_symbol('deref'), read_form(rdr.skip())]

    # list
    when ')' then throw new Error "unexpected ')'"
    when '(' then read_list(rdr)
    # vector
    when ']' then throw new Error "unexpected ']'"
    when '[' then read_vector(rdr)
    # hash-map
    when '}' then throw new Error "unexpected '}'"
    when '{' then read_hash_map(rdr)
    # atom
    else read_atom(rdr)


exports.BlankException = BlankException = (msg) -> null

exports.read_str = read_str = (str) ->
  tokens = tokenize(str)
  throw new BlankException() if tokens.length == 0
  read_form(new Reader(tokens))

#console.log read_str "(1 \"two\" three)"
#console.log read_str "[1 2 3]"
#console.log read_str '{"abc" 123 "def" 456}'

# vim: ts=2:sw=2