├── .gitmodules ├── .travis.yml ├── LICENSE ├── README.md ├── main.c ├── test.c ├── test.toml ├── toml-lemon.c ├── toml-lemon.h ├── toml-lemon.lemon ├── toml-parser.h ├── toml-re2c.re2c ├── toml.c ├── toml.h ├── waf └── wscript /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "vendor/libtap"] 2 | path = vendor/libtap 3 | url = https://github.com/mzgoddard/libtap.git 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | 3 | before_install: 4 | - sudo apt-get install re2c 5 | 6 | script: 7 | - ./waf configure build 8 | - build/toml-lookup test.toml -c 9 | - build/toml-test 10 | 11 | env: 12 | - - ANSICOLOR=1 13 | - LD_LIBRARY_PATH=$TRAVIS_BUILD_DIR/build:$LD_LIBRARY_PATH 14 | 15 | compiler: 16 | - clang 17 | - gcc 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Michael "Z" Goddard 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tomlc [![Build Status](https://travis-ci.org/mzgoddard/tomlc.png?branch=master)](https://travis-ci.org/mzgoddard/tomlc) 2 | 3 | This implementation targets [https://github.com/mojombo/toml](https://github.com/mojombo/toml) commit [4f23be43e4](https://github.com/mojombo/toml/commit/4f23be43e42775493f142e7dd025b6227e037dd9). 4 | 5 | Out of order tables are not yet supported: 6 | 7 | ```toml 8 | [a.b] 9 | value = 1 10 | 11 | [a] 12 | value = 2 13 | ``` 14 | 15 | ### License 16 | 17 | MIT 18 | 19 | ### Dependencies 20 | 21 | `tomlc` uses Lemon and re2c to generate its parser and lexer respectively. 22 | 23 | - Lemon : http://www.hwaci.com/sw/lemon/ 24 | - re2c : http://re2c.org/ 25 | 26 | ### Build Instructions 27 | 28 | 1. Install `re2c`. 29 | 1. `./waf configure build install` 30 | By default, this installs as `toml`. 31 | 32 | 1. Test with `build/toml-test` 33 | 34 | ### Usage 35 | 36 | ```c 37 | // TOML_parse and TOML_load create the root table for you. 38 | TOMLTable *table = NULL; 39 | 40 | // Load TOML from a file. 41 | TOML_load( "test.toml", &table, NULL ); 42 | 43 | // Find the value we want. 44 | TOMLRef value = TOML_find( table, "player", "size", NULL ); 45 | int size = TOML_int( value ); 46 | 47 | // Free the TOML hierarchy when you're done. 48 | TOML_free( table ); 49 | ``` 50 | 51 | Part of `tomlc` is a tool called `toml-lookup` that can be used to access parts of a toml file. For example `toml-lookup test.toml "en.text[0].characterImage"` prints `text-only` to stdout. 52 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "toml.h" 5 | 6 | struct option { 7 | char *shortFlag; 8 | char *longFlag; 9 | char *argName; 10 | char *helpText; 11 | struct option *next; 12 | int argumentIndex; 13 | int argumentCount; 14 | }; 15 | 16 | void add_option( struct option **head, struct option *option ) { 17 | if ( *head == NULL ) { 18 | *head = option; 19 | return; 20 | } 21 | 22 | struct option *prev = *head; 23 | while ( prev->next != NULL ) { 24 | prev = prev->next; 25 | } 26 | 27 | prev->next = option; 28 | } 29 | 30 | int available_option( struct option *head, int i ) { 31 | struct option *ptr = head; 32 | while ( ptr ) { 33 | if ( 34 | ptr->argumentIndex >= i && 35 | ptr->argumentIndex < i + ptr->argumentCount 36 | ) { 37 | return 0; 38 | } 39 | ptr = ptr->next; 40 | } 41 | return 1; 42 | } 43 | 44 | #define USAGE( usageText ) char *usageHelpText = usageText; 45 | 46 | #define FLAG( shortOpt, longOpt, optionName, helpText ) \ 47 | int optionName = 0; \ 48 | struct option optionName ## Option = { \ 49 | shortOpt, longOpt, #optionName, helpText, NULL, -1, 0 \ 50 | }; \ 51 | { \ 52 | add_option( &optionhead, &optionName ## Option ); \ 53 | \ 54 | for ( int i = 1; i < argc; ++i ) { \ 55 | if ( \ 56 | strcmp( argv[ i ], shortOpt ) == 0 || \ 57 | strcmp( argv[ i ], longOpt ) == 0 \ 58 | ) { \ 59 | optionName = 1; \ 60 | optionName ## Option.argumentIndex = i; \ 61 | optionName ## Option.argumentCount = 1; \ 62 | break; \ 63 | } \ 64 | } \ 65 | } 66 | 67 | #define STROPTION( shortOpt, longOpt, optionName, helpText ) \ 68 | char *optionName = NULL; \ 69 | struct option optionName ## Option = { \ 70 | shortOpt, longOpt, #optionName, helpText, NULL, -1, 0 \ 71 | }; \ 72 | { \ 73 | add_option( &optionhead, &optionName ## Option ); \ 74 | \ 75 | for ( int i = 1; i < argc; ++i ) { \ 76 | if ( \ 77 | ( \ 78 | shortOpt && \ 79 | strcmp( argv[ i ], shortOpt ) == 0 \ 80 | ) || ( \ 81 | longOpt && \ 82 | strcmp( argv[ i ], longOpt ) == 0 \ 83 | ) \ 84 | ) { \ 85 | optionName = argv[ i + 1 ]; \ 86 | optionName ## Option.argumentIndex = i; \ 87 | optionName ## Option.argumentCount = 2; \ 88 | break; \ 89 | } else if ( \ 90 | !shortOpt && !longOpt && available_option( optionhead, i ) \ 91 | ) { \ 92 | optionName = argv[ i ]; \ 93 | optionName ## Option.argumentIndex = i; \ 94 | optionName ## Option.argumentCount = 1; \ 95 | break; \ 96 | } \ 97 | } \ 98 | } 99 | 100 | int main( int argc, char **argv ) { 101 | struct option *optionhead = NULL; 102 | 103 | USAGE( "toml [options] filepath [members]" ); 104 | FLAG( "-h", "--help", help, "print help and exit" ); 105 | FLAG( "-c", "--check", check, "check that given source is valid toml" ); 106 | FLAG( "-v", "--version", version, "print version and exit" ); 107 | STROPTION( NULL, NULL, filepath, "path to toml file" ); 108 | STROPTION( NULL, NULL, members, "key names to lookup" ); 109 | 110 | if ( help || filepath == NULL ) { 111 | printf( "%s\n", usageHelpText ); 112 | struct option *tmp = optionhead; 113 | while ( tmp ) { 114 | if ( !tmp->shortFlag && !tmp->longFlag ) { 115 | printf( "\t%-15s %s\n", tmp->argName, tmp->helpText ); 116 | } else { 117 | printf( 118 | "\t%s, %s\t%s\n", tmp->shortFlag, tmp->longFlag, tmp->helpText 119 | ); 120 | } 121 | tmp = tmp->next; 122 | } 123 | return 0; 124 | } 125 | 126 | if ( version ) { 127 | printf( "toml v0.0.0\n" ); 128 | return 0; 129 | } 130 | 131 | TOMLTable *table = NULL; 132 | TOMLError *error = TOML_allocError( TOML_SUCCESS ); 133 | 134 | // Load and exit if there is an error. 135 | if ( TOML_load( filepath, &table, error ) != TOML_SUCCESS ) { 136 | printf( "%s\n", error->fullDescription ); 137 | return error->code; 138 | } 139 | 140 | TOML_free( error ); 141 | 142 | if ( check ) { 143 | printf( "ok\n" ); 144 | return 0; 145 | } 146 | 147 | // Parse path argument. 148 | TOMLRef ref = table; 149 | TOMLBasic *basic = ref; 150 | { 151 | int tokenSize = 0; 152 | char *token = NULL; 153 | char *tokenStart = members; 154 | char *tokenEnd = tokenStart; 155 | 156 | while ( tokenEnd ) { 157 | tokenEnd = strpbrk( tokenStart, ".[]" ); 158 | 159 | if ( tokenEnd ) { 160 | if ( tokenEnd - tokenStart > tokenSize ) { 161 | tokenSize = tokenEnd - tokenStart; 162 | free( token ); 163 | token = malloc( tokenSize + 1 ); 164 | } 165 | 166 | strncpy( token, tokenStart, tokenEnd - tokenStart ); 167 | token[ tokenEnd - tokenStart ] = 0; 168 | 169 | basic = ref = TOML_find( ref, token, NULL ); 170 | 171 | tokenEnd++; 172 | if ( *tokenEnd == '.' || *tokenEnd == '[' ) { 173 | tokenEnd++; 174 | } 175 | tokenStart = tokenEnd; 176 | if ( *tokenEnd == 0 ) { 177 | tokenEnd = NULL; 178 | } 179 | } else { 180 | basic = ref = TOML_find( ref, tokenStart, NULL ); 181 | } 182 | } 183 | } 184 | 185 | error = TOML_allocError( TOML_SUCCESS ); 186 | 187 | char *output; 188 | if ( TOML_stringify( &output, ref, error ) != TOML_SUCCESS ) { 189 | printf( "%s\n", error->fullDescription ); 190 | return error->code; 191 | } 192 | 193 | TOML_free( error ); 194 | 195 | printf( "%s\n", output ); 196 | free( output ); 197 | 198 | return 0; 199 | } 200 | -------------------------------------------------------------------------------- /test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "tap.h" 5 | #include "toml.h" 6 | 7 | int main() { 8 | ansicolor( getenv( "ANSICOLOR" ) != NULL ); 9 | plan( 75 ); 10 | 11 | note( "\n** memory management **" ); 12 | 13 | { /** alloc_number **/ 14 | note( "alloc_number" ); 15 | TOMLNumber *number = TOML_allocInt( 1 ); 16 | ok( TOML_toInt( number ) == 1, "number is 1" ); 17 | TOML_free( number ); 18 | } 19 | 20 | { /** alloc_string **/ 21 | note( "alloc_string" ); 22 | TOMLString *string = TOML_allocString( "Why, yes." ); 23 | char string_buffer[256]; 24 | TOML_copyString( string, 256, string_buffer ); 25 | is( string_buffer, "Why, yes.", "string is correct" ); 26 | TOML_free( string ); 27 | } 28 | 29 | { /** alloc_date **/ 30 | note( "alloc_date" ); 31 | TOMLDate *date = TOML_allocDate( 2013, 11, 20, 14, 30, 0 ); 32 | ok( date->sinceEpoch == 1387549800 ); 33 | TOML_free( date ); 34 | 35 | date = TOML_allocEpochDate( 1387549800 ); 36 | ok( date->year == 2013 ); 37 | ok( date->month == 11 ); 38 | ok( date->day == 20 ); 39 | ok( date->hour == 14 ); 40 | ok( date->minute == 30 ); 41 | ok( date->second == 0 ); 42 | TOML_free( date ); 43 | } 44 | 45 | { /** alloc_array **/ 46 | note( "alloc_array" ); 47 | TOMLArray *array = TOML_allocArray( TOML_INT, 48 | TOML_allocInt( 2 ), 49 | TOML_allocInt( 3 ), 50 | NULL 51 | ); 52 | ok( TOML_toInt( TOMLArray_getIndex( array, 0 ) ) == 2, "array[0] is 2" ); 53 | ok( TOML_toInt( TOMLArray_getIndex( array, 1 ) ) == 3, "array[1] is 3" ); 54 | TOML_free( array ); 55 | } 56 | 57 | { /** alloc_table **/ 58 | note( "alloc_table" ); 59 | TOMLTable *table = TOML_allocTable( 60 | TOML_allocString( "key" ), TOML_allocString( "value" ), 61 | NULL, NULL 62 | ); 63 | TOMLString *table_value = TOMLTable_getKey( table, "key" ); 64 | char value_buffer[256]; 65 | TOML_copyString( table_value, 256, value_buffer ); 66 | is( value_buffer, "value", "table \"key\" is \"value\"" ); 67 | TOML_free( table ); 68 | } 69 | 70 | note( "\n** parse **" ); 71 | 72 | { /** parse_entry_string **/ 73 | note( "parse_entry_string" ); 74 | TOMLTable *table = NULL; 75 | TOML_parse( "world = \"hello\"\n", &table, NULL ); 76 | ok( table != NULL, "table created" ); 77 | TOMLRef world = TOMLTable_getKey( table, "world" ); 78 | char *buffer = malloc( 256 ); 79 | TOML_copyString( world, 256, buffer ); 80 | is( buffer, "hello", "string contained hello" ); 81 | free( buffer ); 82 | TOML_free( table ); 83 | 84 | // escapes 85 | table = NULL; 86 | TOML_parse( "escapes = \"\\b\\t\\f\\n\\r\\\"\\/\\\\\"", &table, NULL ); 87 | ok( table != NULL, "table created" ); 88 | ok( table->keys->size == 1, "table has a member" ); 89 | ok( 90 | TOML_find( table, "escapes", NULL ) != NULL, 91 | "table contained escapes" 92 | ); 93 | is( 94 | ((TOMLString *) TOML_find( table, "escapes", NULL ))->content, 95 | "\b\t\f\n\r\"/\\", 96 | "member contained escaped characters" 97 | ); 98 | TOML_free( table ); 99 | } 100 | 101 | { /** parse_entry_int **/ 102 | note( "parse_entry_int" ); 103 | TOMLTable *table = NULL; 104 | TOML_parse( "world = 123\n", &table, NULL ); 105 | ok( table != NULL ); 106 | ok( TOMLTable_getKey( table, "world" ) != NULL ); 107 | ok( TOML_toInt( TOMLTable_getKey( table, "world" ) ) == 123 ); 108 | TOML_free( table ); 109 | } 110 | 111 | { /** parse_entry_double **/ 112 | note( "parse_entry_double" ); 113 | TOMLTable *table = NULL; 114 | TOML_parse( "world = 1.23\n", &table, NULL ); 115 | ok( table != NULL ); 116 | ok( TOMLTable_getKey( table, "world" ) != NULL ); 117 | ok( TOML_toDouble( TOMLTable_getKey( table, "world" ) ) == 1.23 ); 118 | TOML_free( table ); 119 | } 120 | 121 | { /** parse_entry_boolean **/ 122 | note( "parse_entry_boolean" ); 123 | TOMLTable *table = NULL; 124 | TOML_parse( "world = true\nmoon = false", &table, NULL ); 125 | ok( table != NULL ); 126 | ok( TOML_find( table, "world", NULL ) != NULL ); 127 | ok( TOML_isType( TOML_find( table, "world", NULL ), TOML_BOOLEAN ) ); 128 | ok( TOML_find( table, "moon", NULL ) != NULL ); 129 | ok( TOML_isType( TOML_find( table, "moon", NULL ), TOML_BOOLEAN ) ); 130 | TOML_free( table ); 131 | } 132 | 133 | { /** parse_entry_date **/ 134 | note( "parse_entry_date" ); 135 | TOMLTable *table = NULL; 136 | TOML_parse( "start = 2013-12-20T14:30:00Z", &table, NULL ); 137 | ok( table != NULL ); 138 | ok( TOML_find( table, "start", NULL ) != NULL ); 139 | TOML_free( table ); 140 | } 141 | 142 | { /** parse_entry_array_numbers **/ 143 | note( "parse_entry_array_numbers" ); 144 | TOMLTable *table = NULL; 145 | TOML_parse( "world = [ 1, 2, 3 ]\n", &table, NULL ); 146 | ok( table != NULL ); 147 | ok( TOMLTable_getKey( table, "world" ) != NULL ); 148 | ok( TOML_toInt( 149 | TOMLArray_getIndex( TOMLTable_getKey( table, "world" ), 1 ) 150 | ) == 2 ); 151 | TOML_free( table ); 152 | } 153 | 154 | { /** parse_entry_array_strings **/ 155 | note( "parse_entry_array_strings" ); 156 | TOMLTable *table = NULL; 157 | TOML_parse( "world = [ \"abc\" ]\n", &table, NULL ); 158 | ok( table != NULL ); 159 | ok( TOMLTable_getKey( table, "world" ) != NULL ); 160 | char *buffer = malloc( 256 ); 161 | TOML_copyString( 162 | TOMLArray_getIndex( TOMLTable_getKey( table, "world" ), 0 ), 256, buffer 163 | ); 164 | is( buffer, "abc" ); 165 | free( buffer ); 166 | TOML_free( table ); 167 | } 168 | 169 | { /** parse_table **/ 170 | note( "parse_table" ); 171 | TOMLTable *table = NULL; 172 | TOML_parse( "[world]\nplanet = \"pluto\"", &table, NULL ); 173 | ok( table != NULL ); 174 | ok( TOML_find( table, "world", "planet", NULL ) != NULL ); 175 | TOML_free( table ); 176 | } 177 | 178 | { /** parse_array_table **/ 179 | note( "parse_array_table" ); 180 | TOMLTable *table = NULL; 181 | TOML_parse( "[[world]]\nplanet = \"jupiter\"", &table, NULL ); 182 | ok( table != NULL ); 183 | TOMLArray *world = TOMLTable_getKey( table, "world" ); 184 | ok( world != NULL ); 185 | TOMLTable *index0 = TOMLArray_getIndex( world, 0 ); 186 | ok( index0 != NULL ); 187 | TOMLRef planet = TOMLTable_getKey( index0, "planet" ); 188 | ok( planet != NULL ); 189 | ok( TOML_find( table, "world", "0", "planet", NULL ) != NULL ); 190 | TOML_free( table ); 191 | } 192 | 193 | { /** parse_array_table_2 **/ 194 | note( "parse_array_table_2" ); 195 | TOMLTable *table = NULL; 196 | TOML_parse( 197 | "[[world]]\nplanet = \"jupiter\"\n[[world]]\nplanet = \"saturn\"", 198 | &table, 199 | NULL 200 | ); 201 | ok( table != NULL ); 202 | is( 203 | ((TOMLString *) TOML_find( table, "world", "1", "planet", NULL ))->content, 204 | "saturn" 205 | ); 206 | TOML_free( table ); 207 | } 208 | 209 | { /** parse_entry_array **/ 210 | note( "parse_entry_array" ); 211 | TOMLTable *table = NULL; 212 | TOML_parse( 213 | "planets = [ \"mercury\", \"venus\", \"earth\" ]", 214 | &table, 215 | NULL 216 | ); 217 | ok( table != NULL ); 218 | ok( ((TOMLArray *) TOMLTable_getKey( table, "planets" ) )->size == 3 ); 219 | TOML_free( table ); 220 | } 221 | 222 | note( "\n** errors **" ); 223 | 224 | { /** parse_incomplete_string **/ 225 | note( "parse_incomplete_string" ); 226 | TOMLTable *table = NULL; 227 | TOMLError *error = TOML_allocError( TOML_SUCCESS ); 228 | ok( TOML_parse( 229 | "world = \"planet\"\nmoon = \nothermoon = \"daedulus", 230 | &table, 231 | error 232 | ) != 0 ); 233 | ok( table == NULL ); 234 | ok( error->code != 0 ); 235 | TOML_free( error ); 236 | } 237 | 238 | { /** parse_incomplete_table_header **/ 239 | note( "parse_incomplete_table_header" ); 240 | TOMLTable *table = NULL; 241 | TOMLError *error = TOML_allocError( TOML_SUCCESS ); 242 | ok( TOML_parse( "[world]]\nhello = \"world\"", &table, error ) != 0 ); 243 | ok( table == NULL ); 244 | ok( error->code != 0 ); 245 | TOML_free( error ); 246 | } 247 | 248 | { /** parse_repeated_table_header **/ 249 | note( "parse_repeated_table_header" ); 250 | TOMLTable *table = NULL; 251 | TOMLError *error = TOML_allocError( TOML_SUCCESS ); 252 | ok( TOML_parse( "[world]\n[world]", &table, error ) != 0 ); 253 | ok( table == NULL ); 254 | ok( error->code != 0 ); 255 | TOML_free( error ); 256 | } 257 | 258 | { /** parse_repeated_entry **/ 259 | note( "parse_repeated_entry" ); 260 | TOMLTable *table = NULL; 261 | TOMLError *error = TOML_allocError( TOML_SUCCESS ); 262 | ok( TOML_parse( "planet = 1\nplanet = 2", &table, error ) != 0 ); 263 | ok( table == NULL ); 264 | ok( error->code != 0 ); 265 | TOML_free( error ); 266 | } 267 | 268 | note( "\n** stringify **" ); 269 | 270 | { /** stringify_string **/ 271 | note( "stringify_string" ); 272 | TOMLTable *table = NULL; 273 | TOML_parse( "word = \"some words\"", &table, NULL ); 274 | char *buffer; 275 | TOML_stringify( &buffer, TOMLTable_getKey( table, "word" ), NULL ); 276 | is( buffer, "some words" ); 277 | free( buffer ); 278 | TOML_free( table ); 279 | 280 | // escapes 281 | table = NULL; 282 | TOML_parse( "escapes = \"\\b\\t\\f\\n\\r\\\"\\/\\\\\"", &table, NULL ); 283 | ok( table != NULL ); 284 | ok( TOML_find( table, "escapes", NULL ) != NULL ); 285 | buffer = NULL; 286 | TOML_stringify( &buffer, table, NULL ); 287 | is( buffer, "escapes = \"\\b\\t\\f\\n\\r\\\"\\/\\\\\"\n" ); 288 | free( buffer ); 289 | TOML_free( table ); 290 | } 291 | 292 | { /** stringify_boolean **/ 293 | note( "stringify_boolean" ); 294 | TOMLTable *table = NULL; 295 | TOML_parse( "world = true\nmoon = false", &table, NULL ); 296 | char *buffer; 297 | TOML_stringify( &buffer, table, NULL ); 298 | is( buffer, "world = true\nmoon = false\n" ); 299 | free( buffer ); 300 | TOML_free( table ); 301 | } 302 | 303 | { /** stringify_date **/ 304 | note( "stringify_date" ); 305 | TOMLTable *table = NULL; 306 | TOML_parse( "date = 2013-12-20T14:30:00Z", &table, NULL ); 307 | char *buffer; 308 | TOML_stringify( &buffer, table, NULL ); 309 | is( buffer, "date = 2013-12-20T14:30:00Z\n" ); 310 | free( buffer ); 311 | TOML_free( table ); 312 | } 313 | 314 | { /** stringify_table_simple **/ 315 | note( "stringify_table_simple" ); 316 | TOMLTable *table = NULL; 317 | TOML_parse( "word = \"some \\\"words\\\"\"", &table, NULL ); 318 | char *buffer; 319 | TOML_stringify( &buffer, table, NULL ); 320 | is( buffer, "word = \"some \\\"words\\\"\"\n" ); 321 | free( buffer ); 322 | TOML_free( table ); 323 | } 324 | 325 | { /** stringify_table_child **/ 326 | note( "stringify_table_child" ); 327 | TOMLTable *table = NULL; 328 | TOML_parse( "[table]\nchairs = 4", &table, NULL ); 329 | char *buffer; 330 | TOML_stringify( &buffer, table, NULL ); 331 | is( buffer, "[table]\nchairs = 4\n" ); 332 | free( buffer ); 333 | TOML_free( table ); 334 | } 335 | 336 | { /** stringify_arrays **/ 337 | note( "stringify_arrays" ); 338 | TOMLTable *table = NULL; 339 | TOML_parse( 340 | "[[planets]]\nmoons = [ \"io\" ]\n[[planets]]\n[[planets]]\nmoons = []", &table, NULL 341 | ); 342 | char *buffer; 343 | TOML_stringify( &buffer, table, NULL ); 344 | is( 345 | buffer, 346 | "[[planets]]\nmoons = [ \"io\" ]\n\n[[planets]]\n\n[[planets]]\nmoons = []\n" 347 | ); 348 | free( buffer ); 349 | TOML_free( table ); 350 | } 351 | 352 | { /** stringify_nested_tables **/ 353 | note( "stringify_nested_tables" ); 354 | TOMLTable *table = NULL; 355 | TOML_parse( 356 | "[top.bottom]\nmiddle = \"no\"\n" 357 | "[top.middle.bottom]\nmiddle = \"infact, yes\"", 358 | &table, 359 | NULL 360 | ); 361 | ok( TOML_find( table, "top", "middle", "bottom", "middle", NULL ) != NULL ); 362 | char *buffer; 363 | TOML_stringify( &buffer, table, NULL ); 364 | is( 365 | buffer, 366 | "[top.bottom]\nmiddle = \"no\"\n\n" 367 | "[top.middle.bottom]\nmiddle = \"infact, yes\"\n" 368 | ); 369 | free( buffer ); 370 | TOML_free( table ); 371 | } 372 | 373 | note( "** unicode **" ); 374 | 375 | { /** parse_utf8 **/ 376 | note( "parse_utf8" ); 377 | TOMLTable *table = NULL; 378 | TOML_parse( "utf8 = \"\\u00a0\\u07ff\\u0800\\uffff\"", &table, NULL ); 379 | ok( table != NULL ); 380 | TOMLString *str = TOML_find( table, "utf8", NULL ); 381 | is( str->content, "\u00a0\u07ff\u0800\uffff" ); 382 | TOML_free( table ); 383 | } 384 | 385 | { /** stringify_utf8 **/ 386 | note( "stringify_utf8" ); 387 | TOMLTable *table = NULL; 388 | TOML_parse( "utf8 = \"\\u00a0\\u07ff\\u0800\\uffff\"", &table, NULL ); 389 | ok( table != NULL ); 390 | char *buffer; 391 | TOML_stringify( &buffer, table, NULL ); 392 | is( buffer, "utf8 = \"\\u00a0\\u07ff\\u0800\\uffff\"\n" ); 393 | free( buffer ); 394 | TOML_free( table ); 395 | } 396 | 397 | done_testing(); 398 | } 399 | -------------------------------------------------------------------------------- /test.toml: -------------------------------------------------------------------------------- 1 | # Hello World 2 | [player] 3 | particles = [ 4 | [ 0.0, -5.0 ], 5 | [ 4.330127018922194, -2.4999999999999996 ], 6 | [ -4.330127018922194, -2.4999999999999996 ] 7 | ] 8 | size = 10 9 | maxOxygen = 2048 10 | maxResource = 256 11 | 12 | [[en.text]] 13 | characterImage = "text-only" 14 | 15 | [en.text.details] 16 | who = "me" 17 | 18 | [[en.text.subtext]] 19 | text = "Hello world. I am fine today." 20 | 21 | [[en.text.subtext]] 22 | text = "Hmm ... what's with the monitor." 23 | 24 | [[en.text]] 25 | characterImage = "face-happy" 26 | text = "Oh there we go. Video re-established." 27 | -------------------------------------------------------------------------------- /toml-lemon.c: -------------------------------------------------------------------------------- 1 | /* Driver template for the LEMON parser generator. 2 | ** The author disclaims copyright to this source code. 3 | */ 4 | /* First off, code is included that follows the "include" declaration 5 | ** in the input grammar file. */ 6 | #include 7 | #line 5 "toml-lemon.lemon" 8 | 9 | #include 10 | #include 11 | #include "toml-parser.h" 12 | 13 | typedef struct table_id_node { 14 | char *name; 15 | struct table_id_node *first; 16 | struct table_id_node *next; 17 | } table_id_node; 18 | 19 | char * _TOML_newstr( TOMLToken *token ) { 20 | if ( token->tokenStr ) { 21 | char *buffer = token->tokenStr; 22 | token->tokenStr = NULL; 23 | return buffer; 24 | } 25 | 26 | int size = token->end - token->start; 27 | char *buffer = malloc( size + 1 ); 28 | strncpy( buffer, token->start, size ); 29 | buffer[ size ] = 0; 30 | return buffer; 31 | } 32 | 33 | char * _TOML_getline( TOMLToken *token ) { 34 | char *endOfLine = strchr( token->lineStart, '\n' ); 35 | if ( endOfLine == NULL ) { 36 | endOfLine = strchr( token->lineStart, 0 ); 37 | } 38 | 39 | int size = endOfLine - token->lineStart; 40 | char *buffer = malloc( size + 1 ); 41 | strncpy( buffer, token->lineStart, size ); 42 | buffer[ size ] = 0; 43 | 44 | return buffer; 45 | } 46 | 47 | void _TOML_fillError( 48 | TOMLToken *token, TOMLParserState *state, int errorCode 49 | ) { 50 | state->errorCode = errorCode; 51 | 52 | TOMLError *error = state->errorObj; 53 | if ( error ) { 54 | error->code = errorCode; 55 | error->lineNo = token->line; 56 | error->line = _TOML_getline( state->token ); 57 | 58 | int messageSize = strlen( TOMLErrorDescription[ errorCode ] ); 59 | error->message = malloc( messageSize + 1 ); 60 | strncpy( error->message, TOMLErrorDescription[ errorCode ], messageSize ); 61 | error->message[ messageSize ] = 0; 62 | 63 | char *longMessage = malloc( 64 | strlen( error->line ) + 65 | strlen( error->message ) + 66 | (int) ( error->lineNo / 10 ) + 67 | 20 68 | ); 69 | sprintf( 70 | longMessage, 71 | "Error on line %d. %s: %s", 72 | error->lineNo, 73 | error->message, 74 | error->line 75 | ); 76 | error->fullDescription = longMessage; 77 | } 78 | } 79 | 80 | void TOML_freeToken( TOMLToken *token ) { 81 | free( token->tokenStr ); 82 | free( token ); 83 | } 84 | #line 85 "toml-lemon.c" 85 | /* Next is all token values, in a form suitable for use by makeheaders. 86 | ** This section will be null unless lemon is run with the -m switch. 87 | */ 88 | /* 89 | ** These constants (all generated automatically by the parser generator) 90 | ** specify the various kinds of tokens (terminals) that the parser 91 | ** understands. 92 | ** 93 | ** Each symbol here is a terminal symbol in the grammar. 94 | */ 95 | /* Make sure the INTERFACE macro is defined. 96 | */ 97 | #ifndef INTERFACE 98 | # define INTERFACE 1 99 | #endif 100 | /* The next thing included is series of defines which control 101 | ** various aspects of the generated parser. 102 | ** YYCODETYPE is the data type used for storing terminal 103 | ** and nonterminal numbers. "unsigned char" is 104 | ** used if there are fewer than 250 terminals 105 | ** and nonterminals. "int" is used otherwise. 106 | ** YYNOCODE is a number of type YYCODETYPE which corresponds 107 | ** to no legal terminal or nonterminal number. This 108 | ** number is used to fill in empty slots of the hash 109 | ** table. 110 | ** YYFALLBACK If defined, this indicates that one or more tokens 111 | ** have fall-back values which should be used if the 112 | ** original value of the token will not parse. 113 | ** YYACTIONTYPE is the data type used for storing terminal 114 | ** and nonterminal numbers. "unsigned char" is 115 | ** used if there are fewer than 250 rules and 116 | ** states combined. "int" is used otherwise. 117 | ** TOMLParserTOKENTYPE is the data type used for minor tokens given 118 | ** directly to the parser from the tokenizer. 119 | ** YYMINORTYPE is the data type used for all minor tokens. 120 | ** This is typically a union of many types, one of 121 | ** which is TOMLParserTOKENTYPE. The entry in the union 122 | ** for base tokens is called "yy0". 123 | ** YYSTACKDEPTH is the maximum depth of the parser's stack. If 124 | ** zero the stack is dynamically sized using realloc() 125 | ** TOMLParserARG_SDECL A static variable declaration for the %extra_argument 126 | ** TOMLParserARG_PDECL A parameter declaration for the %extra_argument 127 | ** TOMLParserARG_STORE Code to store %extra_argument into yypParser 128 | ** TOMLParserARG_FETCH Code to extract %extra_argument from yypParser 129 | ** YYNSTATE the combined number of states. 130 | ** YYNRULE the number of rules in the grammar 131 | ** YYERRORSYMBOL is the code number of the error symbol. If not 132 | ** defined, then do no error processing. 133 | */ 134 | #define YYCODETYPE unsigned char 135 | #define YYNOCODE 34 136 | #define YYACTIONTYPE unsigned char 137 | #define TOMLParserTOKENTYPE void* 138 | typedef union { 139 | int yyinit; 140 | TOMLParserTOKENTYPE yy0; 141 | TOMLBoolean * yy3; 142 | TOMLDate * yy4; 143 | TOMLNumber * yy8; 144 | TOMLBasic * yy13; 145 | TOMLArray * yy50; 146 | table_id_node * yy62; 147 | int yy67; 148 | } YYMINORTYPE; 149 | #ifndef YYSTACKDEPTH 150 | #define YYSTACKDEPTH 100 151 | #endif 152 | #define TOMLParserARG_SDECL TOMLParserState *state ; 153 | #define TOMLParserARG_PDECL , TOMLParserState *state 154 | #define TOMLParserARG_FETCH TOMLParserState *state = yypParser->state 155 | #define TOMLParserARG_STORE yypParser->state = state 156 | #define YYNSTATE 46 157 | #define YYNRULE 35 158 | #define YYERRORSYMBOL 14 159 | #define YYERRSYMDT yy67 160 | #define YY_NO_ACTION (YYNSTATE+YYNRULE+2) 161 | #define YY_ACCEPT_ACTION (YYNSTATE+YYNRULE+1) 162 | #define YY_ERROR_ACTION (YYNSTATE+YYNRULE) 163 | 164 | /* The yyzerominor constant is used to initialize instances of 165 | ** YYMINORTYPE objects to zero. */ 166 | static const YYMINORTYPE yyzerominor = { 0 }; 167 | 168 | /* Define the yytestcase() macro to be a no-op if is not already defined 169 | ** otherwise. 170 | ** 171 | ** Applications can choose to define yytestcase() in the %include section 172 | ** to a macro that can assist in verifying code coverage. For production 173 | ** code the yytestcase() macro should be turned off. But it is useful 174 | ** for testing. 175 | */ 176 | #ifndef yytestcase 177 | # define yytestcase(X) 178 | #endif 179 | 180 | 181 | /* Next are the tables used to determine what action to take based on the 182 | ** current state and lookahead token. These tables are used to implement 183 | ** functions that take a state number and lookahead value and return an 184 | ** action integer. 185 | ** 186 | ** Suppose the action integer is N. Then the action is determined as 187 | ** follows 188 | ** 189 | ** 0 <= N < YYNSTATE Shift N. That is, push the lookahead 190 | ** token onto the stack and goto state N. 191 | ** 192 | ** YYNSTATE <= N < YYNSTATE+YYNRULE Reduce by rule N-YYNSTATE. 193 | ** 194 | ** N == YYNSTATE+YYNRULE A syntax error has occurred. 195 | ** 196 | ** N == YYNSTATE+YYNRULE+1 The parser accepts its input. 197 | ** 198 | ** N == YYNSTATE+YYNRULE+2 No such action. Denotes unused 199 | ** slots in the yy_action[] table. 200 | ** 201 | ** The action table is constructed as a single large table named yy_action[]. 202 | ** Given state S and lookahead X, the action is computed as 203 | ** 204 | ** yy_action[ yy_shift_ofst[S] + X ] 205 | ** 206 | ** If the index value yy_shift_ofst[S]+X is out of range or if the value 207 | ** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X or if yy_shift_ofst[S] 208 | ** is equal to YY_SHIFT_USE_DFLT, it means that the action is not in the table 209 | ** and that yy_default[S] should be used instead. 210 | ** 211 | ** The formula above is for computing the action when the lookahead is 212 | ** a terminal symbol. If the lookahead is a non-terminal (as occurs after 213 | ** a reduce action) then the yy_reduce_ofst[] array is used in place of 214 | ** the yy_shift_ofst[] array and YY_REDUCE_USE_DFLT is used in place of 215 | ** YY_SHIFT_USE_DFLT. 216 | ** 217 | ** The following are the tables generated in this section: 218 | ** 219 | ** yy_action[] A single table containing all actions. 220 | ** yy_lookahead[] A table containing the lookahead for each entry in 221 | ** yy_action. Used to detect hash collisions. 222 | ** yy_shift_ofst[] For each state, the offset into yy_action for 223 | ** shifting terminals. 224 | ** yy_reduce_ofst[] For each state, the offset into yy_action for 225 | ** shifting non-terminals after a reduce. 226 | ** yy_default[] Default action for each state. 227 | */ 228 | static const YYACTIONTYPE yy_action[] = { 229 | /* 0 */ 42, 25, 26, 27, 28, 29, 15, 8, 82, 4, 230 | /* 10 */ 31, 33, 23, 22, 44, 10, 7, 1, 43, 11, 231 | /* 20 */ 17, 18, 9, 36, 37, 38, 39, 40, 24, 25, 232 | /* 30 */ 26, 27, 28, 29, 1, 46, 32, 20, 10, 9, 233 | /* 40 */ 36, 37, 38, 39, 40, 16, 45, 21, 35, 25, 234 | /* 50 */ 26, 27, 28, 29, 14, 12, 17, 10, 34, 33, 235 | /* 60 */ 23, 22, 2, 10, 7, 6, 13, 30, 5, 45, 236 | /* 70 */ 30, 5, 45, 83, 3, 45, 19, 41, 237 | }; 238 | static const YYCODETYPE yy_lookahead[] = { 239 | /* 0 */ 24, 25, 26, 27, 28, 29, 30, 31, 15, 16, 240 | /* 10 */ 17, 18, 19, 20, 14, 1, 23, 3, 14, 22, 241 | /* 20 */ 23, 4, 5, 9, 10, 11, 12, 13, 24, 25, 242 | /* 30 */ 26, 27, 28, 29, 3, 0, 4, 14, 1, 5, 243 | /* 40 */ 9, 10, 11, 12, 13, 23, 7, 14, 24, 25, 244 | /* 50 */ 26, 27, 28, 29, 21, 22, 23, 1, 17, 18, 245 | /* 60 */ 19, 20, 6, 1, 23, 3, 1, 2, 3, 7, 246 | /* 70 */ 2, 3, 7, 33, 32, 7, 4, 8, 247 | }; 248 | #define YY_SHIFT_USE_DFLT (-1) 249 | #define YY_SHIFT_MAX 15 250 | static const signed char yy_shift_ofst[] = { 251 | /* 0 */ 68, 31, 14, 31, 65, 62, 39, 56, 69, 39, 252 | /* 10 */ 37, 17, 34, 35, 72, 32, 253 | }; 254 | #define YY_REDUCE_USE_DFLT (-25) 255 | #define YY_REDUCE_MAX 10 256 | static const signed char yy_reduce_ofst[] = { 257 | /* 0 */ -7, -24, 4, 24, 41, 33, -3, 0, 42, 22, 258 | /* 10 */ 23, 259 | }; 260 | static const YYACTIONTYPE yy_default[] = { 261 | /* 0 */ 81, 67, 81, 69, 81, 81, 81, 81, 66, 81, 262 | /* 10 */ 81, 81, 55, 81, 81, 81, 56, 57, 54, 53, 263 | /* 20 */ 77, 78, 52, 51, 58, 60, 61, 62, 63, 64, 264 | /* 30 */ 50, 47, 65, 49, 48, 68, 72, 73, 74, 75, 265 | /* 40 */ 76, 71, 70, 79, 80, 59, 266 | }; 267 | #define YY_SZ_ACTTAB (int)(sizeof(yy_action)/sizeof(yy_action[0])) 268 | 269 | /* The next table maps tokens into fallback tokens. If a construct 270 | ** like the following: 271 | ** 272 | ** %fallback ID X Y Z. 273 | ** 274 | ** appears in the grammar, then ID becomes a fallback token for X, Y, 275 | ** and Z. Whenever one of the tokens X, Y, or Z is input to the parser 276 | ** but it does not parse, the type of the token is changed to ID and 277 | ** the parse is retried before an error is thrown. 278 | */ 279 | #ifdef YYFALLBACK 280 | static const YYCODETYPE yyFallback[] = { 281 | }; 282 | #endif /* YYFALLBACK */ 283 | 284 | /* The following structure represents a single element of the 285 | ** parser's stack. Information stored includes: 286 | ** 287 | ** + The state number for the parser at this level of the stack. 288 | ** 289 | ** + The value of the token stored at this level of the stack. 290 | ** (In other words, the "major" token.) 291 | ** 292 | ** + The semantic value stored at this level of the stack. This is 293 | ** the information used by the action routines in the grammar. 294 | ** It is sometimes called the "minor" token. 295 | */ 296 | struct yyStackEntry { 297 | YYACTIONTYPE stateno; /* The state-number */ 298 | YYCODETYPE major; /* The major token value. This is the code 299 | ** number for the token at this stack level */ 300 | YYMINORTYPE minor; /* The user-supplied minor token value. This 301 | ** is the value of the token */ 302 | }; 303 | typedef struct yyStackEntry yyStackEntry; 304 | 305 | /* The state of the parser is completely contained in an instance of 306 | ** the following structure */ 307 | struct yyParser { 308 | int yyidx; /* Index of top element in stack */ 309 | #ifdef YYTRACKMAXSTACKDEPTH 310 | int yyidxMax; /* Maximum value of yyidx */ 311 | #endif 312 | int yyerrcnt; /* Shifts left before out of the error */ 313 | TOMLParserARG_SDECL /* A place to hold %extra_argument */ 314 | #if YYSTACKDEPTH<=0 315 | int yystksz; /* Current side of the stack */ 316 | yyStackEntry *yystack; /* The parser's stack */ 317 | #else 318 | yyStackEntry yystack[YYSTACKDEPTH]; /* The parser's stack */ 319 | #endif 320 | }; 321 | typedef struct yyParser yyParser; 322 | 323 | #ifndef NDEBUG 324 | #include 325 | static FILE *yyTraceFILE = 0; 326 | static char *yyTracePrompt = 0; 327 | #endif /* NDEBUG */ 328 | 329 | #ifndef NDEBUG 330 | /* 331 | ** Turn parser tracing on by giving a stream to which to write the trace 332 | ** and a prompt to preface each trace message. Tracing is turned off 333 | ** by making either argument NULL 334 | ** 335 | ** Inputs: 336 | **
    337 | **
  • A FILE* to which trace output should be written. 338 | ** If NULL, then tracing is turned off. 339 | **
  • A prefix string written at the beginning of every 340 | ** line of trace output. If NULL, then tracing is 341 | ** turned off. 342 | **
