/
Zamar_Terrier
/
TigorEngine
Обзор
Документация
Войти
/
Zamar_Terrier
/
TigorEngine
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/Tools/glsl_parser.c
2 876 строк
122 KB
Zamar_Terrier
Офигенное обновление движка
31 июл 2026, 21:03
Верифицирован
31 июл 2026, 21:03
1682686
Код
Авторство
О чём код?
/* glsl_parser.c GLSL Shader Parser — полная реализация Поддерживает: массивы, swizzle, layout, множественные объявления, условную компиляцию (#ifdef/#elif/#else/#endif), интерфейсные блоки (UBO/SSBO), break/continue/discard, switch/case/default, do-while, precision qualifiers, макросы с аргументами (#define LERP(a,b,t) ...) */ #include "Tools/glsl_parser.h" #include "Core/e_memory.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> static inline void* safeRealloc(void* old_ptr, size_t old_count, size_t new_count, size_t elem_size) { if (new_count == 0) { if (old_ptr) FreeMemory(old_ptr); return NULL; } void* new_ptr = AllocateMemoryN(new_count, (int32_t)elem_size, "safeRealloc"); if (!new_ptr) return NULL; if (old_ptr && old_count > 0) { size_t copy_size = old_count * elem_size; if (copy_size > new_count * elem_size) copy_size = new_count * elem_size; memcpy(new_ptr, old_ptr, copy_size); FreeMemory(old_ptr); } return new_ptr; } static char* strDup(const char* s) { if (!s) return NULL; size_t len = strlen(s) + 1; char* r = (char*)AllocateMemoryN(1, (int32_t)len, "strDup"); if (!r) return NULL; memcpy(r, s, len); return r; } #define NL_ITEMS(list) ((GlslNode**)VectorGetData((list)->vec)) #define NL_COUNT(list) ((int)VectorGetSize((list)->vec)) static void nodeListInit(NodeList* list) { VectorInit(&list->vec, sizeof(GlslNode*), "NodeList"); } static void nodeListPush(NodeList* list, GlslNode* node) { GlslNode** ptr = (GlslNode**)VectorAdd(list->vec); *ptr = node; } static void nodeListFree(NodeList* list); static GlslNode* nodeCreate(NodeType type, int line, int col); void nodeFree(GlslNode* n); static GlslNode* nodeCreate(NodeType type, int line, int col) { GlslNode* n = (GlslNode*)AllocateMemoryN(1, sizeof(GlslNode), "NodeCreate"); if (!n) { fprintf(stderr, "Out of memory\n"); exit(1); } n->type = type; n->line = line; n->col = col; return n; } void nodeFree(GlslNode* n) { if (!n) return; switch (n->type) { case NODE_PROGRAM: nodeListFree(&n->data.program.declarations); break; case NODE_FUNCTION_DECL: FreeMemory(n->data.funcDecl.returnType); FreeMemory(n->data.funcDecl.name); nodeListFree(&n->data.funcDecl.parameters); nodeFree(n->data.funcDecl.body); break; case NODE_STRUCT_DECL: FreeMemory(n->data.structDecl.name); nodeListFree(&n->data.structDecl.fields); break; case NODE_FIELD_DECL: FreeMemory(n->data.fieldDecl.typeName); FreeMemory(n->data.fieldDecl.name); nodeFree(n->data.fieldDecl.arraySize); break; case NODE_VARIABLE_DECL: FreeMemory(n->data.varDecl.qualifier); FreeMemory(n->data.varDecl.typeName); FreeMemory(n->data.varDecl.name); nodeFree(n->data.varDecl.initializer); nodeFree(n->data.varDecl.arraySize); nodeFree(n->data.varDecl.layout); break; case NODE_PARAMETER: FreeMemory(n->data.parameter.qualifier); FreeMemory(n->data.parameter.typeName); FreeMemory(n->data.parameter.name); nodeFree(n->data.parameter.arraySize); break; case NODE_BLOCK: nodeListFree(&n->data.block.statements); break; case NODE_ASSIGNMENT: FreeMemory(n->data.assignment.op); nodeFree(n->data.assignment.target); nodeFree(n->data.assignment.value); break; case NODE_BINARY_OP: FreeMemory(n->data.binaryOp.op); nodeFree(n->data.binaryOp.left); nodeFree(n->data.binaryOp.right); break; case NODE_UNARY_OP: FreeMemory(n->data.unaryOp.op); nodeFree(n->data.unaryOp.operand); break; case NODE_CALL: FreeMemory(n->data.call.name); nodeListFree(&n->data.call.arguments); break; case NODE_INDEX: nodeFree(n->data.index.object); nodeFree(n->data.index.index); break; case NODE_MEMBER: nodeFree(n->data.member.object); FreeMemory(n->data.member.member); break; case NODE_SWIZZLE: nodeFree(n->data.swizzle.object); FreeMemory(n->data.swizzle.mask); break; case NODE_IF: nodeFree(n->data.ifStmt.condition); nodeFree(n->data.ifStmt.thenBranch); nodeFree(n->data.ifStmt.elseBranch); break; case NODE_FOR: nodeFree(n->data.forStmt.init); nodeFree(n->data.forStmt.condition); nodeFree(n->data.forStmt.increment); nodeFree(n->data.forStmt.body); break; case NODE_WHILE: nodeFree(n->data.whileStmt.condition); nodeFree(n->data.whileStmt.body); break; case NODE_DO_WHILE: nodeFree(n->data.doWhileStmt.body); nodeFree(n->data.doWhileStmt.condition); break; case NODE_SWITCH: nodeFree(n->data.switchStmt.condition); nodeListFree(&n->data.switchStmt.cases); break; case NODE_CASE: nodeListFree(&n->data.caseStmt.labels); nodeListFree(&n->data.caseStmt.statements); break; case NODE_PRECISION_STMT: FreeMemory(n->data.precisionStmt.qualifier); FreeMemory(n->data.precisionStmt.typeName); break; case NODE_RETURN: nodeFree(n->data.returnStmt.value); break; case NODE_EXPR_STMT: nodeFree(n->data.exprStmt.expression); break; case NODE_IDENTIFIER: FreeMemory(n->data.identifier.name); break; case NODE_TYPE_REF: FreeMemory(n->data.typeRef.name); break; case NODE_LAYOUT: for (int i = 0; i < n->data.layout.count; i++) { FreeMemory(n->data.layout.keys[i]); FreeMemory(n->data.layout.values[i]); } FreeMemory(n->data.layout.keys); FreeMemory(n->data.layout.values); FreeMemory(n->data.layout.qualifier); break; case NODE_VARIABLE_DECL_LIST: FreeMemory(n->data.varDeclList.qualifier); FreeMemory(n->data.varDeclList.typeName); for (int i = 0; i < n->data.varDeclList.count; i++) { FreeMemory(n->data.varDeclList.names[i]); nodeFree(n->data.varDeclList.initializers[i]); nodeFree(n->data.varDeclList.arraySizes[i]); } FreeMemory(n->data.varDeclList.names); FreeMemory(n->data.varDeclList.initializers); FreeMemory(n->data.varDeclList.arraySizes); nodeFree(n->data.varDeclList.layout); break; case NODE_VERSION_DECL: FreeMemory(n->data.versionDecl.profile); break; case NODE_MACRO_DEFINE: FreeMemory(n->data.macroDefine.name); FreeMemory(n->data.macroDefine.value); break; case NODE_PREPROCESSOR_LINE: FreeMemory(n->data.preprocessorLine.directive); FreeMemory(n->data.preprocessorLine.argument); break; case NODE_INTERFACE_BLOCK: FreeMemory(n->data.interfaceBlock.qualifier); FreeMemory(n->data.interfaceBlock.blockTypeName); FreeMemory(n->data.interfaceBlock.instanceName); nodeListFree(&n->data.interfaceBlock.fields); nodeFree(n->data.interfaceBlock.arraySize); nodeFree(n->data.interfaceBlock.layout); break; case NODE_LAYOUT_QUALIFIER_DECL: FreeMemory(n->data.layoutQualifierDecl.qualifier); nodeFree(n->data.layoutQualifierDecl.layout); break; default: break; } FreeMemory(n); } static void nodeListFree(NodeList* list) { if (!list || !list->vec) return; GlslNode** items = NL_ITEMS(list); int count = NL_COUNT(list); for (int i = 0; i < count; i++) { nodeFree(items[i]); } VectorDestroy(list->vec); list->vec = NULL; } /* ================================================================ ПРЕПРОЦЕССОР: условная компиляция и #define ================================================================ */ #define MAX_DEFINES 256 #define MAX_IFDEF_DEPTH 32 typedef struct { char* name; char* value; char** params; int paramCount; } Define; typedef struct { bool active; bool seen_true; bool parent_active; } IfdefLevel; typedef struct { Define defines[MAX_DEFINES]; int defineCount; IfdefLevel ifdefStack[MAX_IFDEF_DEPTH]; int ifdefDepth; int skippedLines; } Preprocessor; static void preprocessorInit(Preprocessor* pp) { pp->defineCount = 0; pp->ifdefDepth = 0; pp->skippedLines = 0; } static void preprocessorFree(Preprocessor* pp) { for (int i = 0; i < pp->defineCount; i++) { FreeMemory(pp->defines[i].name); FreeMemory(pp->defines[i].value); for (int j = 0; j < pp->defines[i].paramCount; j++) FreeMemory(pp->defines[i].params[j]); FreeMemory(pp->defines[i].params); } pp->defineCount = 0; } static Define* findDefine(Preprocessor* pp, const char* name) { for (int i = 0; i < pp->defineCount; i++) if (strcmp(pp->defines[i].name, name) == 0) return &pp->defines[i]; return NULL; } static void addDefine(Preprocessor* pp, const char* name, const char* value, char** params, int paramCount) { Define* existing = findDefine(pp, name); if (existing) { FreeMemory(existing->value); for (int i = 0; i < existing->paramCount; i++) FreeMemory(existing->params[i]); FreeMemory(existing->params); existing->value = value ? strDup(value) : NULL; existing->params = params; existing->paramCount = paramCount; return; } if (pp->defineCount >= MAX_DEFINES) { if (params) { for (int i = 0; i < paramCount; i++) FreeMemory(params[i]); FreeMemory(params); } return; } pp->defines[pp->defineCount].name = strDup(name); pp->defines[pp->defineCount].value = value ? strDup(value) : NULL; pp->defines[pp->defineCount].params = params; pp->defines[pp->defineCount].paramCount = paramCount; pp->defineCount++; } static void removeDefine(Preprocessor* pp, const char* name) { for (int i = 0; i < pp->defineCount; i++) { if (strcmp(pp->defines[i].name, name) == 0) { FreeMemory(pp->defines[i].name); FreeMemory(pp->defines[i].value); for (int j = 0; j < pp->defines[i].paramCount; j++) FreeMemory(pp->defines[i].params[j]); FreeMemory(pp->defines[i].params); for (int j = i; j < pp->defineCount - 1; j++) pp->defines[j] = pp->defines[j + 1]; pp->defineCount--; return; } } } static bool isPreprocessorActive(Preprocessor* pp) { return pp->ifdefDepth == 0 || pp->ifdefStack[pp->ifdefDepth - 1].active; } static void pushIfdef(Preprocessor* pp, bool condition) { if (pp->ifdefDepth >= MAX_IFDEF_DEPTH) return; bool parent = isPreprocessorActive(pp); IfdefLevel* lvl = &pp->ifdefStack[pp->ifdefDepth++]; lvl->parent_active = parent; lvl->active = parent && condition; lvl->seen_true = condition; } static void handleElif(Preprocessor* pp, bool condition) { if (pp->ifdefDepth == 0) return; IfdefLevel* lvl = &pp->ifdefStack[pp->ifdefDepth - 1]; if (!lvl->parent_active) lvl->active = false; else if (lvl->seen_true) lvl->active = false; else { lvl->active = condition; if (condition) lvl->seen_true = true; } } static void handleElse(Preprocessor* pp) { if (pp->ifdefDepth == 0) return; IfdefLevel* lvl = &pp->ifdefStack[pp->ifdefDepth - 1]; if (!lvl->parent_active) lvl->active = false; else { lvl->active = !lvl->seen_true; lvl->seen_true = true; } } static void handleEndif(Preprocessor* pp) { if (pp->ifdefDepth > 0) pp->ifdefDepth--; } typedef struct { CustomVector* vec; int readPos; } TokenQueue; static void tokenQueueInit(TokenQueue* q) { VectorInit(&q->vec, sizeof(GLSLToken), "TokenQueue"); q->readPos = 0; } static void tokenQueuePush(TokenQueue* q, GLSLToken t) { GLSLToken* ptr = (GLSLToken*)VectorAdd(q->vec); *ptr = t; } static bool tokenQueuePop(TokenQueue* q, GLSLToken* out) { if (!q || !q->vec || q->readPos >= (int)VectorGetSize(q->vec)) return false; GLSLToken* items = (GLSLToken*)VectorGetData(q->vec); *out = items[q->readPos]; items[q->readPos].value = NULL; q->readPos++; return true; } static void tokenQueueFree(TokenQueue* q) { if (!q || !q->vec) return; GLSLToken* items = (GLSLToken*)VectorGetData(q->vec); uint32_t count = VectorGetSize(q->vec); for (uint32_t i = q->readPos; i < count; i++) { if (items[i].value) { FreeMemory(items[i].value); items[i].value = NULL; } } VectorDestroy(q->vec); q->vec = NULL; q->readPos = 0; } /* ================================================================ ЛЕКСЕР ================================================================ */ typedef struct { const char* src; int pos; int len; int line; int col; Preprocessor pp; TokenQueue queue; int macroExpansions; } Lexer; static void lexerInit(Lexer* l, const char* src); static void lexerFree(Lexer* l); static void tokenFree(GLSLToken* t) { if (t->value) { FreeMemory(t->value); t->value = NULL; } } static void lexerAdvance(Lexer* l) { if (l->pos < l->len) { if (l->src[l->pos] == '\n') { l->line++; l->col = 1; } else l->col++; l->pos++; } } static char lexerPeekAhead(Lexer* l, int offset) { int p = l->pos + offset; return (p < l->len) ? l->src[p] : '\0'; } static void skipWhitespaceAndComments(Lexer* l) { while (l->pos < l->len) { char c = l->src[l->pos]; if (isspace((unsigned char)c)) { lexerAdvance(l); } else if (c == '/' && lexerPeekAhead(l, 1) == '/') { while (l->pos < l->len && l->src[l->pos] != '\n') lexerAdvance(l); } else if (c == '/' && lexerPeekAhead(l, 1) == '*') { lexerAdvance(l); lexerAdvance(l); while (l->pos < l->len) { if (l->src[l->pos] == '*' && lexerPeekAhead(l, 1) == '/') { lexerAdvance(l); lexerAdvance(l); break; } lexerAdvance(l); } } else break; } } typedef struct { const char* word; GLSLTokenType type; } Keyword; static const Keyword keywords[] = { {"void", TOK_VOID}, {"float", TOK_FLOAT}, {"vec2", TOK_VEC2}, {"vec3", TOK_VEC3}, {"vec4", TOK_VEC4}, {"mat2", TOK_MAT2}, {"mat3", TOK_MAT3}, {"mat4", TOK_MAT4}, {"int", TOK_INT}, {"bool", TOK_BOOL}, {"sampler2D", TOK_SAMPLER2D}, {"samplerCube", TOK_SAMPLERCUBE}, {"sampler2DShadow", TOK_SAMPLER2DSHADOW}, {"samplerCubeShadow", TOK_SAMPLERCUBESHADOW}, {"sampler2DArray", TOK_SAMPLER2DARRAY}, {"sampler3D", TOK_SAMPLER3D}, {"struct", TOK_STRUCT}, {"return", TOK_RETURN}, {"if", TOK_IF}, {"else", TOK_ELSE}, {"for", TOK_FOR}, {"while", TOK_WHILE}, {"do", TOK_DO}, {"switch", TOK_SWITCH}, {"case", TOK_CASE}, {"default", TOK_DEFAULT}, {"break", TOK_BREAK}, {"continue", TOK_CONTINUE}, {"discard", TOK_DISCARD}, {"in", TOK_IN}, {"out", TOK_OUT}, {"inout", TOK_INOUT}, {"uniform", TOK_UNIFORM}, {"varying", TOK_VARYING}, {"attribute", TOK_ATTRIBUTE}, {"const", TOK_CONST}, {"flat", TOK_FLAT}, {"smooth", TOK_SMOOTH}, {"layout", TOK_LAYOUT}, {"buffer", TOK_BUFFER}, {"highp", TOK_HIGHP}, {"mediump", TOK_MEDIUMP}, {"lowp", TOK_LOWP}, {"true", TOK_TRUE}, {"false", TOK_FALSE}, {"precision", TOK_PRECISION}, {"writeonly", TOK_WRITEONLY }, {"readonly", TOK_READONLY }, {"coherent", TOK_COHERENT }, {"restrict", TOK_RESTRICT }, {"image2D", TOK_IMAGE2D }, {"image3D", TOK_IMAGE3D }, {"imageCube", TOK_IMAGECUBE }, {"image2DArray", TOK_IMAGE2DARRAY }, {"uint", TOK_UINT }, {"uvec2", TOK_UVEC2 }, {"uvec3", TOK_UVEC3 }, {"uvec4", TOK_UVEC4 }, {"ivec2", TOK_IVEC2 }, {"ivec3", TOK_IVEC3 }, {"ivec4", TOK_IVEC4 }, {"bvec2", TOK_BVEC2 }, {"bvec3", TOK_BVEC3 }, {"bvec4", TOK_BVEC4 }, {NULL, TOK_UNKNOWN} }; static GLSLTokenType checkKeyword(const char* word) { for (int i = 0; keywords[i].word; i++) if (strcmp(word, keywords[i].word) == 0) return keywords[i].type; return TOK_IDENTIFIER; } static bool isTypeToken(GLSLTokenType t) { return t == TOK_VOID || t == TOK_FLOAT || t == TOK_VEC2 || t == TOK_VEC3 || t == TOK_VEC4 || t == TOK_MAT2 || t == TOK_MAT3 || t == TOK_MAT4 || t == TOK_INT || t == TOK_BOOL || t == TOK_SAMPLER2D || t == TOK_SAMPLERCUBE || t == TOK_SAMPLER2DSHADOW || t == TOK_SAMPLERCUBESHADOW || t == TOK_SAMPLER2DARRAY || t == TOK_SAMPLER3D || t == TOK_IDENTIFIER || t == TOK_IMAGE2D || t == TOK_IMAGE3D || t == TOK_IMAGECUBE || t == TOK_IMAGE2DARRAY || t == TOK_UINT || t == TOK_UVEC2 || t == TOK_UVEC3 || t == TOK_UVEC4 || t == TOK_IVEC2 || t == TOK_IVEC3 || t == TOK_IVEC4 || t == TOK_BVEC2 || t == TOK_BVEC3 || t == TOK_BVEC4; } static bool isQualifierToken(GLSLTokenType t) { return t == TOK_IN || t == TOK_OUT || t == TOK_INOUT || t == TOK_UNIFORM || t == TOK_VARYING || t == TOK_ATTRIBUTE || t == TOK_CONST || t == TOK_FLAT || t == TOK_SMOOTH || t == TOK_LAYOUT || t == TOK_BUFFER || t == TOK_HIGHP || t == TOK_MEDIUMP || t == TOK_LOWP || t == TOK_WRITEONLY || t == TOK_READONLY || t == TOK_COHERENT || t == TOK_RESTRICT; } static char* expandFunctionMacro(Define* macro, char** args, int argCount) { if (!macro->value) return strDup(""); CustomVector* resultVec = NULL; VectorInit(&resultVec, sizeof(char), "macroExpand"); const char* p = macro->value; while (*p) { if (isalpha((unsigned char)*p) || *p == '_') { const char* start = p; while (*p && (isalnum((unsigned char)*p) || *p == '_')) p++; size_t len = p - start; char ident[256] = {0}; if (len < 255) { memcpy(ident, start, len); ident[len] = '\0'; } bool found = false; for (int i = 0; i < macro->paramCount; i++) { if (strcmp(ident, macro->params[i]) == 0) { const char* sub = (i < argCount && args[i]) ? args[i] : ""; size_t slen = strlen(sub); char* ptr = (char*)VectorAddArray(resultVec, slen); memcpy(ptr, sub, slen); found = true; break; } } if (!found) { char* ptr = (char*)VectorAddArray(resultVec, len); memcpy(ptr, start, len); } } else { char* ptr = (char*)VectorAdd(resultVec); *ptr = *p++; } } char* ptr = (char*)VectorAdd(resultVec); *ptr = '\0'; char* finalStr = strDup((char*)VectorGetData(resultVec)); VectorDestroy(resultVec); return finalStr; } static GLSLToken lexerNext(Lexer* l); static void lexStringIntoQueue(Lexer* parent, const char* src, TokenQueue* q) { if (!src || !parent || !q) return; Lexer sub; lexerInit(&sub, src); sub.pp = parent->pp; sub.pp.defineCount = 0; sub.pp.ifdefDepth = 0; sub.pp.skippedLines = 0; while (true) { GLSLToken t = lexerNext(&sub); if (t.type == TOK_EOF) { tokenFree(&t); break; } tokenQueuePush(q, t); } tokenQueueFree(&sub.queue); sub.pp.defineCount = 0; } static GLSLToken lexerNext(Lexer* l) { GLSLToken t = {0}; restart: { GLSLToken queued; if (tokenQueuePop(&l->queue, &queued)) return queued; } skipWhitespaceAndComments(l); t.line = l->line; t.col = l->col; if (l->pos >= l->len) { t.type = TOK_EOF; t.value = NULL; return t; } if (l->src[l->pos] == '#') { bool lineStart = true; for (int i = l->pos - 1; i >= 0 && l->src[i] != '\n'; i--) { if (!isspace((unsigned char)l->src[i])) { lineStart = false; break; } } if (lineStart) { lexerAdvance(l); while (l->pos < l->len && (l->src[l->pos] == ' ' || l->src[l->pos] == '\t')) lexerAdvance(l); int start = l->pos; while (l->pos < l->len && (isalnum((unsigned char)l->src[l->pos]) || l->src[l->pos] == '_')) lexerAdvance(l); int len = l->pos - start; char directive[64] = {0}; if (len > 0 && len < 63) { memcpy(directive, l->src + start, len); directive[len] = '\0'; } while (l->pos < l->len && (l->src[l->pos] == ' ' || l->src[l->pos] == '\t')) lexerAdvance(l); if (strcmp(directive, "ifdef") == 0 || strcmp(directive, "ifndef") == 0) { bool isIfndef = (strcmp(directive, "ifndef") == 0); int ns = l->pos; while (l->pos < l->len && (isalnum((unsigned char)l->src[l->pos]) || l->src[l->pos] == '_')) lexerAdvance(l); char name[256] = {0}; int nl = l->pos - ns; if (nl > 0 && nl < 255) { memcpy(name, l->src + ns, nl); name[nl] = '\0'; } pushIfdef(&l->pp, isIfndef ? (findDefine(&l->pp, name) == NULL) : (findDefine(&l->pp, name) != NULL)); while (l->pos < l->len && l->src[l->pos] != '\n') lexerAdvance(l); if (l->pos < l->len) lexerAdvance(l); goto restart; } if (strcmp(directive, "if") == 0 || strcmp(directive, "elif") == 0) { bool isElif = (strcmp(directive, "elif") == 0); int es = l->pos; while (l->pos < l->len && l->src[l->pos] != '\n') lexerAdvance(l); char expr[512] = {0}; int el = l->pos - es; if (el > 0 && el < 511) { memcpy(expr, l->src + es, el); expr[el] = '\0'; } bool cond = false; char* def = strstr(expr, "defined"); if (def) { char* p2 = def + 7; while (*p2 == ' ' || *p2 == '(') p2++; char name[256] = {0}; int i = 0; while (*p2 && (isalnum((unsigned char)*p2) || *p2 == '_') && i < 255) name[i++] = *p2++; cond = (findDefine(&l->pp, name) != NULL); } else cond = (strtol(expr, NULL, 0) != 0); if (isElif) handleElif(&l->pp, cond); else pushIfdef(&l->pp, cond); if (l->pos < l->len) lexerAdvance(l); goto restart; } if (strcmp(directive, "else") == 0) { handleElse(&l->pp); while (l->pos < l->len && l->src[l->pos] != '\n') lexerAdvance(l); if (l->pos < l->len) lexerAdvance(l); goto restart; } if (strcmp(directive, "endif") == 0) { handleEndif(&l->pp); while (l->pos < l->len && l->src[l->pos] != '\n') lexerAdvance(l); if (l->pos < l->len) lexerAdvance(l); goto restart; } if (!isPreprocessorActive(&l->pp)) { while (l->pos < l->len) { while (l->pos < l->len && l->src[l->pos] != '\n' && isspace((unsigned char)l->src[l->pos])) lexerAdvance(l); if (l->pos < l->len && l->src[l->pos] == '#') break; while (l->pos < l->len && l->src[l->pos] != '\n') lexerAdvance(l); if (l->pos < l->len) { l->pp.skippedLines++; lexerAdvance(l); } } goto restart; } if (strcmp(directive, "define") == 0) { int ns = l->pos; while (l->pos < l->len && (isalnum((unsigned char)l->src[l->pos]) || l->src[l->pos] == '_')) lexerAdvance(l); char name[256] = {0}; int nl = l->pos - ns; if (nl > 0 && nl < 255) { memcpy(name, l->src + ns, nl); name[nl] = '\0'; } char** params = NULL; int paramCount = 0; if (l->pos < l->len && l->src[l->pos] == '(') { lexerAdvance(l); int pCap = 4; params = (char**)AllocateMemoryN(pCap, sizeof(char*), "lexerNext1"); while (l->pos < l->len && l->src[l->pos] != ')') { while (l->pos < l->len && isspace((unsigned char)l->src[l->pos])) lexerAdvance(l); if (l->src[l->pos] == ')') break; int ps = l->pos; while (l->pos < l->len && (isalnum((unsigned char)l->src[l->pos]) || l->src[l->pos] == '_')) lexerAdvance(l); int pl = l->pos - ps; if (pl > 0) { if (paramCount >= pCap) { int old_pCap = pCap; pCap *= 2; params = (char**)safeRealloc(params, old_pCap, pCap, sizeof(char*)); } char* p2 = (char*)AllocateMemoryN(1, pl + 1, "lexerNext2"); memcpy(p2, l->src + ps, pl); p2[pl] = '\0'; params[paramCount++] = p2; } while (l->pos < l->len && isspace((unsigned char)l->src[l->pos])) lexerAdvance(l); if (l->pos < l->len && l->src[l->pos] == ',') lexerAdvance(l); } if (l->pos < l->len && l->src[l->pos] == ')') lexerAdvance(l); } while (l->pos < l->len && isspace((unsigned char)l->src[l->pos]) && l->src[l->pos] != '\n') lexerAdvance(l); int vs = l->pos; while (l->pos < l->len && l->src[l->pos] != '\n') lexerAdvance(l); int ve = l->pos; while (ve > vs && isspace((unsigned char)l->src[ve-1])) ve--; char* value = NULL; if (ve > vs) { value = (char*)AllocateMemoryN(1, ve - vs + 1, "lexerNext3"); memcpy(value, l->src + vs, ve - vs); value[ve - vs] = '\0'; } addDefine(&l->pp, name, value, params, paramCount); if (value) FreeMemory(value); if (l->pos < l->len && l->src[l->pos] == '\n') lexerAdvance(l); t.type = TOK_PREPROCESSOR; t.value = strDup("define"); return t; } if (strcmp(directive, "undef") == 0) { int ns = l->pos; while (l->pos < l->len && (isalnum((unsigned char)l->src[l->pos]) || l->src[l->pos] == '_')) lexerAdvance(l); char name[256] = {0}; int nl = l->pos - ns; if (nl > 0 && nl < 255) { memcpy(name, l->src + ns, nl); name[nl] = '\0'; } removeDefine(&l->pp, name); l->pos = ns; t.type = TOK_PREPROCESSOR; t.value = strDup("undef"); return t; } t.type = TOK_PREPROCESSOR; t.value = strDup(directive); return t; } } if (l->pos >= l->len) { t.type = TOK_EOF; t.value = NULL; return t; } if (!isPreprocessorActive(&l->pp)) { while (l->pos < l->len) { while (l->pos < l->len && l->src[l->pos] != '\n' && isspace((unsigned char)l->src[l->pos])) lexerAdvance(l); if (l->pos < l->len && l->src[l->pos] == '#') break; while (l->pos < l->len && l->src[l->pos] != '\n') lexerAdvance(l); if (l->pos < l->len) { l->pp.skippedLines++; lexerAdvance(l); } } goto restart; } char c = l->src[l->pos]; if (c == '0' && (lexerPeekAhead(l, 1) == 'x' || lexerPeekAhead(l, 1) == 'X')) { lexerAdvance(l); // пропускаем '0' lexerAdvance(l); // пропускаем 'x' или 'X' int start = l->pos; while (l->pos < l->len && isxdigit((unsigned char)l->src[l->pos])) { lexerAdvance(l); } int len = l->pos - start; if (len == 0) { // нет цифр после 0x t.type = TOK_UNKNOWN; t.value = strDup("0x"); return t; } t.value = (char*)AllocateMemoryN(1, len + 1, "hex"); memcpy(t.value, l->src + start, len); t.value[len] = '\0'; t.type = TOK_INT_LITERAL; t.is_unsigned = false; // Проверяем суффикс u/U if (l->pos < l->len && (l->src[l->pos] == 'u' || l->src[l->pos] == 'U')) { t.is_unsigned = true; lexerAdvance(l); } return t; } if (isdigit((unsigned char)c) || (c == '.' && isdigit((unsigned char)lexerPeekAhead(l, 1)))) { int start = l->pos; bool isFloat = false; bool isUnsigned = false; while (l->pos < l->len && (isdigit((unsigned char)l->src[l->pos]) || l->src[l->pos] == '.')) { if (l->src[l->pos] == '.') isFloat = true; lexerAdvance(l); } if (l->pos < l->len && (l->src[l->pos] == 'e' || l->src[l->pos] == 'E')) { isFloat = true; lexerAdvance(l); if (l->pos < l->len && (l->src[l->pos] == '+' || l->src[l->pos] == '-')) lexerAdvance(l); while (l->pos < l->len && isdigit((unsigned char)l->src[l->pos])) lexerAdvance(l); } if (l->pos < l->len && strchr("fFuU", l->src[l->pos])) { if (l->src[l->pos] == 'f' || l->src[l->pos] == 'F') isFloat = true; if (l->src[l->pos] == 'u' || l->src[l->pos] == 'U') isUnsigned = true; lexerAdvance(l); } int len = l->pos - start; t.value = (char*)AllocateMemoryN(1, len + 1, "lexerNext4"); memcpy(t.value, l->src + start, len); t.value[len] = '\0'; t.type = isFloat ? TOK_FLOAT_LITERAL : TOK_INT_LITERAL; t.is_unsigned = isUnsigned; return t; } if (isalpha((unsigned char)c) || c == '_') { int start = l->pos; while (l->pos < l->len && (isalnum((unsigned char)l->src[l->pos]) || l->src[l->pos] == '_')) lexerAdvance(l); int len = l->pos - start; t.value = (char*)AllocateMemoryN(1, len + 1, "lexerNext5"); memcpy(t.value, l->src + start, len); t.value[len] = '\0'; t.type = checkKeyword(t.value); if (t.type == TOK_IDENTIFIER) { Define* macro = findDefine(&l->pp, t.value); if (macro) { if (macro->paramCount > 0 || macro->params != NULL) { int savePos = l->pos, saveLine = l->line, saveCol = l->col; while (l->pos < l->len && isspace((unsigned char)l->src[l->pos]) && l->src[l->pos] != '\n') lexerAdvance(l); if (l->pos < l->len && l->src[l->pos] == '(') { lexerAdvance(l); int aCap = 4; char** args = (char**)AllocateMemoryN(aCap, sizeof(char*), "lexerNext6"); int argCount = 0, depth = 1, argStart = l->pos; while (l->pos < l->len && depth > 0) { char ch = l->src[l->pos]; if (ch == '(') depth++; else if (ch == ')') { depth--; if (depth == 0) break; } else if (ch == ',' && depth == 1) { int ae = l->pos; while (ae > argStart && isspace((unsigned char)l->src[ae-1])) ae--; int as = argStart; while (as < ae && isspace((unsigned char)l->src[as])) as++; if (argCount >= aCap) { int old_aCap = aCap; aCap *= 2; args = (char**)safeRealloc(args, old_aCap, aCap, sizeof(char*)); } int alen = ae - as; if (alen > 0) { args[argCount] = (char*)AllocateMemoryN(1, alen + 1, "lexerNext7"); memcpy(args[argCount], l->src + as, alen); args[argCount][alen] = '\0'; } else args[argCount] = strDup(""); argCount++; lexerAdvance(l); argStart = l->pos; continue; } lexerAdvance(l); } int ae = l->pos; while (ae > argStart && isspace((unsigned char)l->src[ae-1])) ae--; int as = argStart; while (as < ae && isspace((unsigned char)l->src[as])) as++; if (argCount >= aCap) { int old_aCap = aCap; aCap *= 2; args = (char**)safeRealloc(args, old_aCap, aCap, sizeof(char*)); } int alen = ae - as; args[argCount] = (char*)AllocateMemoryN(1, alen + 1, "lexerNext8"); if (alen > 0) memcpy(args[argCount], l->src + as, alen); args[argCount][alen] = '\0'; argCount++; if (l->pos < l->len && l->src[l->pos] == ')') lexerAdvance(l); char* expanded = expandFunctionMacro(macro, args, argCount); for (int i = 0; i < argCount; i++) FreeMemory(args[i]); FreeMemory(args); lexStringIntoQueue(l, expanded, &l->queue); FreeMemory(expanded); l->macroExpansions++; tokenFree(&t); goto restart; } else { l->pos = savePos; l->line = saveLine; l->col = saveCol; } } else if (macro->value) { lexStringIntoQueue(l, macro->value, &l->queue); l->macroExpansions++; tokenFree(&t); goto restart; } } } return t; } char next = lexerPeekAhead(l, 1); char next2 = lexerPeekAhead(l, 2); if (c == '<' && next == '<' && next2 == '=') { lexerAdvance(l); lexerAdvance(l); lexerAdvance(l); t.type = TOK_LSHIFT_ASSIGN; t.value = strDup("<<="); return t; } if (c == '>' && next == '>' && next2 == '=') { lexerAdvance(l); lexerAdvance(l); lexerAdvance(l); t.type = TOK_RSHIFT_ASSIGN; t.value = strDup(">>="); return t; } #define OP2(ch1, ch2, tt, s) if (c == ch1 && next == ch2) { lexerAdvance(l); lexerAdvance(l); t.type = tt; t.value = strDup(s); return t; } OP2('+', '+', TOK_INCREMENT, "++") OP2('-', '-', TOK_DECREMENT, "--") OP2('+', '=', TOK_PLUS_ASSIGN, "+=") OP2('-', '=', TOK_MINUS_ASSIGN, "-=") OP2('*', '=', TOK_STAR_ASSIGN, "*=") OP2('/', '=', TOK_SLASH_ASSIGN, "/=") OP2('=', '=', TOK_EQ, "==") OP2('!', '=', TOK_NEQ, "!=") OP2('<', '=', TOK_LTE, "<=") OP2('>', '=', TOK_GTE, ">=") OP2('&', '&', TOK_AND, "&&") OP2('|', '|', TOK_OR, "||") OP2('<', '<', TOK_LSHIFT, "<<") OP2('>', '>', TOK_RSHIFT, ">>") OP2('%', '=', TOK_MOD_ASSIGN, "%=") OP2('&', '=', TOK_BITAND_ASSIGN, "&=") OP2('|', '=', TOK_BITOR_ASSIGN, "|=") OP2('^', '=', TOK_BITXOR_ASSIGN, "^=") #undef OP2 lexerAdvance(l); t.value = (char*)AllocateMemoryN(1, 2, "lexerNextSingle"); t.value[0] = c; t.value[1] = '\0'; switch (c) { case '+': t.type = TOK_PLUS; break; case '-': t.type = TOK_MINUS; break; case '*': t.type = TOK_STAR; break; case '/': t.type = TOK_SLASH; break; case '=': t.type = TOK_ASSIGN; break; case '<': t.type = TOK_LT; break; case '>': t.type = TOK_GT; break; case '!': t.type = TOK_NOT; break; case '?': t.type = TOK_QUESTION; break; case ':': t.type = TOK_COLON; break; case '(': t.type = TOK_LPAREN; break; case ')': t.type = TOK_RPAREN; break; case '{': t.type = TOK_LBRACE; break; case '}': t.type = TOK_RBRACE; break; case '[': t.type = TOK_LBRACKET; break; case ']': t.type = TOK_RBRACKET; break; case ';': t.type = TOK_SEMICOLON; break; case ',': t.type = TOK_COMMA; break; case '.': t.type = TOK_DOT; break; case '%': t.type = TOK_MOD; break; case '&': t.type = TOK_BITAND; break; case '|': t.type = TOK_BITOR; break; case '^': t.type = TOK_BITXOR; break; case '~': t.type = TOK_BITNOT; break; default: t.type = TOK_UNKNOWN; break; } return t; } static void lexerInit(Lexer* l, const char* src) { l->src = src; l->pos = 0; l->len = (int)strlen(src); l->line = 1; l->col = 1; l->macroExpansions = 0; preprocessorInit(&l->pp); tokenQueueInit(&l->queue); } static void lexerFree(Lexer* l) { preprocessorFree(&l->pp); tokenQueueFree(&l->queue); } typedef struct { int pos; int line; int col; GLSLToken current; } ParserState; typedef struct { Lexer lexer; GLSLToken current; bool error; char errorMsg[512]; } Parser; static void parserInit(Parser* p, const char* src) { lexerInit(&p->lexer, src); p->error = false; p->errorMsg[0] = '\0'; p->current = lexerNext(&p->lexer); } static void parserFree(Parser* p) { tokenFree(&p->current); lexerFree(&p->lexer); } static void parserError(Parser* p, const char* msg) { if (!p->error) { p->error = true; snprintf(p->errorMsg, sizeof(p->errorMsg), "Error at %d:%d - %s (got '%s')", p->current.line, p->current.col, msg, p->current.value ? p->current.value : "EOF"); } } static bool match(Parser* p, GLSLTokenType type) { if (p->current.type == type) { tokenFree(&p->current); p->current = lexerNext(&p->lexer); return true; } return false; } static bool expect(Parser* p, GLSLTokenType type, const char* what) { if (!match(p, type)) { parserError(p, what); return false; } return true; } static GLSLToken consume(Parser* p) { GLSLToken t = p->current; p->current = lexerNext(&p->lexer); return t; } static void parserSave(Parser* p, ParserState* s) { s->pos = p->lexer.pos; s->line = p->lexer.line; s->col = p->lexer.col; s->current.type = p->current.type; s->current.line = p->current.line; s->current.col = p->current.col; s->current.value = p->current.value ? strDup(p->current.value) : NULL; } static void parserRestore(Parser* p, ParserState* s) { if (!p || !s) return; p->lexer.pos = s->pos; p->lexer.line = s->line; p->lexer.col = s->col; tokenFree(&p->current); p->current = s->current; s->current.value = NULL; s->current.type = TOK_UNKNOWN; } static void parserStateFree(ParserState* s) { if (!s) return; if (s->current.value) { FreeMemory(s->current.value); s->current.value = NULL; } } static GlslNode* parseBitwiseOr(Parser* p); static GlslNode* parseBitwiseXor(Parser* p); static GlslNode* parseBitwiseAnd(Parser* p); static GlslNode* parseShift(Parser* p); static GlslNode* parseExpression(Parser* p); static GlslNode* parseAssignment(Parser* p); static GlslNode* parseTernary(Parser* p); static GlslNode* parseLogicalOr(Parser* p); static GlslNode* parseLogicalAnd(Parser* p); static GlslNode* parseEquality(Parser* p); static GlslNode* parseRelational(Parser* p); static GlslNode* parseAdditive(Parser* p); static GlslNode* parseMultiplicative(Parser* p); static GlslNode* parseUnary(Parser* p); static GlslNode* parsePostfix(Parser* p); static GlslNode* parsePrimary(Parser* p); static GlslNode* parseStatement(Parser* p); static GlslNode* parseBlock(Parser* p); static GlslNode* parseExpression(Parser* p) { return parseAssignment(p); } static GlslNode* parseBitwiseOr(Parser* p) { GlslNode* left = parseBitwiseXor(p); while (p->current.type == TOK_BITOR) { GLSLToken op = consume(p); GlslNode* n = nodeCreate(NODE_BINARY_OP, op.line, op.col); n->data.binaryOp.op = op.value; n->data.binaryOp.left = left; n->data.binaryOp.right = parseBitwiseXor(p); left = n; } return left; } static GlslNode* parseBitwiseXor(Parser* p) { GlslNode* left = parseBitwiseAnd(p); while (p->current.type == TOK_BITXOR) { GLSLToken op = consume(p); GlslNode* n = nodeCreate(NODE_BINARY_OP, op.line, op.col); n->data.binaryOp.op = op.value; n->data.binaryOp.left = left; n->data.binaryOp.right = parseBitwiseAnd(p); left = n; } return left; } static GlslNode* parseBitwiseAnd(Parser* p) { GlslNode* left = parseEquality(p); while (p->current.type == TOK_BITAND) { GLSLToken op = consume(p); GlslNode* n = nodeCreate(NODE_BINARY_OP, op.line, op.col); n->data.binaryOp.op = op.value; n->data.binaryOp.left = left; n->data.binaryOp.right = parseEquality(p); left = n; } return left; } static GlslNode* parseShift(Parser* p) { GlslNode* left = parseAdditive(p); while (p->current.type == TOK_LSHIFT || p->current.type == TOK_RSHIFT) { GLSLToken op = consume(p); GlslNode* n = nodeCreate(NODE_BINARY_OP, op.line, op.col); n->data.binaryOp.op = op.value; n->data.binaryOp.left = left; n->data.binaryOp.right = parseAdditive(p); left = n; } return left; } static GlslNode* parseAssignment(Parser* p) { GlslNode* left = parseTernary(p); if (!left || p->error) return left; GLSLTokenType t = p->current.type; if (t == TOK_ASSIGN || t == TOK_PLUS_ASSIGN || t == TOK_MINUS_ASSIGN || t == TOK_STAR_ASSIGN || t == TOK_SLASH_ASSIGN || t == TOK_MOD_ASSIGN || t == TOK_LSHIFT_ASSIGN || t == TOK_RSHIFT_ASSIGN || t == TOK_BITAND_ASSIGN || t == TOK_BITOR_ASSIGN || t == TOK_BITXOR_ASSIGN) { GLSLToken op = consume(p); GlslNode* right = parseAssignment(p); GlslNode* node = nodeCreate(NODE_ASSIGNMENT, op.line, op.col); node->data.assignment.op = op.value; node->data.assignment.target = left; node->data.assignment.value = right; return node; } return left; } static GlslNode* parseTernary(Parser* p) { GlslNode* cond = parseLogicalOr(p); if (!cond || p->error) return cond; if (p->current.type == TOK_QUESTION) { GLSLToken qTok = consume(p); tokenFree(&qTok); GlslNode* thenExpr = parseExpression(p); expect(p, TOK_COLON, "Expected ':'"); GlslNode* elseExpr = parseTernary(p); GlslNode* node = nodeCreate(NODE_IF, cond->line, cond->col); node->data.ifStmt.condition = cond; node->data.ifStmt.thenBranch = thenExpr; node->data.ifStmt.elseBranch = elseExpr; return node; } return cond; } static GlslNode* parseLogicalOr(Parser* p) { GlslNode* left = parseLogicalAnd(p); while (p->current.type == TOK_OR) { GLSLToken op = consume(p); GlslNode* n = nodeCreate(NODE_BINARY_OP, op.line, op.col); n->data.binaryOp.op = op.value; n->data.binaryOp.left = left; n->data.binaryOp.right = parseLogicalAnd(p); left = n; } return left; } static GlslNode* parseLogicalAnd(Parser* p) { GlslNode* left = parseBitwiseOr(p); while (p->current.type == TOK_AND) { GLSLToken op = consume(p); GlslNode* n = nodeCreate(NODE_BINARY_OP, op.line, op.col); n->data.binaryOp.op = op.value; n->data.binaryOp.left = left; n->data.binaryOp.right = parseBitwiseOr(p); left = n; } return left; } static GlslNode* parseEquality(Parser* p) { GlslNode* left = parseRelational(p); while (p->current.type == TOK_EQ || p->current.type == TOK_NEQ) { GLSLToken op = consume(p); GlslNode* n = nodeCreate(NODE_BINARY_OP, op.line, op.col); n->data.binaryOp.op = op.value; n->data.binaryOp.left = left; n->data.binaryOp.right = parseRelational(p); left = n; } return left; } static GlslNode* parseRelational(Parser* p) { GlslNode* left = parseShift(p); while (p->current.type == TOK_LT || p->current.type == TOK_GT || p->current.type == TOK_LTE || p->current.type == TOK_GTE) { GLSLToken op = consume(p); GlslNode* n = nodeCreate(NODE_BINARY_OP, op.line, op.col); n->data.binaryOp.op = op.value; n->data.binaryOp.left = left; n->data.binaryOp.right = parseShift(p); left = n; } return left; } static GlslNode* parseAdditive(Parser* p) { GlslNode* left = parseMultiplicative(p); while (p->current.type == TOK_PLUS || p->current.type == TOK_MINUS) { GLSLToken op = consume(p); GlslNode* n = nodeCreate(NODE_BINARY_OP, op.line, op.col); n->data.binaryOp.op = op.value; n->data.binaryOp.left = left; n->data.binaryOp.right = parseMultiplicative(p); left = n; } return left; } static GlslNode* parseMultiplicative(Parser* p) { GlslNode* left = parseUnary(p); while (p->current.type == TOK_STAR || p->current.type == TOK_SLASH || p->current.type == TOK_MOD) { GLSLToken op = consume(p); GlslNode* n = nodeCreate(NODE_BINARY_OP, op.line, op.col); n->data.binaryOp.op = op.value; n->data.binaryOp.left = left; n->data.binaryOp.right = parseUnary(p); left = n; } return left; } static GlslNode* parseUnary(Parser* p) { if (p->current.type == TOK_PLUS) { GLSLToken plusTok = consume(p); tokenFree(&plusTok); return parseUnary(p); } if (p->current.type == TOK_MINUS || p->current.type == TOK_NOT || p->current.type == TOK_INCREMENT || p->current.type == TOK_DECREMENT) { GLSLToken op = consume(p); GlslNode* operand = parseUnary(p); GlslNode* n = nodeCreate(NODE_UNARY_OP, op.line, op.col); n->data.unaryOp.op = op.value; n->data.unaryOp.operand = operand; n->data.unaryOp.isPrefix = true; return n; } return parsePostfix(p); } static GlslNode* parsePostfix(Parser* p) { GlslNode* expr = parsePrimary(p); if (!expr || p->error) return expr; while (true) { if (p->current.type == TOK_LPAREN) { GLSLToken lp = consume(p); tokenFree(&lp); GlslNode* n = nodeCreate(NODE_CALL, expr->line, expr->col); if (expr->type == NODE_IDENTIFIER) { n->data.call.name = strDup(expr->data.identifier.name); nodeFree(expr); } else if (expr->type == NODE_MEMBER) { n->data.call.name = strDup(expr->data.member.member); nodeFree(expr); } else { parserError(p, "Expected function name"); nodeFree(expr); return NULL; } nodeListInit(&n->data.call.arguments); if (p->current.type != TOK_RPAREN) { GlslNode* arg = parseExpression(p); if (!arg) { parserError(p, "Invalid argument in function call"); nodeFree(n); return NULL; } nodeListPush(&n->data.call.arguments, arg); while (match(p, TOK_COMMA)) { arg = parseExpression(p); if (!arg) { parserError(p, "Invalid argument in function call"); nodeFree(n); return NULL; } nodeListPush(&n->data.call.arguments, arg); } } expect(p, TOK_RPAREN, "Expected ')'"); expr = n; } else if (p->current.type == TOK_LBRACKET) { GLSLToken lb = consume(p); tokenFree(&lb); GlslNode* idx = NULL; if (p->current.type != TOK_RBRACKET) { idx = parseExpression(p); if (!idx) { parserError(p, "Invalid index expression"); nodeFree(expr); return NULL; } } expect(p, TOK_RBRACKET, "Expected ']'"); if (p->current.type == TOK_LPAREN && expr->type == NODE_IDENTIFIER && idx == NULL) { GLSLToken lp2 = consume(p); tokenFree(&lp2); GlslNode* n = nodeCreate(NODE_CALL, expr->line, expr->col); char name[128]; snprintf(name, sizeof(name), "%s[]", expr->data.identifier.name); n->data.call.name = strDup(name); nodeListInit(&n->data.call.arguments); if (p->current.type != TOK_RPAREN) { GlslNode* arg = parseExpression(p); if (!arg) { parserError(p, "Invalid argument in array constructor"); nodeFree(n); nodeFree(expr); return NULL; } nodeListPush(&n->data.call.arguments, arg); while (match(p, TOK_COMMA)) { arg = parseExpression(p); if (!arg) { parserError(p, "Invalid argument in array constructor"); nodeFree(n); nodeFree(expr); return NULL; } nodeListPush(&n->data.call.arguments, arg); } } expect(p, TOK_RPAREN, "Expected ')'"); nodeFree(expr); expr = n; } else { GlslNode* n = nodeCreate(NODE_INDEX, expr->line, expr->col); n->data.index.object = expr; n->data.index.index = idx; expr = n; } } else if (p->current.type == TOK_DOT) { GLSLToken dot = consume(p); tokenFree(&dot); if (p->current.type != TOK_IDENTIFIER) { parserError(p, "Expected member name"); return NULL; } GLSLToken member = consume(p); if (strcmp(member.value, "length") == 0 && p->current.type == TOK_LPAREN) { GLSLToken lp = consume(p); tokenFree(&lp); expect(p, TOK_RPAREN, "Expected ')' after length()"); GlslNode* n = nodeCreate(NODE_CALL, expr->line, expr->col); n->data.call.name = strDup("length"); nodeListInit(&n->data.call.arguments); nodeListPush(&n->data.call.arguments, expr); expr = n; tokenFree(&member); continue; } if (isSwizzleMask(member.value)) { GlslNode* n = nodeCreate(NODE_SWIZZLE, expr->line, expr->col); n->data.swizzle.object = expr; n->data.swizzle.mask = member.value; expr = n; } else { GlslNode* n = nodeCreate(NODE_MEMBER, expr->line, expr->col); n->data.member.object = expr; n->data.member.member = member.value; expr = n; } } else if (p->current.type == TOK_INCREMENT || p->current.type == TOK_DECREMENT) { GLSLToken op = consume(p); GlslNode* n = nodeCreate(NODE_UNARY_OP, op.line, op.col); n->data.unaryOp.op = op.value; n->data.unaryOp.operand = expr; n->data.unaryOp.isPrefix = false; expr = n; } else break; } return expr; } static GlslNode* parsePrimary(Parser* p) { GLSLToken t = p->current; if (t.type == TOK_INT_LITERAL) { consume(p); GlslNode* n = nodeCreate(NODE_INT_LITERAL, t.line, t.col); n->data.intLiteral.value = t.value ? strtol(t.value, NULL, 0) : 0; n->data.intLiteral.is_unsigned = t.is_unsigned; tokenFree(&t); return n; } if (t.type == TOK_FLOAT_LITERAL) { consume(p); GlslNode* n = nodeCreate(NODE_FLOAT_LITERAL, t.line, t.col); n->data.floatLiteral.value = t.value ? strtod(t.value, NULL) : 0.0f; tokenFree(&t); return n; } if (t.type == TOK_TRUE || t.type == TOK_FALSE) { consume(p); GlslNode* n = nodeCreate(NODE_BOOL_LITERAL, t.line, t.col); n->data.boolLiteral.value = (t.type == TOK_TRUE); tokenFree(&t); return n; } if (t.type == TOK_IDENTIFIER || t.type == TOK_VEC2 || t.type == TOK_VEC3 || t.type == TOK_VEC4 || t.type == TOK_MAT2 || t.type == TOK_MAT3 || t.type == TOK_MAT4 || t.type == TOK_INT || t.type == TOK_FLOAT || t.type == TOK_BOOL || t.type == TOK_UINT || t.type == TOK_UVEC2 || t.type == TOK_UVEC3 || t.type == TOK_UVEC4 || t.type == TOK_IVEC2 || t.type == TOK_IVEC3 || t.type == TOK_IVEC4 || t.type == TOK_BVEC2 || t.type == TOK_BVEC3 || t.type == TOK_BVEC4) { consume(p); GlslNode* n = nodeCreate(NODE_IDENTIFIER, t.line, t.col); n->data.identifier.name = t.value; return n; } if (t.type == TOK_LPAREN) { GLSLToken lp = consume(p); tokenFree(&lp); GlslNode* expr = parseExpression(p); expect(p, TOK_RPAREN, "Expected ')'"); return expr; } parserError(p, "Expected expression"); return NULL; } static GlslNode* parseBlock(Parser* p) { if (!expect(p, TOK_LBRACE, "Expected '{'")) return NULL; GlslNode* block = nodeCreate(NODE_BLOCK, p->current.line, p->current.col); nodeListInit(&block->data.block.statements); while (p->current.type != TOK_RBRACE && p->current.type != TOK_EOF && !p->error) { GlslNode* stmt = parseStatement(p); if (stmt) nodeListPush(&block->data.block.statements, stmt); if (p->error) break; } expect(p, TOK_RBRACE, "Expected '}'"); return block; } static GlslNode* parseOptionalArraySize(Parser* p) { if (p->current.type == TOK_LBRACKET) { GLSLToken lb = consume(p); tokenFree(&lb); GlslNode* size = NULL; if (p->current.type != TOK_RBRACKET) size = parseExpression(p); expect(p, TOK_RBRACKET, "Expected ']'"); return size; } return NULL; } static GlslNode* parseDoWhile(Parser* p) { GLSLToken doTok = consume(p); GlslNode* n = nodeCreate(NODE_DO_WHILE, doTok.line, doTok.col); tokenFree(&doTok); n->data.doWhileStmt.body = parseStatement(p); if (!expect(p, TOK_WHILE, "Expected 'while' after do-body")) { nodeFree(n); return NULL; } expect(p, TOK_LPAREN, "Expected '('"); n->data.doWhileStmt.condition = parseExpression(p); expect(p, TOK_RPAREN, "Expected ')'"); expect(p, TOK_SEMICOLON, "Expected ';' after do-while"); return n; } static GlslNode* parseSwitch(Parser* p) { GLSLToken switchTok = consume(p); GlslNode* n = nodeCreate(NODE_SWITCH, switchTok.line, switchTok.col); tokenFree(&switchTok); expect(p, TOK_LPAREN, "Expected '('"); n->data.switchStmt.condition = parseExpression(p); expect(p, TOK_RPAREN, "Expected ')'"); expect(p, TOK_LBRACE, "Expected '{'"); nodeListInit(&n->data.switchStmt.cases); while (p->current.type != TOK_RBRACE && p->current.type != TOK_EOF && !p->error) { if (p->current.type == TOK_CASE) { GLSLToken caseTok = consume(p); GlslNode* c = nodeCreate(NODE_CASE, caseTok.line, caseTok.col); tokenFree(&caseTok); c->data.caseStmt.isDefault = false; nodeListInit(&c->data.caseStmt.labels); nodeListInit(&c->data.caseStmt.statements); GlslNode* label = parseExpression(p); if (!label) { parserError(p, "Invalid case label"); nodeFree(c); return NULL; } nodeListPush(&c->data.caseStmt.labels, label); while (match(p, TOK_COMMA)) { label = parseExpression(p); if (!label) { parserError(p, "Invalid case label"); nodeFree(c); return NULL; } nodeListPush(&c->data.caseStmt.labels, label); } expect(p, TOK_COLON, "Expected ':' after case"); while (p->current.type != TOK_CASE && p->current.type != TOK_DEFAULT && p->current.type != TOK_RBRACE && p->current.type != TOK_EOF && !p->error) { GlslNode* stmt = parseStatement(p); if (stmt) nodeListPush(&c->data.caseStmt.statements, stmt); } nodeListPush(&n->data.switchStmt.cases, c); } else if (p->current.type == TOK_DEFAULT) { GLSLToken defTok = consume(p); GlslNode* c = nodeCreate(NODE_CASE, defTok.line, defTok.col); tokenFree(&defTok); c->data.caseStmt.isDefault = true; nodeListInit(&c->data.caseStmt.labels); nodeListInit(&c->data.caseStmt.statements); expect(p, TOK_COLON, "Expected ':' after default"); while (p->current.type != TOK_CASE && p->current.type != TOK_DEFAULT && p->current.type != TOK_RBRACE && p->current.type != TOK_EOF && !p->error) { GlslNode* stmt = parseStatement(p); if (stmt) nodeListPush(&c->data.caseStmt.statements, stmt); } nodeListPush(&n->data.switchStmt.cases, c); } else { parserError(p, "Expected 'case' or 'default' in switch"); break; } } expect(p, TOK_RBRACE, "Expected '}'"); return n; } static GlslNode* parsePrecision(Parser* p) { GLSLToken qualTok = consume(p); GlslNode* n = nodeCreate(NODE_PRECISION_STMT, qualTok.line, qualTok.col); n->data.precisionStmt.qualifier = qualTok.value; if (!isTypeToken(p->current.type)) { parserError(p, "Expected type after precision qualifier"); nodeFree(n); return NULL; } GLSLToken typeTok = consume(p); n->data.precisionStmt.typeName = typeTok.value; expect(p, TOK_SEMICOLON, "Expected ';'"); return n; } static GlslNode* parseStatement(Parser* p) { if (p->current.type == TOK_IF) { GLSLToken ifTok = consume(p); GlslNode* n = nodeCreate(NODE_IF, ifTok.line, ifTok.col); tokenFree(&ifTok); expect(p, TOK_LPAREN, "Expected '('"); n->data.ifStmt.condition = parseExpression(p); expect(p, TOK_RPAREN, "Expected ')'"); n->data.ifStmt.thenBranch = parseStatement(p); n->data.ifStmt.elseBranch = NULL; if (match(p, TOK_ELSE)) n->data.ifStmt.elseBranch = parseStatement(p); return n; } if (p->current.type == TOK_FOR) { GLSLToken forTok = consume(p); GlslNode* n = nodeCreate(NODE_FOR, forTok.line, forTok.col); tokenFree(&forTok); expect(p, TOK_LPAREN, "Expected '('"); n->data.forStmt.init = NULL; bool initWasStatement = false; if (p->current.type != TOK_SEMICOLON) { if (isTypeToken(p->current.type)) { n->data.forStmt.init = parseStatement(p); initWasStatement = true; } else { n->data.forStmt.init = parseExpression(p); initWasStatement = false; } } if (!initWasStatement) { expect(p, TOK_SEMICOLON, "Expected ';' after for-init"); } n->data.forStmt.condition = NULL; if (p->current.type != TOK_SEMICOLON) { n->data.forStmt.condition = parseExpression(p); } expect(p, TOK_SEMICOLON, "Expected ';' after for-condition"); n->data.forStmt.increment = NULL; if (p->current.type != TOK_RPAREN) { n->data.forStmt.increment = parseExpression(p); } expect(p, TOK_RPAREN, "Expected ')'"); n->data.forStmt.body = parseStatement(p); return n; } if (p->current.type == TOK_WHILE) { GLSLToken whileTok = consume(p); GlslNode* n = nodeCreate(NODE_WHILE, whileTok.line, whileTok.col); tokenFree(&whileTok); expect(p, TOK_LPAREN, "Expected '('"); n->data.whileStmt.condition = parseExpression(p); expect(p, TOK_RPAREN, "Expected ')'"); n->data.whileStmt.body = parseStatement(p); return n; } if (p->current.type == TOK_DO) { GLSLToken doTok = consume(p); GlslNode* n = nodeCreate(NODE_DO_WHILE, doTok.line, doTok.col); tokenFree(&doTok); n->data.doWhileStmt.body = parseStatement(p); expect(p, TOK_WHILE, "Expected 'while'"); expect(p, TOK_LPAREN, "Expected '('"); n->data.doWhileStmt.condition = parseExpression(p); expect(p, TOK_RPAREN, "Expected ')'"); expect(p, TOK_SEMICOLON, "Expected ';'"); return n; } if (p->current.type == TOK_SWITCH) { GLSLToken switchTok = consume(p); GlslNode* n = nodeCreate(NODE_SWITCH, switchTok.line, switchTok.col); tokenFree(&switchTok); expect(p, TOK_LPAREN, "Expected '('"); n->data.switchStmt.condition = parseExpression(p); expect(p, TOK_RPAREN, "Expected ')'"); expect(p, TOK_LBRACE, "Expected '{'"); nodeListInit(&n->data.switchStmt.cases); while (p->current.type != TOK_RBRACE && p->current.type != TOK_EOF && !p->error) { if (p->current.type == TOK_CASE) { GLSLToken caseTok = consume(p); GlslNode* c = nodeCreate(NODE_CASE, caseTok.line, caseTok.col); tokenFree(&caseTok); c->data.caseStmt.isDefault = false; nodeListInit(&c->data.caseStmt.labels); nodeListInit(&c->data.caseStmt.statements); GlslNode* label = parseExpression(p); if (!label) { parserError(p, "Invalid case label"); nodeFree(c); return NULL; } nodeListPush(&c->data.caseStmt.labels, label); while (match(p, TOK_COMMA)) { label = parseExpression(p); if (!label) { parserError(p, "Invalid case label"); nodeFree(c); return NULL; } nodeListPush(&c->data.caseStmt.labels, label); } expect(p, TOK_COLON, "Expected ':' after case"); while (p->current.type != TOK_CASE && p->current.type != TOK_DEFAULT && p->current.type != TOK_RBRACE && p->current.type != TOK_EOF && !p->error) { GlslNode* stmt = parseStatement(p); if (stmt) nodeListPush(&c->data.caseStmt.statements, stmt); } nodeListPush(&n->data.switchStmt.cases, c); } else if (p->current.type == TOK_DEFAULT) { GLSLToken defTok = consume(p); GlslNode* c = nodeCreate(NODE_CASE, defTok.line, defTok.col); tokenFree(&defTok); c->data.caseStmt.isDefault = true; nodeListInit(&c->data.caseStmt.labels); nodeListInit(&c->data.caseStmt.statements); expect(p, TOK_COLON, "Expected ':' after default"); while (p->current.type != TOK_CASE && p->current.type != TOK_DEFAULT && p->current.type != TOK_RBRACE && p->current.type != TOK_EOF && !p->error) { GlslNode* stmt = parseStatement(p); if (stmt) nodeListPush(&c->data.caseStmt.statements, stmt); } nodeListPush(&n->data.switchStmt.cases, c); } else { parserError(p, "Expected 'case' or 'default' in switch"); break; } } expect(p, TOK_RBRACE, "Expected '}'"); return n; } if (p->current.type == TOK_RETURN) { GLSLToken retTok = consume(p); GlslNode* n = nodeCreate(NODE_RETURN, retTok.line, retTok.col); tokenFree(&retTok); n->data.returnStmt.value = (p->current.type != TOK_SEMICOLON) ? parseExpression(p) : NULL; expect(p, TOK_SEMICOLON, "Expected ';'"); return n; } if (p->current.type == TOK_BREAK) { GLSLToken tok = consume(p); GlslNode* n = nodeCreate(NODE_BREAK, tok.line, tok.col); tokenFree(&tok); expect(p, TOK_SEMICOLON, "Expected ';'"); return n; } if (p->current.type == TOK_CONTINUE) { GLSLToken tok = consume(p); GlslNode* n = nodeCreate(NODE_CONTINUE, tok.line, tok.col); tokenFree(&tok); expect(p, TOK_SEMICOLON, "Expected ';'"); return n; } if (p->current.type == TOK_DISCARD) { GLSLToken tok = consume(p); GlslNode* n = nodeCreate(NODE_DISCARD, tok.line, tok.col); tokenFree(&tok); expect(p, TOK_SEMICOLON, "Expected ';'"); return n; } if (p->current.type == TOK_HIGHP || p->current.type == TOK_MEDIUMP || p->current.type == TOK_LOWP) { ParserState state; parserSave(p, &state); GLSLToken tempQual = consume(p); bool isPrecision = false; if (isTypeToken(p->current.type)) { GLSLToken tempType = consume(p); if (p->current.type == TOK_SEMICOLON) { isPrecision = true; } tokenFree(&tempType); } tokenFree(&tempQual); parserRestore(p, &state); if (isPrecision) { GLSLToken qualTok = consume(p); GlslNode* n = nodeCreate(NODE_PRECISION_STMT, qualTok.line, qualTok.col); n->data.precisionStmt.qualifier = qualTok.value; GLSLToken typeTok = consume(p); n->data.precisionStmt.typeName = typeTok.value; expect(p, TOK_SEMICOLON, "Expected ';'"); return n; } } if (p->current.type == TOK_LBRACE) { return parseBlock(p); } ParserState state; parserSave(p, &state); GLSLToken qualifierTok = {0}; GLSLToken typeTok = {0}; bool isDeclaration = false; // Пропускаем квалификаторы (const, highp и т.д.), если они есть перед типом if (isQualifierToken(p->current.type) && p->current.type != TOK_LAYOUT) { GLSLToken tempQ = consume(p); if (isTypeToken(p->current.type)) { qualifierTok = tempQ; } else { tokenFree(&tempQ); parserRestore(p, &state); } } if (isTypeToken(p->current.type)) { typeTok = consume(p); // Если после типа идет идентификатор, это скорее всего объявление if (p->current.type == TOK_IDENTIFIER) { GLSLToken nameTok = consume(p); GlslNode* arraySize = parseOptionalArraySize(p); // Если после имени идет '=', ',' или ';', то это точно объявление if (p->current.type == TOK_ASSIGN || p->current.type == TOK_SEMICOLON || p->current.type == TOK_COMMA) { isDeclaration = true; // Логика множественного объявления (уже есть в вашем коде, убедимся, что она работает) int max_vars = 64; char** names = (char**)AllocateMemoryN(max_vars, sizeof(char*), "ParseMultiDecl"); GlslNode** initializers = (GlslNode**)AllocateMemoryN(max_vars, sizeof(GlslNode*), "ParseMultiDeclInit"); GlslNode** arraySizes = (GlslNode**)AllocateMemoryN(max_vars, sizeof(GlslNode*), "ParseMultiDeclArr"); int count = 0; while (true) { if (count >= max_vars) { /* reallocation logic */ } char* currentName = NULL; GlslNode* currentArraySize = NULL; GlslNode* currentInitializer = NULL; if (count == 0) { currentName = nameTok.value; currentArraySize = arraySize; } else { if (p->current.type != TOK_IDENTIFIER) break; GLSLToken nextNameTok = consume(p); currentName = nextNameTok.value; currentArraySize = parseOptionalArraySize(p); } if (p->current.type == TOK_ASSIGN) { GLSLToken assignTok = consume(p); tokenFree(&assignTok); // Освобождаем токен '=' currentInitializer = parseExpression(p); if (!currentInitializer) { parserError(p, "Invalid initializer"); for(int k=0; k<count; k++) { FreeMemory(names[k]); nodeFree(initializers[k]); nodeFree(arraySizes[k]); } FreeMemory(names); FreeMemory(initializers); FreeMemory(arraySizes); break; } } names[count] = currentName; initializers[count] = currentInitializer; arraySizes[count] = currentArraySize; count++; if (p->current.type == TOK_SEMICOLON) break; if (p->current.type == TOK_COMMA) { GLSLToken commaTok = consume(p); tokenFree(&commaTok); // Освобождаем токен ',' } else { parserError(p, "Expected ',' or ';' in declaration"); break; } } expect(p, TOK_SEMICOLON, "Expected ';'"); parserStateFree(&state); GlslNode* n = NULL; if (count == 1) { n = nodeCreate(NODE_VARIABLE_DECL, typeTok.line, typeTok.col); n->data.varDecl.qualifier = qualifierTok.value; n->data.varDecl.typeName = typeTok.value; n->data.varDecl.name = names[0]; n->data.varDecl.initializer = initializers[0]; n->data.varDecl.arraySize = arraySizes[0]; n->data.varDecl.layout = NULL; FreeMemory(names); FreeMemory(initializers); FreeMemory(arraySizes); } else { n = nodeCreate(NODE_VARIABLE_DECL_LIST, typeTok.line, typeTok.col); n->data.varDeclList.qualifier = qualifierTok.value; n->data.varDeclList.typeName = typeTok.value; n->data.varDeclList.names = names; n->data.varDeclList.initializers = initializers; n->data.varDeclList.arraySizes = arraySizes; n->data.varDeclList.count = count; n->data.varDeclList.layout = NULL; } return n; } else { // Это не объявление (например, cast), восстанавливаем состояние tokenFree(&nameTok); if (arraySize) nodeFree(arraySize); tokenFree(&typeTok); if (qualifierTok.value) tokenFree(&qualifierTok); parserRestore(p, &state); } } else { tokenFree(&typeTok); if (qualifierTok.value) tokenFree(&qualifierTok); parserRestore(p, &state); } } else { if (qualifierTok.value) tokenFree(&qualifierTok); parserRestore(p, &state); } // Если не объявление, пробуем распарсить как выражение GlslNode* expr = parseExpression(p); if (!expr) return NULL; expect(p, TOK_SEMICOLON, "Expected ';'"); GlslNode* stmt = nodeCreate(NODE_EXPR_STMT, expr->line, expr->col); stmt->data.exprStmt.expression = expr; return stmt; } static GlslNode* parseStruct(Parser* p) { GLSLToken structTok = consume(p); tokenFree(&structTok); GlslNode* n = nodeCreate(NODE_STRUCT_DECL, p->current.line, p->current.col); if (p->current.type != TOK_IDENTIFIER) { parserError(p, "Expected struct name"); return NULL; } GLSLToken name = consume(p); n->data.structDecl.name = name.value; expect(p, TOK_LBRACE, "Expected '{'"); nodeListInit(&n->data.structDecl.fields); while (p->current.type != TOK_RBRACE && p->current.type != TOK_EOF && !p->error) { GLSLToken typeTok = consume(p); if (p->current.type != TOK_IDENTIFIER) { parserError(p, "Expected field name"); tokenFree(&typeTok); break; } GLSLToken fieldTok = consume(p); GlslNode* arraySize = parseOptionalArraySize(p); GlslNode* field = nodeCreate(NODE_FIELD_DECL, typeTok.line, typeTok.col); field->data.fieldDecl.typeName = typeTok.value; field->data.fieldDecl.name = fieldTok.value; field->data.fieldDecl.arraySize = arraySize; nodeListPush(&n->data.structDecl.fields, field); expect(p, TOK_SEMICOLON, "Expected ';'"); } expect(p, TOK_RBRACE, "Expected '}'"); expect(p, TOK_SEMICOLON, "Expected ';'"); return n; } static GlslNode* parseFunction(Parser* p, char* qualifier, GLSLToken returnTypeTok, GLSLToken nameTok) { GlslNode* n = nodeCreate(NODE_FUNCTION_DECL, returnTypeTok.line, returnTypeTok.col); n->data.funcDecl.returnType = returnTypeTok.value; n->data.funcDecl.name = nameTok.value; nodeListInit(&n->data.funcDecl.parameters); expect(p, TOK_LPAREN, "Expected '('"); while (p->current.type != TOK_RPAREN && p->current.type != TOK_EOF && !p->error) { GLSLToken paramQual = {0}, paramType = {0}; if (isQualifierToken(p->current.type) && p->current.type != TOK_LAYOUT) paramQual = consume(p); if (isTypeToken(p->current.type)) paramType = consume(p); else { parserError(p, "Expected parameter type"); break; } GLSLToken paramName = {0}; if (p->current.type == TOK_IDENTIFIER) paramName = consume(p); GlslNode* arraySize = parseOptionalArraySize(p); GlslNode* param = nodeCreate(NODE_PARAMETER, paramType.line, paramType.col); param->data.parameter.qualifier = paramQual.value; param->data.parameter.typeName = paramType.value; param->data.parameter.name = paramName.value; param->data.parameter.arraySize = arraySize; nodeListPush(&n->data.funcDecl.parameters, param); if (!match(p, TOK_COMMA)) break; } expect(p, TOK_RPAREN, "Expected ')'"); n->data.funcDecl.body = parseBlock(p); (void)qualifier; return n; } static GlslNode* parseLayout(Parser* p) { GLSLToken layoutTok = consume(p); tokenFree(&layoutTok); GlslNode* n = nodeCreate(NODE_LAYOUT, p->current.line, p->current.col); n->data.layout.keys = NULL; n->data.layout.values = NULL; n->data.layout.count = 0; n->data.layout.qualifier = NULL; if (!expect(p, TOK_LPAREN, "Expected '(' after layout")) return n; int cap = 4; n->data.layout.keys = (char**)AllocateMemoryN(cap, sizeof(char*), "parseLayout"); n->data.layout.values = (char**)AllocateMemoryN(cap, sizeof(char*), "parseLayout"); while (p->current.type != TOK_RPAREN && p->current.type != TOK_EOF && !p->error) { if (n->data.layout.count >= cap) { int old_cap = cap; cap *= 2; n->data.layout.keys = (char**)safeRealloc(n->data.layout.keys, old_cap, cap, sizeof(char*)); n->data.layout.values = (char**)safeRealloc(n->data.layout.values, old_cap, cap, sizeof(char*)); } if (p->current.type != TOK_IDENTIFIER) { parserError(p, "Expected layout parameter name"); break; } GLSLToken key = consume(p); n->data.layout.keys[n->data.layout.count] = key.value; n->data.layout.values[n->data.layout.count] = NULL; if (match(p, TOK_ASSIGN)) { GLSLToken val = consume(p); n->data.layout.values[n->data.layout.count] = val.value; } n->data.layout.count++; if (!match(p, TOK_COMMA)) break; } expect(p, TOK_RPAREN, "Expected ')'"); return n; } static GlslNode* parseTopLevel(Parser* p) { if (p->current.type == TOK_PREPROCESSOR) { GLSLToken directive = consume(p); int directiveLine = directive.line; if (strcmp(directive.value, "version") == 0) { GlslNode* n = nodeCreate(NODE_VERSION_DECL, directive.line, directive.col); n->data.versionDecl.version = 0; n->data.versionDecl.profile = NULL; if (p->current.type == TOK_INT_LITERAL && p->current.line == directiveLine) { n->data.versionDecl.version = (int)strtol(p->current.value, NULL, 10); GLSLToken tem = consume(p); tokenFree(&tem); } if (p->current.type == TOK_IDENTIFIER && p->current.line == directiveLine) { n->data.versionDecl.profile = p->current.value; p->current.value = NULL; GLSLToken tem = consume(p); tokenFree(&tem); } while (p->current.type != TOK_EOF && p->current.line == directiveLine) { tokenFree(&p->current); p->current = lexerNext(&p->lexer); } tokenFree(&directive); return n; } else if (strcmp(directive.value, "define") == 0) { GlslNode* n = nodeCreate(NODE_MACRO_DEFINE, directive.line, directive.col); n->data.macroDefine.name = NULL; n->data.macroDefine.value = NULL; const char* src = p->lexer.src; int line = directive.line; int lineStart = 0; int curLine = 1; for (int i = 0; i < p->lexer.len && curLine < line; i++) { if (src[i] == '\n') { curLine++; lineStart = i + 1; } } int lineEnd = lineStart; while (lineEnd < p->lexer.len && src[lineEnd] != '\n') lineEnd++; int lineLen = lineEnd - lineStart; char* lineContent = (char*)AllocateMemoryN(1, lineLen + 1, "parseTop"); memcpy(lineContent, src + lineStart, lineLen); lineContent[lineLen] = '\0'; char* p2 = lineContent; while (*p2 && isspace((unsigned char)*p2)) p2++; if (*p2 == '#') p2++; while (*p2 && isspace((unsigned char)*p2)) p2++; while (*p2 && isalpha((unsigned char)*p2)) p2++; while (*p2 && isspace((unsigned char)*p2)) p2++; char* nameStart = p2; while (*p2 && (isalnum((unsigned char)*p2) || *p2 == '_')) p2++; int nameLen = (int)(p2 - nameStart); if (nameLen > 0) { n->data.macroDefine.name = (char*)AllocateMemoryN(1, nameLen + 1, "parseTop"); memcpy(n->data.macroDefine.name, nameStart, nameLen); n->data.macroDefine.name[nameLen] = '\0'; } while (*p2 && isspace((unsigned char)*p2)) p2++; if (*p2 == '(') { int depth = 1; p2++; while (*p2 && depth > 0) { if (*p2 == '(') depth++; else if (*p2 == ')') depth--; p2++; } } while (*p2 && isspace((unsigned char)*p2)) p2++; char* valEnd = p2 + strlen(p2); while (valEnd > p2 && isspace((unsigned char)*(valEnd-1))) valEnd--; int valLen = (int)(valEnd - p2); if (valLen > 0) { n->data.macroDefine.value = (char*)AllocateMemoryN(1, valLen + 1, "parseTop"); memcpy(n->data.macroDefine.value, p2, valLen); n->data.macroDefine.value[valLen] = '\0'; } FreeMemory(lineContent); tokenFree(&directive); while (p->current.line == line && p->current.type != TOK_EOF) { tokenFree(&p->current); p->current = lexerNext(&p->lexer); } return n; } else { GlslNode* n = nodeCreate(NODE_PREPROCESSOR_LINE, directive.line, directive.col); n->data.preprocessorLine.directive = directive.value; n->data.preprocessorLine.argument = NULL; if ((p->current.type == TOK_IDENTIFIER || p->current.type == TOK_INT_LITERAL || p->current.type == TOK_FLOAT_LITERAL) && p->current.line == directiveLine) { n->data.preprocessorLine.argument = p->current.value; p->current.value = NULL; consume(p); } while (p->current.type != TOK_EOF && p->current.line == directiveLine) { tokenFree(&p->current); p->current = lexerNext(&p->lexer); } tokenFree(&directive); return n; } } if (p->current.type == TOK_PRECISION) { GLSLToken precTok = consume(p); tokenFree(&precTok); if (p->current.type != TOK_HIGHP && p->current.type != TOK_MEDIUMP && p->current.type != TOK_LOWP) { parserError(p, "Expected precision qualifier (highp/mediump/lowp)"); return NULL; } GLSLToken qualTok = consume(p); if (!isTypeToken(p->current.type)) { parserError(p, "Expected type name after precision qualifier"); return NULL; } GLSLToken typeTok = consume(p); if (!expect(p, TOK_SEMICOLON, "Expected ';' after precision statement")) { return NULL; } GlslNode* n = nodeCreate(NODE_PRECISION_STMT, precTok.line, precTok.col); n->data.precisionStmt.qualifier = qualTok.value; n->data.precisionStmt.typeName = typeTok.value; return n; } if (p->current.type == TOK_STRUCT) return parseStruct(p); GlslNode* layoutNode = NULL; if (p->current.type == TOK_LAYOUT) { layoutNode = parseLayout(p); } char qualifiers[512] = {0}; int qual_count = 0; while (isQualifierToken(p->current.type) && p->current.type != TOK_LAYOUT) { GLSLToken q = consume(p); if (qual_count > 0) { strcat(qualifiers, " "); } strncat(qualifiers, q.value, sizeof(qualifiers) - strlen(qualifiers) - 1); qual_count++; FreeMemory(q.value); } char* qualifier = NULL; if (qual_count > 0) { qualifier = strDup(qualifiers); } if (p->current.type == TOK_SEMICOLON) { GLSLToken semiTok = consume(p); tokenFree(&semiTok); GlslNode* n = nodeCreate(NODE_LAYOUT_QUALIFIER_DECL, layoutNode ? layoutNode->line : (qualifier ? 1 : 1), layoutNode ? layoutNode->col : 1); n->data.layoutQualifierDecl.layout = layoutNode; n->data.layoutQualifierDecl.qualifier = qualifier; return n; } if (p->current.type == TOK_EOF) { FreeMemory(qualifier); if (layoutNode) nodeFree(layoutNode); return NULL; } GLSLToken typeTok = consume(p); if (!isTypeToken(typeTok.type)) { parserError(p, "Expected type name"); tokenFree(&typeTok); FreeMemory(qualifier); if (layoutNode) nodeFree(layoutNode); return NULL; } if (p->current.type == TOK_LBRACE) { GlslNode* n = nodeCreate(NODE_INTERFACE_BLOCK, typeTok.line, typeTok.col); n->data.interfaceBlock.qualifier = qualifier; n->data.interfaceBlock.blockTypeName = typeTok.value; n->data.interfaceBlock.instanceName = NULL; n->data.interfaceBlock.arraySize = NULL; n->data.interfaceBlock.layout = layoutNode; nodeListInit(&n->data.interfaceBlock.fields); typeTok.value = NULL; GLSLToken lb = consume(p); tokenFree(&lb); while (p->current.type != TOK_RBRACE && p->current.type != TOK_EOF && !p->error) { GLSLToken fType = consume(p); if (p->current.type != TOK_IDENTIFIER) { parserError(p, "Expected field name in interface block"); tokenFree(&fType); break; } GLSLToken fName = consume(p); GlslNode* fArr = parseOptionalArraySize(p); GlslNode* field = nodeCreate(NODE_FIELD_DECL, fType.line, fType.col); field->data.fieldDecl.typeName = fType.value; field->data.fieldDecl.name = fName.value; field->data.fieldDecl.arraySize = fArr; nodeListPush(&n->data.interfaceBlock.fields, field); if (!expect(p, TOK_SEMICOLON, "Expected ';' after field")) { nodeFree(n); tokenFree(&typeTok); return NULL; } } if (!expect(p, TOK_RBRACE, "Expected '}'")) { nodeFree(n); tokenFree(&typeTok); return NULL; } if (p->current.type == TOK_IDENTIFIER) { GLSLToken inst = consume(p); n->data.interfaceBlock.instanceName = inst.value; n->data.interfaceBlock.arraySize = parseOptionalArraySize(p); } if (!expect(p, TOK_SEMICOLON, "Expected ';' after interface block")) { nodeFree(n); tokenFree(&typeTok); return NULL; } return n; } GLSLToken nameTok = {0}; if (p->current.type == TOK_IDENTIFIER) { nameTok = consume(p); } else { parserError(p, "Expected identifier"); tokenFree(&typeTok); FreeMemory(qualifier); if (layoutNode) nodeFree(layoutNode); return NULL; } if (p->current.type == TOK_LPAREN) { GlslNode* fn = parseFunction(p, qualifier, typeTok, nameTok); FreeMemory(qualifier); if (layoutNode) nodeFree(layoutNode); return fn; } int cap = 4; char** names = (char**)AllocateMemoryN(cap, sizeof(char*), "parseTop"); GlslNode** inits = (GlslNode**)AllocateMemoryN(cap, sizeof(GlslNode*), "parseTop"); GlslNode** arrs = (GlslNode**)AllocateMemoryN(cap, sizeof(GlslNode*), "parseTop"); int count = 0; names[0] = nameTok.value; arrs[0] = parseOptionalArraySize(p); if (match(p, TOK_ASSIGN)) { inits[0] = parseExpression(p); if (!inits[0]) { parserError(p, "Invalid initializer"); FreeMemory(names); FreeMemory(inits); FreeMemory(arrs); return NULL; } } count = 1; while (match(p, TOK_COMMA)) { if (count >= cap) { int old_cap = cap; cap *= 2; names = (char**)safeRealloc(names, old_cap, cap, sizeof(char*)); inits = (GlslNode**)safeRealloc(inits, old_cap, cap, sizeof(GlslNode*)); arrs = (GlslNode**)safeRealloc(arrs, old_cap, cap, sizeof(GlslNode*)); } if (p->current.type != TOK_IDENTIFIER) { parserError(p, "Expected variable name"); break; } GLSLToken nTok = consume(p); names[count] = nTok.value; arrs[count] = parseOptionalArraySize(p); inits[count] = NULL; if (match(p, TOK_ASSIGN)) { inits[count] = parseExpression(p); if (!inits[count]) { parserError(p, "Invalid initializer"); for (int i = 0; i < count; i++) { FreeMemory(names[i]); nodeFree(inits[i]); nodeFree(arrs[i]); } FreeMemory(names); FreeMemory(inits); FreeMemory(arrs); return NULL; } } count++; } if (!expect(p, TOK_SEMICOLON, "Expected ';'")) { for (int i = 0; i < count; i++) { FreeMemory(names[i]); nodeFree(inits[i]); nodeFree(arrs[i]); } FreeMemory(names); FreeMemory(inits); FreeMemory(arrs); tokenFree(&typeTok); FreeMemory(qualifier); if (layoutNode) nodeFree(layoutNode); return NULL; } if (count == 1) { GlslNode* n = nodeCreate(NODE_VARIABLE_DECL, typeTok.line, typeTok.col); n->data.varDecl.qualifier = qualifier; n->data.varDecl.typeName = typeTok.value; n->data.varDecl.name = names[0]; n->data.varDecl.initializer = inits[0]; n->data.varDecl.arraySize = arrs[0]; n->data.varDecl.layout = layoutNode; FreeMemory(names); FreeMemory(inits); FreeMemory(arrs); return n; } else { GlslNode* n = nodeCreate(NODE_VARIABLE_DECL_LIST, typeTok.line, typeTok.col); n->data.varDeclList.qualifier = qualifier; n->data.varDeclList.typeName = typeTok.value; n->data.varDeclList.names = names; n->data.varDeclList.initializers = inits; n->data.varDeclList.arraySizes = arrs; n->data.varDeclList.count = count; n->data.varDeclList.layout = layoutNode; return n; } } GlslNode* parseGLSL(const char* src, char* errorMsg, int errorMsgLen) { Parser p; parserInit(&p, src); GlslNode* program = nodeCreate(NODE_PROGRAM, 1, 1); nodeListInit(&program->data.program.declarations); program->data.program.macroExpansions = 0; while (p.current.type != TOK_EOF && !p.error) { GlslNode* decl = parseTopLevel(&p); if (decl) nodeListPush(&program->data.program.declarations, decl); } program->data.program.macroExpansions = p.lexer.macroExpansions; if (p.error) { if (errorMsg && errorMsgLen > 0) { strncpy(errorMsg, p.errorMsg, errorMsgLen - 1); errorMsg[errorMsgLen - 1] = '\0'; } nodeFree(program); parserFree(&p); return NULL; } parserFree(&p); return program; } const char* nodeTypeName(NodeType t) { static const char* names[] = { "PROGRAM", // 0 "FUNCTION_DECL", // 1 "STRUCT_DECL", // 2 "FIELD_DECL", // 3 "VARIABLE_DECL", // 4 "PARAMETER", // 5 "BLOCK", // 6 "ASSIGNMENT", // 7 "BINARY_OP", // 8 "UNARY_OP", // 9 "CALL", // 10 "INDEX", // 11 "MEMBER", // 12 "SWIZZLE", // 13 "IF", // 14 "FOR", // 15 "WHILE", // 16 "RETURN", // 17 ← ИСПРАВЛЕНО "BREAK", // 18 ← ИСПРАВЛЕНО "CONTINUE", // 19 ← ИСПРАВЛЕНО "DISCARD", // 20 ← ИСПРАВЛЕНО "EXPR_STMT", // 21 ← ИСПРАВЛЕНО "IDENTIFIER", // 22 ← ИСПРАВЛЕНО "INT_LITERAL", // 23 "FLOAT_LITERAL", // 24 "BOOL_LITERAL", // 25 "TYPE_REF", // 26 "LAYOUT", // 27 "VARIABLE_DECL_LIST", // 28 "VERSION_DECL", // 29 "MACRO_DEFINE", // 30 "PREPROCESSOR_LINE", // 31 "INTERFACE_BLOCK", // 32 "SWITCH", // 33 "CASE", // 34 "DO_WHILE", // 35 "PRECISION_STMT", // 36 "LAYOUT_QUALIFIER_DECL" // 37 }; if (t < 0 || t >= NODE_COUNT) return "UNKNOWN"; return names[t]; } void printNode(GlslNode* n, int indent) { if (!n) return; for (int i = 0; i < indent; i++) printf(" "); printf("[%s] ", nodeTypeName(n->type)); switch (n->type) { case NODE_PROGRAM: printf(" (%d decls)\n", NL_COUNT(&n->data.program.declarations)); for (int i = 0; i < NL_COUNT(&n->data.program.declarations); i++) printNode(NL_ITEMS(&n->data.program.declarations)[i], indent + 1); break; case NODE_FUNCTION_DECL: printf("%s %s(", n->data.funcDecl.returnType, n->data.funcDecl.name); for (int i = 0; i < NL_COUNT(&n->data.funcDecl.parameters); i++) { GlslNode* param = NL_ITEMS(&n->data.funcDecl.parameters)[i]; if (i > 0) printf(", "); if (param->data.parameter.qualifier) printf("%s ", param->data.parameter.qualifier); printf("%s %s", param->data.parameter.typeName, param->data.parameter.name); if (param->data.parameter.arraySize) { printf("["); printNode(param->data.parameter.arraySize, 0); printf("]"); } } printf(")\n"); printNode(n->data.funcDecl.body, indent + 1); break; case NODE_STRUCT_DECL: printf("%s\n", n->data.structDecl.name); for (int i = 0; i < NL_COUNT(&n->data.structDecl.fields); i++) printNode(NL_ITEMS(&n->data.structDecl.fields)[i], indent + 1); break; case NODE_FIELD_DECL: printf("%s %s", n->data.fieldDecl.typeName, n->data.fieldDecl.name); if (n->data.fieldDecl.arraySize) { printf("["); printNode(n->data.fieldDecl.arraySize, 0); printf("]"); } printf(";\n"); break; case NODE_VARIABLE_DECL: printf(" "); if (n->data.varDecl.qualifier) printf("%s ", n->data.varDecl.qualifier); printf("%s %s", n->data.varDecl.typeName, n->data.varDecl.name); if (n->data.varDecl.arraySize) { printf("["); printNode(n->data.varDecl.arraySize, 0); printf("]"); } if (n->data.varDecl.initializer) { printf(" = "); printNode(n->data.varDecl.initializer, 0); printf(";\n"); } else printf(";\n"); break; case NODE_PARAMETER: printf(" "); if (n->data.parameter.qualifier) printf("%s ", n->data.parameter.qualifier); printf("%s %s", n->data.parameter.typeName, n->data.parameter.name); if (n->data.parameter.arraySize) { printf("["); printNode(n->data.parameter.arraySize, 0); printf("]"); } printf("\n"); break; case NODE_BLOCK: printf("\n"); for (int i = 0; i < NL_COUNT(&n->data.block.statements); i++) printNode(NL_ITEMS(&n->data.block.statements)[i], indent + 1); break; case NODE_ASSIGNMENT: printf(" "); printNode(n->data.assignment.target, 0); printf(" %s ", n->data.assignment.op); printNode(n->data.assignment.value, 0); printf("\n"); break; case NODE_BINARY_OP: printf("%s\n", n->data.binaryOp.op); printNode(n->data.binaryOp.left, indent + 1); printNode(n->data.binaryOp.right, indent + 1); break; case NODE_UNARY_OP: printf("%s (prefix=%s)\n", n->data.unaryOp.op, n->data.unaryOp.isPrefix ? "true" : "false"); printNode(n->data.unaryOp.operand, indent + 1); break; case NODE_CALL: printf("%s(\n", n->data.call.name); for (int i = 0; i < NL_COUNT(&n->data.call.arguments); i++) printNode(NL_ITEMS(&n->data.call.arguments)[i], indent + 1); for (int i = 0; i < indent; i++) printf(" "); printf(" )\n"); break; case NODE_INDEX: printf(" [index]\n"); printNode(n->data.index.object, indent + 1); printNode(n->data.index.index, indent + 1); break; case NODE_MEMBER: printf(" .%s\n", n->data.member.member); printNode(n->data.member.object, indent + 1); break; case NODE_SWIZZLE: printf(" .%s\n", n->data.swizzle.mask); printNode(n->data.swizzle.object, indent + 1); break; case NODE_IF: printf("\n"); for (int i = 0; i <= indent; i++) printf(" "); printf("cond: "); printNode(n->data.ifStmt.condition, 0); printf("\n"); for (int i = 0; i <= indent; i++) printf(" "); printf("then:\n"); printNode(n->data.ifStmt.thenBranch, indent + 2); if (n->data.ifStmt.elseBranch) { for (int i = 0; i <= indent; i++) printf(" "); printf("else:\n"); printNode(n->data.ifStmt.elseBranch, indent + 2); } break; case NODE_FOR: printf("\n"); for (int i = 0; i <= indent; i++) printf(" "); printf("init:\n"); if (n->data.forStmt.init) printNode(n->data.forStmt.init, indent + 2); for (int i = 0; i <= indent; i++) printf(" "); printf("cond:\n"); if (n->data.forStmt.condition) printNode(n->data.forStmt.condition, indent + 2); for (int i = 0; i <= indent; i++) printf(" "); printf("incr:\n"); if (n->data.forStmt.increment) printNode(n->data.forStmt.increment, indent + 2); for (int i = 0; i <= indent; i++) printf(" "); printf("body:\n"); printNode(n->data.forStmt.body, indent + 2); break; case NODE_WHILE: printf("\n"); for (int i = 0; i <= indent; i++) printf(" "); printf("cond:\n"); printNode(n->data.whileStmt.condition, indent + 2); for (int i = 0; i <= indent; i++) printf(" "); printf("body:\n"); printNode(n->data.whileStmt.body, indent + 2); break; case NODE_DO_WHILE: printf("\n"); for (int i = 0; i <= indent; i++) printf(" "); printf("body:\n"); printNode(n->data.doWhileStmt.body, indent + 2); for (int i = 0; i <= indent; i++) printf(" "); printf("while: "); printNode(n->data.doWhileStmt.condition, 0); printf("\n"); break; case NODE_SWITCH: printf("\n"); for (int i = 0; i <= indent; i++) printf(" "); printf("cond: "); printNode(n->data.switchStmt.condition, 0); printf("\n"); for (int i = 0; i < NL_COUNT(&n->data.switchStmt.cases); i++) printNode(NL_ITEMS(&n->data.switchStmt.cases)[i], indent + 1); break; case NODE_CASE: if (n->data.caseStmt.isDefault) { printf(" default:\n"); } else { printf(" case "); for (int i = 0; i < NL_COUNT(&n->data.caseStmt.labels); i++) { if (i > 0) printf(", "); printNode(NL_ITEMS(&n->data.caseStmt.labels)[i], 0); } printf(":\n"); } for (int i = 0; i < NL_COUNT(&n->data.caseStmt.statements); i++) printNode(NL_ITEMS(&n->data.caseStmt.statements)[i], indent + 1); break; case NODE_PRECISION_STMT: printf(" precision %s %s;\n", n->data.precisionStmt.qualifier, n->data.precisionStmt.typeName); break; case NODE_RETURN: if (n->data.returnStmt.value) { printf(" "); printNode(n->data.returnStmt.value, 0); printf("\n"); } else printf("\n"); break; case NODE_BREAK: printf(" break;\n"); break; case NODE_CONTINUE: printf(" continue;\n"); break; case NODE_DISCARD: printf(" discard;\n"); break; case NODE_EXPR_STMT: printf("\n"); printNode(n->data.exprStmt.expression, indent + 1); break; case NODE_IDENTIFIER: printf(" %s\n", n->data.identifier.name); break; case NODE_INT_LITERAL: printf(" %ld\n", n->data.intLiteral.value); break; case NODE_FLOAT_LITERAL: printf(" %g\n", n->data.floatLiteral.value); break; case NODE_BOOL_LITERAL: printf(" %s\n", n->data.boolLiteral.value ? "true" : "false"); break; case NODE_LAYOUT: printf(" ("); for (int i = 0; i < n->data.layout.count; i++) { if (i > 0) printf(", "); printf("%s", n->data.layout.keys[i]); if (n->data.layout.values[i]) printf("=%s", n->data.layout.values[i]); } printf(")\n"); break; case NODE_VARIABLE_DECL_LIST: printf(" "); if (n->data.varDeclList.qualifier) printf("%s ", n->data.varDeclList.qualifier); printf("%s ", n->data.varDeclList.typeName); for (int i = 0; i < n->data.varDeclList.count; i++) { printf(" %s", n->data.varDeclList.names[i]); if (n->data.varDeclList.arraySizes && n->data.varDeclList.arraySizes[i]) { printf("["); printNode(n->data.varDeclList.arraySizes[i], 0); printf("]"); } if (n->data.varDeclList.initializers && n->data.varDeclList.initializers[i]) { printf(" = "); printNode(n->data.varDeclList.initializers[i], 0); } if (i < n->data.varDeclList.count - 1) printf(","); } printf(";\n"); break; case NODE_VERSION_DECL: printf(" %d ", n->data.versionDecl.version); if (n->data.versionDecl.profile) printf(" %s ", n->data.versionDecl.profile); printf("\n"); break; case NODE_MACRO_DEFINE: printf(" %s ", n->data.macroDefine.name ? n->data.macroDefine.name : "(unnamed)"); if (n->data.macroDefine.value) printf(" = %s ", n->data.macroDefine.value); printf("\n"); break; case NODE_PREPROCESSOR_LINE: printf(" #%s %s\n", n->data.preprocessorLine.directive, n->data.preprocessorLine.argument ? n->data.preprocessorLine.argument : ""); break; case NODE_INTERFACE_BLOCK: printf(" [INTERFACE] "); if (n->data.interfaceBlock.layout) { printf(" layout("); GlslNode* lay = n->data.interfaceBlock.layout; for (int i = 0; i < lay->data.layout.count; i++) { if (i > 0) printf(", "); printf("%s", lay->data.layout.keys[i]); if (lay->data.layout.values[i]) printf("=%s", lay->data.layout.values[i]); } printf(") "); } if (n->data.interfaceBlock.qualifier) printf(" %s ", n->data.interfaceBlock.qualifier); printf(" %s {\n", n->data.interfaceBlock.blockTypeName); for (int i = 0; i < NL_COUNT(&n->data.interfaceBlock.fields); i++) printNode(NL_ITEMS(&n->data.interfaceBlock.fields)[i], indent + 1); for (int i = 0; i < indent; i++) printf(" "); printf(" } "); if (n->data.interfaceBlock.instanceName) { printf(" %s ", n->data.interfaceBlock.instanceName); if (n->data.interfaceBlock.arraySize) { printf("["); printNode(n->data.interfaceBlock.arraySize, 0); printf("]"); } } printf(";\n"); break; case NODE_LAYOUT_QUALIFIER_DECL: printf(" "); if (n->data.layoutQualifierDecl.layout) { printf("layout("); GlslNode* lay = n->data.layoutQualifierDecl.layout; for (int i = 0; i < lay->data.layout.count; i++) { if (i > 0) printf(", "); printf("%s", lay->data.layout.keys[i]); if (lay->data.layout.values[i]) printf("=%s", lay->data.layout.values[i]); } printf(") "); } if (n->data.layoutQualifierDecl.qualifier) printf(" %s ", n->data.layoutQualifierDecl.qualifier); printf(";\n"); break; default: printf("\n"); break; } } int countNodes(GlslNode* n) { if (!n) return 0; int count = 1; switch (n->type) { case NODE_PROGRAM: for (int i = 0; i < NL_COUNT(&n->data.program.declarations); i++) count += countNodes(NL_ITEMS(&n->data.program.declarations)[i]); break; case NODE_FUNCTION_DECL: for (int i = 0; i < NL_COUNT(&n->data.funcDecl.parameters); i++) count += countNodes(NL_ITEMS(&n->data.funcDecl.parameters)[i]); count += countNodes(n->data.funcDecl.body); break; case NODE_STRUCT_DECL: for (int i = 0; i < NL_COUNT(&n->data.structDecl.fields); i++) count += countNodes(NL_ITEMS(&n->data.structDecl.fields)[i]); break; case NODE_BLOCK: for (int i = 0; i < NL_COUNT(&n->data.block.statements); i++) count += countNodes(NL_ITEMS(&n->data.block.statements)[i]); break; case NODE_ASSIGNMENT: count += countNodes(n->data.assignment.target); count += countNodes(n->data.assignment.value); break; case NODE_BINARY_OP: count += countNodes(n->data.binaryOp.left); count += countNodes(n->data.binaryOp.right); break; case NODE_UNARY_OP: count += countNodes(n->data.unaryOp.operand); break; case NODE_CALL: for (int i = 0; i < NL_COUNT(&n->data.call.arguments); i++) count += countNodes(NL_ITEMS(&n->data.call.arguments)[i]); break; case NODE_INDEX: count += countNodes(n->data.index.object); count += countNodes(n->data.index.index); break; case NODE_MEMBER: count += countNodes(n->data.member.object); break; case NODE_SWIZZLE: count += countNodes(n->data.swizzle.object); break; case NODE_IF: count += countNodes(n->data.ifStmt.condition); count += countNodes(n->data.ifStmt.thenBranch); count += countNodes(n->data.ifStmt.elseBranch); break; case NODE_FOR: count += countNodes(n->data.forStmt.init); count += countNodes(n->data.forStmt.condition); count += countNodes(n->data.forStmt.increment); count += countNodes(n->data.forStmt.body); break; case NODE_WHILE: count += countNodes(n->data.whileStmt.condition); count += countNodes(n->data.whileStmt.body); break; case NODE_DO_WHILE: count += countNodes(n->data.doWhileStmt.body); count += countNodes(n->data.doWhileStmt.condition); break; case NODE_SWITCH: count += countNodes(n->data.switchStmt.condition); for (int i = 0; i < NL_COUNT(&n->data.switchStmt.cases); i++) count += countNodes(NL_ITEMS(&n->data.switchStmt.cases)[i]); break; case NODE_CASE: for (int i = 0; i < NL_COUNT(&n->data.caseStmt.labels); i++) count += countNodes(NL_ITEMS(&n->data.caseStmt.labels)[i]); for (int i = 0; i < NL_COUNT(&n->data.caseStmt.statements); i++) count += countNodes(NL_ITEMS(&n->data.caseStmt.statements)[i]); break; case NODE_RETURN: count += countNodes(n->data.returnStmt.value); break; case NODE_EXPR_STMT: count += countNodes(n->data.exprStmt.expression); break; case NODE_VARIABLE_DECL: count += countNodes(n->data.varDecl.initializer); count += countNodes(n->data.varDecl.arraySize); break; case NODE_FIELD_DECL: count += countNodes(n->data.fieldDecl.arraySize); break; case NODE_PARAMETER: count += countNodes(n->data.parameter.arraySize); break; case NODE_VARIABLE_DECL_LIST: for (int i = 0; i < n->data.varDeclList.count; i++) { count += countNodes(n->data.varDeclList.initializers[i]); count += countNodes(n->data.varDeclList.arraySizes[i]); } break; case NODE_INTERFACE_BLOCK: for (int i = 0; i < NL_COUNT(&n->data.interfaceBlock.fields); i++) count += countNodes(NL_ITEMS(&n->data.interfaceBlock.fields)[i]); count += countNodes(n->data.interfaceBlock.arraySize); count += countNodes(n->data.interfaceBlock.layout); break; case NODE_LAYOUT_QUALIFIER_DECL: count += countNodes(n->data.layoutQualifierDecl.layout); break; default: break; } return count; } bool isSwizzleMask(const char* s) { if (!s || !*s) return false; bool hasXYZW = false, hasRGBA = false, hasSTPQ = false; for (const char* p = s; *p; p++) { if (strchr("xyzw", *p)) hasXYZW = true; else if (strchr("rgba", *p)) hasRGBA = true; else if (strchr("stpq", *p)) hasSTPQ = true; else return false; } int sets = (hasXYZW ? 1 : 0) + (hasRGBA ? 1 : 0) + (hasSTPQ ? 1 : 0); return sets == 1 && strlen(s) <= 4; } void collectStats(GlslNode* n, Stats* s) { if (!n) return; switch (n->type) { case NODE_PROGRAM: for (int i = 0; i < NL_COUNT(&n->data.program.declarations); i++) collectStats(NL_ITEMS(&n->data.program.declarations)[i], s); break; case NODE_STRUCT_DECL: s->structs++; for (int i = 0; i < NL_COUNT(&n->data.structDecl.fields); i++) collectStats(NL_ITEMS(&n->data.structDecl.fields)[i], s); break; case NODE_FIELD_DECL: s->fields++; collectStats(n->data.fieldDecl.arraySize, s); break; case NODE_FUNCTION_DECL: s->functions++; for (int i = 0; i < NL_COUNT(&n->data.funcDecl.parameters); i++) collectStats(NL_ITEMS(&n->data.funcDecl.parameters)[i], s); collectStats(n->data.funcDecl.body, s); break; case NODE_PARAMETER: s->parameters++; collectStats(n->data.parameter.arraySize, s); break; case NODE_VARIABLE_DECL: if (n->data.varDecl.qualifier && (strcmp(n->data.varDecl.qualifier, "uniform") == 0 || strcmp(n->data.varDecl.qualifier, "in") == 0 || strcmp(n->data.varDecl.qualifier, "out") == 0 || strcmp(n->data.varDecl.qualifier, "varying") == 0 || strcmp(n->data.varDecl.qualifier, "attribute") == 0 || strcmp(n->data.varDecl.qualifier, "const") == 0)) s->globalVars++; else s->localVars++; collectStats(n->data.varDecl.initializer, s); collectStats(n->data.varDecl.arraySize, s); break; case NODE_VARIABLE_DECL_LIST: for (int i = 0; i < n->data.varDeclList.count; i++) { if (n->data.varDeclList.qualifier && (strcmp(n->data.varDeclList.qualifier, "uniform") == 0 || strcmp(n->data.varDeclList.qualifier, "in") == 0 || strcmp(n->data.varDeclList.qualifier, "out") == 0)) s->globalVars++; else s->localVars++; collectStats(n->data.varDeclList.initializers[i], s); collectStats(n->data.varDeclList.arraySizes[i], s); } break; case NODE_BLOCK: s->blocks++; for (int i = 0; i < NL_COUNT(&n->data.block.statements); i++) collectStats(NL_ITEMS(&n->data.block.statements)[i], s); break; case NODE_ASSIGNMENT: s->assignments++; collectStats(n->data.assignment.target, s); collectStats(n->data.assignment.value, s); break; case NODE_BINARY_OP: s->binaryOps++; collectStats(n->data.binaryOp.left, s); collectStats(n->data.binaryOp.right, s); break; case NODE_UNARY_OP: s->unaryOps++; collectStats(n->data.unaryOp.operand, s); break; case NODE_CALL: s->calls++; for (int i = 0; i < NL_COUNT(&n->data.call.arguments); i++) collectStats(NL_ITEMS(&n->data.call.arguments)[i], s); break; case NODE_INDEX: s->indexes++; collectStats(n->data.index.object, s); collectStats(n->data.index.index, s); break; case NODE_MEMBER: s->members++; collectStats(n->data.member.object, s); break; case NODE_SWIZZLE: s->swizzles++; collectStats(n->data.swizzle.object, s); break; case NODE_IF: s->ifs++; collectStats(n->data.ifStmt.condition, s); collectStats(n->data.ifStmt.thenBranch, s); collectStats(n->data.ifStmt.elseBranch, s); break; case NODE_FOR: s->fors++; collectStats(n->data.forStmt.init, s); collectStats(n->data.forStmt.condition, s); collectStats(n->data.forStmt.increment, s); collectStats(n->data.forStmt.body, s); break; case NODE_WHILE: s->whiles++; collectStats(n->data.whileStmt.condition, s); collectStats(n->data.whileStmt.body, s); break; case NODE_DO_WHILE: s->doWhiles++; collectStats(n->data.doWhileStmt.body, s); collectStats(n->data.doWhileStmt.condition, s); break; case NODE_SWITCH: s->switches++; collectStats(n->data.switchStmt.condition, s); for (int i = 0; i < NL_COUNT(&n->data.switchStmt.cases); i++) collectStats(NL_ITEMS(&n->data.switchStmt.cases)[i], s); break; case NODE_CASE: s->cases++; for (int i = 0; i < NL_COUNT(&n->data.caseStmt.labels); i++) collectStats(NL_ITEMS(&n->data.caseStmt.labels)[i], s); for (int i = 0; i < NL_COUNT(&n->data.caseStmt.statements); i++) collectStats(NL_ITEMS(&n->data.caseStmt.statements)[i], s); break; case NODE_PRECISION_STMT: s->precisionStmts++; break; case NODE_RETURN: s->returns++; collectStats(n->data.returnStmt.value, s); break; case NODE_BREAK: s->breaks++; break; case NODE_CONTINUE: s->continues++; break; case NODE_DISCARD: s->discards++; break; case NODE_EXPR_STMT: collectStats(n->data.exprStmt.expression, s); break; case NODE_IDENTIFIER: s->identifiers++; break; case NODE_INT_LITERAL: s->intLiterals++; break; case NODE_FLOAT_LITERAL: s->floatLiterals++; break; case NODE_BOOL_LITERAL: s->boolLiterals++; break; case NODE_LAYOUT: s->layouts++; break; case NODE_VERSION_DECL: case NODE_MACRO_DEFINE: s->macros++; break; case NODE_INTERFACE_BLOCK: s->interfaceBlocks++; s->globalVars++; for (int i = 0; i < NL_COUNT(&n->data.interfaceBlock.fields); i++) collectStats(NL_ITEMS(&n->data.interfaceBlock.fields)[i], s); collectStats(n->data.interfaceBlock.arraySize, s); break; case NODE_LAYOUT_QUALIFIER_DECL: s->layouts++; collectStats(n->data.layoutQualifierDecl.layout, s); break; default: break; } } void collectCallNames(GlslNode* n, char*** names, int* count, int* cap) { if (!n) return; switch (n->type) { case NODE_CALL: if (*count >= *cap) { int old_cap = *cap; *cap = *cap == 0 ? 8 : *cap * 2; *names = (char**)safeRealloc(*names, old_cap, *cap, sizeof(char*)); } (*names)[(*count)++] = n->data.call.name; for (int i = 0; i < NL_COUNT(&n->data.call.arguments); i++) collectCallNames(NL_ITEMS(&n->data.call.arguments)[i], names, count, cap); break; case NODE_PROGRAM: for (int i = 0; i < NL_COUNT(&n->data.program.declarations); i++) collectCallNames(NL_ITEMS(&n->data.program.declarations)[i], names, count, cap); break; case NODE_FUNCTION_DECL: collectCallNames(n->data.funcDecl.body, names, count, cap); break; case NODE_BLOCK: for (int i = 0; i < NL_COUNT(&n->data.block.statements); i++) collectCallNames(NL_ITEMS(&n->data.block.statements)[i], names, count, cap); break; case NODE_ASSIGNMENT: collectCallNames(n->data.assignment.target, names, count, cap); collectCallNames(n->data.assignment.value, names, count, cap); break; case NODE_BINARY_OP: collectCallNames(n->data.binaryOp.left, names, count, cap); collectCallNames(n->data.binaryOp.right, names, count, cap); break; case NODE_UNARY_OP: collectCallNames(n->data.unaryOp.operand, names, count, cap); break; case NODE_IF: collectCallNames(n->data.ifStmt.condition, names, count, cap); collectCallNames(n->data.ifStmt.thenBranch, names, count, cap); collectCallNames(n->data.ifStmt.elseBranch, names, count, cap); break; case NODE_FOR: collectCallNames(n->data.forStmt.init, names, count, cap); collectCallNames(n->data.forStmt.condition, names, count, cap); collectCallNames(n->data.forStmt.increment, names, count, cap); collectCallNames(n->data.forStmt.body, names, count, cap); break; case NODE_WHILE: collectCallNames(n->data.whileStmt.condition, names, count, cap); collectCallNames(n->data.whileStmt.body, names, count, cap); break; case NODE_DO_WHILE: collectCallNames(n->data.doWhileStmt.body, names, count, cap); collectCallNames(n->data.doWhileStmt.condition, names, count, cap); break; case NODE_SWITCH: collectCallNames(n->data.switchStmt.condition, names, count, cap); for (int i = 0; i < NL_COUNT(&n->data.switchStmt.cases); i++) collectCallNames(NL_ITEMS(&n->data.switchStmt.cases)[i], names, count, cap); break; case NODE_CASE: for (int i = 0; i < NL_COUNT(&n->data.caseStmt.labels); i++) collectCallNames(NL_ITEMS(&n->data.caseStmt.labels)[i], names, count, cap); for (int i = 0; i < NL_COUNT(&n->data.caseStmt.statements); i++) collectCallNames(NL_ITEMS(&n->data.caseStmt.statements)[i], names, count, cap); break; case NODE_RETURN: collectCallNames(n->data.returnStmt.value, names, count, cap); break; case NODE_EXPR_STMT: collectCallNames(n->data.exprStmt.expression, names, count, cap); break; case NODE_VARIABLE_DECL: collectCallNames(n->data.varDecl.initializer, names, count, cap); break; case NODE_VARIABLE_DECL_LIST: for (int i = 0; i < n->data.varDeclList.count; i++) collectCallNames(n->data.varDeclList.initializers[i], names, count, cap); break; case NODE_SWIZZLE: collectCallNames(n->data.swizzle.object, names, count, cap); break; case NODE_MEMBER: collectCallNames(n->data.member.object, names, count, cap); break; case NODE_INDEX: collectCallNames(n->data.index.object, names, count, cap); collectCallNames(n->data.index.index, names, count, cap); break; case NODE_INTERFACE_BLOCK: for (int i = 0; i < NL_COUNT(&n->data.interfaceBlock.fields); i++) collectCallNames(NL_ITEMS(&n->data.interfaceBlock.fields)[i], names, count, cap); break; case NODE_LAYOUT_QUALIFIER_DECL: collectCallNames(n->data.layoutQualifierDecl.layout, names, count, cap); break; default: break; } } void collectIdentifiers(GlslNode* n, IdCount** ids, int* count, int* cap) { if (!n) return; if (n->type == NODE_IDENTIFIER) { int found = 0; for (int i = 0; i < *count; i++) { if (strcmp((*ids)[i].name, n->data.identifier.name) == 0) { (*ids)[i].count++; found = 1; break; } } if (!found) { if (*count >= *cap) { int old_cap = *cap; *cap = *cap == 0 ? 16 : *cap * 2; *ids = (IdCount*)safeRealloc(*ids, old_cap, *cap, sizeof(IdCount)); } (*ids)[*count].name = n->data.identifier.name; (*ids)[*count].count = 1; (*count)++; } } switch (n->type) { case NODE_PROGRAM: for (int i = 0; i < NL_COUNT(&n->data.program.declarations); i++) collectIdentifiers(NL_ITEMS(&n->data.program.declarations)[i], ids, count, cap); break; case NODE_FUNCTION_DECL: for (int i = 0; i < NL_COUNT(&n->data.funcDecl.parameters); i++) collectIdentifiers(NL_ITEMS(&n->data.funcDecl.parameters)[i], ids, count, cap); collectIdentifiers(n->data.funcDecl.body, ids, count, cap); break; case NODE_STRUCT_DECL: for (int i = 0; i < NL_COUNT(&n->data.structDecl.fields); i++) collectIdentifiers(NL_ITEMS(&n->data.structDecl.fields)[i], ids, count, cap); break; case NODE_BLOCK: for (int i = 0; i < NL_COUNT(&n->data.block.statements); i++) collectIdentifiers(NL_ITEMS(&n->data.block.statements)[i], ids, count, cap); break; case NODE_ASSIGNMENT: collectIdentifiers(n->data.assignment.target, ids, count, cap); collectIdentifiers(n->data.assignment.value, ids, count, cap); break; case NODE_BINARY_OP: collectIdentifiers(n->data.binaryOp.left, ids, count, cap); collectIdentifiers(n->data.binaryOp.right, ids, count, cap); break; case NODE_UNARY_OP: collectIdentifiers(n->data.unaryOp.operand, ids, count, cap); break; case NODE_CALL: for (int i = 0; i < NL_COUNT(&n->data.call.arguments); i++) collectIdentifiers(NL_ITEMS(&n->data.call.arguments)[i], ids, count, cap); break; case NODE_MEMBER: collectIdentifiers(n->data.member.object, ids, count, cap); break; case NODE_SWIZZLE: collectIdentifiers(n->data.swizzle.object, ids, count, cap); break; case NODE_INDEX: collectIdentifiers(n->data.index.object, ids, count, cap); collectIdentifiers(n->data.index.index, ids, count, cap); break; case NODE_IF: collectIdentifiers(n->data.ifStmt.condition, ids, count, cap); collectIdentifiers(n->data.ifStmt.thenBranch, ids, count, cap); collectIdentifiers(n->data.ifStmt.elseBranch, ids, count, cap); break; case NODE_FOR: collectIdentifiers(n->data.forStmt.init, ids, count, cap); collectIdentifiers(n->data.forStmt.condition, ids, count, cap); collectIdentifiers(n->data.forStmt.increment, ids, count, cap); collectIdentifiers(n->data.forStmt.body, ids, count, cap); break; case NODE_WHILE: collectIdentifiers(n->data.whileStmt.condition, ids, count, cap); collectIdentifiers(n->data.whileStmt.body, ids, count, cap); break; case NODE_DO_WHILE: collectIdentifiers(n->data.doWhileStmt.body, ids, count, cap); collectIdentifiers(n->data.doWhileStmt.condition, ids, count, cap); break; case NODE_SWITCH: collectIdentifiers(n->data.switchStmt.condition, ids, count, cap); for (int i = 0; i < NL_COUNT(&n->data.switchStmt.cases); i++) collectIdentifiers(NL_ITEMS(&n->data.switchStmt.cases)[i], ids, count, cap); break; case NODE_CASE: for (int i = 0; i < NL_COUNT(&n->data.caseStmt.labels); i++) collectIdentifiers(NL_ITEMS(&n->data.caseStmt.labels)[i], ids, count, cap); for (int i = 0; i < NL_COUNT(&n->data.caseStmt.statements); i++) collectIdentifiers(NL_ITEMS(&n->data.caseStmt.statements)[i], ids, count, cap); break; case NODE_RETURN: collectIdentifiers(n->data.returnStmt.value, ids, count, cap); break; case NODE_EXPR_STMT: collectIdentifiers(n->data.exprStmt.expression, ids, count, cap); break; case NODE_VARIABLE_DECL: collectIdentifiers(n->data.varDecl.initializer, ids, count, cap); break; case NODE_VARIABLE_DECL_LIST: for (int i = 0; i < n->data.varDeclList.count; i++) collectIdentifiers(n->data.varDeclList.initializers[i], ids, count, cap); break; case NODE_INTERFACE_BLOCK: for (int i = 0; i < NL_COUNT(&n->data.interfaceBlock.fields); i++) collectIdentifiers(NL_ITEMS(&n->data.interfaceBlock.fields)[i], ids, count, cap); break; case NODE_LAYOUT_QUALIFIER_DECL: collectIdentifiers(n->data.layoutQualifierDecl.layout, ids, count, cap); break; default: break; } }