story-gen 0.1.5 → 0.1.7
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
|