mirror of
https://github.com/ZDoom/qzdoom.git
synced 2025-02-18 01:52:34 +00:00
- implemented the string concatenation operator '..'. This is capable of stringifying all of the common types for output.
- gave OP_CONCAT some sane semantics. The way this was defined, by specifying the source operands as a range of registers instead of a pair like everything else made it completely useless for the task at hand. - changed formatting for floats to %.5f which for normal output in a game makes more sense. For special cases there should be a special formatting function for ints and floats that can do more specialized conversions.
This commit is contained in:
parent
24481781b4
commit
34c949f84b
7 changed files with 175 additions and 24 deletions
|
@ -2442,7 +2442,7 @@ FxExpression *FxAddSub::Resolve(FCompileContext& ctx)
|
||||||
if (!left || !right)
|
if (!left || !right)
|
||||||
{
|
{
|
||||||
delete this;
|
delete this;
|
||||||
return false;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (left->IsVector() && right->IsVector())
|
if (left->IsVector() && right->IsVector())
|
||||||
|
@ -2607,7 +2607,7 @@ FxExpression *FxMulDiv::Resolve(FCompileContext& ctx)
|
||||||
if (!left || !right)
|
if (!left || !right)
|
||||||
{
|
{
|
||||||
delete this;
|
delete this;
|
||||||
return false;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (left->IsVector() || right->IsVector())
|
if (left->IsVector() || right->IsVector())
|
||||||
|
@ -2628,7 +2628,7 @@ FxExpression *FxMulDiv::Resolve(FCompileContext& ctx)
|
||||||
if (right == nullptr)
|
if (right == nullptr)
|
||||||
{
|
{
|
||||||
delete this;
|
delete this;
|
||||||
return false;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ValueType = left->ValueType;
|
ValueType = left->ValueType;
|
||||||
|
@ -2642,7 +2642,7 @@ FxExpression *FxMulDiv::Resolve(FCompileContext& ctx)
|
||||||
if (left == nullptr)
|
if (left == nullptr)
|
||||||
{
|
{
|
||||||
delete this;
|
delete this;
|
||||||
return false;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ValueType = right->ValueType;
|
ValueType = right->ValueType;
|
||||||
|
@ -2843,7 +2843,7 @@ FxExpression *FxPow::Resolve(FCompileContext& ctx)
|
||||||
if (!left || !right)
|
if (!left || !right)
|
||||||
{
|
{
|
||||||
delete this;
|
delete this;
|
||||||
return false;
|
return nullptr;
|
||||||
}
|
}
|
||||||
if (!left->IsNumeric() || !right->IsNumeric())
|
if (!left->IsNumeric() || !right->IsNumeric())
|
||||||
{
|
{
|
||||||
|
@ -3380,7 +3380,7 @@ FxExpression *FxBitOp::Resolve(FCompileContext& ctx)
|
||||||
if (!left || !right)
|
if (!left || !right)
|
||||||
{
|
{
|
||||||
delete this;
|
delete this;
|
||||||
return false;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (left->ValueType == TypeBool && right->ValueType == TypeBool)
|
if (left->ValueType == TypeBool && right->ValueType == TypeBool)
|
||||||
|
@ -3474,7 +3474,7 @@ FxExpression *FxShift::Resolve(FCompileContext& ctx)
|
||||||
if (!left || !right)
|
if (!left || !right)
|
||||||
{
|
{
|
||||||
delete this;
|
delete this;
|
||||||
return false;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (left->IsNumeric() && right->IsNumeric())
|
if (left->IsNumeric() && right->IsNumeric())
|
||||||
|
@ -3587,7 +3587,7 @@ FxExpression *FxLtGtEq::Resolve(FCompileContext& ctx)
|
||||||
if (!left || !right)
|
if (!left || !right)
|
||||||
{
|
{
|
||||||
delete this;
|
delete this;
|
||||||
return false;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (left->IsNumeric() && right->IsNumeric())
|
if (left->IsNumeric() && right->IsNumeric())
|
||||||
|
@ -3656,6 +3656,137 @@ ExpEmit FxLtGtEq::Emit(VMFunctionBuilder *build)
|
||||||
//
|
//
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
||||||
|
FxConcat::FxConcat(FxExpression *l, FxExpression *r)
|
||||||
|
: FxBinary(TK_DotDot, l, r)
|
||||||
|
{
|
||||||
|
ValueType = TypeString;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
FxExpression *FxConcat::Resolve(FCompileContext& ctx)
|
||||||
|
{
|
||||||
|
CHECKRESOLVED();
|
||||||
|
|
||||||
|
RESOLVE(left, ctx);
|
||||||
|
RESOLVE(right, ctx);
|
||||||
|
if (!left || !right)
|
||||||
|
{
|
||||||
|
delete this;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// To concatenate two operands the only requirement is that they are integral types, i.e. can occupy a register
|
||||||
|
if (left->ValueType->GetRegType() == REGT_NIL || right->ValueType->GetRegType() == REGT_NIL)
|
||||||
|
{
|
||||||
|
ScriptPosition.Message(MSG_ERROR, "Invalid operand for string concatenation");
|
||||||
|
delete this;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (left->isConstant() && right->isConstant() && (left->ValueType == TypeString || left->ValueType == TypeName) && (right->ValueType == TypeString || right->ValueType == TypeName))
|
||||||
|
{
|
||||||
|
// for now this is only types which have a constant string representation.
|
||||||
|
auto v1 = static_cast<FxConstant *>(left)->GetValue().GetString();
|
||||||
|
auto v2 = static_cast<FxConstant *>(right)->GetValue().GetString();
|
||||||
|
auto e = new FxConstant(v1 + v2, ScriptPosition);
|
||||||
|
delete this;
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
ExpEmit FxConcat::Emit(VMFunctionBuilder *build)
|
||||||
|
{
|
||||||
|
ExpEmit op1 = left->Emit(build);
|
||||||
|
ExpEmit op2 = right->Emit(build);
|
||||||
|
ExpEmit strng, strng2;
|
||||||
|
|
||||||
|
if (op1.RegType == REGT_STRING && op1.Konst)
|
||||||
|
{
|
||||||
|
strng = ExpEmit(build, REGT_STRING);
|
||||||
|
build->Emit(OP_LKS, strng.RegNum, op1.RegNum);
|
||||||
|
}
|
||||||
|
else if (op1.RegType == REGT_STRING)
|
||||||
|
{
|
||||||
|
strng = op1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int cast;
|
||||||
|
strng = ExpEmit(build, REGT_STRING);
|
||||||
|
if (op1.Konst)
|
||||||
|
{
|
||||||
|
ExpEmit nonconst(build, op1.RegType);
|
||||||
|
build->Emit(op1.RegType == REGT_INT ? OP_LK : op1.RegType == REGT_FLOAT ? OP_LKF : OP_LKP, nonconst.RegNum, op1.RegNum);
|
||||||
|
op1 = nonconst;
|
||||||
|
}
|
||||||
|
if (op1.RegType == REGT_FLOAT) cast = op1.RegCount == 1 ? CAST_F2S : op1.RegCount == 2 ? CAST_V22S : CAST_V32S;
|
||||||
|
else if (left->ValueType == TypeUInt32) cast = CAST_U2S;
|
||||||
|
else if (left->ValueType == TypeName) cast = CAST_N2S;
|
||||||
|
else if (left->ValueType == TypeSound) cast = CAST_So2S;
|
||||||
|
else if (left->ValueType == TypeColor) cast = CAST_Co2S;
|
||||||
|
else if (op1.RegType == REGT_POINTER) cast = CAST_P2S;
|
||||||
|
else if (op1.RegType == REGT_INT) cast = CAST_I2S;
|
||||||
|
else assert(false && "Bad type for string concatenation");
|
||||||
|
build->Emit(OP_CAST, strng.RegNum, op1.RegNum, cast);
|
||||||
|
op1.Free(build);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (op2.RegType == REGT_STRING && op2.Konst)
|
||||||
|
{
|
||||||
|
strng2 = ExpEmit(build, REGT_STRING);
|
||||||
|
build->Emit(OP_LKS, strng2.RegNum, op2.RegNum);
|
||||||
|
}
|
||||||
|
else if (op2.RegType == REGT_STRING)
|
||||||
|
{
|
||||||
|
strng2 = op2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int cast;
|
||||||
|
strng2 = ExpEmit(build, REGT_STRING);
|
||||||
|
if (op2.Konst)
|
||||||
|
{
|
||||||
|
ExpEmit nonconst(build, op2.RegType);
|
||||||
|
build->Emit(op2.RegType == REGT_INT ? OP_LK : op2.RegType == REGT_FLOAT ? OP_LKF : OP_LKP, nonconst.RegNum, op2.RegNum);
|
||||||
|
op2 = nonconst;
|
||||||
|
}
|
||||||
|
if (op2.RegType == REGT_FLOAT) cast = op2.RegCount == 1 ? CAST_F2S : op2.RegCount == 2 ? CAST_V22S : CAST_V32S;
|
||||||
|
else if (right->ValueType == TypeUInt32) cast = CAST_U2S;
|
||||||
|
else if (right->ValueType == TypeName) cast = CAST_N2S;
|
||||||
|
else if (right->ValueType == TypeSound) cast = CAST_So2S;
|
||||||
|
else if (right->ValueType == TypeColor) cast = CAST_Co2S;
|
||||||
|
else if (op2.RegType == REGT_POINTER) cast = CAST_P2S;
|
||||||
|
else if (op2.RegType == REGT_INT) cast = CAST_I2S;
|
||||||
|
else assert(false && "Bad type for string concatenation");
|
||||||
|
build->Emit(OP_CAST, strng2.RegNum, op2.RegNum, cast);
|
||||||
|
op2.Free(build);
|
||||||
|
}
|
||||||
|
strng.Free(build);
|
||||||
|
strng2.Free(build);
|
||||||
|
ExpEmit dest(build, REGT_STRING);
|
||||||
|
assert(strng.RegType == strng2.RegType && strng.RegType == REGT_STRING);
|
||||||
|
build->Emit(OP_CONCAT, dest.RegNum, strng.RegNum, strng2.RegNum);
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
FxBinaryLogical::FxBinaryLogical(int o, FxExpression *l, FxExpression *r)
|
FxBinaryLogical::FxBinaryLogical(int o, FxExpression *l, FxExpression *r)
|
||||||
: FxExpression(EFX_BinaryLogical, l->ScriptPosition)
|
: FxExpression(EFX_BinaryLogical, l->ScriptPosition)
|
||||||
{
|
{
|
||||||
|
@ -8854,9 +8985,11 @@ ExpEmit FxLocalVariableDeclaration::Emit(VMFunctionBuilder *build)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case REGT_POINTER:
|
case REGT_POINTER:
|
||||||
build->Emit(OP_LKP, RegNum, build->GetConstantAddress(constval->GetValue().GetPointer(), ATAG_GENERIC));
|
{
|
||||||
|
bool isobject = ValueType->IsKindOf(RUNTIME_CLASS(PClassPointer)) || (ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) && static_cast<PPointer*>(ValueType)->PointedType->IsKindOf(RUNTIME_CLASS(PClass)));
|
||||||
|
build->Emit(OP_LKP, RegNum, build->GetConstantAddress(constval->GetValue().GetPointer(), isobject ? ATAG_OBJECT : ATAG_GENERIC));
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case REGT_STRING:
|
case REGT_STRING:
|
||||||
build->Emit(OP_LKS, RegNum, build->GetConstantString(constval->GetValue().GetString()));
|
build->Emit(OP_LKS, RegNum, build->GetConstantString(constval->GetValue().GetString()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -937,6 +937,21 @@ public:
|
||||||
ExpEmit Emit(VMFunctionBuilder *build);
|
ExpEmit Emit(VMFunctionBuilder *build);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
class FxConcat : public FxBinary
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FxConcat(FxExpression*, FxExpression*);
|
||||||
|
FxExpression *Resolve(FCompileContext&);
|
||||||
|
|
||||||
|
ExpEmit Emit(VMFunctionBuilder *build);
|
||||||
|
};
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
//
|
//
|
||||||
// FxBinaryLogical
|
// FxBinaryLogical
|
||||||
|
|
|
@ -125,6 +125,8 @@ enum
|
||||||
CAST_S2So,
|
CAST_S2So,
|
||||||
CAST_Co2S,
|
CAST_Co2S,
|
||||||
CAST_So2S,
|
CAST_So2S,
|
||||||
|
CAST_V22S,
|
||||||
|
CAST_V32S,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Register types for VMParam
|
// Register types for VMParam
|
||||||
|
|
|
@ -396,6 +396,8 @@ void VMDisasm(FILE *out, const VMOP *code, int codesize, const VMScriptFunction
|
||||||
mode = MODE_AI | MODE_BF | MODE_CUNUSED;
|
mode = MODE_AI | MODE_BF | MODE_CUNUSED;
|
||||||
break;
|
break;
|
||||||
case CAST_F2S:
|
case CAST_F2S:
|
||||||
|
case CAST_V22S:
|
||||||
|
case CAST_V32S:
|
||||||
mode = MODE_AS | MODE_BF | MODE_CUNUSED;
|
mode = MODE_AS | MODE_BF | MODE_CUNUSED;
|
||||||
break;
|
break;
|
||||||
case CAST_P2S:
|
case CAST_P2S:
|
||||||
|
|
|
@ -704,16 +704,7 @@ begin:
|
||||||
|
|
||||||
OP(CONCAT):
|
OP(CONCAT):
|
||||||
ASSERTS(a); ASSERTS(B); ASSERTS(C);
|
ASSERTS(a); ASSERTS(B); ASSERTS(C);
|
||||||
{
|
reg.s[a] = reg.s[B] + reg.s[C];
|
||||||
FString *rB = ®.s[B];
|
|
||||||
FString *rC = ®.s[C];
|
|
||||||
FString concat(*rB);
|
|
||||||
for (++rB; rB <= rC; ++rB)
|
|
||||||
{
|
|
||||||
concat += *rB;
|
|
||||||
}
|
|
||||||
reg.s[a] = concat;
|
|
||||||
}
|
|
||||||
NEXTOP;
|
NEXTOP;
|
||||||
OP(LENS):
|
OP(LENS):
|
||||||
ASSERTD(a); ASSERTS(B);
|
ASSERTD(a); ASSERTS(B);
|
||||||
|
@ -1680,8 +1671,16 @@ static void DoCast(const VMRegisters ®, const VMFrame *f, int a, int b, int c
|
||||||
reg.d[a] = (int)(unsigned)reg.f[b];
|
reg.d[a] = (int)(unsigned)reg.f[b];
|
||||||
break;
|
break;
|
||||||
case CAST_F2S:
|
case CAST_F2S:
|
||||||
ASSERTS(a); ASSERTD(b);
|
ASSERTS(a); ASSERTF(b);
|
||||||
reg.s[a].Format("%.14g", reg.f[b]);
|
reg.s[a].Format("%.5f", reg.f[b]); // keep this small. For more precise conversion there should be a conversion function.
|
||||||
|
break;
|
||||||
|
case CAST_V22S:
|
||||||
|
ASSERTS(a); ASSERTF(b+1);
|
||||||
|
reg.s[a].Format("(%.5f, %.5f)", reg.f[b], reg.f[b + 1]);
|
||||||
|
break;
|
||||||
|
case CAST_V32S:
|
||||||
|
ASSERTS(a); ASSERTF(b + 2);
|
||||||
|
reg.s[a].Format("(%.5f, %.5f, %.5f)", reg.f[b], reg.f[b + 1], reg.f[b + 2]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CAST_P2S:
|
case CAST_P2S:
|
||||||
|
|
|
@ -101,7 +101,7 @@ xx(CATCH, catch, CATCH), // A == 0: continue search on next try
|
||||||
xx(BOUND, bound, RII16), // if rA >= BC, throw exception
|
xx(BOUND, bound, RII16), // if rA >= BC, throw exception
|
||||||
|
|
||||||
// String instructions.
|
// String instructions.
|
||||||
xx(CONCAT, concat, RSRSRS), // sA = sB.. ... ..sC
|
xx(CONCAT, concat, RSRSRS), // sA = sB..sC
|
||||||
xx(LENS, lens, RIRS), // dA = sB.Length
|
xx(LENS, lens, RIRS), // dA = sB.Length
|
||||||
xx(CMPS, cmps, I8RXRX), // if ((skB op skC) != (A & 1)) then pc++
|
xx(CMPS, cmps, I8RXRX), // if ((skB op skC) != (A & 1)) then pc++
|
||||||
|
|
||||||
|
|
|
@ -2959,8 +2959,8 @@ FxExpression *ZCCCompiler::ConvertNode(ZCC_TreeNode *ast)
|
||||||
case PEX_Is:
|
case PEX_Is:
|
||||||
return new FxTypeCheck(left, right);
|
return new FxTypeCheck(left, right);
|
||||||
|
|
||||||
// todo: These do not have representations in DECORATE and no implementation exists yet.
|
|
||||||
case PEX_Concat:
|
case PEX_Concat:
|
||||||
|
return new FxConcat(left, right);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
I_Error("Binary operator %d not implemented yet", op);
|
I_Error("Binary operator %d not implemented yet", op);
|
||||||
|
|
Loading…
Reference in a new issue