indent2tree - メモ帳

僕もRubyで実装してみました。いきなり2つインデントした場合とかのエラーチェックは実装していません。手抜きです。

class Node
  def initialize(data = nil)
    @data = data
    @children = []
  end

  def <<(node)
    @children << node
  end

  def dump(prefix = '', nexts = ['', ''])
    print "#{prefix + nexts[0]}#{@data}\n" if @data
    @children.each_with_index do |child, i|
      child.dump(
        prefix + nexts[1], 
        (i == @children.size - 1) ? ['`-', '  '] : ['+-', '| '])
    end
  end
end

def parse(lines)
  table = [Node.new]
  lines.inject(0) do |level, line|
    line =~ /^( *)/
    new_level, new_node = $1.size + 1, Node.new($'.strip)
    table[(level < new_level) ? level : new_level - 1] << new_node
    table[new_level] = new_node
    new_level
  end
  table[0]
end

parse(ARGF.readlines).dump

以下、実行例です。

$ cat a.txt
a
 b
 c
  d
   x
    y
   z
    t
     v
   w
  e
 f
 g
h
$ ruby indent2tree.rb < a.txt
+-a
| +-b
| +-c
| | +-d
| | | +-x
| | | | `-y
| | | +-z
| | | | `-t
| | | |   `-v
| | | `-w
| | `-e
| +-f
| `-g
`-h

(追記)
ロジックを見直したら、微妙に簡単になりました。なんちゃてエラー処理も追加しました。

class Node
  def initialize(data = nil)
    @data = data
    @children = []
  end

  def <<(node)
    @children << node
  end

  def dump(prefix = '', nexts = ['', ''])
    print "#{prefix + nexts[0]}#{@data}\n" if @data
    @children.each do |child|
      child.dump(
        prefix + nexts[1], 
        (child == @children.last) ? ['`-', '  '] : ['+-', '| '])
    end
  end
end

def parse(lines)
  table = [Node.new]
  lines.inject(0) do |level, line|
    line =~ /^( *)/
    new_level, new_node = $1.size + 1, Node.new($'.strip)
    raise 'invalid indent.' if new_level - level > 1
    table[new_level - 1] << new_node
    table[new_level] = new_node
    new_level
  end
  table[0]
end

if $0 == __FILE__
  parse(ARGF.readlines).dump
end