From: Stephan Soller Date: Sat, 11 Feb 2017 10:24:37 +0000 (+0100) Subject: Implemented the basics for new printf() style argument parsing. X-Git-Url: https://git.owens.tech///git?a=commitdiff_plain;h=3f74dbfa39b849ed1ebf3c814f949b96bbeaab65;p=forks%2Fsingle-header-file-c-libs.git Implemented the basics for new printf() style argument parsing. --- diff --git a/.gitignore b/.gitignore index 1b6eb75..eda4ca0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ tests/slim_test_crashtest -tests/math_3d_test -tests/slim_hash_test \ No newline at end of file +tests/*_test \ No newline at end of file diff --git a/Makefile b/Makefile index f8754c6..446cd8c 100644 --- a/Makefile +++ b/Makefile @@ -13,6 +13,7 @@ tests/slim_test_crashtest.c: slim_test.h tests/math_3d_test: math_3d.h slim_test.h tests/math_3d_test: LDLIBS += -lm tests/slim_hash_test: slim_hash.h slim_test.h +tests/slim_gl_test: LDLIBS += -lGL # Clean all files in the .gitignore list, ensures that the ignore file diff --git a/slim_gl.h b/slim_gl.h index 7cd4476..72d3402 100644 --- a/slim_gl.h +++ b/slim_gl.h @@ -81,6 +81,8 @@ License: MIT License // OpenGL program functions // +GLuint sgl_program_new(const char* args, ...); + /** * Compiles a vertex and fragment shader from two files, links them into an OpenGL program reports compiler errors on failure. * @@ -655,6 +657,212 @@ static const char* sgl__type_to_string(GLenum type); static GLuint sgl__create_and_compile_program(const char* vertex_shader_code, const char* fragment_shader_code, const char* vertex_shader_name, const char* fragment_shader_name, char** compiler_errors); static GLuint sgl__create_and_compile_shader(GLenum shader_type, const char* code, const char* filename_for_errors, char** compiler_errors); + +typedef struct { + const char* error_at; + const char* error_message; + // We need a zero terminated string for glGetAttribLocation() so use a buffer instead of pointer + length into the + // argument string. + char name[128]; + char modifiers[16]; + char type; +} sgl_arg_t, *sgl_arg_p; + +/** + * Returns true if a character is a whitespace as defined by the GLSL spec (3.1 Character Set). + * That are spaces and the ASCII range containing horizontal tab, new line, vertical tab, form feed and carriage return. + * If you look at the ASCII table (man ascii) you'll see that all whitespaces except space are consecutive ASCII codes. + * So we cover all these characters with one range check. + */ +static inline int sgl__is_whitespace(char c) { + return c == ' ' || (c >= '\t' && c <= '\r'); +} + +/** + * Returns true if a character is valid for an GLSL identifier (spec chapter 3.7 Identifiers) or a minus sign (-). + * Names starting with a minus are internal options (e.g. -index or -padding) instead of attribute or uniform names. + */ +static inline int sgl__is_name(char c) { + return (c >= 'a' && c <= 'z') || c == '_' || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-'; +} + +#define SGL__NAMED_ARGS (1 << 0) +#define SGL__BUFFER_DIRECTIVES (1 << 1) + +static const char* sgl__next_argument(const char* string, int flags, sgl_arg_p arg) { + const char* c = string; + +#define EXIT_WITH_ERROR_IF(condition, message) if(condition){ arg->error_at = c; arg->error_message = (message); return NULL; } + + // A return value of NULL signals the end of arguments or an error. Return NULL when we're called with a NULL string + // so we can be called repeatedly at the end. It also makes sure we can dereference the string after this. + if (c == NULL) + return NULL; + + // Skip whitespaces + while( sgl__is_whitespace(*c) ) + c++; + + // Return NULL when we're at the zero terminator to signal the end of arguments + if (*c == '\0') + return NULL; + + if (flags & SGL__BUFFER_DIRECTIVES) { + if (*c == ';') { + // Got a buffer end directive + arg->type = *c++; + return c; + } + } + + if (flags & SGL__NAMED_ARGS) { + // Read the name into the name buffer and add the zero-terminator + size_t name_len = 0; + while ( sgl__is_name(*c) ) { + // We can only fill the name buffer up until one byte it left. We need that byte for the zero terminator. + EXIT_WITH_ERROR_IF(name_len >= sizeof(arg->name) - 1, "Name is to long"); + arg->name[name_len++] = *c++; + } + arg->name[name_len] = '\0'; + // If the name isn't followed by a whitespace char we either got an invalid char in a name or the zero terminator. + // Either way we can't continue. + EXIT_WITH_ERROR_IF(!sgl__is_whitespace(*c), "Got invalid character in name"); + + // Skip whitespaces after name + while( sgl__is_whitespace(*c) ) + c++; + } + + EXIT_WITH_ERROR_IF(*c != '%', "Expected at '%' at the start of a directive"); + c++; + + // Read all following chars as modifiers (including the last one for now) + size_t mod_len = 0; + while ( !(sgl__is_whitespace(*c) || *c == '\0') ) { + EXIT_WITH_ERROR_IF(mod_len >= sizeof(arg->modifiers), "To many modifiers for directive"); + arg->modifiers[mod_len++] = *c++; + } + EXIT_WITH_ERROR_IF(mod_len < 1, "At least one character for the type is necessary after a '%'"); + + // The last char in the modifiers buffer is our type so put it into the type field. Then overwrite the last char + // with the zero terminator. + arg->type = arg->modifiers[mod_len-1]; + arg->modifiers[mod_len-1] = '\0'; + + return c; + +#undef EXIT_WITH_ERROR_IF +} + +/* +static const char* sgl__parse_name(const char* string, sgl_arg_p option) { + const char* c = string; + if (c == NULL || *c == '\0') + return NULL; + + // Skip whitespaces + while( sgl__is_whitespace(*c) ) + c++; + + // Read the name into the name buffer and add the zero-terminator + size_t offset = 0; + while ( sgl__is_name(*c) ) { + if (offset >= sizeof(option->name) - 1) { + option->error_at = c; + option->error_message = "Name is to long"; + return NULL; + } + option->name[offset++] = *c++; + } + option->name[offset] = '\0'; + + // Only return the end position if we actually succeeded in reading a name. That is: + // - We read more than 0 characters and + // - the current end pos points to a whitespace or zero terminator + // Otherwise we're stuck inside or at the start of a name and got an invalid character. + if ( offset > 0 && (sgl__is_whitespace(*c) || *c == '\0') ) + return c; + else { + option->error_at = c; + option->error_message = "Got invalid character in name"; + return NULL; + } +} + +static const char* sgl__parse_directive(const char* string, sgl_arg_p option) { + const char* c = string; + if (c == NULL) + return NULL; + + // Skip whitespaces + while( sgl__is_whitespace(*c) ) + c++; + + if (*c == ';') { + option->type = *c++; + return c; + } else if (*c == '%') { + c++; + // Read all following chars as modifiers (including the last one for now) + size_t offset = 0; + while ( !(sgl__is_whitespace(*c) || *c == '\0') ) { + if (offset >= sizeof(option->modifiers)) { + option->error_at = c; + option->error_message = "To many modifiers for directive"; + return NULL; + } + option->modifiers[offset++] = *c++; + } + + if (offset < 1) { + option->error_at = c; + option->error_message = "At least one character is necessary after a '%'"; + return NULL; + } + + // The last char in the modifiers buffer is our type so put it into the type field. Then overwrite the last char + // with the zero terminator. + option->type = option->modifiers[offset-1]; + option->modifiers[offset-1] = '\0'; + + return c; + } else { + option->error_at = c; + option->error_message = "Expected '%' at start of directive or a ';' directive"; + return NULL; + } +} +*/ + + +/* +GLuint sgl_program_new(const char* args, ...) { + directive_t directive; + + while ( (args = next_directive(args, &directive)) != NULL ) { + + } + + char *c = args, *start = NULL, *end = NULL; + while(*c != '\0') { + switch(*c) { + case '%': + c++; + start = c; + while() + break; + case ' ': case '\f': case '\n': case '\r': case '\t': case '\v': + // Ignore whitespaces + break; + default: + // error: unknown character in args parameter + break; + } + c++; + } +} +*/ + GLuint sgl_program_from_files(const char* vertex_shader_file, const char* fragment_shader_file, char** compiler_errors) { char* vertex_shader_code = sgl_fload(vertex_shader_file, NULL); if (vertex_shader_code == NULL) { @@ -736,69 +944,69 @@ void sgl_program_inspect(GLuint program) { */ static const char* sgl__type_to_string(GLenum type) { switch(type){ - case GL_FLOAT: return "float"; - case GL_FLOAT_VEC2: return "vec2"; - case GL_FLOAT_VEC3: return "vec3"; - case GL_FLOAT_VEC4: return "vec4"; - case GL_INT: return "int"; - case GL_INT_VEC2: return "ivec2"; - case GL_INT_VEC3: return "ivec3"; - case GL_INT_VEC4: return "ivec4"; - case GL_UNSIGNED_INT: return "unsigned int"; - case GL_UNSIGNED_INT_VEC2: return "uvec2"; - case GL_UNSIGNED_INT_VEC3: return "uvec3"; - case GL_UNSIGNED_INT_VEC4: return "uvec4"; - case GL_BOOL: return "bool"; - case GL_BOOL_VEC2: return "bvec2"; - case GL_BOOL_VEC3: return "bvec3"; - case GL_BOOL_VEC4: return "bvec4"; - case GL_FLOAT_MAT2: return "mat2"; - case GL_FLOAT_MAT3: return "mat3"; - case GL_FLOAT_MAT4: return "mat4"; - case GL_FLOAT_MAT2x3: return "mat2x3"; - case GL_FLOAT_MAT2x4: return "mat2x4"; - case GL_FLOAT_MAT3x2: return "mat3x2"; - case GL_FLOAT_MAT3x4: return "mat3x4"; - case GL_FLOAT_MAT4x2: return "mat4x2"; - case GL_FLOAT_MAT4x3: return "mat4x3"; - case GL_SAMPLER_1D: return "sampler1D"; - case GL_SAMPLER_2D: return "sampler2D"; - case GL_SAMPLER_3D: return "sampler3D"; - case GL_SAMPLER_CUBE: return "samplerCube"; - case GL_SAMPLER_1D_SHADOW: return "sampler1DShadow"; - case GL_SAMPLER_2D_SHADOW: return "sampler2DShadow"; - case GL_SAMPLER_1D_ARRAY: return "sampler1DArray"; - case GL_SAMPLER_2D_ARRAY: return "sampler2DArray"; - case GL_SAMPLER_1D_ARRAY_SHADOW: return "sampler1DArrayShadow"; - case GL_SAMPLER_2D_ARRAY_SHADOW: return "sampler2DArrayShadow"; - case GL_SAMPLER_CUBE_SHADOW: return "samplerCubeShadow"; - case GL_SAMPLER_BUFFER: return "samplerBuffer"; - case GL_SAMPLER_2D_RECT: return "sampler2DRect"; - case GL_SAMPLER_2D_RECT_SHADOW: return "sampler2DRectShadow"; - case GL_INT_SAMPLER_1D: return "isampler1D"; - case GL_INT_SAMPLER_2D: return "isampler2D"; - case GL_INT_SAMPLER_3D: return "isampler3D"; - case GL_INT_SAMPLER_CUBE: return "isamplerCube"; - case GL_INT_SAMPLER_1D_ARRAY: return "isampler1DArray"; - case GL_INT_SAMPLER_2D_ARRAY: return "isampler2DArray"; - case GL_INT_SAMPLER_BUFFER: return "isamplerBuffer"; - case GL_INT_SAMPLER_2D_RECT: return "isampler2DRect"; - case GL_UNSIGNED_INT_SAMPLER_1D: return "usampler1D"; - case GL_UNSIGNED_INT_SAMPLER_2D: return "usampler2D"; - case GL_UNSIGNED_INT_SAMPLER_3D: return "usampler3D"; - case GL_UNSIGNED_INT_SAMPLER_CUBE: return "usamplerCube"; - case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: return "usampler2DArray"; - case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: return "usampler2DArray"; - case GL_UNSIGNED_INT_SAMPLER_BUFFER: return "usamplerBuffer"; - case GL_UNSIGNED_INT_SAMPLER_2D_RECT: return "usampler2DRect"; + case GL_FLOAT: return "float​"; + case GL_FLOAT_VEC2: return "vec2​"; + case GL_FLOAT_VEC3: return "vec3​"; + case GL_FLOAT_VEC4: return "vec4​"; + case GL_INT: return "int​"; + case GL_INT_VEC2: return "ivec2​"; + case GL_INT_VEC3: return "ivec3​"; + case GL_INT_VEC4: return "ivec4​"; + case GL_UNSIGNED_INT: return "unsigned int​"; + case GL_UNSIGNED_INT_VEC2: return "uvec2​"; + case GL_UNSIGNED_INT_VEC3: return "uvec3​"; + case GL_UNSIGNED_INT_VEC4: return "uvec4​"; + case GL_BOOL: return "bool​"; + case GL_BOOL_VEC2: return "bvec2​"; + case GL_BOOL_VEC3: return "bvec3​"; + case GL_BOOL_VEC4: return "bvec4​"; + case GL_FLOAT_MAT2: return "mat2​"; + case GL_FLOAT_MAT3: return "mat3​"; + case GL_FLOAT_MAT4: return "mat4​"; + case GL_FLOAT_MAT2x3: return "mat2x3​"; + case GL_FLOAT_MAT2x4: return "mat2x4​"; + case GL_FLOAT_MAT3x2: return "mat3x2​"; + case GL_FLOAT_MAT3x4: return "mat3x4​"; + case GL_FLOAT_MAT4x2: return "mat4x2​"; + case GL_FLOAT_MAT4x3: return "mat4x3​"; + case GL_SAMPLER_1D: return "sampler1D​"; + case GL_SAMPLER_2D: return "sampler2D​"; + case GL_SAMPLER_3D: return "sampler3D​"; + case GL_SAMPLER_CUBE: return "samplerCube​"; + case GL_SAMPLER_1D_SHADOW: return "sampler1DShadow​"; + case GL_SAMPLER_2D_SHADOW: return "sampler2DShadow​"; + case GL_SAMPLER_1D_ARRAY: return "sampler1DArray​"; + case GL_SAMPLER_2D_ARRAY: return "sampler2DArray​"; + case GL_SAMPLER_1D_ARRAY_SHADOW: return "sampler1DArrayShadow​"; + case GL_SAMPLER_2D_ARRAY_SHADOW: return "sampler2DArrayShadow​"; + case GL_SAMPLER_CUBE_SHADOW: return "samplerCubeShadow​"; + case GL_SAMPLER_BUFFER: return "samplerBuffer​"; + case GL_SAMPLER_2D_RECT: return "sampler2DRect​"; + case GL_SAMPLER_2D_RECT_SHADOW: return "sampler2DRectShadow​"; + case GL_INT_SAMPLER_1D: return "isampler1D​"; + case GL_INT_SAMPLER_2D: return "isampler2D​"; + case GL_INT_SAMPLER_3D: return "isampler3D​"; + case GL_INT_SAMPLER_CUBE: return "isamplerCube​"; + case GL_INT_SAMPLER_1D_ARRAY: return "isampler1DArray​"; + case GL_INT_SAMPLER_2D_ARRAY: return "isampler2DArray​"; + case GL_INT_SAMPLER_BUFFER: return "isamplerBuffer​"; + case GL_INT_SAMPLER_2D_RECT: return "isampler2DRect​"; + case GL_UNSIGNED_INT_SAMPLER_1D: return "usampler1D​"; + case GL_UNSIGNED_INT_SAMPLER_2D: return "usampler2D​"; + case GL_UNSIGNED_INT_SAMPLER_3D: return "usampler3D​"; + case GL_UNSIGNED_INT_SAMPLER_CUBE: return "usamplerCube​"; + case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: return "usampler2DArray​"; + case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: return "usampler2DArray​"; + case GL_UNSIGNED_INT_SAMPLER_BUFFER: return "usamplerBuffer​"; + case GL_UNSIGNED_INT_SAMPLER_2D_RECT: return "usampler2DRect​"; # ifdef GL_VERSION_3_2 - case GL_SAMPLER_2D_MULTISAMPLE: return "sampler2DMS"; - case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: return "sampler2DMSArray"; - case GL_INT_SAMPLER_2D_MULTISAMPLE: return "isampler2DMS"; - case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: return "isampler2DMSArray"; - case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: return "usampler2DMS"; - case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: return "usampler2DMSArray"; + case GL_SAMPLER_2D_MULTISAMPLE: return "sampler2DMS​"; + case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: return "sampler2DMSArray​"; + case GL_INT_SAMPLER_2D_MULTISAMPLE: return "isampler2DMS​"; + case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: return "isampler2DMSArray​"; + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: return "usampler2DMS​"; + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: return "usampler2DMSArray​"; # endif default: return "unknown"; diff --git a/tests/slim_gl_test.c b/tests/slim_gl_test.c new file mode 100644 index 0000000..d591506 --- /dev/null +++ b/tests/slim_gl_test.c @@ -0,0 +1,342 @@ +#include "../../slim_gl/demos/gl_3_1_core.h" + +#define SLIM_GL_IMPLEMENTATION +#include "../slim_gl.h" +#define SLIM_TEST_IMPLEMENTATION +#include "../slim_test.h" + + +void test_next_argument_basics() { + const char* sample; + const char* next; + sgl_arg_t arg = { 0 }; + + sample = ""; + next = sgl__next_argument(sample, 0, &arg); + st_check_null(next); + st_check_null(arg.error_message); + + sample = " \t \n \v \f \r "; + next = sgl__next_argument(sample, 0, &arg); + st_check_null(next); + st_check_null(arg.error_message); + + sample = NULL; + next = sgl__next_argument(sample, 0, &arg); + st_check_null(next); + st_check_null(arg.error_message); + + sample = ";"; + next = sgl__next_argument(sample, SGL__BUFFER_DIRECTIVES, &arg); + st_check_not_null(next); + st_check_int(arg.type, ';'); + next = sgl__next_argument(next, SGL__BUFFER_DIRECTIVES, &arg); + st_check_null(next); + st_check_null(arg.error_message); + + sample = "foo %4f"; + next = sgl__next_argument(sample, SGL__NAMED_ARGS, &arg); + st_check_not_null(next); + st_check_str(arg.name, "foo"); + st_check_int(arg.type, 'f'); + st_check_str(arg.modifiers, "4"); + + sample = "waaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaay_to_long %4f"; + next = sgl__next_argument(sample, SGL__NAMED_ARGS, &arg); + st_check_null(next); + st_check_not_null(arg.error_message); + printf("error: %s\n%s\n%*s^\n", arg.error_message, sample, (int)(arg.error_at - sample), ""); + + sample = "foo+bar %4f"; + next = sgl__next_argument(sample, SGL__NAMED_ARGS, &arg); + st_check_null(next); + st_check_not_null(arg.error_message); + printf("error: %s\n%s\n%*s^\n", arg.error_message, sample, (int)(arg.error_at - sample), ""); + + sample = "foo"; + next = sgl__next_argument(sample, SGL__NAMED_ARGS, &arg); + st_check_null(next); + st_check_not_null(arg.error_message); + printf("error: %s\n%s\n%*s^\n", arg.error_message, sample, (int)(arg.error_at - sample), ""); + + sample = "x"; + next = sgl__next_argument(sample, 0, &arg); + st_check_null(next); + st_check_not_null(arg.error_message); + printf("error: %s\n%s\n%*s^\n", arg.error_message, sample, (int)(arg.error_at - sample), ""); + + sample = "%"; + next = sgl__next_argument(sample, 0, &arg); + st_check_null(next); + st_check_not_null(arg.error_message); + printf("error: %s\n%s\n%*s^\n", arg.error_message, sample, (int)(arg.error_at - sample), ""); + + sample = "%xxxxxxxxxxxxxxxxf"; + next = sgl__next_argument(sample, 0, &arg); + st_check_null(next); + st_check_not_null(arg.error_message); + printf("error: %s\n%s\n%*s^\n", arg.error_message, sample, (int)(arg.error_at - sample), ""); +} + +void test_next_argument_for_program_new() { + // sgl_program_new() only uses directives, named arguments or buffer directives result in errors + + const char* sample; + const char* next; + sgl_arg_t arg = { 0 }; + + sample = "%G %fV %fF"; + next = sample; + + next = sgl__next_argument(next, 0, &arg); + st_check_not_null(next); + st_check_null(arg.error_message); + st_check_int(arg.type, 'G'); + st_check_str(arg.modifiers, ""); + + next = sgl__next_argument(next, 0, &arg); + st_check_not_null(next); + st_check_null(arg.error_message); + st_check_int(arg.type, 'V'); + st_check_str(arg.modifiers, "f"); + + next = sgl__next_argument(next, 0, &arg); + st_check_not_null(next); + st_check_null(arg.error_message); + st_check_int(arg.type, 'F'); + st_check_str(arg.modifiers, "f"); + + next = sgl__next_argument(next, 0, &arg); + st_check_null(next); + + // Named arguments should give an error + sample = "foo %4f"; + next = sgl__next_argument(sample, 0, &arg); + st_check_null(next); + st_check_not_null(arg.error_message); + printf("error: %s\n%s\n%*s^\n", arg.error_message, sample, (int)(arg.error_at - sample), ""); + + // End of buffer directive should give an error + sample = ";"; + next = sgl__next_argument(sample, 0, &arg); + st_check_null(next); + st_check_not_null(arg.error_message); + printf("error: %s\n%s\n%*s^\n", arg.error_message, sample, (int)(arg.error_at - sample), ""); +} + +void test_next_argument_for_vao_new() { + // sgl_vao_new() uses named arguments as well as the end of buffer directive + + const char* sample; + const char* next; + sgl_arg_t arg = { 0 }; + + sample = "pos %3f ; color %4unb"; + next = sgl__next_argument(sample, SGL__NAMED_ARGS | SGL__BUFFER_DIRECTIVES, &arg); + st_check_not_null(next); + st_check_str(arg.name, "pos"); + st_check_int(arg.type, 'f'); + st_check_str(arg.modifiers, "3"); + + next = sgl__next_argument(next, SGL__NAMED_ARGS | SGL__BUFFER_DIRECTIVES, &arg); + st_check_not_null(next); + st_check_int(arg.type, ';'); + + next = sgl__next_argument(next, SGL__NAMED_ARGS | SGL__BUFFER_DIRECTIVES, &arg); + st_check_not_null(next); + st_check_str(arg.name, "color"); + st_check_int(arg.type, 'b'); + st_check_str(arg.modifiers, "4un"); + + next = sgl__next_argument(next, SGL__NAMED_ARGS | SGL__BUFFER_DIRECTIVES, &arg); + st_check_null(next); + st_check_null(arg.error_message); +} + +void test_next_argument_for_draw() { + // sgl_draw() only uses named directives for the uniforms, end of buffer directives result in errors + + const char* sample; + const char* next; + sgl_arg_t arg = { 0 }; + + sample = "proj %4x4tm light_pos %3f"; + next = sgl__next_argument(sample, SGL__NAMED_ARGS, &arg); + st_check_not_null(next); + st_check_str(arg.name, "proj"); + st_check_int(arg.type, 'm'); + st_check_str(arg.modifiers, "4x4t"); + + next = sgl__next_argument(next, SGL__NAMED_ARGS, &arg); + st_check_not_null(next); + st_check_str(arg.name, "light_pos"); + st_check_int(arg.type, 'f'); + st_check_str(arg.modifiers, "3"); + + next = sgl__next_argument(next, SGL__NAMED_ARGS, &arg); + st_check_null(next); + st_check_null(arg.error_message); + + // End of buffer directive should give an error + sample = ";"; + next = sgl__next_argument(sample, SGL__NAMED_ARGS, &arg); + st_check_null(next); + st_check_not_null(arg.error_message); + printf("error: %s\n%s\n%*s^\n", arg.error_message, sample, (int)(arg.error_at - sample), ""); +} + + +/* +void test_parse_name() { + const char* sample = "foo batz bar"; + sgl_option_t option; + + const char* next = sgl__parse_name(sample, &option); + st_check_str(option.name, "foo"); + st_check_int(option.name[3], '\0'); + st_check_str(next, " batz bar"); + + next = sgl__parse_name(next, &option); + st_check_str(option.name, "batz"); + st_check_int(option.name[4], '\0'); + st_check_str(next, " bar"); + + next = sgl__parse_name(next, &option); + st_check_str(option.name, "bar"); + st_check_int(option.name[3], '\0'); + st_check_str(next, ""); + + next = sgl__parse_name(next, &option); + st_check_null(next); +} + +void test_parse_directive() { + const char* sample = "%4f %unb %4x4tm %*rt"; + sgl_option_t option; + + const char* next = sgl__parse_directive(sample, &option); + st_check_str(option.modifiers, "4"); + st_check_int(option.modifiers[1], '\0'); + st_check_int(option.type, 'f'); + st_check_str(next, " %unb %4x4tm %*rt"); + + next = sgl__parse_directive(next, &option); + st_check_str(option.modifiers, "un"); + st_check_int(option.modifiers[2], '\0'); + st_check_int(option.type, 'b'); + st_check_str(next, " %4x4tm %*rt"); + + next = sgl__parse_directive(next, &option); + st_check_str(option.modifiers, "4x4t"); + st_check_int(option.modifiers[4], '\0'); + st_check_int(option.type, 'm'); + st_check_str(next, " %*rt"); + + next = sgl__parse_directive(next, &option); + st_check_str(option.modifiers, "*r"); + st_check_int(option.modifiers[2], '\0'); + st_check_int(option.type, 't'); + st_check_str(next, ""); + + next = sgl__parse_directive(next, &option); + st_check_null(next); +} + +void test_parse_name_and_directive() { + const char* sample = "pos %4f color %unb -index %s"; + const char* next = sample; + sgl_option_t option; + + next = sgl__parse_name(next, &option); + next = sgl__parse_directive(next, &option); + st_check_str(option.name, "pos"); + st_check_int(option.type, 'f'); + st_check_str(option.modifiers, "4"); + st_check_str(next, " color %unb -index %s"); + + next = sgl__parse_name(next, &option); + next = sgl__parse_directive(next, &option); + st_check_str(option.name, "color"); + st_check_int(option.type, 'b'); + st_check_str(option.modifiers, "un"); + st_check_str(next, " -index %s"); + + next = sgl__parse_name(next, &option); + next = sgl__parse_directive(next, &option); + st_check_str(option.name, "-index"); + st_check_int(option.type, 's'); + st_check_str(option.modifiers, ""); + st_check_str(next, ""); + + next = sgl__parse_name(next, &option); + next = sgl__parse_directive(next, &option); + st_check_null(next); +} + +void test_parse_name_and_directive_errors() { + sgl_option_t option = { 0 }; + + const char* sample = ""; + const char* next = sample; + next = sgl__parse_name(next, &option); + next = sgl__parse_directive(next, &option); + st_check_null(next); + + sample = "waaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaay_to_long %4f"; + next = sample; + next = sgl__parse_name(next, &option); + next = sgl__parse_directive(next, &option); + st_check_null(next); + st_check_not_null(option.error_message); + printf("error: %s\n%s\n%*s^\n", option.error_message, sample, (int)(option.error_at - sample), ""); + + sample = "foo+bar %4f"; + next = sample; + next = sgl__parse_name(next, &option); + next = sgl__parse_directive(next, &option); + st_check_null(next); + st_check_not_null(option.error_message); + printf("error: %s\n%s\n%*s^\n", option.error_message, sample, (int)(option.error_at - sample), ""); + + sample = "foo %"; + next = sample; + next = sgl__parse_name(next, &option); + next = sgl__parse_directive(next, &option); + st_check_null(next); + st_check_not_null(option.error_message); + printf("error: %s\n%s\n%*s^\n", option.error_message, sample, (int)(option.error_at - sample), ""); + + sample = "foo %to_looooooooooooooooongf"; + next = sample; + next = sgl__parse_name(next, &option); + next = sgl__parse_directive(next, &option); + st_check_null(next); + st_check_not_null(option.error_message); + printf("error: %s\n%s\n%*s^\n", option.error_message, sample, (int)(option.error_at - sample), ""); + + sample = "foo bar"; + next = sample; + next = sgl__parse_name(next, &option); + next = sgl__parse_directive(next, &option); + st_check_null(next); + st_check_not_null(option.error_message); + printf("error: %s\n%s\n%*s^\n", option.error_message, sample, (int)(option.error_at - sample), ""); +} +*/ + + +int main() { + /* + st_run(test_parse_name); + st_run(test_parse_directive); + st_run(test_parse_name_and_directive); + st_run(test_parse_name_and_directive_errors); + */ + + st_run(test_next_argument_basics); + st_run(test_next_argument_for_program_new); + st_run(test_next_argument_for_vao_new); + st_run(test_next_argument_for_draw); + + return st_show_report(); +} \ No newline at end of file