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 source is directly included from "vc_decorate.cpp"
28 // forward declarations
29 static VExpression
*ParseExpressionNoAssign (VScriptParser
*sc
, VClass
*Class
);
30 static VStatement
*ParseActionStatement (VScriptParser
*sc
, VClass
*Class
, VState
*State
);
33 //==========================================================================
35 // CheckParseSetUserVarExpr
37 //==========================================================================
38 static VExpression
*CheckParseSetUserVarExpr (VScriptParser
*sc
, VClass
*Class
, VStr FuncName
) {
39 if (FuncName
.strEquCI("A_SetUserVar") || FuncName
.strEquCI("A_SetUserVarFloat")) {
40 auto stloc
= sc
->GetVCLoc();
43 VStr varName
= sc
->String
;
44 if (!varName
.startsWithCI("user_")) sc
->Error(va("%s: user variable name in DECORATE must start with `user_`", *sc
->GetVCLoc().toStringNoCol()));
46 VExpression
*val
= ParseExpressionNoAssign(sc
, Class
);
48 if (!val
) sc
->Error("invalid assignment");
49 VExpression
*dest
= new VDecorateUserVar(varName
, stloc
);
50 return new VAssignment(VAssignment::Assign
, dest
, val
, stloc
);
51 } else if (FuncName
.strEquCI("A_SetUserArray") || FuncName
.strEquCI("A_SetUserArrayFloat")) {
52 auto stloc
= sc
->GetVCLoc();
55 VStr varName
= sc
->String
;
56 if (!varName
.startsWithCI("user_")) sc
->Error(va("%s: user variable name in DECORATE must start with `user_`", *sc
->GetVCLoc().toStringNoCol()));
59 VExpression
*idx
= ParseExpressionNoAssign(sc
, Class
);
60 if (!idx
) sc
->Error("decorate parsing error");
63 VExpression
*val
= ParseExpressionNoAssign(sc
, Class
);
64 if (!val
) sc
->Error("decorate parsing error");
66 VExpression
*dest
= new VDecorateUserVar(varName
, idx
, stloc
);
67 return new VAssignment(VAssignment::Assign
, dest
, val
, stloc
);
73 //==========================================================================
75 // CheckParseSetUserVarStmt
77 //==========================================================================
78 static VStatement
*CheckParseSetUserVarStmt (VScriptParser
*sc
, VClass
*Class
, VStr FuncName
) {
79 VExpression
*asse
= CheckParseSetUserVarExpr(sc
, Class
, FuncName
);
80 if (!asse
) return nullptr;
81 return new VExpressionStatement(asse
);
85 //==========================================================================
89 //==========================================================================
90 static VExpression
*ParseAJump (VScriptParser
*sc
, VClass
*Class
, VState
*State
) {
91 VDecorateAJump
*jexpr
= new VDecorateAJump(sc
->GetVCLoc()); //FIXME: MEMLEAK!
92 jexpr
->CallerState
= State
;
94 VExpression
*prob
= ParseExpressionNoAssign(sc
, Class
);
96 ParseError(sc
->GetVCLoc(), "`A_Jump` oops (0)!");
101 if (sc
->Check(",")) {
103 VExpression
*arg
= ParseExpressionNoAssign(sc
, Class
);
105 ParseError(sc
->GetVCLoc(), "`A_Jump` oops (1)!");
109 jexpr
->labels
.append(arg
);
110 } while (sc
->Check(","));
117 //==========================================================================
123 //==========================================================================
124 static VExpression
*ParseRandomPick (VScriptParser
*sc
, VClass
*Class
, bool asFloat
) {
125 VDecorateRndPick
*pk
= new VDecorateRndPick(asFloat
, sc
->GetVCLoc()); //FIXME: MEMLEAK!
126 if (sc
->Check(")")) {
127 ParseError(sc
->GetVCLoc(), "`%srandompick` expects some arguments!", (asFloat
? "f" : ""));
131 VExpression
*num
= ParseExpressionNoAssign(sc
, Class
);
133 ParseError(sc
->GetVCLoc(), "`%srandompick` oops!", (asFloat
? "f" : ""));
137 pk
->numbers
.append(num
);
138 } while (sc
->Check(","));
144 // map decorate methods to normal methods
145 // used to avoid method call overhead for cvars, for example
146 //FIXME: move this to VC compiler
147 struct DecoMethodMapItem
{
153 static const DecoMethodMapItem dmtmap
[] = {
154 {.dcname
="CvarExists", .vcname
=""},
155 {.dcname
="GetCVar", .vcname
="GetCvarF"},
156 {.dcname
="GetCVarF", .vcname
=""},
157 {.dcname
="GetCVarI", .vcname
=""},
158 {.dcname
="GetCvarB", .vcname
=""},
159 {.dcname
="GetCvarS", .vcname
=""},
161 {.dcname
=0, .vcname
=0},
165 //==========================================================================
167 // ParseFunCallWithName
169 //==========================================================================
170 static VMethod
*ParseFunCallWithName (VScriptParser
*sc
, VStr FuncName
, VClass
*Class
, int &NumArgs
, VExpression
**Args
, bool gotParen
, bool *inIgnoreList
) {
171 // get function name and parse arguments
172 //VStr FuncNameLower = FuncName.ToLower();
176 if (inIgnoreList
) *inIgnoreList
= false;
178 const bool oldSISilenced
= skipSpanishInquisition
;
179 auto oldEsc
= sc
->IsEscape();
182 if (!gotParen
) gotParen
= sc
->Check("(");
184 if (!sc
->Check(")")) {
188 if (totalCount
== 1 && FuncName
.length() == 8 && FuncName
.strEquCI("A_JumpIf")) {
189 // `A_JumpIf()` first arg is always logical
190 skipSpanishInquisition
= true;
192 skipSpanishInquisition
= oldSISilenced
;
195 // check for named arguments
196 auto saved
= sc
->SavePos();
197 bool gotArgName
= false;
198 VStr argName
= VStr();
200 if (sc
->CheckIdentifier()) {
201 argName
= sc
->String
;
202 if (sc
->Check(":")) {
203 argLoc
= sc
->GetVCLoc();
205 //GCon->Logf(NAME_Debug, "***** FOUND NAMEDARG '%s' *****", *argName);
208 if (!gotArgName
) sc
->RestorePos(saved
);
210 if (sc
->Check("__default")) {
212 Args
[NumArgs
] = nullptr;
214 Args
[NumArgs
] = ParseExpressionNoAssign(sc
, Class
);
218 if (gotArgName
&& !argName
.isEmpty()) {
219 //GCon->Logf(NAME_Debug, "***** NAMEDARG '%s': %s *****", *argName, (Args[NumArgs] ? *Args[NumArgs]->toString() : "<null>"));
220 VArgMarshall
*arg
= new VArgMarshall(VName(*argName
), argLoc
);
221 arg
->e
= Args
[NumArgs
];
225 if (NumArgs
== VMethod::MAX_PARAMS
) {
226 delete Args
[NumArgs
];
227 Args
[NumArgs
] = nullptr;
228 if (!FuncName
.strEquCI("A_Jump") &&
229 !FuncName
.strEquCI("randompick") &&
230 !FuncName
.strEquCI("decorate_randompick"))
232 ParseError(sc
->GetVCLoc(), "Too many arguments to `%s`", *FuncName
);
237 } while (sc
->Check(","));
241 sc
->SetEscape(oldEsc
);
242 skipSpanishInquisition
= oldSISilenced
;
244 VMethod
*Func
= nullptr;
247 if (!IgnoredDecorateActions
.get(FuncName
)) {
248 // find the state action method: first check action specials, then state actions
249 // hack: `ACS_ExecuteWithResult` has its own method, but it still should be in line specials
250 if (!FuncName
.strEquCI("ACS_ExecuteWithResult")) {
251 int spcn
= FindScriptLineSpecialByName(FuncName
);
253 Func
= Class
->FindMethodChecked("A_ExecActionSpecial");
255 sc
->Error(va("Too many arguments to translated action special `%s`", *FuncName
));
257 // add missing arguments
258 while (NumArgs
< 5) {
259 Args
[NumArgs
] = new VIntLiteral(0, sc
->GetVCLoc());
262 // add action special number argument
263 Args
[5] = new VIntLiteral(spcn
, sc
->GetVCLoc());
269 // the search is case-insensitive anyway
271 for (const DecoMethodMapItem
*dme
= dmtmap
; dme
->dcname
; ++dme
) {
272 if (FuncName
.strEquCI(dme
->dcname
)) {
273 const char *vcname
= dme
->vcname
;
274 if (!vcname
|| !vcname
[0]) vcname
= dme
->dcname
;
275 Func
= Class
->FindMethodChecked(vcname
);
276 //GCon->Logf(NAME_Debug, "%s: CALL `%s`", *sc->GetVCLoc().toStringNoCol(), *FuncName);
280 if (!Func
) Func
= Class
->FindDecorateStateAction(FuncName
/*.toLowerCase()*/);
283 if (inIgnoreList
) *inIgnoreList
= true;
287 //GCon->Logf(NAME_Debug, "***8:<%s> %s", *FuncName, *sc->GetVCLoc().toStringNoCol());
289 if (NumArgs
> Func
->NumParams
&&
290 (FuncName
.strEquCI("A_Jump") ||
291 FuncName
.strEquCI("randompick") ||
292 FuncName
.strEquCI("decorate_randompick") ||
293 FuncName
.strEquCI("frandompick") ||
294 FuncName
.strEquCI("decorate_frandompick")))
296 ParseWarning(sc
->GetVCLoc(), "Too many arguments to `%s` (%d -- are you nuts?!)", *FuncName
, totalCount
);
297 for (int f
= Func
->NumParams
; f
< NumArgs
; ++f
) { delete Args
[f
]; Args
[f
] = nullptr; }
298 NumArgs
= Func
->NumParams
;
301 // check for "user_" argument for non-string parameter
302 for (int f
= 0; f
< NumArgs
; ++f
) {
303 if (f
>= Func
->NumParams
) break;
304 if (!Args
[f
]) continue;
305 if (!Args
[f
]->IsStrConst()) continue;
306 if (Func
->ParamTypes
[f
].Type
== TYPE_String
) continue;
307 VStr str
= Args
[f
]->GetStrConst(DecPkg
);
308 if (!str
.startsWithNoCase("user_")) continue;
309 auto loc
= Args
[f
]->Loc
;
310 ParseWarning(loc
, "`user_xxx` should not be a string constant, you mo...dder! FIX YOUR BROKEN CODE!");
312 VName fldn
= Class
->FindDecorateStateFieldTrans(*str
);
313 if (fldn
== NAME_None
) ParseWarning(loc
, "`user_xxx` is not a known uservar");
315 Args
[f
] = new VDecorateSingleName(*fldn
, loc
);
318 Args
[f
] = new VDecorateSingleName(str
.toLowerCase(), loc
);
327 //==========================================================================
331 //==========================================================================
332 static VExpression
*ParseMethodCall (VScriptParser
*sc
, VClass
*Class
, VStr Name
, TLocation Loc
, bool parenEaten
=true) {
333 VExpression
*Args
[VMethod::MAX_PARAMS
+1];
335 bool inIgnoreList
= false;
336 VMethod
*Func
= ParseFunCallWithName(sc
, Name
, Class
, NumArgs
, Args
, parenEaten
, &inIgnoreList
); // got paren
338 //return new VDecorateInvocation((Func ? Func->GetVName() : VName(*Name, VName::AddLower)), Loc, NumArgs, Args);
339 if (Func
) return new VDecorateInvocation(Func
, Func
->GetVName(), Loc
, NumArgs
, Args
);
340 return new VDecorateInvocation(VName(*Name
/*, VName::AddLower*/), Loc
, NumArgs
, Args
);
342 // this is ignored method, return zero integer literal instead
343 return new VIntLiteral(0, sc
->GetVCLoc());
347 // ////////////////////////////////////////////////////////////////////////// //
349 MOP_Unary
, // new VUnary((VUnary::EUnaryOp)mop->opcode, lhs, l);
350 MOP_Binary
, // new VBinary((VBinary::EBinOp)mop->opcode, lhs, rhs, l);
351 MOP_Logical
, // new VBinaryLogical((VBinaryLogical::ELogOp)mop->opcode, lhs, rhs, l);
354 struct MathOpHandler
{
356 int prio
; // negative means "right-associative" (not implemented yet)
357 const char *op
; // `nullptr` means "i am the last one, The Omega of it all!"
361 #define DEFOP(type_,prio_,name_,opcode_) \
362 { .type = type_, .prio = prio_, .op = name_, .opcode = (int)opcode_ }
364 static const MathOpHandler oplist
[] = {
365 // unaries; rhs has no sense here
366 DEFOP(MOP_Unary
, 2, "+", VUnary::Plus
),
367 DEFOP(MOP_Unary
, 2, "-", VUnary::Minus
),
368 DEFOP(MOP_Unary
, 2, "!", VUnary::Not
),
369 DEFOP(MOP_Unary
, 2, "~", VUnary::BitInvert
),
372 DEFOP(MOP_Binary
, 3, "*", VBinary::Multiply
),
373 DEFOP(MOP_Binary
, 3, "/", VBinary::Divide
),
374 DEFOP(MOP_Binary
, 3, "%", VBinary::Modulus
),
376 DEFOP(MOP_Binary
, 4, "+", VBinary::Add
),
377 DEFOP(MOP_Binary
, 4, "-", VBinary::Subtract
),
379 DEFOP(MOP_Binary
, 5, "<<", VBinary::LShift
),
380 DEFOP(MOP_Binary
, 5, ">>", VBinary::RShift
),
382 DEFOP(MOP_Binary
, 6, "<", VBinary::Less
),
383 DEFOP(MOP_Binary
, 6, "<=", VBinary::LessEquals
),
384 DEFOP(MOP_Binary
, 6, ">", VBinary::Greater
),
385 DEFOP(MOP_Binary
, 6, ">=", VBinary::GreaterEquals
),
387 DEFOP(MOP_Binary
, 7, "==", VBinary::Equals
),
388 DEFOP(MOP_Binary
, 7, "!=", VBinary::NotEquals
),
390 DEFOP(MOP_Binary
, 8, "&", VBinary::And
),
391 DEFOP(MOP_Binary
, 9, "^", VBinary::XOr
),
392 DEFOP(MOP_Binary
, 10, "|", VBinary::Or
),
394 DEFOP(MOP_Logical
, 11, "&&", VBinaryLogical::And
),
395 DEFOP(MOP_Logical
, 12, "||", VBinaryLogical::Or
),
397 // 13 is ternary, it has no callback (special case)
399 { .type
= MOP_Unary
, .prio
= 0, .op
= nullptr, .opcode
= 0 },
402 #define PRIO_TERNARY (13)
404 #define PRIO_MAX PRIO_TERNARY
406 #define PRIO_NO_ASSIGN PRIO_TERNARY
412 //==========================================================================
414 // ParseConvertToUserVar
416 //==========================================================================
417 static VExpression
*ParseConvertToUserVar (VScriptParser
* /*sc*/, VClass
* /*Class*/, VExpression
*lhs
) {
418 if (!lhs
|| !lhs
->IsDecorateSingleName()) return lhs
;
419 // for locals, `VDecorateSingleName()` will resolve itself into The Right Thing
420 if (((VDecorateSingleName
*)lhs
)->localAccess
) return lhs
;
421 VStr vn
= ((VDecorateSingleName
*)lhs
)->Name
;
422 // decorate uservar should resolve to the special thing
423 VExpression
*e
= new VDecorateUserVar(vn
, lhs
->Loc
);
424 //GCon->Logf(NAME_Debug, "%s: CVT: `%s` -- %s -> %s", *lhs->Loc.toStringNoCol(), *vn, *lhs->toString(), *e->toString());
430 //==========================================================================
432 // ParsePostfixIncDec
434 //==========================================================================
435 static VExpression
*ParsePostfixIncDec (VScriptParser
*sc
, VClass
*Class
, VExpression
*lhs
) {
438 if (sc
->Check("++")) opc
= 1;
439 else if (sc
->Check("--")) opc
= -1;
440 else return lhs
; // nothing to do
442 //GCon->Logf(NAME_Debug, "%s: `%s` (%d)", *lhs->Loc.toStringNoCol(), *lhs->toString(), opc);
444 lhs
= ParseConvertToUserVar(sc
, Class
, lhs
);
447 VExpression
*op1
= lhs
->SyntaxCopy();
448 VExpression
*op2
= new VIntLiteral(opc
, lhs
->Loc
);
449 VExpression
*val
= new VBinary(VBinary::Add
, op1
, op2
, lhs
->Loc
, true/*from decorate*/);
450 VExpression
*ass
= new VAssignment(VAssignment::Assign
, lhs
->SyntaxCopy(), val
, sc
->GetVCLoc());
452 // build resulting operator
453 return new VCommaExprRetOp0(lhs
, ass
, lhs
->Loc
);
463 //==========================================================================
465 // ClassifyLogicalExpression
467 //==========================================================================
468 static int ClassifyLogicalExpression (VExpression
*lhs
) {
469 if (!lhs
) return LHS_NONLOGICAL
; // just in case
471 if (lhs
->IsParens()) {
472 VExprParens
*ep
= (VExprParens
*)lhs
;
473 return ClassifyLogicalExpression(ep
->op
);
476 if (lhs
->IsBinaryLogical()) return LHS_LOGICAL
;
478 if (lhs
->IsBinaryMath() && ((VBinary
*)lhs
)->IsComparison()) return LHS_COMPARISON
;
480 return LHS_NONLOGICAL
;
484 //==========================================================================
486 // VParser::ParseExpressionGeneral
488 //==========================================================================
489 static VExpression
*ParseExpressionGeneral (VScriptParser
*sc
, VClass
*Class
, int prio
) {
494 // check for quoted strings first, since these could also have numbers...
495 if (sc
->CheckQuotedString()) {
496 int Val
= DecPkg
->FindString(*sc
->String
);
497 return new VStringLiteral(sc
->String
, Val
, sc
->GetVCLoc());
501 if (sc
->CheckNumber()) {
502 vint32 Val
= sc
->Number
;
503 return new VIntLiteral(Val
, sc
->GetVCLoc());
507 if (sc
->CheckFloat()) {
508 float Val
= sc
->Float
;
509 return new VFloatLiteral(Val
, sc
->GetVCLoc());
513 if (sc
->Check("false")) return new VIntLiteral(0, sc
->GetVCLoc());
514 if (sc
->Check("true")) return new VIntLiteral(1, sc
->GetVCLoc());
517 if (sc
->Check("(")) {
518 const TLocation l
= sc
->GetVCLoc();
519 VExpression
*op
= ParseExpressionGeneral(sc
, Class
, PRIO_NO_ASSIGN
);
520 if (!op
) ParseError(l
, "Expression expected");
522 return new VExprParens(op
, l
);
526 if (sc
->CheckIdentifier()) {
527 const TLocation l
= sc
->GetVCLoc();
528 VStr Name
= sc
->String
;
529 // args[idx] are special
530 if (Name
.strEquCI("args")) {
531 if (sc
->Check("[")) {
532 const TLocation lidx
= sc
->GetVCLoc();
533 Name
= VStr("GetArg");
534 VExpression
*Args
[1];
535 Args
[0] = ParseExpressionGeneral(sc
, Class
, PRIO_NO_ASSIGN
);
536 if (!Args
[0]) ParseError(lidx
, "`args` index expression expected");
538 return new VDecorateInvocation(VName(*Name
), lidx
, 1, Args
);
541 // skip random generator ID
542 if ((Name
.strEquCI("random") ||
543 Name
.strEquCI("random2") ||
544 Name
.strEquCI("frandom") ||
545 Name
.strEquCI("randompick") ||
546 Name
.strEquCI("frandompick")) && sc
->Check("["))
551 // special argument parsing
552 if (sc
->Check("(")) {
553 if (Name
.strEquCI("randompick") || Name
.strEquCI("frandompick")) {
554 return ParseRandomPick(sc
, Class
, Name
.strEquCI("frandompick"));
556 return ParseMethodCall(sc
, Class
, Name
, l
, true); // paren eaten
558 if (sc
->String
.length() > 2 && sc
->String
[1] == '_' && VStr::toupper(sc
->String
[0]) == 'A') {
559 return ParseMethodCall(sc
, Class
, Name
, l
, false); // paren not eaten
561 //GCon->Logf(NAME_Debug, "%s: PRIO0! (cb=%d) ID=`%s`", *sc->GetVCLoc().toStringNoCol(), (inCodeBlock ? 1 : 0), *Name);
562 return new VDecorateSingleName(Name
, l
);
566 sc
->Error("expression expected");
572 VExpression
*op
= ParseExpressionGeneral(sc
, Class
, prio
-1);
573 if (!op
) return nullptr;
574 if (sc
->Check("[")) {
575 VExpression
*ind
= ParseExpressionNoAssign(sc
, Class
);
576 if (!ind
) sc
->Error("index expression error");
578 //op = new VArrayElement(op, ind, l);
579 if (!op
->IsDecorateSingleName()) sc
->Error("cannot index non-array");
580 VExpression
*e
= new VDecorateUserVar(((VDecorateSingleName
*)op
)->Name
, ind
, op
->Loc
);
589 // get token, this is slightly faster
590 if (!sc
->GetString()) { sc
->Error("expression expected"); return nullptr; } // no more code, wtf?
591 VStr token
= sc
->String
;
592 //GCon->Logf(NAME_Debug, "%s: PRIO2! (cb=%d) %s", *sc->GetVCLoc().toStringNoCol(), (inCodeBlock ? 1 : 0), *token);
594 if (sc
->QuotedString
&& token
== "-") {
595 return new VDecorateSingleName(sc
->String
, sc
->GetVCLoc());
599 if (token
.strEqu("++") || token
.strEqu("--")) {
600 const TLocation l
= sc
->GetVCLoc();
601 VExpression
*lhs
= ParseExpressionGeneral(sc
, Class
, prio
); // rassoc
602 if (!lhs
) return nullptr;
603 lhs
= ParseConvertToUserVar(sc
, Class
, lhs
);
604 //GCon->Logf(NAME_Debug, "%s: `%s`: %s", *l.toStringNoCol(), *token, *lhs->toString());
605 VExpression
*op2
= new VIntLiteral((token
[0] == '+' ? 1 : -1), lhs
->Loc
);
606 VExpression
*val
= new VBinary(VBinary::Add
, lhs
->SyntaxCopy(), op2
, lhs
->Loc
, true/*from decorate*/);
607 return new VAssignment(VAssignment::Assign
, lhs
, val
, l
);
610 // hack for `!XF_HURTSOURCE` (some mod authors loves to do this)
611 if (!sc
->QuotedString
&& token
== "!") {
612 auto sp
= sc
->SavePos();
613 if (sc
->Check("XF_HURTSOURCE")) {
614 if (!sc
->QuotedString
) {
615 //GCon->Logf(NAME_Debug, "%s: !XF_HURTSOURCE hack!", *sc->GetVCLoc().toStringNoCol());
616 return new VIntLiteral(0, sc
->GetVCLoc());
622 for (const MathOpHandler
*mop
= oplist
; mop
->op
; ++mop
) {
623 if (mop
->prio
!= prio
) continue;
624 if (token
.strEqu(mop
->op
)) {
625 vassert(mop
->type
== MOP_Unary
);
626 const TLocation l
= sc
->GetVCLoc();
627 VExpression
*lhs
= ParseExpressionGeneral(sc
, Class
, prio
); // rassoc
628 if (!lhs
) return nullptr;
629 //GCon->Logf(NAME_Debug, "%s: UNARY! %s", *lhs->Loc.toStringNoCol(), *lhs->toString());
630 // as this is right-associative, return here
631 // note that postfix inc/dec is processed by the recursive call
632 return new VUnary((VUnary::EUnaryOp
)mop
->opcode
, lhs
, l
, false/*not resolved*/, true/*from decorate*/);
634 lhs = new VUnary((VUnary::EUnaryOp)mop->opcode, lhs, l);
635 if (inCodeBlock) lhs = ParsePostfixIncDec(sc, Class, lhs);
641 sc
->UnGet(); // return token back
642 // not found, try higher priority
643 VExpression
*lhs
= ParseExpressionGeneral(sc
, Class
, prio
-1);
644 //GCon->Logf(NAME_Debug, "%s: PRIO2, NOUNARY! (cb=%d) ID=`%s`; lhs=<%s>", *sc->GetVCLoc().toStringNoCol(), (inCodeBlock ? 1 : 0), *token, *lhs->toString());
645 if (lhs
&& inCodeBlock
) lhs
= ParsePostfixIncDec(sc
, Class
, lhs
);
650 if (prio
== PRIO_TERNARY
) {
651 VExpression
*cond
= ParseExpressionGeneral(sc
, Class
, prio
-1);
652 if (!cond
) return nullptr;
653 if (sc
->Check("?")) {
654 const TLocation l
= sc
->GetVCLoc();
655 VExpression
*op1
= ParseExpressionGeneral(sc
, Class
, prio
); // rassoc
656 if (!op1
) { delete cond
; return nullptr; }
658 VExpression
*op2
= ParseExpressionGeneral(sc
, Class
, prio
); // rassoc
659 if (!op2
) { delete op1
; delete cond
; return nullptr; }
660 cond
= new VConditional(cond
, op1
, op2
, l
);
666 VExpression
*lhs
= ParseExpressionGeneral(sc
, Class
, prio
-1);
667 if (!lhs
) return nullptr; // some error
669 const MathOpHandler
*mop
= oplist
;
670 // get token, this is slightly faster
671 if (!sc
->GetString()) break; // no more code
672 VStr token
= sc
->String
;
673 //const TLocation tokloc = sc->GetVCLoc();
674 //VStr origToken = token;
675 // hacks for some dumbfucks
676 if (!inCodeBlock
&& !skipSpanishInquisition
) {
677 if (token
.strEqu("||")) {
678 int lcls
= ClassifyLogicalExpression(lhs
);
681 if (prio
== 10 && !vcWarningsSilenced
&& !lhs
->IsParens() && cli_MoreSpanishInquisition
) {
682 GCon
->Logf(NAME_Warning
, "%s: Spanish Inquisition says: `||` is suspicious (but lhs is logical)!", *sc
->GetVCLoc().toStringNoCol());
686 if (prio
== 10 && !vcWarningsSilenced
&& !lhs
->IsParens() && cli_MoreSpanishInquisition
) {
687 GCon
->Logf(NAME_Warning
, "%s: Spanish Inquisition says: `||` is suspicious (but lhs is comparison)!", *sc
->GetVCLoc().toStringNoCol());
691 if (prio
== 10 && !vcWarningsSilenced
) {
692 GCon
->Logf(NAME_Error
, "%s: Spanish Inquisition says: in decorate, thou shalt use `|` to combine constants. Prepare to AUTO-DA-FE, APOSTATE!", *sc
->GetVCLoc().toStringNoCol());
697 } else if (token
.strEqu("&&")) {
698 int lcls
= ClassifyLogicalExpression(lhs
);
701 if (prio
== 8 && !vcWarningsSilenced
&& !lhs
->IsParens() && cli_MoreSpanishInquisition
) {
702 GCon
->Logf(NAME_Warning
, "%s: Spanish Inquisition says: `&&` is suspicious (but lhs is logical)!", *sc
->GetVCLoc().toStringNoCol());
706 if (prio
== 8 && !vcWarningsSilenced
&& !lhs
->IsParens() && cli_MoreSpanishInquisition
) {
707 GCon
->Logf(NAME_Warning
, "%s: Spanish Inquisition says: `&&` is suspicious (but lhs is comparison)!", *sc
->GetVCLoc().toStringNoCol());
711 if (prio
== 8 && !vcWarningsSilenced
) {
712 GCon
->Logf(NAME_Error
, "%s: Spanish Inquisition says: in decorate, thou shalt use `&` to mask constants. Prepare to AUTO-DA-FE, APOSTATE!", *sc
->GetVCLoc().toStringNoCol());
717 } else if (token
.strEqu("=")) {
718 if (prio
== 7 && !vcWarningsSilenced
) {
719 GLog
.Logf(NAME_Error
, "%s: Spanish Inquisition says: in decorate, thou shalt use `==` for comparisons. Prepare to AUTO-DA-FE, APOSTATE!", *sc
->GetVCLoc().toStringNoCol());
723 } else if (!inCodeBlock
) {
724 // Spanish Inquisition is silenced
725 vassert(skipSpanishInquisition
);
727 if (token
== "||" || token
== "&&" || token
== "&" || token
== "|") {
728 GCon
->Logf(NAME_Debug
, "%s: Spanish Inquisition is silenced (token=`%s`)", *sc
->GetVCLoc().toStringNoCol(), *token
);
731 if (token
.strEqu("=")) {
732 if (prio
== 7 && !vcWarningsSilenced
) {
733 GLog
.Logf(NAME_Error
, "%s: Spanish Inquisition says: in decorate, thou shalt use `==` for comparisons. Prepare to AUTO-DA-FE, APOSTATE!", *sc
->GetVCLoc().toStringNoCol());
738 // find math operator
739 for (; mop
->op
; ++mop
) {
740 if (mop
->prio
!= prio
) continue;
741 if (token
.strEqu(mop
->op
)) break;
745 // we're done with this priority level, return a token, and get out
750 const TLocation l
= sc
->GetVCLoc();
751 VExpression
*rhs
= ParseExpressionGeneral(sc
, Class
, prio
-1); // use `prio` for rassoc, and immediately return (see above)
752 if (!rhs
) { delete lhs
; return nullptr; } // some error
754 case MOP_Binary
: lhs
= new VBinary((VBinary::EBinOp
)mop
->opcode
, lhs
, rhs
, l
, true/*from decorate*/); break;
755 case MOP_Logical
: lhs
= new VBinaryLogical((VBinaryLogical::ELogOp
)mop
->opcode
, lhs
, rhs
, l
); break;
756 default: Sys_Error("internal decorate compiler error 697306");
758 // do it all again...
764 //==========================================================================
766 // ParseExpressionNoAssign
768 //==========================================================================
769 static VExpression
*ParseExpressionNoAssign (VScriptParser
*sc
, VClass
*Class
) {
770 return ParseExpressionGeneral(sc
, Class
, PRIO_NO_ASSIGN
);
774 //==========================================================================
778 //==========================================================================
779 static VExpression
*ParseExpression (VScriptParser
*sc
, VClass
*Class
) {
780 if (sc
->Check("A_SetUserVar") || sc
->Check("A_SetUserVarFloat") ||
781 sc
->Check("A_SetUserArray") || sc
->Check("A_SetUserArrayFloat"))
783 VStr FuncName
= sc
->String
;
784 VExpression
*asse
= CheckParseSetUserVarExpr(sc
, Class
, FuncName
);
785 if (!asse
) sc
->Error("error in decorate");
789 VExpression
*lhs
= ParseExpressionNoAssign(sc
, Class
);
790 // this can be assign (if we are in a codeblock)
791 if (!lhs
|| !inCodeBlock
) return lhs
;
794 if (sc
->Check("=")) {
795 const TLocation l
= sc
->GetVCLoc();
796 //!GCon->Logf(NAME_Debug, "ASSIGN(%s): %s", *sc->String, *lhs->toString());
797 lhs
= ParseConvertToUserVar(sc
, Class
, lhs
);
798 VExpression
*rhs
= ParseExpression(sc
, Class
); // rassoc
799 if (!rhs
) { delete lhs
; return nullptr; }
800 return new VAssignment(VAssignment::Assign
, lhs
, rhs
, l
);
802 VBinary::EBinOp opc
= VBinary::Add
;
803 if (sc
->Check("+=")) opc
= VBinary::Add
;
804 else if (sc
->Check("-=")) opc
= VBinary::Subtract
;
805 else if (sc
->Check("*=")) opc
= VBinary::Multiply
;
806 else if (sc
->Check("/=")) opc
= VBinary::Divide
;
807 else if (sc
->Check("%=")) opc
= VBinary::Modulus
;
808 else if (sc
->Check("&=")) opc
= VBinary::And
;
809 else if (sc
->Check("|=")) opc
= VBinary::Or
;
810 else if (sc
->Check("^=")) opc
= VBinary::XOr
;
811 else if (sc
->Check("<<=")) opc
= VBinary::LShift
;
812 else if (sc
->Check(">>=")) opc
= VBinary::RShift
;
813 else if (sc
->Check(">>>=")) opc
= VBinary::URShift
;
815 //!GCon->Logf(NAME_Debug, "ASSIGN(%s): %s", *sc->String, *lhs->toString());
816 const TLocation l
= sc
->GetVCLoc();
817 lhs
= ParseConvertToUserVar(sc
, Class
, lhs
);
819 VExpression
*rhs
= ParseExpression(sc
, Class
); // rassoc
820 if (!rhs
) { delete lhs
; return nullptr; }
821 // we cannot get address of a uservar, so let's build this horrible AST instead
822 VExpression
*val
= new VBinary(opc
, lhs
->SyntaxCopy(), rhs
, lhs
->Loc
, true/*from decorate*/);
823 return new VAssignment(VAssignment::Assign
, lhs
, val
, l
);
828 //==========================================================================
830 // ParseCreateDropResult
832 // this has a special case for `VCommaExprRetOp0`
833 // as `VCommaExprRetOp0` is created only for prefix inc/dec,
834 // we can simply drop the `op0` from it
836 //==========================================================================
837 static VExpression
*ParseCreateDropResult (VExpression
*expr
) {
838 if (!expr
) return nullptr;
839 if (expr
->IsDropResult()) return expr
;
840 if (!expr
->IsCommaRetOp0()) return new VDropResult(expr
);
842 VCommaExprRetOp0
*cex
= (VCommaExprRetOp0
*)expr
;
843 VExpression
*res
= cex
->op1
;
844 //GCon->Logf(NAME_Debug, "%s:DROPRESULT: comma=<%s>; res=<%s>", *expr->Loc.toStringNoCol(), *expr->toString(), *res->toString());
845 cex
->op1
= nullptr; // so it won't be destroyed
847 return ParseCreateDropResult(res
);
851 //==========================================================================
853 // ParseFunCallAsStmt
855 //==========================================================================
856 static VStatement
*ParseFunCallAsStmt (VScriptParser
*sc
, VClass
*Class
, VState
*State
) {
857 // get function name and parse arguments
858 //auto actionLoc = sc->GetVCLoc();
859 VExpression
*Args
[VMethod::MAX_PARAMS
+1];
863 if (sc
->Check("++") || sc
->Check("--") || sc
->Check("(") || sc
->Check("[")) {
864 // this looks like an expression
865 sc
->UnGet(); // return it
866 VExpression
*expr
= ParseExpression(sc
, Class
);
867 return new VExpressionStatement(ParseCreateDropResult(expr
));
871 sc
->ExpectIdentifier();
872 VStr FuncName
= sc
->String
;
873 auto stloc
= sc
->GetVCLoc();
875 //GCon->Logf(NAME_Debug, "%s: %s", *stloc.toStringNoCol(), *FuncName);
878 // if it starts with `user_`, it is an expression too
879 if (FuncName
.startsWithCI("user_") || decoIsLocalName(FuncName
)) {
880 // this looks like an expression
881 //GCon->Logf(NAME_Debug, "%s: USERSHIT! `%s`", *sc->GetVCLoc().toStringNoCol(), *sc->String);
882 sc
->UnGet(); // return it
883 VExpression
*expr
= ParseExpression(sc
, Class
);
884 return new VExpressionStatement(ParseCreateDropResult(expr
));
888 VStatement
*suvst
= CheckParseSetUserVarStmt(sc
, Class
, FuncName
);
889 if (suvst
) return suvst
;
891 if (FuncName
.strEquCI("A_Jump")) {
892 VExpression
*jexpr
= ParseAJump(sc
, Class
, State
);
893 return new VExpressionStatement(jexpr
);
895 bool inIgnoreList
= false;
896 VMethod
*Func
= ParseFunCallWithName(sc
, FuncName
, Class
, NumArgs
, Args
, false, &inIgnoreList
); // no paren
897 if (inIgnoreList
) return new VEmptyStatement(stloc
);
898 VExpression
*callExpr
= nullptr;
900 //GLog.Logf("ERROR000: %s: Unknown state action `%s` in `%s` (replaced with NOP)", *actionLoc.toStringNoCol(), *FuncName, Class->GetName());
902 callExpr
= new VDecorateInvocation(VName(*FuncName
/*, VName::AddLower*/), stloc
, NumArgs
, Args
);
904 VDecorateInvocation
*Expr
= new VDecorateInvocation(Func
, VName(*FuncName
), stloc
, NumArgs
, Args
);
905 Expr
->CallerState
= State
;
908 return new VExpressionStatement(ParseCreateDropResult(callExpr
));
913 //==========================================================================
915 // CheckUnsafeStatement
917 //==========================================================================
918 static void CheckUnsafeStatement (VScriptParser
*sc
, const char *msg
) {
919 if (cli_DecorateAllowUnsafe
> 0) return;
920 GCon
->Logf(NAME_Error
, "Unsafe decorate statement found.");
921 GCon
->Logf(NAME_Error
, "You can allow unsafe statements with \"-decorate-allow-unsafe\", but it is not recommeded.");
922 GCon
->Logf(NAME_Error
, "The engine can crash or hang with such mods.");
927 //==========================================================================
931 //==========================================================================
932 static VStatement
*ParseStatementFor (VScriptParser
*sc
, VClass
*Class
, VState
*State
) {
933 CheckUnsafeStatement(sc
, "`for` is not allowed");
935 auto stloc
= sc
->GetVCLoc();
939 // `for` will always be put into compound, because why not?
940 // init expressions will be put first, and then `for` node
941 // TODO: lazy compound creation?
942 VCompound
*Comp
= new VCompound(stloc
);
944 // parse init expr(s)
945 if (!sc
->Check(";")) {
947 VExpression
*expr
= ParseExpression(sc
, Class
);
949 //forstmt->InitExpr.append(ParseCreateDropResult(expr));
950 if (!expr
->IsDropResult()) expr
= new VDropResult(expr
);
951 Comp
->Statements
.Append(new VExpressionStatement(expr
));
952 // here should be a comma or a semicolon
953 if (!sc
->Check(",")) break;
958 VFor
*forstmt
= new VFor(stloc
);
959 Comp
->Statements
.Append(forstmt
);
961 // parse cond expr(s)
962 VExpression
*lastCondExpr
= nullptr;
963 if (!sc
->Check(";")) {
965 VExpression
*expr
= ParseExpression(sc
, Class
);
967 if (lastCondExpr
) forstmt
->CondExpr
.append(ParseCreateDropResult(lastCondExpr
));
969 // here should be a comma or a semicolon
970 if (!sc
->Check(",")) break;
974 if (lastCondExpr
) forstmt
->CondExpr
.append(lastCondExpr
);
976 // parse loop expr(s)
977 if (!sc
->Check(")")) {
979 VExpression
*expr
= ParseExpression(sc
, Class
);
981 forstmt
->LoopExpr
.append(ParseCreateDropResult(expr
));
982 // here should be a comma or a right paren
983 if (!sc
->Check(",")) break;
988 forstmt
->Statement
= ParseActionStatement(sc
, Class
, State
);
994 //==========================================================================
996 // ParseStatementWhile
998 //==========================================================================
999 static VStatement
*ParseStatementWhile (VScriptParser
*sc
, VClass
*Class
, VState
*State
) {
1000 CheckUnsafeStatement(sc
, "`while` is not allowed");
1002 auto stloc
= sc
->GetVCLoc();
1005 VExpression
*cond
= ParseExpression(sc
, Class
);
1006 if (!cond
) sc
->Error("`while` loop expression expected");
1009 VStatement
*body
= ParseActionStatement(sc
, Class
, State
);
1011 return new VWhile(cond
, body
, stloc
);
1015 //==========================================================================
1019 //==========================================================================
1020 static VStatement
*ParseStatementDo (VScriptParser
*sc
, VClass
*Class
, VState
*State
) {
1021 CheckUnsafeStatement(sc
, "`do` is not allowed");
1023 auto stloc
= sc
->GetVCLoc();
1025 VStatement
*body
= ParseActionStatement(sc
, Class
, State
);
1027 sc
->Expect("while");
1029 VExpression
*cond
= ParseExpression(sc
, Class
);
1030 if (!cond
) sc
->Error("`do` loop expression expected");
1033 return new VDo(cond
, body
, stloc
);
1037 //==========================================================================
1039 // ParseActionStatement
1041 //==========================================================================
1042 static VStatement
*ParseActionStatement (VScriptParser
*sc
, VClass
*Class
, VState
*State
) {
1043 if (sc
->Check("{")) {
1044 DecoLocalsMarker lmark
;
1045 VCompound
*stmt
= new VCompound(sc
->GetVCLoc());
1046 while (!sc
->Check("}")) {
1047 if (sc
->Check(";")) continue;
1049 if (sc
->Check("{")) sc
->UnGet(); // cheat a little
1050 st
= ParseActionStatement(sc
, Class
, State
);
1051 if (st
) stmt
->Statements
.append(st
);
1056 if (sc
->Check(";")) return nullptr;
1058 auto stloc
= sc
->GetVCLoc();
1061 if (sc
->Check("local")) {
1062 VExpression
*typeExpr
= nullptr;
1063 if (sc
->Check("auto")) typeExpr
= new VTypeExprSimple(TYPE_Automatic
, stloc
);
1064 else if (sc
->Check("int")) typeExpr
= new VTypeExprSimple(TYPE_Int
, stloc
);
1065 else if (sc
->Check("bool")) typeExpr
= new VTypeExprSimple(TYPE_Int
, stloc
); // bool is actualy int too
1066 else if (sc
->Check("float")) typeExpr
= new VTypeExprSimple(TYPE_Float
, stloc
);
1067 else if (sc
->Check("string")) typeExpr
= new VTypeExprSimple(TYPE_String
, stloc
);
1068 else if (sc
->Check("name")) typeExpr
= new VTypeExprSimple(TYPE_Name
, stloc
);
1069 else if (sc
->Check("state")) typeExpr
= new VTypeExprSimple(TYPE_State
, stloc
);
1071 //sc->Error("invalid local type");
1073 typeExpr
= new VTypeExprSimple(TYPE_Automatic
, stloc
);
1076 VLocalDecl
*ldecl
= new VLocalDecl(stloc
);
1079 const TLocation l
= sc
->GetVCLoc();
1080 if (!sc
->CheckIdentifier()) sc
->Error("identifier expected");
1081 if (sc
->String
.isEmpty()) sc
->Error("identifier expected");
1082 // check for valid name
1083 if (sc
->String
.startsWithCI("user_")) sc
->Error(va("invalid local name '%s' (cannot starts with 'user_')", *sc
->String
));
1084 if (sc
->String
.startsWithCI("a_")) sc
->Error(va("invalid local name '%s' (cannot starts with 'a_')", *sc
->String
));
1085 VStr vname
= sc
->String
;
1088 e
.TypeExpr
= typeExpr
->SyntaxCopy();
1089 e
.Name
= VName(*sc
->String
, VName::AddLower
);
1093 if (sc
->Check("=")) e
.Value
= ParseExpressionNoAssign(sc
, Class
);
1094 ldecl
->Vars
.Append(e
);
1096 if (!decoAddLocalName(vname
)) sc
->Error(va("duplicate local name '%s'", *vname
));
1097 } while (sc
->Check(","));
1100 return new VLocalVarStatement(ldecl
);
1104 if (sc
->Check("if")) {
1106 VExpression
*cond
= ParseExpression(sc
, Class
);
1108 if (!cond
) sc
->Error("invalid `if` expression");
1109 VStatement
*ts
= ParseActionStatement(sc
, Class
, State
);
1110 if (!ts
) sc
->Error("invalid `if` true branch");
1111 auto elseloc
= sc
->GetVCLoc();
1112 if (sc
->Check("else")) {
1113 VStatement
*fs
= ParseActionStatement(sc
, Class
, State
);
1114 if (fs
) return new VIf(cond
, ts
, fs
, stloc
, elseloc
, false);
1116 return new VIf(cond
, ts
, stloc
, false);
1120 if (sc
->Check("return")) {
1121 if (sc
->Check("state")) {
1122 // return state("name");
1123 // specials: `state()`, `state(0)`, `state("")`
1125 if (sc
->Check(")")) {
1127 return new VReturn(nullptr, stloc
);
1129 VExpression
*ste
= ParseExpression(sc
, Class
);
1130 if (!ste
) sc
->Error("invalid `return`");
1133 // check for specials
1134 // replace `+number` with `number`
1136 if (ste
->IsUnaryMath()) {
1137 VUnary
*un
= (VUnary
*)ste
;
1139 if (un
->Oper
== VUnary::Plus
&& (un
->op
->IsIntConst() || un
->op
->IsFloatConst())) {
1140 VExpression
*etmp
= un
->op
;
1150 if (!ste
) sc
->Error("invalid `return`"); // just in case
1152 if ((ste
->IsIntConst() && ste
->GetIntConst() == 0) ||
1153 (ste
->IsFloatConst() && ste
->GetFloatConst() == 0))
1156 return new VReturn(nullptr, stloc
);
1159 if (ste
->IsStrConst()) {
1160 VStr lbl
= ste
->GetStrConst(DecPkg
);
1161 while (lbl
.length() && (vuint8
)lbl
[0] <= 32) lbl
.chopLeft(1);
1162 while (lbl
.length() && (vuint8
)lbl
[lbl
.length()-1] <= 32) lbl
.chopRight(1);
1163 if (lbl
.length() == 0) {
1165 return new VReturn(nullptr, stloc
);
1168 if (ste
->IsDecorateSingleName()) {
1169 VDecorateSingleName
*e
= (VDecorateSingleName
*)ste
;
1170 if (e
->Name
.length() == 0) {
1172 return new VReturn(nullptr, stloc
);
1175 // call `decorate_A_RetDoJump`
1176 VExpression
*Args
[1];
1178 VExpression
*einv
= new VDecorateInvocation(VName("decorate_A_RetDoJump"), stloc
, 1, Args
);
1179 VStatement
*stinv
= new VExpressionStatement(ParseCreateDropResult(einv
));
1180 VCompound
*cst
= new VCompound(stloc
);
1181 cst
->Statements
.append(stinv
);
1182 cst
->Statements
.append(new VReturn(nullptr, stloc
));
1184 } else if (sc
->Check(";")) {
1186 return new VReturn(nullptr, stloc
);
1189 // just call `expr`, and do normal return
1190 VStatement
*fs
= ParseActionStatement(sc
, Class
, State
);
1191 if (!fs
) return new VReturn(nullptr, stloc
);
1193 VCompound
*cst
= new VCompound(stloc
);
1194 cst
->Statements
.append(fs
);
1195 cst
->Statements
.append(new VReturn(nullptr, stloc
));
1200 if (sc
->Check("for")) return ParseStatementFor(sc
, Class
, State
);
1201 if (sc
->Check("while")) return ParseStatementWhile(sc
, Class
, State
);
1202 if (sc
->Check("do")) return ParseStatementDo(sc
, Class
, State
);
1204 //GCon->Logf(NAME_Debug, "%s: %s", *sc->GetVCLoc().toStringNoCol(), *sc->String);
1205 VStatement
*res
= ParseFunCallAsStmt(sc
, Class
, State
);
1207 if (!res
) sc
->Error("invalid action statement");
1212 //==========================================================================
1214 // CreateStateActionCallWrapperStmt
1216 //==========================================================================
1217 static VMethod
*CreateStateActionCallWrapperStmt (VStatement
*Stmt
, VClass
*Class
, VState
*State
, const TLocation
&Loc
) {
1221 #if defined(VC_DECORATE_ACTION_BELONGS_TO_STATE)
1222 VMethod
*M
= new VMethod(NAME_None
, State
, Loc
);
1224 VMethod
*M
= new VMethod(NAME_None
, Class
, Loc
);
1226 M
->Flags
= FUNC_Final
|FUNC_NoVCalls
;
1227 M
->ReturnTypeExpr
= new VTypeExprSimple(TYPE_Void
, Loc
);
1228 M
->ReturnType
= VFieldType(TYPE_Void
);
1229 M
->Statement
= Stmt
;
1231 #if !defined(VC_DECORATE_ACTION_BELONGS_TO_STATE)
1232 Class
->AddMethod(M
);
1239 //==========================================================================
1241 // CreateStateActionCallWrapper
1243 //==========================================================================
1244 static VMethod
*CreateStateActionCallWrapper (VExpression
*expr
, VClass
*Class
, VState
*State
, const TLocation
&Loc
) {
1247 VExpressionStatement
*Stmt
= new VExpressionStatement(expr
);
1248 return CreateStateActionCallWrapperStmt(Stmt
, Class
, State
, Loc
);
1252 //==========================================================================
1258 //==========================================================================
1259 static void ParseActionBlock (VScriptParser
*sc
, VClass
*Class
, VState
*State
) {
1260 DecoLocalsMarker lmark
;
1261 bool oldicb
= inCodeBlock
;
1263 VCompound
*stmt
= new VCompound(sc
->GetVCLoc());
1264 while (!sc
->Check("}")) {
1265 VStatement
*st
= ParseActionStatement(sc
, Class
, State
);
1267 stmt
->Statements
.append(st
);
1269 inCodeBlock
= oldicb
;
1271 State
->FunctionName
= NAME_None
;
1272 if (stmt
->Statements
.length()) {
1273 VMethod
*M
= CreateStateActionCallWrapperStmt(stmt
, Class
, State
, sc
->GetVCLoc());
1274 State
->Function
= M
;
1277 State
->Function
= nullptr;
1282 //==========================================================================
1286 // parse single decorate action
1288 //==========================================================================
1289 static void ParseActionCall (VScriptParser
*sc
, VClass
*Class
, VState
*State
) {
1290 // get function name and parse arguments
1291 sc
->ExpectIdentifier();
1293 VExpression
*Args
[VMethod::MAX_PARAMS
+1];
1295 auto actionLoc
= sc
->GetVCLoc();
1296 VStr FuncName
= sc
->String
;
1297 VMethod
*Func
= nullptr;
1299 VStatement
*suvst
= CheckParseSetUserVarStmt(sc
, Class
, FuncName
);
1301 Func
= CreateStateActionCallWrapperStmt(suvst
, Class
, State
, sc
->GetVCLoc());
1302 } else if (VStr::strEquCI(*FuncName
, "A_Jump")) {
1303 // `A_Jump` action always need a wrapper
1304 VExpression
*jexpr
= ParseAJump(sc
, Class
, State
);
1305 Func
= CreateStateActionCallWrapper(jexpr
, Class
, State
, sc
->GetVCLoc());
1307 bool inIgnoreList
= false;
1308 Func
= ParseFunCallWithName(sc
, FuncName
, Class
, NumArgs
, Args
, false, &inIgnoreList
); // no paren
1310 // ignored state action
1312 State
->FunctionName
= NAME_None
;
1314 // unknown state action
1315 if (!vcWarningsSilenced
) GLog
.Logf(NAME_Warning
, "%s: Unknown state action `%s` in `%s` (replaced with NOP)", *actionLoc
.toStringNoCol(), *FuncName
, Class
->GetName());
1316 // if function is not found, it means something is wrong
1317 // in that case we need to free argument expressions
1318 for (int i
= 0; i
< NumArgs
; ++i
) {
1324 State
->FunctionName
= NAME_None
;
1325 } else if (NumArgs
|| Func
->Name
== NAME_None
|| !Func
->IsGoodStateMethod()) {
1326 // call with arguments, or need a wrapper
1327 VDecorateInvocation
*Expr
= new VDecorateInvocation(Func
, *FuncName
, sc
->GetVCLoc(), NumArgs
, Args
);
1328 Expr
->CallerState
= State
;
1329 Func
= CreateStateActionCallWrapper(ParseCreateDropResult(Expr
), Class
, State
, sc
->GetVCLoc());
1331 // no need to create any wrappers
1332 //GCon->Logf(NAME_Debug, "*** %s: func=`%s` (%s) (params=%d; args=%d; final=%d; static=%d)", Class->GetName(), Func->GetName(), *FuncName, Func->NumParams, NumArgs, (Func->Flags&FUNC_Final ? 1 : 0), (Func->Flags&FUNC_Static ? 1 : 0));
1333 State
->FunctionName
= Func
->Name
;
1334 vassert(State
->FunctionName
!= NAME_None
);
1339 State
->Function
= Func
;
1340 if (Func
) State
->FunctionName
= NAME_None
;
1342 if (sc
->Check(";")) {}
1346 //==========================================================================
1348 // SetupOldStyleFunction
1350 //==========================================================================
1351 static void SetupOldStyleFunction (VScriptParser
*sc
, VClass
*Class
, VState
*State
, VMethod
*Func
) {
1357 State
->Function
= nullptr;
1358 State
->FunctionName
= NAME_None
;
1362 if (Func
->Name
== NAME_None
|| !Func
->IsGoodStateMethod()) {
1363 // need to create invocation
1364 VDecorateInvocation
*Expr
= new VDecorateInvocation(Func
, Func
->Name
, sc
->GetVCLoc(), 0, nullptr);
1365 Expr
->CallerState
= State
;
1366 Func
= CreateStateActionCallWrapper(ParseCreateDropResult(Expr
), Class
, State
, sc
->GetVCLoc());
1368 //GCon->Logf(NAME_Debug, "*** %s: func=`%s` (%s) (params=%d; args=%d; final=%d; static=%d)", Class->GetName(), Func->GetName(), *FuncName, Func->NumParams, NumArgs, (Func->Flags&FUNC_Final ? 1 : 0), (Func->Flags&FUNC_Static ? 1 : 0));
1369 State
->FunctionName
= Func
->Name
;
1370 vassert(State
->FunctionName
!= NAME_None
);
1374 State
->Function
= Func
;
1375 if (Func
) State
->FunctionName
= NAME_None
;