diff --git a/src/scripting/codegeneration/codegen.cpp b/src/scripting/codegeneration/codegen.cpp index 1b70a1e0c..43d5f8174 100644 --- a/src/scripting/codegeneration/codegen.cpp +++ b/src/scripting/codegeneration/codegen.cpp @@ -6054,9 +6054,14 @@ ExpEmit FxCVar::Emit(VMFunctionBuilder *build) break; case CVAR_DummyBool: - build->Emit(OP_LKP, addr.RegNum, build->GetConstantAddress(&static_cast(CVar)->ValueVar.Value, ATAG_GENERIC)); - build->Emit(OP_LBIT, dest.RegNum, addr.RegNum, static_cast(CVar)->BitNum); + { + auto cv = static_cast(CVar); + build->Emit(OP_LKP, addr.RegNum, build->GetConstantAddress(&cv->ValueVar.Value, ATAG_GENERIC)); + build->Emit(OP_LW, dest.RegNum, addr.RegNum, nul); + build->Emit(OP_SRL_RI, dest.RegNum, dest.RegNum, cv->BitNum); + build->Emit(OP_AND_RK, dest.RegNum, dest.RegNum, build->GetConstantInt(1)); break; + } case CVAR_DummyInt: { @@ -7408,7 +7413,9 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx) bool failed = false; auto proto = Function->Variants[0].Proto; auto &argtypes = proto->ArgumentTypes; + auto &argnames = Function->Variants[0].ArgNames; auto &argflags = Function->Variants[0].ArgFlags; + auto &defaults = Function->Variants[0].Implementation->DefaultArgs; int implicit = Function->GetImplicitArgs(); @@ -7446,6 +7453,67 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx) } assert(type != nullptr); + if (ArgList[i]->ExprType == EFX_NamedNode) + { + if (!(flag & VARF_Optional)) + { + ScriptPosition.Message(MSG_ERROR, "Cannot use a named argument here - not all required arguments have been passed."); + delete this; + return nullptr; + } + if (foundvarargs) + { + ScriptPosition.Message(MSG_ERROR, "Cannot use a named argument in the varargs part of the parameter list."); + delete this; + return nullptr; + } + unsigned j; + bool done = false; + FName name = static_cast(ArgList[i])->name; + for (j = 0; j < argnames.Size() - implicit; j++) + { + if (argnames[j + implicit] == name) + { + if (j < i) + { + ScriptPosition.Message(MSG_ERROR, "Named argument %s comes before current position in argument list.", name.GetChars()); + delete this; + return nullptr; + } + // copy the original argument into the list + auto old = static_cast(ArgList[i]); + ArgList[i] = old->value; + old->value = nullptr; + delete old; + // now fill the gap with constants created from the default list so that we got a full list of arguments. + int insert = j - i; + for (int k = 0; k < insert; k++) + { + auto ntype = argtypes[i + k + implicit]; + // If this is a reference argument, the pointer type must be undone because the code below expects the pointed type as value type. + if (argflags[i + k + implicit] & VARF_Ref) + { + assert(ntype->IsKindOf(RUNTIME_CLASS(PPointer))); + ntype = TypeNullPtr; // the default of a reference type can only be a null pointer + } + auto x = new FxConstant(ntype, defaults[i + k + implicit], ScriptPosition); + ArgList.Insert(i + k, x); + } + done = true; + break; + } + } + if (!done) + { + ScriptPosition.Message(MSG_ERROR, "Named argument %s not found.", name.GetChars()); + delete this; + return nullptr; + } + // re-get the proper info for the inserted node. + type = argtypes[i + implicit]; + flag = argflags[i + implicit]; + } + FxExpression *x; if (!(flag & VARF_Ref)) { diff --git a/src/scripting/codegeneration/codegen.h b/src/scripting/codegeneration/codegen.h index a3b645176..f004e469a 100644 --- a/src/scripting/codegeneration/codegen.h +++ b/src/scripting/codegeneration/codegen.h @@ -285,6 +285,7 @@ enum EFxType EFX_StaticArray, EFX_StaticArrayVariable, EFX_CVar, + EFX_NamedNode, EFX_COUNT }; @@ -472,6 +473,31 @@ public: ValueType = value.Type = TypeNullPtr; isresolved = true; } + + FxConstant(PType *type, VMValue &vmval, const FScriptPosition &pos) : FxExpression(EFX_Constant, pos) + { + ValueType = value.Type = type; + isresolved = true; + switch (vmval.Type) + { + default: + case REGT_INT: + value.Int = vmval.i; + break; + + case REGT_FLOAT: + value.Float = vmval.f; + break; + + case REGT_STRING: + value = ExpVal(vmval.s()); + break; + + case REGT_POINTER: + value.pointer = vmval.a; + break; + } + } static FxExpression *MakeConstant(PSymbol *sym, const FScriptPosition &pos); @@ -1912,5 +1938,24 @@ public: ExpEmit Emit(VMFunctionBuilder *build); }; +class FxNamedNode : public FxExpression +{ +public: + FName name; + FxExpression *value; + FxNamedNode(FName n, FxExpression *x, const FScriptPosition &pos) + : FxExpression(EFX_NamedNode, pos), name(n), value(x) + { + } + + FxExpression *Resolve(FCompileContext&) + { + // This should never reach the backend in a supported context, + // it's just needed to extend argument lists with the skipped parameters and needs to be resolved by the parent node. + ScriptPosition.Message(MSG_ERROR, "Named arguments not supported here"); + delete this; + return nullptr; + } +}; #endif diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index 291387617..f2d847325 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -2845,8 +2845,9 @@ FxExpression *ZCCCompiler::ConvertNode(ZCC_TreeNode *ast) case AST_FuncParm: { auto fparm = static_cast(ast); - // ignore the label for now, that's stuff for far later, when a bit more here is working. - return ConvertNode(fparm->Value); + auto node = ConvertNode(fparm->Value); + if (fparm->Label != NAME_None) node = new FxNamedNode(fparm->Label, node, *ast); + return node; } case AST_ExprID: diff --git a/wadsrc/static/zscript/doom/weaponbfg.txt b/wadsrc/static/zscript/doom/weaponbfg.txt index 3543b9cdb..1b96bfc29 100644 --- a/wadsrc/static/zscript/doom/weaponbfg.txt +++ b/wadsrc/static/zscript/doom/weaponbfg.txt @@ -81,7 +81,7 @@ extend class StateProvider return; } - SpawnPlayerMissile("BFGBall", angle, 0, 0, 0, null, false, sv_nobfgaim); + SpawnPlayerMissile("BFGBall", angle, nofreeaim:sv_nobfgaim); }