Files
simgear/simgear/nasal/parse.c
T
andy 65056bfa72 Support for a "forindex(idx; list) {...}" construct analagous to
foreach, except that the variable gets the index instead of the list
element.  Should be useful, and took almost no code to implement.

Support for operator/assignment syntax: +=, -=, *=, /= and ~= now do
what you think they should.

Library support for a bind() function (see the docs Andy is still
writing), allowing runtime modifications to function lexical
environments.
2005-04-22 21:54:16 +00:00

545 lines
16 KiB
C

#include <setjmp.h>
#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 6
struct precedence {
int toks[MAX_PREC_TOKS];
int rule;
} PRECEDENCE[] = {
{ { TOK_SEMI, TOK_COMMA }, PREC_REVERSE },
{ { TOK_ELLIPSIS }, PREC_SUFFIX },
{ { TOK_RETURN, TOK_BREAK, TOK_CONTINUE }, PREC_PREFIX },
{ { 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_BINARY },
{ { TOK_MUL, TOK_DIV }, PREC_BINARY },
{ { TOK_MINUS, TOK_NEG, TOK_NOT }, PREC_PREFIX },
{ { TOK_LPAR, TOK_LBRA }, PREC_SUFFIX },
{ { TOK_DOT }, PREC_BINARY },
};
#define PRECEDENCE_LEVELS (sizeof(PRECEDENCE)/sizeof(struct precedence))
void naParseError(struct Parser* p, char* msg, int 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);
}
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;
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)
{
int i;
for(i=0; i<p->nChunks; i++) naFree(p->chunks[i]);
naFree(p->chunks);
naFree(p->chunkSizes);
p->buf = 0;
}
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;
// Need a new chunk?
if(p->leftInChunk < bytes) {
void* newChunk;
void** newChunks;
int* newChunkSizes;
int sz, i;
sz = p->len;
if(sz < bytes) sz = bytes;
newChunk = naAlloc(sz);
p->nChunks++;
newChunks = naAlloc(p->nChunks * sizeof(void*));
for(i=1; i<p->nChunks; i++) newChunks[i] = p->chunks[i-1];
newChunks[0] = newChunk;
naFree(p->chunks);
p->chunks = newChunks;
newChunkSizes = naAlloc(p->nChunks * sizeof(int));
for(i=1; i<p->nChunks; i++) newChunkSizes[i] = p->chunkSizes[i-1];
newChunkSizes[0] = sz;
naFree(p->chunkSizes);
p->chunkSizes = newChunkSizes;
p->leftInChunk = sz;
}
result = (char *)p->chunks[0] + p->chunkSizes[0] - p->leftInChunk;
p->leftInChunk -= bytes;
return (void*)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)
{
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;
}
// 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)
{
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;
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);
}
// 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)
{
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;
}
}
// Allocate and return an "empty" token as a parsing placeholder.
static struct Token* emptyToken(struct Parser* p)
{
struct Token* t = naParseAlloc(p, sizeof(struct Token));
t->type = TOK_EMPTY;
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)
{
struct Token *t, *c;
t = start;
while(t) {
switch(t->type) {
case TOK_FUNC:
// Slurp an optional paren block containing an arglist, then
// fall through to parse the curlies...
if(t->next && t->next->type == TOK_LPAR) {
c = t->next;
addNewChild(t, c);
fixBlockStructure(p, c);
}
case TOK_ELSE: // and 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_FORINDEX: 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;
}
// 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);
}
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_FORINDEX: case TOK_WHILE:
addSemi = 1;
break;
case TOK_FUNC:
if(t->prev && t->prev->type == TOK_ASSIGN)
addSemi = 1;
break;
}
if(t->next && t->next->type == TOK_SEMI)
addSemi = 0; // don't bother if it's already there!
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
}
t = t->next;
}
}
// True if the token's type exists in the precedence level.
static int tokInLevel(struct Token* tok, int level)
{
int i;
for(i=0; i<MAX_PREC_TOKS; i++)
if(PRECEDENCE[level].toks[i] == tok->type)
return 1;
return 0;
}
static int isBrace(int type)
{
return type == TOK_LPAR || type == TOK_LBRA || type == TOK_LCURL;
}
static int isBlock(int t)
{
return t == TOK_IF || t == TOK_ELSIF || t == TOK_ELSE
|| t == TOK_FOR || t == TOK_FOREACH || t == TOK_WHILE
|| t == TOK_FUNC || t == TOK_FORINDEX;
}
static void precChildren(struct Parser* p, struct Token* t);
static void precBlock(struct Parser* p, struct Token* t);
static struct Token* parsePrecedence(struct Parser* p,
struct Token* start, struct Token* end,
int level)
{
int rule;
struct Token *t, *top, *left, *right;
struct Token *a, *b, *c, *d; // temporaries
// This is an error. No "siblings" are allowed at the bottom level.
if(level >= PRECEDENCE_LEVELS && start != end)
oops(p, start);
// Synthesize an empty token if necessary
if(end == 0 && start == 0)
return emptyToken(p);
// Sanify the list. This is OK, since we're recursing into the
// list structure; stuff to the left and right has already been
// handled somewhere above.
if(end == 0) end = start;
if(start == 0) start = end;
if(start->prev) start->prev->next = 0;
if(end->next) end->next->prev = 0;
start->prev = end->next = 0;
// 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);
}
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);
start->prev = t;
t->next = start;
start = t;
}
if(end->type == TOK_SEMI || end->type == TOK_COMMA) {
t = emptyToken(p);
end->next = t;
t->prev = end;
end = t;
}
// Another one: the "." and (postfix) "[]/()" operators should
// really be the same precendence level, but the existing
// implementation doesn't allow for it. Bump us up a level if we
// are parsing for DOT but find a LPAR/LBRA at the end of the
// list.
if(PRECEDENCE[level].toks[0] == TOK_DOT)
if(end->type == TOK_LPAR || end->type == TOK_LBRA)
level--;
top = left = right = 0;
rule = PRECEDENCE[level].rule;
switch(rule) {
case PREC_PREFIX:
if(tokInLevel(start, level) && start->next) {
a = start->children;
b = start->lastChild;
c = start->next;
d = end;
top = start;
if(a) left = parsePrecedence(p, a, b, 0);
right = parsePrecedence(p, c, d, level);
}
break;
case PREC_SUFFIX:
if(tokInLevel(end, level) && end->prev) {
a = start;
b = end->prev;
c = end->children;
d = end->lastChild;
top = end;
left = parsePrecedence(p, a, b, level);
if(c) right = parsePrecedence(p, c, d, 0);
}
break;
case PREC_BINARY:
t = end->prev;
while(t->prev) {
if(tokInLevel(t, level)) {
a = t->prev ? start : 0;
b = t->prev;
c = t->next;
d = t->next ? end : 0;
top = t;
left = parsePrecedence(p, a, b, level);
right = parsePrecedence(p, c, d, level+1);
break;
}
t = t->prev;
}
break;
case PREC_REVERSE:
t = start->next;
while(t->next) {
if(tokInLevel(t, level)) {
a = t->prev ? start : 0;
b = t->prev;
c = t->next;
d = t->next ? end : 0;
top = t;
left = parsePrecedence(p, a, b, level+1);
right = parsePrecedence(p, c, d, level);
break;
}
t = t->next;
}
break;
}
// Found nothing, try the next level
if(!top)
return parsePrecedence(p, start, end, level+1);
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;
top->next = top->prev = 0;
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)
{
naRef codeObj;
struct Token* t;
struct Parser p;
// Protect from garbage collection
naVec_append(c->temps, srcFile);
// Catch parser errors here.
*errLine = 0;
if(setjmp(p.jumpHandle)) {
c->error = p.err;
*errLine = p.errLine;
return naNil();
}
naParseInit(&p);
p.context = c;
p.srcFile = srcFile;
p.firstLine = firstLine;
p.buf = buf;
p.len = len;
// Lexify, match brace structure, fixup if/for/etc...
naLex(&p);
braceMatch(&p, p.tree.children);
fixBlockStructure(&p, p.tree.children);
// Recursively run the precedence parser, and fixup the treetop
t = parsePrecedence(&p, p.tree.children, p.tree.lastChild, 0);
t->prev = t->next = 0;
p.tree.children = t;
p.tree.lastChild = t;
// Generate code!
codeObj = naCodeGen(&p, &(p.tree), 0);
// Clean up our mess
naParseDestroy(&p);
naVec_append(c->temps, codeObj);
return codeObj;
}