visitBinaryExpr method
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()}';
}
}