ref: 2b23d05d57743af57385cd42c0fd2d223b11d8c8
dir: /parse.c/
#include <u.h> #include <libc.h> #include <thread.h> #include "dat.h" #include "fns.h" 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 isexprsep(TokenList *t); static int nameclass(char *, Symtab *, Ast *); static void parsesep(TokenList *); static void parseseps(TokenList *, int); static Ast *parseprog(TokenList *); static Ast *parsefuncdef(TokenList *); static Ast *parsefuncheader(TokenList *); static Ast *parsefuncleftopt(TokenList *); static Ast *parselocals(TokenList *); static Ast *parseexpr(TokenList *, Symtab *, 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, Symtab *symtab) { Ast *ast; tokens->offset = 0; if(symtab) ast = parseexpr(tokens, symtab, nil); else ast = parseprog(tokens); match(tokens, TokEnd); return ast; } static int peek(TokenList *tokens) { if(tokens->offset >= tokens->count) error(ESyntax, "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(ESyntax, "Unexpected token: %s", printtok(tokens->tokens[tokens->offset])); 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 isexprsep(TokenList *t) { switch(peek(t)){ case TokNewline: case TokDiamond: case TokDel: case TokEnd: return 1; default: return 0; } } static int nameclass(char *name, Symtab *s, Ast *func) { int class = NameclassUndef; 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; if(s && class == NameclassUndef){ uvlong symid = sym(s, name); void *val = symval(s, symid); if(val) switch(getalloctag(val)){ case DataArray: class = NameclassArray; break; case DataFunction: class = NameclassFunc; break; /* more cases here in the future */ } } /* TODO: Check if the name exist in the locallist */ 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 = nil; switch(peek(t)){ case TokDiamond: case TokNewline: break; case TokDel: child = parsefuncdef(t); break; default: child = parseexpr(t, nil, nil); break; } if(peek(t) != TokEnd) parseseps(t, 1); if(child) addchild(prog, child); } return prog; } static Ast * parsefuncdef(TokenList *t) { Ast *func = parsefuncheader(t); while(peek(t) != TokDel){ Ast *expr = parseexpr(t, nil, 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->funcleftarg = parsefuncleftopt(t); /* optional left arg */ func->funcname = parsename(t); if(peek(t) == TokLarrow && !func->funcleftarg){ match(t, TokLarrow); func->funcresult = func->funcname; func->funcleftarg = parsefuncleftopt(t); func->funcname = parsename(t); } if(peek(t) == TokName) func->funcrightarg = parsename(t); if(peek(t) == TokName && !func->funcleftarg){ func->funcleftarg = func->funcname; func->funcname = func->funcrightarg; func->funcrightarg = parsename(t); } func->funclocals = parselocals(t); return func; } static Ast * parsefuncleftopt(TokenList *t) { Ast *name = nil; if(peek(t) == TokLbrace){ match(t, TokLbrace); name = parsename(t); name->optional = 1; match(t, TokRbrace); } return name; } 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, Symtab *symtab, Ast *func) { uvlong start, end; vlong depth; depth = 0; start = t->offset; while(!isexprsep(t) || 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; if((i+1) < end && t->tokens[i+1].tag == TokLarrow) continue; /* assignment */ name = t->tokens[i].name; class = nameclass(name, symtab, func); t->tokens[i].nameclass = class; if(class == 0){ /* We don't know how to parse it until runtime */ if(symtab) error(EValue, "%s is undefined", name); uvlong count = end-start; Ast *later = alloc(DataAst); later->tag = AstLater; later->tokens = alloc(DataTokenList); later->tokens->count = count+1; later->tokens->tokens = allocextra(later->tokens, sizeof(Token) * later->tokens->count); for(i = 0; i < count; i++){ later->tokens->tokens[i] = t->tokens[start+i]; match(t, peek(t)); } later->tokens->tokens[count].tag = TokEnd; return later; } } /* 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, *strand; strand = nil; again: if(peek(t) == TokLparen){ match(t, TokLparen); val = parseexprsub(t); match(t, TokRparen); }else val = nil; if(peekclass(t) == NameclassFunc){ func: expr = alloc(DataAst); expr->func = parsefunc(t); if(val == nil && (isexprsep(t) || peek(t) == TokRparen)) expr->tag = AstNiladic; else{ if(val){ expr->tag = AstDyadic; expr->left = val; }else expr->tag = AstMonadic; expr->right = parseexprsub(t); } val = expr; goto end; } 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); val = expr; goto end; } } /* We need a value now */ if(val == nil) val = parseconst(t); if(peekclass(t) == NameclassFunc){ if(strand){ addchild(strand, val); val = strand; strand = nil; } goto func; } if(!(isexprsep(t) || peek(t) == TokRparen)){ /* Stranding */ if(!strand){ strand = alloc(DataAst); strand->tag = AstStrand; } addchild(strand, val); goto again; } end: if(strand){ addchild(strand, val); val = strand; } 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) { 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, len; Rune *str; switch(peek(t)){ case TokNumber: num = t->tokens[t->offset].num; val->val = allocarray(TypeNumber, 0, 1); setint(val->val, 0, num); break; case TokString: str = t->tokens[t->offset].string; len = runestrlen(str); if(len == 1) val->val = allocarray(TypeChar, 0, len); else{ val->val = allocarray(TypeChar, 1, len); setshape(val->val, 0, len); } for(uvlong i = 0; i < len; i++) setchar(val->val, i, str[i]); break; default: match(t, TokNumber); /* TODO: syntax error could be better here */ } match(t, peek(t)); return val; }