nudge 0.2.3 → 0.2.4
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/Rakefile +0 -1
- data/Thorfile +17 -1
- data/VERSION +1 -1
- data/lib/interpreter/interpreter.rb +59 -7
- data/readme.md +1 -1
- metadata +3 -16
data/Rakefile
CHANGED
data/Thorfile
CHANGED
@@ -6,10 +6,26 @@ class Extend_Nudge < Thor::Group
|
|
6
6
|
# Define arguments and options
|
7
7
|
argument :project_name
|
8
8
|
class_option :test_framework, :default => :rspec
|
9
|
-
|
9
|
+
desc "Creates a new project folder structure for Nudge types, instructions and specs"
|
10
|
+
|
11
|
+
|
10
12
|
def self.source_root
|
11
13
|
File.dirname(__FILE__)
|
12
14
|
end
|
15
|
+
|
16
|
+
def create_project_folder
|
17
|
+
dirname = "#{Extend_Nudge.source_root}/#{project_name}"
|
18
|
+
puts dirname
|
19
|
+
if Dir.exist?(dirname) then
|
20
|
+
puts "project directory 'dirname' already exists"
|
21
|
+
else
|
22
|
+
empty_directory(dirname)
|
23
|
+
empty_directory("#{dirname}/lib")
|
24
|
+
empty_directory("#{dirname}/lib/instructions")
|
25
|
+
empty_directory("#{dirname}/lib/interpreter/types")
|
26
|
+
empty_directory("#{dirname}/spec")
|
27
|
+
end
|
28
|
+
end
|
13
29
|
end
|
14
30
|
|
15
31
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.2.
|
1
|
+
0.2.4
|
@@ -2,16 +2,20 @@
|
|
2
2
|
module Nudge
|
3
3
|
|
4
4
|
# The Interpreter class executes the Push3-like language loop:
|
5
|
-
# 1. Pop the top item off the <
|
5
|
+
# 1. Pop the top item off the <tt>:exec</tt> Stack
|
6
6
|
# 2. If it is a(n)...
|
7
7
|
# * ... InstructionPoint, execute that instruction's go() method;
|
8
8
|
# * ... ValuePoint, push its value to the Stack it names;
|
9
|
-
# * ... ReferencePoint (Variable or Name), ...
|
9
|
+
# * ... ReferencePoint (a reference to a Variable or Name), ...
|
10
10
|
# * ... if it's bound to a value, push the bound value onto the <b>:exec</b> Stack;
|
11
11
|
# * ... if it's not bound, push the name itself onto the <b>:name</b> Stack;
|
12
12
|
# * ... CodeblockPoint, push its #contents (in the same order) back onto the <b>:exec</b> Stack
|
13
13
|
# * ... NilPoint, do nothing
|
14
|
-
|
14
|
+
#
|
15
|
+
# This cycle repeats until one of the termination conditions is met:
|
16
|
+
# * nothing more remains on the <tt>:exec/tt> stack
|
17
|
+
# * the number of cycles meets or exceeds the <tt>step_limit</tt>
|
18
|
+
# * the wall-clock time meets or exceeds the <tt>time_limit</tt>
|
15
19
|
class Interpreter
|
16
20
|
attr_accessor :program, :step_limit, :steps
|
17
21
|
attr_accessor :stacks, :instructions_library, :variables, :names, :types
|
@@ -21,7 +25,6 @@ module Nudge
|
|
21
25
|
attr_accessor :start_time, :time_limit
|
22
26
|
|
23
27
|
|
24
|
-
# A program to be interpreted can be passed in as an optional parameter
|
25
28
|
def initialize(program = nil, params = {})
|
26
29
|
initialProgram = program
|
27
30
|
@program = initialProgram
|
@@ -55,7 +58,10 @@ module Nudge
|
|
55
58
|
# * parses the program
|
56
59
|
# * if it parses, pushes it onto the <b>:exec</b> Stack
|
57
60
|
# * (and if it doesn't parse, leaves all stacks empty)
|
58
|
-
# * resets the @step counter
|
61
|
+
# * resets the @step counter
|
62
|
+
# * resets the name assignments
|
63
|
+
# * resets the start_time (intentional redundancy)
|
64
|
+
# * resets a number of state variables
|
59
65
|
def reset(program=nil)
|
60
66
|
@program = program
|
61
67
|
self.clear_stacks
|
@@ -70,38 +76,46 @@ module Nudge
|
|
70
76
|
end
|
71
77
|
|
72
78
|
|
79
|
+
|
80
|
+
# Deletes all items from all stacks
|
73
81
|
def clear_stacks
|
74
82
|
@stacks = Hash.new {|hash, key| hash[key] = Stack.new(key) }
|
75
83
|
end
|
76
84
|
|
77
85
|
|
86
|
+
# Returns the count of items in a given stack
|
78
87
|
def depth(stackname)
|
79
88
|
@stacks[stackname].depth
|
80
89
|
end
|
81
90
|
|
82
91
|
|
92
|
+
# Returns a link to the top item in a given stack (not its value)
|
83
93
|
def peek(stackname)
|
84
94
|
@stacks[stackname].peek
|
85
95
|
end
|
86
96
|
|
87
97
|
|
98
|
+
# Returns a link to the value of the top item in a given stack
|
88
99
|
def peek_value(stackname)
|
89
100
|
item = @stacks[stackname].peek
|
90
101
|
item.nil? ? nil : item.value
|
91
102
|
end
|
92
103
|
|
93
104
|
|
105
|
+
# Removes the top item from a given stack and returns it
|
94
106
|
def pop(stackname)
|
95
107
|
@stacks[stackname].pop
|
96
108
|
end
|
97
109
|
|
98
110
|
|
111
|
+
# Removes the top item from a given stack and returns its value
|
99
112
|
def pop_value(stackname)
|
100
113
|
item = @stacks[stackname].pop
|
101
114
|
item.nil? ? nil : item.value
|
102
115
|
end
|
103
116
|
|
104
117
|
|
118
|
+
# Adds a new ValuePoint item, with the given value, to the named stack
|
105
119
|
def push(stackname, value="")
|
106
120
|
@stacks[stackname].push(ValuePoint.new(stackname, value))
|
107
121
|
end
|
@@ -111,6 +125,7 @@ module Nudge
|
|
111
125
|
# Checks to see if either stopping condition applies:
|
112
126
|
# 1. Is the <b>:exec</b> stack empty?
|
113
127
|
# 2. Are the number of steps greater than self.step_limit?
|
128
|
+
# 3. Has the total time since recorded self.start_time exceeded self.time_limit?
|
114
129
|
def notDone?
|
115
130
|
@stacks[:exec].depth > 0 &&
|
116
131
|
@steps < @step_limit &&
|
@@ -120,9 +135,12 @@ module Nudge
|
|
120
135
|
|
121
136
|
# Execute one cycle of the Push3 interpreter rule:
|
122
137
|
# 1. check termination conditions with self.notDone()?
|
123
|
-
# 2. pop one item from <
|
124
|
-
# 3. call
|
138
|
+
# 2. pop one item from <tt>:exec</tt>
|
139
|
+
# 3. call that item's #go method
|
125
140
|
# 4. increment the step counter self#steps
|
141
|
+
#
|
142
|
+
# Note that the start_time attribute is not adjusted; if called a long time after resetting,
|
143
|
+
# it may time out unexpectedly.
|
126
144
|
def step
|
127
145
|
if notDone?
|
128
146
|
nextPoint = @stacks[:exec].pop
|
@@ -132,6 +150,7 @@ module Nudge
|
|
132
150
|
end
|
133
151
|
|
134
152
|
|
153
|
+
# Returns an Array containing the class names of all <i>active</i> instructions
|
135
154
|
def instructions
|
136
155
|
@instructions_library.keys
|
137
156
|
end
|
@@ -147,16 +166,23 @@ module Nudge
|
|
147
166
|
end
|
148
167
|
|
149
168
|
|
169
|
+
# given a string, checks the hash of defined variables, then the names (local variables),
|
170
|
+
# returning the bound value, or nil if it is not found
|
150
171
|
def lookup(name)
|
151
172
|
@variables[name] || @names[name]
|
152
173
|
end
|
153
174
|
|
154
175
|
|
176
|
+
# returns an Array of all strings defined as variables or names
|
155
177
|
def references
|
156
178
|
@names.merge(@variables).keys
|
157
179
|
end
|
158
180
|
|
159
181
|
|
182
|
+
# Convenience method that can be called with either an Instruction or NudgeType class as an
|
183
|
+
# argument. If an Instruction, that class is added to the Interpreter's #instruction_library.
|
184
|
+
# If a NudgeType, that class is added to the list of types that can be used to generate
|
185
|
+
# random code.
|
160
186
|
def enable(item)
|
161
187
|
if item.superclass == Instruction
|
162
188
|
@instructions_library[item] = item.new(self)
|
@@ -166,6 +192,8 @@ module Nudge
|
|
166
192
|
end
|
167
193
|
|
168
194
|
|
195
|
+
# Convenience method that checks to see whether an Instruction or NudgeType class is currently
|
196
|
+
# in the active state. Returns a boolean.
|
169
197
|
def active?(item)
|
170
198
|
if item.superclass == Instruction
|
171
199
|
@instructions_library.include?(item)
|
@@ -175,6 +203,7 @@ module Nudge
|
|
175
203
|
end
|
176
204
|
|
177
205
|
|
206
|
+
# Given a string and a ProgramPoint, binds a variable with that name to that ProgramPoint
|
178
207
|
def bind_variable(name, value)
|
179
208
|
raise(ArgumentError, "Variables can only be bound to ProgramPoints") unless
|
180
209
|
value.kind_of?(ProgramPoint)
|
@@ -182,6 +211,7 @@ module Nudge
|
|
182
211
|
end
|
183
212
|
|
184
213
|
|
214
|
+
# Given a string and a ProgramPoint, binds a name with that name to that ProgramPoint
|
185
215
|
def bind_name(name, value)
|
186
216
|
raise(ArgumentError, "Names can only be bound to ProgramPoints") unless
|
187
217
|
value.kind_of?(ProgramPoint)
|
@@ -189,31 +219,38 @@ module Nudge
|
|
189
219
|
end
|
190
220
|
|
191
221
|
|
222
|
+
# generates an arbitrary string for naming new local variables, by incrememnting
|
223
|
+
# from the starting point "aaa001"
|
192
224
|
def next_name
|
193
225
|
@last_name = @last_name.next
|
194
226
|
end
|
195
227
|
|
196
228
|
|
229
|
+
# removes the named global variable from the Hash that defines them
|
197
230
|
def unbind_variable(name)
|
198
231
|
@variables.delete(name)
|
199
232
|
end
|
200
233
|
|
201
234
|
|
235
|
+
# removes the named local variable from the Hash that defines them
|
202
236
|
def unbind_name(name)
|
203
237
|
@names.delete(name)
|
204
238
|
end
|
205
239
|
|
206
240
|
|
241
|
+
# removes all global variable definitions
|
207
242
|
def reset_variables
|
208
243
|
@variables = Hash.new
|
209
244
|
end
|
210
245
|
|
211
246
|
|
247
|
+
# removes all local variable definitions
|
212
248
|
def reset_names
|
213
249
|
@names = Hash.new
|
214
250
|
end
|
215
251
|
|
216
252
|
|
253
|
+
# activates every Instruction subclass defined in any library
|
217
254
|
def enable_all_instructions
|
218
255
|
Instruction.all_instructions.each do |i|
|
219
256
|
@instructions_library[i] = i.new(self)
|
@@ -221,11 +258,16 @@ module Nudge
|
|
221
258
|
end
|
222
259
|
|
223
260
|
|
261
|
+
# activates every NudgeType subclass defined in any library
|
224
262
|
def enable_all_types
|
225
263
|
@types = NudgeType.all_types
|
226
264
|
end
|
227
265
|
|
228
266
|
|
267
|
+
# Convenience method that can be called with either an Instruction or NudgeType class as an
|
268
|
+
# argument. If an Instruction, that class is removed from the Interpreter's #instruction_library.
|
269
|
+
# If a NudgeType, that class is removed to the list of types that can be used to generate
|
270
|
+
# random code.
|
229
271
|
def disable(item)
|
230
272
|
if item.superclass == Instruction
|
231
273
|
@instructions_library.delete(item)
|
@@ -235,27 +277,37 @@ module Nudge
|
|
235
277
|
end
|
236
278
|
|
237
279
|
|
280
|
+
# Completely empties the set of active Instructions. The interpreter will recognize InstructionPoints,
|
281
|
+
# but will not invoke their #go methods when it does.
|
238
282
|
def disable_all_instructions
|
239
283
|
@instructions_library = Hash.new
|
240
284
|
end
|
241
285
|
|
242
286
|
|
287
|
+
# Completely empties the set of NudgeTypes in play. ValuePoints the Interpreter encounters will
|
288
|
+
# still be recognized in code, and will still be pushed to the appropriate stack, but new
|
289
|
+
# ValuePoints (made by various code-generating methods) will not be created.
|
243
290
|
def disable_all_types
|
244
291
|
@types = []
|
245
292
|
end
|
246
293
|
|
247
294
|
|
295
|
+
# Create a new sensor with the given name, binding the associated block argument. All sensors are
|
296
|
+
# called, in the order registered, when the Interpreter#run cycle terminates normally.
|
248
297
|
def register_sensor(name, &block)
|
249
298
|
raise(ArgumentError, "Sensor name #{name} is not a string") unless name.kind_of?(String)
|
250
299
|
@sensors[name] = block
|
251
300
|
end
|
252
301
|
|
253
302
|
|
303
|
+
# Delete all sensors.
|
254
304
|
def reset_sensors
|
255
305
|
@sensors = Hash.new
|
256
306
|
end
|
257
307
|
|
258
308
|
|
309
|
+
# Iterates through the Interpreter#sensors hash, #calling each one and passing in the current state
|
310
|
+
# of the Interpreter as an argument
|
259
311
|
def fire_all_sensors
|
260
312
|
@sensors.inject({}) do |result, (key, value)|
|
261
313
|
result[key] = @sensors[key].call(self)
|
data/readme.md
CHANGED
@@ -20,7 +20,7 @@ and getting something along the lines of `ruby 1.9.1p378 (2010-01-10 revision 26
|
|
20
20
|
|
21
21
|
gem install nudge
|
22
22
|
|
23
|
-
As of this writing, the `nudge` gem can be used as a library in your Ruby programs.
|
23
|
+
As of this writing, the `nudge` gem can be used as a library in your Ruby programs. So Real Soon Now, it'll be part of a more interesting gem…
|
24
24
|
|
25
25
|
### A test run
|
26
26
|
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 2
|
8
|
-
-
|
9
|
-
version: 0.2.
|
8
|
+
- 4
|
9
|
+
version: 0.2.4
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Bill Tozier
|
@@ -16,7 +16,7 @@ autorequire:
|
|
16
16
|
bindir: bin
|
17
17
|
cert_chain: []
|
18
18
|
|
19
|
-
date: 2010-04-
|
19
|
+
date: 2010-04-27 00:00:00 -04:00
|
20
20
|
default_executable: nudge
|
21
21
|
dependencies:
|
22
22
|
- !ruby/object:Gem::Dependency
|
@@ -33,19 +33,6 @@ dependencies:
|
|
33
33
|
version: 2.3.5
|
34
34
|
type: :runtime
|
35
35
|
version_requirements: *id001
|
36
|
-
- !ruby/object:Gem::Dependency
|
37
|
-
name: thor
|
38
|
-
prerelease: false
|
39
|
-
requirement: &id002 !ruby/object:Gem::Requirement
|
40
|
-
requirements:
|
41
|
-
- - ">="
|
42
|
-
- !ruby/object:Gem::Version
|
43
|
-
segments:
|
44
|
-
- 0
|
45
|
-
- 13
|
46
|
-
version: "0.13"
|
47
|
-
type: :runtime
|
48
|
-
version_requirements: *id002
|
49
36
|
description: Provides a Ruby library & CLI implementing a flexible Nudge Language interpreter, plus a set of generators for adding domain-specific instructions and types.
|
50
37
|
email: bill@vagueinnovation.com
|
51
38
|
executables:
|