Class: Lisp::Frame

Inherits:
Atom show all
Defined in:
lib/rubylisp/frame.rb

Instance Attribute Summary

Attributes inherited from Atom

#value

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Atom

#alist?, #all?, #apply_to, #character?, #class?, #copy, #doc, #evaluate, #false?, #function?, #list?, #macro?, #negative?, #number?, #object?, #pair?, #positive?, #primitive?, #print_string, #quoted, #set!, #special?, #string?, #symbol?, #true?, #vector?, #zero?

Constructor Details

#initialize(m = nil) ⇒ Frame

Returns a new instance of Frame.



158
159
160
# File 'lib/rubylisp/frame.rb', line 158

def initialize(m=nil)
  @value = m || {}
end

Class Method Details

.clone_impl(args, env) ⇒ Object



147
148
149
150
151
# File 'lib/rubylisp/frame.rb', line 147

def self.clone_impl(args, env)
  frame = args.car.evaluate(env)
  raise "Frame data must be a frame but was #{frame.type}." unless frame.frame?
  frame.clone
end

.get_slot_if_impl(args, env) ⇒ Object



81
82
83
84
85
86
87
# File 'lib/rubylisp/frame.rb', line 81

def self.get_slot_if_impl(args, env)
  frame = args.car.evaluate(env)
  raise "Frame data must be a frame but was #{frame.type}." unless frame.frame?
  key = args.cadr.evaluate(env)
  raise "Frame key (#{key.to_s}) must be a symbol but was #{key.type}." unless key.symbol?
  frame.get(key)
end

.get_slot_impl(args, env) ⇒ Object



71
72
73
74
75
76
77
78
# File 'lib/rubylisp/frame.rb', line 71

def self.get_slot_impl(args, env)
  frame = args.car.evaluate(env)
  raise "Frame data must be a frame but was #{frame.type}." unless frame.frame?
  key = args.cadr.evaluate(env)
  raise "Frame key (#{key.to_s}) must be a symbol but was #{key.type}." unless key.symbol?
  raise "Frame key (#{key.to_s}) must name an existing slot." unless frame.has_slot?(key)
  frame.get(key)
end

.get_super_function(selector, env) ⇒ Object



124
125
126
127
128
129
130
131
132
# File 'lib/rubylisp/frame.rb', line 124

def self.get_super_function(selector, env)
  f = env.frame
  return nil if f.nil?
  f.parents.each do |p|
    func = p.get(selector)
    return func unless func.nil?
  end
  nil
end

.has_slot_impl(args, env) ⇒ Object



61
62
63
64
65
66
67
68
# File 'lib/rubylisp/frame.rb', line 61

def self.has_slot_impl(args, env)
  frame = args.car.evaluate(env)
  raise "Frame data must be a frame but was #{frame.type}." unless frame.frame?
  key = args.cadr.evaluate(env)
  raise "Frame key must be a symbol but was #{key.type}." unless key.symbol?
  return Lisp::TRUE if frame.has_slot?(key)
  Lisp::FALSE
end

.make_frame_impl(args, env) ⇒ Object



45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/rubylisp/frame.rb', line 45

def self.make_frame_impl(args, env)
  c = args
  m = {}
  while !c.nil?
    k = c.car
    raise "Slot names must be a symbol, found a {k.type}." unless k.symbol?
    raise "Slot names must end in a colon, found '#{k}'." unless k.naked?
    v = c.cadr.evaluate(env)
    m[k] = v
    c = c.cddr
  end

  Lisp::Frame.with_map(m)
end

.registerObject



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
# File 'lib/rubylisp/frame.rb', line 5

