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,55 @@
1
+ module XlsFunction
2
+ module FormatString
3
+ module ParseRules
4
+ module Times
5
+ def self.included(klass)
6
+ klass.class_eval do
7
+ rule(:hour_half) { str('h').as(:hour) }
8
+ rule(:hour) { str('hh').as(:hour) }
9
+ rule(:minute_half) { str('m').as(:minute) }
10
+ rule(:minute) { str('mm').as(:minute) }
11
+ rule(:second_half) { str('s').as(:second) }
12
+ rule(:second) { str('ss').as(:second) }
13
+
14
+ # elapsed time expressions
15
+ rule(:hour_half_elapsed) { str('[h]').as(:hour_elapsed) }
16
+ rule(:hour_elapsed) { str('[hh]').as(:hour_elapsed) }
17
+ rule(:minute_half_elapsed) { str('[m]').as(:minute_elapsed) }
18
+ rule(:minute_elapsed) { str('[mm]').as(:minute_elapsed) }
19
+ rule(:second_half_elapsed) { str('[s]').as(:second_elapsed) }
20
+ rule(:second_elapsed) { str('[ss]').as(:second_elapsed) }
21
+
22
+ rule(:millisecond) { (str('.000') | str('.00') | str('.0')).as(:millisecond) }
23
+
24
+ rule(:ampm) do
25
+ (str('AM/PM') | str('am/pm') | str('A/P') | str('a/p')).as(:ampm)
26
+ end
27
+
28
+ rule(:hour_expression) { (hour_elapsed | hour_half_elapsed | hour | hour_half).capture(:hour_expr) }
29
+ rule(:minute_expression_core) { minute | minute_half }
30
+ # only mm after hh | before ss regarded as minutes.
31
+ # if hh captured or ss appears later, mm is minute expression.
32
+ rule(:minute_expression) do
33
+ dynamic do |_source, context|
34
+ minute_expression_core if context.captures[:hour_expr]
35
+ rescue Parslet::Scope::NotFound
36
+ minute_expression_core >> (texts.maybe >> second_expression).present?
37
+ end
38
+ end
39
+ rule(:second_expression) { (second_elapsed | second_half_elapsed | second | second_half) >> millisecond.maybe }
40
+
41
+ rule(:time) do
42
+ hour_expression | minute_elapsed | minute_half_elapsed | minute_expression | second_expression
43
+ end
44
+
45
+ rule(:times) do
46
+ (
47
+ time.as(:time) >> (texts.maybe >> time.as(:time)).repeat >> texts.maybe >> ampm.maybe
48
+ ).as(:times)
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,27 @@
1
+ require 'xls_function/format_string/parse_rules/texts'
2
+ require 'xls_function/format_string/parse_rules/numbers'
3
+ require 'xls_function/format_string/parse_rules/dates'
4
+ require 'xls_function/format_string/parse_rules/times'
5
+
6
+ module XlsFunction
7
+ module FormatString
8
+ class Parser < Parslet::Parser
9
+ include ::XlsFunction::FormatString::ParseRules::Texts
10
+ include ::XlsFunction::FormatString::ParseRules::Numbers
11
+ include ::XlsFunction::FormatString::ParseRules::Dates
12
+ include ::XlsFunction::FormatString::ParseRules::Times
13
+
14
+ rule(:patterns) { times | dates | numbers | texts }
15
+ rule(:expression) { patterns.repeat }
16
+ rule(:sep) { str(';') }
17
+ rule(:expr_sep) { space.maybe >> sep >> space.maybe }
18
+ rule(:expressions) do
19
+ (expression.as(:for_plus) >> expr_sep >> expression.as(:for_minus) >> expr_sep >> expression.as(:for_zero) >> expr_sep >> expression.as(:for_string)) |
20
+ (expression.as(:for_plus) >> expr_sep >> expression.as(:for_minus) >> expr_sep >> expression.as(:for_zero)) |
21
+ (expression.as(:for_plus_and_zero) >> expr_sep >> expression.as(:for_minus)) |
22
+ expression.as(:for_all)
23
+ end
24
+ root(:expressions)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,87 @@
1
+ require 'xls_function/format_string/evaluators/number_evaluator'
2
+ require 'xls_function/format_string/evaluators/time_evaluator'
3
+ require 'xls_function/format_string/evaluators/elapsed_time_evaluator'
4
+
5
+ require 'xls_function/format_string/transform_rules/texts'
6
+ require 'xls_function/format_string/transform_rules/numbers'
7
+ require 'xls_function/format_string/transform_rules/dates'
8
+ require 'xls_function/format_string/transform_rules/times'
9
+
10
+ module XlsFunction
11
+ module FormatString
12
+ class Transform < Parslet::Transform
13
+ include ::XlsFunction::FormatString::TransformRules::Texts
14
+ include ::XlsFunction::FormatString::TransformRules::Numbers
15
+ include ::XlsFunction::FormatString::TransformRules::Dates
16
+ include ::XlsFunction::FormatString::TransformRules::Times
17
+
18
+ rule(for_all: subtree(:for_all)) do
19
+ ->(input) do
20
+ result = for_all.map { |expr| expr.call(input) }.join
21
+ result != '' ? result : input
22
+ end
23
+ end
24
+
25
+ rule(for_plus_and_zero: subtree(:for_plus_and_zero), for_minus: subtree(:for_minus)) do
26
+ ->(input) do
27
+ try_result, value = ::XlsFunction::Converters::NumberConverter.try_convert(input)
28
+ return input unless try_result
29
+
30
+ result =
31
+ if value.negative?
32
+ for_minus.map { |expr| expr.call(input) }.join
33
+ else
34
+ for_plus_and_zero.map { |expr| expr.call(input) }.join
35
+ end
36
+
37
+ result != '' ? result : input
38
+ end
39
+ end
40
+
41
+ rule(for_plus: subtree(:for_plus), for_minus: subtree(:for_minus), for_zero: subtree(:for_zero)) do
42
+ ->(input) do
43
+ try_result, value = ::XlsFunction::Converters::NumberConverter.try_convert(input)
44
+ return input unless try_result
45
+
46
+ result =
47
+ if value.negative?
48
+ for_minus.map { |expr| expr.call(input) }.join
49
+ elsif value.zero?
50
+ for_zero.map { |expr| expr.call(input) }.join
51
+ else
52
+ for_plus.map { |expr| expr.call(input) }.join
53
+ end
54
+
55
+ result != '' ? result : input
56
+ end
57
+ end
58
+
59
+ rule(for_plus: subtree(:for_plus), for_minus: subtree(:for_minus), for_zero: subtree(:for_zero), for_string: subtree(:for_string)) do
60
+ ->(input) do
61
+ try_result, value = ::XlsFunction::Converters::NumberConverter.try_convert(input)
62
+ result =
63
+ if try_result
64
+ if value.negative?
65
+ for_minus.map { |expr| expr.call(input) }.join
66
+ elsif value.zero?
67
+ for_zero.map { |expr| expr.call(input) }.join
68
+ else
69
+ for_plus.map { |expr| expr.call(input) }.join
70
+ end
71
+ else
72
+ for_string.map { |expr| expr.call(input) }.join
73
+ end
74
+
75
+ result != '' ? result : input
76
+ end
77
+ end
78
+
79
+ def apply(obj, context = nil)
80
+ context ||= {}
81
+ context[:caches] ||= {}
82
+ context[:flags] ||= {}
83
+ super(obj, context)
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,98 @@
1
+ module XlsFunction
2
+ module FormatString
3
+ module TransformRules
4
+ module Dates
5
+ def self.included(klass)
6
+ klass.class_eval do
7
+ rule(year: simple(:year)) do |context|
8
+ ::XlsFunction::FormatString::Evaluators::TimeEvaluator.new(
9
+ context[:year],
10
+ context[:caches]
11
+ )
12
+ end
13
+
14
+ rule(month: simple(:month)) do |context|
15
+ ::XlsFunction::FormatString::Evaluators::TimeEvaluator.new(
16
+ context[:month],
17
+ context[:caches],
18
+ &:month
19
+ )
20
+ end
21
+
22
+ rule(day: simple(:day)) do |context|
23
+ ::XlsFunction::FormatString::Evaluators::TimeEvaluator.new(
24
+ context[:day],
25
+ context[:caches],
26
+ &:day
27
+ )
28
+ end
29
+
30
+ rule(weekday: simple(:weekday)) do |context|
31
+ ::XlsFunction::FormatString::Evaluators::TimeEvaluator.new(
32
+ context[:weekday],
33
+ context[:caches],
34
+ &:weekday
35
+ )
36
+ end
37
+
38
+ rule(wareki: simple(:wareki)) do |context|
39
+ ::XlsFunction::FormatString::Evaluators::TimeEvaluator.new(
40
+ context[:wareki],
41
+ context[:caches],
42
+ flags: context[:flags],
43
+ &:wareki
44
+ )
45
+ end
46
+
47
+ rule(gengo: simple(:gengo)) do |context|
48
+ ::XlsFunction::FormatString::Evaluators::TimeEvaluator.new(
49
+ context[:gengo],
50
+ context[:caches],
51
+ &:gengo
52
+ )
53
+ end
54
+
55
+ rule(gengo_wareki: simple(:gengo_wareki)) do |context|
56
+ gengo_func =
57
+ ::XlsFunction::FormatString::Evaluators::TimeEvaluator.new(
58
+ 'ggg',
59
+ context[:caches],
60
+ &:gengo
61
+ ).method(:call)
62
+
63
+ wareki_func =
64
+ ::XlsFunction::FormatString::Evaluators::TimeEvaluator.new(
65
+ 'ee',
66
+ context[:caches],
67
+ &:wareki
68
+ ).method(:call)
69
+
70
+ ->(input) do
71
+ gengo_func.call(input) + wareki_func.call(input)
72
+ end
73
+ end
74
+
75
+ rule(gannen: simple(:gannen)) do |context|
76
+ context[:flags][:gannen] = true
77
+ ->(_) { '' }
78
+ end
79
+
80
+ rule(date: subtree(:date)) do
81
+ ->(input) { date.call(input) }
82
+ end
83
+
84
+ rule(texts: subtree(:texts), date: subtree(:date)) do
85
+ ->(input) do
86
+ "#{texts.map { |expr| expr.call(input) }.join}#{date.call(input)}"
87
+ end
88
+ end
89
+
90
+ rule(dates: subtree(:dates)) do
91
+ ->(input) { Array(dates).flatten.map { |expr| expr.call(input) }.join }
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,59 @@
1
+ module XlsFunction
2
+ module FormatString
3
+ module TransformRules
4
+ module Numbers
5
+ def self.included(klass)
6
+ klass.class_eval do
7
+ rule(digit_s: simple(:digit_s)) do
8
+ (->(input) do
9
+ s = input.to_s
10
+ s == '0' ? '' : s
11
+ end).tap do |p|
12
+ p.extend DigitConcern
13
+ end
14
+ end
15
+
16
+ rule(digit_z: simple(:digit_z)) do
17
+ (->(input) do
18
+ s = input.to_s
19
+ s != '' ? s : '0'
20
+ end).tap do |p|
21
+ p.extend DigitConcern
22
+ end
23
+ end
24
+
25
+ rule(digit_q: simple(:digit_q)) do
26
+ (->(input) do
27
+ s = input.to_s
28
+ s != '' ? s : ' '
29
+ end).tap do |p|
30
+ p.extend DigitConcern
31
+ end
32
+ end
33
+
34
+ rule(number: subtree(:number)) { number }
35
+
36
+ # 1 number and 1 suffix patterns such as '0%'
37
+ rule(number: subtree(:number), string: simple(:string)) do
38
+ [number, ->(_) { string }]
39
+ end
40
+
41
+ rule(texts: subtree(:texts), number: subtree(:number)) do
42
+ [texts, number]
43
+ end
44
+
45
+ rule(numbers: subtree(:numbers)) do
46
+ ::XlsFunction::FormatString::Evaluators::NumberEvaluator.new(Array(numbers).flatten)
47
+ end
48
+ end
49
+ end
50
+
51
+ module DigitConcern
52
+ def digits?
53
+ true
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,25 @@
1
+ module XlsFunction
2
+ module FormatString
3
+ module TransformRules
4
+ module Texts
5
+ def self.included(klass)
6
+ klass.class_eval do
7
+ rule(placeholder: simple(:placeholder)) do
8
+ ->(input) { input.to_s }
9
+ end
10
+
11
+ rule(string: simple(:string)) do
12
+ ->(_input) { string.to_s }
13
+ end
14
+
15
+ rule(text: subtree(:text)) { text }
16
+
17
+ rule(texts: subtree(:texts)) do
18
+ ->(input) { texts.map { |text| text.call(input) }.join }
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,97 @@
1
+ module XlsFunction
2
+ module FormatString
3
+ module TransformRules
4
+ module Times
5
+ def self.included(klass)
6
+ klass.class_eval do
7
+ rule(hour: simple(:hour)) do |context|
8
+ ::XlsFunction::FormatString::Evaluators::TimeEvaluator.new(
9
+ context[:hour],
10
+ context[:caches],
11
+ flags: context[:flags],
12
+ &:hour
13
+ )
14
+ end
15
+
16
+ rule(hour_elapsed: simple(:hour_elapsed)) do |context|
17
+ ::XlsFunction::FormatString::Evaluators::ElapsedTimeEvaluator.new(
18
+ context[:caches],
19
+ half: context[:hour_elapsed] == '[h]',
20
+ &:hour
21
+ )
22
+ end
23
+
24
+ rule(minute: simple(:minute)) do |context|
25
+ ::XlsFunction::FormatString::Evaluators::TimeEvaluator.new(
26
+ context[:minute],
27
+ context[:caches],
28
+ minute_mode: true,
29
+ &:minute
30
+ )
31
+ end
32
+
33
+ rule(minute_elapsed: simple(:minute_elapsed)) do |context|
34
+ ::XlsFunction::FormatString::Evaluators::ElapsedTimeEvaluator.new(
35
+ context[:caches],
36
+ half: context[:minute_elapsed] == '[m]',
37
+ &:minute
38
+ )
39
+ end
40
+
41
+ rule(second: simple(:second)) do |context|
42
+ ::XlsFunction::FormatString::Evaluators::TimeEvaluator.new(
43
+ context[:second],
44
+ context[:caches],
45
+ &:second
46
+ )
47
+ end
48
+
49
+ rule(second: simple(:second), millisecond: simple(:millisecond)) do |context|
50
+ ->(input) do
51
+ second_func = ::XlsFunction::FormatString::Evaluators::TimeEvaluator.new(
52
+ context[:second],
53
+ context[:caches],
54
+ &:second
55
+ )
56
+ millisecond_func = ::XlsFunction::FormatString::Evaluators::TimeEvaluator.new(
57
+ context[:millisecond],
58
+ context[:caches]
59
+ )
60
+ "#{second_func.call(input)}.#{millisecond_func.call(input)}"
61
+ end
62
+ end
63
+
64
+ rule(second_elapsed: simple(:second_elapsed)) do |context|
65
+ ::XlsFunction::FormatString::Evaluators::ElapsedTimeEvaluator.new(
66
+ context[:caches],
67
+ half: context[:second_elapsed] == '[s]',
68
+ &:second
69
+ )
70
+ end
71
+
72
+ rule(ampm: simple(:ampm)) do |context|
73
+ context[:flags][:ampm] = true
74
+ ->(input) do
75
+ time = ::XlsFunction::Converters::TimeConverter.convert_with_cache(input, context[:caches])
76
+ am, pm = context[:ampm].to_s.split('/')
77
+ time.hour <= 11 ? am : pm
78
+ end
79
+ end
80
+
81
+ rule(time: subtree(:time)) { time }
82
+
83
+ rule(texts: subtree(:texts), time: subtree(:time)) do
84
+ ->(input) do
85
+ "#{texts.map { |expr| expr.call(input) }.join}#{time.call(input)}"
86
+ end
87
+ end
88
+
89
+ rule(times: subtree(:time)) do
90
+ ->(input) { Array(time).flatten.map { |expr| expr.call(input) }.join }
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,24 @@
1
+ # Parse `format_text` of TEXT function.
2
+ # Some specifications are not supported.
3
+ # - Exponent, Fraction
4
+ # - Convert Chinese characters number
5
+ # - Monospace conversion with underscore
6
+ # - Cell width filling with asterisks
7
+ # - Buddhist calendar
8
+ # - Islamic calendar
9
+ # - Text coloring
10
+
11
+ require 'xls_function/format_string/parser'
12
+ require 'xls_function/format_string/transform'
13
+
14
+ module XlsFunction
15
+ module FormatString
16
+ class << self
17
+ def evaluate_converter(format_string, context)
18
+ parser = XlsFunction::FormatString::Parser.new
19
+ transform = XlsFunction::FormatString::Transform.new
20
+ transform.apply(parser.parse(format_string), context)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,4 @@
1
+ require 'i18n'
2
+
3
+ path = Dir["#{File.expand_path('locales', __dir__)}/*.yml"]
4
+ I18n.load_path << path
@@ -0,0 +1,65 @@
1
+ en:
2
+ xls_function:
3
+ descriptions:
4
+ and: AND(logical1, [logical2], ...) returns TRUE if all its arguments evaluate to TRUE, and returns FALSE if one or more arguments evaluate to FALSE.
5
+ asc: ASC(text) For Double-byte character set (DBCS) languages, the function changes full-width (double-byte) characters to half-width (single-byte) characters.
6
+ char: CHAR(number) Returns the character specified by a number.
7
+ clean: CLEAN(text) Removes all nonprintable characters from text.
8
+ code: CODE(text) Returns a numeric code for the first character in a text string.
9
+ concat: CONCAT(text1, [text2],…) Combines the text from multiple ranges and/or strings.
10
+ datevalue: DATEVALUE(date_text) Converts a date that is stored as text to a serial number that Excel recognizes as a date.
11
+ date: DATE(year,month,day) Returns the sequential serial number that represents a particular date.
12
+ day: DAY(serial_number) Returns the day of a date, represented by a serial number. The day is given as an integer ranging from 1 to 31.
13
+ dbcs: DBCS(text) Converts half-width (single-byte) letters within a character string to full-width (double-byte) characters. The name of the function (and the characters that it converts) depends upon your language settings.
14
+ edate: EDATE(start_date, months) Returns the serial number that represents the date that is the indicated number of months before or after a specified date (the start_date).
15
+ eomonth: EOMONTH(start_date, months) Returns the serial number for the last day of the month that is the indicated number of months before or after start_date.
16
+ exact: EXACT(text1, text2) Compares two text strings and returns TRUE if they are exactly the same, FALSE otherwise.
17
+ find: FIND(find_text, within_text, [start_num]) Locate one text string within a second text string, and return the number of the starting position of the first text string from the first character of the second text string. Always counts each character, whether single-byte or double-byte, as 1, no matter what the default language setting is.
18
+ fixed: FIXED(number, [decimals], [no_commas]) Rounds a number to the specified number of decimals, formats the number in decimal format using a period and commas, and returns the result as text.
19
+ hour: HOUR(serial_number) Returns the hour of a time value. The hour is given as an integer, ranging from 0 (12:00 A.M.) to 23 (11:00 P.M.).
20
+ if: IF(logical_test, value_if_true, [value_if_false]) Use the IF function to return one value if a condition is true and another value if it's false.
21
+ ifs: IFS(logical_test1, value_if_true1, [logical_test2, value_if_true2], [logical_test3, value_if_true3],…) Checks whether one or more conditions are met, and returns a value that corresponds to the first TRUE condition.
22
+ int: INT(number) Rounds a number down to the nearest integer.
23
+ iserror: ISERROR(value) Returns TRUE if value refers to any error value.
24
+ isnumber: ISNUMBER(value) Returns TRUE if value refers to a number.
25
+ lambda: LAMBDA([parameter1, parameter2, …,] calculation) Use a LAMBDA function to create custom, reusable functions and call them by a friendly name. *Do not use this directly.
26
+ left: LEFT(text, [num_chars]) Returns the first character or characters in a text string, based on the number of characters you specify.
27
+ len: LEN(text) Returns the number of characters in a text string.
28
+ let: LET(name1, name_value1, calculation_or_name2, [name_value2, calculation_or_name3...]) Assigns names to calculation results. This allows storing intermediate calculations, values, or defining names inside a formula.
29
+ lower: LOWER(text) Converts all uppercase letters in a text string to lowercase.
30
+ mid: MID(text, start_num, num_chars) Returns a specific number of characters from a text string, starting at the position you specify, based on the number of characters you specify.
31
+ minute: MINUTE(serial_number) Returns the minutes of a time value. The minute is given as an integer, ranging from 0 to 59.
32
+ month: MONTH(serial_number) Returns the month of a date represented by a serial number. The month is given as an integer, ranging from 1 (January) to 12 (December).
33
+ not: NOT(logical) Reverses the value of its argument.
34
+ now: NOW() Returns the serial number of the current date and time.
35
+ or: OR(logical1, [logical2], ...) Returns TRUE if any of its arguments evaluate to TRUE, and returns FALSE if all of its arguments evaluate to FALSE.
36
+ power: POWER(number, power) Returns the result of a number raised to a power.
37
+ proper: Capitalizes the first letter in a text string and any other letters in text that follow any character other than a letter. Converts all other letters to lowercase letters.
38
+ replace: REPLACE(old_text, start_num, num_chars, new_text) Replaces part of a text string, based on the number of characters you specify, with a different text string.
39
+ rept: REPT(text, number_times) Repeats text a given number of times.
40
+ right: RIGHT(text,[num_chars]) Returns the last character or characters in a text string, based on the number of characters you specify.
41
+ rounddown: ROUNDDOWN(number, num_digits) Rounds a number down, toward zero.
42
+ roundup: ROUNDUP(number, num_digits) Rounds a number up, away from 0 (zero).
43
+ round: ROUND(number, num_digits) Rounds a number to a specified number of digits.
44
+ second: SECOND(serial_number) Returns the seconds of a time value. The second is given as an integer in the range 0 (zero) to 59.
45
+ sqrt: SQRT(number) Returns a positive square root.
46
+ substitute: SUBSTITUTE(text, old_text, new_text, [instance_num]) Substitutes new_text for old_text in a text string.
47
+ text: TEXT(value, format_text) Lets you change the way a number appears by applying formatting to it with format codes.
48
+ time: TIME(hour, minute, second) Returns the decimal number for a particular time.
49
+ timevalue: TIMEVALUE(time_text) Returns the decimal number of the time represented by a text string.
50
+ today: TODAY() Returns the serial number of the current date.
51
+ trim: TRIM(text) Removes all spaces from text except for single spaces between words.
52
+ trunc: TRUNC(number, [num_digits]) Truncates a number to an integer by removing the fractional part of the number.
53
+ unichar: UNICHAR(number) Returns the Unicode character that is referenced by the given numeric value.
54
+ unicode: UNICODE(text) Returns the number (code point) corresponding to the first character of the text.
55
+ upper: UPPER(text) Converts text to uppercase.
56
+ value: VALUE(text) Converts a text string that represents a number to a number.
57
+ year: YEAR(serial_number) Returns the year corresponding to a date. The year is returned as an integer in the range 1900-9999.
58
+ errors:
59
+ out_of_range: Value is out of range. %{value}
60
+ missing_target: Missing %{target} in %{source}.
61
+ number_is_negative: "%{label} is negative. %{number}"
62
+ cannot_convert_to_number: Cannot convert %{source} to number.
63
+ cannot_convert_to_date: Cannot convert %{source} to date.
64
+ cannot_convert_to_time: Cannot convert %{source} to time.
65
+ invalid_value_for_function: Invalid value for formula or function.
@@ -0,0 +1,65 @@
1
+ ja:
2
+ xls_function:
3
+ descriptions:
4
+ and: AND(条件1, [条件2], ...) すべての引数が TRUE と評価された場合は TRUE を返し、1 つ以上の引数が FALSE と評価された場合は FALSE を返します。
5
+ asc: ASC(文字列) 2 バイト文字セット (DBCS) 言語の場合、全角 (2 バイト) 文字を半角 (1 バイト) 文字に変更します。
6
+ char: CHAR(数値) 数値で指定された文字を返します。
7
+ clean: CLEAN(文字列) 印刷できない文字を文字列からすべて削除します。
8
+ code: CODE(文字列) テキスト文字列内の先頭文字の数値コードを返します。
9
+ concat: CONCAT(テキスト1, [テキスト2],…) 複数の範囲や文字列のテキストを結合します。
10
+ datevalue: DATEVALUE(日付文字列) 文字列として格納された日付を、Excel で日付として認識できるシリアル値に変換します。
11
+ date: DATE(年,月,日) 特定の日付を表す連続したシリアル値を返します。
12
+ day: DAY(シリアル値) シリアル番号で表された、日付の日情報を返します。 日情報は 1 ~ 31 の範囲内の整数で示されます。
13
+ dbcs: DBCS(文字列) 文字列内の半角 (1 バイト) の英数カナ文字を全角 (2 バイト) の文字に変換します。
14
+ edate: EDATE(開始日, 月) 開始日から起算して、指定された月数だけ前または後の日付に対応するシリアル値を返します。
15
+ eomonth: EOMONTH(開始日, 月) 開始日から起算して、指定された月数だけ前または後の月の最終日に対応するシリアル値を返します。
16
+ exact: EXACT(文字列 1, 文字列 2) 2 つの文字列を比較して、まったく同じである場合は TRUE を、そうでない場合は FALSE を返します。
17
+ find: FIND(検索文字列, 対象, [開始位置]) 指定された文字列を他の文字列の中で検索し、その文字列が最初に現れる位置を左端から数え、その番号を返します。既定の言語の設定に関係なく、1 バイト文字も 2 バイト文字も、各文字が常に 1 つとして数えられます。
18
+ fixed: FIXED(数値, [桁数], [桁区切り]) 指定した桁数に四捨五入し、結果をピリオド (.) とカンマ (,) を使って書式設定した文字列に変換して返します。
19
+ hour: HOUR(シリアル値) 時刻から時間の値を返します。 戻り値は 0 (午前 0 時) ~ 23 (午後 11 時) の範囲の整数となります。
20
+ if: IF(論理式, [値が真の場合], [値が偽の場合]) IF 関数を使うと、条件が true または false の場合に、それぞれ別の値を返すことができます。
21
+ ifs: IFS(logical_test1, value_if_true1, [logical_test2, value_if_true2], [logical_test3, value_if_true3],…) 1 つ以上の条件が満たされているかどうかをチェックして、最初の TRUE 条件に対応する値を返します。
22
+ int: INT(数値) 指定された数値を最も近い整数に切り捨てます。
23
+ iserror: ISERROR(テストの対象) テストの対象が任意のエラー値を参照するとき TRUE を返します。
24
+ isnumber: ISNUMBER(テストの対象) テストの対象が数値を参照するとき TRUE を返します。
25
+ lambda: LAMBDA([parameter1, parameter2, ...,] 計算) LAMBDA 関数を使用して、カスタムで再利用可能な関数を作成し、フレンドリ名で呼び出します。※直接利用しないでください。
26
+ left: LEFT(文字列, [文字数]) 文字列の先頭から指定された数の文字を返します。
27
+ len: LEN(文字列) 文字列の文字数を返します。
28
+ let: LET (name1、name_value1、calculation_or_name2、[name_value2、calculation_or_name3...]) 計算結果に名前を割り当てます。 これにより、中間計算、値、定義名などを数式内に格納できます。
29
+ lower: LOWER(文字列) 文字列に含まれる英大文字をすべて小文字に変換します。
30
+ mid: MID(文字列, 開始位置, 文字数) 文字列の指定された位置から指定された文字数の文字を返します。
31
+ minute: MINUTE(シリアル値) 時刻の分を返します。 戻り値は 0 (分) ~ 59 (分) の範囲の整数となります。
32
+ month: MONTH(シリアル値) データに含まれる月をシリアル値で返します。 戻り値は 1 (月) ~ 12 (月) の範囲の整数となります。
33
+ not: NOT(論理値) 引数の値を反転させます。
34
+ now: NOW() 現在の日付と時刻に対応するシリアル値を返します。
35
+ or: OR(論理式1, [論理式2], ...) いずれかの引数が TRUE と評価された場合は TRUE を返し、すべての引数が FALSE と評価された場合は FALSE を返します。
36
+ power: POWER(数値, 指数) 数値のべき乗を返します。
37
+ proper: PROPER(文字列) 英字文字列の単語の先頭の文字、および記号の次の文字を大文字に変換します。 それ以外の英字はすべて小文字にします。
38
+ replace: REPLACE(文字列, 開始位置, 文字数, 置換文字列) 文字列に含まれる、指定された文字数の文字を別の文字に置き換えます。
39
+ rept: REPT(文字列, 繰り返し回数) 文字列を指定された回数だけ繰り返して表示します。
40
+ right: RIGHT(文字列,[文字数]) 文字列の末尾 (右端) から指定された文字数の文字を返します。
41
+ rounddown: ROUNDDOWN(数値, 桁数) 数値を指定された桁数で切り捨てます。
42
+ roundup: ROUNDUP(数値, 桁数) 数値を指定された桁数に切り上げます。
43
+ round: ROUND(数値, 桁数) 数値を四捨五入して指定された桁数にします。
44
+ second: SECOND(シリアル値) 時刻の秒を返します。 戻り値は 0 (秒) ~ 59 (秒) の範囲の整数となります。
45
+ sqrt: SQRT(数値) 正の平方根を返します。
46
+ substitute: SUBSTITUTE(文字列, 検索文字列, 置換文字列, [置換対象]) 文字列に含まれる検索文字列を置換文字列へ置換します。
47
+ text: TEXT(value, format_text) 表示形式コードを使用して数値に書式設定を適用することで、数値の表示方法を変更することができます。
48
+ time: TIME(時, 分, 秒) 指定した時刻に対応する小数を返します。
49
+ timevalue: TIMEVALUE(時刻文字列) 文字列で表された時刻を小数に変換します。
50
+ today: TODAY() 現在の日付に対応するシリアル値を返します。
51
+ trim: TRIM(文字列) 各単語間のスペースは 1 つ残し、不要なスペースをすべて削除します。
52
+ trunc: TRUNC(数値, [桁数]) 数値の小数部を切り捨てて、整数または指定した桁数に変換します。
53
+ unichar: UNICHAR(数値) 指定された数値により参照される Unicode 文字を返します。
54
+ unicode: UNICODE(文字列) 文字列の最初の文字に対応する番号 (コード ポイント) を返します。
55
+ upper: UPPER(文字列) 文字列を大文字に変換します。
56
+ value: VALUE(文字列) 数値を表す文字列を数値に変換します。
57
+ year: YEAR(シリアル値) 日付に対応する年を返します。 戻り値は、1900 (年) ~ 9999 (年) の範囲の整数となります。
58
+ errors:
59
+ out_of_range: 範囲外の値が指定されています。%{value}
60
+ missing_target: 文字列%{source}中に%{target}が見つかりません。
61
+ number_is_negative: "%{label}が負の値になっています。%{number}"
62
+ cannot_convert_to_number: "%{source}は数値に変換できません。"
63
+ cannot_convert_to_date: "%{source}は日付に変換できません。"
64
+ cannot_convert_to_time: "%{source}は時刻に変換できません。"
65
+ invalid_value_for_function: 値が数式または関数に対して無効です。
@@ -0,0 +1,28 @@
1
+ module XlsFunction
2
+ module ParseRules
3
+ module BinaryOperation
4
+ def self.included(klass)
5
+ klass.class_eval do
6
+ rule(:power_operator) { str('^').as(:operator) >> space? }
7
+ rule(:multiple_divide_operator) { match['*/'].as(:operator) >> space? }
8
+ rule(:plus_minus_opeartor) { match['+-'].as(:operator) >> space? }
9
+ rule(:concat_opeartor) { str('&').as(:operator) >> space? }
10
+ rule(:comparison_operator) { (str('>') | str('>=') | str('<') | str('<=') | str('=') | str('<>')).as(:operator) >> space? }
11
+
12
+ rule(:binary_operation) do
13
+ infix_expression(
14
+ preferred_binary_operation | single_expression,
15
+ [power_operator, 5, :left],
16
+ [multiple_divide_operator, 4, :left],
17
+ [plus_minus_opeartor, 3, :left],
18
+ [concat_opeartor, 2, :left],
19
+ [comparison_operator, 1, :left]
20
+ ) { |l, o, r| { left: l, operator: o[:operator], right: r } }
21
+ end
22
+
23
+ rule(:preferred_binary_operation) { lparen >> binary_operation >> rparen }
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end