// constructor -> '{' [ field { sep field } [sep] ] '}' // sep -> ',' | ';' private void Constructor( ExpDesc t ) { var fs = CurFunc; int line = Lexer.LineNumber; int pc = Coder.CodeABC( fs, OpCode.OP_NEWTABLE, 0, 0, 0 ); var cc = new ConstructorControl(); cc.ExpTable = t; InitExp( t, ExpKind.VRELOCABLE, pc ); InitExp( cc.ExpLastItem, ExpKind.VVOID, 0); // no value (yet) Coder.Exp2NextReg( fs, t ); CheckNext( (int)'{' ); do { Utl.Assert( cc.ExpLastItem.Kind == ExpKind.VVOID || cc.NumToStore > 0 ); if( Lexer.Token.TokenType == (int)'}' ) break; CloseListField( fs, cc ); Field( cc ); } while( TestNext( (int)',' ) || TestNext( (int)';' ) ); CheckMatch( (int)'}', (int)'{', line ); LastListField( fs, cc ); // set initial array size and table size // 因为没有实现 OP_NEWTABLE 对 ARG_B 和 ARG_C 的处理, 所以这里也暂不实现 // 不影响逻辑, 只是效率差别 // var ins = fs.Proto.Code[pc]; // ins.SETARG_B( 0 ); // ins.SETARG_C( 0 ); // 算了, 还是实现吧, 方便检查生成的 bytecode 是否跟 luac 一样 var ins = fs.Proto.Code[pc]; ins.SETARG_B( Integer2FloatingPointByte( (uint)cc.NumArray ) ); ins.SETARG_C( Integer2FloatingPointByte( (uint)cc.NumRecord) ); fs.Proto.Code[pc] = ins; // Instruction 是值类型的 唉 }
// field -> listfield | recfield private void Field( ConstructorControl cc ) { switch( Lexer.Token.TokenType ) { // may be 'listfield' or 'recfield' case (int)TK.NAME: { // expression? if( Lexer.GetLookAhead().TokenType != (int)'=' ) ListField( cc ); else RecField( cc ); break; } case (int)'[': { RecField( cc ); break; } default: { ListField( cc ); break; } } }
// listfield -> exp private void ListField( ConstructorControl cc ) { Expr( cc.ExpLastItem ); CheckLimit( CurFunc, cc.NumArray, LuaLimits.MAX_INT, "items in a constructor" ); cc.NumArray++; cc.NumToStore++; }
private void LastListField( FuncState fs, ConstructorControl cc ) { if( cc.NumToStore == 0 ) return; if( HasMultiRet( cc.ExpLastItem.Kind ) ) { Coder.SetMultiRet( fs, cc.ExpLastItem ); Coder.SetList( fs, cc.ExpTable.Info, cc.NumArray, LuaDef.LUA_MULTRET ); // do not count last expression (unknown number of elements) cc.NumArray--; } else { if( cc.ExpLastItem.Kind != ExpKind.VVOID ) Coder.Exp2NextReg( fs, cc.ExpLastItem ); Coder.SetList( fs, cc.ExpTable.Info, cc.NumArray, cc.NumToStore ); } }
private void CloseListField( FuncState fs, ConstructorControl cc ) { // there is no list item if( cc.ExpLastItem.Kind == ExpKind.VVOID ) return; Coder.Exp2NextReg( fs, cc.ExpLastItem ); cc.ExpLastItem.Kind = ExpKind.VVOID; if( cc.NumToStore == LuaDef.LFIELDS_PER_FLUSH ) { // flush Coder.SetList( fs, cc.ExpTable.Info, cc.NumArray, cc.NumToStore ); // no more item pending cc.NumToStore = 0; } }
// recfield -> (NAME | '[' exp1 ']') = exp1 private void RecField( ConstructorControl cc ) { var fs = CurFunc; int reg = fs.FreeReg; var key = new ExpDesc(); var val = new ExpDesc(); if( Lexer.Token.TokenType == (int)TK.NAME ) { CheckLimit( fs, cc.NumRecord, LuaLimits.MAX_INT, "items in a constructor" ); CodeString( key, CheckName() ); } // ls->t.token == '[' else { YIndex( key ); } cc.NumRecord++; CheckNext( (int)'=' ); int rkkey = Coder.Exp2RK( fs, key ); Expr( val ); Coder.CodeABC( fs, OpCode.OP_SETTABLE, cc.ExpTable.Info, rkkey, Coder.Exp2RK( fs, val ) ); fs.FreeReg = reg; // free registers }