import * as AST from './AST' ; type TransformerFunc = ( node : AST . OperationNode ) => AST . TransformedNode | AST . OperationNode ; const $AT = new AST . RegisterNode ( '$at' ) ; const $0 = new AST . RegisterNode ( '$0' ) ; const transformImmediate = ( op , rOp ) : AST . TransformedNode | AST . OperationNode => { const immNode = op . args [ 2 ] as AST . ImmediateNode ; const split = immNode . split ( ) ; if ( split . length === 1 ) return op ; return new AST . TransformedNode ( [ new AST . OperationNode ( 'lui' , [ $AT , split [ 0 ] ] ) , new AST . OperationNode ( 'ori' , [ $AT , $AT , split [ 1 ] ] ) , new AST . OperationNode ( rOp , [ op . args [ 0 ] , op . args [ 1 ] , $AT ] ) , ] , op ) ; } ; const transformers = { 'bge' : ( op ) => new AST . TransformedNode ( [ new AST . OperationNode ( 'slt' , [ $AT , op . args [ 0 ] , op . args [ 1 ] ] ) , new AST . OperationNode ( 'beq' , [ $AT , $0 , op . args [ 2 ] ] ) , ] , op ) , 'bgt' : ( op ) => new AST . TransformedNode ( [ new AST . OperationNode ( 'slt' , [ $AT , op . args [ 1 ] , op . args [ 0 ] ] ) , new AST . OperationNode ( 'bne' , [ $AT , $0 , op . args [ 2 ] ] ) , ] , op ) , 'ble' : ( op ) => new AST . TransformedNode ( [ new AST . OperationNode ( 'slt' , [ $AT , op . args [ 1 ] , op . args [ 0 ] ] ) , new AST . OperationNode ( 'beq' , [ $AT , $0 , op . args [ 2 ] ] ) , ] , op ) , 'blt' : ( op ) => new AST . TransformedNode ( [ new AST . OperationNode ( 'slt' , [ $AT , op . args [ 0 ] , op . args [ 1 ] ] ) , new AST . OperationNode ( 'bne' , [ $AT , $0 , op . args [ 2 ] ] ) , ] , op ) , 'la' : ( op ) => { const addr = op . args [ 1 ] as AST . AddressNode ; return new AST . TransformedNode ( [ new AST . OperationNode ( 'lui' , [ $AT , addr . partition ( 'upper' ) ] ) , new AST . OperationNode ( 'ori' , [ op . args [ 0 ] , $AT , addr . partition ( 'lower' ) ] ) , ] , op ) ; } , 'move' : ( op ) => new AST . TransformedNode ( [ new AST . OperationNode ( 'addu' , [ op . args [ 0 ] , $0 , op . args [ 1 ] ] ) , ] , op ) , 'li' : ( op ) => { const immNode = op . args [ 1 ] as AST . ImmediateNode ; const split = immNode . split ( ) ; if ( split . length == 1 ) { return new AST . TransformedNode ( [ new AST . OperationNode ( 'addiu' , [ op . args [ 0 ] , $0 , op . args [ 1 ] ] ) , ] , op ) ; } return new AST . TransformedNode ( [ new AST . OperationNode ( 'lui' , [ $AT , split [ 0 ] ] ) , new AST . OperationNode ( 'ori' , [ op . args [ 0 ] , $AT , split [ 1 ] ] ) , ] , op ) ; } , 'addi' : ( op ) => transformImmediate ( op , 'add' ) , 'addiu' : ( op ) => transformImmediate ( op , 'addu' ) , 'andi' : ( op ) => transformImmediate ( op , 'and' ) , 'ori' : ( op ) => transformImmediate ( op , 'or' ) , 'xori' : ( op ) => transformImmediate ( op , 'xor' ) , 'slti' : ( op ) => transformImmediate ( op , 'slt' ) , 'sltiu' : ( op ) => transformImmediate ( op , 'sltu' ) , 'sgt' : ( op ) => new AST . TransformedNode ( [ new AST . OperationNode ( 'slt' , [ op . args [ 0 ] , op . args [ 2 ] , op . args [ 1 ] ] ) , ] , op ) , 'seq' : ( op ) => new AST . TransformedNode ( [ new AST . OperationNode ( 'subu' , op . args ) , new AST . OperationNode ( 'ori' , [ $AT , $0 , new AST . ImmediateNode ( '1' ) ] ) , new AST . OperationNode ( 'sltu' , [ op . args [ 0 ] , op . args [ 0 ] , $AT ] ) , ] , op ) , 'nop' : ( op ) => new AST . TransformedNode ( [ new AST . OperationNode ( 'sll' , [ $AT , $0 , $0 ] ) , ] , op ) , } ; const isOperationNode = ( n ) : n is AST . OperationNode => ( n . type === 'OPERATION' ) ; export default function transform ( root ) { root . text . children . forEach ( ( child , i ) => { let node ; let parent ; let index ; if ( child . type === 'LABEL' ) { parent = child ; node = child . children [ 0 ] ; index = 0 ; } else { node = child ; parent = root . text ; index = i ; } if ( isOperationNode ( node ) ) { if ( node . name in transformers ) { const transformer = transformers [ node . name ] ; parent . children [ index ] = transformer ( node ) ; } } } ) ; }