# Path: rbc/codegen/astnode.py
# def ast_node(cls):
#     """Class decorator which registers the AST node in the _NODE_CLASSES map."""
#     _NODE_CLASSES[cls.__name__] = cls
#     return cls
# 
# class ASTNode(object):
#     """An AST node with parameters directly accessible as attributes."""
#     def __init__(self, **kwargs):
#         # Set attributes on ourself directly from the keyword args.
#         for k, v in kwargs.items():
#             setattr(self, k, v)
# 
# Path: rbc/codegen/context.py
# def address_to_llvm_ptr(context, address_val, ptr_type):
#     """Cast a llvm Value representing a word into a pointer. Performs the
#     appropriate conversion from word-oriented addresses to llvm's
#     byte-oriented addresses. Requires builder to be not None.
# 
#     This method will arrange that
# 
#         address_to_llvm_ptr(llvm_ptr_to_address(ptr_val), ptr_type)
# 
#     is elided into a simple bitcast. This implies that ptr_val must be word
#     aligned. It's the responsibility of the code generator to make sure
#     that's the case.
# 
#     """
#     # If this address came from a pointer, just return the punned pointer.
#     if hasattr(address_val, 'b_ptr'):
#         return context.builder.bitcast(address_val.b_ptr, ptr_type)
# 
#     bpw_const = ir.Constant(context.word_type, context.bytes_per_word)
#     byte_address = context.builder.mul(address_val, bpw_const,
#                                        flags=['nuw', 'nsw'])
#     ptr_val = context.builder.inttoptr(byte_address, ptr_type)
# 
#     # HACK: we tag the value with a 'b_address' attribute so that
#     # llvm_ptr_to_address() can elide address to pointer followed by pointer
#     # to address conversion.
#     ptr_val.b_address = address_val
# 
#     return ptr_val
# 
# def create_constructor(context, priority=0, name_hint=None):
#     """Create a function which is called on module load. Functions are called in
#     increasing order or priority. (The order of functions with equal priority is
#     undefined.)
# 
#     """
#     ctor_record_type = context.ctor_record_type
#     priority_type, ctor_func_ptr_type, data_ptr_type = ctor_record_type.elements
# 
#     # Create the constructor function
#     if name_hint is None:
#         func_name = context.module.get_unique_name('__ctor')
#     else:
#         func_name = context.module.get_unique_name(
#             '__ctor.{}'.format(name_hint))
#     func = ir.Function(context.module, ctor_func_ptr_type.pointee, func_name)
#     func.linkage = 'private'
# 
#     context.ctor_records.append(ir.Constant(ctor_record_type, [
#         ir.Constant(priority_type, priority), func,
#         ir.Constant(data_ptr_type, None)]))
# 
#     return func
# 
# def create_aligned_global(module, type_, name):
#     """Construct and return an ir.GlobalVariable instance which is guaranteed to
#     have a word-aligned pointer. The symbol name is automatically mangled to
#     avoid collision with C symbols.
# 
#     """
#     return _ModifiedGlobalVariable(module, type_, mangle_symbol_name(name))
# 
# def mangle_symbol_name(name):
#     """Mangle an external symbol name for use with B."""
#     return 'b.' + name
# 
# Path: rbc/codegen/expression.py
# class ConstantIntValue(RValue):
#     """An constant integer value."""
#     def emit(self, context):
#         return ir.Constant(context.word_type, self.value)
# 
# class LLVMPointerValue(RValue):
#     """An RValue corresponding to an llvm pointer type punned to the machine
#     word type. The pointer value is divided by the machine word length in bytes
#     to yield a value which is word-oriented.
# 
#     It is a Bad Idea (TM) to pass this AST node a pointer which is not
#     word-aligned. There is no runtime check for alignment. So, "caveat caller".
# 
#     This class is not marked as an AST node and so cannot be constructed via the
#     make_node() function. This reflects the fact that this is an "internal" node
#     not intended for construction by the semantics.
# 
#     """
#     @needs_builder
#     def emit(self, context):
#         return llvm_ptr_to_address(context, self.value)


from llvmlite import ir
from .astnode import ast_node, ASTNode
from .context import (
    address_to_llvm_ptr, create_constructor, create_aligned_global,
    mangle_symbol_name
)
from .expression import ConstantIntValue, LLVMPointerValue
# have their declare() method called before any emittables have emit() called.
# They should use this opportunity to register themselves with the current
# context.
#
# A global should expect the context's builder attribute to be None.

@ast_node
class SimpleDefinition(ASTNode):
    """An initialised external variable."""
    def __init__(self, **kwargs):
        ASTNode.__init__(self, **kwargs)
        self._lvalue = None

    def declare(self, context):
        # A simple global definition is represented in the llvm IR as a pointer
        # to the global value. Thus we may use a LLVMPointerValue to represent
        # the address of the lvalue.
        value = create_aligned_global(
            context.module, context.word_type, self.name)
        value.modifiers = ['align {}'.format(context.bytes_per_word)]

        # Set variable's initialiser. Don't bother with a constructor function
        # if the initialiser is a constant integer
        if self.init is not None and isinstance(self.init, ConstantIntValue):
            init_val = self.init.value
        else:
            init_val = 0
        value.initializer = ir.Constant(context.word_type, init_val)

        # Register this variable as an external symbol.

