ceml 0.2.0

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.
@@ -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