def self.register
  Primitive.register("make-frame", "(make-frame slot-name slot-value ... )\n\nFrames can be created using the make-frame function, passing it an alternating sequence of slot names and values:\n\n        (make-frame a: 1 b: 2)\n\nThis results in a frame with two slots, named a: and b: with values 1 and 2, respectively.") do |args, env|
    Lisp::Frame::make_frame_impl(args, env)
  end
  
  Primitive.register("has-slot?", "(has-slot? frame slot-name)\n\nThe has-slot? function is used to query whether a frame contains (directly or in an ancestor) the particular slot.") do |args, env|
    Lisp::Frame::has_slot_impl(args, env)
  end
  
  Primitive.register("get-slot", "(get-slot _frame_ _slot-name_)\n\nThe get-slot function is used to retrieve values from frame slots") do |args, env|
    Lisp::Frame::get_slot_impl(args, env)
  end
  
  Primitive.register("get-slot-if", "(get-slot-if frame slot-name)\n\nThe same as above, except that if a matching slot is not found, nil is returned instead of raising an error.") do |args, env|
    Lisp::Frame::get_slot_if_impl(args, env)
  end
  
  Primitive.register("remove-slot!", "(remove-slot! frame slot-name)\n\nThe remove-slot! function is used to function is used to remove a slot from a frame. It only removes slots from the frame itself. not any of it's parents. remove-slot! return #t if the slot was removed, #f otherwise.") do |args, env|
    Lisp::Frame::remove_slot_impl(args, env)
  end
  
  Primitive.register("set-slot!", "(set-slot! frame slot-name new-value)\n\nThe set-slot! function is used to change values in frame slots") do |args, env|
    Lisp::Frame::set_slot_impl(args, env)
  end
  
  Primitive.register("send", "(send frame slot-name arg...)\n\nSend the message slot-name to frame, passing along the arg collection. The result is what is returned by the code in that slot.") do |args, env|
    Lisp::Frame::send_impl(args, env)
  end
  
  Primitive.register("send-super", "**(send-super slot-name arg...)\n\nLike send, but sends to the first parent that has the named slot. send-super can only be used from within a frame.") do |args, env|
    Lisp::Frame::send_super_impl(args, env)
  end
  
  Primitive.register("clone", "(clone frame)\n\nFrames represent things. For example, you could use a frame that looks like {x: 1 y: 10} to represent a point. A system that would use point frames will typically need many independant points. The approach to this is to create a prototypical point data frame, and use the clone function to create individual, independant frames.") do |args, env|
    Lisp::Frame::clone_impl(args, env)
  end
  
end

.remove_slot_impl(args, env) ⇒ Object



90
91
92
93
94
95
96
97
# File 'lib/rubylisp/frame.rb', line 90

def self.remove_slot_impl(args, env)
  frame = args.car.evaluate(env)
  raise "Frame data must be a frame but was #{frame.type}." unless frame.frame?
  key = args.cadr.evaluate(env)
  raise "Frame key (#{key.to_s}) must be a symbol but was #{key.type}." unless key.symbol?
  return Lisp::TRUE if frame.remove(key)
  Lisp::FALSE
end

.send_impl(args, env) ⇒ Object



110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/rubylisp/frame.rb', line 110

def self.send_impl(args, env)
  frame = args.car.evaluate(env)
  raise "Frame data must be a frame but was #{frame.type}." unless frame.frame?
  selector = args.cadr.evaluate(env)
  raise "Selector must be a symbol but was #{selector.type}." unless selector.symbol?
  raise "Message sent must name an existing slot in the receiver." unless frame.has_slot?(selector)
  func = frame.get(selector)
  raise "Message sent must select a function slot but was #{func.type}." unless func.function?
  params = args.cddr
  frame_env = Lisp::EnvironmentFrame.extending(env, frame)
  frame_env.bind_locally(Symbol.named("self"), frame)
  func.apply_to(params, frame_env)
end

.send_super_impl(args, env) ⇒ Object



134
135
136
137
138
139
140
141
142
143
144
# File 'lib/rubylisp/frame.rb', line 134

def self.send_super_impl(args, env)
  raise "super can only be used within the context of a frame." unless env.frame
  selector = args.car.evaluate(env)
  raise "Selector must be a symbol but was #{selector.type}." unless selector.symbol?
  func = get_super_function(selector, env)
  raise "Message sent must select a function slot but was #{func.type}." unless func && func.function?
  params = args.cdr
  frame_env = Lisp::EnvironmentFrame.extending(env, env.frame)
  frame_env.bind_locally(Symbol.named("self"), env.frame)
  func.apply_to(params, frame_env)
end

.set_slot_impl(args, env) ⇒ Object



100
101
102
103
104
105
106
107
# File 'lib/rubylisp/frame.rb', line 100

def self.set_slot_impl(args, env)
  frame = args.car.evaluate(env)
  raise "Frame data must be a frame but was #{frame.type}." unless frame.frame?
  key = args.cadr.evaluate(env)
  raise "Frame key (#{key.to_s}) must be a symbol but was #{key.type}." unless key.symbol?
  value = args.caddr.evaluate(env)
  frame.at_put(key, value)
end

.with_map(m) ⇒ Object



154
155
156
# File 'lib/rubylisp/frame.rb', line 154

