confabulator 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|