ceml 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,345 @@
1
+ # Autogenerated from a Treetop grammar. Edits may be lost.
2
+
3
+
4
+ module CEML
5
+ module Scripts
6
+ include Treetop::Runtime
7
+
8
+ def root
9
+ @root || :scripts
10
+ end
11
+
12
+ include Lexer
13
+
14
+ include Casting
15
+
16
+ include Instructions
17
+
18
+ def _nt_scripts
19
+ start_index = index
20
+ if node_cache[:scripts].has_key?(index)
21
+ cached = node_cache[:scripts][index]
22
+ @index = cached.interval.end if cached
23
+ return cached
24
+ end
25
+
26
+ s0, i0 = [], index
27
+ loop do
28
+ r1 = _nt_script
29
+ if r1
30
+ s0 << r1
31
+ else
32
+ break
33
+ end
34
+ end
35
+ if s0.empty?
36
+ @index = i0
37
+ r0 = nil
38
+ else
39
+ r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
40
+ end
41
+
42
+ node_cache[:scripts][start_index] = r0
43
+
44
+ r0
45
+ end
46
+
47
+ def _nt_script
48
+ start_index = index
49
+ if node_cache[:script].has_key?(index)
50
+ cached = node_cache[:script][index]
51
+ @index = cached.interval.end if cached
52
+ return cached
53
+ end
54
+
55
+ i0 = index
56
+ r1 = _nt_script_with_title
57
+ if r1
58
+ r0 = r1
59
+ r0.extend(Script)
60
+ else
61
+ r2 = _nt_script_with_instructions
62
+ if r2
63
+ r0 = r2
64
+ r0.extend(Script)
65
+ else
66
+ @index = i0
67
+ r0 = nil
68
+ end
69
+ end
70
+
71
+ node_cache[:script][start_index] = r0
72
+
73
+ r0
74
+ end
75
+
76
+ module ScriptWithTitle0
77
+ def title
78
+ elements[1]
79
+ end
80
+
81
+ def casting_statement
82
+ elements[2]
83
+ end
84
+
85
+ def instructions
86
+ elements[3]
87
+ end
88
+ end
89
+
90
+ def _nt_script_with_title
91
+ start_index = index
92
+ if node_cache[:script_with_title].has_key?(index)
93
+ cached = node_cache[:script_with_title][index]
94
+ @index = cached.interval.end if cached
95
+ return cached
96
+ end
97
+
98
+ i0, s0 = index, []
99
+ r2 = _nt_nl
100
+ if r2
101
+ r1 = r2
102
+ else
103
+ r1 = instantiate_node(SyntaxNode,input, index...index)
104
+ end
105
+ s0 << r1
106
+ if r1
107
+ r3 = _nt_title
108
+ s0 << r3
109
+ if r3
110
+ r5 = _nt_casting_statement
111
+ if r5
112
+ r4 = r5
113
+ else
114
+ r4 = instantiate_node(SyntaxNode,input, index...index)
115
+ end
116
+ s0 << r4
117
+ if r4
118
+ r7 = _nt_instructions
119
+ if r7
120
+ r6 = r7
121
+ else
122
+ r6 = instantiate_node(SyntaxNode,input, index...index)
123
+ end
124
+ s0 << r6
125
+ end
126
+ end
127
+ end
128
+ if s0.last
129
+ r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
130
+ r0.extend(ScriptWithTitle0)
131
+ else
132
+ @index = i0
133
+ r0 = nil
134
+ end
135
+
136
+ node_cache[:script_with_title][start_index] = r0
137
+
138
+ r0
139
+ end
140
+
141
+ module ScriptWithInstructions0
142
+ def title
143
+ elements[1]
144
+ end
145
+
146
+ def casting_statement
147
+ elements[2]
148
+ end
149
+
150
+ def instructions
151
+ elements[3]
152
+ end
153
+ end
154
+
155
+ def _nt_script_with_instructions
156
+ start_index = index
157
+ if node_cache[:script_with_instructions].has_key?(index)
158
+ cached = node_cache[:script_with_instructions][index]
159
+ @index = cached.interval.end if cached
160
+ return cached
161
+ end
162
+
163
+ i0, s0 = index, []
164
+ r2 = _nt_nl
165
+ if r2
166
+ r1 = r2
167
+ else
168
+ r1 = instantiate_node(SyntaxNode,input, index...index)
169
+ end
170
+ s0 << r1
171
+ if r1
172
+ r4 = _nt_title
173
+ if r4
174
+ r3 = r4
175
+ else
176
+ r3 = instantiate_node(SyntaxNode,input, index...index)
177
+ end
178
+ s0 << r3
179
+ if r3
180
+ r6 = _nt_casting_statement
181
+ if r6
182
+ r5 = r6
183
+ else
184
+ r5 = instantiate_node(SyntaxNode,input, index...index)
185
+ end
186
+ s0 << r5
187
+ if r5
188
+ r7 = _nt_instructions
189
+ s0 << r7
190
+ end
191
+ end
192
+ end
193
+ if s0.last
194
+ r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
195
+ r0.extend(ScriptWithInstructions0)
196
+ else
197
+ @index = i0
198
+ r0 = nil
199
+ end
200
+
201
+ node_cache[:script_with_instructions][start_index] = r0
202
+
203
+ r0
204
+ end
205
+
206
+ module Title0
207
+ end
208
+
209
+ module Title1
210
+ def value
211
+ elements[1]
212
+ end
213
+
214
+ def nl
215
+ elements[3]
216
+ end
217
+ end
218
+
219
+ module Title2
220
+ def value
221
+ super.text_value.gsub(/\\"/, '"')
222
+ end
223
+ end
224
+
225
+ def _nt_title
226
+ start_index = index
227
+ if node_cache[:title].has_key?(index)
228
+ cached = node_cache[:title][index]
229
+ @index = cached.interval.end if cached
230
+ return cached
231
+ end
232
+
233
+ i0, s0 = index, []
234
+ if has_terminal?('"', false, index)
235
+ r1 = instantiate_node(SyntaxNode,input, index...(index + 1))
236
+ @index += 1
237
+ else
238
+ terminal_parse_failure('"')
239
+ r1 = nil
240
+ end
241
+ s0 << r1
242
+ if r1
243
+ s2, i2 = [], index
244
+ loop do
245
+ i3 = index
246
+ if has_terminal?('\\"', false, index)
247
+ r4 = instantiate_node(SyntaxNode,input, index...(index + 2))
248
+ @index += 2
249
+ else
250
+ terminal_parse_failure('\\"')
251
+ r4 = nil
252
+ end
253
+ if r4
254
+ r3 = r4
255
+ else
256
+ i5, s5 = index, []
257
+ i6 = index
258
+ if has_terminal?('"', false, index)
259
+ r7 = instantiate_node(SyntaxNode,input, index...(index + 1))
260
+ @index += 1
261
+ else
262
+ terminal_parse_failure('"')
263
+ r7 = nil
264
+ end
265
+ if r7
266
+ r6 = nil
267
+ else
268
+ @index = i6
269
+ r6 = instantiate_node(SyntaxNode,input, index...index)
270
+ end
271
+ s5 << r6
272
+ if r6
273
+ if index < input_length
274
+ next_character = index + input[index..-1].match(/\A(.)/um).end(1)
275
+ r8 = instantiate_node(SyntaxNode,input, index...next_character)
276
+ @index = next_character
277
+ else
278
+ terminal_parse_failure("any character")
279
+ r8 = nil
280
+ end
281
+ s5 << r8
282
+ end
283
+ if s5.last
284
+ r5 = instantiate_node(SyntaxNode,input, i5...index, s5)
285
+ r5.extend(Title0)
286
+ else
287
+ @index = i5
288
+ r5 = nil
289
+ end
290
+ if r5
291
+ r3 = r5
292
+ else
293
+ @index = i3
294
+ r3 = nil
295
+ end
296
+ end
297
+ if r3
298
+ s2 << r3
299
+ else
300
+ break
301
+ end
302
+ end
303
+ if s2.empty?
304
+ @index = i2
305
+ r2 = nil
306
+ else
307
+ r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
308
+ end
309
+ s0 << r2
310
+ if r2
311
+ if has_terminal?('"', false, index)
312
+ r9 = instantiate_node(SyntaxNode,input, index...(index + 1))
313
+ @index += 1
314
+ else
315
+ terminal_parse_failure('"')
316
+ r9 = nil
317
+ end
318
+ s0 << r9
319
+ if r9
320
+ r10 = _nt_nl
321
+ s0 << r10
322
+ end
323
+ end
324
+ end
325
+ if s0.last
326
+ r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
327
+ r0.extend(Title1)
328
+ r0.extend(Title2)
329
+ else
330
+ @index = i0
331
+ r0 = nil
332
+ end
333
+
334
+ node_cache[:title][start_index] = r0
335
+
336
+ r0
337
+ end
338
+
339
+ end
340
+
341
+ class ScriptsParser < Treetop::Runtime::CompiledParser
342
+ include Scripts
343
+ end
344
+
345
+ end
@@ -0,0 +1,32 @@
1
+ module CEML
2
+ grammar Scripts
3
+ include Lexer
4
+ include Casting
5
+ include Instructions
6
+
7
+ rule scripts
8
+ script+
9
+ end
10
+
11
+ rule script
12
+ (script_with_title / script_with_instructions) <Script>
13
+ end
14
+
15
+ rule script_with_title
16
+ nl? title casting_statement:casting_statement? instructions:instructions?
17
+ end
18
+
19
+ rule script_with_instructions
20
+ nl? title:title? casting_statement:casting_statement? instructions
21
+ end
22
+
23
+ rule title
24
+ '"' value:('\\"' / !'"' .)+ '"' nl {
25
+ def value
26
+ super.text_value.gsub(/\\"/, '"')
27
+ end
28
+ }
29
+ end
30
+
31
+ end
32
+ end
data/lib/ceml.rb ADDED
@@ -0,0 +1,35 @@
1
+ require 'forwardable'
2
+ require 'treetop'
3
+
4
+ require 'ceml/casting'
5
+ require 'ceml/instructions'
6
+ require 'ceml/script'
7
+ require 'ceml/tt/lexer'
8
+ require 'ceml/tt/casting'
9
+ require 'ceml/tt/instructions'
10
+ require 'ceml/tt/scripts'
11
+
12
+ require 'ceml/engine'
13
+
14
+ module CEML
15
+ def parse(what, string)
16
+ result = nil
17
+ string.gsub!(/\n +/, ' ')
18
+ string << "\n"
19
+ p = ScriptsParser.new
20
+ p.root = what
21
+ result = p.parse(string)
22
+ raise "parse failed: \n#{p.failure_reason}" unless result
23
+ case what
24
+ when :scripts
25
+ raise "no scripts found" unless result.elements and !result.elements.empty?
26
+ result = result.elements
27
+ result.each{ |s| s.validate! }
28
+ when :script
29
+ result.validate!
30
+ end
31
+ result
32
+ end
33
+
34
+ extend self
35
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,55 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+
4
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
5
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
6
+ require 'ceml'
7
+
8
+ COMPLIMENT_SCRIPT = <<END_OF_SCRIPT
9
+ "Overwhelm a specific person with compliments"
10
+ gather 5-20 players within 4 blocks
11
+ ask organizer re target: Describe their appearance and location
12
+ tell agents: Look for |otherguy.target| and compliment them briefly, then move on.
13
+ END_OF_SCRIPT
14
+
15
+ ASKCHAIN_SCRIPT = <<ENDOFSCRIPT
16
+ "Meet your neighbor"
17
+ gather 2 players within 1 block
18
+ ask players re color: what's your favorite color?
19
+ ask players re observation: find someone near you with the color |otherguy.color|. what are they wearing?
20
+ ask players re rightmatch: are you wearing |otherguy.observation|?
21
+ ask players re task: take your new partner and see if you can find something beautiful in the park.
22
+
23
+ ENDOFSCRIPT
24
+
25
+
26
+
27
+ class Test::Unit::TestCase
28
+ def play script
29
+ @e = CEML::Engine.new(script)
30
+ end
31
+
32
+ def player id, *roles
33
+ @e.add id, *roles
34
+ @e.run
35
+ end
36
+
37
+ def asked id, rx
38
+ p = @e.parts[id]
39
+ assert_equal :ask, p[:said]
40
+ assert_match rx, p[:q]
41
+ p.delete :said
42
+ end
43
+
44
+ def told id, rx
45
+ p = @e.parts[id]
46
+ assert_match rx, p[:msg]
47
+ p.delete :said
48
+ end
49
+
50
+ def says id, str
51
+ @e.parts[id][:received] = str
52
+ @e.run
53
+ @e.parts[id][:received] = nil
54
+ end
55
+ end
@@ -0,0 +1,78 @@
1
+ require 'test/unit'
2
+ require 'ceml'
3
+
4
+ class TestCasting < Test::Unit::TestCase
5
+ def pcs text
6
+ CEML.parse(:casting_statement, text)
7
+ end
8
+
9
+ def assert_bad text
10
+ assert_raise(RuntimeError){ pcs text }
11
+ end
12
+
13
+ def test_bad
14
+ assert_bad "gather a-b runners"
15
+ assert_bad "gather 40a runners"
16
+ assert_bad "gather 3- runners"
17
+ assert_bad "gather -4 runners"
18
+ assert_bad "grab 4 runners"
19
+ assert_bad "gather 4 *runners"
20
+ assert_bad "gather 4 run*ners"
21
+ assert_bad "gather 4 runners*"
22
+ end
23
+
24
+ def test_range
25
+ cs = pcs "gather 3-4 runners"
26
+ assert cs.type == :gather
27
+ assert cs.roles.include? :runners
28
+ assert cs[:runners].min == 3
29
+ assert cs[:runners].max == 4
30
+ assert cs.max == 4
31
+
32
+ cs = pcs "gather 3+ runners"
33
+ assert cs.type == :gather
34
+ assert cs.roles.include? :runners
35
+ assert cs[:runners].min == 3
36
+ assert cs[:runners].max > 4
37
+ assert cs.min == 3
38
+ assert cs.max > 4
39
+
40
+ cs = pcs "gather 3 runners"
41
+ assert cs.type == :gather
42
+ assert cs.roles.include? :runners
43
+ assert cs[:runners].min == 3
44
+ assert cs[:runners].max == 3
45
+ assert cs.min == 3
46
+ assert cs.max == 3
47
+
48
+ cs = pcs "gather runners"
49
+ assert cs.type == :gather
50
+ assert cs.roles.include? :runners
51
+ assert cs[:runners].min == 2
52
+ assert cs[:runners].max > 10
53
+ assert cs.min == 2
54
+ assert cs.max > 10
55
+
56
+ cs = pcs "teams of 1-2 runners and 1 announcer"
57
+ assert cs.type == :teams
58
+ assert cs.roles.include? :runners
59
+ assert cs.roles.include? :announcer
60
+ assert cs[:runners].min == 1
61
+ assert cs[:runners].max == 2
62
+ assert cs.min == 2
63
+ assert cs.max > 20
64
+
65
+ cs = pcs "nab 1-2 runners within 4 mi"
66
+ assert cs.type == :nab
67
+ assert cs.nab?
68
+ assert !cs.in_teams?
69
+ assert cs[:runners].min == 1
70
+ assert cs[:runners].max == 2
71
+ end
72
+
73
+ def test_radius
74
+ assert_equal 600, pcs("gather 3 runners and 5 hot_babes within 3 blocks").radius
75
+ assert pcs("gather 3 runners and 5 hot_babes").radius > 50000
76
+ end
77
+
78
+ end
@@ -0,0 +1,30 @@
1
+ require 'ceml'
2
+ require 'test/helper'
3
+
4
+ class TestEngine < Test::Unit::TestCase
5
+
6
+ def test_engine
7
+ play COMPLIMENT_SCRIPT
8
+ player :joe, :organizer, :agent
9
+ player :bill, :agent
10
+
11
+ asked :joe, /^Describe/
12
+ says :joe, 'red people'
13
+
14
+ told :bill, /^Look for red people/
15
+ end
16
+
17
+ def test_askchain
18
+ play ASKCHAIN_SCRIPT
19
+ player :joe, :players, :agent
20
+ player :bill, :players, :agent
21
+
22
+ asked :joe, /favorite color/
23
+ asked :bill, /favorite color/
24
+ says :joe, "red"
25
+ says :bill, "green"
26
+ asked :joe, /with the color green/
27
+ asked :bill, /with the color red/
28
+ end
29
+
30
+ end
@@ -0,0 +1,27 @@
1
+ require 'ceml'
2
+
3
+ class TestInstructions < Test::Unit::TestCase
4
+ def pi text
5
+ CEML.parse(:instructions, text)
6
+ end
7
+
8
+ def assert_bad text
9
+ assert_raise(RuntimeError){ pi text }
10
+ end
11
+
12
+ def test_bad
13
+ assert_bad "love your mother"
14
+ assert_bad "tell jim re susan: i hate you"
15
+ assert_bad "tell phil"
16
+ assert_bad "ask susan re clothing"
17
+ assert_bad "ask susan re clothing: "
18
+ end
19
+
20
+ def test_instructions
21
+ assert_equal "run to the kitchen", pi('tell joe: run to the kitchen').tell([:joe]).text
22
+ assert_equal ["favorite color?", "favorite soup?"], pi(
23
+ "ask joe re color: favorite color?\nask joe re soup: favorite soup?"
24
+ ).asks([:joe]).map(&:text)
25
+ end
26
+
27
+ end
@@ -0,0 +1,57 @@
1
+ require 'ceml'
2
+
3
+ class TestInstructions < Test::Unit::TestCase
4
+
5
+ SCRIPTS = <<END_OF_SCRIPTS
6
+ "Help moving |an object|" // okay?
7
+ gather 2-6 movers within 8 blocks
8
+
9
+ "Cleaning up |a public place|"
10
+ gather 3-10 cleaners within 2 miles
11
+
12
+ // the following are dedicated to my mom
13
+
14
+ "Overwhelm a specific person with compliments"
15
+ gather 5-20 players within 4 blocks
16
+ ask organizer re target: Describe their appearance and location
17
+ tell players: Look for |target| and compliment them briefly, then move on.
18
+ END_OF_SCRIPTS
19
+
20
+ def ps text
21
+ CEML.parse(:scripts, text)
22
+ end
23
+
24
+ def test_scripts
25
+ s = ps SCRIPTS
26
+ assert s.size == 3
27
+ s0 = s[0]
28
+ assert_equal "Help moving |an object|", s0.title
29
+ assert s0.allowed_roles.include? :movers
30
+ assert s0.radius == 1600
31
+ assert s0.dramatis_personae.min == 2
32
+ assert s0.dramatis_personae.max == 6
33
+ end
34
+
35
+ def test_ceml_title
36
+ s = CEML.parse(:script, '"hello there"')
37
+ assert_equal "hello there", s.title
38
+
39
+ s = CEML.parse(:script, %q{"say: \"whoa the'a paps\""})
40
+ assert_equal %Q{say: "whoa the'a paps"}, s.title
41
+ end
42
+
43
+ def test_ceml_tell
44
+ cs = CEML.parse(:script, "tell agents: run and jump")
45
+ assert cs.roles.include? :agents
46
+ assert_equal "run and jump", cs.instructions.tell([:agents]).text
47
+ assert cs.concludes_immediately?
48
+ assert !cs.title
49
+ end
50
+
51
+ def test_ceml_questions
52
+ s = CEML.parse(:script, "ask agents: wassup party people?")
53
+ assert_equal "wassup party people?", s.instructions.asks([:agents]).first.text
54
+ assert !s.concludes_immediately?
55
+ end
56
+
57
+ end