# Path: rbc/codegen/astnode.py
# def ast_node(cls):
#     """Class decorator which registers the AST node in the _NODE_CLASSES map."""
#     _NODE_CLASSES[cls.__name__] = cls
#     return cls
# 
# class ASTNode(object):
#     """An AST node with parameters directly accessible as attributes."""
#     def __init__(self, **kwargs):
#         # Set attributes on ourself directly from the keyword args.
#         for k, v in kwargs.items():
#             setattr(self, k, v)
# 
# Path: rbc/codegen/context.py
# def address_to_llvm_ptr(context, address_val, ptr_type):
#     """Cast a llvm Value representing a word into a pointer. Performs the
#     appropriate conversion from word-oriented addresses to llvm's
#     byte-oriented addresses. Requires builder to be not None.
# 
#     This method will arrange that
# 
#         address_to_llvm_ptr(llvm_ptr_to_address(ptr_val), ptr_type)
# 
#     is elided into a simple bitcast. This implies that ptr_val must be word
#     aligned. It's the responsibility of the code generator to make sure
#     that's the case.
# 
#     """
#     # If this address came from a pointer, just return the punned pointer.
#     if hasattr(address_val, 'b_ptr'):
#         return context.builder.bitcast(address_val.b_ptr, ptr_type)
# 
#     bpw_const = ir.Constant(context.word_type, context.bytes_per_word)
#     byte_address = context.builder.mul(address_val, bpw_const,
#                                        flags=['nuw', 'nsw'])
#     ptr_val = context.builder.inttoptr(byte_address, ptr_type)
# 
#     # HACK: we tag the value with a 'b_address' attribute so that
#     # llvm_ptr_to_address() can elide address to pointer followed by pointer
#     # to address conversion.
#     ptr_val.b_address = address_val
# 
#     return ptr_val
# 
# def create_constructor(context, priority=0, name_hint=None):
#     """Create a function which is called on module load. Functions are called in
#     increasing order or priority. (The order of functions with equal priority is
#     undefined.)
# 
#     """
#     ctor_record_type = context.ctor_record_type
#     priority_type, ctor_func_ptr_type, data_ptr_type = ctor_record_type.elements
# 
#     # Create the constructor function
#     if name_hint is None:
#         func_name = context.module.get_unique_name('__ctor')
#     else:
#         func_name = context.module.get_unique_name(
#             '__ctor.{}'.format(name_hint))
#     func = ir.Function(context.module, ctor_func_ptr_type.pointee, func_name)
#     func.linkage = 'private'
# 
#     context.ctor_records.append(ir.Constant(ctor_record_type, [
#         ir.Constant(priority_type, priority), func,
#         ir.Constant(data_ptr_type, None)]))
# 
#     return func
# 
# def create_aligned_global(module, type_, name):
#     """Construct and return an ir.GlobalVariable instance which is guaranteed to
#     have a word-aligned pointer. The symbol name is automatically mangled to
#     avoid collision with C symbols.
# 
#     """
#     return _ModifiedGlobalVariable(module, type_, mangle_symbol_name(name))
# 
# def mangle_symbol_name(name):
#     """Mangle an external symbol name for use with B."""
#     return 'b.' + name
# 
# Path: rbc/codegen/expression.py
# class ConstantIntValue(RValue):
#     """An constant integer value."""
#     def emit(self, context):
#         return ir.Constant(context.word_type, self.value)
# 
# class LLVMPointerValue(RValue):
#     """An RValue corresponding to an llvm pointer type punned to the machine
#     word type. The pointer value is divided by the machine word length in bytes
#     to yield a value which is word-oriented.
# 
#     It is a Bad Idea (TM) to pass this AST node a pointer which is not
#     word-aligned. There is no runtime check for alignment. So, "caveat caller".
# 
#     This class is not marked as an AST node and so cannot be constructed via the
#     make_node() function. This reflects the fact that this is an "internal" node
#     not intended for construction by the semantics.
# 
#     """
#     @needs_builder
#     def emit(self, context):
#         return llvm_ptr_to_address(context, self.value)


# Path: rbc/codegen/external.py
from llvmlite import ir
from .astnode import ast_node, ASTNode
from .context import (
    address_to_llvm_ptr, create_constructor, create_aligned_global,
    mangle_symbol_name
)
from .expression import ConstantIntValue, LLVMPointerValue

# have their declare() method called before any emittables have emit() called.
# They should use this opportunity to register themselves with the current
# context.
#
# A global should expect the context's builder attribute to be None.

@ast_node
class SimpleDefinition(ASTNode):
    """An initialised external variable."""
    def __init__(self, **kwargs):
        ASTNode.__init__(self, **kwargs)
        self._lvalue = None

    def declare(self, context):
        # A simple global definition is represented in the llvm IR as a pointer
        # to the global value. Thus we may use a LLVMPointerValue to represent
        # the address of the lvalue.
        value = create_aligned_global(
            context.module, context.word_type, self.name)
        value.modifiers = ['align {}'.format(context.bytes_per_word)]

        # Set variable's initialiser. Don't bother with a constructor function
        # if the initialiser is a constant integer
        if self.init is not None and isinstance(self.init, ConstantIntValue):
            init_val = self.init.value
        else:
            init_val = 0
        value.initializer = ir.Constant(context.word_type, init_val)

        # Register this variable as an external symbol.

        self._lvalue = LLVMPointerValue(value=value).dereference()