Aria

A low-level systems programming language
git clone git://git.m21c.me/Aria.git
Log | Files | Refs | LICENSE

commit 82b27b67550b5b5d715db6f17b24cfca4cead61b
parent ed3b745b91673943b102e11097e57c7289448601
Author: m21c <ho*******@gmail.com>
Date:   Fri, 27 Jun 2025 21:19:00 +0200

worked on reccord + loops + dispatch

Diffstat:
Mcompiler.c | 453++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 448 insertions(+), 5 deletions(-)

diff --git a/compiler.c b/compiler.c @@ -215,7 +215,10 @@ struct Block Block; entry(ASTMT , SASTMT , 0 , 0 , 0) \ entry(ADECL , SADECL , 0 , 0 , 0) \ entry(ADECLREF , SADECLREF , 0 , 0 , 0) \ + entry(AENV , "env" , 0 , 0 , 0) \ entry(ALOOPUNTIL , SALOOPUNTIL , 0 , 0 , 0) \ + entry(AFOREACH , "for-each" , 0 , 0 , 0) \ + entry(AFORSTEP , "for-step" , 0 , 0 , 0) \ entry(ASCOPE , "scope" , 0 , 0 , 0) \ entry(ALABEL , "label" , 0 , 0 , 0) \ entry(ASWITCH , SASWITCH , 0 , 0 , 0) \ @@ -388,6 +391,8 @@ enum EnvKind { SPARAMLIST, SFUNCTION, SSCOPE, + SIFHEADER, + SLOOPHEADER, SDO, SLOOP, SWHILE, @@ -1141,6 +1146,7 @@ redo: goto redo; } +int auxthen, auxin, auxto, auxstep; int auxself; static int @@ -2194,6 +2200,18 @@ deferfuncenv(Source *source, int keydeclinfunc) return true; } + +static Node * +wrapenv(Node *node, Env *env) +{ + Node *aenv = makenode(node, node); + + aenv->kind = AENV; + aenv->u.env = env; + + return aenv; +} + static void deleteenv(Env *env) { @@ -3138,7 +3156,7 @@ skipnewlineontok(Source *source, Kind kind, int neededindent) if (getkind(source) == LINEDELIM) { savedtok = source->tok; - if (gettok(source) == kind && + if (gettok(source) == (int) kind && source->lastindent >= neededindent) return true; @@ -3149,6 +3167,51 @@ skipnewlineontok(Source *source, Kind kind, int neededindent) } static Node * +readrecordinitfield(Source *source, Type *recordtype) +{ + Node *fieldinit = NULL; + Node savedtok = {0}; + + /* @todo add init-env */ + + skipnewline(source); + + fieldinit = tokennode(source, NULL); + fieldinit->kind = AFIELDINIT; + if (getkind(source) == IDENT) { + savedtok = source->tok; + gettok(source); + if (getkind(source) == COLONDELIM) { + gettok(source); + /* @todo associate field name with field in record type */ + fieldinit->lhs = makenode(&savedtok, NULL); + } else { + pushbacktok(source, &savedtok); + } + } + + fieldinit->rhs = readexpr(source, PASSIGN); + return fieldinit; +} + +static Node * +readrecordinitfieldlist(Source *source, Type *recordtype) +{ + /* @todo add init-env */ + /* @todo check for missing field-initializers */ + Node *init = readrecordinitfield(source, recordtype); + + while (skipnewline(source), getkind(source) == COMMADELIM) { + init = tokennode(source, init); + init->kind = ACOMMA; + gettok(source); + init->rhs = readrecordinitfield(source, recordtype); + } + + return init; +} + +static Node * readatom(Source *source, int flags) { Node *lhs = NULL, *savedis = source->lastis; @@ -3386,6 +3449,7 @@ readatom(Source *source, int flags) indent = source->lastindent; lhs = tokennode(source, NULL); gettok(source); + pushenv(source, SLOOPHEADER); lhs->lhs = stmtlist(source, indent, SLOOP, NULL, false); if (skipnewlineontok(source, KUNTIL, indent)) { @@ -3396,14 +3460,67 @@ readatom(Source *source, int flags) if (lhs->kind != KLOOP) goto joinelse; - else - /* skip postfix-operators */ - return lhs; + + /* skip postfix-operators */ + return wrapenv(lhs, popenv(source)); + + case KFOR: + indent = source->lastindent; + lhs = tokennode(source, NULL); + gettok(source); + + pushenv(source, SLOOPHEADER); + lhs->u.payload = readexpr(source, POR); + if (getkind(source) == IDENT) { + if (source->tok.u.key == auxin) { + Node *aux = tokennode(source, NULL); + aux->lhs = lhs->u.payload; + aux->kind = AFOREACH; + lhs->u.payload = aux; + gettok(source); + aux->rhs = readexpr(source, POR); + } else if (source->tok.u.key == auxto) { + Node *aux = tokennode(source, NULL); + aux->lhs = lhs->u.payload; + aux->kind = AFORSTEP; + lhs->u.payload = aux; + gettok(source); + aux->rhs = readexpr(source, POR); + if (getkind(source) == IDENT + && source->tok.u.key == auxstep) { + gettok(source); + aux->u.payload = readexpr(source, POR); + } else { + aux->u.payload = tokennode(source, NULL); + aux->u.payload->kind = NUMBER; + aux->u.payload->type = primitive(TINFER); + aux->u.payload->u.s = 1; + } + } else if (source->tok.u.key == auxstep) { + Node *aux = tokennode(source, NULL); + aux->lhs = lhs->u.payload; + aux->kind = AFORSTEP; + lhs->u.payload = aux; + gettok(source); + aux->u.payload = readexpr(source, POR); + + /* @note is this even correct? */ + aux->rhs = tokennode(source, NULL); + aux->rhs->kind = NUMBER; + aux->rhs->type = primitive(TINFER); + aux->rhs->u.s = INTMAX_MAX; + } + } + + /* @todo maybe use SFOR instead of SLOOP */ + lhs->lhs = stmtlist(source, indent, SLOOP, NULL, false); + goto joinelse; case KWHILE: indent = source->lastindent; lhs = tokennode(source, NULL); gettok(source); + pushenv(source, SLOOPHEADER); lhs->u.payload = readexpr(source, POR); lhs->lhs = stmtlist(source, indent, SWHILE, NULL, false); goto joinelse; @@ -3412,6 +3529,7 @@ readatom(Source *source, int flags) indent = source->lastindent; lhs = tokennode(source, NULL); gettok(source); + pushenv(source, SIFHEADER); lhs->u.payload = readexpr(source, POR); /* skipnewline(source); */ @@ -3426,7 +3544,7 @@ readatom(Source *source, int flags) } /* skip postfix-operators */ - return lhs; + return wrapenv(lhs, popenv(source)); default: /* joinerror: */ @@ -3446,6 +3564,7 @@ readatom(Source *source, int flags) gettok(source); // source->lastindent = nextindent(source, source->lastindent); + lhs->rhs = readrecordinitfieldlist(source, lhs->type); expect(source, RCURLDELIM, "expected '}'"); } @@ -3513,6 +3632,18 @@ readatom(Source *source, int flags) lhs->rhs = readexpr(source, PRELAT); } + /* skip funtion-call without parentheses when next token is an + * auxiliary keyword */ + if (getkind(source) == IDENT) { + if (source->tok.u.key == auxthen + || source->tok.u.key == auxin + || source->tok.u.key == auxto + || source->tok.u.key == auxstep) { + return lhs; + } + } + + return lhs; } @@ -4036,6 +4167,93 @@ typecheckdecl(Env *env, Decl *decl) } static Node * +substitutedispatch(Env *env, Node *expr) +{ +#if 0 + /* This is wrong (tuple != comma-operator)*/ + assert(expr->u.declref); + assert(expr->type == expr->u.declref->type); + + expr->kind = ACOMMA; + expr->type = maketype(&expr->loc, primitive(TTUPLE), expr->lhs->type); + + expr->rhs->kind = ADECLREF; + expr->rhs->u.declref = expr->u.declref; + expr->rhs->type = expr->type; + + expr->type->u.rtarget = expr->lhs->type; + return expr; +#else + Node *result = makenode(expr, NULL); + result->kind = ADECLREF; + result->u.declref = expr->u.declref; + result->type = expr->type; + + /* @fixme delete doesnt work (self is used multiple times) */ + /* deletenode(expr); */ + return result; +#endif +} + +static Node * +substitutedispatchcall(Env *env, Node *expr) +{ + expr->lhs = substitutedispatch(env, expr->lhs); + + return expr; +} + +static Node * +selfdispatchcall(Env *env, Node *expr) +{ + Node *self, *probe, *insert; + + assert(expr && expr->kind == OCALL); + + if (expr->lhs->kind != ASELFDISP) + return expr; + + self = expr->lhs->lhs; + assert(self); + assert(self->type); + + if (self->type->kind == TSTRUCT || self->type->kind == TUNION) { + self = makenode(self, self); + self->kind = OADDR; + self->type = maketype(&self->loc, primitive(TINT), self->type); + } else if (self->type->kind != TPTR) { + error(&self->loc, "expected struct or union or pointer type"); + } else if (self->type->target->kind != TSTRUCT && + self->type->target->kind != TUNION) { + error(&self->loc, "expected pointer to struct or union type"); + } + + if (!expr->rhs) { + expr->rhs = self; + return substitutedispatchcall(env, expr); + } + + if (expr->rhs->kind != ACOMMA) { + insert = makenode(expr->rhs, self); + insert->kind = ACOMMA; + insert->rhs = expr->rhs; + expr->rhs = insert; + + return substitutedispatchcall(env, expr); + } + + probe = expr->rhs; + while (probe->lhs && probe->lhs->kind == ACOMMA) + probe = probe->lhs; + + insert = makenode(probe, self); + insert->rhs = probe->lhs; + probe->lhs = insert; + + return substitutedispatchcall(env, expr); +} + +static Node * resolvepending(Env *env, Node *expr) { Decl *decl; @@ -4066,6 +4284,134 @@ resolvepending(Env *env, Node *expr) } static Node * +dispatch(Node *expr, Node *parent) +{ + Type *type; + Decl *field; + + /* @note might change in future */ + assert(expr->lhs); + type = expr->lhs->type; + assert(type); + + /* @todo maybe do implicit dereference */ + if (type->kind == TPTR) { + Node *lhs = makenode(expr->lhs, expr->lhs); + lhs->kind = ADEREF; + lhs->type = type->target; + + type = type->target; + expr->lhs = lhs; + } + + if (type->kind != TSTRUCT && type->kind != TUNION) { + error(&expr->lhs->loc, "expected struct or union type"); + return expr; + } + + /* @note might change in future */ + assert(expr->rhs); + assert(expr->rhs->kind == IDENT); + + /* @note improvised for now */ + assert(type->module->contentenv); + field = finddeclinenv(expr->rhs->u.key, type->module->contentenv); + + if (!field) { + const char *typekind = type->kind == TSTRUCT ? "struct" : "union"; + const char *modulename = getstring(idents, type->module->key); + const char *fieldname = getstring(idents, expr->rhs->u.key); + error(&expr->rhs->loc, "%s '%s' has no field '%s'", + typekind, modulename, fieldname); + expr->type = primitive(TERRTYPE); + return expr; + } + + expr->type = field->type; + expr->u.declref = field; + + return expr; +} + +static void +forloop(Env *env, Node *expr, Node *header) +{ + if (header->kind == AFORSTEP) { + Node *init = header->lhs; + if (init->kind == ADECL) { + Decl *it = init->u.declref; + if (!it->u.content) { + if (!isarithtype(it->type)) { + error(&it->loc, "for loop variable must be initialized"); + return; + } + it->u.content = makenode(header, NULL); + it->u.content->kind = NUMBER; + it->u.content->type = it->type; + it->u.content->u.u = 0; + } + + header->lhs = conv(typecheck(env, header->lhs)); + } + + /* @todo do proper typechecking */ + header->rhs = wrap(header->lhs->type, typecheck(env, header->rhs)); + header->u.payload = wrap(header->lhs->type, typecheck(env, header->u.payload)); + return; + } + + if (header->kind == AFOREACH) { + + return; + } + + header = expr->u.payload = conv(typecheck(env, header)); + + if (isarithtype(header->type)) { + Decl *it; + Node *forstep; + + + it = makedecl2(&header->loc, env, + getstringkey(&idents, "it", 2), DVAR); + it->type = header->type; + it->u.content = makenode(header, NULL); + it->u.content->kind = NUMBER; + it->u.content->type = header->type; + it->u.content->u.u = 0; + + forstep = makenode(header, NULL); + forstep->kind = AFORSTEP; + forstep->type = header->type; + + forstep->lhs = makenode(header, NULL); + forstep->lhs->kind = ADECL; + forstep->lhs->u.declref = it; + + forstep->rhs = header; + + forstep->u.payload = makenode(header, NULL); + forstep->u.payload->kind = NUMBER; + forstep->u.payload->type = header->type; + /* @todo handle negative step when possible + * (i.e. when header is signed and constant) */ + if (isfloattype(header->type)) + forstep->u.payload->u.d = 1.0; + else + forstep->u.payload->u.u = 1; + + expr->u.payload = forstep; + return; + } + + /* @todo handle other cases (e.g. arrays/strings/lists/iterators) */ + error(&expr->loc, "invalid loop header"); +} + +Node *parentnodes[1024]; +int parenttop = 0; + +static Node * typecheck(Env *env, Node *expr) { #define return return --parenttop, @@ -4314,6 +4660,22 @@ typecheck(Env *env, Node *expr) expr->type = primitive(TVOID); return expr; + case KFOR: + assert(expr->u.payload); + forloop(env, expr, expr->u.payload); + + if (lhs) + expr->lhs = typecheck(env, lhs); + + if (rhs) + expr->rhs = typecheck(env, rhs); + + /* @todo infer type of the for-loop like in the case above. */ + + expr->type = primitive(TVOID); + return expr; + + case AENV: case ASCOPE: assert(lhs); assert(expr->u.env); @@ -4351,6 +4713,20 @@ typecheck(Env *env, Node *expr) expr->lhs = conv(lhs); return expr; + case ACOMPOUND: + assert(lhs); + assert(rhs); + + expr->lhs = typecheck(env, lhs); + expr->type = lhs->type; + expr->rhs = typecheck(env, rhs); + return expr; + + case AFIELDINIT: + assert(expr->rhs); + expr->rhs = typecheck(env, rhs); + return expr; + case IDENT: return resolvepending(env, expr); @@ -4453,6 +4829,7 @@ typecheck(Env *env, Node *expr) #undef errortype #undef reporton + #undef return } static Node * @@ -5842,11 +6219,23 @@ printexpr(FILE *out, Node *expr, int indent) break; + case CHAR: + /* @todo print chars correctly */ + n += highlight(out, HLCHAR); + n += fprintf(out, "'%c'", (uchar) expr->u.u); + break; + case STRING: n += highlight(out, HLSTRING); n += printstring(out, expr); break; + case TYPE: + /* @note might be unnecessary (only used for ACOMPOUND) */ + n += highlight(out, HLTYPE); + n += printtype(out, expr->type, indent); + break; + case ADECLREF: printdeclname(out, expr->u.declref, false); break; @@ -5915,6 +6304,31 @@ printexpr(FILE *out, Node *expr, int indent) n += fprintf(out, "while "); goto joinifbody; + case KFOR: + n += highlight(out, HLKEYWORD); + n += fprintf(out, "for "); + goto joinifbody; + + case AFORSTEP: + n += printexpr(out, expr->lhs, indent); + n += highlight(out, HLKEYWORD); + n += fprintf(out, " to "); + n += printexpr(out, expr->rhs, indent); + + assert(expr->u.payload); + if (expr->u.payload->kind == NUMBER && false) { + if (isfloattype(expr->u.payload->type) + && expr->u.payload->u.d == 1.0) + break; + if (isinttype(expr->u.payload->type) + && expr->u.payload->u.u == 1) + break; + } + n += highlight(out, HLKEYWORD); + n += fprintf(out, " step "); + n += printexpr(out, expr->u.payload, indent); + break; + case KIF: n += highlight(out, HLKEYWORD); n += fprintf(out, "if "); @@ -6028,6 +6442,10 @@ printexpr(FILE *out, Node *expr, int indent) n += printexpr(out, expr->lhs, indent); n += fprintf(out, "\n"); /* blank line */ break; + + case AENV: + n += printexpr(out, expr->lhs, indent); + break; case ACONV: n += highlight(out, HLNUMBER); @@ -6057,6 +6475,31 @@ printexpr(FILE *out, Node *expr, int indent) n += fputs("}", out); break; + case ACOMPOUND: + n += highlight(out, HLTYPE); + n += printtype(out, expr->type, indent); + n += highlight(out, HLDELIM); + n += fputs("{", out); + n += printexpr(out, expr->rhs, indent); + n += highlight(out, HLDELIM); + n += fputs("}", out); + break; + + case AFIELDINIT: + if (expr->lhs) { + n += highlight(out, HLDELIM); + n += fprintf(out, "%s: ", getstring(idents, expr->u.key)); + } + n += printexpr(out, expr->rhs, indent); + break; + + case ASELFDISP: + n += printexpr(out, expr->lhs, indent); + n += highlight(out, HLNONE); + /* @todo use printexpr when typechecked */ + n += fprintf(out, ":%s", getstring(idents, expr->rhs->u.key)); + break; + default: n += highlight(out, HLINFO); n += fprintf(out, "node(%u)", expr->kind);