// 
        //You can use the following additional attributes as you write your tests:
        //
        //Use ClassInitialize to run code before running the first test in the class
        //[ClassInitialize()]
        //public static void MyClassInitialize(TestContext testContext)
        //{
        //}
        //
        //Use ClassCleanup to run code after all tests in a class have run
        //[ClassCleanup()]
        //public static void MyClassCleanup()
        //{
        //}
        //
        //Use TestInitialize to run code before running each test
        //[TestInitialize()]
        //public void MyTestInitialize()
        //{
        //}
        //
        //Use TestCleanup to run code after each test has run
        //[TestCleanup()]
        //public void MyTestCleanup()
        //{
        //}
        //
        #endregion

        void ValidateDebug(Token debug, string prefix, string value, long startLine, long startPos, long endLine, long endPos)
        {
            Assert.AreEqual(value, debug.Value, prefix + ".Debug.Value");
            Assert.AreEqual(startLine, debug.StartLine, prefix + ".Debug.StartLine");
            Assert.AreEqual(startPos, debug.StartPos, prefix + ".Debug.StartPos");
            Assert.AreEqual(endLine, debug.EndLine, prefix + ".Debug.StartLine");
            Assert.AreEqual(endPos, debug.EndPos, prefix + ".Debug.EndPos");
        }
        /// <summary>
        /// Reads a table from the input.  Input must be either on the starting '{'.
        /// </summary>
        /// <param name="input">Where to read input from.</param>
        /// <param name="token">The token to append the read Tokenm to.</param>
        /// <returns>The table that was read.</returns>
        protected virtual TableItem ReadTable(ITokenizer input, ref Token token)
        {
            Token debug = input.Read();
            if (debug.Value != "{")
                throw new SyntaxException(
                    string.Format(Resources.TokenInvalidExpecting, debug.Value, "table", "{"),
                    input.Name, debug);

            TableItem ret = new TableItem();
            Token last = input.Peek();
            while (last.Value != "}")
            {
                if (last.Value == "[")
                {
                    Read(input, ref debug); // read the "["

                    var temp = ReadExp(input, ref debug);
                    if (temp == null)
                        throw new SyntaxException(string.Format(Resources.InvalidDefinition, "table"),
                            input.Name, debug);

                    // read ']'
                    last = Read(input, ref debug);
                    if (last.Value != "]")
                        throw new SyntaxException(
                            string.Format(Resources.TokenInvalidExpecting, last.Value, "table", "]"), 
                            input.Name, last);

                    // read '='
                    last = Read(input, ref debug);
                    if (last.Value != "=")
                        throw new SyntaxException(
                            string.Format(Resources.TokenInvalidExpecting, last.Value, "table", "="), 
                            input.Name, last);

                    // read the expression
                    var val = ReadExp(input, ref debug);
                    if (val == null)
                        throw new SyntaxException(string.Format(Resources.InvalidDefinition, "table"),
                            input.Name, debug);

                    ret.AddItem(temp, val);
                }
                else
                {
                    var val = ReadExp(input, ref debug);
                    if (input.Peek().Value == "=")
                    {
                        Read(input, ref debug); // read '='

                        NameItem name = val as NameItem;
                        if (name == null)
                            throw new SyntaxException(string.Format(Resources.InvalidDefinition, "table"),
                                input.Name, debug);

                        // read the expression
                        var exp = ReadExp(input, ref debug);
                        ret.AddItem(new LiteralItem(name.Name), exp);
                    }
                    else
                    {
                        ret.AddItem(null, val);
                    }
                }

                if (input.Peek().Value != "," && input.Peek().Value != ";")
                    break;
                else
                    Read(input, ref debug);
                last = input.Peek();
            } // end While

            Token end = Read(input, ref debug); // read the "}"
            if (end.Value != "}")
                throw new SyntaxException(
                    string.Format(Resources.TokenInvalidExpecting, end.Value, "table", "}"), 
                    input.Name, end);

            ret.Debug = debug;
            token.Append(debug);
            return ret;
        }
 /// <summary>
 /// Creates a new SyntaxException with the given message.
 /// </summary>
 /// <param name="message">The cause of the error.</param>
 /// <param name="source">The source token that caused the error.</param>
 /// <param name="inner">The inner exception.</param>
 public SyntaxException(string message, Token source, Exception inner)
     : this(message, null, source, inner) { }
        /// <summary>
        /// Reads a single token from the input stream.
        /// </summary>
        /// <returns>The token that was read or a null string token.</returns>
        /// <remarks>
        /// If it is at the end of the enumeration, it will return a token with
        /// a null string, the values of the other members are unspecified.
        /// </remarks>
        /// <exception cref="ModMaker.Lua.Parser.SyntaxException">If there is
        /// an error in the syntax of the input.</exception>
        protected virtual Token InternalRead()
        {
        start:
            ReadWhitespace();

            Token ret = new Token();
            ret.StartPos = Position;
            ret.StartLine = Line;

            string last = ReadElement();
            if (last == null)
                return new Token();

            // goto the start if this is a comment
            if (last == "-")
            {
                last = PeekElement();
                if (last == "-")
                {
                    ReadElement();
                    ReadComment();
                    goto start;
                }
                else
                    last = "-";
            }
            // read an identifier (e.g. 'foo' or '_cat').
            else if (char.IsLetter(last, 0) || last == "_")
            {
                StringBuilder str = new StringBuilder();
                bool over = false;
                str.Append(last);
                last = PeekElement();
                while (last != null && (char.IsLetterOrDigit(last, 0) || last == "_" || last == "`"))
                {
                    if (over)
                    {
                        if (last == "`")
                            throw new SyntaxException(Resources.OverloadOneGrave, Name, ret);
                        if (!char.IsDigit(last, 0))
                            throw new SyntaxException(Resources.OnlyNumbersInOverload, Name, ret);
                    }
                    else if (last == "`")
                    {
                        over = true;
                    }
                    ReadElement();
                    str.Append(last);
                    last = PeekElement();
                }

                last = str.ToString();
            }
            // read indexer, concat, and ...
            else if (last == ".")
            {
                if (PeekElement() == ".")
                {
                    ReadElement(); // read "."
                    if (PeekElement() == ".")
                    {
                        ReadElement(); // read "."
                        last = "...";
                    }
                    else
                        last = "..";
                }
            }
            // read a number
            else if (char.IsNumber(last, 0) || last == CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator)
            {
                return ReadNumber(last);
            }
            // read a literal string
            else if (last == "\"" || last == "'")
            {
                return ReadString(last == "'" ? -1 : -2, ret.StartPos, ret.StartLine);
            }
            // handle "["
            else if (last == "[")
            {
                last = PeekElement();
                if (last == "[" || last == "=")
                {
                    int dep = 0;
                    while (last == "=")
                    {
                        ReadElement(); // read "="
                        dep++;
                        last = PeekElement();
                    }
                    if (last != "[")
                        throw new SyntaxException(string.Format(Resources.InvalidDefinition, "long string"), Name, ret);

                    ReadElement(); // read "["
                    if (PeekElement() == "\n")
                        ReadElement();
                    return ReadString(dep, ret.StartPos, ret.StartLine);
                }
                else
                    last = "[";
            }
            // read ::
            else if (last == ":")
            {
                if (PeekElement() == ":")
                {
                    ReadElement(); // read ":"
                    last = "::";
                }
            }
            // read comparison operatos
            else if (last == ">" || last == "<" || last == "~" || last == "=")
            {
                if (PeekElement() == "=")
                {
                    last += "=";
                    ReadElement(); // read "="
                }
                else if (last == "~")
                    throw new SyntaxException("Invalid token '~'.", Name, ret);
            }

            // otherwise simply return the read text-element
            ret.EndPos = Position;
            ret.EndLine = Line;
            ret.Value = last;
            return ret;
        }
        /// <summary>
        /// Helper function that reads a number from the input.  The resulting 
        /// token should start with '&amp;' if the number is in hex format, 
        /// otherwise the number should be in a parseable double format.
        /// </summary>
        /// <param name="last">The first character of the number.</param>
        /// <returns>The token that was read.  It should start with '&amp;' if 
        /// the number is hex.</returns>
        protected virtual Token ReadNumber(string last)
        {
            Token ret = new Token();
            ret.StartPos = Position - (last == null ? 0 : last.Length);
            ret.StartLine = Line;

            // this version does nothing to check for a valid number, that is done in the parser.
            //   this only supports 0xNNN notation for hexadecimal numbers (where NNN is a char.IsNumber char or a-f or A-F).
            StringBuilder str = new StringBuilder();
            CultureInfo ci = CultureInfo.CurrentCulture;
            string l = last;
            bool hex = false;
            if (last == "0" && PeekElement().ToLowerInvariant() == "x")
            {
                hex = true;
                str.Append("&");
                ReadElement(); // read the 'x'
                last = PeekElement();
            }
            else
            {
                str.Append(last);
                last = PeekElement();
            }

            while (last != null && (char.IsNumber(last, 0) || (hex && ((last[0] >= 'a' && last[0] <= 'f') || (last[0] >= 'A' && last[0] <= 'F'))) ||
                (!hex && (last == ci.NumberFormat.NumberDecimalSeparator || last == "-" || (l != "." && last == "e")))))
            {
                ReadElement();
                str.Append(last);
                l = last;
                last = PeekElement();
            }

            ret.EndLine = Line;
            ret.EndPos = Position;
            ret.Value = str.ToString();
            return ret;
        }
        /// <summary>
        /// Helper function that reads a comment from the input, it assumes that
        /// the first two chars '--' have been already been read and the input 
        /// is on the next char.
        /// </summary>
        /// <returns>The token that holds the comments, this is unlikely to be 
        /// used except for debugging.</returns>
        /// <exception cref="ModMaker.Lua.Parser.SyntaxException">If there is
        /// an error in the syntax of the input.</exception>
        protected virtual Token ReadComment()
        {
            Token ret = new Token();
            StringBuilder build = new StringBuilder();
            build.Append("--");
            ret.StartLine = Line;
            ret.StartPos = Position;

            int depth = -1;
            string temp;
            if (PeekElement() == "[")
            {
                depth = 0;
                build.Append(ReadElement());
                while ((temp = ReadElement()) != null)
                {
                    build.Append(temp);

                    if (temp == "=")
                        depth++;
                    else if (temp == "\n")
                    {
                        ret.EndLine = Line;
                        ret.EndPos = Position;
                        ret.Value = build.ToString();
                        return ret;
                    }
                    else
                    {
                        if (temp != "[")
                            depth = -1;
                        break;
                    }
                }
            }

            int curDepth = -1;
            while ((temp = ReadElement()) != null)
            {
                build.Append(temp);
                if (depth == -1)
                {
                    if (temp == "\n")
                        break;
                }
                else
                {
                    if (curDepth != -1)
                    {
                        if (temp == "]")
                        {
                            if (curDepth == depth)
                                break;
                            else
                                curDepth = -1;
                        }
                        else if (temp == "=")
                            curDepth++;
                        else
                            curDepth = -1;
                    }
                    else if (temp == "]")
                        curDepth = 0;
                }
            }

            ret.EndLine = Line;
            ret.EndPos = Position;
            ret.Value = build.ToString();

            if (PeekElement() == null && depth != curDepth)
                throw new SyntaxException(string.Format(Resources.MissingEnd, "long comment"), ret);
            return ret;
        }
        /// <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>
        /// Parses the given Lua code into a IParseItem tree.
        /// </summary>
        /// <param name="input">The Lua code to parse.</param>
        /// <param name="name">The name of the chunk, used for exceptions.</param>
        /// <param name="hash">The hash of the Lua code, can be null.</param>
        /// <returns>The code as an IParseItem tree.</returns>
        /// <remarks>Simply calls Parse(Tokenizer, string, bool) with force:false.</remarks>
        public IParseItem Parse(ITokenizer input, string name, string hash)
        {
            if (input == null)
                throw new ArgumentNullException("input");

            // check if the chunk is already loaded
            if (UseCache)
            {
                lock (_lock)
                {
                    if (_cache != null && hash != null && _cache.ContainsKey(hash))
                        return _cache[hash];
                }
            }

            // parse the chunk
            Token temp = new Token();
            IParseItem read = ReadBlock(input, ref temp);
            Token end = Read(input, ref temp);
            if (end.Value != null)
                throw new SyntaxException(string.Format(Resources.TokenEOF, end.Value), input.Name, end);

            // store the loaded chunk in the cache
            lock (_lock)
            {
                if (_cache != null && hash != null)
                    _cache[hash] = read;
            }

            return read;
        }
        /// <summary>
        /// Reads a for statement from the input.
        /// </summary>
        /// <param name="input">Where to read input from.</param>
        /// <param name="prev">The token to append what is read into.</param>
        /// <returns>The object that was read.</returns>
        protected virtual IParseStatement ReadFor(ITokenizer input, ref Token prev)
        {
            var debug = input.Read(); // read 'for'
            if (debug.Value != "for")
                throw new InvalidOperationException(string.Format(Resources.MustBeOn, "for", "ReadFor"));

            // read a name
            var name = Read(input, ref debug);
            if (!IsName(name.Value))
                throw new SyntaxException(
                    string.Format(Resources.TokenNotAName, "for", name.Value),
                    input.Name, name);
            if (_reserved.Contains(name.Value))
                throw new SyntaxException(
                    string.Format(Resources.TokenReserved, name.Value),
                    input.Name, name);

            // numeric for
            if (input.Peek().Value == "=")
            {
                var ret = ReadNumberFor(input, ref debug, name);
                prev.Append(debug);
                return ret;
            }
            // generic for statement
            else
            {
                var ret = ReadGenericFor(input, ref debug, name);
                prev.Append(debug);
                return ret;
            }
        }
 /// <summary>
 /// Reads a normal function statement from the input.
 /// </summary>
 /// <param name="input">Where to read input from.</param>
 /// <param name="prev">The token to append what is read into.</param>
 /// <returns>The object that was read.</returns>
 protected virtual IParseStatement ReadFunction(ITokenizer input, ref Token prev)
 {
     return ReadFunctionHelper(input, ref prev, true, false);
 }
        /// <summary>
        /// Reads a return statement from the input.
        /// </summary>
        /// <param name="input">Where to read input from.</param>
        /// <param name="prev">The token to append what is read into.</param>
        /// <returns>The object that was read.</returns>
        protected virtual ReturnItem ReadReturn(ITokenizer input, ref Token prev)
        {
            var debug = input.Read(); // read 'return'
            ReturnItem r = new ReturnItem();
            if (debug.Value != "return")
                throw new InvalidOperationException(string.Format(Resources.MustBeOn, "return", "ReadReturn"));

            var name = input.Peek();
            if (name.Value != "end" && name.Value != "until" && name.Value != "elseif" &&
                name.Value != "else")
            {
                r.AddExpression(ReadExp(input, ref debug));
                while (input.Peek().Value == ",")
                {
                    Read(input, ref debug); // read ','
                    r.AddExpression(ReadExp(input, ref debug));
                }

                if (input.Peek().Value == ";")
                {
                    Read(input, ref debug); // read ';'
                }

                // look at the next token for validation but keep it in the
                //  reader for the parrent.
                name = input.Peek();
                if (name.Value != "end" && name.Value != "until" && name.Value != "elseif" &&
                    name.Value != "else" && !IsNullOrWhiteSpace(name.Value))
                    throw new SyntaxException(
                        Resources.ReturnAtEnd,
                        input.Name, debug);
            }

            prev.Append(debug);
            r.Debug = debug;
            return r;
        }
        /// <summary>
        /// Reads a class statement from the input.
        /// </summary>
        /// <param name="input">Where to read input from.</param>
        /// <param name="prev">The token to append what is read into.</param>
        /// <returns>The object that was read.</returns>
        protected virtual IParseStatement ReadClass(ITokenizer input, ref Token prev)
        {
            var debug = input.Read(); // read 'class'
            string sname = null;
            List<string> imp = new List<string>();
            if (debug.Value != "class")
                throw new InvalidOperationException(string.Format(Resources.MustBeOn, "class", "ReadClass"));

            if (input.Peek().Value.StartsWith("'", StringComparison.Ordinal) ||
                input.Peek().Value.StartsWith("\"", StringComparison.Ordinal))
            {
                var name = Read(input, ref debug);
                sname = name.Value.Substring(1);
                if (input.Peek().Value == "(")
                {
                    Read(input, ref debug); // read '('
                    while (input.Peek().Value != ")")
                    {
                        // read the name
                        name = Read(input, ref debug);
                        if (!IsName(name.Value))
                            throw new SyntaxException(
                                string.Format(Resources.TokenNotAName, "class", name.Value),
                                input.Name, name);
                        imp.Add(name.Value);

                        // read ','
                        name = Read(input, ref debug);
                        if (name.Value != ",")
                            throw new SyntaxException(
                                string.Format(Resources.TokenInvalid, name.Value, "class"),
                                input.Name, name);
                    }
                    Read(input, ref debug); // read ')'
                }
            }
            else
            {
                var name = Read(input, ref debug);
                sname = name.Value;
                if (!IsName(sname))
                    throw new SyntaxException(
                        string.Format(Resources.TokenNotAName, "class", name.Value),
                        input.Name, name);
                if (input.Peek().Value == ":")
                {
                    do
                    {
                        // simply include the '.' in the name.
                        string n = "";
                        do
                        {
                            Read(input, ref debug); // read ':' or ','
                            n += (n == "" ? "" : ".") + Read(input, ref debug).Value;
                        } while (input.Peek().Value == ".");

                        imp.Add(n);
                    } while (input.Peek().Value == ",");
                }
            }

            prev.Append(debug);
            return new ClassDefItem(sname, imp.ToArray()) { Debug = debug };
        }
        /// <summary>
        /// Reads a local statement from the input.
        /// </summary>
        /// <param name="input">Where to read input from.</param>
        /// <param name="prev">The token to append what is read into.</param>
        /// <returns>The object that was read.</returns>
        protected virtual IParseStatement ReadLocal(ITokenizer input, ref Token prev)
        {
            var debug = input.Read(); // read 'local'
            if (debug.Value != "local")
                throw new InvalidOperationException(string.Format(Resources.MustBeOn, "local", "ReadLocal"));

            var name = input.Peek();
            if (name.Value == "function")
            {
                prev.Append(debug);
                return ReadFunctionHelper(input, ref prev, true, true);
            }
            else
            {
                Read(input, ref debug); // read name
                if (!IsName(name.Value))
                    throw new SyntaxException(
                        string.Format(Resources.TokenNotAName, "local", name.Value),
                        input.Name, name);
                if (_reserved.Contains(name.Value))
                    throw new SyntaxException(
                        string.Format(Resources.TokenReserved, name.Value),
                        input.Name, name);

                var i = ReadAssignment(input, ref debug, true, new NameItem(name.Value) { Debug = name });
                prev.Append(debug);
                return i;
            }
        }
 /// <summary>
 /// Reads a token from the tokenizer and appends the read
 /// value to the given token.
 /// </summary>
 /// <param name="input">Where to get the input from.</param>
 /// <param name="token">A token to append the read token to.</param>
 /// <returns>The token that was read.</returns>
 protected static Token Read(ITokenizer input, ref Token token)
 {
     Token ret = input.Read();
     token.Append(ret);
     return ret;
 }
        /// <summary>
        /// Reads a block of code from the input.  Any end tokens
        /// should not be read and are handled by the parrent call
        /// (e.g. 'end' or 'until').
        /// </summary>
        /// <param name="input">Where to read input from.</param>
        /// <param name="prev">The token to append the total token onto.</param>
        /// <returns>The item that was read.</returns>
        protected virtual BlockItem ReadBlock(ITokenizer input, ref Token prev)
        {
            BlockItem ret = new BlockItem();
            Token total = input.Peek();
            total.Value = "";
            Token name;

            while ((name = input.Peek()).Value != null)
            {
                if (Functions.ContainsKey(name.Value))
                {
                    var temp = Functions[name.Value](input, ref total);
                    ret.AddItem(temp);
                }
                else if (name.Value == "return")
                {
                    ret.Return = ReadReturn(input, ref total);
                    return ret;
                }
                else if (name.Value == ";")
                {
                    Read(input, ref total); // read ';'
                }
                else if (name.Value == "end" || name.Value == "else" || name.Value == "elseif" ||
                    name.Value == "until")
                {
                    // don'type read as it will be handled by the parrent
                    prev.Append(total); // don't add 'end' to the prev 
                    ret.Debug = total;  //   or the current block, this 
                    return ret;         //   end belongs to the parrent.
                }
                else
                {
                    Token debug = name;
                    debug.Value = "";
                    var exp = ReadExp(input, ref debug);
                    if (exp is FuncCallItem)
                    {
                        (exp as FuncCallItem).Statement = true;
                        ret.AddItem((FuncCallItem)exp);
                    }
                    else if (exp is LiteralItem)
                    {
                        throw new SyntaxException(
                            "A literal is not a variable.",
                            input.Name, debug);
                    }
                    else if (exp is NameItem || exp is IndexerItem)
                    {
                        var i = ReadAssignment(input, ref debug, false, (IParseVariable)exp);
                        ret.AddItem(i);
                    }
                    else
                        throw new SyntaxException(
                            string.Format(Resources.TokenStatement, name.Value),
                            input.Name, debug);

                    total.Append(debug);
                }
            } // end While

            // only gets here if this is the global function
            ret.Debug = total;
            ret.Return = ret.Return ?? new ReturnItem();
            return ret;
        }
        /// <summary>
        /// Reads an assignment statement from the input.  The input is currently
        /// after the first name, on the comma or equal sign.  The debug token
        /// contains the name and should contain the entire statement.
        /// </summary>
        /// <param name="input">Where to read input from.</param>
        /// <param name="debug">Currently contains the first name, and after
        /// should contain the entire statement.</param>
        /// <param name="local">True if this is a local definition, otherwise false.</param>
        /// <param name="variable">The first variable that was read.</param>
        /// <returns>The statement that was read.</returns>
        protected virtual AssignmentItem ReadAssignment(ITokenizer input, ref Token debug, bool local, IParseVariable variable)
        {
            // read each of the variable names
            AssignmentItem assign = new AssignmentItem(local);
            assign.AddName(variable);
            while (input.Peek().Value == ",")
            {
                Read(input, ref debug); // read ','

                // read the left-hand-expression
                var exp = ReadExp(input, ref debug);
                if ((local && !(exp is NameItem)) || (!local && !(exp is IParseVariable)))
                    throw new SyntaxException(Resources.NameOrExpForVar, input.Name, debug);
                assign.AddName((IParseVariable)exp);
            }

            // read the initial values
            if (input.Peek().Value == "=")
            {
                Read(input, ref debug); // read '='
                assign.AddItem(ReadExp(input, ref debug));

                while (input.Peek().Value == ",")
                {
                    Read(input, ref debug); // read ','
                    assign.AddItem(ReadExp(input, ref debug));
                }
            }
            else if (!local)
                throw new SyntaxException(
                    string.Format(Resources.InvalidDefinition, "assignment"),
                    input.Name, debug);

            assign.Debug = debug;
            return assign;
        }
        /// <summary>
        /// Reads part of a generic for loop from the input.  The input is
        /// currently on the token after the first name and debug contains
        /// the parts read for the 'for' loop.  'name' contains the name of
        /// the first variable.
        /// </summary>
        /// <param name="input">Where to read input from/</param>
        /// <param name="debug">The token that currently holds what was read
        /// so far in the for statement and should after contain the entire loop.</param>
        /// <param name="name">The token that contains the name of the variable.</param>
        /// <returns>The loop object that was read.</returns>
        protected virtual ForGenItem ReadGenericFor(ITokenizer input, ref Token debug, Token name)
        {
            // read the variables
            List<NameItem> names = new List<NameItem>();
            names.Add(new NameItem(name.Value) { Debug = name });

            while (input.Peek().Value == ",")
            {
                Read(input, ref debug); // read ','

                // read the name
                name = Read(input, ref debug);
                if (!IsName(name.Value))
                    throw new SyntaxException(
                        string.Format(Resources.TokenNotAName, "for", name.Value),
                        input.Name, name);
                if (_reserved.Contains(name.Value))
                    throw new SyntaxException(
                        string.Format(Resources.TokenReserved, name.Value),
                        input.Name, name);

                names.Add(new NameItem(name.Value) { Debug = name });
            }

            // check for 'in'
            name = Read(input, ref debug);
            if (name.Value != "in")
                throw new SyntaxException(
                    string.Format(Resources.TokenInvalidExpecting, name.Value, "for", "in"),
                    input.Name, name);

            // read the expression-list
            ForGenItem f = new ForGenItem(names);
            f.AddExpression(ReadExp(input, ref debug));
            while (input.Peek().Value == ",")
            {
                Read(input, ref debug); // read ","
                f.AddExpression(ReadExp(input, ref debug));
            }

            // check for 'do'
            name = Read(input, ref debug);
            if (name.Value != "do")
                throw new SyntaxException(
                    string.Format(Resources.TokenInvalidExpecting, name.Value, "for", "do"),
                    input.Name, name);

            // read the chunk
            f.Block = ReadBlock(input, ref debug);

            // read 'end'
            name = Read(input, ref debug);
            if (name.Value != "end")
                throw new SyntaxException(
                    string.Format(Resources.TokenInvalidExpecting, name.Value, "for", "end"),
                    input.Name, name);

            f.Debug = debug;
            return f;
        }
        /// <summary>
        /// Reads an if statement from the input.
        /// </summary>
        /// <param name="input">Where to read input from.</param>
        /// <param name="prev">The token to append what is read into.</param>
        /// <returns>The object that was read.</returns>
        protected virtual IParseStatement ReadIf(ITokenizer input, ref Token prev)
        {
            var debug = input.Read(); // read 'if'
            IfItem i = new IfItem();
            if (debug.Value != "if")
                throw new InvalidOperationException(string.Format(Resources.MustBeOn, "if", "ReadIf"));

            // read the initial expression
            i.Exp = ReadExp(input, ref debug);

            // read 'then'
            var name = Read(input, ref debug);
            if (name.Value != "then")
                throw new SyntaxException(
                    string.Format(Resources.TokenInvalid, name.Value, "if"),
                    input.Name, debug);

            // read the block
            var readBlock = ReadBlock(input, ref debug);
            i.Block = readBlock;

            // handle elseif(s)
            while ((name = input.Peek()).Value == "elseif")
            {
                Read(input, ref debug); // read 'elseif'

                // read the expression
                var readExp = ReadExp(input, ref debug);

                // read 'then'
                name = Read(input, ref debug);
                if (name.Value != "then")
                    throw new SyntaxException(
                        string.Format(Resources.TokenInvalid, name.Value, "elseif"),
                        input.Name, debug);

                // read the block
                readBlock = ReadBlock(input, ref debug);
                i.AddElse(readExp, readBlock);
            }

            // handle else
            if (name.Value != "else" && name.Value != "end")
                throw new SyntaxException(
                    string.Format(Resources.TokenInvalid, name.Value, "if"),
                    input.Name, debug);
            if (name.Value == "else")
            {
                Read(input, ref debug); // read 'else'

                // read the block
                readBlock = ReadBlock(input, ref debug);
                i.ElseBlock = readBlock;
            }

            // read 'end'
            name = Read(input, ref debug);
            if (name.Value != "end")
                throw new SyntaxException(
                    string.Format(Resources.TokenInvalid, name.Value, "if"),
                    input.Name, debug);

            prev.Append(debug);
            i.Debug = debug;
            return i;
        }
        /// <summary>
        /// Reads part of a numerical for loop from the input.  The input is 
        /// currently on the equals sign '=' and the debug token currently
        /// contains the parts read for the 'for' loop.  'name' contains the
        /// name of the variable.
        /// </summary>
        /// <param name="input">Where to read input from.</param>
        /// <param name="debug">The token that currently holds what was read
        /// so far in the for statement and should after contain the entire loop.</param>
        /// <param name="name">The token that contains the name of the variable.</param>
        /// <returns>The loop object that was read.</returns>
        protected virtual ForNumItem ReadNumberFor(ITokenizer input, ref Token debug, Token name)
        {
            // read "="
            var temp = Read(input, ref debug);
            if (temp.Value != "=")
                throw new InvalidOperationException(string.Format(Resources.MustBeOn, "=", "ReadNumberFor"));

            // get the 'start' value
            var start = ReadExp(input, ref debug);

            // read ','
            temp = Read(input, ref debug);
            if (temp.Value != ",")
                throw new SyntaxException(
                    string.Format(Resources.TokenInvalidExpecting, temp.Value, "for", ","),
                    input.Name, temp);

            // get the 'limit'
            var limit = ReadExp(input, ref debug);

            // read ','
            IParseExp step = null;
            if (input.Peek().Value == ",")
            {
                Read(input, ref debug);

                // read the 'step'
                step = ReadExp(input, ref debug);
            }

            ForNumItem i = new ForNumItem(new NameItem(name.Value) { Debug = name }, start, limit, step);

            // check for 'do'
            name = Read(input, ref debug);
            if (name.Value != "do")
                throw new SyntaxException(
                    string.Format(Resources.TokenInvalidExpecting, name.Value, "for", "do"),
                    input.Name, name);

            // read the block
            i.Block = ReadBlock(input, ref debug);

            // read 'end'
            name = Read(input, ref debug);
            if (name.Value != "end")
                throw new SyntaxException(
                    string.Format(Resources.TokenInvalidExpecting, name.Value, "for", "end"),
                    input.Name, name);

            i.Debug = debug;
            return i;
        }
        /// <summary>
        /// Reads a repeat statement from the input.
        /// </summary>
        /// <param name="input">Where to read input from.</param>
        /// <param name="prev">The token to append what is read into.</param>
        /// <returns>The object that was read.</returns>
        protected virtual IParseStatement ReadRepeat(ITokenizer input, ref Token prev)
        {
            var debug = input.Read(); // read 'repeat'
            RepeatItem repeat = new RepeatItem();
            if (debug.Value != "repeat")
                throw new InvalidOperationException(string.Format(Resources.MustBeOn, "repeat", "ReadRepeat"));

            // read the block
            repeat.Block = ReadBlock(input, ref debug);

            // read 'until'
            var name = Read(input, ref debug);
            if (name.Value != "until")
                throw new SyntaxException(
                    string.Format(Resources.TokenInvalidExpecting, name.Value, "repeat", "until"),
                    input.Name, name);

            // read the expression
            repeat.Expression = ReadExp(input, ref debug);

            prev.Append(debug);
            repeat.Debug = debug;
            return repeat;
        }
        /// <summary>
        /// Reads a label statement from the input.
        /// </summary>
        /// <param name="input">Where to read input from.</param>
        /// <param name="prev">The token to append what is read into.</param>
        /// <returns>The object that was read.</returns>
        protected virtual IParseStatement ReadLabel(ITokenizer input, ref Token prev)
        {
            var debug = input.Read(); // read '::'
            if (debug.Value != "::")
                throw new InvalidOperationException(string.Format(Resources.MustBeOn, "::", "ReadLabel"));

            // read the label
            Token label = Read(input, ref debug);

            // read '::'
            var name = Read(input, ref debug);
            if (name.Value != "::")
                throw new SyntaxException(
                    string.Format(Resources.TokenInvalidExpecting, name.Value, "label", "::"),
                    input.Name, debug);

            prev.Append(debug);
            return new LabelItem(label.Value) { Debug = debug };
        }
        /// <summary>
        /// Reads a break statement from the input.
        /// </summary>
        /// <param name="input">Where to read input from.</param>
        /// <param name="prev">The token to append what is read into.</param>
        /// <returns>The object that was read.</returns>
        protected virtual IParseStatement ReadBreak(ITokenizer input, ref Token prev)
        {
            var ret = input.Read();
            if (ret.Value != "break")
                throw new InvalidOperationException(string.Format(Resources.MustBeOn, "break", "ReadBreak"));

            prev.Append(ret);
            return new GotoItem("<break>") { Debug = ret };
        }
