view python/ppci/c3/builder.py @ 354:5477e499b039

Added some sort of string functionality
author Windel Bouwman
date Thu, 13 Mar 2014 18:59:06 +0100
parents 6f4753202b9a
children 39bf68bf1891
line wrap: on
line source

import logging
from .lexer import Lexer
from .parser import Parser
from .codegenerator import CodeGenerator
from .scope import createTopScope, Scope
from .visitor import AstPrinter, Visitor
from .astnodes import Package, Function, Identifier, Symbol


class C3Pass:
    def __init__(self, diag):
        self.diag = diag
        self.logger = logging.getLogger('c3')
        self.visitor = Visitor()

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

    def visit(self, pkg, pre, post):
        self.visitor.visit(pkg, pre, post)


class ScopeFiller(C3Pass):
    scoped_types = [Package, Function]

    def __init__(self, diag, topScope, packages):
        super().__init__(diag)
        self.topScope = topScope
        self.packages = packages

    """ Scope is attached to the correct modules. """
    def addScope(self, pkg):
        self.logger.debug('Adding scoping to package {}'.format(pkg.name))
        self.pkg = pkg
        # Prepare top level scope and set scope to all objects:
        self.scopeStack = [self.topScope]
        modScope = Scope(self.CurrentScope)
        self.scopeStack.append(modScope)
        self.visit(pkg, self.enterScope, self.quitScope)
        assert len(self.scopeStack) == 2

        self.logger.debug('Resolving imports for package {}'.format(pkg.name))
        # Handle imports:
        for i in pkg.imports:
            if i not in self.packages:
                self.error('Cannot import {}'.format(i))
                continue
            pkg.scope.addSymbol(self.packages[i])

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

    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):
        # Attach scope to references:
        if type(sym) is Identifier:
            sym.scope = self.CurrentScope

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

        # Create subscope for items creating a scope:
        if type(sym) in self.scoped_types:
            newScope = Scope(self.CurrentScope)
            self.scopeStack.append(newScope)
            sym.innerScope = self.CurrentScope

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


class Builder:
    """
        Generates IR-code from c3 source.
        Reports errors to the diagnostics system.
    """
    def __init__(self, diag, target):
        self.logger = logging.getLogger('c3')
        self.diag = diag
        self.lexer = Lexer(diag)
        self.parser = Parser(diag)
        self.cg = CodeGenerator(diag)
        self.topScope = createTopScope(target)  # Scope with built in types

    def build(self, srcs, imps=[]):
        """ Create IR-code from sources """
        self.logger.debug('Building {} source files'.format(len(srcs)))
        iter(srcs)  # Check if srcs are iterable
        iter(imps)
        self.ok = True
        self.pkgs = {}

        # Parsing stage (phase 1)
        def doParse(src):
            tokens = self.lexer.lex(src)
            return self.parser.parseSource(tokens)
        s_pkgs = set(map(doParse, srcs))
        i_pkgs = set(map(doParse, imps))
        all_pkgs = s_pkgs | i_pkgs
        if not all(all_pkgs):
            self.ok = False
            return

        # Fix scopes and package refs (phase 1.5)
        packages = {pkg.name: pkg for pkg in all_pkgs}
        self.pkgs = packages

        scopeFiller = ScopeFiller(self.diag, self.topScope, packages)
        # Fix scopes:
        for pkg in all_pkgs:
            scopeFiller.addScope(pkg)
        if not all(pkg.ok for pkg in all_pkgs):
            self.ok = False
            return

        # Generate intermediate code (phase 2)
        # Only return ircode when everything is OK
        for pkg in all_pkgs & s_pkgs:
            yield self.cg.gencode(pkg)
        if not all(pkg.ok for pkg in all_pkgs):
            self.ok = False