module Goose

  #
  # Node of a Syntactic Tree.
  #
  class SyntacticNode

    attr_accessor :symbol, :value, :lineno, :filename, :volatile, :parent, :id_as_child

    ops = [
           '*=', '/=', '%=',                         # mult-assign
           '+=', '-=',                               # add-assign
           '<<=', '>>=',                             # shift-assign
           '^=', '&=', '|=',                         # bit-assign
           '=',                                      # assign
          ]
    ops = ops.map {|item| Regexp.escape(item)}
    AssignOpRe = Regexp.union(*ops)

    def initialize(symbol, value, lineno, filename)
      @parent      = nil
      @child       = nil
      @id_as_child = 0
      @symbol      = symbol
      @value       = value
      @lineno      = lineno
      @filename    = filename
      @volatile    = false  # avoid optimization by #refine if true.
    end

    #
    # print descendant nodes in tree form. just for debugging purpose.
    #
    def dump(level)
      msg = "#{@symbol}"
      if @value and @value != ''
        if @value.size < 64
          msg << " (val:'#{@value}')"
        else
          msg << " (val:'#{@value[0..63]}...')"
        end
      end
      msg << " at file:\"#{@filename}\" line:#{@lineno} class:#{self.class}" if @lineno and @value != ''
      puts '    ' * level + msg
    end

    #
    # returns :variables defined.
    #
    def defined_vars
      items = []
      if inherit?(VariableNode)
        items.push self
      elsif @child
        @child.compact.each {|c|
          citems = c.defined_vars
          items.concat citems if citems
        }
      end
      items.uniq_value if items
    end

    #
    # returns :variables directly refered.
    # e.g. for a[id[k]] = 3.0, a[id[k]] is retured but id[k] and k are not.
    #
    def refered_vars
      items = []
      is_direct_ref = inherit?(VarrefExpNode)
      is_funcall = inherit?(FuncallExpNode)
      if is_direct_ref and not is_funcall
        items.push self
      elsif @child
        @child.compact.each {|c|
          citems = c.refered_vars
          items.concat citems if citems
        }
      end
      items.uniq_value if items
    end

    #
    # returns :variables refered anywhere in the descendant nodes.
    # e.g. for a[id[k]] = 3.0, a[id[k]], id[k], k are retured.
    #
    def dependent_vars
      items = []
      if inherit?(VarrefExpNode)
        items.push self
      end
      if @child
        @child.compact.each {|c|
          citems = c.dependent_vars
          items.concat citems if citems
        }
      end
      items.uniq_value if items
    end

    #
    # recursively descends the syntactic tree to collect
    # :variable-s which 'self' depends its value on.
    #
    def prerequisites(root)
      recursive_prerequisites(root, [])
    end

    def recursive_prerequisites(root, checked)
      # vars in the rhs of :assign_exp to self.
      # e.g.  a, b, c in var = a + b + c.
        prs_of_self = direct_prerequisites(root)

      # vars used in the index of self.
      # e.g.  id, i, j in var[id[i]][j].
      prs_of_self.concat dependent_vars()

      # indirect prerequisites.
      # e.g.  d, f, g, in var = a + b + c; a = d + f; b = g + 3;
      checked.push self
      if prs_of_self
        prs_of_prs = []
        prs_of_self.each { |pr|
          unless checked.include_value? pr
            prs_of_pr = pr.recursive_prerequisites(root, checked)
          end
          prs_of_prs.concat prs_of_pr if prs_of_pr and not prs_of_pr.empty?
        }
        prs_of_self.concat prs_of_prs if prs_of_prs
      end
      prs_of_self.uniq_value.delete_if { |node| node.value == self.value }
    end

    #
    # look for assignmnet expressions of which 'self' resides in the lhs,
    # and then collect :variables in the rhs.
    #
    def direct_prerequisites(root)
      items = []
      if root.symbol == :assign_exp
        if root.child[0].value == self.value
          prs = root.child[2].refered_vars
          items.concat prs if prs
        end
      elsif root.child
        begin
          root.child.compact.each {|c|
            citems = direct_prerequisites(c)
            items.concat citems if citems
          }
        rescue => ex
          puts "#{ex.class}:#{ex.message}"
          puts "root:#{root.value}  parent:#{root.parent.value}"
          exit
        end
      end
      items.uniq_value.delete_if { |node| node.value == self.value }
    end

    #
    # recursively descends the syntactic tree to collect nodes.
    # by default, collect nodes which have 'symbol' as their '@symbol'.
    # the default behavior can be modified by an iterator block.
    #
    def recursive_select(val, &block)
      items = []
      if block_given?
        if yield(self, val)
          items.push self
        end
        @child && @child.compact.each {|c|
          citems = c.recursive_select(val, &block)
          items.concat citems if citems
        }
      else
        if @symbol == val
          items.push self
        end
        @child && @child.compact.each {|c|
          citems = c.recursive_select(val)
          items.concat citems if citems
        }
      end
      items
    end

    #
    # substitute a VarrefExpNode whose value is 'srcval' with 'dstnode'.
    #
    def recursive_sub(srcval, dstnode)
      if @value == srcval
        @parent.child.insert(@id_as_child + 1, dstnode)
        @parent.child.delete_at(@id_as_child)
        @parent.refine
      elsif @child and @child.compact.each { |c|
          c.recursive_sub(srcval, dstnode)
        }
      end
    end

    #
    # returns true if self is an instance of a class which inherits cls.
    # returns false otherwise.
    #
    def inherit?(cls)
      self.class.ancestors.include?(cls)
    end

    def dup_tree
      duped_root = recursive_dup
      duped_root.recursive_assign_parent
      duped_root
    end

    def lvalue?(node)
      false
    end

    @@serialno = 0
    def new_serialno
      retval = @@serialno
      @@serialno += 1
      retval
    end

    def child
      nil
    end

    def expand_twodots
      nil
    end

    def flatten
      flatten_nodes = []
      @child and @child.compact.reverse.each { |c|
        flatten_nodes.concat c.flatten
      }
      flatten_nodes
    end

    #
    # virtual methods.
    #
    def refine
      # virtual
    end

    def remove_redundant_nodes
      # virtual
    end

    def recursive_assign_parent
      # virtual
    end

  end # SyntacticNode

  #
  # A Stub Node (i.e., Place-Holder) of a Syntactic Tree.
  #
  class StubNode < SyntacticNode
    def initialize(lineno, filename)
      super(nil, nil, lineno, filename)
    end

    def recursive_reconstruct_value
      ""
    end

    def dump(level)
      puts '    ' * level << "StubNode at file:\"#{@filename}\" line:#{@lineno}" if @lineno and @value != ''
    end

