mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-11-28 15:02:01 +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)
|
||||
{
|
||||
delete this;
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (left->IsVector() && right->IsVector())
|
||||
|
@ -2607,7 +2607,7 @@ FxExpression *FxMulDiv::Resolve(FCompileContext& ctx)
|
|||
if (!left || !right)
|
||||
{
|
||||
delete this;
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (left->IsVector() || right->IsVector())
|
||||
|
@ -2628,7 +2628,7 @@ FxExpression *FxMulDiv::Resolve(FCompileContext& ctx)
|
|||
if (right == nullptr)
|
||||
{
|
||||
delete this;
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
ValueType = left->ValueType;
|
||||
|
@ -2642,7 +2642,7 @@ FxExpression *FxMulDiv::Resolve(FCompileContext& ctx)
|
|||
if (left == nullptr)
|
||||
{
|
||||
delete this;
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
ValueType = right->ValueType;
|
||||
|
@ -2843,7 +2843,7 @@ FxExpression *FxPow::Resolve(FCompileContext& ctx)
|
|||
if (!left || !right)
|
||||
{
|
||||
delete this;
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
if (!left->IsNumeric() || !right->IsNumeric())
|
||||
{
|
||||
|
@ -3380,7 +3380,7 @@ FxExpression *FxBitOp::Resolve(FCompileContext& ctx)
|
|||
if (!left || !right)
|
||||
{
|
||||
delete this;
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (left->ValueType == TypeBool && right->ValueType == TypeBool)
|
||||
|
@ -3474,7 +3474,7 @@ FxExpression *FxShift::Resolve(FCompileContext& ctx)
|
|||
if (!left || !right)
|
||||
{
|
||||
delete this;
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (left->IsNumeric() && right->IsNumeric())
|
||||
|
@ -3587,7 +3587,7 @@ FxExpression *FxLtGtEq::Resolve(FCompileContext& ctx)
|
|||
if (!left || !right)
|
||||
{
|
||||
delete this;
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
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)
|
||||
: FxExpression(EFX_BinaryLogical, l->ScriptPosition)
|
||||
{
|
||||
|
@ -8854,9 +8985,11 @@ ExpEmit FxLocalVariableDeclaration::Emit(VMFunctionBuilder *build)
|
|||
break;
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
case REGT_STRING:
|
||||
build->Emit(OP_LKS, RegNum, build->GetConstantString(constval->GetValue().GetString()));
|
||||
}
|
||||
|
|
|
@ -937,6 +937,21 @@ public:
|
|||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
class FxConcat : public FxBinary
|
||||
{
|
||||
public:
|
||||
FxConcat(FxExpression*, FxExpression*);
|
||||
FxExpression *Resolve(FCompileContext&);
|
||||
|
||||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FxBinaryLogical
|
||||
|
|
|
@ -125,6 +125,8 @@ enum
|
|||
CAST_S2So,
|
||||
CAST_Co2S,
|
||||
CAST_So2S,
|
||||
CAST_V22S,
|
||||
CAST_V32S,
|
||||
};
|
||||
|
||||
// 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;
|
||||
break;
|
||||
case CAST_F2S:
|
||||
case CAST_V22S:
|
||||
case CAST_V32S:
|
||||
mode = MODE_AS | MODE_BF | MODE_CUNUSED;
|
||||
break;
|
||||
case CAST_P2S:
|
||||
|
|
|
@ -704,16 +704,7 @@ begin:
|
|||
|
||||
OP(CONCAT):
|
||||
ASSERTS(a); ASSERTS(B); ASSERTS(C);
|
||||
{
|
||||
FString *rB = ®.s[B];
|
||||
FString *rC = ®.s[C];
|
||||
FString concat(*rB);
|
||||
for (++rB; rB <= rC; ++rB)
|
||||
{
|
||||
concat += *rB;
|
||||
}
|
||||
reg.s[a] = concat;
|
||||
}
|
||||
reg.s[a] = reg.s[B] + reg.s[C];
|
||||
NEXTOP;
|
||||
OP(LENS):
|
||||
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];
|
||||
break;
|
||||
case CAST_F2S:
|
||||
ASSERTS(a); ASSERTD(b);
|
||||
reg.s[a].Format("%.14g", reg.f[b]);
|
||||
ASSERTS(a); ASSERTF(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;
|
||||
|
||||
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
|
||||
|
||||
// String instructions.
|
||||
xx(CONCAT, concat, RSRSRS), // sA = sB.. ... ..sC
|
||||
xx(CONCAT, concat, RSRSRS), // sA = sB..sC
|
||||
xx(LENS, lens, RIRS), // dA = sB.Length
|
||||
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:
|
||||
return new FxTypeCheck(left, right);
|
||||
|
||||
// todo: These do not have representations in DECORATE and no implementation exists yet.
|
||||
case PEX_Concat:
|
||||
return new FxConcat(left, right);
|
||||
|
||||
default:
|
||||
I_Error("Binary operator %d not implemented yet", op);
|
||||
|
|
Loading…
Reference in a new issue