import { Dictionary , attemptName , CompileError } from "../common" ; export interface ScopeVariableInfo { name : string ; type : string ; translatedName : string ; } export class ScopeVariables { private _owner : ScopeStack ; private _variables : Dictionary < ScopeVariableInfo > ; private _parentScope : ScopeVariables ; private _argMap : Dictionary < string > ; constructor ( owner , parentScope = null , argMap = null ) { this . _owner = owner ; this . _variables = new Dictionary < ScopeVariableInfo > ( ) ; this . _parentScope = parentScope || null ; this . _argMap = argMap ; } public addVariable ( name , type ) { if ( this . _variables . has ( name ) || ( this . _argMap != null && this . _argMap . has ( name ) ) ) { throw new CompileError ( `${ name } is already declared.` ) ; } else { let translatedName = this . _owner . newTranslatedName ( name , type ) ; this . _variables . set ( name , { name : name , type : type , translatedName : translatedName } ) ; } } public nextVariable ( type ) { let name = attemptName ( "tmp" , ( name ) => ! this . _variables . has ( name ) && ! ( this . _argMap != null && this . _argMap . has ( name ) ) ) ; this . addVariable ( name , type ) ; return this . getVariable ( name ) ; } public getVariable ( name ) { if ( this . _variables . has ( name ) ) { return this . _variables . get ( name ) ; } else if ( this . _argMap != null && this . _argMap . has ( name ) ) { return this . _parentScope . getVariable ( this . _argMap . get ( name ) ) ; } else if ( this . _parentScope ) { return this . _parentScope . getVariable ( name ) ; } else { throw new CompileError ( `${ name } is undefined.` ) ; } } public get parentScope ( ) : ScopeVariables { return this . _parentScope ; } } export class ScopeStack { private _currentScope : ScopeVariables ; private _globalScope : ScopeVariables ; private _translatedNames : Dictionary < ScopeVariableInfo > ; constructor ( ) { this . _translatedNames = new Dictionary < ScopeVariableInfo > ( ) ; this . _globalScope = new ScopeVariables ( this ) ; this . _currentScope = this . _globalScope ; } public resetScope ( ) { this . _translatedNames = new Dictionary < ScopeVariableInfo > ( ) ; this . _globalScope = new ScopeVariables ( this ) ; this . _currentScope = this . _globalScope ; } public pushScope ( argMap = null ) { this . _currentScope = new ScopeVariables ( this , this . _currentScope , argMap ) ; } public popScope ( ) { this . _currentScope = this . _currentScope . parentScope ; } public newTranslatedName ( name , type ) { let candidate = attemptName ( name , ( c ) => ! this . _translatedNames . has ( c ) ) ; this . _translatedNames . set ( candidate , { name : name , type : type , translatedName : candidate } ) ; return candidate ; } public forEach ( callback ) { this . _translatedNames . forEach ( ( o ) => { callback ( o . translatedName , o . type ) ; } ) ; } public nextVariableTranslatedName ( type ) { return this . nextVariable ( type ) . translatedName ; } public nextVariableName ( type ) { return this . nextVariable ( type ) . name ; } public nextVariable ( type ) { return this . _currentScope . nextVariable ( type ) ; } public addVariable ( name , type , scope ) { if ( scope == "global" ) { this . _globalScope . addVariable ( name , type ) ; } else { this . _currentScope . addVariable ( name , type ) ; } } public translateVariableName ( name ) { return this . getVariable ( name ) . translatedName ; } public getVariable ( name ) { return this . _currentScope . getVariable ( name ) ; } }