private

    #
    # dupulicate a node.
    #
    def recursive_dup
      lineno = @lineno
      filename = nil
      newnode = self.class.new(lineno, filename)

      newnode.symbol      = @symbol
      newnode.value       = @value
      newnode.volatile    = @volatile

      newnode
    end

  end # StubNode

  #
  # Terminal Node of a Syntactic Tree.
  #
  class TNode < SyntacticNode
    def recursive_reconstruct_value
      @value
    end

private
    #
    # dupulicate a node.
    #
    def recursive_dup
      symbol = @symbol
      value  = @value.dup
      lineno = @lineno
      filename = @filename.dup
      newnode = self.class.new(symbol, value, lineno, filename)

      newnode.volatile    = @volatile

      newnode
    end

  end # TNode

  #
  # Non-terminal Node of a Syntactic Tree.
  #
  class NNode < SyntacticNode

    attr_accessor :child

    def initialize(symbol, value, lineno, filename, child)
      super(symbol, value, lineno, filename)
      raise "At least one child must be given.\n" +
        "  symbol:#{symbol} value:#{value} at file:\"#{filename}\" line:#{lineno}" unless child
      @child   = child
    end

    # create a parent node for nodes.
    def NNode.reduce(symbol, value, *nodes)
      lineno = 0
      filename = ''
      nodes.each {|node|
        if (lineno == 0 && node)
          lineno = node.lineno
          filename = node.filename
        end
      }
      self.new(symbol, value, lineno, filename, nodes)
    end

    def dump(level)
      super(level)
      @child and @child.compact.each {|c|
        if c 
          c.dump(level + 1)
        else
          puts '    ' * (level+1) + 'nil'
        end
      }
    end

    def refine
      root = remove_redundant_nodes
      recursive_assign_parent
      recursive_reconstruct_value
      root
    end

    #
    # if a child(A) has only one child(B), remove A and directly connect B to self,
    # unless A is not declared as 'volatile'.
    #
    #   before : self -> A -> B
    #   after  : self -> B
    #
    def remove_redundant_nodes
      begin
        @child and @child = @child.map {|c|
          next nil unless c # StubNode

          c.remove_redundant_nodes if c
          cnew = c
          if c.child and c.child.size == 1 and not c.volatile
            # puts "remove_redundant_nodes #{c.value}: #{c.symbol ? c.symbol : 'nil'} -> #{c.child[0].symbol ? c.child[0].symbol : c.child[0].value }"
            cnew = c.child[0]
          end
          cnew
        }
      rescue => ex
        puts "#{ex.class}:#{ex.message}"
        puts "child:#{child.values(', ')}"
        exit
      end

      self
    end

    def recursive_assign_parent
      @child and @child.each_with_index {|c, i|
        next unless c # StubNode

        c.parent = self
        c.id_as_child = i
        if c.child
          c.recursive_assign_parent
        end
      }
    end

    def recursive_reconstruct_value
      value = ''
      @child and @child.compact.each { |c|
        v = c.recursive_reconstruct_value
        value << v if v
      }
      @value = value
    end

    #
    # return true if node.value appears as lvalue
    # in descendants of self in the syntactic tree.
    #
    def lvalue?(node)
      found = false
      if @symbol == :assign_exp
        if @child[0].value == node.value
          found = true
        end
      elsif @child
        @child.compact.each { |c|
          if c.lvalue?(node)
            found = true
          end
        }
      end
      found
    end

    # append children.
    def push(*nodes)
      @child.push(*nodes)
    end

    #
    # Duplicate an instance of VarrefExpNode (or its subclass)
    # which has an ArrayRangeExpNode in its descendant tree.
    # Then, for each duped VarrefExpNode, substitute
    # an ArrayRangeExpNode with an ArrayExpNode.
    #
    # (e.g. x[0..2][1]  ->  x[0][1], x[1][1], x[2][1])
    #
    # Duplicated instances are connected to the parent
    # of the original instance. The original one is discarded.
    #
    def expand_twodots
      ares = recursive_select(:array_range_exp)     # ares   : x[i][0..1][0..2]  x[i][0..1]
      return nil if ares.empty?

      are = ares[0]                                 # are    : x[i][0..1][0..2]
      k0 = are.child[2].value.to_i
      k1 = are.child[4].value.to_i
      #            puts "!!!twodots: #{are.value}  k0:#{k0}  k1:#{k1}"
      raise "Negative index not supported : #{are.value}" if k0 < 0 or k1 < 0
      if k0 > k1
        tmp = k0
        k0 = k1
        k1 = tmp
      end
      indices = (k0..k1).to_a

      # Duplicate a VarrefExpNode. e.g. x[0..1][i]
      duped_nodes = []
      indices.each {
        duped_node = dup_tree()
        duped_node.parent = @parent
        duped_node.id_as_child = @id_as_child
        duped_nodes.push duped_node
      }
      @parent.child.insert(@id_as_child + 1, *duped_nodes)
      @parent.child.delete_at(@id_as_child)
      @parent.refine

      # Substitute an ArrayRangeExpNode with an ArrayExpNode.
      duped_nodes.each { |node|
        # array_range_exp to be replaced with ae. e.g. x[0..1]
        are = node.recursive_select(:array_range_exp)[0]
        child = are.child
        index = indices.shift
        index_node = TNode.new(:DEC_NUMBER, index.to_s, 0, '')

        # array_exp newly created. e.g. x[0]
        ae = ArrayExpNode.reduce(:array_exp, "", child[0], child[1], index_node, child[5])

        # disconnect/connect are/ae from/to the node.
        # e.g. connect x[0] to x[][i] to obtain x[0][i].
        are.parent.child[are.id_as_child] = ae
      }