Exemple #23
0
        /// <summary>
        /// Helper function that reads a string from the input, it assumes that
        /// it is on the first character in the string.
        /// </summary>
        /// <param name="depth">The depth of the long-string or -1 for ' or 
        /// -2 for ".</param>
        /// <param name="line">The starting line of the string.</param>
        /// <param name="pos">The starting position of the string.</param>
        /// <returns>The token that represents the string read. The token should
        /// start with a ".</returns>
        /// <exception cref="ModMaker.Lua.Parser.SyntaxException">If there is
        /// an error in the syntax of the input.</exception>
        protected virtual Token ReadString(int depth, long pos, long line)
        {
            StringBuilder str = new StringBuilder();
            str.Append("\"");
            Token ret = new Token();
            ret.StartPos = pos;
            ret.StartLine = line;

            while (PeekElement() != null)
            {
                string temp = ReadElement();

                if (temp == "'" && depth == -1)
                    break;
                else if (temp == "\"" && depth == -2)
                    break;
                else if (temp == "\n" && depth < 0)
                    throw new SyntaxException(string.Format(Resources.MissingEnd, "string literal"), Name, ret);
                else if (temp == "]" && depth >= 0)
                {
                    int j = 0;
                    while (PeekElement() == "=")
                    {
                        j++;
                        ReadElement();
                    }

                    if (PeekElement() != "]" || j != depth)
                    {
                        // if this isn't the end of the string,
                        //   append the parts read already.
                        str.Append(']');
                        str.Append('=', j);
                    }
                    else
                    {
                        ReadElement();
                        break;
                    }
                }
                else if (temp == "\\")
                {
                    if (depth >= 0)
                    {
                        str.Append("\\");
                        continue;
                    }

                    temp = ReadElement();
                    switch (temp)
                    {
                        case "'":
                        case "\"":
                        case "\\":
                        case "\n":
                            str.Append(temp);
                            break;
                        case "z":
                            ReadWhitespace();
                            break;
                        case "n":
                            str.Append("\n");
                            break;
                        case "a":
                            str.Append('\a');
                            break;
                        case "b":
                            str.Append('\b');
                            break;
                        case "f":
                            str.Append('\f');
                            break;
                        case "repeat":
                            str.Append('\r');
                            break;
                        case "t":
                            str.Append('\t');
                            break;
                        case "v":
                            str.Append('\v');
                            break;
                        case "x":
                            {
                                int ii = 0;
                                temp = ReadElement();
                                if (!"0123456789ABCDEFabcdef".Contains(temp))
                                    throw new SyntaxException(string.Format(Resources.InvalidEscape, "x" + temp), Name, ret);
                                ii = int.Parse(temp, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture);
                                temp = ReadElement();
                                if (!"0123456789ABCDEFabcdef".Contains(temp))
                                    throw new SyntaxException(string.Format(Resources.InvalidEscape, "x" + ii.ToString("x", CultureInfo.CurrentCulture) + temp), Name, ret);
                                ii = (ii >> 16) + int.Parse(temp, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture);
                                str.Append((char)ii);
                                break;
                            }
                        case "0":
                        case "1":
                        case "2":
                        case "3":
                        case "4":
                        case "5":
                        case "6":
                        case "7":
                        case "8":
                        case "9":
                            {
                                int ii = 0;
                                if (!"0123456789".Contains(PeekElement()))
                                    continue;
                                temp = ReadElement();
                                ii = int.Parse(temp, CultureInfo.InvariantCulture);
                                if ("0123456789".Contains(PeekElement()))
                                {
                                    temp = ReadElement();
                                    ii = (ii * 10) + int.Parse(temp, CultureInfo.InvariantCulture);
                                    if ("0123456789".Contains(PeekElement()))
                                    {
                                        temp = ReadElement();
                                        ii = (ii * 10) + int.Parse(temp, CultureInfo.InvariantCulture);
                                    }
                                }
                                str.Append((char)ii);
                                break;
                            }
                        default:
                            throw new SyntaxException(string.Format(Resources.InvalidEscape, temp), Name, ret);
                    }
                }
                else
                    str.Append(temp);
            }

            ret.EndPos = Position;
            ret.EndLine = Line;
            ret.Value = str.ToString();
            return ret;
        }
        /// <summary>
        /// Reads a goto statement from the input.
        /// </summary>
        /// <param name="input">Where to read input from.</param>
        /// <param name="prev">The token to append what is read into.</param>
        /// <returns>The object that was read.</returns>
        protected virtual IParseStatement ReadGoto(ITokenizer input, ref Token prev)
        {
            var debug = input.Read(); // read 'goto'
            if (debug.Value != "goto")
                throw new InvalidOperationException(string.Format(Resources.MustBeOn, "goto", "ReadGoto"));
            
            // read the target
            var name = Read(input, ref debug);
            if (!IsName(name.Value))
                throw new SyntaxException(
                    string.Format(Resources.TokenNotAName, "goto", name.Value),
                    input.Name, debug);

            if (_reserved.Contains(name.Value))
                throw new SyntaxException(
                    string.Format(Resources.TokenReserved, name.Value),
                    input.Name, name);

            prev.Append(debug);
            return new GotoItem(name.Value) { Debug = debug };
        }
