/** * Apply an action value to a node property. If the tuple property/value allow it, the function * pre compute the value and update the action value to speed up the next call. * @param node Node to edit * @param property Property of the node to edit * @param value Action value containing the value to set to the node property * @param context Call context of the script * @todo refactoring it to remove "context", we should only call that function when the action * value is a leaf (then a value, and not an expression) */ static void UI_NodeSetPropertyFromActionValue (uiNode_t *node, const value_t *property, const uiCallContext_t *context, uiAction_t* value) { /* @todo we can use a new EA_VALUE type to flag already parsed values, we dont need to do it again and again */ /* pre compute value if possible */ if (value->type == EA_VALUE_STRING) { const char* string = value->d.terminal.d1.constString; if ((property->type & V_UI_MASK) == V_UI_CVAR && Q_strstart(string, "*cvar:")) { Com_GetValue<void*>(node, property) = value->d.terminal.d1.data; } else { /** @todo here we must catch error in a better way, and using cvar for error code to create unittest automations */ UI_InitRawActionValue(value, node, property, string); } } /* decode RAW value */ if (value->type == EA_VALUE_RAW) { const void *rawValue = value->d.terminal.d1.constData; const int rawType = value->d.terminal.d2.integer; UI_NodeSetPropertyFromRAW(node, property, rawValue, rawType); } /* else it is an expression */ else { /** @todo we should improve if when the prop is a boolean/int/float */ const char* string = UI_GetStringFromExpression(value, context); UI_NodeSetProperty(node, property, string); } }
/** * @brief Parser for setter command */ static bool UI_ParseSetAction (uiNode_t* node, uiAction_t* action, const char** text, const char** token, const char* errhead) { const value_t* property; int type; uiAction_t* localAction; assert((*token)[0] == '*'); Com_UnParseLastToken(); action->d.nonTerminal.left = UI_ParseExpression(text); type = action->d.nonTerminal.left->type; if (type != EA_VALUE_CVARNAME && type != EA_VALUE_CVARNAME_WITHINJECTION && type != EA_VALUE_PATHPROPERTY && type != EA_VALUE_PATHPROPERTY_WITHINJECTION) { Com_Printf("UI_ParseSetAction: Cvar or Node property expected. Type '%i' found\n", type); return false; } /* must use "equal" char between name and value */ *token = Com_EParse(text, errhead, nullptr); if (!*text) return false; if (!Q_streq(*token, "=")) { Com_Printf("UI_ParseSetAction: Assign sign '=' expected between variable and value. '%s' found in node %s.\n", *token, UI_GetPath(node)); return false; } /* get the value */ if (type == EA_VALUE_CVARNAME || type == EA_VALUE_CVARNAME_WITHINJECTION) { action->d.nonTerminal.right = UI_ParseExpression(text); return true; } property = (const value_t*) action->d.nonTerminal.left->d.terminal.d2.data; *token = Com_EParse(text, errhead, nullptr); if (!*text) return false; if (Q_streq(*token, "{")) { uiAction_t* actionList; if (property != nullptr && property->type != V_UI_ACTION) { Com_Printf("UI_ParseSetAction: Property %s@%s do not expect code block.\n", UI_GetPath(node), property->string); return false; } actionList = UI_ParseActionList(node, text, token); if (actionList == nullptr) return false; localAction = UI_AllocStaticAction(); localAction->type = EA_VALUE_RAW; localAction->d.terminal.d1.data = actionList; localAction->d.terminal.d2.integer = V_UI_ACTION; action->d.nonTerminal.right = localAction; return true; } if (Q_streq(*token, "(")) { Com_UnParseLastToken(); action->d.nonTerminal.right = UI_ParseExpression(text); return true; } /* @todo everything should come from UI_ParseExpression */ if (UI_IsInjectedString(*token)) { localAction = UI_AllocStaticAction(); localAction->type = EA_VALUE_STRING_WITHINJECTION; localAction->d.terminal.d1.data = UI_AllocStaticString(*token, 0); action->d.nonTerminal.right = localAction; return true; } localAction = UI_AllocStaticAction(); UI_InitRawActionValue(localAction, node, property, *token); action->d.nonTerminal.right = localAction; return true; }