DISABLE FDs (REMOVE ME).
[jackhill/mal.git] / coffee / types.coffee
1 Env = require("./env.coffee").Env
2
3 E = exports
4
5 # General functions
6 E._obj_type = _obj_type = (obj) ->
7 if _symbol_Q(obj) then 'symbol'
8 else if _list_Q(obj) then 'list'
9 else if _vector_Q(obj) then 'vector'
10 else if _hash_map_Q(obj) then 'hash-map'
11 else if _nil_Q(obj) then 'nil'
12 else if _true_Q(obj) then 'true'
13 else if _false_Q(obj) then 'false'
14 else if _atom_Q(obj) then 'atom'
15 else
16 switch typeof obj
17 when 'number' then 'number'
18 when 'function' then 'function'
19 when 'string'
20 if obj[0] == '\u029e' then 'keyword' else 'string'
21 else throw new Error "Unknown type '" + typeof(obj) + "'"
22
23 E._sequential_Q = _sequential_Q = (o) -> _list_Q(o) or _vector_Q(o)
24
25 E._equal_Q = _equal_Q = (a,b) ->
26 [ota, otb] = [_obj_type(a), _obj_type(b)]
27 if !(ota == otb or (_sequential_Q(a) && _sequential_Q(b)))
28 return false
29 switch (ota)
30 when 'symbol' then a.name == b.name
31 when 'list', 'vector'
32 return false if a.length != b.length
33 for av,i in a
34 return false if !_equal_Q(av, b[i])
35 true
36 when 'hash-map'
37 akeys = (key for key of a)
38 bkeys = (key for key of b)
39 return false if akeys.length != bkeys.length
40 for akey,i in akeys
41 return false if !_equal_Q(a[akey], b[akey])
42 true
43 else a == b
44
45 E._clone = _clone = (obj) ->
46 switch _obj_type(obj)
47 when 'list' then obj[0..-1]
48 when 'vector' then _vector(obj[0..-1]...)
49 when 'hash-map'
50 new_obj = {}
51 new_obj[k] = v for k,v of obj
52 new_obj
53 when 'function'
54 new_obj = (args...) -> obj(args...)
55 new_obj[k] = v for k,v of obj
56 new_obj
57 else throw new Error "clone called on non-collection" + _obj_type(obj)
58
59
60 # Scalars
61 E._nil_Q = _nil_Q = (o) -> o == null
62 E._true_Q = _true_Q = (o) -> o == true
63 E._false_Q = _false_Q = (o) -> o == false
64 E._string_Q = _string_Q = (o) -> _obj_type(o) == 'string'
65
66 # Symbols
67 class Symbol
68 constructor: (@name) ->
69 E._symbol = (str) -> new Symbol str
70 E._symbol_Q = _symbol_Q = (o) -> o instanceof Symbol
71
72 # Keywords
73 E._keyword = _keyword = (o) ->
74 _keyword_Q(o) && o || ("\u029e" + o)
75 E._keyword_Q = _keyword_Q = (o) ->
76 typeof o == 'string' && o[0] == "\u029e"
77
78 # Functions
79 E._function = (evalfn, ast, env, params) ->
80 fn = (args...) -> evalfn(ast, new Env(env, params, args))
81 fn.__ast__ = ast
82 fn.__gen_env__ = (args) -> new Env(env, params, args)
83 fn.__ismacro__ = false
84 fn
85 E._function_Q = _function_Q = (o) -> !!o.__ast__
86 E._macro_Q = _macro_Q = (o) -> _function_Q(o) and o.__ismacro__
87
88 # Lists
89 E._list_Q = _list_Q = (o) -> Array.isArray(o) && !o.__isvector__
90
91 # Vectors
92 E._vector = _vector = (args...) ->
93 v = args
94 v.__isvector__ = true
95 v
96 E._vector_Q = _vector_Q = (o) -> Array.isArray(o) && !!o.__isvector__
97
98 # Hash Maps
99 E._hash_map = (args...) ->
100 args = [{}].concat args
101 _assoc_BANG(args...)
102 E._assoc_BANG = _assoc_BANG = (hm, args...) ->
103 if args.length %% 2 == 1
104 throw new Error "Odd number of hash map arguments"
105 hm[k] = args[i+1] for k, i in args when i %% 2 == 0
106 hm
107 E._dissoc_BANG = (hm, args...) ->
108 delete hm[k] for k, i in args
109 hm
110 E._hash_map_Q = _hash_map_Q = (o) ->
111 typeof o == "object" && !Array.isArray(o) &&
112 !(o == null) &&
113 !(o instanceof Symbol) &&
114 !(o instanceof Atom)
115
116
117 # Atoms
118 class Atom
119 constructor: (@val) ->
120 E._atom = (val) -> new Atom val
121 E._atom_Q = _atom_Q = (o) -> o instanceof Atom
122
123 # vim: ts=2:sw=2