view ide/compiler/modules.py @ 1:92df07bc2081

Initial import of compiler
author windel
date Sun, 18 Sep 2011 19:00:29 +0200
parents
children
line wrap: on
line source

import struct
from .errors import Error
from .nodes import *
from .builtin import integer, real, char, boolean, void
import os.path

"""
 File format for compiled modules.
 * [11] magic identifier
 * [STR] mod name
 * [STR] signature, a md5 signature of the module.
 * [I32] size of code
 * code image
 * [I32] entrypoint for initcode
 * imported modules
 ** [I32] num of imported modules
 *** [STR] name of module
 *** signature of the module
 *** [I32] offset in the process image where the interface symbols must be placed
 * public interface
 ** [I32] num of interface elements
 *** [STR] proc name
 *** [I32] offset in code image 
 *** [type] return type
 *** [I32] number of parameters
 **** parameter
 ***** parameter kind
 ***** parameter name
 ***** parameter type
"""

MAGIC = b'LCFOSMODC'

loadedModules = []

def loadModule(modname):
   """ returns a Module object specified by a name """
   # Check if the module was already loaded:
   for mod in loadedModules:
      if mod.name == modname:
         return mod

   # Try to load the module from file:
   srcfilename = modname + '.mod'
   binfilename = modname + '.bin'
   sourceExists = os.path.exists(srcfilename)
   if os.path.exists(binfilename):
      if sourceExists:
         compileModule()
      else:
         return loadModuleFromFile(binfilename)
   else:
      Error("Cannot load module '{0}'!".format(modname))

def loadModuleFromFile(filename):
   f = open(filename, 'rb')
   magic = f.read(len(MAGIC))
   assert(magic == MAGIC)

   # Helper functions:
   def readI32():
      int32, = struct.unpack('<I', f.read(4))
      return int32
   def readSTR():
      length = readI32()
      b = f.read(length)
      return b.decode(encoding='ascii')
   def readType():
      code, = f.read(1)
      basetypes = {0x11:integer, 0x12:real, 0x13:char,0x14:boolean, 0x15:void}
      if code in list(basetypes.keys()):
         return basetypes[code]
      elif code == 0x20:
         dimension, elementType = readI32(), readType()
         return ArrayType(dimension, elementType)
      elif code == 0x21:
         returntype = readType()
         numparams = readI32()
         parameters = []
         kinds = {0x1:'value', 0x2:'var', 0x3:'const'}
         for i in range(numparams):
            byt, = f.read(1)
            kind = kinds[byt]
            name, typ = readSTR(), readType()
            parameters.append(Parameter(kind, name, typ))
         return ProcedureType(parameters, returntype)
      else:
         Error('Reading of this typ not supported')

   # Begin of actual loading
   modname = readSTR()
   modsignature = readSTR()
   codesize = readI32()
   image = f.read(codesize)
   initcodeentry = readI32()
   # Check which modules this module loads:
   numimports = readI32()
   imports = []
   for i in range(numimports):
      modname = readSTR()
      signature = readSTR()
      symname = readSTR()
      offset = readI32()
      impsym = ImportedSymbol(modname, symname)
      impsym.signature = signature
      impsym.offset = offset
      imports.append(impsym)
   # Modules exported interface:
   numexports = readI32()
   exports = []
   for i in range(numexports):
      name = readSTR()
      imageoffset = readI32() # Offset in image where symbol is located
      typ = readType()
      export = ExportedSymbol(name, typ)
      export.imageoffset = imageoffset
      exports.append(export)
   f.close()

   # Construct imported module object:
   module = Module(modname)
   module.signature = modsignature
   module.exports = exports # Symbols provided to other modules
   module.imports = imports # Symbols of others used by this module.
   module.initcodeentry = initcodeentry
   module.image = image # The binary blob
   global loadedModules
   loadedModules.append(module)
   return module

def storeModule(mod, filename):
   """ Class to store a module in a file """
   f = open(filename, 'wb')

   def writeI32(int32):
      f.write( struct.pack('<I', int32) )
   def writeSTR(s):
      writeI32(len(s))
      f.write(bytes(s, encoding='ascii'))
   def writeType(typ):
      if type(typ) is BaseType:
         basetypecode = {'integer': 0x11, 'real': 0x12, 'char': 0x13, 'boolean':0x14, 'void':0x15}
         code = basetypecode[typ.name]
         f.write( bytes([code]))
      elif type(typ) is ArrayType:
         f.write(bytes([0x20]))
         writeI32(typ.dimension)
         writeType(typ.elementType)
      elif type(typ) is ProcedureType:
         f.write(bytes([0x21]))
         writeType(typ.returntype)
         writeI32(len(typ.parameters))
         for parameter in typ.parameters:
            kinds = {'value': 0x1, 'var': 0x2, 'const': 0x3}
            kind = kinds[parameter.kind]
            f.write(bytes([kind]))
            writeSTR(parameter.name)
            writeType(parameter.typ)
      else:
         Error('Type storage not implemented {0}'.format(typ))

   # Begin of actual storage function
   f.write(MAGIC)
   writeSTR(mod.name)
   writeSTR(mod.signature)
   writeI32(len(mod.image))
   f.write(bytes(mod.image))
   writeI32(mod.initcodeentry)
   # modules imported symbols:
   writeI32(len(mod.imports))
   for imp in mod.imports:
      writeSTR(imp.modname)
      writeSTR(imp.signature)
      writeSTR(imp.name)
      writeI32(imp.offset)
   # modules provided interface
   writeI32(len(mod.exports))
   # Store exported symbols:
   for sym in mod.exports:
      writeSTR(sym.name) # proc name
      writeI32(sym.imageoffset) # proc entry point
      writeType(sym.typ) # Procedure type
   f.close()

   storeModuleInCache(mod)

def storeModuleInCache(newmod):
   global loadedModules
   for mod in loadedModules:
      if newmod.name == mod.name:
         return
   loadedModules.append(newmod)