plurimath 0.11.1 → 0.11.3
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 +4 -4
- data/.gitignore +3 -0
- data/.rubocop_todo.yml +108 -175
- data/Gemfile +1 -0
- data/README.adoc +282 -6
- data/lib/plurimath/asciimath/parse.rb +6 -1
- data/lib/plurimath/asciimath/transform.rb +2 -0
- data/lib/plurimath/base_number_prefix.rb +43 -0
- data/lib/plurimath/configuration.rb +9 -1
- data/lib/plurimath/errors/evaluation/division_by_zero_error.rb +13 -0
- data/lib/plurimath/errors/evaluation/error.rb +9 -0
- data/lib/plurimath/errors/evaluation/invalid_binding_error.rb +14 -0
- data/lib/plurimath/errors/evaluation/invalid_binding_key_error.rb +14 -0
- data/lib/plurimath/errors/evaluation/math_domain_error.rb +9 -0
- data/lib/plurimath/errors/evaluation/missing_variable_error.rb +13 -0
- data/lib/plurimath/errors/evaluation/non_finite_result_error.rb +13 -0
- data/lib/plurimath/errors/evaluation/unsupported_expression_error.rb +13 -0
- data/lib/plurimath/errors/evaluation.rb +18 -0
- data/lib/plurimath/errors.rb +1 -0
- data/lib/plurimath/formatter/numbers/base_notation.rb +54 -31
- data/lib/plurimath/formatter/numbers/formatted_notation.rb +62 -0
- data/lib/plurimath/formatter/numbers/formatted_number.rb +87 -0
- data/lib/plurimath/formatter/numbers/fraction.rb +1 -1
- data/lib/plurimath/formatter/numbers/mathml_renderer.rb +56 -0
- data/lib/plurimath/formatter/numbers/notation_renderer.rb +30 -29
- data/lib/plurimath/formatter/numbers/number_renderer.rb +10 -9
- data/lib/plurimath/formatter/numbers/omml_renderer.rb +74 -0
- data/lib/plurimath/formatter/numbers/source.rb +29 -4
- data/lib/plurimath/formatter/numbers/text_renderer.rb +52 -0
- data/lib/plurimath/formatter/numbers.rb +6 -2
- data/lib/plurimath/html/parse.rb +5 -0
- data/lib/plurimath/html/transform.rb +2 -0
- data/lib/plurimath/latex/parse.rb +5 -0
- data/lib/plurimath/latex/transform.rb +2 -0
- data/lib/plurimath/math/core.rb +52 -0
- data/lib/plurimath/math/evaluation/evaluator.rb +147 -0
- data/lib/plurimath/math/evaluation/expression_parser.rb +215 -0
- data/lib/plurimath/math/evaluation/iteration.rb +63 -0
- data/lib/plurimath/math/evaluation.rb +13 -0
- data/lib/plurimath/math/formula.rb +9 -0
- data/lib/plurimath/math/function/abs.rb +4 -0
- data/lib/plurimath/math/function/arccos.rb +4 -0
- data/lib/plurimath/math/function/arcsin.rb +4 -0
- data/lib/plurimath/math/function/arctan.rb +4 -0
- data/lib/plurimath/math/function/ceil.rb +4 -0
- data/lib/plurimath/math/function/cos.rb +4 -0
- data/lib/plurimath/math/function/cosh.rb +4 -0
- data/lib/plurimath/math/function/cot.rb +4 -0
- data/lib/plurimath/math/function/coth.rb +4 -0
- data/lib/plurimath/math/function/csc.rb +4 -0
- data/lib/plurimath/math/function/csch.rb +4 -0
- data/lib/plurimath/math/function/exp.rb +4 -0
- data/lib/plurimath/math/function/fenced.rb +4 -0
- data/lib/plurimath/math/function/floor.rb +4 -0
- data/lib/plurimath/math/function/frac.rb +7 -0
- data/lib/plurimath/math/function/gcd.rb +9 -0
- data/lib/plurimath/math/function/lcm.rb +9 -0
- data/lib/plurimath/math/function/lg.rb +4 -0
- data/lib/plurimath/math/function/ln.rb +4 -0
- data/lib/plurimath/math/function/log.rb +19 -0
- data/lib/plurimath/math/function/max.rb +4 -0
- data/lib/plurimath/math/function/min.rb +4 -0
- data/lib/plurimath/math/function/mod.rb +15 -0
- data/lib/plurimath/math/function/power.rb +10 -0
- data/lib/plurimath/math/function/prod.rb +10 -0
- data/lib/plurimath/math/function/root.rb +7 -0
- data/lib/plurimath/math/function/sec.rb +4 -0
- data/lib/plurimath/math/function/sech.rb +4 -0
- data/lib/plurimath/math/function/sin.rb +4 -0
- data/lib/plurimath/math/function/sinh.rb +4 -0
- data/lib/plurimath/math/function/sqrt.rb +4 -0
- data/lib/plurimath/math/function/sum.rb +10 -0
- data/lib/plurimath/math/function/tan.rb +4 -0
- data/lib/plurimath/math/function/tanh.rb +4 -0
- data/lib/plurimath/math/function/text.rb +17 -0
- data/lib/plurimath/math/number.rb +40 -29
- data/lib/plurimath/math/symbols/cdot.rb +4 -0
- data/lib/plurimath/math/symbols/div.rb +4 -0
- data/lib/plurimath/math/symbols/hat.rb +4 -0
- data/lib/plurimath/math/symbols/minus.rb +4 -0
- data/lib/plurimath/math/symbols/pi.rb +5 -1
- data/lib/plurimath/math/symbols/plus.rb +4 -0
- data/lib/plurimath/math/symbols/slash.rb +4 -0
- data/lib/plurimath/math/symbols/symbol.rb +45 -0
- data/lib/plurimath/math/symbols/times.rb +4 -0
- data/lib/plurimath/math.rb +1 -0
- data/lib/plurimath/mathml/constants.rb +18 -0
- data/lib/plurimath/number_formatter.rb +47 -28
- data/lib/plurimath/setup/opal.rb.erb +13 -0
- data/lib/plurimath/unicode_math/parse.rb +5 -1
- data/lib/plurimath/unicode_math/transform.rb +469 -755
- data/lib/plurimath/utility.rb +1 -1
- data/lib/plurimath/version.rb +1 -1
- data/lib/plurimath.rb +1 -0
- metadata +21 -3
- data/lib/plurimath/formatter/numbers/parts_renderer.rb +0 -30
|
@@ -89,6 +89,13 @@ module Plurimath
|
|
|
89
89
|
"#{first_value}∕#{second_value}" if self.options&.key?(:ldiv)
|
|
90
90
|
end
|
|
91
91
|
|
|
92
|
+
def evaluate(evaluator)
|
|
93
|
+
evaluator.divide(
|
|
94
|
+
evaluator.evaluate_node(parameter_one),
|
|
95
|
+
evaluator.evaluate_node(parameter_two),
|
|
96
|
+
)
|
|
97
|
+
end
|
|
98
|
+
|
|
92
99
|
def line_breaking(obj)
|
|
93
100
|
parameter_one&.line_breaking(obj)
|
|
94
101
|
if obj.value_exist?
|
|
@@ -8,6 +8,15 @@ module Plurimath
|
|
|
8
8
|
false
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
+
def evaluate(evaluator)
|
|
12
|
+
values = evaluator.function_arguments(parameter_one)
|
|
13
|
+
unless values.all?(Integer)
|
|
14
|
+
raise Errors::Evaluation::MathDomainError, "gcd requires integer arguments"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
values.reduce(:gcd)
|
|
18
|
+
end
|
|
19
|
+
|
|
11
20
|
def to_omml_without_math_tag(display_style, options:)
|
|
12
21
|
array = []
|
|
13
22
|
array << r_element("gcd", rpr_tag: false) unless hide_function_name
|
|
@@ -8,6 +8,15 @@ module Plurimath
|
|
|
8
8
|
false
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
+
def evaluate(evaluator)
|
|
12
|
+
values = evaluator.function_arguments(parameter_one)
|
|
13
|
+
unless values.all?(Integer)
|
|
14
|
+
raise Errors::Evaluation::MathDomainError, "lcm requires integer arguments"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
values.reduce(:lcm)
|
|
18
|
+
end
|
|
19
|
+
|
|
11
20
|
def to_asciimath(options:)
|
|
12
21
|
first_value = " #{asciimath_value(options: options)}" if parameter_one
|
|
13
22
|
"lcm#{first_value}"
|
|
@@ -8,6 +8,10 @@ module Plurimath
|
|
|
8
8
|
false
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
+
def evaluate(evaluator)
|
|
12
|
+
::Math.log10(evaluator.evaluate_node(parameter_one))
|
|
13
|
+
end
|
|
14
|
+
|
|
11
15
|
def to_omml_without_math_tag(display_style, options:)
|
|
12
16
|
array = []
|
|
13
17
|
array << r_element("lg", rpr_tag: false) unless hide_function_name
|
|
@@ -8,6 +8,10 @@ module Plurimath
|
|
|
8
8
|
false
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
+
def evaluate(evaluator)
|
|
12
|
+
::Math.log(evaluator.evaluate_node(parameter_one))
|
|
13
|
+
end
|
|
14
|
+
|
|
11
15
|
def to_omml_without_math_tag(display_style, options:)
|
|
12
16
|
array = []
|
|
13
17
|
array = r_element("ln", rpr_tag: false) unless hide_function_name
|
|
@@ -10,6 +10,25 @@ module Plurimath
|
|
|
10
10
|
second_value: "supscript",
|
|
11
11
|
}.freeze
|
|
12
12
|
|
|
13
|
+
# `log` carries its base/exponent but not its argument; the parser
|
|
14
|
+
# binds the following fenced group via #evaluate_with_argument, so a
|
|
15
|
+
# bare `log` is unsupported.
|
|
16
|
+
def evaluate(evaluator)
|
|
17
|
+
evaluator.unsupported(self)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def evaluate_with_argument(evaluator, argument)
|
|
21
|
+
base = parameter_one ? evaluator.evaluate_node(parameter_one) : 10
|
|
22
|
+
unless base.positive? && base != 1
|
|
23
|
+
raise Errors::Evaluation::MathDomainError,
|
|
24
|
+
"log base must be a positive number other than 1"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
result = ::Math.log(evaluator.evaluate_node(argument), base)
|
|
28
|
+
result = evaluator.power(result, evaluator.evaluate_node(parameter_two)) if parameter_two
|
|
29
|
+
result
|
|
30
|
+
end
|
|
31
|
+
|
|
13
32
|
def to_asciimath(options:)
|
|
14
33
|
if parameter_one
|
|
15
34
|
first_value = "_#{wrapped(parameter_one,
|
|
@@ -8,6 +8,10 @@ module Plurimath
|
|
|
8
8
|
false
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
+
def evaluate(evaluator)
|
|
12
|
+
evaluator.function_arguments(parameter_one).max
|
|
13
|
+
end
|
|
14
|
+
|
|
11
15
|
def to_omml_without_math_tag(display_style, options:)
|
|
12
16
|
array = []
|
|
13
17
|
array << r_element("max", rpr_tag: false) unless hide_function_name
|
|
@@ -8,6 +8,10 @@ module Plurimath
|
|
|
8
8
|
false
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
+
def evaluate(evaluator)
|
|
12
|
+
evaluator.function_arguments(parameter_one).min
|
|
13
|
+
end
|
|
14
|
+
|
|
11
15
|
def to_omml_without_math_tag(display_style, options:)
|
|
12
16
|
array = []
|
|
13
17
|
array << r_element("min", rpr_tag: false) unless hide_function_name
|
|
@@ -10,6 +10,21 @@ module Plurimath
|
|
|
10
10
|
second_value: "argument",
|
|
11
11
|
}.freeze
|
|
12
12
|
|
|
13
|
+
def evaluate(evaluator)
|
|
14
|
+
evaluator.modulo(
|
|
15
|
+
evaluator.evaluate_node(parameter_one),
|
|
16
|
+
evaluator.evaluate_node(parameter_two),
|
|
17
|
+
)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# `-a mod b` negates the dividend, matching `(-a) mod b`.
|
|
21
|
+
def evaluate_negated(evaluator)
|
|
22
|
+
evaluator.modulo(
|
|
23
|
+
-evaluator.evaluate_node(parameter_one),
|
|
24
|
+
evaluator.evaluate_node(parameter_two),
|
|
25
|
+
)
|
|
26
|
+
end
|
|
27
|
+
|
|
13
28
|
def to_asciimath(options:)
|
|
14
29
|
first_value = parameter_one&.to_asciimath(options: options)
|
|
15
30
|
second_value = parameter_two&.to_asciimath(options: options)
|
|
@@ -80,6 +80,16 @@ module Plurimath
|
|
|
80
80
|
end
|
|
81
81
|
end
|
|
82
82
|
|
|
83
|
+
# Nested Power trees evaluate literally: `Power(Power(2, 3), 2)` is
|
|
84
|
+
# `(2^3)^2`, whether it came from explicit MathML nesting or a
|
|
85
|
+
# left-nested source chain.
|
|
86
|
+
def evaluate(evaluator)
|
|
87
|
+
evaluator.power(
|
|
88
|
+
evaluator.evaluate_node(parameter_one),
|
|
89
|
+
evaluator.evaluate_node(parameter_two),
|
|
90
|
+
)
|
|
91
|
+
end
|
|
92
|
+
|
|
83
93
|
def line_breaking(obj)
|
|
84
94
|
parameter_one&.line_breaking(obj)
|
|
85
95
|
if obj.value_exist?
|
|
@@ -12,6 +12,16 @@ module Plurimath
|
|
|
12
12
|
second_value: "supscript",
|
|
13
13
|
}.freeze
|
|
14
14
|
|
|
15
|
+
def evaluate(evaluator)
|
|
16
|
+
if parameter_one.nil? || parameter_two.nil? || parameter_three.nil?
|
|
17
|
+
evaluator.unsupported(self)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
Evaluation::Iteration
|
|
21
|
+
.new(evaluator, parameter_one, parameter_two, parameter_three)
|
|
22
|
+
.accumulate(1, :*)
|
|
23
|
+
end
|
|
24
|
+
|
|
15
25
|
def initialize(parameter_one = nil,
|
|
16
26
|
parameter_two = nil,
|
|
17
27
|
parameter_three = nil,
|
|
@@ -51,6 +51,13 @@ module Plurimath
|
|
|
51
51
|
second_value = parameter_two.to_unicodemath(options: options) if parameter_two
|
|
52
52
|
"√(#{first_value}&#{second_value})"
|
|
53
53
|
end
|
|
54
|
+
|
|
55
|
+
def evaluate(evaluator)
|
|
56
|
+
evaluator.power(
|
|
57
|
+
evaluator.evaluate_node(parameter_two),
|
|
58
|
+
evaluator.divide(1.0, evaluator.evaluate_node(parameter_one)),
|
|
59
|
+
)
|
|
60
|
+
end
|
|
54
61
|
end
|
|
55
62
|
end
|
|
56
63
|
end
|
|
@@ -13,6 +13,16 @@ module Plurimath
|
|
|
13
13
|
third_value: "term",
|
|
14
14
|
}.freeze
|
|
15
15
|
|
|
16
|
+
def evaluate(evaluator)
|
|
17
|
+
if parameter_one.nil? || parameter_two.nil? || parameter_three.nil?
|
|
18
|
+
evaluator.unsupported(self)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
Evaluation::Iteration
|
|
22
|
+
.new(evaluator, parameter_one, parameter_two, parameter_three)
|
|
23
|
+
.accumulate(0, :+)
|
|
24
|
+
end
|
|
25
|
+
|
|
16
26
|
def initialize(parameter_one = nil,
|
|
17
27
|
parameter_two = nil,
|
|
18
28
|
parameter_three = nil,
|
|
@@ -66,6 +66,23 @@ module Plurimath
|
|
|
66
66
|
false
|
|
67
67
|
end
|
|
68
68
|
|
|
69
|
+
# OMML emits variable text runs as Text nodes. Only a plain, non-blank
|
|
70
|
+
# string names a variable; rich content (arrays/formulas) or a missing
|
|
71
|
+
# value does not.
|
|
72
|
+
def variable_name
|
|
73
|
+
return unless parameter_one.is_a?(String)
|
|
74
|
+
|
|
75
|
+
stripped = parameter_one.strip
|
|
76
|
+
stripped unless stripped.empty?
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def evaluate(evaluator)
|
|
80
|
+
name = variable_name
|
|
81
|
+
evaluator.unsupported(self) unless name
|
|
82
|
+
|
|
83
|
+
evaluator.value_for(name)
|
|
84
|
+
end
|
|
85
|
+
|
|
69
86
|
def to_asciimath_math_zone(spacing, _, _, options:)
|
|
70
87
|
"#{spacing}#{to_asciimath(options: options)} text\n"
|
|
71
88
|
end
|
|
@@ -3,48 +3,59 @@
|
|
|
3
3
|
module Plurimath
|
|
4
4
|
module Math
|
|
5
5
|
class Number < Core
|
|
6
|
-
attr_accessor :value, :mini_sub_sized, :mini_sup_sized
|
|
6
|
+
attr_accessor :value, :mini_sub_sized, :mini_sup_sized, :base
|
|
7
7
|
|
|
8
|
-
def initialize(value = nil, mini_sub_sized: false, mini_sup_sized: false
|
|
8
|
+
def initialize(value = nil, mini_sub_sized: false, mini_sup_sized: false,
|
|
9
|
+
base: nil)
|
|
9
10
|
@value = value.is_a?(::Parslet::Slice) ? value.to_s : value
|
|
10
|
-
@mini_sub_sized = mini_sub_sized
|
|
11
|
-
@mini_sup_sized = mini_sup_sized
|
|
11
|
+
@mini_sub_sized = mini_sub_sized
|
|
12
|
+
@mini_sup_sized = mini_sup_sized
|
|
13
|
+
@base = base
|
|
12
14
|
end
|
|
13
15
|
|
|
14
16
|
def ==(object)
|
|
15
|
-
object.
|
|
17
|
+
object.is_a?(Number) &&
|
|
16
18
|
object.value == value &&
|
|
17
19
|
object.mini_sub_sized == mini_sub_sized &&
|
|
18
|
-
object.mini_sup_sized == mini_sup_sized
|
|
20
|
+
object.mini_sup_sized == mini_sup_sized &&
|
|
21
|
+
object.base == base
|
|
19
22
|
end
|
|
20
23
|
|
|
21
24
|
def element_order=(*); end
|
|
22
25
|
|
|
23
26
|
def to_asciimath(options:)
|
|
24
|
-
|
|
27
|
+
Formatter::Numbers::TextRenderer.render(
|
|
28
|
+
format_value_with_options(options), :asciimath
|
|
29
|
+
)
|
|
25
30
|
end
|
|
26
31
|
|
|
27
32
|
def to_mathml_without_math_tag(_, options:)
|
|
28
|
-
|
|
33
|
+
Formatter::Numbers::MathmlRenderer.render(format_value_with_options(options))
|
|
29
34
|
end
|
|
30
35
|
|
|
31
36
|
def to_latex(options:)
|
|
32
|
-
|
|
37
|
+
Formatter::Numbers::TextRenderer.render(
|
|
38
|
+
format_value_with_options(options), :latex
|
|
39
|
+
)
|
|
33
40
|
end
|
|
34
41
|
|
|
35
42
|
def to_html(options:)
|
|
36
|
-
|
|
43
|
+
Formatter::Numbers::TextRenderer.render(
|
|
44
|
+
format_value_with_options(options), :html
|
|
45
|
+
)
|
|
37
46
|
end
|
|
38
47
|
|
|
39
48
|
def to_omml_without_math_tag(_, options:)
|
|
40
|
-
[
|
|
49
|
+
[Formatter::Numbers::OmmlRenderer.render(format_value_with_options(options))]
|
|
41
50
|
end
|
|
42
51
|
|
|
43
52
|
def to_unicodemath(options:)
|
|
44
53
|
return mini_sub if mini_sub_sized
|
|
45
54
|
return mini_sup if mini_sup_sized
|
|
46
55
|
|
|
47
|
-
|
|
56
|
+
Formatter::Numbers::TextRenderer.render(
|
|
57
|
+
format_value_with_options(options), :unicodemath
|
|
58
|
+
)
|
|
48
59
|
end
|
|
49
60
|
|
|
50
61
|
def insert_t_tag(_, options:)
|
|
@@ -59,17 +70,26 @@ module Plurimath
|
|
|
59
70
|
|
|
60
71
|
def t_tag(options:)
|
|
61
72
|
Utility.ox_element("t",
|
|
62
|
-
namespace: "m") <<
|
|
73
|
+
namespace: "m") << formatted_value(options)
|
|
63
74
|
end
|
|
64
75
|
|
|
65
76
|
def nary_attr_value(options:)
|
|
66
|
-
format_value_with_options(options)
|
|
77
|
+
format_value_with_options(options).to_s
|
|
67
78
|
end
|
|
68
79
|
|
|
69
80
|
def validate_function_formula
|
|
70
81
|
false
|
|
71
82
|
end
|
|
72
83
|
|
|
84
|
+
def evaluate(_evaluator)
|
|
85
|
+
raw_value = value.to_s
|
|
86
|
+
return raw_value.to_i if raw_value.match?(/\A[+-]?\d+\z/)
|
|
87
|
+
|
|
88
|
+
Float(raw_value)
|
|
89
|
+
rescue ArgumentError
|
|
90
|
+
raise Errors::Evaluation::UnsupportedExpressionError, "number `#{raw_value}`"
|
|
91
|
+
end
|
|
92
|
+
|
|
73
93
|
def mini_sized?
|
|
74
94
|
mini_sub_sized || mini_sup_sized
|
|
75
95
|
end
|
|
@@ -98,25 +118,16 @@ module Plurimath
|
|
|
98
118
|
end
|
|
99
119
|
return value unless formatter
|
|
100
120
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
elsif formatter.respond_to?(:localized_number)
|
|
106
|
-
formatter.localized_number(value.to_s,
|
|
107
|
-
**format_kwargs(options[:format]))
|
|
108
|
-
else
|
|
109
|
-
value
|
|
110
|
-
end
|
|
121
|
+
format = options[:format] || {}
|
|
122
|
+
format = format.merge(base: base) if base && !format.key?(:base)
|
|
123
|
+
|
|
124
|
+
formatter.format_number(options[:formula], self, format: format)
|
|
111
125
|
end
|
|
112
126
|
|
|
113
127
|
private
|
|
114
128
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
return {} unless format.is_a?(Hash) && !format.empty?
|
|
118
|
-
|
|
119
|
-
{ format: format }
|
|
129
|
+
def formatted_value(options)
|
|
130
|
+
format_value_with_options(options).to_s
|
|
120
131
|
end
|
|
121
132
|
end
|
|
122
133
|
end
|
|
@@ -10,7 +10,7 @@ module Plurimath
|
|
|
10
10
|
mathml: ["π", "𝜋"],
|
|
11
11
|
latex: ["pi", "uppi", "π", "𝜋"],
|
|
12
12
|
omml: ["π", "𝜋"],
|
|
13
|
-
html: ["π", "π", "𝜋"],
|
|
13
|
+
html: ["π", "π", "𝜋", "π", "𝜋"],
|
|
14
14
|
}.freeze
|
|
15
15
|
|
|
16
16
|
# output methods
|
|
@@ -37,6 +37,10 @@ module Plurimath
|
|
|
37
37
|
def to_html(**)
|
|
38
38
|
"π"
|
|
39
39
|
end
|
|
40
|
+
|
|
41
|
+
def reserved_constant
|
|
42
|
+
::Math::PI
|
|
43
|
+
end
|
|
40
44
|
end
|
|
41
45
|
end
|
|
42
46
|
end
|
|
@@ -108,6 +108,51 @@ module Plurimath
|
|
|
108
108
|
false
|
|
109
109
|
end
|
|
110
110
|
|
|
111
|
+
# Some parser paths emit basic operators as generic symbols instead
|
|
112
|
+
# of their semantic classes, and a hand-built Formula may hold any of
|
|
113
|
+
# them, so the generic symbol answers for every operator from its own
|
|
114
|
+
# value (parsed `+`/`^` arrive as Plus/Hat, so these are inert there).
|
|
115
|
+
def plus_operator?
|
|
116
|
+
value == "+"
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def minus_operator?
|
|
120
|
+
value == "-"
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def multiply_operator?
|
|
124
|
+
value == "*"
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def divide_operator?
|
|
128
|
+
value == "/"
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def power_operator?
|
|
132
|
+
value == "^"
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# A plain symbol names a variable through its `value`. Symbol
|
|
136
|
+
# subclasses (Pi, Plus, ...) carry no `value`, so they return nil here
|
|
137
|
+
# and are never treated as variables.
|
|
138
|
+
def variable_name
|
|
139
|
+
return if reserved_constant || value.nil? || value.empty? || operator?
|
|
140
|
+
|
|
141
|
+
value
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Symbol classes that represent constants (e.g. Pi) override
|
|
145
|
+
# #reserved_constant; everything else resolves through bindings.
|
|
146
|
+
def evaluate(evaluator)
|
|
147
|
+
constant = reserved_constant
|
|
148
|
+
return constant if constant
|
|
149
|
+
|
|
150
|
+
name = variable_name
|
|
151
|
+
evaluator.unsupported(self) unless name
|
|
152
|
+
|
|
153
|
+
evaluator.value_for(name)
|
|
154
|
+
end
|
|
155
|
+
|
|
111
156
|
def omml_nodes(_, options:)
|
|
112
157
|
Array(t_tag(options: options))
|
|
113
158
|
end
|
data/lib/plurimath/math.rb
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
module Plurimath
|
|
4
4
|
module Math
|
|
5
5
|
autoload :Core, "#{__dir__}/math/core"
|
|
6
|
+
autoload :Evaluation, "#{__dir__}/math/evaluation"
|
|
6
7
|
autoload :Formula, "#{__dir__}/math/formula"
|
|
7
8
|
autoload :Function, "#{__dir__}/math/function"
|
|
8
9
|
autoload :InvalidTypeError, "#{__dir__}/errors/invalid_type_error"
|
|
@@ -174,6 +174,24 @@ module Plurimath
|
|
|
174
174
|
"+": "+",
|
|
175
175
|
"-": "-",
|
|
176
176
|
}.freeze
|
|
177
|
+
# Over/under accent decorations. In MathML these are only ever expressed
|
|
178
|
+
# as <mover>/<munder> with a base (recovered from their diacritic
|
|
179
|
+
# CHARACTER), never as a bare <mi>/<mo> identifier word. So a bare token
|
|
180
|
+
# whose text is one of these names is a literal identifier (e.g. the unit
|
|
181
|
+
# "bar"), not the accent. See unitsml/unitsdb#123.
|
|
182
|
+
ACCENT_WORDS = %w[
|
|
183
|
+
bar overline ul hat vec tilde dot ddot obrace ubrace overleftrightarrow
|
|
184
|
+
].freeze
|
|
185
|
+
|
|
186
|
+
# True when +string+ is a bare word naming an over/under accent. Such
|
|
187
|
+
# tokens are literal identifiers in MathML (the accent itself is a
|
|
188
|
+
# diacritic character inside <mover>/<munder>), so callers must not
|
|
189
|
+
# promote them to accent operators. AsciiMath/LaTeX word forms
|
|
190
|
+
# (bar(x), \bar{x}) are parsed elsewhere and never reach this predicate.
|
|
191
|
+
def self.accent_word?(string)
|
|
192
|
+
ACCENT_WORDS.include?(string&.strip)
|
|
193
|
+
end
|
|
194
|
+
|
|
177
195
|
CLASSES = %w[
|
|
178
196
|
mathfrak
|
|
179
197
|
underset
|