######################################################################
#
# C.default_parser and the parse_* methods.
#
# Yeah, this could be so much faster.
#
######################################################################

module C
  @@default_parser = Parser.new
  def self.default_parser
    @@default_parser
  end
  def self.default_parser=(val)
    @@default_parser = val
  end

  class Node
    #
    # Return true if `str' is parsed to something `==' to this Node.
    # str is first converted to a String using #to_s, then given to
    # self.class.parse (along with the optional `parser').
    #
    def match?(str, parser=nil)
      node = self.class.parse(str.to_s, parser) rescue (return false)
      self == node
    end
    #
    # Same as #match?.
    #
    def =~(*args)
      match?(*args)
    end
    private
  end
  class NodeList
    #
    # As defined in Node.
    #
    def match?(arr, parser=nil)
      arr = arr.to_a
      return false if arr.length != self.length
      each_with_index do |node, i|
        node.match?(arr[i], parser) or return false
      end
      return true
    end
  end

  def self.parse(s, parser=nil)
    TranslationUnit.parse(s, parser)
  end

  class TranslationUnit
    def self.parse(s, parser=nil)
      parser ||= C.default_parser
      parser.parse(s)
    end
  end

  class Declaration
    def self.parse(s, parser=nil)
      parser ||= C.default_parser
      ents = parser.parse(s).entities
      if ents.length == 1 &&          # int i; int j;
          ents[0].is_a?(Declaration)  # void f() {}
        return ents[0].detach
      else
        raise ParseError, "invalid Declaration"
      end
    end
  end

  class Parameter
    def self.parse(s, parser=nil)
      parser ||= C.default_parser
      ents = parser.parse("void f(#{s}) {}").entities
      if ents.length == 1              &&  # ) {} void (
          ents[0].is_a?(FunctionDef)   &&  # ); void(
          ents[0].type.params          &&  #
          ents[0].type.params.length <= 1  # x,y
        param = ents[0].type.params[0]
        if param.nil?
          return Parameter.new(Void.new)
        else
          return param.detach
        end
      else
        raise ParseError, "invalid Parameter"
      end
    end
  end

  class Declarator
    def self.parse(s, parser=nil)
      parser ||= C.default_parser
      # if there's a ':', declare in a struct so we can populate num_bits
      if s =~ /:/
        ents = parser.parse("struct {int #{s};};").entities
        if ents.length == 1 &&                              # i:1;}; struct {int i
            ents[0].type.members.length == 1 &&             # i:1; int j
            ents[0].type.members[0].declarators.length == 1 # i:1,j
          return ents[0].type.members[0].declarators[0].detach
        end
      else
        ents = parser.parse("int #{s};").entities
        if ents.length == 1 &&               # f; int f;
            ents[0].declarators.length == 1  # i,j
          return ents[0].declarators[0].detach
        end
      end
      raise ParseError, "invalid Declarator"
    end
  end

  class FunctionDef
    def self.parse(s, parser=nil)
      parser ||= C.default_parser
      ents = parser.parse(s).entities
      if ents.length == 1 &&          # void f(); void g();
          ents[0].is_a?(FunctionDef)  # int i;
        return ents[0].detach
      else
        raise ParseError, "invalid FunctionDef"
      end
    end
  end

  class Enumerator
    def self.parse(s, parser=nil)
      parser ||= C.default_parser
      ents = parser.parse("enum {#{s}};").entities
      if ents.length == 1            &&     # } enum {
          ents[0].is_a?(Declaration) &&     # } f() {
          ents[0].type.members.length == 1  # X, Y
        return ents[0].type.members[0].detach
      else
        raise ParseError, "invalid Enumerator"
      end
    end
  end

  class MemberInit
    def self.parse(s, parser=nil)
      parser ||= C.default_parser
      ents = parser.parse("int f() {struct s x = {#{s}};}").entities
      if ents.length == 1                              &&  # } int f() {
          ents[0].def.stmts.length == 1                &&  # }} f() {{
          ents[0].def.stmts[0].declarators.length == 1 &&  # 1}, y
          ents[0].def.stmts[0].declarators[0].init.member_inits.length == 1 # 1, 2
        return ents[0].def.stmts[0].declarators[0].init.member_inits[0].detach
      else
        raise ParseError, "invalid MemberInit"
      end
    end
  end

  class Member
    def self.parse(s, parser=nil)
      parser ||= C.default_parser
      ents = parser.parse("int f() {struct s x = {.#{s} = 1};}").entities
      if ents.length == 1                              &&  # a = 1};} int f() {struct s x = {a
          ents[0].def.stmts.length == 1                &&  # a = 1}; struct s y = {.a
          #ents[0].def.stmts[0].length == 1            &&  # a = 1}, x = {.a
          ents[0].def.stmts[0].declarators.length == 1 &&  # a = 1}, x = {.a
          ents[0].def.stmts[0].declarators[0].init.member_inits.length == 1 &&        # x = 1, y
          ents[0].def.stmts[0].declarators[0].init.member_inits[0].member   &&        # 1
          ents[0].def.stmts[0].declarators[0].init.member_inits[0].member.length == 1 # a .b
        return ents[0].def.stmts[0].declarators[0].init.member_inits[0].member[0].detach
      else
        raise ParseError, "invalid Member"
      end
    end
  end

  class Statement
    def self.parse(s, parser=nil)
      parser ||= C.default_parser
      ents = parser.parse("void f() {#{s}}").entities
      if ents.length == 1               &&      # } void f() {
          ents[0].def.stmts.length == 1 &&      # ;;
          ents[0].def.stmts[0].is_a?(self)      # int i;
        return ents[0].def.stmts[0].detach
      else
        raise ParseError, "invalid #{self}"
      end
    end
  end

  class Label
    def self.parse(s, parser=nil)
      parser ||= C.default_parser
      ents = parser.parse("void f() {switch (0) #{s};}").entities
      if ents.length == 1                              &&  # } void f() {
          ents[0].def.stmts.length == 1                &&  # ;
          ents[0].def.stmts[0].stmt                    &&  #
          ents[0].def.stmts[0].stmt.labels.length == 1 &&  # x
          ents[0].def.stmts[0].stmt.labels[0].is_a?(self)
        return ents[0].def.stmts[0].stmt.labels[0].detach
      else
        raise ParseError, "invalid #{self}"
      end
    end
  end

  class Expression
    def self.parse(s, parser=nil)
      parser ||= C.default_parser
      if s =~ /\A(\s*(\/\*.*?\*\/)?)*\{/
        # starts with a brace -- must be a CompoundLiteral.  in this
        # case, use a Declarator since only those can handle typeless
        # CompoundLiterals.
        ents = parser.parse("int i = #{s};").entities
        if ents.length == 1                 &&  # 1; int i = 1
            ents[0].declarators.length == 1 &&  # 1, j = 2
            ents[0].declarators[0].init.is_a?(self)
          return ents[0].declarators[0].init.detach
        else
          raise ParseError, "invalid #{self}"
        end
      else
        ents = parser.parse("void f() {#{s};}").entities
        if ents.length == 1                                 &&  # } void f() {
            ents[0].def.stmts.length == 1                   &&  # ;
            ents[0].def.stmts[0].is_a?(ExpressionStatement) &&  # int i
            ents[0].def.stmts[0].expr.is_a?(self)
          return ents[0].def.stmts[0].expr.detach
        else
          raise ParseError, "invalid #{self}"
        end
      end
    end
  end

  class Type
    def self.parse(s, parser=nil)
      parser ||= C.default_parser
      ents = parser.parse("void f() {(#{s})x;}").entities
      if ents.length == 1                        &&  # 1);} void f() {(int
          ents[0].def.stmts.length == 1          &&  # 1); (int
          ents[0].def.stmts[0].expr.type.is_a?(self)
        return ents[0].def.stmts[0].expr.type.detach
      else
        raise ParseError, "invalid #{self}"
      end
    end
  end

  # Make sure we didn't miss any
  CORE_C_NODE_CLASSES.each do |c|
    c.respond_to? :parse or
      raise "#{c}.parse not defined"
  end
end
