Add INI parser
The ini parser will come in handy for configurations, for example once the time has come for adding shader packs. The parser isn't tested yet; writing some (automatic) tests would be a really good idea.
This commit is contained in:
parent
c3fb07b871
commit
6644a59270
|
@ -22,6 +22,43 @@ typedef struct vx_pool vx_pool_s;
|
|||
typedef struct vx_pool_slot_header vx_pool_slot_header_s;
|
||||
typedef struct vx_arena vx_arena_s;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
VX_ASCII_EXCLAMATION_MARK = '!',
|
||||
VX_ASCII_DOUBLE_QUOTATION_MARK = '"',
|
||||
VX_ASCII_NUMBER_SIGN = '#',
|
||||
VX_ASCII_DOLLAR_SIGN = '$',
|
||||
VX_ASCII_PERCENT_SIGN = '%',
|
||||
VX_ASCII_AMPERSAND = '&',
|
||||
VX_ASCII_SINGLE_QUOTATION_MARK = '\'',
|
||||
VX_ASCII_OPENING_PARENTHESIS = '(',
|
||||
VX_ASCII_CLOSING_PARENTHESIS = ')',
|
||||
VX_ASCII_ASTERISK = '*',
|
||||
VX_ASCII_ADDITION_SIGN = '+',
|
||||
VX_ASCII_COMMA = ',',
|
||||
VX_ASCII_HYPHEN = '-',
|
||||
VX_ASCII_DOT = '.',
|
||||
VX_ASCII_FORWARD_SLASH = '/',
|
||||
VX_ASCII_COLON = ':',
|
||||
VX_ASCII_SEMICOLON = ';',
|
||||
VX_ASCII_LESS_THAN_SIGN = '<',
|
||||
VX_ASCII_EQUALS_SIGN = '=',
|
||||
VX_ASCII_GREATER_THAN_SIGN = '>',
|
||||
VX_ASCII_QUESTION_MARK = '?',
|
||||
VX_ASCII_AT_SIGN = '@',
|
||||
VX_ASCII_OPENING_BRACKET = '[',
|
||||
VX_ASCII_BACKSLASH = '\\',
|
||||
VX_ASCII_CLOSING_BRACKET = ']',
|
||||
VX_ASCII_CARET = '^',
|
||||
VX_ASCII_UNDERSCORE = '_',
|
||||
VX_ASCII_BACKTICK = '`',
|
||||
VX_ASCII_OPENING_BRACE = '{',
|
||||
VX_ASCII_VERTICAL_BAR = '|',
|
||||
VX_ASCII_CLOSING_BRACE = '}',
|
||||
VX_ACII_TILDE = '~'
|
||||
|
||||
} vx_ascii_sign_e;
|
||||
|
||||
struct vx_uuid_entry
|
||||
{
|
||||
vx_uuid_d uuid;
|
||||
|
@ -171,5 +208,25 @@ void vx_free_arena(vx_arena_s *arena);
|
|||
void * vx_arena_alloc(vx_arena_s *arena, uint32_t length);
|
||||
char * vx_arena_dupe_string(vx_arena_s *arena, const char *string);
|
||||
char * vx_arena_dupe_string_up_to(vx_arena_s *arena, const char *string, uint32_t maximum);
|
||||
|
||||
|
||||
// Character Functions
|
||||
|
||||
bool vx_is_ascii_sign(char character);
|
||||
bool vx_is_ascii_sign_of_block_1(char character);
|
||||
bool vx_is_ascii_sign_of_block_2(char character);
|
||||
bool vx_is_ascii_sign_of_block_3(char character);
|
||||
bool vx_is_ascii_sign_of_block_4(char character);
|
||||
|
||||
bool vx_is_ascii_letter(char character);
|
||||
bool vx_is_ascii_lower(char character);
|
||||
bool vx_is_ascii_upper(char character);
|
||||
|
||||
bool vx_is_ascii_digit(char character);
|
||||
|
||||
bool vx_is_ascii_blank(char character);
|
||||
bool vx_is_ascii_control(char character);
|
||||
|
||||
uint8_t vx_digits_for_decimal(uint32_t decimal);
|
||||
void vx_write_spaces(FILE *output, uint32_t count);
|
||||
|
||||
#endif // VOXULA_UTILITY_H
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
|
||||
#ifndef VOXULA_INI_H
|
||||
#define VOXULA_INI_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <voxula/internals/utility.h>
|
||||
|
||||
typedef struct vx_ini vx_ini_s;
|
||||
typedef struct vx_ini_section vx_ini_section_s;
|
||||
typedef struct vx_ini_field vx_ini_field_s;
|
||||
typedef struct vx_ini_value vx_ini_value_s;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
VX_INI_VALUE_NULL = 0,
|
||||
|
||||
VX_INI_VALUE_BOOLEAN,
|
||||
VX_INI_VALUE_STRING,
|
||||
VX_INI_VALUE_INTEGER,
|
||||
VX_INI_VALUE_REAL,
|
||||
VX_INI_VALUE_ARRAY
|
||||
|
||||
} vx_ini_value_e;
|
||||
|
||||
struct vx_ini_value
|
||||
{
|
||||
vx_ini_value_e type;
|
||||
union vx_ini_value_specifics
|
||||
{
|
||||
bool boolean;
|
||||
char *string;
|
||||
int64_t integer;
|
||||
double real;
|
||||
struct vx_ini_array
|
||||
{
|
||||
uint32_t values_capacity;
|
||||
uint32_t num_values;
|
||||
vx_ini_value_s *values;
|
||||
|
||||
} array;
|
||||
} specifics;
|
||||
};
|
||||
|
||||
struct vx_ini_field
|
||||
{
|
||||
uint32_t line;
|
||||
uint32_t column;
|
||||
|
||||
uint32_t len_name;
|
||||
char *name;
|
||||
|
||||
vx_ini_value_s value;
|
||||
};
|
||||
|
||||
struct vx_ini_section
|
||||
{
|
||||
uint32_t len_name;
|
||||
char *name;
|
||||
|
||||
uint32_t fields_capacity;
|
||||
uint32_t num_fields;
|
||||
vx_ini_field_s *fields;
|
||||
};
|
||||
|
||||
struct vx_ini
|
||||
{
|
||||
uint32_t sections_capacity;
|
||||
uint32_t num_sections;
|
||||
vx_ini_section_s *sections;
|
||||
|
||||
vx_arena_s *string_arena;
|
||||
};
|
||||
|
||||
vx_ini_s vx_parse_ini(const char *source);
|
||||
void vx_delete_ini(vx_ini_s *ini);
|
||||
|
||||
vx_ini_value_s vx_get_ini(vx_ini_s *ini, const char *field);
|
||||
|
||||
#endif // VOXULA_INI_H
|
|
@ -0,0 +1,64 @@
|
|||
|
||||
#ifndef VOXULA_ASCII_H
|
||||
#define VOXULA_ASCII_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
typedef enum
|
||||
{
|
||||
VX_ASCII_EXCLAMATION_MARK = '!',
|
||||
VX_ASCII_DOUBLE_QUOTATION_MARK = '"',
|
||||
VX_ASCII_NUMBER_SIGN = '#',
|
||||
VX_ASCII_DOLLAR_SIGN = '$',
|
||||
VX_ASCII_PERCENT_SIGN = '%',
|
||||
VX_ASCII_AMPERSAND = '&',
|
||||
VX_ASCII_SINGLE_QUOTATION_MARK = '\'',
|
||||
VX_ASCII_OPENING_PARENTHESIS = '(',
|
||||
VX_ASCII_CLOSING_PARENTHESIS = ')',
|
||||
VX_ASCII_ASTERISK = '*',
|
||||
VX_ASCII_ADDITION_SIGN = '+',
|
||||
VX_ASCII_COMMA = ',',
|
||||
VX_ASCII_HYPHEN = '-',
|
||||
VX_ASCII_DOT = '.',
|
||||
VX_ASCII_FORWARD_SLASH = '/',
|
||||
VX_ASCII_COLON = ':',
|
||||
VX_ASCII_SEMICOLON = ';',
|
||||
VX_ASCII_LESS_THAN_SIGN = '<',
|
||||
VX_ASCII_EQUALS_SIGN = '=',
|
||||
VX_ASCII_GREATER_THAN_SIGN = '>',
|
||||
VX_ASCII_QUESTION_MARK = '?',
|
||||
VX_ASCII_AT_SIGN = '@',
|
||||
VX_ASCII_OPENING_BRACKET = '[',
|
||||
VX_ASCII_BACKSLASH = '\\',
|
||||
VX_ASCII_CLOSING_BRACKET = ']',
|
||||
VX_ASCII_CARET = '^',
|
||||
VX_ASCII_UNDERSCORE = '_',
|
||||
VX_ASCII_BACKTICK = '`',
|
||||
VX_ASCII_OPENING_BRACE = '{',
|
||||
VX_ASCII_VERTICAL_BAR = '|',
|
||||
VX_ASCII_CLOSING_BRACE = '}',
|
||||
VX_ACII_TILDE = '~'
|
||||
|
||||
} vx_ascii_sign_e;
|
||||
|
||||
bool vx_is_ascii_sign(char character);
|
||||
bool vx_is_ascii_sign_of_block_1(char character);
|
||||
bool vx_is_ascii_sign_of_block_2(char character);
|
||||
bool vx_is_ascii_sign_of_block_3(char character);
|
||||
bool vx_is_ascii_sign_of_block_4(char character);
|
||||
|
||||
bool vx_is_ascii_letter(char character);
|
||||
bool vx_is_ascii_lower(char character);
|
||||
bool vx_is_ascii_upper(char character);
|
||||
|
||||
bool vx_is_ascii_digit(char character);
|
||||
|
||||
bool vx_is_ascii_blank(char character);
|
||||
bool vx_is_ascii_control(char character);
|
||||
|
||||
uint8_t vx_digits_for_decimal(uint32_t decimal);
|
||||
void vx_write_spaces(FILE *output, uint32_t count);
|
||||
|
||||
#endif // VOXULA_ASCII_H
|
|
@ -0,0 +1,239 @@
|
|||
#include <voxula/internals/utility/ini.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
char * vx_parse_ini_section_name(
|
||||
vx_arena_s *arena,
|
||||
const char *source,
|
||||
uint32_t offset,
|
||||
uint32_t *len_name
|
||||
) {
|
||||
++offset; // Jump over the starting square bracket
|
||||
uint32_t start_offset = offset;
|
||||
|
||||
while(source[offset] != 0)
|
||||
{
|
||||
if(source[offset] == ']')
|
||||
{
|
||||
++offset;
|
||||
}
|
||||
++offset;
|
||||
}
|
||||
uint32_t name_word_length = offset - start_offset;
|
||||
char *name = malloc(name_word_length + 1);
|
||||
memcpy(name, &source[start_offset], name_word_length);
|
||||
name[name_word_length] = 0;
|
||||
|
||||
if(len_name)
|
||||
{
|
||||
*len_name = name_word_length;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
vx_ini_value_s vx_parse_ini_value(vx_ini_s *ini, const char *source, uint32_t offset, uint32_t *out_length)
|
||||
{
|
||||
// For string values
|
||||
if(source[offset] == '"')
|
||||
{
|
||||
vx_ini_value_s value;
|
||||
uint32_t start_offset = offset;
|
||||
|
||||
if(source[offset + 1] == '"')
|
||||
{
|
||||
// If this is an empty string
|
||||
if(source[offset + 2] != '"')
|
||||
{
|
||||
value.type = VX_INI_VALUE_STRING;
|
||||
value.specifics.string = vx_arena_alloc(ini->string_arena, 1);
|
||||
value.specifics.string[0] = 0;
|
||||
return value;
|
||||
}
|
||||
// If this is a multi-line string
|
||||
offset += 3; // Jump over the quotation marks so they can't be picked up as the ending ones.
|
||||
uint32_t string_data_start = offset;
|
||||
while(source[offset != 0])
|
||||
{
|
||||
if(source[offset] != '"')
|
||||
{
|
||||
++offset;
|
||||
continue;
|
||||
}
|
||||
if(source[offset + 1] != '"')
|
||||
{
|
||||
++offset;
|
||||
continue;
|
||||
}
|
||||
if(source[offset + 2] != '"')
|
||||
{
|
||||
++offset;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
value.type = VX_INI_VALUE_STRING;
|
||||
value.specifics.string = vx_arena_dupe_string_up_to(ini->string_arena, &source[string_data_start], offset - string_data_start);
|
||||
return value;
|
||||
}
|
||||
// A regular, single-line string
|
||||
++offset;
|
||||
while(source[offset] != 0)
|
||||
{
|
||||
if(source[offset] == '\n')
|
||||
{
|
||||
break;
|
||||
}
|
||||
if(source[offset] == '"')
|
||||
{
|
||||
break;
|
||||
}
|
||||
++offset;
|
||||
}
|
||||
value.type = VX_INI_VALUE_STRING;
|
||||
value.specifics.string = vx_arena_dupe_string_up_to(ini->string_arena, &source[start_offset], offset - start_offset);
|
||||
return value;
|
||||
}
|
||||
if(vx_is_ascii_digit(source[offset]))
|
||||
{
|
||||
vx_ini_value_s value;
|
||||
uint32_t start_offset = offset;
|
||||
while(source[offset] != 0)
|
||||
{
|
||||
if( ! vx_is_ascii_digit(source[offset]))
|
||||
{
|
||||
break;
|
||||
}
|
||||
++offset;
|
||||
}
|
||||
// todo(delayed): Add floating-point numbers
|
||||
|
||||
char *integer_string = vx_arena_dupe_string_up_to(ini->string_arena, &source[start_offset], offset - start_offset);
|
||||
|
||||
value.type = VX_INI_VALUE_INTEGER;
|
||||
value.specifics.integer = atol(integer_string);
|
||||
return value;
|
||||
}
|
||||
vx_ini_value_s value;
|
||||
value.type = VX_INI_VALUE_NULL;
|
||||
return value;
|
||||
}
|
||||
|
||||
vx_ini_field_s vx_parse_ini_field(vx_ini_s *ini, const char *source, uint32_t offset, uint32_t *out_length)
|
||||
{
|
||||
uint32_t start_offset = offset;
|
||||
|
||||
// Get the field's name
|
||||
while(source[offset] != 0)
|
||||
{
|
||||
if( ! vx_is_ascii_letter(source[offset])
|
||||
&& (source[offset] != '_'))
|
||||
{
|
||||
break;
|
||||
}
|
||||
++offset;
|
||||
}
|
||||
vx_ini_field_s field;
|
||||
field.len_name = offset - start_offset;
|
||||
field.name = vx_arena_dupe_string_up_to(ini->string_arena, &source[offset], field.len_name);
|
||||
|
||||
uint32_t len_value;
|
||||
field.value = vx_parse_ini_value(ini, source, offset, &len_value);
|
||||
if(out_length)
|
||||
{
|
||||
*out_length = (offset - start_offset) + len_value;
|
||||
}
|
||||
return field;
|
||||
}
|
||||
|
||||
vx_ini_section_s vx_parse_ini_section(vx_ini_s *ini, const char *source, uint32_t offset, uint32_t *out_length)
|
||||
{
|
||||
uint32_t start_offset = offset;
|
||||
vx_ini_section_s section;
|
||||
|
||||
section.name = vx_parse_ini_section_name(ini->string_arena, source, offset, §ion.len_name);
|
||||
offset += section.len_name + 2;
|
||||
section.num_fields = 0;
|
||||
section.fields_capacity = 32;
|
||||
section.fields = malloc(section.fields_capacity * sizeof(vx_ini_field_s));
|
||||
|
||||
while(source[offset] != 0)
|
||||
{
|
||||
// Skip comments
|
||||
if(source[offset] == '#')
|
||||
{
|
||||
while(source[offset] != 0)
|
||||
{
|
||||
if(source[offset] == '\n')
|
||||
{
|
||||
break;
|
||||
}
|
||||
++offset;
|
||||
}
|
||||
++offset;
|
||||
}
|
||||
// Get all the fields of this section
|
||||
while(source[offset] != 0)
|
||||
{
|
||||
if(vx_is_ascii_letter(source[offset]
|
||||
|| (source[offset] == '_'))
|
||||
) {
|
||||
break;
|
||||
}
|
||||
if(source[offset] == '[')
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(source[offset] == '[')
|
||||
{
|
||||
break;
|
||||
}
|
||||
uint32_t bytes_to_jump = 0;
|
||||
vx_parse_ini_field(ini, source, offset, &bytes_to_jump);
|
||||
offset += bytes_to_jump;
|
||||
}
|
||||
++offset;
|
||||
return section;
|
||||
}
|
||||
|
||||
vx_ini_s vx_parse_ini(const char *source)
|
||||
{
|
||||
uint32_t len_source = strlen(source);
|
||||
|
||||
vx_ini_s ini;
|
||||
ini.num_sections = 0;
|
||||
ini.sections_capacity = 64;
|
||||
ini.sections = malloc(ini.sections_capacity * sizeof(vx_ini_section_s));
|
||||
ini.string_arena = vx_new_arena(len_source * 2);
|
||||
|
||||
uint32_t offset = 0;
|
||||
while(offset < len_source)
|
||||
{
|
||||
if(source[offset] == '[')
|
||||
{
|
||||
uint32_t len_section;
|
||||
ini.sections[ini.num_sections] = vx_parse_ini_section(&ini, source, offset, &len_section);
|
||||
offset += len_section;
|
||||
}
|
||||
}
|
||||
return ini;
|
||||
}
|
||||
|
||||
void vx_delete_ini(vx_ini_s *ini)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
vx_ini_value_s vx_get_ini(vx_ini_s *ini, const char *field)
|
||||
{
|
||||
uint32_t splitter = 0;
|
||||
while(field[splitter] != 0)
|
||||
{
|
||||
if(field[splitter] == '.')
|
||||
{
|
||||
break;
|
||||
}
|
||||
++splitter;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,191 @@
|
|||
#include <voxula/internals/utility/text.h>
|
||||
|
||||
bool vx_is_ascii_sign(char character)
|
||||
{
|
||||
if( vx_is_ascii_sign_of_block_1(character)
|
||||
|| vx_is_ascii_sign_of_block_2(character)
|
||||
|| vx_is_ascii_sign_of_block_3(character)
|
||||
|| vx_is_ascii_sign_of_block_4(character)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool vx_is_ascii_sign_of_block_1(char character)
|
||||
{
|
||||
if(character < 0x21)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if(character > 0x2f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool vx_is_ascii_sign_of_block_2(char character)
|
||||
{
|
||||
if(character < 0x3a)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if(character > 0x40)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool vx_is_ascii_sign_of_block_3(char character)
|
||||
{
|
||||
if(character < 0x5b)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if(character > 0x60)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool vx_is_ascii_sign_of_block_4(char character)
|
||||
{
|
||||
if(character < 0x7b)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if(character > 0x7e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool vx_is_ascii_letter(char character)
|
||||
{
|
||||
if( vx_is_ascii_lower(character)
|
||||
|| vx_is_ascii_upper(character)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool vx_is_ascii_lower(char character)
|
||||
{
|
||||
if(character < 'a')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if(character > 'z')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool vx_is_ascii_upper(char character)
|
||||
{
|
||||
if(character < 'A')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if(character > 'Z')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool vx_is_ascii_digit(char character)
|
||||
{
|
||||
if(character < '0')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if(character > '9')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool vx_is_ascii_blank(char character)
|
||||
{
|
||||
if(character == ' ')
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if(character == '\t')
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool vx_is_ascii_control(char character)
|
||||
{
|
||||
if(character < 32)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if(character == 127)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void vx_write_spaces(FILE *output, uint32_t count)
|
||||
{
|
||||
uint32_t space_index = 0;
|
||||
while(space_index < count)
|
||||
{
|
||||
fputc(' ', output);
|
||||
++space_index;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t vx_digits_for_decimal(uint32_t decimal)
|
||||
{
|
||||
if(decimal < 10)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if(decimal < 100)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
if(decimal < 1000)
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
if(decimal < 10000)
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
if(decimal < 100000)
|
||||
{
|
||||
return 5;
|
||||
}
|
||||
if(decimal < 1000000)
|
||||
{
|
||||
return 6;
|
||||
}
|
||||
if(decimal < 10000000)
|
||||
{
|
||||
return 7;
|
||||
}
|
||||
if(decimal < 100000000)
|
||||
{
|
||||
return 8;
|
||||
}
|
||||
if(decimal < 1000000000)
|
||||
{
|
||||
return 9;
|
||||
}
|
||||
return 10;
|
||||
}
|
Loading…
Reference in New Issue