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/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