aboutsummaryrefslogtreecommitdiff
path: root/coffee/types.coffee
blob: d7320644fd669c50611d311cb1d5a51f57e196ac (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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
Env = require("./env.coffee").Env

E = exports

# General functions
E._obj_type = _obj_type = (obj) ->
  if _symbol_Q(obj)        then 'symbol'
  else if _list_Q(obj)     then 'list'
  else if _vector_Q(obj)   then 'vector'
  else if _hash_map_Q(obj) then 'hash-map'
  else if _nil_Q(obj)      then 'nil'
  else if _true_Q(obj)     then 'true'
  else if _false_Q(obj)    then 'false'
  else if _atom_Q(obj)     then 'atom'
  else
    switch typeof obj
      when 'number'   then 'number'
      when 'function' then 'function'
      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)

E._equal_Q = _equal_Q = (a,b) ->
  [ota, otb] = [_obj_type(a), _obj_type(b)]
  if !(ota == otb or (_sequential_Q(a) && _sequential_Q(b)))
    return false
  switch (ota)
    when 'symbol' then a.name == b.name
    when 'list', 'vector'
      return false if a.length != b.length
      for av,i in a
        return false if !_equal_Q(av, b[i])
      true
    when 'hash-map'
      akeys = (key for key of a)
      bkeys = (key for key of b)
      return false if akeys.length != bkeys.length
      for akey,i in akeys
        bkey = bkeys[i]
        return false if akey != bkey
        return false if !_equal_Q(a[akey], b[bkey])
      true
    else a == b

E._clone = _clone = (obj) ->
  switch _obj_type(obj)
    when 'list' then obj[0..-1]
    when 'vector' then _vector(obj[0..-1]...)
    when 'hash-map'
      new_obj = {}
      new_obj[k] = v for k,v of obj
      new_obj
    when 'function'
      new_obj = (args...) -> obj(args...)
      new_obj[k] = v for k,v of obj
      new_obj
    else throw new Error "clone called on non-collection" + _obj_type(obj)


# Scalars
E._nil_Q = _nil_Q = (o) -> o == null
E._true_Q = _true_Q = (o) -> o == true
E._false_Q = _false_Q = (o) -> o == false

# Symbols
class Symbol
  constructor: (@name) ->
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))
  fn.__ast__ = ast
  fn.__gen_env__ = (args) -> new Env(env, params, args)
  fn.__ismacro__ = false
  fn
E._function_Q = _function_Q = (o) -> !!o.__ast__

# Lists
E._list_Q = _list_Q = (o) -> Array.isArray(o) && !o.__isvector__

# Vectors
E._vector = _vector = (args...) ->
  v = args
  v.__isvector__ = true
  v
E._vector_Q = _vector_Q = (o) -> Array.isArray(o) && !!o.__isvector__

# Hash Maps
E._hash_map = (args...) ->
  args = [{}].concat args
  _assoc_BANG(args...)
E._assoc_BANG = _assoc_BANG = (hm, args...) ->
  if args.length %% 2 == 1
    throw new Error "Odd number of hash map arguments"
  hm[k] = args[i+1] for k, i in args when i %% 2 == 0
  hm
E._dissoc_BANG = (hm, args...) ->
  delete hm[k] for k, i in args
  hm
E._hash_map_Q = _hash_map_Q = (o) ->
  typeof o == "object" && !Array.isArray(o) &&
                          !(o == null) &&
                          !(o instanceof Symbol) &&
                          !(o instanceof Atom)


# Atoms
class Atom
  constructor: (@val) ->
E._atom = (val) -> new Atom val
E._atom_Q = _atom_Q = (o) -> o instanceof Atom

# vim: ts=2:sw=2