From 34c949f84b32615f19f14816eb145677de8a9918 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 18 Nov 2016 17:44:25 +0100 Subject: [PATCH] - 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. --- src/scripting/codegeneration/codegen.cpp | 153 +++++++++++++++++++++-- src/scripting/codegeneration/codegen.h | 15 +++ src/scripting/vm/vm.h | 2 + src/scripting/vm/vmdisasm.cpp | 2 + src/scripting/vm/vmexec.h | 23 ++-- src/scripting/vm/vmops.h | 2 +- src/scripting/zscript/zcc_compile.cpp | 2 +- 7 files changed, 175 insertions(+), 24 deletions(-) diff --git a/src/scripting/codegeneration/codegen.cpp b/src/scripting/codegeneration/codegen.cpp index c8cf6b740..a5f6a3bc7 100644 --- a/src/scripting/codegeneration/codegen.cpp +++ b/src/scripting/codegeneration/codegen.cpp @@ -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(left)->GetValue().GetString(); + auto v2 = static_cast(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(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())); } diff --git a/src/scripting/codegeneration/codegen.h b/src/scripting/codegeneration/codegen.h index df0634b43..66a0137b1 100644 --- a/src/scripting/codegeneration/codegen.h +++ b/src/scripting/codegeneration/codegen.h @@ -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 diff --git a/src/scripting/vm/vm.h b/src/scripting/vm/vm.h index 4e833cf0b..deca91a80 100644 --- a/src/scripting/vm/vm.h +++ b/src/scripting/vm/vm.h @@ -125,6 +125,8 @@ enum CAST_S2So, CAST_Co2S, CAST_So2S, + CAST_V22S, + CAST_V32S, }; // Register types for VMParam diff --git a/src/scripting/vm/vmdisasm.cpp b/src/scripting/vm/vmdisasm.cpp index d39723dea..07d6fb8e3 100644 --- a/src/scripting/vm/vmdisasm.cpp +++ b/src/scripting/vm/vmdisasm.cpp @@ -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: diff --git a/src/scripting/vm/vmexec.h b/src/scripting/vm/vmexec.h index 673582bda..918dc6949 100644 --- a/src/scripting/vm/vmexec.h +++ b/src/scripting/vm/vmexec.h @@ -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: diff --git a/src/scripting/vm/vmops.h b/src/scripting/vm/vmops.h index 368f143bd..210ff9c49 100644 --- a/src/scripting/vm/vmops.h +++ b/src/scripting/vm/vmops.h @@ -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++ diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index 206e2dd88..ebe70c340 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -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);