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.
Files changed (136) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/publish_gem.yml +25 -0
  3. data/.github/workflows/rspec.yml +30 -0
  4. data/.gitignore +13 -0
  5. data/.rspec +2 -0
  6. data/.rubocop.yml +41 -0
  7. data/CODE_OF_CONDUCT.md +74 -0
  8. data/Gemfile +8 -0
  9. data/LICENSE +201 -0
  10. data/README.md +48 -0
  11. data/Rakefile +6 -0
  12. data/bin/console +14 -0
  13. data/bin/setup +8 -0
  14. data/lib/xls_function/class_dictionary.rb +7 -0
  15. data/lib/xls_function/converter.rb +24 -0
  16. data/lib/xls_function/converters/date_converter.rb +30 -0
  17. data/lib/xls_function/converters/date_serial_converter.rb +17 -0
  18. data/lib/xls_function/converters/number_converter.rb +28 -0
  19. data/lib/xls_function/converters/time_converter.rb +158 -0
  20. data/lib/xls_function/converters/time_serial_converter.rb +97 -0
  21. data/lib/xls_function/default_logger.rb +7 -0
  22. data/lib/xls_function/error.rb +49 -0
  23. data/lib/xls_function/evaluators/arguments_definable.rb +39 -0
  24. data/lib/xls_function/evaluators/binary_operation_evaluator.rb +20 -0
  25. data/lib/xls_function/evaluators/binary_operations/add.rb +13 -0
  26. data/lib/xls_function/evaluators/binary_operations/concat.rb +13 -0
  27. data/lib/xls_function/evaluators/binary_operations/divide.rb +13 -0
  28. data/lib/xls_function/evaluators/binary_operations/equal.rb +13 -0
  29. data/lib/xls_function/evaluators/binary_operations/greater_than.rb +13 -0
  30. data/lib/xls_function/evaluators/binary_operations/greater_than_or_equal_to.rb +13 -0
  31. data/lib/xls_function/evaluators/binary_operations/less_than.rb +13 -0
  32. data/lib/xls_function/evaluators/binary_operations/less_than_or_equal_to.rb +13 -0
  33. data/lib/xls_function/evaluators/binary_operations/multiple.rb +13 -0
  34. data/lib/xls_function/evaluators/binary_operations/not_equal.rb +13 -0
  35. data/lib/xls_function/evaluators/binary_operations/power.rb +13 -0
  36. data/lib/xls_function/evaluators/binary_operations/subtract.rb +13 -0
  37. data/lib/xls_function/evaluators/class_dictionary.rb +30 -0
  38. data/lib/xls_function/evaluators/error_detector.rb +29 -0
  39. data/lib/xls_function/evaluators/evaluable.rb +37 -0
  40. data/lib/xls_function/evaluators/false_evaluator.rb +19 -0
  41. data/lib/xls_function/evaluators/function_evaluator.rb +133 -0
  42. data/lib/xls_function/evaluators/functions/and.rb +21 -0
  43. data/lib/xls_function/evaluators/functions/asc.rb +17 -0
  44. data/lib/xls_function/evaluators/functions/char.rb +19 -0
  45. data/lib/xls_function/evaluators/functions/clean.rb +22 -0
  46. data/lib/xls_function/evaluators/functions/code.rb +18 -0
  47. data/lib/xls_function/evaluators/functions/concat.rb +27 -0
  48. data/lib/xls_function/evaluators/functions/date.rb +57 -0
  49. data/lib/xls_function/evaluators/functions/date_value.rb +17 -0
  50. data/lib/xls_function/evaluators/functions/day.rb +15 -0
  51. data/lib/xls_function/evaluators/functions/dbcs.rb +18 -0
  52. data/lib/xls_function/evaluators/functions/e_date.rb +19 -0
  53. data/lib/xls_function/evaluators/functions/e_o_month.rb +20 -0
  54. data/lib/xls_function/evaluators/functions/exact.rb +16 -0
  55. data/lib/xls_function/evaluators/functions/find.rb +28 -0
  56. data/lib/xls_function/evaluators/functions/fixed.rb +46 -0
  57. data/lib/xls_function/evaluators/functions/hour.rb +15 -0
  58. data/lib/xls_function/evaluators/functions/if.rb +24 -0
  59. data/lib/xls_function/evaluators/functions/ifs.rb +20 -0
  60. data/lib/xls_function/evaluators/functions/int.rb +15 -0
  61. data/lib/xls_function/evaluators/functions/iserror.rb +20 -0
  62. data/lib/xls_function/evaluators/functions/isnumber.rb +15 -0
  63. data/lib/xls_function/evaluators/functions/lambda.rb +57 -0
  64. data/lib/xls_function/evaluators/functions/left.rb +16 -0
  65. data/lib/xls_function/evaluators/functions/len.rb +15 -0
  66. data/lib/xls_function/evaluators/functions/let.rb +24 -0
  67. data/lib/xls_function/evaluators/functions/lower.rb +15 -0
  68. data/lib/xls_function/evaluators/functions/mid.rb +23 -0
  69. data/lib/xls_function/evaluators/functions/minute.rb +15 -0
  70. data/lib/xls_function/evaluators/functions/month.rb +15 -0
  71. data/lib/xls_function/evaluators/functions/not.rb +15 -0
  72. data/lib/xls_function/evaluators/functions/now.rb +19 -0
  73. data/lib/xls_function/evaluators/functions/or.rb +21 -0
  74. data/lib/xls_function/evaluators/functions/power.rb +16 -0
  75. data/lib/xls_function/evaluators/functions/proper.rb +17 -0
  76. data/lib/xls_function/evaluators/functions/replace.rb +19 -0
  77. data/lib/xls_function/evaluators/functions/rept.rb +25 -0
  78. data/lib/xls_function/evaluators/functions/right.rb +16 -0
  79. data/lib/xls_function/evaluators/functions/round.rb +16 -0
  80. data/lib/xls_function/evaluators/functions/round_down.rb +16 -0
  81. data/lib/xls_function/evaluators/functions/round_up.rb +16 -0
  82. data/lib/xls_function/evaluators/functions/second.rb +15 -0
  83. data/lib/xls_function/evaluators/functions/sqrt.rb +17 -0
  84. data/lib/xls_function/evaluators/functions/substitute.rb +44 -0
  85. data/lib/xls_function/evaluators/functions/text.rb +17 -0
  86. data/lib/xls_function/evaluators/functions/time.rb +54 -0
  87. data/lib/xls_function/evaluators/functions/timevalue.rb +17 -0
  88. data/lib/xls_function/evaluators/functions/today.rb +19 -0
  89. data/lib/xls_function/evaluators/functions/trim.rb +16 -0
  90. data/lib/xls_function/evaluators/functions/trunc.rb +16 -0
  91. data/lib/xls_function/evaluators/functions/unichar.rb +19 -0
  92. data/lib/xls_function/evaluators/functions/unicode.rb +18 -0
  93. data/lib/xls_function/evaluators/functions/upper.rb +15 -0
  94. data/lib/xls_function/evaluators/functions/value.rb +26 -0
  95. data/lib/xls_function/evaluators/functions/year.rb +15 -0
  96. data/lib/xls_function/evaluators/number_evaluator.rb +18 -0
  97. data/lib/xls_function/evaluators/string_evaluator.rb +18 -0
  98. data/lib/xls_function/evaluators/true_evaluator.rb +19 -0
  99. data/lib/xls_function/evaluators/variant_evaluator.rb +24 -0
  100. data/lib/xls_function/extensions/array_extension.rb +22 -0
  101. data/lib/xls_function/extensions/big_decimal_extension.rb +15 -0
  102. data/lib/xls_function/extensions/date_extension.rb +11 -0
  103. data/lib/xls_function/extensions/hash_extension.rb +24 -0
  104. data/lib/xls_function/extensions/time_extension.rb +11 -0
  105. data/lib/xls_function/format_string/evaluators/elapsed_time_evaluator.rb +42 -0
  106. data/lib/xls_function/format_string/evaluators/number_evaluator.rb +243 -0
  107. data/lib/xls_function/format_string/evaluators/time_evaluator.rb +103 -0
  108. data/lib/xls_function/format_string/parse_rules/dates.rb +53 -0
  109. data/lib/xls_function/format_string/parse_rules/numbers.rb +31 -0
  110. data/lib/xls_function/format_string/parse_rules/texts.rb +25 -0
  111. data/lib/xls_function/format_string/parse_rules/times.rb +55 -0
  112. data/lib/xls_function/format_string/parser.rb +27 -0
  113. data/lib/xls_function/format_string/transform.rb +87 -0
  114. data/lib/xls_function/format_string/transform_rules/dates.rb +98 -0
  115. data/lib/xls_function/format_string/transform_rules/numbers.rb +59 -0
  116. data/lib/xls_function/format_string/transform_rules/texts.rb +25 -0
  117. data/lib/xls_function/format_string/transform_rules/times.rb +97 -0
  118. data/lib/xls_function/format_string.rb +24 -0
  119. data/lib/xls_function/i18n.rb +4 -0
  120. data/lib/xls_function/locales/en.yml +65 -0
  121. data/lib/xls_function/locales/ja.yml +65 -0
  122. data/lib/xls_function/parse_rules/binary_operation.rb +28 -0
  123. data/lib/xls_function/parse_rules/common.rb +34 -0
  124. data/lib/xls_function/parser.rb +12 -0
  125. data/lib/xls_function/transform.rb +58 -0
  126. data/lib/xls_function/transform_rules/binary_operation_transform.rb +16 -0
  127. data/lib/xls_function/transform_rules/boolean_transform.rb +12 -0
  128. data/lib/xls_function/transform_rules/function_call_transform.rb +16 -0
  129. data/lib/xls_function/transform_rules/number_transform.rb +11 -0
  130. data/lib/xls_function/transform_rules/string_transform.rb +17 -0
  131. data/lib/xls_function/transform_rules/variant_transform.rb +13 -0
  132. data/lib/xls_function/user_defined_function_factory.rb +59 -0
  133. data/lib/xls_function/version.rb +3 -0
  134. data/lib/xls_function.rb +82 -0
  135. data/xls_function.gemspec +30 -0
  136. metadata +221 -0