343 | ** 344 | ** Outputs: 345 | ** None. 346 | */ 347 | void TOMLParserTrace(FILE *TraceFILE, char *zTracePrompt){ 348 | yyTraceFILE = TraceFILE; 349 | yyTracePrompt = zTracePrompt; 350 | if( yyTraceFILE==0 ) yyTracePrompt = 0; 351 | else if( yyTracePrompt==0 ) yyTraceFILE = 0; 352 | } 353 | #endif /* NDEBUG */ 354 | 355 | #ifndef NDEBUG 356 | /* For tracing shifts, the names of all terminals and nonterminals 357 | ** are required. The following table supplies these names */ 358 | static const char *const yyTokenName[] = { 359 | "$", "EOF", "COMMENT", "LEFT_SQUARE", 360 | "RIGHT_SQUARE", "ID_DOT", "EQ", "ID", 361 | "COMMA", "STRING", "NUMBER", "TRUE", 362 | "FALSE", "DATE", "error", "file", 363 | "line", "line_and_comment", "line_content", "table_header", 364 | "entry", "table_header_2", "table_id", "id", 365 | "value", "array", "string", "number", 366 | "boolean", "date", "members", "value_members", 367 | "comma", 368 | }; 369 | #endif /* NDEBUG */ 370 | 371 | #ifndef NDEBUG 372 | /* For tracing reduce actions, the names of all rules are required. 373 | */ 374 | static const char *const yyRuleName[] = { 375 | /* 0 */ "file ::= line EOF", 376 | /* 1 */ "line ::= line_and_comment", 377 | /* 2 */ "line ::= line line_and_comment", 378 | /* 3 */ "line_and_comment ::= line_content", 379 | /* 4 */ "line_and_comment ::= COMMENT", 380 | /* 5 */ "line_content ::= table_header", 381 | /* 6 */ "line_content ::= entry", 382 | /* 7 */ "table_header ::= LEFT_SQUARE table_header_2 RIGHT_SQUARE", 383 | /* 8 */ "table_header_2 ::= LEFT_SQUARE table_id RIGHT_SQUARE", 384 | /* 9 */ "table_header_2 ::= table_id", 385 | /* 10 */ "table_id ::= table_id ID_DOT id", 386 | /* 11 */ "table_id ::= id", 387 | /* 12 */ "entry ::= id EQ value", 388 | /* 13 */ "id ::= ID", 389 | /* 14 */ "value ::= array", 390 | /* 15 */ "value ::= string", 391 | /* 16 */ "value ::= number", 392 | /* 17 */ "value ::= boolean", 393 | /* 18 */ "value ::= date", 394 | /* 19 */ "array ::= LEFT_SQUARE members RIGHT_SQUARE", 395 | /* 20 */ "members ::= value_members", 396 | /* 21 */ "members ::=", 397 | /* 22 */ "value_members ::= value_members comma value", 398 | /* 23 */ "value_members ::= value_members comma", 399 | /* 24 */ "value_members ::= value", 400 | /* 25 */ "comma ::= COMMA", 401 | /* 26 */ "string ::= STRING", 402 | /* 27 */ "number ::= NUMBER", 403 | /* 28 */ "boolean ::= TRUE", 404 | /* 29 */ "boolean ::= FALSE", 405 | /* 30 */ "date ::= DATE", 406 | /* 31 */ "error ::= EOF error", 407 | /* 32 */ "table_header ::= LEFT_SQUARE error", 408 | /* 33 */ "entry ::= id EQ error", 409 | /* 34 */ "entry ::= id error", 410 | }; 411 | #endif /* NDEBUG */ 412 | 413 | 414 | #if YYSTACKDEPTH<=0 415 | /* 416 | ** Try to increase the size of the parser stack. 417 | */ 418 | static void yyGrowStack(yyParser *p){ 419 | int newSize; 420 | yyStackEntry *pNew; 421 | 422 | newSize = p->yystksz*2 + 100; 423 | pNew = realloc(p->yystack, newSize*sizeof(pNew[0])); 424 | if( pNew ){ 425 | p->yystack = pNew; 426 | p->yystksz = newSize; 427 | #ifndef NDEBUG 428 | if( yyTraceFILE ){ 429 | fprintf(yyTraceFILE,"%sStack grows to %d entries!\n", 430 | yyTracePrompt, p->yystksz); 431 | } 432 | #endif 433 | } 434 | } 435 | #endif 436 | 437 | /* 438 | ** This function allocates a new parser. 439 | ** The only argument is a pointer to a function which works like 440 | ** malloc. 441 | ** 442 | ** Inputs: 443 | ** A pointer to the function used to allocate memory. 444 | ** 445 | ** Outputs: 446 | ** A pointer to a parser. This pointer is used in subsequent calls 447 | ** to TOMLParser and TOMLParserFree. 448 | */ 449 | void *TOMLParserAlloc(void *(*mallocProc)(size_t)){ 450 | yyParser *pParser; 451 | pParser = (yyParser*)(*mallocProc)( (size_t)sizeof(yyParser) ); 452 | if( pParser ){ 453 | pParser->yyidx = -1; 454 | #ifdef YYTRACKMAXSTACKDEPTH 455 | pParser->yyidxMax = 0; 456 | #endif 457 | #if YYSTACKDEPTH<=0 458 | pParser->yystack = NULL; 459 | pParser->yystksz = 0; 460 | yyGrowStack(pParser); 461 | #endif 462 | } 463 | return pParser; 464 | } 465 | 466 | /* The following function deletes the value associated with a 467 | ** symbol. The symbol can be either a terminal or nonterminal. 468 | ** "yymajor" is the symbol code, and "yypminor" is a pointer to 469 | ** the value. 470 | */ 471 | static void yy_destructor( 472 | yyParser *yypParser, /* The parser */ 473 | YYCODETYPE yymajor, /* Type code for object to destroy */ 474 | YYMINORTYPE *yypminor /* The object to be destroyed */ 475 | ){ 476 | TOMLParserARG_FETCH; 477 | switch( yymajor ){ 478 | /* Here is inserted the actions which take place when a 479 | ** terminal or non-terminal is destroyed. This can happen 480 | ** when the symbol is popped from the stack during a 481 | ** reduce or during error processing or when a parser is 482 | ** being destroyed before it is finished parsing. 483 | ** 484 | ** Note: during a reduce, the only symbols destroyed are those 485 | ** which appear on the RHS of the rule, but which are not used 486 | ** inside the C code. 487 | */ 488 | /* TERMINAL Destructor */ 489 | case 1: /* EOF */ 490 | case 2: /* COMMENT */ 491 | case 3: /* LEFT_SQUARE */ 492 | case 4: /* RIGHT_SQUARE */ 493 | case 5: /* ID_DOT */ 494 | case 6: /* EQ */ 495 | case 7: /* ID */ 496 | case 8: /* COMMA */ 497 | case 9: /* STRING */ 498 | case 10: /* NUMBER */ 499 | case 11: /* TRUE */ 500 | case 12: /* FALSE */ 501 | case 13: /* DATE */ 502 | { 503 | #line 1 "toml-lemon.lemon" 504 | TOML_freeToken((yypminor->yy0)); 505 | #line 506 "toml-lemon.c" 506 | } 507 | break; 508 | default: break; /* If no destructor action specified: do nothing */ 509 | } 510 | } 511 | 512 | /* 513 | ** Pop the parser's stack once. 514 | ** 515 | ** If there is a destructor routine associated with the token which 516 | ** is popped from the stack, then call it. 517 | ** 518 | ** Return the major token number for the symbol popped. 519 | */ 520 | static int yy_pop_parser_stack(yyParser *pParser){ 521 | YYCODETYPE yymajor; 522 | yyStackEntry *yytos = &pParser->yystack[pParser->yyidx]; 523 | 524 | if( pParser->yyidx<0 ) return 0; 525 | #ifndef NDEBUG 526 | if( yyTraceFILE && pParser->yyidx>=0 ){ 527 | fprintf(yyTraceFILE,"%sPopping %s\n", 528 | yyTracePrompt, 529 | yyTokenName[yytos->major]); 530 | } 531 | #endif 532 | yymajor = yytos->major; 533 | yy_destructor(pParser, yymajor, &yytos->minor); 534 | pParser->yyidx--; 535 | return yymajor; 536 | } 537 | 538 | /* 539 | ** Deallocate and destroy a parser. Destructors are all called for 540 | ** all stack elements before shutting the parser down. 541 | ** 542 | ** Inputs: 543 | **
    544 | **
  • A pointer to the parser. This should be a pointer 545 | ** obtained from TOMLParserAlloc. 546 | **
  • A pointer to a function used to reclaim memory obtained 547 | ** from malloc. 548 | **
