logo
class

compiler::ResolveImports

sys::Obj
  compiler::CompilerSupport
    compiler::CompilerStep
      compiler::ResolveImports
   1  //
   2  // Copyright (c) 2006, Brian Frank and Andy Frank
   3  // Licensed under the Academic Free License version 3.0
   4  //
   5  // History:
   6  //   26 Dec 05  Brian Frank  Creation
   7  //    5 Jun 06  Brian Frank  Ported from Java to Fan
   8  //
   9  
  10  **
  11  ** ResolveImports maps every Using node in each CompilationUnit to a pod
  12  ** and ensures that it exists and that no imports are duplicated.  Then we
  13  ** create a map for all the types which are imported into the CompilationUnit
  14  ** so that the Parser can quickly distinguish between a type identifier and
  15  ** other identifiers.  The results of this step populate Using.resolvedXXX and
  16  ** CompilationUnit.importedTypes.
  17  **
  18  class ResolveImports : CompilerStep
  19  {
  20  
  21  //////////////////////////////////////////////////////////////////////////
  22  // Construction
  23  //////////////////////////////////////////////////////////////////////////
  24  
  25    **
  26    ** Constructor takes the associated Compiler
  27    **
  28    new make(Compiler compiler)
  29      : super(compiler)
  30    {
  31      resolved[compiler.pod.name] = compiler.pod
  32    }
  33  
  34  //////////////////////////////////////////////////////////////////////////
  35  // Methods
  36  //////////////////////////////////////////////////////////////////////////
  37  
  38    **
  39    ** Run the step
  40    **
  41    override Void run()
  42    {
  43      log.verbose("ResolveImports")
  44  
  45      // process each unit for Import.pod
  46      units.each |CompilationUnit unit|
  47      {
  48        resolveImports(unit)
  49      }
  50      bombIfErr
  51  
  52      // process each unit for CompilationUnit.importedTypes
  53      units.each |CompilationUnit unit|
  54      {
  55        resolveImportedTypes(unit)
  56      }
  57      bombIfErr
  58    }
  59  
  60    **
  61    ** Resolve all the imports in the specified unit
  62    ** and ensure there are no duplicates.
  63    **
  64    private Void resolveImports(CompilationUnit unit)
  65    {
  66      // map to keep track of duplicate imports
  67      // within this compilation unit
  68      dups := Str:Using[:]
  69  
  70      // process each import statement (remember the
  71      // first one is an implicit import of sys)
  72      unit.usings.each |Using u|
  73      {
  74        podName := u.podName
  75  
  76        // check that this podName was in the compiler's
  77        // input dependencies
  78        if (!ns.depends.containsKey(podName) && !compiler.input.isScript)
  79          err("Using '$podName' which is not a declared dependency for '$compiler.pod.name'", u.location)
  80  
  81        // check for duplicate imports
  82        key := podName
  83        if (u.typeName != null) key += "::$u.typeName"
  84        if (dups.containsKey(key))
  85        {
  86          err("Duplicate using '$key'", u.location)
  87          return
  88        }
  89        dups[key] = u
  90  
  91        // if already resolved, then just use it
  92        u.resolvedPod = resolved[podName]
  93  
  94        // resolve the import and cache in resolved map
  95        if (u.resolvedPod == null)
  96        {
  97          pod := ns.resolvePod(podName, false)
  98          if (pod == null)
  99          {
 100            err("Pod not found '$podName'", u.location)
 101            return
 102          }
 103          resolved[podName] = u.resolvedPod = pod
 104        }
 105  
 106        // if type specified, then resolve type
 107        if (u.typeName != null)
 108        {
 109          u.resolvedType = u.resolvedPod.resolveType(u.typeName, false)
 110          if (u.resolvedType== null)
 111          {
 112            err("Type not found in pod '$podName::$u.typeName'", u.location)
 113            return
 114          }
 115        }
 116      }
 117    }
 118  
 119    **
 120    ** Create a unified map of type names to CType[] for all the
 121    ** imports in the specified unit (this includes types within
 122    ** the pod being compilied itself).  For example if foo::Thing
 123    ** and bar::Thing are imported, then importedTypes would contain
 124    **   "Thing" : [foo::Thing, bar::Thing]
 125    **
 126    private Void resolveImportedTypes(CompilationUnit unit)
 127    {
 128      // name -> CType[]
 129      types := Str:CType[][:]
 130  
 131      // add types for my own pod
 132      addAll(types, compiler.pod.types)
 133  
 134      // add pod level imports first
 135      unit.usings.each |Using u|
 136      {
 137        if (u.typeName == null)
 138          addAll(types, u.resolvedPod.types)
 139      }
 140  
 141      // add type specific imports last (these
 142      // override any pod level imports)
 143      unit.usings.each |Using u|
 144      {
 145        if (u.typeName != null)
 146        {
 147          if (u.asName == null)
 148          {
 149            types[u.typeName] = [u.resolvedType]
 150          }
 151          else
 152          {
 153            remove(types, u.resolvedType)
 154            types[u.asName] = [u.resolvedType]
 155          }
 156        }
 157      }
 158  
 159      /*
 160  
 161  
 162  
 163  
 164  
 165  
 166      // save away on unit
 167      unit.importedTypes = types
 168    }
 169  
 170    private Void addAll(Str:CType[] types, CType[] toAdd)
 171    {
 172      toAdd.each |CType t|
 173      {
 174        list := types[t.name]
 175        if (list == null) types[t.name] = list = CType[,]
 176        list.add(t)
 177      }
 178    }
 179  
 180    private Void remove(Str:CType[] types, CType t)
 181    {
 182      list := types[t.name]
 183      if (list != null)
 184      {
 185        for (i:=0; i<list.size; ++i)
 186          if (list[i].qname == t.qname) { list.removeAt(i)break }
 187      }
 188    }
 189  
 190  //////////////////////////////////////////////////////////////////////////
 191  // Utils
 192  //////////////////////////////////////////////////////////////////////////
 193  
 194    **
 195    ** Resolve a fully qualified type name into its CType representation.
 196    ** This may be a TypeDef within the compilation units or could be
 197    ** an imported type.  If the type name cannot be resolved then we
 198    ** log an error and return null.
 199    **
 200    static CType resolveQualified(CompilerSupport cs, Str podName, Str typeName, Location location)
 201    {
 202      // first check pod being compiled
 203      if (podName == cs.compiler.pod.name)
 204      {
 205        t := cs.compiler.pod.resolveType(typeName, false)
 206        if (t == null)
 207        {
 208          cs.err("Type '$typeName' not found within pod being compiled", location)
 209          return null
 210        }
 211        return t
 212      }
 213  
 214      // otherwise we need to try to resolve pod
 215      pod := cs.ns.resolvePod(podName, false)
 216      if (pod == null)
 217      {
 218        cs.err("Pod not found '$podName'", location);
 219        return null
 220      }
 221  
 222      // check that we have a dependency on the pod
 223      if (!cs.ns.depends.containsKey(podName) && !cs.compiler.input.isScript)
 224        cs.err("Using '$podName' which is not a declared dependency for '$cs.compiler.pod.name'", location)
 225  
 226      // now try to lookup type
 227      t := pod.resolveType(typeName, false)
 228      if (t == null)
 229      {
 230        cs.err("Type '$typeName' not found in pod '$podName'", location);
 231        return null
 232      }
 233  
 234      return t
 235    }
 236  
 237  //////////////////////////////////////////////////////////////////////////
 238  // Fields
 239  //////////////////////////////////////////////////////////////////////////
 240  
 241    Str:CPod resolved := Str:CPod[:]  // reuse CPods across units
 242  
 243  }