=begin
= class Node
Node is a parent class for classes of parse tree node.
This is not expected to be instanciated directly.
=end

class Node

    def initialize(*args)
	raise "#{self.class} is virtual."
    end

    def to_s(*args)
	raise "#{self.class}#to_s is virtual."
    end

=begin
--- pow other
    simply constructs a PowNode object.
    No reduction is performed.
=end

    def pow(other)
	PowNode.new(self, other)
    end

=begin
--- mul other
    simply constructs a MulNode object.
    No reduction is performed.
=end

    def mul(other)
	other = NumberNode.new(other) if Numeric === other
	MulNode.new(self, other)
    end

=begin
--- divide other
    simply constructs a MulNode object.
    No reduction is performed.
=end

    def divide(other)
	MulNode.new(self, PowNode.new(other, NumberNode.new(-1)))
    end

=begin
--- shift other
    simply constructs a ShiftNode object.
    No reduction is performed.
=end

    def shift(other)
	ShiftNode.new(self, other)
    end

=begin
--- pow_eval other
    similar to ((<pow other>)), but reduces PowNode[PowNode[...]] into
    single PowNode[...], so overriden at PowNode class.
=end

    def pow_eval(other)
	pow(other)
    end

=begin
--- inspect
=end

    def inspect2;  "#{self.class}[#{to_s}]";  end

    def inspect;  inspect2.gsub(/Units::/, '').gsub(/NumRu::/, '').gsub(/Node\[/, '[');  end

=begin
--- trim
    in most subclasses, "trim" is redirected into "trim2".
=end

    def trim
	trim2
    end

=begin
--- flatten
    in most subclasses, "flatten" is redirected into "flatten2".
=end

    def flatten
	flatten2
    end

=begin
--- sort
=end

    def sort
	raise "#{self.class}#sort is virtual: call after flatten"
    end

=begin
--- reduce1
--- reduce2
--- reduce3
--- reduce4
--- reduce5
=end

    def reduce1
	self
    end

    def reduce2
	trim
    end

    def reduce3
	trim.flatten
    end

    def reduce4
	# unalias(Hash.new).trim.flatten.sort
	foldnumber(nil).trim.flatten.sort
    end

    def reduce5
	expand(Hash.new).trim.flatten.sort
    end

=begin
--- ref
    to be overriden at ShiftNode
--- deref
    to be overriden at ShiftNode
=end

    def ref
	NumberNode::ZERO
    end

    def deref
	self
    end

end

class ErrorNode < Node

    def initialize(string)
	@a = string
    end

    def to_s;  @a;  end

end

class ContainerNode < Node

    def trim2
	x = []
	for child in self
	    x.push child.trim2
	end
	self.class.new(*x)
    end

    def inspect2
	a = []
	for child in self
	    a.push child.inspect2
	end
      	"#{self.class}[#{a.join(', ')}]"
    end

end

module BinaryNode

    def each
	yield @lhs
	yield @rhs
    end

    def expand(stopper)
	self.class.new(@lhs.expand(stopper), @rhs.expand(stopper))
    end

    def unalias(stopper)
	self.class.new(@lhs.unalias(stopper), @rhs.unalias(stopper))
    end

    def foldnumber(stopper)
	self.class.new(@lhs.foldnumber(stopper), @rhs.foldnumber(stopper))
    end

end

class TerminalNode < Node

    def trim2;  self;  end
    def flatten2;  self;  end

    def expand(stopper);  self;  end
    alias :unalias :expand
    alias :foldnumber :expand

    def sort;  self;  end

end
