- added string handling to comparison operators.

This commit is contained in:
Christoph Oelckers 2016-10-29 18:57:56 +02:00
parent 853c6f6684
commit 78a18acf46
3 changed files with 199 additions and 81 deletions

View file

@ -676,7 +676,7 @@ FxExpression *FxBoolCast::Resolve(FCompileContext &ctx)
delete this;
return x;
}
else if ((basex->ValueType->GetRegType() == REGT_INT || basex->ValueType->GetRegType() == REGT_FLOAT || basex->ValueType->GetRegType() == REGT_POINTER) && !basex->IsVector())
else if (basex->IsBoolCompat())
{
if (basex->isConstant())
{
@ -797,7 +797,7 @@ FxExpression *FxIntCast::Resolve(FCompileContext &ctx)
return x;
}
}
else if (basex->ValueType->GetRegType() == REGT_FLOAT && !basex->IsVector())
else if (basex->IsFloat())
{
if (basex->isConstant())
{
@ -875,7 +875,7 @@ FxExpression *FxFloatCast::Resolve(FCompileContext &ctx)
CHECKRESOLVED();
SAFE_RESOLVE(basex, ctx);
if (basex->ValueType->GetRegType() == REGT_FLOAT && !basex->IsVector())
if (basex->IsFloat())
{
FxExpression *x = basex;
basex = NULL;
@ -1325,7 +1325,7 @@ FxExpression *FxTypeCast::Resolve(FCompileContext &ctx)
{
goto basereturn;
}
else if (ValueType->GetRegType() == REGT_FLOAT && !IsVector())
else if (IsFloat())
{
FxExpression *x = new FxFloatCast(basex);
x = x->Resolve(ctx);
@ -1635,7 +1635,7 @@ FxExpression *FxUnaryNotBitwise::Resolve(FCompileContext& ctx)
CHECKRESOLVED();
SAFE_RESOLVE(Operand, ctx);
if (ctx.FromDecorate && Operand->IsNumeric() && Operand->ValueType->GetRegType() == REGT_FLOAT /* lax */)
if (ctx.FromDecorate && Operand->IsFloat() /* lax */)
{
// DECORATE allows floats here so cast them to int.
Operand = new FxIntCast(Operand, true);
@ -1648,7 +1648,7 @@ FxExpression *FxUnaryNotBitwise::Resolve(FCompileContext& ctx)
}
// Names were not blocked in DECORATE here after the scripting branch merge. Now they are again.
if (Operand->ValueType->GetRegType() != REGT_INT || Operand->ValueType == TypeName)
if (!Operand->IsInteger())
{
ScriptPosition.Message(MSG_ERROR, "Integer type expected");
delete this;
@ -2281,7 +2281,51 @@ bool FxBinary::ResolveLR(FCompileContext& ctx, bool castnumeric)
return false;
}
if (left->IsVector() || right->IsVector())
if (left->ValueType == TypeString || right->ValueType == TypeString)
{
switch (Operator)
{
case '+':
// later
break;
case '<':
case '>':
case TK_Geq:
case TK_Leq:
case TK_Eq:
case TK_Neq:
case TK_ApproxEq:
if (left->ValueType != TypeString)
{
left = new FxStringCast(left);
left = left->Resolve(ctx);
if (left == nullptr)
{
delete this;
return nullptr;
}
}
if (right->ValueType != TypeString)
{
right = new FxStringCast(right);
right = right->Resolve(ctx);
if (right == nullptr)
{
delete this;
return nullptr;
}
}
ValueType = TypeBool;
break;
default:
ScriptPosition.Message(MSG_ERROR, "Incompatible operands for comparison");
delete this;
return false;
}
}
else if (left->IsVector() || right->IsVector())
{
switch (Operator)
{
@ -2466,7 +2510,7 @@ FxExpression *FxAddSub::Resolve(FCompileContext& ctx)
}
else if (left->isConstant() && right->isConstant())
{
if (ValueType->GetRegType() == REGT_FLOAT)
if (IsFloat())
{
double v;
double v1 = static_cast<FxConstant *>(left)->GetValue().GetFloat();
@ -2605,7 +2649,7 @@ FxExpression *FxMulDiv::Resolve(FCompileContext& ctx)
}
else if (left->isConstant() && right->isConstant())
{
if (ValueType->GetRegType() == REGT_FLOAT)
if (IsFloat())
{
double v;
double v1 = static_cast<FxConstant *>(left)->GetValue().GetFloat();
@ -2834,7 +2878,17 @@ FxExpression *FxCompareRel::Resolve(FCompileContext& ctx)
{
int v;
if (ValueType->GetRegType() == REGT_FLOAT)
if (ValueType == TypeString)
{
FString v1 = static_cast<FxConstant *>(left)->GetValue().GetString();
FString v2 = static_cast<FxConstant *>(right)->GetValue().GetString();
int res = v1.Compare(v2);
v = Operator == '<' ? res < 0 :
Operator == '>' ? res > 0 :
Operator == TK_Geq ? res >= 0 :
Operator == TK_Leq ? res <= 0 : 0;
}
else if (IsFloat())
{
double v1 = static_cast<FxConstant *>(left)->GetValue().GetFloat();
double v2 = static_cast<FxConstant *>(right)->GetValue().GetFloat();
@ -2873,47 +2927,81 @@ ExpEmit FxCompareRel::Emit(VMFunctionBuilder *build)
ExpEmit op1 = left->Emit(build);
ExpEmit op2 = right->Emit(build);
assert(op1.RegType == op2.RegType);
assert(op1.RegType == REGT_INT || op1.RegType == REGT_FLOAT);
assert(!op1.Konst || !op2.Konst);
assert(Operator == '<' || Operator == '>' || Operator == TK_Geq || Operator == TK_Leq);
static const VM_UBYTE InstrMap[][4] =
{
{ OP_LT_RR, OP_LTF_RR, 0 }, // <
{ OP_LE_RR, OP_LEF_RR, 1 }, // >
{ OP_LT_RR, OP_LTF_RR, 1 }, // >=
{ OP_LE_RR, OP_LEF_RR, 0 } // <=
};
int instr, check, index;
ExpEmit to(build, REGT_INT);
index = Operator == '<' ? 0 :
if (op1.RegType == REGT_STRING)
{
ExpEmit to(build, REGT_INT);
int a = Operator == '<' ? CMP_LT :
Operator == '>' ? CMP_LE | CMP_CHECK :
Operator == TK_Geq ? CMP_LT | CMP_CHECK : CMP_LE;
if (op1.Konst)
{
a |= CMP_BK;
}
else
{
op1.Free(build);
}
if (op2.Konst)
{
a |= CMP_CK;
}
else
{
op2.Free(build);
}
build->Emit(OP_LI, to.RegNum, 0, 0);
build->Emit(OP_CMPS, a, op1.RegNum, op2.RegNum);
build->Emit(OP_JMP, 1);
build->Emit(OP_LI, to.RegNum, 1);
return to;
}
else
{
assert(op1.RegType == REGT_INT || op1.RegType == REGT_FLOAT);
assert(Operator == '<' || Operator == '>' || Operator == TK_Geq || Operator == TK_Leq);
static const VM_UBYTE InstrMap[][4] =
{
{ OP_LT_RR, OP_LTF_RR, 0 }, // <
{ OP_LE_RR, OP_LEF_RR, 1 }, // >
{ OP_LT_RR, OP_LTF_RR, 1 }, // >=
{ OP_LE_RR, OP_LEF_RR, 0 } // <=
};
int instr, check;
ExpEmit to(build, REGT_INT);
int index = Operator == '<' ? 0 :
Operator == '>' ? 1 :
Operator == TK_Geq ? 2 : 3;
instr = InstrMap[index][op1.RegType == REGT_INT ? 0 : 1];
check = InstrMap[index][2];
if (op2.Konst)
{
instr += 1;
}
else
{
op2.Free(build);
}
if (op1.Konst)
{
instr += 2;
}
else
{
op1.Free(build);
}
// See FxBoolCast for comments, since it's the same thing.
build->Emit(OP_LI, to.RegNum, 0, 0);
build->Emit(instr, check, op1.RegNum, op2.RegNum);
build->Emit(OP_JMP, 1);
build->Emit(OP_LI, to.RegNum, 1);
return to;
instr = InstrMap[index][op1.RegType == REGT_INT ? 0 : 1];
check = InstrMap[index][2];
if (op2.Konst)
{
instr += 1;
}
else
{
op2.Free(build);
}
if (op1.Konst)
{
instr += 2;
}
else
{
op1.Free(build);
}
// See FxBoolCast for comments, since it's the same thing.
build->Emit(OP_LI, to.RegNum, 0, 0);
build->Emit(instr, check, op1.RegNum, op2.RegNum);
build->Emit(OP_JMP, 1);
build->Emit(OP_LI, to.RegNum, 1);
return to;
}
}
//==========================================================================
@ -2952,12 +3040,24 @@ FxExpression *FxCompareEq::Resolve(FCompileContext& ctx)
return NULL;
}
if (Operator == TK_ApproxEq && ValueType->GetRegType() != REGT_FLOAT) Operator = TK_Eq;
if (Operator == TK_ApproxEq && left->ValueType->GetRegType() != REGT_FLOAT && left->ValueType->GetRegType() != REGT_STRING)
Operator = TK_Eq;
if (left->isConstant() && right->isConstant())
{
int v;
if (ValueType->GetRegType() == REGT_FLOAT)
if (ValueType == TypeString)
{
FString v1 = static_cast<FxConstant *>(left)->GetValue().GetString();
FString v2 = static_cast<FxConstant *>(right)->GetValue().GetString();
if (Operator == TK_ApproxEq) v = !v1.CompareNoCase(v2);
else
{
v = !!v1.Compare(v2);
if (Operator == TK_Eq) v = !v;
}
}
else if (ValueType->GetRegType() == REGT_FLOAT)
{
double v1 = static_cast<FxConstant *>(left)->GetValue().GetFloat();
double v2 = static_cast<FxConstant *>(right)->GetValue().GetFloat();
@ -2989,39 +3089,55 @@ ExpEmit FxCompareEq::Emit(VMFunctionBuilder *build)
ExpEmit op1 = left->Emit(build);
ExpEmit op2 = right->Emit(build);
assert(op1.RegType == op2.RegType);
assert(op1.RegType == REGT_INT || op1.RegType == REGT_FLOAT || op1.RegType == REGT_POINTER);
int instr;
// Only the second operand may be constant.
if (op1.Konst)
if (op1.RegType == REGT_STRING)
{
swapvalues(op1, op2);
}
assert(!op1.Konst);
assert(op1.RegCount >= 1 && op1.RegCount <= 3);
ExpEmit to(build, REGT_INT);
assert(Operator == TK_Eq || Operator == TK_Neq || Operator == TK_ApproxEq);
int a = Operator == TK_Eq ? CMP_EQ :
Operator == TK_Neq ? CMP_EQ | CMP_CHECK : CMP_EQ | CMP_APPROX;
ExpEmit to(build, REGT_INT);
static int flops[] = { OP_EQF_R, OP_EQV2_R, OP_EQV3_R };
instr = op1.RegType == REGT_INT ? OP_EQ_R :
op1.RegType == REGT_FLOAT ? flops[op1.RegCount-1] :
OP_EQA_R;
op1.Free(build);
if (!op2.Konst)
{
op2.Free(build);
build->Emit(OP_LI, to.RegNum, 0, 0);
build->Emit(OP_CMPS, a, op1.RegNum, op2.RegNum);
build->Emit(OP_JMP, 1);
build->Emit(OP_LI, to.RegNum, 1);
return to;
}
else
{
instr += 1;
}
// See FxUnaryNotBoolean for comments, since it's the same thing.
build->Emit(OP_LI, to.RegNum, 0, 0);
build->Emit(instr, Operator == TK_ApproxEq? CMP_APPROX : Operator != TK_Eq, op1.RegNum, op2.RegNum);
build->Emit(OP_JMP, 1);
build->Emit(OP_LI, to.RegNum, 1);
return to;
// Only the second operand may be constant.
if (op1.Konst)
{
swapvalues(op1, op2);
}
assert(!op1.Konst);
assert(op1.RegCount >= 1 && op1.RegCount <= 3);
ExpEmit to(build, REGT_INT);
static int flops[] = { OP_EQF_R, OP_EQV2_R, OP_EQV3_R };
instr = op1.RegType == REGT_INT ? OP_EQ_R :
op1.RegType == REGT_FLOAT ? flops[op1.RegCount - 1] :
OP_EQA_R;
op1.Free(build);
if (!op2.Konst)
{
op2.Free(build);
}
else
{
instr += 1;
}
// See FxUnaryNotBoolean for comments, since it's the same thing.
build->Emit(OP_LI, to.RegNum, 0, 0);
build->Emit(instr, Operator == TK_ApproxEq ? CMP_APPROX : Operator != TK_Eq, op1.RegNum, op2.RegNum);
build->Emit(OP_JMP, 1);
build->Emit(OP_LI, to.RegNum, 1);
return to;
}
}
//==========================================================================
@ -3047,7 +3163,7 @@ FxExpression *FxBinaryInt::Resolve(FCompileContext& ctx)
CHECKRESOLVED();
if (!ResolveLR(ctx, false)) return NULL;
if (ValueType->GetRegType() == REGT_FLOAT && ctx.FromDecorate)
if (IsFloat() && ctx.FromDecorate)
{
// For DECORATE which allows floats here. ZScript does not.
if (left->ValueType->GetRegType() != REGT_INT)
@ -3557,7 +3673,7 @@ FxExpression *FxConditional::Resolve(FCompileContext& ctx)
ValueType = truex->ValueType;
else if (truex->ValueType == TypeBool && falsex->ValueType == TypeBool)
ValueType = TypeBool;
else if (truex->ValueType->GetRegType() == REGT_INT && falsex->ValueType->GetRegType() == REGT_INT)
else if (truex->IsInteger() && falsex->IsInteger())
ValueType = TypeSInt32;
else if (truex->IsNumeric() && falsex->IsNumeric())
ValueType = TypeFloat64;
@ -3565,7 +3681,7 @@ FxExpression *FxConditional::Resolve(FCompileContext& ctx)
ValueType = TypeVoid;
//else if (truex->ValueType != falsex->ValueType)
if (!IsNumeric() && !IsPointer() && !IsVector())
if ((!IsNumeric() && !IsPointer() && !IsVector()) || ValueType == TypeVoid)
{
ScriptPosition.Message(MSG_ERROR, "Incompatible types for ?: operator");
delete this;
@ -3590,7 +3706,7 @@ FxExpression *FxConditional::Resolve(FCompileContext& ctx)
return e;
}
if (ValueType->GetRegType() == REGT_FLOAT)
if (IsFloat())
{
if (truex->ValueType->GetRegType() != REGT_FLOAT)
{
@ -3911,11 +4027,11 @@ FxExpression *FxMinMax::Resolve(FCompileContext &ctx)
RESOLVE(choices[i], ctx);
ABORT(choices[i]);
if (choices[i]->ValueType->GetRegType() == REGT_FLOAT)
if (choices[i]->IsFloat())
{
floatcount++;
}
else if (choices[i]->ValueType->GetRegType() == REGT_INT && choices[i]->ValueType != TypeName)
else if (choices[i]->IsInteger())
{
intcount++;
}
@ -5690,7 +5806,7 @@ FxExpression *FxActionSpecialCall::Resolve(FCompileContext& ctx)
failed = true;
}
}
else if ((*ArgList)[i]->ValueType->GetRegType() != REGT_INT)
else if (!(*ArgList)[i]->IsInteger())
{
if ((*ArgList)[i]->ValueType->GetRegType() == REGT_FLOAT /* lax */)
{

View file

@ -295,9 +295,11 @@ public:
virtual PPrototype *ReturnProto();
virtual VMFunction *GetDirectFunction();
bool IsNumeric() const { return ValueType != TypeName && ValueType->GetRegCount() == 1 && (ValueType->GetRegType() == REGT_INT || ValueType->GetRegType() == REGT_FLOAT); }
bool IsFloat() const { return ValueType->GetRegType() == REGT_FLOAT && ValueType->GetRegCount() == 1; }
bool IsInteger() const { return ValueType != TypeName && (ValueType->GetRegType() == REGT_INT); }
bool IsPointer() const { return ValueType->GetRegType() == REGT_POINTER; }
bool IsVector() const { return ValueType == TypeVector2 || ValueType == TypeVector3; };
bool IsBoolCompat() const { return ValueType->GetRegCount() == 1 && (ValueType->GetRegType() == REGT_INT || ValueType->GetRegType() == REGT_FLOAT || ValueType->GetRegType() == REGT_POINTER); }
virtual ExpEmit Emit(VMFunctionBuilder *build);

View file

@ -921,7 +921,7 @@ void VMDisasm(FILE *out, const VMOP *code, int codesize, const VMScriptFunction
#define PARAM_SOUND_AT(p,x) assert((p) < numparam); assert(param[p].Type == REGT_INT); FSoundID x = param[p].i;
#define PARAM_COLOR_AT(p,x) assert((p) < numparam); assert(param[p].Type == REGT_INT); PalEntry x; x.d = param[p].i;
#define PARAM_FLOAT_AT(p,x) assert((p) < numparam); assert(param[p].Type == REGT_FLOAT); double x = param[p].f;
#define PARAM_ANGLE_AT(p,x) assert((p) < numparam); assert(param[p].Type == REGT_FLOAT); DAngle x = param[p].f;
#define PARAM_ANGLE_AT(p,x) assert((p) < numparam); assert(param[p].Type == REGT_FLOAT); DAngle x = param[p].f;
#define PARAM_STRING_AT(p,x) assert((p) < numparam); assert(param[p].Type == REGT_STRING); FString x = param[p].s();
#define PARAM_STATE_AT(p,x) assert((p) < numparam); assert(param[p].Type == REGT_POINTER && (param[p].atag == ATAG_STATE || param[p].atag == ATAG_GENERIC || param[p].a == NULL)); FState *x = (FState *)param[p].a;
#define PARAM_POINTER_AT(p,x,type) assert((p) < numparam); assert(param[p].Type == REGT_POINTER); type *x = (type *)param[p].a;