/// <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; }