#include <setjmp.h>
+#include <string.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 5
+#define MAX_PREC_TOKS 6
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 },
void naParseError(struct Parser* p, char* msg, int line)
{
+ // Some errors (e.g. code generation of a null pointer) lack a
+ // line number, so we throw -1 and set the line earlier.
+ if(line > 0) p->errLine = line;
p->err = msg;
- p->errLine = line;
longjmp(p->jumpHandle, 1);
}
return t;
}
+// Synthesize a curly brace token to wrap token t foward to the end of
+// "statement". FIXME: unify this with the addNewChild(), which does
+// very similar stuff.
+static void embrace(struct Parser* p, struct Token* t)
+{
+ struct Token *b, *end = t;
+ if(!t) return;
+ while(end->next) {
+ if(end->next->type == TOK_SEMI) {
+ // Slurp up the semi, iff it is followed by an else/elsif,
+ // otherwise leave it in place.
+ if(end->next->next) {
+ if(end->next->next->type == TOK_ELSE) end = end->next;
+ if(end->next->next->type == TOK_ELSIF) end = end->next;
+ }
+ break;
+ }
+ if(end->next->type == TOK_COMMA) break;
+ if(end->next->type == TOK_ELSE) break;
+ if(end->next->type == TOK_ELSIF) break;
+ end = end->next;
+ }
+ b = emptyToken(p);
+ b->type = TOK_LCURL;
+ b->line = t->line;
+ b->parent = t->parent;
+ b->prev = t->prev;
+ b->next = end->next;
+ b->children = t;
+ b->lastChild = end;
+ if(t->prev) t->prev->next = b;
+ else b->parent->children = b;
+ if(end->next) end->next->prev = b;
+ else b->parent->lastChild = b;
+ t->prev = 0;
+ end->next = 0;
+ for(; t; t = t->next)
+ t->parent = b;
+}
+
+#define NEXT(t) (t ? t->next : 0)
+#define TYPE(t) (t ? t->type : -1)
+
+static void fixBracelessBlocks(struct Parser* p, struct Token* t)
+{
+ // Find the end, and march *backward*
+ while(t && t->next) t = t->next;
+ for(/**/; t; t=t->prev) {
+ switch(t->type) {
+ case TOK_FOR: case TOK_FOREACH: case TOK_FORINDEX: case TOK_WHILE:
+ case TOK_IF: case TOK_ELSIF:
+ if(TYPE(NEXT(t)) == TOK_LPAR && TYPE(NEXT(NEXT(t))) != TOK_LCURL)
+ embrace(p, t->next->next);
+ break;
+ case TOK_ELSE:
+ if(TYPE(NEXT(t)) != TOK_LCURL)
+ embrace(p, t->next);
+ break;
+ case TOK_FUNC:
+ if(TYPE(NEXT(t)) == TOK_LPAR) {
+ if(TYPE(NEXT(NEXT(t))) != TOK_LCURL)
+ embrace(p, NEXT(NEXT(t)));
+ } else if(TYPE(NEXT(t)) != TOK_LCURL)
+ embrace(p, t->next);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
// 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;
+ fixBracelessBlocks(p, start);
t = start;
while(t) {
switch(t->type) {
- case TOK_ELSE: case TOK_FUNC:
+ 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_WHILE:
+ 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);
|| t->prev->type == TOK_LCURL)
addSemi = 1;
break;
- case TOK_FOR: case TOK_FOREACH: case TOK_WHILE:
+ case TOK_FOR: case TOK_FOREACH: case TOK_FORINDEX: case TOK_WHILE:
addSemi = 1;
break;
case TOK_FUNC:
addSemi = 1;
break;
}
+ if(!t->next || t->next->type == TOK_SEMI || t->next->type == TOK_COMMA)
+ addSemi = 0; // don't bother, no need
if(addSemi) {
struct Token* semi = emptyToken(p);
semi->type = TOK_SEMI;
semi->prev = t;
semi->parent = t->parent;
if(semi->next) semi->next->prev = semi;
+ else semi->parent->lastChild = semi;
t->next = semi;
t = semi; // don't bother checking the new one
}
{
return t == TOK_IF || t == TOK_ELSIF || t == TOK_ELSE
|| t == TOK_FOR || t == TOK_FOREACH || t == TOK_WHILE
- || t == TOK_FUNC;
+ || t == TOK_FUNC || t == TOK_FORINDEX;
}
static void precChildren(struct Parser* p, struct Token* t);
if(!top)
return parsePrecedence(p, start, end, level+1);
+ top->rule = rule;
+
if(left) {
left->next = right;
left->prev = 0;
struct Parser p;
// Protect from garbage collection
- naVec_append(c->temps, srcFile);
+ naTempSave(c, srcFile);
// Catch parser errors here.
*errLine = 0;
if(setjmp(p.jumpHandle)) {
- c->error = p.err;
+ strncpy(c->error, p.err, sizeof(c->error));
*errLine = p.errLine;
return naNil();
}
p.tree.lastChild = t;
// Generate code!
- codeObj = naCodeGen(&p, &(p.tree));
+ codeObj = naCodeGen(&p, &(p.tree), 0);
// Clean up our mess
naParseDestroy(&p);
- naVec_append(c->temps, codeObj);
+ naTempSave(c, codeObj);
return codeObj;
}
-
-