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.
- data/README.markdown +79 -32
- data/lib/confabulator/language.rb +137 -68
- data/lib/confabulator/parser.rb +4 -1
- data/lib/confabulator/version.rb +1 -1
- data/spec/confabulator_spec.rb +26 -3
- data/src/confabulator_language.treetop +14 -12
- metadata +8 -8
data/README.markdown
CHANGED
@@ -1,34 +1,81 @@
|
|
1
1
|
# Confabulator
|
2
2
|
|
3
|
-
A Ruby
|
4
|
-
|
5
|
-
##
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
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 =
|
32
|
+
r2 = _nt_protected
|
33
33
|
if r2
|
34
34
|
r1 = r2
|
35
35
|
else
|
36
|
-
r3 =
|
36
|
+
r3 = _nt_substitution
|
37
37
|
if r3
|
38
38
|
r1 = r3
|
39
39
|
else
|
40
|
-
r4 =
|
40
|
+
r4 = _nt_choice
|
41
41
|
if r4
|
42
42
|
r1 = r4
|
43
43
|
else
|
44
|
-
r5 =
|
44
|
+
r5 = _nt_escaped_char
|
45
45
|
if r5
|
46
46
|
r1 = r5
|
47
47
|
else
|
48
|
-
|
49
|
-
|
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[^\\[{}
|
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
|
data/lib/confabulator/parser.rb
CHANGED
@@ -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)
|
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
|
data/lib/confabulator/version.rb
CHANGED
data/spec/confabulator_spec.rb
CHANGED
@@ -80,10 +80,33 @@ describe Confabulator do
|
|
80
80
|
end
|
81
81
|
|
82
82
|
describe "general behavior" do
|
83
|
-
|
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" => "
|
86
|
-
Confabulator::Parser.new("Hello, this
|
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.
|
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-
|
12
|
+
date: 2011-12-07 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
16
|
-
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: *
|
24
|
+
version_requirements: *70215715712220
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: treetop
|
27
|
-
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: *
|
35
|
+
version_requirements: *70215715711800
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: linguistics
|
38
|
-
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: *
|
46
|
+
version_requirements: *70215715711380
|
47
47
|
description: ''
|
48
48
|
email:
|
49
49
|
- andrew@iterationlabs.com
|