unitsml 0.6.9 → 0.6.11
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/lib/unitsml/errors/parg_load_error.rb +12 -0
- data/lib/unitsml/errors/quantity_errors.rb +10 -0
- data/lib/unitsml/errors.rb +5 -0
- data/lib/unitsml/formula.rb +1 -1
- data/lib/unitsml/opal/database_payload.rb +1 -1
- data/lib/unitsml/opal/payload_generator.rb +6 -1
- data/lib/unitsml/opal.rb +4 -3
- data/lib/unitsml/parg/tree_mapper.rb +151 -0
- data/lib/unitsml/parg.rb +7 -0
- data/lib/unitsml/parser.rb +44 -10
- data/lib/unitsml/quantity.rb +181 -0
- data/lib/unitsml/unitsdb/units.rb +6 -0
- data/lib/unitsml/version.rb +1 -1
- data/lib/unitsml.rb +3 -4
- data/unitsml.gemspec +1 -2
- metadata +21 -33
- data/lib/unitsml/intermediate_exp_rules.rb +0 -86
- data/lib/unitsml/parse.rb +0 -82
- data/lib/unitsml/transform.rb +0 -205
|
@@ -72,8 +72,13 @@ module Unitsml
|
|
|
72
72
|
end
|
|
73
73
|
end
|
|
74
74
|
|
|
75
|
+
# Keys sorted so the output is byte-identical regardless of the
|
|
76
|
+
# hash insertion order the unitsdb/lutaml load path happens to
|
|
77
|
+
# produce on a given Ruby or gem version.
|
|
75
78
|
def serialize_hash(value)
|
|
76
|
-
"{#{value.
|
|
79
|
+
"{#{value.sort_by do |k, _|
|
|
80
|
+
k.to_s
|
|
81
|
+
end.map { |k, v| "#{serialize(k)}=>#{serialize(v)}" }.join(',')}}"
|
|
77
82
|
end
|
|
78
83
|
|
|
79
84
|
def serialize_array(value)
|
data/lib/unitsml/opal.rb
CHANGED
|
@@ -36,6 +36,8 @@ require "unitsml/errors/base_error"
|
|
|
36
36
|
require "unitsml/errors/invalid_model_error"
|
|
37
37
|
require "unitsml/errors/opal_payload_not_bundled_error"
|
|
38
38
|
require "unitsml/errors/plurimath_load_error"
|
|
39
|
+
require "unitsml/errors/parg_load_error"
|
|
40
|
+
require "unitsml/errors/quantity_errors"
|
|
39
41
|
require "unitsml/errors/unsupported_payload_type_error"
|
|
40
42
|
|
|
41
43
|
require "unitsml/opal/database_payload"
|
|
@@ -87,15 +89,14 @@ require "unitsml/extender"
|
|
|
87
89
|
require "unitsml/fenced"
|
|
88
90
|
require "unitsml/fenced_numeric"
|
|
89
91
|
require "unitsml/formula"
|
|
90
|
-
require "unitsml/intermediate_exp_rules"
|
|
91
92
|
require "unitsml/mathml_helper"
|
|
92
93
|
require "unitsml/number"
|
|
93
|
-
require "unitsml/
|
|
94
|
+
require "unitsml/parg"
|
|
94
95
|
require "unitsml/parser"
|
|
96
|
+
require "unitsml/quantity"
|
|
95
97
|
require "unitsml/prefix"
|
|
96
98
|
require "unitsml/prefix_adapter"
|
|
97
99
|
require "unitsml/sqrt"
|
|
98
|
-
require "unitsml/transform"
|
|
99
100
|
require "unitsml/unit"
|
|
100
101
|
require "unitsml/utility"
|
|
101
102
|
require "unitsml/xml"
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Unitsml
|
|
4
|
+
module Parg
|
|
5
|
+
# Maps asciiunits artifact parse trees (capture-named hashes whose leaves
|
|
6
|
+
# are {value, offset, ...} slices) onto the same object model the Parslet
|
|
7
|
+
# Transform produces. Dispatch is on the exact key set of each node
|
|
8
|
+
# (sorted), bottom-up, mirroring rule-for-rule the Transform rules in
|
|
9
|
+
# unitsml/transform.rb.
|
|
10
|
+
class TreeMapper
|
|
11
|
+
def apply(tree)
|
|
12
|
+
map(normalize(tree))
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
# The artifact runtime hands back symbol-keyed hashes with parsanol
|
|
18
|
+
# Slice leaves; the mapper works on string keys and stringified leaves.
|
|
19
|
+
def normalize(node)
|
|
20
|
+
case node
|
|
21
|
+
when Hash then node.to_h { |key, value| [key.to_s, normalize(value)] }
|
|
22
|
+
when Array then node.map { |value| normalize(value) }
|
|
23
|
+
else node
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# The case below is a dispatch table mirroring unitsml/transform.rb
|
|
28
|
+
# rule-for-rule; splitting it would obscure the 1:1 parity contract.
|
|
29
|
+
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
30
|
+
def map(node)
|
|
31
|
+
return node unless node.is_a?(Hash)
|
|
32
|
+
|
|
33
|
+
node = node.transform_values do |value|
|
|
34
|
+
value.is_a?(Hash) ? map(value) : value
|
|
35
|
+
end
|
|
36
|
+
case node.keys.map(&:to_s).sort
|
|
37
|
+
when %w[dimension] then Dimension.new(text(node["dimension"]))
|
|
38
|
+
when %w[dimension extender power_numerator sequence]
|
|
39
|
+
Formula.new(
|
|
40
|
+
[Dimension.new(text(node["dimension"]), node["power_numerator"]),
|
|
41
|
+
Extender.new(text(node["extender"])),
|
|
42
|
+
node["sequence"]],
|
|
43
|
+
)
|
|
44
|
+
when %w[dimension extender sequence]
|
|
45
|
+
Formula.new(
|
|
46
|
+
[Dimension.new(text(node["dimension"])),
|
|
47
|
+
Extender.new(text(node["extender"])),
|
|
48
|
+
node["sequence"]],
|
|
49
|
+
)
|
|
50
|
+
when %w[dimension power_numerator]
|
|
51
|
+
Dimension.new(text(node["dimension"]), node["power_numerator"])
|
|
52
|
+
when %w[extender first_set second_set]
|
|
53
|
+
Formula.new(
|
|
54
|
+
[node["first_set"], Extender.new(text(node["extender"])),
|
|
55
|
+
node["second_set"]],
|
|
56
|
+
)
|
|
57
|
+
when %w[extender implicit_extended sequence]
|
|
58
|
+
Formula.new(
|
|
59
|
+
[node["implicit_extended"], Extender.new(text(node["extender"])),
|
|
60
|
+
node["sequence"]],
|
|
61
|
+
)
|
|
62
|
+
when %w[extender int_exp sequence]
|
|
63
|
+
Formula.new(
|
|
64
|
+
[node["int_exp"], Extender.new(text(node["extender"])),
|
|
65
|
+
node["sequence"]],
|
|
66
|
+
)
|
|
67
|
+
when %w[extender power_numerator sequence unit]
|
|
68
|
+
Formula.new(
|
|
69
|
+
[Unit.new(text(node["unit"]), node["power_numerator"]),
|
|
70
|
+
Extender.new(text(node["extender"])),
|
|
71
|
+
node["sequence"]],
|
|
72
|
+
)
|
|
73
|
+
when %w[extender power_numerator prefix sequence unit]
|
|
74
|
+
Formula.new(
|
|
75
|
+
[Unit.new(text(node["unit"]), node["power_numerator"],
|
|
76
|
+
prefix: Prefix.new(text(node["prefix"]))),
|
|
77
|
+
Extender.new(text(node["extender"])),
|
|
78
|
+
node["sequence"]],
|
|
79
|
+
)
|
|
80
|
+
when %w[extender prefix sequence unit]
|
|
81
|
+
Formula.new(
|
|
82
|
+
[Unit.new(text(node["unit"]),
|
|
83
|
+
prefix: Prefix.new(text(node["prefix"]))),
|
|
84
|
+
Extender.new(text(node["extender"])),
|
|
85
|
+
node["sequence"]],
|
|
86
|
+
)
|
|
87
|
+
when %w[extender sequence unit]
|
|
88
|
+
Formula.new(
|
|
89
|
+
[Unit.new(text(node["unit"])),
|
|
90
|
+
Extender.new(text(node["extender"])),
|
|
91
|
+
node["sequence"]],
|
|
92
|
+
)
|
|
93
|
+
when %w[first_int_exp_set second_int_exp_set]
|
|
94
|
+
Formula.new(
|
|
95
|
+
[set_to_fence(node["first_int_exp_set"]),
|
|
96
|
+
set_to_fence(node["second_int_exp_set"])],
|
|
97
|
+
)
|
|
98
|
+
when %w[first_int_exp_set second_set]
|
|
99
|
+
Formula.new(
|
|
100
|
+
[set_to_fence(node["first_int_exp_set"]), node["second_set"]],
|
|
101
|
+
)
|
|
102
|
+
when %w[first_set second_int_exp_set]
|
|
103
|
+
Formula.new(
|
|
104
|
+
[node["first_set"], set_to_fence(node["second_int_exp_set"])],
|
|
105
|
+
)
|
|
106
|
+
when %w[first_set second_set]
|
|
107
|
+
Formula.new([node["first_set"], node["second_set"]])
|
|
108
|
+
when %w[implicit_extended] then node["implicit_extended"]
|
|
109
|
+
when %w[int_exp] then node["int_exp"]
|
|
110
|
+
when %w[int_exp sequence]
|
|
111
|
+
Formula.new([node["int_exp"], node["sequence"]])
|
|
112
|
+
when %w[integer] then Number.new(text(node["integer"]))
|
|
113
|
+
when %w[close_paren integer open_paren]
|
|
114
|
+
Fenced.new(text(node["open_paren"]),
|
|
115
|
+
Number.new(text(node["integer"])),
|
|
116
|
+
text(node["close_paren"]))
|
|
117
|
+
when %w[close_paren int_exp open_paren]
|
|
118
|
+
Fenced.new(text(node["open_paren"]), node["int_exp"],
|
|
119
|
+
text(node["close_paren"]))
|
|
120
|
+
when %w[power_numerator unit]
|
|
121
|
+
Unit.new(text(node["unit"]), node["power_numerator"])
|
|
122
|
+
when %w[power_numerator prefix unit]
|
|
123
|
+
Unit.new(text(node["unit"]), node["power_numerator"],
|
|
124
|
+
prefix: Prefix.new(text(node["prefix"])))
|
|
125
|
+
when %w[prefix] then Prefix.new(text(node["prefix"]))
|
|
126
|
+
when %w[prefix unit]
|
|
127
|
+
Unit.new(text(node["unit"]), prefix: Prefix.new(text(node["prefix"])))
|
|
128
|
+
when %w[sqrt] then Sqrt.new(node["sqrt"])
|
|
129
|
+
when %w[unit] then Unit.new(text(node["unit"]))
|
|
130
|
+
else
|
|
131
|
+
# The Parslet Transform leaves unmatched key combinations as raw
|
|
132
|
+
# hashes with symbol keys (e.g. {sqrt:, sequence:}); mirror that.
|
|
133
|
+
# Terminal slices (identifiable by their "value" key) keep their
|
|
134
|
+
# string keys.
|
|
135
|
+
node.key?("value") ? node : node.transform_keys(&:to_sym)
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def text(slice)
|
|
140
|
+
slice.is_a?(Hash) ? slice.fetch("value") : slice.to_s
|
|
141
|
+
end
|
|
142
|
+
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
143
|
+
|
|
144
|
+
def set_to_fence(set)
|
|
145
|
+
return set if set.is_a?(Fenced)
|
|
146
|
+
|
|
147
|
+
Fenced.new("(", set, ")")
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
data/lib/unitsml/parg.rb
ADDED
data/lib/unitsml/parser.rb
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Unitsml
|
|
4
|
+
# Parses AsciiUnits expressions through the asciiunits PARG artifact
|
|
5
|
+
# (github.com/unitsml/asciiunits) into the Formula object model.
|
|
4
6
|
class Parser
|
|
5
7
|
attr_accessor :text
|
|
6
8
|
|
|
@@ -13,21 +15,42 @@ module Unitsml
|
|
|
13
15
|
end
|
|
14
16
|
|
|
15
17
|
def parse
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
formula = Formula.new(
|
|
20
|
-
formula_value,
|
|
21
|
-
explicit_value: @extras_hash,
|
|
22
|
-
root: true,
|
|
23
|
-
orig_text: @orig_text,
|
|
24
|
-
norm_text: text,
|
|
25
|
-
)
|
|
18
|
+
tree = self.class.artifact.parse("expression", text)
|
|
19
|
+
mapped = Parg::TreeMapper.new.apply(tree)
|
|
20
|
+
formula = build_formula(mapped)
|
|
26
21
|
update_units_exponents(formula.value, false)
|
|
27
22
|
formula.value.first.only_instance = true if text.end_with?("-")
|
|
28
23
|
formula
|
|
29
24
|
end
|
|
30
25
|
|
|
26
|
+
class << self
|
|
27
|
+
def artifact
|
|
28
|
+
@artifact ||= load_artifact
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def load_artifact
|
|
34
|
+
if RUBY_ENGINE == "opal"
|
|
35
|
+
raise Errors::PargLoadError.new(
|
|
36
|
+
"the asciiunits gem",
|
|
37
|
+
"the parsanol runtime is not available under Opal; " \
|
|
38
|
+
"use a runtime with native code access",
|
|
39
|
+
)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Opal's builder statically scans literal requires; neither
|
|
43
|
+
# asciiunits nor parsanol has an Opal build, so keep the requires
|
|
44
|
+
# dynamic (this line never runs under Opal — the RUBY_ENGINE guard
|
|
45
|
+
# above raises first).
|
|
46
|
+
data_gem = "asciiunits"
|
|
47
|
+
require data_gem
|
|
48
|
+
Asciiunits.load_artifact
|
|
49
|
+
rescue LoadError => e
|
|
50
|
+
raise Errors::PargLoadError.new("the asciiunits gem", e.message)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
31
54
|
def update_units_exponents(array, inverse, sqrt = false)
|
|
32
55
|
array.each do |object|
|
|
33
56
|
if object.is_a?(Sqrt)
|
|
@@ -71,6 +94,17 @@ module Unitsml
|
|
|
71
94
|
|
|
72
95
|
private
|
|
73
96
|
|
|
97
|
+
def build_formula(mapped)
|
|
98
|
+
formula_value = mapped.is_a?(Formula) ? mapped.value : [mapped].flatten
|
|
99
|
+
Formula.new(
|
|
100
|
+
formula_value,
|
|
101
|
+
explicit_value: @extras_hash,
|
|
102
|
+
root: true,
|
|
103
|
+
orig_text: @orig_text,
|
|
104
|
+
norm_text: text,
|
|
105
|
+
)
|
|
106
|
+
end
|
|
107
|
+
|
|
74
108
|
def extract_equation(text)
|
|
75
109
|
return text unless text&.start_with?("unitsml(")
|
|
76
110
|
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Unitsml
|
|
4
|
+
# A quantity: a magnitude plus a unit identity, with unit-aware
|
|
5
|
+
# arithmetic (unitsml-ruby#82 R-A1/A2/A4-skeleton/A5).
|
|
6
|
+
#
|
|
7
|
+
# Dimensions come from the parsed unit expression via UnitsDB; every
|
|
8
|
+
# operation checks them. Scale legality (R-A4) gates multiplication
|
|
9
|
+
# and division on non-ratio scales; the classification is pluggable
|
|
10
|
+
# per quantity until the unitsdb scale-legality data lands (#130).
|
|
11
|
+
class Quantity
|
|
12
|
+
attr_reader :value, :formula, :scale
|
|
13
|
+
|
|
14
|
+
def initialize(value:, unit_expression: nil, scale: :ratio)
|
|
15
|
+
@value = value
|
|
16
|
+
@formula = unit_expression && Unitsml.parse(unit_expression)
|
|
17
|
+
@scale = scale.to_sym
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Canonical dimension identity; "1" (dimensionless) for pure numbers.
|
|
21
|
+
def dimension_id
|
|
22
|
+
@dimension_id ||= begin
|
|
23
|
+
list = dimension_list
|
|
24
|
+
list && (Utility.dim_id(list) || "1")
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Base-dimension exponents summed across the composed expression —
|
|
29
|
+
# V (L²MT⁻³I⁻¹) times A (I¹) merges to the watt vector L²MT⁻³.
|
|
30
|
+
def dimension_vector
|
|
31
|
+
vector = raw_dimension_list.each_with_object(Hash.new(0)) do |entry, acc|
|
|
32
|
+
exponent = entry[:exponent]
|
|
33
|
+
exponent = exponent.raw_value if exponent.respond_to?(:raw_value)
|
|
34
|
+
acc[entry[:dimension]] += exponent.to_i
|
|
35
|
+
end
|
|
36
|
+
vector.delete_if { |_, exponent| exponent.zero? }
|
|
37
|
+
vector
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Normalized unit expression text; the value's to_s for pure numbers.
|
|
41
|
+
def norm_text
|
|
42
|
+
formula&.norm_text || value.to_s
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def *(other)
|
|
46
|
+
if other.is_a?(Numeric)
|
|
47
|
+
gate_scaling!
|
|
48
|
+
return Quantity.new(value: value * other, unit_expression: norm_text)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
scale_ratio_gate!(other, "multiplication")
|
|
52
|
+
Quantity.new(value: value * other.value,
|
|
53
|
+
unit_expression: compose(self, other, "*"))
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# rubocop:disable Metrics/AbcSize
|
|
57
|
+
def /(other)
|
|
58
|
+
if other.is_a?(Numeric)
|
|
59
|
+
gate_zero!(other)
|
|
60
|
+
gate_scaling!
|
|
61
|
+
return Quantity.new(value: value / other, unit_expression: norm_text)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
scale_ratio_gate!(other, "division")
|
|
65
|
+
if other.value.zero?
|
|
66
|
+
raise Errors::IndeterminateResultError,
|
|
67
|
+
"division by a zero quantity is indeterminate"
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
Quantity.new(value: value / other.value,
|
|
71
|
+
unit_expression: compose(self, other, "/"))
|
|
72
|
+
end
|
|
73
|
+
# rubocop:enable Metrics/AbcSize
|
|
74
|
+
|
|
75
|
+
# R-A4: differences are legal on both ratio and interval scales;
|
|
76
|
+
# only the dimension identity must match.
|
|
77
|
+
def +(other)
|
|
78
|
+
quantity_kind!(other)
|
|
79
|
+
gate_dimension!(other)
|
|
80
|
+
Quantity.new(value: value + other.value, unit_expression: norm_text)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def -(other)
|
|
84
|
+
quantity_kind!(other)
|
|
85
|
+
gate_dimension!(other)
|
|
86
|
+
Quantity.new(value: value - other.value, unit_expression: norm_text)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def ==(other)
|
|
90
|
+
other.is_a?(Quantity) && value == other.value &&
|
|
91
|
+
dimension_vector == other.dimension_vector
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
private
|
|
95
|
+
|
|
96
|
+
# Base dimensions in canonical DIMS_VECTOR order; nil when all
|
|
97
|
+
# exponents cancel (dimensionless).
|
|
98
|
+
def dimension_list
|
|
99
|
+
merged = Utility::DIMS_VECTOR.filter_map do |dimension|
|
|
100
|
+
exponent = dimension_vector[dimension]
|
|
101
|
+
next if exponent.zero?
|
|
102
|
+
|
|
103
|
+
{ dimension: dimension, exponent: exponent }
|
|
104
|
+
end
|
|
105
|
+
merged.empty? ? nil : merged
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def raw_dimension_list
|
|
109
|
+
return [] unless formula
|
|
110
|
+
|
|
111
|
+
Utility.units2dimensions(extract_units(formula.value))
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Formula#units renders XML; the dimension identity needs the raw
|
|
115
|
+
# decomposition, so extract it the same way Formula does internally.
|
|
116
|
+
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity
|
|
117
|
+
def extract_units(formula)
|
|
118
|
+
formula.each_with_object([]) do |term, units_arr|
|
|
119
|
+
case term
|
|
120
|
+
when Unit
|
|
121
|
+
units_arr << term.dup
|
|
122
|
+
when Formula
|
|
123
|
+
units_arr.concat(extract_units(term.value.dup))
|
|
124
|
+
when Sqrt
|
|
125
|
+
next unless term.value.is_a?(Unit)
|
|
126
|
+
|
|
127
|
+
sqrt_unit = term.value.dup
|
|
128
|
+
sqrt_unit.power_numerator = Number.new("0.5")
|
|
129
|
+
units_arr << sqrt_unit
|
|
130
|
+
when Fenced
|
|
131
|
+
units_arr.concat(extract_units(term.value.value.dup))
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity
|
|
136
|
+
|
|
137
|
+
# R-A4: ratio-scale operations are specified errors on non-ratio
|
|
138
|
+
# quantities. Differences and comparisons remain legal on both.
|
|
139
|
+
# Scalar multiplication/division scales the magnitude: legal on
|
|
140
|
+
# ratio scales, a specified error on non-ratio ones.
|
|
141
|
+
def gate_scaling!
|
|
142
|
+
return if scale == :ratio
|
|
143
|
+
|
|
144
|
+
raise Errors::ScaleOperationError,
|
|
145
|
+
"scaling is not legal on a #{scale} scale quantity"
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def scale_ratio_gate!(other, operation)
|
|
149
|
+
return if scale == :ratio && other.scale == :ratio
|
|
150
|
+
|
|
151
|
+
raise Errors::ScaleOperationError,
|
|
152
|
+
"#{operation} is not legal between #{scale} and " \
|
|
153
|
+
"#{other.scale} scale quantities"
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def quantity_kind!(other)
|
|
157
|
+
unless other.is_a?(Quantity)
|
|
158
|
+
raise ArgumentError, "expected a Unitsml::Quantity, got #{other.class}"
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def gate_dimension!(other)
|
|
163
|
+
return if dimension_vector == other.dimension_vector
|
|
164
|
+
|
|
165
|
+
raise Errors::DimensionMismatchError,
|
|
166
|
+
"dimension mismatch: #{dimension_vector.inspect} " \
|
|
167
|
+
"vs #{other.dimension_vector.inspect}"
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def gate_zero!(other)
|
|
171
|
+
return unless other.zero?
|
|
172
|
+
|
|
173
|
+
raise Errors::IndeterminateResultError,
|
|
174
|
+
"division by a zero quantity is indeterminate"
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def compose(left, right, operator)
|
|
178
|
+
"#{left.norm_text}#{operator}#{right.norm_text}"
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
end
|
|
@@ -19,6 +19,12 @@ module Unitsml
|
|
|
19
19
|
end
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
+
def prefixed_symbols
|
|
23
|
+
@prefixed_symbols ||= symbols_ids.select do |unit|
|
|
24
|
+
find_by_symbol_id(unit).prefixed && !%r{\*|\^|/|^1$}.match?(unit)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
22
28
|
def find_by_symbol_id(sym_id)
|
|
23
29
|
symbols_hash[sym_id]
|
|
24
30
|
end
|
data/lib/unitsml/version.rb
CHANGED
data/lib/unitsml.rb
CHANGED
|
@@ -13,24 +13,23 @@ module Unitsml
|
|
|
13
13
|
autoload :Fenced, "unitsml/fenced"
|
|
14
14
|
autoload :FencedNumeric, "unitsml/fenced_numeric"
|
|
15
15
|
autoload :Formula, "unitsml/formula"
|
|
16
|
-
autoload :IntermediateExpRules, "unitsml/intermediate_exp_rules"
|
|
17
16
|
autoload :MathmlHelper, "unitsml/mathml_helper"
|
|
18
17
|
autoload :Model, "unitsml/model"
|
|
19
18
|
autoload :Namespace, "unitsml/namespace"
|
|
20
19
|
autoload :Number, "unitsml/number"
|
|
21
|
-
autoload :
|
|
20
|
+
autoload :Parg, "unitsml/parg"
|
|
22
21
|
autoload :Parser, "unitsml/parser"
|
|
23
22
|
autoload :Prefix, "unitsml/prefix"
|
|
24
23
|
autoload :PrefixAdapter, "unitsml/prefix_adapter"
|
|
25
24
|
autoload :Sqrt, "unitsml/sqrt"
|
|
26
|
-
autoload :Transform, "unitsml/transform"
|
|
27
25
|
autoload :Unit, "unitsml/unit"
|
|
28
26
|
autoload :Unitsdb, "unitsml/unitsdb"
|
|
29
27
|
autoload :Utility, "unitsml/utility"
|
|
30
28
|
autoload :VERSION, "unitsml/version"
|
|
29
|
+
autoload :Quantity, "unitsml/quantity"
|
|
31
30
|
autoload :Xml, "unitsml/xml"
|
|
32
31
|
|
|
33
32
|
def parse(string)
|
|
34
|
-
|
|
33
|
+
Parser.new(string).parse
|
|
35
34
|
end
|
|
36
35
|
end
|
data/unitsml.gemspec
CHANGED
|
@@ -32,10 +32,9 @@ Gem::Specification.new do |spec|
|
|
|
32
32
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
33
33
|
spec.require_paths = ["lib"]
|
|
34
34
|
|
|
35
|
+
spec.add_dependency "asciiunits", "~> 0.1"
|
|
35
36
|
spec.add_dependency "htmlentities"
|
|
36
37
|
spec.add_dependency "lutaml-model", "~> 0.8.0"
|
|
37
38
|
spec.add_dependency "mml", "~> 2.4.0"
|
|
38
|
-
spec.add_dependency "ox"
|
|
39
|
-
spec.add_dependency "parslet"
|
|
40
39
|
spec.add_dependency "unitsdb", "~> 2.2.5"
|
|
41
40
|
end
|
metadata
CHANGED
|
@@ -1,15 +1,29 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: unitsml
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.6.
|
|
4
|
+
version: 0.6.11
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-10-24 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: asciiunits
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0.1'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '0.1'
|
|
13
27
|
- !ruby/object:Gem::Dependency
|
|
14
28
|
name: htmlentities
|
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -52,34 +66,6 @@ dependencies:
|
|
|
52
66
|
- - "~>"
|
|
53
67
|
- !ruby/object:Gem::Version
|
|
54
68
|
version: 2.4.0
|
|
55
|
-
- !ruby/object:Gem::Dependency
|
|
56
|
-
name: ox
|
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
|
58
|
-
requirements:
|
|
59
|
-
- - ">="
|
|
60
|
-
- !ruby/object:Gem::Version
|
|
61
|
-
version: '0'
|
|
62
|
-
type: :runtime
|
|
63
|
-
prerelease: false
|
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
-
requirements:
|
|
66
|
-
- - ">="
|
|
67
|
-
- !ruby/object:Gem::Version
|
|
68
|
-
version: '0'
|
|
69
|
-
- !ruby/object:Gem::Dependency
|
|
70
|
-
name: parslet
|
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
|
72
|
-
requirements:
|
|
73
|
-
- - ">="
|
|
74
|
-
- !ruby/object:Gem::Version
|
|
75
|
-
version: '0'
|
|
76
|
-
type: :runtime
|
|
77
|
-
prerelease: false
|
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
-
requirements:
|
|
80
|
-
- - ">="
|
|
81
|
-
- !ruby/object:Gem::Version
|
|
82
|
-
version: '0'
|
|
83
69
|
- !ruby/object:Gem::Dependency
|
|
84
70
|
name: unitsdb
|
|
85
71
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -127,13 +113,14 @@ files:
|
|
|
127
113
|
- lib/unitsml/errors/base_error.rb
|
|
128
114
|
- lib/unitsml/errors/invalid_model_error.rb
|
|
129
115
|
- lib/unitsml/errors/opal_payload_not_bundled_error.rb
|
|
116
|
+
- lib/unitsml/errors/parg_load_error.rb
|
|
130
117
|
- lib/unitsml/errors/plurimath_load_error.rb
|
|
118
|
+
- lib/unitsml/errors/quantity_errors.rb
|
|
131
119
|
- lib/unitsml/errors/unsupported_payload_type_error.rb
|
|
132
120
|
- lib/unitsml/extender.rb
|
|
133
121
|
- lib/unitsml/fenced.rb
|
|
134
122
|
- lib/unitsml/fenced_numeric.rb
|
|
135
123
|
- lib/unitsml/formula.rb
|
|
136
|
-
- lib/unitsml/intermediate_exp_rules.rb
|
|
137
124
|
- lib/unitsml/mathml_helper.rb
|
|
138
125
|
- lib/unitsml/model.rb
|
|
139
126
|
- lib/unitsml/model/dimension.rb
|
|
@@ -166,12 +153,13 @@ files:
|
|
|
166
153
|
- lib/unitsml/opal.rb
|
|
167
154
|
- lib/unitsml/opal/database_payload.rb
|
|
168
155
|
- lib/unitsml/opal/payload_generator.rb
|
|
169
|
-
- lib/unitsml/
|
|
156
|
+
- lib/unitsml/parg.rb
|
|
157
|
+
- lib/unitsml/parg/tree_mapper.rb
|
|
170
158
|
- lib/unitsml/parser.rb
|
|
171
159
|
- lib/unitsml/prefix.rb
|
|
172
160
|
- lib/unitsml/prefix_adapter.rb
|
|
161
|
+
- lib/unitsml/quantity.rb
|
|
173
162
|
- lib/unitsml/sqrt.rb
|
|
174
|
-
- lib/unitsml/transform.rb
|
|
175
163
|
- lib/unitsml/unit.rb
|
|
176
164
|
- lib/unitsml/unitsdb.rb
|
|
177
165
|
- lib/unitsml/unitsdb/database.rb
|