story-gen 0.1.5 → 0.1.7
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/NEWS +3 -0
- data/lib/array/chomp.rb +5 -3
- data/lib/array/to_h.rb +8 -4
- data/lib/integer/of.rb +11 -0
- data/lib/names.rb +237 -0
- data/lib/story/compile.rb +0 -1
- data/lib/unique_names.rb +6 -217
- metadata +21 -6
- data/lib/code.rb +0 -138
- data/lib/parse.rb +0 -468
- data/lib/strscan/substr.rb +0 -16
data/NEWS
CHANGED
data/lib/array/chomp.rb
CHANGED
@@ -5,6 +5,11 @@ class Array
|
|
5
5
|
#
|
6
6
|
# @return [self]
|
7
7
|
#
|
8
|
+
# @example
|
9
|
+
#
|
10
|
+
# [1, 2, 3].chomp!(3) #=> [1, 2]
|
11
|
+
# [1, 2, 3].chomp!(2) #=> [1, 2, 3]
|
12
|
+
#
|
8
13
|
def chomp!(item)
|
9
14
|
self.pop if item == self.last
|
10
15
|
return self
|
@@ -16,6 +21,3 @@ class Array
|
|
16
21
|
end
|
17
22
|
|
18
23
|
end
|
19
|
-
|
20
|
-
# p [1, 2, 3].chomp(3)
|
21
|
-
# p [1, 2, 3].chomp(2)
|
data/lib/array/to_h.rb
CHANGED
@@ -1,9 +1,13 @@
|
|
1
1
|
|
2
|
-
|
2
|
+
unless Array.method_defined? :to_h
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
class Array
|
5
|
+
|
6
|
+
# @return [Hash]
|
7
|
+
def to_h
|
8
|
+
self.reduce({}) { |h, e| h[e.first] = e.last; h }
|
9
|
+
end
|
10
|
+
|
7
11
|
end
|
8
12
|
|
9
13
|
end
|
data/lib/integer/of.rb
ADDED
data/lib/names.rb
ADDED
@@ -0,0 +1,237 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
class Names
|
4
|
+
|
5
|
+
# @return [Array<String>]
|
6
|
+
def self.russian_male
|
7
|
+
%W{
|
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
|
+
Георгий
|
35
|
+
Герман
|
36
|
+
Глеб
|
37
|
+
Григорий
|
38
|
+
|
39
|
+
Давид
|
40
|
+
Даниил
|
41
|
+
Денис
|
42
|
+
Дмитрий
|
43
|
+
|
44
|
+
Евгений
|
45
|
+
Егор
|
46
|
+
Ефим
|
47
|
+
|
48
|
+
Захар
|
49
|
+
|
50
|
+
Иван
|
51
|
+
Игорь
|
52
|
+
Илья
|
53
|
+
|
54
|
+
Кирилл
|
55
|
+
Константин
|
56
|
+
|
57
|
+
Лев
|
58
|
+
Леонид
|
59
|
+
|
60
|
+
Максим
|
61
|
+
Марк
|
62
|
+
Матвей
|
63
|
+
Мирослав
|
64
|
+
Михаил
|
65
|
+
|
66
|
+
Назар
|
67
|
+
Нестор
|
68
|
+
Никита
|
69
|
+
Николай
|
70
|
+
|
71
|
+
Олег
|
72
|
+
Орест
|
73
|
+
Оскар
|
74
|
+
Остап
|
75
|
+
|
76
|
+
Павел
|
77
|
+
Петр
|
78
|
+
Платон
|
79
|
+
|
80
|
+
Ренат
|
81
|
+
Родион
|
82
|
+
Роман
|
83
|
+
Ростислав
|
84
|
+
Руслан
|
85
|
+
|
86
|
+
Святослав
|
87
|
+
Семен
|
88
|
+
Сергей
|
89
|
+
Станислав
|
90
|
+
Степан
|
91
|
+
|
92
|
+
Тарас
|
93
|
+
Тимофей
|
94
|
+
Тимур
|
95
|
+
|
96
|
+
Федор
|
97
|
+
Филипп
|
98
|
+
|
99
|
+
Эдуард
|
100
|
+
|
101
|
+
Юрий
|
102
|
+
|
103
|
+
Яков
|
104
|
+
Ян
|
105
|
+
Ярослав
|
106
|
+
}
|
107
|
+
end
|
108
|
+
|
109
|
+
# @return [Array<String>]
|
110
|
+
def self.russian_female
|
111
|
+
%W{
|
112
|
+
Агата
|
113
|
+
Ада
|
114
|
+
Адель
|
115
|
+
Адриана
|
116
|
+
Алевтина
|
117
|
+
Александра
|
118
|
+
Алина
|
119
|
+
Алиса
|
120
|
+
Алла
|
121
|
+
Альбина
|
122
|
+
Анастасия
|
123
|
+
Ангелина
|
124
|
+
Анжела
|
125
|
+
Анна
|
126
|
+
Антонина
|
127
|
+
Анфиса
|
128
|
+
Арина
|
129
|
+
|
130
|
+
Богдана
|
131
|
+
Борислава
|
132
|
+
|
133
|
+
Валентина
|
134
|
+
Валерия
|
135
|
+
Варвара
|
136
|
+
Василиса
|
137
|
+
Вера
|
138
|
+
Вероника
|
139
|
+
Виктория
|
140
|
+
Виолетта
|
141
|
+
|
142
|
+
Галина
|
143
|
+
|
144
|
+
Дарья
|
145
|
+
Диана
|
146
|
+
|
147
|
+
Ева
|
148
|
+
Евгения
|
149
|
+
Екатерина
|
150
|
+
Елена
|
151
|
+
Елизавета
|
152
|
+
|
153
|
+
Жанна
|
154
|
+
|
155
|
+
Зинаида
|
156
|
+
Зоя
|
157
|
+
|
158
|
+
Илона
|
159
|
+
Инга
|
160
|
+
Инесса
|
161
|
+
Инна
|
162
|
+
Ирина
|
163
|
+
|
164
|
+
Карина
|
165
|
+
Кира
|
166
|
+
Клавдия
|
167
|
+
Клара
|
168
|
+
Кристина
|
169
|
+
Ксения
|
170
|
+
|
171
|
+
Лариса
|
172
|
+
Лидия
|
173
|
+
Лилия
|
174
|
+
Любовь
|
175
|
+
Людмила
|
176
|
+
|
177
|
+
Майя
|
178
|
+
Маргарита
|
179
|
+
Марина
|
180
|
+
Мария
|
181
|
+
Марта
|
182
|
+
Марьяна
|
183
|
+
Мирослава
|
184
|
+
|
185
|
+
Надежда
|
186
|
+
Наталья
|
187
|
+
Нелли
|
188
|
+
Нина
|
189
|
+
|
190
|
+
Оксана
|
191
|
+
Ольга
|
192
|
+
|
193
|
+
Полина
|
194
|
+
|
195
|
+
Раиса
|
196
|
+
Регина
|
197
|
+
Римма
|
198
|
+
Роза
|
199
|
+
Роксана
|
200
|
+
Руслана
|
201
|
+
|
202
|
+
Светлана
|
203
|
+
Снежана
|
204
|
+
София
|
205
|
+
|
206
|
+
Таисия
|
207
|
+
Тамара
|
208
|
+
Татьяна
|
209
|
+
|
210
|
+
Ульяна
|
211
|
+
|
212
|
+
Христина
|
213
|
+
|
214
|
+
Эвелина
|
215
|
+
Элеонора
|
216
|
+
Эльвира
|
217
|
+
Эмма
|
218
|
+
|
219
|
+
Юлиана
|
220
|
+
Юлия
|
221
|
+
|
222
|
+
Яна
|
223
|
+
Ярослава
|
224
|
+
}
|
225
|
+
end
|
226
|
+
|
227
|
+
# @return [Array<String>]
|
228
|
+
def self.english_male
|
229
|
+
["Jack", "Thomas", "Joshua", "Oliver", "Harry", "James", "Charlie", "William", "Daniel", "Samuel", "Alfie", "Benjamin", "Joseph", "George", "Callum", "Jake", "Ethan", "Lewis", "Luke", "Mohammed", "Matthew", "Dylan", "Jacob", "Alexander", "Ryan", "Adam", "Tyler", "Harvey", "Liam", "Max", "Cameron", "Harrison", "Jamie", "Jayden", "Leo", "Owen", "Henry", "Connor", "Archie", "Nathan", "Ben", "Muhammad", "Oscar", "Edward", "Lucas", "Michael", "Isaac", "Aaron", "Kyle", "Noah", "Finley", "Rhys", "Bradley", "Charles", "Toby", "Louis", "Brandon", "Logan", "Mason", "Reece", "Kieran", "Alex", "Riley", "Finlay", "Kai", "Freddie", "David", "Harley", "Kian", "Mohammad", "Bailey", "Sam", "Luca", "Joel", "Theo", "Leon", "John", "Robert", "Ashton", "Ellis", "Joe", "Aidan", "Billy", "Corey", "Hayden", "Evan", "Zachary", "Taylor", "Christopher", "Sebastian", "Elliot", "Morgan", "Jay", "Dominic", "Reuben", "Gabriel", "Sean", "Louie", "Andrew", "Frederick", "Ewan", "Zak"]
|
230
|
+
end
|
231
|
+
|
232
|
+
# @return [Array<String>]
|
233
|
+
def self.english_female
|
234
|
+
["Olivia", "Grace", "Ruby", "Jessica", "Emily", "Sophie", "Chloe", "Lucy", "Lily", "Ella", "Ellie", "Amelia", "Charlotte", "Katie", "Mia", "Hannah", "Evie", "Megan", "Amy", "Isabella", "Millie", "Isabelle", "Abigail", "Freya", "Molly", "Daisy", "Holly", "Emma", "Erin", "Poppy", "Jasmine", "Phoebe", "Leah", "Keira", "Caitlin", "Imogen", "Madison", "Rebecca", "Elizabeth", "Georgia", "Sophia", "Lauren", "Scarlett", "Amber", "Ava", "Eleanor", "Bethany", "Alice", "Isabel", "Summer", "Paige", "Anna", "Lola", "Libby", "Eva", "Maisie", "Isobel", "Brooke", "Lilly", "Alisha", "Tia", "Sarah", "Amelie", "Gracie", "Faith", "Rosie", "Courtney", "Matilda", "Niamh", "Maddison", "Sienna", "Eve", "Aimee", "Skye", "Zara", "Isla", "Shannon", "Madeleine", "Harriet", "Zoe", "Nicole", "Sofia", "Abbie", "Maya", "Lacey", "Rachel", "Francesca", "Layla", "Lydia", "Alicia", "Hollie", "Martha", "Alexandra", "Julia", "Natasha", "Lexie", "Mollie", "Morgan", "Maria", "Demi", "Rose", "Laura", "Lara", "Victoria", "Tilly", "Sara", "Evelyn", "Eloise"]
|
235
|
+
end
|
236
|
+
|
237
|
+
end
|
data/lib/story/compile.rb
CHANGED
data/lib/unique_names.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
require 'set'
|
3
3
|
require 'once'
|
4
|
+
require 'names'
|
4
5
|
|
5
6
|
# Infinite set of unique names.
|
6
7
|
class UniqueNames
|
@@ -11,239 +12,27 @@ class UniqueNames
|
|
11
12
|
@base_names = base_names
|
12
13
|
@postfix_id = 0
|
13
14
|
@deleted_names = Set.new
|
14
|
-
reinit_current_names
|
15
|
+
reinit_current_names
|
15
16
|
end
|
16
17
|
|
17
18
|
# @return [UniqueNames]
|
18
19
|
def self.russian_male
|
19
|
-
UniqueNames.new
|
20
|
-
Адам
|
21
|
-
Адриан
|
22
|
-
Александр
|
23
|
-
Алексей
|
24
|
-
Анатолий
|
25
|
-
Андрей
|
26
|
-
Антон
|
27
|
-
Аркадий
|
28
|
-
Арсений
|
29
|
-
Артем
|
30
|
-
Артур
|
31
|
-
|
32
|
-
Богдан
|
33
|
-
Борис
|
34
|
-
|
35
|
-
Вадим
|
36
|
-
Валентин
|
37
|
-
Валерий
|
38
|
-
Василий
|
39
|
-
Виктор
|
40
|
-
Виталий
|
41
|
-
Владимир
|
42
|
-
Владислав
|
43
|
-
Вячеслав
|
44
|
-
|
45
|
-
Геннадий
|
46
|
-
Георгий
|
47
|
-
Герман
|
48
|
-
Глеб
|
49
|
-
Григорий
|
50
|
-
|
51
|
-
Давид
|
52
|
-
Даниил
|
53
|
-
Денис
|
54
|
-
Дмитрий
|
55
|
-
|
56
|
-
Евгений
|
57
|
-
Егор
|
58
|
-
Ефим
|
59
|
-
|
60
|
-
Захар
|
61
|
-
|
62
|
-
Иван
|
63
|
-
Игорь
|
64
|
-
Илья
|
65
|
-
|
66
|
-
Кирилл
|
67
|
-
Константин
|
68
|
-
|
69
|
-
Лев
|
70
|
-
Леонид
|
71
|
-
|
72
|
-
Максим
|
73
|
-
Марк
|
74
|
-
Матвей
|
75
|
-
Мирослав
|
76
|
-
Михаил
|
77
|
-
|
78
|
-
Назар
|
79
|
-
Нестор
|
80
|
-
Никита
|
81
|
-
Николай
|
82
|
-
|
83
|
-
Олег
|
84
|
-
Орест
|
85
|
-
Оскар
|
86
|
-
Остап
|
87
|
-
|
88
|
-
Павел
|
89
|
-
Петр
|
90
|
-
Платон
|
91
|
-
|
92
|
-
Ренат
|
93
|
-
Родион
|
94
|
-
Роман
|
95
|
-
Ростислав
|
96
|
-
Руслан
|
97
|
-
|
98
|
-
Святослав
|
99
|
-
Семен
|
100
|
-
Сергей
|
101
|
-
Станислав
|
102
|
-
Степан
|
103
|
-
|
104
|
-
Тарас
|
105
|
-
Тимофей
|
106
|
-
Тимур
|
107
|
-
|
108
|
-
Федор
|
109
|
-
Филипп
|
110
|
-
|
111
|
-
Эдуард
|
112
|
-
|
113
|
-
Юрий
|
114
|
-
|
115
|
-
Яков
|
116
|
-
Ян
|
117
|
-
Ярослав
|
118
|
-
}
|
20
|
+
UniqueNames.new(Names.russian_male)
|
119
21
|
end
|
120
22
|
|
121
23
|
# @return [UniqueNames]
|
122
24
|
def self.russian_female
|
123
|
-
UniqueNames.new
|
124
|
-
Агата
|
125
|
-
Ада
|
126
|
-
Адель
|
127
|
-
Адриана
|
128
|
-
Алевтина
|
129
|
-
Александра
|
130
|
-
Алина
|
131
|
-
Алиса
|
132
|
-
Алла
|
133
|
-
Альбина
|
134
|
-
Анастасия
|
135
|
-
Ангелина
|
136
|
-
Анжела
|
137
|
-
Анна
|
138
|
-
Антонина
|
139
|
-
Анфиса
|
140
|
-
Арина
|
141
|
-
|
142
|
-
Богдана
|
143
|
-
Борислава
|
144
|
-
|
145
|
-
Валентина
|
146
|
-
Валерия
|
147
|
-
Варвара
|
148
|
-
Василиса
|
149
|
-
Вера
|
150
|
-
Вероника
|
151
|
-
Виктория
|
152
|
-
Виолетта
|
153
|
-
|
154
|
-
Галина
|
155
|
-
|
156
|
-
Дарья
|
157
|
-
Диана
|
158
|
-
|
159
|
-
Ева
|
160
|
-
Евгения
|
161
|
-
Екатерина
|
162
|
-
Елена
|
163
|
-
Елизавета
|
164
|
-
|
165
|
-
Жанна
|
166
|
-
|
167
|
-
Зинаида
|
168
|
-
Зоя
|
169
|
-
|
170
|
-
Илона
|
171
|
-
Инга
|
172
|
-
Инесса
|
173
|
-
Инна
|
174
|
-
Ирина
|
175
|
-
|
176
|
-
Карина
|
177
|
-
Кира
|
178
|
-
Клавдия
|
179
|
-
Клара
|
180
|
-
Кристина
|
181
|
-
Ксения
|
182
|
-
|
183
|
-
Лариса
|
184
|
-
Лидия
|
185
|
-
Лилия
|
186
|
-
Любовь
|
187
|
-
Людмила
|
188
|
-
|
189
|
-
Майя
|
190
|
-
Маргарита
|
191
|
-
Марина
|
192
|
-
Мария
|
193
|
-
Марта
|
194
|
-
Марьяна
|
195
|
-
Мирослава
|
196
|
-
|
197
|
-
Надежда
|
198
|
-
Наталья
|
199
|
-
Нелли
|
200
|
-
Нина
|
201
|
-
|
202
|
-
Оксана
|
203
|
-
Ольга
|
204
|
-
|
205
|
-
Полина
|
206
|
-
|
207
|
-
Раиса
|
208
|
-
Регина
|
209
|
-
Римма
|
210
|
-
Роза
|
211
|
-
Роксана
|
212
|
-
Руслана
|
213
|
-
|
214
|
-
Светлана
|
215
|
-
Снежана
|
216
|
-
София
|
217
|
-
|
218
|
-
Таисия
|
219
|
-
Тамара
|
220
|
-
Татьяна
|
221
|
-
|
222
|
-
Ульяна
|
223
|
-
|
224
|
-
Христина
|
225
|
-
|
226
|
-
Эвелина
|
227
|
-
Элеонора
|
228
|
-
Эльвира
|
229
|
-
Эмма
|
230
|
-
|
231
|
-
Юлиана
|
232
|
-
Юлия
|
233
|
-
|
234
|
-
Яна
|
235
|
-
Ярослава
|
236
|
-
}
|
25
|
+
UniqueNames.new(Names.russian_female)
|
237
26
|
end
|
238
27
|
|
239
28
|
# @return [UniqueNames]
|
240
29
|
def self.english_male
|
241
|
-
UniqueNames.new
|
30
|
+
UniqueNames.new(Names.english_male)
|
242
31
|
end
|
243
32
|
|
244
33
|
# @return [UniqueNames]
|
245
34
|
def self.english_female
|
246
|
-
UniqueNames.new
|
35
|
+
UniqueNames.new(Names.english_female)
|
247
36
|
end
|
248
37
|
|
249
38
|
# @param [String] name
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: story-gen
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.7
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,8 +9,24 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2016-
|
13
|
-
dependencies:
|
12
|
+
date: 2016-05-27 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: parse-framework
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
14
30
|
description: Generate stories from descriptions based on Facts!
|
15
31
|
email: various.furriness@gmail.com
|
16
32
|
executables:
|
@@ -19,11 +35,12 @@ extensions: []
|
|
19
35
|
extra_rdoc_files: []
|
20
36
|
files:
|
21
37
|
- lib/once.rb
|
22
|
-
- lib/strscan/substr.rb
|
23
38
|
- lib/any.rb
|
39
|
+
- lib/integer/of.rb
|
24
40
|
- lib/fact.rb
|
25
41
|
- lib/story/compile.rb
|
26
42
|
- lib/story/samples_dir.rb
|
43
|
+
- lib/names.rb
|
27
44
|
- lib/array/case_equal_fixed.rb
|
28
45
|
- lib/array/separate.rb
|
29
46
|
- lib/array/to_h.rb
|
@@ -31,14 +48,12 @@ files:
|
|
31
48
|
- lib/string/ru_downcase.rb
|
32
49
|
- lib/string/lchomp.rb
|
33
50
|
- lib/story.rb
|
34
|
-
- lib/parse.rb
|
35
51
|
- lib/enumerable/empty.rb
|
36
52
|
- lib/enumerable/lazy.rb
|
37
53
|
- lib/enumerable/new.rb
|
38
54
|
- lib/enumerable/sample.rb
|
39
55
|
- lib/hash/put.rb
|
40
56
|
- lib/unique_names.rb
|
41
|
-
- lib/code.rb
|
42
57
|
- lib/object/map_.rb
|
43
58
|
- lib/object/to_rb.rb
|
44
59
|
- README.md
|
data/lib/code.rb
DELETED
@@ -1,138 +0,0 @@
|
|
1
|
-
|
2
|
-
class Code
|
3
|
-
|
4
|
-
# @api private
|
5
|
-
# @note used by {Code}, {::code}, {::non_code} only.
|
6
|
-
#
|
7
|
-
# @param [Array<String, NonCodePart>] parts
|
8
|
-
# @param [Object, nil] metadata
|
9
|
-
#
|
10
|
-
def initialize(parts, metadata = nil)
|
11
|
-
@parts = parts
|
12
|
-
@metadata = metadata
|
13
|
-
end
|
14
|
-
|
15
|
-
# @overload + str
|
16
|
-
# @param [String] str
|
17
|
-
# @return [Code]
|
18
|
-
# @overload + code
|
19
|
-
# @param [Code] code
|
20
|
-
# @return [Code]
|
21
|
-
def + arg
|
22
|
-
case arg
|
23
|
-
when String then self + Code.new([arg])
|
24
|
-
when Code then Code.new(self.parts + arg.parts)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
# @overload << str
|
29
|
-
# Appends +str+ to self.
|
30
|
-
# @param [String] str
|
31
|
-
# @return [self]
|
32
|
-
# @overload << code
|
33
|
-
# Appends +str+ to self.
|
34
|
-
# @param [Code] code
|
35
|
-
# @return [self]
|
36
|
-
def << arg
|
37
|
-
case arg
|
38
|
-
when String then self << Code.new([arg])
|
39
|
-
when Code then @parts.concat(arg.parts); self
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
# @overload metadata(obj)
|
44
|
-
# @param [Object] obj
|
45
|
-
# @return [Code] a {Code} with +obj+ attached to it. The +obj+ can later
|
46
|
-
# be retrieved with {#metadata}().
|
47
|
-
# @overload metadata
|
48
|
-
# @return [Object, nil] an {Object} attached to this {Code} with
|
49
|
-
# {#metadata}(obj) or nil if no {Object} is attached.
|
50
|
-
def metadata(*args)
|
51
|
-
if args.empty?
|
52
|
-
then @metadata
|
53
|
-
else Code.new(@parts, args.first)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
# @yieldparam [Object] part
|
58
|
-
# @yieldreturn [String]
|
59
|
-
# @return [Code] a {Code} with {#non_code_parts} mapped by the passed block.
|
60
|
-
def map_non_code_parts(&f)
|
61
|
-
Code.new(
|
62
|
-
@parts.map do |part|
|
63
|
-
case part
|
64
|
-
when String then part
|
65
|
-
when NonCodePart then f.(part.data)
|
66
|
-
end
|
67
|
-
end
|
68
|
-
)
|
69
|
-
end
|
70
|
-
|
71
|
-
# @return [Enumerable<Object>] non-code parts of this {Code} introduced
|
72
|
-
# with {::non_code}. See also {#map_non_code_parts}.
|
73
|
-
def non_code_parts
|
74
|
-
@parts.select { |part| part.is_a? NonCodePart }.map(&:data)
|
75
|
-
end
|
76
|
-
|
77
|
-
# @return [String]
|
78
|
-
def to_s
|
79
|
-
if (x = @parts.find { |part| part.is_a? NonCodePart }) then
|
80
|
-
raise "non-code part: #{x.inspect}"
|
81
|
-
end
|
82
|
-
@parts.join
|
83
|
-
end
|
84
|
-
|
85
|
-
# @return [String]
|
86
|
-
def inspect
|
87
|
-
Inspectable.new(@parts, @metadata).inspect
|
88
|
-
end
|
89
|
-
|
90
|
-
# @api private
|
91
|
-
# @note used by {Code}, {::non_code} only.
|
92
|
-
NonCodePart = Struct.new :data
|
93
|
-
|
94
|
-
protected
|
95
|
-
|
96
|
-
# @!visibility private
|
97
|
-
attr_reader :parts
|
98
|
-
|
99
|
-
private
|
100
|
-
|
101
|
-
# @!visibility private
|
102
|
-
Inspectable = Struct.new :parts, :metadata
|
103
|
-
|
104
|
-
end
|
105
|
-
|
106
|
-
class Array
|
107
|
-
|
108
|
-
# @param [Code, String] delimiter
|
109
|
-
# @return [Code]
|
110
|
-
def join_code(delimiter = "")
|
111
|
-
self.reduce do |r, self_i|
|
112
|
-
code << r << delimiter << self_i
|
113
|
-
end or
|
114
|
-
code
|
115
|
-
end
|
116
|
-
|
117
|
-
end
|
118
|
-
|
119
|
-
# @overload code
|
120
|
-
# @return [Code] an empty {Code}.
|
121
|
-
# @overload code(str)
|
122
|
-
# @param [String] str
|
123
|
-
# @return [Code] +str+ converted to {Code}.
|
124
|
-
def code(str = nil)
|
125
|
-
if str
|
126
|
-
then Code.new([str])
|
127
|
-
else Code.new([])
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
# @param [Object] data
|
132
|
-
# @return [Code] a {Code} consisting of the single non-code part +data+.
|
133
|
-
# See also {Code#non_code_parts}.
|
134
|
-
def non_code(data)
|
135
|
-
Code.new([Code::NonCodePart.new(data)])
|
136
|
-
end
|
137
|
-
|
138
|
-
alias __non_code__ non_code
|
data/lib/parse.rb
DELETED
@@ -1,468 +0,0 @@
|
|
1
|
-
require 'strscan'
|
2
|
-
require 'strscan/substr'
|
3
|
-
|
4
|
-
# To disable YARD warnings:
|
5
|
-
# @!parse
|
6
|
-
# class Exception
|
7
|
-
# def message
|
8
|
-
# end
|
9
|
-
# end
|
10
|
-
# class Array
|
11
|
-
# end
|
12
|
-
# class String
|
13
|
-
# end
|
14
|
-
|
15
|
-
class Parse
|
16
|
-
|
17
|
-
#
|
18
|
-
# parses +text+.
|
19
|
-
#
|
20
|
-
# If $DEBUG == true then it prints {#rule} calls to stdout.
|
21
|
-
#
|
22
|
-
# @param [String] text
|
23
|
-
# @param [String] file file the +text+ is taken from.
|
24
|
-
# @raise [Parse::Error, IOError]
|
25
|
-
# @return [Object] what {#start} returns (non-nil).
|
26
|
-
#
|
27
|
-
def call(text, file = "-")
|
28
|
-
@text = StringScanner.new(text)
|
29
|
-
@file = file
|
30
|
-
@most_probable_error = nil
|
31
|
-
@allow_errors = true
|
32
|
-
@rule_start_pos = nil
|
33
|
-
r = start()
|
34
|
-
if r.nil? or not @text.eos? then
|
35
|
-
if @most_probable_error
|
36
|
-
then raise @most_probable_error
|
37
|
-
else raise Error.new(Position.new(@text.pos, @text, @file), "syntax error")
|
38
|
-
end
|
39
|
-
end
|
40
|
-
return r
|
41
|
-
end
|
42
|
-
|
43
|
-
class Position
|
44
|
-
|
45
|
-
include Comparable
|
46
|
-
|
47
|
-
# @api private
|
48
|
-
# @note used by {Parse#pos} only
|
49
|
-
# @param [Integer] text_pos
|
50
|
-
# @param [StringScanner] text
|
51
|
-
# @param [String] file
|
52
|
-
def initialize(text_pos, text, file)
|
53
|
-
@text = text
|
54
|
-
@text_pos = text_pos
|
55
|
-
@file = file
|
56
|
-
end
|
57
|
-
|
58
|
-
# @return [String]
|
59
|
-
attr_reader :file
|
60
|
-
|
61
|
-
# @return [Integer]
|
62
|
-
def line
|
63
|
-
line_and_column[0]
|
64
|
-
end
|
65
|
-
|
66
|
-
# @return [Integer]
|
67
|
-
def column
|
68
|
-
line_and_column[1]
|
69
|
-
end
|
70
|
-
|
71
|
-
# @param [Object] other
|
72
|
-
# @return [-1, 0, 1, nil]
|
73
|
-
def <=> other
|
74
|
-
return nil unless other.is_a? Position and self.text.equal? other.text
|
75
|
-
self.text_pos <=> other.text_pos
|
76
|
-
end
|
77
|
-
|
78
|
-
# @param [Object] other
|
79
|
-
# @return [Boolean]
|
80
|
-
def == other
|
81
|
-
(self <=> other) == 0
|
82
|
-
end
|
83
|
-
|
84
|
-
# @!visibility private
|
85
|
-
attr_reader :text_pos
|
86
|
-
|
87
|
-
# @!visibility private
|
88
|
-
attr_reader :text
|
89
|
-
|
90
|
-
private
|
91
|
-
|
92
|
-
def line_and_column
|
93
|
-
@line_and_column ||= begin
|
94
|
-
s = @text.substr(0, @text_pos)
|
95
|
-
lines = s.split("\n", -1).to_a
|
96
|
-
[
|
97
|
-
line = if lines.size == 0 then 0 else lines.size - 1 end,
|
98
|
-
column = (lines.last || "").size
|
99
|
-
].tap do
|
100
|
-
@text = nil
|
101
|
-
end
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
end
|
106
|
-
|
107
|
-
class Error < Exception
|
108
|
-
|
109
|
-
# @param [Position] pos
|
110
|
-
# @param [String] message
|
111
|
-
def initialize(pos, message)
|
112
|
-
super(message)
|
113
|
-
@pos = pos
|
114
|
-
end
|
115
|
-
|
116
|
-
# @return [Position]
|
117
|
-
attr_reader :pos
|
118
|
-
|
119
|
-
# @param [Error] other +self+.{#pos} must be equal to +other+.{#pos}.
|
120
|
-
# @return [Error] an {Error} with {Exception#message} combined from
|
121
|
-
# {Exception#message}s of +self+ and +other+ (using "or" word).
|
122
|
-
def or other
|
123
|
-
raise "#{self.pos} == #{other.pos} is false" unless self.pos == other.pos
|
124
|
-
Error.new(pos, "#{self.message} or #{other.message}")
|
125
|
-
end
|
126
|
-
|
127
|
-
end
|
128
|
-
|
129
|
-
class Expected < Error
|
130
|
-
|
131
|
-
# @param [Position] pos
|
132
|
-
# @param [String] what_1
|
133
|
-
# @param [*String] what_2_n
|
134
|
-
def initialize(pos, what_1, *what_2_n)
|
135
|
-
@what = [what_1, *what_2_n]
|
136
|
-
super(pos, "#{@what.join(", ")} expected")
|
137
|
-
end
|
138
|
-
|
139
|
-
# (see Error#or)
|
140
|
-
def or other
|
141
|
-
if other.is_a? Expected
|
142
|
-
raise "#{self.pos} == #{other.pos} is false" unless self.pos == other.pos
|
143
|
-
Expected.new(pos, *(self.what + other.what).uniq)
|
144
|
-
else
|
145
|
-
super(other)
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
|
-
protected
|
150
|
-
|
151
|
-
# @!visibility private
|
152
|
-
# @return [Array<String>]
|
153
|
-
attr_reader :what
|
154
|
-
|
155
|
-
end
|
156
|
-
|
157
|
-
protected
|
158
|
-
|
159
|
-
# @!method start()
|
160
|
-
# @abstract
|
161
|
-
#
|
162
|
-
# Implementation of {#call}. Or the starting rule.
|
163
|
-
#
|
164
|
-
# @return [Object, nil] a result of parsing or nil if the parsing failed.
|
165
|
-
# @raise [Parse::Error] if the parsing failed.
|
166
|
-
# @raise [IOError]
|
167
|
-
#
|
168
|
-
|
169
|
-
# @return [Position] current {Position} in +text+ passed to {#call}.
|
170
|
-
def pos
|
171
|
-
Position.new(@text.pos, @text, @file)
|
172
|
-
end
|
173
|
-
|
174
|
-
#
|
175
|
-
# scans +arg+ in +text+ passed to {#call} starting from {#pos} and,
|
176
|
-
# if scanned successfully, advances {#pos} and returns the scanned
|
177
|
-
# sub-{String}. Otherwise it calls {#expected} and returns nil.
|
178
|
-
#
|
179
|
-
# @param [String, Regexp] arg
|
180
|
-
# @return [String, nil]
|
181
|
-
#
|
182
|
-
def scan(arg)
|
183
|
-
case arg
|
184
|
-
when Regexp then @text.scan(arg) or expected(pos, %(regexp "#{arg.source}"))
|
185
|
-
when String then @text.scan(Regexp.new(Regexp.escape(arg))) or expected(pos, %("#{arg}"))
|
186
|
-
end
|
187
|
-
end
|
188
|
-
|
189
|
-
# defines method +name+ with body +body+. Inside +body+ {#rule_start_pos}
|
190
|
-
# is the value of {#pos} right before the defined method's call.
|
191
|
-
#
|
192
|
-
# @param [Symbol] name
|
193
|
-
# @return [void]
|
194
|
-
#
|
195
|
-
def self.rule(name, &body)
|
196
|
-
define_method(name) do
|
197
|
-
old_rule_start_pos = @rule_start_pos
|
198
|
-
@rule_start_pos = pos
|
199
|
-
begin
|
200
|
-
if $DEBUG then STDERR.puts("#{pos.file}:#{pos.line+1}:#{pos.column+1}: entering rule :#{name}"); end
|
201
|
-
r = instance_eval(&body)
|
202
|
-
if $DEBUG then STDERR.puts("#{pos.file}:#{pos.line+1}:#{pos.column+1}: exiting rule :#{name} #{if r.nil? then "(with nil)" end}"); end
|
203
|
-
r
|
204
|
-
ensure
|
205
|
-
@rule_start_pos = old_rule_start_pos
|
206
|
-
end
|
207
|
-
end
|
208
|
-
end
|
209
|
-
|
210
|
-
# See {Parse::rule}.
|
211
|
-
#
|
212
|
-
# @return [Position]
|
213
|
-
#
|
214
|
-
attr_reader :rule_start_pos
|
215
|
-
|
216
|
-
# @param [ASTNode] node
|
217
|
-
# @return [ASTNode] +node+ which is
|
218
|
-
# {ASTNode#initialize_pos}({#rule_start_pos})-ed.
|
219
|
-
def _1(node)
|
220
|
-
node.initialize_pos(rule_start_pos)
|
221
|
-
end
|
222
|
-
|
223
|
-
# is {#pos} at the end of the text?
|
224
|
-
def end?
|
225
|
-
@text.eos?
|
226
|
-
end
|
227
|
-
|
228
|
-
alias eos? end?
|
229
|
-
|
230
|
-
# is {#pos} at the beginning of the text?
|
231
|
-
def begin?
|
232
|
-
@text.pos == 0
|
233
|
-
end
|
234
|
-
|
235
|
-
# alias for {#_1} and {#_2}
|
236
|
-
def _(arg = nil, &block)
|
237
|
-
if arg
|
238
|
-
then _1(arg)
|
239
|
-
else _2(&block)
|
240
|
-
end
|
241
|
-
end
|
242
|
-
|
243
|
-
# --------------------------
|
244
|
-
# @!group Parser Combinators
|
245
|
-
# --------------------------
|
246
|
-
|
247
|
-
# calls +f+ and returns true.
|
248
|
-
#
|
249
|
-
# @return [true]
|
250
|
-
def act(&f)
|
251
|
-
f.()
|
252
|
-
true
|
253
|
-
end
|
254
|
-
|
255
|
-
#
|
256
|
-
# calls +f+. If +f+ results in nil then it restores {#pos} to the
|
257
|
-
# value before the call.
|
258
|
-
#
|
259
|
-
# @return what +f+ returns.
|
260
|
-
#
|
261
|
-
def _2(&f)
|
262
|
-
old_text_pos = @text.pos
|
263
|
-
f.() or begin
|
264
|
-
@text.pos = old_text_pos
|
265
|
-
nil
|
266
|
-
end
|
267
|
-
end
|
268
|
-
|
269
|
-
# calls +f+ using {#_2} many times until it returns nil.
|
270
|
-
#
|
271
|
-
# @return [Array] an {Array} of non-nil results of +f+.
|
272
|
-
#
|
273
|
-
def many(&f)
|
274
|
-
r = []
|
275
|
-
while true
|
276
|
-
f0 = _(&f)
|
277
|
-
if f0
|
278
|
-
then r.push(f0)
|
279
|
-
else break
|
280
|
-
end
|
281
|
-
end
|
282
|
-
r
|
283
|
-
end
|
284
|
-
|
285
|
-
# calls +f+ using {#_2}.
|
286
|
-
#
|
287
|
-
# @return [Array] an empty {Array} if +f+ results in nil and an {Array}
|
288
|
-
# containing the single result of +f+ otherwise.
|
289
|
-
#
|
290
|
-
def opt(&f)
|
291
|
-
[_(&f)].compact
|
292
|
-
end
|
293
|
-
|
294
|
-
# The same as <code>f.() and many(&f)</code>.
|
295
|
-
#
|
296
|
-
# @return [Array, nil] an {Array} of results of +f+ or nil if the first call
|
297
|
-
# to +f+ returned nil.
|
298
|
-
#
|
299
|
-
def one_or_more(&f)
|
300
|
-
f1 = f.() and f2_n = many(&f) and [f1, *f2_n]
|
301
|
-
end
|
302
|
-
|
303
|
-
alias many1 one_or_more
|
304
|
-
|
305
|
-
# @overload not_follows(*method_ids)
|
306
|
-
#
|
307
|
-
# calls methods specified by +method_ids+. If any of them returns non-nil
|
308
|
-
# then this method returns nil, otherwise it returns true. The methods
|
309
|
-
# are called inside {#no_errors}. {#pos} is restored after each method's
|
310
|
-
# call.
|
311
|
-
#
|
312
|
-
# @param [Array<Symbol>] method_ids
|
313
|
-
# @return [true, nil]
|
314
|
-
#
|
315
|
-
# @overload not_follows(&f)
|
316
|
-
#
|
317
|
-
# calls +f+. If +f+ returns non-nil then this method returns nil,
|
318
|
-
# otherwise it returns true. +f+ is called inside {#no_errors}.
|
319
|
-
# {#pos} is restored after +f+'s call.
|
320
|
-
#
|
321
|
-
# @return [true, nil]
|
322
|
-
#
|
323
|
-
def not_follows(*method_ids, &f)
|
324
|
-
if f then
|
325
|
-
if no_errors { _{ f.() } }
|
326
|
-
then nil
|
327
|
-
else true
|
328
|
-
end
|
329
|
-
else # if not method_ids.empty? then
|
330
|
-
if no_errors { method_ids.any? { |method_id| _{ __send__(method_id) } } }
|
331
|
-
then nil
|
332
|
-
else true
|
333
|
-
end
|
334
|
-
end
|
335
|
-
end
|
336
|
-
|
337
|
-
# ----------
|
338
|
-
# @!endgroup
|
339
|
-
# ----------
|
340
|
-
|
341
|
-
# --------------
|
342
|
-
# @!group Errors
|
343
|
-
# --------------
|
344
|
-
|
345
|
-
#
|
346
|
-
# sets {#most_probable_error} to +error+ if it is more probable than
|
347
|
-
# {#most_probable_error} or if {#most_probable_error} is nil.
|
348
|
-
#
|
349
|
-
# @param [Error] error
|
350
|
-
# @return [nil]
|
351
|
-
#
|
352
|
-
def error(error)
|
353
|
-
if @allow_errors then
|
354
|
-
if @most_probable_error.nil? or @most_probable_error.pos < error.pos then
|
355
|
-
@most_probable_error = error
|
356
|
-
elsif @most_probable_error and @most_probable_error.pos == error.pos then
|
357
|
-
@most_probable_error = @most_probable_error.or error
|
358
|
-
else
|
359
|
-
# do nothing
|
360
|
-
end
|
361
|
-
end
|
362
|
-
return nil
|
363
|
-
end
|
364
|
-
|
365
|
-
# macro
|
366
|
-
def expected(pos, what_1, *what_2_n)
|
367
|
-
error(Expected.new(pos, what_1, *what_2_n))
|
368
|
-
end
|
369
|
-
|
370
|
-
# macro
|
371
|
-
def expect(what_1, *what_2_n, &body)
|
372
|
-
p = pos and body.() or expected(p, what_1, *what_2_n)
|
373
|
-
end
|
374
|
-
|
375
|
-
# calls +block+. Inside the +block+ {#error} has no effect.
|
376
|
-
#
|
377
|
-
# @return what +block+ returns.
|
378
|
-
#
|
379
|
-
def no_errors(&block)
|
380
|
-
old_allow_errors = @allow_errors
|
381
|
-
begin
|
382
|
-
@allow_errors = false
|
383
|
-
block.()
|
384
|
-
ensure
|
385
|
-
@allow_errors = old_allow_errors
|
386
|
-
end
|
387
|
-
end
|
388
|
-
|
389
|
-
# @return [Error, nil]
|
390
|
-
attr_reader :most_probable_error
|
391
|
-
|
392
|
-
# ----------
|
393
|
-
# @!endgroup
|
394
|
-
# ----------
|
395
|
-
|
396
|
-
end
|
397
|
-
|
398
|
-
module ASTNode
|
399
|
-
|
400
|
-
# macro
|
401
|
-
def self.new(*properties, &body)
|
402
|
-
(if properties.empty? then Class.new else Struct.new(*properties); end).tap do |c|
|
403
|
-
c.class_eval do
|
404
|
-
|
405
|
-
include ASTNode
|
406
|
-
|
407
|
-
# @return [Boolean]
|
408
|
-
def === other
|
409
|
-
self.class == other.class and
|
410
|
-
self.members.all? { |member| self.__send__(member) === other.__send__(member) }
|
411
|
-
end
|
412
|
-
|
413
|
-
if properties.empty? then
|
414
|
-
# @return [Array<Symbol>]
|
415
|
-
def members
|
416
|
-
[]
|
417
|
-
end
|
418
|
-
end
|
419
|
-
|
420
|
-
end
|
421
|
-
c.class_eval(&body) if body
|
422
|
-
end
|
423
|
-
end
|
424
|
-
|
425
|
-
# @param [Parse::Position] pos
|
426
|
-
# @return [self]
|
427
|
-
def initialize_pos(pos)
|
428
|
-
@pos = pos
|
429
|
-
self
|
430
|
-
end
|
431
|
-
|
432
|
-
# @note {#initialize_pos} must be called before this method can be used.
|
433
|
-
# @return [Parse::Position]
|
434
|
-
def pos
|
435
|
-
raise "initialize_pos() must be called before this method can be used" unless @pos
|
436
|
-
@pos
|
437
|
-
end
|
438
|
-
|
439
|
-
end
|
440
|
-
|
441
|
-
# Alias for {ASTNode#new}.
|
442
|
-
def node(*properties, &body)
|
443
|
-
ASTNode.new(*properties, &body)
|
444
|
-
end
|
445
|
-
|
446
|
-
# class Calc < Parse
|
447
|
-
#
|
448
|
-
# Number = ASTNode.new :val
|
449
|
-
#
|
450
|
-
# rule :start do
|
451
|
-
# x1 = number and x2 = many { comma and number } and [x1, *x2]
|
452
|
-
# end
|
453
|
-
#
|
454
|
-
# rule :number do
|
455
|
-
# s = scan(/\d+/) and _(Number[s])
|
456
|
-
# end
|
457
|
-
#
|
458
|
-
# rule :comma do
|
459
|
-
# scan(",")
|
460
|
-
# end
|
461
|
-
#
|
462
|
-
# end
|
463
|
-
#
|
464
|
-
# begin
|
465
|
-
# p Calc.new.("10, 20", __FILE__)
|
466
|
-
# rescue Parse::Error => e
|
467
|
-
# puts "error: #{e.pos.file}:#{e.pos.line}:#{e.pos.column}: #{e.message}"
|
468
|
-
# end
|
data/lib/strscan/substr.rb
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
|
2
|
-
class StringScanner
|
3
|
-
|
4
|
-
# @param [Integer] start some value of {StringScanner#pos}.
|
5
|
-
# @param [Integer] end_ some value of {StringScanner#pos}.
|
6
|
-
# @return [String]
|
7
|
-
def substr(start, end_)
|
8
|
-
old_pos = self.pos
|
9
|
-
self.pos = start
|
10
|
-
result = peek(end_ - start)
|
11
|
-
self.pos = old_pos
|
12
|
-
return result
|
13
|
-
end
|
14
|
-
|
15
|
-
end
|
16
|
-
|