#include "arc/std/config.h" #include "arc/std/parser/helpers.h" #include "arc/std/bool.h" #include "arc/std/errno.h" #include "arc/std/hashtable.h" #include "arc/std/parser.h" #include #include #include #include struct ARC_Config { ARC_Parser *parser; ARC_Hashtable *types; ARC_Hashtable *groups; ARC_Hashtable *currentGroup; ARC_Bool load; }; typedef struct ARC_ConfigTypeData { void *data; ARC_ConfigType_DestroyFn destroyFn; } ARC_ConfigTypeData; void ARC_Config_InitLexerRulesFn(ARC_Lexer *lexer){ ARC_Lexer_RegisterTokenRule(lexer, ARC_LexerTokenRule_CreateAndReturnMatchCharRule(ARC_CONFIG_TAB , '\t')); ARC_Lexer_RegisterTokenRule(lexer, ARC_LexerTokenRule_CreateAndReturnMatchCharRule(ARC_CONFIG_NEWLINE, '\n')); ARC_Lexer_RegisterTokenRule(lexer, ARC_LexerTokenRule_CreateAndReturnMatchCharRule(ARC_CONFIG_SPACE , ' ' )); ARC_Lexer_RegisterTokenRule(lexer, ARC_LexerTokenRule_CreateAndReturnMatchCharRule(ARC_CONFIG_BANG , '!' )); ARC_Lexer_RegisterTokenRule(lexer, ARC_LexerTokenRule_CreateAndReturnMatchCharRule(ARC_CONFIG_QUOTE , '"' )); ARC_Lexer_RegisterTokenRule(lexer, ARC_LexerTokenRule_CreateAndReturnMatchCharRule(ARC_CONFIG_HASH , '#' )); ARC_Lexer_RegisterTokenRule(lexer, ARC_LexerTokenRule_CreateAndReturnMatchCharRule(ARC_CONFIG_DOLLAR , '$' )); ARC_Lexer_RegisterTokenRule(lexer, ARC_LexerTokenRule_CreateAndReturnMatchCharRule(ARC_CONFIG_PERCENT , '%' )); ARC_Lexer_RegisterTokenRule(lexer, ARC_LexerTokenRule_CreateAndReturnMatchCharRule(ARC_CONFIG_AMPERSAND , '&' )); ARC_Lexer_RegisterTokenRule(lexer, ARC_LexerTokenRule_CreateAndReturnMatchCharRule(ARC_CONFIG_SINGLE_QUOTE, '\'')); ARC_Lexer_RegisterTokenRule(lexer, ARC_LexerTokenRule_CreateAndReturnMatchCharRule(ARC_CONFIG_OPEN_PAREN , '(' )); ARC_Lexer_RegisterTokenRule(lexer, ARC_LexerTokenRule_CreateAndReturnMatchCharRule(ARC_CONFIG_CLOSE_PAREN , ')' )); ARC_Lexer_RegisterTokenRule(lexer, ARC_LexerTokenRule_CreateAndReturnMatchCharRule(ARC_CONFIG_ASTERISK , '*' )); ARC_Lexer_RegisterTokenRule(lexer, ARC_LexerTokenRule_CreateAndReturnMatchCharRule(ARC_CONFIG_PLUS , '+' )); ARC_Lexer_RegisterTokenRule(lexer, ARC_LexerTokenRule_CreateAndReturnMatchCharRule(ARC_CONFIG_COMMA , ',' )); ARC_Lexer_RegisterTokenRule(lexer, ARC_LexerTokenRule_CreateAndReturnMatchCharRule(ARC_CONFIG_MINUS , '-' )); ARC_Lexer_RegisterTokenRule(lexer, ARC_LexerTokenRule_CreateAndReturnMatchCharRule(ARC_CONFIG_PERIOD , '.' )); ARC_Lexer_RegisterTokenRule(lexer, ARC_LexerTokenRule_CreateAndReturnMatchCharRule(ARC_CONFIG_SLASH , '/' )); ARC_Lexer_RegisterTokenRule(lexer, ARC_LexerTokenRule_CreateAndReturnMatchCharOrBetween(ARC_CONFIG_NUMBER , '0', '9')); ARC_Lexer_RegisterTokenRule(lexer, ARC_LexerTokenRule_CreateAndReturnMatchCharRule(ARC_CONFIG_COLON , ':')); ARC_Lexer_RegisterTokenRule(lexer, ARC_LexerTokenRule_CreateAndReturnMatchCharRule(ARC_CONFIG_SEMICOLON , ';')); ARC_Lexer_RegisterTokenRule(lexer, ARC_LexerTokenRule_CreateAndReturnMatchCharRule(ARC_CONFIG_LESS_THAN , '<')); ARC_Lexer_RegisterTokenRule(lexer, ARC_LexerTokenRule_CreateAndReturnMatchCharRule(ARC_CONFIG_GREATER_THAN , '>')); ARC_Lexer_RegisterTokenRule(lexer, ARC_LexerTokenRule_CreateAndReturnMatchCharRule(ARC_CONFIG_EQUAL , '=')); ARC_Lexer_RegisterTokenRule(lexer, ARC_LexerTokenRule_CreateAndReturnMatchCharRule(ARC_CONFIG_QUESTION_MARK, '?')); ARC_Lexer_RegisterTokenRule(lexer, ARC_LexerTokenRule_CreateAndReturnMatchCharRule(ARC_CONFIG_AT , '@')); ARC_Lexer_RegisterTokenRule(lexer, ARC_LexerTokenRule_CreateAndReturnMatchCharOrBetween(ARC_CONFIG_ALPHA_UPPER_CHAR, 'A', 'Z')); ARC_Lexer_RegisterTokenRule(lexer, ARC_LexerTokenRule_CreateAndReturnMatchCharRule(ARC_CONFIG_OPEN_BRACKET , '[' )); ARC_Lexer_RegisterTokenRule(lexer, ARC_LexerTokenRule_CreateAndReturnMatchCharRule(ARC_CONFIG_BACKSLASH , '\\')); ARC_Lexer_RegisterTokenRule(lexer, ARC_LexerTokenRule_CreateAndReturnMatchCharRule(ARC_CONFIG_CLOSE_BRACKET, ']' )); ARC_Lexer_RegisterTokenRule(lexer, ARC_LexerTokenRule_CreateAndReturnMatchCharRule(ARC_CONFIG_CARET , '^' )); ARC_Lexer_RegisterTokenRule(lexer, ARC_LexerTokenRule_CreateAndReturnMatchCharRule(ARC_CONFIG_UNDERSCORE , '_' )); ARC_Lexer_RegisterTokenRule(lexer, ARC_LexerTokenRule_CreateAndReturnMatchCharRule(ARC_CONFIG_GRAVE , '`' )); ARC_Lexer_RegisterTokenRule(lexer, ARC_LexerTokenRule_CreateAndReturnMatchCharOrBetween(ARC_CONFIG_ALPHA_LOWER_CHAR, 'a', 'z')); ARC_Lexer_RegisterTokenRule(lexer, ARC_LexerTokenRule_CreateAndReturnMatchCharRule(ARC_CONFIG_OPEN_CURLY_BRACE , '{')); ARC_Lexer_RegisterTokenRule(lexer, ARC_LexerTokenRule_CreateAndReturnMatchCharRule(ARC_CONFIG_VERTICAL_LINE , '|')); ARC_Lexer_RegisterTokenRule(lexer, ARC_LexerTokenRule_CreateAndReturnMatchCharRule(ARC_CONFIG_CLOSE_CURLY_BRACE, '}')); ARC_Lexer_RegisterTokenRule(lexer, ARC_LexerTokenRule_CreateAndReturnMatchCharRule(ARC_CONFIG_TILDE , '~')); } uint32_t ARC_Config_GetStringIdFn(ARC_String *string){ if(ARC_String_EqualsCStringWithStrlen(string, "LAMBDA")){ return ARC_PARSER_TAG_LAMBDA; } if(ARC_String_EqualsCStringWithStrlen(string, "TAB")){ return ARC_CONFIG_TAB; } if(ARC_String_EqualsCStringWithStrlen(string, "NEWLINE")){ return ARC_CONFIG_NEWLINE; } if(ARC_String_EqualsCStringWithStrlen(string, "SPACE")){ return ARC_CONFIG_SPACE; } if(ARC_String_EqualsCStringWithStrlen(string, "BANG")){ return ARC_CONFIG_BANG; } if(ARC_String_EqualsCStringWithStrlen(string, "QUOTE")){ return ARC_CONFIG_QUOTE; } if(ARC_String_EqualsCStringWithStrlen(string, "HASH")){ return ARC_CONFIG_HASH; } if(ARC_String_EqualsCStringWithStrlen(string, "DOLLAR")){ return ARC_CONFIG_DOLLAR; } if(ARC_String_EqualsCStringWithStrlen(string, "PERCENT")){ return ARC_CONFIG_PERCENT; } if(ARC_String_EqualsCStringWithStrlen(string, "AMPERSAND")){ return ARC_CONFIG_AMPERSAND; } if(ARC_String_EqualsCStringWithStrlen(string, "SINGLE_QUOTE")){ return ARC_CONFIG_SINGLE_QUOTE; } if(ARC_String_EqualsCStringWithStrlen(string, "OPEN_PAREN")){ return ARC_CONFIG_OPEN_PAREN; } if(ARC_String_EqualsCStringWithStrlen(string, "CLOSE_PAREN")){ return ARC_CONFIG_CLOSE_PAREN; } if(ARC_String_EqualsCStringWithStrlen(string, "ASTERISK")){ return ARC_CONFIG_ASTERISK; } if(ARC_String_EqualsCStringWithStrlen(string, "PLUS")){ return ARC_CONFIG_PLUS; } if(ARC_String_EqualsCStringWithStrlen(string, "COMMA")){ return ARC_CONFIG_COMMA; } if(ARC_String_EqualsCStringWithStrlen(string, "MINUS")){ return ARC_CONFIG_MINUS; } if(ARC_String_EqualsCStringWithStrlen(string, "PERIOD")){ return ARC_CONFIG_PERIOD; } if(ARC_String_EqualsCStringWithStrlen(string, "SLASH")){ return ARC_CONFIG_SLASH; } if(ARC_String_EqualsCStringWithStrlen(string, "NUMBER")){ return ARC_CONFIG_NUMBER; } if(ARC_String_EqualsCStringWithStrlen(string, "COLON")){ return ARC_CONFIG_COLON; } if(ARC_String_EqualsCStringWithStrlen(string, "SEMICOLON")){ return ARC_CONFIG_SEMICOLON; } if(ARC_String_EqualsCStringWithStrlen(string, "LESS_THAN")){ return ARC_CONFIG_LESS_THAN; } if(ARC_String_EqualsCStringWithStrlen(string, "GREATER_THAN")){ return ARC_CONFIG_GREATER_THAN; } if(ARC_String_EqualsCStringWithStrlen(string, "EQUAL")){ return ARC_CONFIG_EQUAL; } if(ARC_String_EqualsCStringWithStrlen(string, "QUESTION_MARK")){ return ARC_CONFIG_QUESTION_MARK; } if(ARC_String_EqualsCStringWithStrlen(string, "AT")){ return ARC_CONFIG_AT; } if(ARC_String_EqualsCStringWithStrlen(string, "ALPHA_UPPER_CHAR")){ return ARC_CONFIG_ALPHA_UPPER_CHAR; } if(ARC_String_EqualsCStringWithStrlen(string, "OPEN_BRACKET")){ return ARC_CONFIG_OPEN_BRACKET; } if(ARC_String_EqualsCStringWithStrlen(string, "BACKSLASH")){ return ARC_CONFIG_BACKSLASH; } if(ARC_String_EqualsCStringWithStrlen(string, "CLOSE_BRACKET")){ return ARC_CONFIG_CLOSE_BRACKET; } if(ARC_String_EqualsCStringWithStrlen(string, "CARET")){ return ARC_CONFIG_CARET; } if(ARC_String_EqualsCStringWithStrlen(string, "UNDERSCORE")){ return ARC_CONFIG_UNDERSCORE; } if(ARC_String_EqualsCStringWithStrlen(string, "GRAVE")){ return ARC_CONFIG_GRAVE; } if(ARC_String_EqualsCStringWithStrlen(string, "ALPHA_LOWER_CHAR")){ return ARC_CONFIG_ALPHA_LOWER_CHAR; } if(ARC_String_EqualsCStringWithStrlen(string, "OPEN_CURLY_BRACE")){ return ARC_CONFIG_OPEN_CURLY_BRACE; } if(ARC_String_EqualsCStringWithStrlen(string, "VERTICAL_LINE")){ return ARC_CONFIG_VERTICAL_LINE; } if(ARC_String_EqualsCStringWithStrlen(string, "CLOSE_CURLY_BRACE")){ return ARC_CONFIG_CLOSE_CURLY_BRACE; } if(ARC_String_EqualsCStringWithStrlen(string, "TILDE")){ return ARC_CONFIG_TILDE; } if(ARC_String_EqualsCStringWithStrlen(string, "")){ return ARC_CONFIG_LANGUAGE; } if(ARC_String_EqualsCStringWithStrlen(string, "")){ return ARC_CONFIG_GROUP; } if(ARC_String_EqualsCStringWithStrlen(string, "")){ return ARC_CONFIG_GROUP_NAME; } if(ARC_String_EqualsCStringWithStrlen(string, "")){ return ARC_CONFIG_GROUP_ARGS; } if(ARC_String_EqualsCStringWithStrlen(string, "")){ return ARC_CONFIG_VARIABLE_LINES; } if(ARC_String_EqualsCStringWithStrlen(string, "")){ return ARC_CONFIG_VARIABLE_LINE; } if(ARC_String_EqualsCStringWithStrlen(string, "")){ return ARC_CONFIG_ALLOW_SPACE; } if(ARC_String_EqualsCStringWithStrlen(string, "")){ return ARC_CONFIG_TYPE; } if(ARC_String_EqualsCStringWithStrlen(string, "")){ return ARC_CONFIG_VALUE; } if(ARC_String_EqualsCStringWithStrlen(string, "")){ return ARC_CONFIG_NESTED_VALUE; } if(ARC_String_EqualsCStringWithStrlen(string, "")){ return ARC_CONFIG_VALUE_ARGS; } if(ARC_String_EqualsCStringWithStrlen(string, "")){ return ARC_CONFIG_VARIABLE; } if(ARC_String_EqualsCStringWithStrlen(string, "")){ return ARC_CONFIG_VARIABLE_NAME; } if(ARC_String_EqualsCStringWithStrlen(string, "")){ return ARC_CONFIG_VARIABLE_CHAR; } if(ARC_String_EqualsCStringWithStrlen(string, "")){ return ARC_CONFIG_STRING; } if(ARC_String_EqualsCStringWithStrlen(string, "")){ return ARC_CONFIG_STRING_CHARS; } if(ARC_String_EqualsCStringWithStrlen(string, "")){ return ARC_CONFIG_STRING_CHAR; } if(ARC_String_EqualsCStringWithStrlen(string, "")){ return ARC_CONFIG_ESCAPE_CHAR; } if(ARC_String_EqualsCStringWithStrlen(string, "")){ return ARC_CONFIG_NUMBER_SIGN; } if(ARC_String_EqualsCStringWithStrlen(string, "")){ return ARC_CONFIG_NUMBER_TAG; } if(ARC_String_EqualsCStringWithStrlen(string, "")){ return ARC_CONFIG_WHITESPACE; } return ~(uint32_t)0; } //private function to check hashtable keys used by config ARC_Bool ARC_Config_HashtableKeyCompareFn(void *key1, void *key2){ return (ARC_Bool)strcmp((const char *)key1, (const char *)key2) == 0; } //private function to clean up types void ARC_Config_TypeHashtableDestroyKeyValueFn(void *key, void *value){ free((char *)key); free((ARC_ConfigType *)value); } //private function to clean up groups void ARC_Config_GroupHashtableDestroyKeyValueFn(void *key, void *value){ free((char *)key); ARC_Hashtable_Destroy((ARC_Hashtable *)value); } //private function to clean up goup data void ARC_Config_GroupDataHashtableDestroyKeyValueFn(void *key, void *value){ free((char *)key); ARC_ConfigTypeData *typeData = (ARC_ConfigTypeData *)value; typeData->destroyFn(typeData->data); free(typeData); } // -> EQUAL SEMICOLON void ARC_ConfigData_RunVariableLineTag(ARC_ParserTagToken *tagToken, ARC_Config *config){ //skip whitespace and check for group name ARC_ParserTagToken *childTagToken = (ARC_ParserTagToken *)ARC_Vector_Get(tagToken->tagTokens, 1); //get the type ARC_String *typeString; ARC_String_Create(&typeString, NULL, 0); ARC_ParserData_HelperRecurseStringAdd(&typeString, childTagToken); //check if type exists in the types hashtable ARC_ConfigType *type = (ARC_ConfigType *)ARC_Hashtable_Get(config->types, typeString->data); if(type == NULL){ //throw an error and return arc_errno = ARC_ERRNO_DATA; ARC_DEBUG_LOG_ERROR_WITH_VARIABLES("ARC_ConfigData_RunVariableLineTag(tagToken, config), type \"%s\" was not registered to config", typeString->data); ARC_String_Destroy(typeString); return; } //cleanup ARC_String_Destroy(typeString); //get the variable childTagToken = (ARC_ParserTagToken *)ARC_Vector_Get(tagToken->tagTokens, 3); ARC_String *variableString; ARC_String_Create(&variableString, NULL, 0); ARC_ParserData_HelperRecurseStringAdd(&variableString, childTagToken); //check if removing if(config->load == ARC_False){ ARC_Hashtable_Remove(config->currentGroup, (void *)variableString->data); ARC_String_Destroy(variableString); return; } //check to see if the current variable is already in the current group hashtable ARC_ConfigTypeData *typeData = (ARC_ConfigTypeData *)ARC_Hashtable_Get(config->currentGroup, variableString->data); if(typeData != NULL){ //there is already a value so throw an error and return arc_errno = ARC_ERRNO_DATA; ARC_DEBUG_LOG_ERROR_WITH_VARIABLES("ARC_ConfigData_RunVariableLineTag(tagToken, config), variable \"%s\" already registered to the current group", variableString->data); ARC_String_Destroy(variableString); return; } //copy the string into a cstring that will be stored in the hashtable char *nameVariableCStr = malloc(sizeof(char) * (variableString->length + 1)); strncpy(nameVariableCStr, variableString->data, variableString->length); nameVariableCStr[variableString->length] = '\0'; ARC_String_Destroy(variableString); //get childTagToken = (ARC_ParserTagToken *)ARC_Vector_Get(tagToken->tagTokens, 7); //check if is a reference ARC_String *valueString; ARC_String_Create(&valueString, NULL, 0); ARC_ParserData_HelperRecurseStringAdd(&valueString, childTagToken); void *value = ARC_Hashtable_Get(config->currentGroup, valueString); ARC_String_Destroy(valueString); //create where to store either the reference or type data typeData = (ARC_ConfigTypeData *)malloc(sizeof(ARC_ConfigTypeData)); if(value != NULL){ //point to the already stored data typeData->data = value; typeData->destroyFn = NULL; //add to the current group hashtable ARC_Hashtable_Add(config->currentGroup, (void *)nameVariableCStr, (void *)typeData); return; } //passed the parsed value into the copy type function and set the destroy function type->copyFn(&(typeData->data), childTagToken, config, type->userdata); typeData->destroyFn = type->destroyFn; //add to the current group hashtable ARC_Hashtable_Add(config->currentGroup, (void *)nameVariableCStr, (void *)typeData); } // -> | void ARC_ConfigData_RunVariableLinesTag(ARC_ParserTagToken *tagToken, ARC_Config *config){ //loop through the tags either going to the next line or the next body for(uint32_t index = 0; index < ARC_Vector_GetSize(tagToken->tagTokens); index++){ ARC_ParserTagToken *childTagToken = (ARC_ParserTagToken *)ARC_Vector_Get(tagToken->tagTokens, index); switch(childTagToken->id){ //recuse to run the next line case ARC_CONFIG_VARIABLE_LINES: ARC_ConfigData_RunVariableLinesTag(childTagToken, config); if(arc_errno != 0){ ARC_DEBUG_LOG_ERROR("ARC_ConfigData_RunVariableLinesTag(tagToken, config), config errored when trying to used parsed data for variable lines"); return; } continue; //recurse into a variable line tag case ARC_CONFIG_VARIABLE_LINE: ARC_ConfigData_RunVariableLineTag(childTagToken, config); if(arc_errno != 0){ ARC_DEBUG_LOG_ERROR("ARC_ConfigData_RunVariableLinesTag(tagToken, config), config errored when trying to used parsed data for variable line"); return; } continue; //this is for whitespace and any oddities default: continue; } } } // -> OPEN_CURLY_BRACE CLOSE_CURLY_BRACE void ARC_ConfigData_RunGroupTag(ARC_ParserTagToken *tagToken, ARC_Config *config){ //skip whitespace and check for group name ARC_ParserTagToken *childTagToken = (ARC_ParserTagToken *)ARC_Vector_Get(tagToken->tagTokens, 1); /* ~ ~ */ //get the group name and check if it is named "group" //NOTE: this check may be usedful in the future if there is different functionality for group like tag names ARC_String *groupName; ARC_String_Create(&groupName, NULL, 0); ARC_ParserData_HelperRecurseStringAdd(&groupName, childTagToken); if(ARC_String_EqualsCStringWithStrlen(groupName, ARC_CONFIG_GROUP_TAG_NAME) == ARC_False){ //throw an error and return arc_errno = ARC_ERRNO_DATA; ARC_DEBUG_LOG_ERROR_WITH_VARIABLES("ARC_ConfigData_RunGroupTag(tagToken, config), config contained keyword \"%s\" instead of using the correct keyword: \"%s\" ", groupName->data, ARC_CONFIG_GROUP_TAG_NAME); ARC_String_Destroy(groupName); return; } //cleanup ARC_String_Destroy(groupName); /* ~ ~ */ //get the group's variable name childTagToken = (ARC_ParserTagToken *)ARC_Vector_Get(tagToken->tagTokens, 3); ARC_String *groupVariable; ARC_String_Create(&groupVariable, NULL, 0); ARC_ParserData_HelperRecurseStringAdd(&groupVariable, childTagToken); //check if removing if(config->load == ARC_False){ /* ~ ~ */ childTagToken = (ARC_ParserTagToken *)ARC_Vector_Get(tagToken->tagTokens, 6); // -> | LAMBDA if(childTagToken->token == NULL && ARC_Vector_GetSize(childTagToken->tagTokens) == 2){ ARC_ParserTagToken *variableLines = (ARC_ParserTagToken *)ARC_Vector_Get(childTagToken->tagTokens, 1); ARC_ConfigData_RunVariableLinesTag(variableLines, config); //log error if it happens if(arc_errno != 0){ ARC_DEBUG_LOG_ERROR("ARC_ConfigData_RunGroupTag(tagToken, config), config errored when trying to used parsed data for variable lines"); } } //remove an empty hashtable if it is now empty if(ARC_Hashtable_GetSize(config->groups)){ ARC_String_Destroy(groupVariable); } return; } //get the needed hashtable or create it from the groups hashtable ARC_Hashtable *groupHashtable = ARC_Hashtable_Get(config->groups, (void *)groupVariable->data); if(groupHashtable == NULL){ //copy the string into a cstring that will be stored in the hashtable char *groupVariableCStr = malloc(sizeof(char) * (groupVariable->length + 1)); strncpy(groupVariableCStr, groupVariable->data, groupVariable->length); groupVariableCStr[groupVariable->length] = '\0'; //create the hashtable with the given group name key ARC_Hashtable_KeyCompareFn keyCompareFn = ARC_Config_HashtableKeyCompareFn; ARC_Hashtable_DestroyKeyValueFn groupDataDestroyKeyValueFn = ARC_Config_GroupDataHashtableDestroyKeyValueFn; ARC_Hashtable_Create(&groupHashtable, NULL, &keyCompareFn, &groupDataDestroyKeyValueFn); ARC_Hashtable_Add(config->groups, (void *)groupVariableCStr, (void *)groupHashtable); } //cleanup ARC_String_Destroy(groupVariable); //set the current group to the group just created or found config->currentGroup = groupHashtable; /* ~ ~ */ childTagToken = (ARC_ParserTagToken *)ARC_Vector_Get(tagToken->tagTokens, 6); // -> | LAMBDA if(childTagToken->token == NULL && ARC_Vector_GetSize(childTagToken->tagTokens) == 2){ ARC_ParserTagToken *variableLines = (ARC_ParserTagToken *)ARC_Vector_Get(childTagToken->tagTokens, 1); ARC_ConfigData_RunVariableLinesTag(variableLines, config); //log error if it happens, but as setting the group back is the last thing we shouldn't return if(arc_errno != 0){ ARC_DEBUG_LOG_ERROR("ARC_ConfigData_RunGroupTag(tagToken, config), config errored when trying to used parsed data for variable lines"); } } /* ~ reset current group ~ */ config->currentGroup = ARC_Hashtable_Get(config->groups, (void *)ARC_CONFIG_DEFAULT_GROUP); } // -> | | void ARC_ConfigData_RunLanguageTag(ARC_ParserTagToken *tagToken, ARC_Config *config){ //loop through the tags either going to the next language, group, or variable lines for(uint32_t index = 0; index < ARC_Vector_GetSize(tagToken->tagTokens); index++){ ARC_ParserTagToken *childTagToken = (ARC_ParserTagToken *)ARC_Vector_Get(tagToken->tagTokens, index); switch(childTagToken->id){ //recuse to run the next line case ARC_CONFIG_LANGUAGE: ARC_ConfigData_RunLanguageTag(childTagToken, config); continue; //recurse into a group tag case ARC_CONFIG_GROUP: ARC_ConfigData_RunGroupTag(childTagToken, config); if(arc_errno != 0){ ARC_DEBUG_LOG_ERROR("ARC_ConfigData_RunLanguageTag(tagToken, config), config errored when trying to used parsed data for a group"); return; } continue; //recurse through variable lines case ARC_CONFIG_VARIABLE_LINES: ARC_ConfigData_RunVariableLinesTag(childTagToken, config); ARC_ConfigData_RunGroupTag(childTagToken, config); if(arc_errno != 0){ ARC_DEBUG_LOG_ERROR("ARC_ConfigData_RunLanguageTag(tagToken, config), config errored when trying to used parsed data for variable lines"); return; } continue; //this is for whitespace and any oddities default: continue; } } } void ARC_ConfigData_CreateFn(void **data, ARC_ParserTagToken *parsedData, void *userData){ *data = NULL; if(parsedData == NULL || userData == NULL){ //TODO: error here? *data = NULL; return; } ARC_ConfigData_RunLanguageTag(parsedData, (ARC_Config *)userData); } void ARC_ConfigData_DestroyFn(void *data, ARC_Bool clear, void *userData){ return; } void ARC_Config_Create(ARC_Config **config){ *config = (ARC_Config *)malloc(sizeof(ARC_Config)); /* ~ define the language as a string ~ */ char *languageCString = " -> | | \n" " -> OPEN_CURLY_BRACE CLOSE_CURLY_BRACE\n" " -> \n" " -> | LAMBDA\n" " -> | \n" " -> EQUAL SEMICOLON\n" " -> SPACE | TAB | LAMBDA\n" " -> \n" " -> | | | \n" " -> OPEN_CURLY_BRACE CLOSE_CURLY_BRACE\n" " -> COMMA | \n" " -> ALPHA_UPPER_CHAR | ALPHA_LOWER_CHAR | UNDERSCORE \n" " -> | LAMBDA\n" " -> ALPHA_UPPER_CHAR | ALPHA_LOWER_CHAR | UNDERSCORE | NUMBER\n" " -> QUOTE QUOTE\n" " -> | | LAMBDA\n" " -> TAB | SPACE | BANG | HASH | DOLLAR | PERCENT | AMPERSAND | SINGLE_QUOTE | OPEN_PAREN | CLOSE_PAREN | ASTERISK | PLUS | COMMA | MINUS | PERIOD | SLASH | NUMBER | COLON | SEMICOLON | LESS_THAN | GREATER_THAN | EQUAL | QUESTION_MARK | AT | ALPHA_UPPER_CHAR | OPEN_BRACKET | CLOSE_BRACKET | CARET | UNDERSCORE | GRAVE | ALPHA_LOWER_CHAR | OPEN_CURLY_BRACE | VERTICAL_LINE | CLOSE_CURLY_BRACE | TILDE\n" " -> BACKSLASH BACKSLASH | BACKSLASH QUOTE | BACKSLASH ALPHA_UPPER_CHAR | BACKSLASH ALPHA_LOWER_CHAR\n" " -> MINUS | \n" " -> NUMBER | NUMBER\n" " -> SPACE | TAB | NEWLINE | LAMBDA\n"; /* ~ define the language as a string ~ */ ARC_String *languageString; ARC_String_CreateWithStrlen(&languageString, languageCString); /* ~ set config as userdata ~ */ void *userdata = *config; /* ~ create the language ~ */ ARC_ParserData_CreateFn createCharFn = ARC_ConfigData_CreateFn; ARC_ParserData_DestroyFn destroyCharFn = ARC_ConfigData_DestroyFn; ARC_Parser_CreateFromString(&((*config)->parser), languageString, ARC_Config_InitLexerRulesFn, ARC_Config_GetStringIdFn, &createCharFn, &destroyCharFn, userdata); /* ~ init types ~ */ ARC_Hashtable_KeyCompareFn keyCompareFn = ARC_Config_HashtableKeyCompareFn; ARC_Hashtable_DestroyKeyValueFn typeDestroyKeyValueFn = ARC_Config_TypeHashtableDestroyKeyValueFn; ARC_Hashtable_Create(&((*config)->types), NULL, &keyCompareFn, &typeDestroyKeyValueFn); /* ~ init groups ~ */ ARC_Hashtable_DestroyKeyValueFn groupDestroyKeyValueFn = ARC_Config_GroupHashtableDestroyKeyValueFn; ARC_Hashtable_Create(&((*config)->groups), NULL, &keyCompareFn, &groupDestroyKeyValueFn); //add the default/empty group into the groups ARC_Hashtable_DestroyKeyValueFn groupDataDestroyKeyValueFn = ARC_Config_GroupDataHashtableDestroyKeyValueFn; //copy empty group cstring (to be freed by hashtable on cleanup, passing directly would cause a segfault) char *emptyCStr = (char *)malloc(sizeof(char) * (strlen(ARC_CONFIG_DEFAULT_GROUP) + 1)); strcpy(emptyCStr, ARC_CONFIG_DEFAULT_GROUP); //set the current group as empty, then add that into the groups hashtable ARC_Hashtable_Create(&((*config)->currentGroup), NULL, &keyCompareFn, &groupDataDestroyKeyValueFn); ARC_Hashtable_Add((*config)->groups, (void *)emptyCStr, (*config)->currentGroup); (*config)->load = ARC_True; //cleanup ARC_String_Destroy(languageString); } void ARC_Config_Destroy(ARC_Config *config){ ARC_Parser_Destroy(config->parser); //NOTE: config->currentGroup does not need to be freed as it is stored in config->groups ARC_Hashtable_Destroy(config->groups); ARC_Hashtable_Destroy(config->types); free(config); } void ARC_Config_RegisterType(ARC_Config *config, ARC_String *typeName, ARC_ConfigType type){ ARC_Config_RegisterTypeWithCStr(config, typeName->data, type); } void ARC_Config_RegisterTypeWithCStr(ARC_Config *config, const char *typeNameCStr, ARC_ConfigType type){ //check if the type key already exists and error if it does if(ARC_Hashtable_Get(config->types, (void *)typeNameCStr) != NULL){ arc_errno = ARC_ERRNO_EXISTS; ARC_DEBUG_LOG_ERROR_WITH_VARIABLES("ARC_Config_RegisterTypeWithCStr(config, typeName, type), type \"%s\" has already been registered", typeNameCStr); return; } //copy the string into a cstring for long term storage uint64_t typeNameCopyLength = strlen(typeNameCStr) + 1; char *typeNameCopy = (char *)malloc(sizeof(char) * typeNameCopyLength); strncpy(typeNameCopy, typeNameCStr, typeNameCopyLength); typeNameCopy[strlen(typeNameCStr)] = '\0'; //copy the type for long term storage ARC_ConfigType *typeCopy = (ARC_ConfigType *)malloc(sizeof(ARC_ConfigType)); *typeCopy = type; //add the type to a hashtable containing all the types ARC_Hashtable_Add(config->types, typeNameCopy, typeCopy); } void ARC_Config_SetGroup(ARC_Config *config, ARC_String *groupName){ ARC_Config_SetGroupWithCStr(config, groupName->data); } void ARC_Config_SetGroupWithCStr(ARC_Config *config, const char *groupName){ //try to get the current group void *currentGroup = ARC_Hashtable_Get(config->groups, (void *)groupName); //error if the current group does not exist if(currentGroup == NULL){ arc_errno = ARC_ERRNO_NULL; ARC_DEBUG_LOG_ERROR_WITH_VARIABLES("ARC_Config_SetGroupWithCStr(config, groupName), no group with name \"%s\"", groupName); return; } //set the current group config->currentGroup = currentGroup; } void *ARC_Config_Get(ARC_Config *config, ARC_String *name){ //check if the group separator exists uint64_t startSeparatorIndex = ARC_String_FindCStringWithStrlen(name, ARC_CONFIG_GROUP_SEPARATOR); if(startSeparatorIndex == ~(uint64_t)0){ //use empty group config->currentGroup = ARC_Hashtable_Get(config->groups, (void *)ARC_CONFIG_DEFAULT_GROUP); //get the typeData and pass back the data without the cleanup function ARC_ConfigTypeData *typeData = (ARC_ConfigTypeData *)ARC_Hashtable_Get(config->currentGroup, (void *)name->data); if(typeData == NULL){ return NULL; } return typeData->data; } //get the group startSeparatorIndex--; ARC_String *groupString; ARC_String_CopySubstring(&groupString, name, 0, startSeparatorIndex); //set the group config->currentGroup = ARC_Hashtable_Get(config->groups, (void *)groupString->data); //cleanup ARC_String_Destroy(groupString); //get the name ARC_String *nameString; startSeparatorIndex += strlen(ARC_CONFIG_GROUP_SEPARATOR); ARC_String_CopySubstring(&nameString, name, startSeparatorIndex, name->length - startSeparatorIndex); //this will either return the value or NULL ARC_ConfigTypeData *typeData = (ARC_ConfigTypeData *)ARC_Hashtable_Get(config->currentGroup, (void *)nameString->data); ARC_String_Destroy(nameString); if(typeData == NULL){ return NULL; } return typeData->data; } void *ARC_Config_GetWithCStr(ARC_Config *config, const char *name){ //create and copy into ARC_String ARC_String *nameString; ARC_String_CreateWithStrlen(&nameString, (char *)name); //get the return value void *returnValue = ARC_Config_Get(config, nameString); //cleanup ARC_String_Destroy(nameString); return returnValue; } void ARC_Config_LoadFromString(ARC_Config *config, ARC_String **string){ config->load = ARC_True; ARC_Parser_Parse(config->parser, string); } void ARC_Config_LoadFromFile(ARC_Config *config, ARC_String *path){ config->load = ARC_True; ARC_Parser_ParseFile(config->parser, path); } void ARC_Config_LoadFromFileWithCStr(ARC_Config *config, const char *path){ config->load = ARC_True; ARC_String *pathString; ARC_String_CreateWithStrlen(&pathString, (char *)path); ARC_Parser_ParseFile(config->parser, pathString); ARC_String_Destroy(pathString); } void ARC_Config_UnloadFromString(ARC_Config *config, ARC_String **string){ config->load = ARC_False; ARC_Parser_Parse(config->parser, string); } void ARC_Config_UnloadFromFile(ARC_Config *config, ARC_String *path){ config->load = ARC_False; ARC_Parser_ParseFile(config->parser, path); }