gorilla 0.0.1.beta
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/README.rdoc +3 -0
- data/Rakefile +13 -0
- data/lib/gorilla.rb +11 -0
- data/lib/gorilla/all.rb +5 -0
- data/lib/gorilla/core_ext.rb +72 -0
- data/lib/gorilla/scanner.rb +222 -0
- data/lib/gorilla/scanners.rb +9 -0
- data/lib/gorilla/scanners/omni_scanner.rb +21 -0
- data/lib/gorilla/scanners/temperature_scanner.rb +11 -0
- data/lib/gorilla/scanners/time_scanner.rb +34 -0
- data/lib/gorilla/scanners/volume_scanner.rb +15 -0
- data/lib/gorilla/scanners/weight_scanner.rb +10 -0
- data/lib/gorilla/scantron_ext.rb +83 -0
- data/lib/gorilla/temperature.rb +19 -0
- data/lib/gorilla/time.rb +51 -0
- data/lib/gorilla/unit.rb +453 -0
- data/lib/gorilla/version.rb +10 -0
- data/lib/gorilla/volume.rb +13 -0
- data/lib/gorilla/weight.rb +8 -0
- data/test/gorilla/scanners/temperature_scanner_test.rb +42 -0
- data/test/gorilla/scanners/time_scanner_test.rb +123 -0
- data/test/gorilla/scanners/volume_scanner_test.rb +148 -0
- data/test/gorilla/scanners/weight_scanner_test.rb +57 -0
- data/test/gorilla/time_test.rb +14 -0
- data/test/test_helper.rb +10 -0
- metadata +122 -0
data/README.rdoc
ADDED
data/Rakefile
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
require 'rake/testtask'
|
|
2
|
+
require 'rdoctest/task'
|
|
3
|
+
|
|
4
|
+
Rdoctest::Task.new do |t|
|
|
5
|
+
t.ruby_opts << '-rgorilla/all -rgorilla/core_ext'
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
Rake::TestTask.new do |t|
|
|
9
|
+
t.libs << 'test'
|
|
10
|
+
t.pattern = 'test/**/*_test.rb'
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
task :default => [:doctest, :test]
|
data/lib/gorilla.rb
ADDED
data/lib/gorilla/all.rb
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
module Gorilla::CoreExt
|
|
2
|
+
def unit
|
|
3
|
+
Gorilla::Unit.new self
|
|
4
|
+
end
|
|
5
|
+
alias units unit
|
|
6
|
+
|
|
7
|
+
Gorilla.units.each_pair do |klass_name, configs|
|
|
8
|
+
configs.each_key do |unit|
|
|
9
|
+
klass = Gorilla.const_get klass_name[/\w+$/]
|
|
10
|
+
|
|
11
|
+
define_method unit do
|
|
12
|
+
klass.new self, unit
|
|
13
|
+
end
|
|
14
|
+
alias_method "#{unit}s", unit if klass.pluralize
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
if Gorilla.const_defined? :Temperature
|
|
19
|
+
alias Celsius celsius
|
|
20
|
+
alias C celsius
|
|
21
|
+
alias Fahrenheit fahrenheit
|
|
22
|
+
alias F fahrenheit
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
if Gorilla.const_defined? :Temperature
|
|
26
|
+
alias s second
|
|
27
|
+
alias sec second
|
|
28
|
+
alias ms millisecond
|
|
29
|
+
alias min minute
|
|
30
|
+
alias h hour
|
|
31
|
+
alias hr hour
|
|
32
|
+
alias d day
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
if Gorilla.const_defined? :Volume
|
|
36
|
+
alias litre liter
|
|
37
|
+
alias l liter
|
|
38
|
+
alias L liter
|
|
39
|
+
alias millilitre milliliter
|
|
40
|
+
alias ml milliliter
|
|
41
|
+
alias mL milliliter
|
|
42
|
+
alias centilitre centiliter
|
|
43
|
+
alias cl centiliter
|
|
44
|
+
alias cL centiliter
|
|
45
|
+
alias t teaspoon
|
|
46
|
+
alias tsp teaspoon
|
|
47
|
+
alias T tablespoon
|
|
48
|
+
alias tbs tablespoon
|
|
49
|
+
alias tbsp tablespoon
|
|
50
|
+
alias fl_oz fluid_ounce
|
|
51
|
+
alias oz_fl fluid_ounce
|
|
52
|
+
alias c cup
|
|
53
|
+
alias cu cup
|
|
54
|
+
alias pt pint
|
|
55
|
+
alias qt quart
|
|
56
|
+
alias gal gallon
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
if Gorilla.const_defined? :Weight
|
|
60
|
+
alias g gram
|
|
61
|
+
alias kg kilogram
|
|
62
|
+
alias mg milligram
|
|
63
|
+
alias lb pound
|
|
64
|
+
alias lbs pounds
|
|
65
|
+
alias oz ounce
|
|
66
|
+
alias ozs ounces
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
class Numeric
|
|
71
|
+
include Gorilla::CoreExt
|
|
72
|
+
end
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
require 'gorilla/scantron_ext'
|
|
3
|
+
|
|
4
|
+
module Gorilla
|
|
5
|
+
# A {Scantron}[http://github.com/stephencelis/scantron] scanner class from
|
|
6
|
+
# which all Gorilla::Scanner classes inherit.
|
|
7
|
+
#
|
|
8
|
+
# Your own Gorilla::Scanners will inherit the data assigned their
|
|
9
|
+
# Gorilla::Unit definitions so that, for example, units defined as metric
|
|
10
|
+
# will have additional scanner rules created for each prefix.
|
|
11
|
+
#
|
|
12
|
+
# For more information, see Gorilla::Scanner.rule.
|
|
13
|
+
class Scanner < ::Scantron::Scanner
|
|
14
|
+
# Maps metric prefixes to regular expressions used for parsing.
|
|
15
|
+
METRIC_MAP = {
|
|
16
|
+
# :yotta => /Y/,
|
|
17
|
+
# :zetta => /Z/,
|
|
18
|
+
# :exa => /E/,
|
|
19
|
+
# :peta => /P/,
|
|
20
|
+
# :tera => /T/,
|
|
21
|
+
# :giga => /G/,
|
|
22
|
+
# :mega => /M/,
|
|
23
|
+
:kilo => /k(?:ilo)?/,
|
|
24
|
+
# :hecto => /H/,
|
|
25
|
+
# :deca => /da/,
|
|
26
|
+
# :deci => /d/,
|
|
27
|
+
:centi => /c(?:enti)?/,
|
|
28
|
+
:milli => /m(?:illi)?/
|
|
29
|
+
# :micro => /μ/,
|
|
30
|
+
# :nano => /n/,
|
|
31
|
+
# :pico => /p/,
|
|
32
|
+
# :femto => /f/,
|
|
33
|
+
# :atto => /a/,
|
|
34
|
+
# :zepto => /z/,
|
|
35
|
+
# :yocto => /y/
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
before_match do |r|
|
|
39
|
+
# Adjust for amounts before units.
|
|
40
|
+
pre_match = r.scanner.pre_match
|
|
41
|
+
if result = AmountScanner.new(pre_match).perform.last
|
|
42
|
+
between = r.scanner.string[
|
|
43
|
+
result.scanner.pos, pre_match.length - result.scanner.pos
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
if between =~ /\S/
|
|
47
|
+
result = nil
|
|
48
|
+
else
|
|
49
|
+
r.length = r.length + (r.offset - result.offset)
|
|
50
|
+
r.offset = result.offset
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
amount = result.value if result
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Adjust for generic amounts.
|
|
57
|
+
unless amount
|
|
58
|
+
if match = pre_match =~ /\ban? *$/i
|
|
59
|
+
r.length = r.length + (r.offset - match)
|
|
60
|
+
r.offset = match
|
|
61
|
+
amount = 1
|
|
62
|
+
elsif match = pre_match =~ /\b(?:a )couple *$/i
|
|
63
|
+
r.length = r.length + (r.offset - match)
|
|
64
|
+
r.offset = match
|
|
65
|
+
amount = 2
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Adjust for trailing amounts ("...and a half").
|
|
70
|
+
if match = r.scanner.post_match.match(/^ and(?: an?)? (.+)/)
|
|
71
|
+
if r.scantron.class.parse(match[1]).nil?
|
|
72
|
+
if result = NumberScanner.new(match[1]).perform.first
|
|
73
|
+
if result.offset == 0
|
|
74
|
+
r.length = r.length + result.length + match.offset(1).first
|
|
75
|
+
amount += result.value
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
# Adjust for trailing ranges ("...or two").
|
|
80
|
+
elsif match = r.scanner.post_match.match(/^ or (.+)/)
|
|
81
|
+
if r.scantron.class.parse(match[1]).nil?
|
|
82
|
+
if result = NumberScanner.new(match[1]).perform.first
|
|
83
|
+
if result.offset == 0
|
|
84
|
+
r.length = r.length + result.length + match.offset(1).first
|
|
85
|
+
amount = amount..result.value
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
# Adjust for periods.
|
|
90
|
+
elsif r.scanner.post_match =~ /^\./
|
|
91
|
+
r.length += 1
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
r[:amount] = amount
|
|
95
|
+
r
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
after_match do |r|
|
|
99
|
+
case amount = r[:amount]
|
|
100
|
+
when Range
|
|
101
|
+
unit_class = constantize r.rule.data[:class_name]
|
|
102
|
+
unit_class.new(amount.min, r.name)..unit_class.new(amount.max, r.name)
|
|
103
|
+
else
|
|
104
|
+
constantize(r.rule.data[:class_name]).new amount, r.name
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
class << self
|
|
109
|
+
# ==== Options
|
|
110
|
+
#
|
|
111
|
+
# [<tt>:class_name</tt>] The Gorilla::Unit return class for the rule
|
|
112
|
+
# given. By default, it is inferred from the name
|
|
113
|
+
# of the scanner, so that a "BogosityScanner"
|
|
114
|
+
# would try instantiate matches as "Bogosity"
|
|
115
|
+
# units.
|
|
116
|
+
#
|
|
117
|
+
# [<tt>:metric</tt>] Whether or not additional rules should be
|
|
118
|
+
# generated for metric prefixes. By default, it
|
|
119
|
+
# is inferred from the unit's original
|
|
120
|
+
# definition.
|
|
121
|
+
#
|
|
122
|
+
# ==== Example
|
|
123
|
+
#
|
|
124
|
+
# Here we define a metric unit and scanner rule:
|
|
125
|
+
#
|
|
126
|
+
# class Beauty < Gorilla::Unit
|
|
127
|
+
# base :Helen, :metric => true
|
|
128
|
+
# end
|
|
129
|
+
#
|
|
130
|
+
# And here we define the scanner rule:
|
|
131
|
+
#
|
|
132
|
+
# class BeautyScanner < Gorilla::Scanner
|
|
133
|
+
# rule :Helen, /[Hh](?:elen)?s?/
|
|
134
|
+
# end
|
|
135
|
+
#
|
|
136
|
+
# BeautyScanner.scan '1 milliHelen is required to launch the ship.'
|
|
137
|
+
# # => [(1 milliHelen)]
|
|
138
|
+
#
|
|
139
|
+
# BeautyScanner.scan '2 kiloHelens'
|
|
140
|
+
# # => [(2 kiloHelens)]
|
|
141
|
+
#
|
|
142
|
+
# The return class (Beauty) is inferred from the scanner's class name
|
|
143
|
+
# (less "Scanner"), and the metric setting is taken from the matching
|
|
144
|
+
# rule on that class, but both can be overridden or made explicit.
|
|
145
|
+
#
|
|
146
|
+
# class BeautyScanner < Gorilla::Scanner
|
|
147
|
+
# rule :Helen, /[Hh](?:elen)?s?/, :class_name => 'Beauty',
|
|
148
|
+
# :metric => true
|
|
149
|
+
# end
|
|
150
|
+
def rule unit, regexp, data = {}, &block
|
|
151
|
+
if class_name = data.delete(:class_name)
|
|
152
|
+
klass = constantize class_name
|
|
153
|
+
elsif class_name = name.sub!(/Scanner$/, '')
|
|
154
|
+
klass = constantize class_name rescue nil
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
config = { :class_name => class_name || 'Unit' }
|
|
158
|
+
config.update klass.rules[unit] if klass && klass.rules[unit]
|
|
159
|
+
config.update data
|
|
160
|
+
|
|
161
|
+
super unit, /(?<=^|[\d ])#{regexp}(?=[\d ]|\b|$)/, config, &block
|
|
162
|
+
|
|
163
|
+
if config[:metric]
|
|
164
|
+
METRIC_MAP.each_pair do |pre, sub|
|
|
165
|
+
super :"#{pre}#{unit}", /(?<=^|[\d ])#{sub}#{regexp}(?= |\b|$)/,
|
|
166
|
+
config, &block
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
private
|
|
172
|
+
|
|
173
|
+
def constantize class_name
|
|
174
|
+
names = class_name.split '::'
|
|
175
|
+
names.shift if names.first && names.first.empty?
|
|
176
|
+
|
|
177
|
+
constant = Object
|
|
178
|
+
names.each do |name|
|
|
179
|
+
constant = if constant.const_defined?(name)
|
|
180
|
+
constant.const_get name
|
|
181
|
+
else
|
|
182
|
+
constant.const_missing name
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
constant
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def scan
|
|
190
|
+
results = perform
|
|
191
|
+
array = []
|
|
192
|
+
range = false
|
|
193
|
+
|
|
194
|
+
results.each.with_index do |result, index|
|
|
195
|
+
if range
|
|
196
|
+
range = false
|
|
197
|
+
next
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
if !result.value.is_a?(Range) and next_result = results[index + 1]
|
|
201
|
+
substring = string[
|
|
202
|
+
result.scanner.pos, next_result.offset - result.scanner.pos
|
|
203
|
+
]
|
|
204
|
+
|
|
205
|
+
case substring
|
|
206
|
+
when /^ *(and|or|to) *$/
|
|
207
|
+
if $1 == 'and' && result.pre_match !~ /between $/
|
|
208
|
+
array << (result.value + next_result.value)
|
|
209
|
+
else
|
|
210
|
+
array << (result.value..next_result.value)
|
|
211
|
+
end
|
|
212
|
+
range = true and next
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
array << result.value
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
array
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
require 'scantron'
|
|
2
|
+
require 'number_scanner'
|
|
3
|
+
require 'range_scanner'
|
|
4
|
+
require 'gorilla/scanner'
|
|
5
|
+
require 'gorilla/scanners/temperature_scanner'
|
|
6
|
+
require 'gorilla/scanners/time_scanner'
|
|
7
|
+
require 'gorilla/scanners/volume_scanner'
|
|
8
|
+
require 'gorilla/scanners/weight_scanner'
|
|
9
|
+
require 'gorilla/scanners/omni_scanner'
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
require 'gorilla/scanner'
|
|
3
|
+
require 'gorilla/scanners/temperature_scanner'
|
|
4
|
+
require 'gorilla/scanners/time_scanner'
|
|
5
|
+
require 'gorilla/scanners/volume_scanner'
|
|
6
|
+
require 'gorilla/scanners/weight_scanner'
|
|
7
|
+
|
|
8
|
+
module Gorilla
|
|
9
|
+
# An all-purpose units scanner combining the rules of TemperatureScanner,
|
|
10
|
+
# TimeScanner, VolumeScanner, and WeightScanner.
|
|
11
|
+
#
|
|
12
|
+
# Gorilla::OmniScanner.
|
|
13
|
+
# scan "Add 1 cup flour (125g). Bake @ 350F for 25 min."
|
|
14
|
+
# # => [(1 cup), (125 grams), (350° Fahrenheit), (25 minutes)]
|
|
15
|
+
class OmniScanner < Scanner
|
|
16
|
+
rules.update TemperatureScanner.rules
|
|
17
|
+
rules.update TimeScanner.rules
|
|
18
|
+
rules.update VolumeScanner.rules
|
|
19
|
+
rules.update WeightScanner.rules
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
require 'gorilla/scanner'
|
|
3
|
+
require 'gorilla/temperature'
|
|
4
|
+
|
|
5
|
+
module Gorilla
|
|
6
|
+
class TemperatureScanner < Scanner
|
|
7
|
+
degrees = /°| ?deg(?:ree)?s? /
|
|
8
|
+
rule :celsius, /#{degrees}?(?:C|[Cc]elsius)|℃/
|
|
9
|
+
rule :fahrenheit, /#{degrees}?(?:F|[Ff]ahrenheit)|℉/
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
require 'gorilla/scanner'
|
|
2
|
+
require 'gorilla/time'
|
|
3
|
+
|
|
4
|
+
module Gorilla
|
|
5
|
+
class TimeScanner < Scanner
|
|
6
|
+
rule :second, /[Ss](?:ec(?:ond)?s?)?|S(?:EC(?:OND)?S?)/
|
|
7
|
+
rule :minute, /[Mm](?:in(?:ute)?s?)?|M(?:IN(?:UTE)?S?)/
|
|
8
|
+
rule :hour, /[Hh](?:(?:ou)?rs?)?|H(?:(?:OU)?RS?)?/
|
|
9
|
+
rule :day, /[Dd](?:ays?)?|D(?:AYS?)/
|
|
10
|
+
rule :week, /[Ww](?:ee)?ks?|W(?:EE)?KS?/
|
|
11
|
+
rule :month, /[Mm](?:o(?:n(?:th))?s?)|M(?:O(?:N(?:TH))?S?)/
|
|
12
|
+
rule :year, /[Yy](?:ea)?rs?|Y(?:EA)?RS?/
|
|
13
|
+
rule :decade, /[Dd]ecades?|DECADES?/
|
|
14
|
+
rule :century, /[Cc]entur(?:y|ies)|CENTUR(?:Y|IES)/
|
|
15
|
+
rule :millennium, /[Mm]illeni(?:um|a)|MILLENI(?:UM|A)/
|
|
16
|
+
|
|
17
|
+
rule :iso8601, /\bP(\d+Y)?(\d+W)?(\d+D)?T?(\d+H)?(\d+M)?([\d.]+S)?\b/ do |r|
|
|
18
|
+
y = Time.new r.scanner[1].to_i, :year
|
|
19
|
+
w = Time.new r.scanner[2].to_i, :week
|
|
20
|
+
d = Time.new r.scanner[3].to_i, :day
|
|
21
|
+
h = Time.new r.scanner[4].to_i, :hour
|
|
22
|
+
m = Time.new r.scanner[5].to_i, :minute
|
|
23
|
+
s = Time.new r.scanner[6].to_f, :second
|
|
24
|
+
y + w + d + h + m + s
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
rule :delimited, /\d{1,2}(?::\d{2}){1,2}/ do |r|
|
|
28
|
+
h, m, s = r.to_s.split ':'
|
|
29
|
+
time = Time.new(h, :hour) + Time.new(m, :minute)
|
|
30
|
+
time += Time.new(s, :second) if s
|
|
31
|
+
time
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require 'gorilla/scanner'
|
|
2
|
+
require 'gorilla/volume'
|
|
3
|
+
|
|
4
|
+
module Gorilla
|
|
5
|
+
class VolumeScanner < Scanner
|
|
6
|
+
rule :liter, /[Ll](?:i?t(?:er|re?)|t|r)?s?/
|
|
7
|
+
rule :teaspoon, /(?:t|(?:[Tt](?:ea)?s(?:p(?:oo)?n?)?))s?/
|
|
8
|
+
rule :tablespoon, /(?:T|(?:[Tt](?:bl?s?p?|a?b(?:le?)?(?:s(?:p(?:oo)?n?)))))s?/
|
|
9
|
+
rule :fluid_ounce, /(?:[Ff]l(?:uid|\.)? )?[Oo](?:unce|z)s?/
|
|
10
|
+
rule :cup, /[Cc]u?p?s?/
|
|
11
|
+
rule :pint, /[Pp](?:(?:i?n)?t)?s?/
|
|
12
|
+
rule :quart, /[Qq](?:(?:ua)?r)?ts?/
|
|
13
|
+
rule :gallon, /[Gg](?:a?l(?:(?:lo)?n)?)s?/
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
require 'scantron'
|
|
2
|
+
require 'amount_scanner'
|
|
3
|
+
|
|
4
|
+
class NumberScanner
|
|
5
|
+
words = WORD_MAP.keys.map { |v| v.sub /y$/, 'y-?' } * '|'
|
|
6
|
+
rules[:human].regexp = \
|
|
7
|
+
%r{(?:\b(?:\d+ (?:an?d? )*)?(?:#{words}))(?: ?\b(?:#{words}|an?d?|\d+)\b ?)*}i
|
|
8
|
+
|
|
9
|
+
def self.human_to_number words
|
|
10
|
+
numbers = words.split(/\W+/).map { |w|
|
|
11
|
+
WORD_MAP[w.downcase] || parse(w) || w
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
case numbers.count { |n| n.is_a? Numeric }
|
|
15
|
+
when 0 then false
|
|
16
|
+
when 1 then numbers[0]
|
|
17
|
+
else
|
|
18
|
+
array = []
|
|
19
|
+
total = 0
|
|
20
|
+
limit = 1
|
|
21
|
+
words = []
|
|
22
|
+
reset = true
|
|
23
|
+
|
|
24
|
+
numbers.each.with_index do |n, i|
|
|
25
|
+
words << n and next if n.is_a? String
|
|
26
|
+
|
|
27
|
+
if n == 1 && limit == 1
|
|
28
|
+
reset = false
|
|
29
|
+
next
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
if n >= 1_000
|
|
33
|
+
total += n * limit
|
|
34
|
+
limit = 1
|
|
35
|
+
reset = true
|
|
36
|
+
else
|
|
37
|
+
if n < 1
|
|
38
|
+
if words.join(' ') =~ /\band\b/
|
|
39
|
+
if total > 0 && total % 1_000
|
|
40
|
+
if total % (factor = 10 ** (total.to_i.to_s.size - 1)) == 0
|
|
41
|
+
limit = n * factor
|
|
42
|
+
else
|
|
43
|
+
limit = n
|
|
44
|
+
end
|
|
45
|
+
else
|
|
46
|
+
limit += n
|
|
47
|
+
end
|
|
48
|
+
else
|
|
49
|
+
limit *= n
|
|
50
|
+
end
|
|
51
|
+
elsif words.join(' ') =~ /\band\b/ && numbers[i + 1].to_i < 1
|
|
52
|
+
total += limit
|
|
53
|
+
limit = n
|
|
54
|
+
elsif !reset && limit >= 1 &&
|
|
55
|
+
m1 = (n > (m2 = numbers[i + 1].to_i) ? n + m2 : n) and
|
|
56
|
+
m = [limit, m1].sort and
|
|
57
|
+
!m[1].to_s[-(m0 = m[0].to_i.to_s.size), m0].to_i.zero?
|
|
58
|
+
|
|
59
|
+
array << total + limit
|
|
60
|
+
total = 0
|
|
61
|
+
limit = n
|
|
62
|
+
elsif !reset && limit == 1 && n > numbers[i + 1].to_i &&
|
|
63
|
+
m = [limit, n + numbers[i + 1].to_i].sort and
|
|
64
|
+
!m[1].to_s[-(m[0].to_i.to_s.size), m[0].to_i.to_s.size].to_i.zero?
|
|
65
|
+
|
|
66
|
+
array << total + limit
|
|
67
|
+
total = 0
|
|
68
|
+
limit = n
|
|
69
|
+
else
|
|
70
|
+
n > limit ? limit *= n : limit += n
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
total += limit if numbers[i + 1].nil?
|
|
74
|
+
reset = false
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
words.clear
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
array.empty? ? total : array << total
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|