X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=simgear%2Fnasal%2Fparse.c;h=2e66d8fe92de81f7b0b59cd8c6462683be685c7b;hb=c716cfbb07b6b5a0a85893961795f3c4c9bd9a22;hp=920d7342acbaaa4642a1d7a7c994c6a157359a3d;hpb=5aac63d9f5a305c44683b784eb0225edaa124d38;p=simgear.git diff --git a/simgear/nasal/parse.c b/simgear/nasal/parse.c index 920d7342..2e66d8fe 100644 --- a/simgear/nasal/parse.c +++ b/simgear/nasal/parse.c @@ -1,25 +1,27 @@ #include +#include #include "parse.h" // Static precedence table, from low (loose binding, do first) to high // (tight binding, do last). -enum { PREC_BINARY, PREC_REVERSE, PREC_PREFIX, PREC_SUFFIX }; - -#define MAX_PREC_TOKS 5 -struct precedence { +#define MAX_PREC_TOKS 6 +static const struct precedence { int toks[MAX_PREC_TOKS]; int rule; } PRECEDENCE[] = { { { TOK_SEMI, TOK_COMMA }, PREC_REVERSE }, - { { TOK_COLON }, PREC_BINARY }, + { { TOK_ELLIPSIS }, PREC_SUFFIX }, { { TOK_RETURN, TOK_BREAK, TOK_CONTINUE }, PREC_PREFIX }, - { { TOK_ASSIGN }, PREC_REVERSE }, + { { TOK_ASSIGN, TOK_PLUSEQ, TOK_MINUSEQ, + TOK_MULEQ, TOK_DIVEQ, TOK_CATEQ }, PREC_REVERSE }, + { { TOK_COLON, TOK_QUESTION }, PREC_REVERSE }, + { { TOK_VAR }, PREC_PREFIX }, { { TOK_OR }, PREC_BINARY }, { { TOK_AND }, PREC_BINARY }, { { TOK_EQ, TOK_NEQ }, PREC_BINARY }, { { TOK_LT, TOK_LTE, TOK_GT, TOK_GTE }, PREC_BINARY }, - { { TOK_PLUS, TOK_MINUS, TOK_CAT }, PREC_REVERSE }, + { { TOK_PLUS, TOK_MINUS, TOK_CAT }, PREC_BINARY }, { { TOK_MUL, TOK_DIV }, PREC_BINARY }, { { TOK_MINUS, TOK_NEG, TOK_NOT }, PREC_PREFIX }, { { TOK_LPAR, TOK_LBRA }, PREC_SUFFIX }, @@ -29,38 +31,18 @@ struct precedence { void naParseError(struct Parser* p, char* msg, int line) { + if(line > 0) p->errLine = line; p->err = msg; - p->errLine = line; longjmp(p->jumpHandle, 1); } -// A "generic" (too obfuscated to describe) parser error -static void oops(struct Parser* p, struct Token* t) -{ - naParseError(p, "parse error", t->line); -} +static void oops(struct Parser* p) { naParseError(p, "parse error", -1); } void naParseInit(struct Parser* p) { - p->buf = 0; - p->len = 0; - p->lines = 0; - p->nLines = 0; - p->chunks = 0; - p->chunkSizes = 0; - p->nChunks = 0; - p->leftInChunk = 0; - p->cg = 0; - + memset(p, 0, sizeof(*p)); p->tree.type = TOK_TOP; p->tree.line = 1; - p->tree.str = 0; - p->tree.strlen = 0; - p->tree.num = 0; - p->tree.next = 0; - p->tree.prev = 0; - p->tree.children = 0; - p->tree.lastChild = 0; } void naParseDestroy(struct Parser* p) @@ -75,11 +57,8 @@ void naParseDestroy(struct Parser* p) void* naParseAlloc(struct Parser* p, int bytes) { char* result; - - // Round up to 8 byte chunks for alignment - if(bytes & 0x7) bytes = ((bytes>>3) + 1) << 3; + bytes = (bytes+7) & (~7); // Round up to 8 byte chunks for alignment - // Need a new chunk? if(p->leftInChunk < bytes) { void* newChunk; void** newChunks; @@ -109,187 +88,141 @@ void* naParseAlloc(struct Parser* p, int bytes) result = (char *)p->chunks[0] + p->chunkSizes[0] - p->leftInChunk; p->leftInChunk -= bytes; - return (void*)result; + return result; } -// Remove the child from the list where it exists, and insert it at -// the end of the parents child list. -static void addNewChild(struct Token* p, struct Token* c) +static void addChild(struct Token *par, struct Token *ch) { - if(c->prev) c->prev->next = c->next; - if(c->next) c->next->prev = c->prev; - if(c == c->parent->children) - c->parent->children = c->next; - if(c == c->parent->lastChild) - c->parent->lastChild = c->prev; - c->parent = p; - c->next = 0; - c->prev = p->lastChild; - if(p->lastChild) p->lastChild->next = c; - if(!p->children) p->children = c; - p->lastChild = c; + if(par->lastChild) { + ch->prev = par->lastChild; + par->lastChild->next = ch; + } else + par->children = ch; + par->lastChild = ch; } -// Follows the token list from start (which must be a left brace of -// some type), placing all tokens found into start's child list until -// it reaches the matching close brace. -static void collectBrace(struct Parser* p, struct Token* start) +static int endBrace(int tok) { - struct Token* t; - int closer = -1; - if(start->type == TOK_LPAR) closer = TOK_RPAR; - if(start->type == TOK_LBRA) closer = TOK_RBRA; - if(start->type == TOK_LCURL) closer = TOK_RCURL; + if(tok == TOK_LBRA) return TOK_RBRA; + if(tok == TOK_LPAR) return TOK_RPAR; + if(tok == TOK_LCURL) return TOK_RCURL; + return -1; +} - t = start->next; - while(t) { - struct Token* next; - switch(t->type) { - case TOK_LPAR: case TOK_LBRA: case TOK_LCURL: - collectBrace(p, t); - break; - case TOK_RPAR: case TOK_RBRA: case TOK_RCURL: - if(t->type != closer) - naParseError(p, "mismatched closing brace", t->line); - - // Drop this node on the floor, stitch up the list and return - if(start->parent->lastChild == t) - start->parent->lastChild = t->prev; - start->next = t->next; - if(t->next) t->next->prev = start; - return; - } - // Snip t out of the existing list, and append it to start's - // children. - next = t->next; - addNewChild(start, t); - t = next; - } - naParseError(p, "unterminated brace", start->line); +static int isOpenBrace(int t) +{ + return t==TOK_LPAR || t==TOK_LBRA || t==TOK_LCURL; } -// Recursively find the contents of all matching brace pairs in the -// token list and turn them into children of the left token. The -// right token disappears. -static void braceMatch(struct Parser* p, struct Token* start) +static int isLoopoid(int t) { - struct Token* t = start; - while(t) { - switch(t->type) { - case TOK_LPAR: case TOK_LBRA: case TOK_LCURL: - collectBrace(p, t); - break; - case TOK_RPAR: case TOK_RBRA: case TOK_RCURL: - if(start->type != TOK_LBRA) - naParseError(p, "stray closing brace", t->line); - break; - } - t = t->next; - } + return t==TOK_FOR || t==TOK_FOREACH || t==TOK_WHILE || t==TOK_FORINDEX; +} + +static int isBlockoid(int t) +{ + return isLoopoid(t)||t==TOK_IF||t==TOK_ELSIF||t==TOK_ELSE||t==TOK_FUNC; +} + +/* Yes, a bare else or elsif ends a block; it means we've reached the + * end of the previous if/elsif clause. */ +static int isBlockEnd(int t) +{ + return t==TOK_RPAR||t==TOK_RBRA||t==TOK_RCURL||t==TOK_ELSIF||t==TOK_ELSE; } -// Allocate and return an "empty" token as a parsing placeholder. -static struct Token* emptyToken(struct Parser* p) +/* To match C's grammar, "blockoid" expressions sometimes need + * synthesized terminating semicolons to make them act like + * "statements" in C. Always add one after "loopoid" + * (for/foreach/while) expressions. Add one after a func if it + * immediately follows an assignment, and add one after an + * if/elsif/else if it is the first token in an expression list */ +static int needsSemi(struct Token* t, struct Token* next) +{ + if(!next || next->type == TOK_SEMI || isBlockEnd(next->type)) return 0; + if(t->type == TOK_IF) return !t->prev || t->prev->type == TOK_SEMI; + if(t->type == TOK_FUNC) return t->prev && t->prev->type == TOK_ASSIGN; + if(isLoopoid(t->type)) return 1; + return 0; +} + +static struct Token* newToken(struct Parser* p, int type) { struct Token* t = naParseAlloc(p, sizeof(struct Token)); - t->type = TOK_EMPTY; + memset(t, 0, sizeof(*t)); + t->type = type; t->line = -1; - t->strlen = 0; - t->num = 0; - t->str = 0; - t->next = t->prev = t->children = t->lastChild = 0; - t->parent = 0; return t; } -// Fixes up parenting for obvious parsing situations, like code blocks -// being the child of a func keyword, etc... -static void fixBlockStructure(struct Parser* p, struct Token* start) +static struct Token* parseToken(struct Parser* p, struct Token** list); + +static void parseBlock(struct Parser* p, struct Token *top, + int end, struct Token** list) { - struct Token *t, *c; - t = start; - while(t) { - switch(t->type) { - case TOK_ELSE: case TOK_FUNC: - // These guys precede a single curly block - if(!t->next || t->next->type != TOK_LCURL) oops(p, t); - c = t->next; - addNewChild(t, c); - fixBlockStructure(p, c); - break; - case TOK_FOR: case TOK_FOREACH: case TOK_WHILE: - case TOK_IF: case TOK_ELSIF: - // Expect a paren and then a curly - if(!t->next || t->next->type != TOK_LPAR) oops(p, t); - c = t->next; - addNewChild(t, c); - fixBlockStructure(p, c); - - if(!t->next || t->next->type != TOK_LCURL) oops(p, t); - c = t->next; - addNewChild(t, c); - fixBlockStructure(p, c); - break; - case TOK_LPAR: case TOK_LBRA: case TOK_LCURL: - fixBlockStructure(p, t->children); - break; - } - t = t->next; + struct Token *t; + while(*list) { + if(isBlockEnd((*list)->type) && (*list)->type != end) break; + if(end == TOK_SEMI && (*list)->type == TOK_COMMA) break; + t = parseToken(p, list); + if(t->type == end) return; /* drop end token on the floor */ + addChild(top, t); + if(needsSemi(t, *list)) + addChild(top, newToken(p, TOK_SEMI)); } + /* Context dependency: end of block is a parse error UNLESS we're + * looking for a statement terminator (a braceless block) or a -1 + * (the top level) */ + if(end != TOK_SEMI && end != -1) oops(p); +} - // Another pass to hook up the elsif/else chains. - t = start; - while(t) { - if(t->type == TOK_IF) { - while(t->next && t->next->type == TOK_ELSIF) - addNewChild(t, t->next); - if(t->next && t->next->type == TOK_ELSE) - addNewChild(t, t->next); +static struct Token* parseToken(struct Parser* p, struct Token** list) +{ + struct Token *t = *list; + *list = t->next; + if(t->next) t->next->prev = 0; + t->next = t->prev = 0; + p->errLine = t->line; + + if(!t) return 0; + if(isOpenBrace(t->type)) { + parseBlock(p, t, endBrace(t->type), list); + } else if(isBlockoid(t->type)) { + /* Read an optional paren expression */ + if(!*list) oops(p); + if((*list)->type == TOK_LPAR) + addChild(t, parseToken(p, list)); + + /* And the code block, which might be implicit/braceless */ + if(!*list) oops(p); + if((*list)->type == TOK_LCURL) { + addChild(t, parseToken(p, list)); + } else { + /* Context dependency: if we're reading a braceless block, + * and the first (!) token is itself a "blockoid" + * expression, it is parsed alone, otherwise, read to the + * terminating semicolon. */ + struct Token *blk = newToken(p, TOK_LCURL); + if(isBlockoid((*list)->type)) addChild(blk, parseToken(p, list)); + else parseBlock(p, blk, TOK_SEMI, list); + addChild(t, blk); } - t = t->next; - } - // And a final one to add semicolons. Always add one after - // for/foreach/while expressions. Add one after a function lambda - // if it immediately follows an assignment, and add one after an - // if/elsif/else if it is the first token in an expression list - // (i.e has no previous token, or is preceded by a ';' or '{'). - // This mimicks common usage and avoids a conspicuous difference - // between this grammar and more common languages. It can be - // "escaped" with extra parenthesis if necessary, e.g.: - // a = (func { join(" ", arg) })(1, 2, 3, 4); - t = start; - while(t) { - int addSemi = 0; - switch(t->type) { - case TOK_IF: - if(!t->prev - || t->prev->type == TOK_SEMI - || t->prev->type == TOK_LCURL) - addSemi = 1; - break; - case TOK_FOR: case TOK_FOREACH: case TOK_WHILE: - addSemi = 1; - break; - case TOK_FUNC: - if(t->prev && t->prev->type == TOK_ASSIGN) - addSemi = 1; - break; + /* Read the elsif/else chain */ + if(t->type == TOK_IF) { + while(*list && ((*list)->type == TOK_ELSIF)) + addChild(t, parseToken(p, list)); + if(*list && (*list)->type == TOK_ELSE) + addChild(t, parseToken(p, list)); } - if(addSemi) { - struct Token* semi = emptyToken(p); - semi->type = TOK_SEMI; - semi->line = t->line; - semi->next = t->next; - semi->prev = t; - semi->parent = t->parent; - if(semi->next) semi->next->prev = semi; - t->next = semi; - t = semi; // don't bother checking the new one + + /* Finally, check for proper usage */ + if(t->type != TOK_FUNC) { + if(t->type == TOK_ELSE && t->children->type != TOK_LCURL) oops(p); + if(t->type != TOK_ELSE && t->children->type != TOK_LPAR) oops(p); } - t = t->next; } - + return t; } // True if the token's type exists in the precedence level. @@ -302,20 +235,34 @@ static int tokInLevel(struct Token* tok, int level) return 0; } -static int isBrace(int type) +static struct Token* parsePrecedence(struct Parser* p, struct Token* start, + struct Token* end, int level); + +static void precChildren(struct Parser* p, struct Token* t) { - return type == TOK_LPAR || type == TOK_LBRA || type == TOK_LCURL; + struct Token* top = parsePrecedence(p, t->children, t->lastChild, 0); + t->children = top; + t->lastChild = top; } -static int isBlock(int t) +// Run a "block structure" node (if/elsif/else/for/while/foreach) +// through the precedence parser. The funny child structure makes +// this a little more complicated than it should be. +static void precBlock(struct Parser* p, struct Token* block) { - return t == TOK_IF || t == TOK_ELSIF || t == TOK_ELSE - || t == TOK_FOR || t == TOK_FOREACH || t == TOK_WHILE - || t == TOK_FUNC; + struct Token* t = block->children; + while(t) { + if(isOpenBrace(t->type)) + precChildren(p, t); + else if(isBlockoid(t->type)) + precBlock(p, t); + t = t->next; + } } -static void precChildren(struct Parser* p, struct Token* t); -static void precBlock(struct Parser* p, struct Token* t); +/* Binary tokens that get empties synthesized if one side is missing */ +static int oneSidedBinary(int t) +{ return t == TOK_SEMI || t == TOK_COMMA || t == TOK_COLON; } static struct Token* parsePrecedence(struct Parser* p, struct Token* start, struct Token* end, @@ -327,11 +274,11 @@ static struct Token* parsePrecedence(struct Parser* p, // This is an error. No "siblings" are allowed at the bottom level. if(level >= PRECEDENCE_LEVELS && start != end) - oops(p, start); + naParseError(p, "parse error", start->line); // Synthesize an empty token if necessary if(end == 0 && start == 0) - return emptyToken(p); + return newToken(p, TOK_EMPTY); // Sanify the list. This is OK, since we're recursing into the // list structure; stuff to the left and right has already been @@ -345,26 +292,19 @@ static struct Token* parsePrecedence(struct Parser* p, // Single tokens parse as themselves. Recurse into braces, and // parse children of block structure. if(start == end) { - if(isBrace(start->type)) { - precChildren(p, start); - } else if(isBlock(start->type)) { - precBlock(p, start); - } + if (isOpenBrace(start->type)) precChildren(p, start); + else if(isBlockoid(start->type)) precBlock(p, start); return start; } - // A context-sensitivity: we want to parse ';' and ',' as binary - // operators, but want them to be legal at the beginning and end - // of a list (unlike, say, '+' where we want a parse error). - // Generate empties as necessary. - if(start->type == TOK_SEMI || start->type == TOK_COMMA) { - t = emptyToken(p); + if(oneSidedBinary(start->type)) { + t = newToken(p, TOK_EMPTY); start->prev = t; t->next = start; start = t; } - if(end->type == TOK_SEMI || end->type == TOK_COMMA) { - t = emptyToken(p); + if(oneSidedBinary(end->type)) { + t = newToken(p, TOK_EMPTY); end->next = t; t->prev = end; end = t; @@ -442,17 +382,17 @@ static struct Token* parsePrecedence(struct Parser* p, if(!top) return parsePrecedence(p, start, end, level+1); + top->rule = rule; + if(left) { left->next = right; left->prev = 0; - left->parent = top; } top->children = left; if(right) { right->next = 0; right->prev = left; - right->parent = top; } top->lastChild = right; @@ -460,28 +400,6 @@ static struct Token* parsePrecedence(struct Parser* p, return top; } -static void precChildren(struct Parser* p, struct Token* t) -{ - struct Token* top = parsePrecedence(p, t->children, t->lastChild, 0); - t->children = top; - t->lastChild = top; -} - -// Run a "block structure" node (if/elsif/else/for/while/foreach) -// through the precedence parser. The funny child structure makes -// this a little more complicated than it should be. -static void precBlock(struct Parser* p, struct Token* block) -{ - struct Token* t = block->children; - while(t) { - if(isBrace(t->type)) - precChildren(p, t); - else if(isBlock(t->type)) - precBlock(p, t); - t = t->next; - } -} - naRef naParseCode(struct Context* c, naRef srcFile, int firstLine, char* buf, int len, int* errLine) { @@ -490,17 +408,19 @@ naRef naParseCode(struct Context* c, naRef srcFile, int firstLine, struct Parser p; // Protect from garbage collection - naVec_append(c->temps, srcFile); + naTempSave(c, srcFile); + + naParseInit(&p); // Catch parser errors here. - *errLine = 0; + p.errLine = *errLine = 1; if(setjmp(p.jumpHandle)) { - c->error = p.err; + strncpy(c->error, p.err, sizeof(c->error)); *errLine = p.errLine; + naParseDestroy(&p); return naNil(); } - naParseInit(&p); p.context = c; p.srcFile = srcFile; p.firstLine = firstLine; @@ -509,8 +429,12 @@ naRef naParseCode(struct Context* c, naRef srcFile, int firstLine, // Lexify, match brace structure, fixup if/for/etc... naLex(&p); - braceMatch(&p, p.tree.children); - fixBlockStructure(&p, p.tree.children); + + // Run the block parser, make sure everything was eaten + t = p.tree.children; + p.tree.children = p.tree.lastChild = 0; + parseBlock(&p, &p.tree, -1, &t); + if(t) oops(&p); // Recursively run the precedence parser, and fixup the treetop t = parsePrecedence(&p, p.tree.children, p.tree.lastChild, 0); @@ -518,14 +442,12 @@ naRef naParseCode(struct Context* c, naRef srcFile, int firstLine, p.tree.children = t; p.tree.lastChild = t; - // Generate code! - codeObj = naCodeGen(&p, &(p.tree)); + // Generate code + codeObj = naCodeGen(&p, &(p.tree), 0); // Clean up our mess naParseDestroy(&p); - naVec_append(c->temps, codeObj); + naTempSave(c, codeObj); return codeObj; } - -