#      puts "------------- value:#{@value}"
#      @parent.refine
#      @parent.dump(0)

      duped_nodes
    end

private
    #
    # dupulicate a tree.
    #
    def recursive_dup
      child = []
      if @child
        @child.compact.each { |c|
          child.push c.dup_tree
        }
      end
      symbol = @symbol
      value  = @value.dup
      lineno = @lineno
      filename = @filename.dup
      newnode = self.class.new(symbol, value, lineno, filename, child)

      newnode.volatile    = @volatile

      newnode
    end

  end # NNode

  #
  # For-statement Node of a Syntactic Tree.
  #
  class ForNode < NNode
    def unroll(cnt, indices)
      unrolled_bodies = []
      indices.each { |i|
        duped_body = @child[3].dup_tree # duplicate loop body.
        newnode = TNode.new(:DEC_NUMBER, i.to_s, 0, '')
        duped_body.child.each { |c| c.recursive_sub(cnt, newnode) } # substitute counter with an index.
        unrolled_bodies.concat duped_body.child
      }
      @parent.child.insert(@id_as_child + 1, *unrolled_bodies)
      @parent.child.delete_at(@id_as_child)
      @parent.refine
    end

    def recursive_reconstruct_value
      @child.compact.each { |c|
        v = c.recursive_reconstruct_value
      }
      cond0 = @child[0].recursive_reconstruct_value.strip_balanced_c_exp('(', ')').gsub(/;\z/, '')
      cond1 = @child[1].recursive_reconstruct_value.strip_balanced_c_exp('(', ')').gsub(/;\z/, '')
      cond2 = @child[2].recursive_reconstruct_value.strip_balanced_c_exp('(', ')').gsub(/;\z/, '')
      body  = @child[3].recursive_reconstruct_value.strip_balanced_c_exp('(', ')').gsub(/;\z/, '')
      @value = "for(#{cond0};#{cond1};#{cond2}){#{body}}"
    end

  end

  class GforArgNode < NNode
    def recursive_reconstruct_value
      value = ''
      @child.compact.each { |c|
        v = c.recursive_reconstruct_value
      }
      tagval    = @child[0].value
      paramsval = @child[1].value
      @value = "#{tagval}(#{paramsval}) "
    end
  end

  class GforParamsNode < NNode
    def recursive_reconstruct_value
      value = ''
      @child.compact.each { |c|
        v = c.recursive_reconstruct_value.gsub(/;\z/, '')
        value << "#{v}," if v
      }
      @value = "#{value[0..-2]}"
    end
  end

  class BinaryNode < NNode
    def recursive_reconstruct_value
      left_op, right_op = 255, 255
      left_op  = @child[0].child[1].value if @child[0].class == BinaryNode
      self_op  = @child[1].value
      right_op = @child[2].child[1].value if @child[2].class == BinaryNode
      v1 = @child[1].recursive_reconstruct_value
      v0 = @child[0].recursive_reconstruct_value
      v0 = v0.strip_balanced_c_exp('(', ')') if priority(left_op) <= priority(self_op)
      v2 = @child[2].recursive_reconstruct_value
      v2 = v2.strip_balanced_c_exp('(', ')') if priority(right_op) < priority(self_op)
      @value = "(#{v0}#{v1}#{v2})"
    end

    def priority(op)
      case op
      when '||'
        109
      when '&&'
        108
      when '|'
        107
      when '^'
        106
      when '&'
        105
      when '==', '!='
        104
      when '<=', '>=', '<', '>'
        103
      when '<<', '>>'
        102
      when '+', '-'
        101
      when '*', '/', '%'
        100
      else
        255
      end
    end

  end

  class TrinaryNode < NNode
    def recursive_reconstruct_value
      @child.compact.each { |c|
        v = c.recursive_reconstruct_value
      }
      condval  = @child[0].value
      trueval  = @child[1].value
      falseval = @child[2].value
      @value = "(#{condval} ? #{trueval} : #{falseval})"
    end

    def flatten
      flatten_nodes = []
      @child[1..2].reverse.each { |c|      # flatten true & false terms.
        flatten_nodes.concat c.flatten
      }

      # assign self to a tmporary node.
      #                                                                        parent   res = cond0 ? t0 : f0
      tmpnode   = VarrefExpNode.new2("tmp#{self.new_serialno}_")             # tmpnode  tmp00
      eqnode    = TNode.new('=', '=', 0, '')                                 # eqnode   =
      anode     = AssignNode.reduce(:assign_exp, '', tmpnode, eqnode, self)  # anode    tmp00 = cond1 ? t1 : f1
      enode     = ExpStmtNode.reduce(:exp_stmt, '', anode)                   # enode    tmp00 = cond1 ? t1 : f1;
      enode.volatile = true
      flatten_nodes.push enode

      # replace self with the tmporary node.
      #
      tmpnode2  = tmpnode.dup_tree                                           # tmpnode2 tmp00
      @parent.child[@id_as_child] = tmpnode2                                 # parent   res = tmp00

      flatten_nodes
    end
  end

  class IfNode < NNode
    def recursive_reconstruct_value
      @child.compact.each { |c|
        v = c.recursive_reconstruct_value
      }
      condval  = @child[0].value
      trueval  = @child[1].value
      if @child[2].value and not @child[2].value.empty?
        falseval = @child[2].value
        @value = "if(#{condval}){#{trueval}}else{#{falseval}}"
      else
        @value = "if(#{condval}){#{trueval}}"
      end
    end

    def flatten
      # convert each assign_exp inside if/else_stmt into
      # an assign_exp w/cond_exp on the rhs.
      #   e.g if (cond) {a=A;b=B;} else {a=A';}
      #     -> a = cond ? A : a;
      #        b = cond ? B : b;
      #        a = cond ? a : A';
      # note that the two assign_exps for a shown above should not be combined as
      #        a = cond ? A : A';
      # since this would potentially violate the order of assignments.
      #   e.g if (cond) {a=1;a=a*2;b=0;} else {a=0;b=a;a=a+1;}
      #
      cond_stmt  = @child[0]
      true_stmt  = @child[1]
      false_stmt = @child[2].symbol ? @child[2] : nil
      flatten_nodes = []

      true_stmt and assign_exps = true_stmt.recursive_select(:assign_exp) { |node, s|
        node.symbol == s and node.child[1].value =~ AssignOpRe
      }
