/** * @brief Parse actions and return action list * @return The first element from a list of action * @sa ea_t * @todo need cleanup, compute action out of the final memory; reduce number of var */ static uiAction_t* UI_ParseActionList (uiNode_t* node, const char** text, const char** token) { const char* errhead = "UI_ParseActionList: unexpected end of file (in event)"; uiAction_t* firstAction; uiAction_t* lastAction; uiAction_t* action; lastAction = nullptr; firstAction = nullptr; /* prevent bad position */ if ((*token)[0] != '{') { Com_Printf("UI_ParseActionList: token \"{\" expected, but \"%s\" found (in event) (node: %s)\n", *token, UI_GetPath(node)); return nullptr; } while (true) { bool result; int type = EA_NULL; /* get new token */ *token = Com_EParse(text, errhead, nullptr); if (!*token) return nullptr; if ((*token)[0] == '}') break; type = UI_GetActionTokenType(*token, EA_ACTION); /* setter form */ if (type == EA_NULL && (*token)[0] == '*') type = EA_ASSIGN; /* unknown, we break the parsing */ if (type == EA_NULL) { Com_Printf("UI_ParseActionList: unknown token \"%s\" ignored (in event) (node: %s)\n", *token, UI_GetPath(node)); return nullptr; } /* add the action */ action = UI_AllocStaticAction(); /** @todo better to append the action after initialization */ if (lastAction) lastAction->next = action; if (!firstAction) firstAction = action; action->type = type; /* decode action */ switch (action->type) { case EA_CMD: /* get parameter values */ *token = Com_EParse(text, errhead, nullptr); if (!*text) return nullptr; /* get the value */ action->d.terminal.d1.constString = UI_AllocStaticString(*token, 0); break; case EA_ASSIGN: result = UI_ParseSetAction(node, action, text, token, errhead); if (!result) return nullptr; break; case EA_CALL: result = UI_ParseCallAction(node, action, text, token, errhead); if (!result) return nullptr; break; case EA_DELETE: { uiAction_t* expression; expression = UI_ParseExpression(text); if (expression == nullptr) return nullptr; if (expression->type != EA_VALUE_CVARNAME) { Com_Printf("UI_ParseActionList: \"delete\" keyword only support cvarname (node: %s)\n", UI_GetPath(node)); return nullptr; } action->d.nonTerminal.left = expression; break; } case EA_ELIF: /* check previous action */ if (!lastAction || (lastAction->type != EA_IF && lastAction->type != EA_ELIF)) { Com_Printf("UI_ParseActionList: 'elif' must be set after an 'if' or an 'elif' (node: %s)\n", UI_GetPath(node)); return nullptr; } /* then it execute EA_IF, no break */ case EA_WHILE: case EA_IF: { uiAction_t* expression; /* get the condition */ expression = UI_ParseExpression(text); if (expression == nullptr) return nullptr; action->d.nonTerminal.left = expression; /* get the action block */ *token = Com_EParse(text, errhead, nullptr); if (!*text) return nullptr; action->d.nonTerminal.right = UI_ParseActionList(node, text, token); if (action->d.nonTerminal.right == nullptr) { switch (action->type) { case EA_IF: Com_Printf("UI_ParseActionList: block expected after \"if\" (node: %s)\n", UI_GetPath(node)); break; case EA_ELIF: Com_Printf("UI_ParseActionList: block expected after \"elif\" (node: %s)\n", UI_GetPath(node)); break; case EA_WHILE: Com_Printf("UI_ParseActionList: block expected after \"while\" (node: %s)\n", UI_GetPath(node)); break; default: Com_Printf("UI_ParseActionList: cannot determine statement type (node: %s)\n", UI_GetPath(node)); } return nullptr; } break; } case EA_FORCHILDIN: { uiAction_t* expression; /* get the node */ expression = UI_ParseExpression(text); if (expression == nullptr) { return nullptr; } action->d.nonTerminal.left = expression; /* check the type */ type = action->d.nonTerminal.left->type; if (type != EA_VALUE_PATHNODE && type != EA_VALUE_PATHNODE_WITHINJECTION) { Com_Printf("UI_ParseActionList: Node property expected. Type '%x' found\n", type); return nullptr; } /* get the action block */ *token = Com_EParse(text, errhead, nullptr); if (!*text) return nullptr; action->d.nonTerminal.right = UI_ParseActionList(node, text, token); if (action->d.nonTerminal.right == nullptr) { Com_Printf("UI_ParseActionList: block expected after \"forchildin\" (node: %s)\n", UI_GetPath(node)); return nullptr; } break; } case EA_ELSE: /* check previous action */ if (!lastAction || (lastAction->type != EA_IF && lastAction->type != EA_ELIF)) { Com_Printf("UI_ParseActionList: 'else' must be set after an 'if' or an 'elif' (node: %s)\n", UI_GetPath(node)); return nullptr; } /* get the action block */ *token = Com_EParse(text, errhead, nullptr); if (!*text) return nullptr; action->d.nonTerminal.left = nullptr; action->d.nonTerminal.right = UI_ParseActionList(node, text, token); if (action->d.nonTerminal.right == nullptr) { Com_Printf("UI_ParseActionList: block expected after \"else\" (node: %s)\n", UI_GetPath(node)); return nullptr; } break; default: assert(false); } /* step */ lastAction = action; } assert((*token)[0] == '}'); /* return non nullptr value */ if (firstAction == nullptr) { firstAction = UI_AllocStaticAction(); } return firstAction; }
uiAction_t *UI_ParseExpression (const char **text) { const char* token; token = Com_Parse(text); if (*text == nullptr) return nullptr; if (Q_streq(token, "(")) { uiAction_t *expression; uiAction_t *e; e = UI_ParseExpression(text); token = Com_Parse(text); if (*text == nullptr) return nullptr; /* unary operator or unneed "( ... )" */ if (Q_streq(token, ")")) return e; /* then its an operator */ expression = UI_AllocStaticAction(); expression->d.nonTerminal.left = e; expression->type = UI_GetActionTokenType(token, EA_BINARYOPERATOR); if (expression->type == EA_nullptr) { Com_Printf("UI_ParseExpression: Invalid 'expression' statement. Unknown '%s' operator\n", token); return nullptr; } e = UI_ParseExpression(text); expression->d.nonTerminal.right = e; token = Com_Parse(text); if (*text == nullptr) return nullptr; if (!Q_streq(token, ")")) { Com_Printf("UI_ParseExpression: Token ')' expected\n"); return nullptr; } return expression; } else { const int type = UI_GetActionTokenType(token, EA_UNARYOPERATOR); if (type == EA_nullptr) { Com_UnParseLastToken(); return UI_ParseValueExpression(text); } else { uiAction_t *expression = UI_AllocStaticAction(); uiAction_t *e; e = UI_ParseExpression(text); expression->type = type; expression->d.nonTerminal.left = e; if (expression->type == EA_OPERATOR_EXISTS) { switch (e->type) { case EA_VALUE_CVARNAME: case EA_VALUE_CVARNAME_WITHINJECTION: case EA_VALUE_PATHNODE: case EA_VALUE_PATHNODE_WITHINJECTION: break; default: Com_Printf("UI_ParseExpression: Cvar or Node path expected, but type %d found\n", e->type); return nullptr; } } return expression; } } }