Class: Lisp::Frame
Instance Attribute Summary
Attributes inherited from Atom
Class Method Summary collapse
- .clone_impl(args, env) ⇒ Object
- .get_slot_if_impl(args, env) ⇒ Object
- .get_slot_impl(args, env) ⇒ Object
- .get_super_function(selector, env) ⇒ Object
- .has_slot_impl(args, env) ⇒ Object
- .keys_impl(args, env) ⇒ Object
- .make_frame_impl(args, env) ⇒ Object
- .register ⇒ Object
- .remove_slot_impl(args, env) ⇒ Object
- .send_impl(args, env) ⇒ Object
- .send_super_impl(args, env) ⇒ Object
- .set_slot_impl(args, env) ⇒ Object
- .with_map(m) ⇒ Object
Instance Method Summary collapse
- #at_put(key, value) ⇒ Object
- #car ⇒ Object
- #cdr ⇒ Object
- #clone ⇒ Object
- #empty? ⇒ Boolean
- #eq?(other) ⇒ Boolean
- #frame? ⇒ Boolean
- #get(key) ⇒ Object
- #get_helper(key, v) ⇒ Object
- #has_parent_slots? ⇒ Boolean
- #has_slot?(n) ⇒ Boolean
- #has_slot_helper(n, v) ⇒ Object
- #has_slot_locally?(n) ⇒ Boolean
- #inherited_value_slots ⇒ Object
-
#initialize(m = nil) ⇒ Frame
constructor
A new instance of Frame.
- #is_parent_key(k) ⇒ Object
- #length ⇒ Object
- #lisp_object? ⇒ Boolean
- #local_slots ⇒ Object
- #parent_slots ⇒ Object
- #parents ⇒ Object
- #remove(key) ⇒ Object
- #to_s ⇒ Object
- #type ⇒ Object
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
169 170 171 |
# File 'lib/rubylisp/frame.rb', line 169 def initialize(m=nil) @value = m || {} end |
Class Method Details
.clone_impl(args, env) ⇒ Object
151 152 153 154 155 |
# File 'lib/rubylisp/frame.rb', line 151 def self.clone_impl(args, env) frame = args.car.evaluate(env) return Lisp::Debug.process_error("Frame data must be a frame but was #{frame.type}.", env) unless frame.frame? frame.clone end |
.get_slot_if_impl(args, env) ⇒ Object
85 86 87 88 89 90 91 |
# File 'lib/rubylisp/frame.rb', line 85 def self.get_slot_if_impl(args, env) frame = args.car.evaluate(env) return Lisp::Debug.process_error("Frame data must be a frame but was #{frame.type}.", env) unless frame.frame? key = args.cadr.evaluate(env) return Lisp::Debug.process_error("Frame key (#{key.to_s}) must be a symbol but was #{key.type}.", env) unless key.symbol? frame.get(key) end |
.get_slot_impl(args, env) ⇒ Object
75 76 77 78 79 80 81 82 |
# File 'lib/rubylisp/frame.rb', line 75 def self.get_slot_impl(args, env) frame = args.car.evaluate(env) return Lisp::Debug.process_error("Frame data must be a frame but was #{frame.type}.", env) unless frame.frame? key = args.cadr.evaluate(env) return Lisp::Debug.process_error("Frame key (#{key.to_s}) must be a symbol but was #{key.type}.", env) unless key.symbol? return Lisp::Debug.process_error("Frame key (#{key.to_s}) must name an existing slot.", env) unless frame.has_slot?(key) frame.get(key) end |
.get_super_function(selector, env) ⇒ Object
128 129 130 131 132 133 134 135 136 |
# File 'lib/rubylisp/frame.rb', line 128 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
65 66 67 68 69 70 71 72 |
# File 'lib/rubylisp/frame.rb', line 65 def self.has_slot_impl(args, env) frame = args.car.evaluate(env) return Lisp::Debug.process_error("Frame data must be a frame but was #{frame.type}.", env) unless frame.frame? key = args.cadr.evaluate(env) return Lisp::Debug.process_error("Frame key must be a symbol but was #{key.type}.", env) unless key.symbol? return Lisp::TRUE if frame.has_slot?(key) Lisp::FALSE end |
.keys_impl(args, env) ⇒ Object
158 159 160 161 162 |
# File 'lib/rubylisp/frame.rb', line 158 def self.keys_impl(args, env) frame = args.car.evaluate(env) return Lisp::Debug.process_error("Frame data must be a frame but was #{frame.type}.", env) unless frame.frame? ConsCell.array_to_list(frame.value.keys) end |
.make_frame_impl(args, env) ⇒ Object
49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
# File 'lib/rubylisp/frame.rb', line 49 def self.make_frame_impl(args, env) c = args m = {} while !c.nil? k = c.car return Lisp::Debug.process_error("Slot names must be a symbol, found a {k.type}.", env) unless k.symbol? return Lisp::Debug.process_error("Slot names must end in a colon, found '#{k}'.", env) unless k.naked? v = c.cadr.evaluate(env) m[k] = v c = c.cddr end Lisp::Frame.with_map(m) end |
.register ⇒ Object
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 |
# 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 Primitive.register("keys", "(keys frame)\n\nReturn a list of the keys in the frame.") do |args, env| Lisp::Frame::keys_impl(args, env) end end |
.remove_slot_impl(args, env) ⇒ Object
94 95 96 97 98 99 100 101 |
# File 'lib/rubylisp/frame.rb', line 94 def self.remove_slot_impl(args, env) frame = args.car.evaluate(env) return Lisp::Debug.process_error("Frame data must be a frame but was #{frame.type}.", env) unless frame.frame? key = args.cadr.evaluate(env) return Lisp::Debug.process_error("Frame key (#{key.to_s}) must be a symbol but was #{key.type}.", env) unless key.symbol? return Lisp::TRUE if frame.remove(key) Lisp::FALSE end |
.send_impl(args, env) ⇒ Object
114 115 116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/rubylisp/frame.rb', line 114 def self.send_impl(args, env) frame = args.car.evaluate(env) return Lisp::Debug.process_error("Frame data must be a frame but was #{frame.type}.", env) unless frame.frame? selector = args.cadr.evaluate(env) return Lisp::Debug.process_error("Selector must be a symbol but was #{selector.type}.", env) unless selector.symbol? return Lisp::Debug.process_error("Message sent must name an existing slot in the receiver.", env) unless frame.has_slot?(selector) func = frame.get(selector) return Lisp::Debug.process_error("Message sent must select a function slot but was #{func.type}.", env) 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
138 139 140 141 142 143 144 145 146 147 148 |
# File 'lib/rubylisp/frame.rb', line 138 def self.send_super_impl(args, env) return Lisp::Debug.process_error("super can only be used within the context of a frame.", env) unless env.frame selector = args.car.evaluate(env) return Lisp::Debug.process_error("Selector must be a symbol but was #{selector.type}.", env) unless selector.symbol? func = get_super_function(selector, env) return Lisp::Debug.process_error("Message sent must select a function slot but was #{func.type}.", env) 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
104 105 106 107 108 109 110 111 |
# File 'lib/rubylisp/frame.rb', line 104 def self.set_slot_impl(args, env) frame = args.car.evaluate(env) return Lisp::Debug.process_error("Frame data must be a frame but was #{frame.type}.", env) unless frame.frame? key = args.cadr.evaluate(env) return Lisp::Debug.process_error("Frame key (#{key.to_s}) must be a symbol but was #{key.type}.", env) unless key.symbol? value = args.caddr.evaluate(env) frame.at_put(key, value) end |
.with_map(m) ⇒ Object
165 166 167 |
# File 'lib/rubylisp/frame.rb', line 165 def self.with_map(m) self.new(m) end |
Instance Method Details
#at_put(key, value) ⇒ Object
255 256 257 |
# File 'lib/rubylisp/frame.rb', line 255 def at_put(key, value) return @value[key] = value end |
#car ⇒ Object
280 281 282 |
# File 'lib/rubylisp/frame.rb', line 280 def car nil end |
#cdr ⇒ Object
284 285 286 |
# File 'lib/rubylisp/frame.rb', line 284 def cdr nil end |
#clone ⇒ Object
174 175 176 |
# File 'lib/rubylisp/frame.rb', line 174 def clone Lisp::Frame.with_map(@value.clone) end |
#empty? ⇒ Boolean
268 269 270 |
# File 'lib/rubylisp/frame.rb', line 268 def empty? @value.empty? end |
#eq?(other) ⇒ Boolean
288 289 290 291 292 293 294 295 |
# File 'lib/rubylisp/frame.rb', line 288 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
272 273 274 |
# File 'lib/rubylisp/frame.rb', line 272 def frame? true end |
#get(key) ⇒ Object
243 244 245 |
# File 'lib/rubylisp/frame.rb', line 243 def get(key) get_helper(key, Set.new) end |
#get_helper(key, v) ⇒ Object
231 232 233 234 235 236 237 238 239 240 |
# File 'lib/rubylisp/frame.rb', line 231 def get_helper(key, v) return nil if v.include?(self) v << self return @value[key] if has_slot_locally?(key) parents.each do |p| value = p.get_helper(key, v) return value unless value.nil? end nil end |
#has_parent_slots? ⇒ Boolean
197 198 199 |
# File 'lib/rubylisp/frame.rb', line 197 def has_parent_slots? @value.keys.any? {|k| is_parent_key(k)} end |
#has_slot?(n) ⇒ Boolean
226 227 228 |
# File 'lib/rubylisp/frame.rb', line 226 def has_slot?(n) has_slot_helper(n, Set.new) end |
#has_slot_helper(n, v) ⇒ Object
217 218 219 220 221 222 223 |
# File 'lib/rubylisp/frame.rb', line 217 def has_slot_helper(n, v) return false if v.include?(self) v << self return true if has_slot_locally?(n) return false unless has_parent_slots? return parents.any? {|p| p.has_slot_helper(n, v)} end |
#has_slot_locally?(n) ⇒ Boolean
212 213 214 |
# File 'lib/rubylisp/frame.rb', line 212 def has_slot_locally?(n) @value.has_key?(n) end |
#inherited_value_slots ⇒ Object
189 190 191 192 193 194 |
# File 'lib/rubylisp/frame.rb', line 189 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
179 180 181 |
# File 'lib/rubylisp/frame.rb', line 179 def is_parent_key(k) k.to_s[-2] == "*" end |
#length ⇒ Object
276 277 278 |
# File 'lib/rubylisp/frame.rb', line 276 def length return @value.length end |
#lisp_object? ⇒ Boolean
260 261 262 |
# File 'lib/rubylisp/frame.rb', line 260 def lisp_object? true end |
#local_slots ⇒ Object
184 185 186 |
# File 'lib/rubylisp/frame.rb', line 184 def local_slots @value.keys end |
#parent_slots ⇒ Object
202 203 204 |
# File 'lib/rubylisp/frame.rb', line 202 def parent_slots @value.keys.select {|k| is_parent_key(k)} end |
#parents ⇒ Object
207 208 209 |
# File 'lib/rubylisp/frame.rb', line 207 def parents parent_slots.collect {|pk| @value[pk]} end |
#remove(key) ⇒ Object
248 249 250 251 252 |
# File 'lib/rubylisp/frame.rb', line 248 def remove(key) return false unless has_slot_locally?(key) @value.delete(key) true end |
#to_s ⇒ Object
297 298 299 300 |
# File 'lib/rubylisp/frame.rb', line 297 def to_s pairs = @value.collect {|k, v| "#{k.to_s} #{v.to_s}"} "{#{pairs.join(' ')}}" end |
#type ⇒ Object
264 265 266 |
# File 'lib/rubylisp/frame.rb', line 264 def type :frame end |