1 //**************************************************************************
3 //** ## ## ## ## ## #### #### ### ###
4 //** ## ## ## ## ## ## ## ## ## ## #### ####
5 //** ## ## ## ## ## ## ## ## ## ## ## ## ## ##
6 //** ## ## ######## ## ## ## ## ## ## ## ### ##
7 //** ### ## ## ### ## ## ## ## ## ##
8 //** # ## ## # #### #### ## ##
10 //** Copyright (C) 1999-2006 Jānis Legzdiņš
11 //** Copyright (C) 2018-2023 Ketmar Dark
13 //** This program is free software: you can redistribute it and/or modify
14 //** it under the terms of the GNU General Public License as published by
15 //** the Free Software Foundation, version 3 of the License ONLY.
17 //** This program is distributed in the hope that it will be useful,
18 //** but WITHOUT ANY WARRANTY; without even the implied warranty of
19 //** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 //** GNU General Public License for more details.
22 //** You should have received a copy of the GNU General Public License
23 //** along with this program. If not, see <http://www.gnu.org/licenses/>.
25 //**************************************************************************
26 // this directly included from "vc_decorate.cpp"
29 //==========================================================================
31 // VDecorateInvocation
33 //==========================================================================
34 class VDecorateInvocation
: public VExpression
{
37 VMethod
*Func
; // if this is not null, use it instead of searching by `Name` (yet `Name` still be valid!)
39 VState
*CallerState
; // used for `Func`
40 VExpression
*Args
[VMethod::MAX_PARAMS
+1];
42 VDecorateInvocation (VName AName
, const TLocation
&ALoc
, int ANumArgs
, VExpression
*AArgs
[]);
43 VDecorateInvocation (VMethod
*AFunc
, VName AName
, const TLocation
&ALoc
, int ANumArgs
, VExpression
*AArgs
[]);
44 virtual ~VDecorateInvocation () override
;
45 virtual VExpression
*SyntaxCopy () override
;
46 virtual VExpression
*DoResolve (VEmitContext
&) override
;
47 virtual void Emit (VEmitContext
&) override
;
49 virtual bool HasSideEffects () override
;
50 virtual void VisitChildren (VExprVisitor
*v
) override
;
52 virtual VStr
toString () const override
;
54 void FixKnownShit (VEmitContext
&ec
, const char *FuncName
);
57 VDecorateInvocation () {}
58 virtual void DoSyntaxCopyTo (VExpression
*e
) override
;
60 bool HasFloatArg (VEmitContext
&ec
);
64 //==========================================================================
68 // this generates call to _get/_set uservar methods
70 //==========================================================================
71 class VDecorateUserVar
: public VExpression
{
74 VExpression
*index
; // array index, if not nullptr
76 VDecorateUserVar (VStr afldname
, const TLocation
&aloc
);
77 VDecorateUserVar (VStr afldname
, VExpression
*aindex
, const TLocation
&aloc
);
78 virtual ~VDecorateUserVar () override
;
79 virtual VExpression
*SyntaxCopy () override
;
80 virtual VExpression
*DoResolve (VEmitContext
&ec
) override
;
81 //virtual VExpression *ResolveAssignmentTarget (VEmitContext &ec) override;
82 virtual VExpression
*ResolveCompleteAssign (VEmitContext
&ec
, VExpression
*val
, bool &resolved
) override
;
83 virtual void Emit (VEmitContext
&ec
) override
;
84 virtual bool IsDecorateUserVar () const override
;
85 virtual bool HasSideEffects () override
;
86 virtual void VisitChildren (VExprVisitor
*v
) override
;
87 virtual VStr
toString () const override
;
90 VDecorateUserVar () {}
91 virtual void DoSyntaxCopyTo (VExpression
*e
) override
;
95 //==========================================================================
99 // as `A_Jump()` can have insane amounts of arguments, we'll generate
100 // VM code directly instead
102 //==========================================================================
103 class VDecorateAJump
: public VExpression
{
105 //VExpression *xstc; // bool(`XLevel.StateCall`) access expression
106 //VExpression *xass; // XLevel.StateCall->Result = false
107 VExpression
*crnd0
; // first call to P_Random()
108 VExpression
*crnd1
; // second call to P_Random()
111 VExpression
*prob
; // probability
112 TArray
<VExpression
*> labels
; // jump labels
116 VDecorateAJump (const TLocation
&aloc
);
117 virtual ~VDecorateAJump () override
;
118 virtual VExpression
*SyntaxCopy () override
;
119 virtual VExpression
*DoResolve (VEmitContext
&ec
) override
;
120 virtual void Emit (VEmitContext
&ec
) override
;
121 virtual bool HasSideEffects () override
;
122 virtual void VisitChildren (VExprVisitor
*v
) override
;
123 virtual VStr
toString () const override
;
127 virtual void DoSyntaxCopyTo (VExpression
*e
) override
;
132 //==========================================================================
136 // as `[f]randompick()` can have insane amounts of arguments, we'll
137 // generate VM code directly instead
139 //==========================================================================
140 class VDecorateRndPick
: public VExpression
{
142 VExpression
*crnd0
; // first call to P_Random()
145 TArray
<VExpression
*> numbers
; // jump labels
149 VDecorateRndPick (bool aAsFloat
, const TLocation
&aloc
);
150 virtual ~VDecorateRndPick () override
;
151 virtual VExpression
*SyntaxCopy () override
;
152 virtual VExpression
*DoResolve (VEmitContext
&ec
) override
;
153 virtual void Emit (VEmitContext
&ec
) override
;
154 virtual bool HasSideEffects () override
;
155 virtual void VisitChildren (VExprVisitor
*v
) override
;
156 virtual VStr
toString () const override
;
159 VDecorateRndPick () {}
160 virtual void DoSyntaxCopyTo (VExpression
*e
) override
;
165 //==========================================================================
169 //==========================================================================
170 static bool tryStringAsFloat (float &resf
, const char *str
) {
172 if (!str
|| !str
[0]) return false;
173 if (VStr::convertFloat(str
, &resf
)) {
174 if (isFiniteF(resf
)) {
183 //==========================================================================
187 //==========================================================================
188 static bool tryStringAsInt (int &resi
, const char *str
, const VFieldType
&destType
) {
191 if (!str
|| !str
[0]) return false;
193 if (VStr::convertInt(str
, &resi
, true)) {
194 switch (destType
.Type
) {
195 case TYPE_Byte
: resi
= clampToByte(resi
); break;
196 case TYPE_Bool
: resi
= (resi
? 1 : 0); break;
205 //==========================================================================
207 // tryStringAsFloat2Int
209 //==========================================================================
210 static bool tryStringAsFloat2Int (int &resi
, const char *str
, const VFieldType
&destType
) {
212 if (!str
|| !str
[0]) return false;
215 if (VStr::convertFloat(str
, &resf
)) {
216 if (isFiniteF(resf
)) {
218 if (resf
< -0x3fffffff) resi
= -0x3fffffff;
219 else if (resf
> +0x3fffffff) resi
= +0x3fffffff;
220 else resi
= (int)resf
;
221 switch (destType
.Type
) {
222 case TYPE_Byte
: resi
= clampToByte(resi
); break;
223 case TYPE_Bool
: resi
= (resi
? 1 : 0); break;
232 //==========================================================================
234 // VInvocation::MassageDecorateArg
236 // this will try to coerce some decorate argument to something sensible
238 // `argnum` starts with `1`
240 //==========================================================================
241 VExpression
*VExpression::MassageDecorateArg (VEmitContext
&ec
, VInvocation
*invokation
, VState
*CallerState
, const char *funcName
,
242 int argnum
, const VFieldType
&destType
, bool isOptional
,
243 const TLocation
*aloc
, bool *massaged
)
245 //FIXME: move this to separate method
246 // simplify a little:
247 // replace `+number` with `number`
249 VUnary
*un
= (VUnary
*)this;
251 if (un
->Oper
== VUnary::Plus
&& (un
->op
->IsIntConst() || un
->op
->IsFloatConst())) {
252 VExpression
*enew
= un
->op
;
253 //fprintf(stderr, "SIMPLIFIED! <%s> -> <%s>\n", *un->toString(), *etmp->toString());
256 return enew
->MassageDecorateArg(ec
, invokation
, CallerState
, funcName
, argnum
, destType
, isOptional
, aloc
, massaged
);
262 // note that `invokation` may be `nullptr` if we called from `VDecorateAJump()`, for example
264 for (auto &&FlagName
: invokation
->Func
->Params
[argnum
-1].NamedFlags
) {
265 // hack for idiotic mod authors (hello, LCA!)
267 if (FlagName
== "soundchannel") {
268 if (destType
.Type
== TYPE_Int
) {
269 if (IsStrConst() || IsNameConst() || IsDecorateSingleName()) {
270 VStr s
= (IsDecorateSingleName() ? *((VDecorateSingleName
*)this)->Name
: IsStrConst() ? GetStrConst(ec
.Package
) : VStr(GetNameConst()));
272 if (!VStr::convertInt(*s
, &v
)) {
274 const char *str
= *s
;
276 while (*str
&& digitInBase(*str
, 10) < 0) ++str
;
279 int d
= digitInBase(*str
, 10);
287 if (t
[0] == '_' || (vuint8
)(t
[0]) <= ' ') { t
.chopLeft(1); continue; }
288 if (t
.startsWithNoCase("CHANNEL")) { t
.chopLeft(7); continue; }
289 if (t
.startsWithNoCase("CHAN")) { t
.chopLeft(4); continue; }
290 if (t
.startsWithNoCase("SoundSlot")) { t
.chopLeft(9); continue; }
291 if (t
.startsWithNoCase("Slot")) { t
.chopLeft(4); continue; }
292 if (t
.startsWithNoCase("Sound")) { t
.chopLeft(5); continue; }
295 if (t
.ICmp("Auto") == 0) v
= 0;
296 else if (t
.ICmp("Voice") == 0) v
= 1;
297 else if (t
.ICmp("Weapon") == 0) v
= 2;
298 else if (t
.ICmp("Item") == 0) v
= 3;
299 else if (t
.ICmp("Body") == 0) v
= 4;
303 if (v
< 0 || v
> 127) v
= 0;
304 //GLog.Logf(NAME_Debug, "*** FUNC:`%s`; arg #%d (%s): soundchannel! '%s' -> %d", *Func->GetFullName(), i+1, *Func->Params[i].Name, *s, v);
305 VExpression
*enew
= new VIntLiteral(v
, Loc
);
307 return enew
->MassageDecorateArg(ec
, invokation
, CallerState
, funcName
, argnum
, destType
, isOptional
, aloc
, massaged
);
312 // [color] or [color0]
313 if (FlagName
== "color0" || FlagName
== "color") {
314 if (destType
.Type
== TYPE_String
) {
315 // integer zero? pass empty string
317 if (GetIntConst() == 0 && FlagName
== "color0") {
318 VExpression
*enew
= new VStringLiteral(VStr(), ec
.Package
->FindString(""), Loc
);
320 return enew
->MassageDecorateArg(ec
, invokation
, CallerState
, funcName
, argnum
, destType
, isOptional
, aloc
, massaged
);
323 VStr s
= va("#%06x", GetIntConst()&0xffffff);
324 VExpression
*enew
= new VStringLiteral(s
, ec
.Package
->FindString(*s
), Loc
);
326 return enew
->MassageDecorateArg(ec
, invokation
, CallerState
, funcName
, argnum
, destType
, isOptional
, aloc
, massaged
);
333 ParseWarningAsError(Loc
, "function `%s`, argument #%d has unknown argument flag `%s`!", funcName
, argnum
, *FlagName
);
337 if (massaged
) *massaged
= true;
338 switch (destType
.Type
) {
344 VStr str
= GetStrConst(ec
.Package
);
345 if (str
.isEmpty() || str
.strEquCI("none") || str
.strEquCI("null") || str
.strEquCI("nil") || str
.strEquCI("false")) {
346 ParseWarningAsError((aloc
? *aloc
: Loc
), "`%s` argument #%d should be a number (replaced with 0); PLEASE, FIX THE CODE!", funcName
, argnum
);
347 VExpression
*enew
= new VIntLiteral(0, Loc
);
351 if (str
.strEquCI("true")) {
352 ParseWarningAsError((aloc
? *aloc
: Loc
), "`%s` argument #%d should be a number (replaced with 1); PLEASE, FIX THE CODE!", funcName
, argnum
);
353 VExpression
*enew
= new VIntLiteral(1, Loc
);
357 // doomrl arsenal author is a... well, you know it
358 if (argnum
== 3 && VStr::strEqu(funcName
, "decorate_A_CheckFlag") && str
.strEquCI("Nope")) {
359 ParseWarningAsError((aloc
? *aloc
: Loc
), "`%s` argument #%d should be an aptr (replaced with AAPTR_DEFAULT, mod author is a mo...dder)!", funcName
, argnum
);
360 VExpression
*enew
= new VIntLiteral(0, Loc
);
364 // `A_SpawnParticle()` color
365 if (argnum
== 1 && VStr::strEqu(funcName
, "A_SpawnParticle")) {
366 vuint32 clr
= M_ParseColor(*str
, /*retZeroIfInvalid*/true);
368 ParseWarning((aloc
? *aloc
: Loc
), "color argument \"%s\" to `%s` is not a valid color (replaced with black)!", *str
, funcName
);
370 VExpression
*enew
= new VIntLiteral((vint32
)clr
, Loc
);
374 // ok, try to convert the string to a number... please, Invisible Pink Unicorn, why did you created so many morons?!
375 if (destType
.Type
== TYPE_Float
) {
377 if (tryStringAsFloat(resf
, *str
)) {
378 ParseWarningAsError((aloc
? *aloc
: Loc
), "`%s` argument #%d should be `float`, not a string (coerced, mod author is a mo...dder)!", funcName
, argnum
);
379 VExpression
*enew
= new VFloatLiteral(resf
, Loc
);
386 if (tryStringAsInt(resi
, *str
, destType
)) {
387 ParseWarningAsError((aloc
? *aloc
: Loc
), "`%s` argument #%d should be `%s`, not a string (coerced, mod author is a mo...dder)!", funcName
, argnum
, *destType
.GetName());
388 VExpression
*enew
= new VIntLiteral(resi
, Loc
);
392 if (tryStringAsFloat2Int(resi
, *str
, destType
)) {
393 ParseWarningAsError((aloc
? *aloc
: Loc
), "`%s` argument #%d should be `%s`, not a float-as-string (coerced, mod author is a mo...dder)!", funcName
, argnum
, *destType
.GetName());
394 VExpression
*enew
= new VIntLiteral(resi
, Loc
);
400 // `A_SpawnParticle()` color
401 if (argnum
== 1 && IsDecorateSingleName() && VStr::strEqu(funcName
, "A_SpawnParticle")) {
402 VDecorateSingleName
*e
= (VDecorateSingleName
*)this;
403 vuint32 clr
= M_ParseColor(*e
->Name
, /*retZeroIfInvalid*/true);
405 ParseWarning((aloc
? *aloc
: Loc
), "color argument \"%s\" to `%s` is not a valid color (replaced with black)!", *e
->Name
, funcName
);
407 VExpression
*enew
= new VIntLiteral((vint32
)clr
, Loc
);
412 if (IsNoneLiteral()) {
413 ParseWarningAsError((aloc
? *aloc
: Loc
), "`%s` argument #%d should be a number (replaced with 0); PLEASE, FIX THE CODE!", funcName
, argnum
);
414 VExpression
*enew
= new VIntLiteral(0, Loc
);
419 if (IsDecorateSingleName()) {
420 VDecorateSingleName
*e
= (VDecorateSingleName
*)this;
421 VStr str
= VStr(e
->Name
);
422 // ok, try to convert the string to a number... please, Invisible Pink Unicorn, why did you created so many morons?!
423 if (destType
.Type
== TYPE_Float
) {
425 if (tryStringAsFloat(resf
, *str
)) {
426 ParseWarningAsError((aloc
? *aloc
: Loc
), "`%s` argument #%d should be `float`, not string (coerced, mod author is a mo...dder)!", funcName
, argnum
);
427 VExpression
*enew
= new VFloatLiteral(resf
, Loc
);
434 if (tryStringAsInt(resi
, *str
, destType
)) {
435 ParseWarningAsError((aloc
? *aloc
: Loc
), "`%s` argument #%d should be `%s`, not string (coerced, mod author is a mo...dder)!", funcName
, argnum
, *destType
.GetName());
436 VExpression
*enew
= new VIntLiteral(resi
, Loc
);
440 if (tryStringAsFloat2Int(resi
, *str
, destType
)) {
441 ParseWarningAsError((aloc
? *aloc
: Loc
), "`%s` argument #%d should be `%s`, not float-as-string (coerced, mod author is a mo...dder)!", funcName
, argnum
, *destType
.GetName());
442 VExpression
*enew
= new VIntLiteral(resi
, Loc
);
449 // convert string literal to identifier
451 VStr str
= GetStrConst(ec
.Package
);
452 if (!str
.isEmpty()) {
453 ParseWarningAsError((aloc
? *aloc
: Loc
), "`%s` argument #%d should be `%s`! (trying constant `%s`)", funcName
, argnum
, *destType
.GetName(), *str
);
454 VDecorateSingleName
*enew
= new VDecorateSingleName(str
, Loc
);
464 if (IsDecorateSingleName()) {
465 VDecorateSingleName
*e
= (VDecorateSingleName
*)this;
466 VExpression
*enew
= new VNameLiteral(*e
->Name
, Loc
);
472 VStr val
= GetStrConst(ec
.Package
);
473 VExpression
*enew
= new VNameLiteral(*val
, Loc
);
478 if (IsIntConst() && GetIntConst() == 0) {
479 // "false" or "0" means "empty"
480 ParseWarningAsError((aloc
? *aloc
: Loc
), "`%s` argument #%d should be a string (replaced `0` with empty string); PLEASE, FIX THE CODE!", funcName
, argnum
);
481 VExpression
*enew
= new VNameLiteral(NAME_None
, Loc
);
485 // idiotic call (forgotten "damagetype")
486 if (invokation
&& argnum
== invokation
->NumArgs
&& argnum
< 16 && IsIntConst() && (GetIntConst() == 1 || GetIntConst() == 0) &&
487 (VStr::strEqu(funcName
, "A_CustomMeleeAttack") || VStr::strEqu(funcName
, "A_CustomComboAttack")))
489 ParseWarningAsError((aloc
? *aloc
: Loc
), "`%s` argument #%d should be a DamageType (inserted default value); MOD AUTHOR IS A MO...DDER!", funcName
, argnum
);
490 VExpression
*enew
= new VNameLiteral(NAME_None
, Loc
);
492 invokation
->Args
[invokation
->NumArgs
] = new VIntLiteral(GetIntConst(), Loc
);
493 ++invokation
->NumArgs
;
498 if (IsNoneLiteral()) {
499 VExpression
*enew
= new VNameLiteral("none", Loc
);
507 if (IsDecorateSingleName()) {
508 VDecorateSingleName
*e
= (VDecorateSingleName
*)this;
509 VExpression
*enew
= new VStringLiteral(VStr(e
->Name
), ec
.Package
->FindString(*e
->Name
), Loc
);
514 if (IsIntConst() && GetIntConst() == 0) {
515 // "false" or "0" means "empty"
516 // don't warn for `A_CustomRailgun()`, number `0` seems to be a valid value there
518 if ((argnum == 3 || argnum == 4) && VStr::strEquCI(funcName, "A_CustomRailgun")) {
523 ParseWarningAsError((aloc
? *aloc
: Loc
), "`%s` argument #%d should be a string (replaced `0` with empty string); PLEASE, FIX THE CODE!", funcName
, argnum
);
525 VExpression
*enew
= new VStringLiteral(VStr(), ec
.Package
->FindString(""), Loc
);
530 if (IsNoneLiteral()) {
531 VExpression
*enew
= new VStringLiteral("none", ec
.Package
->FindString(""), Loc
);
535 // `A_CustomRailgun()` hex colors
537 if (IsIntConst() && (argnum == 3 || argnum == 4) && VStr::strEquCI(funcName, "A_CustomRailgun")) {
538 VStr s = va("#%06x", GetIntConst()&0xffffff);
539 VExpression *enew = new VStringLiteral(s, ec.Package->FindString(*s), Loc);
548 if (IsDecorateSingleName()) {
549 VDecorateSingleName
*e
= (VDecorateSingleName
*)this;
550 VExpression
*enew
= new VStringLiteral(VStr(e
->Name
), ec
.Package
->FindString(*e
->Name
), Loc
);
552 return enew
->MassageDecorateArg(ec
, invokation
, CallerState
, funcName
, argnum
, destType
, isOptional
, aloc
, massaged
);
556 VStr CName
= GetStrConst(ec
.Package
);
557 VStr CNameStp
= CName
.xstrip();
558 //TLocation ALoc = Loc;
559 if (CNameStp
.isEmpty() || CNameStp
.strEquCI("None") || CNameStp
.strEquCI("nil") || CNameStp
.strEquCI("null")) {
560 //ParseWarning(ALoc, "NONE CLASS `%s`", CName);
561 VExpression
*enew
= new VNoneLiteral(Loc
);
565 VClass
*Cls
= VClass::FindClassNoCase(*CName
);
566 if (!Cls
&& CNameStp
.length() && CNameStp
.length() != CName
.length()) Cls
= VClass::FindClassNoCase(*CNameStp
);
568 if (!destType
.Class
) {
569 ParseWarningAsError((aloc
? *aloc
: Loc
), "No such class `%s` for argument #%d of `%s`", *CName
, argnum
, funcName
);
570 VExpression
*enew
= new VNoneLiteral(Loc
);
574 if (cli_ShowClassRTRouting
> 0) ParseWarning((aloc
? *aloc
: Loc
), "No such class `%s` for argument #%d of `%s` (rt-routed)", *CName
, argnum
, funcName
);
575 //k8: hack it with runtime class searching
577 VExpression *enew = new VNoneLiteral(Loc);
581 //GLog.Logf("********* %s (%s)", *destType.GetName(), (destType.Class ? destType.Class->GetName() : "<none>"));
582 VExpression
*TmpArgs
[2];
583 TmpArgs
[0] = new VClassConstant(destType
.Class
, Loc
);
584 TmpArgs
[1] = new VNameLiteral(VName(*CName
), Loc
);
585 VExpression
*enew
= new VInvocation(nullptr, ec
.SelfClass
->FindMethodChecked("FindClassNoCaseEx"), nullptr, false, false, Loc
, 2, TmpArgs
);
589 if (destType
.Class
&& !Cls
->IsChildOf(destType
.Class
)) {
590 // exception: allow `RandomSpawner` for inventories
591 bool stillError
= true;
592 if (argnum
== 1 && VStr::strEqu("A_GiveInventory", funcName
)) {
593 static VClass
*invCls
= nullptr;
594 if (invCls
== nullptr) { invCls
= VClass::FindClass("Inventory"); vassert(invCls
); }
595 static VClass
*rndCls
= nullptr;
596 if (rndCls
== nullptr) { rndCls
= VClass::FindClass("RandomSpawner"); vassert(rndCls
); }
597 if (destType
.Class
->IsChildOf(invCls
) && Cls
->IsChildOf(rndCls
)) {
599 ParseWarningAsError((aloc
? *aloc
: Loc
), "using class `%s` (RandomSpawner) for argument #%d of `%s`", Cls
->GetName(), argnum
, funcName
);
603 ParseWarningAsError((aloc
? *aloc
: Loc
), "Class `%s` is not a descendant of `%s` for argument #%d of `%s`", *CName
, destType
.Class
->GetName(), argnum
, funcName
);
604 //for (VClass *cc = Cls; cc; cc = cc->GetSuperClass()) GLog.Logf(NAME_Debug, " %s", cc->GetName());
605 VExpression
*enew
= new VNoneLiteral(Loc
);
610 VExpression
*enew
= new VClassConstant(Cls
, Loc
);
617 if (IsIntConst() && GetIntConst() == 0) {
618 // "false" or "0" means "empty"
619 ParseWarningAsError((aloc
? *aloc
: Loc
), "`%s` argument #%d should be class (replaced with `none`); PLEASE, FIX THE CODE!", funcName
, argnum
);
620 VExpression
*enew
= new VNoneLiteral(Loc
);
627 // some very bright person does this: `A_JumpIfTargetInLOS("1")` -- brilliant!
629 if (IsStrConst() || IsDecorateSingleName()) {
630 const TLocation ALoc
= Loc
;
632 if (IsStrConst()) lblName
= GetStrConst(ec
.Package
); else { VDecorateSingleName
*e
= (VDecorateSingleName
*)this; lblName
= VStr(e
->Name
); }
633 lblName
= lblName
.xstrip();
634 // any special names will be resolved by rt-router
635 // try to convert a string to a number
637 // string-as-a-number?
638 if (lblName
.convertInt(&lbl
)) {
639 // try to find the corresponding label
640 bool useNumber
= true;
642 VStateLabel
*StLbl
= ec
.SelfClass
->FindStateLabel(VName(*lblName
), NAME_None
, true);
644 ParseWarningAsError((aloc
? *aloc
: ALoc
), "do not use numeric labels in `%s` (argument #%d, label '%s')", funcName
, argnum
, *lblName
);
650 ParseError((aloc
? *aloc
: ALoc
), "Negative state jumps are not allowed (function `%s`, argument #%d)", funcName
, argnum
);
654 ParseWarningAsError((aloc
? *aloc
: ALoc
), "`%s` argument #%d should be number %d instead of string \"%s\"; PLEASE, FIX THE CODE!", funcName
, argnum
, lbl
, *lblName
.quote());
655 VExpression
*TmpArgs
[2];
656 TmpArgs
[0] = new VIntLiteral(lbl
, ALoc
);
657 TmpArgs
[1] = new VIntLiteral(GetRTRouteUId(), ALoc
);
659 return new VInvocation(nullptr, ec
.SelfClass
->FindMethodChecked("FindJumpStateOfs"), nullptr, false, false, ALoc
, 2, TmpArgs
);
662 //k8: don't resolve any "::" here, our dynamic resolver will do this for us
663 // it's a virtual state jump
664 //ParseWarning(Args[i]->Loc, "***VSJMP `%s`: <%s>", Func->GetName(), *lblName);
665 VExpression
*TmpArgs
[2];
666 //FIXME: support user arrays here!
667 if (lblName
.startsWithCI("user_")) {
668 //ParseWarningAsError(ALoc, "uservars for state jumps are not supported (arg #%d for action `%s`). please, don't do this, this is a misfeature!", argnum, funcName);
669 // uservar; route with `FindJumpStateOfs()`
670 //GCon->Logf(NAME_Debug, "DECORATE: %s: routing uservar (%s) state with `FindJumpStateOfs()`", *ALoc.toStringNoCol(), *lblName);
672 TmpArgs
[0] = new VDecorateUserVar(lblName
, ALoc
);
673 TmpArgs
[1] = new VIntLiteral(GetRTRouteUId(), ALoc
);
674 return new VInvocation(nullptr, ec
.SelfClass
->FindMethodChecked("FindJumpStateOfs"), nullptr, false, false, ALoc
, 2, TmpArgs
);
676 // final state FindJumpState (name Label, int uniqueId);
679 TmpArgs
[0] = new VNameLiteral(VName(*lblName
), ALoc
);
680 TmpArgs
[1] = new VIntLiteral(GetRTRouteUId(), ALoc
);
681 return new VInvocation(nullptr, ec
.SelfClass
->FindMethodChecked("FindJumpState"), nullptr, false, false, ALoc
, 2, TmpArgs
);
686 const TLocation ALoc
= Loc
;
687 int Offs
= GetIntConst();
689 ParseError((aloc
? *aloc
: ALoc
), "Negative state jumps are not allowed (function `%s`, argument #%d)", funcName
, argnum
);
693 VExpression
*TmpArgs
[2];
695 TmpArgs
[1] = new VIntLiteral(GetRTRouteUId(), ALoc
);
696 return new VInvocation(nullptr, ec
.SelfClass
->FindMethodChecked("FindJumpStateOfs"), nullptr, false, false, ALoc
, 2, TmpArgs
);
698 // none as literal? this usually means "no jump"
699 // but let rt-router decide
700 if (IsNoneLiteral()) {
701 const TLocation ALoc
= Loc
;
702 VExpression
*TmpArgs
[2];
703 // final state FindJumpState (name Label, int uniqueId);
705 TmpArgs
[0] = new VNameLiteral(VName("none"), ALoc
);
706 TmpArgs
[1] = new VIntLiteral(GetRTRouteUId(), ALoc
);
707 return new VInvocation(nullptr, ec
.SelfClass
->FindMethodChecked("FindJumpState"), nullptr, false, false, ALoc
, 2, TmpArgs
);
709 // support things like `A_Jump(n, func())`
710 if (Type
.Type
!= TYPE_State
) {
711 const TLocation ALoc
= Loc
;
712 //GCon->Logf("A_Jump: type=%s; expr=<%s>", *lbl->Type.GetName(), *lbl->toString());
714 VExpression
*lx
= this->SyntaxCopy()->Resolve(ec
);
716 if (lx
->Type
.Type
!= TYPE_State
) {
717 const bool isGoodType
= (lx
->Type
.Type
== TYPE_Int
|| lx
->Type
.Type
== TYPE_Byte
|| lx
->Type
.Type
== TYPE_Bool
|| lx
->Type
.Type
== TYPE_Float
);
719 //GCon->Logf("A_Jump: type=%s; expr=<%s>", *lbl->Type.GetName(), *lbl->toString());
720 VExpression
*TmpArgs
[2];
721 TmpArgs
[0] = this->SyntaxCopy();
722 if (lx
->Type
.Type
== TYPE_Float
) {
723 ParseWarningAsError(ALoc
, "jump offset argument #%d for `%s` should be integer, not float! PLEASE, FIX THE CODE!", argnum
, funcName
);
724 TmpArgs
[0] = new VScalarToInt(TmpArgs
[0], false); // not resolved
726 TmpArgs
[1] = new VIntLiteral(GetRTRouteUId(), ALoc
);
727 VExpression
*eres
= new VInvocation(nullptr, ec
.SelfClass
->FindMethodChecked("FindJumpStateOfs"), nullptr, false, false, ALoc
, 2, TmpArgs
);
728 //GCon->Logf(" NEW: type=%s; expr=<%s>", *lbl->Type.GetName(), *lbl->toString());
738 if (massaged
) *massaged
= false;
743 //==========================================================================
745 // VDecorateInvocation::VDecorateInvocation
747 //==========================================================================
748 VDecorateInvocation::VDecorateInvocation (VName AName
, const TLocation
&ALoc
, int ANumArgs
, VExpression
**AArgs
)
753 , CallerState(nullptr)
755 memset(Args
, 0, sizeof(Args
));
756 for (int i
= 0; i
< NumArgs
; ++i
) Args
[i
] = AArgs
[i
];
760 //==========================================================================
762 // VDecorateInvocation::VDecorateInvocation
764 //==========================================================================
765 VDecorateInvocation::VDecorateInvocation (VMethod
*AFunc
, VName AName
, const TLocation
&ALoc
, int ANumArgs
, VExpression
*AArgs
[])
770 , CallerState(nullptr)
772 memset(Args
, 0, sizeof(Args
));
773 for (int i
= 0; i
< NumArgs
; ++i
) Args
[i
] = AArgs
[i
];
777 //==========================================================================
779 // VDecorateInvocation::~VDecorateInvocation
781 //==========================================================================
782 VDecorateInvocation::~VDecorateInvocation () {
783 for (int i
= 0; i
< NumArgs
; ++i
) {
792 //==========================================================================
794 // VDecorateInvocation::HasSideEffects
796 //==========================================================================
797 bool VDecorateInvocation::HasSideEffects () {
802 //==========================================================================
804 // VDecorateInvocation::VisitChildren
806 //==========================================================================
807 void VDecorateInvocation::VisitChildren (VExprVisitor
*v
) {
808 for (int f
= 0; !v
->stopIt
&& f
< NumArgs
; f
+= 1) {
809 if (Args
[f
]) Args
[f
]->Visit(v
);
814 //==========================================================================
816 // VDecorateInvocation::toString
818 //==========================================================================
819 VStr
VDecorateInvocation::toString () const {
822 for (int f
= 0; f
< NumArgs
; ++f
) {
823 if (f
!= 0) res
+= ", ";
824 if (Args
[f
]) res
+= Args
[f
]->toString(); else res
+= "default";
831 //==========================================================================
833 // VDecorateInvocation::SyntaxCopy
835 //==========================================================================
836 VExpression
*VDecorateInvocation::SyntaxCopy () {
837 auto res
= new VDecorateInvocation();
843 //==========================================================================
845 // VDecorateInvocation::DoSyntaxCopyTo
847 //==========================================================================
848 void VDecorateInvocation::DoSyntaxCopyTo (VExpression
*e
) {
849 VExpression::DoSyntaxCopyTo(e
);
850 auto res
= (VDecorateInvocation
*)e
;
851 memset(res
->Args
, 0, sizeof(res
->Args
));
853 res
->NumArgs
= NumArgs
;
854 for (int f
= 0; f
< NumArgs
; ++f
) res
->Args
[f
] = (Args
[f
] ? Args
[f
]->SyntaxCopy() : nullptr);
858 //==========================================================================
860 // VDecorateInvocation::HasFloatArg
862 //==========================================================================
863 bool VDecorateInvocation::HasFloatArg (VEmitContext
&ec
) {
864 for (int i
= 0; i
< NumArgs
; ++i
) {
865 if (!Args
[i
] || Args
[i
]->IsDefaultArg()) continue;
867 VExpression
*e
= Args
[i
]->SyntaxCopy()->Resolve(ec
);
868 const bool isFloat
= (e
&& e
->Type
.Type
== TYPE_Float
);
870 if (isFloat
) return true;
876 //==========================================================================
878 // VDecorateInvocation::DoResolve
880 //==========================================================================
881 void VDecorateInvocation::FixKnownShit (VEmitContext
&ec
, const char *FuncName
) {
882 // a lot of dumbfucks cannot into wiki
883 if (NumArgs
== 4 && Args
[3] && VStr::strEquCI(FuncName
, "A_CustomMeleeAttack")) {
887 e
= Args
[3]->SyntaxCopy()->Resolve(ec
);
889 bool doit
= (e
&& e
->Type
.CheckMatch(false/*asRef*/, e
->Loc
, VFieldType(TYPE_Bool
), false/*raiseError*/));
892 // missing argument; morons!
893 ParseWarningAsError(Loc
, "`%s` is missing argument `missound`; who cares about stupid wikis?!", FuncName
);
894 // insert dummy empty string argument
895 for (int f
= 3; f
>= 2; --f
) Args
[f
+1] = Args
[f
];
897 int val
= ec
.Package
->FindString(*ns
);
898 Args
[2] = new VStringLiteral(ns
, val
, Args
[2]->Loc
);
900 //GCon->Logf(NAME_Debug, "*** %s", *this->toString());
907 //==========================================================================
909 // VDecorateInvocation::DoResolve
911 //==========================================================================
912 VExpression
*VDecorateInvocation::DoResolve (VEmitContext
&ec
) {
914 //FIXME: sanitize this!
917 if (VStr::strEquCI(*Name
, "va") ||
918 VStr::strEquCI(*Name
, "fmin") || VStr::strEquCI(*Name
, "fmax") ||
919 VStr::strEquCI(*Name
, "fclamp") || VStr::strEquCI(*Name
, "fabs"))
921 Name
= Name
.GetLower();
922 M
= ec
.SelfClass
->FindMethod(Name
);
923 } else if (VStr::strEquCI(*Name
, "min") || VStr::strEquCI(*Name
, "max") ||
924 VStr::strEquCI(*Name
, "clamp") || VStr::strEquCI(*Name
, "abs"))
926 // determine if we want an integer one
927 if (HasFloatArg(ec
)) {
928 VStr fname
= VStr("f")+(*Name
);
929 Name
= VName(*fname
, VName::AddLower
);
931 Name
= Name
.GetLower();
933 M
= ec
.SelfClass
->FindMethod(Name
);
935 M
= ec
.SelfClass
->FindDecorateStateAction(*Name
);
940 if (M
->Flags
&FUNC_Iterator
) {
941 ParseError(Loc
, "Iterator methods can only be used in foreach statements (method '%s', class '%s')", *Name
, *ec
.SelfClass
->GetFullName());
946 FixKnownShit(ec
, *Name
);
948 // rebuild min/max with more than two arguments into recursive expression (for now)
949 //FIXME: generate better VM code for this
950 if (NumArgs
> 2 && (VStr::strEquCI(*Name
, "fmin") || VStr::strEquCI(*Name
, "fmax") ||
951 VStr::strEquCI(*Name
, "min") || VStr::strEquCI(*Name
, "max")))
953 VExpression
*e
= new VInvocation(nullptr, M
, nullptr, false, false, Loc
, 2, Args
+NumArgs
-2);
954 if (Func
&& CallerState
) ((VInvocation
*)e
)->CallerState
= CallerState
;
955 int currlhs
= NumArgs
-3;
956 for (; currlhs
>= 0; --currlhs
) {
957 VExpression
*newargs
[2];
958 newargs
[0] = Args
[currlhs
];
960 e
= new VInvocation(nullptr, M
, nullptr, false, false, Loc
, 2, newargs
);
961 if (Func
&& CallerState
) ((VInvocation
*)e
)->CallerState
= CallerState
;
963 //GCon->Logf(NAME_Debug, "::: %s", *e->toString()); abort();
966 return e
->Resolve(ec
);
969 VExpression
*e
= new VInvocation(nullptr, M
, nullptr, false, false, Loc
, NumArgs
, Args
);
970 if (Func
&& CallerState
) ((VInvocation
*)e
)->CallerState
= CallerState
;
973 return e
->Resolve(ec
);
978 if (VStr::strEquCI(*Name
, "A_WeaponOffset") || VStr::strEquCI(*Name
, "A_Overlay")) {
979 ParseWarning(Loc
, "Unknown decorate action `%s` in class `%s`", *Name
, *ec
.SelfClass
->GetFullName());
980 VExpression
*e
= new VIntLiteral(0, Loc
);
982 return e
->Resolve(ec
);
984 ParseError(Loc
, "Unknown decorate action `%s` in class `%s`", *Name
, *ec
.SelfClass
->GetFullName());
986 ParseError(Loc
, "Unknown decorate action `%s`", *Name
);
995 //==========================================================================
997 // VDecorateInvocation::Emit
999 //==========================================================================
1000 void VDecorateInvocation::Emit (VEmitContext
&) {
1001 ParseError(Loc
, "Should not happen");
1005 //==========================================================================
1007 // VDecorateUserVar::VDecorateUserVar
1009 //==========================================================================
1010 VDecorateUserVar::VDecorateUserVar (VStr afldname
, const TLocation
&aloc
)
1018 //==========================================================================
1020 // VDecorateUserVar::VDecorateUserVar
1022 //==========================================================================
1023 VDecorateUserVar::VDecorateUserVar (VStr afldname
, VExpression
*aindex
, const TLocation
&aloc
)
1031 //==========================================================================
1033 // VDecorateUserVar::~VDecorateUserVar
1035 //==========================================================================
1036 VDecorateUserVar::~VDecorateUserVar () {
1037 delete index
; index
= nullptr;
1038 //fldname.clear(); // just4fun
1042 //==========================================================================
1044 // VDecorateUserVar::HasSideEffects
1046 //==========================================================================
1047 bool VDecorateUserVar::HasSideEffects () {
1048 return (index
&& index
->HasSideEffects());
1052 //==========================================================================
1054 // VDecorateUserVar::VisitChildren
1056 //==========================================================================
1057 void VDecorateUserVar::VisitChildren (VExprVisitor
*v
) {
1058 if (!v
->stopIt
&& index
) index
->Visit(v
);
1062 //==========================================================================
1064 // VDecorateUserVar::SyntaxCopy
1066 //==========================================================================
1067 VExpression
*VDecorateUserVar::SyntaxCopy () {
1068 auto res
= new VDecorateUserVar();
1069 DoSyntaxCopyTo(res
);
1074 //==========================================================================
1076 // VDecorateUserVar::DoSyntaxCopyTo
1078 //==========================================================================
1079 void VDecorateUserVar::DoSyntaxCopyTo (VExpression
*e
) {
1080 VExpression::DoSyntaxCopyTo(e
);
1081 auto res
= (VDecorateUserVar
*)e
;
1082 res
->fldname
= fldname
;
1083 res
->index
= (index
? index
->SyntaxCopy() : nullptr);
1087 //==========================================================================
1089 // VDecorateUserVar::DoResolve
1091 //==========================================================================
1092 VExpression
*VDecorateUserVar::DoResolve (VEmitContext
&ec
) {
1093 if (!ec
.SelfClass
) Sys_Error("VDecorateUserVar::DoResolve: internal compiler error");
1094 //GLog.Logf(NAME_Debug, "%s: user field '%s'", *Loc.toString(), *fldname);
1095 VName fldnamelo
= (fldname
.isEmpty() ? NAME_None
: VName(*fldname
, VName::AddLower
));
1096 VName fldn
= ec
.SelfClass
->FindDecorateStateFieldTrans(fldnamelo
);
1097 if (fldn
== NAME_None
) {
1098 ParseError(Loc
, "field `%s` is not found in class `%s`", *fldname
, *ec
.SelfClass
->GetFullName());
1102 // use checked field access
1103 VField
*fld
= ec
.SelfClass
->FindField(fldn
);
1105 ParseError(Loc
, "field `%s` => `%s` is not found in class `%s`", *fldname
, *fldn
, *ec
.SelfClass
->GetFullName());
1109 VFieldType ftype
= fld
->Type
;
1110 if (ftype
.Type
== TYPE_Array
) ftype
= ftype
.GetArrayInnerType();
1111 VName mtname
= (ftype
.Type
== TYPE_Int
? "_get_user_var_int" : "_get_user_var_float");
1112 VMethod
*mt
= ec
.SelfClass
->FindMethod(mtname
);
1114 ParseError(Loc
, "internal method `%s` not found in class `%s`", *mtname
, *ec
.SelfClass
->GetFullName());
1118 VExpression
*args
[2];
1119 args
[0] = new VNameLiteral(fldn
, Loc
);
1121 VExpression
*e
= new VInvocation(nullptr, mt
, nullptr, false, false, Loc
, 2, args
);
1124 return e
->Resolve(ec
);
1128 //==========================================================================
1130 // VDecorateUserVar::ResolveCompleteAssign
1132 // this will be called before actual assign resolving
1133 // return `nullptr` to indicate error, or consume `val` and set `resolved`
1134 // to `true` if resolved
1135 // if `nullptr` is returned, both `this` and `val` should be destroyed
1137 //==========================================================================
1138 VExpression
*VDecorateUserVar::ResolveCompleteAssign (VEmitContext
&ec
, VExpression
*val
, bool &resolved
) {
1139 if (!ec
.SelfClass
) Sys_Error("VDecorateUserVar::DoResolve: internal compiler error");
1140 if (!val
) { delete this; return nullptr; }
1141 resolved
= true; // anyway
1142 VName fldnamelo
= (fldname
.isEmpty() ? NAME_None
: VName(*fldname
, VName::AddLower
));
1143 VName fldn
= ec
.SelfClass
->FindDecorateStateFieldTrans(fldnamelo
);
1144 if (fldn
== NAME_None
) {
1145 ParseError(Loc
, "field `%s` is not found in class `%s`", *fldname
, *ec
.SelfClass
->GetFullName());
1150 VField
*fld
= ec
.SelfClass
->FindField(fldn
);
1152 ParseError(Loc
, "field `%s` => `%s` is not found in class `%s`", *fldname
, *fldn
, *ec
.SelfClass
->GetFullName());
1157 VFieldType ftype
= fld
->Type
;
1158 if (ftype
.Type
== TYPE_Array
) ftype
= ftype
.GetArrayInnerType();
1159 VName mtname
= (ftype
.Type
== TYPE_Int
? "_set_user_var_int" : "_set_user_var_float");
1160 VMethod
*mt
= ec
.SelfClass
->FindMethod(mtname
);
1162 ParseError(Loc
, "internal method `%s` not found in class `%s`", *mtname
, *ec
.SelfClass
->GetFullName());
1167 VExpression
*args
[3];
1168 args
[0] = new VNameLiteral(fldn
, Loc
);
1171 VExpression
*e
= new VInvocation(nullptr, mt
, nullptr, false, false, Loc
, 3, args
);
1173 //GLog.Logf(NAME_Debug, "**** %s: assign user field '%s' (%s : %s)", *Loc.toString(), *fldname, *fldnamelo, *fldn);
1175 return e
->Resolve(ec
);
1179 //==========================================================================
1181 // VDecorateUserVar::Emit
1183 //==========================================================================
1184 void VDecorateUserVar::Emit (VEmitContext
&/*ec*/) {
1185 Sys_Error("VDecorateUserVar::Emit: the thing that should not be!");
1189 //==========================================================================
1191 // VDecorateUserVar::toString
1193 //==========================================================================
1194 VStr
VDecorateUserVar::toString () const {
1195 VStr res
= VStr(fldname
);
1196 if (index
) { res
+= "["; res
+= index
->toString(); res
+= "]"; }
1201 //==========================================================================
1203 // VDecorateUserVar::IsDecorateUserVar
1205 //==========================================================================
1206 bool VDecorateUserVar::IsDecorateUserVar () const {
1211 //==========================================================================
1213 // VDecorateSingleName::VDecorateSingleName
1215 //==========================================================================
1216 VDecorateSingleName::VDecorateSingleName (VStr AName
, const TLocation
&ALoc
)
1221 if (decoIsLocalName(AName
)) {
1223 //GCon->Logf(NAME_Debug, "%s: LOCAL: `%s`", *Loc.toStringNoCol(), *AName);
1225 localAccess
= false;
1226 //GCon->Logf(NAME_Debug, "%s: USER: `%s`", *Loc.toStringNoCol(), *AName);
1231 //==========================================================================
1233 // VDecorateSingleName::VDecorateSingleName
1235 //==========================================================================
1236 VDecorateSingleName::VDecorateSingleName () {
1240 //==========================================================================
1242 // VDecorateSingleName::HasSideEffects
1244 //==========================================================================
1245 bool VDecorateSingleName::HasSideEffects () {
1250 //==========================================================================
1252 // VDecorateSingleName::VisitChildren
1254 //==========================================================================
1255 void VDecorateSingleName::VisitChildren (VExprVisitor
*v
) {
1259 //==========================================================================
1261 // VDecorateSingleName::toString
1263 //==========================================================================
1264 VStr
VDecorateSingleName::toString () const {
1269 //==========================================================================
1271 // VDecorateSingleName::SyntaxCopy
1273 //==========================================================================
1274 VExpression
*VDecorateSingleName::SyntaxCopy () {
1275 auto res
= new VDecorateSingleName();
1276 DoSyntaxCopyTo(res
);
1281 //==========================================================================
1283 // VDecorateSingleName::DoSyntaxCopyTo
1285 //==========================================================================
1286 void VDecorateSingleName::DoSyntaxCopyTo (VExpression
*e
) {
1287 VExpression::DoSyntaxCopyTo(e
);
1288 auto res
= (VDecorateSingleName
*)e
;
1290 res
->localAccess
= localAccess
;
1294 //==========================================================================
1296 // VDecorateSingleName::DoResolve
1298 //==========================================================================
1299 VExpression
*VDecorateSingleName::DoResolve (VEmitContext
&ec
) {
1302 VExpression
*e
= new VSingleName(VName(*Name
, VName::AddLower
), Loc
);
1304 return e
->Resolve(ec
);
1308 if (VStr::startsWithCI(*Name
, "user_")) {
1309 VExpression
*e
= new VDecorateUserVar(Name
, Loc
);
1311 return e
->Resolve(ec
);
1314 //GLog.Logf(NAME_Debug, "%s: field '%s'", *Loc.toString(), *Name);
1316 VName ExtName
= va("decorate_%s", *Name
.toLowerCase());
1318 // prefixed constant
1319 VConstant
*Const
= ec
.SelfClass
->FindConstant(ExtName
);
1321 VExpression
*e
= new VConstantValue(Const
, Loc
);
1323 return e
->Resolve(ec
);
1326 // prefixed property
1327 VProperty
*Prop
= ec
.SelfClass
->FindDecorateProperty(Name
);
1329 if (!Prop
->GetFunc
) {
1330 ParseError(Loc
, "Property `%s` cannot be read", *Name
);
1334 VExpression
*e
= new VInvocation(nullptr, Prop
->GetFunc
, nullptr, false, false, Loc
, 0, nullptr);
1336 return e
->Resolve(ec
);
1340 // non-prefixed constant
1341 // look only for constants defined in DECORATE scripts (and in the current class)
1343 VConstant
*Const
= nullptr;
1345 Const
= ec
.SelfClass
->FindDecorateConstant(Name
);
1347 // try some other classes
1348 if (!Const
&& ActorClass
) Const
= ActorClass
->FindDecorateConstant(Name
);
1349 if (!Const
&& FakeInventoryClass
) Const
= FakeInventoryClass
->FindDecorateConstant(Name
);
1350 if (!Const
&& InventoryClass
) Const
= InventoryClass
->FindDecorateConstant(Name
);
1351 if (!Const
&& AmmoClass
) Const
= AmmoClass
->FindDecorateConstant(Name
);
1352 if (!Const
&& WeaponClass
) Const
= WeaponClass
->FindDecorateConstant(Name
);
1353 if (!Const
&& PlayerPawnClass
) Const
= PlayerPawnClass
->FindDecorateConstant(Name
);
1356 VName CheckName
= VName(*Name
, VName::AddLower
);
1357 Const
= (ec
.SelfClass
? ec
.SelfClass
->FindPackageConstant(ec
.Package
, CheckName
) : nullptr);
1358 if (!Const
) Const
= ec
.Package
->FindConstant(CheckName
);
1361 VExpression
*e
= new VConstantValue(Const
, Loc
);
1363 return e
->Resolve(ec
);
1367 // paren-less decorate method?
1369 VMethod
*M
= ec
.SelfClass
->FindDecorateStateAction(*Name
);
1371 VExpression
*e
= new VDecorateInvocation(M
, *Name
, Loc
, 0, nullptr);
1373 return e
->Resolve(ec
);
1377 ParseError(Loc
, "Illegal expression identifier `%s`", *Name
);
1383 //==========================================================================
1385 // VDecorateSingleName::Emit
1387 //==========================================================================
1388 void VDecorateSingleName::Emit (VEmitContext
&) {
1389 ParseError(Loc
, "Should not happen");
1393 //==========================================================================
1395 // VDecorateSingleName::IsDecorateSingleName
1397 //==========================================================================
1398 bool VDecorateSingleName::IsDecorateSingleName () const {
1404 //==========================================================================
1406 // VDecorateAJump::VDecorateAJump
1408 //==========================================================================
1409 VDecorateAJump::VDecorateAJump (const TLocation
&aloc
)
1417 , CallerState(nullptr)
1422 //==========================================================================
1424 // VDecorateAJump::~VDecorateAJump
1426 //==========================================================================
1427 VDecorateAJump::~VDecorateAJump () {
1428 //delete xstc; xstc = nullptr;
1429 //delete xass; xass = nullptr;
1430 delete crnd0
; crnd0
= nullptr;
1431 delete crnd1
; crnd1
= nullptr;
1432 delete prob
; prob
= nullptr;
1433 for (int f
= labels
.length()-1; f
>= 0; --f
) delete labels
[f
];
1435 CallerState
= nullptr;
1439 //==========================================================================
1441 // VDecorateAJump::HasSideEffects
1443 //==========================================================================
1444 bool VDecorateAJump::HasSideEffects () {
1449 //==========================================================================
1451 // VDecorateAJump::VisitChildren
1453 //==========================================================================
1454 void VDecorateAJump::VisitChildren (VExprVisitor
*v
) {
1455 if (!v
->stopIt
&& crnd0
) crnd0
->Visit(v
);
1456 if (!v
->stopIt
&& crnd1
) crnd1
->Visit(v
);
1457 if (!v
->stopIt
&& prob
) prob
->Visit(v
);
1458 for (int f
= 0; !v
->stopIt
&& f
< labels
.length(); f
+= 1) {
1459 labels
[f
]->Visit(v
);
1464 //==========================================================================
1466 // VDecorateAJump::SyntaxCopy
1468 //==========================================================================
1469 VExpression
*VDecorateAJump::SyntaxCopy () {
1470 auto res
= new VDecorateAJump();
1471 DoSyntaxCopyTo(res
);
1476 //==========================================================================
1478 // VDecorateAJump::DoSyntaxCopyTo
1480 //==========================================================================
1481 void VDecorateAJump::DoSyntaxCopyTo (VExpression
*e
) {
1482 VExpression::DoSyntaxCopyTo(e
);
1483 auto res
= (VDecorateAJump
*)e
;
1484 res
->prob
= (prob
? prob
->SyntaxCopy() : nullptr);
1485 res
->labels
.setLength(labels
.length());
1486 for (int f
= 0; f
< labels
.length(); ++f
) {
1487 res
->labels
[f
] = (labels
[f
] ? labels
[f
]->SyntaxCopy() : nullptr);
1489 res
->CallerState
= CallerState
;
1493 //==========================================================================
1495 // VDecorateAJump::DoResolve
1497 //==========================================================================
1498 VExpression
*VDecorateAJump::DoResolve (VEmitContext
&ec
) {
1499 if (!ec
.SelfClass
) Sys_Error("VDecorateAJump::DoResolve: internal compiler error");
1501 //AutoCopy probcopy(op);
1503 if (labels
.length() > 255) {
1504 ParseError(Loc
, "%d labels in `A_Jump` -- are you nuts?!", labels
.length());
1509 if (!prob
) { delete this; return nullptr; }
1511 prob
= prob
->Resolve(ec
);
1512 if (!prob
) { delete this; return nullptr; }
1514 if (prob
->Type
.Type
!= TYPE_Int
&& prob
->Type
.Type
!= TYPE_Byte
) {
1515 ParseError(Loc
, "`A_Jump` argument #1 should be integer, not `%s`", *prob
->Type
.GetName());
1520 for (int lbidx
= 0; lbidx
< labels
.length(); ++lbidx
) {
1521 VExpression
*lbl
= labels
[lbidx
];
1524 ParseError(Loc
, "`A_Jump` cannot have default arguments");
1529 bool massaged
= false;
1530 lbl
= lbl
->MassageDecorateArg(ec
, nullptr, CallerState
, "A_Jump", lbidx
+2, VFieldType(TYPE_State
), false/*not optional*/, nullptr, &massaged
);
1531 if (!lbl
) { delete this; return nullptr; } // some error
1533 lbl
= new VCastOrInvocation("DoJump", Loc
, 1, &lbl
);
1534 lbl
= new VDropResult(lbl
);
1536 labels
[lbidx
] = lbl
->Resolve(ec
);
1537 if (!labels
[lbidx
]) { delete this; return nullptr; }
1540 /* generate this code:
1542 if (prob > 255 || P_Random() < prob) {
1543 switch (P_Random()%label.length()) {
1544 case n: do_jump_to_label_n;
1548 // this is not needed, because `DoJump()` does it for us
1549 //if (XLevel.StateCall) XLevel.StateCall->Result = false;
1551 do it by allocate local array for labels, populate it, and generate code
1555 // create `XLevel.StateCall` access expression
1558 VExpression *xlvl = new VSingleName("XLevel", Loc);
1559 xstc = new VDotField(xlvl, "StateCall", Loc);
1561 // XLevel.StateCall->Result
1562 VExpression *xres = new VPointerField(xstc->SyntaxCopy(), "Result", Loc);
1563 // XLevel.StateCall->Result = false
1564 xass = new VAssignment(VAssignment::Assign, xres, new VIntLiteral(0, Loc), Loc);
1566 // call to `P_Random()`
1567 crnd0
= new VCastOrInvocation("P_Random", Loc
, 0, nullptr);
1568 crnd1
= crnd0
->SyntaxCopy();
1570 // now resolve all generated expressions
1571 //xstc = xstc->ResolveBoolean(ec);
1572 //if (!xstc) { delete this; return nullptr; }
1574 //xass = xass->Resolve(ec);
1575 //if (!xass) { delete this; return nullptr; }
1576 //vassert(xass->Type.Type == TYPE_Void);
1578 crnd0
= crnd0
->Resolve(ec
);
1579 if (!crnd0
) { delete this; return nullptr; }
1581 crnd1
= crnd1
->Resolve(ec
);
1582 if (!crnd1
) { delete this; return nullptr; }
1584 if (crnd0
->Type
.Type
!= TYPE_Int
&& crnd0
->Type
.Type
!= TYPE_Byte
) {
1585 ParseError(Loc
, "`P_Random()` should return integer, not `%s`", *crnd0
->Type
.GetName());
1590 if (labels
.length() == 0) {
1591 ParseWarning(Loc
, "this `A_Jump` is never taken");
1592 } else if (labels
.length() == 1 && prob
->IsIntConst() && prob
->GetIntConst() > 255) {
1593 //k8: this is not a bug; usually, this is used to dynamically dispatch control
1594 // to a label that can be defined in subclass, for example.
1595 // note that `Goto` cannod do that.
1596 //ParseWarning(Loc, "this `A_Jump` is uncoditional; this is probably a bug (replace it with `Goto` if it isn't)");
1599 Type
.Type
= TYPE_Void
;
1605 //==========================================================================
1607 // VDecorateAJump::Emit
1609 //==========================================================================
1610 void VDecorateAJump::Emit (VEmitContext
&ec
) {
1611 /* generate this code:
1612 if (prob <= 0) goto end;
1613 if (prob > 255) goto doit;
1614 if (P_Random() >= prob) goto end;
1616 switch (P_Random()%label.length()) {
1617 case n: do_jump_to_label_n;
1620 if (XLevel.StateCall) XLevel.StateCall->Result = false;
1622 do it by allocate local array for labels, populate it, and generate code
1626 EmitCheckResolved(ec
);
1628 VLabel endTarget
= ec
.DefineLabel();
1629 VLabel doitTarget
= ec
.DefineLabel();
1631 bool doDrop
; // do we need to drop `prob` at the end?
1633 if (prob
->IsIntConst() && prob
->GetIntConst() <= 0) {
1634 // jump is never taken
1636 ec
.AddStatement(OPC_Goto
, endTarget
, Loc
);
1637 } else if (prob
->IsIntConst() && prob
->GetIntConst() > 255) {
1639 // ...and check nothing, jump is always taken
1642 prob
->Emit(ec
); // prob
1644 if (!prob
->IsIntConst()) {
1645 //if (prob <= 0) goto end;
1646 ec
.AddStatement(OPC_DupPOD
, Loc
); // prob, prob
1647 ec
.AddStatement(OPC_PushNumber
, 0, Loc
); // prob, prob, 0
1648 ec
.AddStatement(OPC_LessEquals
, Loc
); // prob, flag
1649 ec
.AddStatement(OPC_IfGoto
, endTarget
, Loc
); // prob
1651 //if (prob > 255) goto doit;
1652 ec
.AddStatement(OPC_DupPOD
, Loc
); // prob, prob
1653 ec
.AddStatement(OPC_PushNumber
, 255, Loc
); // prob, prob, 255
1654 ec
.AddStatement(OPC_GreaterEquals
, Loc
); // prob, flag
1655 ec
.AddStatement(OPC_IfGoto
, doitTarget
, Loc
); // prob
1657 ec
.AddStatement(OPC_DupPOD
, Loc
); // prob, prob
1659 // if we know prob value, we can omit most checks, and
1660 // we don't need to keep `prob` on the stack
1664 //if (P_Random() >= prob) goto end;
1665 //equals to: if (prob < P_Random()) goto end;
1666 crnd0
->Emit(ec
); // prob, prob, prand
1667 ec
.AddStatement(OPC_Less
, Loc
); // prob, flag
1668 ec
.AddStatement(OPC_IfGoto
, endTarget
, Loc
); // prob
1671 ec
.MarkLabel(doitTarget
); // prob
1673 if (labels
.length() == 1) {
1674 labels
[0]->Emit(ec
); // prob
1675 } else if (labels
.length() > 0) {
1676 // P_Random()%label.length()
1677 crnd1
->Emit(ec
); // prob, rnd
1678 ec
.AddStatement(OPC_PushNumber
, labels
.length(), Loc
); // prob, rnd, labels.length()
1679 ec
.AddStatement(OPC_Modulus
, Loc
); // prob, lblidx
1681 TArray
<VLabel
> addrs
;
1682 addrs
.setLength(labels
.length());
1683 for (int lidx
= 0; lidx
< labels
.length(); ++lidx
) addrs
[lidx
] = ec
.DefineLabel();
1684 for (int lidx
= 0; lidx
< labels
.length(); ++lidx
) {
1685 if (lidx
>= 0 && lidx
< 256) {
1686 ec
.AddStatement(OPC_CaseGotoB
, lidx
, addrs
[lidx
], Loc
);
1687 }/* else if (lidx >= MIN_VINT16 && lidx < MAX_VINT16) {
1688 ec.AddStatement(OPC_CaseGotoS, lidx, addrs[lidx], Loc);
1690 ec
.AddStatement(OPC_CaseGoto
, lidx
, addrs
[lidx
], Loc
);
1693 // just in case (and for optimiser)
1694 ec
.AddStatement(OPC_DropPOD
, Loc
); // prob (lidx dropped)
1695 ec
.AddStatement(OPC_Goto
, endTarget
, Loc
); // prob
1697 // now generate label jump code
1698 for (int lidx
= 0; lidx
< labels
.length(); ++lidx
) {
1699 ec
.MarkLabel(addrs
[lidx
]); // prob
1700 labels
[lidx
]->Emit(ec
); // prob
1701 if (lidx
!= labels
.length()-1) ec
.AddStatement(OPC_Goto
, endTarget
, Loc
); // prob
1705 ec
.MarkLabel(endTarget
); // prob
1706 if (doDrop
) ec
.AddStatement(OPC_DropPOD
, Loc
);
1708 //if (XLevel.StateCall) XLevel.StateCall->Result = false;
1710 VLabel falseTarget = ec.DefineLabel();
1712 xstc->EmitBranchable(ec, falseTarget, false);
1715 ec.MarkLabel(falseTarget);
1720 //==========================================================================
1722 // VDecorateAJump::toString
1724 //==========================================================================
1725 VStr
VDecorateAJump::toString () const {
1726 VStr res
= "A_Jump("+e2s(prob
);
1727 for (int f
= 0; f
< labels
.length(); ++f
) {
1729 res
+= e2s(labels
[f
]);
1737 //==========================================================================
1739 // VDecorateRndPick::VDecorateRndPick
1741 //==========================================================================
1742 VDecorateRndPick::VDecorateRndPick (bool aAsFloat
, const TLocation
&aloc
)
1751 //==========================================================================
1753 // VDecorateRndPick::~VDecorateRndPick
1755 //==========================================================================
1756 VDecorateRndPick::~VDecorateRndPick () {
1757 delete crnd0
; crnd0
= nullptr;
1758 for (int f
= numbers
.length()-1; f
>= 0; --f
) delete numbers
[f
];
1763 //==========================================================================
1765 // VDecorateRndPick::HasSideEffects
1767 //==========================================================================
1768 bool VDecorateRndPick::HasSideEffects () {
1773 //==========================================================================
1775 // VDecorateRndPick::VisitChildren
1777 //==========================================================================
1778 void VDecorateRndPick::VisitChildren (VExprVisitor
*v
) {
1779 if (!v
->stopIt
&& crnd0
) crnd0
->Visit(v
);
1780 for (int f
= 0; !v
->stopIt
&& f
< numbers
.length(); f
+= 1) {
1781 numbers
[f
]->Visit(v
);
1786 //==========================================================================
1788 // VDecorateRndPick::SyntaxCopy
1790 //==========================================================================
1791 VExpression
*VDecorateRndPick::SyntaxCopy () {
1792 auto res
= new VDecorateRndPick();
1793 DoSyntaxCopyTo(res
);
1798 //==========================================================================
1800 // VDecorateRndPick::DoSyntaxCopyTo
1802 //==========================================================================
1803 void VDecorateRndPick::DoSyntaxCopyTo (VExpression
*e
) {
1804 VExpression::DoSyntaxCopyTo(e
);
1805 auto res
= (VDecorateRndPick
*)e
;
1806 res
->crnd0
= (crnd0
? crnd0
->SyntaxCopy() : nullptr);
1807 res
->numbers
.setLength(numbers
.length());
1808 for (int f
= 0; f
< numbers
.length(); ++f
) {
1809 res
->numbers
[f
] = (numbers
[f
] ? numbers
[f
]->SyntaxCopy() : nullptr);
1811 res
->asFloat
= asFloat
;
1815 //==========================================================================
1817 // VDecorateRndPick::DoResolve
1819 //==========================================================================
1820 VExpression
*VDecorateRndPick::DoResolve (VEmitContext
&ec
) {
1821 if (numbers
.length() == 0) {
1822 ParseError(Loc
, "no choices in `%srandompick` -- are you nuts?!", (asFloat
? "f" : ""));
1827 if (numbers
.length() > 255) {
1828 ParseError(Loc
, "%d choices in `%srandompick` -- are you nuts?!", numbers
.length(), (asFloat
? "f" : ""));
1834 for (int lbidx
= 0; lbidx
< numbers
.length(); ++lbidx
) {
1835 VExpression
*lbl
= numbers
[lbidx
];
1838 ParseError(Loc
, "`%srandompick` cannot have default arguments", (asFloat
? "f" : ""));
1843 bool massaged
= false;
1844 lbl
= lbl
->MassageDecorateArg(ec
, nullptr/*invokation*/, /*CallerState*/nullptr, (asFloat
? "frandompick" : "randompick"), lbidx
+1, (asFloat
? VFieldType(TYPE_Float
) : VFieldType(TYPE_Int
)), false/*not optional*/, nullptr, &massaged
);
1845 if (!lbl
) { delete this; return nullptr; } // some error
1847 numbers
[lbidx
] = lbl
->Resolve(ec
);
1848 lbl
= numbers
[lbidx
];
1849 if (!lbl
) { delete this; return nullptr; }
1851 if (lbl
->Type
.Type
== TYPE_Int
|| lbl
->Type
.Type
== TYPE_Byte
) {
1853 numbers
[lbidx
] = new VScalarToFloat(lbl
, false);
1854 if (numbers
[lbidx
]) numbers
[lbidx
] = numbers
[lbidx
]->Resolve(ec
);
1855 if (!numbers
[lbidx
]) { delete this; return nullptr; }
1857 } else if (lbl
->Type
.Type
== TYPE_Float
) {
1859 ParseWarning(Loc
, "`%srandompick()` argument #%d must be int", (asFloat
? "f" : ""), lbidx
+1);
1860 numbers
[lbidx
] = new VScalarToInt(lbl
, false);
1861 if (numbers
[lbidx
]) numbers
[lbidx
] = numbers
[lbidx
]->Resolve(ec
);
1862 if (!numbers
[lbidx
]) { delete this; return nullptr; }
1865 ParseError(Loc
, "`%srandompick()` argument #%d has invalid type `%s`", (asFloat
? "f" : ""), lbidx
+1, *lbl
->Type
.GetName());
1871 // one number means "just return it"
1872 if (numbers
.length() > 1) {
1873 // call to `P_Random()`
1874 crnd0
= new VBinary(VBinary::Modulus
,
1875 new VCastOrInvocation("P_Random", Loc
, 0, nullptr),
1876 new VIntLiteral(numbers
.length(), Loc
), Loc
);
1878 crnd0
= crnd0
->Resolve(ec
);
1879 if (!crnd0
) { delete this; return nullptr; }
1881 if (crnd0
->Type
.Type
!= TYPE_Int
&& crnd0
->Type
.Type
!= TYPE_Byte
) {
1882 ParseError(Loc
, "`P_Random()` should return integer, not `%s`", *crnd0
->Type
.GetName());
1887 crnd0
= nullptr; // just in case
1890 Type
.Type
= (asFloat
? TYPE_Float
: TYPE_Int
);
1896 //==========================================================================
1898 // VDecorateRndPick::Emit
1900 //==========================================================================
1901 void VDecorateRndPick::Emit (VEmitContext
&ec
) {
1902 EmitCheckResolved(ec
);
1904 vassert(numbers
.length() > 0);
1905 if (numbers
.length() == 1) {
1906 numbers
[0]->Emit(ec
); // number
1908 VLabel endTarget
= ec
.DefineLabel();
1914 TArray
<VLabel
> addrs
;
1915 addrs
.setLength(numbers
.length());
1916 for (int lidx
= 0; lidx
< numbers
.length(); ++lidx
) addrs
[lidx
] = ec
.DefineLabel();
1917 for (int lidx
= 0; lidx
< numbers
.length(); ++lidx
) {
1918 if (lidx
>= 0 && lidx
< 256) {
1919 ec
.AddStatement(OPC_CaseGotoB
, lidx
, addrs
[lidx
], Loc
);
1920 }/* else if (lidx >= MIN_VINT16 && lidx < MAX_VINT16) {
1921 ec.AddStatement(OPC_CaseGotoS, lidx, addrs[lidx], Loc);
1923 ec
.AddStatement(OPC_CaseGoto
, lidx
, addrs
[lidx
], Loc
);
1926 // just in case (and for optimiser)
1927 ec
.AddStatement(OPC_DropPOD
, Loc
); // (index dropped)
1928 ec
.AddStatement(OPC_PushNumber
, -1, Loc
);
1929 ec
.AddStatement(OPC_Goto
, endTarget
, Loc
);
1931 // now generate label jump code
1932 for (int lidx
= 0; lidx
< numbers
.length(); ++lidx
) {
1933 ec
.MarkLabel(addrs
[lidx
]);
1934 numbers
[lidx
]->Emit(ec
); // number
1935 if (lidx
!= numbers
.length()-1) ec
.AddStatement(OPC_Goto
, endTarget
, Loc
); // prob
1938 ec
.MarkLabel(endTarget
);
1943 //==========================================================================
1945 // VDecorateRndPick::toString
1947 //==========================================================================
1948 VStr
VDecorateRndPick::toString () const {
1949 VStr res
= va("%srandompick(", (asFloat
? "f" : ""));
1950 bool putComma
= false;
1951 for (auto &&n
: numbers
) {
1952 if (putComma
) res
+= ", "; else putComma
= true;