- 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:
Christoph Oelckers 2016-11-18 17:44:25 +01:00
parent 24481781b4
commit 34c949f84b
7 changed files with 175 additions and 24 deletions

View file

@ -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()));
}

View file

@ -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

View file

@ -125,6 +125,8 @@ enum
CAST_S2So,
CAST_Co2S,
CAST_So2S,
CAST_V22S,
CAST_V32S,
};
// Register types for VMParam

View file

@ -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:

View file

@ -704,16 +704,7 @@ begin:
OP(CONCAT):
ASSERTS(a); ASSERTS(B); ASSERTS(C);
{
FString *rB = &reg.s[B];
FString *rC = &reg.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 &reg, 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:

View file

@ -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++

View file

@ -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);