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 CHANGED
@@ -1,3 +1,6 @@
1
+ 0.1.7
2
+ Bug fixes, extract Parse into separate gem
3
+
1
4
  0.1.5:
2
5
  Add "once" and "UniqueNames.delete"
3
6
 
@@ -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)
@@ -1,9 +1,13 @@
1
1
 
2
- class Array
2
+ unless Array.method_defined? :to_h
3
3
 
4
- # @return [Hash]
5
- def to_h
6
- self.reduce({}) { |h, e| h[e.first] = e.last; h }
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
@@ -0,0 +1,11 @@
1
+
2
+ class Integer
3
+
4
+ # @return [Array] an {Array} of n results of call to +block+, where n is self.
5
+ def of(&block)
6
+ r = []
7
+ self.times { r.push(block.()) }
8
+ return r;
9
+ end
10
+
11
+ end
@@ -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
@@ -53,7 +53,6 @@ class Story
53
53
  #
54
54
  c = code <<
55
55
  "require 'enumerable/empty'\n"<<
56
- "require 'enumerable/lazy'\n"<<
57
56
  "require 'set'\n"<<
58
57
  "require 'story'\n"<<
59
58
  "require 'enumerable/new'\n"<<
@@ -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 %W{
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 %W{
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 ["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"]
30
+ UniqueNames.new(Names.english_male)
242
31
  end
243
32
 
244
33
  # @return [UniqueNames]
245
34
  def self.english_female
246
- UniqueNames.new ["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"]
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.5
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-02-28 00:00:00.000000000 Z
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
@@ -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
@@ -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
@@ -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
-