Skip to content

Synthesizing names

icefapper edited this page Feb 10, 2017 · 10 revisions
  • Synthesizing begins when the whole source is parsed, to avoid things like below:
var l; // emitName=self:l
{
  let l; // emitName=synth:l->l1
  (function() {
    var l1; // emitName=self:l1
    l; // emitName=synth:l->l1 // conflict
  
  })();
}
  • if a catch parameter is an identifier, it will be treated as a real, non-synthesized name.

  • a var name is treated as a real, non-synthesized name.

  • a complex catch parameter is replace by a synthesized name; that synthesized name will not be added to the surrounding concrete scope's list of synthesized names, but the names that are contained in the original param are synthesized and added to the surrounding concrete scope's list of synthesized names.

  • name synthesis begins from the topmost scope; name synthesis is outlined in the pseudo-code below:

  function synthName(n: name): name {
    var mustNotBe = [ownerScope.varNames, ownerScope.createMapForTheEmitNamesOfReferencedNames()];
    for each (var scope that has referenced n) {
      mustNotBe.push(scope.varNames);
    }
    var nameMap = createMapContainingExistingVarNames(mustNotBe);
    var nonce = 0, candidate = n;
    while (true) {
      if (n not in.own nameMap) { break }
      nonce++;
      candidate = n + "" + nonce;
    }
    
    return candidate;
  }

#Some terminology ##Handover When a scope ends, all the references that have not been resolved in it are handed over to the parent scope, where they are reference again; for example:

{ // scope A:
  // A.unresolved: {}
  { // scope B:
    // B.unresolved: {}
    a; // B.ref('a')-> B.unresolved: {'a'}
    // B.unresolved: {'a'}
  } // handover B -> A = A.ref('a') -> A.unresolved: {'a'}
  // A.unresolved: {'a'}
  // etc.
}

or

{ // scope A
  // A.unresolved: {}
  var a = 12;
  { // scope B
    // B.unresolved: {}
    a; // B.ref('a')-> B.unresolved: {'a'}
    // B.unresolved: {'a'}
  } // handover B -> A = A.ref('a') -> <resolved>
  // A.unresolved: {}
  // etc.
}

##Real Scope A scope whose own names need not be synthesized; function/modules/script scopes are the only such scopes.

##List of referencing real scopes: It is the list of all "Real scope" (cf. above) a binding has directly or indirectly been referenced in; please note that the elements of this list are necessarily the descendants of the scope the binding has been defined in; for example:

// scope A
var a;
{ // scope B; not real
  a; // ref.a.lorrs = []
  (function() { // scope E; it's real
    var a1;
    { // scope L
    a; // ref.a.lorrs = []
    } // handover will cause 'a' get referenced in E: E.ref('a') = E.ref.a.lorrs += B.ref.a.lorrs
  }) // E -> B: B.ref('a') = B.ref.a.lorrs += [E, E.ref.a.lorrs]
} // B -> A: A.ref('a') = A.ref.a.lorrs += B.ref.a.lorrs
// A.ref.a.lorrs ==> [E]

#Further comments on how the name synthesizer works
the `synthName` thing, listed above, gives all the details on how name synthesis works; but in case it does not look intuitive to you, then this is how it actually works -- given a name <n>, it keeps renaming it to <n>1, <n>2, etc., until the following criteria are all met:
  * the synth name does not exist in the surrounding real scope's `varNames`
  * the synth name does not exist in the list of the emit-names of the surrounding real scope's referenced names
  * the synth name does not exist in the list of the emit-names of the current scope's referenced names
  * the synth name does not exist in the `varNames` of any of the current binding's `lorrs` list