aboutsummaryrefslogtreecommitdiff
path: root/coffee/reader.coffee
diff options
context:
space:
mode:
Diffstat (limited to 'coffee/reader.coffee')
-rw-r--r--coffee/reader.coffee87
1 files changed, 87 insertions, 0 deletions
diff --git a/coffee/reader.coffee b/coffee/reader.coffee
new file mode 100644
index 0000000..83d24d2
--- /dev/null
+++ b/coffee/reader.coffee
@@ -0,0 +1,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