######################################################################
#
# All those Node classes.
#
######################################################################

module C

  # ------------------------------------------------------------------
  #                         Class declarations
  # ------------------------------------------------------------------

  class Statement            < Node           ; abstract; end
  class Label                < Node           ; abstract; end
  class Expression           < Node           ; abstract; end
  class UnaryExpression      < Expression     ; abstract; end
  class PostfixExpression    < UnaryExpression; abstract; end
  class PrefixExpression     < UnaryExpression; abstract; end
  class BinaryExpression     < Expression     ; abstract; end
  class AssignmentExpression < Expression     ; abstract; end
  class Literal              < Expression     ; abstract; end
  class Type                 < Node           ; abstract; end
  class IndirectType         < Type           ; abstract; end
  class DirectType           < Type           ; abstract; end
  class PrimitiveType        < DirectType     ; abstract; end

  class TranslationUnit      < Node                ; end
  class Declaration          < Node                ; end
  class Declarator           < Node                ; end
  class FunctionDef          < Node                ; end
  class Parameter            < Node                ; end
  class Enumerator           < Node                ; end
  class MemberInit           < Node                ; end
  class Member               < Node                ; end

  class Block                < Statement           ; end
  class If                   < Statement           ; end
  class Switch               < Statement           ; end
  class While                < Statement           ; end
  class For                  < Statement           ; end
  class Goto                 < Statement           ; end
  class Continue             < Statement           ; end
  class Break                < Statement           ; end
  class Return               < Statement           ; end
  class ExpressionStatement  < Statement           ; end

  class PlainLabel           < Label               ; end
  class Default              < Label               ; end
  class Case                 < Label               ; end

  class Comma                < Expression          ; end
  class Conditional          < Expression          ; end
  class Variable             < Expression          ; end
  class BlockExpression      < Expression          ; end

  class Index                < PostfixExpression   ; end
  class Call                 < PostfixExpression   ; end
  class Dot                  < PostfixExpression   ; end
  class Arrow                < PostfixExpression   ; end
  class PostInc              < PostfixExpression   ; end
  class PostDec              < PostfixExpression   ; end

  class Cast                 < PrefixExpression    ; end
  class Address              < PrefixExpression    ; end
  class Dereference          < PrefixExpression    ; end
  class Sizeof               < PrefixExpression    ; end
  class Positive             < PrefixExpression    ; end
  class Negative             < PrefixExpression    ; end
  class PreInc               < PrefixExpression    ; end
  class PreDec               < PrefixExpression    ; end
  class BitNot               < PrefixExpression    ; end
  class Not                  < PrefixExpression    ; end

  class Add                  < BinaryExpression    ; end
  class Subtract             < BinaryExpression    ; end
  class Multiply             < BinaryExpression    ; end
  class Divide               < BinaryExpression    ; end
  class Mod                  < BinaryExpression    ; end
  class Equal                < BinaryExpression    ; end
  class NotEqual             < BinaryExpression    ; end
  class Less                 < BinaryExpression    ; end
  class More                 < BinaryExpression    ; end
  class LessOrEqual          < BinaryExpression    ; end
  class MoreOrEqual          < BinaryExpression    ; end
  class BitAnd               < BinaryExpression    ; end
  class BitOr                < BinaryExpression    ; end
  class BitXor               < BinaryExpression    ; end
  class ShiftLeft            < BinaryExpression    ; end
  class ShiftRight           < BinaryExpression    ; end
  class And                  < BinaryExpression    ; end
  class Or                   < BinaryExpression    ; end

  class Assign               < AssignmentExpression; end
  class MultiplyAssign       < AssignmentExpression; end
  class DivideAssign         < AssignmentExpression; end
  class ModAssign            < AssignmentExpression; end
  class AddAssign            < AssignmentExpression; end
  class SubtractAssign       < AssignmentExpression; end
  class ShiftLeftAssign      < AssignmentExpression; end
  class ShiftRightAssign     < AssignmentExpression; end
  class BitAndAssign         < AssignmentExpression; end
  class BitXorAssign         < AssignmentExpression; end
  class BitOrAssign          < AssignmentExpression; end

  class StringLiteral        < Literal             ; end
  class CharLiteral          < Literal             ; end
  class CompoundLiteral      < Literal             ; end
  class IntLiteral           < Literal             ; end
  class FloatLiteral         < Literal             ; end

  class Pointer              < IndirectType        ; end
  class Array                < IndirectType        ; end
  class Function             < IndirectType        ; end

  class Struct               < DirectType          ; end
  class Union                < DirectType          ; end
  class Enum                 < DirectType          ; end
  class CustomType           < DirectType          ; end

  class Void                 < PrimitiveType       ; end
  class Int                  < PrimitiveType       ; end
  class Float                < PrimitiveType       ; end
  class Char                 < PrimitiveType       ; end
  class Bool                 < PrimitiveType       ; end
  class Complex              < PrimitiveType       ; end
  class Imaginary            < PrimitiveType       ; end

  # ------------------------------------------------------------------
  #                       Class implementations
  # ------------------------------------------------------------------

  class Node
    initializer
  end

  class TranslationUnit
    child :entities, lambda{NodeChain.new}
    initializer :entities
  end

  class Declaration
    field :storage
    child :type
    child :declarators, lambda{NodeArray.new}
    field :inline?
    initializer :type, :declarators
    def typedef?
      storage.equal? :typedef
    end
    def extern?
      storage.equal? :extern
    end
    def static?
      storage.equal? :static
    end
    def auto?
      storage.equal? :auto
    end
    def register?
      storage.equal? :register
    end
  end

  class Declarator
    child :indirect_type
    field :name
    child :init
    child :num_bits
    initializer :indirect_type, :name, :init, :num_bits
    def declaration
      parent and parent.parent
    end
    #
    # Return (a copy of) the type of the variable this Declarator
    # declares.
    #
    def type
      if indirect_type
        ret = indirect_type.clone
        ret.direct_type = declaration.type.clone
        return ret
      else
        declaration.type.clone
      end
    end
  end

  class FunctionDef
    field :storage
    field :inline?
    child :type
    field :name
    child :def, lambda{Block.new}
    field :no_prototype?
    initializer :type, :name, :def
    def extern?
      storage.equal? :extern
    end
    def static?
      storage.equal? :static
    end
    def prototype=(val)
      self.no_prototype = !val
    end
    def prototype?
      !no_prototype?
    end
  end

  class Parameter
    field :register?
    child :type
    field :name
    initializer :type, :name
  end

  class Enumerator
    field :name
    child :val
    initializer :name, :val
  end

  class MemberInit
    # member is a _NodeList_ of:
    #   -- Member (for struct/union members)
    #   -- Expression (for array members)
    child :member
    child :init
    initializer :member, :init
  end

  class Member
    field :name
    initializer :name
  end

  # ------------------------------------------------------------------
  #                             Statements
  # ------------------------------------------------------------------

  class Block
    child :labels, lambda{NodeArray.new}
    child :stmts, lambda{NodeChain.new}
    initializer :stmts
  end

  class If
    child :labels, lambda{NodeArray.new}
    child :cond
    child :then
    child :else
    initializer :cond, :then, :else
  end

  class Switch
    child :labels, lambda{NodeArray.new}
    child :cond
    child :stmt
    initializer :cond, :stmt
  end

  class While
    child :labels, lambda{NodeArray.new}
    field :do?
    child :cond
    child :stmt
    initializer :cond, :stmt, :do?
  end

  class For
    child :labels, lambda{NodeArray.new}
    child :init
    child :cond
    child :iter
    child :stmt
    initializer :init, :cond, :iter, :stmt
  end

  class Goto
    child :labels, lambda{NodeArray.new}
    field :target
    initializer :target
  end

  class Continue
    child :labels, lambda{NodeArray.new}
  end

  class Break
    child :labels, lambda{NodeArray.new}
  end

  class Return
    child :labels, lambda{NodeArray.new}
    child :expr
    initializer :expr
  end

  class ExpressionStatement
    child :labels, lambda{NodeArray.new}
    child :expr
    initializer :expr
  end

  # ------------------------------------------------------------------
  #                               Labels
  # ------------------------------------------------------------------

  class PlainLabel
    field :name
    initializer :name
  end

  class Default
  end

  class Case
    child :expr
    initializer :expr
  end

  # ------------------------------------------------------------------
  #                            Expressions
  # ------------------------------------------------------------------

  class Comma
    child :exprs, lambda{NodeArray.new}
    initializer :exprs
  end

  class Conditional
    child :cond
    child :then
    child :else
    initializer :cond, :then, :else
  end

  class Variable
    field :name
    initializer :name
  end

  class BlockExpression
    child :block, lambda{Block.new}
    initializer :block
  end

  # ------------------------------------------------------------------
  #                         PrefixExpressions
  # ------------------------------------------------------------------

  class Cast
    child :type
    child :expr
    initializer :type, :expr
  end

  class Address
    child :expr
    initializer :expr
  end

  class Dereference
    child :expr
    initializer :expr
  end

  class Sizeof
    child :expr
    initializer :expr
  end

  class Positive
    child :expr
    initializer :expr
  end

  class Negative
    child :expr
    initializer :expr
  end

  class PreInc
    child :expr
    initializer :expr
  end

  class PreDec
    child :expr
    initializer :expr
  end

  class BitNot
    child :expr
    initializer :expr
  end

  class Not
    child :expr
    initializer :expr
  end

  # ------------------------------------------------------------------
  #                         PostfixExpressions
  # ------------------------------------------------------------------

  class Index
    child :expr
    child :index
    initializer :expr, :index
  end

  class Call
    child :expr
    child :args, lambda{NodeArray.new}
    initializer :expr, :args
  end

  class Dot
    child :expr
    child :member
    initializer :expr, :member
  end

  class Arrow
    child :expr
    child :member
    initializer :expr, :member
  end

  class PostInc
    child :expr
    initializer :expr
  end

  class PostDec
    child :expr
    initializer :expr
  end

  # ------------------------------------------------------------------
  #                         BinaryExpressions
  # ------------------------------------------------------------------

  class BinaryExpression
    class << self
      #
      # The operator (a String) pertaining to the class (e.g.,
      # Add.operator is '+').
      #
      attr_accessor :operator
    end
  end

  class Add
    child :expr1
    child :expr2
    initializer :expr1, :expr2
    self.operator = '+'
  end

  class Subtract
    child :expr1
    child :expr2
    initializer :expr1, :expr2
    self.operator = '-'
  end

  class Multiply
    child :expr1
    child :expr2
    initializer :expr1, :expr2
    self.operator = '*'
  end

  class Divide
    child :expr1
    child :expr2
    initializer :expr1, :expr2
    self.operator = '/'
  end

  class Mod
    child :expr1
    child :expr2
    initializer :expr1, :expr2
    self.operator = '%'
  end

  class Equal
    child :expr1
    child :expr2
    initializer :expr1, :expr2
    self.operator = '=='
  end

  class NotEqual
    child :expr1
    child :expr2
    initializer :expr1, :expr2
    self.operator = '!='
  end

  class Less
    child :expr1
    child :expr2
    initializer :expr1, :expr2
    self.operator = '<'
  end

  class More
    child :expr1
    child :expr2
    initializer :expr1, :expr2
    self.operator = '>'
  end

  class LessOrEqual
    child :expr1
    child :expr2
    initializer :expr1, :expr2
    self.operator = '<='
  end

  class MoreOrEqual
    child :expr1
    child :expr2
    initializer :expr1, :expr2
    self.operator = '>='
  end

  class BitAnd
    child :expr1
    child :expr2
    initializer :expr1, :expr2
    self.operator = '&'
  end

  class BitOr
    child :expr1
    child :expr2
    initializer :expr1, :expr2
    self.operator = '|'
  end

  class BitXor
    child :expr1
    child :expr2
    initializer :expr1, :expr2
    self.operator = '^'
  end

  class ShiftLeft
    child :expr1
    child :expr2
    initializer :expr1, :expr2
    self.operator = '<<'
  end

  class ShiftRight
    child :expr1
    child :expr2
    initializer :expr1, :expr2
    self.operator = '>>'
  end

  class And
    child :expr1
    child :expr2
    initializer :expr1, :expr2
    self.operator = '&&'
  end

  class Or
    child :expr1
    child :expr2
    initializer :expr1, :expr2
    self.operator = '||'
  end

  # ------------------------------------------------------------------
  #                       AssignmentExpressions
  # ------------------------------------------------------------------

  class AssignmentExpression
    class << self
      #
      # The operator (a String) pertaining to the class (e.g.,
      # Assign.operator is '=').
      #
      attr_accessor :operator
    end
  end

  class Assign
    child :lval
    child :rval
    initializer :lval, :rval
    self.operator = '='
  end

  class MultiplyAssign
    child :lval
    child :rval
    initializer :lval, :rval
    self.operator = '*='
  end

  class DivideAssign
    child :lval
    child :rval
    initializer :lval, :rval
    self.operator = '/='
  end

  class ModAssign
    child :lval
    child :rval
    initializer :lval, :rval
    self.operator = '%='
  end

  class AddAssign
    child :lval
    child :rval
    initializer :lval, :rval
    self.operator = '+='
  end

  class SubtractAssign
    child :lval
    child :rval
    initializer :lval, :rval
    self.operator = '-='
  end

  class ShiftLeftAssign
    child :lval
    child :rval
    initializer :lval, :rval
    self.operator = '<<='
  end

  class ShiftRightAssign
    child :lval
    child :rval
    initializer :lval, :rval
    self.operator = '>>='
  end

  class BitAndAssign
    child :lval
    child :rval
    initializer :lval, :rval
    self.operator = '&='
  end

  class BitXorAssign
    child :lval
    child :rval
    initializer :lval, :rval
    self.operator = '^='
  end

  class BitOrAssign
    child :lval
    child :rval
    initializer :lval, :rval
    self.operator = '|='
  end

  # ------------------------------------------------------------------
  #                              Literals
  # ------------------------------------------------------------------

  class StringLiteral
    field :prefix
    field :val
    initializer :val, :prefix
    def wide?
      prefix == 'L'
    end
    def wide=(val)
      return if wide? == !!val
      self.prefix = val ? 'L' : nil
    end
  end

  class CharLiteral
    field :prefix
    field :val
    initializer :val, :prefix
    def wide?
      prefix == 'L'
    end
    def wide=(val)
      return if wide? == !!val
      self.prefix = val ? 'L' : nil
    end
  end

  class CompoundLiteral
    child :type
    child :member_inits, lambda{NodeArray.new}
    initializer :type, :member_inits
  end

  class IntLiteral
    field :format, :dec
    field :val
    field :suffix
    initializer :val
    def dec?
      format.equal? :dec
    end
    def hex?
      format.equal? :hex
    end
    def oct?
      format.equal? :oct
    end
  end

  class FloatLiteral
    field :format, :dec
    field :val
    field :exponent
    field :suffix
    initializer :val
  end

  # ------------------------------------------------------------------
  #                               Types
  # ------------------------------------------------------------------

  class DirectType
    def direct_type
      self
    end
    def indirect_type
      nil
    end
  end

  class IndirectType
    def direct_type
      if type.is_a? IndirectType
        type.direct_type
      else
        type
      end
    end
    def direct_type=(val)
      if type.is_a? IndirectType
        type.direct_type = val
      else
        self.type = val
      end
    end
    def indirect_type
      ret = self.clone
      t = ret
      while t.type.is_a? IndirectType
        t = t.type
      end
      t.type = nil
      return ret
    end
  end

  class Pointer
    field :const?
    field :restrict?
    field :volatile?
    child :type
    initializer :type
  end

  class Array
    field :const?
    field :restrict?
    field :volatile?
    child :type
    child :length
    initializer :type, :length
  end

  class Function
    field :const?
    field :restrict?
    field :volatile?
    child :type
    child :params
    field :var_args?
    initializer :type, :params
  end

  class Struct
    field :const?
    field :restrict?
    field :volatile?
    field :name
    child :members
    initializer :name, :members
  end

  class Union
    field :const?
    field :restrict?
    field :volatile?
    field :name
    child :members
    initializer :name, :members
  end

  class Enum
    field :const?
    field :restrict?
    field :volatile?
    field :name
    child :members
    initializer :name, :members
  end

  class CustomType
    field :const?
    field :restrict?
    field :volatile?
    field :name
    initializer :name
  end

  class Void
    field :const?
    field :restrict?
    field :volatile?
  end

  class Int
    field :const?
    field :restrict?
    field :volatile?
    field :longness, 0
    field :unsigned?, false
    initializer :longness
    def signed?
      !unsigned?
    end
    def signed=(val)
      self.unsigned = !val
    end
    def short?
      longness.equal?(-1)
    end
    def plain?
      longness.equal? 0
    end
    def long?
      longness.equal? 1
    end
    def long_long?
      longness.equal? 2
    end
  end

  class Float
    field :const?
    field :restrict?
    field :volatile?
    field :longness, 0
    initializer :longness
    def plain?
      longness.equal? 0
    end
    def double?
      longness.equal? 1
    end
    def long_double?
      longness.equal? 2
    end
  end

  class Char
    field :const?
    field :restrict?
    field :volatile?
    # 6.2.5p15: `char', `signed char', and `unsigned char' are
    # distinct types
    field :signed
    def signed?
      signed.equal? true
    end
    def unsigned?
      signed.equal? false
    end
    def plain?
      signed.nil?
    end
  end

  class Bool
    field :const?
    field :restrict?
    field :volatile?
  end

  class Complex
    field :const?
    field :restrict?
    field :volatile?
    field :longness, 0
    initializer :longness
    def plain?
      longness.equal? 0
    end
    def double?
      longness.equal? 1
    end
    def long_double?
      longness.equal? 2
    end
  end

  class Imaginary
    field :const?
    field :restrict?
    field :volatile?
    field :longness, 0
    initializer :longness
    def plain?
      longness.equal? 0
    end
    def double?
      longness.equal? 1
    end
    def long_double?
      longness.equal? 2
    end
  end

  # ------------------------------------------------------------------
  #                            Tag classes
  # ------------------------------------------------------------------

  # classify the node classes by including modules
  tagger = lambda do |included, *includers|
    includers.each{|mod| mod.send(:include, included)}
  end

  # expression classes
  module ArithmeticExpression; end
  module BitwiseExpression   ; end
  module LogicalExpression   ; end
  module RelationalExpression; end
  module ShiftExpression     ; end
  #
  tagger.call(ArithmeticExpression,
              PostInc, PostDec, Positive, Negative, PreInc, PreDec, Add,
              Subtract, Multiply, Divide, Mod)
  tagger.call(BitwiseExpression,
              BitNot, BitAnd, BitOr, BitXor)
  tagger.call(LogicalExpression,
              Not, And, Or)
  tagger.call(RelationalExpression,
              Equal, NotEqual, Less, More, LessOrEqual, MoreOrEqual)
  tagger.call(ShiftExpression,
              ShiftLeft, ShiftRight)

  # ------------------------------------------------------------------
  #                        CORE_C_NODE_CLASSES
  # ------------------------------------------------------------------

  CORE_C_NODE_CLASSES = [
    TranslationUnit,
    Declaration,
    Declarator,
    FunctionDef,
    Parameter,
    Enumerator,
    MemberInit,
    Member,

    Block,
    If,
    Switch,
    While,
    For,
    Goto,
    Continue,
    Break,
    Return,
    ExpressionStatement,

    PlainLabel,
    Default,
    Case,

    Comma,
    Conditional,
    Variable,
    BlockExpression,

    Index,
    Call,
    Dot,
    Arrow,
    PostInc,
    PostDec,

    Cast,
    Address,
    Dereference,
    Sizeof,
    Positive,
    Negative,
    PreInc,
    PreDec,
    BitNot,
    Not,

    Add,
    Subtract,
    Multiply,
    Divide,
    Mod,
    Equal,
    NotEqual,
    Less,
    More,
    LessOrEqual,
    MoreOrEqual,
    BitAnd,
    BitOr,
    BitXor,
    ShiftLeft,
    ShiftRight,
    And,
    Or,

    Assign,
    MultiplyAssign,
    DivideAssign,
    ModAssign,
    AddAssign,
    SubtractAssign,
    ShiftLeftAssign,
    ShiftRightAssign,
    BitAndAssign,
    BitXorAssign,
    BitOrAssign,

    StringLiteral,
    CharLiteral,
    CompoundLiteral,
    IntLiteral,
    FloatLiteral,

    Pointer,
    Array,
    Function,

    Struct,
    Union,
    Enum,
    CustomType,

    Void,
    Int,
    Float,
    Char,
    Bool,
    Complex,
    Imaginary
  ]

  # check we didn't miss any
  expected_classes = Node.subclasses_recursive.sort_by{|c| c.name}
  expected_classes -= NodeList.subclasses_recursive
  expected_classes -= [NodeList]
  expected_classes -= [
    Statement,
    Label,
    Expression,
    UnaryExpression,
    PostfixExpression,
    PrefixExpression,
    BinaryExpression,
    AssignmentExpression,
    Literal,
    Type,
    IndirectType,
    DirectType,
    PrimitiveType
  ]
  #
  CORE_C_NODE_CLASSES.sort_by{|c| c.name} == expected_classes or raise
end
