export interface BasicGeneratedFile { output : string ; } export class GeneratedFile < Scope = any > implements BasicGeneratedFile { scopeStack : Scope [ ] = [ ] ; indentWidth = 2 ; indentLevel = 0 ; startOfIndentLevel = false ; public output = '' ; pushScope ( scope ) { this . scopeStack . push ( scope ) ; } popScope ( ) { return this . scopeStack . pop ( ) ; } get scope ( ) : Scope { if ( this . scopeStack . length < 1 ) throw new Error ( 'No active scope' ) ; return this . scopeStack [ this . scopeStack . length - 1 ] ; } print ( string ? ) { if ( string ) { this . output += string ; } } printNewline ( ) { if ( this . output ) { this . print ( '\n' ) ; this . startOfIndentLevel = false ; } } printNewlineIfNeeded ( ) { if ( ! this . startOfIndentLevel ) { this . printNewline ( ) ; } } printOnNewline ( string ? ) { if ( string ) { this . printNewline ( ) ; this . printIndent ( ) ; this . print ( string ) ; } } printIndent ( ) { const indentation = ' ' . repeat ( this . indentLevel * this . indentWidth ) ; this . output += indentation ; } withIndent ( closure ) { if ( ! closure ) return ; this . indentLevel ++ ; this . startOfIndentLevel = true ; closure ( ) ; this . indentLevel -- ; } withinBlock ( closure , open = ' {' , close = '}' ) { this . print ( open ) ; this . withIndent ( closure ) ; this . printOnNewline ( close ) ; } } export class CodeGenerator < Context = any , Scope = any > { generatedFiles : { [ fileName ] : GeneratedFile < Scope > ; } = { } ; currentFile : GeneratedFile < Scope > ; constructor ( public context ) { this . currentFile = new GeneratedFile ( ) ; } withinFile ( fileName , closure ) { let file = this . generatedFiles [ fileName ] ; if ( ! file ) { file = new GeneratedFile ( ) ; this . generatedFiles [ fileName ] = file ; } const oldCurrentFile = this . currentFile ; this . currentFile = file ; closure ( ) ; this . currentFile = oldCurrentFile ; } get output ( ) : string { return this . currentFile . output ; } pushScope ( scope ) { this . currentFile . pushScope ( scope ) ; } popScope ( ) { this . currentFile . popScope ( ) ; } get scope ( ) : Scope { return this . currentFile . scope ; } print ( string ? ) { this . currentFile . print ( string ) ; } printNewline ( ) { this . currentFile . printNewline ( ) ; } printNewlineIfNeeded ( ) { this . currentFile . printNewlineIfNeeded ( ) ; } printOnNewline ( string ? ) { this . currentFile . printOnNewline ( string ) ; } printIndent ( ) { this . currentFile . printIndent ( ) ; } withIndent ( closure ) { this . currentFile . withIndent ( closure ) ; } withinBlock ( closure , open = ' {' , close = '}' ) { this . currentFile . withinBlock ( closure , open , close ) ; } }