diff --git a/lib/unit/system.rb b/lib/unit/system.rb
index ccd57e7..3783b39 100644
--- a/lib/unit/system.rb
+++ b/lib/unit/system.rb
@@ -60,6 +60,30 @@ def validate_unit(units)
end
end
+ # Parse a unit expression into a unit definition: an array of
+ # [factor, unit, exponent] triples suitable for +Unit.new+ and
+ # +validate_unit+.
+ #
+ # The expression is tokenized (see +TOKENIZER+) and evaluated with a
+ # shunting-yard pass over +OPERATOR+, supporting multiplication
+ # (*, ·, or juxtaposition), division (/),
+ # exponentiation (^, **), grouping parentheses, and
+ # numeric literals.
+ #
+ # system.parse_unit("m/s^2") # => [[:one, 1, 1], [:meter, ..., 1], [:second, ..., -2]]
+ #
+ # An empty or whitespace-only expression has no tokens and yields the
+ # *dimensionless* unit [] — the same definition produced by
+ # Unit(1, "") — so conversion stays consistent with construction
+ # rather than returning +nil+.
+ #
+ # system.parse_unit("") # => []
+ # system.parse_unit(" ") # => []
+ #
+ # Unrecognized glyphs survive lexing (see +SYMBOL+) and fail loudly as an
+ # "Undefined unit" +TypeError+ during validation rather than being dropped.
+ #
+ # Raises +SyntaxError+ on unbalanced parentheses.
def parse_unit(expr)
stack, result, implicit_mul = [], [], false
expr.to_s.scan(TOKENIZER).each do |tok|
@@ -87,7 +111,7 @@ def parse_unit(expr)
end
end
compute(result, stack.pop) while !stack.empty?
- result.last
+ result.last || []
end
private
diff --git a/spec/error_spec.rb b/spec/error_spec.rb
index 71747e7..d0e55a1 100644
--- a/spec/error_spec.rb
+++ b/spec/error_spec.rb
@@ -26,4 +26,27 @@
end
end
+ describe "converting to an empty (dimensionless) unit string" do
+ it "treats #in(\"\") as the dimensionless unit" do
+ expect(Unit(1, "").in("")).to eq(Unit(1, ""))
+ end
+
+ it "treats #in(\" \") (whitespace-only) as the dimensionless unit" do
+ expect(Unit(1, "").in(" ")).to eq(Unit(1, ""))
+ end
+
+ it "treats #in!(\"\") as the dimensionless unit" do
+ expect(Unit(1, "").in!("")).to eq(Unit(1, ""))
+ end
+
+ it "treats #in!(\" \") (whitespace-only) as the dimensionless unit" do
+ expect(Unit(1, "").in!(" ")).to eq(Unit(1, ""))
+ end
+
+ it "raises a clean TypeError (not NoMethodError) from #in! for an incompatible unit" do
+ unit = Unit(5, "m/s")
+ expect { unit.in!("") }.to raise_error(TypeError)
+ end
+ end
+
end
diff --git a/spec/system_spec.rb b/spec/system_spec.rb
index c5bcd2b..32ed18b 100644
--- a/spec/system_spec.rb
+++ b/spec/system_spec.rb
@@ -172,4 +172,16 @@
expect(Unit(1, "MeV", system).unit).to eq(Unit(1, "megaelectronvolt", system).unit)
end
end
+
+ describe "#parse_unit with an empty or whitespace-only expression" do
+ before { system.load(:si) }
+
+ it "returns [] (the dimensionless unit) for an empty string" do
+ expect(system.parse_unit("")).to eq([])
+ end
+
+ it "returns [] (the dimensionless unit) for a whitespace-only string" do
+ expect(system.parse_unit(" ")).to eq([])
+ end
+ end
end