* tool/transcode-tblgen.rb: refactored.

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@26923 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
akr 2010-03-14 17:01:06 +00:00
Родитель f5ce5551c8
Коммит 1db141ed13
2 изменённых файлов: 252 добавлений и 228 удалений

Просмотреть файл

@ -1,3 +1,7 @@
Mon Mar 15 01:52:46 2010 Tanaka Akira <akr@fsij.org>
* tool/transcode-tblgen.rb: refactored.
Mon Mar 15 01:18:31 2010 Alexander Zavorine <alexandre.zavorine@nokia.com>
* symbian/setup (*.pkg): Ruby Core installation separated from standard extensions.

Просмотреть файл

@ -1,6 +1,23 @@
require 'optparse'
require 'erb'
require 'fileutils'
require 'pp'
class Array
unless [].respond_to? :product
def product(*args)
if args.empty?
self.map {|e| [e] }
else
result = []
self.each {|e0|
result.concat args.first.product(*args[1..-1]).map {|es| [e0, *es] }
}
result
end
end
end
end
NUM_ELEM_BYTELOOKUP = 2
@ -18,155 +35,7 @@ def c_esc(str)
'"' + str.gsub(C_ESC_PAT) { C_ESC[$&] } + '"'
end
HEX2 = /[0-9A-Fa-f]{2}/
class StrSet
attr_reader :pat
SINGLE_BYTE_RANGES = (0..255).map {|i| [i..i] }
def self.parse(pattern)
if /\A\s*((#{HEX2}|\{(#{HEX2}|#{HEX2}-#{HEX2})(,(#{HEX2}|#{HEX2}-#{HEX2}))*\})+(\s+|\z))*\z/o !~ pattern
raise ArgumentError, "invalid pattern: #{pattern.inspect}"
end
result = []
pattern.scan(/\S+/) {|seq|
seq_result = []
while !seq.empty?
if /\A(#{HEX2})/o =~ seq
byte = $1.to_i(16)
seq_result << SINGLE_BYTE_RANGES[byte]
seq = $'
elsif /\A\{([^\}]+)\}/ =~ seq
set = $1
seq = $'
set_result = []
set.scan(/[^,]+/) {|range|
if /\A(#{HEX2})-(#{HEX2})\z/o =~ range
b = $1.to_i(16)
e = $2.to_i(16)
set_result << (b..e)
elsif /\A(#{HEX2})\z/o =~ range
byte = $1.to_i(16)
set_result << (byte..byte)
else
raise "invalid range: #{range.inspect}"
end
}
seq_result << set_result
else
raise "invalid sequence: #{seq.inspect}"
end
end
result << seq_result
}
self.new(result)
end
def initialize(pat)
@pat = pat
end
def hash
return @hash if defined? @hash
@hash = @pat.hash
end
def eql?(other)
self.class == other.class &&
@pat == other.pat
end
alias == eql?
def to_s
if @pat.empty?
"(empset)"
else
@pat.map {|seq|
if seq.empty?
"(empstr)"
else
seq.map {|byteset|
if byteset.length == 1 && byteset[0].begin == byteset[0].end
"%02x" % byteset[0].begin
else
"{" +
byteset.map {|range|
if range.begin == range.end
"%02x" % range.begin
else
"%02x-%02x" % [range.begin, range.end]
end
}.join(',') +
"}"
end
}.join('')
end
}.join(' ')
end
end
def inspect
"\#<#{self.class}: #{self.to_s}>"
end
def min_length
if @pat.empty?
nil
else
@pat.map {|seq| seq.length }.min
end
end
def max_length
if @pat.empty?
nil
else
@pat.map {|seq| seq.length }.max
end
end
def emptyable?
@pat.any? {|seq|
seq.empty?
}
end
def has_nonempty?
@pat.any? {|seq|
!seq.empty?
}
end
def first_bytes
result = {}
@pat.each {|seq|
next if seq.empty?
seq.first.each {|range|
range.each {|byte|
result[byte] = true
}
}
}
result.keys.sort
end
def each_firstbyte
h = {}
@pat.each {|seq|
next if seq.empty?
seq.first.each {|range|
range.each {|byte|
(h[byte] ||= []) << seq[1..-1]
}
}
}
h.keys.sort.each {|byte|
yield byte, StrSet.new(h[byte])
}
end
end
HEX2 = /(?:[0-9A-Fa-f]{2})/
class ArrayCode
def initialize(type, name)
@ -195,98 +64,248 @@ End
end
end
class Action
def initialize(value)
@value = value
end
attr_reader :value
end
class ActionMap
def self.parse(hash)
def self.parse_to_rects(hash)
h = {}
hash.each {|pat, action|
h[StrSet.parse(pat)] = action
pat = pat.to_s
h[pat] = action
}
self.new(h)
hash = h
rects = []
n = 0
hash.each {|pat, action|
if /\A\s*\(empset\)\s*\z/ =~ pat
next
elsif /\A\s*\(empstr\)\s*\z/ =~ pat
rects << ['', '', action]
n += 1
elsif /\A\s*(#{HEX2}+)\s*\z/o =~ pat
hex = $1.upcase
rects << [hex, hex, action]
elsif /\A\s*((#{HEX2}|\{#{HEX2}(?:-#{HEX2})?(,#{HEX2}(?:-#{HEX2})?)*\})+(\s+|\z))*\z/o =~ pat
pat = pat.upcase
pat.scan(/\S+/) {
pat1 = $&
ranges_list = []
pat1.scan(/#{HEX2}|\{([^\}]*)\}/o) {
ranges_list << []
if !$1
ranges_list.last << [$&,$&]
else
set = {}
$1.scan(/(#{HEX2})(?:-(#{HEX2}))?/o) {
if !$2
c = $1.to_i(16)
set[c] = true
else
b = $1.to_i(16)
e = $2.to_i(16)
b.upto(e) {|c| set[c] = true }
end
}
i = nil
0.upto(256) {|j|
if set[j]
if !i
i = j
end
if !set[j+1]
ranges_list.last << ["%02X" % i, "%02X" % j]
i = nil
end
end
}
end
}
first_ranges = ranges_list.shift
first_ranges.product(*ranges_list).each {|range_list|
min = range_list.map {|x, y| x }.join
max = range_list.map {|x, y| y }.join
rects << [min, max, action]
}
}
else
raise ArgumentError, "invalid pattern: #{pat.inspect}"
end
}
rects
end
def initialize(h)
@map = h
def self.unambiguous_action(actions0)
actions = actions0.uniq
if actions.length == 1
actions[0]
else
actions = actions.find_all {|action| action != :nomap0 }
if actions.length == 1
actions[0]
else
raise ArgumentError, "ambiguous actions: #{actions0.inspect}"
end
end
end
def self.build_tree(rects)
expand("", rects) {|actions|
unambiguous_action(actions)
}
end
def self.parse(hash)
rects = parse_to_rects(hash)
tree = build_tree(rects)
self.new("", tree)
end
def self.merge(*rects_list)
if rects_list.length < 2
raise ArgumentError, "not enough arguments"
end
all_rects = []
rects_list.each_with_index {|rects, i|
all_rects.concat rects.map {|min, max, action| [min, max, [i, action]] }
}
tree = expand("", all_rects) {|actions|
args = Array.new(rects_list.length) { [] }
actions.each {|i, action|
args[i] << action
}
yield(args)
}
self.new("", tree)
end
def self.expand(prefix, rects, &block)
return [] if rects.empty?
has_empty = false
has_nonempty = false
rects.each {|min, max, action|
if min.empty?
has_empty = true
else
has_nonempty = true
end
}
if has_empty && has_nonempty
raise ArgumentError, "ambiguous pattern: #{prefix}"
end
if has_empty
actions = rects.map {|min, max, action| action }.uniq
act = block.call(actions)
tree = Action.new(act)
else
tree = []
each_firstbyte_range(prefix, rects) {|byte_min, byte_max, rects2|
prefix2 = prefix
if byte_min == byte_max
prefix2 += "%02X" % byte_min
else
prefix2 += "{%02X-%02X}" % [byte_min, byte_max]
end
child_tree = expand(prefix2, rects2, &block)
tree << [byte_min, byte_max, child_tree]
}
end
return tree
end
def self.each_firstbyte_range(prefix, rects)
a = []
index_from = {}
rects.each {|min, max, action|
raise ArgumentError, "emptyable pattern" if min.empty?
min_firstbyte = min[0,2].to_i(16)
min_rest = min[2..-1]
max_firstbyte = max[0,2].to_i(16)
max_rest = max[2..-1]
a << [min_firstbyte, max_firstbyte, [min_rest, max_rest, action]]
index_from[min_firstbyte] = true
index_from[max_firstbyte+1] = true
}
byte_from = {}
index_from.keys.sort.each_with_index {|byte, i|
index_from[byte] = i
byte_from[i] = byte
}
rects_hash = {}
a.each {|min_firstbyte, max_firstbyte, rest_elt|
index_from[min_firstbyte].upto(index_from[max_firstbyte+1]-1) {|i|
rects_hash[i] ||= []
rects_hash[i] << rest_elt
}
}
0.upto(index_from.size-1) {|i|
rects2 = rects_hash[i]
yield byte_from[i], byte_from[i+1]-1, rects2 if rects2
}
end
def initialize(prefix, tree)
@prefix = prefix # just for debug
@tree = tree
end
def hash
return @hash if defined? @hash
hash = 0
@map.each {|k,v|
hash ^= k.hash ^ v.hash
}
@hash = hash
@hash = @tree.hash
end
def eql?(other)
self.class == other.class &&
@map == other.instance_eval { @map }
@tree == other.instance_eval { @tree }
end
alias == eql?
def inspect
"\#<#{self.class}:" +
@map.map {|k, v| " [" + k.to_s + "]=>" + v.inspect }.join('') +
@tree.inspect +
">"
end
def max_input_length
@map.keys.map {|k| k.max_length }.max
def max_input_length_rec(tree)
case tree
when Action
0
else
tree.map {|byte_min, byte_max, child_tree|
max_input_length_rec(child_tree)
}.max + 1
end
end
def check_conflict
has_empty = false
has_nonempty = false
@map.each {|ss, action|
has_empty = true if ss.emptyable?
has_nonempty = true if ss.has_nonempty?
}
if has_empty && has_nonempty
raise "conflict between empty and nonempty sequence"
end
def max_input_length
max_input_length_rec(@tree)
end
def empty_action
@map.each {|ss, action|
return action if ss.emptyable?
}
nil
if @tree.kind_of? Action
@tree.value
else
nil
end
end
def each_firstbyte(valid_encoding=nil)
h = {}
@map.each {|ss, action|
if ss.emptyable?
raise "emptyable pattern"
else
ss.each_firstbyte {|byte, rest|
h[byte] ||= {}
if h[byte][rest].nil?
elsif action == :nomap0
next
elsif h[byte][rest] != :nomap0
raise "ambiguous %s or %s (%02X/%s)" % [h[byte][rest], action, byte, rest]
end
h[byte][rest] = action
}
end
def each_firstbyte
@tree.each {|byte_min, byte_max, child_tree|
byte_min.upto(byte_max) {|byte|
prefix = @prefix + ("%02X" % byte)
am = ActionMap.new(prefix, child_tree)
yield byte, am
}
}
if valid_encoding
valid_encoding.each_firstbyte {|byte, rest|
if h[byte]
am = ActionMap.new(h[byte])
yield byte, am, rest
else
am = ActionMap.new(rest => :undef)
yield byte, am, nil
end
}
else
h.keys.sort.each {|byte|
am = ActionMap.new(h[byte])
yield byte, am, nil
}
end
end
OffsetsMemo = {}
@ -451,25 +470,24 @@ End
PostMemo = {}
NextName = "a"
def generate_node(bytes_code, words_code, name_hint=nil, valid_encoding=nil)
if n = PreMemo[[self,valid_encoding]]
def generate_node(bytes_code, words_code, name_hint=nil)
if n = PreMemo[self]
return n
end
table = Array.new(0x100, :invalid)
each_firstbyte(valid_encoding) {|byte, rest, rest_valid_encoding|
rest.check_conflict
each_firstbyte {|byte, rest|
if a = rest.empty_action
table[byte] = a
else
name_hint2 = nil
name_hint2 = "#{name_hint}_#{'%02X' % byte}" if name_hint
table[byte] = "/*BYTE_LOOKUP*/" + rest.gennode(bytes_code, words_code, name_hint2, rest_valid_encoding)
table[byte] = "/*BYTE_LOOKUP*/" + rest.gennode(bytes_code, words_code, name_hint2)
end
}
if n = PostMemo[table]
return PreMemo[[self,valid_encoding]] = n
return PreMemo[self] = n
end
if !name_hint
@ -477,16 +495,16 @@ End
NextName.succ!
end
PreMemo[[self,valid_encoding]] = PostMemo[table] = name_hint
PreMemo[self] = PostMemo[table] = name_hint
generate_lookup_node(bytes_code, words_code, name_hint, table)
name_hint
end
def gennode(bytes_code, words_code, name_hint=nil, valid_encoding=nil)
def gennode(bytes_code, words_code, name_hint=nil)
@bytes_code = bytes_code
@words_code = words_code
name = generate_node(bytes_code, words_code, name_hint, valid_encoding)
name = generate_node(bytes_code, words_code, name_hint)
@bytes_code = nil
@words_code = nil
return name
@ -627,18 +645,20 @@ def transcode_compile_tree(name, from, map)
map.each {|k, v|
h[k] = v unless h[k] # use first mapping
}
am = ActionMap.parse(h)
max_input = am.max_input_length
if ValidEncoding[from]
valid_encoding = StrSet.parse(ValidEncoding[from])
max_input = [max_input, valid_encoding.max_length].max
if valid_encoding = ValidEncoding[from]
rects = ActionMap.parse_to_rects(h)
undef_rects = ActionMap.parse_to_rects(valid_encoding => :undef)
am = ActionMap.merge(rects, undef_rects) {|a1, a2|
a1 = a1.empty? ? nil : ActionMap.unambiguous_action(a1)
a2 = a2.empty? ? nil : ActionMap.unambiguous_action(a2)
a1 || a2
}
else
valid_encoding = nil
am = ActionMap.parse(h)
end
defined_name = am.gennode(TRANSCODE_GENERATED_BYTES_CODE, TRANSCODE_GENERATED_WORDS_CODE, name, valid_encoding)
max_input = am.max_input_length
defined_name = am.gennode(TRANSCODE_GENERATED_BYTES_CODE, TRANSCODE_GENERATED_WORDS_CODE, name)
return defined_name, max_input
end