@@ -0,0 +1,18 @@
1
+ module XlsFunction
2
+ module Evaluators
3
+ class NumberEvaluator
4
+ include Evaluable
5
+
6
+ attr_reader :source
7
+
8
+ def initialize(source, context)
9
+ @source = source
10
+ @context = context
11
+ end
12
+
13
+ def eval
14
+ source.to_s.to_d
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ module XlsFunction
2
+ module Evaluators
3
+ class StringEvaluator
4
+ include Evaluable
5
+
6
+ attr_reader :source
7
+
8
+ def initialize(source, context)
9
+ @source = source
10
+ @context = context
11
+ end
12
+
13
+ def eval
14
+ source.to_s
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,19 @@
1
+ module XlsFunction
2
+ module Evaluators
3
+ class TrueEvaluator
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
+ true
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,24 @@
1
+ module XlsFunction
2
+ module Evaluators
3
+ class VariantEvaluator
4
+ include Evaluable
5
+
6
+ attr_reader :name
7
+
8
+ def initialize(name, context)
9
+ @name = name.to_s
10
+ @context = context
11
+ end
12
+
13
+ def variant_context
14
+ context[:variants]
15
+ end
16
+
17
+ def eval
18
+ raise ::XlsFunction::Transform::VariantUndefinedError, "name: `#{name}` is undefined" unless variant_context.key?(name)
19
+
20
+ variant_context[name]
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,22 @@
1
+ module XlsFunction
2
+ module Extensions
3
+ module ArrayExtension
4
+ class << self
5
+ def max_depth(array, depth = 1)
6
+ array.inject(depth) do |current_depth, item|
7
+ next current_depth unless item.is_a?(Array)
8
+
9
+ child_depth = max_depth(item, depth + 1)
10
+ [current_depth, child_depth].max
11
+ end
12
+ end
13
+ end
14
+
15
+ refine Array do
16
+ def max_depth(depth = 1)
17
+ ArrayExtension.max_depth(self, depth)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,15 @@
1
+ module XlsFunction
2
+ module Extensions
3
+ module BigDecimalExtension
4
+ refine BigDecimal do
5
+ def to_date
6
+ XlsFunction::Converters::NumberConverter.decimal_to_date(self)
7
+ end
8
+
9
+ def to_time(now: ::XlsFunction::Converters::DateSerialConverter::ORIGIN)
10
+ XlsFunction::Converters::NumberConverter.decimal_to_time(self, now: now)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,11 @@
1
+ module XlsFunction
2
+ module Extensions
3
+ module DateExtension
4
+ refine Date do
5
+ def to_serial
6
+ ::XlsFunction::Converters::DateSerialConverter.date_to_serial(self)
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,24 @@
1
+ module XlsFunction
2
+ module Extensions
3
+ module HashExtension
4
+ class << self
5
+ # @param [Hash] source
6
+ # @param [Hash] other
7
+ def deep_merge(source, other, &block)
8
+ source.merge(other) do |key, oldval, newval|
9
+ next deep_merge(oldval, newval, &block) if oldval.is_a?(Hash) && newval.is_a?(Hash)
10
+ next block.call(key, oldval, newval) if block_given?
11
+
12
+ newval
13
+ end
14
+ end
15
+ end
16
+
17
+ refine Hash do
18
+ def deep_merge(other, &block)
19
+ HashExtension.deep_merge(self, other, &block)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,11 @@
1
+ module XlsFunction
2
+ module Extensions
3
+ module TimeExtension
4
+ refine Time do
5
+ def to_serial(except_date: false)
6
+ ::XlsFunction::Converters::TimeSerialConverter.time_to_serial(self, except_date: except_date)
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,42 @@
1
+ module XlsFunction
2
+ module FormatString
3
+ module Evaluators
4
+ class ElapsedTimeEvaluator
5
+ attr_reader :cache, :half, :parse_proc
6
+
7
+ using ::XlsFunction::Extensions::DateExtension
8
+ using ::XlsFunction::Extensions::TimeExtension
9
+
10
+ def initialize(cache, half: true, &parse_proc)
11
+ @cache = cache
12
+ @half = half
13
+ @parse_proc = parse_proc
14
+ end
15
+
16
+ def call(input)
17
+ time = ::XlsFunction::Converters::TimeConverter.convert_with_cache(input, cache)
18
+
19
+ result = parse_proc.call(self, time).to_i.to_s
20
+ half ? result : result.rjust(2, '0')
21
+ end
22
+
23
+ def hour(time)
24
+ elapsed_days = days_from_origin(time)
25
+ elapsed_days * 24 + time.hour
26
+ end
27
+
28
+ def minute(time)
29
+ hour(time) * 60 + time.min
30
+ end
31
+
32
+ def second(time)
33
+ minute(time) * 60 + time.sec
34
+ end
35
+
36
+ def days_from_origin(time)
37
+ time.to_date.to_serial
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,243 @@
1
+ module XlsFunction
2
+ module FormatString
3
+ module Evaluators
4
+ # @todo refactoring...
5
+ # rubocop:disable Metrics/AbcSize
6
+ class NumberEvaluator
7
+ attr_reader :parts
8
+
9
+ def initialize(parts)
10
+ @parts = parts
11
+ end
12
+
13
+ def call(input)
14
+ extract_parts!(input)
15
+ value = adjust_value(input)
16
+ format_value(value)
17
+ rescue ArgumentError
18
+ input
19
+ end
20
+
21
+ def interger_parts
22
+ @interger_parts ||= PartsList.new
23
+ end
24
+
25
+ def decimal_parts
26
+ @decimal_parts ||= PartsList.new
27
+ end
28
+
29
+ def parts_length
30
+ @parts_length ||= parts.length
31
+ end
32
+
33
+ def extract_parts!(input)
34
+ parts_target = interger_parts
35
+
36
+ comma_index = []
37
+ @parts.each_with_index do |expr, i|
38
+ if expr.respond_to?(:digits?)
39
+ parts_target.add_parts(expr, i, true)
40
+ next
41
+ end
42
+
43
+ # evaluate except number placeholder
44
+ str = expr.call(input)
45
+ # switch stack when decimal point comes
46
+ parts_target = decimal_parts if str == '.'
47
+ # count comma
48
+ if str == ','
49
+ comma_index << i
50
+ # ignore
51
+ next
52
+ end
53
+ # last % is parcentage flag
54
+ @has_parcentage = true if str == '%' && (i == parts_length - 1)
55
+
56
+ parts_target.add_parts(str, i, false)
57
+ end.compact
58
+ check_comma(comma_index)
59
+ end
60
+
61
+ # ↓ check comma
62
+ # When % exists, check 2 or 1 chars before from %.
63
+ # If not, check last 2 or 1 chars.
64
+ def comma_index_pattern_thousands
65
+ offset = @has_parcentage ? 2 : 1
66
+ [parts_length - offset]
67
+ end
68
+
69
+ def comma_index_pattern_millions
70
+ offset = @has_parcentage ? 2 : 1
71
+ [parts_length - offset - 1, parts_length - offset]
72
+ end
73
+
74
+ def check_comma(comma_index)
75
+ return if comma_index.empty?
76
+
77
+ if (comma_index_pattern_millions - comma_index).empty?
78
+ @unit = :millions
79
+ @format = true unless (comma_index - comma_index_pattern_millions).empty?
80
+ elsif (comma_index_pattern_thousands - comma_index).empty?
81
+ @unit = :thousands
82
+ @format = true unless (comma_index - comma_index_pattern_thousands).empty?
83
+ else
84
+ @format = true
85
+ end
86
+ end
87
+
88
+ def adjust_value(value)
89
+ decimal_value = value.to_s.to_d
90
+
91
+ case @unit
92
+ when :millions
93
+ decimal_value /= '1000000'.to_d
94
+ when :thousands
95
+ decimal_value /= '1000'.to_d
96
+ end
97
+
98
+ decimal_value *= '100'.to_d if @has_parcentage
99
+
100
+ decimal_value
101
+ end
102
+
103
+ def format_value(value)
104
+ value_stack = ValueStack.new(value, decimal_parts.size, @format)
105
+
106
+ # format decimals
107
+ decimal_chars =
108
+ create_dec_pair(decimal_parts, value_stack).reverse
109
+ .then { |value_pairs| eval_pairs(value_pairs) }
110
+ .reverse
111
+ .join
112
+
113
+ # format integers
114
+ integer_chars =
115
+ create_int_pair(interger_parts, value_stack).reverse
116
+ .then { |value_pairs| eval_pairs(value_pairs, int_remain: value_stack.int_remain?) }
117
+ .join
118
+
119
+ "#{!value_stack.empty? ? value_stack.to_s : ''}#{integer_chars}#{decimal_chars}"
120
+ end
121
+
122
+ def create_dec_pair(parts_list, value_stack)
123
+ parts_list.parts_with_index.map do |part, index|
124
+ if parts_list.num?(index)
125
+ [value_stack.shift_dec, part]
126
+ else
127
+ [part, nil] # means already evaluated
128
+ end
129
+ end
130
+ end
131
+
132
+ def create_int_pair(parts_list, value_stack)
133
+ parts_list.parts_with_index.reverse.map do |part, index|
134
+ if parts_list.num?(index)
135
+ [value_stack.pop_int, part]
136
+ else
137
+ [part, nil]
138
+ end
139
+ end
140
+ end
141
+
142
+ def eval_pairs(value_eval_pairs, int_remain: false)
143
+ require_zero = false
144
+ value_eval_pairs.map do |value, eval|
145
+ next value unless eval
146
+
147
+ evaluated_value = eval.call(value)
148
+ if evaluated_value.strip != '' && evaluated_value != '0'
149
+ require_zero = true
150
+ next evaluated_value
151
+ end
152
+
153
+ # `#` returns empty if zero but must return zero if higher place exists.
154
+ next '0' if evaluated_value == '' && (require_zero || int_remain)
155
+
156
+ evaluated_value
157
+ end
158
+ end
159
+ end
160
+ # rubocop:enable Metrics/AbcSize
161
+
162
+ class PartsList
163
+ attr_reader :parts_with_index
164
+
165
+ def initialize
166
+ @parts_with_index = []
167
+ @num_indexies = []
168
+ end
169
+
170
+ def add_parts(part, index, is_num)
171
+ @parts_with_index << [part, index]
172
+ @num_indexies << index if is_num
173
+ end
174
+
175
+ def num?(index)
176
+ @num_indexies.include?(index)
177
+ end
178
+
179
+ def size
180
+ parts_with_index.size
181
+ end
182
+ end
183
+
184
+ # decomposes a number and takes from last.
185
+ class ValueStack
186
+ def initialize(source, dec_digit, format)
187
+ # reduce 1 because dec_digit include decimal point.
188
+ # call to_d because round(0) in ruby 3.0 returns integer.
189
+ value = dec_digit.positive? ? source.round(dec_digit - 1).to_d : source.round(0).to_d
190
+ str = value.to_s('F')
191
+ int, dec = str.split('.')
192
+
193
+ @ints = format ? int_split_with_comma(int) : int.split('')
194
+ @decs = dec_digit.positive? ? dec.split('') : []
195
+ end
196
+
197
+ def int_split_with_comma(int_s)
198
+ result = []
199
+ count = 0
200
+ arr = int_s.split('')
201
+ length = arr.length
202
+ length.times do |i|
203
+ result.unshift(arr.pop)
204
+ count += 1
205
+ next if count < 3 || i == length - 1
206
+
207
+ result.unshift(',')
208
+ count = 0
209
+ end
210
+ result
211
+ end
212
+
213
+ def pop_int
214
+ s = @ints.pop
215
+ return s unless @ints.last == ','
216
+
217
+ # return with comma if exists
218
+ @ints.pop + s
219
+ end
220
+
221
+ def shift_dec
222
+ @decs.shift
223
+ end
224
+
225
+ def empty?
226
+ @ints.empty? && @decs.empty?
227
+ end
228
+
229
+ def int_remain?
230
+ @ints.filter { |x| x != '-' }
231
+ .any?
232
+ end
233
+
234
+ def to_s
235
+ s = @ints.join
236
+ return s if @decs.empty? || @decs == ['0']
237
+
238
+ "#{s}.#{@decs.join}"
239
+ end
240
+ end
241
+ end
242
+ end
243
+ end
@@ -0,0 +1,103 @@
1
+ module XlsFunction
2
+ module FormatString
3
+ module Evaluators
4
+ class TimeEvaluator
5
+ attr_reader :source, :cache, :minute_mode, :flags, :after_effect
6
+
7
+ def initialize(source, cache, minute_mode: false, flags: {}, &after_effect)
8
+ @source = source.to_s
9
+ @cache = cache
10
+ @minute_mode = minute_mode
11
+ @flags = flags
12
+ @after_effect = after_effect
13
+ end
14
+
15
+ def call(input)
16
+ time = ::XlsFunction::Converters::TimeConverter.convert_with_cache(input, cache)
17
+ format = convert_format
18
+ result = convert_to_string(time, format)
19
+ return result unless after_effect
20
+
21
+ after_effect.call(self, result)
22
+ end
23
+
24
+ def convert_format
25
+ XlsFunction::Converters::TimeConverter.convert_format(modified_source, ampm_mode: ampm_mode)
26
+ end
27
+
28
+ def ampm_mode
29
+ flags[:ampm]
30
+ end
31
+
32
+ def modified_source
33
+ minute_mode ? source.upcase : source.downcase
34
+ end
35
+
36
+ def convert_to_string(time, format)
37
+ if format.start_with?('%J')
38
+ time.to_date.strftime(format)
39
+ else
40
+ time.strftime(format)
41
+ end
42
+ end
43
+
44
+ # ↓ after_effects: treat formats not covered by strftime
45
+ def month(value)
46
+ return value[1..] if source == 'm' && value.start_with?('0')
47
+ return value[0] if source == 'mmmmm'
48
+
49
+ value
50
+ end
51
+
52
+ def day(value)
53
+ return value[1..] if source == 'd' && value.start_with?('0')
54
+
55
+ value
56
+ end
57
+
58
+ def weekday(value)
59
+ return ::XlsFunction::Converters::TimeConverter.convert_weekday_jp(value.to_i) if source == 'aaa'
60
+ return "#{::XlsFunction::Converters::TimeConverter.convert_weekday_jp(value.to_i)}曜日" if source == 'aaaa'
61
+
62
+ value
63
+ end
64
+
65
+ def wareki(value)
66
+ return '元' if gannen && value == '1'
67
+ return value.rjust(2, '0') if %w[ee r].include?(source)
68
+
69
+ value
70
+ end
71
+
72
+ def gannen
73
+ flags[:gannen]
74
+ end
75
+
76
+ def gengo(value)
77
+ return ::XlsFunction::Converters::TimeConverter.convert_wareki_to_alphabet(value) if source == 'g'
78
+ return value[0] if source == 'gg'
79
+
80
+ value
81
+ end
82
+
83
+ def hour(value)
84
+ return value[1..] if source == 'h' && value.length == 2 && value.start_with?('0')
85
+
86
+ value
87
+ end
88
+
89
+ def minute(value)
90
+ return value[1..] if source == 'm' && value.length == 2 && value.start_with?('0')
91
+
92
+ value
93
+ end
94
+
95
+ def second(value)
96
+ return value[1..] if source == 's' && value.length == 2 && value.start_with?('0')
97
+
98
+ value
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,53 @@
1
+ module XlsFunction
2
+ module FormatString
3
+ module ParseRules
4
+ module Dates
5
+ def self.included(klass)
6
+ klass.class_eval do
7
+ rule(:year_half) { str('yy').as(:year) }
8
+ rule(:year) { str('yyyy').as(:year) }
9
+ rule(:month_half) { str('m').as(:month) }
10
+ rule(:month) { str('mm').as(:month) }
11
+ rule(:month_3) { str('mmm').as(:month) }
12
+ rule(:month_4) { str('mmmm').as(:month) }
13
+ rule(:month_5) { str('mmmmm').as(:month) }
14
+ rule(:day_half) { str('d').as(:day) }
15
+ rule(:day) { str('dd').as(:day) }
16
+ rule(:weekday) { str('aaa').as(:weekday) }
17
+ rule(:weekday_4) { str('aaaa').as(:weekday) }
18
+ rule(:weekday_d) { str('ddd').as(:weekday) }
19
+ rule(:weekday_d_4) { str('dddd').as(:weekday) }
20
+
21
+ rule(:wareki_year_half) { str('e').as(:wareki) }
22
+ rule(:wareki_year) { (str('ee') | str('r')).as(:wareki) }
23
+ rule(:gengo) { str('g').as(:gengo) }
24
+ rule(:gengo_2) { (str('gg')).as(:gengo) }
25
+ rule(:gengo_3) { str('ggg').as(:gengo) }
26
+
27
+ rule(:gengo_wareki) { (str('gggee') | str('rr')).as(:gengo_wareki) }
28
+ rule(:gannen) { str('[$-ja-JP-x-gannen]').as(:gannen) }
29
+
30
+ rule(:date) do
31
+ (
32
+ year | year_half |
33
+ month_5 | month_4 | month_3 | month | month_half |
34
+ day | day_half |
35
+ weekday_d_4 | weekday_d | weekday_4 | weekday |
36
+ gengo_wareki |
37
+ wareki_year | wareki_year_half |
38
+ gengo_3 | gengo_2 | gengo |
39
+ gannen
40
+ ).as(:date)
41
+ end
42
+
43
+ rule(:dates) do
44
+ (
45
+ date >> (texts.maybe >> date).repeat
46
+ ).as(:dates)
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,31 @@
1
+ module XlsFunction
2
+ module FormatString
3
+ module ParseRules
4
+ module Numbers
5
+ def self.included(klass)
6
+ klass.class_eval do
7
+ rule(:digit_s) { str('#').as(:digit_s) }
8
+ rule(:digit_z) { str('0').as(:digit_z) }
9
+ rule(:digit_q) { str('?').as(:digit_q) }
10
+
11
+ rule(:number) { (digit_s | digit_z | digit_q).as(:number) }
12
+
13
+ rule(:suffixable) do
14
+ str(',').as(:string).repeat(1, 2) |
15
+ str('%').as(:string) |
16
+ str('.').as(:string)
17
+ end
18
+
19
+ rule(:numbers) do
20
+ (
21
+ (
22
+ number >> (texts.maybe >> number).repeat
23
+ ) >> suffixable.maybe
24
+ ).as(:numbers)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,25 @@
1
+ module XlsFunction
2
+ module FormatString
3
+ module ParseRules
4
+ module Texts
5
+ def self.included(klass)
6
+ klass.class_eval do
7
+ rule(:space) { match('\s').repeat(1) }
8
+
9
+ rule(:default) { str('G/標準').as(:placeholder) }
10
+ rule(:placeholder) { str('@').as(:placeholder) }
11
+
12
+ rule(:escaped_string) { str('!') >> match('.').as(:string) }
13
+
14
+ rule(:string) do
15
+ (expr_sep | time | ampm | date | number).absent? >> any.as(:string)
16
+ end
17
+
18
+ rule(:text) { (escaped_string | placeholder | default | string).as(:text) }
19
+ rule(:texts) { text.repeat(1).as(:texts) }
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end