def self.with_map(m)
  self.new(m)
end

Instance Method Details

#at_put(key, value) ⇒ Object



230
231
232
233
234
235
236
237
# File 'lib/rubylisp/frame.rb', line 230

def at_put(key, value)
  return @value[key] = value if !has_slot?(key) || has_slot_locally?(key)
  parents.each do |p|
    v = p.at_put(key, value)
    return v unless v.nil?
  end
  nil
end

#carObject



260
261
262
# File 'lib/rubylisp/frame.rb', line 260

def car
  nil
end

#cdrObject



264
265
266
# File 'lib/rubylisp/frame.rb', line 264

def cdr
  nil
end

#cloneObject



163
164
165
# File 'lib/rubylisp/frame.rb', line 163

def clone
  Lisp::Frame.with_map(@value.clone)
end

#empty?Boolean

Returns:



248
249
250
# File 'lib/rubylisp/frame.rb', line 248

def empty?
  @value.empty?
end

#eq?(other) ⇒ Boolean

Returns:



268
269
270
271
272
273
274
275
# File 'lib/rubylisp/frame.rb', line 268

def eq?(other)
  return false unless other.frame?
  return false unless @value.length == other.value.length
  @value.each do |k, v|
    return false unless Lisp::Equivalence.equal_check(other.value[k], v).value
  end
  true
end

#frame?Boolean

Returns:



252
253
254
# File 'lib/rubylisp/frame.rb', line 252

def frame?
  true
end

#get(key) ⇒ Object



213
214
215
216
217
218
219
220
# File 'lib/rubylisp/frame.rb', line 213

def get(key)
  return @value[key] if has_slot_locally?(key)
  parents.each do |p|
    value = p.get(key)
    return value unless value.nil?
  end
  nil
end

#has_parent_slots?Boolean

Returns:



186
187
188
# File 'lib/rubylisp/frame.rb', line 186

def has_parent_slots?
  @value.keys.any? {|k| is_parent_key(k)}
end

#has_slot?(n) ⇒ Boolean

Returns:



206
207
208
209
210
# File 'lib/rubylisp/frame.rb', line 206

def has_slot?(n)
  return true if has_slot_locally?(n)
  return false unless has_parent_slots?
  return parents.any? {|p| p.has_slot?(n)}
end

#has_slot_locally?(n) ⇒ Boolean

Returns:



201
202
203
# File 'lib/rubylisp/frame.rb', line 201

def has_slot_locally?(n)
  @value.has_key?(n)
end

#inherited_value_slotsObject



178
179
180
181
182
183
# File 'lib/rubylisp/frame.rb', line 178

def inherited_value_slots
  parent_frames = parent_slots.collect {|pk| get(pk)}
  parent_slots = parent_frames.collect {|p| p.inherited_value_slots}
  local_value_slots = Set[local_slots.reject {|s| is_parent_key(k)}]
  parent_slots.inject(local_value_slots) {|all, s| all + s} 
end

#is_parent_key(k) ⇒ Object



168
169
170
# File 'lib/rubylisp/frame.rb', line 168

def is_parent_key(k)
  k.to_s[-2] == "*"
end

#lengthObject



256
257
258
# File 'lib/rubylisp/frame.rb', line 256

def length
  return @value.length
end

#lisp_object?Boolean

Returns:



240
241
242
# File 'lib/rubylisp/frame.rb', line 240

def lisp_object?
  true
end

#local_slotsObject



173
174
175
# File 'lib/rubylisp/frame.rb', line 173

def local_slots
  @value.keys
end

#parent_slotsObject



191
192
193
# File 'lib/rubylisp/frame.rb', line 191

def parent_slots
  @value.keys.select {|k| is_parent_key(k)}
end

#parentsObject



196
197
198
# File 'lib/rubylisp/frame.rb', line 196

def parents
  parent_slots.collect {|pk| @value[pk]}
end

#remove(key) ⇒ Object



223
224
225
226
227
# File 'lib/rubylisp/frame.rb', line 223

def remove(key)
  return false unless has_slot_locally?(key)
  @value.delete(key)
  true
end

#to_sObject



277
278
279
280
# File 'lib/rubylisp/frame.rb', line 277

def to_s
  pairs = @value.collect {|k, v| "#{k.to_s} #{v.to_s}"}
  "{#{pairs.join(' ')}}"
end

#typeObject



244
245
246
# File 'lib/rubylisp/frame.rb', line 244

def type
  :frame
end