shithub: lpa

ref: dcb761aebd2815fdf2d04c0b05724292de9dc98b
dir: /parse.c/

View raw version
#include <u.h>
#include <libc.h>
#include <thread.h>

#include "dat.h"
#include "fns.h"

static void _Noreturn error(TokenList *, char *);
static int peek(TokenList *);
static int peekclass(TokenList *);
static void match(TokenList *, int);
static void addchild(Ast *, Ast *);
static int issep(TokenList *t);
static int nameclass(char *, Ast *);

static void parsesep(TokenList *);
static void parseseps(TokenList *, int);
static Ast *parseprog(TokenList *);
static Ast *parsefuncdef(TokenList *);
static Ast *parsefuncheader(TokenList *);
static Ast *parselocals(TokenList *);
static Ast *parseexpr(TokenList *, Ast *);
static Ast *parseexprsub(TokenList *);
static Ast *parseline(TokenList *);
static Ast *parsename(TokenList *);
static Ast *parsefunc(TokenList *);
static Ast *parseconst(TokenList *);

Ast *
parse(TokenList *tokens, char **errp)
{
	Ast *ast;

	tokens->offset = 0;
	tokens->err = nil;
	if(setjmp(tokens->errbuf)){
		*errp = tokens->err;
		return nil;
	}else{
		ast = parseprog(tokens);
		match(tokens, TokEnd);
	}
	return ast;
}

static void _Noreturn
error(TokenList *tokens, char *msg)
{
	tokens->err = msg;
	longjmp(tokens->errbuf, 1);
}

static int
peek(TokenList *tokens)
{
	if(tokens->offset >= tokens->count)
		error(tokens, "unexpected end of token stream");

	return tokens->tokens[tokens->offset].tag;
}

static int
peekclass(TokenList *tokens)
{
	peek(tokens); /* triggers an error if we are at the end */
	return tokens->tokens[tokens->offset].nameclass;
}

static void
match(TokenList *tokens, int tag)
{
	if(peek(tokens) != tag)
		error(tokens, "Unexpected token (match failed)");
	tokens->offset++;
}

static void
addchild(Ast *ast, Ast *child)
{
	ast->childcount++;
	ast->children = allocextra(ast, sizeof(Ast *) * ast->childcount);
	ast->children[ast->childcount-1] = child;
}

static int
issep(TokenList *t)
{
	switch(peek(t)){
	case TokNewline:
	case TokDiamond:
		return 1;
	default:
		return 0;
	}
}

static int
nameclass(char *name, Ast *func)
{
	int class;

	if(func == nil)
		class = NameclassUndef;
	else if(strcmp(name, func->funcname->name) == 0)
		class = NameclassFunc;
	else if(func->funcresult && strcmp(name, func->funcresult->name) == 0)
		class = NameclassLocal;
	else if(func->funcleftarg && strcmp(name, func->funcleftarg->name) == 0)
		class = NameclassLocal;
	else if(func->funcrightarg && strcmp(name, func->funcrightarg->name) == 0)
		class = NameclassLocal;
	else{
		/* Check if the name exist in the locallist */
		class = NameclassUndef;
	}
	return class;
}

static void
parsesep(TokenList *t)
{
	if(issep(t))
		match(t, peek(t));
}

static void
parseseps(TokenList *t, int required)
{
	while(issep(t)){
		match(t, peek(t));
		required = 0;
	}
	if(required)
		match(t, TokNewline);
}

static Ast *
parseprog(TokenList *t)
{
	Ast *prog = alloc(DataAst);
	prog->tag = AstProg;

	while(peek(t) != TokEnd){
		Ast *child;
		if(peek(t) == TokDel)
			child = parsefuncdef(t);
		else
			child = parseexpr(t, nil);
		print("After expr: %d\n", peek(t));
		if(peek(t) != TokEnd)
			parseseps(t, 1);
		addchild(prog, child);
	}
	print("got prog, peek is: %d\n", peek(t));
	return prog;
}

static Ast *
parsefuncdef(TokenList *t)
{
	Ast *func = parsefuncheader(t);
	while(peek(t) != TokDel){
		Ast *expr = parseexpr(t, func);
		addchild(func, expr);
		if(peek(t) != TokDel)
			parseseps(t, 1);
	}
	match(t, TokDel);
	return func;
}

