xls_function 0.1.0
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.
- checksums.yaml +7 -0
- data/.github/workflows/publish_gem.yml +25 -0
- data/.github/workflows/rspec.yml +30 -0
- data/.gitignore +13 -0
- data/.rspec +2 -0
- data/.rubocop.yml +41 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +8 -0
- data/LICENSE +201 -0
- data/README.md +48 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/xls_function/class_dictionary.rb +7 -0
- data/lib/xls_function/converter.rb +24 -0
- data/lib/xls_function/converters/date_converter.rb +30 -0
- data/lib/xls_function/converters/date_serial_converter.rb +17 -0
- data/lib/xls_function/converters/number_converter.rb +28 -0
- data/lib/xls_function/converters/time_converter.rb +158 -0
- data/lib/xls_function/converters/time_serial_converter.rb +97 -0
- data/lib/xls_function/default_logger.rb +7 -0
- data/lib/xls_function/error.rb +49 -0
- data/lib/xls_function/evaluators/arguments_definable.rb +39 -0
- data/lib/xls_function/evaluators/binary_operation_evaluator.rb +20 -0
- data/lib/xls_function/evaluators/binary_operations/add.rb +13 -0
- data/lib/xls_function/evaluators/binary_operations/concat.rb +13 -0
- data/lib/xls_function/evaluators/binary_operations/divide.rb +13 -0
- data/lib/xls_function/evaluators/binary_operations/equal.rb +13 -0
- data/lib/xls_function/evaluators/binary_operations/greater_than.rb +13 -0
- data/lib/xls_function/evaluators/binary_operations/greater_than_or_equal_to.rb +13 -0
- data/lib/xls_function/evaluators/binary_operations/less_than.rb +13 -0
- data/lib/xls_function/evaluators/binary_operations/less_than_or_equal_to.rb +13 -0
- data/lib/xls_function/evaluators/binary_operations/multiple.rb +13 -0
- data/lib/xls_function/evaluators/binary_operations/not_equal.rb +13 -0
- data/lib/xls_function/evaluators/binary_operations/power.rb +13 -0
- data/lib/xls_function/evaluators/binary_operations/subtract.rb +13 -0
- data/lib/xls_function/evaluators/class_dictionary.rb +30 -0
- data/lib/xls_function/evaluators/error_detector.rb +29 -0
- data/lib/xls_function/evaluators/evaluable.rb +37 -0
- data/lib/xls_function/evaluators/false_evaluator.rb +19 -0
- data/lib/xls_function/evaluators/function_evaluator.rb +133 -0
- data/lib/xls_function/evaluators/functions/and.rb +21 -0
- data/lib/xls_function/evaluators/functions/asc.rb +17 -0
- data/lib/xls_function/evaluators/functions/char.rb +19 -0
- data/lib/xls_function/evaluators/functions/clean.rb +22 -0
- data/lib/xls_function/evaluators/functions/code.rb +18 -0
- data/lib/xls_function/evaluators/functions/concat.rb +27 -0
- data/lib/xls_function/evaluators/functions/date.rb +57 -0
- data/lib/xls_function/evaluators/functions/date_value.rb +17 -0
- data/lib/xls_function/evaluators/functions/day.rb +15 -0
- data/lib/xls_function/evaluators/functions/dbcs.rb +18 -0
- data/lib/xls_function/evaluators/functions/e_date.rb +19 -0
- data/lib/xls_function/evaluators/functions/e_o_month.rb +20 -0
- data/lib/xls_function/evaluators/functions/exact.rb +16 -0
- data/lib/xls_function/evaluators/functions/find.rb +28 -0
- data/lib/xls_function/evaluators/functions/fixed.rb +46 -0
- data/lib/xls_function/evaluators/functions/hour.rb +15 -0
- data/lib/xls_function/evaluators/functions/if.rb +24 -0
- data/lib/xls_function/evaluators/functions/ifs.rb +20 -0
- data/lib/xls_function/evaluators/functions/int.rb +15 -0
- data/lib/xls_function/evaluators/functions/iserror.rb +20 -0
- data/lib/xls_function/evaluators/functions/isnumber.rb +15 -0
- data/lib/xls_function/evaluators/functions/lambda.rb +57 -0
- data/lib/xls_function/evaluators/functions/left.rb +16 -0
- data/lib/xls_function/evaluators/functions/len.rb +15 -0
- data/lib/xls_function/evaluators/functions/let.rb +24 -0
- data/lib/xls_function/evaluators/functions/lower.rb +15 -0
- data/lib/xls_function/evaluators/functions/mid.rb +23 -0
- data/lib/xls_function/evaluators/functions/minute.rb +15 -0
- data/lib/xls_function/evaluators/functions/month.rb +15 -0
- data/lib/xls_function/evaluators/functions/not.rb +15 -0
- data/lib/xls_function/evaluators/functions/now.rb +19 -0
- data/lib/xls_function/evaluators/functions/or.rb +21 -0
- data/lib/xls_function/evaluators/functions/power.rb +16 -0
- data/lib/xls_function/evaluators/functions/proper.rb +17 -0
- data/lib/xls_function/evaluators/functions/replace.rb +19 -0
- data/lib/xls_function/evaluators/functions/rept.rb +25 -0
- data/lib/xls_function/evaluators/functions/right.rb +16 -0
- data/lib/xls_function/evaluators/functions/round.rb +16 -0
- data/lib/xls_function/evaluators/functions/round_down.rb +16 -0
- data/lib/xls_function/evaluators/functions/round_up.rb +16 -0
- data/lib/xls_function/evaluators/functions/second.rb +15 -0
- data/lib/xls_function/evaluators/functions/sqrt.rb +17 -0
- data/lib/xls_function/evaluators/functions/substitute.rb +44 -0
- data/lib/xls_function/evaluators/functions/text.rb +17 -0
- data/lib/xls_function/evaluators/functions/time.rb +54 -0
- data/lib/xls_function/evaluators/functions/timevalue.rb +17 -0
- data/lib/xls_function/evaluators/functions/today.rb +19 -0
- data/lib/xls_function/evaluators/functions/trim.rb +16 -0
- data/lib/xls_function/evaluators/functions/trunc.rb +16 -0
- data/lib/xls_function/evaluators/functions/unichar.rb +19 -0
- data/lib/xls_function/evaluators/functions/unicode.rb +18 -0
- data/lib/xls_function/evaluators/functions/upper.rb +15 -0
- data/lib/xls_function/evaluators/functions/value.rb +26 -0
- data/lib/xls_function/evaluators/functions/year.rb +15 -0
- data/lib/xls_function/evaluators/number_evaluator.rb +18 -0
- data/lib/xls_function/evaluators/string_evaluator.rb +18 -0
- data/lib/xls_function/evaluators/true_evaluator.rb +19 -0
- data/lib/xls_function/evaluators/variant_evaluator.rb +24 -0
- data/lib/xls_function/extensions/array_extension.rb +22 -0
- data/lib/xls_function/extensions/big_decimal_extension.rb +15 -0
- data/lib/xls_function/extensions/date_extension.rb +11 -0
- data/lib/xls_function/extensions/hash_extension.rb +24 -0
- data/lib/xls_function/extensions/time_extension.rb +11 -0
- data/lib/xls_function/format_string/evaluators/elapsed_time_evaluator.rb +42 -0
- data/lib/xls_function/format_string/evaluators/number_evaluator.rb +243 -0
- data/lib/xls_function/format_string/evaluators/time_evaluator.rb +103 -0
- data/lib/xls_function/format_string/parse_rules/dates.rb +53 -0
- data/lib/xls_function/format_string/parse_rules/numbers.rb +31 -0
- data/lib/xls_function/format_string/parse_rules/texts.rb +25 -0
- data/lib/xls_function/format_string/parse_rules/times.rb +55 -0
- data/lib/xls_function/format_string/parser.rb +27 -0
- data/lib/xls_function/format_string/transform.rb +87 -0
- data/lib/xls_function/format_string/transform_rules/dates.rb +98 -0
- data/lib/xls_function/format_string/transform_rules/numbers.rb +59 -0
- data/lib/xls_function/format_string/transform_rules/texts.rb +25 -0
- data/lib/xls_function/format_string/transform_rules/times.rb +97 -0
- data/lib/xls_function/format_string.rb +24 -0
- data/lib/xls_function/i18n.rb +4 -0
- data/lib/xls_function/locales/en.yml +65 -0
- data/lib/xls_function/locales/ja.yml +65 -0
- data/lib/xls_function/parse_rules/binary_operation.rb +28 -0
- data/lib/xls_function/parse_rules/common.rb +34 -0
- data/lib/xls_function/parser.rb +12 -0
- data/lib/xls_function/transform.rb +58 -0
- data/lib/xls_function/transform_rules/binary_operation_transform.rb +16 -0
- data/lib/xls_function/transform_rules/boolean_transform.rb +12 -0
- data/lib/xls_function/transform_rules/function_call_transform.rb +16 -0
- data/lib/xls_function/transform_rules/number_transform.rb +11 -0
- data/lib/xls_function/transform_rules/string_transform.rb +17 -0
- data/lib/xls_function/transform_rules/variant_transform.rb +13 -0
- data/lib/xls_function/user_defined_function_factory.rb +59 -0
- data/lib/xls_function/version.rb +3 -0
- data/lib/xls_function.rb +82 -0
- data/xls_function.gemspec +30 -0
- metadata +221 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
module XlsFunction
|
|
2
|
+
module Converters
|
|
3
|
+
module TimeConverter
|
|
4
|
+
class << self
|
|
5
|
+
def try_convert(time_value)
|
|
6
|
+
[true, convert(time_value)]
|
|
7
|
+
rescue ArgumentError
|
|
8
|
+
[false, invalid_time(time_value)]
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def invalid_time(time_value)
|
|
12
|
+
I18n.t('xls_function.errors.cannot_convert_to_time', source: time_value)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def convert(input)
|
|
16
|
+
case input
|
|
17
|
+
when Time
|
|
18
|
+
input
|
|
19
|
+
when Date
|
|
20
|
+
input.to_time
|
|
21
|
+
when BigDecimal
|
|
22
|
+
NumberConverter.decimal_to_time(input)
|
|
23
|
+
when Numeric
|
|
24
|
+
Time.at(input)
|
|
25
|
+
else
|
|
26
|
+
parse(input)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# @param [object] input
|
|
31
|
+
# @param [Hash] cache context
|
|
32
|
+
def convert_with_cache(input, cache)
|
|
33
|
+
cache[:cache_convert_time] ||= {}
|
|
34
|
+
cache[:cache_convert_time][input] ||= convert(input)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
FORMAT_MAP = {
|
|
38
|
+
'yyyy' => '%Y',
|
|
39
|
+
'yy' => '%y',
|
|
40
|
+
'm' => '%m',
|
|
41
|
+
'mm' => '%m',
|
|
42
|
+
'mmm' => '%b',
|
|
43
|
+
'mmmm' => '%B',
|
|
44
|
+
'mmmmm' => '%B',
|
|
45
|
+
'd' => '%d',
|
|
46
|
+
'dd' => '%d',
|
|
47
|
+
'aaa' => '%w',
|
|
48
|
+
'aaaa' => '%w',
|
|
49
|
+
'ddd' => '%a',
|
|
50
|
+
'dddd' => '%A',
|
|
51
|
+
'e' => '%Jg',
|
|
52
|
+
'ee' => '%Jg',
|
|
53
|
+
'r' => '%Jg',
|
|
54
|
+
'g' => '%Je',
|
|
55
|
+
'gg' => '%Je',
|
|
56
|
+
'ggg' => '%Je',
|
|
57
|
+
'hh' => '%H',
|
|
58
|
+
'h' => '%H',
|
|
59
|
+
'M' => '%M',
|
|
60
|
+
'MM' => '%M',
|
|
61
|
+
's' => '%S',
|
|
62
|
+
'ss' => '%S',
|
|
63
|
+
'.0' => '%1N',
|
|
64
|
+
'.00' => '%2N',
|
|
65
|
+
'.000' => '%3N'
|
|
66
|
+
}.freeze
|
|
67
|
+
|
|
68
|
+
def convert_format(format, ampm_mode: false)
|
|
69
|
+
mapped = FORMAT_MAP[format]
|
|
70
|
+
mapped = '%I' if mapped == '%H' && ampm_mode
|
|
71
|
+
raise NotImplementedError, "#{format} is not supported" unless mapped
|
|
72
|
+
|
|
73
|
+
mapped
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
WEEKDAYS_JP = %w[日 月 火 水 木 金 土].freeze
|
|
77
|
+
|
|
78
|
+
def convert_weekday_jp(week_index)
|
|
79
|
+
WEEKDAYS_JP[week_index]
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
WAREKI_ALPHABETS = {
|
|
83
|
+
'明治' => 'M',
|
|
84
|
+
'大正' => 'T',
|
|
85
|
+
'昭和' => 'S',
|
|
86
|
+
'平成' => 'H',
|
|
87
|
+
'令和' => 'R'
|
|
88
|
+
}.freeze
|
|
89
|
+
|
|
90
|
+
def convert_wareki_to_alphabet(gengo)
|
|
91
|
+
WAREKI_ALPHABETS[gengo] || ''
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Accepts elapsed time expression that Time.parse cannot parse.
|
|
95
|
+
def parse(date, now = Time.now, &block)
|
|
96
|
+
Time.parse(date, now, &block)
|
|
97
|
+
rescue ArgumentError
|
|
98
|
+
result = fix_elapsed_time(date, now)
|
|
99
|
+
raise if result == :throw
|
|
100
|
+
|
|
101
|
+
Time.parse(result, now, &block)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def fix_elapsed_time(time_value, now)
|
|
105
|
+
h = Date._parse(time_value)
|
|
106
|
+
return :throw if h.key?(:yday) # give up when yday exists.
|
|
107
|
+
return :throw if !h.key?(:sec) && !h.key?(:min) && !h.key?(:hour) # give up when not elapsed time expression.
|
|
108
|
+
|
|
109
|
+
time_str, elapsed_day = fixed_time_str(h)
|
|
110
|
+
date_str = fixed_date_str(h, now, elapsed_day)
|
|
111
|
+
"#{date_str} #{time_str}"
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def fixed_time_str(parsed)
|
|
115
|
+
sec = parsed.fetch(:sec, 0)
|
|
116
|
+
min = parsed.fetch(:min, 0)
|
|
117
|
+
hour = parsed.fetch(:hour, 0)
|
|
118
|
+
|
|
119
|
+
adjusted = adjust_elapsed_time(hour, min, sec)
|
|
120
|
+
|
|
121
|
+
[
|
|
122
|
+
"#{adjusted[:hour]}:#{adjusted[:minute]}:#{adjusted[:second]}#{sec_fraction_str(parsed)}#{zone_str(parsed)}",
|
|
123
|
+
adjusted[:day]
|
|
124
|
+
]
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def fixed_date_str(parsed, now, elapsed_day)
|
|
128
|
+
year = parsed.fetch(:year, now.year)
|
|
129
|
+
month = parsed.fetch(:mon, now.month)
|
|
130
|
+
day = parsed.fetch(:mday, now.day)
|
|
131
|
+
date = Date.new(year, month, day) + elapsed_day
|
|
132
|
+
|
|
133
|
+
"#{date.year}-#{date.month}-#{date.day}"
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def sec_fraction_str(parsed)
|
|
137
|
+
return '' unless parsed.key?(:sec_fraction)
|
|
138
|
+
|
|
139
|
+
".#{(parsed[:sec_fraction] * 1000).to_i.to_s.rjust(3, '0')}"
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def zone_str(parsed)
|
|
143
|
+
return '' unless parsed.key?(:zone)
|
|
144
|
+
|
|
145
|
+
" #{parsed[:zone]}"
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def adjust_elapsed_time(hour, minute, second)
|
|
149
|
+
elapsed_minute, s = second.divmod(60)
|
|
150
|
+
elapsed_hour, m = (minute + elapsed_minute).divmod(60)
|
|
151
|
+
elapsed_day, h = (hour + elapsed_hour).divmod(24)
|
|
152
|
+
|
|
153
|
+
{ day: elapsed_day, hour: h, minute: m, second: s }
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
module XlsFunction
|
|
2
|
+
module Converters
|
|
3
|
+
module TimeSerialConverter
|
|
4
|
+
H_UNIT = (BigDecimal('1') / 24)
|
|
5
|
+
M_UNIT = (H_UNIT / 60)
|
|
6
|
+
S_UNIT = (M_UNIT / 60)
|
|
7
|
+
MS_UNIT = (S_UNIT / 1000)
|
|
8
|
+
|
|
9
|
+
ORIGIN_TIME = Time.new(1899, 12, 31, 0, 0, 0).freeze
|
|
10
|
+
|
|
11
|
+
class << self
|
|
12
|
+
def time_to_serial(time, except_date: false)
|
|
13
|
+
(except_date ? 0 : DateSerialConverter.date_to_serial(time.to_date)) +
|
|
14
|
+
(
|
|
15
|
+
hour_value_of(time) +
|
|
16
|
+
minute_value_of(time) +
|
|
17
|
+
second_value_of(time) +
|
|
18
|
+
millisecond_value_of(time)
|
|
19
|
+
).round(15)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# @param [BigDecimal] time_serial
|
|
23
|
+
def serial_to_time(time_serial, now = ORIGIN_TIME)
|
|
24
|
+
Time.parse(TimeSerial.new(time_serial).to_s, now.to_time)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def hour_value_of(time)
|
|
30
|
+
time.hour * H_UNIT
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def minute_value_of(time)
|
|
34
|
+
time.min * M_UNIT
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def second_value_of(time)
|
|
38
|
+
time.sec * S_UNIT
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def millisecond_value_of(time)
|
|
42
|
+
time.usec.to_s.rjust(6, '0')[0, 3].to_i * MS_UNIT
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
class TimeSerial
|
|
47
|
+
attr_reader :serial
|
|
48
|
+
|
|
49
|
+
def initialize(serial)
|
|
50
|
+
@serial = serial.frac
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def hour
|
|
54
|
+
@hour ||= (serial / H_UNIT).round(15).to_i
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def minute
|
|
58
|
+
@minute ||= (without_hour / M_UNIT).round(15).to_i
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def second
|
|
62
|
+
@second ||= (without_hour_and_minute / S_UNIT).round(15).to_i
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def millisecond
|
|
66
|
+
@millisecond ||= (without_hour_and_minute_and_second / MS_UNIT).round(15).to_i
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def to_s
|
|
70
|
+
"#{pad_zero(&:hour)}:#{pad_zero(&:minute)}:#{pad_zero(&:second)}#{millisecond_suffix}"
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
private
|
|
74
|
+
|
|
75
|
+
def without_hour
|
|
76
|
+
serial - (H_UNIT * hour)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def without_hour_and_minute
|
|
80
|
+
without_hour - (M_UNIT * minute)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def without_hour_and_minute_and_second
|
|
84
|
+
without_hour_and_minute - (S_UNIT * second)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def pad_zero(count = 2, &block)
|
|
88
|
+
block.call(self).to_s.rjust(count, '0')
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def millisecond_suffix
|
|
92
|
+
millisecond.zero? ? '' : ".#{pad_zero(3, &:millisecond)}"
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
module XlsFunction
|
|
2
|
+
# Default Error
|
|
3
|
+
class EvaluationError < StandardError
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
class ErrorValue < String
|
|
7
|
+
attr_reader :error_info
|
|
8
|
+
|
|
9
|
+
def initialize(string = '', error_info = '')
|
|
10
|
+
@error_info = error_info
|
|
11
|
+
super(string)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def to_s
|
|
15
|
+
super + error_info
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def error?
|
|
19
|
+
true
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class << self
|
|
23
|
+
# @return #VALUE!
|
|
24
|
+
def value!(error_info = '')
|
|
25
|
+
new(ERROR_VALUE, error_info)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# @return #NUM!
|
|
29
|
+
def num!(error_info = '')
|
|
30
|
+
new(ERROR_NUM, error_info)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# @return #N/A
|
|
34
|
+
def na(error_info = '')
|
|
35
|
+
new(ERROR_NA, error_info)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
ERROR_VALUE = '#VALUE!'.freeze
|
|
41
|
+
ERROR_NUM = '#NUM!'.freeze
|
|
42
|
+
ERROR_NA = '#N/A'.freeze
|
|
43
|
+
|
|
44
|
+
ERRORS = [
|
|
45
|
+
ERROR_VALUE,
|
|
46
|
+
ERROR_NUM,
|
|
47
|
+
ERROR_NA
|
|
48
|
+
].freeze
|
|
49
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module XlsFunction
|
|
2
|
+
module Evaluators
|
|
3
|
+
module ArgumentsDefinable
|
|
4
|
+
def self.included(klass)
|
|
5
|
+
klass.class_eval do
|
|
6
|
+
class << self
|
|
7
|
+
def arg_definitions
|
|
8
|
+
@arg_definitions || []
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def inherited(subclass)
|
|
12
|
+
super
|
|
13
|
+
subclass.instance_variable_set(:@arg_definitions, arg_definitions.dup)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
klass.extend(ClassMethods)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def defined_args
|
|
24
|
+
self.class.arg_definitions.map do |name, _default, _type|
|
|
25
|
+
instance_variable_get("@#{name}")
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
module ClassMethods
|
|
30
|
+
def define_arg(name, default: nil, type: nil)
|
|
31
|
+
@arg_definitions ||= []
|
|
32
|
+
|
|
33
|
+
@arg_definitions << [name, default, type]
|
|
34
|
+
attr_reader(name)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module XlsFunction
|
|
2
|
+
module Evaluators
|
|
3
|
+
class BinaryOperationEvaluator < FunctionEvaluator
|
|
4
|
+
define_arg :left
|
|
5
|
+
define_arg :right
|
|
6
|
+
|
|
7
|
+
class << self
|
|
8
|
+
alias operator_as register_dictionary
|
|
9
|
+
|
|
10
|
+
def create(left, right, context)
|
|
11
|
+
new(context.merge(arglist: [left, right]))
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
Dir[File.expand_path('binary_operations', __dir__) << '/*.rb'].sort.each do |file|
|
|
19
|
+
require file
|
|
20
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module XlsFunction
|
|
2
|
+
module Evaluators
|
|
3
|
+
module ClassDictionary
|
|
4
|
+
def self.included(klass)
|
|
5
|
+
klass.class_eval do
|
|
6
|
+
class << self
|
|
7
|
+
def register_dictionary(key, value = self)
|
|
8
|
+
@register_key = key
|
|
9
|
+
dictionary[key] = value
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def from_dictionary(key)
|
|
13
|
+
dictionary[key]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def description(text)
|
|
17
|
+
@description = text
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def dictionary
|
|
23
|
+
XlsFunction.class_dictionary
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module XlsFunction
|
|
2
|
+
module Evaluators
|
|
3
|
+
module ErrorDetector
|
|
4
|
+
def self.included(klass)
|
|
5
|
+
klass.class_eval do
|
|
6
|
+
attr_reader :error_value
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def rescue_with(error_class)
|
|
11
|
+
yield
|
|
12
|
+
rescue error_class => e
|
|
13
|
+
if XlsFunction.verbose
|
|
14
|
+
XlsFunction.logger.write(e.inspect)
|
|
15
|
+
XlsFunction.logger.write(e.backtrace)
|
|
16
|
+
end
|
|
17
|
+
::XlsFunction::ErrorValue.value!(class_info(e.message))
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def class_info(message = '')
|
|
21
|
+
"#{class_name.upcase}:#{message}"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def class_name
|
|
25
|
+
self.class.name&.split('::')&.last || 'Class'
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
module XlsFunction
|
|
2
|
+
module Evaluators
|
|
3
|
+
module Evaluable
|
|
4
|
+
using ::XlsFunction::Extensions::HashExtension
|
|
5
|
+
|
|
6
|
+
def self.included(klass)
|
|
7
|
+
klass.class_eval do
|
|
8
|
+
attr_reader :context
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def evaluate(runtime_context = nil)
|
|
13
|
+
merge_context(runtime_context) do
|
|
14
|
+
before_eval
|
|
15
|
+
eval
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def merge_context(runtime_context = nil)
|
|
22
|
+
origin_context = context
|
|
23
|
+
@context = runtime_context.deep_merge(context) if runtime_context && runtime_context != context
|
|
24
|
+
|
|
25
|
+
yield
|
|
26
|
+
ensure
|
|
27
|
+
@context = origin_context
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def before_eval; end
|
|
31
|
+
|
|
32
|
+
def eval
|
|
33
|
+
raise NotImplementedError, "You must implement #{self.class}##{__method__}"
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module XlsFunction
|
|
2
|
+
module Evaluators
|
|
3
|
+
class FalseEvaluator
|
|
4
|
+
include Evaluable
|
|
5
|
+
|
|
6
|
+
# unused, but hold for future logging...
|
|
7
|
+
attr_reader :source
|
|
8
|
+
|
|
9
|
+
def initialize(source, context)
|
|
10
|
+
@source = source
|
|
11
|
+
@context = context
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def eval
|
|
15
|
+
false
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|