public IParseItem Visit(IndexerItem target) { if (target == null) { throw new ArgumentNullException(nameof(target)); } target.Prefix.Accept(this); target.Expression.Accept(this); return(target); }
/// <summary> /// Called when the item is an indexer item. /// </summary> /// <param name="target">The object that was passed to IParseItem.Visit.</param> /// <returns>The passed target or a modification of it.</returns> /// <exception cref="System.ArgumentNullException">If target is null.</exception> public IParseItem Visit(IndexerItem target) { if (target == null) { throw new ArgumentNullException(nameof(target)); } //! push {Prefix}.GetIndex({Expression}) var gen = compiler.CurrentGenerator; target.Prefix.Accept(this); target.Expression.Accept(this); gen.Emit(OpCodes.Callvirt, typeof(ILuaValue).GetMethod(nameof(ILuaValue.GetIndex))); return(target); }
/// <summary> /// Assigns the values of the parse item to the given value. /// </summary> /// <param name="target">The item to assign the value to (e.g. NameItem).</param> /// <param name="local">Whether this is a local definition.</param> /// <param name="getIndex">A function to get the index of the object, /// pass null to use the default.</param> /// <param name="getValue">A function to get the value to set to.</param> void AssignValue(IParseItem target, bool local, Action getIndex, Action getValue) { ILGenerator gen = compiler.CurrentGenerator; ChunkBuilder.VarDefinition field; if (local) { field = compiler.DefineLocal((NameItem)target); } else if (target is IndexerItem) { IndexerItem name = (IndexerItem)target; // {name.Prefix}.SetIndex({name.Expression}, value); name.Prefix.Accept(this); if (getIndex != null) { getIndex(); } else { name.Expression.Accept(this); } getValue(); gen.Emit(OpCodes.Callvirt, typeof(ILuaValue).GetMethod(nameof(ILuaValue.SetIndex))); return; } else // names[i] is NameItem { NameItem item = (NameItem)target; field = compiler.FindVariable(item); } // envField = value; field.StartSet(); getValue(); field.EndSet(); }
/// <summary> /// Called when the item is a function definition item. /// </summary> /// <param name="target">The object that was passed to IParseItem.Visit.</param> /// <returns>The passed target or a modification of it.</returns> /// <exception cref="System.ArgumentNullException">If target is null.</exception> public IParseItem Visit(FuncDefItem target) { if (target == null) { throw new ArgumentNullException(nameof(target)); } var gen = compiler.CurrentGenerator; ChunkBuilder.VarDefinition 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) { name = ((NameItem)target.Prefix).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) { // global function definition with indexer // {Prefix}.SetIndex({Expression}, {ImplementFunction(..)}) IndexerItem index = (IndexerItem)target.Prefix; 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); }
/// <summary> /// Called when the item is a function call item. /// </summary> /// <param name="target">The object that was passed to IParseItem.Visit.</param> /// <returns>The passed target or a modification of it.</returns> /// <exception cref="System.ArgumentNullException">If target is null.</exception> public IParseItem Visit(FuncCallItem target) { if (target == null) { throw new ArgumentNullException(nameof(target)); } //// load the args into an array. ILGenerator gen = compiler.CurrentGenerator; LocalBuilder f = compiler.CreateTemporary(typeof(ILuaValue)); LocalBuilder self = compiler.CreateTemporary(typeof(object)); /* add 'self' if instance call */ if (target.InstanceName != null) { // self = {Prefix}; target.Prefix.Accept(this); gen.Emit(OpCodes.Stloc, self); // f = self.GetIndex(temp); gen.Emit(OpCodes.Ldloc, self); 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))); gen.Emit(OpCodes.Callvirt, typeof(ILuaValue).GetMethod(nameof(ILuaValue.GetIndex))); gen.Emit(OpCodes.Stloc, f); } else if (target.Prefix is IndexerItem) { // self = {Prefix}; IndexerItem item = (IndexerItem)target.Prefix; item.Prefix.Accept(this); gen.Emit(OpCodes.Stloc, self); // Store the old value to restore later, add a dummy. var tempPrefix = item.Prefix; item.Prefix = new IndexerHelper(gen, self); // f = {Prefix}; target.Prefix.Accept(this); gen.Emit(OpCodes.Stloc, f); // Restore the old value item.Prefix = tempPrefix; } else { // self = LuaNil.Nil; gen.Emit(OpCodes.Ldnull); gen.Emit(OpCodes.Ldfld, typeof(LuaNil).GetField(nameof(LuaNil.Nil), BindingFlags.Static | BindingFlags.Public)); gen.Emit(OpCodes.Stloc, self); // f = {Prefix}; target.Prefix.Accept(this); gen.Emit(OpCodes.Stloc, f); } // var args = new ILuaValue[...]; LocalBuilder args = compiler.CreateArray(typeof(ILuaValue), target.Arguments.Count); for (int i = 0; i < target.Arguments.Count; i++) { // args[i] = {item}; gen.Emit(OpCodes.Ldloc, args); gen.Emit(OpCodes.Ldc_I4, i); target.Arguments[i].Expression.Accept(this); if (i + 1 == target.Arguments.Count && target.IsLastArgSingle) { gen.Emit(OpCodes.Callvirt, typeof(ILuaValue).GetMethod(nameof(ILuaValue.Single))); } gen.Emit(OpCodes.Stelem, typeof(ILuaValue)); } // var rargs = E.Runtime.CreateMultiValue(args); var rargs = compiler.CreateTemporary(typeof(ILuaMultiValue)); gen.Emit(OpCodes.Ldarg_1); gen.Emit(OpCodes.Callvirt, typeof(ILuaEnvironment).GetProperty(nameof(ILuaEnvironment.Runtime)).GetGetMethod()); gen.Emit(OpCodes.Ldloc, args); gen.Emit(OpCodes.Callvirt, typeof(ILuaRuntime).GetMethod(nameof(ILuaRuntime.CreateMultiValue))); gen.Emit(OpCodes.Stloc, rargs); compiler.RemoveTemporary(args); //! push f.Invoke(self, {!!InstanceName}, {Overload}, rargs); gen.Emit(OpCodes.Ldloc, f); gen.Emit(OpCodes.Ldloc, self); gen.Emit(target.InstanceName != null ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0); gen.Emit(OpCodes.Ldc_I4, target.Overload); gen.Emit(OpCodes.Ldloc, rargs); if (target.IsTailCall) { gen.Emit(OpCodes.Tailcall); } gen.Emit(OpCodes.Callvirt, typeof(ILuaValue).GetMethod(nameof(ILuaValue.Invoke))); compiler.RemoveTemporary(f); compiler.RemoveTemporary(self); //! pop if (target.Statement) { gen.Emit(OpCodes.Pop); } // support byRef for (int i = 0; i < target.Arguments.Count; i++) { if (target.Arguments[i].IsByRef) { AssignValue(target.Arguments[i].Expression, false, null, () => { // $value = rargs[{i}]; gen.Emit(OpCodes.Ldloc, rargs); gen.Emit(OpCodes.Ldc_I4, i); gen.Emit(OpCodes.Callvirt, typeof(ILuaMultiValue).GetMethod("get_Item")); }); } } compiler.RemoveTemporary(rargs); return(target); }
/// <summary> /// Called when the item is an assignment item. /// </summary> /// <param name="target">The object that was passed to IParseItem.Visit.</param> /// <returns>The passed target or a modification of it.</returns> /// <exception cref="System.ArgumentNullException">If target is null.</exception> public IParseItem Visit(AssignmentItem target) { if (target == null) { throw new ArgumentNullException(nameof(target)); } ILGenerator gen = compiler.CurrentGenerator; // ILuaValue[] loc = new ILuaValue[{target.Expressions.Count}]; LocalBuilder loc = compiler.CreateArray(typeof(ILuaValue), target.Expressions.Count); // ILuaValue[] names = new ILuaValue[{target.Names.Count}]; LocalBuilder names = compiler.CreateArray(typeof(ILuaValue), target.Names.Count); // have to evaluate the name indexer expressions before // setting the values otherwise the following will fail: // i, t[i] = i+1, 20 for (int i = 0; i < target.Names.Count; i++) { if (target.Names[i] is IndexerItem) { IndexerItem item = (IndexerItem)target.Names[i]; gen.Emit(OpCodes.Ldloc, names); gen.Emit(OpCodes.Ldc_I4, i); item.Expression.Accept(this); gen.Emit(OpCodes.Stelem, typeof(ILuaValue)); } } for (int i = 0; i < target.Expressions.Count; i++) { // loc[{i}] = {exps[i]}; gen.Emit(OpCodes.Ldloc, loc); gen.Emit(OpCodes.Ldc_I4, i); target.Expressions[i].Accept(this); if (i + 1 == target.Expressions.Count && target.IsLastExpressionSingle) { gen.Emit(OpCodes.Callvirt, typeof(ILuaValue).GetMethod(nameof(ILuaValue.Single))); } gen.Emit(OpCodes.Stelem, typeof(ILuaValue)); } // ILuaMultiValue exp = E.Runtime.CreateMultiValue(loc); LocalBuilder exp = compiler.CreateTemporary(typeof(ILuaMultiValue)); gen.Emit(OpCodes.Ldarg_1); gen.Emit(OpCodes.Callvirt, typeof(ILuaEnvironment).GetProperty(nameof(ILuaEnvironment.Runtime)).GetGetMethod()); gen.Emit(OpCodes.Ldloc, loc); gen.Emit(OpCodes.Callvirt, typeof(ILuaRuntime).GetMethod(nameof(ILuaRuntime.CreateMultiValue))); gen.Emit(OpCodes.Stloc, exp); compiler.RemoveTemporary(loc); for (int i = 0; i < target.Names.Count; i++) { AssignValue(target.Names[i], target.Local, !(target.Names[i] is IndexerItem) ? (Action)null : () => { // only called if the target object is an indexer item. // $index = names[{i}]; gen.Emit(OpCodes.Ldloc, names); gen.Emit(OpCodes.Ldc_I4, i); gen.Emit(OpCodes.Ldelem, typeof(ILuaValue)); }, () => { // $value = exp[{i}]; gen.Emit(OpCodes.Ldloc, exp); gen.Emit(OpCodes.Ldc_I4, i); gen.Emit(OpCodes.Callvirt, typeof(ILuaMultiValue).GetMethod("get_Item")); }); } compiler.RemoveTemporary(exp); compiler.RemoveTemporary(names); return(target); }
/// <summary> /// Reads a function from the input. Input must be on the word 'function'. If canName is true, /// it will give the function the read name; otherwise it will give it a null name. /// </summary> /// <param name="input">Where to read input from.</param> /// <param name="canName">True if the function can have a name, otherwise false.</param> /// <param name="local">True if this function is a local definition, otherwise false.</param> /// <returns>The function definition that was read.</returns> protected virtual FuncDefItem _readFunctionHelper(Lexer input, bool canName, bool local) { Token debug = input.Expect(TokenType.Function); IParseVariable name = null; string instName = null; if (input.PeekType(TokenType.Identifier)) { Token temp = input.Expect(TokenType.Identifier); name = new NameItem(temp.Value) { Debug = temp }; while (input.ReadIfType(TokenType.Indexer)) { temp = input.Expect(TokenType.Identifier); var literal = new LiteralItem(temp.Value) { Debug = temp }; name = new IndexerItem(name, literal) { Debug = name.Debug }; } if (input.ReadIfType(TokenType.Colon)) { instName = input.Expect(TokenType.Identifier).Value; } } if (name != null && !canName) { throw new SyntaxException(Resources.FunctionCantHaveName, input.Name, debug); } if (name == null && canName) { throw new SyntaxException("Function statements must provide name", input.Name, debug); } var args = new List <NameItem>(); input.Expect(TokenType.BeginParen); if (!input.PeekType(TokenType.EndParen)) { do { Token temp = input.PeekType(TokenType.Elipsis) ? input.Expect(TokenType.Elipsis) : input.Expect(TokenType.Identifier); args.Add(new NameItem(temp.Value) { Debug = temp }); if (temp.Value == "...") { break; } } while (input.ReadIfType(TokenType.Comma)); } input.Expect(TokenType.EndParen); BlockItem chunk = _readBlock(input); input.Expect(TokenType.End); chunk.Return ??= new ReturnItem(); return(new FuncDefItem(args.ToArray(), chunk) { Debug = debug, InstanceName = instName, Prefix = name, Local = local, }); }
/// <summary> /// Reads a prefix-expression from the input. /// </summary> /// <param name="input">The input to read from.</param> /// <returns>The parsed expression.</returns> protected virtual IParseExp _readPrefixExp(Lexer input, out bool isParentheses) { Token debug = input.Peek(); IParseExp ret; if (input.ReadIfType(TokenType.BeginParen)) { isParentheses = true; ret = _readExp(input, out _); input.Expect(TokenType.EndParen); } else { isParentheses = false; Token name = input.Expect(TokenType.Identifier); ret = new NameItem(name.Value) { Debug = name }; } while (true) { if (input.ReadIfType(TokenType.BeginBracket)) { isParentheses = false; IParseExp temp = _readExp(input, out _); ret = new IndexerItem(ret, temp) { Debug = debug }; input.Expect(TokenType.EndBracket); } else if (input.ReadIfType(TokenType.Indexer)) { isParentheses = false; Token token = input.Expect(TokenType.Identifier); var name = new LiteralItem(token.Value) { Debug = token }; ret = new IndexerItem(ret, name) { Debug = debug }; } else { string instName = null; int overload = -1; if (input.ReadIfType(TokenType.Colon)) { instName = input.Expect(TokenType.Identifier).Value; int idx = instName.IndexOf('`'); if (idx >= 0) { if (!int.TryParse(instName.Substring(idx + 1), out overload)) { throw input.SyntaxError(Resources.OnlyNumbersInOverload); } instName = instName.Substring(0, idx); } } else if (ret is NameItem name) { int idx = name.Name.IndexOf('`'); if (idx >= 0) { if (!int.TryParse(name.Name.Substring(idx + 1), out overload)) { throw input.SyntaxError(Resources.OnlyNumbersInOverload); } name.Name = name.Name.Substring(0, idx); } } bool isLastSingle = false; var args = new List <FuncCallItem.ArgumentInfo>(); if (input.PeekType(TokenType.BeginTable)) { args.Add(new FuncCallItem.ArgumentInfo(_readTable(input), false)); } else if (input.PeekType(TokenType.StringLiteral)) { Token token = input.Expect(TokenType.StringLiteral); args.Add(new FuncCallItem.ArgumentInfo(new LiteralItem(token.Value) { Debug = token }, false)); } else if (input.ReadIfType(TokenType.BeginParen)) { if (!input.PeekType(TokenType.EndParen)) { do { bool isRef = input.ReadIfType(TokenType.Ref); bool isRefParen = false; if (isRef) { isRefParen = input.ReadIfType(TokenType.BeginParen); } else { isRef = input.ReadIfType(TokenType.RefSymbol); } args.Add(new FuncCallItem.ArgumentInfo(_readExp(input, out isLastSingle), isRef)); if (isRefParen) { input.Expect(TokenType.EndParen); } } while (input.ReadIfType(TokenType.Comma)); } input.Expect(TokenType.EndParen); } else { break; } isParentheses = false; ret = new FuncCallItem(ret, args.ToArray()) { Debug = debug, InstanceName = instName, Overload = overload, IsLastArgSingle = isLastSingle, }; } } return(ret); }