1
3 require 'rational'
4 require 'forwardable'
5
6 class IntegerOverflow < ::StandardError; end
7 class ParseError < ::StandardError; end
8
9 class Component
10 @LLDN = nil
11 def Component.LLDN
12 @LLDN ||= defcomponentlist(
13 %w( ¡» °ì Æó »° »Í ¸Þ Ï» ¼· Ȭ ¶å ),
14 ['̵ÎÌÂç¿ô', 8],
15 ['ÉԲĻ׵Ä', 8],
16 ['Æáͳ¿', 8],
17 ['°¤ÁεÀ', 8],
18 ['¹±²Ïº»', 8],
19 ['¶Ë', 4],
20 ['ºÜ', 4],
21 ['˵', 4],
22 ['´Â', 4],
23 ['¹Â', 4],
24 ['¾÷', 4],
25 ['²Óͽ', 4],
26 ['Ô¶', 4],
27 ['µþ', 4],
28 ['Ãû', 4],
29 ['²¯', 4],
30 ['Ëü', 1],
31 ['Àé', 1],
32 ['É´', 1],
33 ['½½', 1],
34 [nil, 0]
35 )
36 end
37
38 def Component.defcomponentlist(nums, *deflist)
39 term = Terminator.new(nums)
40 list = deflist.inject(nil) {|_next, a| new(_next, *a) }
41 list.each do |c|
42 c.instance_eval {
43 pow = next_pow()
44 @branch = (pow > 1 ? list.take(pow) : term)
45 }
46 end
47 Head.new(list)
48 end
49
50 include Enumerable
51
52 def initialize(_next, unit, pow)
53 @next = _next
54 @unit = unit
55 @pow = pow
56 end
57
58 attr_reader :unit
59 attr_reader :pow
60 attr_reader :branch
61
62 def inspect
63 "#<Component #{@unit.to_s}>"
64 end
65
66 def next_pow
67 @next ? @next.pow : 8
68 end
69
70 def each(&block)
71 yield self
72 @next.each(&block) if @next
73 end
74
75 def length
76 inject(0) {|len, i| len + 1 }
77 end
78
79 def take(n)
80 if n - @pow > 0
81 then dup(@next.take(n - @pow))
82 else nil
83 end
84 end
85
86 def dup(_next = @next, branch = @branch)
87 obj = self.class.new(_next, @unit, @pow)
88 obj.instance_eval { @branch = branch }
89 obj
90 end
91
92 def chars_re
93 @chars_re ||= /#{Regexp.union(*chars())}+/
94 end
95
96 def chars
97 inject([]) {|set, c| set + [c.chars1] }.flatten.uniq.compact
98 end
99
100 def chars1
101 @branch.inject([@unit]) {|set, c| set + c.chars1 }
102 end
103
104 def parse(str)
105 if m = regexp().match(str)
106 then upnum(m.pre_match) + @branch.parse(m[1])
107 else upnum(str)
108 end
109 end
110
111 def upnum(s)
112 return 0 if s.empty?
113 raise IntegerOverflow, "integer too big" unless @next
114 @next.parse(s) * (10 ** @next.pow)
115 end
116 private :upnum
117
118 def regexp
119 @re ||= /(#{@branch.pattern})#{@unit ? '?' : ''}#{@unit.to_s}\z/
120 end
121 private :regexp
122
123 def pattern
124 (@next ? @next.pattern : '') + "(?:(#{@branch.pattern})?#{@unit.to_s})?"
125 end
126
127 def string_expr(n)
128 up, base = *n.divmod(10 ** next_pow())
129 upstr(up) + basestr(base)
130 end
131
132 def upstr(n)
133 unless @next
134 return '' if n == 0
135 raise IntegerOverflow, "integer too big"
136 end
137 @next.string_expr(n)
138 end
139 private :upstr
140
141 def basestr(n)
142 if n == 0
143 then ''
144 else "#{@branch.string_expr(n)}#{@unit}"
145 end
146 end
147 private :basestr
148 end
149
150 class Terminator
151 include Enumerable
152
153 def initialize(nums)
154 @nums = nums
155 @re = nil
156 end
157
158 def inspect
159 "#<TERM>"
160 end
161
162 def each
163 yield self
164 end
165
166 def chars1
167 @nums
168 end
169
170 def pattern
171 "[#{@nums.join()}]"
172 end
173
174 def parse(str)
175 return Rational(1) unless str
176 return Rational(1) if str.empty?
177 n = @nums.index(str) or
178 raise ParseError, "unexpected string: #{str.inspect}"
179 Rational(n)
180 end
181
182 def string_expr(n)
183 @nums[n]
184 end
185 end
186
187 class Head
188 def initialize(list)
189 @list = list
190 end
191
192 def inspect
193 "#<HEAD>"
194 end
195
196 extend Forwardable
197 def_delegator "@list", :chars_re
198 def_delegator "@list", :parse
199
200 def string_expr(n)
201 str = @list.string_expr(n.to_i.abs).gsub(/°ì(?=[ÀéÉ´½½])/, '')
202 str = '¡»' if str.empty?
203 (n.to_i < 0 ? 'Éé' : '') + str + (n.denominator==1 ? '' : ';')
204 end
205 end