numru-units 1.7.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.
- data/ChangeLog +111 -0
- data/LICENSE.txt +34 -0
- data/Makefile +14 -0
- data/Rakefile +39 -0
- data/doc/Makefile +5 -0
- data/doc/units.html +241 -0
- data/doc/units.rd +232 -0
- data/install.rb +104 -0
- data/lib/numru/units.rb +3505 -0
- data/src/Makefile +35 -0
- data/src/dcunits.txt +212 -0
- data/src/lex.rb +434 -0
- data/src/makeutab.rb +87 -0
- data/src/mulnode.rb +150 -0
- data/src/namenode.rb +63 -0
- data/src/node.rb +210 -0
- data/src/numbernode.rb +53 -0
- data/src/pownode.rb +98 -0
- data/src/rules.rb +65 -0
- data/src/shiftnode.rb +63 -0
- data/src/test.rb +122 -0
- data/src/timenode.rb +136 -0
- data/src/units.racc +2728 -0
- data/src/units.rb +3505 -0
- data/src/units.rd +139 -0
- data/src/utab.rb +1456 -0
- metadata +96 -0
data/src/makeutab.rb
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
|
2
|
+
SI_ABBREV = {
|
3
|
+
'E' => 18, 'P' => 15, 'T' => 12,
|
4
|
+
'G' => 9, 'M' => 6, 'k' => 3,
|
5
|
+
'h' => 2, 'da' => 1,
|
6
|
+
'd' => -1, 'c' => -2,
|
7
|
+
'm' => -3, 'u' => -6, 'n' => -9,
|
8
|
+
'p' => -12, 'f' => -15, 'a' => -18,
|
9
|
+
}
|
10
|
+
|
11
|
+
SI_PREFIX = {
|
12
|
+
'exa' => 18, 'peta' => 15, 'tela' => 12,
|
13
|
+
'giga' => 9, 'mega' => 6, 'kilo' => 3,
|
14
|
+
'hecto' => 2, 'deca' => 1,
|
15
|
+
'deci' => -1, 'centi' => -2,
|
16
|
+
'milli' => -3, 'micro' => -6, 'nano' => -9,
|
17
|
+
'pico' => -12, 'femto' => -15, 'atto' => -18,
|
18
|
+
}
|
19
|
+
|
20
|
+
def plural(string)
|
21
|
+
case string
|
22
|
+
when /^([^_]+)(_.*)/
|
23
|
+
pre, post = $1, $2
|
24
|
+
plural(pre) + post
|
25
|
+
when /[szoj]$/
|
26
|
+
string.sub(/$/, "es")
|
27
|
+
when /[^aeou]y$/
|
28
|
+
string.sub(/y$/, "ies")
|
29
|
+
else
|
30
|
+
string.sub(/$/, "s")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
udefs = {}
|
35
|
+
ualiases = {}
|
36
|
+
uplurals = {}
|
37
|
+
|
38
|
+
while (line = gets)
|
39
|
+
next if /^#/ =~ line
|
40
|
+
case line.strip
|
41
|
+
when /(\S+)\s+(\S)\s+(\S.*)/
|
42
|
+
name, mode, definition = $1, $2, $3
|
43
|
+
udefs[name] = definition
|
44
|
+
when /(\S+)\s+(\S)/
|
45
|
+
name, mode = $1, $2
|
46
|
+
else
|
47
|
+
next
|
48
|
+
end
|
49
|
+
case mode
|
50
|
+
when /^S/
|
51
|
+
for prefix, power in SI_ABBREV
|
52
|
+
next if prefix + name == 'kg'
|
53
|
+
ualiases[prefix + name] = [power, name]
|
54
|
+
end
|
55
|
+
when /^P/
|
56
|
+
pname = plural(name)
|
57
|
+
ualiases[pname] = [0, name]
|
58
|
+
uplurals[pname] = name
|
59
|
+
for prefix, power in SI_PREFIX
|
60
|
+
ualiases[prefix + name] = [power, name]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def dumphash(hname, h)
|
66
|
+
puts "#{hname} = {"
|
67
|
+
s = ""
|
68
|
+
for name in h.keys.sort
|
69
|
+
a = " #{name.dump} => #{h[name].inspect},"
|
70
|
+
if s.length + a.length > 72
|
71
|
+
puts s
|
72
|
+
s = ""
|
73
|
+
end
|
74
|
+
s += a
|
75
|
+
end
|
76
|
+
if s.length > 0
|
77
|
+
puts s
|
78
|
+
s = ""
|
79
|
+
end
|
80
|
+
puts "}"
|
81
|
+
end
|
82
|
+
|
83
|
+
puts "class NameNode"
|
84
|
+
dumphash('UDEFS', udefs)
|
85
|
+
dumphash('UALIASES', ualiases)
|
86
|
+
dumphash('UPLURALS', uplurals)
|
87
|
+
puts "end"
|
data/src/mulnode.rb
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
|
2
|
+
module Kakezan
|
3
|
+
|
4
|
+
def flatten2
|
5
|
+
r = MultiNode.new()
|
6
|
+
each do |child|
|
7
|
+
case child
|
8
|
+
when MultiNode
|
9
|
+
r.append child
|
10
|
+
when MulNode
|
11
|
+
r.append child.flatten2
|
12
|
+
when ContainerNode
|
13
|
+
r.append child.flatten2
|
14
|
+
else
|
15
|
+
r.append child
|
16
|
+
end
|
17
|
+
end
|
18
|
+
r
|
19
|
+
end
|
20
|
+
|
21
|
+
def name
|
22
|
+
n = nil
|
23
|
+
for c in @children
|
24
|
+
next if NumberNode === c
|
25
|
+
na = c.name
|
26
|
+
if n.nil?
|
27
|
+
n = na
|
28
|
+
else
|
29
|
+
raise "multiple names found" if na != n
|
30
|
+
end
|
31
|
+
end
|
32
|
+
n = "1" if n.nil?
|
33
|
+
n
|
34
|
+
end
|
35
|
+
|
36
|
+
def factor
|
37
|
+
f = 1
|
38
|
+
for c in @children
|
39
|
+
f *= c.factor
|
40
|
+
end
|
41
|
+
f
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
class MulNode < ContainerNode
|
47
|
+
|
48
|
+
include BinaryNode
|
49
|
+
include Kakezan
|
50
|
+
|
51
|
+
def initialize(lhs, rhs)
|
52
|
+
@lhs, @rhs = lhs, rhs
|
53
|
+
end
|
54
|
+
|
55
|
+
def to_s
|
56
|
+
lhs = @lhs.to_s
|
57
|
+
rhs = @rhs.to_s
|
58
|
+
if (/\d$/ =~ lhs && /^\w/ =~ rhs) then
|
59
|
+
"#{lhs} #{rhs}"
|
60
|
+
else
|
61
|
+
"#{lhs}.#{rhs}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
class MultiNode < ContainerNode
|
68
|
+
|
69
|
+
include Kakezan
|
70
|
+
|
71
|
+
def initialize(*children)
|
72
|
+
@children = children
|
73
|
+
for c in @children
|
74
|
+
raise "# MultiNode.new(#{children.inspect})" unless Node === c
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def to_s
|
79
|
+
s = @children.join(';')
|
80
|
+
s.gsub(/\d;\w/) { |dsw| dsw.sub(/;/, ' ') }.gsub(/;/, '.')
|
81
|
+
end
|
82
|
+
|
83
|
+
def each
|
84
|
+
@children.each {|child| yield child }
|
85
|
+
end
|
86
|
+
|
87
|
+
attr_reader :children
|
88
|
+
|
89
|
+
def append(other)
|
90
|
+
case other
|
91
|
+
when MultiNode
|
92
|
+
@children += other.children
|
93
|
+
else
|
94
|
+
@children.push other
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def sort
|
99
|
+
table = {}
|
100
|
+
for child in self
|
101
|
+
name = child.name
|
102
|
+
if (table.include?(name)) then
|
103
|
+
table[name] = table[name].mul_eval(child)
|
104
|
+
else
|
105
|
+
table[name] = child
|
106
|
+
end
|
107
|
+
end
|
108
|
+
list = []
|
109
|
+
for name in table.keys.sort
|
110
|
+
candi = table[name]
|
111
|
+
if PowNode === candi and NumberNode === candi.lhs then
|
112
|
+
v = candi.value
|
113
|
+
list.push NumberNode.new(v) unless v == 1
|
114
|
+
next
|
115
|
+
end
|
116
|
+
next if candi.power.value == 0
|
117
|
+
list.push candi
|
118
|
+
end
|
119
|
+
if list.length > 1
|
120
|
+
list.delete(NumberNode::UNITY)
|
121
|
+
end
|
122
|
+
self.class.new(*list)
|
123
|
+
end
|
124
|
+
|
125
|
+
def collect_hash(stopper, op)
|
126
|
+
list = []
|
127
|
+
for child in self
|
128
|
+
list.push(child.send(op, stopper))
|
129
|
+
end
|
130
|
+
self.class.new(*list).flatten2
|
131
|
+
end
|
132
|
+
|
133
|
+
def expand(stopper)
|
134
|
+
collect_hash(stopper, :expand)
|
135
|
+
end
|
136
|
+
|
137
|
+
def unalias(stopper)
|
138
|
+
collect_hash(stopper, :unalias)
|
139
|
+
end
|
140
|
+
|
141
|
+
def foldnumber(stopper)
|
142
|
+
collect_hash(stopper, :foldnumber)
|
143
|
+
end
|
144
|
+
|
145
|
+
def value
|
146
|
+
raise "this is dimensional units" if (@children.size > 1)
|
147
|
+
@children.first ? @children.first.value : NumberNode::UNITY.value
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
data/src/namenode.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
class NameNode < TerminalNode
|
2
|
+
|
3
|
+
def initialize(string)
|
4
|
+
@a = string
|
5
|
+
end
|
6
|
+
|
7
|
+
def to_s; @a; end
|
8
|
+
|
9
|
+
alias :name :to_s
|
10
|
+
|
11
|
+
def power; NumberNode::UNITY; end
|
12
|
+
|
13
|
+
def mul_eval(another)
|
14
|
+
raise "internal error (#{name}, #{another.name})" if name != another.name
|
15
|
+
PowNode.new(self, self.power.add_eval(another.power))
|
16
|
+
end
|
17
|
+
|
18
|
+
def expand(stopper)
|
19
|
+
raise "circular dependency for #{@a}" if stopper[@a]
|
20
|
+
return self if basic?
|
21
|
+
return CACHE[@a] if CACHE.include?(@a)
|
22
|
+
CACHE[@a] = expand2(stopper)
|
23
|
+
end
|
24
|
+
|
25
|
+
def expand2(stopper)
|
26
|
+
newstopper = stopper.dup
|
27
|
+
newstopper[@a] = true
|
28
|
+
if UDEFS.include?(@a) then
|
29
|
+
Units.new(UDEFS[@a]).ptree.expand(newstopper)
|
30
|
+
else
|
31
|
+
p, n = UALIASES[@a]
|
32
|
+
u = Units.new(UDEFS[n] || n).ptree.expand(newstopper)
|
33
|
+
MulNode.new(u, PowNode.new(NumberNode.new(10), NumberNode.new(p)))
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def unalias(stopper)
|
38
|
+
raise "circular dependency for #{@a}" if stopper[@a]
|
39
|
+
return self unless UALIASES.include?(@a)
|
40
|
+
p, n = UALIASES[@a]
|
41
|
+
u = NameNode.new(n)
|
42
|
+
q = PowNode.new(NumberNode.new(10), NumberNode.new(p))
|
43
|
+
MulNode.new(u, q)
|
44
|
+
end
|
45
|
+
|
46
|
+
def foldnumber(stopper)
|
47
|
+
return self unless UPLURALS.include?(@a)
|
48
|
+
n = UPLURALS[@a]
|
49
|
+
NameNode.new(n)
|
50
|
+
end
|
51
|
+
|
52
|
+
def basic?
|
53
|
+
not (UDEFS.include?(@a) or UALIASES.include?(@a))
|
54
|
+
end
|
55
|
+
|
56
|
+
CACHE = {}
|
57
|
+
|
58
|
+
def factor
|
59
|
+
1
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
data/src/node.rb
ADDED
@@ -0,0 +1,210 @@
|
|
1
|
+
=begin
|
2
|
+
= class Node
|
3
|
+
Node is a parent class for classes of parse tree node.
|
4
|
+
This is not expected to be instanciated directly.
|
5
|
+
=end
|
6
|
+
|
7
|
+
class Node
|
8
|
+
|
9
|
+
def initialize(*args)
|
10
|
+
raise "#{self.class} is virtual."
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_s(*args)
|
14
|
+
raise "#{self.class}#to_s is virtual."
|
15
|
+
end
|
16
|
+
|
17
|
+
=begin
|
18
|
+
--- pow other
|
19
|
+
simply constructs a PowNode object.
|
20
|
+
No reduction is performed.
|
21
|
+
=end
|
22
|
+
|
23
|
+
def pow(other)
|
24
|
+
PowNode.new(self, other)
|
25
|
+
end
|
26
|
+
|
27
|
+
=begin
|
28
|
+
--- mul other
|
29
|
+
simply constructs a MulNode object.
|
30
|
+
No reduction is performed.
|
31
|
+
=end
|
32
|
+
|
33
|
+
def mul(other)
|
34
|
+
other = NumberNode.new(other) if Numeric === other
|
35
|
+
MulNode.new(self, other)
|
36
|
+
end
|
37
|
+
|
38
|
+
=begin
|
39
|
+
--- divide other
|
40
|
+
simply constructs a MulNode object.
|
41
|
+
No reduction is performed.
|
42
|
+
=end
|
43
|
+
|
44
|
+
def divide(other)
|
45
|
+
MulNode.new(self, PowNode.new(other, NumberNode.new(-1)))
|
46
|
+
end
|
47
|
+
|
48
|
+
=begin
|
49
|
+
--- shift other
|
50
|
+
simply constructs a ShiftNode object.
|
51
|
+
No reduction is performed.
|
52
|
+
=end
|
53
|
+
|
54
|
+
def shift(other)
|
55
|
+
ShiftNode.new(self, other)
|
56
|
+
end
|
57
|
+
|
58
|
+
=begin
|
59
|
+
--- pow_eval other
|
60
|
+
similar to ((<pow other>)), but reduces PowNode[PowNode[...]] into
|
61
|
+
single PowNode[...], so overriden at PowNode class.
|
62
|
+
=end
|
63
|
+
|
64
|
+
def pow_eval(other)
|
65
|
+
pow(other)
|
66
|
+
end
|
67
|
+
|
68
|
+
=begin
|
69
|
+
--- inspect
|
70
|
+
=end
|
71
|
+
|
72
|
+
def inspect2; "#{self.class}[#{to_s}]"; end
|
73
|
+
|
74
|
+
def inspect; inspect2.gsub(/Units::/, '').gsub(/NumRu::/, '').gsub(/Node\[/, '['); end
|
75
|
+
|
76
|
+
=begin
|
77
|
+
--- trim
|
78
|
+
in most subclasses, "trim" is redirected into "trim2".
|
79
|
+
=end
|
80
|
+
|
81
|
+
def trim
|
82
|
+
trim2
|
83
|
+
end
|
84
|
+
|
85
|
+
=begin
|
86
|
+
--- flatten
|
87
|
+
in most subclasses, "flatten" is redirected into "flatten2".
|
88
|
+
=end
|
89
|
+
|
90
|
+
def flatten
|
91
|
+
flatten2
|
92
|
+
end
|
93
|
+
|
94
|
+
=begin
|
95
|
+
--- sort
|
96
|
+
=end
|
97
|
+
|
98
|
+
def sort
|
99
|
+
raise "#{self.class}#sort is virtual: call after flatten"
|
100
|
+
end
|
101
|
+
|
102
|
+
=begin
|
103
|
+
--- reduce1
|
104
|
+
--- reduce2
|
105
|
+
--- reduce3
|
106
|
+
--- reduce4
|
107
|
+
--- reduce5
|
108
|
+
=end
|
109
|
+
|
110
|
+
def reduce1
|
111
|
+
self
|
112
|
+
end
|
113
|
+
|
114
|
+
def reduce2
|
115
|
+
trim
|
116
|
+
end
|
117
|
+
|
118
|
+
def reduce3
|
119
|
+
trim.flatten
|
120
|
+
end
|
121
|
+
|
122
|
+
def reduce4
|
123
|
+
# unalias(Hash.new).trim.flatten.sort
|
124
|
+
foldnumber(nil).trim.flatten.sort
|
125
|
+
end
|
126
|
+
|
127
|
+
def reduce5
|
128
|
+
expand(Hash.new).trim.flatten.sort
|
129
|
+
end
|
130
|
+
|
131
|
+
=begin
|
132
|
+
--- ref
|
133
|
+
to be overriden at ShiftNode
|
134
|
+
--- deref
|
135
|
+
to be overriden at ShiftNode
|
136
|
+
=end
|
137
|
+
|
138
|
+
def ref
|
139
|
+
NumberNode::ZERO
|
140
|
+
end
|
141
|
+
|
142
|
+
def deref
|
143
|
+
self
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
147
|
+
|
148
|
+
class ErrorNode < Node
|
149
|
+
|
150
|
+
def initialize(string)
|
151
|
+
@a = string
|
152
|
+
end
|
153
|
+
|
154
|
+
def to_s; @a; end
|
155
|
+
|
156
|
+
end
|
157
|
+
|
158
|
+
class ContainerNode < Node
|
159
|
+
|
160
|
+
def trim2
|
161
|
+
x = []
|
162
|
+
for child in self
|
163
|
+
x.push child.trim2
|
164
|
+
end
|
165
|
+
self.class.new(*x)
|
166
|
+
end
|
167
|
+
|
168
|
+
def inspect2
|
169
|
+
a = []
|
170
|
+
for child in self
|
171
|
+
a.push child.inspect2
|
172
|
+
end
|
173
|
+
"#{self.class}[#{a.join(', ')}]"
|
174
|
+
end
|
175
|
+
|
176
|
+
end
|
177
|
+
|
178
|
+
module BinaryNode
|
179
|
+
|
180
|
+
def each
|
181
|
+
yield @lhs
|
182
|
+
yield @rhs
|
183
|
+
end
|
184
|
+
|
185
|
+
def expand(stopper)
|
186
|
+
self.class.new(@lhs.expand(stopper), @rhs.expand(stopper))
|
187
|
+
end
|
188
|
+
|
189
|
+
def unalias(stopper)
|
190
|
+
self.class.new(@lhs.unalias(stopper), @rhs.unalias(stopper))
|
191
|
+
end
|
192
|
+
|
193
|
+
def foldnumber(stopper)
|
194
|
+
self.class.new(@lhs.foldnumber(stopper), @rhs.foldnumber(stopper))
|
195
|
+
end
|
196
|
+
|
197
|
+
end
|
198
|
+
|
199
|
+
class TerminalNode < Node
|
200
|
+
|
201
|
+
def trim2; self; end
|
202
|
+
def flatten2; self; end
|
203
|
+
|
204
|
+
def expand(stopper); self; end
|
205
|
+
alias :unalias :expand
|
206
|
+
alias :foldnumber :expand
|
207
|
+
|
208
|
+
def sort; self; end
|
209
|
+
|
210
|
+
end
|