public IParseItem Visit(BinOpItem target) { if (target == null) { throw new ArgumentNullException(nameof(target)); } target.Lhs.Accept(this); target.Rhs.Accept(this); return(target); }
/// <summary> /// Called when the item is a binary expression item. /// </summary> /// <param name="target">The object that was passed to IParseItem.Accept.</param> /// <returns>The passed target or a modification of it.</returns> /// <exception cref="System.ArgumentNullException">If target is null.</exception> public IParseItem Visit(BinOpItem target) { if (target == null) { throw new ArgumentNullException(nameof(target)); } ILGenerator gen = compiler.CurrentGenerator; if (target.OperationType == BinaryOperationType.And || target.OperationType == BinaryOperationType.Or) { // object temp = {Lhs}; var end = gen.DefineLabel(); var temp = compiler.CreateTemporary(typeof(ILuaValue)); target.Lhs.Accept(this); gen.Emit(OpCodes.Stloc, temp); // Push Lhs onto the stack, if going to end, this will be the result. gen.Emit(OpCodes.Ldloc, temp); // if (temp.IsTrue) goto end; gen.Emit(OpCodes.Ldloc, temp); gen.Emit(OpCodes.Callvirt, typeof(ILuaValue).GetProperty(nameof(ILuaValue.IsTrue)).GetGetMethod()); if (target.OperationType == BinaryOperationType.And) { // We want to break if the value is truthy and it's an OR, // or it's falsy and it's an AND. // Boolean negation. gen.Emit(OpCodes.Ldc_I4_1); gen.Emit(OpCodes.Xor); } gen.Emit(OpCodes.Brtrue, end); // Replace Lhs on stack with Rhs. gen.Emit(OpCodes.Pop); target.Rhs.Accept(this); // :end gen.MarkLabel(end); } else { //! push {Lhs}.Arithmetic({OperationType}, {Rhs}) target.Lhs.Accept(this); gen.Emit(OpCodes.Ldc_I4, (int)target.OperationType); target.Rhs.Accept(this); gen.Emit(OpCodes.Callvirt, typeof(ILuaValue).GetMethod(nameof(ILuaValue.Arithmetic))); } return(target); }
/// <summary> /// Reads an expression from the input. /// </summary> /// <param name="input">Where to read input from.</param> /// <param name="precedence">The precedence of the previous expression or -1 if a root.</param> /// <returns>The expression that was read.</returns> protected virtual IParseExp _readExp(Lexer input, out bool isParentheses, int precedence = -1) { Token debug = input.Peek(); IParseExp ret; var unOpType = _getUnaryOperationType(input.Peek().Type); isParentheses = false; if (unOpType != UnaryOperationType.Unknown) { input.Read(); int unaryPrec = 11; if (unaryPrec > precedence && precedence >= 0) { unaryPrec = precedence; } ret = new UnOpItem(_readExp(input, out _, unaryPrec), unOpType) { Debug = debug }; } else if (input.ReadIfType(TokenType.Nil)) { ret = new LiteralItem(null) { Debug = debug }; } else if (input.ReadIfType(TokenType.False)) { ret = new LiteralItem(false) { Debug = debug }; } else if (input.ReadIfType(TokenType.True)) { ret = new LiteralItem(true) { Debug = debug }; } else if (input.ReadIfType(TokenType.NumberLiteral)) { ret = new LiteralItem(Helpers.ParseNumber(debug.Value)) { Debug = debug }; } else if (input.ReadIfType(TokenType.StringLiteral)) { ret = new LiteralItem(debug.Value) { Debug = debug }; } else if (input.ReadIfType(TokenType.Elipsis)) { ret = new NameItem("...") { Debug = debug }; } else if (input.PeekType(TokenType.BeginTable)) { ret = _readTable(input); } else if (input.PeekType(TokenType.Function)) { ret = _readFunctionHelper(input, false, false); } else { ret = _readPrefixExp(input, out isParentheses); } while (true) { BinaryOperationType binOpType = _getBinaryOperationType(input.Peek().Type); int newPrecedence = _getPrecedence(binOpType); if (binOpType == BinaryOperationType.Unknown || (newPrecedence < precedence && precedence >= 0)) { break; } input.Read(); // For left-associative operations, use a lower precedence so the nested call doesn't read // more than it should. a+b+c should be (a+b)+c, so we need the first add to be its own // item and then have that should be the lhs of another add. Note this only works if // operations of the same precedence have the same associativity. int extra = _isRightAssociative(binOpType) ? 0 : 1; IParseExp other = _readExp(input, out _, newPrecedence + extra); ret = new BinOpItem(ret, binOpType, other) { Debug = debug }; isParentheses = false; } return(ret); }