calyx 0.17.0 → 0.17.1

Sign up to get free protection for your applications and to get access to all the features.
data/docs/calyx-docs.scss CHANGED
@@ -1 +1,66 @@
1
+ $green: #218764;
2
+
3
+ $family-monospace: "Oxygen Mono", "Monaco", Inconsolata, monospace;
4
+ $family-sans-serif: "Libre Franklin", sans-serif;
5
+
6
+ $background: #fafafa;
7
+
8
+ $content-pre-padding: 0;
9
+ $content-heading-color: #218764;
10
+ $content-heading-weight: 700;
11
+
12
+ $navbar-item-color: #fff;
13
+ $navbar-background-color: #1BDE81;
14
+ $navbar-item-hover-color: $navbar-item-color;
15
+ $navbar-item-hover-background-color: darken($navbar-background-color, 6%);
16
+ $navbar-item-active-background-color: #1BDE81;
17
+
1
18
  @import "bulma";
19
+ @import "syntax";
20
+ @import "styleguide";
21
+
22
+ body {
23
+ font-weight: 500;
24
+ height: 100%;
25
+ background-color: $background;
26
+ }
27
+
28
+ .sidebar {
29
+ background-color: #eee;
30
+ height: calc(100vh - 3.25rem);
31
+ }
32
+
33
+ .navbar-item {
34
+ font-weight: 500;
35
+ }
36
+
37
+ .navbar-brand {
38
+ .navbar-item {
39
+ font-family: "Work Sans";
40
+ font-weight: 800;
41
+ font-size: 24px;
42
+ line-height: 24px;
43
+
44
+ span {
45
+ margin-left: 6px;
46
+ }
47
+ }
48
+ }
49
+
50
+ .content {
51
+ h1, h2, h3 {
52
+ letter-spacing: -1px;
53
+ font-family: "Work Sans";
54
+ font-weight: 700;
55
+ }
56
+
57
+ .highlighter-rouge {
58
+ margin-bottom: 1rem;
59
+ }
60
+
61
+ pre {
62
+ code {
63
+ font-size: 1em;
64
+ }
65
+ }
66
+ }
@@ -0,0 +1,45 @@
1
+ ---
2
+ layout: default
3
+ title: Context
4
+ permalink: /context/
5
+ ---
6
+
7
+ j,jnnnnnnjjj
8
+
9
+ ## Constructing rules
10
+
11
+ To achieve particular effects, structured repetition of grammar rules can be extremely useful, but these sets of rules can be boring and error-prone to write by hand.
12
+
13
+ ```ruby
14
+ row "{point}{point}{point}{point}{point}{point}{point}{point}{point}{point}{point}{point}"
15
+ ```
16
+
17
+ An easier
18
+
19
+ ```ruby
20
+ row (0..12).map { "{point}" }.join
21
+ ```
22
+
23
+ ## Injecting rules
24
+
25
+ Sometimes it’s useful to calculate template expansions outside of Calyx and inject them where they’re needed, rather than rely on the hard-coded rules defined in grammar classes and object instances.
26
+
27
+ The `#generate` method accepts an optional context map parameter which can be used to dynamically combine rules with an existing grammar during the generation process. This feature is generally used like a traditional template engine to substitute individual values that aren’t known ahead of time.
28
+
29
+ The context map is a hash, with the keys being symbols defining the rules and the values being template expansion strings.
30
+
31
+ The following example demonstrates how a field returned from a database query could be injected into a grammar to produce a randomized welcome message for users of an app.
32
+
33
+ ```ruby
34
+ greeting = Calyx::Grammar.new do
35
+ start 'Hi {username}', 'Welcome back {username}', 'Hola {username}'
36
+ end
37
+
38
+ user = User.find(id: user_id)
39
+
40
+ context = {
41
+ username: user.name
42
+ }
43
+
44
+ greeting.generate(context)
45
+ ```
@@ -0,0 +1,59 @@
1
+ ---
2
+ layout: default
3
+ title: Examples
4
+ permalink: /examples/
5
+ ---
6
+
7
+ # Examples
8
+
9
+ The best way to get started quickly is to install the gem and run the examples locally.
10
+
11
+ ## Any Gradient
12
+
13
+ Requires Roda and Rack to be available.
14
+
15
+ ```
16
+ gem install roda
17
+ ```
18
+
19
+ Demonstrates how to use Calyx to construct SVG graphics. **Any Gradient** generates a rectangle with a linear gradient of random colours.
20
+
21
+ Run as a web server and preview the output in a browser (`http://localhost:9292`):
22
+
23
+ ```
24
+ ruby examples/any_gradient.rb
25
+ ```
26
+
27
+ Or generate SVG files via a command line pipe:
28
+
29
+ ```
30
+ ruby examples/any_gradient > gradient1.xml
31
+ ```
32
+
33
+ ## Tiny Woodland Bot
34
+
35
+ Requires the Twitter client gem and API access configured for a specific Twitter handle.
36
+
37
+ ```
38
+ gem install twitter
39
+ ```
40
+
41
+ Demonstrates how to use Calyx to make a minimal Twitter bot that periodically posts unique tweets. See [@tiny_woodland on Twitter](https://twitter.com/tiny_woodland) and the [writeup here](http://maetl.net/notes/storyboard/tiny-woodlands).
42
+
43
+ ```
44
+ TWITTER_CONSUMER_KEY=XXX-XXX
45
+ TWITTER_CONSUMER_SECRET=XXX-XXX
46
+ TWITTER_ACCESS_TOKEN=XXX-XXX
47
+ TWITTER_CONSUMER_SECRET=XXX-XXX
48
+ ruby examples/tiny_woodland_bot.rb
49
+ ```
50
+
51
+ ## Faker
52
+
53
+ [Faker](https://github.com/stympy/faker) is a popular library for generating fake names and associated sample data like internet addresses, company names and locations.
54
+
55
+ This example demonstrates how to use Calyx to reproduce the same functionality using custom lists defined in a YAML configuration file.
56
+
57
+ ```
58
+ ruby examples/faker.rb
59
+ ```
@@ -0,0 +1,175 @@
1
+ ---
2
+ layout: default
3
+ title: Template Expressions
4
+ permalink: /expressions/
5
+ ---
6
+
7
+ # Template Expressions
8
+
9
+ Dot-notation is supported in template expressions, allowing you to call any available method on the `String` object returned from a rule. Formatting methods can be chained arbitrarily and will execute in the same way as they would in native Ruby code.
10
+
11
+ ```ruby
12
+ greeting = Calyx::Grammar.new do
13
+ start '{hello.capitalize} there.', 'Why, {hello} there.'
14
+ hello 'hello', 'hi'
15
+ end
16
+
17
+ 4.times { greeting.generate }
18
+ # => "Hello there."
19
+ # => "Hi there."
20
+ # => "Why, hello there."
21
+ # => "Why, hi there."
22
+ ```
23
+
24
+ You can also extend the grammar with custom modifiers that provide useful formatting functions.
25
+
26
+ ### Filters
27
+
28
+ Filters accept an input string and return the transformed output:
29
+
30
+ ```ruby
31
+ greeting = Calyx::Grammar.new do
32
+ filter :shoutycaps do |input|
33
+ input.upcase
34
+ end
35
+
36
+ start '{hello.shoutycaps} there.', 'Why, {hello.shoutycaps} there.'
37
+ hello 'hello', 'hi'
38
+ end
39
+
40
+ 4.times { greeting.generate }
41
+ # => "HELLO there."
42
+ # => "HI there."
43
+ # => "Why, HELLO there."
44
+ # => "Why, HI there."
45
+ ```
46
+
47
+ ### Mappings
48
+
49
+ The mapping shortcut allows you to specify a map of regex patterns pointing to their resulting substitution strings:
50
+
51
+ ```ruby
52
+ green_bottle = Calyx::Grammar.new do
53
+ mapping :pluralize, /(.+)/ => '\\1s'
54
+ start 'One green {bottle}.', 'Two green {bottle.pluralize}.'
55
+ bottle 'bottle'
56
+ end
57
+
58
+ 2.times { green_bottle.generate }
59
+ # => "One green bottle."
60
+ # => "Two green bottles."
61
+ ```
62
+
63
+ ### Modifier Mixins
64
+
65
+ In order to use more intricate rewriting and formatting methods in a modifier chain, you can add methods to a module and embed it in a grammar using the `modifier` classmethod.
66
+
67
+ Modifier methods accept a single argument representing the input string from the previous step in the expression chain and must return a string, representing the modified output.
68
+
69
+ ```ruby
70
+ module FullStop
71
+ def full_stop(input)
72
+ input << '.'
73
+ end
74
+ end
75
+
76
+ hello = Calyx::Grammar.new do
77
+ modifier FullStop
78
+ start '{hello.capitalize.full_stop}'
79
+ hello 'hello'
80
+ end
81
+
82
+ hello.generate
83
+ # => "Hello."
84
+ ```
85
+
86
+ To share custom modifiers across multiple grammars, you can include the module in `Calyx::Modifiers`. This will make the methods available to all subsequent instances:
87
+
88
+ ```ruby
89
+ module FullStop
90
+ def full_stop(input)
91
+ input << '.'
92
+ end
93
+ end
94
+
95
+ class Calyx::Modifiers
96
+ include FullStop
97
+ end
98
+ ```
99
+
100
+ ### Monkeypatching String
101
+
102
+ Alternatively, you can combine methods from existing Gems that monkeypatch `String`:
103
+
104
+ ```ruby
105
+ require 'indefinite_article'
106
+
107
+ module FullStop
108
+ def full_stop
109
+ self << '.'
110
+ end
111
+ end
112
+
113
+ class String
114
+ include FullStop
115
+ end
116
+
117
+ noun_articles = Calyx::Grammar.new do
118
+ start '{fruit.with_indefinite_article.capitalize.full_stop}'
119
+ fruit 'apple', 'orange', 'banana', 'pear'
120
+ end
121
+
122
+ 4.times { noun_articles.generate }
123
+ # => "An apple."
124
+ # => "An orange."
125
+ # => "A banana."
126
+ # => "A pear."
127
+ ```
128
+
129
+ ### Memoized Rules
130
+
131
+ Rule expansions can be ‘memoized’ so that multiple references to the same rule return the same value. This is useful for picking a noun from a list and reusing it in multiple places within a text.
132
+
133
+ The `@` sigil is used to mark memoized rules. This evaluates the rule and stores it in memory the first time it’s referenced. All subsequent references to the memoized rule use the same stored value.
134
+
135
+ ```ruby
136
+ # Without memoization
137
+ grammar = Calyx::Grammar.new do
138
+ start '{name} <{name.downcase}>'
139
+ name 'Daenerys', 'Tyrion', 'Jon'
140
+ end
141
+
142
+ 3.times { grammar.generate }
143
+ # => Daenerys <jon>
144
+ # => Tyrion <daenerys>
145
+ # => Jon <tyrion>
146
+
147
+ # With memoization
148
+ grammar = Calyx::Grammar.new do
149
+ start '{@name} <{@name.downcase}>'
150
+ name 'Daenerys', 'Tyrion', 'Jon'
151
+ end
152
+
153
+ 3.times { grammar.generate }
154
+ # => Tyrion <tyrion>
155
+ # => Daenerys <daenerys>
156
+ # => Jon <jon>
157
+ ```
158
+
159
+ Note that the memoization symbol can only be used on the right hand side of a production rule.
160
+
161
+ ### Unique Rules
162
+
163
+ Rule expansions can be marked as ‘unique’, meaning that multiple references to the same rule always return a different value. This is useful for situations where the same result appearing twice would appear awkward and messy.
164
+
165
+ Unique rules are marked by the `$` sigil.
166
+
167
+ ```ruby
168
+ grammar = Calyx::Grammar.new do
169
+ start "{$medal}, {$medal}, {$medal}"
170
+ medal 'Gold', 'Silver', 'Bronze'
171
+ end
172
+
173
+ grammar.generate
174
+ # => Silver, Bronze, Gold
175
+ ```
@@ -0,0 +1,37 @@
1
+ ---
2
+ layout: default
3
+ title: File Formats
4
+ permalink: /formats/
5
+ ---
6
+
7
+ # File Formats
8
+
9
+ In addition to defining grammars in pure Ruby, you can load them from external JSON and YAML files:
10
+
11
+ ```ruby
12
+ hello = Calyx::Grammar.load('hello.yml')
13
+ hello.generate
14
+ ```
15
+
16
+ The format requires a flat map with keys representing the left-hand side named symbols and the values representing the right hand side substitution rules.
17
+
18
+ In JSON:
19
+
20
+ ```json
21
+ {
22
+ "start": "{greeting} world.",
23
+ "greeting": ["Hello", "Hi", "Hey", "Yo"]
24
+ }
25
+ ```
26
+
27
+ In YAML:
28
+
29
+ ```yaml
30
+ ---
31
+ start: "{greeting} world."
32
+ greeting:
33
+ - Hello
34
+ - Hi
35
+ - Hey
36
+ - Yo
37
+ ```
@@ -0,0 +1,129 @@
1
+ ---
2
+ layout: default
3
+ title: Getting Started
4
+ permalink: /getting-started/
5
+ ---
6
+
7
+ # Getting Started
8
+
9
+ Require the library and inherit from `Calyx::Grammar` to construct a set of rules to generate a text.
10
+
11
+ ```ruby
12
+ require 'calyx'
13
+
14
+ class HelloWorld < Calyx::Grammar
15
+ start 'Hello world.'
16
+ end
17
+ ```
18
+
19
+ To generate the text itself, initialize the object and call the `generate` method.
20
+
21
+ ```ruby
22
+ hello = HelloWorld.new
23
+ hello.generate
24
+ # > "Hello world."
25
+ ```
26
+
27
+ Obviously, this hardcoded sentence isn’t very interesting by itself. Possible variations can be added to the text by adding additional rules which provide a named set of text strings. The rule delimiter syntax (`{}`) can be used to substitute the generated content of other rules.
28
+
29
+ ```ruby
30
+ class HelloWorld < Calyx::Grammar
31
+ start '{greeting} world.'
32
+ greeting 'Hello', 'Hi', 'Hey', 'Yo'
33
+ end
34
+ ```
35
+
36
+ Each time `#generate` runs, it evaluates the tree and randomly selects variations of rules to construct a resulting string.
37
+
38
+ ```ruby
39
+ hello = HelloWorld.new
40
+
41
+ hello.generate
42
+ # > "Hi world."
43
+
44
+ hello.generate
45
+ # > "Hello world."
46
+
47
+ hello.generate
48
+ # > "Yo world."
49
+ ```
50
+
51
+ By convention, the `start` rule specifies the default starting point for generating the final text. You can start from any other named rule by passing it explicitly to the generate method.
52
+
53
+ ```ruby
54
+ class HelloWorld < Calyx::Grammar
55
+ hello 'Hello world.'
56
+ end
57
+
58
+ hello = HelloWorld.new
59
+ hello.generate(:hello)
60
+ ```
61
+
62
+ ## Block Constructors
63
+
64
+ As an alternative to subclassing, you can also construct rules unique to an instance by passing a block when initializing the class:
65
+
66
+ ```ruby
67
+ hello = Calyx::Grammar.new do
68
+ start '{greeting} world.'
69
+ greeting 'Hello', 'Hi', 'Hey', 'Yo'
70
+ end
71
+
72
+ hello.generate
73
+ ```
74
+
75
+ ## Template Expressions
76
+
77
+ Basic rule substitution uses single curly brackets as delimiters for template expressions:
78
+
79
+ ```ruby
80
+ fruit = Calyx::Grammar.new do
81
+ start '{colour} {fruit}'
82
+ colour 'red', 'green', 'yellow'
83
+ fruit 'apple', 'pear', 'tomato'
84
+ end
85
+
86
+ 6.times { fruit.generate }
87
+ # => "yellow pear"
88
+ # => "red apple"
89
+ # => "green tomato"
90
+ # => "red pear"
91
+ # => "yellow tomato"
92
+ # => "green apple"
93
+ ```
94
+
95
+ ## Nesting and Substitution
96
+
97
+ Rules are recursive. They can be arbitrarily nested and connected to generate larger and more complex texts.
98
+
99
+ ```ruby
100
+ class HelloWorld < Calyx::Grammar
101
+ start '{greeting} {world_phrase}.'
102
+ greeting 'Hello', 'Hi', 'Hey', 'Yo'
103
+ world_phrase '{happy_adj} world', '{sad_adj} world', 'world'
104
+ happy_adj 'wonderful', 'amazing', 'bright', 'beautiful'
105
+ sad_adj 'cruel', 'miserable'
106
+ end
107
+ ```
108
+
109
+ Nesting and hierarchy can be manipulated to balance consistency with novelty. The exact same word atoms can be combined in a variety of ways to produce strikingly different resulting texts.
110
+
111
+ ```ruby
112
+ module HelloWorld
113
+ class Sentiment < Calyx::Grammar
114
+ start '{happy_phrase}', '{sad_phrase}'
115
+ happy_phrase '{happy_greeting} {happy_adj} world.'
116
+ happy_greeting 'Hello', 'Hi', 'Hey', 'Yo'
117
+ happy_adj 'wonderful', 'amazing', 'bright', 'beautiful'
118
+ sad_phrase '{sad_greeting} {sad_adj} world.'
119
+ sad_greeting 'Goodbye', 'So long', 'Farewell'
120
+ sad_adj 'cruel', 'miserable'
121
+ end
122
+
123
+ class Mixed < Calyx::Grammar
124
+ start '{greeting} {adj} world.'
125
+ greeting 'Hello', 'Hi', 'Hey', 'Yo', 'Goodbye', 'So long', 'Farewell'
126
+ adj 'wonderful', 'amazing', 'bright', 'beautiful', 'cruel', 'miserable'
127
+ end
128
+ end
129
+ ```
@@ -0,0 +1,19 @@
1
+ ---
2
+ layout: default
3
+ title: Install
4
+ permalink: /installation/
5
+ ---
6
+
7
+ # Install
8
+
9
+ ## Command Line
10
+
11
+ ```
12
+ gem install calyx
13
+ ```
14
+
15
+ ## Gemfile
16
+
17
+ ```
18
+ gem 'calyx'
19
+ ```
@@ -0,0 +1,100 @@
1
+ ---
2
+ layout: default
3
+ title: Random Sampling
4
+ permalink: /random/
5
+ ---
6
+
7
+ # Random Sampling
8
+
9
+ By default, the outcomes of generated rules are selected with Ruby’s built-in pseudorandom number generator (as seen in methods like `Kernel.rand` and `Array.sample`). To seed the random number generator, pass in an integer seed value as the first argument to the constructor:
10
+
11
+ ```ruby
12
+ grammar = Calyx::Grammar.new(seed: 12345) do
13
+ # rules...
14
+ end
15
+ ```
16
+
17
+ Alternatively, you can pass a preconfigured instance of Ruby’s stdlib `Random` class:
18
+
19
+ ```ruby
20
+ random = Random.new(12345)
21
+
22
+ grammar = Calyx::Grammar.new(rng: random) do
23
+ # rules...
24
+ end
25
+ ```
26
+
27
+ When a random seed isn’t supplied, `Time.new.to_i` is used as the default seed, which makes each run of the generator relatively unique.
28
+
29
+ ## Weighted Choices
30
+
31
+ Choices can be weighted so that some rules have a greater probability of expanding than others.
32
+
33
+ Weights are defined by passing a hash instead of a list of rules where the keys are strings or symbols representing the grammar rules and the values are weights.
34
+
35
+ Weights can be represented as floats, integers or ranges.
36
+
37
+ - Floats must be in the interval 0..1 and the given weights for a production must sum to 1.
38
+ - Ranges must be contiguous and cover the entire interval from 1 to the maximum value of the largest range.
39
+ - Integers (Fixnums) will produce a distribution based on the sum of all given numbers, with each number being a fraction of that sum.
40
+
41
+ The following definitions produce an equivalent weighting of choices:
42
+
43
+ ```ruby
44
+ Calyx::Grammar.new do
45
+ start 'heads' => 1, 'tails' => 1
46
+ end
47
+
48
+ Calyx::Grammar.new do
49
+ start 'heads' => 0.5, 'tails' => 0.5
50
+ end
51
+
52
+ Calyx::Grammar.new do
53
+ start 'heads' => 1..5, 'tails' => 6..10
54
+ end
55
+
56
+ Calyx::Grammar.new do
57
+ start 'heads' => 50, 'tails' => 50
58
+ end
59
+ ```
60
+
61
+ There’s a lot of interesting things you can do with this. For example, you can model the triangular distribution produced by rolling 2d6:
62
+
63
+ ```ruby
64
+ Calyx::Grammar.new do
65
+ start(
66
+ '2' => 1,
67
+ '3' => 2,
68
+ '4' => 3,
69
+ '5' => 4,
70
+ '6' => 5,
71
+ '7' => 6,
72
+ '8' => 5,
73
+ '9' => 4,
74
+ '10' => 3,
75
+ '11' => 2,
76
+ '12' => 1
77
+ )
78
+ end
79
+ ```
80
+
81
+ Or reproduce Gary Gygax’s famous generation table from the original [Dungeon Master’s Guide](https://en.wikipedia.org/wiki/Dungeon_Master%27s_Guide#Advanced_Dungeons_.26_Dragons) (page 171):
82
+
83
+ ```ruby
84
+ Calyx::Grammar.new do
85
+ start(
86
+ :empty => 0.6,
87
+ :monster => 0.1,
88
+ :monster_treasure => 0.15,
89
+ :special => 0.05,
90
+ :trick_trap => 0.05,
91
+ :treasure => 0.05
92
+ )
93
+ empty 'Empty'
94
+ monster 'Monster Only'
95
+ monster_treasure 'Monster and Treasure'
96
+ special 'Special'
97
+ trick_trap 'Trick/Trap.'
98
+ treasure 'Treasure'
99
+ end
100
+ ```
@@ -0,0 +1,62 @@
1
+ ---
2
+ layout: default
3
+ title: Results
4
+ permalink: /results/
5
+ ---
6
+
7
+ # Generating Results
8
+
9
+ ## Result objects
10
+
11
+ Results are generated by calling `#generate` on the grammar instance. Results are immutable objects representing the unique randomized output from evaluating the grammar.
12
+
13
+ ```ruby
14
+ grammar = Calyx::Grammar.new do
15
+ start "Where is the green sheep?"
16
+ end
17
+
18
+ result = grammar.generate
19
+ # => Calyx::Result
20
+ ```
21
+
22
+ The core accessor methods on the result are:
23
+
24
+ - `#text`
25
+ - `#tree`
26
+
27
+ Results can only be deterministically generated by seeding the random number generator.
28
+
29
+ ## Accessing the flattened text string
30
+
31
+ The `#text` method of the result provides access to the flattened text string generated by the grammar.
32
+
33
+ ```ruby
34
+ result = grammar.generate
35
+
36
+ result.text
37
+ # => "Where is the green sheep?"
38
+ ```
39
+
40
+ `#text` is aliased to the `#to_s` method so result objects can be concatenated and interpolated within other strings directly.
41
+
42
+ ```ruby
43
+ result = grammar.generate
44
+
45
+ templated = "Q: #{result}"
46
+ # => "Q: Where is the green sheep?"
47
+ ```
48
+
49
+ ## Accessing the generated tree
50
+
51
+ The `#tree` method of the result provides access to the raw generated tree structure without being flattened into a string.
52
+
53
+ The tree is encoded as an array of nested arrays, with the leading symbols labeling the choices and rules selected, and the trailing terminal leaves encoding string values.
54
+
55
+ This is related to the concept of [s-expressions](https://en.wikipedia.org/wiki/S-expression). It’s a fairly speculative feature at this stage, but it leads to some interesting possibilities.
56
+
57
+ ```ruby
58
+ result = grammar.generate
59
+
60
+ result.tree
61
+ # => [:start, [:choice, [:concat, [[:atom, "Where is the green sheep?"]]]]]
62
+ ```