kotodama 0.0.3
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/bin/wordgen +59 -0
- data/lib/kotodama.rb +23 -0
- data/lib/kotodama/array.rb +22 -0
- data/lib/kotodama/change.rb +103 -0
- data/lib/kotodama/change_list.rb +31 -0
- data/lib/kotodama/language.rb +62 -0
- data/lib/kotodama/parser.rb +151 -0
- data/lib/kotodama/rule.rb +31 -0
- data/lib/kotodama/string.rb +29 -0
- data/lib/kotodama/type.rb +33 -0
- data/lib/kotodama/word.rb +66 -0
- metadata +86 -0
data/bin/wordgen
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'kotodama'
|
4
|
+
|
5
|
+
flags = []
|
6
|
+
files = []
|
7
|
+
starting_rule = nil
|
8
|
+
starting_change = nil
|
9
|
+
number = 1
|
10
|
+
ARGV.each do |n|
|
11
|
+
if n[0] == '-'
|
12
|
+
case n[1]
|
13
|
+
when 'r'
|
14
|
+
starting_rule = n[2..-1]
|
15
|
+
when 'c'
|
16
|
+
starting_change = n[2..-1]
|
17
|
+
else
|
18
|
+
flags |= n[1..-1].split
|
19
|
+
end
|
20
|
+
elsif n.match /\A\d+\Z/
|
21
|
+
number = n.to_i
|
22
|
+
else
|
23
|
+
files << n
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
if files.length == 1
|
28
|
+
File.open files[0] do |file|
|
29
|
+
result = Kotodama::Parser.new.parse file
|
30
|
+
if result.parsed?
|
31
|
+
lang = result.result
|
32
|
+
if flags.member? 'u'
|
33
|
+
trials = []
|
34
|
+
until trials.length == number
|
35
|
+
trials |= lang.generate
|
36
|
+
end
|
37
|
+
puts trials
|
38
|
+
else
|
39
|
+
number.times { puts lang.generate }
|
40
|
+
end
|
41
|
+
else
|
42
|
+
flags.member?('v') ? result.backtrace : STDERR.puts(result.error_message)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
elsif files.length == 2
|
46
|
+
File.open files[0] do |file|
|
47
|
+
result = Kotodama::Parser.new.parse file
|
48
|
+
if result.parsed?
|
49
|
+
lang = result.result
|
50
|
+
File.open files[1] do |file2|
|
51
|
+
puts lang.apply_to file2.to_a.collect {|n| n.chomp }
|
52
|
+
end
|
53
|
+
else
|
54
|
+
flags.member?('v') ? result.backtrace : STDERR.puts(result.error_message)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
else
|
58
|
+
STDERR.puts "wrong number of arguments (#{files.length} for 2)"
|
59
|
+
end
|
data/lib/kotodama.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'kaiseki'
|
3
|
+
|
4
|
+
module Kotodama
|
5
|
+
VERSION = '0.0.3'
|
6
|
+
end
|
7
|
+
|
8
|
+
dir_path = Pathname.new(__FILE__).realpath.dirname
|
9
|
+
|
10
|
+
[
|
11
|
+
'kotodama/word',
|
12
|
+
'kotodama/language',
|
13
|
+
'kotodama/type',
|
14
|
+
'kotodama/rule',
|
15
|
+
'kotodama/change',
|
16
|
+
'kotodama/change_list',
|
17
|
+
|
18
|
+
'kotodama/array',
|
19
|
+
'kotodama/string',
|
20
|
+
'kotodama/parser',
|
21
|
+
].each do |path|
|
22
|
+
require dir_path + path
|
23
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class Array
|
2
|
+
def random weights = nil
|
3
|
+
if weights
|
4
|
+
if weights.length == self.length
|
5
|
+
max_weight = 0
|
6
|
+
weights.each {|n| max_weight += n }
|
7
|
+
target_weight = rand max_weight
|
8
|
+
index = 0
|
9
|
+
index_weight = weights[index]
|
10
|
+
while index_weight <= target_weight
|
11
|
+
index += 1
|
12
|
+
index_weight += weights[index]
|
13
|
+
end
|
14
|
+
self[index]
|
15
|
+
else
|
16
|
+
raise "Array length and weights length not equal (#{self.length} and #{weights.length})"
|
17
|
+
end
|
18
|
+
else
|
19
|
+
self[rand(self.length)]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module Kotodama
|
2
|
+
class Change
|
3
|
+
def initialize action, target, env
|
4
|
+
@action = action
|
5
|
+
@target = target
|
6
|
+
@env = env
|
7
|
+
end
|
8
|
+
|
9
|
+
def apply_to word, options = {}
|
10
|
+
unless word.is_a? Word
|
11
|
+
word = word.to_word
|
12
|
+
end
|
13
|
+
i = 0
|
14
|
+
while i < word.length
|
15
|
+
if check_environment word, i, options
|
16
|
+
case @action
|
17
|
+
when :insert
|
18
|
+
@target.length.times do |i2|
|
19
|
+
word.insert i + i2, @target[i2]
|
20
|
+
end
|
21
|
+
i += @target.length - 1
|
22
|
+
when :delete
|
23
|
+
catch :fail do
|
24
|
+
@target.length.times do |i2|
|
25
|
+
if options[:lang].types.key? @target[i2]
|
26
|
+
throw :fail unless options[:lang].types[@target[i2]].has? word[i + i2]
|
27
|
+
else
|
28
|
+
throw :fail unless word[i + i2] == @target[i2]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
@target.each { word.delete_at i }
|
32
|
+
end
|
33
|
+
when :transform
|
34
|
+
catch :fail do
|
35
|
+
insertion_string = []
|
36
|
+
@target[0].length.times do |i2|
|
37
|
+
if options[:lang].types.key? @target[0][i2]
|
38
|
+
if options[:lang].types[@target[0][i2]].has?(word[i + i2])
|
39
|
+
insertion_string << options[:lang].types[@target[0][i2]].symbols.find_index(word[i + i2])
|
40
|
+
else
|
41
|
+
throw :fail
|
42
|
+
end
|
43
|
+
else
|
44
|
+
throw :fail unless word[i + i2] == @target[0][i2]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
@target[0].each { word.delete_at i }
|
48
|
+
insertion_string.reverse!
|
49
|
+
@target[1].length.times do |i2|
|
50
|
+
if options[:lang].types.key? @target[1][i2]
|
51
|
+
raise "Wrong number of type transformations" unless insertion_string.last
|
52
|
+
word.insert i + i2, options[:lang].types[@target[1][i2]].symbols[insertion_string.pop]
|
53
|
+
else
|
54
|
+
word.insert i + i2, @target[1][i2]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
i += 1
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def check_environment word, index, options = {}
|
65
|
+
if @env
|
66
|
+
catch :fail do
|
67
|
+
@env[1].length.times do |i|
|
68
|
+
if @action == :insert
|
69
|
+
next_n = index + i
|
70
|
+
else
|
71
|
+
next_n = index + @target[0].length + i
|
72
|
+
end
|
73
|
+
if @env[1][i] == '#'
|
74
|
+
throw :fail unless index + 1 + i >= word.length
|
75
|
+
elsif options[:lang].types.key? @env[1][i]
|
76
|
+
throw :fail if next_n >= word.length
|
77
|
+
throw :fail unless options[:lang].types[@env[1][i]].has? word[next_n]
|
78
|
+
else
|
79
|
+
throw :fail if next_n >= word.length
|
80
|
+
throw :fail unless @env[1][i] == word[next_n]
|
81
|
+
end
|
82
|
+
end
|
83
|
+
@env[0].length.times do |i|
|
84
|
+
last_n = index - @env[0].length + i
|
85
|
+
if @env[0][i] == '#'
|
86
|
+
throw :fail unless last_n < 0
|
87
|
+
elsif options[:lang].types.key? @env[0][i]
|
88
|
+
throw :fail if last_n < 0
|
89
|
+
throw :fail unless options[:lang].types[@env[0][i]].has? word[last_n]
|
90
|
+
else
|
91
|
+
throw :fail if last_n < 0
|
92
|
+
throw :fail unless @env[0][i] == word[last_n]
|
93
|
+
end
|
94
|
+
end
|
95
|
+
return true
|
96
|
+
end
|
97
|
+
false
|
98
|
+
else
|
99
|
+
true
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Kotodama
|
2
|
+
class ChangeList
|
3
|
+
attr_reader :name, :list
|
4
|
+
|
5
|
+
def initialize name
|
6
|
+
@name = name
|
7
|
+
@list = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def push n
|
11
|
+
@list << n
|
12
|
+
end
|
13
|
+
|
14
|
+
alias :<< :push
|
15
|
+
|
16
|
+
def apply_to word, options = {}
|
17
|
+
@list.each do |n|
|
18
|
+
if n.is_a? Change
|
19
|
+
n.apply_to word, options
|
20
|
+
else
|
21
|
+
if options[:lang].changes.key? n
|
22
|
+
options[:lang].changes[n].apply_to word, options
|
23
|
+
else
|
24
|
+
raise "#{n} is not a sound change"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
word
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Kotodama
|
2
|
+
class Language
|
3
|
+
attr_reader :types, :rules, :changes, :options
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@types = {}
|
7
|
+
@rules = {}
|
8
|
+
@changes = {}
|
9
|
+
@options = {:symbol_weight => 1, :phrase_weight => 1}
|
10
|
+
end
|
11
|
+
|
12
|
+
def add_type type
|
13
|
+
@types[type.name] = type
|
14
|
+
end
|
15
|
+
|
16
|
+
def add_rule rule
|
17
|
+
@rules[rule.name] = rule
|
18
|
+
end
|
19
|
+
|
20
|
+
def add_change change
|
21
|
+
@changes[change.name] = change
|
22
|
+
end
|
23
|
+
|
24
|
+
def find_type symbol
|
25
|
+
@types.values.find {|n| n.has? symbol }
|
26
|
+
end
|
27
|
+
|
28
|
+
def generate starting_rule = nil, starting_change = nil
|
29
|
+
starting_rule ||= @options['rule']
|
30
|
+
starting_change ||= @options['change']
|
31
|
+
if @rules.key? starting_rule
|
32
|
+
word = @rules[starting_rule].generate @options.merge(:lang => self)
|
33
|
+
if starting_change
|
34
|
+
if @changes.key? starting_change
|
35
|
+
word.apply @changes[starting_change], @options.merge(:lang => self)
|
36
|
+
else
|
37
|
+
raise "#{starting_change || 'nil'} is not a sound change"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
word.to_s
|
41
|
+
else
|
42
|
+
raise "#{starting_rule || 'nil'} is not a rule"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def apply_to array, starting_change = nil
|
47
|
+
starting_change ||= @options['change']
|
48
|
+
if @changes.key? starting_change
|
49
|
+
array.collect do |n|
|
50
|
+
word = n.to_word
|
51
|
+
if word
|
52
|
+
word.apply @changes[starting_change], @options.merge(:lang => self)
|
53
|
+
else
|
54
|
+
"input `#{n}' cannot be correctly converted into a word"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
else
|
58
|
+
raise "#{starting_change || 'nil'} is not a sound change"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
module Kotodama
|
2
|
+
Parser = Kaiseki::Grammar.subclass do
|
3
|
+
starting :document
|
4
|
+
skipping /\s+/
|
5
|
+
simplify
|
6
|
+
|
7
|
+
rule :document do
|
8
|
+
parse :options_block.optional & :content_block.zero_or_more & :EOF
|
9
|
+
node :params => [:options, :content]
|
10
|
+
action do
|
11
|
+
lang = Language.new
|
12
|
+
lang.options.merge! @options
|
13
|
+
@content.each do |n|
|
14
|
+
if n.is_a? Type
|
15
|
+
lang.add_type n
|
16
|
+
elsif n.is_a? Rule
|
17
|
+
lang.add_rule n
|
18
|
+
elsif n.is_a? ChangeList
|
19
|
+
lang.add_change n
|
20
|
+
else
|
21
|
+
raise "#{n.class} is not a valid Language class"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
lang
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
rule :options_block do
|
29
|
+
parse 'options'.skip & '{'.skip & :option.zero_or_more & '}'.skip
|
30
|
+
action do
|
31
|
+
options_hash = {}
|
32
|
+
@results.each {|n| options_hash.merge! n }
|
33
|
+
options_hash
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
rule :option do
|
38
|
+
parse :ID & '=>'.skip & (:ID | :INT) & ';'.skip
|
39
|
+
node :params => [:key, :value]
|
40
|
+
action { {@key => @value} }
|
41
|
+
end
|
42
|
+
|
43
|
+
rule :content_block do
|
44
|
+
parse :type_block | :rule_block | :change_block
|
45
|
+
end
|
46
|
+
|
47
|
+
rule :type_block do
|
48
|
+
parse 'type'.skip & :ID & '{'.skip & :symbol.zero_or_more & '}'.skip
|
49
|
+
node :params => [:name, :symbols]
|
50
|
+
action do
|
51
|
+
type = Type.new @name
|
52
|
+
@symbols.each do |symbol|
|
53
|
+
if symbol[0].respond_to? :each
|
54
|
+
symbol[0].each {|n| type.add_symbol n, symbol[1] }
|
55
|
+
else
|
56
|
+
type.add_symbol symbol[0], symbol[1]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
type
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
rule :symbol do
|
64
|
+
parse (:ID | :LIT) >> (','.skip & (:ID | :LIT)).zero_or_more & :weight.optional & ';'.skip
|
65
|
+
end
|
66
|
+
|
67
|
+
rule :rule_block do
|
68
|
+
parse 'rule'.skip & :ID & '{'.skip & :rule.zero_or_more & '}'.skip
|
69
|
+
node :params => [:name, :phrases]
|
70
|
+
action do
|
71
|
+
rule = Rule.new @name
|
72
|
+
@phrases.each {|phrase| rule.add_phrase phrase[0], phrase[1] }
|
73
|
+
rule
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
rule :rule do
|
78
|
+
parse (:ID | :LIT).one_or_more & :weight.optional & ';'.skip
|
79
|
+
end
|
80
|
+
|
81
|
+
rule :change_block do
|
82
|
+
parse 'change'.skip & :ID & '{'.skip & :change.zero_or_more & '}'.skip
|
83
|
+
node :params => [:name, :changes]
|
84
|
+
action do
|
85
|
+
change_list = ChangeList.new @name
|
86
|
+
@changes.each {|change| change_list.push change }
|
87
|
+
change_list
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
rule :change do
|
92
|
+
parse :insertion_change | :deletion_change | :transformation_change | :change_name
|
93
|
+
end
|
94
|
+
|
95
|
+
rule :insertion_change do
|
96
|
+
parse 'insert'.skip & (:ID | :LIT).one_or_more & :change_env.optional & ';'.skip
|
97
|
+
override :simplify => false
|
98
|
+
node :params => [:target, :env]
|
99
|
+
action do
|
100
|
+
Change.new :insert, @target, @env[0]
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
rule :deletion_change do
|
105
|
+
parse 'delete'.skip & (:ID | :LIT).one_or_more & :change_env.optional & ';'.skip
|
106
|
+
override :simplify => false
|
107
|
+
node :params => [:target, :env]
|
108
|
+
action do
|
109
|
+
Change.new :delete, @target, @env[0]
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
rule :transformation_change do
|
114
|
+
parse (:ID | :LIT).one_or_more & '>'.skip & (:ID | :LIT).one_or_more & :change_env.optional & ';'.skip
|
115
|
+
override :simplify => false
|
116
|
+
node :params => [:target1, :target2, :env]
|
117
|
+
action do
|
118
|
+
Change.new :transform, [@target1, @target2], @env[0]
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
rule :change_name do
|
123
|
+
parse :ID & ';'.skip
|
124
|
+
end
|
125
|
+
|
126
|
+
rule :change_env do
|
127
|
+
parse '/'.skip & ('#'.optional >> :ID.zero_or_more) & '_'.skip & (:ID.zero_or_more >> '#'.optional)
|
128
|
+
action { @results.to_a }
|
129
|
+
end
|
130
|
+
|
131
|
+
rule :weight do
|
132
|
+
parse ':'.skip & :INT
|
133
|
+
override :simplify => true
|
134
|
+
end
|
135
|
+
|
136
|
+
rule :ID do
|
137
|
+
parse /_*[a-zA-Z][a-zA-Z0-9_]*/
|
138
|
+
action { @results.to_s }
|
139
|
+
end
|
140
|
+
|
141
|
+
rule :INT do
|
142
|
+
parse /\d+/
|
143
|
+
action { @results.to_i }
|
144
|
+
end
|
145
|
+
|
146
|
+
rule :LIT do
|
147
|
+
parse /\'([^']*)\'/ | /\"([^"]*)\"/
|
148
|
+
action { @results.info[:captures][0] }
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Kotodama
|
2
|
+
class Rule
|
3
|
+
attr_reader :name, :phrases, :weights
|
4
|
+
|
5
|
+
def initialize name
|
6
|
+
@name = name
|
7
|
+
@phrases = []
|
8
|
+
@weights = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def add_phrase rule, weight = 1
|
12
|
+
@phrases << rule
|
13
|
+
@weights << weight
|
14
|
+
end
|
15
|
+
|
16
|
+
def generate options = {}
|
17
|
+
word = Word.new
|
18
|
+
phrase = @phrases.random @weights.collect {|n| n || options[:phrase_weight] || 1 }
|
19
|
+
phrase.each do |n|
|
20
|
+
if options[:lang].rules.key? n
|
21
|
+
word << options[:lang].rules[n].generate(options)
|
22
|
+
elsif options[:lang].types.key? n
|
23
|
+
word << options[:lang].types[n].generate(options)
|
24
|
+
else
|
25
|
+
word << n
|
26
|
+
end
|
27
|
+
end
|
28
|
+
word
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
class String
|
2
|
+
def to_word language
|
3
|
+
max_symbol_length = 0
|
4
|
+
language.types.each_pair do |key, type|
|
5
|
+
type.each do |symbol|
|
6
|
+
max_symbol_length = symbol.length if symbol.length > max_symbol_length
|
7
|
+
end
|
8
|
+
end
|
9
|
+
word = Kotodama::Word.new
|
10
|
+
index = 0
|
11
|
+
catch :fail do
|
12
|
+
while index < self.length
|
13
|
+
catch :succeed do
|
14
|
+
max_symbol_length.downto 1 do |length|
|
15
|
+
type = language.find_type self[index, length]
|
16
|
+
if type
|
17
|
+
word << self[index, length]
|
18
|
+
index += length
|
19
|
+
throw :succeed
|
20
|
+
end
|
21
|
+
end
|
22
|
+
throw :fail
|
23
|
+
end
|
24
|
+
end
|
25
|
+
return word
|
26
|
+
end
|
27
|
+
nil
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Kotodama
|
2
|
+
class Type
|
3
|
+
include Enumerable
|
4
|
+
attr_reader :name, :symbols, :weights
|
5
|
+
|
6
|
+
def initialize name
|
7
|
+
@name = name
|
8
|
+
@symbols = []
|
9
|
+
@weights = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def add_symbol symbol, weight = 1
|
13
|
+
@symbols << symbol
|
14
|
+
@weights << weight
|
15
|
+
end
|
16
|
+
|
17
|
+
def [] n
|
18
|
+
@symbols[n]
|
19
|
+
end
|
20
|
+
|
21
|
+
def each &block
|
22
|
+
@symbols.each &block
|
23
|
+
end
|
24
|
+
|
25
|
+
def has? symbol
|
26
|
+
@symbols.member? symbol
|
27
|
+
end
|
28
|
+
|
29
|
+
def generate options = {}
|
30
|
+
@symbols.random(@weights.collect {|n| n || options[:symbol_weight] || 1 })
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Kotodama
|
2
|
+
class Word
|
3
|
+
include Enumerable
|
4
|
+
attr_reader :letters
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@letters = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def [] n
|
11
|
+
@letters[n]
|
12
|
+
end
|
13
|
+
|
14
|
+
def []= n, value
|
15
|
+
@letters[n] = value
|
16
|
+
end
|
17
|
+
|
18
|
+
def empty?
|
19
|
+
@letters.empty?
|
20
|
+
end
|
21
|
+
|
22
|
+
def length
|
23
|
+
@letters.length
|
24
|
+
end
|
25
|
+
|
26
|
+
def push letter
|
27
|
+
if letter.is_a? Word
|
28
|
+
letter.each {|n| @letters.push n }
|
29
|
+
else
|
30
|
+
@letters.push letter
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
alias :<< :push
|
35
|
+
|
36
|
+
def insert index, letter
|
37
|
+
if letter.is_a? Word
|
38
|
+
letter.length.times do |i|
|
39
|
+
@letters.insert index + i, letter[i]
|
40
|
+
end
|
41
|
+
else
|
42
|
+
@letters.insert index, letter
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def delete_at index
|
47
|
+
@letters.delete_at index
|
48
|
+
end
|
49
|
+
|
50
|
+
def each &block
|
51
|
+
@letters.each &block
|
52
|
+
end
|
53
|
+
|
54
|
+
def apply change, options = {}
|
55
|
+
change.apply_to self, options
|
56
|
+
end
|
57
|
+
|
58
|
+
def to_s
|
59
|
+
@letters.join
|
60
|
+
end
|
61
|
+
|
62
|
+
def to_word *args
|
63
|
+
self
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
metadata
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: kotodama
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 3
|
9
|
+
version: 0.0.3
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- William Hamilton-Levi
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-11-23 00:00:00 -05:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: kaiseki
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
version: "0"
|
31
|
+
type: :runtime
|
32
|
+
version_requirements: *id001
|
33
|
+
description: A word generator written in Ruby.
|
34
|
+
email: whamilt1@swarthmore.edu
|
35
|
+
executables:
|
36
|
+
- wordgen
|
37
|
+
extensions: []
|
38
|
+
|
39
|
+
extra_rdoc_files: []
|
40
|
+
|
41
|
+
files:
|
42
|
+
- lib/kotodama.rb
|
43
|
+
- lib/kotodama/type.rb
|
44
|
+
- lib/kotodama/word.rb
|
45
|
+
- lib/kotodama/change.rb
|
46
|
+
- lib/kotodama/array.rb
|
47
|
+
- lib/kotodama/rule.rb
|
48
|
+
- lib/kotodama/change_list.rb
|
49
|
+
- lib/kotodama/string.rb
|
50
|
+
- lib/kotodama/language.rb
|
51
|
+
- lib/kotodama/parser.rb
|
52
|
+
- bin/wordgen
|
53
|
+
has_rdoc: true
|
54
|
+
homepage: http://github.com/phi2dao/Kotodama
|
55
|
+
licenses: []
|
56
|
+
|
57
|
+
post_install_message:
|
58
|
+
rdoc_options: []
|
59
|
+
|
60
|
+
require_paths:
|
61
|
+
- lib
|
62
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
segments:
|
68
|
+
- 0
|
69
|
+
version: "0"
|
70
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
71
|
+
none: false
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
segments:
|
76
|
+
- 0
|
77
|
+
version: "0"
|
78
|
+
requirements: []
|
79
|
+
|
80
|
+
rubyforge_project:
|
81
|
+
rubygems_version: 1.3.7
|
82
|
+
signing_key:
|
83
|
+
specification_version: 3
|
84
|
+
summary: A word generator written in Ruby.
|
85
|
+
test_files: []
|
86
|
+
|