confabulator 0.0.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.
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use 1.9.2@confabulator --create
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in confabulator.gemspec
4
+ gemspec
@@ -0,0 +1,34 @@
1
+ # Confabulator
2
+
3
+ A Ruby grammar for procedural generation of random sentences.
4
+
5
+ ## Choice blocks
6
+
7
+ > 5.times { puts Confabulator::Parser.new("{Choice one|Choice two} and stuff").confabulate }
8
+ Choice one and stuff
9
+ Choice one and stuff
10
+ ...
11
+ # Recursion is fine!
12
+ > 5.times { puts Confabulator::Parser.new("{Choice {1|2}|Choice 3} and stuff").confabulate }
13
+ Choice 1 and stuff
14
+ Choice 2 and stuff
15
+ Choice 3 and stuff
16
+ ...
17
+ # You can differentially weight the options: {5:This is 5 times more likely|than this}
18
+
19
+ ## Substitutions
20
+
21
+ > knowledge = Confabulator::Knowledge.new
22
+ > knowledge.add "world", "there"
23
+ > Confabulator::Parser.new("Hello, [world]!", :knowledge => knowledge)
24
+ => "Hello, there!"
25
+ # Equivalently:
26
+ > knowledge.confabulate("Hello, [world]!")
27
+ => "Hello, there!"
28
+ # You can ask a substitution to be capitalized:
29
+ > knowledge.confabulate("Hello, [world:c]!")
30
+ => "Hello, There!"
31
+ # Or pluralized:
32
+ > knowledge.add "dude", "friend"
33
+ > knowledge.confabulate("Hello, [dude:p]!")
34
+ => "Hello, friends!"
@@ -0,0 +1,13 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
7
+
8
+ desc "Rebuild the TreeTop grammer parser"
9
+ task :rebuild do
10
+ grammer = File.expand_path(File.join(File.dirname(__FILE__), "src", "confabulator_language.treetop"))
11
+ output = File.expand_path(File.join(File.dirname(__FILE__), "lib", "confabulator", "language.rb"))
12
+ puts `tt #{grammer} -o #{output}`
13
+ end
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "confabulator/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "confabulator"
7
+ s.version = Confabulator::VERSION
8
+ s.authors = ["Andrew Cantino"]
9
+ s.email = ["andrew@iterationlabs.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{Ruby generative grammer for conversational text}
12
+ s.description = %q{}
13
+
14
+ s.rubyforge_project = "confabulator"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here; for example:
22
+ s.add_development_dependency "rspec"
23
+ s.add_runtime_dependency "treetop"
24
+ s.add_runtime_dependency "linguistics"
25
+ end
@@ -0,0 +1,6 @@
1
+ require 'treetop'
2
+ require "confabulator/version"
3
+ require 'confabulator/language'
4
+ require 'confabulator/parser'
5
+ require 'confabulator/knowledge'
6
+ require 'pp'
@@ -0,0 +1,36 @@
1
+ module Confabulator
2
+ class Knowledge
3
+ def initialize
4
+ end
5
+
6
+ def find(name)
7
+ knowledge[name] || empty_confabulator
8
+ end
9
+
10
+ def add(name, sentence = nil)
11
+ if name.is_a?(Hash)
12
+ name.each do |n, s|
13
+ knowledge[n] = Confabulator::Parser.new(s, :knowledge => self)
14
+ end
15
+ else
16
+ knowledge[name] = Confabulator::Parser.new(sentence, :knowledge => self)
17
+ end
18
+ end
19
+
20
+ def empty_confabulator
21
+ @empty ||= Confabulator::Parser.new("", :knowledge => self)
22
+ end
23
+
24
+ def knowledge
25
+ @knowledge ||= {}
26
+ end
27
+
28
+ def clear
29
+ @knowledge = {}
30
+ end
31
+
32
+ def confabulate(sentence)
33
+ Parser.new(sentence, :knowledge => self).confabulate
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,695 @@
1
+ # Autogenerated from a Treetop grammar. Edits may be lost.
2
+
3
+
4
+ module Confabulator
5
+ module ConfabulatorLanguage
6
+ include Treetop::Runtime
7
+
8
+ def root
9
+ @root ||= :sentence
10
+ end
11
+
12
+ module Sentence0
13
+ def compose(kb = nil)
14
+ elements.map {|e| e.compose(kb) }.join
15
+ end
16
+ end
17
+
18
+ def _nt_sentence
19
+ start_index = index
20
+ if node_cache[:sentence].has_key?(index)
21
+ cached = node_cache[:sentence][index]
22
+ if cached
23
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
24
+ @index = cached.interval.end
25
+ end
26
+ return cached
27
+ end
28
+
29
+ s0, i0 = [], index
30
+ loop do
31
+ i1 = index
32
+ r2 = _nt_substitution
33
+ if r2
34
+ r1 = r2
35
+ else
36
+ r3 = _nt_choice
37
+ if r3
38
+ r1 = r3
39
+ else
40
+ r4 = _nt_escaped_char
41
+ if r4
42
+ r1 = r4
43
+ else
44
+ r5 = _nt_words
45
+ if r5
46
+ r1 = r5
47
+ else
48
+ @index = i1
49
+ r1 = nil
50
+ end
51
+ end
52
+ end
53
+ end
54
+ if r1
55
+ s0 << r1
56
+ else
57
+ break
58
+ end
59
+ end
60
+ if s0.empty?
61
+ @index = i0
62
+ r0 = nil
63
+ else
64
+ r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
65
+ r0.extend(Sentence0)
66
+ end
67
+
68
+ node_cache[:sentence][start_index] = r0
69
+
70
+ r0
71
+ end
72
+
73
+ module Choice0
74
+ def weight
75
+ elements[0]
76
+ end
77
+
78
+ def sentence
79
+ elements[1]
80
+ end
81
+ end
82
+
83
+ module Choice1
84
+ def weight
85
+ elements[1]
86
+ end
87
+
88
+ def sentence
89
+ elements[2]
90
+ end
91
+ end
92
+
93
+ module Choice2
94
+ def first_sentence
95
+ elements[1]
96
+ end
97
+
98
+ def rest_sentences
99
+ elements[2]
100
+ end
101
+
102
+ end
103
+
104
+ module Choice3
105
+ def compose(kb = nil)
106
+ elems = []
107
+ (first_sentence.weight.empty? ? 1 : first_sentence.weight.value).times { elems << first_sentence.sentence }
108
+ rest_sentences.elements.each do |s|
109
+ (s.weight.empty? ? 1 : s.weight.value).times { elems << s.sentence }
110
+ end
111
+ elems[elems.length * rand].compose(kb)
112
+ end
113
+ end
114
+
115
+ def _nt_choice
116
+ start_index = index
117
+ if node_cache[:choice].has_key?(index)
118
+ cached = node_cache[:choice][index]
119
+ if cached
120
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
121
+ @index = cached.interval.end
122
+ end
123
+ return cached
124
+ end
125
+
126
+ i0, s0 = index, []
127
+ if has_terminal?('{', false, index)
128
+ r1 = instantiate_node(SyntaxNode,input, index...(index + 1))
129
+ @index += 1
130
+ else
131
+ terminal_parse_failure('{')
132
+ r1 = nil
133
+ end
134
+ s0 << r1
135
+ if r1
136
+ i2, s2 = index, []
137
+ r4 = _nt_weight
138
+ if r4
139
+ r3 = r4
140
+ else
141
+ r3 = instantiate_node(SyntaxNode,input, index...index)
142
+ end
143
+ s2 << r3
144
+ if r3
145
+ r5 = _nt_sentence
146
+ s2 << r5
147
+ end
148
+ if s2.last
149
+ r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
150
+ r2.extend(Choice0)
151
+ else
152
+ @index = i2
153
+ r2 = nil
154
+ end
155
+ s0 << r2
156
+ if r2
157
+ s6, i6 = [], index
158
+ loop do
159
+ i7, s7 = index, []
160
+ if has_terminal?('|', false, index)
161
+ r8 = instantiate_node(SyntaxNode,input, index...(index + 1))
162
+ @index += 1
163
+ else
164
+ terminal_parse_failure('|')
165
+ r8 = nil
166
+ end
167
+ s7 << r8
168
+ if r8
169
+ r10 = _nt_weight
170
+ if r10
171
+ r9 = r10
172
+ else
173
+ r9 = instantiate_node(SyntaxNode,input, index...index)
174
+ end
175
+ s7 << r9
176
+ if r9
177
+ r11 = _nt_sentence
178
+ s7 << r11
179
+ end
180
+ end
181
+ if s7.last
182
+ r7 = instantiate_node(SyntaxNode,input, i7...index, s7)
183
+ r7.extend(Choice1)
184
+ else
185
+ @index = i7
186
+ r7 = nil
187
+ end
188
+ if r7
189
+ s6 << r7
190
+ else
191
+ break
192
+ end
193
+ end
194
+ r6 = instantiate_node(SyntaxNode,input, i6...index, s6)
195
+ s0 << r6
196
+ if r6
197
+ if has_terminal?('}', false, index)
198
+ r12 = instantiate_node(SyntaxNode,input, index...(index + 1))
199
+ @index += 1
200
+ else
201
+ terminal_parse_failure('}')
202
+ r12 = nil
203
+ end
204
+ s0 << r12
205
+ end
206
+ end
207
+ end
208
+ if s0.last
209
+ r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
210
+ r0.extend(Choice2)
211
+ r0.extend(Choice3)
212
+ else
213
+ @index = i0
214
+ r0 = nil
215
+ end
216
+
217
+ node_cache[:choice][start_index] = r0
218
+
219
+ r0
220
+ end
221
+
222
+ module Weight0
223
+ def w
224
+ elements[0]
225
+ end
226
+
227
+ end
228
+
229
+ module Weight1
230
+ def value
231
+ w.text_value.to_i
232
+ end
233
+ end
234
+
235
+ def _nt_weight
236
+ start_index = index
237
+ if node_cache[:weight].has_key?(index)
238
+ cached = node_cache[:weight][index]
239
+ if cached
240
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
241
+ @index = cached.interval.end
242
+ end
243
+ return cached
244
+ end
245
+
246
+ i0, s0 = index, []
247
+ s1, i1 = [], index
248
+ loop do
249
+ if has_terminal?('\G[0-9]', true, index)
250
+ r2 = true
251
+ @index += 1
252
+ else
253
+ r2 = nil
254
+ end
255
+ if r2
256
+ s1 << r2
257
+ else
258
+ break
259
+ end
260
+ end
261
+ if s1.empty?
262
+ @index = i1
263
+ r1 = nil
264
+ else
265
+ r1 = instantiate_node(SyntaxNode,input, i1...index, s1)
266
+ end
267
+ s0 << r1
268
+ if r1
269
+ if has_terminal?(':', false, index)
270
+ r3 = instantiate_node(SyntaxNode,input, index...(index + 1))
271
+ @index += 1
272
+ else
273
+ terminal_parse_failure(':')
274
+ r3 = nil
275
+ end
276
+ s0 << r3
277
+ end
278
+ if s0.last
279
+ r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
280
+ r0.extend(Weight0)
281
+ r0.extend(Weight1)
282
+ else
283
+ @index = i0
284
+ r0 = nil
285
+ end
286
+
287
+ node_cache[:weight][start_index] = r0
288
+
289
+ r0
290
+ end
291
+
292
+ module Substitution0
293
+ end
294
+
295
+ module Substitution1
296
+ end
297
+
298
+ module Substitution2
299
+ def w1
300
+ elements[2]
301
+ end
302
+
303
+ def name
304
+ elements[3]
305
+ end
306
+
307
+ def w2
308
+ elements[4]
309
+ end
310
+
311
+ def options
312
+ elements[5]
313
+ end
314
+
315
+ def w3
316
+ elements[6]
317
+ end
318
+
319
+ end
320
+
321
+ module Substitution3
322
+ def compose(kb = nil)
323
+ if kb
324
+ result = kb.find(name.text_value).confabulate
325
+ if options.text_value =~ /p/
326
+ result = result.en.plural
327
+ elsif options.text_value =~ /c/
328
+ result[0] = result[0].upcase if result[0]
329
+ end
330
+ result
331
+ else
332
+ ""
333
+ end
334
+ end
335
+ end
336
+
337
+ def _nt_substitution
338
+ start_index = index
339
+ if node_cache[:substitution].has_key?(index)
340
+ cached = node_cache[:substitution][index]
341
+ if cached
342
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
343
+ @index = cached.interval.end
344
+ end
345
+ return cached
346
+ end
347
+
348
+ i0, s0 = index, []
349
+ i1 = index
350
+ if has_terminal?('\\\\', false, index)
351
+ r2 = instantiate_node(SyntaxNode,input, index...(index + 2))
352
+ @index += 2
353
+ else
354
+ terminal_parse_failure('\\\\')
355
+ r2 = nil
356
+ end
357
+ if r2
358
+ r1 = nil
359
+ else
360
+ @index = i1
361
+ r1 = instantiate_node(SyntaxNode,input, index...index)
362
+ end
363
+ s0 << r1
364
+ if r1
365
+ if has_terminal?('[', false, index)
366
+ r3 = instantiate_node(SyntaxNode,input, index...(index + 1))
367
+ @index += 1
368
+ else
369
+ terminal_parse_failure('[')
370
+ r3 = nil
371
+ end
372
+ s0 << r3
373
+ if r3
374
+ r4 = _nt_w
375
+ s0 << r4
376
+ if r4
377
+ i5, s5 = index, []
378
+ if has_terminal?('\G[a-zA-Z]', true, index)
379
+ r6 = true
380
+ @index += 1
381
+ else
382
+ r6 = nil
383
+ end
384
+ s5 << r6
385
+ if r6
386
+ s7, i7 = [], index
387
+ loop do
388
+ if has_terminal?('\G[a-zA-Z_0-9-]', true, index)
389
+ r8 = true
390
+ @index += 1
391
+ else
392
+ r8 = nil
393
+ end
394
+ if r8
395
+ s7 << r8
396
+ else
397
+ break
398
+ end
399
+ end
400
+ r7 = instantiate_node(SyntaxNode,input, i7...index, s7)
401
+ s5 << r7
402
+ end
403
+ if s5.last
404
+ r5 = instantiate_node(SyntaxNode,input, i5...index, s5)
405
+ r5.extend(Substitution0)
406
+ else
407
+ @index = i5
408
+ r5 = nil
409
+ end
410
+ s0 << r5
411
+ if r5
412
+ r9 = _nt_w
413
+ s0 << r9
414
+ if r9
415
+ i11, s11 = index, []
416
+ if has_terminal?(":", false, index)
417
+ r12 = instantiate_node(SyntaxNode,input, index...(index + 1))
418
+ @index += 1
419
+ else
420
+ terminal_parse_failure(":")
421
+ r12 = nil
422
+ end
423
+ s11 << r12
424
+ if r12
425
+ s13, i13 = [], index
426
+ loop do
427
+ if has_terminal?('\G[a-zA-Z]', true, index)
428
+ r14 = true
429
+ @index += 1
430
+ else
431
+ r14 = nil
432
+ end
433
+ if r14
434
+ s13 << r14
435
+ else
436
+ break
437
+ end
438
+ end
439
+ if s13.empty?
440
+ @index = i13
441
+ r13 = nil
442
+ else
443
+ r13 = instantiate_node(SyntaxNode,input, i13...index, s13)
444
+ end
445
+ s11 << r13
446
+ end
447
+ if s11.last
448
+ r11 = instantiate_node(SyntaxNode,input, i11...index, s11)
449
+ r11.extend(Substitution1)
450
+ else
451
+ @index = i11
452
+ r11 = nil
453
+ end
454
+ if r11
455
+ r10 = r11
456
+ else
457
+ r10 = instantiate_node(SyntaxNode,input, index...index)
458
+ end
459
+ s0 << r10
460
+ if r10
461
+ r15 = _nt_w
462
+ s0 << r15
463
+ if r15
464
+ if has_terminal?(']', false, index)
465
+ r16 = instantiate_node(SyntaxNode,input, index...(index + 1))
466
+ @index += 1
467
+ else
468
+ terminal_parse_failure(']')
469
+ r16 = nil
470
+ end
471
+ s0 << r16
472
+ end
473
+ end
474
+ end
475
+ end
476
+ end
477
+ end
478
+ end
479
+ if s0.last
480
+ r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
481
+ r0.extend(Substitution2)
482
+ r0.extend(Substitution3)
483
+ else
484
+ @index = i0
485
+ r0 = nil
486
+ end
487
+
488
+ node_cache[:substitution][start_index] = r0
489
+
490
+ r0
491
+ end
492
+
493
+ module W0
494
+ def compose(kb = nil)
495
+ text_value
496
+ end
497
+ end
498
+
499
+ def _nt_w
500
+ start_index = index
501
+ if node_cache[:w].has_key?(index)
502
+ cached = node_cache[:w][index]
503
+ if cached
504
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
505
+ @index = cached.interval.end
506
+ end
507
+ return cached
508
+ end
509
+
510
+ s0, i0 = [], index
511
+ loop do
512
+ if has_terminal?('\G[ \\t]', true, index)
513
+ r1 = true
514
+ @index += 1
515
+ else
516
+ r1 = nil
517
+ end
518
+ if r1
519
+ s0 << r1
520
+ else
521
+ break
522
+ end
523
+ end
524
+ r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
525
+ r0.extend(W0)
526
+
527
+ node_cache[:w][start_index] = r0
528
+
529
+ r0
530
+ end
531
+
532
+ module EscapedChar0
533
+ end
534
+
535
+ module EscapedChar1
536
+ def compose(kb = nil)
537
+ text_value
538
+ end
539
+ end
540
+
541
+ def _nt_escaped_char
542
+ start_index = index
543
+ if node_cache[:escaped_char].has_key?(index)
544
+ cached = node_cache[:escaped_char][index]
545
+ if cached
546
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
547
+ @index = cached.interval.end
548
+ end
549
+ return cached
550
+ end
551
+
552
+ i0, s0 = index, []
553
+ if has_terminal?('\\', false, index)
554
+ r1 = instantiate_node(SyntaxNode,input, index...(index + 1))
555
+ @index += 1
556
+ else
557
+ terminal_parse_failure('\\')
558
+ r1 = nil
559
+ end
560
+ s0 << r1
561
+ if r1
562
+ if index < input_length
563
+ r2 = instantiate_node(SyntaxNode,input, index...(index + 1))
564
+ @index += 1
565
+ else
566
+ terminal_parse_failure("any character")
567
+ r2 = nil
568
+ end
569
+ s0 << r2
570
+ end
571
+ if s0.last
572
+ r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
573
+ r0.extend(EscapedChar0)
574
+ r0.extend(EscapedChar1)
575
+ else
576
+ @index = i0
577
+ r0 = nil
578
+ end
579
+
580
+ node_cache[:escaped_char][start_index] = r0
581
+
582
+ r0
583
+ end
584
+
585
+ module Words0
586
+ def compose(kb = nil)
587
+ text_value
588
+ end
589
+ end
590
+
591
+ def _nt_words
592
+ start_index = index
593
+ if node_cache[:words].has_key?(index)
594
+ cached = node_cache[:words][index]
595
+ if cached
596
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
597
+ @index = cached.interval.end
598
+ end
599
+ return cached
600
+ end
601
+
602
+ s0, i0 = [], index
603
+ loop do
604
+ if has_terminal?('\G[^\\[{}\\|\\\\]', true, index)
605
+ r1 = true
606
+ @index += 1
607
+ else
608
+ r1 = nil
609
+ end
610
+ if r1
611
+ s0 << r1
612
+ else
613
+ break
614
+ end
615
+ end
616
+ if s0.empty?
617
+ @index = i0
618
+ r0 = nil
619
+ else
620
+ r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
621
+ r0.extend(Words0)
622
+ end
623
+
624
+ node_cache[:words][start_index] = r0
625
+
626
+ r0
627
+ end
628
+
629
+ module Char0
630
+ end
631
+
632
+ module Char1
633
+ def compose(kb = nil)
634
+ text_value
635
+ end
636
+ end
637
+
638
+ def _nt_char
639
+ start_index = index
640
+ if node_cache[:char].has_key?(index)
641
+ cached = node_cache[:char][index]
642
+ if cached
643
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
644
+ @index = cached.interval.end
645
+ end
646
+ return cached
647
+ end
648
+
649
+ i0, s0 = index, []
650
+ i1 = index
651
+ if has_terminal?('\\\\', false, index)
652
+ r2 = instantiate_node(SyntaxNode,input, index...(index + 2))
653
+ @index += 2
654
+ else
655
+ terminal_parse_failure('\\\\')
656
+ r2 = nil
657
+ end
658
+ if r2
659
+ r1 = nil
660
+ else
661
+ @index = i1
662
+ r1 = instantiate_node(SyntaxNode,input, index...index)
663
+ end
664
+ s0 << r1
665
+ if r1
666
+ if index < input_length
667
+ r3 = instantiate_node(SyntaxNode,input, index...(index + 1))
668
+ @index += 1
669
+ else
670
+ terminal_parse_failure("any character")
671
+ r3 = nil
672
+ end
673
+ s0 << r3
674
+ end
675
+ if s0.last
676
+ r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
677
+ r0.extend(Char0)
678
+ r0.extend(Char1)
679
+ else
680
+ @index = i0
681
+ r0 = nil
682
+ end
683
+
684
+ node_cache[:char][start_index] = r0
685
+
686
+ r0
687
+ end
688
+
689
+ end
690
+
691
+ class ConfabulatorLanguageParser < Treetop::Runtime::CompiledParser
692
+ include ConfabulatorLanguage
693
+ end
694
+
695
+ end
@@ -0,0 +1,31 @@
1
+ require "rubygems"
2
+ require "linguistics"
3
+ Linguistics::use( :en )
4
+
5
+ module Confabulator
6
+ class Parser
7
+ attr_accessor :confabulation, :kb
8
+
9
+ def initialize(str, opts = {})
10
+ self.confabulation = str
11
+ self.kb = opts[:knowledge]
12
+ end
13
+
14
+ def confabulate
15
+ if parser
16
+ parser.compose(kb).squeeze(" ")
17
+ else
18
+ ""
19
+ end
20
+ end
21
+
22
+ def parser
23
+ if !@parsed # this caches even a nil result
24
+ @cached_parser = ConfabulatorLanguageParser.new.parse(confabulation)
25
+ @parsed = true
26
+ end
27
+
28
+ @cached_parser
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,3 @@
1
+ module Confabulator
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,89 @@
1
+ # encoding: UTF-8
2
+ require 'spec_helper'
3
+
4
+ describe Confabulator do
5
+ describe "choice blocks" do
6
+ it "should work" do
7
+ 100.times.map {
8
+ Confabulator::Parser.new("{Choice one|Choice two} and stuff").confabulate
9
+ }.uniq.sort.should == [
10
+ "Choice one and stuff",
11
+ "Choice two and stuff"
12
+ ]
13
+ end
14
+
15
+ it "should be recursive" do
16
+ 100.times.map {
17
+ Confabulator::Parser.new("{Choice {1|2}|Choice 3} and stuff").confabulate
18
+ }.uniq.sort.should == [
19
+ "Choice 1 and stuff",
20
+ "Choice 2 and stuff",
21
+ "Choice 3 and stuff"
22
+ ]
23
+ end
24
+
25
+ it "should allow differential weighting" do
26
+ one = two = 0
27
+ 500.times do
28
+ case Confabulator::Parser.new("{5:Choice 1|Choice 2} and stuff").confabulate
29
+ when "Choice 1 and stuff"
30
+ one += 1
31
+ when "Choice 2 and stuff"
32
+ two += 1
33
+ end
34
+ end
35
+ one.should > two * 3
36
+ one.should < two * 7
37
+ end
38
+ end
39
+
40
+ describe "substitutions" do
41
+ it "should use the knowledge base" do
42
+ knowledge = Confabulator::Knowledge.new
43
+ knowledge.add "world", "there"
44
+ Confabulator::Parser.new("Hello, [world]!", :knowledge => knowledge).confabulate.should == "Hello, there!"
45
+ end
46
+
47
+ it "should return an empty string if it cannot be found" do
48
+ Confabulator::Parser.new("Hello, [world]!").confabulate.should == "Hello, !"
49
+ end
50
+
51
+ it "should work recursively" do
52
+ k = Confabulator::Knowledge.new
53
+ k.add "expand" => "is [recursive]",
54
+ "recursive" => "pretty cool"
55
+ k.confabulate("Hello, this [expand]!").should == "Hello, this is pretty cool!"
56
+ end
57
+
58
+ it "should work with choices too" do
59
+ k = Confabulator::Knowledge.new
60
+ k.add "expand" => "is {[recursive]|not recursive}", "recursive" => "pretty cool"
61
+ 100.times.map {
62
+ Confabulator::Parser.new("Hello, this [expand]!", :knowledge => k).confabulate
63
+ }.uniq.sort.should == [
64
+ "Hello, this is not recursive!",
65
+ "Hello, this is pretty cool!"
66
+ ]
67
+ end
68
+
69
+ it "should be able to capitalize" do
70
+ k = Confabulator::Knowledge.new
71
+ k.add "blah" => "world foo"
72
+ k.confabulate("Hello. [blah:c]!").should == "Hello. World foo!"
73
+ end
74
+
75
+ it "should be able to pluralize" do
76
+ k = Confabulator::Knowledge.new
77
+ k.add "blah" => "ancient dog"
78
+ k.confabulate("Many [blah:p]!").should == "Many ancient dogs!"
79
+ end
80
+ end
81
+
82
+ describe "general behavior" do
83
+ it "should remove repeated spaces" do
84
+ k = Confabulator::Knowledge.new
85
+ k.add "expand" => " is {[recursive]| not recursive}", "recursive" => "pretty cool "
86
+ Confabulator::Parser.new("Hello, this [expand]!", :knowledge => k).confabulate.should_not =~ / /
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,4 @@
1
+ --colour
2
+ --format s -c
3
+ --loadby mtime
4
+ --reverse
@@ -0,0 +1,7 @@
1
+ require 'rubygems'
2
+ require 'rspec'
3
+ $: << File.expand_path(File.join(File.dirname(__FILE__), "..", "lib"))
4
+ require 'confabulator'
5
+
6
+ RSpec.configure do |c|
7
+ end
@@ -0,0 +1,82 @@
1
+ module Confabulator
2
+ grammar ConfabulatorLanguage
3
+ rule sentence
4
+ (substitution / choice / escaped_char / words)+ {
5
+ def compose(kb = nil)
6
+ elements.map {|e| e.compose(kb) }.join
7
+ end
8
+ }
9
+ end
10
+
11
+ rule choice
12
+ '{' first_sentence:(weight:weight? sentence) rest_sentences:('|' weight:weight? sentence)* '}' {
13
+ def compose(kb = nil)
14
+ elems = []
15
+ (first_sentence.weight.empty? ? 1 : first_sentence.weight.value).times { elems << first_sentence.sentence }
16
+ rest_sentences.elements.each do |s|
17
+ (s.weight.empty? ? 1 : s.weight.value).times { elems << s.sentence }
18
+ end
19
+ elems[elems.length * rand].compose(kb)
20
+ end
21
+ }
22
+ end
23
+
24
+ rule weight
25
+ w:([0-9]+) ':' {
26
+ def value
27
+ w.text_value.to_i
28
+ end
29
+ }
30
+ end
31
+
32
+ rule substitution
33
+ !'\\\\' '[' w name:( [a-zA-Z] [a-zA-Z_0-9-]* ) w options:(":" [a-zA-Z]+)? w ']' {
34
+ def compose(kb = nil)
35
+ if kb
36
+ result = kb.find(name.text_value).confabulate
37
+ if options.text_value =~ /p/
38
+ result = result.en.plural
39
+ elsif options.text_value =~ /c/
40
+ result[0] = result[0].upcase if result[0]
41
+ end
42
+ result
43
+ else
44
+ ""
45
+ end
46
+ end
47
+ }
48
+ end
49
+
50
+ rule w
51
+ [ \t]* {
52
+ def compose(kb = nil)
53
+ text_value
54
+ end
55
+ }
56
+ end
57
+
58
+ rule escaped_char
59
+ '\\' . {
60
+ def compose(kb = nil)
61
+ text_value
62
+ end
63
+ }
64
+ end
65
+
66
+ rule words
67
+ [^\[{}\|\\]+ {
68
+ def compose(kb = nil)
69
+ text_value
70
+ end
71
+ }
72
+ end
73
+
74
+ rule char
75
+ !'\\\\' . {
76
+ def compose(kb = nil)
77
+ text_value
78
+ end
79
+ }
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,8 @@
1
+ require 'rubygems'
2
+ $: << File.expand_path(File.join(File.dirname(__FILE__), "lib"))
3
+ require 'confabulator'
4
+
5
+ kb = Confabulator::Knowledge.new
6
+ kb.add "ok" => "{ok|sure|sure thing|no problem|gotcha}",
7
+ "time" => "{current time|time}"
8
+ puts kb.confabulate("[ok], the [time] is #{Time.now}")
metadata ADDED
@@ -0,0 +1,97 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: confabulator
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Andrew Cantino
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-12-06 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: &70312387498520 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70312387498520
25
+ - !ruby/object:Gem::Dependency
26
+ name: treetop
27
+ requirement: &70312387498100 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70312387498100
36
+ - !ruby/object:Gem::Dependency
37
+ name: linguistics
38
+ requirement: &70312387497680 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *70312387497680
47
+ description: ''
48
+ email:
49
+ - andrew@iterationlabs.com
50
+ executables: []
51
+ extensions: []
52
+ extra_rdoc_files: []
53
+ files:
54
+ - .gitignore
55
+ - .rvmrc
56
+ - Gemfile
57
+ - README.markdown
58
+ - Rakefile
59
+ - confabulator.gemspec
60
+ - lib/confabulator.rb
61
+ - lib/confabulator/knowledge.rb
62
+ - lib/confabulator/language.rb
63
+ - lib/confabulator/parser.rb
64
+ - lib/confabulator/version.rb
65
+ - spec/confabulator_spec.rb
66
+ - spec/spec.opts
67
+ - spec/spec_helper.rb
68
+ - src/confabulator_language.treetop
69
+ - test_grammer.rb
70
+ homepage: ''
71
+ licenses: []
72
+ post_install_message:
73
+ rdoc_options: []
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ none: false
78
+ requirements:
79
+ - - ! '>='
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ required_rubygems_version: !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ requirements: []
89
+ rubyforge_project: confabulator
90
+ rubygems_version: 1.8.10
91
+ signing_key:
92
+ specification_version: 3
93
+ summary: Ruby generative grammer for conversational text
94
+ test_files:
95
+ - spec/confabulator_spec.rb
96
+ - spec/spec.opts
97
+ - spec/spec_helper.rb