├── LICENCE.md ├── README.md ├── example.c ├── example.graph └── graph_file_format.h /LICENCE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 temper-studios 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # graph-file-format 2 | This repo is a header only, C/C++ program that loads and saves the .gf/.graph file format. 3 | To get started, download the graph_file_format.h and include it in your C/C++ program. That's it! 4 | 5 | Check out example.c for examples of usage. Check out the .graph files to see what the format looks like. 6 | 7 | The graph file format is a plain text, hierarchical, data file format that is trivial to read and easy to parse. 8 | For instance, the parser is only tens of lines long! It's simpler than JSON and as a bonus, can have comments. 9 | 10 | If you want to simply serialise and deserialise data this could be an option for you. 11 | 12 | It is cross platform. 13 | 14 | It was designed primarily for games to save information about game state which is usually hierarchical. 15 | Credit for the design goes to my Dad 16 | 17 | The goals are 18 | - Be as simple as possible. 19 | - Be easy to parse. 20 | - Be easy to read. 21 | - Be frictionless. 22 | - Be easy to follow the code. 23 | 24 | The grammar is very simple and free form. Everything is a node and a node can be 4 different types: "composite", "floats", "integers" and "strings". 25 | 26 | Nodes that have child nodes are called composite nodes. Below is a composite node called "node" with a single child node which is a value "1.0". 27 | ``` 28 | node { 1.0 } /* node with a value child */ 29 | ``` 30 | Below is a composite node that has a child which is a composite node. 31 | ``` 32 | node { child { 2.0 } } /* node with a child node */ 33 | ``` 34 | ``` 35 | node { 1 2 3.0 "hello" child { -1.0 } } /* node with a list of data including a child node */ 36 | ``` 37 | The delimiter between nodes can be a comma or a space. Nodes scope are ended with a closing brace 38 | ``` 39 | node { a, b } /* a and b are two seperate nodes. */ 40 | next { a } /* next is seperate from node */ 41 | ``` 42 | Arrays are just value nodes that are next to each other. 43 | ``` 44 | matrix { 1 0 0 45 | 0 1 0 46 | 0 0 1 } 47 | ``` 48 | Multi line comments are allowed. 49 | ``` 50 | myFloat { 1.0 } /* this is a number */ 51 | ``` 52 | Nested data structures are recommended! 53 | ``` 54 | Parent { 55 | Child { 56 | Child { 57 | "hello, world" 58 | } 59 | } 60 | } 61 | ``` 62 | Features: 63 | - Saving and loading a C struct. 64 | - Saving and loading anything really. 65 | - Saving graphs or linked data structures. 66 | - Can use a custom allocator so (admittedly not battle tested yet) can be used in an embedded environment. 67 | - Send it over a network... or don't! 68 | - Is fast. Potentially. Not measured it, but it doesn't do much and its written in C. Obvious optimisation can be done. 69 | - Just looks pretty nice dunnit? 70 | - It's not JSON. 71 | - It's not XML. 72 | - C is the first class citizen so can port anywhere! To any language! (but someone else should do that not me) 73 | - You should use a binary format. But this is the next best thing. 74 | 75 | Where is it used? 76 | - Blinded by Fear: https://store.steampowered.com/app/2102680/Blinded_by_Fear/ 77 | - That's it. But if you do use it I'd love to know so I can list it here! 78 | -------------------------------------------------------------------------------- /example.c: -------------------------------------------------------------------------------- 1 | // Include the graph file format header. 2 | // If you want to include the implementation (which should be included in exactly one translation unit of your project) 3 | // you precede the include with either GF_IMPLEMENTATION or GF_IMPLEMENTATION_WITH_TESTS 4 | // The latter includes the tests the former does not. 5 | #define GF_IMPLEMENTATION_WITH_TESTS 6 | #include "graph_file_format.h" 7 | 8 | int main(void) { 9 | 10 | // As an example, lets include some semi-realistic data that we might 11 | // want to save or load. 12 | 13 | struct InnerStruct { 14 | double a; 15 | int64_t b; 16 | int32_t list[4]; 17 | }; 18 | 19 | struct MyStruct { 20 | uint32_t a; 21 | float b; 22 | float xyz[3]; 23 | struct InnerStruct inner; 24 | char str[4]; 25 | uint64_t c; 26 | }; 27 | 28 | { 29 | // Initialise our structure for saving. 30 | struct MyStruct myStruct; 31 | myStruct.a = 10; 32 | myStruct.b = 3.14f; 33 | myStruct.xyz[0] = 1.0; 34 | myStruct.xyz[1] = 2.0; 35 | myStruct.xyz[2] = 3.0; 36 | myStruct.str[0] = 'S'; 37 | myStruct.str[1] = 't'; 38 | myStruct.str[2] = 'r'; 39 | myStruct.str[3] = '\0'; 40 | myStruct.c = 63; 41 | 42 | myStruct.inner.a = -1.32; 43 | myStruct.inner.b = -232; 44 | myStruct.inner.list[0] = 0; 45 | myStruct.inner.list[1] = 1; 46 | myStruct.inner.list[2] = 2; 47 | myStruct.inner.list[3] = 3; 48 | 49 | // When we want to save we use the gf_Saver object. 50 | // Before we use this, we must call gf_InitSaver(). 51 | gf_Saver saver; 52 | gf_InitSaver(&saver, NULL); 53 | 54 | // To save, we need a stream to save to. Let's open the file ourselves. 55 | FILE *file = fopen("data.gf", "wb"); 56 | if (!file) { 57 | puts("could not open file"); 58 | return 0; 59 | } 60 | 61 | // The saver is very simple and is just a helper that writes to the file. 62 | // For ease, we want to match the structure of our existing data. Our struct 63 | // has various levels of nesting. We can start a nested list by calling gf_SaveStartList. 64 | // This writes a identifier and an opening brace { to the file. 65 | gf_SaveStartList(&saver, file, "MyStruct"); 66 | { 67 | // Now we can save variables to the file. We want to one to one match our struct because 68 | // it is just easier to deal with that way. Each of these variable files saves the data 69 | // along with an identifier that we specify. Like so => a { 2 } or b { 3.2 }. 70 | gf_SaveVariableU32(&saver, file, "a", &myStruct.a); 71 | gf_SaveVariableF32(&saver, file, "b", &myStruct.b); 72 | gf_SaveVariableVec3(&saver, file, "xyz", &myStruct.xyz[0], &myStruct.xyz[1], &myStruct.xyz[2]); 73 | 74 | // Since Inner is nested inside MyStruct, we want to start a new nested list. 75 | gf_SaveStartList(&saver, file, "Inner"); 76 | { 77 | gf_SaveVariableF64(&saver, file, "a", &myStruct.inner.a); 78 | gf_SaveVariableS64(&saver, file, "b", &myStruct.inner.b); 79 | gf_SaveArrayS32(&saver, file, "list", myStruct.inner.list, 4); 80 | } 81 | // Make sure to match a start list with an end list otherwise you get a malformed file! 82 | gf_SaveEndList(&saver, file); 83 | 84 | gf_SaveVariableString(&saver, file, "str", myStruct.str); 85 | gf_SaveVariableU64(&saver, file, "c", &myStruct.c); 86 | 87 | } 88 | // Make sure to match a start list with an end list otherwise you get a malformed file! 89 | gf_SaveEndList(&saver, file); 90 | 91 | // Close the file at the end and we are done! 92 | fclose(file); 93 | } 94 | 95 | { 96 | // Loading is slightly more involved. 97 | struct MyStruct myStruct; 98 | 99 | // We can use the gf_Loader object to help us load text. 100 | gf_Loader loader; 101 | int result = 0; 102 | 103 | // We can load directly from a file. This allocates an internal buffer that stores the file. 104 | // This acts as an initialisation for the loader. We don't have to initialise loader ourselves. 105 | // When you have called a load function on loader, you must match it with gf_Unload(), even if it fails. 106 | result = gf_LoadFromFile(&loader, "data.gf", NULL); 107 | if (!result) {return 0;} 108 | { 109 | // Text is tokenised and parsed into a graph like structure. The graph is made of loader nodes. 110 | // These can be traversed and converted to values. 111 | // When loaded, there is an intrinsic root node that always exists that we can start with. 112 | gf_LoaderNode *root = gf_GetRoot(&loader); 113 | 114 | // All parsed data in the text is a subtree of the root. The order of the text is maintained in parsing, so 115 | // we can expect the first child of the intrinsic root to be the MyStruct { identifier we saved previously. 116 | gf_LoaderNode *nodeMyStruct = gf_GetChild(&loader, root); 117 | { 118 | // Our MyStruct nodes contains a list of children. 119 | gf_LoaderNode *child = gf_GetChild(&loader, nodeMyStruct); 120 | gf_LoadVariableU32(&loader, child, &myStruct.a); 121 | 122 | // We get the next child in the the MyStruct child list. 123 | child = gf_GetNext(&loader, child); 124 | gf_LoadVariableF32(&loader, child, &myStruct.b); 125 | 126 | child = gf_GetNext(&loader, child); 127 | gf_LoadVariableVec3(&loader, child, &myStruct.xyz[0], &myStruct.xyz[1], &myStruct.xyz[2]); 128 | 129 | child = gf_GetNext(&loader, child); 130 | { 131 | gf_LoaderNode *childInner = gf_GetChild(&loader, child); 132 | gf_LoadVariableF64(&loader, childInner, &myStruct.inner.a); 133 | 134 | childInner = gf_GetNext(&loader, childInner); 135 | gf_LoadVariableS64(&loader, childInner, &myStruct.inner.b); 136 | 137 | childInner = gf_GetNext(&loader, childInner); 138 | gf_LoadArrayS32(&loader, childInner, myStruct.inner.list, 4); 139 | } 140 | 141 | child = gf_GetNext(&loader, child); 142 | gf_LoadVariableString(&loader, child, myStruct.str, 4); 143 | 144 | child = gf_GetNext(&loader, child); 145 | gf_LoadVariableU64(&loader, child, &myStruct.c); 146 | } 147 | 148 | } 149 | // You must call this when loading data into loader. 150 | gf_Unload(&loader); 151 | } 152 | 153 | { 154 | struct MyStruct myStruct; 155 | gf_Loader loader; 156 | gf_LoadFromFile(&loader, "data.gf", NULL); 157 | { 158 | gf_LoaderNode *root = gf_GetRoot(&loader); 159 | 160 | // This is a different API which instead searches for identifier nodes by name. 161 | gf_LoaderNode *nodeMyStruct = gf_FindFirstChild(&loader, root, "MyStruct"); 162 | 163 | gf_LoaderNode *nodeA = gf_FindFirstChild(&loader, nodeMyStruct, "a"); 164 | gf_LoadVariableU32(&loader, nodeA, &myStruct.a); 165 | 166 | gf_LoaderNode *nodeB = gf_FindFirstChild(&loader, nodeMyStruct, "b"); 167 | gf_LoadVariableF32(&loader, nodeB, &myStruct.b); 168 | 169 | gf_LoaderNode *nodeXYZ = gf_FindFirstChild(&loader, nodeMyStruct, "xyz"); 170 | gf_LoadVariableVec3(&loader, nodeXYZ, &myStruct.xyz[0], &myStruct.xyz[1], &myStruct.xyz[2]); 171 | 172 | gf_LoaderNode *nodeInner = gf_FindFirstChild(&loader, nodeMyStruct, "Inner"); 173 | { 174 | gf_LoaderNode *nodeInnerA = gf_FindFirstChild(&loader, nodeInner, "a"); 175 | gf_LoadVariableF64(&loader, nodeInnerA, &myStruct.inner.a); 176 | 177 | gf_LoaderNode *nodeInnerB = gf_FindFirstChild(&loader, nodeInner, "b"); 178 | gf_LoadVariableS64(&loader, nodeInnerB, &myStruct.inner.b); 179 | 180 | gf_LoaderNode *nodeInnerList = gf_FindFirstChild(&loader, nodeInner, "list"); 181 | gf_LoadArrayS32(&loader, nodeInnerList, myStruct.inner.list, 4); 182 | } 183 | 184 | gf_LoaderNode *nodeStr = gf_FindFirstChild(&loader, nodeMyStruct, "str"); 185 | gf_LoadVariableString(&loader, nodeStr, myStruct.str, 4); 186 | 187 | gf_LoaderNode *nodeC = gf_FindFirstChild(&loader, nodeMyStruct, "c"); 188 | gf_LoadVariableU64(&loader, nodeC, &myStruct.c); 189 | } 190 | gf_Unload(&loader); 191 | 192 | } 193 | 194 | // We can also load directly from a buffer 195 | const char *str = 196 | "MyStruct {\n" 197 | " a { 3.14000 }\n" 198 | " b { 101 }\n" 199 | " c { -13 }\n" 200 | " str { \"hello\" }\n" 201 | "}\n"; 202 | 203 | gf_Loader loader; 204 | gf_LoadFromBuffer(&loader, str, gf_StringLength(str), NULL); 205 | gf_Unload(&loader); 206 | 207 | return gf_Test(); 208 | } 209 | -------------------------------------------------------------------------------- /example.graph: -------------------------------------------------------------------------------- 1 | MyStruct { 2 | a { 3.140000 } 3 | b { 101 } 4 | c { -13 } 5 | str { "hello" } 6 | } 7 | -------------------------------------------------------------------------------- /graph_file_format.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef GF_GRAPH_FILE_FORMAT_H 3 | #define GF_GRAPH_FILE_FORMAT_H 4 | 5 | // This header file parses the .gf/.graph file format. 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | /*----------------------------------TYPEDEFS----------------------------------------*/ 19 | 20 | typedef uint32_t gf_u32; 21 | typedef uint64_t gf_u64; 22 | typedef int32_t gf_s32; 23 | typedef int64_t gf_s64; 24 | typedef float gf_f32; 25 | typedef double gf_f64; 26 | 27 | /*----------------------------------------------------------------------------------*/ 28 | 29 | /*-------------------------STRING CONVERSIONS---------------------------------------*/ 30 | 31 | /* 32 | Name: int gf_AreStringSpansEqual(const char *a, gf_u64 alength, const char *b, gf_u64 blength); 33 | Description: Checks if *a and *b spanning strings are equal in value. 34 | Assumptions: - *a and *b are not NULL and they are NULL terminated strings. 35 | - alength and blength are the string lengths of a and b respectively (they do not include the null terminating character). 36 | Returns: Returns 1 if they are equal. 0 if they are not. 37 | */ 38 | int gf_AreStringSpansEqual(const char *a, gf_u64 alength, const char *b, gf_u64 blength); 39 | 40 | /* 41 | Name: gf_u64 gf_StringLength(const char *str); 42 | Description: Computes the length of *str. 43 | Assumptions: - *str is not NULL and is NULL terminated. 44 | Returns: The length of the string as a gf_u64. 45 | */ 46 | gf_u64 gf_StringLength(const char *str); 47 | 48 | /* 49 | Name: int gf_StringSpanToU32(const char *start, uint64_t length, gf_u32 *value); 50 | Description: Converts a string that spans a certain length into an unsigned 32 bit integer. 51 | If the provided length is longer than the NULL terminated string then it will only parse 52 | up to the null terminated character. 53 | Assumptions: - *start is not NULL and is NULL terminated. 54 | - *value is not NULL 55 | Returns: Returns 1 if the conversion was successful. 0 if it was not successful. 56 | */ 57 | int gf_StringSpanToU32(const char *start, uint64_t length, gf_u32 *value); 58 | 59 | /* 60 | Name: int gf_StringSpanToU64(const char *start, uint64_t length, gf_u64 *value); 61 | Description: Converts a string that spans a certain length into an unsigned 64 bit integer. 62 | If the provided length is longer than the NULL terminated string then it will only parse 63 | up to the null terminated character. 64 | Assumptions: - *start is not NULL and is NULL terminated. 65 | - *value is not NULL 66 | Returns: Returns 1 if the conversion was successful. 0 if it was not successful. 67 | */ 68 | int gf_StringSpanToU64(const char *start, uint64_t length, gf_u64 *value); 69 | 70 | /* 71 | Name: int gf_StringSpanToS32(const char *start, uint64_t length, gf_s32 *value); 72 | Description: Converts a string that spans a certain length into an signed 32 bit integer. 73 | If the provided length is longer than the NULL terminated string then it will only parse 74 | up to the null terminated character. 75 | Assumptions: - *start is not NULL and is NULL terminated. 76 | - *value is not NULL 77 | Returns: Returns 1 if the conversion was successful. 0 if it was not successful. 78 | */ 79 | int gf_StringSpanToS32(const char *start, uint64_t length, gf_s32 *value); 80 | 81 | /* 82 | Name: int gf_StringSpanToS64(const char *start, uint64_t length, gf_s64 *value); 83 | Description: Converts a string that spans a certain length into an signed 64 bit integer. 84 | If the provided length is longer than the NULL terminated string then it will only parse 85 | up to the null terminated character. 86 | Assumptions: - *start is not NULL and is NULL terminated. 87 | - *value is not NULL 88 | Returns: Returns 1 if the conversion was successful. 0 if it was not successful. 89 | */ 90 | int gf_StringSpanToS64(const char *start, uint64_t length, gf_s64 *value); 91 | 92 | /* 93 | Name: int gf_StringSpanToF64(const char *start, uint64_t length, gf_f64 *value); 94 | Description: Converts a string that spans a certain length into an 64 bit floating point number. 95 | If the provided length is longer than the NULL terminated string then it will only parse 96 | up to the null terminated character. 97 | Assumptions: - *start is not NULL and is NULL terminated. 98 | - *value is not NULL 99 | Returns: Returns 1 if the conversion was successful. 0 if it was not successful. 100 | */ 101 | int gf_StringSpanToF64(const char *start, uint64_t length, gf_f64 *value); 102 | 103 | /* 104 | Name: int gf_StringSpanToF32(const char *start, uint64_t length, gf_f32 *value); 105 | Description: Converts a string that spans a certain length into an 32 bit floating point number. 106 | If the provided length is longer than the NULL terminated string then it will only parse 107 | up to the null terminated character. 108 | Assumptions: - *start is not NULL and is NULL terminated. 109 | - *value is not NULL 110 | Returns: Returns 1 if the conversion was successful. 0 if it was not successful. 111 | */ 112 | int gf_StringSpanToF32(const char *start, uint64_t length, gf_f32 *value); 113 | 114 | /*-----------------------------------------------------------------------------------*/ 115 | 116 | /*-------------------------------------TOKENS----------------------------------------*/ 117 | 118 | typedef enum gf_TokenType { 119 | GF_TOKEN_TYPE_ROOT, // The type exclusively held by the hidden root token of the parser. 120 | GF_TOKEN_TYPE_NAME, // A named/identifier token that isn't a string, integer, float or string. 121 | GF_TOKEN_TYPE_CURLY_CLOSE, // This } token. 122 | GF_TOKEN_TYPE_COMMENT, // The token associated with a comment which is /* and */ 123 | GF_TOKEN_TYPE_COMPOSITE_TYPE, // This is a named/identifier token that contains other tokens. 124 | GF_TOKEN_TYPE_STRING, // A string value token which is between two quotations "" 125 | GF_TOKEN_TYPE_FLOAT, // A floating point value like 1.0 or 1. 126 | GF_TOKEN_TYPE_INTEGER, // An integer value which is a essentially a number without a floating point. 127 | GF_TOKEN_TYPE_END_FILE, // The token reserved for the end of a file. 128 | GF_TOKEN_TYPE_VALUE_ASSIGN // This token { which designates an assignment is about to happen for a composite token. 129 | } gf_TokenType; 130 | 131 | /* 132 | Holds information about each token that is created from the tokeniser. 133 | Tokens are stored as a singly linked list where each token points to the next one. 134 | */ 135 | typedef struct gf_Token { 136 | const char *start; // The pointer into the buffer that signifies the start string of the token. 137 | gf_u64 length; // The length of the start string. 138 | gf_TokenType type; // The token type 139 | gf_u64 lineno; // The line number that this token starts on. 140 | gf_u64 colno; // The column number on the current line that this token starts on. 141 | struct gf_Token *next; // The next token in the list 142 | } gf_Token; 143 | 144 | /* 145 | Name: void gf_PrintToken(gf_Token *token); 146 | Description: Prints a token to stdout. 147 | Assumptions: - *token is not NULL and is NULL terminated. 148 | Returns: Nothing. 149 | */ 150 | void gf_PrintToken(gf_Token *token); 151 | 152 | /* 153 | Name: void gf_PrintLineToken(gf_Token *token); 154 | Description: Prints a token to stdout with a newline character at the end 155 | Assumptions: - *token is not NULL and is NULL terminated. 156 | Returns: Nothing. 157 | */ 158 | void gf_PrintLineToken(gf_Token *token); 159 | 160 | /* 161 | Name: const char *gf_TokenTypeToString(gf_TokenType type); 162 | Description: Returns a NULL terminated string which corresponds to the given name of a token type. 163 | Assumptions: - type is a valid gf_TokenType 164 | Returns: The names of the token type as a string. The string is NULL terminated. 165 | */ 166 | const char *gf_TokenTypeToString(gf_TokenType type); 167 | 168 | /*----------------------------------------------------------------------------------*/ 169 | 170 | /*-------------------------LOG AND FUNCTION POINTERS--------------------------*/ 171 | 172 | // Used to specify what kind of "error" you want to log. 173 | typedef enum gf_LogLevel { 174 | GF_LOG_ERROR = 0, 175 | GF_LOG_WARNING, 176 | GF_LOG_INFO 177 | } gf_LogLevel; 178 | 179 | /* 180 | Name: gf_LogFunctionPtr 181 | Description: This is the function signature for the log function. This can be user defined. The provided token can be NULL. 182 | */ 183 | typedef void (*gf_LogFunctionPtr)(gf_LogLevel, int, gf_Token *, const char *, va_list); 184 | /* 185 | Name: gf_AllocatorFunctionPtr 186 | Description: This is the function signature for the allocation function. This can be user defined. By default this is malloc(). 187 | It must allocate pointers that are not invalidated with subsequent allocations. 188 | */ 189 | typedef void *(*gf_AllocatorFunctionPtr)(size_t); 190 | /* 191 | Name: gf_FreeFunctionPtr 192 | Description: This is the function signature for the free function. This can be user defined. By default this is free(). 193 | This function must be able to deal with NULL pointers. 194 | */ 195 | typedef void (*gf_FreeFunctionPtr)(void *); 196 | 197 | /* 198 | Name: gf_LogAllocateFreeFunctions 199 | Description: This is a helper struct that stores function pointers responsible for logging, allocating and freeing. 200 | If any of these are NULL then gf_DefaultLog(), malloc() and free() are used respectively. 201 | The allocator must obey the rules of malloc() which means it's pointers can not be invalidated after they 202 | are allocated. free() must work on a NULL. 203 | */ 204 | typedef struct gf_LogAllocateFreeFunctions { 205 | gf_LogFunctionPtr Log; // A pointer to the log function you want to use. If this is NULL gf_DefaultLog() is used 206 | gf_AllocatorFunctionPtr Allocate; // A pointer to the allocation function you want to use. If this is NULL malloc() is used. 207 | gf_FreeFunctionPtr Free; // A pointer to the free function you want to use. If this is NULL free() is used. 208 | } gf_LogAllocateFreeFunctions; 209 | 210 | /* 211 | Name: void gf_DefaultLog(gf_LogLevel level, int lineno, gf_Token *, const char *format, va_list vlist); 212 | Description: The default logging function used if none is specified. This prints to stdout. 213 | Assumptions: - The token can be NULL. 214 | - *format is assumed to be not NULL. 215 | Returns: Nothing. 216 | */ 217 | void gf_DefaultLog(gf_LogLevel level, int lineno, gf_Token *, const char *format, va_list vlist); 218 | 219 | /* 220 | Name: void gf_Log(gf_LogFunctionPtr Log, gf_LogLevel level, int lineno, gf_Token *, const char *format, ...); 221 | Description: An internal function that is called when the logging macro is invoked. This function calls the user specified log function. 222 | Assumptions: - The token can be NULL. 223 | - *format is assumed to be not NULL. 224 | Returns: Nothing. 225 | */ 226 | void gf_Log(gf_LogFunctionPtr Log, gf_LogLevel level, int lineno, gf_Token *, const char *format, ...); 227 | 228 | /* 229 | Name: GF_LOG(loaderOrSaver, level, ...) gf_Log(loaderOrSaver->Log, level, __LINE__, NULL, __VA_ARGS__) 230 | Description: A helper macro that calls gf_Log which in turn, calls the users logging function. A macro is used so the line number can be obtained 231 | Assumptions: - loaderOrSave is not NULL and is of type gf_Loader or gf_Saver. 232 | Returns: Nothing. 233 | */ 234 | #define GF_LOG(loaderOrSaver, level, ...) gf_Log(loaderOrSaver->Log, level, __LINE__, NULL, __VA_ARGS__) 235 | 236 | /* 237 | Name: GF_LOG(loaderOrSaver, level, ...) gf_Log(loaderOrSaver->Log, level, __LINE__, NULL, __VA_ARGS__) 238 | Description: The same as GF_LOG but with a token too. 239 | Assumptions: - loaderOrSave is not NULL and is of type gf_Loader or gf_Saver. 240 | - The token can be NULL. 241 | Returns: Nothing. 242 | */ 243 | #define GF_LOG_WITH_TOKEN(loaderOrSaver, level, token, ...) gf_Log(loaderOrSaver->Log, level, __LINE__, token, __VA_ARGS__) 244 | 245 | /*-----------------------------------------------------------------------------------*/ 246 | 247 | /*--------------------------------------SAVER----------------------------------------*/ 248 | 249 | // A helper function used to save data. Use this to begin "serialisation". 250 | typedef struct gf_Saver { 251 | gf_LogFunctionPtr Log; // The function used to log errors. 252 | unsigned int indent; // Tracks the current indent level of your data. Each level of nested data has an indentation. 253 | } gf_Saver; 254 | 255 | /* 256 | Name: void gf_InitSaver(gf_Saver *saver, gf_LogFunctionPtr logfunction); 257 | Description: Initialises the gf_Saver. Must be called before using the gf_Saver. 258 | You can call it again to reset the saver. 259 | If the logfunction is set to the NULL the default gf_DefaultLog function is used instead. 260 | logfunction is a function pointer to a logging function of gf_LogFunctionPtr signature. 261 | Assumptions: - saver is not NULL. 262 | - logfunction can be NULL. If it is, the default logging function is used. 263 | Returns: Nothing. 264 | Examples: 265 | { 266 | gf_Saver saver; 267 | gf_SaveInit(&saver, NULL); 268 | } 269 | */ 270 | void gf_InitSaver(gf_Saver *saver, gf_LogFunctionPtr logfunction); 271 | 272 | /* 273 | Name: int gf_PrintIndent(gf_Saver *saver, FILE *file); 274 | Description: Internal function used to insert spaces into the file corresponding to the current indent level of the saver. 275 | Assumptions: - gf_InitSaver must have been called on saver atleast once. 276 | - *saver is not NULL 277 | - *file is not NULL and is a valid file. 278 | Returns: 1 if it was successful. 0 if not. If an error occurs this is logged. 279 | */ 280 | int gf_PrintIndent(gf_Saver *saver, FILE *file); 281 | 282 | /* 283 | Name: int gf_SaveVariableS64(gf_Saver *saver, FILE *file, const char *identifier, gf_s64 *value); 284 | Description: Saves a variable to the file with the given value. This will be in the format "a { 2 }" 285 | where "a" is the identifier and "2" is the value. 286 | Assumptions: - gf_InitSaver must have been called on saver atleast once. 287 | - *saver is not NULL 288 | - *file is not NULL and is a valid file. 289 | - *identifier is not NULL and is a NULL terminated string. 290 | - *value is not NULL 291 | Returns: 1 if it was successful. 0 if not. If an error occurs this is logged. 292 | Examples: 293 | { 294 | gf_Saver saver; 295 | gf_SaveInit(&saver, NULL); 296 | 297 | FILE *file = fopen(file, "myfile", "rb"); 298 | if (!file) return 0; 299 | 300 | gf_s64 value = 1; 301 | 302 | gf_SaveVariableS64(&saver, file, "identifier", &value); 303 | 304 | fclose(file); 305 | } 306 | */ 307 | int gf_SaveVariableS64(gf_Saver *saver, FILE *file, const char *identifier, gf_s64 *value); 308 | 309 | /* 310 | Name: int gf_SaveVariableS32(gf_Saver *saver, FILE *file, const char *identifier, gf_s32 *value); 311 | Description: Saves a variable to the file with the given value. This will be in the format "a { 2 }" 312 | where "a" is the identifier and "2" is the value. 313 | Assumptions: - gf_InitSaver must have been called on saver atleast once. 314 | - *saver is not NULL 315 | - *file is not NULL and is a valid file. 316 | - *identifier is not NULL and is a NULL terminated string. 317 | - *value is not NULL 318 | Returns: 1 if it was successful. 0 if not. If an error occurs this is logged. 319 | Examples: 320 | { 321 | gf_Saver saver; 322 | gf_SaveInit(&saver, NULL); 323 | 324 | FILE *file = fopen(file, "myfile", "rb"); 325 | if (!file) return 0; 326 | 327 | gf_s32 value = 1; 328 | 329 | gf_SaveVariableS32(&saver, file, "identifier", &value); 330 | 331 | fclose(file); 332 | } 333 | */ 334 | int gf_SaveVariableS32(gf_Saver *saver, FILE *file, const char *identifier, gf_s32 *value); 335 | 336 | /* 337 | Name: int gf_SaveVariableU32(gf_Saver *saver, FILE *file, const char *identifier, gf_u32 *value); 338 | Description: Saves a variable to the file with the given value. This will be in the format "a { 2 }" 339 | where "a" is the identifier and "2" is the value. 340 | Assumptions: - gf_InitSaver must have been called on saver atleast once. 341 | - *saver is not NULL 342 | - *file is not NULL and is a valid file. 343 | - *identifier is not NULL and is a NULL terminated string. 344 | - *value is not NULL 345 | Returns: 1 if it was successful. 0 if not. If an error occurs this is logged. 346 | Examples: 347 | { 348 | gf_Saver saver; 349 | gf_SaveInit(&saver, NULL); 350 | 351 | FILE *file = fopen(file, "myfile", "rb"); 352 | if (!file) return 0; 353 | 354 | gf_u32 value = 1; 355 | 356 | gf_SaveVariableU32(&saver, file, "identifier", &value); 357 | 358 | fclose(file); 359 | } 360 | */ 361 | int gf_SaveVariableU32(gf_Saver *saver, FILE *file, const char *identifier, gf_u32 *value); 362 | 363 | /* 364 | Name: int gf_SaveVariableU64(gf_Saver *saver, FILE *file, const char *identifier, gf_u64 *value); 365 | Description: Saves a variable to the file with the given value. This will be in the format "a { 2 }" 366 | where "a" is the identifier and "2" is the value. 367 | Assumptions: - gf_InitSaver must have been called on saver atleast once. 368 | - *saver is not NULL 369 | - *file is not NULL and is a valid file. 370 | - *identifier is not NULL and is a NULL terminated string. 371 | - *value is not NULL 372 | Returns: 1 if it was successful. 0 if not. If an error occurs this is logged. 373 | Examples: 374 | { 375 | gf_Saver saver; 376 | gf_SaveInit(&saver, NULL); 377 | 378 | FILE *file = fopen(file, "myfile", "rb"); 379 | if (!file) return 0; 380 | 381 | gf_u64 value = 1; 382 | 383 | gf_SaveVariableU64(&saver, file, "identifier", &value); 384 | 385 | fclose(file); 386 | } 387 | */ 388 | int gf_SaveVariableU64(gf_Saver *saver, FILE *file, const char *identifier, gf_u64 *value); 389 | 390 | /* 391 | Name: int gf_SaveVariableString(gf_Saver *saver, FILE *file, const char *identifier, const char *str); 392 | Description: Saves a string to the file with the given value. This will be in the format "a { "Hello, world" }" 393 | where "a" is the identifier and "Hello, world" is the string. 394 | Assumptions: - gf_InitSaver must have been called on saver atleast once. 395 | - *saver is not NULL 396 | - *file is not NULL and is a valid file. 397 | - *identifier is not NULL and is a NULL terminated string. 398 | - *str is not NULL and is a NULL terminated string. 399 | Returns: 1 if it was successful. 0 if not. If an error occurs this is logged. 400 | Examples: 401 | { 402 | gf_Saver saver; 403 | gf_SaveInit(&saver, NULL); 404 | 405 | FILE *file = fopen(file, "myfile", "rb"); 406 | if (!file) return 0; 407 | 408 | const char *str = "hello, world"; 409 | 410 | gf_SaveVariableString(&saver, file, "identifier", str); 411 | 412 | fclose(file); 413 | } 414 | */ 415 | int gf_SaveVariableString(gf_Saver *saver, FILE *file, const char *identifier, const char *str); 416 | 417 | /* 418 | Name: int gf_SaveVariableF32(gf_Saver *saver, FILE *file, const char *identifier, gf_f32 *value); 419 | Description: Saves a variable to the file with the given value. This will be in the format "a { 2.0 }" 420 | where "a" is the identifier and "2.0" is the value. 421 | Assumptions: - gf_InitSaver must have been called on saver atleast once. 422 | - *saver is not NULL 423 | - *file is not NULL and is a valid file. 424 | - *identifier is not NULL and is a NULL terminated string. 425 | - *value is not NULL 426 | Returns: 1 if it was successful. 0 if not. If an error occurs this is logged. 427 | Examples: 428 | { 429 | gf_Saver saver; 430 | gf_SaveInit(&saver, NULL); 431 | 432 | FILE *file = fopen(file, "myfile", "rb"); 433 | if (!file) return 0; 434 | 435 | float value = 1.0; 436 | 437 | gf_SaveVariableF32(&saver, file, "identifier", &value); 438 | 439 | fclose(file); 440 | } 441 | */ 442 | int gf_SaveVariableF32(gf_Saver *saver, FILE *file, const char *identifier, gf_f32 *value); 443 | 444 | /* 445 | Name: int gf_SaveVariableF64(gf_Saver *saver, FILE *file, const char *identifier, gf_f64 *value); 446 | Description: Saves a variable to the file with the given value. This will be in the format "a { 2.0 }" 447 | where "a" is the identifier and "2.0" is the value. 448 | Assumptions: - gf_InitSaver must have been called on saver atleast once. 449 | - *saver is not NULL 450 | - *file is not NULL and is a valid file. 451 | - *identifier is not NULL and is a NULL terminated string. 452 | - *value is not NULL 453 | Returns: 1 if it was successful. 0 if not. If an error occurs this is logged. 454 | Examples: 455 | { 456 | gf_Saver saver; 457 | gf_SaveInit(&saver, NULL); 458 | 459 | FILE *file = fopen(file, "myfile", "rb"); 460 | if (!file) return 0; 461 | 462 | double value = 1.0; 463 | 464 | gf_SaveVariableF64(&saver, file, "identifier", &value); 465 | 466 | fclose(file); 467 | } 468 | */ 469 | int gf_SaveVariableF64(gf_Saver *saver, FILE *file, const char *identifier, gf_f64 *value); 470 | 471 | /* 472 | Name: int gf_SaveVariableStringSpan(gf_Saver *saver, FILE *file, const char *identifier, const char *str, int strLen); 473 | Description: Saves a string span to the file with the given value. This will be in the format "a { "Hello, world" }" 474 | where "a" is the identifier and "Hellow, world" is the string. 475 | Assumptions: - gf_InitSaver must have been called on saver atleast once. 476 | - *saver is not NULL 477 | - *file is not NULL and is a valid file. 478 | - *identifier is not NULL and is a NULL terminated string. 479 | - *str is not NULL and is a NULL terminated string. 480 | - if strlen is longer than the NULL terminated string then only the string up to the NULL terminated character is saved. 481 | Returns: 1 if it was successful. 0 if not. If an error occurs this is logged. 482 | Examples: 483 | { 484 | gf_Saver saver; 485 | gf_SaveInit(&saver, NULL); 486 | 487 | FILE *file = fopen(file, "myfile", "rb"); 488 | if (!file) return 0; 489 | 490 | const char *str = "hello, world"; 491 | 492 | gf_SaveVariableStringSpan(&saver, file, "identifier", str, 5); 493 | 494 | fclose(file); 495 | } 496 | */ 497 | int gf_SaveVariableStringSpan(gf_Saver *saver, FILE *file, const char *identifier, const char *str, int strLen); 498 | 499 | /* 500 | Name: int gf_SaveVariableVec3(gf_Saver *saver, FILE *file, const char *identifier, gf_f32 *x, gf_f32 *y, gf_f32 *z); 501 | Description: Saves a vector to the file with the given value. This will be in the format "a { 2.0, 1.0, 1.2 }" 502 | where "a" is the identifier and "2.0, 1.0, 1.2" is x, y and z respectively. 503 | Assumptions: - gf_InitSaver must have been called on saver atleast once. 504 | - *saver is not NULL 505 | - *file is not NULL and is a valid file. 506 | - *identifier is not NULL and is a NULL terminated string. 507 | - *x, *y and *z are not NULL 508 | Returns: 1 if it was successful. 0 if not. If an error occurs this is logged. 509 | Examples: 510 | { 511 | gf_Saver saver; 512 | gf_SaveInit(&saver, NULL); 513 | 514 | FILE *file = fopen(file, "myfile", "rb"); 515 | if (!file) return 0; 516 | 517 | float x = 1.0f; 518 | float y = 2.0f; 519 | float z = 3.0f; 520 | 521 | gf_SaveVariableVec3(&saver, file, "identifier", x, y, z); 522 | 523 | fclose(file); 524 | } 525 | */ 526 | int gf_SaveVariableVec3(gf_Saver *saver, FILE *file, const char *identifier, gf_f32 *x, gf_f32 *y, gf_f32 *z); 527 | 528 | /* 529 | Name: int gf_SaveArrayU64(gf_Saver *saver, FILE *file, const char *identifier, gf_u64 *value, int count); 530 | Description: Saves an array to the file. This will be in the format "a { 1, 2, ... }" 531 | up to a "count" of values. 532 | Assumptions: - gf_InitSaver must have been called on saver atleast once. 533 | - *saver is not NULL 534 | - *file is not NULL and is a valid file. 535 | - *identifier is not NULL and is a NULL terminated string. 536 | - *value is not NULL 537 | - value is atleast of length count. 538 | Returns: 1 if it was successful. 0 if not. If an error occurs this is logged. 539 | Examples: 540 | { 541 | gf_Saver saver; 542 | gf_SaveInit(&saver, NULL); 543 | 544 | FILE *file = fopen(file, "myfile", "rb"); 545 | if (!file) return 0; 546 | 547 | gf_u64 value[] = { 1, 2 }; 548 | gf_SaveArrayU64(&saver, file, "identifier", value, 2); 549 | 550 | fclose(file); 551 | } 552 | */ 553 | int gf_SaveArrayU64(gf_Saver *saver, FILE *file, const char *identifier, gf_u64 *value, int count); 554 | 555 | /* 556 | Name: int gf_SaveArrayS64(gf_Saver *saver, FILE *file, const char *identifier, gf_s64 *value, int count); 557 | Description: Saves an array to the file. This will be in the format "a { 1, 2, ... }" 558 | up to a "count" of values. 559 | Assumptions: - gf_InitSaver must have been called on saver atleast once. 560 | - *saver is not NULL 561 | - *file is not NULL and is a valid file. 562 | - *identifier is not NULL and is a NULL terminated string. 563 | - *value is not NULL 564 | - value is atleast of length count. 565 | Returns: 1 if it was successful. 0 if not. If an error occurs this is logged. 566 | Examples: 567 | { 568 | gf_Saver saver; 569 | gf_SaveInit(&saver, NULL); 570 | 571 | FILE *file = fopen(file, "myfile", "rb"); 572 | if (!file) return 0; 573 | 574 | gf_s64 value[] = { 1, 2 }; 575 | gf_SaveArrayS64(&saver, file, "identifier", value, 2); 576 | 577 | fclose(file); 578 | } 579 | */ 580 | int gf_SaveArrayS64(gf_Saver *saver, FILE *file, const char *identifier, gf_s64 *value, int count); 581 | 582 | /* 583 | Name: int gf_SaveArrayS32(gf_Saver *saver, FILE *file, const char *identifier, gf_s32 *value, int count); 584 | Description: Saves an array to the file. This will be in the format "a { 1, 2, ... }" 585 | up to a "count" of values. 586 | Assumptions: - gf_InitSaver must have been called on saver atleast once. 587 | - *saver is not NULL 588 | - *file is not NULL and is a valid file. 589 | - *identifier is not NULL and is a NULL terminated string. 590 | - *value is not NULL 591 | - value is atleast of length count. 592 | Returns: 1 if it was successful. 0 if not. If an error occurs this is logged. 593 | Examples: 594 | { 595 | gf_Saver saver; 596 | gf_SaveInit(&saver, NULL); 597 | 598 | FILE *file = fopen(file, "myfile", "rb"); 599 | if (!file) return 0; 600 | 601 | gf_u32 value[] = { 1, 2 }; 602 | gf_SaveArrayS32(&saver, file, "identifier", value, 2); 603 | 604 | fclose(file); 605 | } 606 | */ 607 | int gf_SaveArrayS32(gf_Saver *saver, FILE *file, const char *identifier, gf_s32 *value, int count); 608 | 609 | /* 610 | Name: int gf_SaveStartList(gf_Saver *saver, FILE *file, const char *identifier); 611 | Description: Saves a composite list to the file. This will be in the format "a { " 612 | If you want to start writing an arbitrary list of values then this is a good start. 613 | Must be capped off with a call to gf_SaveEndList otherwise your file will be malformed. 614 | Assumptions: - gf_InitSaver must have been called on saver atleast once. 615 | - *saver is not NULL 616 | - *file is not NULL and is a valid file. 617 | - *identifier is not NULL and is a NULL terminated string. 618 | - When finished saveing to this list you end with a call to gf_SaveEndList. 619 | Returns: 1 if it was successful. 0 if not. If an error occurs this is logged. 620 | Examples: 621 | { 622 | gf_Saver saver; 623 | gf_SaveInit(&saver, NULL); 624 | 625 | FILE *file = fopen(file, "myfile", "rb"); 626 | if (!file) return 0; 627 | 628 | gf_SaverStartList(&saver, file); 629 | { 630 | float value = 1.0f; 631 | gf_SaveVariableF64(&saver, file, "myfloat", &value); 632 | } 633 | gf_SaverEndList(&saver, file); 634 | 635 | fclose(file); 636 | } 637 | */ 638 | int gf_SaveStartList(gf_Saver *saver, FILE *file, const char *identifier); 639 | 640 | /* 641 | Name: int gf_SaveEndList(gf_Saver *saver, FILE *file); 642 | Description: Saves the end of a composite list to the file. This will be in the format " }" 643 | If you want to end a list this is how you do it. 644 | Assumptions: - gf_InitSaver must have been called on saver atleast once. 645 | - *saver is not NULL 646 | - *file is not NULL and is a valid file. 647 | - *identifier is not NULL and is a NULL terminated string. 648 | - Assumes gf_SaveEndList was called to start the list you want to end. 649 | Returns: 1 if it was successful. 0 if not. If an error occurs this is logged. 650 | Examples: 651 | { 652 | gf_Saver saver; 653 | gf_SaveInit(&saver, NULL); 654 | 655 | FILE *file = fopen(file, "myfile", "rb"); 656 | if (!file) return 0; 657 | 658 | gf_SaverStartList(&saver, file); 659 | { 660 | float value = 1.0f; 661 | gf_SaveVariableF64(&saver, file, "myfloat", &value); 662 | } 663 | gf_SaverEndList(&saver, file); 664 | 665 | fclose(file); 666 | } 667 | */ 668 | int gf_SaveEndList(gf_Saver *saver, FILE *file); 669 | 670 | /*-----------------------------------------------------------------------------------*/ 671 | 672 | /*------------------------------------LOADER-----------------------------------------*/ 673 | 674 | /* 675 | Stores a pointer to a buffer that you want to tokeniser. Tracks the current index 676 | into this buffer as you tokenise. Also tracks things like the current line number and 677 | column number. 678 | */ 679 | typedef struct gf_Tokeniser { 680 | const char *buffer; // A pointer to a null terminated buffer that needs to be tokenised 681 | gf_u64 index; // The internal tracking index that records the current character in the buffer 682 | gf_u64 count; // The total size of the buffer. 683 | gf_u64 lineno; // The current line number. 684 | gf_u64 colno; // The current column number. 685 | } gf_Tokeniser; 686 | 687 | /* 688 | Name: void gf_InitTokeniser(gf_Tokeniser *tokeniser, const char *buffer, gf_u64 count); 689 | Description: Initialises the tokeniser. This must be called before using the tokeniser. You can call this multiple times to reset the tokeniser. 690 | The buffer you wish to tokenise is passed in, along with the buffer length (count). This is not copied. So, while you 691 | are tokenising and using the tokeniser, this buffer must be valid. 692 | This buffer must be NULL terminated. 693 | Assumptions: - *tokeniser is not NULL 694 | - *buffer is not NULL and is NULL terminated. 695 | - The count must represent a valid length for the buffer. If NULL terminating character is 696 | encountered before the count is reached then tokenising will stop. 697 | Returns: Nothing. 698 | */ 699 | void gf_InitTokeniser(gf_Tokeniser *tokeniser, const char *buffer, gf_u64 count); 700 | 701 | /* 702 | Name: char gf_GetChar(gf_Tokeniser *tokeniser); 703 | Description: Get the character at the tokenisers internal current index, from the buffer pointed to by the tokeniser. 704 | If the buffer's end is reached this will return a NULL terminating character. 705 | Assumptions: - tokeniser has been initialised with a call to gf_InitTokeniser(); 706 | - *tokeniser is not NULL 707 | - Assumes the buffer that the tokeniser is pointing to is still valid. 708 | Returns: Returns the character of the buffer pointed to by the tokeniser, at the tracked index. Returns a NULL terminator if the end of this buffer is reached. 709 | */ 710 | char gf_GetChar(gf_Tokeniser *tokeniser); 711 | 712 | /* 713 | Name: void gf_IncrementIndex(gf_Tokeniser *tokeniser); 714 | Description: Increments the internal tracking index of the tokeniser. If the end of the buffer tracked 715 | by the tokeniser is reached, this tracking index is not incremented. 716 | Assumptions: - tokeniser has been initialised with a call to gf_InitTokeniser(); 717 | - *tokeniser is not NULL 718 | Returns: Nothing. 719 | */ 720 | void gf_IncrementIndex(gf_Tokeniser *tokeniser); 721 | 722 | /* 723 | Name: void gf_IncrementLineNo(gf_Tokeniser *tokeniser); 724 | Description: Increments the current line number that is being tracked by the tokeniser. 725 | Assumptions: - tokeniser has been initialised with a call to gf_InitTokeniser(); 726 | - *tokeniser is not NULL 727 | Returns: Nothing. 728 | */ 729 | void gf_IncrementLineNo(gf_Tokeniser *tokeniser); 730 | 731 | /* 732 | Name: const char *gf_Ptr(gf_Tokeniser *tokeniser); 733 | Description: Returns a pointer into the buffer pointed to by the tokeniser at the internal tokeniser index. 734 | Assumptions: - tokeniser has been initialised with a call to gf_InitTokeniser(); 735 | - *tokeniser is not NULL 736 | - Assumes the buffer that the tokeniser is pointing to is still valid. 737 | Returns: A pointer into the buffer that the tokeniser points to at the location of the tokenisers internal tracking index. 738 | If the end of the buffer is reached by the internal tracking index, then a NULL is returned. 739 | */ 740 | const char *gf_Ptr(gf_Tokeniser *tokeniser); 741 | 742 | /* 743 | A node that is stored as a graph that represents parsed text. 744 | A node has an associated token. When the text forms a list these 745 | nodes are stored as a doubly linked list where they are next to each other. 746 | When data is nested, nodes will be stored as children of other nodes. 747 | */ 748 | typedef struct gf_LoaderNode { 749 | gf_Token *token; // The token associated with this list 750 | struct gf_LoaderNode *parent; // The parent of this node. This will only ever be NULL for the root token. 751 | struct gf_LoaderNode *next; // The next node in the list. 752 | struct gf_LoaderNode *prev; // The prev node in the list 753 | struct gf_LoaderNode *childrenHead; // The head of the children list of this node 754 | struct gf_LoaderNode *childrenTail; // The tail of the children list of this node. 755 | struct gf_LoaderNode *nextAllocated; // A helper linked list that tracks allocated nodes. 756 | } gf_LoaderNode; 757 | 758 | /* 759 | A helper function that is responsible for storing all tokens, nodes and potentially a buffer that 760 | was allocated when opening a file. 761 | */ 762 | typedef struct gf_Loader { 763 | gf_LogFunctionPtr Log; // The function used to log. 764 | gf_AllocatorFunctionPtr Allocate; // The function used to allocate. 765 | gf_FreeFunctionPtr Free; // The function used to free 766 | gf_Token rootToken; // The root token that is always guaranteed to exist when parsing text. 767 | gf_Token *lastToken; // The last token in the token list. 768 | gf_Token *firstToken; // The first token in the token list. 769 | gf_Token *curToken; // The current token in the token list. 770 | gf_LoaderNode *rootNode; // The root node in the node graph. 771 | gf_LoaderNode *lastNode; // The last allocated node in the node graph. 772 | char *fileContentsBuffer; // A pointer that points to memory allocated from a file. 773 | gf_u64 nestLevel; // When parsing, tracks how many {} we are nested in. 774 | } gf_Loader; 775 | 776 | /* 777 | Name: void gf_InitLoader(gf_Loader *loader, gf_LogAllocateFreeFunctions *helperfunctions); 778 | Description: Initialises the loader. This is called internally when calling one of the gf_Load...() functions. Do not call again until gf_Unload is called. 779 | Ideally don't call at all and just call one of the load function instead to begin using the loader. 780 | The loader is initialised with a log, allocate and 781 | free function pointer data structure. If this is NULL, the default logging function, malloc() and free() are used respectively. 782 | If it is not NULL and any of the helper functions within are NULL these default to gf_DefaultLog, malloc() and free respectively. 783 | The allocator MUST have pointers that are not invalidated as more data is allocated. 784 | Assumptions: - *loader is not NULL 785 | - gf_InitLoader() has not been called yet. 786 | Returns: Nothing. 787 | */ 788 | void gf_InitLoader(gf_Loader *loader, gf_LogAllocateFreeFunctions *helperfunctions); 789 | 790 | /* 791 | Name: void gf_IncrementLastTokenLength(gf_Loader *loader); 792 | Description: Increments the length of the last allocated token by loader. 793 | Assumptions: - gf_InitLoader() has been called on *loader. 794 | - gf_LoadInternal() has been called. i.e. some data has been loaded. 795 | - *loader is not NULL. 796 | Returns: Nothing. 797 | */ 798 | void gf_IncrementLastTokenLength(gf_Loader *loader); 799 | 800 | /* 801 | Name: int gf_AddToken(gf_Loader *loader, const char *start, gf_TokenType type, gf_u64 lineno, gf_u64 colno); 802 | Description: Allocates a new token used by the loader. The user specified allocator is used when the loader is 803 | initialised. The token is given the specified type and lineno along with the start string within the loaded 804 | buffer. It starts with a length of 1. 805 | Assumptions: - gf_InitLoader() has been called on *loader. 806 | - gf_LoadInternal() has been called. i.e. some data has been loaded. 807 | - *loader is not NULL 808 | - *start is not NULL 809 | Returns: Returns 1 if it succeeds. 0 if it fails. The error is logged. 810 | */ 811 | int gf_AddToken(gf_Loader *loader, const char *start, gf_TokenType type, gf_u64 lineno, gf_u64 colno); 812 | 813 | /* 814 | Name: char *gf_AllocateNullTerminatedBufferFromFile(gf_Loader *loader, const char *filename, gf_u64 *bufferCountWithNullTerminator); 815 | Description: Opens the given file. Allocates it's contents using loader's defined allocator (+1 to include the null terminator). 816 | Then closes the file. 817 | This allocated buffer is returned as a NULL terminated string. 818 | The size of the allocated buffer is stored in bufferCountWithNullTerminator. 819 | Assumptions: - gf_InitLoader() has been called on *loader. 820 | - *loader is not NULL 821 | - *filename is NOT NULL 822 | - bufferCountWithNullTerminator is not NULL. 823 | Returns: Returns a terminated string that holds the entire contents of the file +1 for the NULL terminator. 824 | Returns NULL if this failed and the error is logged. 825 | */ 826 | char *gf_AllocateNullTerminatedBufferFromFile(gf_Loader *loader, const char *filename, gf_u64 *bufferCountWithNullTerminator); 827 | 828 | /* 829 | Name: int gf_TokeniseInternal(gf_Loader *loader, gf_Tokeniser *tokeniser); 830 | Description: An internal function that begins tokenising the buffer pointed to by the tokeniser. 831 | Assumptions: - gf_InitLoader() has been called on *loader. 832 | - gf_InitTokeniser() has been called on *tokeniser. 833 | - *loader is not NULL 834 | - *tokeniser is not NULL 835 | Returns: Returns 1 if this succeeds. 0 if this fails. The error is logged. 836 | */ 837 | int gf_TokeniseInternal(gf_Loader *loader, gf_Tokeniser *tokeniser); 838 | 839 | /* 840 | Name: int gf_Tokenise(gf_Loader *loader, const char *buffer, gf_u64 count); 841 | Description: Tokenises the NULL terminated buffer that has the length count. 842 | Assumptions: - gf_InitLoader() has been called on *loader. 843 | - *buffer is NULL terminated. 844 | - count is a valid length for the buffer. 845 | - *loader is not NULL 846 | - *buffer is not NULL 847 | Returns: Returns 1 if this succeeds. 0 if this fails. The error is logged. 848 | */ 849 | int gf_Tokenise(gf_Loader *loader, const char *buffer, gf_u64 count); 850 | 851 | /* 852 | Name: gf_Token *gf_ConsumeToken(gf_Loader *loader); 853 | Description: Returns the next token in the token list and increments the current tracking index that 854 | points to the current token. 855 | Assumptions: - gf_InitLoader() has been called on *loader. 856 | - *loader is not NULL 857 | Returns: Returns NULL if there are no more tokens, else returns the token as a pointer. 858 | */ 859 | gf_Token *gf_ConsumeToken(gf_Loader *loader); 860 | 861 | /* 862 | Name: gf_Token *gf_PeekToken(gf_Loader *loader); 863 | Description: Returns the current token. 864 | Assumptions: - gf_InitLoader() has been called on *loader. 865 | - *loader is not NULL 866 | Returns: Returns the current token. This can be NULL. 867 | */ 868 | gf_Token *gf_PeekToken(gf_Loader *loader); 869 | 870 | /* 871 | Name: gf_LoaderNode *gf_AddNode(gf_Loader *loader, gf_Token *token); 872 | Description: Allocates a node using the specified allocation function. Assigns the token to this 873 | node. Returns the newly allocated node. 874 | Assumptions: - gf_InitLoader() has been called on *loader. 875 | - *token is not NULL. 876 | Returns: Returns the newly allocated node. Returns NULL if it fails. The error is logged. 877 | */ 878 | gf_LoaderNode *gf_AddNode(gf_Loader *loader, gf_Token *token); 879 | 880 | /* 881 | Name: void gf_AddChild(gf_LoaderNode *parent, gf_LoaderNode *child); 882 | Description: Adds the child node to the parents children list of nodes. 883 | Assumptions: - gf_InitLoader() has been called on *loader. 884 | - *parent is not NULL. 885 | - *child is not NULL. 886 | Returns: Nothing. 887 | */ 888 | void gf_AddChild(gf_LoaderNode *parent, gf_LoaderNode *child); 889 | 890 | /* 891 | Name: int gf_Parse(gf_Loader *loader, gf_LoaderNode *parentNode); 892 | Description: Parses the token list. The tokeniser must have been called before this happens and 893 | the root node of loader must have been created. This is a recursive function. The first time 894 | you call it, parentNode must be the root node of the loader. 895 | Assumptions: - gf_InitLoader() has been called on *loader. 896 | - The loader has performed tokenisation. 897 | - *loader is not NULL. 898 | - *parentNode is not NULL and needs to be the root node when you call this initially. 899 | Returns: Returns 1 if it succeeds. Returns 0 if it fails. The error is logged. 900 | */ 901 | int gf_Parse(gf_Loader *loader, gf_LoaderNode *parentNode); 902 | 903 | /* 904 | Name: int gf_LoadInternal(gf_Loader *loader, const char *buffer, gf_u64 bufferCount); 905 | Description: Begins tokenising and parsing the passed in buffer, preparing data that can be queried by the user. 906 | Buffer needs to be NULL terminated. The bufferCount needs to represent a valid span for the buffer. 907 | Assumptions: - *loader is not NULL. 908 | - *buffer is not NULL. 909 | - bufferCount is a valid length for the buffer. 910 | Returns: Returns 1 if it succeeds. Returns 0 if it fails. The error is logged. 911 | */ 912 | int gf_LoadInternal(gf_Loader *loader, const char *buffer, gf_u64 bufferCount); 913 | 914 | /* 915 | Name: int gf_LoadFromBuffer(gf_Loader *loader, const char *buffer, gf_u64 bufferCount, gf_LogAllocateFreeFunctions *funcs); 916 | Description: Begins tokenising and parsing the passed in buffer, preparing data that can be queried by the user. 917 | This should be the first thing you call before using the loader. After you are done, you need to call gf_Unload(), even if this function fails. 918 | The buffer needs to be NULL terminated. The bufferCount needs to represent a valid span for the buffer. 919 | *funcs can be NULL or contain NULL function pointers. In this case, the default function pointers are used, 920 | gf_DefaultLog, malloc() and free(). 921 | This function allocates things using the passed in allocation function. 922 | Assumptions: - *loader is not NULL. 923 | - *buffer is not NULL. 924 | - funcs can be NULL. 925 | - bufferCount is a valid length for the buffer. 926 | Returns: Returns 1 if it succeeds. Returns 0 if it fails. The error is logged. 927 | Examples: 928 | { 929 | gf_Loader loader; 930 | const char *buffer = "mydata { 1.0 }"; 931 | gf_LoadFromBuffer(&loader, buffer, gf_StringLength(buffer), NULL); 932 | // ... do stuff ... 933 | gf_Unload(&loader); 934 | } 935 | */ 936 | int gf_LoadFromBuffer(gf_Loader *loader, const char *buffer, gf_u64 bufferCount, gf_LogAllocateFreeFunctions *funcs); 937 | 938 | /* 939 | Name: int gf_LoadFromFile(gf_Loader *loader, const char *filename, gf_LogAllocateFreeFunctions *funcs); 940 | Description: Begins tokenising and parsing the passed in buffer, preparing data that can be queried by the user. 941 | This should be the first thing you call before using the loader. After you are done, you need to call gf_Unload(), even if this function fails. 942 | This opens the specified file and allocates its contents into a buffer. This buffer is then tokenised and parsed to 943 | be inspected by the user. 944 | *funcs can be NULL or contain NULL function pointers. In this case, the default function pointers are used, 945 | gf_DefaultLog, malloc() and free(). 946 | This function allocates things using the passed in allocation function. 947 | This open 948 | Assumptions: - *loader is not NULL. 949 | - *filename is not NULL. 950 | - funcs can be NULL. 951 | Returns: Returns 1 if it succeeds. Returns 0 if it fails. The error is logged. 952 | Examples: 953 | { 954 | gf_Loader loader; 955 | const char *filename = "myfile.gf"; 956 | gf_LoadFromFile(&loader, filename, NULL); 957 | // ... do stuff ... 958 | gf_Unload(&loader); 959 | } 960 | */ 961 | int gf_LoadFromFile(gf_Loader *loader, const char *filename, gf_LogAllocateFreeFunctions *funcs); 962 | 963 | /* 964 | Name: void gf_Unload(gf_Loader *loader); 965 | Description: Frees all data allocated by the loader using the user defined Free() function. Must be called after gf_Load...() 966 | function has been called prefably when you are finished with the loader. It does not matter, 967 | if the load failed, you still need to call gf_Unload. 968 | After unload is called you must load the loader again with new data if you want to use it. 969 | If you called gf_LoadFromFile, this deallocates the buffer that was allocated. 970 | Assumptions: - gf_LoadFromBuffer or gf_LoadFromFile has been called. 971 | - *loader is not NULL. 972 | Returns: Nothing 973 | Examples: 974 | { 975 | gf_Loader loader; 976 | const char *filename = "myfile.gf"; 977 | gf_LoadFromFile(&loader, filename, NULL); 978 | // ... do stuff ... 979 | gf_Unload(&loader); 980 | } 981 | */ 982 | void gf_Unload(gf_Loader *loader); 983 | 984 | /* 985 | Name: int gf_LoaderNodeToU32(gf_Loader *loader, gf_LoaderNode *node, gf_u32 *value); 986 | Description: Converts the value of the node and copies it into value. 987 | Does type checking of the node. 988 | A u32 node is an integer node. This takes the form => 123 989 | Assumptions: - gf_LoadFromBuffer or gf_LoadFromFile has been called and was successful. 990 | - *loader is not NULL. 991 | - *value is not NULL. 992 | - node can be NULL. 993 | Returns: Returns 1 if the conversion was successful. Returns 0 if node was NULL, the node 994 | was not an integer type or the conversion fail. The error is logged. 995 | Examples: 996 | { 997 | gf_Loader loader; 998 | const char *filename = "myfile.gf"; 999 | gf_LoadFromFile(&loader, filename, NULL); 1000 | 1001 | gf_LoaderNode *root = gf_GetRoot(loader); 1002 | gf_LoaderNode *child = gf_GetChild(loader, root); 1003 | 1004 | gf_u32 value; 1005 | gf_LoaderNodeToU32(&loader, child, &value); 1006 | 1007 | gf_Unload(&loader); 1008 | } 1009 | */ 1010 | int gf_LoaderNodeToU32(gf_Loader *loader, gf_LoaderNode *node, gf_u32 *value); 1011 | 1012 | /* 1013 | Name: int gf_LoaderNodeToU64(gf_Loader *loader, gf_LoaderNode *node, gf_u64 *value); 1014 | Description: Converts the value of the node and copies it into value. 1015 | Does type checking of the node. 1016 | A u64 node is an integer node. This takes the form => 123 1017 | Assumptions: - gf_LoadFromBuffer or gf_LoadFromFile has been called and was successful. 1018 | - *loader is not NULL. 1019 | - *value is not NULL. 1020 | - node can be NULL. 1021 | Returns: Returns 1 if the conversion was successful. Returns 0 if node was NULL, the node 1022 | was not an integer type or the conversion fail. The error is logged. 1023 | Examples: 1024 | { 1025 | gf_Loader loader; 1026 | const char *filename = "myfile.gf"; 1027 | gf_LoadFromFile(&loader, filename, NULL); 1028 | 1029 | gf_LoaderNode *root = gf_GetRoot(loader); 1030 | gf_LoaderNode *child = gf_GetChild(loader, root); 1031 | 1032 | gf_u64 value; 1033 | gf_LoaderNodeToU64(&loader, child, &value); 1034 | 1035 | gf_Unload(&loader); 1036 | } 1037 | */ 1038 | int gf_LoaderNodeToU64(gf_Loader *loader, gf_LoaderNode *node, gf_u64 *value); 1039 | 1040 | /* 1041 | Name: int gf_LoaderNodeToS32(gf_Loader *loader, gf_LoaderNode *node, gf_s32 *value); 1042 | Description: Converts the value of the node and copies it into value. 1043 | Does type checking of the node. 1044 | A s32 node is an integer node. This takes the form => 123 1045 | Assumptions: - gf_LoadFromBuffer or gf_LoadFromFile has been called and was successful. 1046 | - *loader is not NULL. 1047 | - *value is not NULL. 1048 | - node can be NULL. 1049 | Returns: Returns 1 if the conversion was successful. Returns 0 if node was NULL, the node 1050 | was not an integer type or the conversion fail. The error is logged. 1051 | Examples: 1052 | { 1053 | gf_Loader loader; 1054 | const char *filename = "myfile.gf"; 1055 | gf_LoadFromFile(&loader, filename, NULL); 1056 | 1057 | gf_LoaderNode *root = gf_GetRoot(loader); 1058 | gf_LoaderNode *child = gf_GetChild(loader, root); 1059 | 1060 | gf_s32 value; 1061 | gf_LoaderNodeToS32(&loader, child, &value); 1062 | 1063 | gf_Unload(&loader); 1064 | } 1065 | */ 1066 | int gf_LoaderNodeToS32(gf_Loader *loader, gf_LoaderNode *node, gf_s32 *value); 1067 | 1068 | /* 1069 | Name: int gf_LoaderNodeToS64(gf_Loader *loader, gf_LoaderNode *node, gf_s64 *value); 1070 | Description: Converts the value of the node and copies it into value. 1071 | Does type checking of the node. 1072 | A s64 node is an integer node. This takes the form => 123 1073 | Assumptions: - gf_LoadFromBuffer or gf_LoadFromFile has been called and was successful. 1074 | - *loader is not NULL. 1075 | - *value is not NULL. 1076 | - node can be NULL. 1077 | Returns: Returns 1 if the conversion was successful. Returns 0 if node was NULL, the node 1078 | was not an integer type or the conversion fail. The error is logged. 1079 | Examples: 1080 | { 1081 | gf_Loader loader; 1082 | const char *filename = "myfile.gf"; 1083 | gf_LoadFromFile(&loader, filename, NULL); 1084 | 1085 | gf_LoaderNode *root = gf_GetRoot(loader); 1086 | gf_LoaderNode *child = gf_GetChild(loader, root); 1087 | 1088 | gf_s64 value; 1089 | gf_LoaderNodeToS64(&loader, child, &value); 1090 | 1091 | gf_Unload(&loader); 1092 | } 1093 | */ 1094 | int gf_LoaderNodeToS64(gf_Loader *loader, gf_LoaderNode *node, gf_s64 *value); 1095 | 1096 | /* 1097 | Name: int gf_LoaderNodeToF64(gf_Loader *loader, gf_LoaderNode *node, gf_f64 *value); 1098 | Description: Converts the value of the node and copies it into value. 1099 | Does type checking of the node. 1100 | A f64 node is an floating point node. This takes the form => 1.0 1101 | Assumptions: - gf_LoadFromBuffer or gf_LoadFromFile has been called and was successful. 1102 | - *loader is not NULL. 1103 | - *value is not NULL. 1104 | - node can be NULL. 1105 | Returns: Returns 1 if the conversion was successful. Returns 0 if node was NULL, the node 1106 | was not an floating point type or the conversion fail. The error is logged. 1107 | Examples: 1108 | { 1109 | gf_Loader loader; 1110 | const char *filename = "myfile.gf"; 1111 | gf_LoadFromFile(&loader, filename, NULL); 1112 | 1113 | gf_LoaderNode *root = gf_GetRoot(loader); 1114 | gf_LoaderNode *child = gf_GetChild(loader, root); 1115 | 1116 | gf_f64 value; 1117 | gf_LoaderNodeToF64(&loader, child, &value); 1118 | 1119 | gf_Unload(&loader); 1120 | } 1121 | */ 1122 | int gf_LoaderNodeToF64(gf_Loader *loader, gf_LoaderNode *node, gf_f64 *value); 1123 | 1124 | /* 1125 | Name: int gf_LoaderNodeToF32(gf_Loader *loader, gf_LoaderNode *node, gf_f32 *value); 1126 | Description: Converts the value of the node and copies it into value. 1127 | Does type checking of the node. 1128 | A f32 node is an floating point node. This takes the form => 1.0 1129 | Assumptions: - gf_LoadFromBuffer or gf_LoadFromFile has been called and was successful. 1130 | - *loader is not NULL. 1131 | - *value is not NULL. 1132 | - node can be NULL. 1133 | Returns: Returns 1 if the conversion was successful. Returns 0 if node was NULL, the node 1134 | was not an floating point type or the conversion fail. The error is logged. 1135 | Examples: 1136 | { 1137 | gf_Loader loader; 1138 | const char *filename = "myfile.gf"; 1139 | gf_LoadFromFile(&loader, filename, NULL); 1140 | 1141 | gf_LoaderNode *root = gf_GetRoot(loader); 1142 | gf_LoaderNode *child = gf_GetChild(loader, root); 1143 | 1144 | gf_f32 value; 1145 | gf_LoaderNodeToF32(&loader, child, &value); 1146 | 1147 | gf_Unload(&loader); 1148 | } 1149 | */ 1150 | int gf_LoaderNodeToF32(gf_Loader *loader, gf_LoaderNode *node, gf_f32 *value); 1151 | 1152 | /* 1153 | Name: int gf_LoaderNodeToString(gf_Loader *loader, gf_LoaderNode *node, char *src, gf_u64 srcCapacityIncludesNullTerminator); 1154 | Description: Converts the value of the node and copies it into src. 1155 | srcCapacityIncludesNullTerminator is the length of src + the NULL terminating character. 1156 | Does type checking of the node. 1157 | A string node is a string node. This takes the form => "Hello!" 1158 | Assumptions: - gf_LoadFromBuffer or gf_LoadFromFile has been called and was successful. 1159 | - *loader is not NULL. 1160 | - *src is not NULL. 1161 | - node can be NULL. 1162 | Returns: Returns 1 if the conversion was successful. Returns 0 if node was NULL, the node 1163 | was not an string type or the passed in capacity is not big enough to fit the size of the token. The error is logged. 1164 | Examples: 1165 | { 1166 | gf_Loader loader; 1167 | const char *filename = "myfile.gf"; 1168 | gf_LoadFromFile(&loader, filename, NULL); 1169 | 1170 | gf_LoaderNode *root = gf_GetRoot(loader); 1171 | gf_LoaderNode *child = gf_GetChild(loader, root); 1172 | 1173 | gf_f32 value; 1174 | gf_LoaderNodeToF32(&loader, child, &value); 1175 | 1176 | gf_Unload(&loader); 1177 | } 1178 | */ 1179 | int gf_LoaderNodeToString(gf_Loader *loader, gf_LoaderNode *node, char *src, gf_u64 srcCapacityIncludesNullTerminator); 1180 | 1181 | /* 1182 | Name: gf_LoaderNode *gf_GetRoot(gf_Loader *loader); 1183 | Description: Gets the root node of the parsed contents of a file. gf_LoadFromBuffer or gf_LoadFromFile 1184 | must have been called and must have been successful before a valid root exists. 1185 | Assumptions: - gf_LoadFromBuffer or gf_LoadFromFile has been called and was successful. 1186 | - *loader is not NULL. 1187 | Returns: Returns the root node. This cannot be NULL if the assumptions are satisfied. 1188 | Examples: 1189 | { 1190 | gf_Loader loader; 1191 | const char *filename = "myfile.gf"; 1192 | gf_LoadFromFile(&loader, filename, NULL); 1193 | 1194 | gf_LoaderNode *root = gf_GetRoot(&loader); 1195 | 1196 | gf_Unload(&loader); 1197 | } 1198 | */ 1199 | gf_LoaderNode *gf_GetRoot(gf_Loader *loader); 1200 | 1201 | /* 1202 | Name: gf_LoaderNode *gf_GetNext(gf_Loader *loader, gf_LoaderNode *node); 1203 | Description: Gets the next node after *node. 1204 | Assumptions: - gf_LoadFromBuffer or gf_LoadFromFile has been called and was successful. 1205 | - *loader is not NULL. 1206 | - node can be NULL 1207 | Returns: Returns the next node. Returns NULL if there is no next node or node is NULL 1208 | Examples: 1209 | { 1210 | gf_Loader loader; 1211 | const char *filename = "myfile.gf"; 1212 | gf_LoadFromFile(&loader, filename, NULL); 1213 | 1214 | gf_LoaderNode *root = gf_GetRoot(&loader); 1215 | gf_LoaderNode *nextNode = gf_GetNext(&loader, root); 1216 | 1217 | gf_Unload(&loader); 1218 | } 1219 | */ 1220 | gf_LoaderNode *gf_GetNext(gf_Loader *loader, gf_LoaderNode *node); 1221 | 1222 | /* 1223 | Name: gf_LoaderNode *gf_GetChild(gf_Loader *loader, gf_LoaderNode *node); 1224 | Description: Gets the first child node in *node's child list. 1225 | Assumptions: - gf_LoadFromBuffer or gf_LoadFromFile has been called and was successful. 1226 | - *loader is not NULL. 1227 | - node can be NULL 1228 | Returns: Returns the first child node. If there are no children or node is NULL this returns NULL. 1229 | Examples: 1230 | { 1231 | gf_Loader loader; 1232 | const char *filename = "myfile.gf"; 1233 | gf_LoadFromFile(&loader, filename, NULL); 1234 | 1235 | gf_LoaderNode *root = gf_GetRoot(&loader); 1236 | gf_LoaderNode *childNode = gf_GetChild(&loader, root); 1237 | 1238 | gf_Unload(&loader); 1239 | } 1240 | */ 1241 | gf_LoaderNode *gf_GetChild(gf_Loader *loader, gf_LoaderNode *node); 1242 | 1243 | /* 1244 | Name: gf_TokenType gf_GetType(gf_Loader *loader, gf_LoaderNode *node); 1245 | Description: Gets the token type of the node. 1246 | Assumptions: - gf_LoadFromBuffer or gf_LoadFromFile has been called and was successful. 1247 | - *loader is not NULL. 1248 | - node can be NULL 1249 | Returns: Returns the token type of the node. If the node is NULL then the returned token type is GF_TOKEN_TYPE_END_FILE 1250 | Examples: 1251 | { 1252 | gf_Loader loader; 1253 | const char *filename = "myfile.gf"; 1254 | gf_LoadFromFile(&loader, filename, NULL); 1255 | 1256 | gf_LoaderNode *root = gf_GetRoot(&loader); 1257 | 1258 | gf_TokenType tokenType = gf_GetType(&loader, root); 1259 | 1260 | gf_Unload(&loader); 1261 | } 1262 | */ 1263 | gf_TokenType gf_GetType(gf_Loader *loader, gf_LoaderNode *node); 1264 | 1265 | /* 1266 | Name: gf_LoaderNode *gf_FindFirstChild(gf_Loader *loader, gf_LoaderNode *node, const char *str); 1267 | Description: Finds the first child in the node's child list that has a name that is equal to str. 1268 | Only finds nodes that are composite/named types (named nodes that may have children). 1269 | If two nodes have the same name, the first child in the list is returned. The list of children 1270 | has the same order as the children in the file. 1271 | Assumptions: - gf_LoadFromBuffer or gf_LoadFromFile has been called and was successful. 1272 | - *loader is not NULL. 1273 | - node can be NULL 1274 | - str is not NULL and is NULL terminated. 1275 | Returns: Returns the first child in the node's child list that has the same name as str. Returns NULL if 1276 | this could not be found or node is NULL. See description for more details. 1277 | Examples: 1278 | { 1279 | gf_Loader loader; 1280 | const char *filename = "myfile.gf"; 1281 | gf_LoadFromFile(&loader, filename, NULL); 1282 | 1283 | gf_LoaderNode *root = gf_GetRoot(&loader); 1284 | 1285 | gf_LoaderNode *childNode = gf_FindFirstChild(&loader, root, "ChildName"); 1286 | 1287 | gf_Unload(&loader); 1288 | } 1289 | */ 1290 | gf_LoaderNode *gf_FindFirstChild(gf_Loader *loader, gf_LoaderNode *node, const char *str); 1291 | 1292 | /* 1293 | Name: gf_LoaderNode *gf_FindFirstNext(gf_Loader *loader, gf_LoaderNode *node, const char *str); 1294 | Description: Finds the first node in the passed in node's next list. 1295 | The first node that has a name equal to str in the next list, starting at node, is returned. 1296 | Only finds nodes that are composite/named types (named nodes that may have children). 1297 | If two nodes have the same name, the first node in the list is returned. 1298 | Assumptions: - gf_LoadFromBuffer or gf_LoadFromFile has been called and was successful. 1299 | - *loader is not NULL. 1300 | - node can be NULL 1301 | - str is not NULL and is NULL terminated. 1302 | Returns: Returns the first node after the current node that has the same name as str. Returns NULL if 1303 | this could not be found or node is NULL. See description for more details. 1304 | Examples: 1305 | { 1306 | gf_Loader loader; 1307 | const char *filename = "myfile.gf"; 1308 | gf_LoadFromFile(&loader, filename, NULL); 1309 | 1310 | gf_LoaderNode *root = gf_GetRoot(&loader); 1311 | 1312 | gf_LoaderNode *childNode = gf_FindFirstChild(&loader, root, "ChildName"); 1313 | 1314 | gf_LoaderNode *nextNode = gf_FindFirstNext(&loader, childNode, "NextName"); 1315 | 1316 | gf_Unload(&loader); 1317 | } 1318 | */ 1319 | gf_LoaderNode *gf_FindFirstNext(gf_Loader *loader, gf_LoaderNode *node, const char *str); 1320 | 1321 | /* 1322 | Name: int gf_LoadVariableU32(gf_Loader *loader, gf_LoaderNode *node, gf_u32 *value); 1323 | Description: A helper function that takes a set of nodes in a certain form and converts them to a value. 1324 | If the node has the form => Var { 1 }, where Var corresponds to the passed in node, the value inside 1325 | the braces is converted and copied into value. 1326 | Assumptions: - gf_LoadFromBuffer or gf_LoadFromFile has been called and was successful. 1327 | - *loader is not NULL. 1328 | - node can be NULL 1329 | - value is not NULL 1330 | Returns: Returns 1 if successful. Returns 0 if, either, the node is not in the form as described 1331 | in the description, the conversion to the value failed, the value node in is the wrong type or 1332 | node is NULL. 1333 | Examples: 1334 | { 1335 | gf_Loader loader; 1336 | const char *filename = "myfile.gf"; 1337 | gf_LoadFromFile(&loader, filename, NULL); 1338 | 1339 | gf_LoaderNode *root = gf_GetRoot(&loader); 1340 | 1341 | gf_LoaderNode *childNode = gf_FindFirstChild(&loader, root, "ChildName"); 1342 | 1343 | gf_u32 value = 0; 1344 | gf_LoadVariableU32(&loader, childNode, &value); 1345 | 1346 | gf_Unload(&loader); 1347 | } 1348 | */ 1349 | int gf_LoadVariableU32(gf_Loader *loader, gf_LoaderNode *node, gf_u32 *value); 1350 | 1351 | /* 1352 | Name: int gf_LoadVariableU64(gf_Loader *loader, gf_LoaderNode *node, gf_u64 *value); 1353 | Description: A helper function that takes a set of nodes in a certain form and converts them to a value. 1354 | If the node has the form => Var { 1 }, where Var corresponds to the passed in node, the value inside 1355 | the braces is converted and copied into value. 1356 | Assumptions: - gf_LoadFromBuffer or gf_LoadFromFile has been called and was successful. 1357 | - *loader is not NULL. 1358 | - node can be NULL 1359 | - value is not NULL 1360 | Returns: Returns 1 if successful. Returns 0 if, either, the node is not in the form as described 1361 | in the description, the conversion to the value failed, the value node in is the wrong type or 1362 | node is NULL. 1363 | Examples: 1364 | { 1365 | gf_Loader loader; 1366 | const char *filename = "myfile.gf"; 1367 | gf_LoadFromFile(&loader, filename, NULL); 1368 | 1369 | gf_LoaderNode *root = gf_GetRoot(&loader); 1370 | 1371 | gf_LoaderNode *childNode = gf_FindFirstChild(&loader, root, "ChildName"); 1372 | 1373 | gf_u64 value = 0; 1374 | gf_LoadVariableU64(&loader, childNode, &value); 1375 | 1376 | gf_Unload(&loader); 1377 | } 1378 | */ 1379 | int gf_LoadVariableU64(gf_Loader *loader, gf_LoaderNode *node, gf_u64 *value); 1380 | 1381 | /* 1382 | Name: int gf_LoadVariableS32(gf_Loader *loader, gf_LoaderNode *node, gf_s32 *value); 1383 | Description: A helper function that takes a set of nodes in a certain form and converts them to a value. 1384 | If the node has the form => Var { 1 }, where Var corresponds to the passed in node, the value inside 1385 | the braces is converted and copied into value. 1386 | Assumptions: - gf_LoadFromBuffer or gf_LoadFromFile has been called and was successful. 1387 | - *loader is not NULL. 1388 | - node can be NULL 1389 | - value is not NULL 1390 | Returns: Returns 1 if successful. Returns 0 if, either, the node is not in the form as described 1391 | in the description, the conversion to the value failed, the value node in is the wrong type or 1392 | node is NULL. 1393 | Examples: 1394 | { 1395 | gf_Loader loader; 1396 | const char *filename = "myfile.gf"; 1397 | gf_LoadFromFile(&loader, filename, NULL); 1398 | 1399 | gf_LoaderNode *root = gf_GetRoot(&loader); 1400 | 1401 | gf_LoaderNode *childNode = gf_FindFirstChild(&loader, root, "ChildName"); 1402 | 1403 | gf_s32 value = 0; 1404 | gf_LoadVariableS32(&loader, childNode, &value); 1405 | 1406 | gf_Unload(&loader); 1407 | } 1408 | */ 1409 | int gf_LoadVariableS32(gf_Loader *loader, gf_LoaderNode *node, gf_s32 *value); 1410 | 1411 | /* 1412 | Name: int gf_LoadVariableS64(gf_Loader *loader, gf_LoaderNode *node, gf_s64 *value); 1413 | Description: A helper function that takes a set of nodes in a certain form and converts them to a value. 1414 | If the node has the form => Var { 1 }, where Var corresponds to the passed in node, the value inside 1415 | the braces is converted and copied into value. 1416 | Assumptions: - gf_LoadFromBuffer or gf_LoadFromFile has been called and was successful. 1417 | - *loader is not NULL. 1418 | - node can be NULL 1419 | - value is not NULL 1420 | Returns: Returns 1 if successful. Returns 0 if, either, the node is not in the form as described 1421 | in the description, the conversion to the value failed, the value node in is the wrong type or 1422 | node is NULL. 1423 | Examples: 1424 | { 1425 | gf_Loader loader; 1426 | const char *filename = "myfile.gf"; 1427 | gf_LoadFromFile(&loader, filename, NULL); 1428 | 1429 | gf_LoaderNode *root = gf_GetRoot(&loader); 1430 | 1431 | gf_LoaderNode *childNode = gf_FindFirstChild(&loader, root, "ChildName"); 1432 | 1433 | gf_s64 value = 0; 1434 | gf_LoadVariableS64(&loader, childNode, &value); 1435 | 1436 | gf_Unload(&loader); 1437 | } 1438 | */ 1439 | int gf_LoadVariableS64(gf_Loader *loader, gf_LoaderNode *node, gf_s64 *value); 1440 | 1441 | /* 1442 | Name: int gf_LoadVariableF64(gf_Loader *loader, gf_LoaderNode *node, gf_f64 *value); 1443 | Description: A helper function that takes a set of nodes in a certain form and converts them to a value. 1444 | If the node has the form => Var { 1.0 }, where Var corresponds to the passed in node, the value inside 1445 | the braces is converted and copied into value. 1446 | Assumptions: - gf_LoadFromBuffer or gf_LoadFromFile has been called and was successful. 1447 | - *loader is not NULL. 1448 | - node can be NULL 1449 | - value is not NULL 1450 | Returns: Returns 1 if successful. Returns 0 if, either, the node is not in the form as described 1451 | in the description, the conversion to the value failed, the value node in is the wrong type or 1452 | node is NULL. 1453 | Examples: 1454 | { 1455 | gf_Loader loader; 1456 | const char *filename = "myfile.gf"; 1457 | gf_LoadFromFile(&loader, filename, NULL); 1458 | 1459 | gf_LoaderNode *root = gf_GetRoot(&loader); 1460 | 1461 | gf_LoaderNode *childNode = gf_FindFirstChild(&loader, root, "ChildName"); 1462 | 1463 | gf_f64 value = 0; 1464 | gf_LoadVariableF64(&loader, childNode, &value); 1465 | 1466 | gf_Unload(&loader); 1467 | } 1468 | */ 1469 | int gf_LoadVariableF64(gf_Loader *loader, gf_LoaderNode *node, gf_f64 *value); 1470 | 1471 | /* 1472 | Name: int gf_LoadVariableF32(gf_Loader *loader, gf_LoaderNode *node, gf_f32 *value); 1473 | Description: A helper function that takes a set of nodes in a certain form and converts them to a value. 1474 | If the node has the form => Var { 1.0 }, where Var corresponds to the passed in node, the value inside 1475 | the braces is converted and copied into value. 1476 | Assumptions: - gf_LoadFromBuffer or gf_LoadFromFile has been called and was successful. 1477 | - *loader is not NULL. 1478 | - node can be NULL 1479 | - value is not NULL 1480 | Returns: Returns 1 if successful. Returns 0 if, either, the node is not in the form as described 1481 | in the description, the conversion to the value failed, the value node in is the wrong type or 1482 | node is NULL. 1483 | Examples: 1484 | { 1485 | gf_Loader loader; 1486 | const char *filename = "myfile.gf"; 1487 | gf_LoadFromFile(&loader, filename, NULL); 1488 | 1489 | gf_LoaderNode *root = gf_GetRoot(&loader); 1490 | 1491 | gf_LoaderNode *childNode = gf_FindFirstChild(&loader, root, "ChildName"); 1492 | 1493 | gf_f32 value = 0; 1494 | gf_LoadVariableF32(&loader, childNode, &value); 1495 | 1496 | gf_Unload(&loader); 1497 | } 1498 | */ 1499 | int gf_LoadVariableF32(gf_Loader *loader, gf_LoaderNode *node, gf_f32 *value); 1500 | 1501 | /* 1502 | Name: int gf_LoadVariableVec3(gf_Loader *loader, gf_LoaderNode *node, gf_f32 *x, gf_f32 *y, gf_f32 *z); 1503 | Description: A helper function that takes a set of nodes in a certain form and converts them to a value. 1504 | If the node has the form => Var { 1.0, 2.0, 3.0 }, where Var corresponds to the passed in node, the value inside 1505 | the braces is converted and copied into value. 1506 | Assumptions: - gf_LoadFromBuffer or gf_LoadFromFile has been called and was successful. 1507 | - *loader is not NULL. 1508 | - node can be NULL 1509 | - x, y, z is not NULL 1510 | Returns: Returns 1 if successful. Returns 0 if, either, the node is not in the form as described 1511 | in the description, the conversion to the value failed, the value nodes are in the wrong type or 1512 | node is NULL. 1513 | Examples: 1514 | { 1515 | gf_Loader loader; 1516 | const char *filename = "myfile.gf"; 1517 | gf_LoadFromFile(&loader, filename, NULL); 1518 | 1519 | gf_LoaderNode *root = gf_GetRoot(&loader); 1520 | 1521 | gf_LoaderNode *childNode = gf_FindFirstChild(&loader, root, "ChildName"); 1522 | 1523 | gf_f32 x = 0.0f; 1524 | gf_f32 y = 0.0f; 1525 | gf_f32 z = 0.0f; 1526 | gf_LoadVariableVec3(&loader, childNode, &x, &y, &z); 1527 | 1528 | gf_Unload(&loader); 1529 | } 1530 | */ 1531 | int gf_LoadVariableVec3(gf_Loader *loader, gf_LoaderNode *node, gf_f32 *x, gf_f32 *y, gf_f32 *z); 1532 | 1533 | /* 1534 | Name: int gf_LoadVariableString(gf_Loader *loader, gf_LoaderNode *node, char *str, gf_u64 lenWithNullTerminator); 1535 | Description: A helper function that takes a set of nodes in a certain form and converts them to a value. 1536 | If the node has the form => Var { "Hello, world" }, where Var corresponds to the passed in node, the value inside 1537 | the braces is converted and copied into value. 1538 | Assumptions: - gf_LoadFromBuffer or gf_LoadFromFile has been called and was successful. 1539 | - *loader is not NULL. 1540 | - node can be NULL 1541 | - str is not NULL 1542 | - str is NULL terminated. 1543 | - lenWithNullTerminator corresponds to the length of the str + 1 for the null terminator. 1544 | Returns: Returns 1 if successful. Returns 0 if, either, the node is not in the form as described 1545 | in the description, the lenWithNullTerminator is not big enough to fit the value of the node, the value nodes are in the wrong type or the 1546 | node is NULL. 1547 | Examples: 1548 | { 1549 | gf_Loader loader; 1550 | const char *filename = "myfile.gf"; 1551 | gf_LoadFromFile(&loader, filename, NULL); 1552 | 1553 | gf_LoaderNode *root = gf_GetRoot(&loader); 1554 | 1555 | gf_LoaderNode *childNode = gf_FindFirstChild(&loader, root, "ChildName"); 1556 | 1557 | char strbuffer[5]; 1558 | gf_LoadVariableString(&loader, childNode, strbuffer, 5); 1559 | 1560 | gf_Unload(&loader); 1561 | } 1562 | */ 1563 | int gf_LoadVariableString(gf_Loader *loader, gf_LoaderNode *node, char *str, gf_u64 lenWithNullTerminator); 1564 | 1565 | /* 1566 | Name: int gf_LoadArrayS32(gf_Loader *loader, gf_LoaderNode *node, gf_s32 *value, gf_u64 count); 1567 | Description: A helper function that takes a set of nodes in a certain form and converts them to a value. 1568 | If the node has the form => Var { 1, 2, 3, ... }, where Var corresponds to the passed in node, the value inside 1569 | the braces is converted and copied into value. 1570 | Assumptions: - gf_LoadFromBuffer or gf_LoadFromFile has been called and was successful. 1571 | - *loader is not NULL. 1572 | - node can be NULL 1573 | - value is not NULL. 1574 | - count corresponds to a valid range of the value array. 1575 | Returns: Returns 1 if successful. Returns 0 if, either, the node is not in the form as described 1576 | in the description, the conversion to the value failed, the value nodes are in the wrong type, the length of the 1577 | list is bigger than the count and array provided or node is NULL. 1578 | Examples: 1579 | { 1580 | gf_Loader loader; 1581 | const char *filename = "myfile.gf"; 1582 | gf_LoadFromFile(&loader, filename, NULL); 1583 | 1584 | gf_LoaderNode *root = gf_GetRoot(&loader); 1585 | 1586 | gf_LoaderNode *childNode = gf_FindFirstChild(&loader, root, "ChildName"); 1587 | 1588 | gf_s32 arr[3] = {1, 2, 3}; 1589 | gf_LoadArrayS32(&loader, childNode, arr, 3); 1590 | 1591 | gf_Unload(&loader); 1592 | } 1593 | */ 1594 | int gf_LoadArrayS32(gf_Loader *loader, gf_LoaderNode *node, gf_s32 *value, gf_u64 count); 1595 | 1596 | /*-----------------------------------------------------------------------------------*/ 1597 | 1598 | /*-------------------------TESTING---------------------------------------------------*/ 1599 | 1600 | #ifdef GF_IMPLEMENTATION_WITH_TESTS 1601 | 1602 | #define GF_TEST_ASSERT(arg, str) if (!(arg)) { printf("[%s]: test failed! Check logs for more.", str); return 0; } 1603 | 1604 | int gf_Test(void); 1605 | 1606 | #endif 1607 | 1608 | /*-----------------------------------------------------------------------------------*/ 1609 | 1610 | // IMPLEMENTATION 1611 | #if defined(GF_IMPLEMENTATION) || defined(GF_IMPLEMENTATION_WITH_TESTS) 1612 | 1613 | /*-------------------------STRING CONVERSIONS---------------------------------------*/ 1614 | 1615 | int gf_AreStringSpansEqual(const char *a, gf_u64 alength, const char *b, gf_u64 blength) { 1616 | assert(a); 1617 | assert(b); 1618 | 1619 | if (alength != blength) return 0; 1620 | gf_u64 i = 0; 1621 | while (i < alength) { 1622 | if (a[i] == '\0' && b[i] != '\0') return 0; 1623 | if (a[i] != '\0' && b[i] == '\0') return 0; 1624 | if (a[i] != b[i]) return 0; 1625 | i++; 1626 | } 1627 | return 1; 1628 | } 1629 | 1630 | gf_u64 gf_StringLength(const char *str) { 1631 | assert(str); 1632 | 1633 | gf_u64 len = 0; 1634 | while (str[len] != '\0') len++; 1635 | return len; 1636 | } 1637 | 1638 | int gf_StringSpanToU32(const char *start, uint64_t length, gf_u32 *value) { 1639 | assert(start); 1640 | 1641 | if (*start == '-') { 1642 | return 0; 1643 | } 1644 | 1645 | gf_u32 i = strtoul(start, NULL, 10); 1646 | // 0 could mean a failure, or it could just mean the string is actually 0. 1647 | if (i == 0) { 1648 | uint64_t index = 0; 1649 | while (!isdigit(start[index]) && index < length) { 1650 | index++; 1651 | } 1652 | if (start[index] != '0') { 1653 | return 0; 1654 | } 1655 | } 1656 | if (i == ULONG_MAX) { 1657 | return 0; 1658 | } 1659 | *value = i; 1660 | return 1; 1661 | } 1662 | 1663 | int gf_StringSpanToU64(const char *start, uint64_t length, gf_u64 *value) { 1664 | assert(start); 1665 | 1666 | if (*start == '-') { 1667 | return 0; 1668 | } 1669 | 1670 | gf_u64 i = strtoull(start, NULL, 10); 1671 | // 0 could mean a failure, or it could just mean the string is actually 0. 1672 | if (i == 0) { 1673 | uint64_t index = 0; 1674 | while (!isdigit(start[index]) && index < length) { 1675 | index++; 1676 | } 1677 | if (start[index] != '0') { 1678 | return 0; 1679 | } 1680 | } 1681 | if (i == ULLONG_MAX) { 1682 | return 0; 1683 | } 1684 | *value = i; 1685 | return 1; 1686 | } 1687 | 1688 | int gf_StringSpanToS32(const char *start, uint64_t length, gf_s32 *value) { 1689 | assert(start); 1690 | 1691 | gf_s32 i = strtol(start, NULL, 10); 1692 | // 0 could mean a failure 1693 | if (i == 0) { 1694 | uint64_t index = 0; 1695 | while (!isdigit(start[index]) && index < length) { 1696 | index++; 1697 | } 1698 | if (start[index] != '0') { 1699 | return 0; 1700 | } 1701 | } 1702 | if (i == LONG_MIN || i == LONG_MAX) { 1703 | return 0; 1704 | } 1705 | *value = i; 1706 | return 1; 1707 | } 1708 | 1709 | int gf_StringSpanToS64(const char *start, uint64_t length, gf_s64 *value) { 1710 | assert(start); 1711 | 1712 | gf_s64 i = strtoll(start, NULL, 10); 1713 | // 0 could mean a failure, or it could just mean the string is actually 0. 1714 | if (i == 0) { 1715 | uint64_t index = 0; 1716 | while (!isdigit(start[index]) && index < length) { 1717 | index++; 1718 | } 1719 | if (start[index] != '0') { 1720 | return 0; 1721 | } 1722 | } 1723 | if (i == LLONG_MIN || i == LLONG_MAX) { 1724 | return 0; 1725 | } 1726 | *value = i; 1727 | return 1; 1728 | } 1729 | 1730 | int gf_StringSpanToF64(const char *start, uint64_t length, gf_f64 *value) { 1731 | assert(start); 1732 | 1733 | double d = strtod(start, NULL); 1734 | // 0.0 could mean a failure, or it could just mean the string is actually 0. 1735 | if (d == 0.0) { 1736 | uint64_t index = 0; 1737 | while (!isdigit(start[index]) && index < length) { 1738 | index++; 1739 | } 1740 | if (start[index] != '0') { 1741 | return 0; 1742 | } 1743 | } 1744 | else if (d == HUGE_VAL || d == -HUGE_VAL) { 1745 | return 0; 1746 | } 1747 | *value = d; 1748 | return 1; 1749 | } 1750 | 1751 | int gf_StringSpanToF32(const char *start, uint64_t length, gf_f32 *value) { 1752 | assert(start); 1753 | 1754 | float f = strtof(start, NULL); 1755 | // 0.0 could mean a failure, or it could just mean the string is actually 0 1756 | if (f == 0.0) { 1757 | uint64_t index = 0; 1758 | while (!isdigit(start[index]) && index < length) { 1759 | index++; 1760 | } 1761 | if (start[index] != '0') { 1762 | return 0; 1763 | } 1764 | } 1765 | else if (f == HUGE_VALF || f == -HUGE_VALF) { 1766 | return 0; 1767 | } 1768 | *value = f; 1769 | return 1; 1770 | } 1771 | 1772 | /*-----------------------------------------------------------------------------------*/ 1773 | 1774 | /*-------------------------------------TOKENS----------------------------------------*/ 1775 | 1776 | void gf_PrintToken(gf_Token *token) { 1777 | assert(token); 1778 | 1779 | for (gf_u64 i = 0; i < token->length; i++) { 1780 | printf("%c", token->start[i]); 1781 | } 1782 | } 1783 | 1784 | void gf_PrintLineToken(gf_Token *token) { 1785 | assert(token); 1786 | 1787 | gf_PrintToken(token); 1788 | printf("\n"); 1789 | } 1790 | 1791 | const char *gf_TokenTypeToString(gf_TokenType type) { 1792 | switch (type) { 1793 | case GF_TOKEN_TYPE_ROOT: return "GF_TOKEN_TYPE_ROOT"; 1794 | case GF_TOKEN_TYPE_NAME: return "GF_TOKEN_TYPE_NAME"; 1795 | case GF_TOKEN_TYPE_CURLY_CLOSE: return "GF_TOKEN_TYPE_CURLY_CLOSE"; 1796 | case GF_TOKEN_TYPE_COMMENT: return "GF_TOKEN_TYPE_COMMENT"; 1797 | case GF_TOKEN_TYPE_COMPOSITE_TYPE: return "GF_TOKEN_TYPE_COMPOSITE_TYPE"; 1798 | case GF_TOKEN_TYPE_STRING: return "GF_TOKEN_TYPE_STRING"; 1799 | case GF_TOKEN_TYPE_FLOAT: return "GF_TOKEN_TYPE_FLOAT"; 1800 | case GF_TOKEN_TYPE_INTEGER: return "GF_TOKEN_TYPE_INTEGER"; 1801 | case GF_TOKEN_TYPE_END_FILE: return "GF_TOKEN_TYPE_END_FILE"; 1802 | case GF_TOKEN_TYPE_VALUE_ASSIGN: return "GF_TOKEN_TYPE_VALUE_ASSIGN"; 1803 | default: return ""; 1804 | } 1805 | } 1806 | 1807 | /*-----------------------------------------------------------------------------------*/ 1808 | 1809 | /*-------------------------------------LOGGING----------------------------------------*/ 1810 | 1811 | void gf_DefaultLog(gf_LogLevel level, int lineno, gf_Token *token, const char *format, va_list vlist) { 1812 | switch (level) { 1813 | case GF_LOG_ERROR: printf("[ERROR][%d]", lineno); break; 1814 | case GF_LOG_WARNING: printf("[WARNING][%d]", lineno); break; 1815 | case GF_LOG_INFO: printf("[INFO][%d]", lineno); break; 1816 | } 1817 | if (token) { 1818 | printf("[\""); 1819 | gf_PrintToken(token); 1820 | printf("\"]"); 1821 | printf("[lineno: %" PRIu64 "]", token->lineno); 1822 | printf("[colno: %" PRIu64 "]", token->colno); 1823 | } 1824 | printf(": "); 1825 | vprintf(format, vlist); 1826 | printf("\n"); 1827 | } 1828 | 1829 | void gf_Log(gf_LogFunctionPtr Log, gf_LogLevel level, int lineno, gf_Token *token, const char *format, ...) { 1830 | va_list args; 1831 | va_start(args, format); 1832 | Log(level, lineno, token, format, args); 1833 | va_end(args); 1834 | }; 1835 | 1836 | /*-----------------------------------------------------------------------------------*/ 1837 | 1838 | /*-------------------------------------SAVER----------------------------------------*/ 1839 | 1840 | void gf_InitSaver(gf_Saver *saver, gf_LogFunctionPtr logfunction) { 1841 | assert(saver); 1842 | 1843 | saver->indent = 0; 1844 | if (logfunction == NULL) { 1845 | saver->Log = gf_DefaultLog; 1846 | } 1847 | } 1848 | 1849 | int gf_PrintIndent(gf_Saver *saver, FILE *file) { 1850 | assert(saver); 1851 | assert(file); 1852 | 1853 | int result = 0; 1854 | for (unsigned int i = 0; i < saver->indent; i++) { 1855 | result = fprintf(file, " "); 1856 | if (result < 0) { 1857 | GF_LOG(saver, GF_LOG_ERROR, "fprintf failed with value [%d] in gf_PrintIndent", result); 1858 | return 0; 1859 | } 1860 | } 1861 | return 1; 1862 | } 1863 | 1864 | int gf_SaveVariableS64(gf_Saver *saver, FILE *file, const char *identifier, gf_s64 *value) { 1865 | assert(saver); 1866 | assert(file); 1867 | assert(identifier); 1868 | assert(value); 1869 | 1870 | int result = 0; 1871 | 1872 | result = gf_PrintIndent(saver, file); 1873 | if (!result) { return 0; } 1874 | 1875 | result = fprintf(file, "%s { %" PRIi64 " }\n", identifier, *value); 1876 | if (result < 0) { 1877 | GF_LOG(saver, GF_LOG_ERROR, "fprintf failed with value [%d] in gf_SaveVariableS64", result); 1878 | return 0; 1879 | } 1880 | return 1; 1881 | } 1882 | 1883 | int gf_SaveVariableS32(gf_Saver *saver, FILE *file, const char *identifier, gf_s32 *value) { 1884 | assert(saver); 1885 | assert(file); 1886 | assert(identifier); 1887 | assert(value); 1888 | 1889 | int result = 0; 1890 | 1891 | result = gf_PrintIndent(saver, file); 1892 | if (!result) { return 0; } 1893 | 1894 | result = fprintf(file, "%s { %" PRIi32 " }\n", identifier, *value); 1895 | if (result < 0) { 1896 | GF_LOG(saver, GF_LOG_ERROR, "fprintf failed with value [%d] in gf_SaveVariableS32", result); 1897 | return 0; 1898 | } 1899 | return 1; 1900 | } 1901 | 1902 | int gf_SaveVariableU32(gf_Saver *saver, FILE *file, const char *identifier, gf_u32 *value) { 1903 | assert(saver); 1904 | assert(file); 1905 | assert(identifier); 1906 | assert(value); 1907 | 1908 | int result = 0; 1909 | 1910 | result = gf_PrintIndent(saver, file); 1911 | if (!result) { return 0; } 1912 | 1913 | result = fprintf(file, "%s { %" PRIu32 " }\n", identifier, *value); 1914 | if (result < 0) { 1915 | GF_LOG(saver, GF_LOG_ERROR, "fprintf failed with value [%d] in gf_SaveVariableU32", result); 1916 | return 0; 1917 | } 1918 | return 1; 1919 | } 1920 | 1921 | 1922 | int gf_SaveVariableU64(gf_Saver *saver, FILE *file, const char *identifier, gf_u64 *value) { 1923 | assert(saver); 1924 | assert(file); 1925 | assert(identifier); 1926 | assert(value); 1927 | 1928 | int result = 0; 1929 | 1930 | result = gf_PrintIndent(saver, file); 1931 | if (!result) { return 0; } 1932 | 1933 | result = fprintf(file, "%s { %" PRIu64 " }\n", identifier, *value); 1934 | if (result < 0) { 1935 | GF_LOG(saver, GF_LOG_ERROR, "fprintf failed with value [%d] in gf_SaveVariableU64", result); 1936 | return 0; 1937 | } 1938 | return 1; 1939 | } 1940 | 1941 | int gf_SaveVariableString(gf_Saver *saver, FILE *file, const char *identifier, const char *str) { 1942 | assert(saver); 1943 | assert(file); 1944 | assert(identifier); 1945 | assert(str); 1946 | 1947 | int result = 0; 1948 | 1949 | result = gf_PrintIndent(saver, file); 1950 | if (!result) { return 0; } 1951 | 1952 | result = fprintf(file, "%s { \"%s\" }\n", identifier, str); 1953 | if (result < 0) { 1954 | GF_LOG(saver, GF_LOG_ERROR, "fprintf failed with value [%d] in gf_SaveVariableString", result); 1955 | return 0; 1956 | } 1957 | return 1; 1958 | } 1959 | 1960 | int gf_SaveVariableF32(gf_Saver *saver, FILE *file, const char *identifier, gf_f32 *value) { 1961 | assert(saver); 1962 | assert(file); 1963 | assert(identifier); 1964 | assert(value); 1965 | 1966 | int result = 0; 1967 | 1968 | result = gf_PrintIndent(saver, file); 1969 | if (!result) { return 0; } 1970 | 1971 | result = fprintf(file, "%s { %f }\n", identifier, *value); 1972 | if (result < 0) { 1973 | GF_LOG(saver, GF_LOG_ERROR, "fprintf failed with value [%d] in gf_SaveVariableF32", result); 1974 | return 0; 1975 | } 1976 | 1977 | return 1; 1978 | } 1979 | 1980 | int gf_SaveVariableF64(gf_Saver *saver, FILE *file, const char *identifier, gf_f64 *value) { 1981 | assert(saver); 1982 | assert(file); 1983 | assert(identifier); 1984 | assert(value); 1985 | 1986 | int result = 0; 1987 | 1988 | result = gf_PrintIndent(saver, file); 1989 | if (!result) { return 0; } 1990 | 1991 | result = fprintf(file, "%s { %lf }\n", identifier, *value); 1992 | if (result < 0) { 1993 | GF_LOG(saver, GF_LOG_ERROR, "fprintf failed with value [%d] in gf_SaveVariableF64", result); 1994 | return 0; 1995 | } 1996 | 1997 | return 1; 1998 | } 1999 | 2000 | int gf_SaveVariableStringSpan(gf_Saver *saver, FILE *file, const char *identifier, const char *str, int strLen) { 2001 | assert(saver); 2002 | assert(file); 2003 | assert(identifier); 2004 | assert(str); 2005 | 2006 | int result = 0; 2007 | 2008 | result = gf_PrintIndent(saver, file); 2009 | if (!result) { return 0; } 2010 | 2011 | result = fprintf(file, "%s { \"", identifier); 2012 | if (result < 0) { 2013 | GF_LOG(saver, GF_LOG_ERROR, "fprintf failed with value [%d] in gf_SaveVariableStringSpan", result); 2014 | return 0; 2015 | } 2016 | 2017 | for (int i = 0; i < strLen; i++) { 2018 | result = fprintf(file, "%c", str[i]); 2019 | if (result < 0) { 2020 | GF_LOG(saver, GF_LOG_ERROR, "fputc failed with value [%d] in gf_SaveVariableStringSpan", result); 2021 | return 0; 2022 | } 2023 | } 2024 | result = fprintf(file, "\" }\n"); 2025 | if (result < 0) { 2026 | GF_LOG(saver, GF_LOG_ERROR, "fprintf failed with value [%d] in gf_SaveVariableStringSpan", result); 2027 | return 0; 2028 | } 2029 | 2030 | return 1; 2031 | } 2032 | 2033 | int gf_SaveVariableVec3(gf_Saver *saver, FILE *file, const char *identifier, gf_f32 *x, gf_f32 *y, gf_f32 *z) { 2034 | assert(saver); 2035 | assert(file); 2036 | assert(identifier); 2037 | assert(x && y && z); 2038 | 2039 | int result = 0; 2040 | 2041 | result = gf_PrintIndent(saver, file); 2042 | if (!result) { return 0; } 2043 | 2044 | result = fprintf(file, "%s { %f, %f, %f }\n", identifier, *x, *y, *z); 2045 | if (result < 0) { 2046 | GF_LOG(saver, GF_LOG_ERROR, "fprintf failed with value [%d] in gf_SaveVariableVec3", result); 2047 | return 0; 2048 | } 2049 | 2050 | return 1; 2051 | } 2052 | 2053 | int gf_SaveArrayU64(gf_Saver *saver, FILE *file, const char *identifier, gf_u64 *value, int count) { 2054 | int result = 0; 2055 | 2056 | result = gf_PrintIndent(saver, file); 2057 | if (!result) { return 0; } 2058 | 2059 | if (count) { 2060 | result = fprintf(file, "%s { ", identifier); 2061 | if (result < 0) { 2062 | GF_LOG(saver, GF_LOG_ERROR, "fprintf failed with value [%d] in gf_SaveArrayU64", result); 2063 | return 0; 2064 | } 2065 | result = fprintf(file, "%" PRIu64, value[0]); 2066 | if (result < 0) { 2067 | GF_LOG(saver, GF_LOG_ERROR, "fprintf failed with value [%d] in gf_SaveArrayU64", result); 2068 | return 0; 2069 | } 2070 | for (int i = 1; i < count; i++) { 2071 | result = fprintf(file, ", %" PRIi64, value[i]); 2072 | if (result < 0) { 2073 | GF_LOG(saver, GF_LOG_ERROR, "fprintf failed with value [%d] in gf_SaveArrayU64", result); 2074 | return 0; 2075 | } 2076 | } 2077 | result = fprintf(file, " }\n"); 2078 | if (result < 0) { 2079 | GF_LOG(saver, GF_LOG_ERROR, "fprintf failed with value [%d] in gf_SaveArrayU64", result); 2080 | return 0; 2081 | } 2082 | } 2083 | 2084 | return 1; 2085 | } 2086 | 2087 | int gf_SaveArrayS64(gf_Saver *saver, FILE *file, const char *identifier, gf_s64 *value, int count) { 2088 | int result = 0; 2089 | 2090 | result = gf_PrintIndent(saver, file); 2091 | if (!result) { return 0; } 2092 | 2093 | if (count) { 2094 | result = fprintf(file, "%s { ", identifier); 2095 | if (result < 0) { 2096 | GF_LOG(saver, GF_LOG_ERROR, "fprintf failed with value [%d] in gf_SaveArrayI64", result); 2097 | return 0; 2098 | } 2099 | result = fprintf(file, "%" PRIi64, value[0]); 2100 | if (result < 0) { 2101 | GF_LOG(saver, GF_LOG_ERROR, "fprintf failed with value [%d] in gf_SaveArrayI64", result); 2102 | return 0; 2103 | } 2104 | for (int i = 1; i < count; i++) { 2105 | result = fprintf(file, ", %" PRIi64, value[i]); 2106 | if (result < 0) { 2107 | GF_LOG(saver, GF_LOG_ERROR, "fprintf failed with value [%d] in gf_SaveArrayI64", result); 2108 | return 0; 2109 | } 2110 | } 2111 | result = fprintf(file, " }\n"); 2112 | if (result < 0) { 2113 | GF_LOG(saver, GF_LOG_ERROR, "fprintf failed with value [%d] in gf_SaveArrayI64", result); 2114 | return 0; 2115 | } 2116 | } 2117 | 2118 | return 1; 2119 | } 2120 | 2121 | 2122 | int gf_SaveArrayS32(gf_Saver *saver, FILE *file, const char *identifier, gf_s32 *value, int count) { 2123 | int result = 0; 2124 | 2125 | result = gf_PrintIndent(saver, file); 2126 | if (!result) { return 0; } 2127 | 2128 | if (count) { 2129 | result = fprintf(file, "%s { ", identifier); 2130 | if (result < 0) { 2131 | GF_LOG(saver, GF_LOG_ERROR, "fprintf failed with value [%d] in gf_SaveArrayS32", result); 2132 | return 0; 2133 | } 2134 | result = fprintf(file, "%" PRIi32, value[0]); 2135 | if (result < 0) { 2136 | GF_LOG(saver, GF_LOG_ERROR, "fprintf failed with value [%d] in gf_SaveArrayS32", result); 2137 | return 0; 2138 | } 2139 | for (int i = 1; i < count; i++) { 2140 | result = fprintf(file, ", %" PRIi32, value[i]); 2141 | if (result < 0) { 2142 | GF_LOG(saver, GF_LOG_ERROR, "fprintf failed with value [%d] in gf_SaveArrayS32", result); 2143 | return 0; 2144 | } 2145 | } 2146 | result = fprintf(file, " }\n"); 2147 | if (result < 0) { 2148 | GF_LOG(saver, GF_LOG_ERROR, "fprintf failed with value [%d] in gf_SaveArrayS32", result); 2149 | return 0; 2150 | } 2151 | } 2152 | 2153 | return 1; 2154 | } 2155 | 2156 | int gf_SaveStartList(gf_Saver *saver, FILE *file, const char *identifier) { 2157 | int result = 0; 2158 | 2159 | result = gf_PrintIndent(saver, file); 2160 | if (!result) { return 0; } 2161 | 2162 | result = fprintf(file, "%s {\n", identifier); 2163 | if (result < 0) { 2164 | GF_LOG(saver, GF_LOG_ERROR, "fprintf failed with value [%d] in gf_SaveStartList", result); 2165 | return 0; 2166 | } 2167 | saver->indent++; 2168 | 2169 | return 1; 2170 | } 2171 | 2172 | int gf_SaveEndList(gf_Saver *saver, FILE *file) { 2173 | int result = 0; 2174 | 2175 | if (saver->indent) { 2176 | for (unsigned int i = 0; i < saver->indent - 1; i++) { 2177 | result = fprintf(file, " "); 2178 | if (result < 0) { 2179 | GF_LOG(saver, GF_LOG_ERROR, "fprintf failed with value [%d] in gf_SaveEndList", result); 2180 | return 0; 2181 | } 2182 | } 2183 | } 2184 | result = fprintf(file, "}\n"); 2185 | if (result < 0) { 2186 | GF_LOG(saver, GF_LOG_ERROR, "fprintf failed with value [%d] in gf_SaveEndList", result); 2187 | return 0; 2188 | } 2189 | if (saver->indent) saver->indent--; 2190 | 2191 | return 1; 2192 | } 2193 | 2194 | /*-----------------------------------------------------------------------------------*/ 2195 | 2196 | /*-------------------------------------LOADER----------------------------------------*/ 2197 | 2198 | void gf_InitLoader(gf_Loader *loader, gf_LogAllocateFreeFunctions *helperfunctions) { 2199 | assert(loader); 2200 | 2201 | if (!helperfunctions || helperfunctions->Log == NULL) { 2202 | loader->Log = gf_DefaultLog; 2203 | } 2204 | else { 2205 | loader->Log = helperfunctions->Log; 2206 | } 2207 | if (!helperfunctions || helperfunctions->Allocate == NULL) { 2208 | loader->Allocate = malloc; 2209 | } 2210 | else { 2211 | loader->Allocate = helperfunctions->Allocate; 2212 | } 2213 | if (!helperfunctions || helperfunctions->Free == NULL) { 2214 | loader->Free = free; 2215 | } 2216 | else { 2217 | loader->Free = helperfunctions->Free; 2218 | } 2219 | 2220 | loader->curToken = NULL; 2221 | loader->rootNode = NULL; 2222 | loader->lastNode = NULL; 2223 | loader->firstToken = NULL; 2224 | 2225 | // Set up the root token 2226 | loader->rootToken.start = "root"; 2227 | loader->rootToken.length = 4; 2228 | loader->rootToken.lineno = 0; 2229 | loader->rootToken.next = NULL; 2230 | loader->rootToken.type = GF_TOKEN_TYPE_ROOT; 2231 | 2232 | loader->lastToken = &loader->rootToken; 2233 | 2234 | loader->fileContentsBuffer = NULL; 2235 | loader->nestLevel = 0; 2236 | } 2237 | 2238 | void gf_IncrementLastTokenLength(gf_Loader *loader) { 2239 | assert(loader); 2240 | assert(loader->lastToken); 2241 | loader->lastToken->length++; 2242 | } 2243 | 2244 | int gf_AddToken(gf_Loader *loader, const char *start, gf_TokenType type, gf_u64 lineno, gf_u64 colno) { 2245 | assert(loader->lastToken); 2246 | 2247 | if (!start) { 2248 | GF_LOG_WITH_TOKEN(loader, GF_LOG_ERROR, loader->lastToken, "Expecting a token but at the end of the file/buffer"); 2249 | return 0; 2250 | } 2251 | 2252 | gf_Token *token = (gf_Token *)loader->Allocate(sizeof(gf_Token)); 2253 | if (!token) { 2254 | GF_LOG_WITH_TOKEN(loader, GF_LOG_ERROR, loader->lastToken, "Out of memory in gf_AddToken"); 2255 | return 0; 2256 | } 2257 | 2258 | if (!loader->firstToken) { 2259 | loader->firstToken = token; 2260 | } 2261 | 2262 | loader->lastToken->next = token; 2263 | loader->lastToken = token; 2264 | 2265 | token->start = start; 2266 | token->type = type; 2267 | token->length = 1; 2268 | token->lineno = lineno; 2269 | token->colno = colno; 2270 | token->next = NULL; 2271 | 2272 | return 1; 2273 | } 2274 | 2275 | char *gf_AllocateNullTerminatedBufferFromFile(gf_Loader *loader, const char *filename, gf_u64 *bufferCountWithNullTerminator) { 2276 | assert(loader); 2277 | assert(filename); 2278 | assert(bufferCountWithNullTerminator); 2279 | 2280 | uint64_t fileSize = 0; 2281 | uint64_t readBytes = 0; 2282 | FILE *file = NULL; 2283 | 2284 | file = fopen(filename, "rb"); 2285 | if (file == NULL) { 2286 | GF_LOG(loader, GF_LOG_ERROR, "Failed to load file [%s]. It could not be opened", filename); 2287 | return NULL; 2288 | } 2289 | fseek(file, 0, SEEK_END); 2290 | fileSize = (uint64_t)ftell(file); 2291 | fseek(file, 0, SEEK_SET); 2292 | 2293 | char *buffer = (char *)loader->Allocate(fileSize + 1); 2294 | if (buffer == NULL) { 2295 | GF_LOG(loader, GF_LOG_ERROR, "Out of memory. Failed to allocate buffer for file [%s]", filename); 2296 | fclose(file); 2297 | return NULL; 2298 | } 2299 | 2300 | readBytes = fread(buffer, fileSize, sizeof(char), file); 2301 | if (readBytes != 1) { 2302 | GF_LOG(loader, GF_LOG_ERROR, "Failed to read file [%s]", filename); 2303 | fclose(file); 2304 | return NULL; 2305 | } 2306 | 2307 | fclose(file); 2308 | 2309 | buffer[fileSize] = '\0'; 2310 | 2311 | *bufferCountWithNullTerminator = fileSize + 1; 2312 | 2313 | return buffer; 2314 | } 2315 | 2316 | void gf_InitTokeniser(gf_Tokeniser *tokeniser, const char *buffer, gf_u64 count) { 2317 | assert(tokeniser); 2318 | assert(buffer); 2319 | 2320 | tokeniser->buffer = buffer; 2321 | tokeniser->count = count; 2322 | tokeniser->index = 0; 2323 | tokeniser->lineno = 1; 2324 | tokeniser->colno = 1; 2325 | } 2326 | 2327 | char gf_GetChar(gf_Tokeniser *tokeniser) { 2328 | if (tokeniser->index >= tokeniser->count) { 2329 | return '\0'; 2330 | } 2331 | return tokeniser->buffer[tokeniser->index]; 2332 | } 2333 | 2334 | void gf_IncrementIndex(gf_Tokeniser *tokeniser) { 2335 | if (tokeniser->index < tokeniser->count) { 2336 | tokeniser->index++; 2337 | tokeniser->colno++; 2338 | } 2339 | } 2340 | 2341 | void gf_IncrementLineNo(gf_Tokeniser *tokeniser) { 2342 | tokeniser->lineno++; 2343 | tokeniser->colno = 1; 2344 | } 2345 | 2346 | const char *gf_Ptr(gf_Tokeniser *tokeniser) { 2347 | if (tokeniser->index < tokeniser->count) { 2348 | return &tokeniser->buffer[tokeniser->index]; 2349 | } 2350 | return NULL; 2351 | } 2352 | 2353 | int gf_TokeniseInternal(gf_Loader *loader, gf_Tokeniser *tokeniser) { 2354 | assert(loader); 2355 | assert(tokeniser); 2356 | 2357 | int result = 0; 2358 | 2359 | while (1) { 2360 | 2361 | if (gf_GetChar(tokeniser) == '\0') { 2362 | 2363 | result = gf_AddToken(loader, "", GF_TOKEN_TYPE_END_FILE, tokeniser->lineno, tokeniser->colno); 2364 | if (!result) { 2365 | GF_LOG_WITH_TOKEN(loader, GF_LOG_ERROR, loader->lastToken, "Failed to add end file token"); 2366 | return 0; 2367 | } 2368 | break; 2369 | } 2370 | else if (gf_GetChar(tokeniser) == '{') { 2371 | result = gf_AddToken(loader, gf_Ptr(tokeniser), GF_TOKEN_TYPE_VALUE_ASSIGN, tokeniser->lineno, tokeniser->colno); 2372 | if (!result) { 2373 | GF_LOG_WITH_TOKEN(loader, GF_LOG_ERROR, loader->lastToken, "Failed to add value assign token"); 2374 | return 0; 2375 | } 2376 | gf_IncrementIndex(tokeniser); 2377 | } 2378 | else if (gf_GetChar(tokeniser) == '}') { 2379 | result = gf_AddToken(loader, gf_Ptr(tokeniser), GF_TOKEN_TYPE_CURLY_CLOSE, tokeniser->lineno, tokeniser->colno); 2380 | if (!result) { 2381 | GF_LOG_WITH_TOKEN(loader, GF_LOG_ERROR, loader->lastToken, "Failed to add curly close token"); 2382 | return 0; 2383 | } 2384 | gf_IncrementIndex(tokeniser); 2385 | } 2386 | else if (gf_GetChar(tokeniser) == '/') { 2387 | 2388 | gf_IncrementIndex(tokeniser); 2389 | 2390 | if (gf_GetChar(tokeniser) == '*') { 2391 | 2392 | int nestedCommentDepth = 1; 2393 | gf_IncrementIndex(tokeniser); 2394 | 2395 | while (nestedCommentDepth > 0) { 2396 | 2397 | if (gf_GetChar(tokeniser) == '\0') { 2398 | GF_LOG_WITH_TOKEN(loader, GF_LOG_ERROR, loader->lastToken, "Comment does not end before the file ends"); 2399 | return 0; 2400 | } 2401 | else if (gf_GetChar(tokeniser) == '/') { 2402 | 2403 | gf_IncrementIndex(tokeniser); 2404 | if (gf_GetChar(tokeniser) == '*') { 2405 | nestedCommentDepth++; 2406 | } 2407 | } 2408 | else if (gf_GetChar(tokeniser) == '*') { 2409 | gf_IncrementIndex(tokeniser); 2410 | if (gf_GetChar(tokeniser) == '/') { 2411 | nestedCommentDepth--; 2412 | } 2413 | } 2414 | else if (gf_GetChar(tokeniser) == '\n') { 2415 | gf_IncrementLineNo(tokeniser); 2416 | gf_IncrementIndex(tokeniser); 2417 | } 2418 | else if (gf_GetChar(tokeniser) == '\r') { 2419 | gf_IncrementLineNo(tokeniser); 2420 | gf_IncrementIndex(tokeniser); 2421 | if (gf_GetChar(tokeniser) == '\n') { 2422 | gf_IncrementIndex(tokeniser); 2423 | } 2424 | } 2425 | else { 2426 | gf_IncrementIndex(tokeniser); 2427 | } 2428 | } 2429 | } 2430 | } 2431 | else if (gf_GetChar(tokeniser) == ' ' || gf_GetChar(tokeniser) == ',' || gf_GetChar(tokeniser) == '\t') { 2432 | gf_IncrementIndex(tokeniser); 2433 | } 2434 | else if (gf_GetChar(tokeniser) == '\n') { 2435 | gf_IncrementLineNo(tokeniser); 2436 | gf_IncrementIndex(tokeniser); 2437 | } 2438 | else if (gf_GetChar(tokeniser) == '\r') { 2439 | gf_IncrementLineNo(tokeniser); 2440 | gf_IncrementIndex(tokeniser); 2441 | if (gf_GetChar(tokeniser) == '\n') { 2442 | gf_IncrementIndex(tokeniser); 2443 | } 2444 | } 2445 | else if (gf_GetChar(tokeniser) == '\"') { 2446 | 2447 | gf_IncrementIndex(tokeniser); 2448 | 2449 | result = gf_AddToken(loader, gf_Ptr(tokeniser), GF_TOKEN_TYPE_STRING, tokeniser->lineno, tokeniser->colno); 2450 | if (!result) { 2451 | GF_LOG(loader, GF_LOG_ERROR, "Failed to add string token"); 2452 | return 0; 2453 | } 2454 | 2455 | if (gf_GetChar(tokeniser) != '\"') { 2456 | int lastCharPossibleEscapeChar = 0; 2457 | gf_IncrementIndex(tokeniser); 2458 | while (1) { 2459 | 2460 | if (gf_GetChar(tokeniser) == '\0') { 2461 | GF_LOG(loader, GF_LOG_ERROR, "String does not end before the file ends"); 2462 | return 0; 2463 | } 2464 | else if (gf_GetChar(tokeniser) == '\"' && !lastCharPossibleEscapeChar) { 2465 | break; 2466 | } 2467 | else if (gf_GetChar(tokeniser) == '\\') { 2468 | lastCharPossibleEscapeChar = 1; 2469 | } 2470 | else { 2471 | lastCharPossibleEscapeChar = 0; 2472 | } 2473 | 2474 | gf_IncrementLastTokenLength(loader); 2475 | gf_IncrementIndex(tokeniser); 2476 | } 2477 | } 2478 | 2479 | gf_IncrementIndex(tokeniser); 2480 | } 2481 | else if (isalpha(gf_GetChar(tokeniser))) { 2482 | 2483 | result = gf_AddToken(loader, gf_Ptr(tokeniser), GF_TOKEN_TYPE_NAME, tokeniser->lineno, tokeniser->colno); 2484 | if (!result) { 2485 | GF_LOG_WITH_TOKEN(loader, GF_LOG_ERROR, loader->lastToken, "Failed to add name token"); 2486 | return 0; 2487 | } 2488 | gf_IncrementIndex(tokeniser); 2489 | 2490 | while (isalpha(gf_GetChar(tokeniser)) || isdigit(gf_GetChar(tokeniser)) || gf_GetChar(tokeniser) == '_') { 2491 | gf_IncrementLastTokenLength(loader); 2492 | gf_IncrementIndex(tokeniser); 2493 | } 2494 | } 2495 | else if (isdigit(gf_GetChar(tokeniser)) || gf_GetChar(tokeniser) == '-' || gf_GetChar(tokeniser) == '+') { 2496 | 2497 | int hasFloatingPoint = 0; 2498 | int hasPlusOrMinus = gf_GetChar(tokeniser) == '-' || gf_GetChar(tokeniser) == '+'; 2499 | 2500 | result = gf_AddToken(loader, gf_Ptr(tokeniser), GF_TOKEN_TYPE_FLOAT, tokeniser->lineno, tokeniser->colno); 2501 | if (!result) { 2502 | GF_LOG_WITH_TOKEN(loader, GF_LOG_ERROR, loader->lastToken, "Failed to add float token"); 2503 | return 0; 2504 | } 2505 | gf_IncrementIndex(tokeniser); 2506 | 2507 | while (isdigit(gf_GetChar(tokeniser)) || gf_GetChar(tokeniser) == '.') { 2508 | if (gf_GetChar(tokeniser) == '.') { 2509 | 2510 | if (hasFloatingPoint == 0) { 2511 | hasFloatingPoint = 1; 2512 | } 2513 | 2514 | gf_IncrementLastTokenLength(loader); 2515 | gf_IncrementIndex(tokeniser); 2516 | while (isdigit(gf_GetChar(tokeniser))) { 2517 | gf_IncrementLastTokenLength(loader); 2518 | gf_IncrementIndex(tokeniser); 2519 | } 2520 | break; 2521 | } 2522 | else { 2523 | gf_IncrementLastTokenLength(loader); 2524 | gf_IncrementIndex(tokeniser); 2525 | } 2526 | } 2527 | 2528 | if (!hasFloatingPoint) { 2529 | loader->lastToken->type = GF_TOKEN_TYPE_INTEGER; 2530 | } 2531 | // Prevents just having a + or - as a valid number 2532 | if (hasPlusOrMinus && loader->lastToken->length == 1) { 2533 | GF_LOG_WITH_TOKEN(loader, GF_LOG_ERROR, loader->lastToken, "There is a + or - without a number after it."); 2534 | return 0; 2535 | } 2536 | } 2537 | else { 2538 | GF_LOG_WITH_TOKEN(loader, GF_LOG_ERROR, loader->lastToken, "Unrecognised character %c", gf_GetChar(tokeniser)); 2539 | return 0; 2540 | } 2541 | } 2542 | 2543 | return 1; 2544 | } 2545 | 2546 | int gf_Tokenise(gf_Loader *loader, const char *buffer, gf_u64 count) { 2547 | assert(loader); 2548 | assert(buffer); 2549 | 2550 | gf_Tokeniser tokeniser; 2551 | gf_InitTokeniser(&tokeniser, buffer, count); 2552 | 2553 | return gf_TokeniseInternal(loader, &tokeniser); 2554 | } 2555 | 2556 | gf_Token *gf_ConsumeToken(gf_Loader *loader) { 2557 | gf_Token *token = loader->curToken; 2558 | if (!token) { 2559 | return NULL; 2560 | } 2561 | loader->curToken = loader->curToken->next; 2562 | return token; 2563 | } 2564 | 2565 | gf_Token *gf_PeekToken(gf_Loader *loader) { 2566 | return loader->curToken; 2567 | } 2568 | 2569 | gf_LoaderNode *gf_AddNode(gf_Loader *loader, gf_Token *token) { 2570 | gf_LoaderNode *node = (gf_LoaderNode *)loader->Allocate(sizeof(gf_LoaderNode)); 2571 | if (!node) { 2572 | GF_LOG(loader, GF_LOG_ERROR, "Out of memory. Failed to allocate loader node"); 2573 | return NULL; 2574 | } 2575 | 2576 | if (loader->lastNode) { 2577 | loader->lastNode->nextAllocated = node; 2578 | } 2579 | loader->lastNode = node; 2580 | 2581 | node->token = token; 2582 | node->parent = NULL; 2583 | node->next = NULL; 2584 | node->prev = NULL; 2585 | node->childrenHead = NULL; 2586 | node->childrenTail = NULL; 2587 | node->nextAllocated = NULL; 2588 | return node; 2589 | } 2590 | 2591 | void gf_AddChild(gf_LoaderNode *parent, gf_LoaderNode *child) { 2592 | if (parent == NULL) return; 2593 | if (parent->childrenHead == NULL) { 2594 | parent->childrenHead = child; 2595 | parent->childrenTail = child; 2596 | child->next = NULL; 2597 | child->prev = NULL; 2598 | } 2599 | else { 2600 | parent->childrenTail->next = child; 2601 | child->prev = parent->childrenTail; 2602 | child->next = NULL; 2603 | parent->childrenTail = child; 2604 | } 2605 | child->parent = parent; 2606 | } 2607 | 2608 | int gf_Parse(gf_Loader *loader, gf_LoaderNode *parentNode) { 2609 | assert(loader); 2610 | assert(loader->curToken); 2611 | assert(loader->lastToken); 2612 | assert(parentNode); 2613 | 2614 | int result = 0; 2615 | gf_Token *token = NULL; 2616 | while ((token = gf_ConsumeToken(loader))) { 2617 | 2618 | if (token->type == GF_TOKEN_TYPE_NAME) { 2619 | 2620 | if (parentNode) { 2621 | parentNode->token->type = GF_TOKEN_TYPE_COMPOSITE_TYPE; 2622 | } 2623 | 2624 | gf_Token *peek = gf_PeekToken(loader); 2625 | if (!peek) { 2626 | return 0; 2627 | } 2628 | 2629 | if (peek->type == GF_TOKEN_TYPE_VALUE_ASSIGN) { 2630 | 2631 | gf_LoaderNode *node = gf_AddNode(loader, token); 2632 | if (!node) { 2633 | return 0; 2634 | } 2635 | 2636 | gf_AddChild(parentNode, node); 2637 | gf_ConsumeToken(loader); 2638 | 2639 | loader->nestLevel++; 2640 | result = gf_Parse(loader, node); 2641 | if (!result) { 2642 | return 0; 2643 | } 2644 | } 2645 | else { 2646 | gf_LoaderNode *node = gf_AddNode(loader, token); 2647 | if (!node) { 2648 | return 0; 2649 | } 2650 | 2651 | gf_AddChild(parentNode, node); 2652 | } 2653 | } 2654 | else if (token->type == GF_TOKEN_TYPE_STRING || token->type == GF_TOKEN_TYPE_FLOAT || token->type == GF_TOKEN_TYPE_INTEGER) { 2655 | 2656 | gf_LoaderNode *node = gf_AddNode(loader, token); 2657 | if (!node) { 2658 | return 0; 2659 | } 2660 | 2661 | gf_AddChild(parentNode, node); 2662 | } 2663 | else if (token->type == GF_TOKEN_TYPE_CURLY_CLOSE) { 2664 | loader->nestLevel--; 2665 | break; 2666 | } 2667 | else if (token->type == GF_TOKEN_TYPE_END_FILE) { 2668 | break; 2669 | } 2670 | else if (token->type == GF_TOKEN_TYPE_VALUE_ASSIGN) { 2671 | GF_LOG_WITH_TOKEN(loader, GF_LOG_ERROR, token, "unexpected value assign at token. It is likely because the token before it is not an identifier node."); 2672 | return 0; 2673 | } 2674 | } 2675 | 2676 | return 1; 2677 | } 2678 | 2679 | 2680 | int gf_LoadInternal(gf_Loader *loader, const char *buffer, gf_u64 bufferCount) { 2681 | assert(loader); 2682 | assert(buffer); 2683 | 2684 | int result = gf_Tokenise(loader, buffer, bufferCount); 2685 | if (!result) { 2686 | GF_LOG(loader, GF_LOG_ERROR, "Failed to tokenise"); 2687 | return 0; 2688 | } 2689 | 2690 | loader->rootNode = gf_AddNode(loader, &loader->rootToken); 2691 | if (!loader->rootNode) { 2692 | return 0; 2693 | } 2694 | loader->curToken = &loader->rootToken; 2695 | 2696 | loader->nestLevel = 0; 2697 | result = gf_Parse(loader, loader->rootNode); 2698 | 2699 | if (loader->nestLevel != 0) { 2700 | GF_LOG(loader, GF_LOG_ERROR, "There is a missing closing brace }. A brace has been opened { without a matching close."); 2701 | result = 0; 2702 | } 2703 | 2704 | if (!result) { 2705 | GF_LOG(loader, GF_LOG_ERROR, "Failed to parse"); 2706 | return 0; 2707 | } 2708 | 2709 | return 1; 2710 | } 2711 | 2712 | int gf_LoadFromBuffer(gf_Loader *loader, const char *buffer, gf_u64 bufferCount, gf_LogAllocateFreeFunctions *funcs) { 2713 | assert(loader); 2714 | assert(buffer); 2715 | 2716 | gf_InitLoader(loader, funcs); 2717 | 2718 | return gf_LoadInternal(loader, buffer, bufferCount); 2719 | } 2720 | 2721 | int gf_LoadFromFile(gf_Loader *loader, const char *filename, gf_LogAllocateFreeFunctions *funcs) { 2722 | assert(loader); 2723 | assert(filename); 2724 | 2725 | gf_InitLoader(loader, funcs); 2726 | 2727 | gf_u64 bufferCount = 0; 2728 | loader->fileContentsBuffer = gf_AllocateNullTerminatedBufferFromFile(loader, filename, &bufferCount); 2729 | if (!loader->fileContentsBuffer) { 2730 | return 0; 2731 | } 2732 | 2733 | return gf_LoadInternal(loader, loader->fileContentsBuffer, bufferCount); 2734 | } 2735 | 2736 | void gf_Unload(gf_Loader *loader) { 2737 | assert(loader); 2738 | 2739 | gf_Token *token = loader->firstToken; 2740 | gf_Token *nextToken; 2741 | while (token) { 2742 | nextToken = token->next; 2743 | loader->Free(token); 2744 | token = nextToken; 2745 | } 2746 | 2747 | gf_LoaderNode *node = loader->rootNode; 2748 | gf_LoaderNode *nextNode; 2749 | while (node) { 2750 | nextNode = node->nextAllocated; 2751 | loader->Free(node); 2752 | node = nextNode; 2753 | } 2754 | 2755 | loader->Free(loader->fileContentsBuffer); 2756 | loader->fileContentsBuffer = NULL; 2757 | } 2758 | 2759 | int gf_LoaderNodeToU32(gf_Loader *loader, gf_LoaderNode *node, gf_u32 *value) { 2760 | if (!node) { 2761 | GF_LOG(loader, GF_LOG_ERROR, "node is null in gf_LoaderNodeToU32"); 2762 | return 0; 2763 | } 2764 | if (node->token->type != GF_TOKEN_TYPE_INTEGER) { 2765 | GF_LOG_WITH_TOKEN(loader, GF_LOG_ERROR, node->token, "node type is not integer"); 2766 | return 0; 2767 | } 2768 | if (!gf_StringSpanToU32(node->token->start, node->token->length, value)) { 2769 | GF_LOG_WITH_TOKEN(loader, GF_LOG_ERROR, node->token, "unable to convert token to u32 in gf_LoaderNodeToU32"); 2770 | return 0; 2771 | } 2772 | return 1; 2773 | } 2774 | 2775 | int gf_LoaderNodeToU64(gf_Loader *loader, gf_LoaderNode *node, gf_u64 *value) { 2776 | if (!node) { 2777 | GF_LOG(loader, GF_LOG_ERROR, "node is null in gf_LoaderNodeToU64"); 2778 | return 0; 2779 | } 2780 | if (node->token->type != GF_TOKEN_TYPE_INTEGER) { 2781 | GF_LOG_WITH_TOKEN(loader, GF_LOG_ERROR, node->token, "node type is not integer"); 2782 | return 0; 2783 | } 2784 | if (!gf_StringSpanToU64(node->token->start, node->token->length, value)) { 2785 | GF_LOG_WITH_TOKEN(loader, GF_LOG_ERROR, node->token, "unable to convert token to u64 in gf_LoaderNodeToU64"); 2786 | return 0; 2787 | } 2788 | return 1; 2789 | } 2790 | 2791 | int gf_LoaderNodeToS32(gf_Loader *loader, gf_LoaderNode *node, gf_s32 *value) { 2792 | if (!node) { 2793 | GF_LOG(loader, GF_LOG_ERROR, "node is null in gf_LoaderNodeToS32"); 2794 | return 0; 2795 | } 2796 | if (node->token->type != GF_TOKEN_TYPE_INTEGER) { 2797 | GF_LOG_WITH_TOKEN(loader, GF_LOG_ERROR, node->token, "node type is not integer"); 2798 | return 0; 2799 | } 2800 | if (!gf_StringSpanToS32(node->token->start, node->token->length, value)) { 2801 | GF_LOG_WITH_TOKEN(loader, GF_LOG_ERROR, node->token, "unable to convert token to S64 in gf_LoaderNodeToS32"); 2802 | return 0; 2803 | } 2804 | return 1; 2805 | } 2806 | 2807 | 2808 | int gf_LoaderNodeToS64(gf_Loader *loader, gf_LoaderNode *node, gf_s64 *value) { 2809 | if (!node) { 2810 | GF_LOG(loader, GF_LOG_ERROR, "node is null in gf_LoaderNodeToS64"); 2811 | return 0; 2812 | } 2813 | if (node->token->type != GF_TOKEN_TYPE_INTEGER) { 2814 | GF_LOG_WITH_TOKEN(loader, GF_LOG_ERROR, node->token, "node type is not integer"); 2815 | return 0; 2816 | } 2817 | if (!gf_StringSpanToS64(node->token->start, node->token->length, value)) { 2818 | GF_LOG_WITH_TOKEN(loader, GF_LOG_ERROR, node->token, "unable to convert token to S64 in gf_LoaderNodeToS64"); 2819 | return 0; 2820 | } 2821 | return 1; 2822 | } 2823 | 2824 | int gf_LoaderNodeToF64(gf_Loader *loader, gf_LoaderNode *node, gf_f64 *value) { 2825 | if (!node) { 2826 | GF_LOG(loader, GF_LOG_ERROR, "node is null in gf_LoaderNodeToF64"); 2827 | return 0; 2828 | } 2829 | if (node->token->type != GF_TOKEN_TYPE_FLOAT) { 2830 | GF_LOG_WITH_TOKEN(loader, GF_LOG_ERROR, node->token, "node type is not float"); 2831 | return 0; 2832 | } 2833 | if (!gf_StringSpanToF64(node->token->start, node->token->length, value)) { 2834 | GF_LOG_WITH_TOKEN(loader, GF_LOG_ERROR, node->token, "unable to convert token to S64 in gf_LoaderNodeToF64"); 2835 | return 0; 2836 | } 2837 | return 1; 2838 | } 2839 | 2840 | int gf_LoaderNodeToF32(gf_Loader *loader, gf_LoaderNode *node, gf_f32 *value) { 2841 | if (!node) { 2842 | GF_LOG(loader, GF_LOG_ERROR, "node is null in gf_LoaderNodeToF32"); 2843 | return 0; 2844 | } 2845 | if (node->token->type != GF_TOKEN_TYPE_FLOAT) { 2846 | GF_LOG_WITH_TOKEN(loader, GF_LOG_ERROR, node->token, "node type is not float"); 2847 | return 0; 2848 | } 2849 | if (!gf_StringSpanToF32(node->token->start, node->token->length, value)) { 2850 | GF_LOG_WITH_TOKEN(loader, GF_LOG_ERROR, node->token, "unable to convert token to S64 in gf_LoaderNodeToF32"); 2851 | return 0; 2852 | } 2853 | return 1; 2854 | } 2855 | 2856 | int gf_LoaderNodeToString(gf_Loader *loader, gf_LoaderNode *node, char *src, gf_u64 srcCapacityIncludesNullTerminator) { 2857 | if (!node) { 2858 | GF_LOG(loader, GF_LOG_ERROR, "node is null in gf_LoaderNodeToString"); 2859 | return 0; 2860 | } 2861 | if (node->token->type != GF_TOKEN_TYPE_STRING) { 2862 | GF_LOG_WITH_TOKEN(loader, GF_LOG_ERROR, node->token, "node type is not a string"); 2863 | return 0; 2864 | } 2865 | if (node->token->length >= srcCapacityIncludesNullTerminator) { 2866 | GF_LOG_WITH_TOKEN(loader, GF_LOG_ERROR, node->token, "unable to convert token to string in gf_LoaderNodeToString"); 2867 | return 0; 2868 | } 2869 | memcpy(src, node->token->start, node->token->length); 2870 | src[node->token->length] = '\0'; 2871 | 2872 | return 1; 2873 | } 2874 | 2875 | 2876 | gf_LoaderNode *gf_GetRoot(gf_Loader *loader) { 2877 | assert(loader); 2878 | 2879 | if (!loader->rootNode) { 2880 | GF_LOG(loader, GF_LOG_WARNING, "loader has no root node"); 2881 | return NULL; 2882 | } 2883 | return loader->rootNode; 2884 | } 2885 | 2886 | gf_LoaderNode *gf_GetNext(gf_Loader *loader, gf_LoaderNode *node) { 2887 | assert(loader); 2888 | 2889 | if (!node) { 2890 | GF_LOG(loader, GF_LOG_ERROR, "node is null"); 2891 | return NULL; 2892 | } 2893 | else if (!node->next) { 2894 | GF_LOG_WITH_TOKEN(loader, GF_LOG_WARNING, node->token, "node's next is null"); 2895 | return NULL; 2896 | } 2897 | else { 2898 | return node->next; 2899 | } 2900 | } 2901 | 2902 | gf_LoaderNode *gf_GetChild(gf_Loader *loader, gf_LoaderNode *node) { 2903 | assert(loader); 2904 | 2905 | if (!node) { 2906 | GF_LOG(loader, GF_LOG_ERROR, "node is null"); 2907 | return NULL; 2908 | } 2909 | else if (!node->childrenHead) { 2910 | GF_LOG_WITH_TOKEN(loader, GF_LOG_WARNING, node->token, "node's child is null"); 2911 | return NULL; 2912 | } 2913 | else { 2914 | return node->childrenHead; 2915 | } 2916 | } 2917 | 2918 | gf_TokenType gf_GetType(gf_Loader *loader, gf_LoaderNode *node) { 2919 | if (!node) { 2920 | GF_LOG(loader, GF_LOG_ERROR, "node is null"); 2921 | return GF_TOKEN_TYPE_END_FILE; 2922 | } 2923 | return node->token->type; 2924 | } 2925 | 2926 | gf_LoaderNode *gf_FindFirstChild(gf_Loader *loader, gf_LoaderNode *node, const char *str) { 2927 | // TODO: change to hash map? 2928 | if (!node) { 2929 | GF_LOG(loader, GF_LOG_ERROR, "node is null"); 2930 | return NULL; 2931 | } 2932 | 2933 | gf_u64 len = gf_StringLength(str); 2934 | 2935 | gf_LoaderNode *child = node->childrenHead; 2936 | while (child) { 2937 | if (child->token->type == GF_TOKEN_TYPE_COMPOSITE_TYPE || child->token->type == GF_TOKEN_TYPE_NAME) { 2938 | if (gf_AreStringSpansEqual(child->token->start, child->token->length, str, len)) { 2939 | return child; 2940 | } 2941 | } 2942 | child = child->next; 2943 | } 2944 | 2945 | return NULL; 2946 | } 2947 | 2948 | gf_LoaderNode *gf_FindFirstNext(gf_Loader *loader, gf_LoaderNode *node, const char *str) { 2949 | // TODO: change to hash map? 2950 | if (!node) { 2951 | GF_LOG(loader, GF_LOG_ERROR, "node is null"); 2952 | return NULL; 2953 | } 2954 | 2955 | gf_u64 len = gf_StringLength(str); 2956 | 2957 | gf_LoaderNode *next = node->next; 2958 | while (next) { 2959 | if (next->token->type == GF_TOKEN_TYPE_COMPOSITE_TYPE || next->token->type == GF_TOKEN_TYPE_NAME) { 2960 | if (gf_AreStringSpansEqual(next->token->start, next->token->length, str, len)) { 2961 | return next; 2962 | } 2963 | } 2964 | next = next->next; 2965 | } 2966 | 2967 | return NULL; 2968 | } 2969 | 2970 | int gf_LoadVariableU32(gf_Loader *loader, gf_LoaderNode *node, gf_u32 *value) { 2971 | if (!node) { 2972 | GF_LOG(loader, GF_LOG_ERROR, "node is null"); 2973 | return 0; 2974 | } 2975 | 2976 | gf_LoaderNode *child = gf_GetChild(loader, node); 2977 | if (!child) { 2978 | GF_LOG_WITH_TOKEN(loader, GF_LOG_ERROR, node->token, "child is null"); 2979 | return 0; 2980 | } 2981 | return gf_LoaderNodeToU32(loader, child, value); 2982 | } 2983 | 2984 | 2985 | int gf_LoadVariableU64(gf_Loader *loader, gf_LoaderNode *node, gf_u64 *value) { 2986 | if (!node) { 2987 | GF_LOG(loader, GF_LOG_ERROR, "node is null"); 2988 | return 0; 2989 | } 2990 | 2991 | gf_LoaderNode *child = gf_GetChild(loader, node); 2992 | if (!child) { 2993 | GF_LOG_WITH_TOKEN(loader, GF_LOG_ERROR, node->token, "child is null"); 2994 | return 0; 2995 | } 2996 | return gf_LoaderNodeToU64(loader, child, value); 2997 | } 2998 | 2999 | int gf_LoadVariableS32(gf_Loader *loader, gf_LoaderNode *node, gf_s32 *value) { 3000 | if (!node) { 3001 | GF_LOG(loader, GF_LOG_ERROR, "node is null"); 3002 | return 0; 3003 | } 3004 | 3005 | gf_LoaderNode *child = gf_GetChild(loader, node); 3006 | if (!child) { 3007 | GF_LOG_WITH_TOKEN(loader, GF_LOG_ERROR, node->token, "child is null"); 3008 | return 0; 3009 | } 3010 | return gf_LoaderNodeToS32(loader, child, value); 3011 | } 3012 | 3013 | int gf_LoadVariableS64(gf_Loader *loader, gf_LoaderNode *node, gf_s64 *value) { 3014 | if (!node) { 3015 | GF_LOG(loader, GF_LOG_ERROR, "node is null"); 3016 | return 0; 3017 | } 3018 | 3019 | gf_LoaderNode *child = gf_GetChild(loader, node); 3020 | if (!child) { 3021 | GF_LOG_WITH_TOKEN(loader, GF_LOG_ERROR, node->token, "child is null"); 3022 | return 0; 3023 | } 3024 | return gf_LoaderNodeToS64(loader, child, value); 3025 | } 3026 | 3027 | int gf_LoadVariableF64(gf_Loader *loader, gf_LoaderNode *node, gf_f64 *value) { 3028 | if (!node) { 3029 | GF_LOG(loader, GF_LOG_ERROR, "node is null"); 3030 | return 0; 3031 | } 3032 | 3033 | gf_LoaderNode *child = gf_GetChild(loader, node); 3034 | if (!child) { 3035 | GF_LOG_WITH_TOKEN(loader, GF_LOG_ERROR, node->token, "child is null"); 3036 | return 0; 3037 | } 3038 | return gf_LoaderNodeToF64(loader, child, value); 3039 | } 3040 | 3041 | 3042 | int gf_LoadVariableF32(gf_Loader *loader, gf_LoaderNode *node, gf_f32 *value) { 3043 | if (!node) { 3044 | GF_LOG(loader, GF_LOG_ERROR, "node is null"); 3045 | return 0; 3046 | } 3047 | 3048 | gf_LoaderNode *child = gf_GetChild(loader, node); 3049 | if (!child) { 3050 | GF_LOG_WITH_TOKEN(loader, GF_LOG_ERROR, node->token, "child is null"); 3051 | return 0; 3052 | } 3053 | return gf_LoaderNodeToF32(loader, child, value); 3054 | } 3055 | 3056 | int gf_LoadVariableVec3(gf_Loader *loader, gf_LoaderNode *node, gf_f32 *x, gf_f32 *y, gf_f32 *z) { 3057 | if (!node) { 3058 | GF_LOG(loader, GF_LOG_ERROR, "node is null"); 3059 | return 0; 3060 | } 3061 | 3062 | gf_LoaderNode *nodeX = gf_GetChild(loader, node); 3063 | if (!nodeX) { 3064 | GF_LOG_WITH_TOKEN(loader, GF_LOG_ERROR, node->token, "child is null"); 3065 | return 0; 3066 | } 3067 | 3068 | gf_LoaderNode *nodeY = gf_GetNext(loader, nodeX); 3069 | if (!nodeY) { 3070 | GF_LOG_WITH_TOKEN(loader, GF_LOG_ERROR, nodeX->token, "y value is null"); 3071 | return 0; 3072 | } 3073 | 3074 | gf_LoaderNode *nodeZ = gf_GetNext(loader, nodeY); 3075 | if (!nodeZ) { 3076 | GF_LOG_WITH_TOKEN(loader, GF_LOG_ERROR, nodeY->token, "z value is null"); 3077 | return 0; 3078 | } 3079 | 3080 | if (!gf_LoaderNodeToF32(loader, nodeX, x)) { 3081 | return 0; 3082 | } 3083 | if (!gf_LoaderNodeToF32(loader, nodeY, y)) { 3084 | return 0; 3085 | } 3086 | if (!gf_LoaderNodeToF32(loader, nodeZ, z)) { 3087 | return 0; 3088 | } 3089 | 3090 | return 1; 3091 | } 3092 | 3093 | int gf_LoadVariableString(gf_Loader *loader, gf_LoaderNode *node, char *str, gf_u64 lenWithNullTerminator) { 3094 | if (!node) { 3095 | GF_LOG(loader, GF_LOG_ERROR, "node is null"); 3096 | return 0; 3097 | } 3098 | 3099 | gf_LoaderNode *child = gf_GetChild(loader, node); 3100 | if (!child) { 3101 | GF_LOG_WITH_TOKEN(loader, GF_LOG_ERROR, node->token, "child is null"); 3102 | return 0; 3103 | } 3104 | 3105 | gf_u64 index = 0; 3106 | for (index = 0; index < lenWithNullTerminator - 1 && index < child->token->length; index++) { 3107 | str[index] = child->token->start[index]; 3108 | } 3109 | str[index] = '\0'; 3110 | 3111 | return 1; 3112 | } 3113 | 3114 | int gf_LoadArrayS32(gf_Loader *loader, gf_LoaderNode *node, gf_s32 *value, gf_u64 count) { 3115 | if (!node) { 3116 | GF_LOG(loader, GF_LOG_ERROR, "node is null"); 3117 | return 0; 3118 | } 3119 | 3120 | gf_LoaderNode *child = gf_GetChild(loader, node); 3121 | if (!child) { 3122 | GF_LOG_WITH_TOKEN(loader, GF_LOG_ERROR, node->token, "child is null"); 3123 | return 0; 3124 | } 3125 | 3126 | gf_u64 index = 0; 3127 | while (child && index < count) { 3128 | if (!gf_LoaderNodeToS32(loader, child, &value[index])) { 3129 | return 0; 3130 | } 3131 | child = child->next; 3132 | index++; 3133 | } 3134 | 3135 | return 1; 3136 | } 3137 | 3138 | /*-----------------------------------------------------------------------------------*/ 3139 | 3140 | #endif 3141 | 3142 | #ifdef GF_IMPLEMENTATION_WITH_TESTS 3143 | 3144 | /*-------------------------------------TESTS-----------------------------------------*/ 3145 | 3146 | int gf_Test(void) { 3147 | 3148 | { 3149 | const char *str = "a {"; 3150 | gf_Loader loader; 3151 | int result = gf_LoadFromBuffer(&loader, str, gf_StringLength(str), NULL); 3152 | GF_TEST_ASSERT(result == 0, str); 3153 | gf_Unload(&loader); 3154 | } 3155 | { 3156 | const char *str = "{{{{{{{{{{{{{}}"; 3157 | gf_Loader loader; 3158 | int result = gf_LoadFromBuffer(&loader, str, gf_StringLength(str), NULL); 3159 | GF_TEST_ASSERT(result == 0, str); 3160 | gf_Unload(&loader); 3161 | } 3162 | { 3163 | const char *str = "{{{{{{{{{{{{{a}}}}}}}}}}}}}"; 3164 | gf_Loader loader; 3165 | int result = gf_LoadFromBuffer(&loader, str, gf_StringLength(str), NULL); 3166 | GF_TEST_ASSERT(result == 0, str); 3167 | gf_Unload(&loader); 3168 | } 3169 | { 3170 | const char *str = " "; 3171 | gf_Loader loader; 3172 | int result = gf_LoadFromBuffer(&loader, str, gf_StringLength(str), NULL); 3173 | GF_TEST_ASSERT(result == 1, str); 3174 | gf_Unload(&loader); 3175 | } 3176 | { 3177 | const char *str = "/* this is a comment */"; 3178 | gf_Loader loader; 3179 | int result = gf_LoadFromBuffer(&loader, str, gf_StringLength(str), NULL); 3180 | GF_TEST_ASSERT(result == 1, str); 3181 | gf_Unload(&loader); 3182 | } 3183 | { 3184 | const char *str = "3.0 { a }"; 3185 | gf_Loader loader; 3186 | int result = gf_LoadFromBuffer(&loader, str, gf_StringLength(str), NULL); 3187 | GF_TEST_ASSERT(result == 0, str); 3188 | gf_Unload(&loader); 3189 | } 3190 | { 3191 | const char *str = "{ a, b, c, d }"; 3192 | gf_Loader loader; 3193 | int result = gf_LoadFromBuffer(&loader, str, gf_StringLength(str), NULL); 3194 | GF_TEST_ASSERT(result == 0, str); 3195 | gf_Unload(&loader); 3196 | } 3197 | { 3198 | const char *str = "a, b, c, d"; 3199 | gf_Loader loader; 3200 | int result = gf_LoadFromBuffer(&loader, str, gf_StringLength(str), NULL); 3201 | GF_TEST_ASSERT(result == 1, str); 3202 | gf_Unload(&loader); 3203 | } 3204 | { 3205 | gf_Loader loader; 3206 | static char src[1 << 20]; 3207 | memset(src, '-', sizeof(src) - 1); 3208 | int result = gf_LoadFromBuffer(&loader, src, sizeof(src) - 1, 0); 3209 | GF_TEST_ASSERT(result == 0, "big test"); 3210 | gf_Unload(&loader); 3211 | } 3212 | 3213 | { 3214 | const char *str = "a b c d e "; 3215 | gf_Loader loader; 3216 | int result = gf_LoadFromBuffer(&loader, str, gf_StringLength(str), NULL); 3217 | GF_TEST_ASSERT(result == 1, str); 3218 | gf_Unload(&loader); 3219 | } 3220 | { 3221 | const char *str = "a /*"; 3222 | gf_Loader loader; 3223 | int result = gf_LoadFromBuffer(&loader, str, gf_StringLength(str), NULL); 3224 | GF_TEST_ASSERT(result == 0, str); 3225 | gf_Unload(&loader); 3226 | } 3227 | { 3228 | const char *str = "a \""; 3229 | gf_Loader loader; 3230 | int result = gf_LoadFromBuffer(&loader, str, gf_StringLength(str), NULL); 3231 | GF_TEST_ASSERT(result == 0, str); 3232 | gf_Unload(&loader); 3233 | } 3234 | { 3235 | const char *str = "1, 1.0"; 3236 | gf_Loader loader; 3237 | int result = gf_LoadFromBuffer(&loader, str, gf_StringLength(str), NULL); 3238 | GF_TEST_ASSERT(result == 1, str); 3239 | 3240 | gf_LoaderNode *root = gf_GetRoot(&loader); 3241 | gf_LoaderNode *intNode = gf_GetChild(&loader, root); 3242 | GF_TEST_ASSERT(intNode, str); 3243 | GF_TEST_ASSERT(gf_GetType(&loader, intNode) == GF_TOKEN_TYPE_INTEGER, str); 3244 | gf_LoaderNode *floatNode = gf_GetNext(&loader, intNode); 3245 | GF_TEST_ASSERT(floatNode, str); 3246 | GF_TEST_ASSERT(gf_GetType(&loader, floatNode) == GF_TOKEN_TYPE_FLOAT, str); 3247 | gf_Unload(&loader); 3248 | } 3249 | { 3250 | const char *str = 3251 | "MyStruct {\n" 3252 | " a { 3.14000 }\n" 3253 | " b { 101 }\n" 3254 | " c { -13 }\n" 3255 | " str { \"hello\" }\n" 3256 | "}\n"; 3257 | 3258 | gf_Loader loader; 3259 | int result = gf_LoadFromBuffer(&loader, str, gf_StringLength(str), NULL); 3260 | GF_TEST_ASSERT(result == 1, str); 3261 | gf_Unload(&loader); 3262 | } 3263 | { 3264 | const char *str = 3265 | "1.0\n" 3266 | "2.0\n" 3267 | "Hello\n"; 3268 | gf_Loader loader; 3269 | int result = gf_LoadFromBuffer(&loader, str, gf_StringLength(str), NULL); 3270 | GF_TEST_ASSERT(result == 1, str); 3271 | gf_Unload(&loader); 3272 | } 3273 | { 3274 | const char *str = "/* this is a comment */"; 3275 | gf_Loader loader; 3276 | int result = gf_LoadFromBuffer(&loader, str, gf_StringLength(str), NULL); 3277 | GF_TEST_ASSERT(result == 1, str); 3278 | gf_Unload(&loader); 3279 | } 3280 | { 3281 | const char *str = 3282 | "MyStruct {\n" 3283 | " a { 3.14000 }\n" 3284 | " b { *$3dnNONSENSE }\n" 3285 | " c { -13 }\n" 3286 | " str { \"hello\" }\n" 3287 | "}\n"; 3288 | 3289 | gf_Loader loader; 3290 | int result = gf_LoadFromBuffer(&loader, str, gf_StringLength(str), NULL); 3291 | GF_TEST_ASSERT(result == 0, str); 3292 | gf_Unload(&loader); 3293 | } 3294 | 3295 | puts("All tests passed!"); 3296 | 3297 | return 1; 3298 | } 3299 | 3300 | /*-----------------------------------------------------------------------------------*/ 3301 | 3302 | #endif 3303 | 3304 | #endif 3305 | --------------------------------------------------------------------------------