nudge 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.1.1
@@ -16,6 +16,7 @@ module Nudge
16
16
  attr_accessor :program, :stepLimit, :steps
17
17
  attr_accessor :stacks, :instructions_library, :variables, :names, :types
18
18
  attr_accessor :last_name, :evaluate_references
19
+ attr_accessor :sensors
19
20
 
20
21
 
21
22
  # A program to be interpreted can be passed in as an optional parameter
@@ -24,6 +25,7 @@ module Nudge
24
25
  @program = initialProgram
25
26
  @types = params[:types] || NudgeType.all_types
26
27
  @stepLimit = params[:step_limit] || 3000
28
+ @sensors = Hash.new
27
29
 
28
30
  instructions = params[:instructions] || Instruction.all_instructions
29
31
  @instructions_library = Hash.new {|hash, key| raise InstructionPoint::InstructionNotFoundError,
@@ -54,6 +56,7 @@ module Nudge
54
56
  @program = program
55
57
  self.clear_stacks
56
58
  self.reset_names
59
+ self.reset_sensors
57
60
  if program
58
61
  @stacks[:exec].push(NudgeProgram.new(program).linked_code)
59
62
  end
@@ -67,6 +70,11 @@ module Nudge
67
70
  end
68
71
 
69
72
 
73
+ def depth(stackname)
74
+ @stacks[stackname].depth
75
+ end
76
+
77
+
70
78
  def peek(stackname)
71
79
  @stacks[stackname].peek
72
80
  end
@@ -127,6 +135,7 @@ module Nudge
127
135
  while notDone?
128
136
  self.step
129
137
  end
138
+ fire_all_sensors
130
139
  end
131
140
 
132
141
 
@@ -226,5 +235,24 @@ module Nudge
226
235
  def disable_all_types
227
236
  @types = []
228
237
  end
238
+
239
+
240
+ def register_sensor(name, &block)
241
+ raise(ArgumentError, "Sensor name #{name} is not a string") unless name.kind_of?(String)
242
+ @sensors[name] = block
243
+ end
244
+
245
+
246
+ def reset_sensors
247
+ @sensors = Hash.new
248
+ end
249
+
250
+
251
+ def fire_all_sensors
252
+ @sensors.inject({}) do |result, (key, value)|
253
+ result[key] = @sensors[key].call(self)
254
+ result
255
+ end
256
+ end
229
257
  end
230
258
  end
@@ -9,7 +9,6 @@ module Nudge
9
9
  attr_accessor :linked_code,:footnotes
10
10
  attr_accessor :raw_code
11
11
  attr_accessor :code_section, :footnote_section
12
- attr_reader :parser
13
12
  attr_reader :points
14
13
 
15
14
 
@@ -17,7 +16,6 @@ module Nudge
17
16
  raise(ArgumentError, "NudgeProgram.new should be passed a string") unless sourcecode.kind_of?(String)
18
17
  @raw_code = sourcecode
19
18
  program_split!
20
- @parser = NudgeCodeblockParser.new
21
19
  relink_code!
22
20
  @points = self.points
23
21
  end
@@ -49,7 +47,7 @@ module Nudge
49
47
 
50
48
  def relink_code!
51
49
  if parses?
52
- @linked_code = @parser.parse(@code_section).to_point
50
+ @linked_code = NudgeCodeblockParser.new.parse(@code_section).to_point
53
51
  depth_first_association!
54
52
  else
55
53
  @linked_code = NilPoint.new
@@ -76,7 +74,7 @@ module Nudge
76
74
  def pursue_more_footnotes(codepoint_as_string, collected_footnotes = "")
77
75
  local_footnotes = ""
78
76
  if self.contains_valuepoints?(codepoint_as_string)
79
- local_parsetree = @parser.parse(codepoint_as_string)
77
+ local_parsetree = NudgeCodeblockParser.new.parse(codepoint_as_string)
80
78
  if local_parsetree != nil
81
79
  local_subtree = local_parsetree.to_point
82
80
  if local_subtree.kind_of?(CodeblockPoint)
@@ -213,7 +211,7 @@ module Nudge
213
211
 
214
212
 
215
213
  def parses?(program_blueprint = @code_section)
216
- (@parser.parse(program_blueprint) != nil)
214
+ (NudgeCodeblockParser.new.parse(program_blueprint) != nil)
217
215
  end
218
216
 
219
217
 
@@ -1,3 +1,4 @@
1
+ #encoding: utf-8
1
2
  module Nudge
2
3
 
3
4
  # Nudge Stacks are Arrays with some added convenience functions.
@@ -34,6 +35,13 @@ module Nudge
34
35
  @entries.length
35
36
  end
36
37
 
38
+ def inspect
39
+ result = @entries.reverse.inject("[") do |return_string, item|
40
+ return_string << "\n«#{item.type}» #{item.value},"
41
+ end
42
+ result.chop+"]"
43
+ end
44
+
37
45
  end
38
46
 
39
47
  end
@@ -45,6 +45,13 @@ describe "initialization" do
45
45
  @ii.steps.should == 0
46
46
  end
47
47
 
48
+ it "#reset should reset the #sensors Hash" do
49
+ @ii.register_sensor("z") {1201}
50
+ @ii.reset
51
+ @ii.sensors["z"].should == nil
52
+ end
53
+
54
+
48
55
  it "should load a complex CodeBlock as a single item on the exec stack" do
49
56
  myCode = "block {\ndo foo\n do bar\n block {\ndo baz}}"
50
57
  @ii.reset(myCode)
@@ -230,6 +237,31 @@ describe "Interpreter#peek" do
230
237
  end
231
238
 
232
239
 
240
+ describe "Interpreter#depth" do
241
+ before(:each) do
242
+ @ii = Interpreter.new()
243
+ @ii.clear_stacks
244
+ end
245
+
246
+ it "should invoke a particular stack's #depth method" do
247
+ 30.times { @ii.stacks[:donut].push(ValuePoint.new("donut", "0")) }
248
+ lambda{@ii.depth(:donut)}.should_not raise_error
249
+ @ii.depth(:donut).should == 30
250
+ end
251
+
252
+ it "should return 0 if the stack doesn't exist" do
253
+ lambda{@ii.depth(:nonexistent)}.should_not raise_error
254
+ @ii.depth(:nonexistent).should == 0
255
+ end
256
+
257
+ it "should validate the stackname as a symbol" do
258
+ lambda{@ii.depth("not there")}.should raise_error(ArgumentError)
259
+ lambda{@ii.depth(99)}.should raise_error(ArgumentError)
260
+ end
261
+ end
262
+
263
+
264
+
233
265
 
234
266
  describe "Interpreter#peek_value" do
235
267
  before(:each) do
@@ -417,6 +449,10 @@ describe "running" do
417
449
  @ii.steps.should == 0
418
450
  end
419
451
 
452
+ it "should #fire_all_sensors at the end of running" do
453
+ @ii.should_receive(:fire_all_sensors)
454
+ @ii.run
455
+ end
420
456
  end
421
457
 
422
458
 
@@ -458,3 +494,69 @@ describe "resetting" do
458
494
  ii.variables.keys.should == ["a"]
459
495
  end
460
496
  end
497
+
498
+
499
+ describe "sensors" do
500
+ before(:each) do
501
+ @ii = Interpreter.new
502
+ end
503
+
504
+ describe "register_sensor" do
505
+ it "should add a sensor block to the #sensors Hash" do
506
+ lambda{@ii.register_sensor("y") {}}.should_not raise_error
507
+ @ii.sensors.length.should == 1
508
+ @ii.sensors["y"].should be_a_kind_of(Proc)
509
+ end
510
+
511
+ it "should take a string name, validated as such" do
512
+ lambda{@ii.register_sensor("z")}.should_not raise_error
513
+ lambda{@ii.register_sensor(8)}.should raise_error(ArgumentError)
514
+ end
515
+
516
+ it "should take a block, which is called when the sensor is fired" do
517
+ @ii.register_sensor("y") {|x| 9}
518
+ @ii.sensors["y"].call.should == 9
519
+ end
520
+
521
+ it "should have access to the Interpreter state through a parameter" do
522
+ @ii.push(:int, 10202)
523
+ @ii.register_sensor("x1") {|interpreter| interpreter.peek_value(:int)}
524
+ @ii.sensors["x1"].call(@ii).should == 10202
525
+ end
526
+ end
527
+
528
+ describe "clear_sensors" do
529
+ it "should reset the #sensors Hash to empty" do
530
+ @ii.register_sensor("y1") {|x| 1}
531
+ @ii.register_sensor("y2") {|x| 2}
532
+ @ii.register_sensor("y3") {|x| 4}
533
+ @ii.sensors.keys.should == ["y1", "y2", "y3"]
534
+ @ii.reset_sensors
535
+ @ii.sensors.should == {}
536
+ end
537
+ end
538
+
539
+ describe "Interpreter#fire_all_sensors(name)" do
540
+ it "should call all registered sensors and return a Hash of results" do
541
+ @ii.register_sensor("y1") {|x| 1}
542
+ @ii.register_sensor("y2") {|x| 2}
543
+ @ii.register_sensor("y3") {|x| 4}
544
+ @ii.fire_all_sensors.should == {"y1"=>1, "y2"=>2, "y3"=>4}
545
+ end
546
+
547
+ it "should return an empty Hash if nothing is registered" do
548
+ @ii.fire_all_sensors.should == {}
549
+ end
550
+ end
551
+
552
+ describe "interrogating Interpreter state" do
553
+ it "should be possible to read anything about the Interpreter state" do
554
+ @ii.reset("block {value «int» value «int»}\n«int» 88\n«int» 11")
555
+ @ii.register_sensor("steps") {|me| me.steps}
556
+ @ii.register_sensor("top_int") {|me| me.pop_value(:int)}
557
+ @ii.register_sensor("second_int") {|me| me.pop_value(:int)}
558
+ 3.times {@ii.step} # so as not to fire sensors at the end
559
+ @ii.fire_all_sensors.should == {"steps"=>3, "top_int"=>11, "second_int"=>88}
560
+ end
561
+ end
562
+ end
@@ -1,3 +1,4 @@
1
+ #encoding: utf-8
1
2
  require File.join(File.dirname(__FILE__), "/../spec_helper")
2
3
  include Nudge
3
4
 
@@ -78,4 +79,38 @@ describe "stack" do
78
79
  @myStack.depth.should == 2
79
80
  end
80
81
  end
82
+
83
+
84
+ describe "inspect" do
85
+ it "should return the entries wrapped in brackets" do
86
+ inspectable_me = Stack.new(:foo)
87
+ 3.times {inspectable_me.push(ValuePoint.new("foo", rand(1000)))}
88
+ inspectable_me.inspect.first.should == "["
89
+ inspectable_me.inspect.last.should == "]"
90
+ end
91
+
92
+ it "should return them top-to-bottom" do
93
+ inspectable_me = Stack.new(:foo)
94
+ inspectable_me.push(ValuePoint.new("foo", 'bottom'))
95
+ inspectable_me.push(ValuePoint.new("foo", 'top'))
96
+ inspectable_me.inspect.should =~ /(.+)top(.+)bottom\]/um
97
+ end
98
+
99
+ it "should return a string containing every entry" do
100
+ inspectable_me = Stack.new(:foo)
101
+ (-2..2).each {|i| inspectable_me.push(ValuePoint.new("foo", i))}
102
+ (inspectable_me.inspect.scan(/(-\d|\d)/)).flatten.collect {|n| n.to_i}.should ==
103
+ [2,1,0,-1,-2]
104
+ end
105
+
106
+ it "should return the type and value of each entry" do
107
+ inspectable_me = Stack.new(:foo)
108
+ inspectable_me.push(ValuePoint.new("a", '1'))
109
+ inspectable_me.push(ValuePoint.new("b", '2'))
110
+ inspectable_me.push(ValuePoint.new("c", '3'))
111
+ inspectable_me.push(ValuePoint.new("d", '4'))
112
+
113
+ inspectable_me.inspect.should == "[\n«d» 4,\n«c» 3,\n«b» 2,\n«a» 1]"
114
+ end
115
+ end
81
116
  end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 1
8
- - 0
9
- version: 0.1.0
8
+ - 1
9
+ version: 0.1.1
10
10
  platform: ruby
11
11
  authors:
12
12
  - Bill Tozier