/// <summary> /// Creates a new ForNumItem with the given name. /// </summary> /// <param name="name">The name of the variable defined.</param> /// <param name="limit">The item that defines the limit of the loop.</param> /// <param name="start">The item that defines the start of the loop.</param> /// <param name="step">The item that defines the step of the loop.</param> /// <exception cref="System.ArgumentNullException">If name, start, /// or limit is null.</exception> public ForNumItem(NameItem name, IParseExp start, IParseExp limit, IParseExp step) { if (name == null) throw new ArgumentNullException("name"); if (start == null) throw new ArgumentNullException("start"); if (limit == null) throw new ArgumentNullException("limit"); this.start = start; this.limit = limit; this.Step = step; this.Name = name; this.Break = new LabelItem("<break>"); }
/// <summary> /// Creates a new ForNumItem with the given name. /// </summary> /// <param name="name">The name of the variable defined.</param> /// <param name="limit">The item that defines the limit of the loop.</param> /// <param name="start">The item that defines the start of the loop.</param> /// <param name="step">The item that defines the step of the loop.</param> /// <exception cref="System.ArgumentNullException">If name, start, or limit is null.</exception> public ForNumItem(NameItem name, IParseExp start, IParseExp limit, IParseExp step, BlockItem block) { if (name == null) { throw new ArgumentNullException(nameof(name)); } if (start == null) { throw new ArgumentNullException(nameof(start)); } if (limit == null) { throw new ArgumentNullException(nameof(limit)); } Start = start; Limit = limit; Step = step; Name = name; Block = block; }
/// <summary> /// Creates a new nest with the given parrent. /// </summary> /// <param name="parrent">The parrent nest.</param> /// <param name="gen">The generator used to generate code for this /// function.</param> /// <param name="storeParrent">True to create a field that stores /// the parrent instance; otherwise false.</param> /// <param name="captures">The local variables that have been /// captured by nested functions.</param> /// <param name="createType">True to create a nested type, otherwise /// false.</param> public NestInfo(NestInfo parrent, ILGenerator gen, NameItem[] captures, bool createType, bool storeParrent) { this.FreeLocals = new Dictionary<Type, Stack<LocalBuilder>>(); this.members = new HashSet<string>(); this.captures = new HashSet<NameItem>(captures); this.Parrent = parrent; this.Generator = gen; this.Locals = new Stack<Dictionary<string, VarDefinition>>(); this.Locals.Push(new Dictionary<string, VarDefinition>()); if (createType) { // create the type and constructor. this.TypeDef = parrent.TypeDef.DefineNestedType("<>c__DisplayClass" + (ID++), TypeAttributes.NestedPublic | TypeAttributes.Sealed | TypeAttributes.BeforeFieldInit); var ctor = this.TypeDef.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new Type[0]); var cgen = ctor.GetILGenerator(); // base(); cgen.Emit(OpCodes.Ldarg_0); cgen.Emit(OpCodes.Call, typeof(object).GetConstructor(new Type[0])); cgen.Emit(OpCodes.Ret); if (storeParrent) this.ParrentInst = this.TypeDef.DefineField("CS$<>__locals", parrent.TypeDef, FieldAttributes.Public); else this.ParrentInst = null; // create the local definition // ThisInst = new TypeDef(); this.ThisInst = gen.DeclareLocal(this.TypeDef); gen.Emit(OpCodes.Newobj, ctor); gen.Emit(OpCodes.Stloc, this.ThisInst); if (storeParrent) { // ThisInst.ParrentInst = this; gen.Emit(OpCodes.Ldloc, this.ThisInst); gen.Emit(OpCodes.Ldarg_0); gen.Emit(OpCodes.Stfld, this.ParrentInst); } } else { this.TypeDef = null; this.ThisInst = null; this.ParrentInst = null; } }
/// <summary> /// Defines a new local variable and returns an object used to get/set /// it's value. There are two possible variable types: Local and /// Captured. Which one is chosen depends on whether the variable is /// captured in the FunctionInfo used to create the current function. /// </summary> /// <param name="name">The name of the variable.</param> /// <returns>An object used to get/set it's value.</returns> public VarDefinition DefineLocal(NameItem name) { return curNest.DefineLocal(name); }
/// <summary> /// Searches for a variable with the given name and returns an object /// used to get/set it's value. There are three kinds of variables: /// Local, Captured, and Global. /// </summary> /// <param name="name">The name of the variable.</param> /// <returns>An object used to generate code for this variable.</returns> public VarDefinition FindVariable(NameItem name) { // search in the current nest var varDef = curNest.FindLocal(name); if (varDef != null) return varDef; // search for parrent captures var fields = new List<FieldBuilder>(); var cur = curNest.Parrent; while (cur != null) { varDef = cur.FindLocal(name); if (varDef != null) { if (varDef is LocalVarDef) throw new InvalidOperationException(); fields.Add(((CapturedVarDef)varDef).field); return new CapturedParVarDef(CurrentGenerator, fields.ToArray()); } fields.Add(cur.ParrentInst); cur = cur.Parrent; } // still not found, it is a global variable return new GlobalVarDef(CurrentGenerator, name.Name); }
/// <summary> /// Creates a new ChunkBuilder and initializes the state. /// </summary> /// <param name="tb">The root type of this chunk.</param> /// <param name="captures">An array of the global captures.</param> /// <param name="createType">True to create a nested type for the global /// function, this means that there are nested functions.</param> public ChunkBuilder(TypeBuilder tb, NameItem[] captures, bool createType) { //// ILuaEnviormnent $Env; var field = tb.DefineField("$Env", typeof(ILuaEnvironment), FieldAttributes.Private); //// ILuaMultiValue Invoke(ILuaEnvironment E, ILuaMultiValue args); var method = tb.DefineMethod("Invoke", MethodAttributes.Public | MethodAttributes.HideBySig, typeof(ILuaMultiValue), new[] { typeof(ILuaEnvironment), typeof(ILuaMultiValue) }); curNest = NestInfo.Create(tb, method.GetILGenerator(), captures, createType); AddInvoke(tb, method, field); AddConstructor(tb, field); AddAbstracts(tb); }
/// <summary> /// Defines a new Local variable and returns the field that /// represents it. /// </summary> /// <param name="name">The Lua name of the variable.</param> /// <returns>The variable that represents the local.</returns> public VarDefinition DefineLocal(NameItem name) { if (captures.Contains(name)) { string mName = name.Name; if (members.Contains(mName)) { int i = 0; while (members.Contains(mName + "_" + i)) i++; mName += "_" + i; } members.Add(mName); var field = TypeDef.DefineField(mName, typeof(ILuaValue), FieldAttributes.Public); return Locals.Peek()[name.Name] = new CapturedVarDef(Generator, ThisInst, field); } else { var loc = Generator.DeclareLocal(typeof(ILuaValue)); return Locals.Peek()[name.Name] = new LocalVarDef(Generator, loc); } }
/// <summary> /// Searches this type for a given local variable and returns an /// object to manipulate it's value. /// </summary> /// <param name="name">The Lua name of the variable.</param> /// <returns>A variable that will manipulate it's value or null if /// not found.</returns> public VarDefinition FindLocal(NameItem name) { // the iterator will return in the order they would be pop'd. foreach (var item in Locals) { VarDefinition ret; if (item.TryGetValue(name.Name, out ret)) return ret; } return null; }
/// <summary> /// Creates the root nest node from the given TypeBuilder. /// </summary> /// <param name="tb">The type builder to create for.</param> /// <param name="gen">The ILGenerator for the global function.</param> /// <param name="captures">The captures for the global function.</param> /// <param name="createType">Whether to create a type for the global /// function.</param> /// <returns>The new root nest node.</returns> public static NestInfo Create(TypeBuilder tb, ILGenerator gen, NameItem[] captures, bool createType) { NestInfo temp = new NestInfo(tb); return new NestInfo(temp, gen, captures, createType, false); }
/// <summary> /// Called when get/set the value of a variable, determines whether the /// given variable is a capture from a parrent nested function. /// </summary> /// <param name="name">The name of the variable.</param> public void GetName(NameItem/*!*/ name) { bool inFunc = true; // true if cur is in the current function. TreeNode node = cur; while (node != null) { if (node.CapturedLocals.ContainsKey(name.Name) || node.TrueLocals.ContainsKey(name.Name)) { // ignore the local if it is the current function if (!inFunc) { // if it is in TrueLocals, move it to CapturedLocals. NameItem boundItem; if (node.TrueLocals.TryGetValue(name.Name, out boundItem)) { node.TrueLocals.Remove(name.Name); node.CapturedLocals.Add(name.Name, boundItem); } // update all the CapturesParrent for any nodes between // the current node and the node that defines the local. TreeNode cur2 = cur; while (cur2 != node) { cur2.CapturesParrent = true; cur2 = cur2.Parrent; } } return; } if (node.IsFunction) inFunc = false; node = node.Parrent; } }
/// <summary> /// Adds a new argument to the definition. /// </summary> /// <param name="item">The item to add.</param> /// <exception cref="System.ArgumentNullException">If item is null.</exception> public void AddArgument(NameItem item) { if (item == null) throw new ArgumentNullException("item"); args.Add(item); }
/// <summary> /// Called when the item is a name 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(NameItem target) { if (target == null) throw new ArgumentNullException("target"); tree.GetName(target); return target; }
/// <summary> /// Called when the item is a name 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(NameItem target) { if (target == null) throw new ArgumentNullException("target"); // get the vaue of the given name and push onto stack. var field = compiler.FindVariable(target); field.Get(); return target; }
/// <summary> /// Reads a prefix-expression from the input. /// </summary> /// <param name="input">Where to read input from.</param> /// <param name="token">The token to append the total token onto.</param> /// <returns>The expression that was read.</returns> protected virtual IParseExp ReadPrefixExp(ITokenizer input, ref Token token) { Stack<UnaryInfo> ex = new Stack<UnaryInfo>(); IParseExp o = null; Token last, debug = input.Peek(); debug.Value = ""; // check for unary operators last = input.Peek(); while (last.Value == "-" || last.Value == "not" || last.Value == "#") { Read(input, ref debug); if (last.Value == "-") { ex.Push(new UnaryInfo(1, last.StartPos, last.StartLine)); } else if (last.Value == "not") { ex.Push(new UnaryInfo(2, last.StartPos, last.StartLine)); } else { Contract.Assert(last.Value == "#"); ex.Push(new UnaryInfo(3, last.StartPos, last.StartLine)); } last = input.Peek(); } // check for literals last = input.Peek(); int over = -1; if (last.Value != null) { NumberFormatInfo ni = CultureInfo.CurrentCulture.NumberFormat; if (last.Value != "..." && (char.IsNumber(last.Value, 0) || last.Value.StartsWith(ni.NumberDecimalSeparator, StringComparison.CurrentCulture))) { Read(input, ref debug); // read the number. try { o = new LiteralItem(double.Parse(last.Value, CultureInfo.CurrentCulture)) { Debug = last }; } catch (FormatException e) { throw new SyntaxException(Resources.BadNumberFormat, input.Name, last, e); } } else if (last.Value.StartsWith("&", StringComparison.Ordinal)) { Read(input, ref debug); try { o = new LiteralItem(Convert.ToDouble(long.Parse(last.Value.Substring(1), NumberStyles.AllowHexSpecifier, CultureInfo.CurrentCulture))) { Debug = last }; } catch (FormatException e) { throw new SyntaxException(Resources.BadNumberFormat, input.Name, last, e); } } else if (last.Value.StartsWith("\"", StringComparison.Ordinal)) { Read(input, ref debug); o = new LiteralItem(last.Value.Substring(1)) { Debug = last }; } else if (last.Value.StartsWith("{", StringComparison.Ordinal)) { o = ReadTable(input, ref debug); } else if (last.Value == "(") { Read(input, ref debug); o = ReadExp(input, ref debug); last = Read(input, ref debug); if (last.Value != ")") throw new SyntaxException(string.Format(Resources.TokenInvalidExpecting, last.Value, "expression", ")"), input.Name, last); } else if (last.Value == "true") { Read(input, ref debug); o = new LiteralItem(true) { Debug = last }; } else if (last.Value == "false") { Read(input, ref debug); o = new LiteralItem(false) { Debug = last }; } else if (last.Value == "nil") { Read(input, ref debug); o = new LiteralItem(null) { Debug = last }; } else if (last.Value == "function") { o = ReadFunctionHelper(input, ref debug, false, false); } else { // allow for specifying overloads on global variables if (last.Value.IndexOf('`') != -1) { if (!int.TryParse(last.Value.Substring(last.Value.IndexOf('`') + 1), out over)) throw new InvalidOperationException(Resources.OnlyNumbersInOverload); last.Value = last.Value.Substring(0, last.Value.IndexOf('`')); } Read(input, ref debug); o = new NameItem(last.Value) { Debug = last }; } } // read function calls and indexers { string inst = null; bool cont = true; while (cont) { last = input.Peek(); last.Value = last.Value ?? ""; if (last.Value == ".") { Read(input, ref debug); if (over != -1) throw new SyntaxException(Resources.FunctionCallAfterOverload, input.Name, last); if (inst != null) throw new SyntaxException(Resources.IndexerAfterInstance, input.Name, last); last = Read(input, ref debug); // allow for specifying an overload if (last.Value.IndexOf('`') != -1) { if (!int.TryParse(last.Value.Substring(last.Value.IndexOf('`') + 1), out over)) throw new InvalidOperationException(Resources.OnlyNumbersInOverload); last.Value = last.Value.Substring(0, last.Value.IndexOf('`')); } if (!IsName(last.Value)) throw new SyntaxException(string.Format(Resources.TokenNotAName, "indexer", last.Value), input.Name, last); if (!(o is IParsePrefixExp)) throw new SyntaxException(Resources.IndexAfterExpression, input.Name, last); o = new IndexerItem(o, new LiteralItem(last.Value) { Debug = last }) { Debug = debug }; } else if (last.Value == ":") { Read(input, ref debug); if (over != -1) throw new SyntaxException(Resources.FunctionCallAfterOverload, input.Name, last); if (inst != null) throw new SyntaxException(Resources.OneInstanceCall, input.Name, last); inst = Read(input, ref debug).Value; if (!IsName(inst)) throw new SyntaxException(string.Format(Resources.TokenNotAName, "indexer", last.Value), input.Name, last); } else if (last.Value == "[") { Read(input, ref debug); if (over != -1) throw new SyntaxException(Resources.FunctionCallAfterOverload, input.Name, last); if (inst != null) throw new SyntaxException(Resources.IndexerAfterInstance, input.Name, last); var temp = ReadExp(input, ref debug); last = Read(input, ref debug); o = new IndexerItem(o, temp) { Debug = debug }; if (last.Value != "]") throw new SyntaxException( string.Format(Resources.TokenInvalidExpecting, last.Value, "indexer", "]"), input.Name, last); } else if (last.Value.StartsWith("\"", StringComparison.Ordinal)) { Read(input, ref debug); FuncCallItem temp = new FuncCallItem(o, inst, over) { Debug = debug }; o = temp; temp.AddItem(new LiteralItem(last.Value.Substring(1)), false); inst = null; over = -1; } else if (last.Value == "{") { var temp = ReadTable(input, ref debug); FuncCallItem func = new FuncCallItem(o, inst, over) { Debug = debug }; o = func; func.AddItem(temp, false); inst = null; over = -1; } else if (last.Value == "(") { Read(input, ref debug); FuncCallItem func = new FuncCallItem(o, inst, over); o = func; inst = null; over = -1; while (input.Peek().Value != ")" && input.Peek().Value != null) { bool? byRef = null; if (input.Peek().Value == "@") { byRef = false; Read(input, ref debug); } else if (input.Peek().Value == "ref") { Read(input, ref debug); if (input.Peek().Value == "(") { Read(input, ref debug); byRef = true; } else byRef = false; } var temp = ReadExp(input, ref debug); if (byRef != null && !(temp is NameItem) && !(temp is IndexerItem)) throw new SyntaxException(Resources.OnlyVarByReference, input.Name, last); if (temp == null) throw new SyntaxException(string.Format(Resources.InvalidDefinition, "function call"), input.Name, last); func.AddItem(temp, byRef != null); if (byRef == true && (last = input.Read()).Value != ")") throw new SyntaxException(Resources.RefOneArgument, input.Name, last); if (input.Peek().Value == ",") Read(input, ref debug); else if (input.Peek().Value == ")") break; else throw new SyntaxException(string.Format(Resources.TokenInvalidExpecting2, input.Peek().Value, "function call", ",", ")"), input.Name, last); } if (input.Peek() == null) throw new SyntaxException(string.Format(Resources.UnexpectedEOF, "function call"), input.Name, last); Read(input, ref debug); func.Debug = debug; } else { if (inst != null) throw new SyntaxException(Resources.InstanceMissingArgs, input.Name, last); if (over != -1) throw new SyntaxException(Resources.OverloadMissingArgs, input.Name, last); cont = false; } } } // read exponents // HACK: This is needed here because the power operator has // higher precedence than the unary operators. Rather than // have unary operators handled in ReadExp, they are handled // so exponents need to be handled before we apply the // unary operators. if (input.Peek().Value == "^") { Read(input, ref debug); var temp = ReadPrefixExp(input, ref debug); BinOpItem item = new BinOpItem(o, BinaryOperationType.Power, temp) { Debug = debug }; o = item; } // now apply the unary operators while (ex.Count > 0) { var loc = ex.Pop(); Token tok = new Token(debug.Value, loc.StartPos, debug.EndPos, loc.StartLine, debug.EndLine); switch (loc.Version) { case 1: // neg if (o is LiteralItem) { object oo = (o as LiteralItem).Value; if (!(oo is double)) throw new SyntaxException(Resources.InvalidUnary, input.Name, debug); o = new LiteralItem(-(double)oo) { Debug = tok }; } else o = new UnOpItem(o, UnaryOperationType.Minus) { Debug = tok }; break; case 2: // not o = new UnOpItem(o, UnaryOperationType.Not) { Debug = tok }; break; case 3: // len o = new UnOpItem(o, UnaryOperationType.Length) { Debug = tok }; break; } } // finaly return token.Append(debug); return o; }
/// <summary> /// Reads a function from the input. Input must either be on the word 'function' or /// on the next token. If it is on 'function' and 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="token">The token to append the read Token to.</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(ITokenizer input, ref Token token, bool canName, bool local) { IParseVariable name = null; string inst = null; Token last = input.Peek(), debug = last; if (last.Value == "function") { input.Read(); // read 'function' last = input.Peek(); if (IsName(last.Value)) { Token nameTok = input.Read(); // read name name = new NameItem(last.Value) { Debug = last }; // handle indexers last = input.Peek(); while (last.Value == ".") { Read(input, ref nameTok); // read '.' last = input.Peek(); if (!IsName(last.Value)) break; name = new IndexerItem(name, new LiteralItem(last.Value) { Debug = last }) { Debug = nameTok }; Read(input, ref nameTok); } if (input.Peek().Value == ":") { Read(input, ref nameTok); inst = Read(input, ref nameTok).Value; if (!IsName(inst)) throw new SyntaxException(string.Format(Resources.TokenInvalid, last.Value, "function"), input.Name, last); } debug.Append(nameTok); } } if (name != null && !canName) throw new SyntaxException(Resources.FunctionCantHaveName, input.Name, debug); FuncDefItem ret = new FuncDefItem(name, local); ret.InstanceName = inst; last = Read(input, ref debug); if (last.Value != "(") throw new SyntaxException( string.Format(Resources.TokenInvalidExpecting, last.Value, "function", "("), input.Name, last); last = input.Peek(); while (last.Value != ")") { Token temp = Read(input, ref debug); // read the name if (!IsName(last.Value) && last.Value != "...") throw new SyntaxException(string.Format(Resources.TokenInvalid, last.Value, "function"), input.Name, temp); ret.AddArgument(new NameItem(last.Value) { Debug = last }); last = input.Peek(); if (last.Value == ",") Read(input, ref debug); else if (last.Value != ")") throw new SyntaxException( string.Format(Resources.TokenInvalidExpecting2, last.Value, "function", ",", ")"), input.Name, last); last = input.Peek(); } if (last.Value != ")") throw new SyntaxException( string.Format(Resources.TokenInvalidExpecting, last.Value, "function", ")"), input.Name, last); Read(input, ref debug); // read ')' BlockItem chunk = ReadBlock(input, ref debug); chunk.Return = chunk.Return ?? new ReturnItem(); ret.Block = chunk; last = Read(input, ref debug); if (last.Value != "end") throw new SyntaxException( string.Format(Resources.TokenInvalidExpecting, last.Value, "function", "end"), input.Name, last); token.Append(debug); ret.Debug = debug; return ret; }