add db.rb, ids.rb, rbchise.rb
authoreto <eto>
Tue, 18 Mar 2003 06:22:28 +0000 (06:22 +0000)
committereto <eto>
Tue, 18 Mar 2003 06:22:28 +0000 (06:22 +0000)
src/db.rb [new file with mode: 0755]
src/ids.rb [new file with mode: 0755]
src/rbchise.rb [new file with mode: 0755]

diff --git a/src/db.rb b/src/db.rb
new file mode 100755 (executable)
index 0000000..c2b5a11
--- /dev/null
+++ b/src/db.rb
@@ -0,0 +1,393 @@
+#!/usr/bin/env ruby
+# rbchise compatible ruby library by eto 2003-0317
+
+require 'db'
+
+module CHISE
+
+  class DBS #======================================================================複数のDBを集めたclass、未完成
+  end
+
+  class ADB < BDB::Hash #======================================================================一つのDB
+    def initialize(*args)
+      super
+      @modified = false
+      at_exit {
+       if @modified
+         self.close #これがないと、うまくデータベースがセーブされないのです。
+       end
+      }
+    end
+    def self.open_create(filename)
+      ADB.open(filename, nil, BDB::CREATE | BDB::EXCL) #上書きはしない
+    end
+    def mykey(key)
+      if key.is_a?(String)
+       if key.char_length == 1
+         return '?'+key  #Stringだったら引く前に?を足す
+       end
+      end
+      #key = key.to_s if key.is_a?(Numeric) #NumberだったらStringにする。
+      #ここで && key ! =~ /^\?/ をいれると、?自身を検索できなくなってしまう。
+      return key
+    end
+    def myvalue(v)
+      return v if v == nil
+      return v.to_i if v =~ /^\d+$/ #数字だったらここで変換しておく
+      return v.sub(/^\?/, '') if v =~ /^\?/ #冒頭の?は取り除く
+      return $1 if v =~ /^"(.+)"$/ #最初と最後に"がついていたら、取り除く
+      #p ['get', v, t, key, db]
+      #return parse_sexp(v) if v =~ /^\(.+\)$/ #最初と最後が()の時は、S式にparseする
+      return v #それ以外って何?
+    end
+    def myget(key) #keyキーを引いて返す
+      key = mykey(key)
+      v = get(key) #存在しなかったらnilを返すことになる
+      return myvalue(v)
+    end
+    def myput(key, v) #keyにvをいれる
+      key = mykey(key)
+      put(key, v) #putする
+      @modified = true
+    end
+  end
+
+  class DB #======================================================= データベース群のabstract class
+    def self.unix_to_win(unix) #Windowsファイル名制限のため、変換する
+      win = unix.gsub(/</, '(')
+      win.gsub!(/>/, ')')
+      win.gsub!(/\*/, '+')
+      win.gsub!(/\?/, '!')
+      return win
+    end
+    def self.win_to_unix(win)
+      unix = win.gsub(%r|\)|, '>')
+      unix.gsub!(%r|\(|, '<')
+      unix.gsub!(%r|!|, '?')
+      unix.gsub!(%r|\+|, '*')
+      return unix
+    end
+    def get_filename(t)
+      return @pre + DB.unix_to_win(t) + @post if windows?
+      return @pre + t + @post
+    end
+    def get_dirname(t) File.dirname(get_filename(t)) end
+    def open_dbs()
+      @dbs = Hash.new
+      keys = find_keys()
+      keys.each {|key| open_db(key) }
+    end
+    def find_keys()
+      files = []
+      Dir.glob(@glob){|f|
+       next if ! File.file?(f)
+       next if f =~ /.txt$/
+       files << f
+      }
+      keys = []
+      files.each {|f|
+       t = DB.win_to_unix(f)
+       t.sub!(%r|^#{@pre}|, '')
+       t.sub!(%r|#{@post}$|, '') if @post != ""
+       keys << t
+      }
+      return keys
+    end
+    def close_db(t)
+      db = get(t)
+      return nil if db.nil?
+      db.close
+      @dbs.delete(t)
+    end
+    def open_db(t)
+      return nil if get(t) #すでにopenしていたら再openはしない。
+      begin
+       bdb = ADB.open(get_filename(t), nil, 0)
+       @dbs[t] = bdb if bdb != nil
+      rescue
+       p ["open error", get_filename(t)]; return nil
+      end
+      return true
+    end
+    def make_db(t, h=nil) #tという名前でhという中身のデータベースを作る
+      return nil if get(t) #すでにある場合はreturn
+      Dir.mkdir(get_dirname(t)) unless FileTest.exist?(get_dirname(t))
+      db = nil
+      begin
+       db = ADB.open_create(get_filename(t)) #上書きはしない
+       if h != nil
+         h.each {|k, v|
+           k = '?'+k if k.is_a?(String)
+           db[k] = v
+         }
+       end
+       db.close
+      rescue
+       p ["make error", get_filename(t)]; return nil
+      end
+      return true
+    end
+    def make_db_no_question_mark(t, h=nil) #tという名前でhという中身のデータベースを作る
+      return nil if get(t) #すでにある場合はreturn
+      Dir.mkdir(get_dirname(t)) unless FileTest.exist?(get_dirname(t))
+      db = nil
+      begin
+       db = ADB.open_create(get_filename(t)) #上書きはしない
+       if h != nil
+         h.each {|k, v|
+           #        k = '?'+k if k.is_a?(String)
+           db[k] = v
+         }
+       end
+       db.close
+      rescue
+       p ["make error", get_filename(t)]; return nil
+      end
+      return true
+    end
+    def remove_db(t) #tという名前のデータベースを消去する
+      db = get(t)
+      if db
+       db.close
+       @dbs.delete(t)
+      end
+      begin
+       File.unlink(get_filename(t)) if FileTest.file?(get_filename(t))
+      rescue
+       p ["unlink error", get_filename(t)]; return nil
+      end
+      dn = get_dirname(t)
+      Dir.rmdir(dn) if FileTest.directory?(dn) && Dir.entries(dn).length <= 2 #空directoryだったら消す
+      return true
+    end
+    def to_num(s)
+      return s.to_i if s =~ /^\d+$/
+      return s
+    end
+    def dump_db(t)
+      db = get(t)
+      return nil unless db
+      file = get_filename(t)
+      open("#{file}.txt", "w"){|out|
+       #        out.binmode.sync = true
+       ar = db.to_a
+       ar.map! {|k, v| [to_num(k), to_num(v)] }
+       ar.sort.each {|k, v|
+         out.printf("%s\t%s\n", k, v)
+       }
+      }
+      return true
+    end
+    def each_db()  @dbs.to_a.sort.each {|t, db| yield(t, db) } end
+    def dump_all()  each_db {|t, db| dump_db(t) } end
+    def close_all() each_db {|t, db| db.close   } end
+    def keys() @dbs.keys end
+    def each(t)
+      return unless block_given?
+      db = @dbs[t]
+      return nil unless db
+      db.each {|k, v|
+       k = to_num(k)
+       v = to_num(v)
+       k.sub!(/^\?/, '') if k =~ /^\?/ #冒頭の?は取り除く
+       vv = get(t, k)  #p ['each', t, k, v, vv]
+       yield(k, vv)
+      }
+    end
+    def each_sort(t)
+      return unless block_given?
+      db = @dbs[t]
+      return nil unless db
+      ar = db.to_a
+      ar.map! {|k, v| [to_num(k), to_num(v)] }
+      ar.sort.each {|k, v|
+       k.sub!(/^\?/, '') if k =~ /^\?/ #冒頭の?は取り除く
+       vv = get(t, k)  #p ['each', t, k, v, vv]
+       yield(k, vv)
+      }
+    end
+    #----------------------------------------------------------------------
+    def get(t, key=nil) #tというデータベースのkeyキーを引いて返す
+      db = @dbs[t]
+      return db if key.nil?
+      return nil unless db
+      return db.myget(key)
+    end
+    def put(t, key, v) #tというデータベースのkeyにvをいれる
+      db = @dbs[t]
+      if db == nil
+       db = make_db(t) 
+       db = open_db(t) 
+       db = @dbs[t]
+      end
+      db.myput(key, v) #putする
+    end
+  end
+
+  class CharDB < DB #------------------------------------ MCS-UTF8をキーとした属性へのデータベース
+    include Singleton
+    def initialize()
+      super
+      @glob, @pre, @post = "#{DB_DIR}/system-char-id/*", "#{DB_DIR}/system-char-id/", ""
+      open_dbs()
+    end
+    def get_all(u8) #全データベースのu8キーを引いてHashにまとめて返す
+      atrs = Hash.new
+      @dbs.each {|t, db|
+       v = get(t, u8)
+       atrs[t] = v if v != nil
+      }
+      return atrs
+    end
+  end
+
+  class CodesysDB < DB #----------------------------------------------------------------------
+    include Singleton
+    def initialize()
+      super
+      @glob, @pre, @post = "#{DB_DIR}/*/system-char-id", "#{DB_DIR}/", "/system-char-id"
+      open_dbs()
+    end
+    #def keys() @dbs.keys.sort end #どんなCodesysの情報を持っているかの一覧
+    def keys() @dbs.keys end #どんなCodesysの情報を持っているかの一覧
+    def get_codesys(t)
+      db = get(t)
+      return nil unless db
+      return Codesys.new(t)
+    end
+  end
+
+  class Codesys < DB #======================================================================
+    def initialize(name)
+      #       super
+      @name = name
+      @dbs = CodesysDB.instance
+    end
+    def keys() #どんなコードポイントの情報を持っているかの一覧
+      ks = @dbs.get(@name).keys
+      if @name =~ /jisx0208/ #特別処理
+       n = @dbs.get('=jis-x0208').keys 
+       #        p ['keys', @name, ks, n]
+       ks += n
+      end
+      ks.map! {|k| to_num(k) }
+      ks
+    end
+    def get(key)
+      v = @dbs.get(@name, key)
+      return v if v
+      if @name =~ /jisx0208/ #jisx0208が含まれている場合だけ特別処理する
+       return @dbs.get('=jis-x0208', key)
+      end
+      return nil
+    end
+    def each()
+      return unless block_given?
+      db = @dbs.get(@name)
+      return nil unless db
+      db.each {|k, v|
+       k = to_num(k)
+       v = to_num(v)
+       k.sub!(/^\?/, '') if k =~ /^\?/ #冒頭の?は取り除く
+       vv = @dbs.get(@name, k) #p ['each', t, k, v, vv]
+       yield(k, vv)
+      }
+    end
+    def each_sort()
+      return unless block_given?
+      db = @dbs.get(@name)
+      return nil unless db
+      ar = db.to_a
+      ar.map! {|k, v| [to_num(k), to_num(v)] }
+      ar.sort.each {|k, v|
+       k.sub!(/^\?/, '') if k =~ /^\?/ #冒頭の?は取り除く
+       vv = @dbs.get(@name, k) #p ['each', t, k, v, vv]
+       yield(k, vv)
+      }
+    end
+  end
+
+  class JISX0208 #======================================================================
+    def initialize
+      db = CodesysDB.instance
+      @common = db.get_codesys('=jis-x0208')
+      @newest = db.get_codesys('japanese-jisx0208-1990')
+    end
+    def get_char(code)
+      char = @common.get(code)
+      return char unless char.nil?
+      char = @newest.get(code)
+      return char unless char.nil?
+      return nil
+    end
+  end
+
+  class DBS_Management #======================================================================ファイル管理
+    OBSOLETE_ATTRIBUTES = "
+cns-radical
+cns-radical?
+kangxi-radical
+daikanwa-radical
+unicode-radical
+
+cns-strokes
+kangxi-strokes
+daikanwa-strokes
+shinjigen-1-radical
+gb-original-radical
+japanese-strokes
+jis-strokes-a
+jis-strokes-b
+jisx0208-strokes
+jis-x0213-strokes
+jisx0213-strokes
+unicode-strokes
+
+totalstrokes
+cns-total-strokes
+jis-total-strokes-b
+
+non-morohashi
+
+=>ucs*
+#=>mojikyo
+#=mojikyo
+->identical
+
+ancient-ideograph-of
+ancient-char-of-shinjigen-1
+original-ideograph-of
+original-char-of-shinjigen-1
+simplified-ideograph-of
+vulgar-ideograph-of
+vulgar-char-of-shinjigen-1
+ideograph=
+ideographic-variants
+variant-of-shinjigen-1
+
+iso-10646-comment
+".split
+    def initialize
+      @odir = DB_DIR+"/system-char-id/obsolete" #直打ちしている。
+    end
+    def move_obsolete_files # 廃止予定のbdbファイルをobsoleteディレクトリーにつっこむ
+      db = CharDB.instance
+      db.close_all
+      Dir.mkdir(@odir) unless FileTest.directory? @odir
+      OBSOLETE_ATTRIBUTES.each {|attr|
+       next if attr =~ /^#/
+       filename = db.get_filename(attr)
+       move_to_obsolete(filename)
+       move_to_obsolete(filename+".txt")
+      }
+    end
+    def move_to_obsolete(file)
+      cmd = "mv #{file} #{@odir}"
+      #      p cmd
+      system cmd
+    end
+  end
+
+end
+
+#----------------------------------------------------------------------終了
diff --git a/src/ids.rb b/src/ids.rb
new file mode 100755 (executable)
index 0000000..bb80bbe
--- /dev/null
@@ -0,0 +1,458 @@
+#!/usr/bin/env ruby
+# rbchise compatible ruby library by eto 2003-0317
+
+module CHISE
+
+  class IDS_TEXT_DB < DB #======================================================================
+    include Singleton
+    IDS_LIST = "
+IDS-UCS-Basic.txt
+#IDS-UCS-Compat-Supplement.txt
+#IDS-UCS-Compat.txt
+IDS-UCS-Ext-A.txt
+IDS-UCS-Ext-B-1.txt
+IDS-UCS-Ext-B-2.txt
+IDS-UCS-Ext-B-3.txt
+IDS-UCS-Ext-B-4.txt
+IDS-UCS-Ext-B-5.txt
+IDS-UCS-Ext-B-6.txt
+IDS-JIS-X0208-1990.txt
+IDS-Daikanwa-01.txt
+IDS-Daikanwa-02.txt
+IDS-Daikanwa-03.txt
+IDS-Daikanwa-04.txt
+IDS-Daikanwa-05.txt
+IDS-Daikanwa-06.txt
+IDS-Daikanwa-07.txt
+IDS-Daikanwa-08.txt
+IDS-Daikanwa-09.txt
+IDS-Daikanwa-10.txt
+IDS-Daikanwa-11.txt
+IDS-Daikanwa-12.txt
+IDS-Daikanwa-dx.txt
+IDS-Daikanwa-ho.txt
+IDS-CBETA.txt
+".split
+    def initialize()
+      super
+      @ids_list = IDS_LIST
+      @chars = []
+      @glob, @pre, @post = "#{IDS_DB_DIR}/db/*", "#{IDS_DB_DIR}/db/", ""
+      dir = File.dirname(@pre)
+      Dir.mkdir(dir) unless FileTest.exist?(dir)
+      open_dbs()
+    end
+    def each_file()
+      return unless block_given?
+      @ids_list.each {|file|
+       next if file =~ /^#/
+       yield(IDS_DB_DIR+file)
+      }
+    end
+    def each_line(file)
+      open(file){|f|
+       while line = f.gets
+         next if line =~ /^;/ #コメントはとばす
+         line.chomp!
+         code, char, ids = line.split
+         yield(code, char, ids)
+       end
+      }
+    end
+    def dump_text_all
+      each_file {|file|
+       dir = File.dirname(file) + '/../ids-new/'
+       Dir.mkdir(dir) if ! FileTest.directory?(dir)
+       newfile = dir + File.basename(file)
+       p [file, newfile]
+       open(newfile, "w"){|out|
+         out.binmode.sync = true
+         each_line(file){|code, ch, ids|
+           char = Character.get(ch)
+           ids = char.decompose
+           out.print "#{code}  #{ch}   #{ids}\n"
+         }
+       }
+      }
+    end
+    def make_ids_error
+      each_file {|file|
+       dir = File.dirname(file) + '/../ids-error'
+       Dir.mkdir(dir) unless FileTest.exist?(dir)
+       errfile = dir + '/' + File.basename(file)
+       #       p [file, errfile]
+       open(errfile, "w"){|out|
+         out.binmode.sync = true
+         each_line(file){|code, ch, ids|
+           char = Character.get(ch)
+           ids_error = char['ids-error']
+           next if ids_error.nil?
+           out.print "#{code}  #{ch}   #{ids}  #{ids_error}\n"
+         }
+       }
+      }
+    end
+  end
+
+  class IDS_DB < DB #======================================================================BDB化したIDS DBを扱う
+    include Singleton
+    def initialize
+      @dbs = CharDB.instance
+    end
+    def make_ids_db
+      db = IDS_TEXT_DB.instance
+      db.each_file {|file|
+       @char_counter = 0
+       @same_ids_counter = 0
+       @good_ids_counter = 0
+       @conflict_ids_counter = 0
+       db.each_line(file){|code, ch, ids|
+         @char_counter += 1
+
+         ids = "" if ids == nil
+         next if ids == "" #IDSが定義されていない場合は、さっくりと無視するべしよ。
+
+         charimg = Character.get(ch) #実体参照である可能性がある
+
+         next if code =~ /'$/ || code =~ /"$/ #大漢和番号のダッシュ付きは無視する
+         char = Character.get("&"+code+";") #code表記を元に実体参照を作って解釈する
+         if char.nil? || char.to_s == "" #うまく文字にならなかった
+           print "char == null #{char.inspect} #{code} #{ch}   #{ids}\n" unless code =~ /^M-/ || code =~ /^CB/
+           #大漢和、CBETA以外の場合は、エラーメッセージ。
+           next
+         end
+         if char != charimg #code表記と文字が一致していない?
+           unless code =~ /^M-/ || code =~ /^MH-/ || code =~ /^CB/ #食い違っていて当然であるので何もしない
+             print "unknown char       #{char.inspect} #{code} #{ch}   #{ids}\n"
+             next #それ以外の場合はエラーメッセージをだして、次へ。
+           end
+         end
+         #next if !char.has_attribute? #isolated characterはまぎれこませない。
+
+         ids.de_er! #実体参照を解除する
+         next if ids == char.to_s #もし文字とまったく一緒なら、意味が無いので情報を持たない
+         next if ids.char_length == 1
+
+         idstree = IDS_Tree.new(ids)
+         c = idstree.check_integrity
+         c = "contains self" if ids.include?(char.to_s)
+         if c #ちょっとでもエラーがある場合は、
+           char['ids-error'] = c #エラーを記録して、データとしては保持しない
+           next
+         end
+
+         if char['ids'].nil? || char['ids'] == "" #元々IDSが無かった場合は、
+           char['ids'] = ids #普通に代入すればそれでいいです。
+           @good_ids_counter += 1
+         else #しかしいままでにすでにIDSが定義されていた場合は?
+           if char['ids'] == ids #新しいIDSと古いIDSが完全に一致するなら無視しましょう。
+             @same_ids_counter += 1
+           else #しかしいままでのIDSと新しいIDSが食い違った場合は?
+             @conflict_ids_counter += 1
+             #       print "conflict   #{char.inspect} #{code} #{ids}  #{char['ids']}\n"
+           end
+         end
+       }
+       print "#{file}  #{@char_counter}        #{@same_ids_counter}    #{@conflict_ids_counter}        #{@good_ids_counter}\n"
+       CharacterFactory.instance.reset()
+      }
+      @dbs.dump_db('ids-error') #テキスト化する
+      @dbs.dump_db('ids') #テキスト化する
+    end
+    def make_ids_reverse
+      h = Hash.new
+      @dbs.each('ids') {|k, v|
+       char = k.char
+       ids = char.decompose
+       h[ids] = "" if h[ids].nil?
+       h[ids] += k #追加する
+      }
+      h.each {|k, v|
+       h[k] = char_sort(v) #文字の順番を、よく使うっぽいものからの順番にする
+      }
+      h.delete_if {|k, v| #h[k]が""になる可能性もあるが、それはkeyとして入れないことにする。
+       v == ""
+      }
+      print "length    #{h.length}\n"
+      cdb = CodesysDB.instance
+      cdb.make_db_no_question_mark('ids', h)
+      cdb.open_db('ids') #これが無いと、dump_dbされません。
+      cdb.dump_db('ids')
+    end
+    def char_sort(composed)
+      return composed if composed.char_length == 1
+      ar = composed.to_a
+      arorg = ar.dup
+      ar2 = []
+      ar.dup.each {|ch|
+       char = ch.char
+       if char.char_id < 0xfffff #Unicodeっぽい?
+         ar2 << ch
+         ar.delete(ch)
+       end
+      }
+      if 0 < ar.length
+       EntityReference.each_codesys{|codesys, er_prefix, keta, numtype|
+         ar.each {|ch|
+           char = ch.char
+           v = char[codesys]
+           #       p [codesys, v] if v
+           if v #EntityReferenceの順番に準拠する。
+             ar2 << ch
+             ar.delete(ch)
+           end
+         }
+       }
+      end
+      if 0 < ar.length
+       #       p ['yokuwakaran character', ar, ar[0].inspect_all, arorg]
+       EntityReference.each_codesys{|codesys, er_prefix, keta, numtype|
+         ar.dup.each {|ch|
+           char = ch.char
+           v = char[codesys]
+           #       p [codesys, v] if v
+         }
+       }
+      end
+      return ar2.join("")
+    end
+    def dump_ids_duplicated
+      open('ids-duplicated.txt', 'w'){|out|
+       #out.binmode
+       CodesysDB.instance.each('ids') {|k, v|
+         if v.nil?
+           out.print "nil      #{k}    #{v}\n"
+           next
+         end
+         n = v.char_length
+         next if n == 1
+         out.print "#{n}       #{k}    #{v}"
+         v.each_char {|ch|
+           char = ch.char
+           out.print " #{char.inspect}"
+         }
+         out.print "\n"
+       }
+      }
+    end
+    def make_ids_aggregated
+      @dbs.each('ids') {|k, v|
+       char = k.char
+       ids = char.decompose
+       ag = ids.aggregate
+       char['ids-aggregated'] = ag
+      }
+      @dbs.dump_db('ids-aggregated')
+    end
+    def dump_ids_aggregated
+      open('ids-aggregated.txt', 'w'){|out|
+       #out.binmode
+       @dbs.each('ids') {|k, v|
+         char = k.char
+         ids = char['ids']
+         ag  = char['ids-aggregated']
+         out.print "#{char.to_s}       #{ag}   #{ids}\n" if ids != ag
+       }
+      }
+    end
+    def make_ids_parts
+      @dbs.each('ids') {|k, v|
+       char = k.char
+       pids = char.to_s
+       ar = []
+       counter = 0
+       loop {
+         ids = pids.decompose
+         break if ids == pids #これ以上分割できないようだったら終了〜。
+         ar += ids.to_a
+         counter += 1
+         p [char.to_s, pids, ids, ar] if 10 < counter #これは何かおかしいぞと
+         pids = ids
+       }
+       ar.sort!
+       ar.uniq!
+       #やっぱりIDS文字も加えることにする. by eto 2003-02-05
+       #       ar.delete_if {|ch|
+       #         ch.char.is_ids? #IDS文字はまぎれこませない。
+       #       }
+       str = ar.join('')
+       char['ids-parts'] = str
+      }
+      @dbs.dump_db('ids-parts')
+    end
+    def make_ids_contained
+      h = Hash.new
+      @dbs.each('ids-parts') {|k, v|
+       char = k.char
+       parts = char.ids_parts
+       parts.each_char {|ch|
+         #       part = ch.char
+         h[ch] = [] if h[ch].nil?
+         h[ch] << k
+         #       h[ch] += k
+         #       part['ids-contained'] = "" if part['ids-contained'].nil?
+         #       part['ids-contained'] += k
+       }
+      }
+      h.each {|k, v|
+       char = k.char
+       v.sort!
+       char['ids-contained'] = v.join('')
+       
+      }
+      @dbs.dump_db('ids-contained')
+    end
+    def make_ids_decomposed
+      @dbs.each('ids') {|k, v|
+       char = k.char
+       de= char.decompose_all
+       char['ids-decomposed'] = de
+      }
+      @dbs.dump_db('ids-decomposed')
+    end
+  end
+
+  class Node < Array #=======================================================木構造の中の一つの枝
+    def initialize(nodeleaf=nil, nodenum=nil)
+      super()
+      @nodeleaf = nodeleaf
+      @nodenum = nodenum
+      if @nodeleaf
+       original_add(@nodeleaf)
+      end
+    end
+    attr_reader :nodenum
+    alias original_add <<
+      private :original_add
+    def <<(obj)
+      original_add(obj)
+      @nodenum -= 1 if @nodenum
+    end
+    def nodes
+      ar = []
+      ar << self.to_s
+      self.each {|n|
+       ar += n.nodes if n.is_a? Node
+      }
+      return ar
+    end
+  end
+
+  class Tree #======================================================================木構造を扱う
+    def initialize()
+      @root = Node.new()
+      @stack = [@root]
+      @leafnum = 0
+      @depth = 1 #stackの深さが最大になったところの値、木構造が無いときは1となる
+    end
+    def depth() @depth - 1 end
+    def add_node(nodeleaf=nil, nodenum=nil) #枝を追加
+      new_node = Node.new(nodeleaf, nodenum)
+      @stack.last << new_node
+      @stack << new_node
+      if @depth < @stack.length
+       @depth = @stack.length
+      end
+      self
+    end
+    def end_node() #この枝は終り
+      @stack.pop
+      self
+    end
+    def add_leaf(a) #葉を追加
+      @stack.last << a
+      end_check()
+      self
+    end
+    def end_check()
+      n = @stack.last.nodenum
+      if n && n == 0
+       end_node()
+       end_check() #再帰
+      end
+    end
+    def check_integrity
+      n = @stack.last.nodenum
+      return nil if @root.length == 0 #no tree is good tree
+      return "unmatch leaves" if n && n != 0
+      return "extra nodes" if @root.first.is_a?(Node) && @root.length != 1
+      return "extra leaves" if @root.length != 1
+      return nil
+    end
+    def nodes
+      r = @root.nodes
+      r.shift
+      r
+    end
+    def sub_nodes
+      r = nodes
+      r.shift
+      r
+    end
+    def to_s()    @root.to_s    end
+    def inspect() @root.inspect end
+  end
+
+  class IDS_Tree < Tree #======================================================================
+    def initialize(str)
+      @str = str
+      super()
+      parse()
+    end
+    def parse()
+      @str.each_char {|ch|
+       char = Character.new(ch)
+       if is_ids?(char)
+         add_node(char, ids_operator_argc(char))
+       else
+         add_leaf(char)
+       end
+      }
+    end
+    def is_ids?(obj)
+      return true if "+*".include?(obj.to_s) #テスト用ですかね
+      return true if obj.is_ids?
+      return false
+    end
+    def ids_operator_argc(obj)
+      return obj.ids_operator_argc if 0 < obj.ids_operator_argc
+      return 2 #テスト用ってことで
+    end
+    def check_integrity
+      r = super
+      return r if r #不完全がすでにわかっているならreturn
+      return "contains ques" if @str =~ /\?/ #?が含まれている?
+      return nil
+    end
+  end
+
+  class IDS #======================================================================IDSそのものを扱うclass
+    def initialize(str) #IDS文字列をうけとる。
+      @str = str
+    end
+    def parse
+    end
+    def parse_x #柔軟型のParse. IDSキャラクターが前にきてなくてもよい。などなど。
+    end
+  end
+
+  class Counter #======================================================================
+    #使い方
+    #counter = Counter.new(50) { exit }
+    #counter.count
+    def initialize(max)
+      @max = max
+      @count = 0
+      @proc = proc
+    end
+    def count
+      @count += 1
+      if @max <= @count
+       @proc.call
+      end
+    end
+  end
+
+end
+
+#----------------------------------------------------------------------end.
diff --git a/src/rbchise.rb b/src/rbchise.rb
new file mode 100755 (executable)
index 0000000..0333a69
--- /dev/null
@@ -0,0 +1,69 @@
+#!/usr/bin/env ruby
+# rbchise compatible ruby library by eto 2003-0317
+
+module CHISE
+
+  class DataSource #======================================================================
+    NONE = 0
+    Berkeley_DB = 1
+    #DEFAULT_CHAR_DB_DIR = "/usr/local/lib/chise/char-db"
+    DEFAULT_CHAR_DB_DIR = "d:/work/chise/char-db"
+    def initialize(type=Berkeley_DB, location = DEFAULT_CHAR_DB_DIR)
+      @type, @location = type, location
+    end
+    def close
+    end
+    def open_decofing_table(ccs)
+      DecodingTable.new(self, ccs)
+    end
+    def open_feature_table(feature)
+      FeatureTable.new(self, feature)
+    end
+  end
+
+  class AttributeTable #======================================================================
+    def open(from, to, real_subtpe, accessmask, modemask)
+    end
+    def get_value(char_id)
+      @db.get(char_id)
+    end
+    def close
+    end
+  end
+  
+  class DecodingTable #======================================================================
+    def initialize(ds, ccs)
+      @ds, @ccs = ds, ccs
+      #\82±\82±\82Åopen\82·\82é
+      @db = nil
+    end
+    def get_char(code_point)
+      @db.get(code_point)
+    end
+    def close
+    end
+  end
+  
+  class FeatureTable #======================================================================
+    def initialize(ds, feature)
+      @ds, @feature = ds, feature
+      #\82±\82±\82Åopen\82·\82é
+      @db = nil
+    end
+    def get_value(char_id)
+      @db.get(char_id)
+    end
+    def close
+    end
+  end
+  
+  class Value #======================================================================
+    def initialize(v)
+      @v = v;
+    end
+    def to_s()      @v;    end
+  end
+  
+end
+
+#----------------------------------------------------------------------end.