aboutsummaryrefslogtreecommitdiff
path: root/coffee
diff options
context:
space:
mode:
authorJoel Martin <github@martintribe.org>2014-12-18 20:33:49 -0600
committerJoel Martin <github@martintribe.org>2015-01-09 16:16:50 -0600
commitb8ee29b22fbaa7a01f2754b4d6dd9af52e02017c (patch)
treef4d977ed220e9a3f665cfbf4f68770a81e4c2095 /coffee
parentaaba249304b184e12e2445ab22d66df1f39a51a5 (diff)
downloadmal-b8ee29b22fbaa7a01f2754b4d6dd9af52e02017c.tar.gz
mal-b8ee29b22fbaa7a01f2754b4d6dd9af52e02017c.zip
All: add keywords.
Also, fix nth and count to match cloure.
Diffstat (limited to 'coffee')
-rw-r--r--coffee/core.coffee7
-rw-r--r--coffee/env.coffee15
-rw-r--r--coffee/printer.coffee1
-rw-r--r--coffee/reader.coffee9
-rw-r--r--coffee/step3_env.coffee14
-rw-r--r--coffee/step4_if_fn_do.coffee8
-rw-r--r--coffee/step5_tco.coffee8
-rw-r--r--coffee/step6_file.coffee14
-rw-r--r--coffee/step7_quote.coffee14
-rw-r--r--coffee/step8_macros.coffee20
-rw-r--r--coffee/step9_try.coffee22
-rw-r--r--coffee/stepA_interop.coffee20
-rw-r--r--coffee/types.coffee9
13 files changed, 88 insertions, 73 deletions
diff --git a/coffee/core.coffee b/coffee/core.coffee
index d01e76f..8bb6958 100644
--- a/coffee/core.coffee
+++ b/coffee/core.coffee
@@ -32,6 +32,8 @@ exports.ns = {
'false?': types._false_Q,
'symbol': types._symbol,
'symbol?': types._symbol_Q,
+ 'keyword': types._keyword,
+ 'keyword?': types._keyword_Q,
'pr-str': (a...) -> a.map((exp) -> _pr_str(exp,true)).join(" "),
'str': (a...) -> a.map((exp) -> _pr_str(exp,false)).join(""),
@@ -66,11 +68,12 @@ exports.ns = {
'sequential?': types._sequential_Q,
'cons': (a,b) -> [a].concat(b),
'concat': (a=[],b...) -> a.concat(b...),
- 'nth': (a,b) -> if a.length > b then a[b] else null,
+ 'nth': (a,b) -> if a.length > b then a[b] else
+ throw new Error "nth: index out of bounds",
'first': (a) -> if a.length > 0 then a[0] else null,
'rest': (a) -> a[1..],
'empty?': (a) -> a.length == 0,
- 'count': (a) -> a.length,
+ 'count': (a) -> if a == null then 0 else a.length,
'apply': (a,b...) -> a(b[0..-2].concat(b[b.length-1])...),
'map': (a,b) -> b.map((x) -> a(x)),
'conj': conj,
diff --git a/coffee/env.coffee b/coffee/env.coffee
index 667e467..80fbf12 100644
--- a/coffee/env.coffee
+++ b/coffee/env.coffee
@@ -12,13 +12,20 @@ exports.Env = class Env
else
@data[b.name] = exprs[i]
find: (key) ->
- if key of @data then @
+ if not types._symbol_Q(key)
+ throw new Error("env.find key must be symbol")
+ if key.name of @data then @
else if @outer then @outer.find(key)
else null
- set: (key, value) -> @data[key] = value
+ set: (key, value) ->
+ if not types._symbol_Q(key)
+ throw new Error("env.set key must be symbol")
+ @data[key.name] = value
get: (key) ->
+ if not types._symbol_Q(key)
+ throw new Error("env.get key must be symbol")
env = @find(key)
- throw new Error("'" + key + "' not found") if !env
- env.data[key]
+ throw new Error("'" + key.name + "' not found") if !env
+ env.data[key.name]
# vim: ts=2:sw=2
diff --git a/coffee/printer.coffee b/coffee/printer.coffee
index 7844416..9f56e2e 100644
--- a/coffee/printer.coffee
+++ b/coffee/printer.coffee
@@ -16,6 +16,7 @@ exports._pr_str = _pr_str = (obj, print_readably=true) ->
.replace(/"/g, '\\"')
.replace(/\n/g, '\\n')) + '"'
else obj
+ when 'keyword' then ":" + obj.slice(1)
when 'symbol' then obj.name
when 'nil' then 'nil'
when 'atom' then "(atom " + _pr_str(obj.val,_r) + ")"
diff --git a/coffee/reader.coffee b/coffee/reader.coffee
index 5882b43..83d24d2 100644
--- a/coffee/reader.coffee
+++ b/coffee/reader.coffee
@@ -1,7 +1,5 @@
types = require "./types.coffee"
-[_symbol, _vector, _hash_map] = [types._symbol,
- types._vector,
- types._hash_map]
+_symbol = types._symbol
class Reader
@@ -28,6 +26,7 @@ read_atom = (rdr) ->
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
@@ -44,10 +43,10 @@ read_list = (rdr, start='(', end=')') ->
ast
read_vector = (rdr) ->
- _vector(read_list(rdr, '[', ']')...)
+ types._vector(read_list(rdr, '[', ']')...)
read_hash_map = (rdr) ->
- _hash_map(read_list(rdr, '{', '}')...)
+ types._hash_map(read_list(rdr, '{', '}')...)
read_form = (rdr) ->
token = rdr.peek()
diff --git a/coffee/step3_env.coffee b/coffee/step3_env.coffee
index a5398e7..1446197 100644
--- a/coffee/step3_env.coffee
+++ b/coffee/step3_env.coffee
@@ -9,7 +9,7 @@ READ = (str) -> reader.read_str str
# eval
eval_ast = (ast, env) ->
- if types._symbol_Q(ast) then env.get ast.name
+ if types._symbol_Q(ast) then env.get ast
else if types._list_Q(ast) then ast.map((a) -> EVAL(a, env))
else if types._vector_Q(ast)
types._vector(ast.map((a) -> EVAL(a, env))...)
@@ -27,11 +27,11 @@ EVAL = (ast, env) ->
[a0, a1, a2, a3] = ast
switch a0.name
when "def!"
- env.set(a1.name, EVAL(a2, env))
+ env.set(a1, EVAL(a2, env))
when "let*"
let_env = new Env(env)
for k,i in a1 when i %% 2 == 0
- let_env.set(a1[i].name, EVAL(a1[i+1], let_env))
+ let_env.set(a1[i], EVAL(a1[i+1], let_env))
EVAL(a2, let_env)
else
[f, args...] = eval_ast ast, env
@@ -45,10 +45,10 @@ PRINT = (exp) -> printer._pr_str exp, true
repl_env = new Env()
rep = (str) -> PRINT(EVAL(READ(str), repl_env))
-repl_env.set "+", (a,b) -> a+b
-repl_env.set "-", (a,b) -> a-b
-repl_env.set "*", (a,b) -> a*b
-repl_env.set "/", (a,b) -> a/b
+repl_env.set types._symbol("+"), (a,b) -> a+b
+repl_env.set types._symbol("-"), (a,b) -> a-b
+repl_env.set types._symbol("*"), (a,b) -> a*b
+repl_env.set types._symbol("/"), (a,b) -> a/b
# repl loop
while (line = readline.readline("user> ")) != null
diff --git a/coffee/step4_if_fn_do.coffee b/coffee/step4_if_fn_do.coffee
index 037b58a..ef33478 100644
--- a/coffee/step4_if_fn_do.coffee
+++ b/coffee/step4_if_fn_do.coffee
@@ -10,7 +10,7 @@ READ = (str) -> reader.read_str str
# eval
eval_ast = (ast, env) ->
- if types._symbol_Q(ast) then env.get ast.name
+ if types._symbol_Q(ast) then env.get ast
else if types._list_Q(ast) then ast.map((a) -> EVAL(a, env))
else if types._vector_Q(ast)
types._vector(ast.map((a) -> EVAL(a, env))...)
@@ -28,11 +28,11 @@ EVAL = (ast, env) ->
[a0, a1, a2, a3] = ast
switch a0.name
when "def!"
- env.set(a1.name, EVAL(a2, env))
+ env.set(a1, EVAL(a2, env))
when "let*"
let_env = new Env(env)
for k,i in a1 when i %% 2 == 0
- let_env.set(a1[i].name, EVAL(a1[i+1], let_env))
+ let_env.set(a1[i], EVAL(a1[i+1], let_env))
EVAL(a2, let_env)
when "do"
el = eval_ast(ast[1..], env)
@@ -58,7 +58,7 @@ repl_env = new Env()
rep = (str) -> PRINT(EVAL(READ(str), repl_env))
# core.coffee: defined using CoffeeScript
-repl_env.set k, v for k,v of core.ns
+repl_env.set types._symbol(k), v for k,v of core.ns
# core.mal: defined using the language itself
rep("(def! not (fn* (a) (if a false true)))");
diff --git a/coffee/step5_tco.coffee b/coffee/step5_tco.coffee
index 4003e79..fa5aced 100644
--- a/coffee/step5_tco.coffee
+++ b/coffee/step5_tco.coffee
@@ -10,7 +10,7 @@ READ = (str) -> reader.read_str str
# eval
eval_ast = (ast, env) ->
- if types._symbol_Q(ast) then env.get ast.name
+ if types._symbol_Q(ast) then env.get ast
else if types._list_Q(ast) then ast.map((a) -> EVAL(a, env))
else if types._vector_Q(ast)
types._vector(ast.map((a) -> EVAL(a, env))...)
@@ -29,11 +29,11 @@ EVAL = (ast, env) ->
[a0, a1, a2, a3] = ast
switch a0.name
when "def!"
- return env.set(a1.name, EVAL(a2, env))
+ return env.set(a1, EVAL(a2, env))
when "let*"
let_env = new Env(env)
for k,i in a1 when i %% 2 == 0
- let_env.set(a1[i].name, EVAL(a1[i+1], let_env))
+ let_env.set(a1[i], EVAL(a1[i+1], let_env))
ast = a2
env = let_env
when "do"
@@ -64,7 +64,7 @@ repl_env = new Env()
rep = (str) -> PRINT(EVAL(READ(str), repl_env))
# core.coffee: defined using CoffeeScript
-repl_env.set k, v for k,v of core.ns
+repl_env.set types._symbol(k), v for k,v of core.ns
# core.mal: defined using the language itself
rep("(def! not (fn* (a) (if a false true)))");
diff --git a/coffee/step6_file.coffee b/coffee/step6_file.coffee
index 449b03e..feafc63 100644
--- a/coffee/step6_file.coffee
+++ b/coffee/step6_file.coffee
@@ -10,7 +10,7 @@ READ = (str) -> reader.read_str str
# eval
eval_ast = (ast, env) ->
- if types._symbol_Q(ast) then env.get ast.name
+ if types._symbol_Q(ast) then env.get ast
else if types._list_Q(ast) then ast.map((a) -> EVAL(a, env))
else if types._vector_Q(ast)
types._vector(ast.map((a) -> EVAL(a, env))...)
@@ -29,11 +29,11 @@ EVAL = (ast, env) ->
[a0, a1, a2, a3] = ast
switch a0.name
when "def!"
- return env.set(a1.name, EVAL(a2, env))
+ return env.set(a1, EVAL(a2, env))
when "let*"
let_env = new Env(env)
for k,i in a1 when i %% 2 == 0
- let_env.set(a1[i].name, EVAL(a1[i+1], let_env))
+ let_env.set(a1[i], EVAL(a1[i+1], let_env))
ast = a2
env = let_env
when "do"
@@ -64,16 +64,16 @@ repl_env = new Env()
rep = (str) -> PRINT(EVAL(READ(str), repl_env))
# core.coffee: defined using CoffeeScript
-repl_env.set k, v for k,v of core.ns
-repl_env.set 'eval', (ast) -> EVAL(ast, repl_env)
-repl_env.set '*ARGV*', []
+repl_env.set types._symbol(k), v for k,v of core.ns
+repl_env.set types._symbol('eval'), (ast) -> EVAL(ast, repl_env)
+repl_env.set types._symbol('*ARGV*'), []
# core.mal: defined using the language itself
rep("(def! not (fn* (a) (if a false true)))");
rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))");
if process? && process.argv.length > 2
- repl_env.set '*ARGV*', process.argv[3..]
+ repl_env.set types._symbol('*ARGV*'), process.argv[3..]
rep('(load-file "' + process.argv[2] + '")')
process.exit 0
diff --git a/coffee/step7_quote.coffee b/coffee/step7_quote.coffee
index 404e684..7652a79 100644
--- a/coffee/step7_quote.coffee
+++ b/coffee/step7_quote.coffee
@@ -22,7 +22,7 @@ quasiquote = (ast) ->
eval_ast = (ast, env) ->
- if types._symbol_Q(ast) then env.get ast.name
+ if types._symbol_Q(ast) then env.get ast
else if types._list_Q(ast) then ast.map((a) -> EVAL(a, env))
else if types._vector_Q(ast)
types._vector(ast.map((a) -> EVAL(a, env))...)
@@ -41,11 +41,11 @@ EVAL = (ast, env) ->
[a0, a1, a2, a3] = ast
switch a0.name
when "def!"
- return env.set(a1.name, EVAL(a2, env))
+ return env.set(a1, EVAL(a2, env))
when "let*"
let_env = new Env(env)
for k,i in a1 when i %% 2 == 0
- let_env.set(a1[i].name, EVAL(a1[i+1], let_env))
+ let_env.set(a1[i], EVAL(a1[i+1], let_env))
ast = a2
env = let_env
when "quote"
@@ -80,16 +80,16 @@ repl_env = new Env()
rep = (str) -> PRINT(EVAL(READ(str), repl_env))
# core.coffee: defined using CoffeeScript
-repl_env.set k, v for k,v of core.ns
-repl_env.set 'eval', (ast) -> EVAL(ast, repl_env)
-repl_env.set '*ARGV*', []
+repl_env.set types._symbol(k), v for k,v of core.ns
+repl_env.set types._symbol('eval'), (ast) -> EVAL(ast, repl_env)
+repl_env.set types._symbol('*ARGV*'), []
# core.mal: defined using the language itself
rep("(def! not (fn* (a) (if a false true)))");
rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))");
if process? && process.argv.length > 2
- repl_env.set '*ARGV*', process.argv[3..]
+ repl_env.set types._symbol('*ARGV*'), process.argv[3..]
rep('(load-file "' + process.argv[2] + '")')
process.exit 0
diff --git a/coffee/step8_macros.coffee b/coffee/step8_macros.coffee
index 39404e7..6150d2e 100644
--- a/coffee/step8_macros.coffee
+++ b/coffee/step8_macros.coffee
@@ -21,17 +21,17 @@ quasiquote = (ast) ->
is_macro_call = (ast, env) ->
return types._list_Q(ast) && types._symbol_Q(ast[0]) &&
- env.find(ast[0].name) && env.get(ast[0].name).__ismacro__
+ env.find(ast[0]) && env.get(ast[0]).__ismacro__
macroexpand = (ast, env) ->
while is_macro_call(ast, env)
- ast = env.get(ast[0].name)(ast[1..]...)
+ ast = env.get(ast[0])(ast[1..]...)
ast
eval_ast = (ast, env) ->
- if types._symbol_Q(ast) then env.get ast.name
+ if types._symbol_Q(ast) then env.get ast
else if types._list_Q(ast) then ast.map((a) -> EVAL(a, env))
else if types._vector_Q(ast)
types._vector(ast.map((a) -> EVAL(a, env))...)
@@ -53,11 +53,11 @@ EVAL = (ast, env) ->
[a0, a1, a2, a3] = ast
switch a0.name
when "def!"
- return env.set(a1.name, EVAL(a2, env))
+ return env.set(a1, EVAL(a2, env))
when "let*"
let_env = new Env(env)
for k,i in a1 when i %% 2 == 0
- let_env.set(a1[i].name, EVAL(a1[i+1], let_env))
+ let_env.set(a1[i], EVAL(a1[i+1], let_env))
ast = a2
env = let_env
when "quote"
@@ -67,7 +67,7 @@ EVAL = (ast, env) ->
when "defmacro!"
f = EVAL(a2, env)
f.__ismacro__ = true
- return env.set(a1.name, f)
+ return env.set(a1, f)
when "macroexpand"
return macroexpand(a1, env)
when "do"
@@ -98,9 +98,9 @@ repl_env = new Env()
rep = (str) -> PRINT(EVAL(READ(str), repl_env))
# core.coffee: defined using CoffeeScript
-repl_env.set k, v for k,v of core.ns
-repl_env.set 'eval', (ast) -> EVAL(ast, repl_env)
-repl_env.set '*ARGV*', []
+repl_env.set types._symbol(k), v for k,v of core.ns
+repl_env.set types._symbol('eval'), (ast) -> EVAL(ast, repl_env)
+repl_env.set types._symbol('*ARGV*'), []
# core.mal: defined using the language itself
rep("(def! not (fn* (a) (if a false true)))");
@@ -109,7 +109,7 @@ rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (
rep("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))")
if process? && process.argv.length > 2
- repl_env.set '*ARGV*', process.argv[3..]
+ repl_env.set types._symbol('*ARGV*'), process.argv[3..]
rep('(load-file "' + process.argv[2] + '")')
process.exit 0
diff --git a/coffee/step9_try.coffee b/coffee/step9_try.coffee
index d7919e7..d8f6f43 100644
--- a/coffee/step9_try.coffee
+++ b/coffee/step9_try.coffee
@@ -21,17 +21,17 @@ quasiquote = (ast) ->
is_macro_call = (ast, env) ->
return types._list_Q(ast) && types._symbol_Q(ast[0]) &&
- env.find(ast[0].name) && env.get(ast[0].name).__ismacro__
+ env.find(ast[0]) && env.get(ast[0]).__ismacro__
macroexpand = (ast, env) ->
while is_macro_call(ast, env)
- ast = env.get(ast[0].name)(ast[1..]...)
+ ast = env.get(ast[0])(ast[1..]...)
ast
eval_ast = (ast, env) ->
- if types._symbol_Q(ast) then env.get ast.name
+ if types._symbol_Q(ast) then env.get ast
else if types._list_Q(ast) then ast.map((a) -> EVAL(a, env))
else if types._vector_Q(ast)
types._vector(ast.map((a) -> EVAL(a, env))...)
@@ -53,11 +53,11 @@ EVAL = (ast, env) ->
[a0, a1, a2, a3] = ast
switch a0.name
when "def!"
- return env.set(a1.name, EVAL(a2, env))
+ return env.set(a1, EVAL(a2, env))
when "let*"
let_env = new Env(env)
for k,i in a1 when i %% 2 == 0
- let_env.set(a1[i].name, EVAL(a1[i+1], let_env))
+ let_env.set(a1[i], EVAL(a1[i+1], let_env))
ast = a2
env = let_env
when "quote"
@@ -67,7 +67,7 @@ EVAL = (ast, env) ->
when "defmacro!"
f = EVAL(a2, env)
f.__ismacro__ = true
- return env.set(a1.name, f)
+ return env.set(a1, f)
when "macroexpand"
return macroexpand(a1, env)
when "try*"
@@ -106,24 +106,22 @@ repl_env = new Env()
rep = (str) -> PRINT(EVAL(READ(str), repl_env))
# core.coffee: defined using CoffeeScript
-repl_env.set k, v for k,v of core.ns
-repl_env.set 'eval', (ast) -> EVAL(ast, repl_env)
-repl_env.set '*ARGV*', []
+repl_env.set types._symbol(k), v for k,v of core.ns
+repl_env.set types._symbol('eval'), (ast) -> EVAL(ast, repl_env)
+repl_env.set types._symbol('*ARGV*'), []
# core.mal: defined using the language itself
-rep("(def! *host-language* \"CoffeeScript\")")
rep("(def! not (fn* (a) (if a false true)))");
rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))");
rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))")
rep("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))")
if process? && process.argv.length > 2
- repl_env.set '*ARGV*', process.argv[3..]
+ repl_env.set types._symbol('*ARGV*'), process.argv[3..]
rep('(load-file "' + process.argv[2] + '")')
process.exit 0
# repl loop
-rep("(println (str \"Mal [\" *host-language* \"]\"))")
while (line = readline.readline("user> ")) != null
continue if line == ""
try
diff --git a/coffee/stepA_interop.coffee b/coffee/stepA_interop.coffee
index aa9c5cc..751f9ad 100644
--- a/coffee/stepA_interop.coffee
+++ b/coffee/stepA_interop.coffee
@@ -21,17 +21,17 @@ quasiquote = (ast) ->
is_macro_call = (ast, env) ->
return types._list_Q(ast) && types._symbol_Q(ast[0]) &&
- env.find(ast[0].name) && env.get(ast[0].name).__ismacro__
+ env.find(ast[0]) && env.get(ast[0]).__ismacro__
macroexpand = (ast, env) ->
while is_macro_call(ast, env)
- ast = env.get(ast[0].name)(ast[1..]...)
+ ast = env.get(ast[0])(ast[1..]...)
ast
eval_ast = (ast, env) ->
- if types._symbol_Q(ast) then env.get ast.name
+ if types._symbol_Q(ast) then env.get ast
else if types._list_Q(ast) then ast.map((a) -> EVAL(a, env))
else if types._vector_Q(ast)
types._vector(ast.map((a) -> EVAL(a, env))...)
@@ -53,11 +53,11 @@ EVAL = (ast, env) ->
[a0, a1, a2, a3] = ast
switch a0.name
when "def!"
- return env.set(a1.name, EVAL(a2, env))
+ return env.set(a1, EVAL(a2, env))
when "let*"
let_env = new Env(env)
for k,i in a1 when i %% 2 == 0
- let_env.set(a1[i].name, EVAL(a1[i+1], let_env))
+ let_env.set(a1[i], EVAL(a1[i+1], let_env))
ast = a2
env = let_env
when "quote"
@@ -67,7 +67,7 @@ EVAL = (ast, env) ->
when "defmacro!"
f = EVAL(a2, env)
f.__ismacro__ = true
- return env.set(a1.name, f)
+ return env.set(a1, f)
when "macroexpand"
return macroexpand(a1, env)
when "try*"
@@ -112,9 +112,9 @@ repl_env = new Env()
rep = (str) -> PRINT(EVAL(READ(str), repl_env))
# core.coffee: defined using CoffeeScript
-repl_env.set k, v for k,v of core.ns
-repl_env.set 'eval', (ast) -> EVAL(ast, repl_env)
-repl_env.set '*ARGV*', []
+repl_env.set types._symbol(k), v for k,v of core.ns
+repl_env.set types._symbol('eval'), (ast) -> EVAL(ast, repl_env)
+repl_env.set types._symbol('*ARGV*'), []
# core.mal: defined using the language itself
rep("(def! *host-language* \"CoffeeScript\")")
@@ -124,7 +124,7 @@ rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (
rep("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))")
if process? && process.argv.length > 2
- repl_env.set '*ARGV*', process.argv[3..]
+ repl_env.set types._symbol('*ARGV*'), process.argv[3..]
rep('(load-file "' + process.argv[2] + '")')
process.exit 0
diff --git a/coffee/types.coffee b/coffee/types.coffee
index 8982120..d732064 100644
--- a/coffee/types.coffee
+++ b/coffee/types.coffee
@@ -16,7 +16,8 @@ E._obj_type = _obj_type = (obj) ->
switch typeof obj
when 'number' then 'number'
when 'function' then 'function'
- when 'string' then 'string'
+ when 'string'
+ if obj[0] == '\u029e' then 'keyword' else 'string'
else throw new Error "Unknown type '" + typeof(obj) + "'"
E._sequential_Q = _sequential_Q = (o) -> _list_Q(o) or _vector_Q(o)
@@ -69,6 +70,11 @@ class Symbol
E._symbol = (str) -> new Symbol str
E._symbol_Q = _symbol_Q = (o) -> o instanceof Symbol
+# Keywords
+E._keyword = _keyword = (str) -> "\u029e" + str
+E._keyword_Q = _keyword_Q = (o) ->
+ typeof o == 'string' && o[0] == "\u029e"
+
# Functions
E._function = (evalfn, ast, env, params) ->
fn = (args...) -> evalfn(ast, new Env(env, params, args))
@@ -103,6 +109,7 @@ E._dissoc_BANG = (hm, args...) ->
E._hash_map_Q = _hash_map_Q = (o) ->
typeof o == "object" && !Array.isArray(o) &&
!(o == null) &&
+ !(o instanceof Symbol) &&
!(o instanceof Atom)