tra38-calyx 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 85ba5ec4d74873b85ad9e7aafd4694191717b00b
4
+ data.tar.gz: 93e6e5cc487f09443953e46d334e746aff12d919
5
+ SHA512:
6
+ metadata.gz: 88d7fd63cdf3e7cd491cd001093d5762e5a0b8dff5716d493bafb0afde2d2ffef82014f5fb68a5bdfdcd94b19fd24b8ac215f1e18a4aa77f4f41ffca9b7e33f8
7
+ data.tar.gz: 0c407e12b96da714500fb2827bf62c2f28ab9e6ae5fd97fea4535f672672e6f685cfede0d5f2e36ea64e104c07f9bf0aa74650b7a7d7ffa91a6c5a7a88335976
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /*.gem
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,9 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1
4
+ - 2.2
5
+ - rbx-2
6
+ - jruby-9
7
+ - jruby-head
8
+ before_install: gem install bundler -v 1.10.6
9
+ script: bundle exec rspec
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Mark Rickerby
4
+ Copyright (c) 2016 Tariq Ali
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in
14
+ all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,282 @@
1
+ # Calyx
2
+
3
+ Calyx provides a simple API for generating text with declarative recursive grammars.
4
+
5
+ ## Install
6
+
7
+ ### Command Line
8
+
9
+ ```
10
+ gem install calyx
11
+ ```
12
+
13
+ ## Gemfile
14
+
15
+ ```
16
+ gem 'calyx'
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ Calyx support two types of classes.
22
+
23
+ Classes that inherit from `Calyx::Grammar` are used to construct a set of rules that can generate a text. All grammars require a `start` rule, which specifies the starting point for generating the text structure.
24
+
25
+ Classes that inherit from `Calyx::DataTemplate` are used to construct a set of "meta-rules" that will invoke Grammar rules for you. All templates require a `write_narrative` method which specifies what "meta-rules" are being called.
26
+
27
+ ```ruby
28
+ require 'calyx'
29
+
30
+ class HelloWorld < Calyx::Grammar
31
+ start "Hello World."
32
+ end
33
+
34
+ class Greeting < Calyx::DataTemplate
35
+ def write_narrative
36
+ write HelloWorld
37
+ end
38
+ end
39
+ ```
40
+
41
+ There are two ways to generate text. You can generate text using Calyx::Grammar by initializing the object and calling the `generate` method.
42
+
43
+ ```ruby
44
+ hello = HelloWorld.new
45
+ hello.generate
46
+ # > "Hello World."
47
+ ```
48
+
49
+ Or, you can generate text by initializing the Calyx::DataTemplate class and calling the `result` method.
50
+ ```ruby
51
+ greeting = Greeting.new
52
+ greeting.result
53
+ # > "Hello World."
54
+ ```
55
+
56
+ ### Calyx::Grammar
57
+ Obviously, "Hello World" isn’t very interesting by itself. Possible variations can be added to the text using the `rule` constructor to provide a named set of text strings and the rule delimiter syntax (`{}`) within the text strings to substitute the generated content of the rule.
58
+
59
+ ```ruby
60
+ class HelloWorld < Calyx::Grammar
61
+ start '{greeting} world.'
62
+ rule :greeting, 'Hello', 'Hi', 'Hey', 'Yo'
63
+ end
64
+
65
+ hello = HelloWorld.new
66
+
67
+ hello.generate
68
+ # > "Hi world."
69
+
70
+ hello.generate
71
+ # > "Hello world."
72
+
73
+ hello.generate
74
+ # > "Yo world."
75
+ ```
76
+
77
+ Rules are recursive. They can be arbitrarily nested and connected to generate larger and more complex texts.
78
+
79
+ ```ruby
80
+ class HelloWorld < Calyx::Grammar
81
+ start '{greeting} {world_phrase}.'
82
+ rule :greeting, 'Hello', 'Hi', 'Hey', 'Yo'
83
+ rule :world_phrase, '{happy_adj} world', '{sad_adj} world', 'world'
84
+ rule :happy_adj, 'wonderful', 'amazing', 'bright', 'beautiful'
85
+ rule :sad_adj, 'cruel', 'miserable'
86
+ end
87
+ ```
88
+
89
+ Nesting and hierarchy can be manipulated to balance consistency with variation. The exact same word atoms can be combined in different ways to produce strikingly different resulting texts.
90
+
91
+ ```ruby
92
+ module HelloWorld
93
+ Sentiment < Calyx::Grammar
94
+ start '{happy_phrase}', '{sad_phrase}'
95
+ rule :happy_phrase, '{happy_greeting} {happy_adj} world.'
96
+ rule :happy_greeting, 'Hello', 'Hi', 'Hey', 'Yo'
97
+ rule :happy_adj, 'wonderful', 'amazing', 'bright', 'beautiful'
98
+ rule :sad_phrase, '{sad_greeting} {sad_adj} world.'
99
+ rule :sad_greeting, 'Goodbye', 'So long', 'Farewell'
100
+ rule :sad_adj, 'cruel', 'miserable'
101
+ end
102
+
103
+ Mixed < Calyx::Grammar
104
+ start '{greeting} {adj} world.'
105
+ rule :greeting, 'Hello', 'Hi', 'Hey', 'Yo', 'Goodbye', 'So long', 'Farewell'
106
+ rule :adj, 'wonderful', 'amazing', 'bright', 'beautiful', 'cruel', 'miserable'
107
+ end
108
+ end
109
+ ```
110
+
111
+ By default, the outcomes of generated rules are selected with Ruby’s built-in random number generator (as seen in methods like `Kernel.rand` and `Array.sample`). If you want to supply a weighted probability list, you can pass in arrays to the rule constructor, with the first argument being the template text string and the second argument being a float representing the probability between `0` and `1` of this choice being selected.
112
+
113
+ For example, you can model the triangular distribution produced by rolling 2d6:
114
+
115
+ ```ruby
116
+ class Roll2D6 < Calyx::Grammar
117
+ start(
118
+ ['2', 0.0278],
119
+ ['3', 0.556],
120
+ ['4', 0.833],
121
+ ['5', 0.1111],
122
+ ['6', 0.1389],
123
+ ['7', 0.1667],
124
+ ['8', 0.1389],
125
+ ['9', 0.1111],
126
+ ['10', 0.833],
127
+ ['11', 0.556],
128
+ ['12', 0.278]
129
+ )
130
+ end
131
+ ```
132
+
133
+ Or reproduce Gary Gygax’s famous generation table from the original Dungeon Master’s Guide (page 171):
134
+
135
+ ```ruby
136
+ class ChamberOrRoomContents < Calyx::Grammar
137
+ start(
138
+ [:empty, 0.6],
139
+ [:monster, 0.1],
140
+ [:monster_treasure, 0.15],
141
+ [:special, 0.05],
142
+ [:trick_trap, 0.05],
143
+ [:treasure, 0.05]
144
+ )
145
+
146
+ rule :empty, 'Empty'
147
+ rule :monster, 'Monster Only'
148
+ rule :monster_treasure, 'Monster and Treasure'
149
+ rule :special, 'Special'
150
+ rule :trick_trap, 'Trick/Trap.'
151
+ rule :treasure, 'Treasure'
152
+ end
153
+ ```
154
+
155
+ 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.
156
+
157
+ ```ruby
158
+ class Greeting < Calyx::Grammar
159
+ start '{hello.capitalize} there.', 'Why, {hello} there.'
160
+ rule :hello, 'hello'
161
+ end
162
+
163
+ # => "Hello there."
164
+ # => "Why, hello there."
165
+ ```
166
+
167
+ In order to use more intricate natural language processing capabilities, you can embed additional methods onto the `String` class yourself, as well as use methods from existing Gems that monkeypatch `String`.
168
+
169
+ ```ruby
170
+ require 'indefinite_article'
171
+
172
+ module FullStop
173
+ def full_stop
174
+ self << '.'
175
+ end
176
+ end
177
+
178
+ class String
179
+ include FullStop
180
+ end
181
+
182
+ class NounsWithArticles < Calyx::Grammar
183
+ start '{fruit.with_indefinite_article.capitalize.full_stop}'
184
+ rule :fruit, 'apple', 'orange', 'banana', 'pear'
185
+ end
186
+
187
+ # => "An apple."
188
+ # => "An orange."
189
+ # => "A banana."
190
+ # => "A pear."
191
+ ```
192
+
193
+ ### Calyx::DataTemplate
194
+ Calyx::DataTemplate is useful for allowing a computer to write stories based on data stored within a Hash. The data can be plugged instantly into generated content, so long as you use erb syntax (to distingush from the rule delimiter syntax).
195
+
196
+ ```ruby
197
+ class StockReport < Calyx::Grammar
198
+ start "The price of one share of <%= name %> on <%= date %> is <%= price %> Yen."
199
+ end
200
+
201
+ class StockWriter < Calyx::DataTemplate
202
+ def write_narrative
203
+ write StockReport
204
+ end
205
+ end
206
+
207
+ cyberdyne = { :name => "Cyberdyne", :price => 1897.0, :date => Date.new(2015,1,14) }
208
+
209
+ stock_writer = StockWriter.new(cyberdyne)
210
+ stock_writer.result
211
+ # => "The price of one share of Cyberdyne on 2015-01-14 is 1897.0 Yen."
212
+ ```
213
+
214
+ `conditional_write` allows Calyx::DataTemplate to choose what grammar rule to invoke. If the condition is true, use the first grammar; otherwise, use the second grammar.
215
+ ```ruby
216
+ class GoodStock < Calyx::Grammar
217
+ start "You should buy stock in <%= name %> because this company has a low EPS."
218
+ end
219
+
220
+ class BadStock < Calyx:Grammar
221
+ start "You should sell stock in <%= name %> because this company has a high EPS."
222
+ end
223
+
224
+ class StockWriter < Calyx::DataTemplate
225
+ def write_narrative
226
+ conditional_write eps <= 20, GoodStock, BadStock
227
+ end
228
+ end
229
+
230
+ mitsui = { :name => "Mitsui", :eps => 15.8}
231
+ mitsui_writer = StockWriter.new(mitsui)
232
+ mitsui_writer.result
233
+ # => "You should buy stock in Mitsui because this company has a low EPS."
234
+
235
+ tokoyo_electric = { :name => "Tokyo Electric Power", :eps => 275.2 }
236
+ tokoyo_electric_writer = StockWriter.new(tokoyo_electric)
237
+ tokoyo_electric_writer.result
238
+ # => "You should sell stock in Tokoyo Electric Power because this company has a high EPS."
239
+ ```
240
+
241
+ You may also only provide only one grammar for `conditional_write`. If the condition is false, then nothing will be written.
242
+ ```ruby
243
+ class StockWriter < Calyx::DataTemplate
244
+ def write_narrative
245
+ conditional_write eps <= 20, GoodStock
246
+ end
247
+ end
248
+
249
+ tokoyo_electric = { :name => "Tokyo Electric Power", :eps => 275.2 }
250
+ tokoyo_electric_writer = StockWriter.new(tokoyo_electric)
251
+ tokoyo_electric_writer.result
252
+ # => ""
253
+ ```
254
+
255
+ By simply specifying a few "meta-rules" with conditionals and Grammars, you can generate unique, readable narratives based on your data.
256
+ ```ruby
257
+ class StockWriter < Calyx::DataTemplate
258
+ def write_narrative
259
+ write StockReport
260
+ conditional_write eps <= 20, GoodStock, BadStock
261
+ conditional_write eps <= 10, WonderfulStock
262
+ conditional_write eps >= 50, AbsolutelyHorribleStock
263
+ write ThanksForReading
264
+ end
265
+ end
266
+ ```
267
+
268
+ ###
269
+
270
+ ## License
271
+
272
+ Calyx is open source and provided under the terms of the MIT license. Copyright (c) 2015 Mark Rickerby, (c) 2016 Tariq Ali
273
+
274
+ See the `LICENSE` file [included with the project distribution](https://github.com/tra38/calyx/blob/master/LICENSE) for more information.
275
+
276
+ ## History
277
+ In November 2015, Mark Rickerby created Calyx and used that gem to create [choose-your-own adventure gamebooks](https://github.com/dariusk/NaNoGenMo-2015/issues/189). He later on wrote a [blog post](http://maetl.net/notes/storyboard/gamebook-of-dungeon-tropes) explaining his thought process.
278
+
279
+ In January 2016, Tariq Ali forked Calyx and started adding in new features to turn Calyx into a useful tool for generating data-driven narratives (robojournalism).
280
+
281
+ ## Disclaimer
282
+ In the real world, you would probably not want to buy or sell Japanese stock based solely on EPS. [The MIT Encyclopedia of the Japanese Economy](https://books.google.com/books?id=0RS0CGUaef8C&pg=PA423&lpg=PA423&dq=high+earnings+per+share+in+japan&source=bl&ots=sR8KV0fBTk&sig=qHspeX72SmpsU25wz9AZnhaAxyU&hl=en&sa=X&ved=0ahUKEwjcnqqctrLKAhWKRiYKHdKACaoQ6AEIHDAA#v=onepage&q=high%20earnings%20per%20share%20in%20japan&f=false) can provide some reasons why.
data/calyx.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'calyx/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'tra38-calyx'
8
+ spec.version = Calyx::VERSION
9
+ spec.authors = ['Mark Rickerby','Tariq Ali']
10
+ spec.email = ['me@maetl.net','tra38@nau.edu']
11
+
12
+ spec.summary = %q{Generate text with declarative recursive grammars}
13
+ spec.description = %q{Calyx provides a simple API for generating text with declarative recursive grammars.}
14
+ spec.homepage = 'https://github.com/tra38/calyx'
15
+ spec.license = 'MIT'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(spec)/}) }
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_development_dependency 'bundler', '~> 1.10'
22
+ spec.add_development_dependency 'rake', '~> 10.0'
23
+ spec.add_development_dependency 'rspec'
24
+ end
data/lib/calyx.rb ADDED
@@ -0,0 +1,217 @@
1
+ require 'erb'
2
+
3
+ module Calyx
4
+ #The Grammar class and the Production module was written by Mark Rickerby in 2015, and licensed under the MIT license.
5
+ class Grammar
6
+ class << self
7
+ attr_accessor :registry
8
+
9
+ def start(*productions, &production)
10
+ registry[:start] = construct_rule(productions)
11
+ end
12
+
13
+ def rule(name, *productions, &production)
14
+ registry[name.to_sym] = construct_rule(productions)
15
+ end
16
+
17
+ def inherit_registry(rules)
18
+ @registry ||= {}
19
+ @registry.merge!(rules || {})
20
+ end
21
+
22
+ def inherited(subclass)
23
+ subclass.inherit_registry(@registry)
24
+ end
25
+
26
+ private
27
+
28
+ def construct_rule(productions)
29
+ if productions.first.is_a?(Enumerable)
30
+ Production::WeightedChoices.parse(productions, registry)
31
+ else
32
+ Production::Choices.parse(productions, registry)
33
+ end
34
+ end
35
+ end
36
+
37
+ module Production
38
+ class NonTerminal
39
+ def initialize(expansion, registry)
40
+ @expansion = expansion.to_sym
41
+ @registry = registry
42
+ end
43
+
44
+ def evaluate
45
+ @registry[@expansion].evaluate
46
+ end
47
+ end
48
+
49
+ class Terminal
50
+ def initialize(atom)
51
+ @atom = atom
52
+ end
53
+
54
+ def evaluate
55
+ @atom
56
+ end
57
+ end
58
+
59
+ class Expression
60
+ def initialize(production, methods)
61
+ @production = production
62
+ @methods = methods.map { |m| m.to_sym }
63
+ end
64
+
65
+ def evaluate
66
+ @methods.reduce(@production.evaluate) do |value,method|
67
+ value.send(method)
68
+ end
69
+ end
70
+ end
71
+
72
+ class Concat
73
+ EXPRESSION = /(\{[A-Za-z0-9_\.]+\})/.freeze
74
+ START_TOKEN = '{'.freeze
75
+ END_TOKEN = '}'.freeze
76
+ DEREF_TOKEN = '.'.freeze
77
+
78
+ def self.parse(production, registry)
79
+ expansion = production.split(EXPRESSION).map do |atom|
80
+ if atom.is_a?(String)
81
+ if atom.chars.first == START_TOKEN && atom.chars.last == END_TOKEN
82
+ head, *tail = atom.slice(1, atom.length-2).split(DEREF_TOKEN)
83
+ rule = NonTerminal.new(head, registry)
84
+ unless tail.empty?
85
+ Expression.new(rule, tail)
86
+ else
87
+ rule
88
+ end
89
+ else
90
+ Terminal.new(atom)
91
+ end
92
+ elsif atom.is_a?(Symbol)
93
+ NonTerminal.new(atom, registry)
94
+ end
95
+ end
96
+
97
+ self.new(expansion)
98
+ end
99
+
100
+ def initialize(expansion)
101
+ @expansion = expansion
102
+ end
103
+
104
+ def evaluate
105
+ @expansion.reduce('') do |exp, atom|
106
+ exp << atom.evaluate
107
+ end
108
+ end
109
+ end
110
+
111
+ class WeightedChoices
112
+ def self.parse(productions, registry)
113
+ weights_sum = productions.reduce(0) do |memo, choice|
114
+ memo += choice.last
115
+ end
116
+
117
+ raise 'Weights must sum to 1' if weights_sum != 1.0
118
+
119
+ choices = productions.map do |choice, weight|
120
+ if choice.is_a?(String)
121
+ [Concat.parse(choice, registry), weight]
122
+ elsif choice.is_a?(Symbol)
123
+ [NonTerminal.new(choice, registry), weight]
124
+ end
125
+ end
126
+
127
+ self.new(choices)
128
+ end
129
+
130
+ def initialize(collection)
131
+ @collection = collection
132
+ end
133
+
134
+ def evaluate
135
+ choice = @collection.max_by do |_, weight|
136
+ rand ** (1.0 / weight)
137
+ end.first
138
+
139
+ choice.evaluate
140
+ end
141
+ end
142
+
143
+ class Choices
144
+ def self.parse(productions, registry)
145
+ choices = productions.map do |choice|
146
+ if choice.is_a?(String)
147
+ Concat.parse(choice, registry)
148
+ elsif choice.is_a?(Symbol)
149
+ NonTerminal.new(choice, registry)
150
+ end
151
+ end
152
+ self.new(choices)
153
+ end
154
+
155
+ def initialize(collection)
156
+ @collection = collection
157
+ end
158
+
159
+ def evaluate
160
+ @collection.sample.evaluate
161
+ end
162
+ end
163
+ end
164
+
165
+ def initialize(seed=nil)
166
+ @seed = seed
167
+ @seed = Time.new.to_i unless @seed
168
+ srand(@seed)
169
+ end
170
+
171
+ def registry
172
+ self.class.registry
173
+ end
174
+
175
+ def generate
176
+ registry[:start].evaluate
177
+ end
178
+ end
179
+
180
+ #The DataTemplate class was written by Tariq Ali in 2016, and licensed under the MIT License.
181
+ class DataTemplate
182
+ attr_reader :narrative
183
+
184
+ def initialize(data_hash = {})
185
+ data_hash.each do |key, value|
186
+ self.define_singleton_method(:"#{key}") do
187
+ value
188
+ end
189
+ end
190
+ @narrative = []
191
+ write_narrative
192
+ end
193
+
194
+ def write_narrative
195
+ #user writes in what should happened next
196
+ raise "There is no 'write_narrative' method present in your class."
197
+ end
198
+
199
+ def write(klass)
200
+ @narrative << klass.new.generate
201
+ end
202
+
203
+ def conditional_write(condition, klass_a, klass_b = nil)
204
+ if condition
205
+ @narrative << klass_a.new.generate
206
+ elsif klass_b
207
+ @narrative << klass_b.new.generate
208
+ else
209
+ end
210
+ end
211
+
212
+ def result
213
+ ERB.new(@narrative.join(" ")).result(binding)
214
+ end
215
+
216
+ end
217
+ end
@@ -0,0 +1,3 @@
1
+ module Calyx
2
+ VERSION = '0.6.2'.freeze
3
+ end
metadata ADDED
@@ -0,0 +1,98 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tra38-calyx
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.6.2
5
+ platform: ruby
6
+ authors:
7
+ - Mark Rickerby
8
+ - Tariq Ali
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2016-01-30 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '1.10'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '1.10'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rake
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '10.0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '10.0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: rspec
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ description: Calyx provides a simple API for generating text with declarative recursive
57
+ grammars.
58
+ email:
59
+ - me@maetl.net
60
+ - tra38@nau.edu
61
+ executables: []
62
+ extensions: []
63
+ extra_rdoc_files: []
64
+ files:
65
+ - ".gitignore"
66
+ - ".rspec"
67
+ - ".travis.yml"
68
+ - Gemfile
69
+ - LICENSE
70
+ - README.md
71
+ - calyx.gemspec
72
+ - lib/calyx.rb
73
+ - lib/calyx/version.rb
74
+ homepage: https://github.com/tra38/calyx
75
+ licenses:
76
+ - MIT
77
+ metadata: {}
78
+ post_install_message:
79
+ rdoc_options: []
80
+ require_paths:
81
+ - lib
82
+ required_ruby_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ required_rubygems_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ requirements: []
93
+ rubyforge_project:
94
+ rubygems_version: 2.4.8
95
+ signing_key:
96
+ specification_version: 4
97
+ summary: Generate text with declarative recursive grammars
98
+ test_files: []