context: // NAnt - A .NET build tool
  // Copyright (C) 2001-2003 Gerry Shaw
  //
  // This program is free software; you can redistribute it and/or modify
  // it under the terms of the GNU General Public License as published by
  // the Free Software Foundation; either version 2 of the License, or
  // (at your option) any later version.
  //
  // This program is distributed in the hope that it will be useful,
  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  // GNU General Public License for more details.
  //
  // You should have received a copy of the GNU General Public License
  // along with this program; if not, write to the Free Software
  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  //
  // Jaroslaw Kowalski (jkowalski@users.sourceforge.net)
  
  using System;
  using System.IO;
  using System.Collections;
  using System.Globalization;
  using System.Reflection;
  using System.Text;
  
  using NAnt.Core.Extensibility;
  using NAnt.Core.Util;
  
  namespace NAnt.Core {
      /// <summary>
      /// 
      /// </summary>
      public abstract class ExpressionEvalBase {
  
          enum EvalMode {
              Evaluate,
              ParseOnly
          }
  
          private EvalMode _evalMode = EvalMode.Evaluate;
          private ExpressionTokenizer _tokenizer;
          private readonly Project _project;
  
          /// <summary>
          /// Initializes a new instance of the <see cref="ExpressionEvalBase"/> class.
          /// </summary>
          /// <param name="project">The project.</param>
          /// <exception cref="System.ArgumentNullException">project</exception>
          public ExpressionEvalBase(Project project) {
              if (project == null)
                  throw new ArgumentNullException("project");
  
              _project = project;
          }
  
          #region Public Instance Properties
  
          /// <summary>
          /// Gets the project.
          /// </summary>
          /// <value>
          /// The project.
          /// </value>
          public Project Project {
              get { return _project; }
          }
  
          #endregion Public Instance Properties
  
          /// <summary>
          /// Evaluates the specified tokenizer.
          /// </summary>
          /// <param name="tokenizer">The tokenizer.</param>
          /// <returns></returns>
          public object Evaluate(ExpressionTokenizer tokenizer) {
              _evalMode = EvalMode.Evaluate;
              _tokenizer = tokenizer;
              return ParseExpression();
          }
  
          /// <summary>
          /// Evaluates the specified s.
          /// </summary>
          /// <param name="s">The s.</param>
          /// <returns></returns>
          public object Evaluate(string s) {
              _tokenizer = new ExpressionTokenizer();
              _evalMode = EvalMode.Evaluate;
  
              _tokenizer.InitTokenizer(s);
              object val = ParseExpression();
              if (_tokenizer.CurrentToken != ExpressionTokenizer.TokenType.EOF) {
                  throw BuildParseError("Unexpected token at the end of expression", _tokenizer.CurrentPosition);
              }
              return val;
          }
  
          /// <summary>
          /// Checks the syntax.
          /// </summary>
          /// <param name="s">The s.</param>
          public void CheckSyntax(string s) {
              _tokenizer = new ExpressionTokenizer();
              _evalMode = EvalMode.ParseOnly;
  
              _tokenizer.InitTokenizer(s);
              ParseExpression();
              if (_tokenizer.CurrentToken != ExpressionTokenizer.TokenType.EOF) {
                  throw BuildParseError("Unexpected token at the end of expression", _tokenizer.CurrentPosition);
              }
          }
  
          #region Parser
  
          bool SyntaxCheckOnly() {
              return _evalMode == EvalMode.ParseOnly;
          }
  
          private object ParseExpression() {
              return ParseBooleanOr();
          }
  
          private object ParseBooleanOr() {
              ExpressionTokenizer.Position p0 = _tokenizer.CurrentPosition;
              object o = ParseBooleanAnd();
              EvalMode oldEvalMode = _evalMode;
              try {
                  while (_tokenizer.IsKeyword("or")) {
                      bool v1 = true;
  
                      if (!SyntaxCheckOnly()) {
                          v1 = (bool)SafeConvert(typeof(bool), o, "the left hand side of the 'or' operator", p0, _tokenizer.CurrentPosition);
  
                          if (v1) {
                              // we're lazy - don't evaluate anything from now, we know that the result is 'true'
                              _evalMode = EvalMode.ParseOnly;
                          }
                      }
  
                      _tokenizer.GetNextToken();
                      ExpressionTokenizer.Position p2 = _tokenizer.CurrentPosition;
                      object o2 = ParseBooleanAnd();
                      ExpressionTokenizer.Position p3 = _tokenizer.CurrentPosition;
  
                      if (!SyntaxCheckOnly()) {
                          bool v2 = (bool)SafeConvert(typeof(bool), o2, "the right hand side of the 'or' operator", p2, p3);
                          o = v1 || v2;
                      }
                  }
                  return o;
              } finally {
                  _evalMode = oldEvalMode;
              }
          }
  
          private object ParseBooleanAnd() {
              ExpressionTokenizer.Position p0 = _tokenizer.CurrentPosition;
              object o = ParseRelationalExpression();
              EvalMode oldEvalMode = _evalMode;
              try {
                  while (_tokenizer.IsKeyword("and")) {
                      bool v1 = true;
  
                      if (!SyntaxCheckOnly()) {
                          v1 = (bool)SafeConvert(typeof(bool), o, "the left hand side of the 'and' operator", p0, _tokenizer.CurrentPosition);
  
                          if (!v1) {
                              // we're lazy - don't evaluate anything from now, we know that the result is 'true'
                              _evalMode = EvalMode.ParseOnly;
                          }
                      }
  
                      _tokenizer.GetNextToken();
                      ExpressionTokenizer.Position p2 = _tokenizer.CurrentPosition;
                      object o2 = ParseRelationalExpression();
                      ExpressionTokenizer.Position p3 = _tokenizer.CurrentPosition;
                      if (!SyntaxCheckOnly()) {
                          bool v2 = (bool)SafeConvert(typeof(bool), o2, "the right hand side of the 'and' operator", p2, p3);
  
                          o = v1 && v2;
                      }
                  }
                  return o;
              } finally {
                  _evalMode = oldEvalMode;
              }
          }
  
          private object ParseRelationalExpression() {
              ExpressionTokenizer.Position p0 = _tokenizer.CurrentPosition;
              object o = ParseAddSubtract();
  
              if (_tokenizer.CurrentToken == ExpressionTokenizer.TokenType.EQ
                  || _tokenizer.CurrentToken == ExpressionTokenizer.TokenType.NE
                  || _tokenizer.CurrentToken == ExpressionTokenizer.TokenType.LT
                  || _tokenizer.CurrentToken == ExpressionTokenizer.TokenType.GT
                  || _tokenizer.CurrentToken == ExpressionTokenizer.TokenType.LE
                  || _tokenizer.CurrentToken == ExpressionTokenizer.TokenType.GE) {
                  
                  ExpressionTokenizer.TokenType op = _tokenizer.CurrentToken;
                  _tokenizer.GetNextToken();
                  
                  object o2 = ParseAddSubtract();
                  ExpressionTokenizer.Position p2 = _tokenizer.CurrentPosition;
  
                  if (SyntaxCheckOnly()) {
                      return null;
                  }
  
                  switch (op) {
                      case ExpressionTokenizer.TokenType.EQ:
                          if (o is string && o2 is string) {
                              return o.Equals(o2);
                          } else if (o is bool && o2 is bool) {
                              return o.Equals(o2);
                          } else if (o is int && o2 is int) {
                              return o.Equals(o2);
                          } else if (o is int && o2 is long) {
                              return (Convert.ToInt64(o)).Equals(o2);
                          } else if (o is int && o2 is double) {
                              return (Convert.ToDouble(o)).Equals(o2);
                          } else if (o is long && o2 is long) {
                              return o.Equals(o2);
                          } else if (o is long && o2 is int) {
                              return (o.Equals(Convert.ToInt64(o2)));
                          } else if (o is long && o2 is double) {
                              return (Convert.ToDouble(o)).Equals(o2);
                          } else if (o is double && o2 is double) {
                              return o.Equals(o2);
                          } else if (o is double && o2 is int) {
                              return o.Equals(Convert.ToDouble(o2));
                          } else if (o is double && o2 is long) {
                              return o.Equals(Convert.ToDouble(o2));
                          } else if (o is DateTime && o2 is DateTime) {
                              return o.Equals(o2);
                          } else if (o is TimeSpan && o2 is TimeSpan) {
                              return o.Equals(o2);
                          } else if (o is Version && o2 is Version) {
                              return o.Equals(o2);
                          } else if (o.GetType().IsEnum) {
                              if (o2 is string) {
                                  return o.Equals(Enum.Parse(o.GetType(), (string) o2, false));
                              } else {
                                  return o.Equals(Enum.ToObject(o.GetType(), o2));
                              }
                          } else if (o2.GetType().IsEnum) {
                              if (o is string) {
                                  return o2.Equals(Enum.Parse(o2.GetType(), (string) o, false));
                              } else {
                                  return o2.Equals(Enum.ToObject(o2.GetType(), o));
                              }
                          }
  
                          throw BuildParseError(string.Format(CultureInfo.InvariantCulture, 
                              ResourceUtils.GetString("NA1038"), 
                              GetSimpleTypeName(o.GetType()), GetSimpleTypeName(o2.GetType())), 
                              p0, p2);
                      case ExpressionTokenizer.TokenType.NE:
                          if (o is string && o2 is string) {
                              return !o.Equals(o2);
                          } else if (o is bool && o2 is bool) {
                              return !o.Equals(o2);
                          } else if (o is int && o2 is int) {
                              return !o.Equals(o2);
                          } else if (o is int && o2 is long) {
                              return !(Convert.ToInt64(o)).Equals(o2);
                          } else if (o is int && o2 is double) {
                              return !(Convert.ToDouble(o)).Equals(o2);
                          } else if (o is long && o2 is long) {
                              return !o.Equals(o2);
                          } else if (o is long && o2 is int) {
                              return !(o.Equals(Convert.ToInt64(o2)));
                          } else if (o is long && o2 is double) {
                              return !(Convert.ToDouble(o)).Equals(o2);
                          } else if (o is double && o2 is double) {
                              return !o.Equals(o2);
                          } else if (o is double && o2 is int) {
                              return !o.Equals(Convert.ToDouble(o2));
                          } else if (o is double && o2 is long) {
                              return !o.Equals(Convert.ToDouble(o2));
                          } else if (o is DateTime && o2 is DateTime) {
                              return !o.Equals(o2);
                          } else if (o is TimeSpan && o2 is TimeSpan) {
                              return !o.Equals(o2);
                          } else if (o is Version && o2 is Version) {
                              return !o.Equals(o2);
                          } else if (o.GetType().IsEnum) {
                              if (o2 is string) {
                                  return !o.Equals(Enum.Parse(o.GetType(), (string) o2, false));
                              } else {
                                  return !o.Equals(Enum.ToObject(o.GetType(), o2));
                              }
                          } else if (o2.GetType().IsEnum) {
                              if (o is string) {
                                  return !o2.Equals(Enum.Parse(o2.GetType(), (string) o, false));
                              } else {
                                  return !o2.Equals(Enum.ToObject(o2.GetType(), o));
                              }
                          }
  
                          throw BuildParseError(string.Format(CultureInfo.InvariantCulture, 
                              ResourceUtils.GetString("NA1042"), 
                              GetSimpleTypeName(o.GetType()), GetSimpleTypeName(o2.GetType())), 
                              p0, p2);
                      case ExpressionTokenizer.TokenType.LT:
                          if (o is string && o2 is string) {
                              return string.Compare((string) o, (string) o2, false, 
                                  CultureInfo.InvariantCulture) < 0;
                          } else if (o is bool && o2 is bool) {
                              return ((IComparable) o).CompareTo(o2) < 0;
                          } else if (o is int && o2 is int) {
                              return ((IComparable) o).CompareTo(o2) < 0;
                          } else if (o is int && o2 is long) {
                              return ((IComparable) Convert.ToInt64(o)).CompareTo(o2) < 0;
                          } else if (o is int && o2 is double) {
                              return ((IComparable) Convert.ToDouble(o)).CompareTo(o2) < 0;
                          } else if (o is long && o2 is long) {
                              return ((IComparable) o).CompareTo(o2) < 0;
                          } else if (o is long && o2 is int) {
                              return ((IComparable) o).CompareTo(Convert.ToInt64(o2)) < 0;
                          } else if (o is long && o2 is double) {
                              return ((IComparable) Convert.ToDouble(o)).CompareTo(o2) < 0;
                          } else if (o is double && o2 is double) {
                              return ((IComparable) o).CompareTo(o2) < 0;
                          } else if (o is double && o2 is int) {
                              return ((IComparable) o).CompareTo(Convert.ToDouble(o2)) < 0;
                          } else if (o is double && o2 is long) {
                              return ((IComparable) o).CompareTo(Convert.ToDouble(o2)) < 0;
                          } else if (o is DateTime && o2 is DateTime) {
                              return ((IComparable) o).CompareTo(o2) < 0;
                          } else if (o is TimeSpan && o2 is TimeSpan) {
                              return ((IComparable) o).CompareTo(o2) < 0;
                          } else if (o is Version && o2 is Version) {
                              return ((IComparable) o).CompareTo(o2) < 0;
                          }
  
                          throw BuildParseError(string.Format(CultureInfo.InvariantCulture, 
                              ResourceUtils.GetString("NA1051"), 
                              GetSimpleTypeName(o.GetType()), GetSimpleTypeName(o2.GetType())), 
                              p0, p2);
                      case ExpressionTokenizer.TokenType.GT:
                          if (o is string && o2 is string) {
                              return string.Compare((string) o, (string) o2, false, 
                                  CultureInfo.InvariantCulture) > 0;
                          } else if (o is bool && o2 is bool) {
                              return ((IComparable) o).CompareTo(o2) > 0;
                          } else if (o is int && o2 is int) {
                              return ((IComparable) o).CompareTo(o2) > 0;
                          } else if (o is int && o2 is long) {
                              return ((IComparable) Convert.ToInt64(o)).CompareTo(o2) > 0;
                          } else if (o is int && o2 is double) {
                              return ((IComparable) Convert.ToDouble(o)).CompareTo(o2) > 0;
                          } else if (o is long && o2 is long) {
                              return ((IComparable) o).CompareTo(o2) > 0;
                          } else if (o is long && o2 is int) {
                              return ((IComparable) o).CompareTo(Convert.ToInt64(o2)) > 0;
                          } else if (o is long && o2 is double) {
                              return ((IComparable) Convert.ToDouble(o)).CompareTo(o2) > 0;
                          } else if (o is double && o2 is double) {
                              return ((IComparable) o).CompareTo(o2) > 0;
                          } else if (o is double && o2 is int) {
                              return ((IComparable) o).CompareTo(Convert.ToDouble(o2)) > 0;
                          } else if (o is double && o2 is long) {
                              return ((IComparable) o).CompareTo(Convert.ToDouble(o2)) > 0;
                          } else if (o is DateTime && o2 is DateTime) {
                              return ((IComparable) o).CompareTo(o2) > 0;
                          } else if (o is TimeSpan && o2 is TimeSpan) {
                              return ((IComparable) o).CompareTo(o2) > 0;
                          } else if (o is Version && o2 is Version) {
                              return ((IComparable) o).CompareTo(o2) > 0;
                          }
  
                          throw BuildParseError(string.Format(CultureInfo.InvariantCulture, 
                              ResourceUtils.GetString("NA1037"), 
                              GetSimpleTypeName(o.GetType()), GetSimpleTypeName(o2.GetType())), 
                              p0, p2);
                      case ExpressionTokenizer.TokenType.LE:
                          if (o is string && o2 is string) {
                              return string.Compare((string) o, (string) o2, false, 
                                  CultureInfo.InvariantCulture) <= 0;
                          } else if (o is bool && o2 is bool) {
                              return ((IComparable) o).CompareTo(o2) <= 0;
                          } else if (o is int && o2 is int) {
                              return ((IComparable) o).CompareTo(o2) <= 0;
                          } else if (o is int && o2 is long) {
                              return ((IComparable) Convert.ToInt64(o)).CompareTo(o2) <= 0;
                          } else if (o is int && o2 is double) {
                              return ((IComparable) Convert.ToDouble(o)).CompareTo(o2) <= 0;
                          } else if (o is long && o2 is long) {
                              return ((IComparable) o).CompareTo(o2) <= 0;
                          } else if (o is long && o2 is int) {
                              return ((IComparable) o).CompareTo(Convert.ToInt64(o2)) <= 0;
                          } else if (o is long && o2 is double) {
                              return ((IComparable) Convert.ToDouble(o)).CompareTo(o2) <= 0;
                          } else if (o is double && o2 is double) {
                              return ((IComparable) o).CompareTo(o2) <= 0;
                          } else if (o is double && o2 is int) {
                              return ((IComparable) o).CompareTo(Convert.ToDouble(o2)) <= 0;
                          } else if (o is double && o2 is long) {
                              return ((IComparable) o).CompareTo(Convert.ToDouble(o2)) <= 0;
                          } else if (o is DateTime && o2 is DateTime) {
                              return ((IComparable) o).CompareTo(o2) <= 0;
                          } else if (o is TimeSpan && o2 is TimeSpan) {
                              return ((IComparable) o).CompareTo(o2) <= 0;
                          } else if (o is Version && o2 is Version) {
                              return ((IComparable) o).CompareTo(o2) <= 0;
                          }
  
                          throw BuildParseError(string.Format(CultureInfo.InvariantCulture, 
                              ResourceUtils.GetString("NA1049"), 
                              GetSimpleTypeName(o.GetType()), GetSimpleTypeName(o2.GetType())), 
                              p0, p2);
                      case ExpressionTokenizer.TokenType.GE:
                          if (o is string && o2 is string) {
                              return string.Compare((string) o, (string) o2, false, 
                                  CultureInfo.InvariantCulture) >= 0;
                          } else if (o is bool && o2 is bool) {
                              return ((IComparable) o).CompareTo(o2) >= 0;
                          } else if (o is int && o2 is int) {
                              return ((IComparable) o).CompareTo(o2) >= 0;
                          } else if (o is int && o2 is long) {
                              return ((IComparable) Convert.ToInt64(o)).CompareTo(o2) >= 0;
                          } else if (o is int && o2 is double) {
                              return ((IComparable) Convert.ToDouble(o)).CompareTo(o2) >= 0;
                          } else if (o is long && o2 is long) {
                              return ((IComparable) o).CompareTo(o2) >= 0;
                          } else if (o is long && o2 is int) {
                              return ((IComparable) o).CompareTo(Convert.ToInt64(o2)) >= 0;
                          } else if (o is long && o2 is double) {
                              return ((IComparable) Convert.ToDouble(o)).CompareTo(o2) >= 0;
                          } else if (o is double && o2 is double) {
                              return ((IComparable) o).CompareTo(o2) >= 0;
                          } else if (o is double && o2 is int) {
                              return ((IComparable) o).CompareTo(Convert.ToDouble(o2)) >= 0;
                          } else if (o is double && o2 is long) {
                              return ((IComparable) o).CompareTo(Convert.ToDouble(o2)) >= 0;
                          } else if (o is DateTime && o2 is DateTime) {
                              return ((IComparable) o).CompareTo(o2) >= 0;
                          } else if (o is TimeSpan && o2 is TimeSpan) {
                              return ((IComparable) o).CompareTo(o2) >= 0;
                          } else if (o is Version && o2 is Version) {
                              return ((IComparable) o).CompareTo(o2) >= 0;
                          }
  
                          throw BuildParseError(string.Format(CultureInfo.InvariantCulture, 
                              ResourceUtils.GetString("NA1050"), 
                              GetSimpleTypeName(o.GetType()), GetSimpleTypeName(o2.GetType())), 
                              p0, p2);
                  }
              }
              return o;
          }
  
          private object ParseAddSubtract() {
              ExpressionTokenizer.Position p0 = _tokenizer.CurrentPosition;
              object o = ParseMulDiv();
  
              while (true) {
                  if (_tokenizer.CurrentToken == ExpressionTokenizer.TokenType.Plus) {
                      _tokenizer.GetNextToken();
                      object o2 = ParseMulDiv();
                      ExpressionTokenizer.Position p3 = _tokenizer.CurrentPosition;
  
                      if (!SyntaxCheckOnly()) {
                          if (o is string && o2 is string) {
                              o = (string) o + (string) o2;
                          } else if (o is int && o2 is int) {
                              o = (int) o + (int) o2;
                          } else if (o is int && o2 is long) {
                              o = (int) o + (long) o2;
                          } else if (o is int && o2 is double) {
                              o = (int) o + (double) o2;
                          } else if (o is long && o2 is long) {
                              o = (long) o + (long) o2;
                          } else if (o is long && o2 is int) {
                              o = (long) o + (int) o2;
                          } else if (o is long && o2 is double) {
                              o = (long) o + (double) o2;
                          } else if (o is double && o2 is double) {
                              o = (double) o + (double) o2;
                          } else if (o is double && o2 is int) {
                              o = (double) o + (int) o2;
                          } else if (o is double && o2 is long) {
                              o = (double) o + (long) o2;
                          } else if (o is DateTime && o2 is TimeSpan) {
                              o = (DateTime) o + (TimeSpan) o2;
                          } else if (o is TimeSpan && o2 is TimeSpan) {
                              o = (TimeSpan) o + (TimeSpan) o2;
                          } else {
                              throw BuildParseError(string.Format(CultureInfo.InvariantCulture, 
                                  ResourceUtils.GetString("NA1041"), 
                                  GetSimpleTypeName(o.GetType()), GetSimpleTypeName(o2.GetType())), 
                                  p0, p3);
                          }
                      }
                  } else if (_tokenizer.CurrentToken == ExpressionTokenizer.TokenType.Minus) {
                      _tokenizer.GetNextToken();
  
                      object o2 = ParseMulDiv();
                      ExpressionTokenizer.Position p3 = _tokenizer.CurrentPosition;
  
                      if (!SyntaxCheckOnly()) {
                          if (o is int && o2 is int) {
                              o = (int) o - (int) o2;
                          } else if (o is int && o2 is long) {
                              o = (int) o - (long) o2;
                          } else if (o is int && o2 is double) {
                              o = (int) o - (double) o2;
                          } else if (o is long && o2 is long) {
                              o = (long) o - (long) o2;
                          } else if (o is long && o2 is int) {
                              o = (long) o - (int) o2;
                          } else if (o is long && o2 is double) {
                              o = (long) o - (double) o2;
                          } else if (o is double && o2 is double) {
                              o = (double) o - (double) o2;
                          } else if (o is double && o2 is int) {
                              o = (double) o - (int) o2;
                          } else if (o is double && o2 is long) {
                              o = (double) o - (long) o2;
                          } else if (o is DateTime && o2 is DateTime) {
                              o = (DateTime) o - (DateTime) o2;
                          } else if (o is DateTime && o2 is TimeSpan) {
                              o = (DateTime) o - (TimeSpan) o2;
                          } else if (o is TimeSpan && o2 is TimeSpan) {
                              o = (TimeSpan) o - (TimeSpan) o2;
                          } else {
                              throw BuildParseError(string.Format(CultureInfo.InvariantCulture, 
                                  ResourceUtils.GetString("NA1048"), 
                                  GetSimpleTypeName(o.GetType()), GetSimpleTypeName(o2.GetType())), 
                                  p0, p3);
                          }
                      }
                  } else {
                      break;
                  }
              }
              return o;
          }
  
          private object ParseMulDiv() {
              ExpressionTokenizer.Position p0 = _tokenizer.CurrentPosition;
              object o = ParseValue();
  
              while (true) {
                  if (_tokenizer.CurrentToken == ExpressionTokenizer.TokenType.Mul) {
                      _tokenizer.GetNextToken();
                      
                      object o2 = ParseValue();
                      ExpressionTokenizer.Position p3 = _tokenizer.CurrentPosition;
  
                      if (!SyntaxCheckOnly()) {
                          if (o is int && o2 is int) {
                              o = (int) o * (int) o2;
                          } else if (o is int && o2 is long) {
                              o = (int) o * (long) o2;
                          } else if (o is int && o2 is double) {
                              o = (int) o * (double) o2;
                          } else if (o is long && o2 is long) {
                              o = (long) o * (long) o2;
                          } else if (o is long && o2 is int) {
                              o = (long) o * (int) o2;
                          } else if (o is long && o2 is double) {
                              o = (long) o * (double) o2;
                          } else if (o is double && o2 is double) {
                              o = (double) o * (double) o2;
                          } else if (o is double && o2 is int) {
                              o = (double) o * (int) o2;
                          } else if (o is double && o2 is long) {
                              o = (double) o * (long) o2;
                          } else {
                              throw BuildParseError(string.Format(CultureInfo.InvariantCulture, 
                                  ResourceUtils.GetString("NA1036"), 
                                  GetSimpleTypeName(o.GetType()), GetSimpleTypeName(o2.GetType())), p0, p3);
                          }
                      }
                  } else if (_tokenizer.CurrentToken == ExpressionTokenizer.TokenType.Div) {
                      _tokenizer.GetNextToken();
                      
                      ExpressionTokenizer.Position p2 = _tokenizer.CurrentPosition;
                      object o2 = ParseValue();
                      ExpressionTokenizer.Position p3 = _tokenizer.CurrentPosition;
  
                      if (!SyntaxCheckOnly()) {
                          if (o is int && o2 is int) {
                              if ((int) o2 == 0) {
                                  throw BuildParseError(string.Format(CultureInfo.InvariantCulture, 
                                      ResourceUtils.GetString("NA1043")), p2, p3);
                              }
  
                              o = (int) o / (int) o2;
                          } else if (o is int && o2 is long) {
                              if ((long) o2 == 0) {
                                  throw BuildParseError(string.Format(CultureInfo.InvariantCulture, 
                                      ResourceUtils.GetString("NA1043")), p2, p3);
                              }
  
                              o = (int) o / (long) o2;
                          } else if (o is int && o2 is double) {
                              if ((double) o2 == 0) {
                                  throw BuildParseError(string.Format(CultureInfo.InvariantCulture, 
                                      ResourceUtils.GetString("NA1043")), p2, p3);
                              }
  
                              o = (int) o / (double) o2;
                          } else if (o is long && o2 is long) {
                              if ((long) o2 == 0) {
                                  throw BuildParseError(string.Format(CultureInfo.InvariantCulture, 
                                      ResourceUtils.GetString("NA1043")), p2, p3);
                              }
  
                              o = (long) o / (long) o2;
                          } else if (o is long && o2 is int) {
                              if ((int) o2 == 0) {
                                  throw BuildParseError(string.Format(CultureInfo.InvariantCulture, 
                                      ResourceUtils.GetString("NA1043")), p2, p3);
                              }
  
                              o = (long) o / (int) o2;
                          } else if (o is long && o2 is double) {
                              if ((double) o2 == 0) {
                                  throw BuildParseError(string.Format(CultureInfo.InvariantCulture, 
                                      ResourceUtils.GetString("NA1043")), p2, p3);
                              }
  
                              o = (long) o / (double) o2;
                          } else if (o is double && o2 is double) {
                              if ((double) o2 == 0) {
                                  throw BuildParseError(string.Format(CultureInfo.InvariantCulture, 
                                      ResourceUtils.GetString("NA1043")), p2, p3);
                              }
  
                              o = (double) o / (double) o2;
                          } else if (o is double && o2 is int) {
                              if ((int) o2 == 0) {
                                  throw BuildParseError(string.Format(CultureInfo.InvariantCulture, 
                                      ResourceUtils.GetString("NA1043")), p2, p3);
                              }
  
                              o = (double) o / (int) o2;
                          } else if (o is double && o2 is long) {
                              if ((long) o2 == 0) {
                                  throw BuildParseError(string.Format(CultureInfo.InvariantCulture, 
                                      ResourceUtils.GetString("NA1043")), p2, p3);
                              }
  
                              o = (double) o / (long) o2;
                          } else {
                              throw BuildParseError(string.Format(CultureInfo.InvariantCulture, 
                                  ResourceUtils.GetString("NA1039"), 
                                  GetSimpleTypeName(o.GetType()), GetSimpleTypeName(o2.GetType())), p0, p3);
                          }
                      }
                  } else if (_tokenizer.CurrentToken == ExpressionTokenizer.TokenType.Mod) {
                      _tokenizer.GetNextToken();
  
                      ExpressionTokenizer.Position p2 = _tokenizer.CurrentPosition;
                      object o2 = ParseValue();
                      ExpressionTokenizer.Position p3 = _tokenizer.CurrentPosition;
  
                      if (!SyntaxCheckOnly()) {
                          if (o is int && o2 is int) {
                              if ((int) o2 == 0) {
                                  throw BuildParseError(string.Format(CultureInfo.InvariantCulture, 
                                      ResourceUtils.GetString("NA1043")), p2, p3);
                              }
  
                              o = (int) o % (int) o2;
                          } else if (o is int && o2 is long) {
                              if ((long) o2 == 0) {
                                  throw BuildParseError(string.Format(CultureInfo.InvariantCulture, 
                                      ResourceUtils.GetString("NA1043")), p2, p3);
                              }
  
                              o = (int) o % (long) o2;
                          } else if (o is int && o2 is double) {
                              if ((double) o2 == 0) {
                                  throw BuildParseError(string.Format(CultureInfo.InvariantCulture, 
                                      ResourceUtils.GetString("NA1043")), p2, p3);
                              }
  
                              o = (int) o % (double) o2;
                          } else if (o is long && o2 is long) {
                              if ((long) o2 == 0) {
                                  throw BuildParseError(string.Format(CultureInfo.InvariantCulture, 
                                      ResourceUtils.GetString("NA1043")), p2, p3);
                              }
  
                              o = (long) o % (long) o2;
                          } else if (o is long && o2 is int) {
                              if ((int) o2 == 0) {
                                  throw BuildParseError(string.Format(CultureInfo.InvariantCulture, 
                                      ResourceUtils.GetString("NA1043")), p2, p3);
                              }
  
                              o = (long) o % (int) o2;
                          } else if (o is long && o2 is double) {
                              if ((double) o2 == 0) {
                                  throw BuildParseError(string.Format(CultureInfo.InvariantCulture, 
                                      ResourceUtils.GetString("NA1043")), p2, p3);
                              }
  
                              o = (long) o % (double) o2;
                          } else if (o is double && o2 is double) {
                              if ((double) o2 == 0) {
                                  throw BuildParseError(string.Format(CultureInfo.InvariantCulture, 
                                      ResourceUtils.GetString("NA1043")), p2, p3);
                              }
  
                              o = (double) o % (double) o2;
                          } else if (o is double && o2 is int) {
                              if ((int) o2 == 0) {
                                  throw BuildParseError(string.Format(CultureInfo.InvariantCulture, 
                                      ResourceUtils.GetString("NA1043")), p2, p3);
                              }
  
                              o = (double) o % (int) o2;
                          } else if (o is double && o2 is long) {
                              if ((long) o2 == 0) {
                                  throw BuildParseError(string.Format(CultureInfo.InvariantCulture, 
                                      ResourceUtils.GetString("NA1043")), p2, p3);
                              }
  
                              o = (double) o % (long) o2;
                          } else {
                              throw BuildParseError(string.Format(CultureInfo.InvariantCulture, 
                                  ResourceUtils.GetString("NA1047"), 
                                  GetSimpleTypeName(o.GetType()), GetSimpleTypeName(o2.GetType())), p0, p3);
                          }
                      }
                  } else {
                      break;
                  }
              }
              return o;
          }
  
          private object ParseConditional() {
              // we're on "if" token - skip it 
              _tokenizer.GetNextToken();
              if (_tokenizer.CurrentToken != ExpressionTokenizer.TokenType.LeftParen) {
                  throw BuildParseError("'(' expected.", _tokenizer.CurrentPosition);
              }
              _tokenizer.GetNextToken();
  
              
              ExpressionTokenizer.Position p0 = _tokenizer.CurrentPosition;
              object val = ParseExpression();
              ExpressionTokenizer.Position p1 = _tokenizer.CurrentPosition;
              
              bool cond = false;
              if (!SyntaxCheckOnly()) {
                  cond = (bool) SafeConvert(typeof(bool), val, "the conditional expression", p0, p1);
              }
  
              // skip comma between condition value and then
              if (_tokenizer.CurrentToken != ExpressionTokenizer.TokenType.Comma) {
                  throw BuildParseError("',' expected.", _tokenizer.CurrentPosition);
              }
              _tokenizer.GetNextToken();
  
              EvalMode oldEvalMode = _evalMode;
  
              try {
                  if (!cond) {
                      // evaluate 'then' clause without executing functions
                      _evalMode = EvalMode.ParseOnly;
                  } else {
                      _evalMode = oldEvalMode;
                  }
                  object thenValue = ParseExpression();
                  _evalMode = oldEvalMode;
  
                  if (_tokenizer.CurrentToken != ExpressionTokenizer.TokenType.Comma) {
                      throw BuildParseError("',' expected.", _tokenizer.CurrentPosition);
                  }
                  _tokenizer.GetNextToken(); // skip comma
  
                  if (cond) {
                      // evaluate 'else' clause without executing functions
                      _evalMode = EvalMode.ParseOnly;
                  } else {
                      _evalMode = oldEvalMode;
                  }
                  object elseValue = ParseExpression();
  
                  _evalMode = oldEvalMode;
  
                  // skip closing ')'
                  if (_tokenizer.CurrentToken != ExpressionTokenizer.TokenType.RightParen) {
                      throw BuildParseError("')' expected.", _tokenizer.CurrentPosition);
                  }
                  _tokenizer.GetNextToken();
  
                  return cond ? thenValue : elseValue;
              }
              finally {
                  // restore evaluation mode - even on exceptions
                  _evalMode = oldEvalMode;
              }
          }
  
          private object ParseValue() {
              if (_tokenizer.CurrentToken == ExpressionTokenizer.TokenType.String) {
                  object v = _tokenizer.TokenText;
                  _tokenizer.GetNextToken();
                  return v;
              }
  
              if (_tokenizer.CurrentToken == ExpressionTokenizer.TokenType.Number) {
                  string number = _tokenizer.TokenText;
  
                  ExpressionTokenizer.Position p0 = _tokenizer.CurrentPosition;
                  _tokenizer.GetNextToken();
                  ExpressionTokenizer.Position p1 = new ExpressionTokenizer.Position(
                      _tokenizer.CurrentPosition.CharIndex - 1);
  
                  if (_tokenizer.CurrentToken == ExpressionTokenizer.TokenType.Dot) {
                      number += ".";
                      _tokenizer.GetNextToken();
                      if (_tokenizer.CurrentToken != ExpressionTokenizer.TokenType.Number) {
                          throw BuildParseError("Fractional part expected.", _tokenizer.CurrentPosition);
                      }
                      number += _tokenizer.TokenText;
  
                      _tokenizer.GetNextToken();
  
                      p1 = _tokenizer.CurrentPosition;
  
                      try {
                          return Double.Parse(number, CultureInfo.InvariantCulture);
                      } catch (OverflowException) {
                          throw BuildParseError("Value was either too large or too"
                              + " small for type 'double'.", p0, p1);
                      }
                  } else {
                      try {
                          return Int32.Parse(number, CultureInfo.InvariantCulture);
                      } catch (OverflowException) {
                          try {
                              return long.Parse(number, CultureInfo.InvariantCulture);
                          } catch (OverflowException) {
                              throw BuildParseError("Value was either too large or too"
                                  + " small for type 'long'.", p0, p1);
                          }
                      }
                  }
              }
  
              if (_tokenizer.CurrentToken == ExpressionTokenizer.TokenType.Minus) {
                  _tokenizer.GetNextToken();
  
                  // unary minus
                  ExpressionTokenizer.Position p0 = _tokenizer.CurrentPosition;
                  object v = ParseValue();
                  ExpressionTokenizer.Position p1 = _tokenizer.CurrentPosition;
                  if (!SyntaxCheckOnly()) {
                      if (v is int) {
                          return -((int) v);
                      }
                      if (v is long) {
                          return -((long) v);
                      }
                      if (v is double) {
                          return -((double) v);
                      }
                      throw BuildParseError(string.Format(CultureInfo.InvariantCulture, 
                          ResourceUtils.GetString("NA1040"), 
                          GetSimpleTypeName(v.GetType())), p0, p1);
                  }
                  return null;
              }
  
              if (_tokenizer.IsKeyword("not")) {
                  _tokenizer.GetNextToken();
  
                  // unary boolean not
                  ExpressionTokenizer.Position p0 = _tokenizer.CurrentPosition;
                  object v = ParseValue();
                  ExpressionTokenizer.Position p1 = _tokenizer.CurrentPosition;
                  if (!SyntaxCheckOnly()) {
                      bool value = (bool)SafeConvert(typeof(bool), v, "the argument of 'not' operator", p0, p1);
                      return !value;
                  }
                  return null;
              }
  
              if (_tokenizer.CurrentToken == ExpressionTokenizer.TokenType.LeftParen) {
                  _tokenizer.GetNextToken();
                  object v = ParseExpression();
                  if (_tokenizer.CurrentToken != ExpressionTokenizer.TokenType.RightParen) {
                      throw BuildParseError("')' expected.", _tokenizer.CurrentPosition);
                  }
                  _tokenizer.GetNextToken();
                  return v;
              }
  
              if (_tokenizer.CurrentToken == ExpressionTokenizer.TokenType.Keyword) {
                  ExpressionTokenizer.Position p0 = _tokenizer.CurrentPosition;
  
                  string functionOrPropertyName = _tokenizer.TokenText;
                  if (functionOrPropertyName == "if") {
                      return ParseConditional();
                  }
  
                  if (functionOrPropertyName == "true") {
                      _tokenizer.GetNextToken();
                      return true;
                  }
  
                  if (functionOrPropertyName == "false") {
                      _tokenizer.GetNextToken();
                      return false;
                  }
  
                  // don't ignore whitespace - properties shouldn't be written with spaces in them
  
                  _tokenizer.IgnoreWhitespace = false; 
                  _tokenizer.GetNextToken();
  
                  ArrayList args = new ArrayList();
                  bool isFunction = false;
  
                  // gather function or property name
                  if (_tokenizer.CurrentToken == ExpressionTokenizer.TokenType.DoubleColon) {
                      isFunction = true;
                      functionOrPropertyName += "::";
                      _tokenizer.GetNextToken();
                      if (_tokenizer.CurrentToken != ExpressionTokenizer.TokenType.Keyword) {
                          throw BuildParseError("Function name expected.", p0, _tokenizer.CurrentPosition);
                      }
  
gt:                     functionOrPropertyName += _tokenizer.TokenText;