/** * @brief Parser for call command */ static bool UI_ParseCallAction (uiNode_t* node, uiAction_t* action, const char** text, const char** token, const char* errhead) { uiAction_t* expression; uiAction_t* lastParam = nullptr; int paramID = 0; expression = UI_ParseExpression(text); if (expression == nullptr) return false; if (expression->type != EA_VALUE_PATHNODE_WITHINJECTION && expression->type != EA_VALUE_PATHNODE && expression->type != EA_VALUE_PATHPROPERTY && expression->type != EA_VALUE_PATHPROPERTY_WITHINJECTION) { Com_Printf("UI_ParseCallAction: \"call\" keyword only support pathnode and pathproperty (node: %s)\n", UI_GetPath(node)); return false; } action->d.nonTerminal.left = expression; /* check parameters */ *token = Com_EParse(text, errhead, nullptr); if ((*token)[0] == '\0') return false; /* there is no parameters */ if (!Q_streq(*token, "(")) { Com_UnParseLastToken(); return true; } /* read parameters */ do { uiAction_t* param; paramID++; /* parameter */ param = UI_ParseExpression(text); if (param == nullptr) { Com_Printf("UI_ParseCallAction: problem with the %i parameter\n", paramID); return false; } if (lastParam == nullptr) action->d.nonTerminal.right = param; else lastParam->next = param; lastParam = param; /* separator */ *token = Com_EParse(text, errhead, nullptr); if (!*token) return false; if (!Q_streq(*token, ",")) { if (Q_streq(*token, ")")) break; Com_UnParseLastToken(); Com_Printf("UI_ParseCallAction: Invalidate end of 'call' after param %i\n", paramID); return false; } } while(true); return true; }
/** * @brief unittest around default use of parser */ TEST_F(ParserTest, ParserWithUnParse) { const char* string = "aaaaa\n\tbbbbb \"ccccc\""; const char* cursor = string; const char* token; token = Com_Parse(&cursor); ASSERT_NE(Com_GetType(&cursor), TT_QUOTED_WORD); ASSERT_STREQ(token, "aaaaa"); Com_UnParseLastToken(); token = Com_Parse(&cursor); ASSERT_NE(Com_GetType(&cursor), TT_QUOTED_WORD); ASSERT_STREQ(token, "aaaaa"); Com_UnParseLastToken(); token = Com_Parse(&cursor); ASSERT_NE(Com_GetType(&cursor), TT_QUOTED_WORD); ASSERT_STREQ(token, "aaaaa"); token = Com_Parse(&cursor); ASSERT_NE(Com_GetType(&cursor), TT_QUOTED_WORD); ASSERT_STREQ(token, "bbbbb"); Com_UnParseLastToken(); token = Com_Parse(&cursor); ASSERT_NE(Com_GetType(&cursor), TT_QUOTED_WORD); ASSERT_STREQ(token, "bbbbb"); Com_UnParseLastToken(); token = Com_Parse(&cursor); ASSERT_NE(Com_GetType(&cursor), TT_QUOTED_WORD); ASSERT_STREQ(token, "bbbbb"); token = Com_Parse(&cursor); ASSERT_EQ(Com_GetType(&cursor), TT_QUOTED_WORD); ASSERT_STREQ(token, "ccccc"); Com_UnParseLastToken(); token = Com_Parse(&cursor); ASSERT_EQ(Com_GetType(&cursor), TT_QUOTED_WORD); ASSERT_STREQ(token, "ccccc"); Com_UnParseLastToken(); token = Com_Parse(&cursor); ASSERT_EQ(Com_GetType(&cursor), TT_QUOTED_WORD); ASSERT_STREQ(token, "ccccc"); }
/** * @brief unittest around default use of parser */ static void testParserWithUnParse (void) { const char* string = "aaaaa\n\tbbbbb \"ccccc\""; const char* cursor = string; const char *token; token = Com_Parse(&cursor); CU_ASSERT_FALSE(Com_ParsedTokenIsQuoted()); CU_ASSERT_STRING_EQUAL(token, "aaaaa"); Com_UnParseLastToken(); token = Com_Parse(&cursor); CU_ASSERT_FALSE(Com_ParsedTokenIsQuoted()); CU_ASSERT_STRING_EQUAL(token, "aaaaa"); Com_UnParseLastToken(); token = Com_Parse(&cursor); CU_ASSERT_FALSE(Com_ParsedTokenIsQuoted()); CU_ASSERT_STRING_EQUAL(token, "aaaaa"); token = Com_Parse(&cursor); CU_ASSERT_FALSE(Com_ParsedTokenIsQuoted()); CU_ASSERT_STRING_EQUAL(token, "bbbbb"); Com_UnParseLastToken(); token = Com_Parse(&cursor); CU_ASSERT_FALSE(Com_ParsedTokenIsQuoted()); CU_ASSERT_STRING_EQUAL(token, "bbbbb"); Com_UnParseLastToken(); token = Com_Parse(&cursor); CU_ASSERT_FALSE(Com_ParsedTokenIsQuoted()); CU_ASSERT_STRING_EQUAL(token, "bbbbb"); token = Com_Parse(&cursor); CU_ASSERT_TRUE(Com_ParsedTokenIsQuoted()); CU_ASSERT_STRING_EQUAL(token, "ccccc"); Com_UnParseLastToken(); token = Com_Parse(&cursor); CU_ASSERT_TRUE(Com_ParsedTokenIsQuoted()); CU_ASSERT_STRING_EQUAL(token, "ccccc"); Com_UnParseLastToken(); token = Com_Parse(&cursor); CU_ASSERT_TRUE(Com_ParsedTokenIsQuoted()); CU_ASSERT_STRING_EQUAL(token, "ccccc"); }
/** * @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; }
/** * @brief Reads the sequence values from given text-pointer * @sa CL_ParseClientData */ void CL_ParseSequence (const char *name, const char **text) { const char *errhead = "CL_ParseSequence: unexpected end of file (sequence "; sequence_t *sp; const char *token; int i; /* search for sequences with same name */ for (i = 0; i < numSequences; i++) if (Q_streq(name, sequences[i].name)) break; if (i < numSequences) { Com_Printf("CL_ParseSequence: sequence def \"%s\" with same name found, second ignored\n", name); return; } /* initialize the sequence */ if (numSequences >= MAX_SEQUENCES) Com_Error(ERR_FATAL, "Too many sequences"); sp = &sequences[numSequences++]; OBJZERO(*sp); Q_strncpyz(sp->name, name, sizeof(sp->name)); sp->start = numSeqCmds; /* get it's body */ token = Com_Parse(text); if (!*text || *token != '{') { Com_Printf("CL_ParseSequence: sequence def \"%s\" without body ignored\n", name); numSequences--; return; } do { token = Com_EParse(text, errhead, name); if (!*text) break; if (*token == '}') break; /* check for commands */ int i = CL_FindSequenceCommand(token); if (i != -1) { int maxLength = MAX_DATA_LENGTH; char *data; seqCmd_t *sc; /* found a command */ token = Com_EParse(text, errhead, name); if (!*text) return; if (numSeqCmds >= MAX_SEQCMDS) Com_Error(ERR_FATAL, "Too many sequence commands for %s", name); /* init seqCmd */ if (seqCmds == NULL) seqCmds = Mem_PoolAllocTypeN(seqCmd_t, MAX_SEQCMDS, cl_genericPool); sc = &seqCmds[numSeqCmds++]; OBJZERO(*sc); sc->handler = seqCmdFunc[i]; sp->length++; /* copy name */ Q_strncpyz(sc->name, token, sizeof(sc->name)); /* read data */ token = Com_EParse(text, errhead, name); if (!*text) return; if (*token == '{') { // TODO depth is useless IMHO (bayo) int depth = 1; data = &sc->data[0]; while (depth) { if (maxLength <= 0) { Com_Printf("Too much data for sequence %s", sc->name); break; } token = Com_EParse(text, errhead, name); if (!*text) return; if (*token == '{') depth++; else if (*token == '}') depth--; if (depth) { Q_strncpyz(data, token, maxLength); data += strlen(token) + 1; maxLength -= (strlen(token) + 1); } } } else if (*token == '(') { linkedList_t *list; Com_UnParseLastToken(); if (!Com_ParseList(text, &list)) { Com_Error(ERR_DROP, "CL_ParseSequence: error while reading list (sequence \"%s\")", name); } data = &sc->data[0]; for (linkedList_t *element = list; element != NULL; element = element->next) { if (maxLength <= 0) { Com_Printf("Too much data for sequence %s", sc->name); break; } const char* v = (char*)element->data; Q_strncpyz(data, v, maxLength); data += strlen(v) + 1; maxLength -= (strlen(v) + 1); } LIST_Delete(&list); } else { Com_UnParseLastToken(); } } else { Com_Printf("CL_ParseSequence: unknown command \"%s\" ignored (sequence %s)\n", token, name); Com_EParse(text, errhead, name); } } while (*text); }
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; } } }