engine: reject mbf21 and shit24 wads. there is no way to know if it is safe to ignore...
[k8vavoom.git] / source / decorate / vc_decorate_expr.cpp
blobbce260bb674f29781455108d0322b43df3a1ad21
1 //**************************************************************************
2 //**
3 //** ## ## ## ## ## #### #### ### ###
4 //** ## ## ## ## ## ## ## ## ## ## #### ####
5 //** ## ## ## ## ## ## ## ## ## ## ## ## ## ##
6 //** ## ## ######## ## ## ## ## ## ## ## ### ##
7 //** ### ## ## ### ## ## ## ## ## ##
8 //** # ## ## # #### #### ## ##
9 //**
10 //** Copyright (C) 1999-2006 Jānis Legzdiņš
11 //** Copyright (C) 2018-2023 Ketmar Dark
12 //**
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.
16 //**
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.
21 //**
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/>.
24 //**
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();
41 sc->Expect("(");
42 sc->ExpectString();
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()));
45 sc->Expect(",");
46 VExpression *val = ParseExpressionNoAssign(sc, Class);
47 sc->Expect(")");
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();
53 sc->Expect("(");
54 sc->ExpectString();
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()));
57 sc->Expect(",");
58 // index
59 VExpression *idx = ParseExpressionNoAssign(sc, Class);
60 if (!idx) sc->Error("decorate parsing error");
61 sc->Expect(",");
62 // value
63 VExpression *val = ParseExpressionNoAssign(sc, Class);
64 if (!val) sc->Error("decorate parsing error");
65 sc->Expect(")");
66 VExpression *dest = new VDecorateUserVar(varName, idx, stloc);
67 return new VAssignment(VAssignment::Assign, dest, val, stloc);
69 return nullptr;
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 //==========================================================================
87 // VExpression
89 //==========================================================================
90 static VExpression *ParseAJump (VScriptParser *sc, VClass *Class, VState *State) {
91 VDecorateAJump *jexpr = new VDecorateAJump(sc->GetVCLoc()); //FIXME: MEMLEAK!
92 jexpr->CallerState = State;
93 sc->Expect("(");
94 VExpression *prob = ParseExpressionNoAssign(sc, Class);
95 if (!prob) {
96 ParseError(sc->GetVCLoc(), "`A_Jump` oops (0)!");
97 sc->Expect(")");
98 return jexpr;
100 jexpr->prob = prob;
101 if (sc->Check(",")) {
102 do {
103 VExpression *arg = ParseExpressionNoAssign(sc, Class);
104 if (!arg) {
105 ParseError(sc->GetVCLoc(), "`A_Jump` oops (1)!");
106 sc->Expect(")");
107 return jexpr;
109 jexpr->labels.append(arg);
110 } while (sc->Check(","));
112 sc->Expect(")");
113 return jexpr;
117 //==========================================================================
119 // ParseRandomPick
121 // paren eated
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" : ""));
128 return pk;
130 do {
131 VExpression *num = ParseExpressionNoAssign(sc, Class);
132 if (!num) {
133 ParseError(sc->GetVCLoc(), "`%srandompick` oops!", (asFloat ? "f" : ""));
134 sc->Expect(")");
135 return pk;
137 pk->numbers.append(num);
138 } while (sc->Check(","));
139 sc->Expect(")");
140 return pk;
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 {
148 const char *dcname;
149 const char *vcname;
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();
173 NumArgs = 0;
174 int totalCount = 0;
176 if (inIgnoreList) *inIgnoreList = false;
178 const bool oldSISilenced = skipSpanishInquisition;
179 auto oldEsc = sc->IsEscape();
180 sc->SetEscape(true);
182 if (!gotParen) gotParen = sc->Check("(");
183 if (gotParen) {
184 if (!sc->Check(")")) {
185 do {
186 ++totalCount;
188 if (totalCount == 1 && FuncName.length() == 8 && FuncName.strEquCI("A_JumpIf")) {
189 // `A_JumpIf()` first arg is always logical
190 skipSpanishInquisition = true;
191 } else {
192 skipSpanishInquisition = oldSISilenced;
195 // check for named arguments
196 auto saved = sc->SavePos();
197 bool gotArgName = false;
198 VStr argName = VStr();
199 TLocation argLoc;
200 if (sc->CheckIdentifier()) {
201 argName = sc->String;
202 if (sc->Check(":")) {
203 argLoc = sc->GetVCLoc();
204 gotArgName = true;
205 //GCon->Logf(NAME_Debug, "***** FOUND NAMEDARG '%s' *****", *argName);
208 if (!gotArgName) sc->RestorePos(saved);
210 if (sc->Check("__default")) {
211 // default
212 Args[NumArgs] = nullptr;
213 } else {
214 Args[NumArgs] = ParseExpressionNoAssign(sc, Class);
217 // named arg?
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];
222 Args[NumArgs] = arg;
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);
234 } else {
235 ++NumArgs;
237 } while (sc->Check(","));
238 sc->Expect(")");
241 sc->SetEscape(oldEsc);
242 skipSpanishInquisition = oldSISilenced;
244 VMethod *Func = nullptr;
246 // check ignores
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);
252 if (spcn) {
253 Func = Class->FindMethodChecked("A_ExecActionSpecial");
254 if (NumArgs > 5) {
255 sc->Error(va("Too many arguments to translated action special `%s`", *FuncName));
256 } else {
257 // add missing arguments
258 while (NumArgs < 5) {
259 Args[NumArgs] = new VIntLiteral(0, sc->GetVCLoc());
260 ++NumArgs;
262 // add action special number argument
263 Args[5] = new VIntLiteral(spcn, sc->GetVCLoc());
264 ++NumArgs;
269 // the search is case-insensitive anyway
270 if (!Func) {
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);
277 break;
280 if (!Func) Func = Class->FindDecorateStateAction(FuncName/*.toLowerCase()*/);
282 } else {
283 if (inIgnoreList) *inIgnoreList = true;
286 if (!Func) {
287 //GCon->Logf(NAME_Debug, "***8:<%s> %s", *FuncName, *sc->GetVCLoc().toStringNoCol());
288 } else {
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!");
311 if (Class) {
312 VName fldn = Class->FindDecorateStateFieldTrans(*str);
313 if (fldn == NAME_None) ParseWarning(loc, "`user_xxx` is not a known uservar");
314 delete Args[f];
315 Args[f] = new VDecorateSingleName(*fldn, loc);
316 } else {
317 delete Args[f];
318 Args[f] = new VDecorateSingleName(str.toLowerCase(), loc);
323 return Func;
327 //==========================================================================
329 // ParseMethodCall
331 //==========================================================================
332 static VExpression *ParseMethodCall (VScriptParser *sc, VClass *Class, VStr Name, TLocation Loc, bool parenEaten=true) {
333 VExpression *Args[VMethod::MAX_PARAMS+1];
334 int NumArgs = 0;
335 bool inIgnoreList = false;
336 VMethod *Func = ParseFunCallWithName(sc, Name, Class, NumArgs, Args, parenEaten, &inIgnoreList); // got paren
337 if (!inIgnoreList) {
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 // ////////////////////////////////////////////////////////////////////////// //
348 enum MathOpType {
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 {
355 MathOpType type;
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!"
358 int opcode;
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),
371 // binaries
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)
403 // for now
404 #define PRIO_MAX PRIO_TERNARY
405 // for now
406 #define PRIO_NO_ASSIGN PRIO_TERNARY
408 #undef DEFOP
409 #undef ENDOP
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());
425 delete lhs;
426 return e;
430 //==========================================================================
432 // ParsePostfixIncDec
434 //==========================================================================
435 static VExpression *ParsePostfixIncDec (VScriptParser *sc, VClass *Class, VExpression *lhs) {
436 int opc = 0;
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);
446 // build assignment
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);
457 enum {
458 LHS_NONLOGICAL,
459 LHS_LOGICAL,
460 LHS_COMPARISON,
463 //==========================================================================
465 // ClassifyLogicalExpression
467 //==========================================================================
468 static int ClassifyLogicalExpression (VExpression *lhs) {
469 if (!lhs) return LHS_NONLOGICAL; // just in case
470 // (...)?
471 if (lhs->IsParens()) {
472 VExprParens *ep = (VExprParens *)lhs;
473 return ClassifyLogicalExpression(ep->op);
475 // logical op?
476 if (lhs->IsBinaryLogical()) return LHS_LOGICAL;
477 // comparison?
478 if (lhs->IsBinaryMath() && ((VBinary *)lhs)->IsComparison()) return LHS_COMPARISON;
479 // oops
480 return LHS_NONLOGICAL;
484 //==========================================================================
486 // VParser::ParseExpressionGeneral
488 //==========================================================================
489 static VExpression *ParseExpressionGeneral (VScriptParser *sc, VClass *Class, int prio) {
490 vassert(prio >= 0);
492 // term
493 if (prio == 0) {
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());
500 // integer?
501 if (sc->CheckNumber()) {
502 vint32 Val = sc->Number;
503 return new VIntLiteral(Val, sc->GetVCLoc());
506 // float?
507 if (sc->CheckFloat()) {
508 float Val = sc->Float;
509 return new VFloatLiteral(Val, sc->GetVCLoc());
512 // booleans?
513 if (sc->Check("false")) return new VIntLiteral(0, sc->GetVCLoc());
514 if (sc->Check("true")) return new VIntLiteral(1, sc->GetVCLoc());
516 // subexpression?
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");
521 sc->Expect(")");
522 return new VExprParens(op, l);
525 // identifier?
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");
537 sc->Expect("]");
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("["))
548 sc->ExpectString();
549 sc->Expect("]");
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);
565 // some unknown shit
566 sc->Error("expression expected");
567 return nullptr;
570 // indexing
571 if (prio == 1) {
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");
577 sc->Expect("]");
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);
581 delete op;
582 op = e;
584 return op;
587 // unaries
588 if (prio == 2) {
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);
593 // quoted crap
594 if (sc->QuotedString && token == "-") {
595 return new VDecorateSingleName(sc->String, sc->GetVCLoc());
597 // prefix increment
598 if (inCodeBlock) {
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());
619 sc->RestorePos(sp);
621 // process unaries
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);
636 return lhs;
640 // no unary found
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);
646 return lhs;
649 // ternary
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; }
657 sc->Expect(":");
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);
662 return cond;
665 // binaries
666 VExpression *lhs = ParseExpressionGeneral(sc, Class, prio-1);
667 if (!lhs) return nullptr; // some error
668 for (;;) {
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);
679 switch (lcls) {
680 case LHS_LOGICAL:
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());
684 break;
685 case LHS_COMPARISON:
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());
689 break;
690 default:
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());
694 token = "|";
695 break;
697 } else if (token.strEqu("&&")) {
698 int lcls = ClassifyLogicalExpression(lhs);
699 switch (lcls) {
700 case LHS_LOGICAL:
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());
704 break;
705 case LHS_COMPARISON:
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());
709 break;
710 default:
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());
714 token = "&";
715 break;
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());
721 token = "==";
723 } else if (!inCodeBlock) {
724 // Spanish Inquisition is silenced
725 vassert(skipSpanishInquisition);
726 #if 0
727 if (token == "||" || token == "&&" || token == "&" || token == "|") {
728 GCon->Logf(NAME_Debug, "%s: Spanish Inquisition is silenced (token=`%s`)", *sc->GetVCLoc().toStringNoCol(), *token);
730 #endif
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());
735 token = "==";
738 // find math operator
739 for (; mop->op; ++mop) {
740 if (mop->prio != prio) continue;
741 if (token.strEqu(mop->op)) break;
743 // not found?
744 if (!mop->op) {
745 // we're done with this priority level, return a token, and get out
746 sc->UnGet();
747 break;
749 // get rhs
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
753 switch (mop->type) {
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...
760 return lhs;
764 //==========================================================================
766 // ParseExpressionNoAssign
768 //==========================================================================
769 static VExpression *ParseExpressionNoAssign (VScriptParser *sc, VClass *Class) {
770 return ParseExpressionGeneral(sc, Class, PRIO_NO_ASSIGN);
774 //==========================================================================
776 // ParseExpression
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");
786 return asse;
789 VExpression *lhs = ParseExpressionNoAssign(sc, Class);
790 // this can be assign (if we are in a codeblock)
791 if (!lhs || !inCodeBlock) return lhs;
793 // easy case?
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);
801 } else {
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;
814 else return lhs;
815 //!GCon->Logf(NAME_Debug, "ASSIGN(%s): %s", *sc->String, *lhs->toString());
816 const TLocation l = sc->GetVCLoc();
817 lhs = ParseConvertToUserVar(sc, Class, lhs);
818 // get new value
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);
841 // drop `op0`
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
846 delete expr;
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];
860 int NumArgs = 0;
862 if (inCodeBlock) {
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);
877 if (inCodeBlock) {
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);
894 } else {
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;
899 if (!Func) {
900 //GLog.Logf("ERROR000: %s: Unknown state action `%s` in `%s` (replaced with NOP)", *actionLoc.toStringNoCol(), *FuncName, Class->GetName());
901 //return nullptr;
902 callExpr = new VDecorateInvocation(VName(*FuncName/*, VName::AddLower*/), stloc, NumArgs, Args);
903 } else {
904 VDecorateInvocation *Expr = new VDecorateInvocation(Func, VName(*FuncName), stloc, NumArgs, Args);
905 Expr->CallerState = State;
906 callExpr = Expr;
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.");
923 sc->Error(msg);
927 //==========================================================================
929 // ParseStatementFor
931 //==========================================================================
932 static VStatement *ParseStatementFor (VScriptParser *sc, VClass *Class, VState *State) {
933 CheckUnsafeStatement(sc, "`for` is not allowed");
935 auto stloc = sc->GetVCLoc();
937 sc->Expect("(");
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(";")) {
946 for (;;) {
947 VExpression *expr = ParseExpression(sc, Class);
948 if (!expr) break;
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;
955 sc->Expect(";");
958 VFor *forstmt = new VFor(stloc);
959 Comp->Statements.Append(forstmt);
961 // parse cond expr(s)
962 VExpression *lastCondExpr = nullptr;
963 if (!sc->Check(";")) {
964 for (;;) {
965 VExpression *expr = ParseExpression(sc, Class);
966 if (!expr) break;
967 if (lastCondExpr) forstmt->CondExpr.append(ParseCreateDropResult(lastCondExpr));
968 lastCondExpr = expr;
969 // here should be a comma or a semicolon
970 if (!sc->Check(",")) break;
972 sc->Expect(";");
974 if (lastCondExpr) forstmt->CondExpr.append(lastCondExpr);
976 // parse loop expr(s)
977 if (!sc->Check(")")) {
978 for (;;) {
979 VExpression *expr = ParseExpression(sc, Class);
980 if (!expr) break;
981 forstmt->LoopExpr.append(ParseCreateDropResult(expr));
982 // here should be a comma or a right paren
983 if (!sc->Check(",")) break;
985 sc->Expect(")");
988 forstmt->Statement = ParseActionStatement(sc, Class, State);
990 return Comp;
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();
1004 sc->Expect("(");
1005 VExpression *cond = ParseExpression(sc, Class);
1006 if (!cond) sc->Error("`while` loop expression expected");
1007 sc->Expect(")");
1009 VStatement *body = ParseActionStatement(sc, Class, State);
1011 return new VWhile(cond, body, stloc);
1015 //==========================================================================
1017 // ParseStatementDo
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");
1028 sc->Expect("(");
1029 VExpression *cond = ParseExpression(sc, Class);
1030 if (!cond) sc->Error("`do` loop expression expected");
1031 sc->Expect(")");
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;
1048 VStatement *st;
1049 if (sc->Check("{")) sc->UnGet(); // cheat a little
1050 st = ParseActionStatement(sc, Class, State);
1051 if (st) stmt->Statements.append(st);
1053 return stmt;
1056 if (sc->Check(";")) return nullptr;
1058 auto stloc = sc->GetVCLoc();
1060 // local var
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);
1070 else {
1071 //sc->Error("invalid local type");
1072 // `auto` type
1073 typeExpr = new VTypeExprSimple(TYPE_Automatic, stloc);
1076 VLocalDecl *ldecl = new VLocalDecl(stloc);
1077 do {
1078 // parse name
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;
1086 // create local
1087 VLocalEntry e;
1088 e.TypeExpr = typeExpr->SyntaxCopy();
1089 e.Name = VName(*sc->String, VName::AddLower);
1090 e.Loc = l;
1091 // initialisation
1092 e.Value = nullptr;
1093 if (sc->Check("=")) e.Value = ParseExpressionNoAssign(sc, Class);
1094 ldecl->Vars.Append(e);
1095 // append it here
1096 if (!decoAddLocalName(vname)) sc->Error(va("duplicate local name '%s'", *vname));
1097 } while (sc->Check(","));
1098 delete typeExpr;
1099 sc->Expect(";");
1100 return new VLocalVarStatement(ldecl);
1103 // if
1104 if (sc->Check("if")) {
1105 sc->Expect("(");
1106 VExpression *cond = ParseExpression(sc, Class);
1107 sc->Expect(")");
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);
1119 // return
1120 if (sc->Check("return")) {
1121 if (sc->Check("state")) {
1122 // return state("name");
1123 // specials: `state()`, `state(0)`, `state("")`
1124 sc->Expect("(");
1125 if (sc->Check(")")) {
1126 sc->Expect(";");
1127 return new VReturn(nullptr, stloc);
1129 VExpression *ste = ParseExpression(sc, Class);
1130 if (!ste) sc->Error("invalid `return`");
1131 sc->Expect(")");
1132 sc->Expect(";");
1133 // check for specials
1134 // replace `+number` with `number`
1135 for (;;) {
1136 if (ste->IsUnaryMath()) {
1137 VUnary *un = (VUnary *)ste;
1138 if (un->op) {
1139 if (un->Oper == VUnary::Plus && (un->op->IsIntConst() || un->op->IsFloatConst())) {
1140 VExpression *etmp = un->op;
1141 un->op = nullptr;
1142 delete ste;
1143 ste = etmp;
1144 continue;
1148 break;
1150 if (!ste) sc->Error("invalid `return`"); // just in case
1151 // `state(0)`?
1152 if ((ste->IsIntConst() && ste->GetIntConst() == 0) ||
1153 (ste->IsFloatConst() && ste->GetFloatConst() == 0))
1155 delete ste;
1156 return new VReturn(nullptr, stloc);
1158 // `state("")`?
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) {
1164 delete ste;
1165 return new VReturn(nullptr, stloc);
1168 if (ste->IsDecorateSingleName()) {
1169 VDecorateSingleName *e = (VDecorateSingleName *)ste;
1170 if (e->Name.length() == 0) {
1171 delete ste;
1172 return new VReturn(nullptr, stloc);
1175 // call `decorate_A_RetDoJump`
1176 VExpression *Args[1];
1177 Args[0] = ste;
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));
1183 return cst;
1184 } else if (sc->Check(";")) {
1185 // return;
1186 return new VReturn(nullptr, stloc);
1187 } else {
1188 // return expr;
1189 // just call `expr`, and do normal return
1190 VStatement *fs = ParseActionStatement(sc, Class, State);
1191 if (!fs) return new VReturn(nullptr, stloc);
1192 // create compound
1193 VCompound *cst = new VCompound(stloc);
1194 cst->Statements.append(fs);
1195 cst->Statements.append(new VReturn(nullptr, stloc));
1196 return cst;
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);
1206 sc->Expect(";");
1207 if (!res) sc->Error("invalid action statement");
1208 return res;
1212 //==========================================================================
1214 // CreateStateActionCallWrapperStmt
1216 //==========================================================================
1217 static VMethod *CreateStateActionCallWrapperStmt (VStatement *Stmt, VClass *Class, VState *State, const TLocation &Loc) {
1218 vassert(Stmt);
1219 vassert(State);
1220 (void)Class;
1221 #if defined(VC_DECORATE_ACTION_BELONGS_TO_STATE)
1222 VMethod *M = new VMethod(NAME_None, State, Loc);
1223 #else
1224 VMethod *M = new VMethod(NAME_None, Class, Loc);
1225 #endif
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;
1230 M->NumParams = 0;
1231 #if !defined(VC_DECORATE_ACTION_BELONGS_TO_STATE)
1232 Class->AddMethod(M);
1233 M->Define();
1234 #endif
1235 return M;
1239 //==========================================================================
1241 // CreateStateActionCallWrapper
1243 //==========================================================================
1244 static VMethod *CreateStateActionCallWrapper (VExpression *expr, VClass *Class, VState *State, const TLocation &Loc) {
1245 vassert(expr);
1246 vassert(State);
1247 VExpressionStatement *Stmt = new VExpressionStatement(expr);
1248 return CreateStateActionCallWrapperStmt(Stmt, Class, State, Loc);
1252 //==========================================================================
1254 // ParseActionBlock
1256 // "{" checked
1258 //==========================================================================
1259 static void ParseActionBlock (VScriptParser *sc, VClass *Class, VState *State) {
1260 DecoLocalsMarker lmark;
1261 bool oldicb = inCodeBlock;
1262 inCodeBlock = true;
1263 VCompound *stmt = new VCompound(sc->GetVCLoc());
1264 while (!sc->Check("}")) {
1265 VStatement *st = ParseActionStatement(sc, Class, State);
1266 if (!st) continue;
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;
1275 } else {
1276 delete stmt;
1277 State->Function = nullptr;
1282 //==========================================================================
1284 // ParseActionCall
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];
1294 int NumArgs = 0;
1295 auto actionLoc = sc->GetVCLoc();
1296 VStr FuncName = sc->String;
1297 VMethod *Func = nullptr;
1299 VStatement *suvst = CheckParseSetUserVarStmt(sc, Class, FuncName);
1300 if (suvst) {
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());
1306 } else {
1307 bool inIgnoreList = false;
1308 Func = ParseFunCallWithName(sc, FuncName, Class, NumArgs, Args, false, &inIgnoreList); // no paren
1309 if (inIgnoreList) {
1310 // ignored state action
1311 vassert(!Func);
1312 State->FunctionName = NAME_None;
1313 } else if (!Func) {
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) {
1319 if (Args[i]) {
1320 delete Args[i];
1321 Args[i] = nullptr;
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());
1330 } else {
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);
1335 Func = nullptr;
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) {
1352 vassert(sc);
1353 vassert(Class);
1354 if (!State) return;
1356 if (!Func) {
1357 State->Function = nullptr;
1358 State->FunctionName = NAME_None;
1359 return;
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());
1367 } else {
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);
1371 Func = nullptr;
1374 State->Function = Func;
1375 if (Func) State->FunctionName = NAME_None;