X-Git-Url: https://git.xonotic.org/?a=blobdiff_plain;f=parse.c;h=cc228ed1b9c268970de55f072a89d8c9e0352a5a;hb=29a3c02c354a4f6056c0e311a42293f4dc639fd8;hp=939831949df4b6c98107f140ce7c2e747c0050e0;hpb=e6a270a7e00abd3bcc1b587594e21ed75e341f43;p=xonotic%2Fgmqcc.git diff --git a/parse.c b/parse.c index 9398319..cc228ed 100644 --- a/parse.c +++ b/parse.c @@ -21,22 +21,25 @@ * SOFTWARE. */ #include +#include +#include +#include #include "gmqcc.h" +/* compile-time constant for type constants */ +typedef struct { + char *name; + int type; + float value[3]; + char *string; /* string value if constant is string literal */ +} constant; +VECTOR_MAKE(constant, compile_constants); -static const char *const parse_punct[] = { - "&&", "||", "<=", ">=", "==", "!=", ";", ",", "!", "*", - "/" , "(" , "-" , "+" , "=" , "[" , "]", "{", "}", "...", - "." , "<" , ">" , "#" , "&" , "|" , "$", "@", ":", NULL - /* - * $,@,: are extensions: - * $ is a shorter `self`, so instead of self.frags, $.frags - * @ is a constructor - * : is compiler builtin functions - */ -}; - -int parse(struct lex_file *file) { +/* + * Generates a parse tree out of the lexees generated by the lexer. This + * is where the tree is built. This is where valid check is performed. + */ +int parse_gen(struct lex_file *file) { int token = 0; while ((token = lex_token(file)) != ERROR_LEX && \ token != ERROR_COMPILER && \ @@ -44,100 +47,237 @@ int parse(struct lex_file *file) { token != ERROR_PARSE && \ token != ERROR_PREPRO && file->length >= 0) { switch (token) { - case TOKEN_IF: + case TOKEN_TYPEDEF: { + char *f; /* from */ + char *t; /* to */ + + token = lex_token(file); + token = lex_token(file); f = util_strdup(file->lastok); + token = lex_token(file); + token = lex_token(file); t = util_strdup(file->lastok); + + typedef_add(f, t); + mem_d(f); + mem_d(t); + token = lex_token(file); - while ((token == ' ' || token == '\n') && file->length >= 0) + if (token == ' ') token = lex_token(file); - if (token != '(') - error(ERROR_PARSE, "Expected `(` after if\n", ""); - break; - - /* TODO: Preprocessor */ - case '#': - token = lex_token(file); - token = lex_token(file); - token = lex_token(file); - token = lex_token(file); - token = lex_token(file); + if (token != ';') + error(ERROR_PARSE, "%s:%d Expected a `;` at end of typedef statement\n", file->name, file->line); + token = lex_token(file); break; + } + + case TOKEN_VOID: goto fall; + case TOKEN_STRING: goto fall; + case TOKEN_VECTOR: goto fall; + case TOKEN_ENTITY: goto fall; + case TOKEN_FLOAT: goto fall; + { + fall:; + char *name = NULL; + int type = token; /* story copy */ - /* PUNCTUATION PARSING BEGINS */ - case '&': /* & */ + /* skip over space */ token = lex_token(file); - if (token == '&') { /* && */ + if (token == ' ') token = lex_token(file); - printf("--> LOGICAL AND\n"); - goto end; - } - printf("--> BITWISE AND\n"); - break; - case '|': /* | */ - token = lex_token(file); - if (token == '|') { /* || */ - token = lex_token(file); - printf("--> LOGICAL OR\n"); - goto end; - } - printf("--> BITWISE OR\n"); - break; - case '!': + + /* save name */ + name = util_strdup(file->lastok); + + /* skip spaces */ token = lex_token(file); - if (token == '=') { /* != */ + if (token == ' ') token = lex_token(file); - printf("--> LOGICAL NOT EQUAL\n"); - goto end; - } - printf("--> LOGICAL NOT\n"); - break; - case '<': /* < */ - token = lex_token(file); - if (token == '=') { /* <= */ + + if (token == ';') { + /* + * Definitions go to the defs table, they don't have + * any sort of data with them yet. + */ + } else if (token == '=') { token = lex_token(file); - printf("--> LESS THAN OR EQUALL\n"); - goto end; + if (token == ' ') + token = lex_token(file); + + /* strings are in file->lastok */ + switch (type) { + case TOKEN_VOID: + return error(ERROR_PARSE, "%s:%d Cannot assign value to type void\n", file->name, file->line); + + /* TODO: Validate (end quote), strip quotes for constant add, name constant */ + case TOKEN_STRING: + if (*file->lastok != '"') + error(ERROR_PARSE, "%s:%d Expected a '\"' (quote) for string constant\n", file->name, file->line); + /* add the compile-time constant */ + compile_constants_add((constant){ + .name = util_strdup(name), + .type = TYPE_STRING, + .value = {0,0,0}, + .string = util_strdup(file->lastok) + }); + break; + /* TODO: name constant, old qc vec literals, whitespace fixes, name constant */ + case TOKEN_VECTOR: { + float compile_calc_x = 0; + float compile_calc_y = 0; + float compile_calc_z = 0; + int compile_calc_d = 0; /* dot? */ + int compile_calc_s = 0; /* sign (-, +) */ + + char compile_data[1024]; + char *compile_eval = compile_data; + + if (token != '{') + error(ERROR_PARSE, "%s:%d Expected initializer list `{`,`}` for vector constant\n", file->name, file->line); + + /* + * This parses a single vector element: x,y & z. This will handle all the + * complicated mechanics of a vector, and can be extended as well. This + * is a rather large macro, and is #undef'd after it's use below. + */ + #define PARSE_VEC_ELEMENT(NAME, BIT) \ + token = lex_token(file); \ + if (token == ' ') \ + token = lex_token(file); \ + if (token == '.') \ + compile_calc_d = 1; \ + if (!isdigit(token) && !compile_calc_d && token != '+' && token != '-') \ + error(ERROR_PARSE,"%s:%d Invalid constant initializer element %c for vector, must be numeric\n", file->name, file->line, NAME); \ + if (token == '+') \ + compile_calc_s = '+'; \ + if (token == '-' && !compile_calc_s) \ + compile_calc_s = '-'; \ + while (isdigit(token) || token == '.' || token == '+' || token == '-') { \ + *compile_eval++ = token; \ + token = lex_token(file); \ + if (token == '.' && compile_calc_d) { \ + error(ERROR_PARSE, "%s:%d Invalid constant initializer element %c for vector, must be numeric.\n", file->name, file->line, NAME); \ + token = lex_token(file); \ + } \ + if ((token == '-' || token == '+') && compile_calc_s) { \ + error(ERROR_PARSE, "%s:%d Invalid constant initializer sign for vector element %c\n", file->name, file->line, NAME); \ + token = lex_token(file); \ + } \ + else if (token == '.' && !compile_calc_d) \ + compile_calc_d = 1; \ + else if (token == '-' && !compile_calc_s) \ + compile_calc_s = '-'; \ + else if (token == '+' && !compile_calc_s) \ + compile_calc_s = '+'; \ + } \ + if (token == ' ') \ + token = lex_token(file); \ + if (NAME != 'z') { \ + if (token != ',' && token != ' ') \ + error(ERROR_PARSE, "%s:%d invalid constant initializer element %c for vector (missing spaces, or comma delimited list?)\n", file->name, file->line, NAME); \ + } else if (token != '}') { \ + error(ERROR_PARSE, "%s:%d Expected `}` on end of constant initialization for vector\n", file->name, file->line); \ + } \ + compile_calc_##BIT = atof(compile_data); \ + compile_calc_d = 0; \ + compile_calc_s = 0; \ + compile_eval = &compile_data[0]; \ + memset(compile_data, 0, sizeof(compile_data)) + + /* + * Parse all elements using the macro above. + * We must undef the macro afterwards. + */ + PARSE_VEC_ELEMENT('x', x); + PARSE_VEC_ELEMENT('y', y); + PARSE_VEC_ELEMENT('z', z); + #undef PARSE_VEC_ELEMENT + + /* Check for the semi-colon... */ + token = lex_token(file); + if (token == ' ') + token = lex_token(file); + if (token != ';') + error(ERROR_PARSE, "%s:%d Expected `;` on end of constant initialization for vector\n", file->name, file->line); + + /* add the compile-time constant */ + compile_constants_add((constant){ + .name = util_strdup(name), + .type = TYPE_VECTOR, + .value = { + [0] = compile_calc_x, + [1] = compile_calc_y, + [2] = compile_calc_z + }, + .string = NULL + }); + break; + } + + case TOKEN_ENTITY: + case TOKEN_FLOAT: /*TODO: validate, constant generation, name constant */ + if (!isdigit(token)) + error(ERROR_PARSE, "%s:%d Expected numeric constant for float constant\n"); + compile_constants_add((constant){ + .name = util_strdup(name), + .type = TOKEN_FLOAT, + .value = {0,0,0}, + .string = NULL + }); + break; + } + } else if (token == '(') { + printf("FUNCTION ??\n"); } - printf("--> LESS THAN\n"); - break; - case '>': /* > */ - token = lex_token(file); - if (token == '=') { /* >= */ + mem_d(name); + } + + /* + * From here down is all language punctuation: There is no + * need to actual create tokens from these because they're already + * tokenized as these individual tokens (which are in a special area + * of the ascii table which doesn't conflict with our other tokens + * which are higer than the ascii table.) + */ + case '#': + token = lex_token(file); /* skip '#' */ + if (token == ' ') token = lex_token(file); - printf("--> GREATER THAN OR EQUAL\n"); - goto end; + /* + * If we make it here we found a directive, the supported + * directives so far are #include. + */ + if (strncmp(file->lastok, "include", sizeof("include")) == 0) { + /* + * We only suport include " ", not <> like in C (why?) + * because the latter is silly. + */ + while (*file->lastok != '"' && token != '\n') + token = lex_token(file); + if (token == '\n') + return error(ERROR_PARSE, "%d: Invalid use of include preprocessor directive: wanted #include \"file.h\"\n", file->line-1); + + char *copy = util_strdup(file->lastok); + struct lex_file *next = lex_include(file, copy); + + if (!next) { + error(ERROR_INTERNAL, "Include subsystem failure\n"); + exit (-1); + } + parse_gen(next); + mem_d (copy); + lex_close(next); } - printf("--> GREATER THAN\n"); - break; - case '=': - token = lex_token(file); - if (token == '=') { /* == */ + /* skip all tokens to end of directive */ + while (token != '\n') token = lex_token(file); - printf("--> COMPARISION \n"); - goto end; - } - printf("--> ASSIGNMENT\n"); break; - case ';': - token = lex_token(file); - printf("--> FINISHED STATMENT\n"); - break; - case '-': - token = lex_token(file); - printf("--> SUBTRACTION EXPRESSION\n"); - break; - case '+': + + case LEX_IDENT: token = lex_token(file); - printf("--> ASSIGNMENT EXPRRESSION\n"); break; } - end:; } lex_reset(file); - - // "&&", "||", "<=", ">=", "==", "!=", ";", ",", "!", "*", - //"/" , "(" , "-" , "+" , "=" , "[" , "]", "{", "}", "...", - //"." , "<" , ">" , "#" , "&" , "|" , "$", "@", ":", NULL - return 1; }