Exemple #25
0
 /// <summary>
 /// Pushes a token back onto the tokenizer.  This will allow to reverse
 /// a read.
 /// </summary>
 /// <param name="token">The token to push-back.</param>
 public void PushBack(Token token)
 {
     peek.Push(token);
 }
        /// <summary>
        /// Reads a do statement from the input.
        /// </summary>
        /// <param name="input">Where to read input from.</param>
        /// <param name="prev">The token to append what is read into.</param>
        /// <returns>The object that was read.</returns>
        protected virtual IParseStatement ReadDo(ITokenizer input, ref Token prev)
        {
            var debug = input.Read(); // read 'do'
            if (debug.Value != "do")
                throw new InvalidOperationException(string.Format(Resources.MustBeOn, "do", "ReadDo"));

            // read the block
            var ret = ReadBlock(input, ref debug);

            // ensure that it ends with 'end'
            Token end = Read(input, ref debug);
            if (end.Value != "end")
                throw new SyntaxException(
                    string.Format(Resources.TokenInvalidExpecting, end.Value, "do", "end"),
                    input.Name, end);
            
            prev.Append(debug);
            return ret;
        }
 /// <summary>
 /// Creates a new SyntaxException with the given message.
 /// </summary>
 /// <param name="message">The cause of the exception.</param>
 /// <param name="file">The source file that caused the exception.</param>
 /// <param name="source">The source token that caused the exception.</param>
 public SyntaxException(string message, string file, Token source)
     : this(message, file, source, null) { }
        /// <summary>
        /// Reads a while statement from the input.
        /// </summary>
        /// <param name="input">Where to read input from.</param>
        /// <param name="prev">The token to append what is read into.</param>
        /// <returns>The object that was read.</returns>
        protected virtual IParseStatement ReadWhile(ITokenizer input, ref Token prev)
        {
            var debug = input.Read(); // read 'while'
            WhileItem w = new WhileItem();
            if (debug.Value != "while")
                throw new InvalidOperationException(string.Format(Resources.MustBeOn, "while", "ReadWhile"));
            
            // read the expression
            w.Exp = ReadExp(input, ref debug);

            // read 'do'
            var name = Read(input, ref debug);
            if (name.Value != "do")
                throw new SyntaxException(
                    string.Format(Resources.TokenInvalidExpecting, name.Value, "while", "do"),
                    input.Name, name);

            // read the block
            w.Block = ReadBlock(input, ref debug);

            // read 'end'
            name = Read(input, ref debug);
            if (name.Value != "end")
                throw new SyntaxException(
                    string.Format(Resources.TokenInvalidExpecting, name.Value, "while", "end"),
                    input.Name, name);

            prev.Append(debug);
            w.Debug = debug;
            return w;
        }
 /// <summary>
 /// Creates a new SyntaxException with the given message.
 /// </summary>
 /// <param name="file">The file that caused the exception.</param>
 /// <param name="message">The cause of the exception.</param>
 /// <param name="source">The source token that caused the exception.</param>
 /// <param name="inner">The inner exception.</param>
 public SyntaxException(string message, string file, Token source, Exception inner)
     : base("Error in the syntax of the file.\nMessage: " + message, inner)
 {
     this.SourceToken = source;
     this.SourceFile = file;
 }
        /// <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;
        }