예제 #1
0
        /// <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>");
        }
예제 #2
0
        /// <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;
        }
예제 #3
0
            /// <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;
                }
            }
예제 #4
0
 /// <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);
 }
예제 #5
0
        /// <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);
        }
예제 #6
0
        /// <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);
        }
예제 #7
0
            /// <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);
                }
            }
예제 #8
0
 /// <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;
 }
예제 #9
0
 /// <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);
 }
예제 #10
0
        /// <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;
            }
        }
예제 #11
0
        /// <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);
        }
예제 #12
0
        /// <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;
        }
예제 #13
0
        /// <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;
        }
예제 #14
0
        /// <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;
        }
예제 #15
0
        /// <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;
        }