/// <summary> /// Implements a function definition based on a given function definition. /// </summary> /// <param name="funcName">The simple name of the function, can be null.</param> /// <param name="visitor">The current visitor object.</param> /// <param name="function">The function to generate for.</param> public void ImplementFunction(IParseItemVisitor visitor, FuncDefItem function, string funcName) { NameItem[] args = function.Arguments.ToArray(); if (function.InstanceName != null) { args = new[] { new NameItem("self") }.Concat(args).ToArray(); } // ILuaMultiValue function(ILuaEnvironment E, ILuaMultiValue args, ILuaValue target, // bool memberCall); funcName ??= "<>__" + (_mid++); string name = _curNest.Members.Contains(funcName) ? funcName + "_" + (_mid++) : funcName; MethodBuilder mb = _curNest.TypeDef.DefineMethod( name, MethodAttributes.Public, typeof(ILuaMultiValue), new Type[] { typeof(ILuaEnvironment), typeof(ILuaMultiValue), typeof(ILuaValue), typeof(bool) }); var gen = mb.GetILGenerator(); _curNest = new NestInfo( _curNest, gen, function.FunctionInformation.CapturedLocals, function.FunctionInformation.HasNested, function.FunctionInformation.CapturesParent); // if this is an instance method, create a BaseAccessor object to help types. if (function.InstanceName != null) { // TODO: Add base accessor back. //var field = curNest.DefineLocal(new NameItem("base")); } // If this was an instance call, the first Lua argument is the 'target'; // otherwise the it is the zero'th index in args. // int c = 0; var c = gen.DeclareLocal(typeof(int)); if (args.Length > 0) { var field = _curNest.DefineLocal(args[0]); var end = gen.DefineLabel(); var else_ = gen.DefineLabel(); // if (!memberCall) c = 1; // {field_0} = (memberCall ? target : args[0]); field.StartSet(); gen.Emit(OpCodes.Ldarg, 4); gen.Emit(OpCodes.Brfalse, else_); gen.Emit(OpCodes.Ldarg_3); gen.Emit(OpCodes.Br, end); gen.MarkLabel(else_); gen.Emit(OpCodes.Ldc_I4_1); gen.Emit(OpCodes.Stloc, c); gen.Emit(OpCodes.Ldarg_2); if (args[0].Name != "...") { gen.Emit(OpCodes.Ldc_I4_0); gen.Emit(OpCodes.Callvirt, typeof(ILuaMultiValue).GetMethod("get_Item")); } else { if (args.Length != 1) { throw new InvalidOperationException( "Variable arguments (...) only valid at end of argument list."); } } gen.MarkLabel(end); field.EndSet(); } for (int i = 1; i < args.Length; i++) { var field = _curNest.DefineLocal(args[i]); if (args[i].Name == "...") { if (i != args.Length - 1) { throw new InvalidOperationException( "Variable arguments (...) only valid at end of argument list."); } // {field} = E.Runtime.CreateMultiValue(args.Skip({args.Length - 1}); field.StartSet(); gen.Emit(OpCodes.Ldarg_1); gen.Emit( OpCodes.Callvirt, typeof(ILuaEnvironment).GetProperty(nameof(ILuaEnvironment.Runtime)).GetGetMethod()); gen.Emit(OpCodes.Ldarg_2); gen.Emit(OpCodes.Ldc_I4, args.Length - 1); gen.Emit(OpCodes.Call, typeof(Enumerable).GetMethod(nameof(Enumerable.Skip)) .MakeGenericMethod(typeof(object))); gen.Emit(OpCodes.Callvirt, typeof(ILuaRuntime).GetMethod(nameof(ILuaRuntime.CreateMultiValue))); field.EndSet(); } else { // {field} = args[{i - 1} + c]; field.StartSet(); gen.Emit(OpCodes.Ldarg_2); gen.Emit(OpCodes.Ldc_I4, i - 1); gen.Emit(OpCodes.Ldloc, c); gen.Emit(OpCodes.Add); gen.Emit(OpCodes.Callvirt, typeof(ILuaMultiValue).GetMethod("get_Item")); field.EndSet(); } } function.Block.Accept(visitor); if (_curNest.TypeDef != null) { _curNest.TypeDef.CreateType(); } _curNest = _curNest.Parent; // push a pointer to the new method onto the stack of the previous nest method // the above line restores the nest to the previous state and this code will // push the new method. //! push E.Runtime.CreateFunctionValue({name}, {nest.TypeDef}.GetMethod({name}), // {nest.ThisInst != null ? nest.NestInst : this} ); _curNest.Generator.Emit(OpCodes.Ldarg_1); _curNest.Generator.Emit( OpCodes.Callvirt, typeof(ILuaEnvironment).GetProperty(nameof(ILuaEnvironment.Runtime)).GetGetMethod()); _curNest.Generator.Emit(OpCodes.Ldstr, name); _curNest.Generator.Emit(OpCodes.Ldtoken, _curNest.TypeDef); _curNest.Generator.Emit( OpCodes.Call, typeof(Type).GetMethod(nameof(Type.GetTypeFromHandle), new Type[] { typeof(RuntimeTypeHandle) })); _curNest.Generator.Emit(OpCodes.Ldstr, name); _curNest.Generator.Emit( OpCodes.Callvirt, typeof(Type).GetMethod(nameof(Type.GetMethod), new[] { typeof(string) })); if (_curNest.ThisInst != null) { _curNest.Generator.Emit(OpCodes.Ldloc, _curNest.ThisInst); } else { _curNest.Generator.Emit(OpCodes.Ldarg_0); } _curNest.Generator.Emit( OpCodes.Callvirt, typeof(ILuaRuntime).GetMethod(nameof(ILuaRuntime.CreateImplementationFunction))); }
public void GenralParse() { PlainParser target = new PlainParser(); TextElementEnumerator input1 = StringInfo.GetTextElementEnumerator( @"local a = 12 t = { [34]= function() print(i) end } function Some(a, ...) a, b, c = ... for i= 12, 23 do print(i) end end" ); IParseItem actual; actual = target.Parse(new Tokenizer(input1, null), null, null); // check the main block BlockItem block = actual as BlockItem; Assert.IsInstanceOf <BlockItem>(actual); Assert.IsNotNull(block.Children); Assert.AreEqual(3, block.Children.Count, "Block.Children.Count"); ValidateDebug(block.Debug, "Block", "local a = 12 t = { [ 34 ] = function ( ) print ( i ) end } function Some ( a , ... ) a , b , c = ... for i = 12 , 23 do print ( i ) end end", 1, 1, 8, 4); // check the return statement of the main block { ReturnItem ret = block.Return; Assert.IsInstanceOf <ReturnItem>(block.Return); ValidateDebug(ret.Debug, "Block.Return", null, 0, 0, 0, 0); Assert.IsNotNull(ret.Expressions); Assert.AreEqual(0, ret.Expressions.Count); } // local a = 12 { AssignmentItem init = block.Children[0] as AssignmentItem; Assert.IsNotNull(init, "Block.Children[0]"); Assert.AreEqual(true, init.Local); ValidateDebug(init.Debug, "Block.Children[0]", "local a = 12", 1, 1, 1, 13); // check the names { Assert.IsNotNull(init.Names, "Block.Children[0].Names"); Assert.AreEqual(1, init.Names.Count, "Block.Children[0].Names.Count"); NameItem name = init.Names[0] as NameItem; Assert.IsNotNull(name, "Block.Children[0].Names[0]"); Assert.AreEqual("a", name.Name, "Block.Children[0].Names[0].Name"); ValidateDebug(name.Debug, "Block.Children[0].Names[0]", "a", 1, 7, 1, 8); } // check the expressions { Assert.IsNotNull(init.Expressions, "Block.Children[0].Expressions"); Assert.AreEqual(1, init.Expressions.Count, "Block.Children[0].Expressions.Count"); LiteralItem literal = init.Expressions[0] as LiteralItem; Assert.IsNotNull(literal, "Block.Children[0].Expressions[0]"); Assert.AreEqual(12.0, literal.Value, "Block.Children[0].Expressions[0].Value"); ValidateDebug(literal.Debug, "Block.Children[0].Expressions[0]", "12", 1, 11, 1, 13); } } // t = { [34]= function() print(i) end } { AssignmentItem init = block.Children[1] as AssignmentItem; Assert.IsNotNull(init, "Block.Children[1]"); Assert.AreEqual(false, init.Local); ValidateDebug(init.Debug, "Block.Children[1]", "t = { [ 34 ] = function ( ) print ( i ) end }", 2, 1, 2, 38); // check the names { Assert.IsNotNull(init.Names, "Block.Children[1].Names"); Assert.AreEqual(1, init.Names.Count, "Block.Children[1].Names.Count"); NameItem name = init.Names[0] as NameItem; Assert.IsNotNull(name, "Block.Children[1].Names[0]"); Assert.AreEqual("t", name.Name, "Block.Children[1].Names[0].Name"); ValidateDebug(name.Debug, "Block.Children[1].Names[0]", "t", 2, 1, 2, 2); } // check the expressions { Assert.IsNotNull(init.Expressions, "Block.Children[1].Expressions"); Assert.AreEqual(1, init.Expressions.Count, "Block.Children[1].Expressions.Count"); TableItem table = init.Expressions[0] as TableItem; Assert.IsNotNull(table, "Block.Children[1].Expressions[0]"); ValidateDebug(table.Debug, "Block.Children[1].Expressions[0]", "{ [ 34 ] = function ( ) print ( i ) end }", 2, 5, 2, 38); Assert.IsNotNull(table.Fields, "Block.Children[1].Expressions[0].Fields"); Assert.AreEqual(1, table.Fields.Count, "Block.Children[1].Expressions[0].Fields.Count"); var field = table.Fields[0]; { LiteralItem literal = field.Key as LiteralItem; Assert.IsNotNull(literal, "Block.Children[1].Expressions[0].Fields[0].Item1"); Assert.AreEqual(34.0, literal.Value, "Block.Children[1].Expressions[0].Fields[0].Item1.Value"); ValidateDebug(literal.Debug, "Block.Children[1].Expressions[0].Fields[0].Item1", "34", 2, 8, 2, 10); } { FuncDefItem func = field.Value as FuncDefItem; Assert.IsNotNull(func, "Block.Children[1].Expressions[0].Fields[0].Item2"); Assert.IsNull(func.InstanceName, "Block.Children[1].Expressions[0].Fields[0].Item2.InstanceName"); Assert.IsNull(func.Prefix, "Block.Children[1].Expressions[0].Fields[0].Item2.Prefix"); Assert.AreEqual(false, func.Local, "Block.Children[1].Expressions[0].Fields[0].Item2.Local"); Assert.IsNull(func.FunctionInformation, "Block.Children[1].Expressions[0].Fields[0].Item2.FunctionInformation"); ValidateDebug(func.Debug, "Block.Children[1].Expressions[0].Fields[0].Item2", "function ( ) print ( i ) end", 2, 13, 2, 36); // validate the block { BlockItem funcBlock = func.Block; Assert.IsNotNull(funcBlock, "Block.Children[1].Expressions[0].Fields[0].Item2.Block"); ValidateDebug(funcBlock.Debug, "Block.Children[1].Expressions[0].Fields[0].Item2.Block", "print ( i )", 2, 24, 2, 32); // validate the return { ReturnItem ret = funcBlock.Return; Assert.IsNotNull(ret, "Block.Children[1].Expressions[0].Fields[0].Item2.Block.Return"); ValidateDebug(ret.Debug, "Block.Children[1].Expressions[0].Fields[0].Item2.Block.Return", null, 0, 0, 0, 0); Assert.IsNotNull(ret.Expressions, "Block.Children[1].Expressions[0].Fields[0].Item2.Block.Return.Expressions"); Assert.AreEqual(0, ret.Expressions.Count, "Block.Children[1].Expressions[0].Fields[0].Item2.Block.Return.Expressions.Count"); } // validate the statement { Assert.IsNotNull(funcBlock.Children, "Block.Children[1].Expressions[0].Fields[0].Item2.Block.Children"); Assert.AreEqual(1, funcBlock.Children.Count, "Block.Children[1].Expressions[0].Fields[0].Item2.Block.Children.Count"); // print ( i ) { FuncCallItem call = funcBlock.Children[0] as FuncCallItem; Assert.IsNotNull(call, "Block.Children[1].Expressions[0].Fields[0].Item2.Block.Children[0]"); Assert.AreEqual(true, call.Statement, "Block.Children[1].Expressions[0].Fields[0].Item2.Block.Children[0].Statement"); Assert.IsNull(call.InstanceName, "Block.Children[1].Expressions[0].Fields[0].Item2.Block.Children[0].InstanceName"); ValidateDebug(call.Debug, "Block.Children[1].Expressions[0].Fields[0].Item2.Block.Children[0]", "print ( i )", 2, 24, 2, 32); // validate the prefix { NameItem name = call.Prefix as NameItem; Assert.IsNotNull(call.Prefix, "Block.Children[1].Expressions[0].Fields[0].Item2.Block.Children[0].Prefix"); Assert.AreEqual("print", name.Name, "Block.Children[1].Expressions[0].Fields[0].Item2.Block.Children[0].Prefix.Name"); ValidateDebug(name.Debug, "Block.Children[1].Expressions[0].Fields[0].Item2.Block.Children[0].Prefix.Name", "print", 2, 24, 2, 29); } // validate the arguments { Assert.IsNotNull(call.Arguments, "Block.Children[1].Expressions[0].Fields[0].Item2.Block.Children[0].Arguments"); Assert.AreEqual(1, call.Arguments.Count, "Block.Children[1].Expressions[0].Fields[0].Item2.Block.Children[0].Arguments.Count"); NameItem name = call.Arguments[0].Expression as NameItem; Assert.IsNotNull(name, "Block.Children[1].Expressions[0].Fields[0].Item2.Block.Children[0].Arguments[0]"); Assert.AreEqual("i", name.Name, "Block.Children[1].Expressions[0].Fields[0].Item2.Block.Children[0].Arguments[0].Name"); ValidateDebug(name.Debug, "Block.Children[1].Expressions[0].Fields[0].Item2.Block.Children[0].Arguments[0]", "i", 2, 30, 2, 31); } } } } } } } // function Some(a, ...) { FuncDefItem func = block.Children[2] as FuncDefItem; Assert.IsNotNull(func, "Block.Children[2]"); Assert.AreEqual(false, func.Local, "Block.Children[2].Local"); Assert.IsNull(func.InstanceName, "Block.Children[2].InstanceName"); ValidateDebug(func.Debug, "Block.Children[2]", "function Some ( a , ... ) a , b , c = ... for i = 12 , 23 do print ( i ) end end", 3, 1, 8, 4); // validate the block { BlockItem someBlock = func.Block; ValidateDebug(someBlock.Debug, "Block.Children[2].Block", "a , b , c = ... for i = 12 , 23 do print ( i ) end", 4, 5, 7, 8); // validate the return { ReturnItem ret = someBlock.Return; Assert.IsNotNull(ret, "Block.Children[2].Block.Return"); ValidateDebug(ret.Debug, "Block.Children[2].Block.Return", null, 0, 0, 0, 0); Assert.IsNotNull(ret.Expressions, "Block.Children[2].Block.Return.Expressions"); Assert.AreEqual(0, ret.Expressions.Count, "Block.Children[2].Block.Return.Expressions.Count"); } // check the children { Assert.IsNotNull(someBlock.Children, "Block.Children[2].Block.Children"); Assert.AreEqual(2, someBlock.Children.Count, "Block.Children[2].Block.Children.Count"); // a , b , c = ... { AssignmentItem varInit = someBlock.Children[0] as AssignmentItem; Assert.IsNotNull(varInit, "Block.Children[2].Block.Children[0]"); Assert.AreEqual(false, varInit.Local, "Block.Children[2].Block.Children[0].Local"); ValidateDebug(varInit.Debug, "Block.Children[2].Block.Children[0]", "a , b , c = ...", 4, 5, 4, 18); // validate the names { Assert.IsNotNull(varInit.Names, "Block.Children[2].Block.Children[0].Names"); Assert.AreEqual(3, varInit.Names.Count, "Block.Children[2].Block.Children[0].Names.Count"); NameItem name = varInit.Names[0] as NameItem; Assert.IsNotNull(name, "Block.Children[2].Block.Children[0].Names[0]"); Assert.AreEqual(name.Name, "a", "Block.Children[2].Block.Children[0].Names[0].Name"); ValidateDebug(name.Debug, "Block.Children[2].Block.Children[0].Names[0]", "a", 4, 5, 4, 6); name = varInit.Names[1] as NameItem; Assert.IsNotNull(name, "Block.Children[2].Block.Children[0].Names[1]"); Assert.AreEqual(name.Name, "b", "Block.Children[2].Block.Children[0].Names[1].Name"); ValidateDebug(name.Debug, "Block.Children[2].Block.Children[0].Names[1]", "b", 4, 8, 4, 9); name = varInit.Names[2] as NameItem; Assert.IsNotNull(name, "Block.Children[2].Block.Children[0].Names[2]"); Assert.AreEqual(name.Name, "c", "Block.Children[2].Block.Children[0].Names[2].Name"); ValidateDebug(name.Debug, "Block.Children[2].Block.Children[0].Names[2]", "c", 4, 11, 4, 12); } // validate the expressions { Assert.IsNotNull(varInit.Expressions, "Block.Children[2].Block.Children[0].Expressions"); Assert.AreEqual(1, varInit.Expressions.Count, "Block.Children[2].Block.Children[0].Expressions.Count"); NameItem name = varInit.Expressions[0] as NameItem; Assert.IsNotNull(name, "Block.Children[2].Block.Children[0].Expressions[0]"); Assert.AreEqual(name.Name, "...", "Block.Children[2].Block.Children[0].Expressions[0].Name"); ValidateDebug(name.Debug, "Block.Children[2].Block.Children[0].Expressions[0]", "...", 4, 15, 4, 18); } } // for i= 12, 23 do print ( i ) end { ForNumItem forLoop = someBlock.Children[1] as ForNumItem; Assert.IsNotNull(forLoop, "Block.Children[2].Block.Children[1]"); ValidateDebug(forLoop.Debug, "Block.Children[2].Block.Children[1]", "for i = 12 , 23 do print ( i ) end", 5, 5, 7, 8); // validate the name { NameItem name = forLoop.Name; Assert.IsNotNull(name, "Block.Children[2].Block.Children[1].Name"); ValidateDebug(name.Debug, "Block.Children[2].Block.Children[1].Name", "i", 5, 9, 5, 10); Assert.AreEqual(name.Name, "i", "Block.Children[2].Block.Children[1].Name.Name"); } // validate the start { LiteralItem lit = forLoop.Start as LiteralItem; Assert.IsNotNull(lit, "Block.Children[2].Block.Children[1].Start"); Assert.AreEqual(12.0, lit.Value, "Block.Children[2].Block.Children[1].Start.Value"); ValidateDebug(lit.Debug, "Block.Children[2].Block.Children[1].Start", "12", 5, 12, 5, 14); } // validate the limit { LiteralItem lit = forLoop.Limit as LiteralItem; Assert.IsNotNull(lit, "Block.Children[2].Block.Children[1].Limit"); Assert.AreEqual(23.0, lit.Value, "Block.Children[2].Block.Children[1].Limit.Value"); ValidateDebug(lit.Debug, "Block.Children[2].Block.Children[1].Limit", "23", 5, 16, 5, 18); } // validate the step { Assert.IsNull(forLoop.Step, "Block.Children[2].Block.Children[1].Step"); } // validate the block { BlockItem forBlock = forLoop.Block; ValidateDebug(forBlock.Debug, "Block.Children[2].Block.Children[1].Block", "print ( i )", 6, 9, 6, 17); Assert.IsNull(forBlock.Return, "Block.Children[2].Block.Children[1].Block.Return"); // validate the statement { Assert.IsNotNull(forBlock.Children, "Block.Children[2].Block.Children[1].Block.Children"); Assert.AreEqual(1, forBlock.Children.Count, "Block.Children[2].Block.Children[1].Block.Children.Count"); // print ( i ) { FuncCallItem call = forBlock.Children[0] as FuncCallItem; Assert.IsNotNull(call, "Block.Children[2].Block.Children[1].Block.Children[0]"); Assert.AreEqual(true, call.Statement, "Block.Children[2].Block.Children[1].Block.Children[0].Statement"); Assert.IsNull(call.InstanceName, "Block.Children[2].Block.Children[1].Block.Children[0].InstanceName"); ValidateDebug(call.Debug, "Block.Children[2].Block.Children[1].Block.Children[0]", "print ( i )", 6, 9, 6, 17); // validate the prefix { NameItem name = call.Prefix as NameItem; Assert.IsNotNull(call.Prefix, "Block.Children[2].Block.Children[1].Block.Children[0].Prefix"); Assert.AreEqual("print", name.Name, "Block.Children[2].Block.Children[1].Block.Children[0].Prefix.Name"); ValidateDebug(name.Debug, "Block.Children[2].Block.Children[1].Block.Children[0].Prefix.Name", "print", 6, 9, 6, 14); } // validate the arguments { Assert.IsNotNull(call.Arguments, "Block.Children[2].Block.Children[1].Block.Children[0].Arguments"); Assert.AreEqual(1, call.Arguments.Count, "Block.Children[2].Block.Children[1].Block.Children[0].Arguments.Count"); NameItem name = call.Arguments[0].Expression as NameItem; Assert.IsNotNull(name, "Block.Children[2].Block.Children[1].Block.Children[0].Arguments[0]"); Assert.AreEqual("i", name.Name, "Block.Children[2].Block.Children[1].Block.Children[0].Arguments[0].Name"); ValidateDebug(name.Debug, "Block.Children[2].Block.Children[1].Block.Children[0].Arguments[0]", "i", 6, 15, 6, 16); } } } } } } } } }
public IParseItem Visit(FuncDefItem target) { if (target == null) { throw new ArgumentNullException(nameof(target)); } var gen = _compiler.CurrentGenerator; ChunkBuilder.IVarDefinition field = null; string name = null; bool store = false; if (target.Local) { // Local function definition if (target.InstanceName != null) { throw new SyntaxException(Resources.InstanceLocalMethod, target.Debug); } if (!(target.Prefix is NameItem)) { throw new SyntaxException(Resources.IndexerLocalMethod, target.Debug); } NameItem namei = (NameItem)target.Prefix; name = namei.Name; field = _compiler.DefineLocal(namei); field.StartSet(); } else if (target.Prefix != null) { if (target.InstanceName != null) { // Instance function definition name = null; if (target.Prefix is NameItem nameItem) { name = nameItem.Name; } else { name = (string)((LiteralItem)((IndexerItem)target.Prefix).Expression).Value; } name += ":" + target.InstanceName; // {Prefix}.SetIndex({InstanceName}, {ImplementFunction(..)}) target.Prefix.Accept(this); gen.Emit(OpCodes.Ldarg_1); gen.Emit( OpCodes.Callvirt, typeof(ILuaEnvironment).GetProperty(nameof(ILuaEnvironment.Runtime)).GetGetMethod()); gen.Emit(OpCodes.Ldstr, target.InstanceName); gen.Emit(OpCodes.Callvirt, typeof(ILuaRuntime).GetMethod(nameof(ILuaRuntime.CreateValue))); store = true; } else if (target.Prefix is IndexerItem index) { // Global function definition with indexer // {Prefix}.SetIndex({Expression}, {ImplementFunction(..)}) name = (string)((LiteralItem)index.Expression).Value; index.Prefix.Accept(this); index.Expression.Accept(this); store = true; } else { // Global function definition with name name = ((NameItem)target.Prefix).Name; field = _compiler.FindVariable((NameItem)target.Prefix); field.StartSet(); } } _compiler.ImplementFunction(this, target, name); if (field != null) { field.EndSet(); } else if (store) { gen.Emit(OpCodes.Callvirt, typeof(ILuaValue).GetMethod(nameof(ILuaValue.SetIndex))); } return(target); }