nudge 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/VERSION +1 -1
- data/lib/interpreter/interpreter.rb +28 -0
- data/lib/interpreter/nudge_program.rb +3 -5
- data/lib/interpreter/stack.rb +8 -0
- data/spec/interpreter/interpreter_spec.rb +102 -0
- data/spec/interpreter/stack_spec.rb +35 -0
- metadata +2 -2
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
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 =
|
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 =
|
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
|
-
(
|
214
|
+
(NudgeCodeblockParser.new.parse(program_blueprint) != nil)
|
217
215
|
end
|
218
216
|
|
219
217
|
|
data/lib/interpreter/stack.rb
CHANGED
@@ -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
|