#      puts("!!! trues: #{assign_exps.values}") if assign_exps
      assign_exps and assign_exps.compact.each { |anode|                                       # a =  A;            a += A;
        case anode.child[1].value
        when '='
          false_node = anode.child[0]
        when '+=', '-='
          false_node = TNode.new('0.0', '0.0', 0, '')
        else
          false_node = anode.child[0]
          vwarn("Non-supported abstract assignment    : #{anode.child[1]}")
        end
        trinode = TrinaryNode.reduce(:cond_exp, '', cond_stmt, anode.child[2], false_node)     # cond ? A : a       cond ? A : 0.0
        anode1  = AssignNode.reduce(:assign_exp, '', anode.child[0], anode.child[1], trinode)  # a += cond ? a : A
        enode   = ExpStmtNode.reduce(:exp_stmt, '', anode1)                                    # a += cond ? a : A;
        enode.volatile = true
        flatten_nodes.push enode
      }

      assign_exps = nil
      false_stmt and assign_exps = false_stmt.recursive_select(:assign_exp) { |node, s|
        node.symbol == s and node.child[1].value =~ AssignOpRe
      }
#      puts("!!! falses: #{assign_exps.values}") if assign_exps
      assign_exps and assign_exps.compact.each { |anode|                                       # a += A;
        case anode.child[1].value
        when '='
          false_node = anode.child[0]
        when '+=', '-='
          false_node = TNode.new('0.0', '0.0', 0, '')
        else
          false_node = anode.child[0]
          vwarn("Non-supported abstract assignment    : #{anode.child[1]}")
        end
        trinode = TrinaryNode.reduce(:cond_exp, '', cond_stmt, false_node, anode.child[2])     # cond ? a : A
        anode1  = AssignNode.reduce(:assign_exp, '', anode.child[0], anode.child[1], trinode)  # a += cond ? a : A
        enode   = ExpStmtNode.reduce(:exp_stmt, '', anode1)                                    # a += cond ? a : A;
        enode.volatile = true
        flatten_nodes.push enode
      }
      @parent.child.insert(@id_as_child + 1, *flatten_nodes)
      @parent.child.delete_at(@id_as_child)
      @parent.refine
    end
  end

  class AssignNode < NNode
    def recursive_reconstruct_value
      value = ''
      @child.compact.each { |c|
        v = c.recursive_reconstruct_value
        v = v.strip_balanced_c_exp('(', ')')
        value << "#{v} " if v
      }
      @value = "#{value[0..-2]}"
    end

    def flatten
      flatten_nodes = super # flatten descendants.

      return flatten_nodes if @parent.symbol == :exp_stmt # no need to flatten.

      # assign rhs to a temporary node.                                                self     a = b
      #
      tmpnode   = VarrefExpNode.new2("tmp#{self.new_serialno}_")                     # tmpnode  tmp00
      eqnode    = @child[1].dup_tree                                                 # eqnode   =
      anode     = AssignNode.reduce(:assign_exp, '', tmpnode, eqnode, @child[2])     # anode    tmp00 = b
      enode     = ExpStmtNode.reduce(:exp_stmt, '', anode)                           # enode    tmp00 = b;
      enode.volatile = true
      flatten_nodes.push enode

      # assign the temporary node to lhs.                                              self     a = b
      #
      tmpnode   = tmpnode.dup_tree                                                   # tmpnode  tmp00
      eqnode    = eqnode.dup_tree                                                    # eqnode   =
      anode     = AssignNode.reduce(:assign_exp, '', @child[0], eqnode, tmpnode)     # anode    a = tmp00
      enode     = ExpStmtNode.reduce(:exp_stmt, '', anode)                           # enode    a = tmp00;
      enode.volatile = true
      flatten_nodes.push enode

      # replace self with the temporary node.
      #
      tmpnode   = tmpnode.dup_tree            # tmpnode tmp00
      @parent.child[@id_as_child] = tmpnode   # parent   res = tmp00 or maybe sqrt(tmp00), if (tmp00), etc.

      flatten_nodes
    end
  end

  class AssignsNode < NNode
    def recursive_reconstruct_value
      value = ''
      @child.compact.each { |c|
        v = c.recursive_reconstruct_value.gsub(/;\z/, '')
        value << "#{v}, " if v
      }
      @value = "#{value[0..-2]}"
    end
  end

  class DefvarNode < NNode
    def recursive_reconstruct_value
      @child.compact.each { |c|
        v = c.recursive_reconstruct_value
      }
      specval   = @child[0].value
      declsval  = @child[1].value
      @value = "#{specval} #{declsval};"
    end
  end

  class DefSpecsNode < NNode
    def recursive_reconstruct_value
      @value
    end
  end

  class DeclsNode < NNode
    def recursive_reconstruct_value
      value = ''
      @child.compact.each { |c|
        v = c.recursive_reconstruct_value
        value << "#{v}," if v
      }
      @value = "#{value[0..-2]}"
    end
  end

  class ParamDefNode < NNode
    def recursive_reconstruct_value
      value = ''
      @child.compact.each { |c|
        v = c.recursive_reconstruct_value
        value << "#{v} " if v
      }
      @value = "#{value[0..-2]}"
    end
  end

  class ParamDefsNode < NNode
    def recursive_reconstruct_value
      value = ''
      @child.compact.each { |c|
        v = c.recursive_reconstruct_value
        value << "#{v}," if v
      }
      @value = "#{value[0..-2]}"
    end
  end

  class DefunNode < NNode
    def recursive_reconstruct_value
      value = ''
      @child.compact.each { |c|
        v = c.recursive_reconstruct_value
        value << "#{v} " if v
      }
      @value = "#{value[0..-2]}"
    end
  end

  class InitDeclNode < NNode
    def recursive_reconstruct_value
      @child.compact.each { |c|
        v = c.recursive_reconstruct_value
      }
      declval = @child[0].value
      initval = @child[1].value
      @value = "#{declval} = #{initval}"
    end
  end

  class InitializerNode < NNode
    def recursive_reconstruct_value
      value = ''
      @child.compact.each { |c|
        v = c.recursive_reconstruct_value
        value << "#{v}" if v
      }
      @value = "{#{value}}"
    end
  end

  class InitializersNode < NNode
    def recursive_reconstruct_value
      value = ''
      @child.compact.each { |c|
        v = c.recursive_reconstruct_value
        value << "#{v}," if v
      }
      @value = "#{value[0..-2]}"
    end
  end

  class SizeofExpNode < NNode
    def recursive_reconstruct_value
      val0 = @child[0].recursive_reconstruct_value
      val1 = @child[1].recursive_reconstruct_value
      @value = "#{val0} #{val1}"
    end
  end

  class SwitchNode < NNode
    def recursive_reconstruct_value
      val0 = @child[0].recursive_reconstruct_value
      val1 = @child[1].recursive_reconstruct_value
      @value = "switch(#{val0}){#{val1}}"
    end
  end

  class DowhileNode < NNode
    def recursive_reconstruct_value
      val0 = @child[0].recursive_reconstruct_value
      val1 = @child[1].recursive_reconstruct_value
      @value = "do {#{val0}} while(#{val1})"
    end
  end

  class WhileNode < NNode
    def recursive_reconstruct_value
      val0 = @child[0].recursive_reconstruct_value
      val1 = @child[1].recursive_reconstruct_value
      @value = "while(#{val0}){#{val1}}"
    end
  end

  class ReturnNode < NNode
    def recursive_reconstruct_value
      val1 = @child[1].recursive_reconstruct_value if @child[1]
      @value = "return #{val1};"
    end
  end

  class ExpStmtNode < NNode
    def recursive_reconstruct_value
      val0 = @child[0].recursive_reconstruct_value
      @value = "#{val0};"
    end

    def flatten
      flatten_nodes = @child[0].flatten
      flatten_nodes.push self
      flatten_nodes
    end
  end

  class VariableNode < TNode
    # to be defined
  end

  #
  # A node that refers to a defined variable (VariableNode).
  #
  class VarrefExpNode < NNode
    attr_accessor :iotype, :type

    def initialize(symbol, value, lineno, filename, child)
      super(symbol, value, lineno, filename, child)
      init_attrs()
    end

    #
    # A constructor with minimum args just for convenience.
    #
    def self.new2(value)
      newnode = self.new(:variable, value, 0, '', [ TNode.new(:IDENT, value, 0, '') ])
      newnode.init_attrs()
      newnode
    end

    def self.reduce(symbol, value, *nodes)
      newnode = super(symbol, value, *nodes)
      newnode.init_attrs()
      newnode
    end

    def init_attrs
      @volatile = true
      @iotype   = nil
      @type     = nil
    end

  end

  class ArrayExpNode < VarrefExpNode
    def recursive_reconstruct_value
      v0 = @child[0].recursive_reconstruct_value
      v2 = @child[2].recursive_reconstruct_value.strip_balanced_c_exp('(', ')')
      @value = "#{v0}[#{v2}]"
    end
  end

  class ArrayRangeExpNode < VarrefExpNode
    def recursive_reconstruct_value
      v0 = @child[0].recursive_reconstruct_value
      v2 = @child[2].recursive_reconstruct_value.strip_balanced_c_exp('(', ')')
      v4 = @child[4].recursive_reconstruct_value.strip_balanced_c_exp('(', ')')
      @value = "#{v0}[#{v2}..#{v4}]"
    end
  end

  class MemberExpNode < VarrefExpNode
    def recursive_reconstruct_value
      value = ''
      @child.compact.each { |c|
        v = c.recursive_reconstruct_value
        value << v if v
      }
      @value = value
    end
  end

  class MemberpExpNode < VarrefExpNode
    def recursive_reconstruct_value
      value = ''
      @child.compact.each { |c|
        v = c.recursive_reconstruct_value
        value << v if v
      }
      @value = value
    end
  end

  class FuncallExpNode < VarrefExpNode
    def recursive_reconstruct_value
      v0 = @child[0].recursive_reconstruct_value
      v2 = @child[2].recursive_reconstruct_value.strip_balanced_c_exp('(', ')')
      @value = "#{v0}(#{v2})"
    end
  end

  class PrefExpNode < VarrefExpNode
    def recursive_reconstruct_value
      value = ''
      @child.compact.each { |c|
        v = c.recursive_reconstruct_value
        value << v if v
      }
      @value = value
    end
  end

  class PderefExpNode < VarrefExpNode
    def recursive_reconstruct_value
      value = ''
      @child.compact.each { |c|
        v = c.recursive_reconstruct_value
        value << v if v
      }
      @value = value
    end
  end

end # module Goose

class Array

  def uniq_value
    uniqed = []
    self.each { |n0|
      found = false
      uniqed.each { |n1|
        if n1.value == n0.value
          found = true 
        end
      }
      uniqed.push n0 unless found
    }
    uniqed
  end

  def include_value?(node)
    found = false
    self.each { |n0|
      if n0.value == node.value
        found = true
      end
    }
    found
  end

  def values(sep = ' ')
    buf = self.inject('') { |buf, elm|
      buf += "#{elm.value}" + sep
    }
    buf[0..-sep.size-1]  # remove a trailing sep.
  end

end # Array
