├── .gitignore ├── README.md ├── argv.h ├── gpuc.c ├── gpuc.cpp ├── gpuc.h ├── gpuc.inl ├── implementation ├── array │ ├── array.h │ └── array_tests.c ├── assert.h ├── diagnostic.h ├── lexeme.h ├── location.h ├── module.h ├── module.lex.h ├── module.parse.h ├── node.h ├── scan.h ├── semantic.h ├── stream.h ├── tests.h ├── token.h ├── writer.h └── writers │ ├── glsl.h │ ├── hlsl.h │ └── metal.h └── sample.gpuc /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .vscode 3 | *.dSYM 4 | *.exe -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GPUC 2 | 3 | A generic shading language compiler that writes metal, HLSL, and GLSL 4 | 5 | ***GPUC is a work in progress, not ready for prime time.*** 6 | 7 | The primary motivation was to create a header-only shader language translator that has negligible effect on compile times. Including this should not add more than a fraction of a second to your compile time, unlike other shader translators I tried. 8 | 9 | Only the metal backend is actually useful right now, and probably only for simple shaders. The HLSL and GLSL backends have rotted a bit due to some recent changes in the source language. 10 | 11 | I initially wrote this over a two week holiday vacation in December of 2017. I was amazed how easy it was to continue building upon when I returned to it many months later. For me, this was the first reasonably useful thing I implemented in C, and since then I have switched to C for the majority of my hobby projects. 12 | 13 | ## Usage 14 | 15 | ### Header-only library 16 | 17 | ```c 18 | // include API declarations as needed 19 | #include "gpuc/gpuc.h" 20 | ``` 21 | 22 | ```c 23 | // include implementation in one translation unit 24 | #include "gpuc/gpuc.inl" 25 | ``` 26 | 27 | ### Stand-alone compiler 28 | 29 | ```sh 30 | » sh gpuc.c --help 31 | usage: 32 | 33 | gpuc [options] 34 | 35 | options: 36 | 37 | --help display this usage summary 38 | --ast print AST to stdout 39 | --debug print debug summary to stdout 40 | --test run tests 41 | --glsl translate GPUC to GLSL 42 | --hlsl translate GPUC to HLSL 43 | --metal translate GPUC to Metal 44 | --frag write output to , or '-' for stdout 45 | --vert write output to , or '-' for stdout 46 | ``` 47 | 48 | ```sh 49 | » sh gpuc.c sample.gpuc --vert - --frag - --metal 50 | ``` 51 | ```c 52 | #include 53 | #include 54 | 55 | using namespace metal; 56 | 57 | float4 sample(const texture2d t, float2 uv) { 58 | constexpr sampler s(filter::nearest); 59 | return t.sample(s, uv); 60 | } 61 | 62 | float4 sample(const texture2d t, float2 uv, const sampler s) { 63 | return t.sample(s, uv); 64 | } 65 | 66 | struct gpuc { 67 | 68 | // float4 sample(texture2d, float2); 69 | 70 | // float4 sample(texture2d, float2, sampler); 71 | 72 | struct Camera { 73 | float4x4 mvp; 74 | float4x4 mvn; 75 | }; 76 | 77 | struct Vertex { 78 | float3 position [[attribute(0)]]; 79 | float4 color [[attribute(1)]]; 80 | float4 texcoords [[attribute(2)]]; 81 | }; 82 | 83 | struct Fragment { 84 | float4 position [[position]]; 85 | float4 color; 86 | float4 texcoords; 87 | }; 88 | 89 | struct Sample { 90 | float4 color; 91 | }; 92 | 93 | constant const Camera& cam; 94 | 95 | const texture2d color; 96 | 97 | const sampler samp; 98 | 99 | Fragment vert(const Vertex v) const { 100 | Fragment f; 101 | f.position = cam.mvp * float4(v.position, 1); 102 | f.color = v.color; 103 | f.texcoords = v.texcoords; 104 | return f; 105 | } 106 | 107 | }; 108 | 109 | vertex gpuc::Fragment vert( 110 | constant const gpuc::Camera& cam [[buffer(0)]], 111 | texture2d color [[texture(0)]], 112 | sampler samp [[sampler(0)]], 113 | const gpuc::Vertex v [[stage_in]] 114 | ) { 115 | return gpuc{cam, color, samp}.vert(v); 116 | } 117 | #include 118 | #include 119 | 120 | using namespace metal; 121 | 122 | float4 sample(const texture2d t, float2 uv) { 123 | constexpr sampler s(filter::nearest); 124 | return t.sample(s, uv); 125 | } 126 | 127 | float4 sample(const texture2d t, float2 uv, const sampler s) { 128 | return t.sample(s, uv); 129 | } 130 | 131 | struct gpuc { 132 | 133 | // float4 sample(texture2d, float2); 134 | 135 | // float4 sample(texture2d, float2, sampler); 136 | 137 | struct Camera { 138 | float4x4 mvp; 139 | float4x4 mvn; 140 | }; 141 | 142 | struct Vertex { 143 | float3 position; 144 | float4 color; 145 | float4 texcoords; 146 | }; 147 | 148 | struct Fragment { 149 | float4 position [[position]]; 150 | float4 color; 151 | float4 texcoords; 152 | }; 153 | 154 | struct Sample { 155 | float4 color; 156 | }; 157 | 158 | constant const Camera& cam; 159 | 160 | const texture2d color; 161 | 162 | const sampler samp; 163 | 164 | Sample frag(const Fragment f) const { 165 | Sample s; 166 | s.color = sample(color, f.texcoords.st, samp); 167 | return s; 168 | } 169 | 170 | }; 171 | 172 | fragment gpuc::Sample frag( 173 | constant const gpuc::Camera& cam [[buffer(0)]], 174 | texture2d color [[texture(0)]], 175 | sampler samp [[sampler(0)]], 176 | const gpuc::Fragment f [[stage_in]] 177 | ) { 178 | return gpuc{cam, color, samp}.frag(f); 179 | } 180 | ``` -------------------------------------------------------------------------------- /argv.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef ARGV_H_7D43C499_77B8_4062_8F43_245354FD2C4C 3 | #define ARGV_H_7D43C499_77B8_4062_8F43_245354FD2C4C 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif // __cplusplus 16 | 17 | 18 | //------------------------------------------------------------------------------ 19 | 20 | 21 | static inline 22 | bool arg_skip_prefix(const char** arg, const char* const prefix) { 23 | const char* p = prefix; 24 | const char* a = *arg; 25 | while (*p and *a and *p == *a) { ++p; ++a; } 26 | if (*p == '\0') { 27 | // string starts with prefix 28 | *arg = a; 29 | return true; 30 | } 31 | return false; 32 | } 33 | 34 | 35 | static inline 36 | bool arg_skip_name(const char** arg, const char* const name) { 37 | if (name) { 38 | const char* s = *arg; 39 | if (arg_skip_prefix(&s, "--") and 40 | arg_skip_prefix(&s, name)) { 41 | *arg = s; 42 | return true; 43 | } 44 | } 45 | return false; 46 | } 47 | 48 | 49 | static inline 50 | bool arg_skip_flag(const char** arg, const char flag) { 51 | if (flag) { 52 | const char* s = *arg; 53 | if (*s++ == '-' and *s++ == flag) { 54 | *arg = s; 55 | return true; 56 | } 57 | } 58 | return false; 59 | } 60 | 61 | 62 | static inline 63 | bool arg_skip_name_or_flag( 64 | const char** arg, 65 | const char* const name, 66 | const char flag) 67 | { 68 | return arg_skip_name(arg, name) 69 | or arg_skip_flag(arg, flag); 70 | } 71 | 72 | 73 | //------------------------------------------------------------------------------ 74 | 75 | 76 | static inline 77 | bool arg_to_char(const char* arg, char* out) 78 | { 79 | if (arg == NULL) return false; 80 | if (arg[0] == 0) return false; 81 | if (arg[1] != 0) return false; 82 | *out = *arg; 83 | return true; 84 | } 85 | 86 | 87 | static inline 88 | bool arg_to_int(const char* arg, int* out) 89 | { 90 | if (arg == NULL) return false; 91 | char* end = NULL; 92 | const long l = strtol(arg, &end, 0); 93 | if (end <= arg) return false; 94 | if (l < INT_MIN or l > INT_MAX) { 95 | printf("error: argument out of range: %s\n", arg); 96 | exit(1); 97 | } 98 | *out = (int)l; 99 | return true; 100 | } 101 | 102 | 103 | static inline 104 | bool arg_to_unsigned(const char* arg, unsigned* out) 105 | { 106 | if (arg == NULL) return false; 107 | char* end = NULL; 108 | const unsigned long ul = strtoul(arg, &end, 0); 109 | if (end <= arg) return false; 110 | if (ul > ~0u) { 111 | printf("error: argument out of range: %s\n", arg); 112 | exit(1); 113 | } 114 | *out = (unsigned)ul; 115 | return true; 116 | } 117 | #define arg_to_uint arg_to_unsigned 118 | 119 | 120 | static inline 121 | bool arg_to_float(const char* arg, float* out) 122 | { 123 | if (arg == NULL) return false; 124 | char* end = NULL; 125 | const float f = strtof(arg, &end); 126 | if (end <= arg) return false; 127 | *out = f; 128 | return true; 129 | } 130 | 131 | 132 | static inline 133 | bool arg_to_double(const char* arg, double* out) 134 | { 135 | if (arg == NULL) return false; 136 | char* end = NULL; 137 | const double d = strtod(arg, &end); 138 | if (end <= arg) return false; 139 | *out = d; 140 | return true; 141 | } 142 | 143 | 144 | //------------------------------------------------------------------------------ 145 | 146 | 147 | const char* argv_consume(int* argc, const char** argp) { 148 | const char* const arg = *argp; 149 | while (*argp) { *argc -= 1; argp[0] = argp[1]; ++argp; } 150 | return arg; 151 | } 152 | 153 | 154 | //------------------------------------------------------------------------------ 155 | 156 | 157 | typedef struct argv_bool_params { 158 | const char* const name; 159 | const char flag; 160 | bool init; 161 | } argv_bool_params; 162 | 163 | 164 | bool argv_bool(int* argc, const char** argv, argv_bool_params p) { 165 | const char** argp = argv + 1; 166 | while (*argp) { 167 | const char* arg = *argp; 168 | if (arg_skip_name_or_flag(&arg, p.name, p.flag)) { 169 | if (arg[0] == 0) { 170 | argv_consume(argc, argp); return true; 171 | } 172 | if (arg[0] == '=') { 173 | printf("error: invalid argument %s\n", *argp); 174 | exit(1); 175 | } 176 | } 177 | ++argp; 178 | } 179 | return p.init; 180 | } 181 | 182 | 183 | //------------------------------------------------------------------------------ 184 | 185 | 186 | typedef struct argv_string_params { 187 | const char* const name; 188 | const char flag; 189 | const char* init; 190 | } argv_string_params; 191 | 192 | 193 | const char* argv_string(int* argc, const char** argv, argv_string_params p) { 194 | const char** argp = argv + 1; 195 | const bool unnamed = not p.name and not p.flag; 196 | while (*argp) { 197 | const char* arg = *argp; 198 | if (unnamed) { 199 | return argv_consume(argc, argp); 200 | } 201 | if (arg_skip_name_or_flag(&arg, p.name, p.flag)) { 202 | if (arg[0] == 0) { 203 | argv_consume(argc, argp); return argv_consume(argc, argp); 204 | } 205 | if (arg[0] == '=') { 206 | argv_consume(argc, argp); return ++arg; 207 | } 208 | } 209 | ++argp; 210 | } 211 | return p.init; 212 | } 213 | #define argv_string_(argc, argv, name, flag) \ 214 | argv_string(argc, argv, (argv_string_params){ name, flag }) 215 | 216 | 217 | //------------------------------------------------------------------------------ 218 | 219 | 220 | typedef struct argv_char_params { 221 | const char* const name; 222 | const char flag; 223 | char init; 224 | } argv_char_params; 225 | 226 | 227 | char argv_char(int* argc, const char** argv, argv_char_params p) { 228 | const char* arg = argv_string_(argc, argv, p.name, p.flag); 229 | if (arg and not arg_to_char(arg, &p.init)) { 230 | printf("error: expected letter '%s'\n", arg); 231 | exit(1); 232 | } 233 | return p.init; 234 | } 235 | 236 | 237 | //------------------------------------------------------------------------------ 238 | 239 | 240 | typedef struct argv_int_params { 241 | const char* const name; 242 | const char flag; 243 | int init; 244 | } argv_int_params; 245 | 246 | 247 | int argv_int(int* argc, const char** argv, argv_int_params p) { 248 | const char* arg = argv_string_(argc, argv, p.name, p.flag); 249 | if (arg and not arg_to_int(arg, &p.init)) { 250 | printf("error: expected number: '%s'\n", arg); 251 | exit(1); 252 | } 253 | return p.init; 254 | } 255 | 256 | 257 | //------------------------------------------------------------------------------ 258 | 259 | 260 | typedef struct argv_unsigned_params { 261 | const char* const name; 262 | const char flag; 263 | unsigned init; 264 | } argv_unsigned_params; 265 | #define argv_uint_params argv_unsigned_params 266 | 267 | 268 | unsigned argv_unsigned(int* argc, const char** argv, argv_unsigned_params p) { 269 | const char* arg = argv_string_(argc, argv, p.name, p.flag); 270 | if (arg and not arg_to_unsigned(arg, &p.init)) { 271 | printf("error: expected number: '%s'\n", arg); 272 | exit(1); 273 | } 274 | return p.init; 275 | } 276 | #define argv_uint argv_unsigned 277 | 278 | 279 | //------------------------------------------------------------------------------ 280 | 281 | 282 | typedef struct argv_float_params { 283 | const char* const name; 284 | const char flag; 285 | float init; 286 | } argv_float_params; 287 | 288 | 289 | float argv_float(int* argc, const char** argv, argv_float_params p) { 290 | const char* arg = argv_string_(argc, argv, p.name, p.flag); 291 | if (arg and not arg_to_float(arg, &p.init)) { 292 | printf("error: expected number: '%s'\n", arg); 293 | exit(1); 294 | } 295 | return p.init; 296 | } 297 | 298 | 299 | //------------------------------------------------------------------------------ 300 | 301 | 302 | typedef struct argv_double_params { 303 | const char* const name; 304 | const char flag; 305 | double init; 306 | } argv_double_params; 307 | 308 | 309 | double argv_double(int* argc, const char** argv, argv_double_params p) { 310 | const char* arg = argv_string_(argc, argv, p.name, p.flag); 311 | if (arg and not arg_to_double(arg, &p.init)) { 312 | printf("error: expected number: '%s'\n", arg); 313 | exit(1); 314 | } 315 | return p.init; 316 | } 317 | 318 | 319 | //------------------------------------------------------------------------------ 320 | 321 | 322 | /// argv(type, name[, flag[, init]]) 323 | #define argv(TYPE, NAME, ...) \ 324 | argv_##TYPE(&argc, argv, (argv_##TYPE##_params){ NAME, __VA_ARGS__ }) 325 | 326 | 327 | //------------------------------------------------------------------------------ 328 | 329 | 330 | void argv_print_arguments(const char** argv) { 331 | while (*argv) puts(*argv++); 332 | } 333 | 334 | 335 | int argv_print_illegal_arguments(const char** argv) { 336 | int count = 0; 337 | const char** argp = argv + 1; 338 | while (*argp) { 339 | printf("error: illegal argument: '%s'\n", *argp++); 340 | ++count; 341 | } 342 | return count; 343 | } 344 | 345 | 346 | //------------------------------------------------------------------------------ 347 | 348 | 349 | #ifdef __cplusplus 350 | } // extern "C" 351 | #endif // __cplusplus 352 | 353 | 354 | #endif // ARGV_H_7D43C499_77B8_4062_8F43_245354FD2C4C -------------------------------------------------------------------------------- /gpuc.c: -------------------------------------------------------------------------------- 1 | ///usr/bin/env \ 2 | [ -n "${PATHEXT}" ] && ext='.exe'; \ 3 | bin="$(dirname $0)/$(basename ${0%.*})$ext"; \ 4 | cc -std=c11 -Werror -o $bin $0 \ 5 | && \ 6 | $bin "$@"; \ 7 | status=$?; \ 8 | rm $bin; \ 9 | exit $status 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "gpuc.inl" 17 | #include "implementation/array/array_tests.c" 18 | #include "argv.h" 19 | 20 | //------------------------------------------------------------------------------ 21 | 22 | static inline 23 | const char* fload(const char* const path) { 24 | if (not path) 25 | return NULL; 26 | 27 | FILE* const file = fopen(path, "r"); 28 | if (not file) 29 | return NULL; 30 | 31 | fseek(file, 0, SEEK_END); 32 | const long size = ftell(file); 33 | rewind(file); 34 | 35 | char* const data = (char*)calloc(size + 1, sizeof(char)); 36 | fread(data, sizeof(char), size, file); 37 | fclose(file); 38 | 39 | return data; 40 | } 41 | 42 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 43 | 44 | static inline 45 | bool fsave(const char* const path, const char* const data) { 46 | FILE* const file = strcmp(path, "-") ? fopen(path, "w") : stdout; 47 | if (not file) 48 | return false; 49 | 50 | fwrite(data, sizeof(char), strlen(data), file); 51 | 52 | if (file != stdout) 53 | fclose(file); 54 | 55 | return true; 56 | } 57 | 58 | //------------------------------------------------------------------------------ 59 | 60 | #ifdef __cplusplus 61 | extern "C" 62 | #endif // __cplusplus 63 | void enableUTF8(void) { 64 | #ifdef _WIN32 65 | // enable UTF-8 output on Windows 66 | extern int SetConsoleOutputCP(unsigned wCodePageID); 67 | SetConsoleOutputCP(65001); 68 | #endif 69 | } 70 | 71 | //------------------------------------------------------------------------------ 72 | 73 | int main(int argc, const char* argv[], const char* envp[]) { 74 | enableUTF8(); 75 | 76 | const bool help = argv(bool, "help"); 77 | const bool ast = argv(bool, "ast"); 78 | const bool debug = argv(bool, "debug"); 79 | const bool test = argv(bool, "test"); 80 | const bool glsl = argv(bool, "glsl"); 81 | const bool hlsl = argv(bool, "hlsl"); 82 | const bool metal = argv(bool, "metal"); 83 | const char* const frag = argv(string, "frag"); 84 | const char* const vert = argv(string, "vert"); 85 | const char* const file = argv(string, NULL); 86 | 87 | if (argv_print_illegal_arguments(argv)) { 88 | return 1; 89 | } 90 | 91 | if (help or not file) { 92 | const char* name = argv[0]; 93 | for (const char* p = name; *p; ++p) { 94 | if (*p == '\\' or *p == '/') 95 | name = p + 1; 96 | } 97 | puts("usage:\n"); 98 | printf(" %s [options] \n\n", name); 99 | puts("options:\n"); 100 | puts(" --help display this usage summary"); 101 | puts(" --ast print AST to stdout"); 102 | puts(" --debug print debug summary to stdout"); 103 | puts(" --test run tests"); 104 | puts(" --glsl translate GPUC to GLSL"); 105 | puts(" --hlsl translate GPUC to HLSL"); 106 | puts(" --metal translate GPUC to Metal"); 107 | puts(" --frag write output to , or '-' for stdout"); 108 | puts(" --vert write output to , or '-' for stdout"); 109 | return 1; 110 | } 111 | 112 | if (test) { 113 | array_tests(); 114 | if (gpucTests()) { 115 | return 1; 116 | } 117 | } 118 | 119 | const char* const source = fload(file); 120 | if (not source) { 121 | printf("file not found: '%s'\n", file?file:""); 122 | return 1; 123 | } 124 | 125 | GpucModule* const module = gpucModuleAlloc(file, source); 126 | if (not gpucModuleCompile(module)) { 127 | if (debug) { 128 | gpucModulePrintDebug(module); 129 | } else { 130 | gpucModulePrintDiagnostics(module); 131 | } 132 | gpucModuleFree(module); 133 | free((void*)source); 134 | return 1; 135 | } 136 | 137 | if (ast) { 138 | gpucModulePrintAST(module); 139 | } 140 | 141 | if (debug) { 142 | gpucModulePrintDebug(module); 143 | } 144 | 145 | GpucLanguage language = GpucLanguage_GPUC; 146 | if (glsl) { language = GpucLanguage_GLSL_330; } 147 | if (hlsl) { language = GpucLanguage_HLSL_11; } 148 | if (metal) { language = GpucLanguage_Metal_1; } 149 | 150 | if (vert) { 151 | const GpucStage stage = GpucStage_Vertex; 152 | const char* out = gpucModuleGetTranslation(module, stage, language); 153 | fsave(vert, out); 154 | } 155 | 156 | if (frag) { 157 | const GpucStage stage = GpucStage_Fragment; 158 | const char* out = gpucModuleGetTranslation(module, stage, language); 159 | fsave(frag, out); 160 | } 161 | 162 | gpucModuleFree(module); 163 | free((void*)source); 164 | 165 | return 0; 166 | } -------------------------------------------------------------------------------- /gpuc.cpp: -------------------------------------------------------------------------------- 1 | ///usr/bin/env \ 2 | [ -n "${PATHEXT}" ] && ext='.exe'; \ 3 | bin="$(dirname $0)/$(basename ${0%.*})$ext"; \ 4 | c++ -std=c++11 -Werror -o $bin $0 \ 5 | && \ 6 | $bin "$@"; \ 7 | status=$?; \ 8 | rm -f $bin; \ 9 | exit $status 10 | 11 | #include "gpuc.c" -------------------------------------------------------------------------------- /gpuc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifdef GPUC_H_25A109C0_B38B_4E2F_BFDD_8B6B28467F01 3 | #if GPUC_H_25A109C0_B38B_4E2F_BFDD_8B6B28467F01 < 100 ||\ 4 | GPUC_H_25A109C0_B38B_4E2F_BFDD_8B6B28467F01 > 199 5 | #error "expected gpuc v1" 6 | #endif 7 | #else 8 | #define GPUC_H_25A109C0_B38B_4E2F_BFDD_8B6B28467F01 100 9 | #include 10 | 11 | //------------------------------------------------------------------------------ 12 | 13 | #ifdef __cplusplus 14 | #define GPUC_EXTERN_C_BEGIN extern "C" { 15 | #define GPUC_EXTERN_C_END } 16 | #else 17 | #define GPUC_EXTERN_C_BEGIN /* extern "C" { */ 18 | #define GPUC_EXTERN_C_END /* } */ 19 | #endif // __cplusplus 20 | 21 | //------------------------------------------------------------------------------ 22 | 23 | #define GPUC(GPUC) _GPUC(GPUC) 24 | #define _GPUC(GPUC) #GPUC 25 | 26 | //------------------------------------------------------------------------------ 27 | 28 | GPUC_EXTERN_C_BEGIN 29 | 30 | //------------------------------------------------------------------------------ 31 | 32 | typedef struct GpucSubstring { 33 | const char* head; 34 | unsigned length; 35 | } GpucSubstring; 36 | 37 | //------------------------------------------------------------------------------ 38 | 39 | typedef struct GpucLocation { 40 | GpucSubstring file; 41 | GpucSubstring site; 42 | GpucSubstring context; 43 | unsigned line; 44 | unsigned column; 45 | } GpucLocation; 46 | 47 | size_t gpucLocationStringWrite(char** out, const GpucLocation*); 48 | 49 | size_t gpucLocationStringLength(const GpucLocation*); 50 | 51 | const char* gpucLocationStringAlloc(const GpucLocation*); 52 | 53 | void gpucLocationPrint(const GpucLocation*); 54 | 55 | //------------------------------------------------------------------------------ 56 | 57 | typedef enum GpucDiagnosticLevel { 58 | GpucDiagnosticLevel_Note, 59 | GpucDiagnosticLevel_Warning, 60 | GpucDiagnosticLevel_Error, 61 | } GpucDiagnosticLevel; 62 | 63 | typedef struct GpucDiagnostic { 64 | GpucDiagnosticLevel level; 65 | GpucLocation location; 66 | char message[]; 67 | } GpucDiagnostic; 68 | 69 | size_t gpucDiagnosticStringWrite(char** out, const GpucDiagnostic*); 70 | 71 | size_t gpucDiagnosticStringLength(const GpucDiagnostic*); 72 | 73 | const char* gpucDiagnosticStringAlloc(const GpucDiagnostic*); 74 | 75 | void gpucDiagnosticPrint(const GpucDiagnostic*); 76 | 77 | void gpucDiagnosticPrintLine(const GpucDiagnostic*); 78 | 79 | 80 | //------------------------------------------------------------------------------ 81 | 82 | typedef enum GpucLanguage { 83 | GpucLanguage_Default, 84 | 85 | #define GpucLanguage(A,B,C,D)\ 86 | ((unsigned)(\ 87 | (((unsigned)(A)) << 24)|\ 88 | (((unsigned)(B)) << 16)|\ 89 | (((unsigned)(C)) << 8)|\ 90 | (((unsigned)(D)) << 0))) 91 | 92 | GpucLanguage_GPUC = GpucLanguage('G','P', 0,0), 93 | GpucLanguage_GLSL_330 = GpucLanguage('G','L', 3,3), 94 | GpucLanguage_HLSL_11 = GpucLanguage('H','L',11,0), 95 | GpucLanguage_Metal_1 = GpucLanguage('M','E', 1,0), 96 | 97 | #undef GpucLanguage 98 | } GpucLanguage; 99 | 100 | typedef enum GpucStage { 101 | GpucStage_None, 102 | GpucStage_Compute, 103 | GpucStage_Vertex, 104 | GpucStage_Fragment, 105 | } GpucStage; 106 | 107 | //------------------------------------------------------------------------------ 108 | 109 | typedef struct GpucModule GpucModule; 110 | 111 | GpucModule* gpucModuleAlloc(const char* file, const char* source); 112 | 113 | void gpucModuleFree(GpucModule*); 114 | 115 | bool gpucModuleCompile(GpucModule*); 116 | 117 | void gpucModulePrintDebug(const GpucModule*); 118 | 119 | void gpucModulePrintDiagnostics(const GpucModule* const module); 120 | 121 | void gpucModulePrintLexemes(const GpucModule* const module); 122 | 123 | void gpucModulePrintAST(const GpucModule* const module); 124 | 125 | const GpucDiagnostic* gpucModuleGetDiagnostic(const GpucModule*, unsigned index); 126 | 127 | const char* gpucModuleGetTranslation( 128 | GpucModule*, 129 | GpucStage, 130 | GpucLanguage); 131 | 132 | //------------------------------------------------------------------------------ 133 | 134 | int gpucTests(void); 135 | 136 | //------------------------------------------------------------------------------ 137 | 138 | GPUC_EXTERN_C_END 139 | 140 | #endif // GPUC_H_25A109C0_B38B_4E2F_BFDD_8B6B28467F01 141 | -------------------------------------------------------------------------------- /gpuc.inl: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "implementation/module.h" 3 | -------------------------------------------------------------------------------- /implementation/array/array.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file array.h 3 | @author Garett Bass (https://github.com/garettbass) 4 | @copyright Copyright (c) 2016 Garett Bass (https://github.com/garettbass) 5 | 6 | The MIT License (MIT) 7 | Copyright (c) 2016 Garett Bass (https://github.com/garettbass) 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy of 10 | this software and associated documentation files (the "Software"), to deal in 11 | the Software without restriction, including without limitation the rights to 12 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 13 | of the Software, and to permit persons to whom the Software is furnished to do 14 | so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in all 17 | copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | SOFTWARE. 26 | */ 27 | #pragma once 28 | #ifdef ARRAY_H_6BD98208_07AE_43B7_98CF_8B81AD3BEABF 29 | #if ARRAY_H_6BD98208_07AE_43B7_98CF_8B81AD3BEABF != 100 30 | #error "expected version 1.0.0" 31 | #endif 32 | #else 33 | #define ARRAY_H_6BD98208_07AE_43B7_98CF_8B81AD3BEABF 100 34 | #include 35 | 36 | 37 | #ifdef __cplusplus 38 | extern "C" { 39 | #endif // __cplusplus 40 | 41 | 42 | #ifndef array_allocator 43 | static inline void* array_allocator(void* ptr, size_t size) { 44 | #ifndef realloc 45 | extern void* realloc(void* ptr, size_t size); 46 | #endif 47 | #ifndef free 48 | extern void free(void* ptr); 49 | #endif 50 | return size ? (realloc(ptr, size)) : ((void)free(ptr), (void*)NULL); 51 | } 52 | #endif 53 | 54 | 55 | //------------------------------------------------------------------------------ 56 | 57 | 58 | #define array_t(T) T* 59 | 60 | 61 | // void array_alloc(T*& a, size_t capacity, void (*destructor)(T* begin, T* end)) 62 | #define array_alloc(a, capacity, destructor) \ 63 | (_array_alloc(_array_ptr((a)), (capacity ? capacity : 1) * _array_stride((a)), (_array_destructor_t)(destructor), (array_allocator))) 64 | /**< Allocates initial storage for a dynamic array. 65 | 66 | @param a - the array for which storage will be allocated 67 | @param capacity - the initial capacity of the dynamic array 68 | @param destructor - an optional destructor to be called by array_remove, array_clear, and array_free 69 | 70 | @code{.c} 71 | array_t(int) ia = NULL; 72 | array_alloc(ia, 16, NULL); // NULL destructor for POD types 73 | 74 | // ... 75 | 76 | typedef array_t(FILE*) file_array; 77 | void file_array_destructor(FILE** itr, FILE** end) { 78 | for (; itr < end; ++itr) fclose(*itr); 79 | } 80 | 81 | // ... 82 | 83 | file_array files = NULL; 84 | array_alloc(files, 16, file_array_destructor); // destructor closes FILE* 85 | array_append(files, fopen("some/file", "r")); 86 | @endcode 87 | @hideinitializer **/ 88 | 89 | 90 | // void array_alloc2(T*& a, size_t capacity, void (*destructor)(T* begin, T* end), void* (*allocator)(void* ptr, size_t size)) 91 | #define array_alloc2(a, capacity, destructor, allocator) \ 92 | (_array_alloc(_array_ptr((a)), (capacity ? capacity : 1) * _array_stride((a)), (_array_destructor_t)(destructor), (allocator))) 93 | /**< Allocates initial storage for a dynamic array with provided allocator. 94 | 95 | @param a - the array for which storage will be allocated 96 | @param capacity - the initial capacity of the dynamic array 97 | @param destructor - an optional destructor to be called by array_remove, array_clear, and array_free 98 | @param allocator - a specific allocator for the array 99 | @hideinitializer **/ 100 | 101 | 102 | // void array_free(T*& a) 103 | #define array_free(a) \ 104 | (_array_free(_array_ptr((a)))) 105 | /**< Frees storage held by a dynamic array. 106 | @hideinitializer **/ 107 | 108 | 109 | // void array_reserve(T*& a, size_t capacity) 110 | #define array_reserve(a, capacity) \ 111 | (_array_reserve(_array_ptr((a)), (capacity) * _array_stride((a)))) 112 | /**< Reserves additional storage for a dynamic array. 113 | 114 | After a call to array_reserve(), the dynamic array's capacity will be at least 115 | as large as the requested capacity, possibly larger. 116 | @hideinitializer **/ 117 | 118 | 119 | // void array_resize(T*& a, size_t size) 120 | #define array_resize(a, size) \ 121 | (_array_resize(_array_ptr((a)), (size) * _array_stride((a)))) 122 | /**< Resizes the dynamic array. 123 | 124 | If the new size is smaller than the old size, old elements are destructed. 125 | If the new size is greater than before, new elements are zero-initialized. 126 | @hideinitializer **/ 127 | 128 | 129 | // void array_shrink(T*& a) 130 | #define array_shrink(a) \ 131 | (_array_shrink(_array_ptr((a)))) 132 | /**< Shrinks the dynamic array's storage to fit the array's current size. 133 | @hideinitializer **/ 134 | 135 | 136 | // size_t array_capacity(T* a) 137 | #define array_capacity(a) \ 138 | (_array_capacity(_array_ptr((a))) / _array_stride((a))) 139 | /**< Returns the number of elements that can fit in the dynamic array's storage 140 | without allocating additional memory, or zero for NULL arrays. 141 | @hideinitializer **/ 142 | 143 | 144 | // int array_compare(T* a, T* a) 145 | #define array_compare(a, b) \ 146 | (_array_compare(_array_ptr((a)), _array_ptr((b)))) 147 | /**< Performs a lexicographic comparison of two arrays using memcmp(). 148 | @hideinitializer **/ 149 | 150 | 151 | // size_t array_size(T* a) 152 | #define array_size(a) \ 153 | (_array_size(_array_ptr((a))) / _array_stride((a))) 154 | /**< Returns the number of elements stored in the dynamic array, or zero for 155 | NULL arrays. 156 | @hideinitializer **/ 157 | 158 | 159 | #define array_length(a) array_size(a) 160 | 161 | 162 | // bool array_empty(T* a) 163 | #define array_empty(a) \ 164 | ((bool)(_array_size(_array_ptr((a))) == 0)) 165 | /**< Returns true if the dynamic array's size is zero. 166 | @hideinitializer **/ 167 | 168 | 169 | // T& array_front(T* array) 170 | #define array_front(a) \ 171 | ((a)[ _array_front_index(_array_ptr((a))) ]) 172 | /**< Returns a reference to the first element in the dynamic array. 173 | An assertion will fail if the array is empty. 174 | @hideinitializer **/ 175 | 176 | 177 | // T& array_back(T* array) 178 | #define array_back(a) \ 179 | ((a)[ _array_back_index(_array_ptr((a)), _array_stride((a))) ]) 180 | /**< Returns a reference to the final element in the dynamic array. 181 | An assertion will fail if the array is empty. 182 | @hideinitializer **/ 183 | 184 | 185 | // void array_append(T*& array, T value) 186 | #define array_append(a, v) \ 187 | ( _array_append(_array_ptr((a)), _array_stride((a))), (array_back(a) = (v)) ) 188 | /**< Appends a single element to the dynamic array, allocating additional 189 | storage if necessary. 190 | 191 | @code{.c} 192 | array_t(int) ia = NULL; 193 | array_alloc(ia, 16, NULL); 194 | // ... 195 | array_append(ia, 123); 196 | assert(ia[0] == 123); 197 | @endcode 198 | @hideinitializer **/ 199 | 200 | 201 | // void array_insert(T*& a, size_t index, T value) 202 | #define array_insert(a, index, v) \ 203 | ( _array_insert(_array_ptr((a)), _array_offset((a), (index)), _array_stride((a))), (a)[index] = v ) 204 | /**< Inserts a single element at the provided index, allocating additional 205 | storage if necessary. 206 | 207 | @code{.c} 208 | array_t(int) ia = NULL; 209 | array_alloc(ia, 16, NULL); 210 | // ... 211 | array_insert(ia, 1, 123); 212 | assert(ia[1] == 123); 213 | @endcode 214 | @hideinitializer **/ 215 | 216 | 217 | // void array_remove(T*& a, size_t index) 218 | #define array_remove(a, index) \ 219 | ( _array_remove(_array_ptr((a)), _array_offset((a), (index)), _array_stride((a))) ) 220 | /**< Removes a single element from the dynamic array at index. The removed 221 | element is passed to the array's destructor if it is not NULL. 222 | @hideinitializer **/ 223 | 224 | 225 | // void array_remove_unordered(T*& a, size_t index) 226 | #define array_remove_unordered(a, index) \ 227 | ( _array_remove_unordered(_array_ptr((a)), _array_offset((a), (index)), _array_stride((a))) ) 228 | /**< Removes a single element from the dynamic array at index. The removed 229 | element is passed to the array's destructor if it is not NULL. The removed 230 | element is replaced by the final element. This can be faster than shifting 231 | the remaining elements into place to maintain their relative order. 232 | @hideinitializer **/ 233 | 234 | 235 | // void array_remove_n(T*& a, size_t index, size_t count) 236 | #define array_remove_n(a, index, count) \ 237 | ( _array_remove(_array_ptr((a)), _array_offset((a), (index)), _array_offset((a), (count))) ) 238 | /**< Removes count elements from the dynamic array, starting at index. Removed 239 | elements are passed to the array's destructor if it is not NULL. 240 | @hideinitializer **/ 241 | 242 | 243 | // void array_push(T*& array, T value) 244 | #define array_push(a, v) array_append(a, v) 245 | 246 | 247 | // void array_pop(T*& array) 248 | #define array_pop(a) array_remove(a, array_size(a) - 1) 249 | 250 | 251 | // void array_clear(T*& a) 252 | #define array_clear(a) \ 253 | ( _array_clear(_array_ptr((a))) ) 254 | /**< Removes all elements from the dynamic array. Removed elements are passed 255 | to the array's destructor if it is not NULL. 256 | @hideinitializer **/ 257 | 258 | 259 | // T* array_begin(T* array) 260 | #define array_begin(a) (a) 261 | /**< Returns a pointer to the first element of the array, or NULL if the array 262 | is NULL. 263 | @hideinitializer **/ 264 | 265 | 266 | // T* array_end(T* array) 267 | #define array_end(a) ((a) + ( _array_size(_array_ptr((a))) / _array_stride((a)) )) 268 | /**< Returns a pointer just past the last element of the array, or NULL if the 269 | array is NULL. 270 | @hideinitializer **/ 271 | 272 | 273 | // T* array_rbegin(T* array) 274 | #define array_rbegin(a) (array_end(a) - 1) 275 | 276 | 277 | // T* array_rend(T* array) 278 | #define array_rend(a) (array_begin(a) - 1) 279 | 280 | 281 | #define array_at(a, index) \ 282 | (a[_array_index_check(array_size(a), index)]) 283 | 284 | 285 | #define array_at_reverse(a, reverse_index) \ 286 | (a[_array_reverse_index_check(array_size(a), reverse_index)]) 287 | 288 | 289 | // array_for(T, element, array) { ... } 290 | #define array_for(T, element, a) \ 291 | for (size_t \ 292 | array_for_index = 0, \ 293 | array_for_once = 0; \ 294 | array_for_once == 0; \ 295 | array_for_once = 1) \ 296 | for (T element = a[0]; \ 297 | (array_for_index < array_size(a)) \ 298 | ? (element = array_at(a, array_for_index), 1) \ 299 | : (0); \ 300 | array_for_index += 1) 301 | 302 | 303 | // array_for_reverse(T, element, array) { ... } 304 | #define array_for_reverse(T, element, a) \ 305 | for (size_t \ 306 | array_for_reverse_index = 0, \ 307 | array_for_reverse_once = 0; \ 308 | array_for_reverse_once == 0; \ 309 | array_for_reverse_once = 1) \ 310 | for (T element = a[0]; \ 311 | (array_for_reverse_index < array_size(a)) \ 312 | ? (element = array_at_reverse(a, array_for_reverse_index), 1) \ 313 | : (0); \ 314 | array_for_reverse_index += 1) 315 | 316 | //============================================================================== 317 | 318 | 319 | #define _array_ptr(a) ((_array_t*)&(a)) 320 | 321 | #define _array_stride(a) (sizeof((a)[0])) 322 | 323 | #define _array_offset(a, n) ((size_t)(_array_stride((a)) * n)) 324 | 325 | 326 | //------------------------------------------------------------------------------ 327 | 328 | 329 | #ifndef _array_assert 330 | static inline 331 | void _array_error(const char* file, const int line, const char* msg) { 332 | #ifndef printf 333 | extern int printf(const char*, ...); 334 | #endif 335 | #ifndef exit 336 | extern void exit(int); 337 | #endif 338 | printf("%s:%i: %s\n", file, line, msg); 339 | exit(1); 340 | } 341 | #define _array_assert(expr, msg) \ 342 | (((expr)||(_array_error(__FILE__, __LINE__, "assert(" #expr ") failed: " msg),0))) 343 | #endif 344 | 345 | 346 | #ifndef _array_memcmp 347 | #define _array_memcmp memcmp 348 | #ifndef memcmp 349 | int memcmp(const void*, const void*, size_t); 350 | #endif 351 | #endif 352 | 353 | 354 | #ifndef _array_memcpy 355 | #define _array_memcpy memcpy 356 | #ifndef memcpy 357 | void* memcpy(void*, const void*, size_t); 358 | #endif 359 | #endif 360 | 361 | 362 | #ifndef _array_memmove 363 | #define _array_memmove memmove 364 | #ifndef memmove 365 | void* memmove(void*, const void*, size_t); 366 | #endif 367 | #endif 368 | 369 | 370 | #ifndef _array_memset 371 | #define _array_memset memset 372 | #ifndef memset 373 | void* memset(void*, int, size_t); 374 | #endif 375 | #endif 376 | 377 | 378 | //------------------------------------------------------------------------------ 379 | 380 | 381 | typedef char* _array_t; 382 | 383 | typedef void* (*_array_allocator_t)(void* array, size_t size); 384 | 385 | typedef void (*_array_destructor_t)(void* begin, void* end); 386 | 387 | typedef struct _array_header_t { 388 | _array_allocator_t allocator; 389 | _array_destructor_t destructor; 390 | size_t capacity, size; 391 | char data[]; 392 | } _array_header_t; 393 | 394 | 395 | //------------------------------------------------------------------------------ 396 | 397 | 398 | static inline 399 | size_t _array_index_check(const size_t size, const size_t index) { 400 | _array_assert(index < size, "index out of range"); 401 | return index; 402 | } 403 | 404 | static inline 405 | size_t _array_reverse_index_check(const size_t size, const size_t reverse_index) { 406 | _array_assert(reverse_index < size, "index out of range"); 407 | return size - (reverse_index + 1); 408 | } 409 | 410 | 411 | static inline 412 | size_t _array_ceilpow2(size_t x) { 413 | enum { _32_OR_0 = 32 * (sizeof(void*) > 4) }; 414 | x -= 1; 415 | x |= (x >> 1); 416 | x |= (x >> 2); 417 | x |= (x >> 4); 418 | x |= (x >> 8); 419 | x |= (x >> 16); 420 | x |= (x >> _32_OR_0); 421 | x += 1; 422 | return x ? x : ~((size_t)0); 423 | } 424 | 425 | 426 | static inline 427 | _array_header_t* _array_header(_array_t* const a) { 428 | _array_header_t* const headers = (_array_header_t*)(*a); 429 | return headers ? (headers - 1) : headers; 430 | } 431 | 432 | 433 | #ifdef ARRAY_DEBUG 434 | #define _array_assign(a, b)\ 435 | (printf("%s(a: %p, *a: %p -> ",__func__,a,*a),(*a)=b,printf("%p)\n", *a)) 436 | #else 437 | #define _array_assign(a, b) ((*a)=b) 438 | #endif 439 | 440 | 441 | static inline 442 | void _array_alloc(_array_t* a, const size_t capacity, _array_destructor_t destructor, _array_allocator_t allocator) { 443 | _array_assert(!(*a), "array already allocated"); 444 | const size_t mem_size = sizeof(_array_header_t) + capacity; 445 | _array_header_t* const header = (_array_header_t*)allocator(NULL, mem_size); 446 | header->allocator = allocator ? allocator : array_allocator; 447 | header->destructor = destructor; 448 | header->capacity = capacity; 449 | header->size = 0; 450 | _array_assign(a, header->data); 451 | } 452 | 453 | 454 | static inline 455 | void _array_free(_array_t* a) { 456 | _array_header_t* header = _array_header(a); 457 | if (header) { 458 | if (header->destructor) { 459 | const size_t free_size = header->size; 460 | char* free_begin = (*a); 461 | char* free_end = free_begin + free_size; 462 | header->destructor(free_begin, free_end); 463 | } 464 | header = (_array_header_t*)header->allocator(header, 0); 465 | _array_assert(header == NULL, "allocator leaked memory"); 466 | _array_assign(a, NULL); 467 | } 468 | } 469 | 470 | 471 | static inline 472 | size_t _array_capacity(_array_t* const a) { 473 | const _array_header_t* const header = _array_header(a); 474 | return header ? header->capacity : ((size_t)0); 475 | } 476 | 477 | 478 | static inline 479 | size_t _array_size(_array_t* const a) { 480 | const _array_header_t* const header = _array_header(a); 481 | return header ? header->size : ((size_t)0); 482 | } 483 | 484 | 485 | static inline 486 | void _array_grow(_array_t* a, const size_t capacity) { 487 | const size_t mem_size = sizeof(_array_header_t) + capacity; 488 | _array_header_t* header = _array_header(a); 489 | header = (_array_header_t*)header->allocator(header, mem_size); 490 | _array_assert(header, "out of memory"); 491 | header->capacity = capacity; 492 | _array_assign(a, header->data); 493 | } 494 | 495 | 496 | static inline 497 | void _array_shrink(_array_t* a) { 498 | _array_header_t* old_header = _array_header(a); 499 | if (old_header->capacity > old_header->size) { 500 | const size_t new_capacity = old_header->size; 501 | const size_t mem_size = sizeof(_array_header_t) + new_capacity; 502 | const _array_allocator_t allocator = old_header->allocator; 503 | _array_header_t* new_header = (_array_header_t*)allocator(NULL, mem_size); 504 | _array_memcpy(new_header, old_header, mem_size); 505 | old_header = (_array_header_t*)old_header->allocator(old_header, 0); 506 | _array_assert(old_header == NULL, "allocator leaked memory"); 507 | new_header->capacity = new_capacity; 508 | _array_assign(a, new_header->data); 509 | } 510 | } 511 | 512 | 513 | static inline 514 | void _array_reserve(_array_t* a, const size_t capacity) { 515 | _array_assert((*a), "array uninitialized"); 516 | if (_array_capacity(a) < capacity) { 517 | _array_grow(a, _array_ceilpow2(capacity)); 518 | _array_assert(_array_capacity(a) >= capacity, "_array_grow() failed"); 519 | } 520 | } 521 | 522 | 523 | static inline 524 | void _array_resize(_array_t* a, const size_t new_size) { 525 | _array_assert((*a), "array uninitialized"); 526 | _array_header_t* header = _array_header(a); 527 | const size_t old_size = header->size; 528 | if (old_size > new_size) { 529 | if (header->destructor) { 530 | const size_t discard_size = old_size - new_size; 531 | char* discard_begin = (*a) + new_size; 532 | char* discard_end = discard_begin + discard_size; 533 | header->destructor(discard_begin, discard_end); 534 | } 535 | header->size = new_size; 536 | return; 537 | } 538 | if (new_size > old_size) { 539 | _array_reserve(a, new_size); 540 | _array_header(a)->size = new_size; 541 | const size_t append_size = new_size - old_size; 542 | char* append_begin = (*a) + old_size; 543 | _array_memset(append_begin, 0, append_size); 544 | return; 545 | } 546 | } 547 | 548 | 549 | static inline 550 | int _array_compare(_array_t* a, _array_t* b) { 551 | const size_t size_a = _array_size(a); 552 | const size_t size_b = _array_size(b); 553 | const size_t size = (size_a < size_b) ? size_a : size_b; 554 | const int cmp = _array_memcmp((*a), (*b), size); 555 | return (cmp) ? cmp : (int)(size_b - size_a); 556 | } 557 | 558 | 559 | static inline 560 | size_t _array_append(_array_t* a, const size_t append_size) { 561 | _array_assert((*a), "array uninitialized"); 562 | const size_t append_offset = _array_size(a); 563 | const size_t new_size = append_offset + append_size; 564 | _array_reserve(a, new_size); 565 | _array_header(a)->size = new_size; 566 | return append_offset; 567 | } 568 | 569 | 570 | static inline 571 | size_t _array_insert(_array_t* a, const size_t insert_offset, const size_t insert_size) { 572 | _array_assert((*a), "array uninitialized"); 573 | const size_t old_size = _array_size(a); 574 | _array_assert(insert_offset <= old_size, "array index out of range"); 575 | const size_t new_size = old_size + insert_size; 576 | _array_reserve(a, new_size); 577 | _array_header(a)->size = new_size; 578 | char* insert_begin = (*a) + insert_offset; 579 | char* insert_end = insert_begin + insert_size; 580 | const size_t end_size = old_size - insert_offset; 581 | _array_memmove(insert_end, insert_begin, end_size); 582 | return insert_offset; 583 | } 584 | 585 | 586 | static inline 587 | void _array_remove(_array_t* a, const size_t remove_offset, const size_t remove_size) { 588 | _array_assert((*a), "array uninitialized"); 589 | const size_t old_size = _array_size(a); 590 | _array_assert(remove_offset <= old_size, "array index out of range"); 591 | _array_assert(remove_size <= old_size - remove_offset, "array index out of range"); 592 | const size_t new_size = old_size - remove_size; 593 | _array_header_t* header = _array_header(a); 594 | char* remove_begin = (*a) + remove_offset; 595 | char* remove_end = remove_begin + remove_size; 596 | if (header->destructor) { 597 | header->destructor(remove_begin, remove_end); 598 | } 599 | const size_t tail_size = new_size - remove_offset; 600 | _array_memmove(remove_begin, remove_end, tail_size); 601 | header->size = new_size; 602 | } 603 | 604 | 605 | static inline 606 | void _array_remove_unordered(_array_t* a, const size_t remove_offset, const size_t remove_size) { 607 | _array_assert((*a), "array uninitialized"); 608 | const size_t old_size = _array_size(a); 609 | _array_assert(remove_offset <= old_size, "array index out of range"); 610 | _array_assert(remove_size <= old_size - remove_offset, "array index out of range"); 611 | const size_t new_size = old_size - remove_size; 612 | _array_header_t* header = _array_header(a); 613 | char* remove_begin = (*a) + remove_offset; 614 | char* remove_end = remove_begin + remove_size; 615 | if (header->destructor) { 616 | header->destructor(remove_begin, remove_end); 617 | } 618 | char* tail_begin = (*a) + new_size; 619 | _array_memmove(remove_begin, tail_begin, remove_size); 620 | header->size = new_size; 621 | } 622 | 623 | 624 | static inline 625 | size_t _array_front_index(_array_t* const a) { 626 | _array_assert((*a), "array uninitialized"); 627 | return 0; 628 | } 629 | 630 | 631 | static inline 632 | size_t _array_back_index(_array_t* const a, const size_t stride) { 633 | _array_assert((*a), "array uninitialized"); 634 | const size_t size = _array_size(a); 635 | _array_assert(size, "array index out of range"); 636 | return (size / stride) - 1; 637 | } 638 | 639 | 640 | static inline 641 | void _array_clear(_array_t* const a) { 642 | _array_assert((*a), "array uninitialized"); 643 | _array_header_t* header = _array_header(a); 644 | const size_t clear_size = header->size; 645 | char* clear_begin = (*a); 646 | char* clear_end = clear_begin + clear_size; 647 | if (header->destructor) { 648 | header->destructor(clear_begin, clear_end); 649 | } 650 | header->size = 0; 651 | } 652 | 653 | 654 | //------------------------------------------------------------------------------ 655 | 656 | 657 | #ifdef __cplusplus 658 | } // extern "C" 659 | #endif // __cplusplus 660 | 661 | 662 | #endif // ARRAY_H_6BD98208_07AE_43B7_98CF_8B81AD3BEABF 663 | -------------------------------------------------------------------------------- /implementation/array/array_tests.c: -------------------------------------------------------------------------------- 1 | #include "array.h" 2 | 3 | 4 | #ifndef test 5 | void exit(int); 6 | static inline 7 | void test_failed(const char* file, const int line, const char* msg) { 8 | printf("%s:%i: %s\n", file, line, msg); 9 | exit(1); 10 | } 11 | #define test(expr) \ 12 | (((expr) ? 0 : \ 13 | (test_failed(__FILE__, __LINE__, "test("#expr") failed"), 1))) 14 | #endif 15 | 16 | 17 | static size_t destructed_element_count = 0; 18 | 19 | 20 | void destructed_element_count_destructor(int* begin, int* end) { 21 | for(; begin < end; ++begin) { 22 | destructed_element_count += 1; 23 | } 24 | } 25 | 26 | 27 | void array_tests(void) { 28 | array_t(int) a = NULL; 29 | test(array_size(a) == 0); 30 | test(array_capacity(a) == 0); 31 | 32 | array_alloc(a, 0, destructed_element_count_destructor); 33 | test(array_size(a) == 0); 34 | test(array_capacity(a) == 0); 35 | 36 | array_append(a, 1); 37 | test(array_size(a) == 1); 38 | test(array_capacity(a) >= 1); 39 | test(a[0] == 1); 40 | 41 | 42 | array_append(a, 2); 43 | test(array_size(a) == 2); 44 | test(array_capacity(a) >= 2); 45 | test(a[0] == 1); 46 | test(a[1] == 2); 47 | 48 | 49 | array_append(a, 3); 50 | test(array_size(a) == 3); 51 | test(array_capacity(a) >= 3); 52 | test(a[0] == 1); 53 | test(a[1] == 2); 54 | test(a[2] == 3); 55 | 56 | 57 | array_insert(a, 0, 0); 58 | test(array_size(a) == 4); 59 | test(array_capacity(a) >= 4); 60 | test(a[0] == 0); 61 | test(a[1] == 1); 62 | test(a[2] == 2); 63 | test(a[3] == 3); 64 | 65 | 66 | array_reserve(a, 16); 67 | test(array_size(a) == 4); 68 | test(array_capacity(a) == 16); 69 | test(a[0] == 0); 70 | test(a[1] == 1); 71 | test(a[2] == 2); 72 | test(a[3] == 3); 73 | 74 | 75 | array_shrink(a); 76 | test(array_size(a) == 4); 77 | test(array_capacity(a) == 4); 78 | test(a[0] == 0); 79 | test(a[1] == 1); 80 | test(a[2] == 2); 81 | test(a[3] == 3); 82 | 83 | 84 | array_remove(a, 0); 85 | test(array_size(a) == 3); 86 | test(array_capacity(a) == 4); 87 | test(a[0] == 1); 88 | test(a[1] == 2); 89 | test(a[2] == 3); 90 | test(destructed_element_count == 1); 91 | destructed_element_count = 0; 92 | 93 | 94 | array_remove_unordered(a,0); 95 | test(array_size(a) == 2); 96 | test(array_capacity(a) == 4); 97 | test(a[0] == 3); 98 | test(a[1] == 2); 99 | test(destructed_element_count == 1); 100 | destructed_element_count = 0; 101 | 102 | 103 | array_clear(a); 104 | test(array_size(a) == 0); 105 | test(array_capacity(a) >= 0); 106 | test(destructed_element_count == 2); 107 | destructed_element_count = 0; 108 | 109 | 110 | array_append(a, 0); 111 | array_append(a, 1); 112 | array_append(a, 2); 113 | test(array_size(a) == 3); 114 | test(array_capacity(a) >= 3); 115 | test(destructed_element_count == 0); 116 | 117 | 118 | array_free(a); 119 | test(a == NULL); 120 | test(array_size(a) == 0); 121 | test(array_capacity(a) == 0); 122 | test(destructed_element_count == 3); 123 | destructed_element_count = 0; 124 | 125 | 126 | enum { TEST_LENGTH = 1024 }; 127 | 128 | 129 | array_alloc(a, 0, destructed_element_count_destructor); 130 | for (int i = 0; i < TEST_LENGTH; ++i) { 131 | array_append(a, i); 132 | test(a[i] == i); 133 | } 134 | test(array_size(a) == TEST_LENGTH); 135 | test(array_capacity(a) >= TEST_LENGTH); 136 | for (int i = 0; i < TEST_LENGTH; ++i) { 137 | test(a[i] == i); 138 | } 139 | { 140 | int i = 0; 141 | const int* const end = array_end(a); 142 | for (int* itr = array_begin(a); itr < end; ++itr) { 143 | test(*itr == i++); 144 | } 145 | } 146 | { 147 | int i = 0; 148 | while (array_size(a)) { 149 | test(a[0] == i++); 150 | array_remove(a,0); 151 | } 152 | test(array_size(a) == 0); 153 | test(array_capacity(a) >= TEST_LENGTH); 154 | test(destructed_element_count == TEST_LENGTH); 155 | destructed_element_count = 0; 156 | } 157 | array_free(a); 158 | test(a == NULL); 159 | test(array_size(a) == 0); 160 | test(array_capacity(a) == 0); 161 | 162 | 163 | array_alloc(a, 0, destructed_element_count_destructor); 164 | for (int i = 0; i < TEST_LENGTH; ++i) { 165 | array_insert(a, 0, i); 166 | } 167 | test(array_size(a) == TEST_LENGTH); 168 | test(array_capacity(a) >= TEST_LENGTH); 169 | for (int i = 0; i < TEST_LENGTH; ++i) { 170 | test(a[i] == (TEST_LENGTH - 1) - i); 171 | } 172 | array_free(a); 173 | test(a == NULL); 174 | test(array_size(a) == 0); 175 | test(array_capacity(a) == 0); 176 | 177 | 178 | puts("array tests passed"); 179 | } 180 | -------------------------------------------------------------------------------- /implementation/assert.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | //------------------------------------------------------------------------------ 6 | 7 | static inline 8 | void _gpucFatal(const char* file, const int line, const char* msg) { 9 | printf("%s:%i: %s\n", file, line, msg); 10 | exit(1); 11 | } 12 | 13 | #define gpucFatal(msg) _gpucFatal(__FILE__, __LINE__, msg) 14 | 15 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 16 | 17 | #define gpucAssert(expr, msg) \ 18 | ((expr)||(gpucFatal("assert("#expr") failed: " msg),0)) 19 | -------------------------------------------------------------------------------- /implementation/diagnostic.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "location.h" 4 | 5 | //------------------------------------------------------------------------------ 6 | 7 | static inline 8 | const GpucDiagnostic* gpucDiagnosticAllocV( 9 | GpucDiagnosticLevel level, 10 | GpucLocation location, 11 | const char* const format, va_list args) 12 | { 13 | char* out = NULL; 14 | 15 | const size_t messageLength = gpucStreamFormatV(&out, format, args); 16 | 17 | const size_t size = sizeof(GpucDiagnostic) + messageLength + 1; 18 | GpucDiagnostic* const diagnostic = (GpucDiagnostic*)calloc(1, size); 19 | diagnostic->level = level; 20 | diagnostic->location = location; 21 | 22 | out = diagnostic->message; 23 | gpucStreamFormatV(&out, format, args); 24 | 25 | return diagnostic; 26 | } 27 | 28 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 29 | 30 | static inline 31 | const GpucDiagnostic* gpucDiagnosticAlloc( 32 | GpucDiagnosticLevel level, 33 | GpucLocation location, 34 | const char* const format, ...) 35 | { 36 | va_list args; 37 | va_start(args, format); 38 | return gpucDiagnosticAllocV(level, location, format, args); 39 | va_end(args); 40 | } 41 | 42 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 43 | 44 | static inline 45 | void gpucDiagnosticFree(const GpucDiagnostic* const diagnostic) { 46 | gpucAssert(diagnostic, "diagnostic is NULL"); 47 | free((void*)diagnostic); 48 | } 49 | 50 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 51 | 52 | static inline 53 | void gpucDiagnosticArrayDestructor( 54 | const GpucDiagnostic* const * itr, 55 | const GpucDiagnostic* const * const end) 56 | { 57 | while (itr < end) gpucDiagnosticFree(*itr++); 58 | } 59 | 60 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 61 | 62 | /*public*/ 63 | size_t gpucDiagnosticStringWrite( 64 | char** out, 65 | const GpucDiagnostic* const diagnostic) 66 | { 67 | static const char* LevelPrefix[] = { " note: ", " warning: ", " error: " }; 68 | const GpucDiagnosticLevel level = diagnostic->level; 69 | const GpucLocation* const location = &diagnostic->location; 70 | const char* const message = diagnostic->message; 71 | size_t length = 0; 72 | length += gpucStreamSubstring(out, &location->file); 73 | length += gpucStreamFormat(out, ":%u:%u:", location->line, location->column); 74 | length += gpucStreamString(out, LevelPrefix[level]); 75 | length += gpucStreamString(out, message); 76 | length += gpucStreamChar(out, '\n'); 77 | length += gpucStreamSubstring(out, &location->context); 78 | length += gpucStreamChar(out, '\n'); 79 | for (unsigned i = 1; i < location->column; ++i) { 80 | length += gpucStreamChar(out, ' '); 81 | } 82 | for (unsigned i = 0; i < location->site.length; ++i) { 83 | length += gpucStreamChar(out, '^'); 84 | } 85 | return length; 86 | } 87 | 88 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 89 | 90 | /*public*/ 91 | size_t gpucDiagnosticStringLength( 92 | const GpucDiagnostic* const diagnostic) 93 | { 94 | char* out = NULL; 95 | return gpucDiagnosticStringWrite(&out, diagnostic); 96 | } 97 | 98 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 99 | 100 | /*public*/ 101 | const char* gpucDiagnosticStringAlloc( 102 | const GpucDiagnostic* const diagnostic) 103 | { 104 | const size_t length = gpucDiagnosticStringLength(diagnostic); 105 | char* const mem = (char*)calloc(length + 1, sizeof(char)); 106 | char* out = mem; 107 | gpucDiagnosticStringWrite(&out, diagnostic); 108 | return mem; 109 | } 110 | 111 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 112 | 113 | /*public*/ 114 | void gpucDiagnosticPrint(const GpucDiagnostic* const diagnostic) { 115 | const char* const mem = gpucDiagnosticStringAlloc(diagnostic); 116 | fputs(mem, stdout); 117 | free((void*)mem); 118 | } 119 | 120 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 121 | 122 | /*public*/ 123 | void gpucDiagnosticPrintLine(const GpucDiagnostic* const diagnostic) { 124 | const char* const mem = gpucDiagnosticStringAlloc(diagnostic); 125 | puts(mem); 126 | free((void*)mem); 127 | } 128 | -------------------------------------------------------------------------------- /implementation/lexeme.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include "assert.h" 5 | #include "token.h" 6 | 7 | //------------------------------------------------------------------------------ 8 | 9 | typedef struct GpucLexeme { 10 | const char* head; 11 | unsigned length; 12 | GpucToken token; 13 | } GpucLexeme; 14 | 15 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 16 | 17 | static inline 18 | bool gpucLexemeIsEqualToSubstring( 19 | const GpucLexeme* const lexeme, 20 | const GpucSubstring* const substring) 21 | { 22 | return 23 | lexeme->length == substring->length 24 | and not strncmp(lexeme->head, substring->head, substring->length); 25 | } 26 | 27 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 28 | 29 | static inline 30 | bool gpucLexemeIsEqualToString( 31 | const GpucLexeme* const lexeme, 32 | const char* const string) 33 | { 34 | const GpucSubstring substring = { 35 | .head = string, 36 | .length = (unsigned)strlen(string), 37 | }; 38 | return gpucLexemeIsEqualToSubstring(lexeme, &substring); 39 | } 40 | 41 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 42 | 43 | static inline 44 | bool gpucLexemesAreEqual( 45 | const GpucLexeme* const a, 46 | const GpucLexeme* const b) 47 | { 48 | const GpucSubstring substring = { 49 | .head = b->head, 50 | .length = b->length, 51 | }; 52 | return gpucLexemeIsEqualToSubstring(a, &substring); 53 | } 54 | 55 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 56 | 57 | static inline 58 | const char* gpucLexemeStringAlloc(const GpucLexeme* const lexeme) { 59 | char* const mem = (char*)calloc(lexeme->length + 1, sizeof(char)); 60 | strncpy(mem, lexeme->head, lexeme->length); 61 | return mem; 62 | } 63 | 64 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 65 | 66 | static inline 67 | void gpucLexemeDebugPrint(const GpucLexeme* const lexeme) { 68 | const char* const tokenName = gpucTokenName(lexeme->token); 69 | printf("%-12s \"%.*s\"", tokenName, lexeme->length, lexeme->head); 70 | } 71 | 72 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 73 | 74 | static inline 75 | void gpucLexemeDebugPrintLine(const GpucLexeme* const lexeme) { 76 | gpucLexemeDebugPrint(lexeme); 77 | fputc('\n', stdout); 78 | } 79 | -------------------------------------------------------------------------------- /implementation/location.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "assert.h" 4 | #include "lexeme.h" 5 | #include "scan.h" 6 | #include "stream.h" 7 | 8 | //------------------------------------------------------------------------------ 9 | 10 | static inline 11 | unsigned gpucLocationLineDirectiveFileLength(const char* file) { 12 | unsigned i = 0; 13 | for (char c = file[i]; c and c != '"' and c != '\n'; c = file[++i]); 14 | return i; 15 | } 16 | 17 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 18 | 19 | static inline 20 | unsigned gpucLocationContextLength( 21 | const char* const itr, 22 | const char* const end) 23 | { 24 | const char* p = itr; 25 | gpucSeekNewLine(&p, end); 26 | if (p >= itr and p[-1] == '\n') p -= 1; 27 | return (unsigned)(p - itr); 28 | } 29 | 30 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 31 | 32 | static inline 33 | bool gpucLocationFromLineDirective( 34 | GpucLocation* location, 35 | const char** itr, 36 | const char* const end) 37 | { 38 | // # line ["filename"] 39 | if (gpucSkipDirectiveName(itr, end, "line")) { 40 | gpucSkipWhitespace(itr, end); 41 | unsigned line = 0; 42 | if (gpucScanUIntBase10(&line, itr, end)) { 43 | location->line = line; 44 | gpucSkipWhitespace(itr, end); 45 | if (gpucSkipChar(itr, end, '"')) { 46 | location->file.head = *itr; 47 | location->file.length = 48 | gpucLocationLineDirectiveFileLength(*itr); 49 | } 50 | } 51 | return true; 52 | } 53 | return false; 54 | } 55 | 56 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 57 | 58 | static inline 59 | void gpucLocationFromLexeme( 60 | GpucLocation* location, 61 | const GpucSubstring* const file, 62 | const GpucSubstring* const source, 63 | const GpucLexeme* const lexeme) 64 | { 65 | const char* itr = source->head; 66 | const char* const end = itr + source->length; 67 | // gpucAssert(lexeme->head >= itr, "!!!"); 68 | // gpucAssert(lexeme->head + lexeme->length < end, "!!!"); 69 | 70 | location->file.head = file->head; 71 | location->file.length = file->length; 72 | location->site.head = lexeme->head; 73 | location->site.length = lexeme->length; 74 | location->line = 1; 75 | location->column = 1; 76 | location->context.head = source->head; 77 | location->context.length = gpucLocationContextLength(itr, end); 78 | 79 | while (itr < end and itr < lexeme->head) { 80 | if (gpucLocationFromLineDirective(location, &itr, end)) { 81 | continue; 82 | } 83 | if (gpucSkipChar(&itr, end, '\n')) { 84 | location->line += 1; 85 | location->column = 1; 86 | location->context.head = itr; 87 | location->context.length = gpucLocationContextLength(itr, end); 88 | continue; 89 | } 90 | ++location->column; 91 | ++itr; 92 | } 93 | } 94 | 95 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 96 | 97 | /*public*/ 98 | size_t gpucLocationStringWrite(char** out, const GpucLocation* const location) { 99 | size_t length = 0; 100 | length += gpucStreamSubstring(out, &location->file); 101 | length += gpucStreamFormat(out, ":%u:%u:", location->line, location->column); 102 | return length; 103 | } 104 | 105 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 106 | 107 | /*public*/ 108 | size_t gpucLocationStringLength(const GpucLocation* const location) { 109 | char* out = NULL; 110 | return gpucLocationStringWrite(&out, location); 111 | } 112 | 113 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 114 | 115 | /*public*/ 116 | const char* gpucLocationStringAlloc(const GpucLocation* const location) { 117 | const size_t length = gpucLocationStringLength(location); 118 | char* const mem = (char*)calloc(length + 1, sizeof(char)); 119 | char* out = mem; 120 | gpucLocationStringWrite(&out, location); 121 | return mem; 122 | } 123 | 124 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 125 | 126 | void gpucLocationPrint(const GpucLocation* const location) { 127 | const char* const mem = gpucLocationStringAlloc(location); 128 | fputs(mem, stdout); 129 | free((void*)mem); 130 | } 131 | -------------------------------------------------------------------------------- /implementation/module.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "../gpuc.h" 8 | #include "array/array.h" 9 | #include "diagnostic.h" 10 | #include "node.h" 11 | 12 | //------------------------------------------------------------------------------ 13 | 14 | struct GpucModule { 15 | GpucSubstring file; 16 | GpucSubstring source; 17 | GpucNode* root; 18 | array_t(const GpucDiagnostic*) diagnostics; 19 | array_t(GpucLexeme) lexemes; 20 | array_t(const GpucNode*) symbols; 21 | array_t(unsigned) scopes; 22 | array_t(char) translation; 23 | }; 24 | 25 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 26 | 27 | /*public*/ 28 | GpucModule* gpucModuleAlloc( 29 | const char* file, 30 | const char* source) 31 | { 32 | GpucModule* const module = (GpucModule*)calloc(1, sizeof(GpucModule)); 33 | 34 | const size_t sourceLength = strlen(source); 35 | 36 | file = file ? file : "?"; 37 | module->file.head = file; 38 | module->file.length = (unsigned)strlen(file); 39 | module->source.head = source; 40 | module->source.length = (unsigned)sourceLength; 41 | module->root = gpucNodeAlloc_(Module, NULL, NULL); 42 | 43 | array_alloc(module->diagnostics, 4, gpucDiagnosticArrayDestructor); 44 | array_alloc(module->lexemes, 4 + sourceLength / 4, NULL); 45 | array_alloc(module->symbols, 4 + sourceLength / 16, NULL); 46 | array_alloc(module->scopes, 4 + sourceLength / 32, NULL); 47 | 48 | void 49 | _gpucModuleBeginScope(GpucModule* module); 50 | _gpucModuleBeginScope(module); // global scope 51 | 52 | void 53 | _gpucModuleDefinePrimitiveTypes(GpucModule* module); 54 | _gpucModuleDefinePrimitiveTypes(module); 55 | 56 | void 57 | _gpucModuleDeclareBuiltinFunctions(GpucModule* module); 58 | _gpucModuleDeclareBuiltinFunctions(module); 59 | 60 | _gpucModuleBeginScope(module); // module scope 61 | 62 | return module; 63 | } 64 | 65 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 66 | 67 | /*public*/ 68 | void gpucModuleFree(GpucModule* const module) { 69 | gpucAssert(module, ""); 70 | gpucNodeFree(module->root); 71 | array_free(module->diagnostics); 72 | array_free(module->lexemes); 73 | array_free(module->symbols); 74 | array_free(module->scopes); 75 | array_free(module->translation); 76 | free(module); 77 | } 78 | 79 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 80 | 81 | /*public*/ 82 | void gpucModulePrintDebug(const GpucModule* const module) { 83 | fputs("lexemes: [", stdout); 84 | if (array_length(module->lexemes)) { 85 | fputc('\n', stdout); 86 | array_for(GpucLexeme, lexeme, module->lexemes) { 87 | fputs(" ", stdout); 88 | gpucLexemeDebugPrintLine(&lexeme); 89 | } 90 | } 91 | puts("]"); 92 | fputs("symbols: [", stdout); 93 | if (array_length(module->symbols)) { 94 | fputc('\n', stdout); 95 | array_for(const GpucNode*, symbol, module->symbols) { 96 | gpucNodeDebugPrintTree(symbol, 1); 97 | } 98 | } 99 | puts("]"); 100 | } 101 | 102 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 103 | 104 | /*public*/ 105 | void gpucModulePrintDiagnostics(const GpucModule* const module) { 106 | array_for(const GpucDiagnostic*, diagnostic, module->diagnostics) { 107 | gpucDiagnosticPrintLine(diagnostic); 108 | } 109 | } 110 | 111 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 112 | 113 | /*public*/ 114 | void gpucModulePrintLexemes(const GpucModule* const module) { 115 | array_for(GpucLexeme, lexeme, module->lexemes) { 116 | gpucLexemeDebugPrintLine(&lexeme); 117 | } 118 | } 119 | 120 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 121 | 122 | /*public*/ 123 | void gpucModulePrintAST(const GpucModule* const module) { 124 | gpucNodeDebugPrintTree(module->root, 0); 125 | } 126 | 127 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 128 | 129 | /*public*/ 130 | const GpucDiagnostic* gpucModuleGetDiagnostic( 131 | const GpucModule* module, 132 | unsigned index) 133 | { 134 | if (index < array_length(module->diagnostics)) { 135 | return module->diagnostics[index]; 136 | } 137 | return NULL; 138 | } 139 | 140 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 141 | 142 | static void gpucModuleAppendDiagnostic( 143 | GpucModule* const module, 144 | const GpucDiagnosticLevel level, 145 | const GpucLexeme* const lexeme, 146 | const char* const format, ...) 147 | { 148 | GpucLocation location = {0}; 149 | gpucLocationFromLexeme( 150 | &location, 151 | &module->file, 152 | &module->source, 153 | lexeme); 154 | 155 | va_list args; 156 | va_start(args, format); 157 | const GpucDiagnostic* const diagnostic = 158 | gpucDiagnosticAllocV(level, location, format, args); 159 | va_end(args); 160 | array_append(module->diagnostics, diagnostic); 161 | } 162 | 163 | #define gpucAppendDiagnostic(level, lexeme, /*format, args*/...) \ 164 | gpucModuleAppendDiagnostic(\ 165 | module,\ 166 | GpucDiagnosticLevel_##level,\ 167 | lexeme,\ 168 | __VA_ARGS__) 169 | 170 | #define gpucNote(lexeme, /*format, args*/...) \ 171 | gpucAppendDiagnostic(Note, lexeme, __VA_ARGS__) 172 | 173 | #define gpucWarning(lexeme, /*format, args*/...) \ 174 | gpucAppendDiagnostic(Warning, lexeme, __VA_ARGS__) 175 | 176 | #define gpucError(lexeme, /*format, args*/...) \ 177 | gpucAppendDiagnostic(Error, lexeme, __VA_ARGS__) 178 | 179 | //------------------------------------------------------------------------------ 180 | 181 | void 182 | _gpucModuleBeginScope(GpucModule* module) { 183 | const unsigned symbolOffset = (unsigned)array_length(module->symbols); 184 | array_append(module->scopes, symbolOffset); 185 | } 186 | 187 | #define gpucBeginScope() _gpucModuleBeginScope(module) 188 | 189 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 190 | 191 | static void gpucModuleEndScope(GpucModule* module) { 192 | const unsigned symbolOffset = array_back(module->scopes); 193 | array_resize(module->symbols, symbolOffset); 194 | array_pop(module->scopes); 195 | } 196 | 197 | #define gpucEndScope() gpucModuleEndScope(module) 198 | 199 | //------------------------------------------------------------------------------ 200 | 201 | static bool gpucModuleDefineType(GpucModule* module, const GpucNode* type) { 202 | gpucAssert(gpucNodeIsType(type), "expected type"); 203 | 204 | const GpucLexeme* const name = type->lexeme; 205 | 206 | array_for_reverse(const GpucNode*, symbol, module->symbols) { 207 | const GpucLexeme* const symbolName = symbol->lexeme; 208 | 209 | if (not gpucLexemesAreEqual(name, symbolName)) 210 | continue; 211 | 212 | gpucError(name, "redefinition of '%.*s'", name->length, name->head); 213 | gpucNote(symbolName, "previous definition is here"); 214 | return false; 215 | } 216 | 217 | array_append(module->symbols, type); 218 | return true; 219 | } 220 | 221 | #define gpucDefineType(type) gpucModuleDefineType(module, type) 222 | 223 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 224 | 225 | void 226 | _gpucModuleDefinePrimitiveTypes(GpucModule* module) { 227 | gpucAssert(array_empty(module->symbols), "before all other symbols"); 228 | 229 | #define GPUC_DEFINE_PRIMITIVE_TYPE(TOKEN, LITERAL)\ 230 | static const GpucLexeme gpucLexeme_##TOKEN = {\ 231 | .head = LITERAL,\ 232 | .length = sizeof(LITERAL) - 1,\ 233 | .token = GpucToken_##TOKEN,\ 234 | };\ 235 | static const GpucNode gpucNode_##TOKEN = {\ 236 | .semantic = GpucSemantic_PrimitiveType,\ 237 | .lexeme = &gpucLexeme_##TOKEN,\ 238 | .type = &gpucNode_##TOKEN,\ 239 | };\ 240 | gpucModuleDefineType(module, &gpucNode_##TOKEN); 241 | GPUC_TYPENAME_TOKENS(GPUC_DEFINE_PRIMITIVE_TYPE) 242 | #undef GPUC_DEFINE_PRIMITIVE_TYPE 243 | } 244 | 245 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 246 | 247 | static bool gpucModuleDeclareFunction( 248 | GpucModule* const module, 249 | const GpucNode* const function) 250 | { 251 | gpucAssert(array_length(module->scopes) <= 2, "parameter or module scope only"); 252 | gpucAssert(gpucNodeIsFunctionDeclaration(function), "expected function"); 253 | 254 | const GpucLexeme* const name = function->lexeme; 255 | 256 | array_for_reverse(const GpucNode*, symbol, module->symbols) { 257 | const GpucLexeme* const symbolName = symbol->lexeme; 258 | 259 | if (not gpucLexemesAreEqual(name, symbolName)) 260 | continue; 261 | 262 | if (gpucNodeIsFunction(symbol)) { 263 | 264 | if (gpucNodesHaveSameFunctionOrCallParameterTypes(function, symbol) 265 | and not gpucNodesHaveSameType(function, symbol)) 266 | { 267 | gpucError(name, "declaration differs only in return type"); 268 | if (gpucNodeIsFunctionDeclaration(symbol)) { 269 | gpucNote(symbolName, "previous declaration is here"); 270 | } else { 271 | gpucNote(symbolName, "previous definition is here"); 272 | } 273 | return false; 274 | } 275 | 276 | } else { 277 | gpucError(name, "redefinition of '%.*s'", name->length, name->head); 278 | gpucNote(symbolName, "previous definition is here"); 279 | return false; 280 | } 281 | 282 | // NOTE: multiple identical function declarations are OK 283 | } 284 | 285 | array_append(module->symbols, function); 286 | return true; 287 | } 288 | 289 | #define gpucDeclareFunction(function)\ 290 | gpucModuleDeclareFunction(module, function) 291 | 292 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 293 | 294 | void 295 | _gpucModuleDeclareBuiltinFunctions(GpucModule* module) { 296 | static const char builtin_functions[] = GPUC( 297 | float4 sample(texture2d, float2); 298 | float4 sample(texture2d, float2, sampler); 299 | ); 300 | 301 | bool _gpucModuleLexString( 302 | GpucModule* module, 303 | const char* itr, 304 | const char* const end); 305 | 306 | const bool did_lex_builtin_functions = 307 | _gpucModuleLexString( 308 | module, 309 | builtin_functions, 310 | builtin_functions + sizeof(builtin_functions) - 1); 311 | if (not did_lex_builtin_functions) { 312 | gpucModulePrintDiagnostics(module); 313 | } 314 | 315 | gpucAssert(did_lex_builtin_functions, "failed to lex builtin functions"); 316 | } 317 | 318 | static bool _gpucModuleIsBuiltinSymbol( 319 | const GpucModule* const module, 320 | const GpucNode* const symbol) 321 | { 322 | return 323 | symbol->lexeme->head < module->source.head || 324 | symbol->lexeme->head > module->source.head + module->source.length; 325 | } 326 | 327 | static bool _gpucModuleIsUserDefinedSymbol( 328 | const GpucModule* const module, 329 | const GpucNode* const symbol) 330 | { 331 | return 332 | symbol->lexeme->head > module->source.head && 333 | symbol->lexeme->head < module->source.head + module->source.length; 334 | } 335 | 336 | 337 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 338 | 339 | static bool gpucModuleDefineFunction( 340 | GpucModule* const module, 341 | const GpucNode* const function) 342 | { 343 | gpucAssert(array_length(module->scopes) <= 2, "parameter or module scope only"); 344 | gpucAssert(gpucNodeIsFunctionDefinition(function), "expected function definition"); 345 | 346 | const GpucLexeme* const name = function->lexeme; 347 | 348 | array_for_reverse(const GpucNode*, symbol, module->symbols) { 349 | const GpucLexeme* const symbolName = symbol->lexeme; 350 | 351 | if (not gpucLexemesAreEqual(name, symbolName)) 352 | continue; 353 | 354 | if (gpucNodeIsFunctionDeclaration(symbol)) { 355 | 356 | if (gpucNodesHaveSameFunctionOrCallParameterTypes(function, symbol) 357 | and not gpucNodesHaveSameType(function, symbol)) 358 | { 359 | gpucError(name, "definition differs only in return type"); 360 | gpucNote(symbolName, "previous declaration is here"); 361 | return false; 362 | } 363 | 364 | } else { 365 | gpucError(name, "redefinition of '%.*s'", name->length, name->head); 366 | gpucNote(symbolName, "previous definition is here"); 367 | return false; 368 | } 369 | } 370 | 371 | array_append(module->symbols, function); 372 | return true; 373 | } 374 | 375 | #define gpucDefineFunction(function) gpucModuleDefineFunction(module, function) 376 | 377 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 378 | 379 | static bool gpucModuleDefineVariable( 380 | GpucModule* const module, 381 | const GpucNode* const variable) 382 | { 383 | gpucAssert(gpucNodeIsVariable(variable), "expected variable"); 384 | 385 | const GpucLexeme* const name = variable->lexeme; 386 | 387 | const unsigned scope = array_back(module->scopes); 388 | 389 | const GpucNode** ritr = array_rbegin(module->symbols); 390 | const GpucNode** const rend = array_rend(module->symbols) + scope; 391 | for (; ritr > rend; --ritr) { 392 | const GpucNode* const symbol = *ritr; 393 | const GpucLexeme* const symbolName = symbol->lexeme; 394 | 395 | if (gpucLexemesAreEqual(name, symbolName)) { 396 | gpucError(name, "redefinition of '%.*s'", name->length, name->head); 397 | gpucNote(symbolName, "previous definition is here"); 398 | return false; 399 | } 400 | } 401 | 402 | array_append(module->symbols, variable); 403 | return true; 404 | } 405 | 406 | #define gpucDefineVariable(variable) gpucModuleDefineVariable(module, variable) 407 | 408 | //------------------------------------------------------------------------------ 409 | 410 | static const GpucNode* gpucModuleFindSymbol( 411 | const GpucModule* const module, 412 | const GpucLexeme* const name) 413 | { 414 | array_for_reverse(const GpucNode*, symbol, module->symbols) { 415 | const GpucLexeme* const symbolName = symbol->lexeme; 416 | 417 | if (gpucLexemesAreEqual(symbolName, name)) 418 | return symbol; 419 | } 420 | return NULL; 421 | } 422 | 423 | #define gpucFindSymbol(name) gpucModuleFindSymbol(module, name) 424 | 425 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 426 | 427 | static const GpucNode* gpucModuleGetPrimitiveType( 428 | const GpucModule* const module, 429 | const char* const name) 430 | { 431 | array_for(const GpucNode*, symbol, module->symbols) { 432 | if (symbol->semantic == GpucSemantic_PrimitiveType) { 433 | const GpucLexeme* const symbolName = symbol->lexeme; 434 | if (gpucLexemeIsEqualToString(symbolName, name)) 435 | return symbol; 436 | } 437 | } 438 | puts(name); 439 | gpucFatal("primitive type not found"); 440 | return NULL; 441 | } 442 | 443 | #define gpucGetPrimitiveType(name) gpucModuleGetPrimitiveType(module, name) 444 | 445 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 446 | 447 | static const GpucNode* gpucModuleRequireType( 448 | GpucModule* const module, 449 | const GpucLexeme* const name) 450 | { 451 | const GpucNode* const symbol = gpucModuleFindSymbol(module, name); 452 | 453 | if (not symbol) { 454 | gpucError(name, "undeclared identifier"); 455 | return NULL; 456 | } 457 | 458 | if (not gpucNodeIsType(symbol)) { 459 | gpucError(name, "identifier is not a type"); 460 | gpucNote(symbol->lexeme, "see definition here"); 461 | return NULL; 462 | } 463 | 464 | return symbol; 465 | } 466 | 467 | #define gpucRequireType(name) gpucModuleRequireType(module, name) 468 | 469 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 470 | 471 | static const GpucNode* gpucModuleRequireFunction( 472 | GpucModule* const module, 473 | const GpucNode* const invocation) 474 | { 475 | gpucAssert(gpucNodeIsCallExpression(invocation), ""); 476 | 477 | const GpucLexeme* const name = invocation->lexeme; 478 | 479 | unsigned overloadCount = 0; 480 | 481 | array_for_reverse(const GpucNode*, symbol, module->symbols) { 482 | const GpucLexeme* const symbolName = symbol->lexeme; 483 | if (not gpucLexemesAreEqual(name, symbolName)) 484 | continue; 485 | 486 | if (not gpucNodeIsFunction(symbol)) { 487 | gpucError(name, "identifier is not a function"); 488 | gpucNote(symbolName, "see definition here"); 489 | return NULL; 490 | } 491 | 492 | if (gpucNodesHaveSameFunctionOrCallParameterTypes(invocation, symbol)) 493 | return symbol; 494 | 495 | overloadCount += 1; 496 | } 497 | 498 | if (overloadCount) { 499 | const char* const parameterTypeString = 500 | gpucNodeParameterTypeStringAlloc(invocation); 501 | gpucError(name, 502 | "no instance of function " 503 | "matches the argument list %s", 504 | name, parameterTypeString); 505 | free((void*)parameterTypeString); 506 | 507 | array_for(const GpucNode*, symbol, module->symbols) { 508 | const GpucLexeme* const symbolName = symbol->lexeme; 509 | if (gpucNodeIsFunction(symbol) and 510 | gpucLexemesAreEqual(name, symbolName)) 511 | gpucNote(symbolName, "see definition here"); 512 | } 513 | } else { 514 | gpucError(name, "identifier is undefined"); 515 | } 516 | return NULL; 517 | } 518 | 519 | #define gpucRequireFunction(invocation) \ 520 | gpucModuleRequireFunction(module, invocation) 521 | 522 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 523 | 524 | static const GpucNode* gpucModuleFindEnclosingFunction( 525 | GpucModule* const module) 526 | { 527 | array_for_reverse(const GpucNode*, symbol, module->symbols) { 528 | if (gpucNodeIsFunctionDefinition(symbol)) 529 | return symbol; 530 | } 531 | return NULL; 532 | } 533 | 534 | #define gpucFindEnclosingFunction() \ 535 | gpucModuleFindEnclosingFunction(module) 536 | 537 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 538 | 539 | static const GpucNode* gpucModuleFindStageEntryPoint( 540 | GpucModule* const module, 541 | const GpucStage stage) 542 | { 543 | array_for_reverse(const GpucNode*, symbol, module->symbols) { 544 | if (gpucNodeIsStageEntryPoint(symbol, stage)) 545 | return symbol; 546 | } 547 | return NULL; 548 | } 549 | 550 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 551 | 552 | static const GpucNode* gpucModuleRequireVariable( 553 | GpucModule* const module, 554 | const GpucLexeme* const name) 555 | { 556 | const GpucNode* const symbol = gpucModuleFindSymbol(module, name); 557 | 558 | if (not symbol) { 559 | gpucError(name, "undeclared identifier"); 560 | return NULL; 561 | } 562 | 563 | if (not gpucNodeIsVariable(symbol)) { 564 | gpucError(name, "identifier is not a variable"); 565 | gpucNote(symbol->lexeme, "see definition here"); 566 | return NULL; 567 | } 568 | 569 | return symbol; 570 | } 571 | 572 | #define gpucRequireVariable(name) gpucModuleRequireVariable(module, name) 573 | 574 | //------------------------------------------------------------------------------ 575 | 576 | #include "module.lex.h" 577 | #include "module.parse.h" 578 | #include "tests.h" 579 | #include "writer.h" 580 | 581 | /*public*/ 582 | bool gpucModuleCompile(GpucModule* module) { 583 | return gpucModuleLex(module) and gpucModuleParse(module); 584 | } 585 | 586 | 587 | //------------------------------------------------------------------------------ 588 | 589 | const char* gpucModuleGetTranslation( 590 | GpucModule* const module, 591 | const GpucStage stage, 592 | const GpucLanguage language) 593 | { 594 | GpucWriter writer; 595 | gpucWriterSetup(&writer, module, stage); 596 | switch (language) { 597 | case GpucLanguage_GPUC: 598 | gpucWriterWriteGpuc(&writer); 599 | break; 600 | case GpucLanguage_GLSL_330: 601 | gpucWriterWriteGlsl(&writer); 602 | break; 603 | case GpucLanguage_HLSL_11: 604 | gpucWriterWriteHlslModule(&writer); 605 | break; 606 | case GpucLanguage_Metal_1: 607 | gpucWriterWriteMetalModule(&writer); 608 | break; 609 | default: 610 | gpucWriterWriteGpuc(&writer); 611 | break; 612 | } 613 | 614 | // trim trailing whitespace 615 | while ( 616 | array_size(module->translation) and 617 | isspace(array_back(module->translation))) 618 | array_pop(module->translation); 619 | 620 | array_append(module->translation, '\n'); 621 | 622 | // append nul terminator 623 | array_append(module->translation, '\0'); 624 | 625 | return module->translation; 626 | } 627 | -------------------------------------------------------------------------------- /implementation/module.lex.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "assert.h" 4 | #include "module.h" 5 | 6 | //------------------------------------------------------------------------------ 7 | 8 | static inline 9 | bool gpucModuleAppendIdentifierLexeme( 10 | GpucModule* const module, 11 | const char** itr, 12 | const char* const end) 13 | { 14 | static_assert(GpucTokenCount < 255, "NumGpucTokens < 255"); 15 | static const unsigned char tokens[] = { 16 | #define GPUC_IDENTIFIER_TOKEN(TOKEN, LITERAL) GpucToken_##TOKEN, 17 | GPUC_KEYWORD_TOKENS(GPUC_IDENTIFIER_TOKEN) 18 | GPUC_TYPENAME_TOKENS(GPUC_IDENTIFIER_TOKEN) 19 | #undef GPUC_IDENTIFIER_TOKEN 20 | GpucToken_None, 21 | }; 22 | 23 | static const char* const literals[] = { 24 | #define GPUC_IDENTIFIER_TOKEN(TOKEN, LITERAL) LITERAL, 25 | GPUC_KEYWORD_TOKENS(GPUC_IDENTIFIER_TOKEN) 26 | GPUC_TYPENAME_TOKENS(GPUC_IDENTIFIER_TOKEN) 27 | #undef GPUC_IDENTIFIER_TOKEN 28 | }; 29 | 30 | const char* p = *itr; 31 | 32 | if (gpucSkipChar(&p, end, '_') or gpucSkipAlphaChar(&p, end)) { 33 | while (gpucSkipChar(&p, end, '_') or gpucSkipAlnumChar(&p, end)); 34 | 35 | GpucLexeme lexeme = { 36 | .head = *itr, 37 | .length = (unsigned)(((size_t)p) - ((size_t)*itr)), 38 | .token = GpucToken_Identifier, 39 | }; 40 | *itr = p; 41 | 42 | array_append(module->lexemes, lexeme); 43 | 44 | for (unsigned i = 0; tokens[i]; ++i) { 45 | const char* const literal = literals[i]; 46 | if (gpucLexemeIsEqualToString(&lexeme, literal)) { 47 | array_back(module->lexemes).token = (GpucToken)tokens[i]; 48 | break; 49 | } 50 | } 51 | 52 | return true; 53 | } 54 | return false; 55 | } 56 | 57 | //------------------------------------------------------------------------------ 58 | 59 | static inline 60 | bool gpucModuleAppendNumberLexeme( 61 | GpucModule* const module, 62 | const char** itr, 63 | const char* const end) 64 | { 65 | const char* p = *itr; 66 | 67 | if (gpucSkipDigitChar(&p, end)) { 68 | GpucLexeme lexeme = { .head = *itr }; 69 | 70 | while (gpucSkipDigitChar(&p, end)); 71 | lexeme.token = GpucToken_IntLiteral; 72 | 73 | if (gpucSkipChar(&p, end, '.')) { 74 | while(gpucSkipDigitChar(&p, end)); 75 | lexeme.token = GpucToken_DoubleLiteral; 76 | } 77 | 78 | if (gpucSkipChar(&p, end, 'e') or gpucSkipChar(&p, end, 'E')) { 79 | gpucSkipChar(&p, end, '-') or gpucSkipChar(&p, end, '+'); 80 | while(gpucSkipDigitChar(&p, end)); 81 | lexeme.token = GpucToken_DoubleLiteral; 82 | } 83 | 84 | if (gpucSkipChar(&p, end, 'f') or gpucSkipChar(&p, end, 'F')) { 85 | lexeme.token = GpucToken_FloatLiteral; 86 | } 87 | 88 | if (lexeme.token == GpucToken_IntLiteral) { 89 | if (gpucSkipChar(&p, end, 'u') or gpucSkipChar(&p, end, 'U')) { 90 | lexeme.token = GpucToken_UIntLiteral; 91 | } 92 | } 93 | 94 | bool hasInvalidSuffix = false; 95 | if (gpucSkipAlnumChar(&p, end)) { 96 | while (gpucSkipAlnumChar(&p, end)); 97 | hasInvalidSuffix = true; 98 | } 99 | 100 | lexeme.length = (unsigned)(p - *itr); 101 | *itr = p; 102 | 103 | if (hasInvalidSuffix) { 104 | gpucError(&lexeme, "invalid number literal suffix"); 105 | } 106 | 107 | array_append(module->lexemes, lexeme); 108 | return true; 109 | } 110 | return false; 111 | } 112 | 113 | //------------------------------------------------------------------------------ 114 | 115 | static inline 116 | bool gpucModuleAppendSymbolLexeme( 117 | GpucModule* const module, 118 | const char** itr, 119 | const char* const end) 120 | { 121 | static const unsigned char tokens[] = { 122 | #define GPUC_SYMBOL_TOKEN(TOKEN, LITERAL) GpucToken_##TOKEN, 123 | GPUC_SYMBOL_TOKENS(GPUC_SYMBOL_TOKEN) 124 | #undef GPUC_SYMBOL_TOKEN 125 | GpucToken_None, 126 | }; 127 | 128 | static const char* const literals[] = { 129 | #define GPUC_SYMBOL_TOKEN(TOKEN, LITERAL) LITERAL, 130 | GPUC_SYMBOL_TOKENS(GPUC_SYMBOL_TOKEN) 131 | #undef GPUC_SYMBOL_TOKEN 132 | }; 133 | 134 | const char* p = *itr; 135 | 136 | GpucLexeme lexeme = { .head = *itr }; 137 | 138 | for (unsigned i = 0; tokens[i]; ++i) { 139 | const char* const literal = literals[i]; 140 | if (gpucSkipString(&p, end, literal)) { 141 | lexeme.length = (unsigned)(p - *itr); 142 | lexeme.token = (GpucToken)tokens[i]; 143 | *itr = p; 144 | array_append(module->lexemes, lexeme); 145 | return true; 146 | } 147 | } 148 | 149 | ++p; 150 | lexeme.length = (unsigned)(p - *itr); 151 | *itr = p; 152 | gpucError(&lexeme, "invalid symbol"); 153 | 154 | return false; 155 | } 156 | 157 | //------------------------------------------------------------------------------ 158 | 159 | static inline 160 | bool gpucModuleAppendLexeme( 161 | GpucModule* const module, 162 | const char** itr, 163 | const char* const end) 164 | { 165 | gpucSkipNonTokens(itr, end); 166 | if (*itr < end) { 167 | return gpucModuleAppendIdentifierLexeme(module, itr, end) 168 | or gpucModuleAppendNumberLexeme(module, itr, end) 169 | or gpucModuleAppendSymbolLexeme(module, itr, end); 170 | } 171 | return false; 172 | } 173 | 174 | //------------------------------------------------------------------------------ 175 | 176 | static inline 177 | bool gpucModuleLex(GpucModule* module) { 178 | bool _gpucModuleLexString( 179 | GpucModule* module, 180 | const char* itr, 181 | const char* const end); 182 | return 183 | _gpucModuleLexString( 184 | module, 185 | module->source.head, 186 | module->source.head + module->source.length); 187 | } 188 | 189 | bool _gpucModuleLexString( 190 | GpucModule* module, 191 | const char* itr, 192 | const char* const end) 193 | { 194 | while (gpucModuleAppendLexeme(module, &itr, end)); 195 | return array_empty(module->diagnostics); 196 | } 197 | -------------------------------------------------------------------------------- /implementation/node.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "../gpuc.h" 4 | #include "array/array.h" 5 | #include "lexeme.h" 6 | #include "semantic.h" 7 | 8 | typedef struct GpucNode GpucNode; 9 | 10 | typedef array_t(const GpucNode*) GpucNodeArray; 11 | 12 | struct GpucNode { 13 | GpucSemantic semantic; 14 | const GpucLexeme* lexeme; 15 | const GpucNode* type; 16 | const GpucNode* parent; 17 | GpucNodeArray children; 18 | }; 19 | 20 | const GpucNode GpucNodeDefaults = { GpucSemantic_None }; 21 | 22 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 23 | 24 | static inline 25 | GpucNode* gpucNodeAllocCopy(const GpucNode* node) { 26 | GpucNode* copy = (GpucNode*)calloc(1, sizeof(GpucNode)); 27 | memcpy(copy, node, sizeof(*node)); 28 | return copy; 29 | } 30 | 31 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 32 | 33 | static inline 34 | GpucNode* gpucNodeAlloc( 35 | const GpucSemantic semantic, 36 | const GpucLexeme* const lexeme, 37 | const GpucNode* const type) 38 | { 39 | GpucNode node = GpucNodeDefaults; 40 | node.semantic = semantic; 41 | node.lexeme = lexeme; 42 | node.type = type; 43 | return gpucNodeAllocCopy(&node); 44 | } 45 | 46 | #define gpucNodeAlloc_(semantic, lexeme, type) \ 47 | gpucNodeAlloc(GpucSemantic_##semantic, lexeme, type) 48 | 49 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 50 | 51 | static inline 52 | void gpucNodeFree(const GpucNode* const node) { 53 | gpucAssert(node, ""); 54 | array_free(node->children); 55 | free((void*)node); 56 | } 57 | 58 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 59 | 60 | static inline 61 | void gpucNodeArrayDestructor( 62 | const GpucNode** itr, 63 | const GpucNode** const end) 64 | { 65 | while (itr < end) gpucNodeFree(*itr++); 66 | } 67 | 68 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 69 | 70 | static inline 71 | const GpucNode* gpucNodeConcreteType(const GpucNode* node) { 72 | gpucAssert(node, ""); 73 | const GpucNode* type = node->type; 74 | if (type) { 75 | while (type->type != type) { 76 | gpucAssert(type->type, ""); 77 | type = type->type; 78 | } 79 | } 80 | return type; 81 | } 82 | 83 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 84 | 85 | static inline 86 | bool gpucNodeHasSemantic( 87 | const GpucNode* const node, 88 | const GpucSemantic semantic) 89 | { 90 | return node and node->semantic == semantic; 91 | } 92 | 93 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 94 | 95 | static inline 96 | bool gpucNodeHasLexeme( 97 | const GpucNode* const node, 98 | const GpucLexeme* const lexeme) 99 | { 100 | return node and gpucLexemesAreEqual(node->lexeme, lexeme); 101 | } 102 | 103 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 104 | 105 | static inline 106 | bool gpucNodeIsAssignable(const GpucNode* const node) { 107 | gpucAssert(node, ""); 108 | switch (node->semantic) { 109 | case GpucSemantic_PrefixExpression: 110 | case GpucSemantic_MemberReference: 111 | case GpucSemantic_VariableReference: 112 | return true; 113 | case GpucSemantic_NestedExpression: 114 | return gpucNodeIsAssignable(node->children[0]); 115 | default: 116 | return false; 117 | } 118 | } 119 | 120 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 121 | 122 | static inline 123 | bool gpucNodeIsFunctionDeclaration(const GpucNode* const node) { 124 | return gpucNodeHasSemantic(node, GpucSemantic_FunctionDeclaration); 125 | } 126 | 127 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 128 | 129 | static inline 130 | bool gpucNodeIsFunctionDefinition(const GpucNode* const node) { 131 | return gpucNodeHasSemantic(node, GpucSemantic_FunctionDefinition); 132 | } 133 | 134 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 135 | 136 | static inline 137 | bool gpucNodeIsFunction(const GpucNode* const node) { 138 | return gpucNodeIsFunctionDeclaration(node) 139 | or gpucNodeIsFunctionDefinition(node); 140 | } 141 | 142 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 143 | 144 | static inline 145 | bool gpucNodeIsStageEntryPoint( 146 | const GpucNode* const node, 147 | const GpucStage stage) 148 | { 149 | return 150 | (gpucNodeIsFunctionDefinition(node)) 151 | and 152 | (gpucTokenStage(node->lexeme->token) == stage); 153 | } 154 | 155 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 156 | 157 | static inline 158 | bool gpucNodeIsCallExpression(const GpucNode* const node) { 159 | return gpucNodeHasSemantic(node, GpucSemantic_CallExpression); 160 | } 161 | 162 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 163 | 164 | static inline 165 | bool gpucNodeIsFunctionOrCall(const GpucNode* const node) { 166 | return gpucNodeIsFunction(node) 167 | or gpucNodeIsCallExpression(node); 168 | } 169 | 170 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 171 | 172 | static inline 173 | bool gpucNodeIsType(const GpucNode* const node) { 174 | return node and gpucSemanticIsType(node->semantic); 175 | } 176 | 177 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 178 | 179 | static inline 180 | bool gpucNodeIsMatrixType(const GpucNode* const node) { 181 | return gpucNodeIsType(node) and gpucTokenIsMatrix(node->lexeme->token); 182 | } 183 | 184 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 185 | 186 | static inline 187 | bool gpucNodeIsVariable(const GpucNode* const node) { 188 | return node and gpucSemanticIsVariable(node->semantic); 189 | } 190 | 191 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 192 | 193 | static inline 194 | bool gpucNodeIsStructure(const GpucNode* const node) { 195 | return node and node->semantic == GpucSemantic_StructDefinition; 196 | } 197 | 198 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 199 | 200 | static inline 201 | bool gpucNodeAddChild(GpucNode* parent, GpucNode* child) { 202 | gpucAssert(parent, ""); 203 | if (not child) 204 | return false; 205 | 206 | if (not parent->children) 207 | array_alloc(parent->children, 6, gpucNodeArrayDestructor); 208 | 209 | child->parent = parent; 210 | array_append(parent->children, child); 211 | return true; 212 | } 213 | 214 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 215 | 216 | static inline 217 | const int gpucNodeGetChildIndex(const GpucNode* const node) { 218 | const GpucNode* const parent = node->parent; 219 | if (parent) 220 | { 221 | int index = 0; 222 | array_t(const GpucNode* const) children = parent->children; 223 | array_for(const GpucNode*, child, children) { 224 | if (child == node) 225 | return index; 226 | index += 1; 227 | } 228 | gpucFatal("node not found in parent->children"); 229 | } 230 | return -1; 231 | } 232 | 233 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 234 | 235 | static inline 236 | const GpucNode* gpucNodeFindChildBySemantic( 237 | const GpucNode* const parent, 238 | const GpucSemantic semantic) 239 | { 240 | gpucAssert(parent, ""); 241 | if (parent->children) { 242 | const GpucNode** itr = array_begin(parent->children); 243 | const GpucNode** end = array_end(parent->children); 244 | for (; itr < end; ++itr) { 245 | const GpucNode* child = *itr; 246 | if (gpucNodeHasSemantic(child, semantic)) { 247 | return child; 248 | } 249 | } 250 | } 251 | return NULL; 252 | } 253 | 254 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 255 | 256 | static inline 257 | const GpucNode* gpucNodeFindChildByLexeme( 258 | const GpucNode* const parent, 259 | const GpucLexeme* const lexeme) 260 | { 261 | gpucAssert(parent, ""); 262 | gpucAssert(lexeme, ""); 263 | if (parent->children) { 264 | const GpucNode** itr = array_begin(parent->children); 265 | const GpucNode** end = array_end(parent->children); 266 | for (; itr < end; ++itr) { 267 | const GpucNode* child = *itr; 268 | if (gpucNodeHasLexeme(child, lexeme)) { 269 | return child; 270 | } 271 | } 272 | } 273 | return NULL; 274 | } 275 | 276 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 277 | 278 | static inline 279 | const GpucNode* gpucNodeFindFunctionOrCallParameterList( 280 | const GpucNode* const node) 281 | { 282 | gpucAssert(node, ""); 283 | const GpucSemantic FParameterList = GpucSemantic_FunctionParameterList; 284 | const GpucSemantic CParameterList = GpucSemantic_CallParameterList; 285 | 286 | const GpucNode* list = NULL; 287 | (list = gpucNodeFindChildBySemantic(node, FParameterList)) or 288 | (list = gpucNodeFindChildBySemantic(node, CParameterList)); 289 | return list; 290 | } 291 | 292 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 293 | 294 | static inline 295 | bool gpucNodesHaveSameType( 296 | const GpucNode* const a, 297 | const GpucNode* const b) 298 | { 299 | return gpucNodeConcreteType(a) == gpucNodeConcreteType(b); 300 | } 301 | 302 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 303 | 304 | static inline 305 | bool gpucNodesHaveSameFunctionOrCallParameterTypes( 306 | const GpucNode* const f, 307 | const GpucNode* const g) 308 | { 309 | gpucAssert(gpucNodeIsFunctionOrCall(f), ""); 310 | gpucAssert(gpucNodeIsFunctionOrCall(g), ""); 311 | 312 | const GpucNode* const fParameterList = gpucNodeFindFunctionOrCallParameterList(f); 313 | const GpucNode* const gParameterList = gpucNodeFindFunctionOrCallParameterList(g); 314 | gpucAssert(fParameterList, ""); 315 | gpucAssert(gParameterList, ""); 316 | 317 | array_t(const GpucNode*) const fParameters = fParameterList->children; 318 | array_t(const GpucNode*) const gParameters = gParameterList->children; 319 | 320 | const size_t fParameterCount = array_length(fParameters); 321 | const size_t gParameterCount = array_length(gParameters); 322 | 323 | if (fParameterCount != gParameterCount) 324 | return false; 325 | 326 | for (size_t i = 0; i < fParameterCount; ++i) { 327 | const GpucNode* fParameter = fParameters[i]; 328 | const GpucNode* gParameter = gParameters[i]; 329 | if (not gpucNodesHaveSameType(fParameter, gParameter)) 330 | return false; 331 | } 332 | 333 | return true; 334 | } 335 | 336 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 337 | 338 | static inline 339 | size_t gpucNodeParameterTypeStringWrite( 340 | char** out, 341 | array_t(const GpucNode*) parameters) 342 | { 343 | unsigned parameterCount = 0; 344 | size_t length = 0; 345 | length += gpucStreamChar(out, '('); 346 | array_for(const GpucNode*, parameter, parameters) { 347 | if (parameterCount++) { 348 | length += gpucStreamChar(out, ','); 349 | } 350 | const GpucNode* parameterType = parameter->type; 351 | const GpucLexeme* parameterTypeName = parameterType->lexeme; 352 | length += gpucStreamLexeme(out, parameterTypeName); 353 | } 354 | length += gpucStreamChar(out, ')'); 355 | return length; 356 | } 357 | 358 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 359 | 360 | static inline 361 | const char* gpucNodeParameterTypeStringAlloc(const GpucNode* node) { 362 | gpucAssert(gpucNodeIsFunctionOrCall(node), ""); 363 | gpucAssert(node->type, ""); 364 | 365 | const GpucNode* const parameterList = gpucNodeFindFunctionOrCallParameterList(node); 366 | gpucAssert(parameterList, ""); 367 | 368 | char* out = NULL; 369 | 370 | array_t(const GpucNode*) const parameters = parameterList->children; 371 | const size_t length = gpucNodeParameterTypeStringWrite(&out, parameters); 372 | 373 | char* const mem = (char*)calloc(length + 1, sizeof(char)); 374 | out = mem; 375 | 376 | gpucNodeParameterTypeStringWrite(&out, parameters); 377 | 378 | return mem; 379 | } 380 | 381 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 382 | 383 | static inline 384 | size_t gpucNodeFunctionSignatureStringWrite( 385 | char** out, 386 | const GpucLexeme* returnTypeName, 387 | const GpucLexeme* functionName, 388 | array_t(const GpucNode*) parameters) 389 | { 390 | size_t length = 0; 391 | length += gpucStreamLexeme(out, returnTypeName); 392 | length += gpucStreamChar(out, ' '); 393 | length += gpucStreamLexeme(out, functionName); 394 | length += gpucNodeParameterTypeStringWrite(out, parameters); 395 | return length; 396 | } 397 | 398 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 399 | 400 | static inline 401 | const char* gpucNodeFunctionSignatureStringAlloc(const GpucNode* node) { 402 | gpucAssert(gpucNodeIsFunctionOrCall(node), ""); 403 | gpucAssert(node->type, ""); 404 | 405 | const GpucLexeme* returnTypeName = node->type->lexeme; 406 | const GpucLexeme* functionName = node->lexeme; 407 | const GpucNode* parameterList = gpucNodeFindFunctionOrCallParameterList(node); 408 | gpucAssert(parameterList, ""); 409 | 410 | char* out = NULL; 411 | 412 | const size_t length = gpucNodeFunctionSignatureStringWrite( 413 | &out, 414 | returnTypeName, 415 | functionName, 416 | parameterList->children); 417 | 418 | char* const mem = (char*)calloc(length + 1, sizeof(char)); 419 | out = mem; 420 | 421 | gpucNodeFunctionSignatureStringWrite( 422 | &out, 423 | returnTypeName, 424 | functionName, 425 | parameterList->children); 426 | 427 | return mem; 428 | } 429 | 430 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 431 | 432 | static inline 433 | void gpucNodeDebugPrint(const GpucNode* const node) { 434 | const char* const semanticName = gpucSemanticName(node->semantic); 435 | const GpucLexeme* const lexeme = node->lexeme; 436 | if (lexeme) { 437 | printf("%s \"%.*s\"", semanticName, lexeme->length, lexeme->head); 438 | } else { 439 | fputs(semanticName, stdout); 440 | } 441 | const GpucNode* const type = node->type; 442 | if (type) { 443 | const GpucLexeme* const typeName = type->lexeme; 444 | printf(" : %.*s", typeName->length, typeName->head); 445 | if (gpucNodeIsFunction(node)) { 446 | const char* const parameterString = 447 | gpucNodeParameterTypeStringAlloc(node); 448 | fputs(parameterString, stdout); 449 | free((void*)parameterString); 450 | } 451 | } 452 | } 453 | 454 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 455 | 456 | void gpucNodeDebugPrintLine(const GpucNode* const node) { 457 | gpucNodeDebugPrint(node); 458 | fputc('\n', stdout); 459 | } 460 | 461 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 462 | 463 | static inline 464 | void gpucNodeDebugPrintTreeLines( 465 | const GpucNode* const root, 466 | const GpucNode* const node, 467 | const int depth) 468 | { 469 | const bool isRoot = node == root; 470 | if (isRoot) 471 | return; 472 | 473 | const GpucNode* const parent = node->parent; 474 | if (parent) { 475 | gpucNodeDebugPrintTreeLines(root, parent, depth + 1); 476 | 477 | const GpucNode* const lastNode = array_back(parent->children); 478 | const bool isLastNode = node == lastNode; 479 | if (isLastNode) { 480 | if (depth) { 481 | fputs(" ", stdout); 482 | return; 483 | } 484 | fputs(" \u2514\u2574", stdout); 485 | return; 486 | } 487 | 488 | if (depth) { 489 | fputs(" \u2502 ", stdout); 490 | return; 491 | } 492 | 493 | fputs(" \u251C\u2574", stdout); 494 | } 495 | } 496 | 497 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 498 | 499 | static inline 500 | void gpucNodeDebugPrintTreeInternal( 501 | const GpucNode* const root, 502 | const GpucNode* const node, 503 | const int rootIndentDepth, 504 | const int depth) 505 | { 506 | for (int i = 0; i < rootIndentDepth; ++i) { 507 | fputs(" ", stdout); 508 | } 509 | 510 | gpucNodeDebugPrintTreeLines(root, node, 0); 511 | 512 | gpucNodeDebugPrintLine(node); 513 | 514 | if (array_length(node->children)) { 515 | const int childDepth = depth + 1; 516 | array_for(const GpucNode*, child, node->children) { 517 | gpucNodeDebugPrintTreeInternal( 518 | root, 519 | child, 520 | rootIndentDepth, 521 | childDepth); 522 | } 523 | } 524 | } 525 | 526 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 527 | 528 | static inline 529 | void gpucNodeDebugPrintTree( 530 | const GpucNode* const node, 531 | const int rootIndentDepth) 532 | { 533 | gpucNodeDebugPrintTreeInternal( 534 | node, 535 | node, 536 | rootIndentDepth, 537 | 0); 538 | } 539 | -------------------------------------------------------------------------------- /implementation/scan.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | static bool gpucIsChar( 8 | const char* const itr, 9 | const char* const end, 10 | const char c) 11 | { 12 | if (itr < end) { 13 | return *itr == c; 14 | } 15 | return false; 16 | } 17 | 18 | static bool gpucIsCharType( 19 | const char* const itr, 20 | const char* const end, 21 | int (*ischar)(int)) 22 | { 23 | if (itr < end) { 24 | return ischar(*itr); 25 | } 26 | return false; 27 | } 28 | 29 | static bool gpucIsString( 30 | const char* const itr, 31 | const char* const end, 32 | const char* s) 33 | { 34 | if (itr < end) { 35 | for (const char* p = itr; p < end and *s and *s == *p; ++p, ++s); 36 | return *s == '\0'; 37 | } 38 | return false; 39 | } 40 | 41 | static bool gpucSkipChar( 42 | const char** itr, 43 | const char* const end, 44 | const char c) 45 | { 46 | if (gpucIsChar(*itr, end, c)) { 47 | *itr += 1; 48 | return true; 49 | } 50 | return false; 51 | } 52 | 53 | static bool gpucSkipCharType( 54 | const char** itr, 55 | const char* const end, 56 | int (*ischar)(int)) 57 | { 58 | if (gpucIsCharType(*itr, end, ischar)) { 59 | *itr += 1; 60 | return true; 61 | } 62 | return false; 63 | } 64 | 65 | #define gpucSkipAlphaChar(itr, end) gpucSkipCharType(itr, end, isalpha) 66 | #define gpucSkipAlnumChar(itr, end) gpucSkipCharType(itr, end, isalnum) 67 | #define gpucSkipDigitChar(itr, end) gpucSkipCharType(itr, end, isdigit) 68 | #define gpucSkipSpaceChar(itr, end) gpucSkipCharType(itr, end, isspace) 69 | 70 | static bool gpucSkipString( 71 | const char** itr, 72 | const char* const end, 73 | const char* s) 74 | { 75 | if (gpucIsString(*itr, end, s)) { 76 | *itr += strlen(s); 77 | return true; 78 | } 79 | return false; 80 | } 81 | 82 | static bool gpucSkipIdentifier( 83 | const char** itr, 84 | const char* const end, 85 | const char* s) 86 | { 87 | const char* p = *itr; 88 | if (gpucSkipString(&p, end, s)) { 89 | if (p < end and (*p == '_' or isalnum(*p))) { 90 | return false; 91 | } 92 | *itr = p; 93 | return true; 94 | } 95 | return false; 96 | } 97 | 98 | //static bool gpucSeekChar( 99 | // const char** itr, 100 | // const char* const end, 101 | // const char c) 102 | //{ 103 | // for (const char* p = *itr; p < end; ++p) { 104 | // if (gpucIsChar(p, end, c)) { 105 | // *itr = p; 106 | // return true; 107 | // } 108 | // } 109 | // return false; 110 | //} 111 | 112 | static bool gpucSeekString( 113 | const char** itr, 114 | const char* const end, 115 | const char* const s) 116 | { 117 | for (const char* p = *itr; p < end; ++p) { 118 | if (gpucIsString(p, end, s)) { 119 | *itr = p; 120 | return true; 121 | } 122 | } 123 | return false; 124 | } 125 | 126 | static bool gpucSkipLineContinuation(const char** itr, const char* const end) { 127 | return gpucSkipString(itr, end, "\\\n"); 128 | } 129 | 130 | static bool gpucSeekNewLine(const char** itr, const char* const end) { 131 | if (*itr < end) { 132 | const char* p = *itr; 133 | for (char c = *p; p < end and c != '\n';) { 134 | if (not gpucSkipLineContinuation(&p, end)) { 135 | c = *++p; 136 | } 137 | } 138 | gpucSkipChar(&p, end, '\n'); 139 | *itr = p; 140 | return true; 141 | } 142 | return false; 143 | } 144 | 145 | static bool gpucSkipBlockComment( 146 | const char** itr, 147 | const char* const end) 148 | { 149 | if (gpucSkipString(itr, end, "/*")) { 150 | if (gpucSeekString(itr, end, "*/")) 151 | gpucSkipString(itr, end, "*/"); 152 | else 153 | *itr = end; 154 | return true; 155 | } 156 | return false; 157 | } 158 | 159 | static bool gpucSkipLineComment( 160 | const char** itr, 161 | const char* const end) 162 | { 163 | const char* p = *itr; 164 | if (gpucSkipString(&p, end, "//")) { 165 | gpucSeekNewLine(&p, end); 166 | *itr = p; 167 | return true; 168 | } 169 | return false; 170 | } 171 | 172 | static bool gpucIsWhitespace( 173 | const char* itr, 174 | const char* const end) 175 | { 176 | return 177 | gpucSkipSpaceChar(&itr, end) or 178 | gpucSkipLineContinuation(&itr, end) or 179 | gpucSkipBlockComment(&itr, end) or 180 | gpucSkipLineComment(&itr, end); 181 | } 182 | 183 | static bool gpucSkipWhitespace( 184 | const char** itr, 185 | const char* const end) 186 | { 187 | if (gpucSkipSpaceChar(itr, end) or 188 | gpucSkipLineContinuation(itr, end) or 189 | gpucSkipBlockComment(itr, end) or 190 | gpucSkipLineComment(itr, end)) 191 | { 192 | while ( 193 | gpucSkipSpaceChar(itr, end) or 194 | gpucSkipLineContinuation(itr, end) or 195 | gpucSkipBlockComment(itr, end) or 196 | gpucSkipLineComment(itr, end) 197 | ); 198 | return true; 199 | } 200 | return false; 201 | } 202 | 203 | static bool gpucIsDirective(const char* itr, const char* const end) { 204 | return gpucSkipChar(&itr, end, '#'); 205 | } 206 | 207 | static bool gpucSkipDirectiveName( 208 | const char** itr, 209 | const char* const end, 210 | const char* const directive) 211 | { 212 | const char* p = *itr; 213 | if (gpucSkipChar(&p, end, '#')) { 214 | gpucSkipWhitespace(&p, end); 215 | if (gpucSkipIdentifier(&p, end, directive)) { 216 | *itr = p; 217 | return true; 218 | } 219 | } 220 | return false; 221 | } 222 | 223 | static bool gpucSkipDirective( 224 | const char** itr, 225 | const char* const end) 226 | { 227 | return gpucIsDirective(*itr, end) and gpucSeekNewLine(itr, end); 228 | } 229 | 230 | static bool gpucSkipNonTokens(const char** itr, const char* const end) { 231 | if (gpucIsWhitespace(*itr, end) or gpucIsDirective(*itr, end)) { 232 | while (gpucSkipWhitespace(itr, end) or gpucSkipDirective(itr, end)); 233 | return true; 234 | } 235 | return false; 236 | } 237 | 238 | static bool gpucScanUIntBase10( 239 | unsigned* value, 240 | const char** itr, 241 | const char* const end) 242 | { 243 | unsigned result = 0; 244 | const char* p = *itr; 245 | char c = *p; 246 | if (isdigit(c)) { 247 | for (; p < end and isdigit(c); c = *++p) { 248 | const unsigned digit = c - '\0'; 249 | result *= 10; 250 | result += digit; 251 | } 252 | *value = result; 253 | *itr = p; 254 | return true; 255 | } 256 | return false; 257 | } 258 | -------------------------------------------------------------------------------- /implementation/semantic.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define GPUC_SEMANTICS(GPUC_SEMANTIC)\ 4 | GPUC_SEMANTIC(None)\ 5 | /**/\ 6 | GPUC_SEMANTIC(PrimitiveType)\ 7 | /**/\ 8 | GPUC_SEMANTIC(Module)\ 9 | /**/\ 10 | GPUC_SEMANTIC(StructDefinition)\ 11 | GPUC_SEMANTIC(StructFieldDefinition)\ 12 | /**/\ 13 | GPUC_SEMANTIC(FunctionDeclaration)\ 14 | GPUC_SEMANTIC(FunctionDefinition)\ 15 | GPUC_SEMANTIC(FunctionParameterList)\ 16 | GPUC_SEMANTIC(ParameterDeclaration)\ 17 | /**/\ 18 | GPUC_SEMANTIC(GlobalParameterDeclaration)\ 19 | GPUC_SEMANTIC(VariableDeclaration)\ 20 | /**/\ 21 | GPUC_SEMANTIC(EmptyStatement)\ 22 | GPUC_SEMANTIC(IfStatement)\ 23 | GPUC_SEMANTIC(SwitchStatement)\ 24 | GPUC_SEMANTIC(WhileStatement)\ 25 | GPUC_SEMANTIC(DoStatement)\ 26 | GPUC_SEMANTIC(ForStatement)\ 27 | GPUC_SEMANTIC(GotoStatement)\ 28 | GPUC_SEMANTIC(ContinueStatement)\ 29 | GPUC_SEMANTIC(BreakStatement)\ 30 | GPUC_SEMANTIC(ReturnStatement)\ 31 | GPUC_SEMANTIC(CompoundStatement)\ 32 | GPUC_SEMANTIC(ExpressionStatement)\ 33 | /**/\ 34 | GPUC_SEMANTIC(AssignmentExpression)\ 35 | GPUC_SEMANTIC(PrefixExpression)\ 36 | GPUC_SEMANTIC(PostfixExpression)\ 37 | GPUC_SEMANTIC(NestedExpression)\ 38 | GPUC_SEMANTIC(MemberExpression)\ 39 | GPUC_SEMANTIC(SubscriptExpression)\ 40 | GPUC_SEMANTIC(CallExpression)\ 41 | GPUC_SEMANTIC(CallParameterList)\ 42 | GPUC_SEMANTIC(TernaryExpression)\ 43 | GPUC_SEMANTIC(LogicalOrExpression)\ 44 | GPUC_SEMANTIC(LogicalAndExpression)\ 45 | GPUC_SEMANTIC(BitwiseOrExpression)\ 46 | GPUC_SEMANTIC(BitwiseXorExpression)\ 47 | GPUC_SEMANTIC(BitwiseAndExpression)\ 48 | GPUC_SEMANTIC(EqualityExpression)\ 49 | GPUC_SEMANTIC(RelationalExpression)\ 50 | GPUC_SEMANTIC(ShiftExpression)\ 51 | GPUC_SEMANTIC(SumExpression)\ 52 | GPUC_SEMANTIC(ProductExpression)\ 53 | GPUC_SEMANTIC(LiteralExpression)\ 54 | /**/\ 55 | GPUC_SEMANTIC(MemberReference)\ 56 | GPUC_SEMANTIC(ParamReference)\ 57 | GPUC_SEMANTIC(VariableReference)\ 58 | /**/ 59 | 60 | typedef enum GpucSemantic { 61 | #define GPUC_SEMANTIC_ENUM(SEMANTIC)\ 62 | GpucSemantic_##SEMANTIC, 63 | GPUC_SEMANTICS(GPUC_SEMANTIC_ENUM) 64 | #undef GPUC_SEMANTIC_ENUM 65 | GpucSemanticCount, 66 | } GpucSemantic; 67 | 68 | static inline const char* gpucSemanticName(GpucSemantic semantic) { 69 | switch (semantic) { 70 | #define GPUC_SEMANTIC_NAME(SEMANTIC)\ 71 | case GpucSemantic_##SEMANTIC: return #SEMANTIC; 72 | GPUC_SEMANTICS(GPUC_SEMANTIC_NAME) 73 | #undef GPUC_SEMANTIC_NAME 74 | default: return ""; 75 | } 76 | } 77 | 78 | static inline bool gpucSemanticIsFunction(GpucSemantic semantic) { 79 | switch (semantic) { 80 | case GpucSemantic_FunctionDeclaration: 81 | case GpucSemantic_FunctionDefinition: 82 | return true; 83 | default: return false; 84 | } 85 | } 86 | 87 | static inline bool gpucSemanticIsType(GpucSemantic semantic) { 88 | switch (semantic) { 89 | case GpucSemantic_PrimitiveType: 90 | case GpucSemantic_StructDefinition: 91 | return true; 92 | default: return false; 93 | } 94 | } 95 | 96 | static inline bool gpucSemanticIsVariable(GpucSemantic semantic) { 97 | switch (semantic) { 98 | case GpucSemantic_GlobalParameterDeclaration: 99 | case GpucSemantic_VariableDeclaration: 100 | return true; 101 | default: return false; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /implementation/stream.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "lexeme.h" 9 | 10 | static size_t gpucStreamWrite(char** out, const char* head, size_t length) { 11 | const bool canWrite = (out and *out); 12 | if (canWrite) { 13 | strncpy(*out, head, length); 14 | *out += length; 15 | **out = '\0'; 16 | } 17 | return length; 18 | } 19 | 20 | static size_t gpucStreamChar(char** out, const char c) { 21 | return gpucStreamWrite(out, &c, 1); 22 | } 23 | 24 | static size_t gpucStreamSubstring(char** out, const GpucSubstring* substring) { 25 | return gpucStreamWrite(out, substring->head, substring->length); 26 | } 27 | 28 | static size_t gpucStreamString(char** out, const char* s) { 29 | return gpucStreamWrite(out, s, strlen(s)); 30 | } 31 | 32 | static size_t gpucStreamLexeme(char** out, const GpucLexeme* lexeme) { 33 | return gpucStreamWrite(out, lexeme->head, lexeme->length); 34 | } 35 | 36 | static size_t gpucStreamFormatV(char** out, const char* format, va_list args) { 37 | const bool canWrite = (out and *out); 38 | 39 | va_list argsCopy; 40 | va_copy(argsCopy, args); 41 | int length = 42 | canWrite 43 | ? vsprintf(*out, format, argsCopy) 44 | : vsnprintf(NULL, 0, format, argsCopy); 45 | va_end(argsCopy); 46 | 47 | if (length < 0) 48 | length = 0; 49 | 50 | if (canWrite) { 51 | *out += length; 52 | **out = '\0'; 53 | } 54 | return length; 55 | } 56 | 57 | static size_t gpucStreamFormat(char** out, const char* format, ...) { 58 | va_list args; 59 | va_start(args, format); 60 | const size_t length = gpucStreamFormatV(out, format, args); 61 | va_end(args); 62 | return length; 63 | } 64 | -------------------------------------------------------------------------------- /implementation/tests.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "module.h" 3 | 4 | //------------------------------------------------------------------------------ 5 | 6 | enum { GpucParseRulesArrayLength = 8 }; 7 | 8 | typedef struct GpucParseRules { 9 | const GpucParseRule array[GpucParseRulesArrayLength]; 10 | } GpucParseRules; 11 | 12 | #define GpucParseRules(...)\ 13 | static const GpucParseRules gpucParseRules = {{ __VA_ARGS__ }} 14 | 15 | //------------------------------------------------------------------------------ 16 | 17 | static bool gpucModuleParseTest( 18 | GpucModule* const module, 19 | const GpucParseRules* const rules) 20 | { 21 | const GpucLexeme* itr = array_begin(module->lexemes); 22 | const GpucLexeme* const end = array_end(module->lexemes); 23 | const GpucNode* const structType = gpucGetPrimitiveType("void"); 24 | for (unsigned i = 0; i < GpucParseRulesArrayLength; ++i) { 25 | const GpucParseRule rule = rules->array[i]; 26 | if (not rule) break; 27 | GpucNode* const node = 28 | gpucModuleParseNode(module, &itr, end, structType, rule); 29 | if (not node) return false; 30 | gpucNodeAddChild(module->root, node); 31 | if (array_length(module->diagnostics)) return false; 32 | } 33 | return true; 34 | } 35 | 36 | //------------------------------------------------------------------------------ 37 | 38 | static bool gpucParseTest( 39 | const char* const source, 40 | const GpucParseRules* const rules) 41 | { 42 | GpucModule* const module = gpucModuleAlloc("test", source); 43 | const bool passed = 44 | gpucModuleLex(module) and 45 | gpucModuleParseTest(module, rules); 46 | if (not passed) { 47 | gpucModulePrintDiagnostics(module); 48 | gpucModulePrintDebug(module); 49 | } 50 | gpucModuleFree(module); 51 | return passed; 52 | } 53 | 54 | //------------------------------------------------------------------------------ 55 | 56 | /*public*/ 57 | int gpucTests(void) { 58 | #define gpucParseTest_(source) \ 59 | failureCount += !gpucParseTest(source, &gpucParseRules) 60 | int failureCount = 0; 61 | 62 | { 63 | GpucParseRules(GpucParseRule_StructDefinition); 64 | gpucParseTest_("struct Foo {};"); 65 | } 66 | 67 | { 68 | GpucParseRules( 69 | GpucParseRule_VariableStatement, 70 | GpucParseRule_SumExpression, 71 | ); 72 | gpucParseTest_("int a; a + a"); 73 | } 74 | 75 | { 76 | GpucParseRules( 77 | GpucParseRule_VariableStatement, 78 | GpucParseRule_ExpressionStatement, 79 | ); 80 | gpucParseTest_("int a; a + a;"); 81 | } 82 | 83 | return failureCount; 84 | #undef gpucParseTest_ 85 | } 86 | -------------------------------------------------------------------------------- /implementation/token.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | #define GPUC_KEYWORD_TOKENS(GPUC_KEYWORD_TOKEN)\ 7 | GPUC_KEYWORD_TOKEN(Attribute, "attribute")\ 8 | GPUC_KEYWORD_TOKEN(Break, "break")\ 9 | GPUC_KEYWORD_TOKEN(Case, "case")\ 10 | GPUC_KEYWORD_TOKEN(Const, "const")\ 11 | GPUC_KEYWORD_TOKEN(Continue, "continue")\ 12 | GPUC_KEYWORD_TOKEN(Default, "default")\ 13 | GPUC_KEYWORD_TOKEN(Discard, "discard")\ 14 | GPUC_KEYWORD_TOKEN(Do, "do")\ 15 | GPUC_KEYWORD_TOKEN(Else, "else")\ 16 | GPUC_KEYWORD_TOKEN(FalseLiteral, "false")\ 17 | GPUC_KEYWORD_TOKEN(For, "for")\ 18 | GPUC_KEYWORD_TOKEN(Goto, "goto")\ 19 | GPUC_KEYWORD_TOKEN(If, "if")\ 20 | GPUC_KEYWORD_TOKEN(Param, "param")\ 21 | GPUC_KEYWORD_TOKEN(Return, "return")\ 22 | GPUC_KEYWORD_TOKEN(Struct, "struct")\ 23 | GPUC_KEYWORD_TOKEN(Switch, "switch")\ 24 | GPUC_KEYWORD_TOKEN(TrueLiteral, "true")\ 25 | GPUC_KEYWORD_TOKEN(Typedef, "typedef")\ 26 | GPUC_KEYWORD_TOKEN(While, "while")\ 27 | GPUC_KEYWORD_TOKEN(Comp, "comp")\ 28 | GPUC_KEYWORD_TOKEN(Frag, "frag")\ 29 | GPUC_KEYWORD_TOKEN(Vert, "vert")\ 30 | 31 | #define GPUC_TYPENAME_TOKENS(GPUC_TYPENAME_TOKEN)\ 32 | GPUC_TYPENAME_TOKEN(Void, "void")\ 33 | GPUC_TYPENAME_TOKEN(Bool, "bool")\ 34 | GPUC_TYPENAME_TOKEN(Bool2, "bool2")\ 35 | GPUC_TYPENAME_TOKEN(Bool3, "bool3")\ 36 | GPUC_TYPENAME_TOKEN(Bool4, "bool4")\ 37 | GPUC_TYPENAME_TOKEN(Double, "double")\ 38 | GPUC_TYPENAME_TOKEN(Float, "float")\ 39 | GPUC_TYPENAME_TOKEN(Float2, "float2")\ 40 | GPUC_TYPENAME_TOKEN(Float3, "float3")\ 41 | GPUC_TYPENAME_TOKEN(Float4, "float4")\ 42 | GPUC_TYPENAME_TOKEN(Float2x2, "float2x2")\ 43 | GPUC_TYPENAME_TOKEN(Float2x3, "float2x3")\ 44 | GPUC_TYPENAME_TOKEN(Float2x4, "float2x4")\ 45 | GPUC_TYPENAME_TOKEN(Float3x2, "float3x2")\ 46 | GPUC_TYPENAME_TOKEN(Float3x3, "float3x3")\ 47 | GPUC_TYPENAME_TOKEN(Float3x4, "float3x4")\ 48 | GPUC_TYPENAME_TOKEN(Float4x2, "float4x2")\ 49 | GPUC_TYPENAME_TOKEN(Float4x3, "float4x3")\ 50 | GPUC_TYPENAME_TOKEN(Float4x4, "float4x4")\ 51 | GPUC_TYPENAME_TOKEN(Int, "int")\ 52 | GPUC_TYPENAME_TOKEN(Int2, "int2")\ 53 | GPUC_TYPENAME_TOKEN(Int3, "int3")\ 54 | GPUC_TYPENAME_TOKEN(Int4, "int4")\ 55 | GPUC_TYPENAME_TOKEN(UInt, "uint")\ 56 | GPUC_TYPENAME_TOKEN(UInt2, "uint2")\ 57 | GPUC_TYPENAME_TOKEN(UInt3, "uint3")\ 58 | GPUC_TYPENAME_TOKEN(UInt4, "uint4")\ 59 | GPUC_TYPENAME_TOKEN(TEXTURE1D, "texture1d")\ 60 | GPUC_TYPENAME_TOKEN(TEXTURE2D, "texture2d")\ 61 | GPUC_TYPENAME_TOKEN(TEXTURE3D, "texture3d")\ 62 | GPUC_TYPENAME_TOKEN(TEXTURECUBE, "texturecube")\ 63 | GPUC_TYPENAME_TOKEN(DEPTH2D, "depth2d")\ 64 | GPUC_TYPENAME_TOKEN(DEPTHCUBE, "depthcube")\ 65 | GPUC_TYPENAME_TOKEN(SAMPLER, "sampler")\ 66 | 67 | 68 | #define GPUC_SYMBOL_TOKENS(GPUC_SYMBOL_TOKEN)\ 69 | /* Delimiters */\ 70 | GPUC_SYMBOL_TOKEN(LBrace, "{")\ 71 | GPUC_SYMBOL_TOKEN(RBrace, "}")\ 72 | GPUC_SYMBOL_TOKEN(LBrack, "[")\ 73 | GPUC_SYMBOL_TOKEN(RBrack, "]")\ 74 | GPUC_SYMBOL_TOKEN(LParen, "(")\ 75 | GPUC_SYMBOL_TOKEN(RParen, ")")\ 76 | GPUC_SYMBOL_TOKEN(Comma, ",")\ 77 | GPUC_SYMBOL_TOKEN(Semicolon, ";")\ 78 | GPUC_SYMBOL_TOKEN(Arrow, "->")\ 79 | GPUC_SYMBOL_TOKEN(Ellipsis, "...")\ 80 | GPUC_SYMBOL_TOKEN(Dot, ".")\ 81 | /* Ternary Operator */\ 82 | GPUC_SYMBOL_TOKEN(Question, "?")\ 83 | GPUC_SYMBOL_TOKEN(Colon, ":")\ 84 | /* Integral Operators */\ 85 | GPUC_SYMBOL_TOKEN(Compl, "~")\ 86 | GPUC_SYMBOL_TOKEN(LshEq, "<<=")\ 87 | GPUC_SYMBOL_TOKEN(Lsh, "<<")\ 88 | GPUC_SYMBOL_TOKEN(RshEq, "<<=")\ 89 | GPUC_SYMBOL_TOKEN(Rsh, ">>")\ 90 | GPUC_SYMBOL_TOKEN(OrEq, "|=")\ 91 | GPUC_SYMBOL_TOKEN(Or, "|")\ 92 | GPUC_SYMBOL_TOKEN(XorEq, "^=")\ 93 | GPUC_SYMBOL_TOKEN(Xor, "^")\ 94 | GPUC_SYMBOL_TOKEN(AndEq, "&=")\ 95 | GPUC_SYMBOL_TOKEN(And, "&")\ 96 | /* Relational Operators */\ 97 | GPUC_SYMBOL_TOKEN(EqEq, "==")\ 98 | GPUC_SYMBOL_TOKEN(LtEq, "<=")\ 99 | GPUC_SYMBOL_TOKEN(Lt, "<")\ 100 | GPUC_SYMBOL_TOKEN(GtEq, ">=")\ 101 | GPUC_SYMBOL_TOKEN(Gt, ">")\ 102 | GPUC_SYMBOL_TOKEN(NotEq, "!=")\ 103 | /* Logical Operators */\ 104 | GPUC_SYMBOL_TOKEN(OrOr, "||")\ 105 | GPUC_SYMBOL_TOKEN(AndAnd, "&&")\ 106 | GPUC_SYMBOL_TOKEN(Not, "!")\ 107 | /* Arithmetic Operators */\ 108 | GPUC_SYMBOL_TOKEN(Inc, "++")\ 109 | GPUC_SYMBOL_TOKEN(Dec, "--")\ 110 | GPUC_SYMBOL_TOKEN(AddEq, "+=")\ 111 | GPUC_SYMBOL_TOKEN(Add, "+")\ 112 | GPUC_SYMBOL_TOKEN(SubEq, "-=")\ 113 | GPUC_SYMBOL_TOKEN(Sub, "-")\ 114 | GPUC_SYMBOL_TOKEN(DivEq, "/=")\ 115 | GPUC_SYMBOL_TOKEN(Div, "/")\ 116 | GPUC_SYMBOL_TOKEN(ModEq, "%=")\ 117 | GPUC_SYMBOL_TOKEN(Mod, "%")\ 118 | GPUC_SYMBOL_TOKEN(MulEq, "*=")\ 119 | GPUC_SYMBOL_TOKEN(Mul, "*")\ 120 | GPUC_SYMBOL_TOKEN(Eq, "=")\ 121 | 122 | #define GPUC_TOKENS(GPUC_TOKEN)\ 123 | GPUC_TOKEN(None, "")\ 124 | GPUC_TOKEN(Directive, "")\ 125 | GPUC_TOKEN(DoubleLiteral, "")\ 126 | GPUC_TOKEN(FloatLiteral, "")\ 127 | GPUC_TOKEN(IntLiteral, "")\ 128 | GPUC_TOKEN(UIntLiteral, "")\ 129 | GPUC_TOKEN(Identifier, "")\ 130 | GPUC_KEYWORD_TOKENS(GPUC_TOKEN)\ 131 | GPUC_TYPENAME_TOKENS(GPUC_TOKEN)\ 132 | GPUC_SYMBOL_TOKENS(GPUC_TOKEN)\ 133 | 134 | typedef enum GpucToken { 135 | #define GPUC_TOKEN(TOKEN,...) GpucToken_##TOKEN, 136 | GPUC_TOKENS(GPUC_TOKEN) 137 | #undef GPUC_TOKEN 138 | GpucTokenCount, 139 | } GpucToken; 140 | 141 | static_assert(GpucTokenCount < 255, "NumGpucTokens < 255"); 142 | 143 | static inline const char* gpucTokenName(GpucToken token) { 144 | switch (token) { 145 | #define GPUC_TOKEN_NAME(TOKEN,...) \ 146 | case GpucToken_##TOKEN: return #TOKEN; 147 | GPUC_TOKENS(GPUC_TOKEN_NAME) 148 | #undef GPUC_TOKEN_NAME 149 | default: return ""; 150 | } 151 | } 152 | 153 | static inline const char* gpucTokenLiteral(GpucToken token) { 154 | switch (token) { 155 | #define GPUC_TOKEN_LITERAL(TOKEN, LITERAL) \ 156 | case GpucToken_##TOKEN: return LITERAL; 157 | GPUC_TOKENS(GPUC_TOKEN_LITERAL) 158 | #undef GPUC_TOKEN_LITERAL 159 | default: return ""; 160 | } 161 | } 162 | 163 | static inline bool gpucTokenIsKeyword(GpucToken token) { 164 | switch (token) { 165 | #define GPUC_KEYWORD_TOKEN(TOKEN,...)\ 166 | case GpucToken_##TOKEN: return true; 167 | GPUC_KEYWORD_TOKENS(GPUC_KEYWORD_TOKEN) 168 | #undef GPUC_KEYWORD_TOKEN 169 | 170 | default: return false; 171 | } 172 | } 173 | 174 | static inline bool gpucTokenIsTypename(GpucToken token) { 175 | switch (token) { 176 | #define GPUC_TYPENAME_TOKEN(TOKEN,...)\ 177 | case GpucToken_##TOKEN: return true; 178 | GPUC_TYPENAME_TOKENS(GPUC_TYPENAME_TOKEN) 179 | #undef GPUC_TYPENAME_TOKEN 180 | 181 | default: return false; 182 | } 183 | } 184 | 185 | static inline bool gpucTokenIsReservedWord(GpucToken token) { 186 | return gpucTokenIsKeyword(token) 187 | or gpucTokenIsTypename(token); 188 | } 189 | 190 | static inline bool gpucTokenIsEntryPoint(GpucToken token) { 191 | switch (token) { 192 | case GpucToken_Comp: 193 | case GpucToken_Frag: 194 | case GpucToken_Vert: 195 | return true; 196 | 197 | default: return false; 198 | } 199 | } 200 | 201 | static inline GpucStage gpucTokenStage(GpucToken token) { 202 | switch (token) { 203 | case GpucToken_Comp: return GpucStage_Compute; 204 | case GpucToken_Frag: return GpucStage_Fragment; 205 | case GpucToken_Vert: return GpucStage_Vertex; 206 | 207 | default: return GpucStage_None; 208 | } 209 | } 210 | 211 | static inline bool gpucTokenIsIdentifierOrEntryPoint(GpucToken token) { 212 | return token == GpucToken_Identifier 213 | or gpucTokenIsEntryPoint(token); 214 | } 215 | 216 | static inline bool gpucTokenIsIdentifierOrTypename(GpucToken token) { 217 | return token == GpucToken_Identifier 218 | or gpucTokenIsTypename(token); 219 | } 220 | 221 | static inline bool gpucTokenIsIdentifierOrReservedWord(GpucToken token) { 222 | return token == GpucToken_Identifier 223 | or gpucTokenIsReservedWord(token); 224 | } 225 | 226 | static inline bool gpucTokenIsAssignmentOperator(GpucToken token) { 227 | switch (token) { 228 | case GpucToken_LshEq: // <<= 229 | case GpucToken_RshEq: // <<= 230 | case GpucToken_AddEq: // += 231 | case GpucToken_AndEq: // &= 232 | case GpucToken_DivEq: // /= 233 | case GpucToken_ModEq: // %= 234 | case GpucToken_MulEq: // *= 235 | case GpucToken_OrEq: // |= 236 | case GpucToken_SubEq: // -= 237 | case GpucToken_XorEq: // ^= 238 | case GpucToken_Eq: // = 239 | return true; 240 | 241 | default: return false; 242 | } 243 | } 244 | 245 | static inline bool gpucTokenIsEqualityOperator(GpucToken token) { 246 | switch (token) { 247 | case GpucToken_EqEq: // == 248 | case GpucToken_NotEq: // != 249 | return true; 250 | 251 | default: return false; 252 | } 253 | } 254 | 255 | static inline bool gpucTokenIsRelationalOperator(GpucToken token) { 256 | switch (token) { 257 | case GpucToken_Lt: // < 258 | case GpucToken_LtEq: // <= 259 | case GpucToken_Gt: // > 260 | case GpucToken_GtEq: // >= 261 | return true; 262 | 263 | default: return false; 264 | } 265 | } 266 | 267 | static inline bool gpucTokenIsShiftOperator(GpucToken token) { 268 | switch (token) { 269 | case GpucToken_Lsh: // << 270 | case GpucToken_Rsh: // >> 271 | return true; 272 | 273 | default: return false; 274 | } 275 | } 276 | 277 | static inline bool gpucTokenIsSumOperator(GpucToken token) { 278 | switch (token) { 279 | case GpucToken_Add: // + 280 | case GpucToken_Sub: // - 281 | return true; 282 | 283 | default: return false; 284 | } 285 | } 286 | 287 | static inline bool gpucTokenIsProductOperator(GpucToken token) { 288 | switch (token) { 289 | case GpucToken_Div: // / 290 | case GpucToken_Mod: // % 291 | case GpucToken_Mul: // * 292 | return true; 293 | 294 | default: return false; 295 | } 296 | } 297 | 298 | static inline bool gpucTokenIsPrefixOperator(GpucToken token) { 299 | switch (token) { 300 | case GpucToken_Inc: // ++ 301 | case GpucToken_Dec: // -- 302 | case GpucToken_Add: // + 303 | case GpucToken_Sub: // - 304 | case GpucToken_Not: // ! 305 | case GpucToken_Compl: // ~ 306 | return true; 307 | 308 | default: return false; 309 | } 310 | } 311 | 312 | static inline bool gpucTokenIsPostfixOperator(GpucToken token) { 313 | switch (token) { 314 | case GpucToken_Inc: // ++ 315 | case GpucToken_Dec: // -- 316 | return true; 317 | 318 | default: return false; 319 | } 320 | } 321 | 322 | static inline bool gpucTokenIsLiteral(GpucToken token) { 323 | switch (token) { 324 | case GpucToken_DoubleLiteral: 325 | case GpucToken_FalseLiteral: 326 | case GpucToken_FloatLiteral: 327 | case GpucToken_IntLiteral: 328 | case GpucToken_TrueLiteral: 329 | case GpucToken_UIntLiteral: 330 | return true; 331 | 332 | default: return false; 333 | } 334 | } 335 | 336 | static inline bool gpucTokenIsMatrix(GpucToken token) { 337 | switch (token) { 338 | case GpucToken_Float2x2: 339 | case GpucToken_Float2x3: 340 | case GpucToken_Float2x4: 341 | case GpucToken_Float3x2: 342 | case GpucToken_Float3x3: 343 | case GpucToken_Float3x4: 344 | case GpucToken_Float4x2: 345 | case GpucToken_Float4x3: 346 | case GpucToken_Float4x4: 347 | return true; 348 | 349 | default: return false; 350 | } 351 | } 352 | -------------------------------------------------------------------------------- /implementation/writer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "module.h" 3 | 4 | //------------------------------------------------------------------------------ 5 | 6 | typedef struct GpucWriter GpucWriter; 7 | 8 | //------------------------------------------------------------------------------ 9 | 10 | typedef void (*GpucWriterWriteNode)(GpucWriter* const, const GpucNode* const); 11 | 12 | #define GPUC_WRITER_WRITE(SEMANTIC)\ 13 | static inline\ 14 | void gpucWriterWrite##SEMANTIC##Node(\ 15 | GpucWriter* const writer,\ 16 | const GpucNode* const node) 17 | 18 | #define GPUC_WRITER_WRITE_DECLARATION(SEMANTIC)\ 19 | GPUC_WRITER_WRITE(SEMANTIC); 20 | GPUC_SEMANTICS(GPUC_WRITER_WRITE_DECLARATION) 21 | #undef GPUC_WRITER_WRITE_DECLARATION 22 | 23 | //------------------------------------------------------------------------------ 24 | 25 | static inline 26 | void gpucWriterWriteNodeLexeme( 27 | GpucWriter* const writer, 28 | const GpucNode* const node); 29 | 30 | static inline 31 | void gpucWriterWriteNodeTypeLexeme( 32 | GpucWriter* const writer, 33 | const GpucNode* const node) 34 | { 35 | gpucWriterWriteNodeLexeme(writer, node->type); 36 | } 37 | 38 | //------------------------------------------------------------------------------ 39 | 40 | struct GpucWriter { 41 | GpucModule* module; 42 | GpucStage stage; 43 | const GpucNode* stageEntryPoint; 44 | const GpucNode* stageInputStruct; 45 | const GpucNode* stageOutputStruct; 46 | GpucWriterWriteNode writeNode[GpucSemanticCount]; 47 | GpucWriterWriteNode writeNodeType; 48 | unsigned indentDepth; 49 | bool indentPending; 50 | }; 51 | 52 | static const GpucWriter gpucWriterDefaults = { NULL }; 53 | 54 | //------------------------------------------------------------------------------ 55 | 56 | static inline 57 | void gpucWriterSetup( 58 | GpucWriter* const writer, 59 | GpucModule* const module, 60 | const GpucStage stage) 61 | { 62 | *writer = gpucWriterDefaults; 63 | writer->module = module; 64 | writer->stage = stage; 65 | writer->stageEntryPoint = gpucModuleFindStageEntryPoint(module, stage); 66 | gpucAssert(writer->stageEntryPoint, "expected entry point"); 67 | if (writer->stageEntryPoint) 68 | { 69 | const GpucNode* const parameterList = 70 | gpucNodeFindFunctionOrCallParameterList(writer->stageEntryPoint); 71 | array_t(const GpucNode*) const parameters = parameterList->children; 72 | array_for(const GpucNode*, parameter, parameters) { 73 | if (parameter->semantic == GpucSemantic_VariableDeclaration) { 74 | gpucAssert( 75 | not writer->stageInputStruct, 76 | "redundant attributes"); 77 | const GpucNode* const paramType = parameter->type; 78 | gpucAssert(gpucNodeIsStructure(paramType), "expected struct"); 79 | writer->stageInputStruct = paramType; 80 | } 81 | } 82 | gpucAssert( 83 | writer->stageInputStruct, 84 | "expected attributes"); 85 | 86 | const GpucNode* const resultType = writer->stageEntryPoint->type; 87 | gpucAssert(gpucNodeIsStructure(resultType), "expected struct"); 88 | writer->stageOutputStruct = resultType; 89 | } 90 | 91 | #define GPUC_TRANSLATE(SEMANTIC)\ 92 | writer->writeNode[GpucSemantic_##SEMANTIC] =\ 93 | gpucWriterWrite##SEMANTIC##Node; 94 | GPUC_SEMANTICS(GPUC_TRANSLATE) 95 | #undef GPUC_TRANSLATE 96 | 97 | writer->writeNodeType = gpucWriterWriteNodeTypeLexeme; 98 | 99 | const size_t sourceLength = module->source.length; 100 | array_free(module->translation); 101 | array_alloc(module->translation, sourceLength * 2, NULL); 102 | } 103 | 104 | //------------------------------------------------------------------------------ 105 | 106 | static inline 107 | void gpucWriterIndentBegin(GpucWriter* const writer) { 108 | writer->indentDepth += 1; 109 | writer->indentPending = true; 110 | } 111 | 112 | static inline 113 | void gpucWriterIndentEnd(GpucWriter* const writer) { 114 | assert(writer->indentDepth > 0); 115 | writer->indentDepth -= 1; 116 | } 117 | 118 | #define gpucIndentBegin() gpucWriterIndentBegin(writer) 119 | #define gpucIndentEnd() gpucWriterIndentEnd(writer) 120 | 121 | //------------------------------------------------------------------------------ 122 | 123 | static inline 124 | void gpucWriterWriteChar( 125 | GpucWriter* const writer, 126 | const char c) 127 | { 128 | GpucModule* const module = writer->module; 129 | if (c == '\n') { 130 | array_append(module->translation, c); 131 | writer->indentPending = true; 132 | return; 133 | } 134 | if (writer->indentPending) { 135 | writer->indentPending = false; 136 | const unsigned indentDepth = writer->indentDepth; 137 | for (unsigned i = 0; i < indentDepth; ++i) { 138 | array_append(module->translation, ' '); 139 | array_append(module->translation, ' '); 140 | array_append(module->translation, ' '); 141 | array_append(module->translation, ' '); 142 | } 143 | } 144 | array_append(module->translation, c); 145 | } 146 | 147 | #define gpucWriteChar(c) gpucWriterWriteChar(writer, c) 148 | 149 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 150 | 151 | static inline 152 | void gpucWriterWriteLexeme( 153 | GpucWriter* const writer, 154 | const GpucLexeme* const lexeme) 155 | { 156 | const char* const head = lexeme->head; 157 | const unsigned length = lexeme->length; 158 | for (unsigned i = 0; i < length; ++i) { 159 | gpucWriterWriteChar(writer, head[i]); 160 | } 161 | } 162 | 163 | #define gpucWriteLexeme(lexeme) gpucWriterWriteLexeme(writer, lexeme) 164 | 165 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 166 | 167 | static inline 168 | void gpucWriterWriteString( 169 | GpucWriter* const writer, 170 | const char* const string) 171 | { 172 | for (const char* p = string; *p; ++p) { 173 | gpucWriterWriteChar(writer, *p); 174 | } 175 | } 176 | 177 | #define gpucWriteString(string) gpucWriterWriteString(writer, string) 178 | 179 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 180 | 181 | static inline 182 | void gpucWriterWriteFormatV( 183 | GpucWriter* const writer, 184 | const char* const format, 185 | va_list args) 186 | { 187 | const size_t length = gpucStreamFormatV(NULL, format, args); 188 | const size_t size = length + sizeof('\0'); 189 | char* const buffer = (char*)calloc(size, sizeof(char)); 190 | char* out = buffer; 191 | gpucStreamFormatV(&out, format, args); 192 | for (const char* p = buffer; *p; ++p) { 193 | gpucWriterWriteChar(writer, *p); 194 | } 195 | free(buffer); 196 | } 197 | 198 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 199 | 200 | static inline 201 | void gpucWriterWriteFormat( 202 | GpucWriter* const writer, 203 | const char* const format, ...) 204 | { 205 | va_list args; 206 | va_start(args, format); 207 | gpucWriterWriteFormatV(writer, format, args); 208 | va_end(args); 209 | } 210 | 211 | #define gpucWriteFormat(format, ...)\ 212 | gpucWriterWriteFormat(writer, format, __VA_ARGS__) 213 | 214 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 215 | 216 | static inline 217 | void gpucWriterWriteNode( 218 | GpucWriter* const writer, 219 | const GpucNode* const node) 220 | { 221 | const GpucSemantic semantic = node->semantic; 222 | const GpucWriterWriteNode writeNode = writer->writeNode[semantic]; 223 | writeNode(writer, node); 224 | } 225 | 226 | #define gpucWriteNode(node)\ 227 | gpucWriterWriteNode(writer, node) 228 | 229 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 230 | 231 | static inline 232 | void gpucWriterWriteNodeChildren( 233 | GpucWriter* const writer, 234 | const GpucNode* const node) 235 | { 236 | array_for(const GpucNode*, child, node->children) { 237 | gpucWriterWriteNode(writer, child); 238 | } 239 | } 240 | 241 | #define gpucWriteNodeChildren(node)\ 242 | gpucWriterWriteNodeChildren(writer, node) 243 | 244 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 245 | 246 | static inline 247 | void gpucWriterWriteNodeLexeme( 248 | GpucWriter* const writer, 249 | const GpucNode* const node) 250 | { 251 | gpucWriterWriteLexeme(writer, node->lexeme); 252 | } 253 | 254 | #define gpucWriteNodeLexeme(node)\ 255 | gpucWriterWriteNodeLexeme(writer, node) 256 | 257 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 258 | 259 | static inline 260 | void gpucWriterWriteNodeType( 261 | GpucWriter* const writer, 262 | const GpucNode* const node) 263 | { 264 | const GpucWriterWriteNode writeNodeType = writer->writeNodeType; 265 | writeNodeType(writer, node); 266 | } 267 | 268 | #define gpucWriteNodeType(node)\ 269 | gpucWriterWriteNodeType(writer, node) 270 | 271 | //------------------------------------------------------------------------------ 272 | 273 | static inline 274 | void gpucWriterWriteModule(GpucWriter* const writer) { 275 | gpucWriterWriteNode(writer, writer->module->root); 276 | } 277 | 278 | //------------------------------------------------------------------------------ 279 | 280 | static inline 281 | void gpucWriterWriteGpuc(GpucWriter* const writer) 282 | { 283 | gpucWriterWriteModule(writer); 284 | } 285 | 286 | //------------------------------------------------------------------------------ 287 | 288 | GPUC_WRITER_WRITE(None) { 289 | gpucAssert(false, "invalid node"); 290 | } 291 | 292 | //------------------------------------------------------------------------------ 293 | 294 | GPUC_WRITER_WRITE(PrimitiveType) { 295 | gpucAssert(false, "invalid node"); 296 | } 297 | 298 | //------------------------------------------------------------------------------ 299 | 300 | GPUC_WRITER_WRITE(Module) { 301 | gpucWriteNodeChildren(node); 302 | } 303 | 304 | //------------------------------------------------------------------------------ 305 | 306 | GPUC_WRITER_WRITE(StructDefinition) { 307 | gpucWriteString("struct "); 308 | gpucWriteNodeLexeme(node); 309 | gpucWriteString(" {\n"); 310 | gpucIndentBegin(); 311 | gpucWriteNodeChildren(node); 312 | gpucIndentEnd(); 313 | gpucWriteString("};\n\n"); 314 | } 315 | 316 | GPUC_WRITER_WRITE(StructFieldDefinition) { 317 | gpucWriteNodeType(node); 318 | gpucWriteChar(' '); 319 | gpucWriteNodeLexeme(node); 320 | gpucWriteString(";\n"); 321 | } 322 | 323 | //------------------------------------------------------------------------------ 324 | 325 | GPUC_WRITER_WRITE(FunctionDeclaration) { 326 | if (_gpucModuleIsBuiltinSymbol(writer->module, node)) { 327 | gpucWriteString("// "); 328 | } 329 | gpucWriteNodeType(node); 330 | gpucWriteChar(' '); 331 | gpucWriteNodeLexeme(node); 332 | gpucWriteNodeChildren(node); 333 | gpucWriteString(";\n\n"); 334 | } 335 | 336 | GPUC_WRITER_WRITE(FunctionDefinition) { 337 | gpucWriteNodeType(node); 338 | gpucWriteChar(' '); 339 | gpucWriteNodeLexeme(node); 340 | gpucWriteNodeChildren(node); 341 | gpucWriteString("\n"); 342 | } 343 | 344 | GPUC_WRITER_WRITE(FunctionParameterList) { 345 | gpucWriteChar('('); 346 | unsigned childCount = 0; 347 | array_for(const GpucNode*, child, node->children) { 348 | if (childCount++) gpucWriteString(", "); 349 | gpucWriteNode(child); 350 | } 351 | gpucWriteChar(')'); 352 | } 353 | 354 | GPUC_WRITER_WRITE(ParameterDeclaration) { 355 | gpucWriteNodeType(node); 356 | } 357 | 358 | //------------------------------------------------------------------------------ 359 | 360 | GPUC_WRITER_WRITE(GlobalParameterDeclaration) { 361 | gpucWriteString("param "); 362 | gpucWriteNodeType(node); 363 | gpucWriteChar(' '); 364 | gpucWriteNodeLexeme(node); 365 | gpucWriteString(";\n\n"); 366 | } 367 | 368 | GPUC_WRITER_WRITE(VariableDeclaration) { 369 | gpucWriteNodeType(node); 370 | gpucWriteChar(' '); 371 | gpucWriteNodeLexeme(node); 372 | } 373 | 374 | //------------------------------------------------------------------------------ 375 | 376 | GPUC_WRITER_WRITE(EmptyStatement) { 377 | gpucWriteString(";\n"); 378 | } 379 | 380 | GPUC_WRITER_WRITE(IfStatement) { 381 | gpucWriteString("/* TODO: IfStatement */;\n"); 382 | } 383 | 384 | GPUC_WRITER_WRITE(SwitchStatement) { 385 | gpucWriteString("/* TODO: SwitchStatement */;\n"); 386 | } 387 | 388 | GPUC_WRITER_WRITE(WhileStatement) { 389 | gpucWriteString("/* TODO: WhileStatement */;\n"); 390 | } 391 | 392 | GPUC_WRITER_WRITE(DoStatement) { 393 | gpucWriteString("/* TODO: DoStatement */;\n"); 394 | } 395 | 396 | GPUC_WRITER_WRITE(ForStatement) { 397 | gpucWriteString("/* TODO: ForStatement */;\n"); 398 | } 399 | 400 | GPUC_WRITER_WRITE(GotoStatement) { 401 | gpucWriteString("/* TODO: GotoStatement */;\n"); 402 | } 403 | 404 | GPUC_WRITER_WRITE(ContinueStatement) { 405 | gpucWriteString("/* TODO: ContinueStatement */;\n"); 406 | } 407 | 408 | GPUC_WRITER_WRITE(BreakStatement) { 409 | gpucWriteString("/* TODO: BreakStatement */;\n"); 410 | } 411 | 412 | GPUC_WRITER_WRITE(ReturnStatement) { 413 | gpucWriteNodeLexeme(node); 414 | if (array_length(node->children)) { 415 | gpucWriteChar(' '); 416 | gpucWriteNodeChildren(node); 417 | } 418 | gpucWriteString(";\n"); 419 | } 420 | 421 | GPUC_WRITER_WRITE(CompoundStatement) { 422 | gpucWriteString(" {\n"); 423 | gpucIndentBegin(); 424 | gpucWriteNodeChildren(node); 425 | gpucIndentEnd(); 426 | gpucWriteString("}\n"); 427 | } 428 | 429 | GPUC_WRITER_WRITE(ExpressionStatement) { 430 | gpucWriteNodeChildren(node); 431 | gpucWriteString(";\n"); 432 | } 433 | 434 | //------------------------------------------------------------------------------ 435 | 436 | GPUC_WRITER_WRITE(AssignmentExpression) { 437 | gpucWriteNode(node->children[0]); 438 | gpucWriteString(" = "); 439 | gpucWriteNode(node->children[1]); 440 | } 441 | 442 | GPUC_WRITER_WRITE(PrefixExpression) { 443 | gpucWriteNodeLexeme(node); 444 | gpucWriteNode(node->children[0]); 445 | } 446 | 447 | GPUC_WRITER_WRITE(PostfixExpression) { 448 | gpucWriteNode(node->children[0]); 449 | gpucWriteNodeLexeme(node); 450 | } 451 | 452 | GPUC_WRITER_WRITE(NestedExpression) { 453 | gpucWriteChar('('); 454 | gpucWriteNodeChildren(node); 455 | gpucWriteChar(')'); 456 | } 457 | 458 | GPUC_WRITER_WRITE(MemberExpression) { 459 | gpucWriteNode(node->children[0]); 460 | gpucWriteChar('.'); 461 | gpucWriteNode(node->children[1]); 462 | } 463 | 464 | GPUC_WRITER_WRITE(SubscriptExpression) { 465 | gpucWriteString("/* TODO: SubscriptExpression */"); 466 | } 467 | 468 | GPUC_WRITER_WRITE(CallExpression) { 469 | gpucWriteNodeLexeme(node); 470 | gpucWriteNode(node->children[0]); 471 | } 472 | 473 | GPUC_WRITER_WRITE(CallParameterList) { 474 | gpucWriteChar('('); 475 | unsigned childCount = 0; 476 | array_for(const GpucNode*, child, node->children) { 477 | if (childCount++) gpucWriteString(", "); 478 | gpucWriteNode(child); 479 | } 480 | gpucWriteChar(')'); 481 | } 482 | 483 | GPUC_WRITER_WRITE(TernaryExpression) { 484 | gpucWriteNode(node->children[0]); 485 | gpucWriteString(" ? "); 486 | gpucWriteNode(node->children[1]); 487 | gpucWriteString(" : "); 488 | gpucWriteNode(node->children[2]); 489 | } 490 | 491 | GPUC_WRITER_WRITE(BinaryExpression) { 492 | gpucWriteNode(node->children[0]); 493 | gpucWriteChar(' '); 494 | gpucWriteNodeLexeme(node); 495 | gpucWriteChar(' '); 496 | gpucWriteNode(node->children[1]); 497 | } 498 | 499 | GPUC_WRITER_WRITE(LogicalOrExpression) { 500 | gpucWriterWriteBinaryExpressionNode(writer, node); 501 | } 502 | 503 | GPUC_WRITER_WRITE(LogicalAndExpression) { 504 | gpucWriterWriteBinaryExpressionNode(writer, node); 505 | } 506 | 507 | GPUC_WRITER_WRITE(BitwiseOrExpression) { 508 | gpucWriterWriteBinaryExpressionNode(writer, node); 509 | } 510 | 511 | GPUC_WRITER_WRITE(BitwiseXorExpression) { 512 | gpucWriterWriteBinaryExpressionNode(writer, node); 513 | } 514 | 515 | GPUC_WRITER_WRITE(BitwiseAndExpression) { 516 | gpucWriterWriteBinaryExpressionNode(writer, node); 517 | } 518 | 519 | GPUC_WRITER_WRITE(EqualityExpression) { 520 | gpucWriterWriteBinaryExpressionNode(writer, node); 521 | } 522 | 523 | GPUC_WRITER_WRITE(RelationalExpression) { 524 | gpucWriterWriteBinaryExpressionNode(writer, node); 525 | } 526 | 527 | GPUC_WRITER_WRITE(ShiftExpression) { 528 | gpucWriterWriteBinaryExpressionNode(writer, node); 529 | } 530 | 531 | GPUC_WRITER_WRITE(SumExpression) { 532 | gpucWriterWriteBinaryExpressionNode(writer, node); 533 | } 534 | 535 | GPUC_WRITER_WRITE(ProductExpression) { 536 | gpucWriterWriteBinaryExpressionNode(writer, node); 537 | } 538 | 539 | GPUC_WRITER_WRITE(LiteralExpression) { 540 | gpucWriteNodeLexeme(node); 541 | } 542 | 543 | //------------------------------------------------------------------------------ 544 | 545 | GPUC_WRITER_WRITE(MemberReference) { 546 | gpucWriteNodeLexeme(node); 547 | } 548 | 549 | GPUC_WRITER_WRITE(ParamReference) { 550 | gpucWriteNodeLexeme(node); 551 | } 552 | 553 | GPUC_WRITER_WRITE(VariableReference) { 554 | gpucWriteNodeLexeme(node); 555 | } 556 | 557 | #include "writers/glsl.h" 558 | #include "writers/hlsl.h" 559 | #include "writers/metal.h" 560 | -------------------------------------------------------------------------------- /implementation/writers/glsl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../writer.h" 3 | 4 | //------------------------------------------------------------------------------ 5 | 6 | static inline 7 | void gpucWriterWriteGlslNodeType( 8 | GpucWriter* const writer, 9 | const GpucNode* const node) 10 | { 11 | const GpucNode* const type = node->type; 12 | const GpucLexeme* const name = type->lexeme; 13 | const GpucToken token = name->token; 14 | switch (token) { 15 | case GpucToken_Bool2: gpucWriteString("bvec2"); return; 16 | case GpucToken_Bool3: gpucWriteString("bvec3"); return; 17 | case GpucToken_Bool4: gpucWriteString("bvec4"); return; 18 | 19 | case GpucToken_Float2: gpucWriteString("vec2"); return; 20 | case GpucToken_Float3: gpucWriteString("vec3"); return; 21 | case GpucToken_Float4: gpucWriteString("vec4"); return; 22 | 23 | case GpucToken_Float2x2: gpucWriteString("mat2x2"); return; 24 | case GpucToken_Float2x3: gpucWriteString("mat2x3"); return; 25 | case GpucToken_Float2x4: gpucWriteString("mat2x4"); return; 26 | case GpucToken_Float3x2: gpucWriteString("mat3x2"); return; 27 | case GpucToken_Float3x3: gpucWriteString("mat3x3"); return; 28 | case GpucToken_Float3x4: gpucWriteString("mat3x4"); return; 29 | case GpucToken_Float4x2: gpucWriteString("mat4x2"); return; 30 | case GpucToken_Float4x3: gpucWriteString("mat4x3"); return; 31 | case GpucToken_Float4x4: gpucWriteString("mat4x4"); return; 32 | 33 | case GpucToken_Int2: gpucWriteString("ivec2"); return; 34 | case GpucToken_Int3: gpucWriteString("ivec3"); return; 35 | case GpucToken_Int4: gpucWriteString("ivec4"); return; 36 | 37 | case GpucToken_UInt2: gpucWriteString("uvec2"); return; 38 | case GpucToken_UInt3: gpucWriteString("uvec3"); return; 39 | case GpucToken_UInt4: gpucWriteString("uvec4"); return; 40 | 41 | case GpucToken_TEXTURE1D: gpucWriteString("texture1D"); return; 42 | case GpucToken_TEXTURE2D: gpucWriteString("texture2D"); return; 43 | case GpucToken_TEXTURE3D: gpucWriteString("texture3D"); return; 44 | case GpucToken_TEXTURECUBE: gpucWriteString("textureCube"); return; 45 | 46 | default: gpucWriteLexeme(name); return; 47 | } 48 | } 49 | 50 | #define gpucWriteGlslNodeType(node)\ 51 | gpucWriterWriteGlslNodeType(writer, node); 52 | 53 | //------------------------------------------------------------------------------ 54 | 55 | static inline 56 | bool gpucNodeIsGlslUniform(const GpucNode* const node) { 57 | if (node->semantic == GpucSemantic_GlobalParameterDeclaration) 58 | return true; 59 | 60 | const GpucNode* const type = node->type; 61 | const GpucLexeme* const name = type->lexeme; 62 | const GpucToken token = name->token; 63 | switch (token) { 64 | case GpucToken_TEXTURE1D: 65 | case GpucToken_TEXTURE2D: 66 | case GpucToken_TEXTURE3D: 67 | case GpucToken_TEXTURECUBE: return true; 68 | 69 | default: return false; 70 | } 71 | } 72 | 73 | static inline 74 | void gpucTranslateParameterToGlslUniform( 75 | GpucWriter* const writer, 76 | const GpucNode* const node, 77 | unsigned* const uniformCount) 78 | { 79 | *uniformCount += 1; 80 | gpucWriteString("\nuniform "); 81 | gpucWriteGlslNodeType(node); 82 | gpucWriteChar(' '); 83 | gpucWriteNodeLexeme(node); 84 | gpucWriteString(";\n\n"); 85 | } 86 | 87 | static inline 88 | void gpucTranslateParameterToGlslInput( 89 | GpucWriter* const writer, 90 | const GpucNode* const node, 91 | const GpucLexeme* const prefix, 92 | unsigned* const inputCount) 93 | { 94 | const GpucLexeme* const name = node->lexeme; 95 | const GpucNode* const type = node->type; 96 | if (gpucNodeIsStructure(type)) { 97 | array_for(const GpucNode*, field, type->children) { 98 | gpucTranslateParameterToGlslInput( 99 | writer, 100 | field, 101 | name, 102 | inputCount); 103 | } 104 | return; 105 | } 106 | 107 | char layout[32]; 108 | snprintf( 109 | layout, sizeof(layout), 110 | "layout(location = %u) in ", 111 | *inputCount); 112 | *inputCount += 1; 113 | gpucWriteString(layout); 114 | 115 | gpucWriteGlslNodeType(node); 116 | gpucWriteChar(' '); 117 | gpucWriteString("_gpuc_in_"); 118 | if (prefix) { 119 | gpucWriteLexeme(prefix); 120 | gpucWriteChar('_'); 121 | } 122 | gpucWriteNodeLexeme(node); 123 | gpucWriteString(";\n"); 124 | } 125 | 126 | static inline 127 | void gpucTranslateParameterListToGlslInputs( 128 | GpucWriter* const writer, 129 | const GpucNode* const node) 130 | { 131 | const GpucNode* const parameterList = gpucNodeFindFunctionOrCallParameterList(node); 132 | array_t(const GpucNode*) const parameters = parameterList->children; 133 | unsigned inputCount = 0; 134 | unsigned uniformCount = 0; 135 | array_for(const GpucNode*, parameter, parameters) { 136 | if (gpucNodeIsGlslUniform(parameter)) 137 | gpucTranslateParameterToGlslUniform( 138 | writer, 139 | parameter, 140 | &uniformCount); 141 | else 142 | gpucTranslateParameterToGlslInput( 143 | writer, 144 | parameter, 145 | NULL, 146 | &inputCount); 147 | } 148 | } 149 | 150 | //------------------------------------------------------------------------------ 151 | 152 | static inline 153 | void gpucTranslateReturnTypeToGlslOutput( 154 | GpucWriter* const writer, 155 | const GpucNode* const node, 156 | const GpucLexeme* const prefix, 157 | unsigned* const outputCount) 158 | { 159 | char layout[32]; 160 | snprintf( 161 | layout, sizeof(layout), 162 | "layout(location = %u) out ", 163 | *outputCount); 164 | *outputCount += 1; 165 | gpucWriteString(layout); 166 | 167 | gpucWriteGlslNodeType(node); 168 | gpucWriteChar(' '); 169 | gpucWriteString("_gpuc_out_"); 170 | if (prefix) { 171 | gpucWriteLexeme(prefix); 172 | gpucWriteChar('_'); 173 | } 174 | gpucWriteNodeLexeme(node); 175 | gpucWriteString(";\n"); 176 | } 177 | 178 | static inline 179 | void gpucTranslateReturnTypeToGlslOutputs( 180 | GpucWriter* const writer, 181 | const GpucNode* const node) 182 | { 183 | const GpucLexeme* const name = node->lexeme; 184 | const GpucNode* const type = node->type; 185 | unsigned outputCount = 0; 186 | if (gpucNodeIsStructure(type)) { 187 | array_for(const GpucNode*, field, type->children) { 188 | gpucTranslateReturnTypeToGlslOutput( 189 | writer, 190 | field, 191 | name, 192 | &outputCount); 193 | } 194 | return; 195 | } 196 | gpucTranslateReturnTypeToGlslOutput( 197 | writer, 198 | node, 199 | NULL, 200 | &outputCount); 201 | } 202 | 203 | //------------------------------------------------------------------------------ 204 | 205 | static inline 206 | void gpucInvokeEntryPointFromGlsl( 207 | GpucWriter* const writer, 208 | const GpucNode* const node) 209 | { 210 | const GpucNode* const parameterList = gpucNodeFindFunctionOrCallParameterList(node); 211 | array_t(const GpucNode*) const parameters = parameterList->children; 212 | array_for(const GpucNode*, parameter, parameters) { 213 | if (gpucNodeIsGlslUniform(parameter)) 214 | continue; 215 | 216 | const GpucNode* const type = parameter->type; 217 | if (gpucNodeIsStructure(type)) { 218 | gpucWriteNodeLexeme(type); 219 | gpucWriteChar(' '); 220 | gpucWriteNodeLexeme(parameter); 221 | gpucWriteString(";\n"); 222 | array_for(const GpucNode*, field, type->children) { 223 | gpucWriteNodeLexeme(parameter); 224 | gpucWriteChar('.'); 225 | gpucWriteNodeLexeme(field); 226 | gpucWriteString(" = _gpuc_in_"); 227 | gpucWriteNodeLexeme(parameter); 228 | gpucWriteChar('_'); 229 | gpucWriteNodeLexeme(field); 230 | gpucWriteString(";\n"); 231 | } 232 | } 233 | } 234 | gpucWriteChar('\n'); 235 | 236 | const GpucNode* const type = node->type; 237 | gpucWriteNodeLexeme(type); 238 | gpucWriteString(" result = "); 239 | gpucWriteNodeLexeme(node); 240 | gpucWriteChar('('); 241 | { 242 | const GpucNode* const parameterList = gpucNodeFindFunctionOrCallParameterList(node); 243 | array_t(const GpucNode*) const parameters = parameterList->children; 244 | unsigned parameterCount = 0; 245 | array_for(const GpucNode*, parameter, parameters) { 246 | if (parameterCount++) gpucWriteString(", "); 247 | gpucWriteNodeLexeme(parameter); 248 | } 249 | } 250 | gpucWriteString(");\n\n"); 251 | 252 | if (gpucNodeIsStructure(type)) { 253 | array_for(const GpucNode*, field, type->children) { 254 | gpucWriteString("_gpuc_out_"); 255 | gpucWriteNodeLexeme(node); 256 | gpucWriteChar('_'); 257 | gpucWriteNodeLexeme(field); 258 | gpucWriteString(" = result."); 259 | gpucWriteNodeLexeme(field); 260 | gpucWriteString(";\n"); 261 | } 262 | const bool isVertexStage = writer->stage == GpucStage_Vertex; 263 | if (isVertexStage and array_length(type->children)) { 264 | gpucWriteChar('\n'); 265 | const GpucNode* const position = type->children[0]; 266 | gpucWriteString("gl_Position = result."); 267 | gpucWriteNodeLexeme(position); 268 | gpucWriteString(";\n"); 269 | } 270 | } else { 271 | gpucWriteString("// TODO"); 272 | } 273 | } 274 | 275 | //------------------------------------------------------------------------------ 276 | 277 | static inline 278 | void gpucWriterWriteGlslFunctionDefinitionNode( 279 | GpucWriter* const writer, 280 | const GpucNode* const node) 281 | { 282 | const GpucLexeme* const name = node->lexeme; 283 | const GpucToken token = name->token; 284 | 285 | if (not gpucTokenIsEntryPoint(token)) { 286 | gpucWriterWriteFunctionDefinitionNode(writer, node); 287 | return; 288 | } 289 | 290 | if (gpucTokenStage(token) != writer->stage) 291 | return; 292 | 293 | gpucWriterWriteFunctionDefinitionNode(writer, node); 294 | 295 | gpucTranslateParameterListToGlslInputs(writer, node); 296 | gpucTranslateReturnTypeToGlslOutputs(writer, node); 297 | gpucWriteChar('\n'); 298 | 299 | gpucWriteString("void main() {\n"); 300 | gpucIndentBegin(); 301 | { 302 | gpucInvokeEntryPointFromGlsl(writer, node); 303 | } 304 | gpucIndentEnd(); 305 | gpucWriteString("}\n\n"); 306 | } 307 | 308 | //------------------------------------------------------------------------------ 309 | 310 | static inline 311 | void gpucWriterWriteGlsl(GpucWriter* const writer) 312 | { 313 | gpucWriteString("// GLSL 330\n"); 314 | gpucWriteString("#version 330\n\n"); 315 | 316 | writer->writeNodeType = gpucWriterWriteGlslNodeType; 317 | 318 | writer->writeNode[GpucSemantic_FunctionDefinition] = 319 | gpucWriterWriteGlslFunctionDefinitionNode; 320 | 321 | writer->writeNode[GpucSemantic_GlobalParameterDeclaration] = 322 | gpucWriterWriteVariableDeclarationNode; 323 | 324 | gpucWriterWriteModule(writer); 325 | } 326 | 327 | -------------------------------------------------------------------------------- /implementation/writers/hlsl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../writer.h" 3 | 4 | //------------------------------------------------------------------------------ 5 | 6 | static inline 7 | void gpucWriterWriteHlslAttributeSemantic( 8 | GpucWriter* const writer, 9 | const int attributeIndex) 10 | { 11 | if (attributeIndex == 0) 12 | gpucWriteString(" : SV_Position"); 13 | else 14 | gpucWriteFormat(" : Attribute%i", attributeIndex); 15 | } 16 | 17 | static inline 18 | void gpucWriterWriteHlslTargetSemantic( 19 | GpucWriter* const writer, 20 | const int targetIndex) 21 | { 22 | gpucWriteFormat(" : SV_Target%i", targetIndex); 23 | } 24 | 25 | //------------------------------------------------------------------------------ 26 | 27 | static inline 28 | void gpucWriterWriteHlslStructFieldDefinitionNode( 29 | GpucWriter* const writer, 30 | const GpucNode* const node) 31 | { 32 | gpucWriteNodeType(node); 33 | gpucWriteChar(' '); 34 | gpucWriteNodeLexeme(node); 35 | const GpucNode* const structDefinition = node->parent; 36 | if (structDefinition == writer->stageInputStruct) { 37 | const int index = gpucNodeGetChildIndex(node); 38 | gpucWriterWriteHlslAttributeSemantic(writer, index); 39 | } 40 | else 41 | if (structDefinition == writer->stageOutputStruct) { 42 | switch (writer->stage) 43 | { 44 | case GpucStage_Vertex: { 45 | const int index = gpucNodeGetChildIndex(node); 46 | gpucWriterWriteHlslAttributeSemantic(writer, index); 47 | } break; 48 | case GpucStage_Fragment: { 49 | const int index = gpucNodeGetChildIndex(node); 50 | gpucWriterWriteHlslTargetSemantic(writer, index); 51 | } break; 52 | default: { 53 | assert(false); 54 | } break; 55 | } 56 | } 57 | gpucWriteString(";\n"); 58 | } 59 | 60 | //------------------------------------------------------------------------------ 61 | 62 | static inline 63 | void gpucWriterWriteHlslConstantBufferDeclaration( 64 | GpucWriter* const writer, 65 | const GpucNode* const uniformDeclaration, 66 | unsigned const cbufferRegisterIndex) 67 | { 68 | const GpucNode* const type = uniformDeclaration->type; 69 | gpucWriteString("cbuffer _gpuc_cbuffer_"); 70 | gpucWriteNodeLexeme(type); 71 | gpucWriteFormat(" : register(b%u) {\n", cbufferRegisterIndex); 72 | gpucIndentBegin(); 73 | if (gpucNodeIsStructure(type)) { 74 | const GpucNodeArray fields = type->children; 75 | array_for(const GpucNode*, field, fields) { 76 | gpucWriteNodeType(field); 77 | gpucWriteString(" _gpuc_cbuffer_"); 78 | gpucWriteNodeLexeme(uniformDeclaration); 79 | gpucWriteChar('_'); 80 | gpucWriteNodeLexeme(field); 81 | gpucWriteString(";\n"); 82 | } 83 | } else { 84 | gpucWriteNodeType(uniformDeclaration); 85 | gpucWriteChar(' '); 86 | gpucWriteNodeLexeme(uniformDeclaration); 87 | gpucWriteString(";\n"); 88 | } 89 | gpucIndentEnd(); 90 | gpucWriteString("};\n\n"); 91 | } 92 | 93 | static inline 94 | void gpucWriterWriteHlslConstantBufferDeclarations( 95 | GpucWriter* const writer, 96 | array_t(const GpucNode*) const parameters) 97 | { 98 | unsigned cbufferRegisterIndex = 0; 99 | array_for(const GpucNode*, parameter, parameters) { 100 | if (parameter->semantic == GpucSemantic_GlobalParameterDeclaration) { 101 | gpucWriterWriteHlslConstantBufferDeclaration( 102 | writer, 103 | parameter, 104 | cbufferRegisterIndex); 105 | cbufferRegisterIndex += 1; 106 | } 107 | } 108 | } 109 | 110 | //------------------------------------------------------------------------------ 111 | 112 | static inline 113 | void gpucWriterWriteHlslUniformAssignment( 114 | GpucWriter* const writer, 115 | const GpucNode* const uniformDeclaration) 116 | { 117 | gpucWriteNodeType(uniformDeclaration); 118 | gpucWriteChar(' '); 119 | gpucWriteNodeLexeme(uniformDeclaration); 120 | gpucWriteString(";\n"); 121 | const GpucNode* const type = uniformDeclaration->type; 122 | if (gpucNodeIsStructure(type)) { 123 | const GpucNodeArray fields = type->children; 124 | array_for(const GpucNode*, field, fields) { 125 | gpucWriteNodeLexeme(uniformDeclaration); 126 | gpucWriteChar('.'); 127 | gpucWriteNodeLexeme(field); 128 | gpucWriteString(" = _gpuc_cbuffer_"); 129 | gpucWriteNodeLexeme(uniformDeclaration); 130 | gpucWriteChar('_'); 131 | gpucWriteNodeLexeme(field); 132 | gpucWriteString(";\n"); 133 | } 134 | } 135 | } 136 | 137 | static inline 138 | void gpucWriterWriteHlslUniformAssignments( 139 | GpucWriter* const writer, 140 | array_t(const GpucNode*) const parameters) 141 | { 142 | array_for(const GpucNode*, parameter, parameters) { 143 | if (parameter->semantic == GpucSemantic_GlobalParameterDeclaration) { 144 | gpucWriterWriteHlslUniformAssignment(writer, parameter); 145 | } 146 | } 147 | } 148 | 149 | //------------------------------------------------------------------------------ 150 | 151 | static inline 152 | void gpucWriterWriteHlslEntryPoint(GpucWriter* const writer) 153 | { 154 | const GpucNode* const entryPoint = writer->stageEntryPoint; 155 | const GpucNode* const parameterList = entryPoint->children[0]; 156 | const GpucNode* const functionBody = entryPoint->children[1]; 157 | array_t(const GpucNode*) const parameters = parameterList->children; 158 | 159 | gpucWriterWriteHlslConstantBufferDeclarations(writer, parameters); 160 | 161 | gpucWriteNodeType(entryPoint); 162 | gpucWriteChar(' '); 163 | gpucWriteNodeLexeme(entryPoint); 164 | gpucWriteChar('('); 165 | array_for(const GpucNode*, parameter, parameters) { 166 | if (parameter->semantic == GpucSemantic_VariableDeclaration) { 167 | gpucWriterWriteVariableDeclarationNode(writer, parameter); 168 | break; 169 | } 170 | } 171 | gpucWriteString(") {\n"); 172 | gpucIndentBegin(); 173 | gpucWriterWriteHlslUniformAssignments(writer, parameters); 174 | gpucWriteNodeChildren(functionBody); 175 | gpucIndentEnd(); 176 | gpucWriteString("};\n\n"); 177 | } 178 | 179 | //------------------------------------------------------------------------------ 180 | 181 | static inline 182 | void gpucWriterWriteHlslFunctionDefinitionNode( 183 | GpucWriter* const writer, 184 | const GpucNode* const node) 185 | { 186 | const GpucLexeme* const name = node->lexeme; 187 | const GpucToken token = name->token; 188 | 189 | if (gpucTokenIsEntryPoint(token)) { 190 | if (node == writer->stageEntryPoint) { 191 | gpucWriterWriteHlslEntryPoint(writer); 192 | } 193 | return; 194 | } 195 | 196 | gpucWriterWriteFunctionDefinitionNode(writer, node); 197 | } 198 | 199 | //------------------------------------------------------------------------------ 200 | 201 | static inline 202 | void gpucWriterWriteHlslProductExpressionNode( 203 | GpucWriter* const writer, 204 | const GpucNode* const node) 205 | { 206 | const GpucNode* const lhs = node->children[0]; 207 | const GpucNode* const rhs = node->children[1]; 208 | const GpucNode* const lhsType = lhs->type; 209 | const GpucNode* const rhsType = rhs->type; 210 | if (gpucNodeIsMatrixType(lhsType) or gpucNodeIsMatrixType(rhsType)) 211 | { 212 | gpucWriteString("mul("); 213 | gpucWriteNode(lhs); 214 | gpucWriteString(", "); 215 | gpucWriteNode(rhs); 216 | gpucWriteString(")"); 217 | return; 218 | } 219 | gpucWriterWriteProductExpressionNode(writer, node); 220 | } 221 | 222 | //------------------------------------------------------------------------------ 223 | 224 | static inline 225 | void gpucWriterWriteHlslModule(GpucWriter* const writer) 226 | { 227 | writer->writeNode[GpucSemantic_StructFieldDefinition] = 228 | gpucWriterWriteHlslStructFieldDefinitionNode; 229 | 230 | writer->writeNode[GpucSemantic_FunctionDefinition] = 231 | gpucWriterWriteHlslFunctionDefinitionNode; 232 | 233 | writer->writeNode[GpucSemantic_GlobalParameterDeclaration] = 234 | gpucWriterWriteVariableDeclarationNode; 235 | 236 | writer->writeNode[GpucSemantic_ProductExpression] = 237 | gpucWriterWriteHlslProductExpressionNode; 238 | 239 | gpucWriterWriteModule(writer); 240 | } 241 | -------------------------------------------------------------------------------- /implementation/writers/metal.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../writer.h" 3 | 4 | //------------------------------------------------------------------------------ 5 | 6 | static inline 7 | void gpucWriterWriteMetalVertexAttributeSemantic( 8 | GpucWriter* const writer, 9 | const int attributeIndex) 10 | { 11 | gpucWriteFormat(" [[attribute(%i)]]", attributeIndex); 12 | } 13 | 14 | static inline 15 | void gpucWriterWriteMetalFragmentAttributeSemantic( 16 | GpucWriter* const writer, 17 | const int attributeIndex) 18 | { 19 | if (attributeIndex == 0) 20 | gpucWriteString(" [[position]]"); 21 | } 22 | 23 | static inline 24 | void gpucWriterWriteMetalTargetSemantic( 25 | GpucWriter* const writer, 26 | const int targetIndex) 27 | { 28 | // TODO 29 | } 30 | 31 | //------------------------------------------------------------------------------ 32 | 33 | static inline 34 | void gpucWriterWriteMetalStructFieldDefinitionNode( 35 | GpucWriter* const writer, 36 | const GpucNode* const node) 37 | { 38 | gpucWriteNodeType(node); 39 | gpucWriteChar(' '); 40 | gpucWriteNodeLexeme(node); 41 | const GpucNode* const structDefinition = node->parent; 42 | if (structDefinition == writer->stageInputStruct) { 43 | switch (writer->stage) { 44 | case GpucStage_Vertex: { 45 | const int index = gpucNodeGetChildIndex(node); 46 | gpucWriterWriteMetalVertexAttributeSemantic(writer, index); 47 | } break; 48 | case GpucStage_Fragment: { 49 | const int index = gpucNodeGetChildIndex(node); 50 | gpucWriterWriteMetalFragmentAttributeSemantic(writer, index); 51 | } break; 52 | default: { 53 | assert(false); 54 | } break; 55 | } 56 | } 57 | else 58 | if (structDefinition == writer->stageOutputStruct) { 59 | switch (writer->stage) { 60 | case GpucStage_Vertex: { 61 | const int index = gpucNodeGetChildIndex(node); 62 | gpucWriterWriteMetalFragmentAttributeSemantic(writer, index); 63 | } break; 64 | case GpucStage_Fragment: { 65 | const int index = gpucNodeGetChildIndex(node); 66 | gpucWriterWriteMetalTargetSemantic(writer, index); 67 | } break; 68 | default: { 69 | assert(false); 70 | } break; 71 | } 72 | } 73 | gpucWriteString(";\n"); 74 | } 75 | 76 | //------------------------------------------------------------------------------ 77 | 78 | static inline 79 | void gpucWriterWriteMetalEntryPointStageInParameter( 80 | GpucWriter* const writer, 81 | const GpucNode* const node) 82 | { 83 | gpucWriteString("const gpuc::"); 84 | gpucWriteNodeType(node); 85 | gpucWriteChar(' '); 86 | gpucWriteNodeLexeme(node); 87 | gpucWriteString(" [[stage_in]]"); 88 | } 89 | 90 | //------------------------------------------------------------------------------ 91 | 92 | static inline 93 | void gpucWriterWriteMetalEntryPointConstantParameter( 94 | GpucWriter* const writer, 95 | const GpucNode* const node, 96 | unsigned* const bufferIndex, 97 | unsigned* const textureIndex, 98 | unsigned* const samplerIndex) 99 | { 100 | switch (node->type->lexeme->token) { 101 | case GpucToken_Identifier: { 102 | gpucWriteString("constant const gpuc::"); 103 | gpucWriteNodeType(node); 104 | gpucWriteString("& "); 105 | gpucWriteNodeLexeme(node); 106 | gpucWriteFormat(" [[buffer(%i)]]", (*bufferIndex)++); 107 | } break; 108 | case GpucToken_TEXTURE1D: { 109 | gpucWriteString("texture1d "); 110 | gpucWriteNodeLexeme(node); 111 | gpucWriteFormat(" [[texture(%i)]]", (*textureIndex)++); 112 | } break; 113 | case GpucToken_TEXTURE2D: { 114 | gpucWriteString("texture2d "); 115 | gpucWriteNodeLexeme(node); 116 | gpucWriteFormat(" [[texture(%i)]]", (*textureIndex)++); 117 | } break; 118 | case GpucToken_TEXTURE3D: { 119 | gpucWriteString("texture3d "); 120 | gpucWriteNodeLexeme(node); 121 | gpucWriteFormat(" [[texture(%i)]]", (*textureIndex)++); 122 | } break; 123 | case GpucToken_TEXTURECUBE: { 124 | gpucWriteString("textureCube "); 125 | gpucWriteNodeLexeme(node); 126 | gpucWriteFormat(" [[texture(%i)]]", (*textureIndex)++); 127 | } break; 128 | case GpucToken_DEPTH2D: { 129 | gpucWriteString("depth2d "); 130 | gpucWriteNodeLexeme(node); 131 | gpucWriteFormat(" [[texture(%i)]]", (*textureIndex)++); 132 | } break; 133 | case GpucToken_DEPTHCUBE: { 134 | gpucWriteString("depthCube "); 135 | gpucWriteNodeLexeme(node); 136 | gpucWriteFormat(" [[texture(%i)]]", (*textureIndex)++); 137 | } break; 138 | case GpucToken_SAMPLER: { 139 | gpucWriteString("sampler "); 140 | gpucWriteNodeLexeme(node); 141 | gpucWriteFormat(" [[sampler(%i)]]", (*samplerIndex)++); 142 | } break; 143 | default: { 144 | gpucAssert(false, "unsupported resource type"); 145 | } break; 146 | } 147 | } 148 | 149 | //------------------------------------------------------------------------------ 150 | 151 | static inline 152 | void gpucWriterWriteMetalEntryPoint(GpucWriter* const writer) 153 | { 154 | const GpucNode* const entryPoint = writer->stageEntryPoint; 155 | 156 | gpucWriteNodeType(entryPoint); 157 | gpucWriteChar(' '); 158 | gpucWriteNodeLexeme(entryPoint); 159 | gpucWriteChar('('); 160 | { 161 | unsigned uniformIndex = 0; 162 | unsigned parameterCount = 0; 163 | const GpucNode* const parameterList = entryPoint->children[0]; 164 | array_t(const GpucNode*) const parameters = parameterList->children; 165 | array_for(const GpucNode*, parameter, parameters) { 166 | if (parameterCount++) gpucWriteString(", "); 167 | switch (parameter->semantic) { 168 | case GpucSemantic_VariableDeclaration: 169 | gpucWriteString("const "); 170 | gpucWriteNodeType(parameter); 171 | gpucWriteChar(' '); 172 | gpucWriteNodeLexeme(parameter); 173 | break; 174 | default: 175 | assert(false); 176 | break; 177 | } 178 | } 179 | } 180 | gpucWriteString(") const"); 181 | const GpucNode* const functionBody = entryPoint->children[1]; 182 | gpucWriteNode(functionBody); 183 | gpucWriteChar('\n'); 184 | } 185 | 186 | //------------------------------------------------------------------------------ 187 | 188 | static inline 189 | void gpucWriterWriteMetalFunctionDefinitionNode( 190 | GpucWriter* const writer, 191 | const GpucNode* const node) 192 | { 193 | const GpucLexeme* const name = node->lexeme; 194 | const GpucToken token = name->token; 195 | 196 | if (gpucTokenIsEntryPoint(token)) { 197 | if (node == writer->stageEntryPoint) { 198 | gpucWriterWriteMetalEntryPoint(writer); 199 | } 200 | return; 201 | } 202 | 203 | gpucWriterWriteFunctionDefinitionNode(writer, node); 204 | } 205 | 206 | //------------------------------------------------------------------------------ 207 | 208 | static inline 209 | void gpucWriterWriteMetalGlobalParameterDeclarationNode( 210 | GpucWriter* const writer, 211 | const GpucNode* const node) 212 | { 213 | switch (node->type->lexeme->token) { 214 | case GpucToken_Identifier: { 215 | gpucWriteString("constant const "); 216 | gpucWriteNodeType(node); 217 | gpucWriteString("& "); 218 | } break; 219 | case GpucToken_TEXTURE1D: { 220 | gpucWriteString("const texture1d "); 221 | } break; 222 | case GpucToken_TEXTURE2D: { 223 | gpucWriteString("const texture2d "); 224 | } break; 225 | case GpucToken_TEXTURE3D: { 226 | gpucWriteString("const texture3d "); 227 | } break; 228 | case GpucToken_TEXTURECUBE: { 229 | gpucWriteString("const textureCube "); 230 | } break; 231 | case GpucToken_DEPTH2D: { 232 | gpucWriteString("const depth2d "); 233 | } break; 234 | case GpucToken_DEPTHCUBE: { 235 | gpucWriteString("const depthCube "); 236 | } break; 237 | case GpucToken_SAMPLER: { 238 | gpucWriteString("const sampler "); 239 | } break; 240 | default: { 241 | gpucAssert(false, "unsupported resource type"); 242 | } break; 243 | } 244 | gpucWriteNodeLexeme(node); 245 | gpucWriteString(";\n\n"); 246 | } 247 | 248 | //------------------------------------------------------------------------------ 249 | 250 | static inline 251 | void gpucWriterWriteMetalModule(GpucWriter* const writer) { 252 | writer->writeNode[GpucSemantic_StructFieldDefinition] = 253 | gpucWriterWriteMetalStructFieldDefinitionNode; 254 | 255 | writer->writeNode[GpucSemantic_FunctionDefinition] = 256 | gpucWriterWriteMetalFunctionDefinitionNode; 257 | 258 | writer->writeNode[GpucSemantic_GlobalParameterDeclaration] = 259 | gpucWriterWriteMetalGlobalParameterDeclarationNode; 260 | 261 | gpucWriteString( 262 | "#include \n" 263 | "#include \n" 264 | "\n" 265 | "using namespace metal;\n" 266 | "\n" 267 | "float4 sample(const texture2d t, float2 uv) {\n" 268 | " constexpr sampler s(filter::nearest);\n" 269 | " return t.sample(s, uv);\n" 270 | "}\n" 271 | "\n" 272 | "float4 sample(const texture2d t, float2 uv, const sampler s) {\n" 273 | " return t.sample(s, uv);\n" 274 | "}\n" 275 | "\n" 276 | ); 277 | 278 | gpucWriteString("struct gpuc {\n\n"); 279 | gpucIndentBegin(); 280 | gpucWriterWriteModule(writer); 281 | gpucIndentEnd(); 282 | gpucWriteString("};\n\n"); 283 | 284 | const GpucNode* const entryPoint = writer->stageEntryPoint; 285 | const GpucNode* const parameterList = entryPoint->children[0]; 286 | array_t(const GpucNode*) const parameters = parameterList->children; 287 | array_t(const GpucNode*) const globals = writer->module->root->children; 288 | 289 | switch (writer->stage) { 290 | case GpucStage_Vertex: 291 | gpucWriteString("vertex "); 292 | break; 293 | case GpucStage_Fragment: 294 | gpucWriteString("fragment "); 295 | break; 296 | default: 297 | assert(false); 298 | break; 299 | } 300 | gpucWriteString("gpuc::"); 301 | gpucWriteNodeType(entryPoint); 302 | gpucWriteChar(' '); 303 | gpucWriteNodeLexeme(entryPoint); 304 | gpucWriteString("(\n"); 305 | { 306 | gpucIndentBegin(); 307 | unsigned bufferIndex = 0; 308 | unsigned textureIndex = 0; 309 | unsigned samplerIndex = 0; 310 | unsigned parameterCount = 0; 311 | array_for(const GpucNode*, global, globals) { 312 | switch (global->semantic) { 313 | case GpucSemantic_GlobalParameterDeclaration: 314 | if (parameterCount++) gpucWriteString(",\n"); 315 | gpucWriterWriteMetalEntryPointConstantParameter( 316 | writer, 317 | global, 318 | &bufferIndex, 319 | &textureIndex, 320 | &samplerIndex); 321 | break; 322 | default: 323 | break; 324 | } 325 | } 326 | array_for(const GpucNode*, parameter, parameters) { 327 | if (parameterCount++) gpucWriteString(",\n"); 328 | switch (parameter->semantic) { 329 | case GpucSemantic_VariableDeclaration: 330 | gpucWriteString("const gpuc::"); 331 | gpucWriteNodeType(parameter); 332 | gpucWriteChar(' '); 333 | gpucWriteNodeLexeme(parameter); 334 | gpucWriteString(" [[stage_in]]"); 335 | break; 336 | default: 337 | assert(false); 338 | break; 339 | } 340 | } 341 | gpucIndentEnd(); 342 | } 343 | gpucWriteString("\n) {\n"); 344 | gpucIndentBegin(); 345 | gpucWriteString("return gpuc{"); 346 | { 347 | unsigned parameterCount = 0; 348 | array_for(const GpucNode*, global, globals) { 349 | switch (global->semantic) { 350 | case GpucSemantic_GlobalParameterDeclaration: 351 | if (parameterCount++) gpucWriteString(", "); 352 | gpucWriteNodeLexeme(global); 353 | break; 354 | default: 355 | break; 356 | } 357 | } 358 | } 359 | gpucWriteString("}."); 360 | gpucWriteNodeLexeme(entryPoint); 361 | gpucWriteString("("); 362 | { 363 | unsigned parameterCount = 0; 364 | array_for(const GpucNode*, parameter, parameters) { 365 | switch (parameter->semantic) { 366 | case GpucSemantic_VariableDeclaration: 367 | if (parameterCount++) gpucWriteString(", "); 368 | gpucWriteNodeLexeme(parameter); 369 | break; 370 | default: 371 | assert(false); 372 | break; 373 | } 374 | } 375 | } 376 | gpucWriteString(");\n"); 377 | gpucIndentEnd(); 378 | gpucWriteString("}\n\n"); 379 | 380 | } 381 | -------------------------------------------------------------------------------- /sample.gpuc: -------------------------------------------------------------------------------- 1 | struct Camera { 2 | float4x4 mvp; 3 | float4x4 mvn; 4 | }; 5 | 6 | struct Vertex { 7 | float3 position; 8 | float4 color; 9 | float4 texcoords; 10 | }; 11 | 12 | struct Fragment { 13 | float4 position; 14 | float4 color; 15 | float4 texcoords; 16 | }; 17 | 18 | struct Sample { 19 | float4 color; 20 | }; 21 | 22 | param Camera cam; 23 | 24 | param texture2d color; 25 | 26 | param sampler samp; 27 | 28 | Fragment vert(Vertex v) { 29 | Fragment f; 30 | f.position = cam.mvp * float4(v.position,1); 31 | f.color = v.color; 32 | f.texcoords = v.texcoords; 33 | return f; 34 | } 35 | 36 | Sample frag(Fragment f) { 37 | Sample s; 38 | s.color = sample(color, f.texcoords.st, samp); 39 | //s.color.rgba = f.color.rgba; 40 | return s; 41 | } 42 | --------------------------------------------------------------------------------