class Reality::Measure::Unit

Constants

OP_REGEX
POWER_REGEX
UNICODE_SUPER
UNIT_REGEX

Attributes

unicode[RW]
components[R]

Public Class Methods

new(*components) click to toggle source
# File lib/reality/measure/unit.rb, line 53
def initialize(*components)
  @components = components.
    group_by{|sig, pow| sig}.
    map{|sig, cmps| [sig, cmps.map(&:last).inject(:+)]}.
    reject{|sig, pow| pow.zero?}
end
parse(str) click to toggle source
# File lib/reality/measure/unit.rb, line 15
def parse(str)
  return str if str.kind_of?(Unit)
  
  scanner = StringScanner.new(str)
  denom = false
  units = []
  
  loop do
    # (variable [power] operator) ....
    unit = scanner.scan(UNIT_REGEX) or fail("Variable expected at #{scanner.rest}")
    pow = scanner.scan(POWER_REGEX)
    units << [unit, parse_pow(pow, denom)]
    break if scanner.eos?

    op = scanner.scan(OP_REGEX) or fail("Operator expected at #{scanner.rest}")
    if op == '/'
      denom and fail("Second division at #{scanner.rest}")
      denom = true
    end
  end
  new(*units)
end
parse_pow(p, denom) click to toggle source
# File lib/reality/measure/unit.rb, line 38
def parse_pow(p, denom)
  res = case p
  when nil then 1
  when '²' then 2
  when '³' then 3
  when /^\^(\d+)$/ then $1.to_i
  else fail(ArgumentError, "Can't parse power #{p}")
  end

  denom ? -res : res
end

Public Instance Methods

*(other) click to toggle source
# File lib/reality/measure/unit.rb, line 72
def *(other)
  other.class == self.class or
    fail(TypeError, "Can't multiply #{self.class} by #{other.class}")

  self.class.new(*components, *other.components)
end
-@() click to toggle source
# File lib/reality/measure/unit.rb, line 68
def -@
  self.class.new(*components.map{|sig, pow| [sig, -pow]})
end
/(other) click to toggle source
# File lib/reality/measure/unit.rb, line 79
def /(other)
  other.class == self.class or
    fail(TypeError, "Can't divide #{self.class} by #{other.class}")

  self * -other
end
==(other) click to toggle source
# File lib/reality/measure/unit.rb, line 60
def ==(other)
  other.class == self.class && other.components == self.components
end
inspect() click to toggle source
# File lib/reality/measure/unit.rb, line 100
def inspect
  "#<#{self.class}(#{to_s})>"
end
scalar?() click to toggle source
# File lib/reality/measure/unit.rb, line 64
def scalar?
  components.empty?
end
to_s() click to toggle source
# File lib/reality/measure/unit.rb, line 86
def to_s
  num, denom = components.partition{|sig, pow| pow > 0}
  numerator = num.map{|sig, pow| "#{sig}#{power(pow)}"}.join(mul)
  denominator = denom.map{|sig, pow| "#{sig}#{power(pow)}"}.join(mul)
  case
  when numerator.empty?
    [1, denominator].join('/')
  when denominator.empty?
    numerator
  else
    [numerator, denominator].join('/')
  end
end

Private Instance Methods

mul() click to toggle source
# File lib/reality/measure/unit.rb, line 108
def mul
  self.class.unicode ? '·' : '*'
end
power(num) click to toggle source
# File lib/reality/measure/unit.rb, line 112
def power(num)
  num = num.abs
  case num
    when 0 then fail(ArgumentError, "0-power unit!")
    when 1 then ''
    when 2..3
      self.class.unicode ? UNICODE_SUPER.fetch(num) : "^#{num}"
    else "^#{num}"
  end
end