confabulator 0.0.1 → 0.0.2

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.
@@ -1,34 +1,81 @@
1
1
  # Confabulator
2
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!"
3
+ A recursive Ruby tempting language for the procedural generation of random sentences.
4
+
5
+ ## Install
6
+
7
+ gem install confabulator
8
+
9
+ ## Use
10
+
11
+ require 'confabulator'
12
+
13
+ ### Choice blocks
14
+
15
+ Choice blocks let the parser make a random choice.
16
+
17
+ > 5.times { puts Confabulator::Parser.new("{Choice one|Choice two} and stuff").confabulate }
18
+ Choice two and stuff
19
+ Choice one and stuff
20
+ Choice two and stuff
21
+ ...
22
+
23
+ Recursion is fine (just try to avoid loops):
24
+
25
+ > 5.times { puts Confabulator::Parser.new("{Choice {1|2}|Choice 3} and stuff").confabulate }
26
+ Choice 3 and stuff
27
+ Choice 1 and stuff
28
+ Choice 2 and stuff
29
+ ...
30
+
31
+ You can differentially weight the options: {5:This is 5 times more likely|than this}
32
+
33
+ ### Substitutions
34
+
35
+ Substitutions let you re-use common templates.
36
+
37
+ > knowledge = Confabulator::Knowledge.new
38
+ > knowledge.add "world", "there" # a hash is also acceptable
39
+ > Confabulator::Parser.new("Hello, [world]!", :knowledge => knowledge).confabulate
40
+ => "Hello, there!"
41
+
42
+ Equivalently:
43
+
44
+ > knowledge.confabulate("Hello, [world]!")
45
+ => "Hello, there!"
46
+
47
+ You can ask a substitution to be capitalized:
48
+
49
+ > knowledge.confabulate("Hello, [world:c]!")
50
+ => "Hello, There!"
51
+
52
+ Or pluralized:
53
+
54
+ > knowledge.add "dude" => "friend"
55
+ > knowledge.confabulate("Hello, [dude:p]!")
56
+ => "Hello, friends!"
57
+
58
+ Substitutions can contain other substitutions in choice nodes inside of substitutions, etc., ad infinitum.
59
+
60
+ ### Escaping
61
+
62
+ You must escape the special characters {, [, `, and | with backslashes:
63
+
64
+ > knowledge.add "dude" => "friend"
65
+ > knowledge.confabulate("Hello, \\{friend\\|something\\} \\`\\`stuff\\`\\` \\[dude:p]!")
66
+ => "Hello, {friend|something} ``stuff`` [dude:p]!!"
67
+
68
+ ### Protected regions
69
+
70
+ Sometimes you want to insert user generated content without having to escape every {, [, `, and |. For this you use protected regions.
71
+
72
+ > knowledge.add "dude" => "friend"
73
+ > user_content = "protect regions [and stuff] with double backticks (`)!"
74
+ > knowledge.confabulate("Hello, ``#{user_content}``")
75
+ => "Hello, protect regions [and stuff] with double backticks (`)!"
76
+
77
+ At the moment, sequences of more than one backtick are never allowed inside of a protected region.
78
+
79
+ ## Helping out
80
+
81
+ Fork, write specs, add a feature, send me a pull request!
@@ -29,24 +29,29 @@ module Confabulator
29
29
  s0, i0 = [], index
30
30
  loop do
31
31
  i1 = index
32
- r2 = _nt_substitution
32
+ r2 = _nt_protected
33
33
  if r2
34
34
  r1 = r2
35
35
  else
36
- r3 = _nt_choice
36
+ r3 = _nt_substitution
37
37
  if r3
38
38
  r1 = r3
39
39
  else
40
- r4 = _nt_escaped_char
40
+ r4 = _nt_choice
41
41
  if r4
42
42
  r1 = r4
43
43
  else
44
- r5 = _nt_words
44
+ r5 = _nt_escaped_char
45
45
  if r5
46
46
  r1 = r5
47
47
  else
48
- @index = i1
49
- r1 = nil
48
+ r6 = _nt_words
49
+ if r6
50
+ r1 = r6
51
+ else
52
+ @index = i1
53
+ r1 = nil
54
+ end
50
55
  end
51
56
  end
52
57
  end
@@ -289,6 +294,127 @@ module Confabulator
289
294
  r0
290
295
  end
291
296
 
297
+ module Protected0
298
+ end
299
+
300
+ module Protected1
301
+ def words
302
+ elements[1]
303
+ end
304
+
305
+ end
306
+
307
+ module Protected2
308
+ def compose(kb = nil)
309
+ words.elements.map { |element|
310
+ element.text_value == "\\`" ? "`" : element.text_value
311
+ }.join
312
+ end
313
+ end
314
+
315
+ def _nt_protected
316
+ start_index = index
317
+ if node_cache[:protected].has_key?(index)
318
+ cached = node_cache[:protected][index]
319
+ if cached
320
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
321
+ @index = cached.interval.end
322
+ end
323
+ return cached
324
+ end
325
+
326
+ i0, s0 = index, []
327
+ if has_terminal?("``", false, index)
328
+ r1 = instantiate_node(SyntaxNode,input, index...(index + 2))
329
+ @index += 2
330
+ else
331
+ terminal_parse_failure("``")
332
+ r1 = nil
333
+ end
334
+ s0 << r1
335
+ if r1
336
+ s2, i2 = [], index
337
+ loop do
338
+ i3 = index
339
+ i4, s4 = index, []
340
+ if has_terminal?("`", false, index)
341
+ r5 = instantiate_node(SyntaxNode,input, index...(index + 1))
342
+ @index += 1
343
+ else
344
+ terminal_parse_failure("`")
345
+ r5 = nil
346
+ end
347
+ s4 << r5
348
+ if r5
349
+ if has_terminal?('\G[^`]', true, index)
350
+ r6 = true
351
+ @index += 1
352
+ else
353
+ r6 = nil
354
+ end
355
+ s4 << r6
356
+ end
357
+ if s4.last
358
+ r4 = instantiate_node(SyntaxNode,input, i4...index, s4)
359
+ r4.extend(Protected0)
360
+ else
361
+ @index = i4
362
+ r4 = nil
363
+ end
364
+ if r4
365
+ r3 = r4
366
+ else
367
+ if has_terminal?('\G[^`]', true, index)
368
+ r7 = true
369
+ @index += 1
370
+ else
371
+ r7 = nil
372
+ end
373
+ if r7
374
+ r3 = r7
375
+ else
376
+ @index = i3
377
+ r3 = nil
378
+ end
379
+ end
380
+ if r3
381
+ s2 << r3
382
+ else
383
+ break
384
+ end
385
+ end
386
+ if s2.empty?
387
+ @index = i2
388
+ r2 = nil
389
+ else
390
+ r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
391
+ end
392
+ s0 << r2
393
+ if r2
394
+ if has_terminal?("``", false, index)
395
+ r8 = instantiate_node(SyntaxNode,input, index...(index + 2))
396
+ @index += 2
397
+ else
398
+ terminal_parse_failure("``")
399
+ r8 = nil
400
+ end
401
+ s0 << r8
402
+ end
403
+ end
404
+ if s0.last
405
+ r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
406
+ r0.extend(Protected1)
407
+ r0.extend(Protected2)
408
+ else
409
+ @index = i0
410
+ r0 = nil
411
+ end
412
+
413
+ node_cache[:protected][start_index] = r0
414
+
415
+ r0
416
+ end
417
+
292
418
  module Substitution0
293
419
  end
294
420
 
@@ -530,11 +656,14 @@ module Confabulator
530
656
  end
531
657
 
532
658
  module EscapedChar0
659
+ def character
660
+ elements[1]
661
+ end
533
662
  end
534
663
 
535
664
  module EscapedChar1
536
665
  def compose(kb = nil)
537
- text_value
666
+ character.text_value
538
667
  end
539
668
  end
540
669
 
@@ -601,7 +730,7 @@ module Confabulator
601
730
 
602
731
  s0, i0 = [], index
603
732
  loop do
604
- if has_terminal?('\G[^\\[{}\\|\\\\]', true, index)
733
+ if has_terminal?('\G[^\\[{}`\\|\\\\]', true, index)
605
734
  r1 = true
606
735
  @index += 1
607
736
  else
@@ -626,66 +755,6 @@ module Confabulator
626
755
  r0
627
756
  end
628
757
 
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
758
  end
690
759
 
691
760
  class ConfabulatorLanguageParser < Treetop::Runtime::CompiledParser
@@ -4,6 +4,7 @@ Linguistics::use( :en )
4
4
 
5
5
  module Confabulator
6
6
  class Parser
7
+ REMOVE_SPACES = /(\S+) {2,}(\S+)/
7
8
  attr_accessor :confabulation, :kb
8
9
 
9
10
  def initialize(str, opts = {})
@@ -13,7 +14,9 @@ module Confabulator
13
14
 
14
15
  def confabulate
15
16
  if parser
16
- parser.compose(kb).squeeze(" ")
17
+ result = parser.compose(kb)
18
+ result.gsub!(REMOVE_SPACES, '\1 \2') while result =~ REMOVE_SPACES
19
+ result
17
20
  else
18
21
  ""
19
22
  end
@@ -1,3 +1,3 @@
1
1
  module Confabulator
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -80,10 +80,33 @@ describe Confabulator do
80
80
  end
81
81
 
82
82
  describe "general behavior" do
83
- it "should remove repeated spaces" do
83
+ before do
84
+ @k = Confabulator::Knowledge.new
85
+ @k.add "expand" => " is {[recursive]| not recursive}", "recursive" => "pretty cool "
86
+ end
87
+
88
+ it "should remove repeated spaces between words" do
89
+ @k.confabulate("Hello, this [expand]!").should_not =~ / /
90
+ end
91
+
92
+ it "should not remove leading spaces" do
93
+ @k.confabulate(" Hello, this [expand]!").should =~ /^ Hello/
94
+ @k.confabulate("Foo bar\n\n baz").should == "Foo bar\n\n baz"
95
+ @k.confabulate("List:\n * foo\n * bar baz bing blah").should == "List:\n * foo\n * bar baz bing blah"
96
+ end
97
+ end
98
+
99
+ describe "protected regions" do
100
+ it "should not parse content inside of the region" do
84
101
  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 =~ / /
102
+ k.add "expand" => "is ``{[recursive]|not recursive} single ticks (`) are ok``"
103
+ Confabulator::Parser.new("Hello, this [expand]!", :knowledge => k).confabulate.should == "Hello, this is {[recursive]|not recursive} single ticks (`) are ok!"
104
+ end
105
+ end
106
+
107
+ describe "escaped characters" do
108
+ it "should show up as unescaped" do
109
+ Confabulator::Parser.new("Hi \\[foo]\\{bar\\|foo\\}\\`baz\\` and stuff").confabulate.should == "Hi [foo]{bar|foo}`baz` and stuff"
87
110
  end
88
111
  end
89
112
  end
@@ -1,7 +1,7 @@
1
1
  module Confabulator
2
2
  grammar ConfabulatorLanguage
3
3
  rule sentence
4
- (substitution / choice / escaped_char / words)+ {
4
+ (protected / substitution / choice / escaped_char / words)+ {
5
5
  def compose(kb = nil)
6
6
  elements.map {|e| e.compose(kb) }.join
7
7
  end
@@ -29,6 +29,16 @@ module Confabulator
29
29
  }
30
30
  end
31
31
 
32
+ rule protected
33
+ "``" words:(("`" [^`]) / [^`])+ "``" {
34
+ def compose(kb = nil)
35
+ words.elements.map { |element|
36
+ element.text_value == "\\`" ? "`" : element.text_value
37
+ }.join
38
+ end
39
+ }
40
+ end
41
+
32
42
  rule substitution
33
43
  !'\\\\' '[' w name:( [a-zA-Z] [a-zA-Z_0-9-]* ) w options:(":" [a-zA-Z]+)? w ']' {
34
44
  def compose(kb = nil)
@@ -56,23 +66,15 @@ module Confabulator
56
66
  end
57
67
 
58
68
  rule escaped_char
59
- '\\' . {
69
+ '\\' character:. {
60
70
  def compose(kb = nil)
61
- text_value
71
+ character.text_value
62
72
  end
63
73
  }
64
74
  end
65
75
 
66
76
  rule words
67
- [^\[{}\|\\]+ {
68
- def compose(kb = nil)
69
- text_value
70
- end
71
- }
72
- end
73
-
74
- rule char
75
- !'\\\\' . {
77
+ [^\[{}`\|\\]+ {
76
78
  def compose(kb = nil)
77
79
  text_value
78
80
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: confabulator
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-12-06 00:00:00.000000000Z
12
+ date: 2011-12-07 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
16
- requirement: &70312387498520 !ruby/object:Gem::Requirement
16
+ requirement: &70215715712220 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *70312387498520
24
+ version_requirements: *70215715712220
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: treetop
27
- requirement: &70312387498100 !ruby/object:Gem::Requirement
27
+ requirement: &70215715711800 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70312387498100
35
+ version_requirements: *70215715711800
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: linguistics
38
- requirement: &70312387497680 !ruby/object:Gem::Requirement
38
+ requirement: &70215715711380 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,7 +43,7 @@ dependencies:
43
43
  version: '0'
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *70312387497680
46
+ version_requirements: *70215715711380
47
47
  description: ''
48
48
  email:
49
49
  - andrew@iterationlabs.com