visitBinaryExpr method

  1. @override
Object? visitBinaryExpr(
  1. BinaryExpr expr
)
override

Implementation

@override
Object? visitBinaryExpr(BinaryExpr expr) {
  final String lineInfo = this.lineInfo(expr.op);
  final op = expr.op.type;

  int asInt(Object? obj) {
    if (obj == null) throw '$lineInfo Operand was null for binary $op.';
    if (obj is LuaObject) {
      final value = obj.valueAs<num>();
      if (value == null) {
        throw '$lineInfo Failed to convert lua type "${obj.typeinfo}" to "int".';
      }
      return value.toInt();
    } else if (obj is num) {
      return obj.toInt();
    }

    throw '$lineInfo Unexpected type while casting to "int". Found "${obj.runtimeType}".';
  }

  num asNum(Object? obj) {
    if (obj == null) throw '$lineInfo Operand was null for binary $op.';
    if (obj is LuaObject) {
      final value = switch (obj.value) {
        final num n => n,
        _ => null,
      };
      if (value == null) {
        throw '$lineInfo Failed to convert lua type "${obj.typeinfo}" to "int".';
      }
      return value;
    } else if (obj is num) {
      return obj;
    }

    throw '$lineInfo Unexpected type while casting to "num". Found "${obj.runtimeType}".';
  }

  /*
  bool asBool(Object? obj) {
    if (obj == null) throw '$lineInfo Operand was null for binary $op.';
    if (obj is LuaObject) {
      final value = obj.valueAs<bool>();
      if (value == null) {
        throw '$lineInfo Failed to convert lua type "${obj.typeinfo}" to "bool".';
      }
      return value;
    } else if (obj is bool) {
      return obj;
    }

    throw '$lineInfo Unexpected type while casting to "bool". Found "${obj.runtimeType}".';
  }*/

  String str(Object? obj) {
    if (obj == null) return 'nil';
    return obj.toString();
  }

  String strConcat(Object? lhs, Object? rhs) {
    check(Object? obj) {
      if (obj is LuaObject) {
        if (!(obj.valueAsInt() is int || obj.value is String)) {
          throw 'Attempt to concat ${obj.value.runtimeType} value.';
        }
      } else if (!(obj is num || obj is String)) {
        throw 'Attempt to concat ${obj.runtimeType} value.';
      }
    }

    check(lhs);
    final strL = str(lhs);
    check(rhs);
    final strR = str(rhs);

    return strL + strR;
  }

  try {
    final lhs = expr.lhs.accept(this);
    final rhs = expr.rhs.accept(this);

    switch (op) {
      case TokenType.kConcat:
        return strConcat(lhs, rhs);
      case TokenType.kMod:
        return asInt(lhs) % asInt(rhs);
      case TokenType.kAnd:
        if (lhs.isTruthy) return rhs;
        return lhs;
      case TokenType.kOr:
        if (lhs.isTruthy) return lhs;
        return rhs;
      case TokenType.kBitAnd:
        return asInt(lhs) & asInt(rhs);
      case TokenType.kBitOr:
        return asInt(lhs) | asInt(rhs);
      case TokenType.kCarrot:
        return math.pow(asNum(lhs), asNum(rhs));
      case TokenType.kDiv:
        return asNum(lhs) /
            switch (asNum(rhs)) {
              == 0.0 => throw '$lineInfo Divide by zero.',
              final num n => n,
            };
      case TokenType.kDivFloor:
        return asNum(lhs) /
            switch (asNum(rhs)) {
              == 0.0 => throw '$lineInfo Divide by zero.',
              final num n => asNum(n.floor()),
            };
      case TokenType.kSub:
        return asNum(lhs) - asNum(rhs);
      case TokenType.kAdd:
        return asNum(lhs) + asNum(rhs);
      case TokenType.kMult:
        return asNum(lhs) * asNum(rhs);
      case TokenType.kLTE:
        return asNum(lhs) <= asNum(rhs);
      case TokenType.kLT:
        return asNum(lhs) < asNum(rhs);
      case TokenType.kGT:
        return asNum(lhs) > asNum(rhs);
      case TokenType.kGTE:
        return asNum(lhs) >= asNum(rhs);
      case TokenType.kEQ:
        Object? lval = lhs;

        if (lhs is LuaObject) {
          lval = lhs.deref().value;
        }

        Object? rval = rhs;

        if (rhs is LuaObject) {
          rval = rhs.deref().value;
        }

        return lval == rval;
      case TokenType.kNEQ:
        Object? lval = lhs;

        if (lhs is LuaObject) {
          lval = lhs.deref().value;
        }

        Object? rval = rhs;

        if (rhs is LuaObject) {
          rval = rhs.deref().value;
        }
        return lval != rval;
      default:
        throw '$lineInfo Unsupported binary operation $op.';
    }
  } catch (e) {
    throw '$lineInfo ${e.toString()}';
  }
}