# -*- coding: UTF-8 -*-
#    This file is part of arena.
#   
#    arena is free software: you can redistribute it and/or modify it
#    under the terms of the GNU General Public License as published by the
#    Free Software Foundation; either version 3 of the License or, at your
#    option, any later version.
#
#    arena is distributed in the hope that it will be useful, but
#    WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABLITY
#    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
#    License for more details.
#
#    You should have received a copy of the GNU General Public License along
#    with arena.  If not, see <http://www.gnu.org/licenses/>. 

## @file
#  This file defines the various symbol types that may appear in a symbol table 

from Library.Memory.StringTrees import AStringLeaf
from Library.String.Utilities import String

## Symbol category codes
class SymbolCategory:
    ## An undefined symbol
    undefined = 0
    ## A symbol that defines a type
    typedef = 1
    ## A symbol that defines a literal value
    literal = 2
    ## A symbol that defines a constant value
    constant = 3
    ## A symbol that defines a variable
    variable = 4
    ## A symbol that defines a member of a structured type
    member = 5
    ## A symbol that defines a parameter passed to a routine
    parameter = 6
    ## An external symbol
    external = 7
    ## The total number of symbol types
    count = 8
    
## A basic symbol.
class ASymbol(AStringLeaf):
    ## Constructor
    def __init__(Self, thisName):
        AStringLeaf.__init__(Self, thisName)

    ## Initializer        
    def init(Self):
        result = AStringLeaf.init(Self)
        
        if result:
            # Establish private attributes
            # The symbol category, which indicates how it was defined
            Self._category = SymbolCategory.undefined
            # Establish public attributes
            # The symbol table in which the symbol is stored
            Self.scope = 0
            # The name of the stream in which the symbol is declared
            Self.source = ""            
            # The line number in the source on which the symbol is declared
            Self.line = 0
            
        return result
        
    ## Stream the symbol to the specified stream.
    def streamTo(Self, Dest):
        result = 0
        
        if Dest is not None:
            # Write the scope and index to the stream
            result = Dest.write(Self.scope)
            result += Dest.write(Self.index)
            # Write the category code
            result += Dest.write(Self._category)
                
        return result
                
    # IsPrintable implementation -----------------------------------------------
    ## Construct a string representation of the symbol.  
    #    
    #  In the base implementation, this is simply a quoted form of the
    #  symbol name.
    def toString(Self):
        return String.quote(Self._name)                
                
    # Properties ---------------------------------------------------------------
    ## Retrieve the symbol category
    def category(Self):
        return Self._category
                
## A string literal.
class AStringLiteral(ASymbol):
    
    ## Initializer
    def init(Self):
        result = ASymbol.init(Self)
        
        if result:
            # Set the symbol category
            Self._category = SymbolCategory.literal
            
        return result
        
    # IsStreamable implementation ----------------------------------------------
    ## Write the literal to the specified stream.
    #    
    #  This routine calls the inherited method, then appends the value of
    #  the literal to the stream. 
    def writeTo(Self, Dest):
        result = 0
        
        if Dest is not None:
            result = ASymbol.writeTo(Self, Dest)
            result += Dest.writeString(Self._name)
            
        return result
        
## A constant.
class AConstant(ASymbol):
    ## Construct a new constant instance with the specified name and value.
    def __init__(Self, thisName, thisValue):
        ASymbol.__init__(Self, thisName)
        
        Self.value = thisValue
    
    ## Initializer    
    def init(Self):
        result = ASymbol.init(Self)
        
        if result:
            # Establish public attributes
            # The value of the constant
            Self.value = None
            # Set the symbol category
            Self._category = SymbolCategory.constant
            
        return result
        
    ## Write the constant to the specified stream.
    #    
    #  This routine calls the inherited method, then appends the value of the
    #  constant.
    def streamTo(Self, Dest):
        result = 0
        
        if Dest is not None:
            result = ASymbol.streamTo(Self, Dest)
            
            # Write the value to the stream
            if isinstance(Self.value, float):
                # Write a value identification byte
                result += Dest.write('f')
                
            else:
                # Write a value identification byte
                result += Dest.write('i')
                
            # Write the value
            result += Dest.write(Self.value)
            
        return result
        
    # IsPrintable implementation -----------------------------------------------
    ## Construct a string representation of the constant.
    #    
    # This representation is merely a quoted form of the constant value.
    def toString(Self):
        return String.quote(str(Self.value))