static Ast *
parsefuncheader(TokenList *t)
{
	Ast *func = alloc(DataAst);
	func->tag = AstFunc;

	match(t, TokDel);

	func->funcname = parsename(t);
	if(peek(t) == TokLarrow){
		match(t, TokLarrow);
		func->funcresult = func->funcname;
		func->funcname = parsename(t);
	}
	if(peek(t) == TokName)
		func->funcrightarg = parsename(t);
	if(peek(t) == TokName){
		func->funcleftarg = func->funcname;
		func->funcname = func->funcrightarg;
		func->funcrightarg = parsename(t);
	}
	func->funclocals = parselocals(t);

	return func;
}

static Ast *
parselocals(TokenList *t)
{
	Ast *locals = alloc(DataAst);
	locals->tag = AstLocals;
	while(peek(t) == TokSemi){
		match(t, TokSemi);
		Ast *name = parsename(t);
		name->nameclass = NameclassLocal;
		addchild(locals, name);
	}
	parseseps(t, 1);
	return locals;
}

static Ast *
parseexpr(TokenList *t, Ast *func)
{
	uvlong start, end;
	vlong depth;

	depth = 0;
	start = t->offset;
	while(!(issep(t) || (peek(t) == TokDel) || (peek(t) == TokEnd)) || depth != 0){
		switch(peek(t)){
		case TokLparen: depth++; break;
		case TokRparen: depth--; break;
		}
		match(t, peek(t));
	}
	end = t->offset;
	t->offset = start;

	for(uvlong i = start; i < end; i++){
		char *name;
		int class;

		if(t->tokens[i].tag != TokName)
			continue;
		name = t->tokens[i].name;
		class = nameclass(name, func);
		t->tokens[i].nameclass = class;
		if(class == 0)
			error(t, "parseexpr() can't deal with free variables yet");
	}

	/* We know the nameclass of each name, and assume that the nameclasses do not change.
	 * Now create the AST.
	 */
	return parseexprsub(t);
}

static Ast *
parseexprsub(TokenList *t)
{
	Ast *expr, *val;

	if(peek(t) == TokLparen){
		match(t, TokLparen);
		val = parseexprsub(t);
		match(t, TokRparen);
	}else
		val = nil;

	if(peekclass(t) == NameclassFunc){
func:
		expr = alloc(DataAst);
		if(val){
			expr->tag = AstDyadic;
			expr->left = val;
		}else
			expr->tag = AstMonadic;
		expr->func = parsefunc(t);
		expr->right = parseexprsub(t);
		return expr;
	}

	if(peek(t) == TokName){
		val = parsename(t);
		if(peek(t) == TokLarrow){
			match(t, TokLarrow);
			expr = alloc(DataAst);
			expr->tag = AstAssign;
			expr->left = val;
			expr->right = parseexprsub(t);
			return expr;
		}
	}

	/* We need a value now. Stranding is not implemented */
	if(val == nil)
		val = parseconst(t);

	if(peekclass(t) == NameclassFunc)
		goto func;
	
	return val;
}

static Ast *
parsename(TokenList *t)
{
	Ast *name = alloc(DataAst);
	name->tag = AstName;
	name->name = t->tokens[t->offset].name;
	match(t, TokName);
	return name;
}

static Ast *
parsefunc(TokenList *t)
{
	/* TODO: parse primitives as well */
	Ast *func;
	if(peek(t) == TokName && peekclass(t) == NameclassFunc)
		func = parsename(t);
	else{
		func = alloc(DataAst);
		func->tag = AstPrim;
		func->prim = t->tokens[t->offset].prim;
		match(t, TokPrimitive);
	}

	return func;
}

static Ast *
parseconst(TokenList *t)
{
	Ast *val = alloc(DataAst);
	val->tag = AstConst;

	vlong num = t->tokens[t->offset].num;
	match(t, TokNumber);
	val->val = allocarray(TypeNumber, 0, 1);
	setint(val->val, 0, num);

	return val;
}