X-Git-Url: https://git.xonotic.org/?a=blobdiff_plain;f=parse.c;h=0dfe3e9441a5387b932cd6964de22516b26947d4;hb=cc2c0ddbabf9fe26037a345c2ed1e18d798889a5;hp=0e1c6a511d84473c70187ebbd7cc0fa2f5e4f012;hpb=bd5ba9e0fe0e9bdaea3f869f55d8f40651a2d0fb;p=xonotic%2Fgmqcc.git diff --git a/parse.c b/parse.c index 0e1c6a5..0dfe3e9 100644 --- a/parse.c +++ b/parse.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "gmqcc.h" /* @@ -83,7 +84,7 @@ } while (0) /* - * These are all the punctuation handled in the parser, these don't + * This is all the punctuation handled in the parser, these don't * need tokens, they're already tokens. */ #if 0 @@ -92,59 +93,63 @@ "." , "<" , ">" , "&" , "|" , #endif -#define STORE(X) { \ - printf(X); \ - break; \ +#define STORE(X,C) { \ + long f = fill; \ + while(f--) { \ + putchar(' '); \ + } \ + fill C; \ + printf(X); \ + break; \ } void parse_debug(struct parsenode *tree) { + long fill = 0; while (tree) { switch (tree->type) { - case PARSE_TYPE_ADD: STORE("OPERATOR: ADD \n"); - case PARSE_TYPE_BAND: STORE("OPERATOR: BITAND \n"); - case PARSE_TYPE_BOR: STORE("OPERATOR: BITOR \n"); - case PARSE_TYPE_COMMA: STORE("OPERATOR: SEPERATOR\n"); - case PARSE_TYPE_DOT: STORE("OPERATOR: DOT\n"); - case PARSE_TYPE_DIVIDE: STORE("OPERATOR: DIVIDE\n"); - case PARSE_TYPE_EQUAL: STORE("OPERATOR: ASSIGNMENT\n"); + case PARSE_TYPE_ADD: STORE("OPERATOR: ADD \n", -=0); + case PARSE_TYPE_BAND: STORE("OPERATOR: BITAND \n",-=0); + case PARSE_TYPE_BOR: STORE("OPERATOR: BITOR \n",-=0); + case PARSE_TYPE_COMMA: STORE("OPERATOR: SEPERATOR\n",-=0); + case PARSE_TYPE_DOT: STORE("OPERATOR: DOT\n",-=0); + case PARSE_TYPE_DIVIDE: STORE("OPERATOR: DIVIDE\n",-=0); + case PARSE_TYPE_EQUAL: STORE("OPERATOR: ASSIGNMENT\n",-=0); - case PARSE_TYPE_BREAK: STORE("STATEMENT: BREAK \n"); - case PARSE_TYPE_CONTINUE: STORE("STATEMENT: CONTINUE\n"); - case PARSE_TYPE_GOTO: STORE("STATEMENT: GOTO\n"); - case PARSE_TYPE_RETURN: STORE("STATEMENT: RETURN\n"); - case PARSE_TYPE_DONE: STORE("STATEMENT: DONE\n"); + case PARSE_TYPE_BREAK: STORE("STATEMENT: BREAK \n",-=0); + case PARSE_TYPE_CONTINUE: STORE("STATEMENT: CONTINUE\n",-=0); + case PARSE_TYPE_GOTO: STORE("STATEMENT: GOTO\n",-=0); + case PARSE_TYPE_RETURN: STORE("STATEMENT: RETURN\n",-=0); + case PARSE_TYPE_DONE: STORE("STATEMENT: DONE\n",-=0); - case PARSE_TYPE_VOID: STORE("DECLTYPE: VOID\n"); - case PARSE_TYPE_STRING: STORE("DECLTYPE: STRING\n"); - case PARSE_TYPE_ELIP: STORE("DECLTYPE: VALIST\n"); - case PARSE_TYPE_ENTITY: STORE("DECLTYPE: ENTITY\n"); - case PARSE_TYPE_FLOAT: STORE("DECLTYPE: FLOAT\n"); - case PARSE_TYPE_VECTOR: STORE("DECLTYPE: VECTOR\n"); + case PARSE_TYPE_VOID: STORE("DECLTYPE: VOID\n",-=0); + case PARSE_TYPE_STRING: STORE("DECLTYPE: STRING\n",-=0); + case PARSE_TYPE_ELIP: STORE("DECLTYPE: VALIST\n",-=0); + case PARSE_TYPE_ENTITY: STORE("DECLTYPE: ENTITY\n",-=0); + case PARSE_TYPE_FLOAT: STORE("DECLTYPE: FLOAT\n",-=0); + case PARSE_TYPE_VECTOR: STORE("DECLTYPE: VECTOR\n",-=0); - case PARSE_TYPE_GT: STORE("TEST: GREATER THAN\n"); - case PARSE_TYPE_LT: STORE("TEST: LESS THAN\n"); - case PARSE_TYPE_GTEQ: STORE("TEST: GREATER THAN OR EQUAL\n"); - case PARSE_TYPE_LTEQ: STORE("TEST: LESS THAN OR EQUAL\n"); - case PARSE_TYPE_LNEQ: STORE("TEST: NOT EQUAL\n"); - case PARSE_TYPE_EQEQ: STORE("TEST: EQUAL-EQUAL\n"); + case PARSE_TYPE_GT: STORE("TEST: GREATER THAN\n",-=0); + case PARSE_TYPE_LT: STORE("TEST: LESS THAN\n",-=0); + case PARSE_TYPE_GTEQ: STORE("TEST: GREATER THAN OR EQUAL\n",-=0); + case PARSE_TYPE_LTEQ: STORE("TEST: LESS THAN OR EQUAL\n",-=0); + case PARSE_TYPE_LNEQ: STORE("TEST: NOT EQUAL\n",-=0); + case PARSE_TYPE_EQEQ: STORE("TEST: EQUAL-EQUAL\n",-=0); - case PARSE_TYPE_LBS: STORE("BLOCK: BEG\n"); - case PARSE_TYPE_RBS: STORE("BLOCK: END\n"); - case PARSE_TYPE_ELSE: STORE("BLOCK: ELSE\n"); - case PARSE_TYPE_IF: STORE("BLOCK: IF\n"); + case PARSE_TYPE_LBS: STORE("BLOCK: BEG\n",+=4); + case PARSE_TYPE_RBS: STORE("BLOCK: END\n",-=4); + case PARSE_TYPE_ELSE: STORE("BLOCK: ELSE\n",+=0); + case PARSE_TYPE_IF: STORE("BLOCK: IF\n",+=0); - case PARSE_TYPE_LAND: STORE("LOGICAL: AND\n"); - case PARSE_TYPE_LNOT: STORE("LOGICAL: NOT\n"); - case PARSE_TYPE_LOR: STORE("LOGICAL: OR\n"); + case PARSE_TYPE_LAND: STORE("LOGICAL: AND\n",-=0); + case PARSE_TYPE_LNOT: STORE("LOGICAL: NOT\n",-=0); + case PARSE_TYPE_LOR: STORE("LOGICAL: OR\n",-=0); - case PARSE_TYPE_LPARTH: STORE("PARTH: BEG\n"); - case PARSE_TYPE_RPARTH: STORE("PARTH: END\n"); + case PARSE_TYPE_LPARTH: STORE("PARTH: BEG\n",-=0); + case PARSE_TYPE_RPARTH: STORE("PARTH: END\n",-=0); - case PARSE_TYPE_WHILE: STORE("LOOP: WHILE\n"); - case PARSE_TYPE_FOR: STORE("LOOP: FOR\n"); - case PARSE_TYPE_DO: STORE("LOOP: DO\n"); - - //case PARSE_TYPE_IDENT: STORE("IDENT: ???\n"); + case PARSE_TYPE_WHILE: STORE("LOOP: WHILE\n",-=0); + case PARSE_TYPE_FOR: STORE("LOOP: FOR\n",-=0); + case PARSE_TYPE_DO: STORE("LOOP: DO\n",-=0); } tree = tree->next; } @@ -158,13 +163,13 @@ void parse_debug(struct parsenode *tree) { * and everything will fail. */ #define PARSE_PERFORM(X,C) { \ - token = lex_token(file); \ - { C } \ - while (token != '\n') { \ - token = lex_token(file); \ - } \ - PARSE_TREE_ADD(X); \ - break; \ + token = lex_token(file); \ + { C } \ + while (token != '\n') { \ + token = lex_token(file); \ + } \ + PARSE_TREE_ADD(X); \ + break; \ } void parse_clear(struct parsenode *tree) { @@ -180,6 +185,23 @@ void parse_clear(struct parsenode *tree) { typedef_clear(); } +const char *STRING_(char ch) { + if (ch == ' ') + return ""; + if (ch == '\n') + return ""; + if (ch == '\0') + return ""; + + return &ch; +} + +#define TOKEN_SKIPWHITE() \ + token = lex_token(file); \ + while (token == ' ') { \ + token = lex_token(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. @@ -202,71 +224,184 @@ int parse_tree(struct lex_file *file) { } int token = 0; + long line = 0; while ((token = lex_token(file)) != ERROR_LEX && \ token != ERROR_COMPILER && \ token != ERROR_INTERNAL && \ token != ERROR_PARSE && \ token != ERROR_PREPRO && file->length >= 0) { + line = file->line; switch (token) { - case TOKEN_IF: - while ((token == ' ' || token == '\n') && file->length >= 0) - token = lex_token(file); - PARSE_TREE_ADD(PARSE_TYPE_IF); - break; - case TOKEN_ELSE: - token = lex_token(file); - PARSE_TREE_ADD(PARSE_TYPE_ELSE); - break; - case TOKEN_FOR: - //token = lex_token(file); - while ((token == ' ' || token == '\n') && file->length >= 0) - token = lex_token(file); - PARSE_TREE_ADD(PARSE_TYPE_FOR); - break; - - /* - * This is a quick and easy way to do typedefs at parse time - * all power is in typedef_add(), in typedef.c. We handle - * the tokens accordingly here. - */ case TOKEN_TYPEDEF: { - char *f = NULL; - char *t = NULL; + char *f; /* from */ + char *t; /* to */ + token = lex_token(file); - token = lex_token(file); f = strdup(file->lastok); + token = lex_token(file); f = util_strdup(file->lastok); token = lex_token(file); - token = lex_token(file); t = strdup(file->lastok); + token = lex_token(file); t = util_strdup(file->lastok); typedef_add(f, t); + mem_d(f); + mem_d(t); - free(f); - free(t); - - while (token != '\n') + token = lex_token(file); + if (token == ' ') 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; } - /* - * Returns are addable as-is, statement checking is during - * the actual parse tree check. - */ - case TOKEN_RETURN: - PARSE_TREE_ADD(PARSE_TYPE_RETURN); - break; - //PARSE_PERFORM(PARSE_TYPE_RETURN, {}); - + case TOKEN_VOID: PARSE_TREE_ADD(PARSE_TYPE_VOID); goto fall; + case TOKEN_STRING: PARSE_TREE_ADD(PARSE_TYPE_STRING); goto fall; + case TOKEN_VECTOR: PARSE_TREE_ADD(PARSE_TYPE_VECTOR); goto fall; + case TOKEN_ENTITY: PARSE_TREE_ADD(PARSE_TYPE_ENTITY); goto fall; + case TOKEN_FLOAT: PARSE_TREE_ADD(PARSE_TYPE_FLOAT); goto fall; + { + fall:; + char *name = NULL; + int type = token; /* story copy */ - case TOKEN_DO: PARSE_PERFORM(PARSE_TYPE_DO, {}); - case TOKEN_WHILE: PARSE_PERFORM(PARSE_TYPE_WHILE, {}); - case TOKEN_BREAK: PARSE_PERFORM(PARSE_TYPE_BREAK, {}); - case TOKEN_CONTINUE: PARSE_PERFORM(PARSE_TYPE_CONTINUE,{}); - case TOKEN_GOTO: PARSE_PERFORM(PARSE_TYPE_GOTO, {}); - case TOKEN_VOID: PARSE_PERFORM(PARSE_TYPE_VOID, {}); - case TOKEN_STRING: PARSE_PERFORM(PARSE_TYPE_STRING, {}); - case TOKEN_FLOAT: PARSE_PERFORM(PARSE_TYPE_FLOAT, {}); - case TOKEN_VECTOR: PARSE_PERFORM(PARSE_TYPE_VECTOR, {}); - case TOKEN_ENTITY: PARSE_PERFORM(PARSE_TYPE_ENTITY, {}); + /* skip over space */ + token = lex_token(file); + if (token == ' ') + token = lex_token(file); + + /* save name */ + name = util_strdup(file->lastok); + + /* skip spaces */ + token = lex_token(file); + if (token == ' ') + token = lex_token(file); + + 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); + 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); + case TOKEN_STRING: + if (*file->lastok != '"') + error(ERROR_PARSE, "%s:%d Expected a '\"' (quote) for string constant\n", file->name, file->line); + break; + 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 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", NAME, file->name, file->line); \ + } \ + } 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... This is insane + * the amount of parsing here that is. + */ + 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); + + printf("VEC_X: %f\n", compile_calc_x); + printf("VEC_Y: %f\n", compile_calc_y); + printf("VEC_Z: %f\n", compile_calc_z); + break; + } + + case TOKEN_ENTITY: + case TOKEN_FLOAT: + + if (!isdigit(token)) + error(ERROR_PARSE, "%s:%d Expected numeric constant for float constant\n"); + break; + } + } else if (token == '(') { + printf("FUNCTION ??\n"); + } + mem_d(name); + } /* * From here down is all language punctuation: There is no @@ -276,114 +411,29 @@ int parse_tree(struct lex_file *file) { * which are higer than the ascii table.) */ case '#': + token = lex_token(file); /* skip '#' */ + if (token == ' ') + token = lex_token(file); /* - * Skip the preprocessor for now: We'll implement our own - * eventually. For now we need to make sure directives are - * not accidently tokenized. + * If we make it here we found a directive, the supported + * directives so far are #include. */ - token = lex_token(file); - token = lex_token(file); - + 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); + } + /* skip all tokens to end of directive */ while (token != '\n') token = lex_token(file); break; - case '.': - token = lex_token(file); - PARSE_TREE_ADD(PARSE_TYPE_DOT); - break; - - case '(': - token = lex_token(file); - PARSE_TREE_ADD(PARSE_TYPE_LPARTH); - break; - case ')': - token = lex_token(file); - PARSE_TREE_ADD(PARSE_TYPE_RPARTH); - break; - - case '&': /* & */ - token = lex_token(file); - if (token == '&') { /* && */ - token = lex_token(file); - PARSE_TREE_ADD(PARSE_TYPE_LAND); - break; - } - PARSE_TREE_ADD(PARSE_TYPE_BAND); - break; - case '|': /* | */ - token = lex_token(file); - if (token == '|') { /* || */ - token = lex_token(file); - PARSE_TREE_ADD(PARSE_TYPE_LOR); - break; - } - PARSE_TREE_ADD(PARSE_TYPE_BOR); - break; - case '!': - token = lex_token(file); - if (token == '=') { /* != */ - token = lex_token(file); - PARSE_TREE_ADD(PARSE_TYPE_LNEQ); - break; - } - PARSE_TREE_ADD(PARSE_TYPE_LNOT); - break; - case '<': /* < */ - token = lex_token(file); - if (token == '=') { /* <= */ - token = lex_token(file); - PARSE_TREE_ADD(PARSE_TYPE_LTEQ); - break; - } - PARSE_TREE_ADD(PARSE_TYPE_LT); - break; - case '>': /* > */ - token = lex_token(file); - if (token == '=') { /* >= */ - token = lex_token(file); - PARSE_TREE_ADD(PARSE_TYPE_GTEQ); - break; - } - PARSE_TREE_ADD(PARSE_TYPE_GT); - break; - case '=': - token = lex_token(file); - if (token == '=') { /* == */ - token = lex_token(file); - PARSE_TREE_ADD(PARSE_TYPE_EQEQ); - break; - } - PARSE_TREE_ADD(PARSE_TYPE_EQUAL); - break; - case ';': - token = lex_token(file); - PARSE_TREE_ADD(PARSE_TYPE_DONE); - break; - case '-': - token = lex_token(file); - PARSE_TREE_ADD(PARSE_TYPE_MINUS); - break; - case '+': - token = lex_token(file); - PARSE_TREE_ADD(PARSE_TYPE_ADD); - break; - case '{': - token = lex_token(file); - PARSE_TREE_ADD(PARSE_TYPE_LBS); - break; - case '}': - token = lex_token(file); - PARSE_TREE_ADD(PARSE_TYPE_RBS); - break; - - /* - * TODO: Fix lexer to spit out ( ) as tokens, it seems the - * using '(' or ')' in parser doesn't work properly unless - * there are spaces before them to allow the lexer to properly - * seperate identifiers. -- otherwise it eats all of it. - */ case LEX_IDENT: token = lex_token(file); PARSE_TREE_ADD(PARSE_TYPE_IDENT);