549 | */ 550 | void TOMLParserFree( 551 | void *p, /* The parser to be deleted */ 552 | void (*freeProc)(void*) /* Function used to reclaim memory */ 553 | ){ 554 | yyParser *pParser = (yyParser*)p; 555 | if( pParser==0 ) return; 556 | while( pParser->yyidx>=0 ) yy_pop_parser_stack(pParser); 557 | #if YYSTACKDEPTH<=0 558 | free(pParser->yystack); 559 | #endif 560 | (*freeProc)((void*)pParser); 561 | } 562 | 563 | /* 564 | ** Return the peak depth of the stack for a parser. 565 | */ 566 | #ifdef YYTRACKMAXSTACKDEPTH 567 | int TOMLParserStackPeak(void *p){ 568 | yyParser *pParser = (yyParser*)p; 569 | return pParser->yyidxMax; 570 | } 571 | #endif 572 | 573 | /* 574 | ** Find the appropriate action for a parser given the terminal 575 | ** look-ahead token iLookAhead. 576 | ** 577 | ** If the look-ahead token is YYNOCODE, then check to see if the action is 578 | ** independent of the look-ahead. If it is, return the action, otherwise 579 | ** return YY_NO_ACTION. 580 | */ 581 | static int yy_find_shift_action( 582 | yyParser *pParser, /* The parser */ 583 | YYCODETYPE iLookAhead /* The look-ahead token */ 584 | ){ 585 | int i; 586 | int stateno = pParser->yystack[pParser->yyidx].stateno; 587 | 588 | if( stateno>YY_SHIFT_MAX || (i = yy_shift_ofst[stateno])==YY_SHIFT_USE_DFLT ){ 589 | return yy_default[stateno]; 590 | } 591 | assert( iLookAhead!=YYNOCODE ); 592 | i += iLookAhead; 593 | if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){ 594 | if( iLookAhead>0 ){ 595 | #ifdef YYFALLBACK 596 | YYCODETYPE iFallback; /* Fallback token */ 597 | if( iLookAhead %s\n", 602 | yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]); 603 | } 604 | #endif 605 | return yy_find_shift_action(pParser, iFallback); 606 | } 607 | #endif 608 | #ifdef YYWILDCARD 609 | { 610 | int j = i - iLookAhead + YYWILDCARD; 611 | if( j>=0 && j %s\n", 615 | yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[YYWILDCARD]); 616 | } 617 | #endif /* NDEBUG */ 618 | return yy_action[j]; 619 | } 620 | } 621 | #endif /* YYWILDCARD */ 622 | } 623 | return yy_default[stateno]; 624 | }else{ 625 | return yy_action[i]; 626 | } 627 | } 628 | 629 | /* 630 | ** Find the appropriate action for a parser given the non-terminal 631 | ** look-ahead token iLookAhead. 632 | ** 633 | ** If the look-ahead token is YYNOCODE, then check to see if the action is 634 | ** independent of the look-ahead. If it is, return the action, otherwise 635 | ** return YY_NO_ACTION. 636 | */ 637 | static int yy_find_reduce_action( 638 | int stateno, /* Current state number */ 639 | YYCODETYPE iLookAhead /* The look-ahead token */ 640 | ){ 641 | int i; 642 | #ifdef YYERRORSYMBOL 643 | if( stateno>YY_REDUCE_MAX ){ 644 | return yy_default[stateno]; 645 | } 646 | #else 647 | assert( stateno<=YY_REDUCE_MAX ); 648 | #endif 649 | i = yy_reduce_ofst[stateno]; 650 | assert( i!=YY_REDUCE_USE_DFLT ); 651 | assert( iLookAhead!=YYNOCODE ); 652 | i += iLookAhead; 653 | #ifdef YYERRORSYMBOL 654 | if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){ 655 | return yy_default[stateno]; 656 | } 657 | #else 658 | assert( i>=0 && iyyidx--; 670 | #ifndef NDEBUG 671 | if( yyTraceFILE ){ 672 | fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt); 673 | } 674 | #endif 675 | while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); 676 | /* Here code is inserted which will execute if the parser 677 | ** stack every overflows */ 678 | TOMLParserARG_STORE; /* Suppress warning about unused %extra_argument var */ 679 | } 680 | 681 | /* 682 | ** Perform a shift action. 683 | */ 684 | static void yy_shift( 685 | yyParser *yypParser, /* The parser to be shifted */ 686 | int yyNewState, /* The new state to shift in */ 687 | int yyMajor, /* The major token to shift in */ 688 | YYMINORTYPE *yypMinor /* Pointer to the minor token to shift in */ 689 | ){ 690 | yyStackEntry *yytos; 691 | yypParser->yyidx++; 692 | #ifdef YYTRACKMAXSTACKDEPTH 693 | if( yypParser->yyidx>yypParser->yyidxMax ){ 694 | yypParser->yyidxMax = yypParser->yyidx; 695 | } 696 | #endif 697 | #if YYSTACKDEPTH>0 698 | if( yypParser->yyidx>=YYSTACKDEPTH ){ 699 | yyStackOverflow(yypParser, yypMinor); 700 | return; 701 | } 702 | #else 703 | if( yypParser->yyidx>=yypParser->yystksz ){ 704 | yyGrowStack(yypParser); 705 | if( yypParser->yyidx>=yypParser->yystksz ){ 706 | yyStackOverflow(yypParser, yypMinor); 707 | return; 708 | } 709 | } 710 | #endif 711 | yytos = &yypParser->yystack[yypParser->yyidx]; 712 | yytos->stateno = (YYACTIONTYPE)yyNewState; 713 | yytos->major = (YYCODETYPE)yyMajor; 714 | yytos->minor = *yypMinor; 715 | #ifndef NDEBUG 716 | if( yyTraceFILE && yypParser->yyidx>0 ){ 717 | int i; 718 | fprintf(yyTraceFILE,"%sShift %d\n",yyTracePrompt,yyNewState); 719 | fprintf(yyTraceFILE,"%sStack:",yyTracePrompt); 720 | for(i=1; i<=yypParser->yyidx; i++) 721 | fprintf(yyTraceFILE," %s",yyTokenName[yypParser->yystack[i].major]); 722 | fprintf(yyTraceFILE,"\n"); 723 | } 724 | #endif 725 | } 726 | 727 | /* The following table contains information about every rule that 728 | ** is used during the reduce. 729 | */ 730 | static const struct { 731 | YYCODETYPE lhs; /* Symbol on the left-hand side of the rule */ 732 | unsigned char nrhs; /* Number of right-hand side symbols in the rule */ 733 | } yyRuleInfo[] = { 734 | { 15, 2 }, 735 | { 16, 1 }, 736 | { 16, 2 }, 737 | { 17, 1 }, 738 | { 17, 1 }, 739 | { 18, 1 }, 740 | { 18, 1 }, 741 | { 19, 3 }, 742 | { 21, 3 }, 743 | { 21, 1 }, 744 | { 22, 3 }, 745 | { 22, 1 }, 746 | { 20, 3 }, 747 | { 23, 1 }, 748 | { 24, 1 }, 749 | { 24, 1 }, 750 | { 24, 1 }, 751 | { 24, 1 }, 752 | { 24, 1 }, 753 | { 25, 3 }, 754 | { 30, 1 }, 755 | { 30, 0 }, 756 | { 31, 3 }, 757 | { 31, 2 }, 758 | { 31, 1 }, 759 | { 32, 1 }, 760 | { 26, 1 }, 761 | { 27, 1 }, 762 | { 28, 1 }, 763 | { 28, 1 }, 764 | { 29, 1 }, 765 | { 14, 2 }, 766 | { 19, 2 }, 767 | { 20, 3 }, 768 | { 20, 2 }, 769 | }; 770 | 771 | static void yy_accept(yyParser*); /* Forward Declaration */ 772 | 773 | /* 774 | ** Perform a reduce action and the shift that must immediately 775 | ** follow the reduce. 776 | */ 777 | static void yy_reduce( 778 | yyParser *yypParser, /* The parser */ 779 | int yyruleno /* Number of the rule by which to reduce */ 780 | ){ 781 | int yygoto; /* The next state */ 782 | int yyact; /* The next action */ 783 | YYMINORTYPE yygotominor; /* The LHS of the rule reduced */ 784 | yyStackEntry *yymsp; /* The top of the parser's stack */ 785 | int yysize; /* Amount to pop the stack */ 786 | TOMLParserARG_FETCH; 787 | yymsp = &yypParser->yystack[yypParser->yyidx]; 788 | #ifndef NDEBUG 789 | if( yyTraceFILE && yyruleno>=0 790 | && yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ){ 791 | fprintf(yyTraceFILE, "%sReduce [%s].\n", yyTracePrompt, 792 | yyRuleName[yyruleno]); 793 | } 794 | #endif /* NDEBUG */ 795 | 796 | /* Silence complaints from purify about yygotominor being uninitialized 797 | ** in some cases when it is copied into the stack after the following 798 | ** switch. yygotominor is uninitialized when a rule reduces that does 799 | ** not set the value of its left-hand side nonterminal. Leaving the 800 | ** value of the nonterminal uninitialized is utterly harmless as long 801 | ** as the value is never used. So really the only thing this code 802 | ** accomplishes is to quieten purify. 803 | ** 804 | ** 2007-01-16: The wireshark project (www.wireshark.org) reports that 805 | ** without this code, their parser segfaults. I'm not sure what there 806 | ** parser is doing to make this happen. This is the second bug report 807 | ** from wireshark this week. Clearly they are stressing Lemon in ways 808 | ** that it has not been previously stressed... (SQLite ticket #2172) 809 | */ 810 | /*memset(&yygotominor, 0, sizeof(yygotominor));*/ 811 | yygotominor = yyzerominor; 812 | 813 | 814 | switch( yyruleno ){ 815 | /* Beginning here are the reduction cases. A typical example 816 | ** follows: 817 | ** case 0: 818 | ** #line 819 | ** { ... } // User supplied code 820 | ** #line 821 | ** break; 822 | */ 823 | case 0: /* file ::= line EOF */ 824 | #line 85 "toml-lemon.lemon" 825 | { 826 | yy_destructor(yypParser,1,&yymsp[0].minor); 827 | } 828 | #line 829 "toml-lemon.c" 829 | break; 830 | case 4: /* line_and_comment ::= COMMENT */ 831 | #line 89 "toml-lemon.lemon" 832 | { 833 | yy_destructor(yypParser,2,&yymsp[0].minor); 834 | } 835 | #line 836 "toml-lemon.c" 836 | break; 837 | case 7: /* table_header ::= LEFT_SQUARE table_header_2 RIGHT_SQUARE */ 838 | #line 93 "toml-lemon.lemon" 839 | { 840 | yy_destructor(yypParser,3,&yymsp[-2].minor); 841 | yy_destructor(yypParser,4,&yymsp[0].minor); 842 | } 843 | #line 844 "toml-lemon.c" 844 | break; 845 | case 8: /* table_header_2 ::= LEFT_SQUARE table_id RIGHT_SQUARE */ 846 | #line 95 "toml-lemon.lemon" 847 | { 848 | table_id_node *first = yymsp[-1].minor.yy62->first; 849 | table_id_node *node = first; 850 | table_id_node *next = node->next; 851 | TOMLTable *table = state->rootTable; 852 | 853 | for ( ; node; node = next ) { 854 | TOMLTable *tmpTable = TOMLTable_getKey( table, node->name ); 855 | TOMLBasic *tmpBasic = (TOMLBasic *) tmpTable; 856 | 857 | if ( tmpTable && tmpBasic->type == TOML_ARRAY && node->next ) { 858 | TOMLArray *tmpArray = (TOMLArray *) tmpBasic; 859 | tmpTable = TOMLArray_getIndex( tmpArray, tmpArray->size - 1 ); 860 | } 861 | 862 | if ( !tmpTable ) { 863 | TOMLRef nextValue; 864 | if ( node->next ) { 865 | nextValue = TOML_allocTable( NULL, NULL ); 866 | } else { 867 | nextValue = TOML_allocArray( TOML_TABLE, NULL ); 868 | } 869 | TOMLTable_setKey( table, node->name, nextValue ); 870 | tmpTable = nextValue; 871 | } 872 | table = tmpTable; 873 | next = node->next; 874 | free( node->name ); 875 | free( node ); 876 | } 877 | 878 | TOMLArray *array = (TOMLArray *) table; 879 | table = TOML_allocTable( NULL, NULL ); 880 | TOMLArray_append( array, table ); 881 | 882 | state->currentTable = table; 883 | yy_destructor(yypParser,3,&yymsp[-2].minor); 884 | yy_destructor(yypParser,4,&yymsp[0].minor); 885 | } 886 | #line 887 "toml-lemon.c" 887 | break; 888 | case 9: /* table_header_2 ::= table_id */ 889 | #line 132 "toml-lemon.lemon" 890 | { 891 | table_id_node *first = yymsp[0].minor.yy62->first; 892 | table_id_node *node = first; 893 | table_id_node *next = node->next; 894 | TOMLTable *table = state->rootTable; 895 | 896 | for ( ; node; node = next ) { 897 | TOMLTable *tmpTable = TOMLTable_getKey( table, node->name ); 898 | TOMLBasic *tmpBasic = (TOMLBasic *) tmpTable; 899 | 900 | if ( tmpTable && tmpBasic->type == TOML_ARRAY ) { 901 | TOMLArray *tmpArray = (TOMLArray *) tmpBasic; 902 | tmpTable = TOMLArray_getIndex( tmpArray, tmpArray->size - 1 ); 903 | } 904 | 905 | if ( tmpTable && node->next == NULL ) { 906 | _TOML_fillError( state->token, state, TOML_ERROR_TABLE_DEFINED ); 907 | } else if ( !tmpTable ) { 908 | tmpTable = TOML_allocTable( NULL, NULL ); 909 | TOMLTable_setKey( table, node->name, tmpTable ); 910 | } 911 | table = tmpTable; 912 | next = node->next; 913 | free( node->name ); 914 | free( node ); 915 | } 916 | 917 | state->currentTable = table; 918 | } 919 | #line 920 "toml-lemon.c" 920 | break; 921 | case 10: /* table_id ::= table_id ID_DOT id */ 922 | #line 163 "toml-lemon.lemon" 923 | { 924 | table_id_node *node = malloc( sizeof(table_id_node) ); 925 | node->name = yymsp[0].minor.yy0; 926 | node->first = yymsp[-2].minor.yy62->first; 927 | node->next = NULL; 928 | yymsp[-2].minor.yy62->next = node; 929 | yygotominor.yy62 = node; 930 | yy_destructor(yypParser,5,&yymsp[-1].minor); 931 | } 932 | #line 933 "toml-lemon.c" 933 | break; 934 | case 11: /* table_id ::= id */ 935 | #line 171 "toml-lemon.lemon" 936 | { 937 | table_id_node *node = malloc( sizeof(table_id_node) ); 938 | node->name = yymsp[0].minor.yy0; 939 | node->first = node; 940 | node->next = NULL; 941 | yygotominor.yy62 = node; 942 | } 943 | #line 944 "toml-lemon.c" 944 | break; 945 | case 12: /* entry ::= id EQ value */ 946 | #line 179 "toml-lemon.lemon" 947 | { 948 | if ( yymsp[-2].minor.yy0 != NULL || yymsp[0].minor.yy13 != NULL ) { 949 | TOMLRef oldValue = TOMLTable_getKey( state->currentTable, yymsp[-2].minor.yy0 ); 950 | if ( oldValue != NULL ) { 951 | _TOML_fillError( state->token, state, TOML_ERROR_ENTRY_DEFINED ); 952 | } else { 953 | TOMLTable_setKey( state->currentTable, yymsp[-2].minor.yy0, yymsp[0].minor.yy13 ); 954 | } 955 | } 956 | free( yymsp[-2].minor.yy0 ); 957 | yy_destructor(yypParser,6,&yymsp[-1].minor); 958 | } 959 | #line 960 "toml-lemon.c" 960 | break; 961 | case 13: /* id ::= ID */ 962 | #line 191 "toml-lemon.lemon" 963 | { 964 | yygotominor.yy0 = _TOML_newstr( yymsp[0].minor.yy0 ); 965 | } 966 | #line 967 "toml-lemon.c" 967 | break; 968 | case 14: /* value ::= array */ 969 | case 15: /* value ::= string */ yytestcase(yyruleno==15); 970 | #line 196 "toml-lemon.lemon" 971 | { yygotominor.yy13 = (TOMLBasic *) yymsp[0].minor.yy0; } 972 | #line 973 "toml-lemon.c" 973 | break; 974 | case 16: /* value ::= number */ 975 | #line 198 "toml-lemon.lemon" 976 | { yygotominor.yy13 = (TOMLBasic *) yymsp[0].minor.yy8; } 977 | #line 978 "toml-lemon.c" 978 | break; 979 | case 17: /* value ::= boolean */ 980 | #line 199 "toml-lemon.lemon" 981 | { yygotominor.yy13 = (TOMLBasic *) yymsp[0].minor.yy3; } 982 | #line 983 "toml-lemon.c" 983 | break; 984 | case 18: /* value ::= date */ 985 | #line 200 "toml-lemon.lemon" 986 | { yygotominor.yy13 = (TOMLBasic *) yymsp[0].minor.yy4; } 987 | #line 988 "toml-lemon.c" 988 | break; 989 | case 19: /* array ::= LEFT_SQUARE members RIGHT_SQUARE */ 990 | #line 202 "toml-lemon.lemon" 991 | { 992 | yygotominor.yy0 = yymsp[-1].minor.yy0; 993 | yy_destructor(yypParser,3,&yymsp[-2].minor); 994 | yy_destructor(yypParser,4,&yymsp[0].minor); 995 | } 996 | #line 997 "toml-lemon.c" 997 | break; 998 | case 20: /* members ::= value_members */ 999 | #line 205 "toml-lemon.lemon" 1000 | { yygotominor.yy0 = yymsp[0].minor.yy50; } 1001 | #line 1002 "toml-lemon.c" 1002 | break; 1003 | case 21: /* members ::= */ 1004 | #line 206 "toml-lemon.lemon" 1005 | { yygotominor.yy0 = TOML_allocArray( TOML_NOTYPE, NULL ); } 1006 | #line 1007 "toml-lemon.c" 1007 | break; 1008 | case 22: /* value_members ::= value_members comma value */ 1009 | #line 209 "toml-lemon.lemon" 1010 | { 1011 | if ( yymsp[-2].minor.yy50->memberType != yymsp[0].minor.yy13->type ) { 1012 | _TOML_fillError( state->token, state, TOML_ERROR_ARRAY_MEMBER_MISMATCH ); 1013 | } 1014 | yygotominor.yy50 = yymsp[-2].minor.yy50; 1015 | TOMLArray_append( yygotominor.yy50, yymsp[0].minor.yy13 ); 1016 | } 1017 | #line 1018 "toml-lemon.c" 1018 | break; 1019 | case 23: /* value_members ::= value_members comma */ 1020 | #line 216 "toml-lemon.lemon" 1021 | { 1022 | yygotominor.yy50 = yymsp[-1].minor.yy50; 1023 | } 1024 | #line 1025 "toml-lemon.c" 1025 | break; 1026 | case 24: /* value_members ::= value */ 1027 | #line 219 "toml-lemon.lemon" 1028 | { 1029 | yygotominor.yy50 = TOML_allocArray( yymsp[0].minor.yy13->type, yymsp[0].minor.yy13, NULL ); 1030 | } 1031 | #line 1032 "toml-lemon.c" 1032 | break; 1033 | case 25: /* comma ::= COMMA */ 1034 | #line 223 "toml-lemon.lemon" 1035 | { 1036 | yy_destructor(yypParser,8,&yymsp[0].minor); 1037 | } 1038 | #line 1039 "toml-lemon.c" 1039 | break; 1040 | case 26: /* string ::= STRING */ 1041 | #line 225 "toml-lemon.lemon" 1042 | { 1043 | TOMLToken *token = yymsp[0].minor.yy0; 1044 | int size = token->end - token->start; 1045 | 1046 | char *tmp = _TOML_newstr( token ); 1047 | 1048 | char *dest = malloc( size + 1 ); 1049 | strncpy( dest, tmp, size ); 1050 | dest[ size ] = 0; 1051 | 1052 | char *tmpCursor = tmp; 1053 | char *destCursor = dest; 1054 | 1055 | // replace \\b with \b (U+0008) 1056 | // replace \\t with \t (U+0009) 1057 | // replace \\n with \n (U+000A) 1058 | // replace \\f with \f (U+000C) 1059 | // replace \\r with \r (U+000D) 1060 | // replace \\\" with " (U+0022) 1061 | // replace \/ with / (U+002F) 1062 | // replace \\ with \ (U+005C) 1063 | // replace \\uxxxx with encoded character 1064 | while ( tmpCursor != NULL ) { 1065 | char *next = strchr( tmpCursor, '\\' ); 1066 | if ( next && next[1] ) { 1067 | char *nextDest = destCursor + ( (int) next - (int) tmpCursor ); 1068 | #define REPLACE( match, value ) \ 1069 | if ( next[1] == match ) { \ 1070 | *nextDest = value; \ 1071 | } 1072 | REPLACE( 'b', '\b' ) 1073 | else REPLACE( 't', '\t' ) 1074 | else REPLACE( 'f', '\f' ) 1075 | else REPLACE( 'n', '\n' ) 1076 | else REPLACE( 'r', '\r' ) 1077 | else REPLACE( '"', '"' ) 1078 | else REPLACE( '/', '/' ) 1079 | else REPLACE( '\\', '\\' ) 1080 | #undef REPLACE 1081 | else if ( next[1] == 'u' ) { 1082 | int num = 0; 1083 | sscanf( next + 2, "%04x", &num ); 1084 | int chsize = 0; 1085 | 1086 | // Number is in normal ascii range. 1087 | if ( num < 0x80 ) { 1088 | nextDest[0] = num; // Up to 0x7f 1089 | chsize = 1; 1090 | // Split the value into 2 or 3 chars as utf8. 1091 | } else if ( num < 0x800 ) { 1092 | nextDest[0] = 0xc0 | ( ( num >> 6 ) & 0x1f ); 1093 | nextDest[1] = 0x80 | ( num & 0x3f ); 1094 | chsize = 2; 1095 | } else { 1096 | nextDest[0] = 0xe0 | ( ( num >> 12 ) & 0x0f ); 1097 | nextDest[1] = 0x80 | ( ( num >> 6 ) & 0x3f ); 1098 | nextDest[2] = 0x80 | ( num & 0x3f ); 1099 | chsize = 3; 1100 | } 1101 | 1102 | next += 4; 1103 | nextDest += chsize - 1; 1104 | size -= 5 - chsize; 1105 | } 1106 | 1107 | size--; 1108 | strcpy( nextDest + 1, next + 2 ); 1109 | tmpCursor = next + 2; 1110 | destCursor = nextDest + 1; 1111 | } else { 1112 | tmpCursor = next; 1113 | } 1114 | } 1115 | 1116 | yygotominor.yy0 = TOML_allocStringN( dest + 1, size - 2 ); 1117 | 1118 | free( dest ); 1119 | free( tmp ); 1120 | } 1121 | #line 1122 "toml-lemon.c" 1122 | break; 1123 | case 27: /* number ::= NUMBER */ 1124 | #line 306 "toml-lemon.lemon" 1125 | { 1126 | char *tmp = _TOML_newstr( yymsp[0].minor.yy0 ); 1127 | 1128 | if ( strchr( tmp, '.' ) != NULL ) { 1129 | yygotominor.yy8 = TOML_allocDouble( atof( tmp ) ); 1130 | } else { 1131 | yygotominor.yy8 = TOML_allocInt( atoi( tmp ) ); 1132 | } 1133 | 1134 | free( tmp ); 1135 | } 1136 | #line 1137 "toml-lemon.c" 1137 | break; 1138 | case 28: /* boolean ::= TRUE */ 1139 | #line 319 "toml-lemon.lemon" 1140 | { 1141 | yygotominor.yy3 = TOML_allocBoolean( 1 ); 1142 | yy_destructor(yypParser,11,&yymsp[0].minor); 1143 | } 1144 | #line 1145 "toml-lemon.c" 1145 | break; 1146 | case 29: /* boolean ::= FALSE */ 1147 | #line 323 "toml-lemon.lemon" 1148 | { 1149 | yygotominor.yy3 = TOML_allocBoolean( 0 ); 1150 | yy_destructor(yypParser,12,&yymsp[0].minor); 1151 | } 1152 | #line 1153 "toml-lemon.c" 1153 | break; 1154 | case 30: /* date ::= DATE */ 1155 | #line 328 "toml-lemon.lemon" 1156 | { 1157 | int year; 1158 | int month; 1159 | int day; 1160 | int hour; 1161 | int minute; 1162 | int second; 1163 | sscanf( 1164 | ((TOMLToken *) yymsp[0].minor.yy0)->tokenStr, 1165 | "%d-%d-%dT%d:%d:%dZ", 1166 | &year, &month, &day, &hour, &minute, &second 1167 | ); 1168 | yygotominor.yy4 = TOML_allocDate( year, month, day, hour, minute, second ); 1169 | } 1170 | #line 1171 "toml-lemon.c" 1171 | break; 1172 | case 31: /* error ::= EOF error */ 1173 | #line 347 "toml-lemon.lemon" 1174 | { yygotominor.yy67 = yymsp[0].minor.yy67; yy_destructor(yypParser,1,&yymsp[-1].minor); 1175 | } 1176 | #line 1177 "toml-lemon.c" 1177 | break; 1178 | case 32: /* table_header ::= LEFT_SQUARE error */ 1179 | #line 349 "toml-lemon.lemon" 1180 | { 1181 | _TOML_fillError( yymsp[-1].minor.yy0, state, TOML_ERROR_INVALID_HEADER ); 1182 | } 1183 | #line 1184 "toml-lemon.c" 1184 | break; 1185 | case 33: /* entry ::= id EQ error */ 1186 | #line 353 "toml-lemon.lemon" 1187 | { 1188 | _TOML_fillError( state->token, state, TOML_ERROR_NO_VALUE ); 1189 | free( yymsp[-2].minor.yy0 ); 1190 | yy_destructor(yypParser,6,&yymsp[-1].minor); 1191 | } 1192 | #line 1193 "toml-lemon.c" 1193 | break; 1194 | case 34: /* entry ::= id error */ 1195 | #line 358 "toml-lemon.lemon" 1196 | { 1197 | _TOML_fillError( state->token, state, TOML_ERROR_NO_EQ ); 1198 | free( yymsp[-1].minor.yy0 ); 1199 | } 1200 | #line 1201 "toml-lemon.c" 1201 | break; 1202 | default: 1203 | /* (1) line ::= line_and_comment */ yytestcase(yyruleno==1); 1204 | /* (2) line ::= line line_and_comment */ yytestcase(yyruleno==2); 1205 | /* (3) line_and_comment ::= line_content */ yytestcase(yyruleno==3); 1206 | /* (5) line_content ::= table_header */ yytestcase(yyruleno==5); 1207 | /* (6) line_content ::= entry */ yytestcase(yyruleno==6); 1208 | break; 1209 | }; 1210 | yygoto = yyRuleInfo[yyruleno].lhs; 1211 | yysize = yyRuleInfo[yyruleno].nrhs; 1212 | yypParser->yyidx -= yysize; 1213 | yyact = yy_find_reduce_action(yymsp[-yysize].stateno,(YYCODETYPE)yygoto); 1214 | if( yyact < YYNSTATE ){ 1215 | #ifdef NDEBUG 1216 | /* If we are not debugging and the reduce action popped at least 1217 | ** one element off the stack, then we can push the new element back 1218 | ** onto the stack here, and skip the stack overflow test in yy_shift(). 1219 | ** That gives a significant speed improvement. */ 1220 | if( yysize ){ 1221 | yypParser->yyidx++; 1222 | yymsp -= yysize-1; 1223 | yymsp->stateno = (YYACTIONTYPE)yyact; 1224 | yymsp->major = (YYCODETYPE)yygoto; 1225 | yymsp->minor = yygotominor; 1226 | }else 1227 | #endif 1228 | { 1229 | yy_shift(yypParser,yyact,yygoto,&yygotominor); 1230 | } 1231 | }else{ 1232 | assert( yyact == YYNSTATE + YYNRULE + 1 ); 1233 | yy_accept(yypParser); 1234 | } 1235 | } 1236 | 1237 | /* 1238 | ** The following code executes when the parse fails 1239 | */ 1240 | #ifndef YYNOERRORRECOVERY 1241 | static void yy_parse_failed( 1242 | yyParser *yypParser /* The parser */ 1243 | ){ 1244 | TOMLParserARG_FETCH; 1245 | #ifndef NDEBUG 1246 | if( yyTraceFILE ){ 1247 | fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt); 1248 | } 1249 | #endif 1250 | while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); 1251 | /* Here code is inserted which will be executed whenever the 1252 | ** parser fails */ 1253 | #line 3 "toml-lemon.lemon" 1254 | _TOML_fillError( state->token, state, TOML_ERROR_FATAL ); 1255 | #line 1256 "toml-lemon.c" 1256 | TOMLParserARG_STORE; /* Suppress warning about unused %extra_argument variable */ 1257 | } 1258 | #endif /* YYNOERRORRECOVERY */ 1259 | 1260 | /* 1261 | ** The following code executes when a syntax error first occurs. 1262 | */ 1263 | static void yy_syntax_error( 1264 | yyParser *yypParser, /* The parser */ 1265 | int yymajor, /* The major type of the error token */ 1266 | YYMINORTYPE yyminor /* The minor type of the error token */ 1267 | ){ 1268 | TOMLParserARG_FETCH; 1269 | #define TOKEN (yyminor.yy0) 1270 | TOMLParserARG_STORE; /* Suppress warning about unused %extra_argument variable */ 1271 | } 1272 | 1273 | /* 1274 | ** The following is executed when the parser accepts 1275 | */ 1276 | static void yy_accept( 1277 | yyParser *yypParser /* The parser */ 1278 | ){ 1279 | TOMLParserARG_FETCH; 1280 | #ifndef NDEBUG 1281 | if( yyTraceFILE ){ 1282 | fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt); 1283 | } 1284 | #endif 1285 | while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); 1286 | /* Here code is inserted which will be executed whenever the 1287 | ** parser accepts */ 1288 | TOMLParserARG_STORE; /* Suppress warning about unused %extra_argument variable */ 1289 | } 1290 | 1291 | /* The main parser program. 1292 | ** The first argument is a pointer to a structure obtained from 1293 | ** "TOMLParserAlloc" which describes the current state of the parser. 1294 | ** The second argument is the major token number. The third is 1295 | ** the minor token. The fourth optional argument is whatever the 1296 | ** user wants (and specified in the grammar) and is available for 1297 | ** use by the action routines. 1298 | ** 1299 | ** Inputs: 1300 | **
    1301 | **
  • A pointer to the parser (an opaque structure.) 1302 | **
  • The major token number. 1303 | **
  • The minor token number. 1304 | **
  • An option argument of a grammar-specified type. 1305 | **
1306 | ** 1307 | ** Outputs: 1308 | ** None. 1309 | */ 1310 | void TOMLParser( 1311 | void *yyp, /* The parser */ 1312 | int yymajor, /* The major token code number */ 1313 | TOMLParserTOKENTYPE yyminor /* The value for the token */ 1314 | TOMLParserARG_PDECL /* Optional %extra_argument parameter */ 1315 | ){ 1316 | YYMINORTYPE yyminorunion; 1317 | int yyact; /* The parser action. */ 1318 | int yyendofinput; /* True if we are at the end of input */ 1319 | #ifdef YYERRORSYMBOL 1320 | int yyerrorhit = 0; /* True if yymajor has invoked an error */ 1321 | #endif 1322 | yyParser *yypParser; /* The parser */ 1323 | 1324 | /* (re)initialize the parser, if necessary */ 1325 | yypParser = (yyParser*)yyp; 1326 | if( yypParser->yyidx<0 ){ 1327 | #if YYSTACKDEPTH<=0 1328 | if( yypParser->yystksz <=0 ){ 1329 | /*memset(&yyminorunion, 0, sizeof(yyminorunion));*/ 1330 | yyminorunion = yyzerominor; 1331 | yyStackOverflow(yypParser, &yyminorunion); 1332 | return; 1333 | } 1334 | #endif 1335 | yypParser->yyidx = 0; 1336 | yypParser->yyerrcnt = -1; 1337 | yypParser->yystack[0].stateno = 0; 1338 | yypParser->yystack[0].major = 0; 1339 | } 1340 | yyminorunion.yy0 = yyminor; 1341 | yyendofinput = (yymajor==0); 1342 | TOMLParserARG_STORE; 1343 | 1344 | #ifndef NDEBUG 1345 | if( yyTraceFILE ){ 1346 | fprintf(yyTraceFILE,"%sInput %s\n",yyTracePrompt,yyTokenName[yymajor]); 1347 | } 1348 | #endif 1349 | 1350 | do{ 1351 | yyact = yy_find_shift_action(yypParser,(YYCODETYPE)yymajor); 1352 | if( yyactyyerrcnt--; 1356 | yymajor = YYNOCODE; 1357 | }else if( yyact < YYNSTATE + YYNRULE ){ 1358 | yy_reduce(yypParser,yyact-YYNSTATE); 1359 | }else{ 1360 | assert( yyact == YY_ERROR_ACTION ); 1361 | #ifdef YYERRORSYMBOL 1362 | int yymx; 1363 | #endif 1364 | #ifndef NDEBUG 1365 | if( yyTraceFILE ){ 1366 | fprintf(yyTraceFILE,"%sSyntax Error!\n",yyTracePrompt); 1367 | } 1368 | #endif 1369 | #ifdef YYERRORSYMBOL 1370 | /* A syntax error has occurred. 1371 | ** The response to an error depends upon whether or not the 1372 | ** grammar defines an error token "ERROR". 1373 | ** 1374 | ** This is what we do if the grammar does define ERROR: 1375 | ** 1376 | ** * Call the %syntax_error function. 1377 | ** 1378 | ** * Begin popping the stack until we enter a state where 1379 | ** it is legal to shift the error symbol, then shift 1380 | ** the error symbol. 1381 | ** 1382 | ** * Set the error count to three. 1383 | ** 1384 | ** * Begin accepting and shifting new tokens. No new error 1385 | ** processing will occur until three tokens have been 1386 | ** shifted successfully. 1387 | ** 1388 | */ 1389 | if( yypParser->yyerrcnt<0 ){ 1390 | yy_syntax_error(yypParser,yymajor,yyminorunion); 1391 | } 1392 | yymx = yypParser->yystack[yypParser->yyidx].major; 1393 | if( yymx==YYERRORSYMBOL || yyerrorhit ){ 1394 | #ifndef NDEBUG 1395 | if( yyTraceFILE ){ 1396 | fprintf(yyTraceFILE,"%sDiscard input token %s\n", 1397 | yyTracePrompt,yyTokenName[yymajor]); 1398 | } 1399 | #endif 1400 | yy_destructor(yypParser, (YYCODETYPE)yymajor,&yyminorunion); 1401 | yymajor = YYNOCODE; 1402 | }else{ 1403 | while( 1404 | yypParser->yyidx >= 0 && 1405 | yymx != YYERRORSYMBOL && 1406 | (yyact = yy_find_reduce_action( 1407 | yypParser->yystack[yypParser->yyidx].stateno, 1408 | YYERRORSYMBOL)) >= YYNSTATE 1409 | ){ 1410 | yy_pop_parser_stack(yypParser); 1411 | } 1412 | if( yypParser->yyidx < 0 || yymajor==0 ){ 1413 | yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); 1414 | yy_parse_failed(yypParser); 1415 | yymajor = YYNOCODE; 1416 | }else if( yymx!=YYERRORSYMBOL ){ 1417 | YYMINORTYPE u2; 1418 | u2.YYERRSYMDT = 0; 1419 | yy_shift(yypParser,yyact,YYERRORSYMBOL,&u2); 1420 | } 1421 | } 1422 | yypParser->yyerrcnt = 3; 1423 | yyerrorhit = 1; 1424 | #elif defined(YYNOERRORRECOVERY) 1425 | /* If the YYNOERRORRECOVERY macro is defined, then do not attempt to 1426 | ** do any kind of error recovery. Instead, simply invoke the syntax 1427 | ** error routine and continue going as if nothing had happened. 1428 | ** 1429 | ** Applications can set this macro (for example inside %include) if 1430 | ** they intend to abandon the parse upon the first syntax error seen. 1431 | */ 1432 | yy_syntax_error(yypParser,yymajor,yyminorunion); 1433 | yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); 1434 | yymajor = YYNOCODE; 1435 | 1436 | #else /* YYERRORSYMBOL is not defined */ 1437 | /* This is what we do if the grammar does not define ERROR: 1438 | ** 1439 | ** * Report an error message, and throw away the input token. 1440 | ** 1441 | ** * If the input token is $, then fail the parse. 1442 | ** 1443 | ** As before, subsequent error messages are suppressed until 1444 | ** three input tokens have been successfully shifted. 1445 | */ 1446 | if( yypParser->yyerrcnt<=0 ){ 1447 | yy_syntax_error(yypParser,yymajor,yyminorunion); 1448 | } 1449 | yypParser->yyerrcnt = 3; 1450 | yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); 1451 | if( yyendofinput ){ 1452 | yy_parse_failed(yypParser); 1453 | } 1454 | yymajor = YYNOCODE; 1455 | #endif 1456 | } 1457 | }while( yymajor!=YYNOCODE && yypParser->yyidx>=0 ); 1458 | return; 1459 | } 1460 | -------------------------------------------------------------------------------- /toml-lemon.h: -------------------------------------------------------------------------------- 1 | #define EOF 1 2 | #define COMMENT 2 3 | #define LEFT_SQUARE 3 4 | #define RIGHT_SQUARE 4 5 | #define ID_DOT 5 6 | #define EQ 6 7 | #define ID 7 8 | #define COMMA 8 9 | #define STRING 9 10 | #define NUMBER 10 11 | #define TRUE 11 12 | #define FALSE 12 13 | #define DATE 13 14 | -------------------------------------------------------------------------------- /toml-lemon.lemon: -------------------------------------------------------------------------------- 1 | %token_destructor { TOML_freeToken($$); } 2 | %extra_argument { TOMLParserState *state } 3 | %parse_failure { _TOML_fillError( state->token, state, TOML_ERROR_FATAL ); } 4 | 5 | %include { 6 | #include 7 | #include 8 | #include "toml-parser.h" 9 | 10 | typedef struct table_id_node { 11 | char *name; 12 | struct table_id_node *first; 13 | struct table_id_node *next; 14 | } table_id_node; 15 | 16 | char * _TOML_newstr( TOMLToken *token ) { 17 | if ( token->tokenStr ) { 18 | char *buffer = token->tokenStr; 19 | token->tokenStr = NULL; 20 | return buffer; 21 | } 22 | 23 | int size = token->end - token->start; 24 | char *buffer = malloc( size + 1 ); 25 | strncpy( buffer, token->start, size ); 26 | buffer[ size ] = 0; 27 | return buffer; 28 | } 29 | 30 | char * _TOML_getline( TOMLToken *token ) { 31 | char *endOfLine = strchr( token->lineStart, '\n' ); 32 | if ( endOfLine == NULL ) { 33 | endOfLine = strchr( token->lineStart, 0 ); 34 | } 35 | 36 | int size = endOfLine - token->lineStart; 37 | char *buffer = malloc( size + 1 ); 38 | strncpy( buffer, token->lineStart, size ); 39 | buffer[ size ] = 0; 40 | 41 | return buffer; 42 | } 43 | 44 | void _TOML_fillError( 45 | TOMLToken *token, TOMLParserState *state, int errorCode 46 | ) { 47 | state->errorCode = errorCode; 48 | 49 | TOMLError *error = state->errorObj; 50 | if ( error ) { 51 | error->code = errorCode; 52 | error->lineNo = token->line; 53 | error->line = _TOML_getline( state->token ); 54 | 55 | int messageSize = strlen( TOMLErrorDescription[ errorCode ] ); 56 | error->message = malloc( messageSize + 1 ); 57 | strncpy( error->message, TOMLErrorDescription[ errorCode ], messageSize ); 58 | error->message[ messageSize ] = 0; 59 | 60 | char *longMessage = malloc( 61 | strlen( error->line ) + 62 | strlen( error->message ) + 63 | (int) ( error->lineNo / 10 ) + 64 | 20 65 | ); 66 | sprintf( 67 | longMessage, 68 | "Error on line %d. %s: %s", 69 | error->lineNo, 70 | error->message, 71 | error->line 72 | ); 73 | error->fullDescription = longMessage; 74 | } 75 | } 76 | 77 | void TOML_freeToken( TOMLToken *token ) { 78 | free( token->tokenStr ); 79 | free( token ); 80 | } 81 | } 82 | 83 | %name TOMLParser 84 | 85 | file ::= line EOF . 86 | line ::= line_and_comment . 87 | line ::= line line_and_comment . 88 | line_and_comment ::= line_content . 89 | line_and_comment ::= COMMENT . 90 | line_content ::= table_header . 91 | line_content ::= entry . 92 | 93 | table_header ::= LEFT_SQUARE table_header_2 RIGHT_SQUARE . 94 | 95 | table_header_2 ::= LEFT_SQUARE table_id(TABLE_ID) RIGHT_SQUARE . { 96 | table_id_node *first = TABLE_ID->first; 97 | table_id_node *node = first; 98 | table_id_node *next = node->next; 99 | TOMLTable *table = state->rootTable; 100 | 101 | for ( ; node; node = next ) { 102 | TOMLTable *tmpTable = TOMLTable_getKey( table, node->name ); 103 | TOMLBasic *tmpBasic = (TOMLBasic *) tmpTable; 104 | 105 | if ( tmpTable && tmpBasic->type == TOML_ARRAY && node->next ) { 106 | TOMLArray *tmpArray = (TOMLArray *) tmpBasic; 107 | tmpTable = TOMLArray_getIndex( tmpArray, tmpArray->size - 1 ); 108 | } 109 | 110 | if ( !tmpTable ) { 111 | TOMLRef nextValue; 112 | if ( node->next ) { 113 | nextValue = TOML_allocTable( NULL, NULL ); 114 | } else { 115 | nextValue = TOML_allocArray( TOML_TABLE, NULL ); 116 | } 117 | TOMLTable_setKey( table, node->name, nextValue ); 118 | tmpTable = nextValue; 119 | } 120 | table = tmpTable; 121 | next = node->next; 122 | free( node->name ); 123 | free( node ); 124 | } 125 | 126 | TOMLArray *array = (TOMLArray *) table; 127 | table = TOML_allocTable( NULL, NULL ); 128 | TOMLArray_append( array, table ); 129 | 130 | state->currentTable = table; 131 | } 132 | table_header_2 ::= table_id(TABLE_ID) . { 133 | table_id_node *first = TABLE_ID->first; 134 | table_id_node *node = first; 135 | table_id_node *next = node->next; 136 | TOMLTable *table = state->rootTable; 137 | 138 | for ( ; node; node = next ) { 139 | TOMLTable *tmpTable = TOMLTable_getKey( table, node->name ); 140 | TOMLBasic *tmpBasic = (TOMLBasic *) tmpTable; 141 | 142 | if ( tmpTable && tmpBasic->type == TOML_ARRAY ) { 143 | TOMLArray *tmpArray = (TOMLArray *) tmpBasic; 144 | tmpTable = TOMLArray_getIndex( tmpArray, tmpArray->size - 1 ); 145 | } 146 | 147 | if ( tmpTable && node->next == NULL ) { 148 | _TOML_fillError( state->token, state, TOML_ERROR_TABLE_DEFINED ); 149 | } else if ( !tmpTable ) { 150 | tmpTable = TOML_allocTable( NULL, NULL ); 151 | TOMLTable_setKey( table, node->name, tmpTable ); 152 | } 153 | table = tmpTable; 154 | next = node->next; 155 | free( node->name ); 156 | free( node ); 157 | } 158 | 159 | state->currentTable = table; 160 | } 161 | 162 | %type table_id { table_id_node * } 163 | table_id(TABLE_ID) ::= table_id(LAST_ID) ID_DOT id(ID) . { 164 | table_id_node *node = malloc( sizeof(table_id_node) ); 165 | node->name = ID; 166 | node->first = LAST_ID->first; 167 | node->next = NULL; 168 | LAST_ID->next = node; 169 | TABLE_ID = node; 170 | } 171 | table_id(TABLE_ID) ::= id(ID) . { 172 | table_id_node *node = malloc( sizeof(table_id_node) ); 173 | node->name = ID; 174 | node->first = node; 175 | node->next = NULL; 176 | TABLE_ID = node; 177 | } 178 | 179 | entry ::= id(ID) EQ value(VALUE) . { 180 | if ( ID != NULL || VALUE != NULL ) { 181 | TOMLRef oldValue = TOMLTable_getKey( state->currentTable, ID ); 182 | if ( oldValue != NULL ) { 183 | _TOML_fillError( state->token, state, TOML_ERROR_ENTRY_DEFINED ); 184 | } else { 185 | TOMLTable_setKey( state->currentTable, ID, VALUE ); 186 | } 187 | } 188 | free( ID ); 189 | } 190 | 191 | id(ID) ::= ID(TOKEN) . { 192 | ID = _TOML_newstr( TOKEN ); 193 | } 194 | 195 | %type value { TOMLBasic * } 196 | value(VALUE) ::= array(ARRAY) . { VALUE = (TOMLBasic *) ARRAY; } 197 | value(VALUE) ::= string(STRING) . { VALUE = (TOMLBasic *) STRING; } 198 | value(VALUE) ::= number(NUMBER) . { VALUE = (TOMLBasic *) NUMBER; } 199 | value(VALUE) ::= boolean(BOOLEAN) . { VALUE = (TOMLBasic *) BOOLEAN; } 200 | value(VALUE) ::= date(DATE) . { VALUE = (TOMLBasic *) DATE; } 201 | 202 | array(ARY) ::= LEFT_SQUARE members(OLD_ARY) RIGHT_SQUARE . { 203 | ARY = OLD_ARY; 204 | } 205 | members(ARY) ::= value_members(OLD_ARY) . { ARY = OLD_ARY; } 206 | members(ARY) ::= . { ARY = TOML_allocArray( TOML_NOTYPE, NULL ); } 207 | 208 | %type value_members { TOMLArray * } 209 | value_members(NEW_ARY) ::= value_members(OLD_ARY) comma value(VALUE) . { 210 | if ( OLD_ARY->memberType != VALUE->type ) { 211 | _TOML_fillError( state->token, state, TOML_ERROR_ARRAY_MEMBER_MISMATCH ); 212 | } 213 | NEW_ARY = OLD_ARY; 214 | TOMLArray_append( NEW_ARY, VALUE ); 215 | } 216 | value_members(NEW_ARY) ::= value_members(OLD_ARY) comma . { 217 | NEW_ARY = OLD_ARY; 218 | } 219 | value_members(ARY) ::= value(VALUE) . { 220 | ARY = TOML_allocArray( VALUE->type, VALUE, NULL ); 221 | } 222 | 223 | comma ::= COMMA . 224 | 225 | string(STR) ::= STRING(STR_TOKEN) . { 226 | TOMLToken *token = STR_TOKEN; 227 | int size = token->end - token->start; 228 | 229 | char *tmp = _TOML_newstr( token ); 230 | 231 | char *dest = malloc( size + 1 ); 232 | strncpy( dest, tmp, size ); 233 | dest[ size ] = 0; 234 | 235 | char *tmpCursor = tmp; 236 | char *destCursor = dest; 237 | 238 | // replace \\b with \b (U+0008) 239 | // replace \\t with \t (U+0009) 240 | // replace \\n with \n (U+000A) 241 | // replace \\f with \f (U+000C) 242 | // replace \\r with \r (U+000D) 243 | // replace \\\" with " (U+0022) 244 | // replace \/ with / (U+002F) 245 | // replace \\ with \ (U+005C) 246 | // replace \\uxxxx with encoded character 247 | while ( tmpCursor != NULL ) { 248 | char *next = strchr( tmpCursor, '\\' ); 249 | if ( next && next[1] ) { 250 | char *nextDest = destCursor + ( (int) next - (int) tmpCursor ); 251 | #define REPLACE( match, value ) \ 252 | if ( next[1] == match ) { \ 253 | *nextDest = value; \ 254 | } 255 | REPLACE( 'b', '\b' ) 256 | else REPLACE( 't', '\t' ) 257 | else REPLACE( 'f', '\f' ) 258 | else REPLACE( 'n', '\n' ) 259 | else REPLACE( 'r', '\r' ) 260 | else REPLACE( '"', '"' ) 261 | else REPLACE( '/', '/' ) 262 | else REPLACE( '\\', '\\' ) 263 | #undef REPLACE 264 | else if ( next[1] == 'u' ) { 265 | int num = 0; 266 | sscanf( next + 2, "%04x", &num ); 267 | int chsize = 0; 268 | 269 | // Number is in normal ascii range. 270 | if ( num < 0x80 ) { 271 | nextDest[0] = num; // Up to 0x7f 272 | chsize = 1; 273 | // Split the value into 2 or 3 chars as utf8. 274 | } else if ( num < 0x800 ) { 275 | nextDest[0] = 0xc0 | ( ( num >> 6 ) & 0x1f ); 276 | nextDest[1] = 0x80 | ( num & 0x3f ); 277 | chsize = 2; 278 | } else { 279 | nextDest[0] = 0xe0 | ( ( num >> 12 ) & 0x0f ); 280 | nextDest[1] = 0x80 | ( ( num >> 6 ) & 0x3f ); 281 | nextDest[2] = 0x80 | ( num & 0x3f ); 282 | chsize = 3; 283 | } 284 | 285 | next += 4; 286 | nextDest += chsize - 1; 287 | size -= 5 - chsize; 288 | } 289 | 290 | size--; 291 | strcpy( nextDest + 1, next + 2 ); 292 | tmpCursor = next + 2; 293 | destCursor = nextDest + 1; 294 | } else { 295 | tmpCursor = next; 296 | } 297 | } 298 | 299 | STR = TOML_allocStringN( dest + 1, size - 2 ); 300 | 301 | free( dest ); 302 | free( tmp ); 303 | } 304 | 305 | %type number { TOMLNumber * } 306 | number(NUMBER) ::= NUMBER(NUMBER_TOKEN) . { 307 | char *tmp = _TOML_newstr( NUMBER_TOKEN ); 308 | 309 | if ( strchr( tmp, '.' ) != NULL ) { 310 | NUMBER = TOML_allocDouble( atof( tmp ) ); 311 | } else { 312 | NUMBER = TOML_allocInt( atoi( tmp ) ); 313 | } 314 | 315 | free( tmp ); 316 | } 317 | 318 | %type boolean { TOMLBoolean * } 319 | boolean(BOOLEAN) ::= TRUE . { 320 | BOOLEAN = TOML_allocBoolean( 1 ); 321 | } 322 | 323 | boolean(BOOLEAN) ::= FALSE . { 324 | BOOLEAN = TOML_allocBoolean( 0 ); 325 | } 326 | 327 | %type date { TOMLDate * } 328 | date(DATE) ::= DATE(DATE_TOKEN) . { 329 | int year; 330 | int month; 331 | int day; 332 | int hour; 333 | int minute; 334 | int second; 335 | sscanf( 336 | ((TOMLToken *) DATE_TOKEN)->tokenStr, 337 | "%d-%d-%dT%d:%d:%dZ", 338 | &year, &month, &day, &hour, &minute, &second 339 | ); 340 | DATE = TOML_allocDate( year, month, day, hour, minute, second ); 341 | } 342 | 343 | /** 344 | ** Errors. 345 | **/ 346 | 347 | error(A) ::= EOF error(B) . { A = B; } 348 | 349 | table_header ::= LEFT_SQUARE(SQUARE) error . { 350 | _TOML_fillError( SQUARE, state, TOML_ERROR_INVALID_HEADER ); 351 | } 352 | 353 | entry ::= id(ID) EQ error . { 354 | _TOML_fillError( state->token, state, TOML_ERROR_NO_VALUE ); 355 | free( ID ); 356 | } 357 | 358 | entry ::= id(ID) error . { 359 | _TOML_fillError( state->token, state, TOML_ERROR_NO_EQ ); 360 | free( ID ); 361 | } 362 | -------------------------------------------------------------------------------- /toml-parser.h: -------------------------------------------------------------------------------- 1 | #ifndef TOML_PARSER_H_Y469ZFLB 2 | #define TOML_PARSER_H_Y469ZFLB 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | 10 | #include "toml.h" 11 | 12 | typedef struct TOMLParser {} *pTOMLParser; 13 | 14 | typedef struct TOMLToken { 15 | int token; 16 | char *marker; 17 | char *start; 18 | char *end; 19 | int line; 20 | char *lineStart; 21 | char *tokenStr; 22 | } TOMLToken; 23 | 24 | typedef struct TOMLParserState { 25 | TOMLTable *rootTable; 26 | TOMLTable *currentTable; 27 | int errorCode; 28 | TOMLError *errorObj; 29 | TOMLToken *token; 30 | } TOMLParserState; 31 | 32 | void * TOMLParserAlloc( void * (*malloc)( size_t ) ); 33 | void TOMLParserFree( void *, void (*free)( void * ) ); 34 | void TOMLParser( void *, int hTokenId, void *, TOMLParserState * ); 35 | 36 | int TOMLScan(char *p, int* token, TOMLToken * ); 37 | 38 | #ifdef __cplusplus 39 | }; 40 | #endif 41 | 42 | #endif /* end of include guard: TOML_PARSER_H_Y469ZFLB */ 43 | -------------------------------------------------------------------------------- /toml-re2c.re2c: -------------------------------------------------------------------------------- 1 | #include "toml-lemon.h" 2 | #include "toml-parser.h" 3 | 4 | #define COUNTLINES \ 5 | tokenData->end = p; \ 6 | char *line = tokenData->start; \ 7 | \ 8 | line = strchr( line, '\n' ); \ 9 | while ( line != NULL && line < tokenData->end ) { \ 10 | tokenData->line++; \ 11 | tokenData->lineStart = line + 1; \ 12 | } 13 | #define RETURNTOKEN( tokenid ) *token = tokenData->token = tokenid; \ 14 | tokenData->end = p; \ 15 | return tokenid != EOF 16 | 17 | #define YYMARKER tokenData->marker 18 | #define SKIP(x) { tokenData->start = p; goto yy0; } 19 | 20 | int TOMLScan(char *p, int* token, TOMLToken *tokenData ) { 21 | tokenData->start = p; 22 | yy0: 23 | /*!re2c 24 | re2c:define:YYCTYPE = "char"; 25 | re2c:define:YYCURSOR = p; 26 | re2c:yyfill:enable = 0; 27 | 28 | comment_start = [#]; 29 | noteol = [^\n\x00]; 30 | eol = [\n]; 31 | eof = [\x00]; 32 | quote = ["]; 33 | escaped_chars = "\\" [btfnr/\\"]; 34 | string_bulk = [^"\x00\\]; 35 | digit = [0-9]; 36 | hex = [0-9a-fA-F]; 37 | digit2 = digit digit; 38 | negate = [\-]; 39 | dot = "\."; 40 | unicode = "\\u" hex{4}; 41 | invalid_unicode = "\\u" hex{0,3}; 42 | 43 | eof { RETURNTOKEN( EOF ); } 44 | "[" { RETURNTOKEN( LEFT_SQUARE ); } 45 | "]" { RETURNTOKEN( RIGHT_SQUARE ); } 46 | dot { RETURNTOKEN( ID_DOT ); } 47 | "=" { RETURNTOKEN( EQ ); } 48 | "," { RETURNTOKEN( COMMA ); } 49 | "true" { RETURNTOKEN( TRUE ); } 50 | "false" { RETURNTOKEN( FALSE ); } 51 | 52 | comment_start noteol* { RETURNTOKEN( COMMENT ); } 53 | [a-zA-Z_][a-zA-Z0-9_]* { RETURNTOKEN( ID ); } 54 | quote (escaped_chars|unicode|string_bulk)* quote { 55 | COUNTLINES; 56 | RETURNTOKEN( STRING ); 57 | } 58 | digit{4} "-" digit2 "-" digit2 "T" digit2 ":" digit2 ":" digit2 "Z" { 59 | RETURNTOKEN( DATE ); 60 | } 61 | negate? digit* dot? digit+ { RETURNTOKEN( NUMBER ); } 62 | [ \t\r]+ { SKIP(); } 63 | [\n] { tokenData->line++; tokenData->lineStart = p; SKIP(); } 64 | 65 | quote (escaped_chars|unicode|string_bulk)* (invalid_unicode|eof) { 66 | COUNTLINES; 67 | RETURNTOKEN( EOF ); 68 | } 69 | */ 70 | } 71 | -------------------------------------------------------------------------------- /toml.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | // #include 10 | 11 | #include "toml.h" 12 | #include "toml-parser.h" 13 | // #include "tomlParser.h" 14 | // #include "tomlLexer.h" 15 | 16 | struct _TOMLStringifyData { 17 | TOMLError *error; 18 | 19 | int bufferSize; 20 | int bufferIndex; 21 | char *buffer; 22 | int tableNameDepth; 23 | int tableNameStackSize; 24 | TOMLString **tableNameStack; 25 | }; 26 | 27 | int _TOML_stringify( struct _TOMLStringifyData *self, TOMLRef src ); 28 | 29 | TOMLRef TOML_alloc( TOMLType type ) { 30 | switch ( type ) { 31 | case TOML_TABLE: 32 | return TOML_allocTable( NULL, NULL ); 33 | case TOML_ARRAY: 34 | return TOML_allocArray( TOML_NOTYPE ); 35 | case TOML_STRING: 36 | return TOML_allocString( "" ); 37 | case TOML_INT: 38 | return TOML_allocInt( 0 ); 39 | case TOML_DOUBLE: 40 | return TOML_allocDouble( 0 ); 41 | case TOML_BOOLEAN: 42 | return TOML_allocBoolean( 0 ); 43 | case TOML_DATE: 44 | return TOML_allocEpochDate( 0 ); 45 | default: 46 | return NULL; 47 | } 48 | } 49 | 50 | TOMLTable * TOML_allocTable( TOMLString *key, TOMLRef value, ... ) { 51 | TOMLTable *self = malloc( sizeof(TOMLTable) ); 52 | self->type = TOML_TABLE; 53 | self->keys = TOML_allocArray( TOML_STRING, NULL ); 54 | self->values = TOML_allocArray( TOML_NOTYPE, NULL ); 55 | 56 | if ( key != NULL ) { 57 | TOMLArray_append( self->keys, key ); 58 | TOMLArray_append( self->values, value ); 59 | } else { 60 | return self; 61 | } 62 | 63 | va_list args; 64 | va_start( args, value ); 65 | 66 | key = va_arg( args, TOMLString * ); 67 | while ( key != NULL ) { 68 | value = va_arg( args, TOMLRef ); 69 | TOMLArray_append( self->keys, key ); 70 | TOMLArray_append( self->values, value ); 71 | key = va_arg( args, TOMLString * ); 72 | } 73 | 74 | va_end( args ); 75 | 76 | return self; 77 | } 78 | 79 | TOMLArray * TOML_allocArray( TOMLType memberType, ... ) { 80 | TOMLArray *self = malloc( sizeof(TOMLArray) ); 81 | self->type = TOML_ARRAY; 82 | self->memberType = memberType; 83 | self->size = 0; 84 | self->members = NULL; 85 | 86 | va_list args; 87 | va_start( args, memberType ); 88 | 89 | TOMLRef member = va_arg( args, TOMLRef ); 90 | while ( member != NULL ) { 91 | TOMLArray_append( self, member ); 92 | member = va_arg( args, TOMLRef ); 93 | } 94 | 95 | va_end( args ); 96 | 97 | return self; 98 | } 99 | 100 | TOMLString * TOML_allocString( char *content ) { 101 | int size = strlen( content ); 102 | TOMLString *self = malloc( sizeof(TOMLString) + size + 1 ); 103 | self->type = TOML_STRING; 104 | self->size = size; 105 | self->content[ self->size ] = 0; 106 | strncpy( self->content, content, size ); 107 | 108 | return self; 109 | } 110 | 111 | TOMLString * TOML_allocStringN( char *content, int n ) { 112 | TOMLString *self = malloc( sizeof(TOMLString) + n + 1 ); 113 | self->type = TOML_STRING; 114 | self->size = n; 115 | self->content[ n ] = 0; 116 | strncpy( self->content, content, n ); 117 | 118 | return self; 119 | } 120 | 121 | TOMLNumber * TOML_allocInt( int value ) { 122 | TOMLNumber *self = malloc( sizeof(TOMLNumber) ); 123 | self->type = TOML_INT; 124 | // self->numberType = TOML_INT; 125 | self->intValue = value; 126 | 127 | return self; 128 | } 129 | 130 | TOMLNumber * TOML_allocDouble( double value ) { 131 | TOMLNumber *self = malloc( sizeof(TOMLNumber) ); 132 | self->type = TOML_DOUBLE; 133 | // self->numberType = TOML_DOUBLE; 134 | self->doubleValue = value; 135 | 136 | return self; 137 | } 138 | 139 | TOMLBoolean * TOML_allocBoolean( int truth ) { 140 | TOMLBoolean *self = malloc( sizeof(TOMLBoolean) ); 141 | self->type = TOML_BOOLEAN; 142 | self->isTrue = truth; 143 | return self; 144 | } 145 | 146 | int _TOML_isLeapYear( int year ) { 147 | if ( year % 400 == 0 ) { 148 | return 1; 149 | } else if ( year % 100 == 0 ) { 150 | return 0; 151 | } else if ( year % 4 == 0 ) { 152 | return 1; 153 | } else { 154 | return 0; 155 | } 156 | } 157 | 158 | TOMLDate * TOML_allocDate( 159 | int year, int month, int day, int hour, int minute, int second 160 | ) { 161 | TOMLDate *self = malloc( sizeof(TOMLDate) ); 162 | self->type = TOML_DATE; 163 | 164 | self->year = year; 165 | self->month = month; 166 | self->day = day; 167 | 168 | self->hour = hour; 169 | self->minute = minute; 170 | self->second = second; 171 | 172 | struct tm _time = { 173 | second, 174 | minute, 175 | hour, 176 | day, 177 | month, 178 | year - 1900 179 | }; 180 | 181 | // local time 182 | time_t localEpoch = mktime( &_time ); 183 | // gm time 184 | _time = *gmtime( &localEpoch ); 185 | time_t gmEpoch = mktime( &_time ); 186 | 187 | double diff = difftime( localEpoch, gmEpoch ); 188 | 189 | // Adjust the localEpock made by mktime to a gmt epoch. 190 | self->sinceEpoch = localEpoch + diff; 191 | 192 | return self; 193 | } 194 | 195 | TOMLDate * TOML_allocEpochDate( time_t stamp ) { 196 | TOMLDate *self = malloc( sizeof(TOMLDate) ); 197 | self->type = TOML_DATE; 198 | self->sinceEpoch = stamp; 199 | 200 | struct tm _time = *gmtime( &stamp ); 201 | 202 | self->second = _time.tm_sec; 203 | self->minute = _time.tm_min; 204 | self->hour = _time.tm_hour; 205 | self->day = _time.tm_mday; 206 | self->month = _time.tm_mon; 207 | self->year = _time.tm_year + 1900; 208 | 209 | return self; 210 | } 211 | 212 | TOMLError * TOML_allocError( int code ) { 213 | TOMLError *self = malloc( sizeof(TOMLError) ); 214 | self->type = TOML_ERROR; 215 | self->code = code; 216 | self->lineNo = 0; 217 | self->line = NULL; 218 | self->message = NULL; 219 | self->fullDescription = NULL; 220 | 221 | return self; 222 | } 223 | 224 | char * _TOML_cstringCopy( char *str ) { 225 | if ( !str ) { 226 | return NULL; 227 | } 228 | 229 | int size = strlen( str ); 230 | char *newstr = malloc( size + 1 ); 231 | newstr[ size ] = 0; 232 | strncpy( newstr, str, size ); 233 | 234 | return newstr; 235 | } 236 | 237 | TOMLRef TOML_copy( TOMLRef self ) { 238 | TOMLBasic *basic = (TOMLBasic *) self; 239 | 240 | if ( basic->type == TOML_TABLE ) { 241 | TOMLTable *table = (TOMLTable *) self; 242 | TOMLTable *newTable = malloc( sizeof(TOMLTable) ); 243 | newTable->type = TOML_TABLE; 244 | newTable->keys = TOML_copy( table->keys ); 245 | newTable->values = TOML_copy( table->values ); 246 | return newTable; 247 | } else if ( basic->type == TOML_ARRAY ) { 248 | TOMLArray *array = (TOMLArray *) self; 249 | TOMLArray *newArray = malloc( sizeof(TOMLArray) ); 250 | newArray->type = TOML_ARRAY; 251 | newArray->memberType = array->memberType; 252 | int i; 253 | for ( i = 0; i < array->size; ++i ) { 254 | TOMLArray_append( 255 | newArray, 256 | TOML_copy( TOMLArray_getIndex( array, i ) ) 257 | ); 258 | } 259 | return newArray; 260 | } else if ( basic->type == TOML_STRING ) { 261 | TOMLString *string = (TOMLString *) self; 262 | TOMLString *newString = malloc( sizeof(TOMLString) + string->size + 1 ); 263 | newString->type = TOML_STRING; 264 | newString->size = string->size; 265 | strncpy( newString->content, string->content, string->size + 1 ); 266 | return newString; 267 | } else if ( basic->type == TOML_INT || basic->type == TOML_DOUBLE ) { 268 | TOMLNumber *number = (TOMLNumber *) self; 269 | TOMLNumber *newNumber = malloc( sizeof(TOMLNumber) ); 270 | newNumber->type = number->type; 271 | // newNumber->numberType = number->numberType; 272 | memcpy( newNumber->bytes, number->bytes, 8 ); 273 | return newNumber; 274 | } else if ( basic->type == TOML_BOOLEAN ) { 275 | TOMLBoolean *boolean = (TOMLBoolean *) self; 276 | TOMLBoolean *newBoolean = malloc( sizeof(TOMLBoolean) ); 277 | newBoolean->type = boolean->type; 278 | newBoolean->isTrue = boolean->isTrue; 279 | return newBoolean; 280 | } else if ( basic->type == TOML_DATE ) { 281 | TOMLDate *date = (TOMLDate *) self; 282 | TOMLDate *newDate = malloc( sizeof(TOMLDate) ); 283 | *newDate = *date; 284 | return newDate; 285 | } else if ( basic->type == TOML_ERROR ) { 286 | TOMLError *error = (TOMLError *) self; 287 | TOMLError *newError = malloc( sizeof(TOMLError) ); 288 | newError->type = TOML_ERROR; 289 | newError->code = error->code; 290 | newError->lineNo = error->lineNo; 291 | newError->line = _TOML_cstringCopy( error->line ); 292 | newError->message = _TOML_cstringCopy( error->message ); 293 | newError->fullDescription = _TOML_cstringCopy( error->fullDescription ); 294 | return newError; 295 | } else { 296 | return NULL; 297 | } 298 | } 299 | 300 | void TOML_free( TOMLRef self ) { 301 | TOMLBasic *basic = (TOMLBasic *) self; 302 | 303 | if ( basic->type == TOML_TABLE ) { 304 | TOMLTable *table = (TOMLTable *) self; 305 | TOML_free( table->keys ); 306 | TOML_free( table->values ); 307 | } else if ( basic->type == TOML_ARRAY ) { 308 | TOMLArray *array = (TOMLArray *) self; 309 | int i; 310 | for ( i = 0; i < array->size; ++i ) { 311 | TOML_free( array->members[ i ] ); 312 | } 313 | free( array->members ); 314 | } else if ( basic->type == TOML_ERROR ) { 315 | TOMLError *error = (TOMLError *) self; 316 | free( error->line ); 317 | free( error->message ); 318 | free( error->fullDescription ); 319 | } 320 | 321 | free( self ); 322 | } 323 | 324 | int TOML_isType( TOMLRef self, TOMLType type ) { 325 | TOMLBasic *basic = (TOMLBasic *) self; 326 | return basic->type == type; 327 | } 328 | 329 | int TOML_isNumber( TOMLRef self ) { 330 | TOMLBasic *basic = (TOMLBasic *) self; 331 | return basic->type == TOML_INT || basic->type == TOML_DOUBLE; 332 | } 333 | 334 | TOMLRef TOML_find( TOMLRef self, ... ) { 335 | TOMLBasic *basic = self; 336 | va_list args; 337 | va_start( args, self ); 338 | 339 | char *key; 340 | 341 | do { 342 | if ( basic->type == TOML_TABLE ) { 343 | key = va_arg( args, char * ); 344 | if ( key == NULL ) { 345 | break; 346 | } 347 | basic = self = TOMLTable_getKey( self, key ); 348 | } else if ( basic->type == TOML_ARRAY ) { 349 | key = va_arg( args, char * ); 350 | if ( key == NULL ) { 351 | break; 352 | } 353 | basic = self = TOMLArray_getIndex( self, atoi( key ) ); 354 | } else { 355 | break; 356 | } 357 | } while ( self ); 358 | 359 | va_end( args ); 360 | return self; 361 | } 362 | 363 | TOMLRef TOMLTable_getKey( TOMLTable *self, char *key ) { 364 | int keyLength = strlen( key ); 365 | int i; 366 | for ( i = 0; i < self->keys->size; ++i ) { 367 | TOMLString *tableKey = TOMLArray_getIndex( self->keys, i ); 368 | int minSize = keyLength < tableKey->size ? keyLength : tableKey->size; 369 | if ( strncmp( tableKey->content, key, minSize + 1 ) == 0 ) { 370 | return TOMLArray_getIndex( self->values, i ); 371 | } 372 | } 373 | return NULL; 374 | } 375 | 376 | void TOMLTable_setKey( TOMLTable *self, char *key, TOMLRef value ) { 377 | int keyLength = strlen( key ); 378 | int i; 379 | for ( i = 0; i < self->keys->size; ++i ) { 380 | TOMLString *tableKey = TOMLArray_getIndex( self->keys, i ); 381 | int minSize = keyLength < tableKey->size ? keyLength : tableKey->size; 382 | if ( strncmp( tableKey->content, key, minSize ) == 0 ) { 383 | TOMLArray_setIndex( self->values, i, value ); 384 | return; 385 | } 386 | } 387 | 388 | TOMLArray_append( self->keys, TOML_allocString( key ) ); 389 | TOMLArray_append( self->values, value ); 390 | } 391 | 392 | TOMLRef TOMLArray_getIndex( TOMLArray *self, int index ) { 393 | return self->members && self->size > index ? self->members[ index ] : NULL; 394 | } 395 | 396 | void TOMLArray_setIndex( TOMLArray *self, int index, TOMLRef value ) { 397 | if ( index < self->size ) { 398 | TOML_free( self->members[ index ] ); 399 | self->members[ index ] = value; 400 | } else { 401 | TOMLArray_append( self, value ); 402 | } 403 | } 404 | 405 | void TOMLArray_append( TOMLArray *self, TOMLRef value ) { 406 | TOMLRef *oldMembers = self->members; 407 | 408 | self->members = malloc( ( self->size + 1 ) * sizeof(TOMLRef) ); 409 | int i = 0; 410 | for ( ; i < self->size; ++i ) { 411 | self->members[ i ] = oldMembers[ i ]; 412 | } 413 | // memcpy( self->members, oldMembers, self->size * sizeof(TOMLRef) ); 414 | self->members[ self->size ] = value; 415 | self->size++; 416 | 417 | free( oldMembers ); 418 | } 419 | 420 | char * TOML_toString( TOMLString *self ) { 421 | char *string = malloc( self->size + 1 ); 422 | TOML_copyString( self, self->size + 1, string ); 423 | return string; 424 | } 425 | 426 | #define RETURN_VALUE switch ( self->type ) { \ 427 | case TOML_INT: \ 428 | return self->intValue; \ 429 | case TOML_DOUBLE: \ 430 | return self->doubleValue; \ 431 | default: \ 432 | return 0; \ 433 | } 434 | 435 | int TOML_toInt( TOMLNumber *self ) { 436 | RETURN_VALUE; 437 | } 438 | 439 | double TOML_toDouble( TOMLNumber *self ) { 440 | RETURN_VALUE; 441 | } 442 | 443 | #undef RETURN_VALUE 444 | 445 | struct tm TOML_toTm( TOMLDate *self ) { 446 | return *gmtime( &self->sinceEpoch ); 447 | } 448 | 449 | int TOML_toBoolean( TOMLBoolean *self ) { 450 | return self->isTrue; 451 | } 452 | 453 | TOMLToken * TOML_newToken( TOMLToken *token ) { 454 | TOMLToken *heapToken = malloc( sizeof(TOMLToken) ); 455 | memcpy( heapToken, token, sizeof(TOMLToken) ); 456 | 457 | int size = token->end - token->start; 458 | heapToken->tokenStr = malloc( size + 1 ); 459 | heapToken->tokenStr[ size ] = 0; 460 | strncpy( heapToken->tokenStr, token->start, size ); 461 | 462 | return heapToken; 463 | } 464 | 465 | void TOML_strcpy( char *buffer, TOMLString *self, int size ) { 466 | if ( self->type != TOML_STRING ) { 467 | buffer[0] = 0; 468 | } else { 469 | strncpy( 470 | buffer, self->content, size < self->size + 1 ? size : self->size + 1 471 | ); 472 | } 473 | } 474 | 475 | char * _TOML_increaseBuffer( char *oldBuffer, int *size ) { 476 | int newSize = *size + 1024; 477 | char *newBuffer = malloc( newSize + 1 ); 478 | // Always have a null terminator so TOMLScan can exit without segfault. 479 | newBuffer[ newSize ] = 0; 480 | 481 | if ( oldBuffer ) { 482 | strncpy( newBuffer, oldBuffer, *size + 1 ); 483 | free( oldBuffer ); 484 | } 485 | 486 | *size = newSize; 487 | 488 | return newBuffer; 489 | } 490 | 491 | int TOML_load( char *filename, TOMLTable **dest, TOMLError *error ) { 492 | assert( *dest == NULL ); 493 | 494 | FILE *fd = fopen( filename, "r" ); 495 | if ( fd == NULL ) { 496 | if ( error ) { 497 | error->code = TOML_ERROR_FILEIO; 498 | error->lineNo = -1; 499 | error->line = NULL; 500 | 501 | int messageSize = strlen( TOMLErrorDescription[ error->code ] ); 502 | error->message = 503 | malloc( messageSize + 1 ); 504 | strcpy( error->message, TOMLErrorDescription[ error->code ] ); 505 | error->message[ messageSize ] = 0; 506 | 507 | int fullDescSize = messageSize + strlen( filename ) + 8; 508 | error->fullDescription = malloc( fullDescSize + 1 ); 509 | snprintf( 510 | error->fullDescription, 511 | fullDescSize, 512 | "%s File: %s", 513 | error->message, 514 | filename 515 | ); 516 | } 517 | 518 | return TOML_ERROR_FILEIO; 519 | } 520 | 521 | int bufferSize = 0; 522 | char * buffer = _TOML_increaseBuffer( NULL, &bufferSize ); 523 | int copyBufferSize = 0; 524 | char * copyBuffer = _TOML_increaseBuffer( NULL, ©BufferSize ); 525 | int read = fread( buffer, 1, bufferSize, fd ); 526 | int incomplete = read == bufferSize; 527 | 528 | int hTokenId; 529 | TOMLToken token = { 0, NULL, NULL, buffer, 0, buffer, NULL }; 530 | TOMLToken lastToken = token; 531 | 532 | TOMLTable *topTable = *dest = TOML_allocTable( NULL, NULL ); 533 | TOMLParserState state = { topTable, topTable, 0, error, &token }; 534 | 535 | pTOMLParser parser = TOMLParserAlloc( malloc ); 536 | 537 | while ( 538 | state.errorCode == 0 && ( 539 | TOMLScan( token.end, &hTokenId, &token ) || incomplete 540 | ) 541 | ) { 542 | while ( token.end >= buffer + bufferSize && incomplete ) { 543 | int lineSize = buffer + bufferSize - lastToken.lineStart; 544 | 545 | if ( lastToken.lineStart == buffer ) { 546 | int oldBufferSize = bufferSize; 547 | strncpy( copyBuffer, lastToken.lineStart, lineSize ); 548 | buffer = _TOML_increaseBuffer( buffer, &bufferSize ); 549 | copyBuffer = _TOML_increaseBuffer( copyBuffer, ©BufferSize ); 550 | strncpy( buffer, copyBuffer, lineSize ); 551 | } else { 552 | strncpy( copyBuffer, lastToken.lineStart, lineSize ); 553 | strncpy( buffer, copyBuffer, lineSize ); 554 | } 555 | 556 | int read = fread( buffer + lineSize, 1, bufferSize - lineSize, fd ); 557 | incomplete = read == bufferSize - lineSize; 558 | if ( !incomplete ) { 559 | buffer[ lineSize + read ] = 0; 560 | } 561 | 562 | token = lastToken; 563 | token.end = buffer + ( token.end - token.lineStart ); 564 | token.lineStart = buffer; 565 | lastToken = token; 566 | TOMLScan( token.end, &hTokenId, &token ); 567 | } 568 | 569 | lastToken = token; 570 | 571 | int tmpSize = token.end - token.start; 572 | char *tmp = malloc( tmpSize + 1 ); 573 | strncpy( tmp, token.start, tmpSize ); 574 | tmp[ tmpSize ] = 0; 575 | free( tmp ); 576 | 577 | TOMLParser( parser, hTokenId, TOML_newToken( &token ), &state ); 578 | } 579 | 580 | if ( state.errorCode == 0 ) { 581 | TOMLParser( parser, hTokenId, TOML_newToken( &token ), &state ); 582 | } 583 | 584 | TOMLParserFree( parser, free ); 585 | 586 | free( copyBuffer ); 587 | free( buffer ); 588 | fclose( fd ); 589 | 590 | if ( state.errorCode != 0 ) { 591 | TOML_free( *dest ); 592 | *dest = NULL; 593 | return state.errorCode; 594 | } 595 | 596 | return 0; 597 | } 598 | 599 | // int TOML_dump( char *filename, TOMLTable * ); 600 | 601 | int TOML_parse( char *buffer, TOMLTable **dest, TOMLError *error ) { 602 | assert( *dest == NULL ); 603 | 604 | int hTokenId; 605 | TOMLToken token = { 0, NULL, NULL, buffer, 0, buffer, NULL }; 606 | 607 | TOMLTable *topTable = *dest = TOML_allocTable( NULL, NULL ); 608 | TOMLParserState state = { topTable, topTable, 0, error, &token }; 609 | 610 | pTOMLParser parser = TOMLParserAlloc( malloc ); 611 | 612 | while ( state.errorCode == 0 && TOMLScan( token.end, &hTokenId, &token ) ) { 613 | TOMLParser( parser, hTokenId, TOML_newToken( &token ), &state ); 614 | } 615 | 616 | if ( state.errorCode == 0 ) { 617 | TOMLParser( parser, hTokenId, TOML_newToken( &token ), &state ); 618 | } 619 | 620 | TOMLParserFree( parser, free ); 621 | 622 | if ( state.errorCode != 0 ) { 623 | TOML_free( *dest ); 624 | *dest = NULL; 625 | return state.errorCode; 626 | } 627 | 628 | return 0; 629 | } 630 | 631 | TOMLString ** _TOML_increaseNameStack( 632 | TOMLString **nameStack, int *nameStackSize 633 | ) { 634 | TOMLString **oldStack = nameStack; 635 | int oldSize = *nameStackSize; 636 | *nameStackSize += 16; 637 | nameStack = malloc( *nameStackSize * sizeof(TOMLString *) ); 638 | if ( oldStack ) { 639 | memcpy( nameStack, oldStack, oldSize ); 640 | free( oldStack ); 641 | } 642 | return nameStack; 643 | } 644 | 645 | void _TOML_stringifyPushName( 646 | struct _TOMLStringifyData *self, TOMLRef src 647 | ) { 648 | if ( self->tableNameDepth >= self->tableNameStackSize ) { 649 | self->tableNameStack = _TOML_increaseNameStack( 650 | self->tableNameStack, 651 | &( self->tableNameStackSize ) 652 | ); 653 | } 654 | self->tableNameStack[ self->tableNameDepth ] = src; 655 | self->tableNameDepth++; 656 | } 657 | 658 | void _TOML_stringifyPopName( 659 | struct _TOMLStringifyData *self 660 | ) { 661 | self->tableNameDepth--; 662 | self->tableNameStack[ self->tableNameDepth ] = NULL; 663 | } 664 | 665 | void _TOML_stringifyText( struct _TOMLStringifyData *self, char *text, int n ) { 666 | if ( self->bufferIndex + n + 1 >= self->bufferSize ) { 667 | self->buffer = _TOML_increaseBuffer( self->buffer, &self->bufferSize ); 668 | } 669 | strncpy( self->buffer + self->bufferIndex, text, n ); 670 | self->bufferIndex += n; 671 | self->buffer[ self->bufferIndex ] = 0; 672 | } 673 | 674 | void _TOML_stringifyTableHeader( 675 | struct _TOMLStringifyData *self, TOMLTable *table 676 | ) { 677 | TOMLBasic *first = TOMLArray_getIndex( table->values, 0 ); 678 | if ( 679 | !first || 680 | first->type == TOML_TABLE || ( 681 | first->type == TOML_ARRAY && 682 | ((TOMLArray *) first)->memberType == TOML_TABLE 683 | ) 684 | ) { 685 | return; 686 | } 687 | 688 | if ( self->bufferIndex != 0 ) { 689 | _TOML_stringifyText( self, "\n", 1 ); 690 | } 691 | 692 | _TOML_stringifyText( self, "[", 1 ); 693 | for ( int i = 0; i < self->tableNameDepth; ++i ) { 694 | TOMLString *tableName = self->tableNameStack[ i ]; 695 | if ( i > 0 ) { 696 | _TOML_stringifyText( self, ".", 1 ); 697 | } 698 | _TOML_stringifyText( self, tableName->content, tableName->size ); 699 | } 700 | _TOML_stringifyText( self, "]\n", 2 ); 701 | } 702 | 703 | void _TOML_stringifyArrayHeader( struct _TOMLStringifyData *self ) { 704 | if ( self->bufferIndex != 0 ) { 705 | _TOML_stringifyText( self, "\n", 1 ); 706 | } 707 | 708 | _TOML_stringifyText( self, "[[", 2 ); 709 | for ( int i = 0; i < self->tableNameDepth; ++i ) { 710 | TOMLString *tableName = self->tableNameStack[ i ]; 711 | if ( i > 0 ) { 712 | _TOML_stringifyText( self, ".", 1 ); 713 | } 714 | _TOML_stringifyText( self, tableName->content, tableName->size ); 715 | } 716 | _TOML_stringifyText( self, "]]\n", 3 ); 717 | } 718 | 719 | void _TOML_stringifyString( 720 | struct _TOMLStringifyData *self, TOMLString *string 721 | ) { 722 | char *cursor = string->content; 723 | while ( cursor != NULL ) { 724 | // Scan for escapable character or unicode. 725 | char *next = cursor; 726 | unsigned int ch = *next; 727 | for ( ; 728 | !( 729 | ch == 0 || 730 | ch == '\b' || 731 | ch == '\t' || 732 | ch == '\f' || 733 | ch == '\n' || 734 | ch == '\r' || 735 | ch == '"' || 736 | ch == '/' || 737 | ch == '\\' || 738 | ch > 0x7f 739 | ); 740 | next++, ch = *next 741 | ) {} 742 | 743 | if ( *next == 0 ) { 744 | next = NULL; 745 | } 746 | 747 | // Copy text up to character and then insert escaped character. 748 | if ( next ) { 749 | _TOML_stringifyText( self, cursor, next - cursor ); 750 | 751 | #define REPLACE( match, value ) \ 752 | if ( *next == match ) { \ 753 | _TOML_stringifyText( self, value, 2 ); \ 754 | } 755 | REPLACE( '\b', "\\b" ) 756 | else REPLACE( '\t', "\\t" ) 757 | else REPLACE( '\f', "\\f" ) 758 | else REPLACE( '\n', "\\n" ) 759 | else REPLACE( '\r', "\\r" ) 760 | else REPLACE( '"', "\\\"" ) 761 | else REPLACE( '/', "\\/" ) 762 | else REPLACE( '\\', "\\\\" ) 763 | #undef REPLACE 764 | else if ( ((unsigned int) *next ) > 0x7f ) { 765 | int num = 0; 766 | int chsize; 767 | 768 | // Decode the numeric representation of the utf8 character 769 | if ( ( *next & 0xe0 ) == 0xe0 ) { 770 | chsize = 3; 771 | num = 772 | ( ( next[0] & 0x0f ) << 12 ) | 773 | ( ( next[1] & 0x3f ) << 6 ) | 774 | ( next[2] & 0x3f ); 775 | } else if ( ( *next & 0xc0 ) == 0xc0 ) { 776 | chsize = 2; 777 | num = 778 | ( ( next[0] & 0x1f ) << 6 ) | 779 | ( next[1] & 0x3f ); 780 | } else { 781 | assert( 0 ); 782 | } 783 | 784 | // Stringify \uxxxx 785 | char utf8Buffer[5]; 786 | snprintf( utf8Buffer, 5, "%04x", num ); 787 | _TOML_stringifyText( self, "\\u", 2 ); 788 | _TOML_stringifyText( self, utf8Buffer, 4 ); 789 | 790 | next += chsize - 1; 791 | } 792 | 793 | next++; 794 | // Copy everything up to the end. 795 | } else { 796 | _TOML_stringifyText( self, cursor, strlen( cursor ) ); 797 | } 798 | 799 | cursor = next; 800 | } 801 | } 802 | 803 | void _TOML_stringifyEntry( 804 | struct _TOMLStringifyData *self, TOMLString *key, TOMLBasic *value 805 | ) { 806 | _TOML_stringifyText( self, key->content, key->size ); 807 | _TOML_stringifyText( self, " = ", 3 ); 808 | 809 | if ( value->type == TOML_STRING ) { 810 | _TOML_stringifyText( self, "\"", 1 ); 811 | _TOML_stringifyString( self, (TOMLString *) value ); 812 | _TOML_stringifyText( self, "\"", 1 ); 813 | } else { 814 | _TOML_stringify( self, value ); 815 | } 816 | 817 | _TOML_stringifyText( self, "\n", 1 ); 818 | } 819 | 820 | int _TOML_stringify( 821 | struct _TOMLStringifyData *self, TOMLRef src 822 | ) { 823 | // Cast to TOMLBasic to discover type. 824 | TOMLBasic *basic = src; 825 | 826 | // if null 827 | if ( src == NULL ) { 828 | _TOML_stringifyText( self, "(null)", 6 ); 829 | // if table 830 | } else if ( basic->type == TOML_TABLE ) { 831 | TOMLTable *table = src; 832 | 833 | // loop keys 834 | for ( int i = 0; i < table->keys->size; ++i ) { 835 | TOMLRef key = TOMLArray_getIndex( table->keys, i ); 836 | TOMLRef value = TOMLArray_getIndex( table->values, i ); 837 | TOMLBasic *basicValue = value; 838 | 839 | // if value is table, print header, recurse 840 | if ( basicValue->type == TOML_TABLE ) { 841 | TOMLTable *tableValue = value; 842 | _TOML_stringifyPushName( self, key ); 843 | _TOML_stringifyTableHeader( self, value ); 844 | _TOML_stringify( self, value ); 845 | _TOML_stringifyPopName( self ); 846 | // if value is array 847 | } else if ( basicValue->type == TOML_ARRAY ) { 848 | TOMLArray *array = value; 849 | 850 | // if value is object array 851 | if ( array->memberType == TOML_TABLE ) { 852 | // loop indices, print headers, recurse 853 | for ( int j = 0; j < array->size; ++j ) { 854 | _TOML_stringifyPushName( self, key ); 855 | _TOML_stringifyArrayHeader( self ); 856 | _TOML_stringify( self, TOMLArray_getIndex( array, j ) ); 857 | _TOML_stringifyPopName( self ); 858 | } 859 | } else { 860 | // print entry line with dense (no newlines) array 861 | _TOML_stringifyEntry( self, key, value ); 862 | } 863 | } else { 864 | // if value is string or number, print entry 865 | _TOML_stringifyEntry( self, key, value ); 866 | } 867 | } 868 | // if array 869 | } else if ( basic->type == TOML_ARRAY ) { 870 | TOMLArray *array = src; 871 | 872 | // print array densely 873 | _TOML_stringifyText( self, "[", 1 ); 874 | for ( int i = 0; i < array->size; ++i ) { 875 | _TOML_stringifyText( self, " ", 1 ); 876 | TOMLBasic *arrayValue = TOMLArray_getIndex( array, i ); 877 | if ( arrayValue->type == TOML_STRING ) { 878 | _TOML_stringifyText( self, "\"", 1 ); 879 | _TOML_stringifyString( self, (TOMLString *) arrayValue ); 880 | _TOML_stringifyText( self, "\"", 1 ); 881 | } else { 882 | _TOML_stringify( self, arrayValue ); 883 | } 884 | if ( i != array->size - 1 ) { 885 | _TOML_stringifyText( self, ",", 1 ); 886 | } else { 887 | _TOML_stringifyText( self, " ", 1 ); 888 | } 889 | } 890 | _TOML_stringifyText( self, "]", 1 ); 891 | // if string 892 | } else if ( basic->type == TOML_STRING ) { 893 | TOMLString *string = src; 894 | 895 | // print string 896 | _TOML_stringifyText( self, string->content, string->size ); 897 | // if number 898 | } else if ( TOML_isNumber( basic ) ) { 899 | TOMLNumber *number = src; 900 | char numberBuffer[ 16 ]; 901 | memset( numberBuffer, 0, 16 ); 902 | 903 | int size; 904 | if ( number->type == TOML_INT ) { 905 | size = snprintf( numberBuffer, 15, "%d", number->intValue ); 906 | } else if ( fmod( number->doubleValue, 1 ) == 0 ) { 907 | size = snprintf( numberBuffer, 15, "%.1f", number->doubleValue ); 908 | } else { 909 | size = snprintf( numberBuffer, 15, "%g", number->doubleValue ); 910 | } 911 | 912 | // print number 913 | _TOML_stringifyText( self, numberBuffer, size ); 914 | } else if ( basic->type == TOML_BOOLEAN ) { 915 | TOMLBoolean *boolean = (TOMLBoolean *) basic; 916 | 917 | if ( boolean->isTrue ) { 918 | _TOML_stringifyText( self, "true", 4 ); 919 | } else { 920 | _TOML_stringifyText( self, "false", 5 ); 921 | } 922 | } else if ( basic->type == TOML_DATE ) { 923 | TOMLDate *date = (TOMLDate *) basic; 924 | char numberBuffer[ 16 ]; 925 | int size; 926 | 927 | #define STRINGIFY_DATE_SECTION( format, part, spacer ) \ 928 | size = snprintf( numberBuffer, 15, format, date->part ); \ 929 | _TOML_stringifyText( self, numberBuffer, size ); \ 930 | _TOML_stringifyText( self, spacer, 1 ) 931 | 932 | STRINGIFY_DATE_SECTION( "%d", year, "-" ); 933 | STRINGIFY_DATE_SECTION( "%0.2d", month, "-" ); 934 | STRINGIFY_DATE_SECTION( "%0.2d", day, "T" ); 935 | STRINGIFY_DATE_SECTION( "%0.2d", hour, ":" ); 936 | STRINGIFY_DATE_SECTION( "%0.2d", minute, ":" ); 937 | STRINGIFY_DATE_SECTION( "%0.2d", second, "Z" ); 938 | 939 | #undef STRINGIFY_DATE_SECTION 940 | } else { 941 | assert( 0 ); 942 | } 943 | // if error 944 | // print error 945 | 946 | return 0; 947 | } 948 | 949 | int TOML_stringify( char **buffer, TOMLRef src, TOMLError *error ) { 950 | int bufferSize = 0; 951 | char *output = _TOML_increaseBuffer( NULL, &bufferSize ); 952 | 953 | int stackSize = 0; 954 | TOMLString **tableNameStack = _TOML_increaseNameStack( NULL, &stackSize ); 955 | 956 | struct _TOMLStringifyData stringifyData = { 957 | error, 958 | 959 | bufferSize, 960 | 0, 961 | output, 962 | 0, 963 | stackSize, 964 | tableNameStack 965 | }; 966 | 967 | int errorCode = _TOML_stringify( &stringifyData, src ); 968 | 969 | free( tableNameStack ); 970 | *buffer = stringifyData.buffer; 971 | 972 | return errorCode; 973 | } 974 | -------------------------------------------------------------------------------- /toml.h: -------------------------------------------------------------------------------- 1 | #ifndef TOML_H_N6JCXECL 2 | #define TOML_H_N6JCXECL 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include 10 | 11 | // Values identifying what the TOML object is. 12 | typedef enum { 13 | TOML_NOTYPE, 14 | TOML_TABLE, 15 | TOML_ARRAY, 16 | TOML_STRING, 17 | TOML_INT, 18 | TOML_DOUBLE, 19 | TOML_BOOLEAN, 20 | TOML_DATE, 21 | TOML_ERROR 22 | } TOMLType; 23 | 24 | // Values identifying what the underlying number type is. 25 | // typedef enum { 26 | // TOML_INT, 27 | // TOML_DOUBLE 28 | // } TOMLNumberType; 29 | 30 | typedef enum { 31 | TOML_SUCCESS, 32 | TOML_ERROR_FILEIO, 33 | TOML_ERROR_FATAL, 34 | TOML_ERROR_TABLE_DEFINED, 35 | TOML_ERROR_ENTRY_DEFINED, 36 | TOML_ERROR_NO_VALUE, 37 | TOML_ERROR_NO_EQ, 38 | TOML_ERROR_INVALID_HEADER, 39 | TOML_ERROR_ARRAY_MEMBER_MISMATCH 40 | } TOMLErrorType; 41 | 42 | static char *TOMLErrorStrings[] = { 43 | "TOML_SUCCESS", 44 | "TOML_ERROR_FILEIO", 45 | "TOML_ERROR_FATAL", 46 | "TOML_ERROR_TABLE_DEFINED", 47 | "TOML_ERROR_ENTRY_DEFINED", 48 | "TOML_ERROR_NO_VALUE", 49 | "TOML_ERROR_NO_EQ", 50 | "TOML_ERROR_INVALID_HEADER", 51 | "TOML_ERROR_ARRAY_MEMBER_MISMATCH" 52 | }; 53 | 54 | static char *TOMLErrorDescription[] = { 55 | NULL, 56 | "Error reading from/writing to file.", 57 | "Fatal error.", 58 | "Table is already defined.", 59 | "Entry is already defined.", 60 | "Missing valid value.", 61 | "Missing equal sign in table entry.", 62 | "Incomplete table header.", 63 | "Array member must be the same type as other members." 64 | }; 65 | 66 | // Arbitrary pointer to a TOML object. 67 | typedef void * TOMLRef; 68 | 69 | // Struct defining the common part of all TOML objects, giving access to 70 | // the type. 71 | typedef struct TOMLBasic { 72 | TOMLType type; 73 | } TOMLBasic; 74 | 75 | // A TOML array. 76 | typedef struct TOMLArray { 77 | TOMLType type; 78 | TOMLType memberType; 79 | int size; 80 | TOMLRef *members; 81 | } TOMLArray; 82 | 83 | // A TOML table. 84 | typedef struct TOMLTable { 85 | TOMLType type; 86 | TOMLArray *keys; 87 | TOMLArray *values; 88 | } TOMLTable; 89 | 90 | // A TOML string. 91 | typedef struct TOMLString { 92 | TOMLType type; 93 | int size; 94 | char content[]; 95 | } TOMLString; 96 | 97 | // A TOML number. 98 | typedef struct TOMLNumber { 99 | TOMLType type; 100 | union { 101 | int intValue; 102 | double doubleValue; 103 | char bytes[8]; 104 | }; 105 | } TOMLNumber; 106 | 107 | // A TOML boolean. 108 | typedef struct TOMLBoolean { 109 | TOMLType type; 110 | int isTrue; 111 | } TOMLBoolean; 112 | 113 | // A TOML date. 114 | typedef struct TOMLDate { 115 | TOMLType type; 116 | long int sinceEpoch; 117 | int year; 118 | int month; 119 | int day; 120 | int hour; 121 | int minute; 122 | int second; 123 | } TOMLDate; 124 | 125 | typedef struct TOMLError { 126 | TOMLType type; 127 | TOMLErrorType code; 128 | int lineNo; 129 | char * line; 130 | char * message; 131 | char * fullDescription; 132 | } TOMLError; 133 | 134 | /********************** 135 | ** Memory Functions ** 136 | **********************/ 137 | 138 | // Allocate an empty TOML object of a given type. 139 | TOMLRef TOML_alloc( TOMLType ); 140 | 141 | // Allocates a table and assigns key, value pairs. Table takes ownership of any 142 | // keys and values given and they will be freed if overwritten with setKey or 143 | // freed by TOML_free. 144 | TOMLTable * TOML_allocTable( TOMLString *key, TOMLRef value, ... ); 145 | 146 | // Allocates an array and appends any values given. Array takes ownership of 147 | // any keys and values given and they will be freed if overwritten with 148 | // setIndex or freed by TOML_free. 149 | TOMLArray * TOML_allocArray( TOMLType memberType, ... ); 150 | 151 | // TOML_allocString creates a copy of the given string. 152 | TOMLString * TOML_allocString( char *content ); 153 | 154 | // TOML_allocStringN creates a copy of the given string up to n. 155 | TOMLString * TOML_allocStringN( char *content, int n ); 156 | 157 | // Allocate a TOMLNumber and store an int value. 158 | TOMLNumber * TOML_allocInt( int value ); 159 | 160 | // Allocate a TOMLNumber and store a double value. 161 | TOMLNumber * TOML_allocDouble( double value ); 162 | 163 | // Allocate a TOMLBoolean and store the truth. 164 | TOMLBoolean * TOML_allocBoolean( int truth ); 165 | 166 | // Allocate a TOMLDate with the given values. 167 | // 168 | // These values work with c standard tm values. Month is bound 0 to 11. 169 | TOMLDate * TOML_allocDate( 170 | int year, int month, int day, int hour, int minute, int second 171 | ); 172 | 173 | // Allocate a TOMLDate with a given GMT timestamp in seconds. 174 | TOMLDate * TOML_allocEpochDate( time_t stamp ); 175 | 176 | // Allocate an error to be filled by TOML_parse or TOML_stringify. 177 | TOMLError * TOML_allocError( int code ); 178 | 179 | // Copy any TOML object. 180 | TOMLRef TOML_copy( TOMLRef ); 181 | 182 | // Free a TOML object. 183 | void TOML_free( TOMLRef ); 184 | 185 | /***************** 186 | ** Interaction ** 187 | *****************/ 188 | 189 | int TOML_isType( TOMLRef, TOMLType ); 190 | int TOML_isNumber( TOMLRef ); 191 | 192 | // Dive through the hierarchy. For each level getKey and getIndex are called to 193 | // get the next level. If it is a table getKey. If it is an array getIndex. 194 | // Each lookup is a string, before getIndex is called, the string will be 195 | // translated into an integer. TOML_find works this way to provide a convenient 196 | // and clear API. 197 | // 198 | // Example: 199 | // TOMLRef ref = TOML_find( table, "child", "nextchild", "0", NULL ); 200 | TOMLRef TOML_find( TOMLRef, ... ); 201 | 202 | // Get the value at the given key. 203 | TOMLRef TOMLTable_getKey( TOMLTable *, char * ); 204 | 205 | // Set the value at the given key. If the key is already set, the replaced 206 | // value will be freed. 207 | void TOMLTable_setKey( TOMLTable *, char *, TOMLRef ); 208 | 209 | // Return the value stored at the index or NULL. 210 | TOMLRef TOMLArray_getIndex( TOMLArray *, int index ); 211 | 212 | // Set index of array to the given value. If the index is greater than or equal 213 | // to the current size of the array, the value will be appended to the end. 214 | // 215 | // If a value is replaced, the replaced value will be freed. 216 | void TOMLArray_setIndex( TOMLArray *, int index, TOMLRef ); 217 | 218 | // Append the given TOML object to the array. 219 | void TOMLArray_append( TOMLArray *, TOMLRef ); 220 | 221 | /**************** 222 | ** Raw Values ** 223 | ****************/ 224 | 225 | // Allocates a copy of the string held in TOMLString. 226 | char * TOML_toString( TOMLString * ); 227 | 228 | // Copy the content of TOMLString to a destination. 229 | void TOML_strcpy( char *dest, TOMLString *src, int size ); 230 | 231 | // Copy a string. 232 | #define TOML_copyString( self, size, string ) TOML_strcpy( string, self, size ) 233 | 234 | // Return the TOMLNumber value as integer. 235 | int TOML_toInt( TOMLNumber * ); 236 | 237 | // Return the TOMLNumber value as double. 238 | double TOML_toDouble( TOMLNumber * ); 239 | 240 | // Return the c standard tm struct filled with data from this date. 241 | struct tm TOML_toTm( TOMLDate * ); 242 | 243 | // Return the truth of a TOMLBoolean. 244 | int TOML_toBoolean( TOMLBoolean * ); 245 | 246 | /************************ 247 | ** Loading and Saving ** 248 | ************************/ 249 | 250 | // Allocates a table filled with the parsed content of the file. 251 | // Returns non-zero if there was an error. 252 | int TOML_load( char *filename, TOMLTable **, TOMLError * ); 253 | 254 | // Writes a stringified table to the indicated file. 255 | // Returns non-zero if there was an error. 256 | // TODO: Implement TOML_dump. 257 | // int TOML_dump( char *filename, TOMLTable *, TOMLError * ); 258 | 259 | // Allocates a table filled with the parsed content of the buffer. 260 | // Returns non-zero if there was an error. 261 | int TOML_parse( char *buffer, TOMLTable **, TOMLError * ); 262 | 263 | // Allocates a string filled a string version of the table. 264 | // Returns non-zero if there was an error. 265 | int TOML_stringify( char **buffer, TOMLRef, TOMLError * ); 266 | 267 | #ifdef __cplusplus 268 | }; 269 | #endif 270 | 271 | #endif /* end of include guard: TOML_H_N6JCXECL */ 272 | -------------------------------------------------------------------------------- /waf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mzgoddard/tomlc/a48be2eb22f090c304d9b6f6415d54427b3d1d43/waf -------------------------------------------------------------------------------- /wscript: -------------------------------------------------------------------------------- 1 | #!./waf 2 | 3 | import os 4 | import subprocess 5 | 6 | def options(ctx): 7 | ctx.load('compiler_c') 8 | 9 | import waflib.Logs 10 | def configure(ctx): 11 | ctx.load('compiler_c') 12 | 13 | ctx.env.append_value( 'CFLAGS', '-g' ) 14 | ctx.env.append_value( 'CFLAGS', '-O2' ) 15 | ctx.env.append_value( 'CFLAGS', '-std=c99' ) 16 | 17 | ctx.start_msg( 'init submodules' ) 18 | gitStatus = ctx.exec_command( 'git submodule init && git submodule update' ) 19 | if gitStatus == 0: 20 | ctx.end_msg( 'ok', 'GREEN' ) 21 | else: 22 | ctx.fatal( 'fail' ) 23 | 24 | ctx.start_msg( 'build libtap dependency' ) 25 | libtapStatus = ctx.exec_command( 'cd vendor/libtap && make' ) 26 | if libtapStatus == 0: 27 | ctx.end_msg( 'ok', 'GREEN' ) 28 | else: 29 | ctx.fatal( 'fail' ) 30 | 31 | from waflib.TaskGen import extension 32 | 33 | from waflib.Task import Task 34 | 35 | class re2c(Task): 36 | run_str = 're2c ${SRC} > ${TGT}' 37 | color = 'PINK' 38 | 39 | @extension('re2c') 40 | def process_re2c( self, node ): 41 | task = self.create_task( 're2c', node, node.change_ext( '.c' ) ) 42 | self.source.extend( task.outputs ) 43 | 44 | def build(bld): 45 | bld.install_files( '${PREFIX}/include', 'toml.h' ) 46 | 47 | source = bld.path.ant_glob('toml.c toml-lemon.c toml-re2c.re2c') 48 | d = { 49 | 'source': source, 50 | 'includes': '.', 51 | 'target': 'toml', 52 | 'install_path': '${PREFIX}/lib', 53 | 'lib': 'm' 54 | } 55 | bld.stlib( **d ) 56 | bld.shlib( **d ) 57 | 58 | bld.program( 59 | source='main.c', 60 | includes='.', 61 | target='toml-lookup', 62 | lib='m', 63 | use='toml', 64 | install_path='${PREFIX}/bin' 65 | ) 66 | 67 | bld.program( 68 | source=bld.path.ant_glob('test.c'), 69 | includes='. ../vendor/libtap', 70 | target='toml-test', 71 | libpath='../vendor/libtap', 72 | lib='tap m', 73 | use='toml', 74 | install_path=None 75 | ) 76 | 77 | import waflib.Scripting, waflib.Context 78 | 79 | def test(ctx): 80 | waflib.Scripting.run_command( 'build' ) 81 | ctx.to_log( '\n' ) 82 | ctx.cmd_and_log( 83 | 'build/test\n', 84 | shell=True, 85 | stdout=subprocess.PIPE, 86 | stderr=subprocess.PIPE, 87 | quiet=waflib.Context.BOTH 88 | ) 89 | --------------------------------------------------------------------------------