view python/c3/analyse.py @ 272:e64bae57cda8

refactor ir
author Windel Bouwman
date Sat, 31 Aug 2013 17:58:54 +0200
parents 7416c923a02a
children 6f2423df0675
line wrap: on
line source

import logging
from .visitor import Visitor
from .astnodes import *
from .scope import Scope, topScope
from .typecheck import theType

class Analyzer:
    """ 
        Context handling is done here.
        Scope is attached to the correct modules.
        This class checks names and references 
    """
    def __init__(self, diag):
        self.diag = diag
        self.logger = logging.getLogger('c3')

    def analyzePackage(self, pkg, packageProvider):
        self.logger.info('Checking package {}'.format(pkg.name))
        self.ok = True
        visitor = Visitor()
        # Prepare top level scope:
        self.scopeStack = [topScope]
        modScope = Scope(self.CurrentScope)
        self.scopeStack.append(modScope)
        visitor.visit(pkg, self.enterScope, self.quitScope)
        del self.scopeStack

        # Handle imports:
        for i in pkg.imports:
            ip = packageProvider.getPackage(i)
            if not ip:
                self.error('Cannot import {}'.format(i))
                continue
            for x in ip.declarations:
                modScope.addSymbol(x)
        visitor.visit(pkg, self.findRefs)
        visitor.visit(pkg, self.sanity)
        return self.ok

    def error(self, msg, loc=None):
        self.ok = False
        self.diag.error(msg, loc)

    @property
    def CurrentScope(self):
        return self.scopeStack[-1]

    # Scope creation:
    def addSymbol(self, sym):
        if self.CurrentScope.hasSymbol(sym.name):
            self.error('Redefinition of {0}'.format(sym.name), sym.loc)
        else:
            self.CurrentScope.addSymbol(sym)

    def enterScope(self, sym):
        # Distribute the scope:
        sym.scope = self.CurrentScope

        # Add symbols to current scope:
        if isinstance(sym, Symbol) or isinstance(sym, DefinedType):
            self.addSymbol(sym)

        # Create subscope:
        if type(sym) in [Package, Function]:
            newScope = Scope(self.CurrentScope)
            self.scopeStack.append(newScope)
            sym.innerScope = self.CurrentScope

    def quitScope(self, sym):
        # Pop out of scope:
        if type(sym) in [Package, Function]:
            self.scopeStack.pop(-1)

    # Reference fixups:
    def resolveDesignator(self, d, scope):
        assert type(d) is Designator, type(d)
        assert type(scope) is Scope
        if scope.hasSymbol(d.tname):
            s = scope.getSymbol(d.tname)
            if hasattr(s, 'addRef'):
                # TODO: make this nicer
                s.addRef(None)
            return s
        else:
            self.ok = False
            msg = 'Cannot resolve name {0}'.format(d.tname)
            self.diag.error(msg, d.loc)

    def resolveType(self, t, scope):
        # TODO: what about structs?
        if type(t) is PointerType:
            t.ptype = self.resolveType(t.ptype, scope)
            return t
        elif type(t) is StructureType:
            offset = 0
            for mem in t.mems:
                mem.offset = offset
                mem.typ = self.resolveType(mem.typ, scope)
                offset += theType(mem.typ).bytesize
            t.bytesize = offset
            return t
        elif type(t) is Designator:
            t = self.resolveDesignator(t, scope)
            if t:
                return self.resolveType(t, scope)
        elif isinstance(t, Type):
            # Already resolved??
            return t
        else:
            raise Exception('Error resolving type {} {}'.format(t, type(t)))

    def findRefs(self, sym):
        if type(sym) in [Constant] or isinstance(sym, Variable):
            sym.typ = self.resolveType(sym.typ, sym.scope)
        elif type(sym) is TypeCast:
            sym.to_type = self.resolveType(sym.to_type, sym.scope)
        elif type(sym) is VariableUse:
            sym.target = self.resolveDesignator(sym.target, sym.scope)
        elif type(sym) is FunctionCall:
            varuse = sym.proc
            sym.proc = self.resolveDesignator(varuse.target, sym.scope)
        elif type(sym) is Function:
            # Checkup function type:
            ft = sym.typ
            ft.returntype = self.resolveType(ft.returntype, sym.scope)
            ft.parametertypes = [self.resolveType(pt, sym.scope) for pt in ft.parametertypes]
        elif type(sym) is DefinedType:
            sym.typ = self.resolveType(sym.typ, sym.scope)

    def sanity(self, sym):
        if type(sym) is FunctionType:
            pass
        elif type(sym) is Function:
            pass