Aria

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

commit aea94da4800cedfc179539b45fc8c425f2dc7591
Author: m21c <ho*******@gmail.com>
Date:   Sun, 28 Mar 2021 16:01:16 +0200

initial commit

Diffstat:
A.gitignore | 7+++++++
Aaria.c | 2027+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 2034 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,7 @@ +.oldcode/* +.vscode/* +bin/* +test/* + +debug*.cmd +debug*.sh diff --git a/aria.c b/aria.c @@ -0,0 +1,2027 @@ +#include <assert.h> +#include <ctype.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> + + +static int mystrncasecmp(const char *str1, const char *str2, size_t max_len) +{ + char tmp1[] = {'\0', '\0'}; + char tmp2[] = {'\0', '\0'}; + char c1, c2; + int result; + + size_t i; + + if (max_len == 0) { + size_t len1 = strlen(str1); + size_t len2 = strlen(str2); + max_len = len1 > len2 ? len1 : len2; + } + + for (i = 0; i < max_len; ++i) { + c1 = tolower(str1[i]); + c2 = tolower(str2[i]); + if (c1 == '\0' && c2 == '\0') return 0; + tmp1[0] = c1; + tmp2[0] = c2; + result = strcmp(tmp1, tmp2); + if (result != 0) return result; + } + + return 0; +} + +static int mystrcasecmp(const char *str1, const char *str2) +{ + char tmp1[] = {'\0', '\0'}; + char tmp2[] = {'\0', '\0'}; + char c1, c2; + int result; + + size_t i; + + for (i = 0;; ++i) { + c1 = tolower(str1[i]); + c2 = tolower(str2[i]); + if (c1 == '\0' && c2 == '\0') return 0; + tmp1[0] = c1; + tmp2[0] = c2; + result = strcmp(tmp1, tmp2); + if (result != 0) return result; + } + + return 0; +} + +/* - forward declarations - */ + +typedef struct Node Node; + +/* - type struct - */ + +enum { + TVOID, TBOOL, + + TU8, TS8, TU16, TS16, TU32, TS32, TU64, TS64, + + TF32, TF64, + + TPTR, TARRAY, + + TMAX +}; + +#define TUCHAR TU8 +#define TCHAR TS8 +#define TUSHORT TU16 +#define TSHORT TS16 +#define TUINT TU32 +#define TINT TS32 +#define TULONG TU64 +#define TLONG TS64 +#define TULLONG TU64 +#define TLLONG TS64 + +#define TFLOAT TF32 +#define TDOUBLE TF64 +#define TLDOUBLE TF64 + +#define TUSIZE TU64 +#define TSSIZE TS64 +/* TODO(m21c): maybe add long double type ? */ + +typedef struct Type Type; + +struct Type { + int kind; + + size_t size, align; + + union { + struct { + int offset, size; + } bit; + struct { + size_t length; + size_t elemsize; + } array; + Node *val; + } u; + + Type *target; +}; + +Type prim[] = { + [TVOID] = {TVOID, 0, 0, {0}, NULL}, + [TBOOL] = {TBOOL, 1, 1, {0}, NULL}, + + [TU8] = {TU8, 1, 1, {0}, NULL}, + [TS8] = {TS8, 1, 1, {0}, NULL}, + [TU16] = {TU16, 2, 2, {0}, NULL}, + [TS16] = {TS16, 2, 2, {0}, NULL}, + [TU32] = {TU32, 4, 4, {0}, NULL}, + [TS32] = {TS32, 4, 4, {0}, NULL}, + [TU64] = {TU64, 8, 8, {0}, NULL}, + [TS64] = {TS64, 8, 8, {0}, NULL}, + + [TF32] = {TF32, 4, 4, {0}, NULL}, + [TF64] = {TF64, 8, 8, {0}, NULL}, +}; + +Type typebuf[4096]; +int typetop; + +Type *maketype(void) { + return typebuf + typetop++; +} + +/* - pre-lexer - */ + +char line[4096]; +int currline, lastline; +long linepos; + +bool mygetline(FILE *in) { + int i, l, c = getc(in); + + linepos = ftell(in); + +advance: + ++currline; + + i = 0, l = 0; + while (c == '\r' || c == '\n') { + l = c, c = getc(in); + + if (l == '\r' && c == '\n') + c = getc(in); + + ++currline; + } + + lastline = currline; + + while (c != EOF && c != '\n' && c != '\r') { + line[i++] = c; + c = getc(in); + + if (c == '\\') { + int x = getc(in); + if (x == '\n') { + c = getc(in); + ++currline; + } else if (x == '\r') { + int y = getc(in); + c = (y == '\n') ? getc(in) : y; + ++currline; + } else if (x == EOF) { + c = x; + } else { + ungetc(x, in); + } + } + } + + if (c == '\r') { + int x = getc(in); + if (x != '\n') + ungetc(x, in); + } + + if (c != EOF && i == 0) goto advance; + + line[i] = 0; + return c != EOF || i; +} + +/* - lexer - */ + +enum { + OSUFINC, OSUFDEC, OARRAY, OCALL, ODISP, + + ODEREF, OINC, ODEC, OBNOT, OLNOT, OFLIP, OADDR, OPLUS, OMINUS, + OCAST, + + OMUL, ODIV, OMOD, OLSH, OARSH, ORSH, OBAND, + + OADD, OSUB, OBOR, OXOR, + + ORANGE, + + OLEQ, OLET, OGEQ, OGRT, ONEQ, OEQU, OIDENT, + + OLAND, + + OLOR, + + OASS, + OMULA, ODIVA, OMODA, OLSHA, OARSHA, ORSHA, OANDA, + OADDA, OSUBA, OORA, OXORA, +}; + +enum { + PUNSUF = 9, + PUNARY = 8, + PMUL = 7, + PADD = 6, + PRANGE = 5, + PRELAT = 4, + PAND = 3, + POR = 2, + PASSIGN = 1, + + PSTART = 0 +}; + +typedef struct Op { + const char *debugstr; + const char *str; + + int num; + int prec; + bool rassoc; +} Op; + +const Op ops[] = { + [OSUFINC] = {"unary++", "++", 1, PUNSUF, false}, + [OSUFDEC] = {"unary--", "++", 1, PUNSUF, false}, + [OARRAY] = {"unary[]", "", 1, PUNSUF, false}, + [OCALL] = {"unary()", "", 1, PUNSUF, false}, + [ODISP] = {"unary._", ".", 1, PUNSUF, false}, + + [ODEREF] = {"*unary", "*", 1, PUNARY, true}, + [OINC] = {"++unary", "++", 1, PUNARY, true}, + [ODEC] = {"--unary", "--", 1, PUNARY, true}, + [OBNOT] = {"~unary", "~", 1, PUNARY, true}, + [OLNOT] = {"!unary", "!", 1, PUNARY, true}, + [OFLIP] = {"~=unary", "~=", 1, PUNARY, true}, + [OADDR] = {"&unary", "&", 1, PUNARY, true}, + [OPLUS] = {"+unary", "+", 1, PUNARY, true}, + [OMINUS] = {"-unary", "-", 1, PUNARY, true}, + [OCAST] = {"(type) unary", "", 1, PUNARY, true}, + + [OMUL] = {"*", "*", 2, PMUL, false}, + [ODIV] = {"/", "/", 2, PMUL, false}, + [OMOD] = {"%", "%", 2, PMUL, false}, + [OLSH] = {"<<", "<<", 2, PMUL, false}, + [OARSH] = {">>>", ">>>", 2, PMUL, false}, + [ORSH] = {">>", ">>", 2, PMUL, false}, + [OBAND] = {"&", "&", 2, PMUL, false}, + + [OADD] = {"+", "+", 2, PADD, false}, + [OSUB] = {"-", "-", 2, PADD, false}, + [OBOR] = {"|", "|", 2, PADD, false}, + [OXOR] = {"^", "^", 2, PADD, false}, + + [ORANGE] = {"..", "..", 2, PRANGE, false}, + + [OLEQ] = {"<=", "<=", 2, PRELAT, false}, + [OLET] = {"<", "<", 2, PRELAT, false}, + [OGEQ] = {">=", ">=", 2, PRELAT, false}, + [OGRT] = {">", ">", 2, PRELAT, false}, + [ONEQ] = {"!=", "!=", 2, PRELAT, false}, + [OEQU] = {"==", "==", 2, PRELAT, false}, + [OIDENT] = {"===", "===", 2, PRELAT, false}, + + [OLAND] = {"&&", "&&", 2, PAND, false}, + + [OLOR] = {"||", "||", 2, POR, false}, + + [OASS] = {"=", "=", 2, PASSIGN, true}, + [OMULA] = {"*=", "*=", 2, PASSIGN, true}, + [ODIVA] = {"/=", "/=", 2, PASSIGN, true}, + [OMODA] = {"%=", "%=", 2, PASSIGN, true}, + [OLSHA] = {"<<=", "<<=", 2, PASSIGN, true}, + [OARSHA] = {">>>=", ">>>=", 2, PASSIGN, true}, + [ORSHA] = {">>=", ">>=", 2, PASSIGN, true}, + [OANDA] = {"&=", "&=", 2, PASSIGN, true}, + [OADDA] = {"+=", "+=", 2, PASSIGN, true}, + [OSUBA] = {"-=", "-=", 2, PASSIGN, true}, + [OORA] = {"|=", "|=", 2, PASSIGN, true}, + [OXORA] = {"^=", "^=", 2, PASSIGN, true} +}; + +#define lengthof(array) ((int) sizeof(array) / (int) sizeof (*(array))) + +typedef struct Keyword { + const char *str; + int len; + + bool isop; + int opid; + + bool istype; + int typeid; +} Keyword; + +enum { + KVOID, KBOOL, + + KU8, KS8, KU16, KS16, KU32, KS32, KU64, KS64, + + KF32, KF64, + + KUCHAR, KCHAR, KUSHORT, KSHORT, KUINT, KINT, + KULONG, KLONG, KULLONG, KLLONG, + + KFLOAT, KDOUBLE, KLDOUBLE, + + KUSIZE, KSSIZE, + + KUSE, + KNOT, KAND, KOR, + KIS, + + KEXTERN, KINTERN, KSTATIC, KCONST, KVAR, + + KBREAK, KCONTINUE, KGOTO, KRETURN, + KIF, KELSE, KCASE, KOF, KDO, + + KFOR, KLOOP, KWHILE, KUNTIL +}; + +struct Keyword keywords[] = { + {"void", 0, false, 0, true, TVOID}, + {"bool", 0, false, 0, true, TBOOL}, + + {"u8", 0, false, 0, true, TU8}, + {"s8", 0, false, 0, true, TS8}, + {"u16", 0, false, 0, true, TU16}, + {"s16", 0, false, 0, true, TS16}, + {"u32", 0, false, 0, true, TU32}, + {"s32", 0, false, 0, true, TS32}, + {"u64", 0, false, 0, true, TU64}, + {"s64", 0, false, 0, true, TS64}, + {"f32", 0, false, 0, true, TF32}, + {"f64", 0, false, 0, true, TF64}, + + {"uchar", 0, false, 0, true, TUCHAR}, + {"char", 0, false, 0, true, TCHAR}, + {"ushort", 0, false, 0, true, TUSHORT}, + {"short", 0, false, 0, true, TSHORT}, + {"uint", 0, false, 0, true, TUINT}, + {"int", 0, false, 0, true, TINT}, + {"ulong", 0, false, 0, true, TULONG}, + {"long", 0, false, 0, true, TLONG}, + {"ullong", 0, false, 0, true, TULLONG}, + {"llong", 0, false, 0, true, TLLONG}, + {"float", 0, false, 0, true, TFLOAT}, + {"double", 0, false, 0, true, TDOUBLE}, + {"ldouble", 0, false, 0, true, TLDOUBLE}, + {"usize", 0, false, 0, true, TUSIZE}, + {"ssize", 0, false, 0, true, TSSIZE}, + + {"use", 0, false, 0, false, 0}, + {"not", 0, false, 0, false, 0}, + {"and", 0, true , OLAND, false, 0}, + {"or", 0, true , OLOR, false, 0}, + {"is", 0, false, 0, false, 0}, + + {"extern", 0, false, 0, false, 0}, + {"intern", 0, false, 0, false, 0}, + {"static", 0, false, 0, false, 0}, + {"const", 0, false, 0, false, 0}, + {"var", 0, false, 0, false, 0}, + + {"break", 0, false, 0, false, 0}, + {"continue", 0, false, 0, false, 0}, + {"goto", 0, false, 0, false, 0}, + {"return", 0, false, 0, false, 0}, + {"if", 0, false, 0, false, 0}, + {"else", 0, false, 0, false, 0}, + {"case", 0, false, 0, false, 0}, + {"of", 0, false, 0, false, 0}, + {"do", 0, false, 0, false, 0}, + + {"for", 0, false, 0, false, 0}, + {"loop", 0, false, 0, false, 0}, + {"while", 0, false, 0, false, 0}, + {"until", 0, false, 0, false, 0}, +}; + +#define KEYWORD_MAP_SIZE 128 +const char *keywordkeys[KEYWORD_MAP_SIZE]; +int keywordvals[KEYWORD_MAP_SIZE]; + +int strnhash(const char *str, int n) { + int hash = 5381, i; + for (i = 0; i < n && str[i]; ++i) + hash = (hash << 5) + hash + str[i]; + return hash; +} + +void initkeywords(void) { + int i, j, h; + for (i = 0; i < lengthof(keywords); ++i) { + int n = keywords[i].len = strlen(keywords[i].str); + h = strnhash(keywords[i].str, n) & (lengthof(keywordkeys) - 8); + for (j = 0; j < 8; ++j, ++h) { + if (!keywordkeys[h]) { + keywordkeys[h] = keywords[i].str; + keywordvals[h] = i; + goto nextkeyword; + } + } + + fprintf(stderr, "bug: keyword hash-map is too small\n"); + abort(); + nextkeyword: + (void) 0; + } + + /* + for (i = 0; i < lengthof(keywordkeys); ++i) { + printf("%-12s%c", keywordkeys[i] ? keywordkeys[i] : ".", (i+1) % 8 ? ' ' : '\n'); + } + */ +} + +int getkeyword(const char *str, int n) { + + int i, h = strnhash(str, n) & (lengthof(keywordkeys) - 8); + for (i = 0; i < 8; ++i, ++h) { + int len; + if (!keywordkeys[h]) + return -1; + len = keywords[keywordvals[h]].len; + if (n == len && memcmp(keywordkeys[h], str, n) == 0) + return keywordvals[h]; + } + return -1; +} + +typedef struct StringEntry { + int len; + const char *str; +} StringEntry; + +typedef struct StringMap { + int *keys; + int keyscap; + + StringEntry *vals; + int valscap, valslen; +} StringMap; + +StringMap idents; +StringMap strings; + +void initstrmap(StringMap *map) +{ + map->keys = calloc(32, sizeof(int)); + map->keyscap = 32; + assert(map->keys); + + map->vals = calloc(32, sizeof(StringEntry)); + map->valslen = 0; + map->valscap = 32; + assert(map->vals); +} + +void disposestrmap(StringMap *map) +{ + int i; + for (i = map->valslen - 1; i >= 0; --i) { + free((char *) map->vals[i].str); + } + + free(map->vals); + free(map->keys); +} + +static void putstringkey(StringMap *map, int key, int hash) +{ + int *keys = map->keys; + StringEntry *vals = map->vals; + + int i, j; + +redo: + j = (hash << 3) & (map->keyscap - 1); + for (i = 0; i < 8; ++i, ++j) { + if (!keys[j]) { + keys[j] = key; + return; + } + } + + free(keys); + map->keyscap *= 2; + keys = map->keys = calloc(map->keyscap, sizeof(int)); + for (i = 0; i < map->valslen; ++i) { + j = strnhash(vals[i].str, vals[i].len); + putstringkey(map, i + 1, j); + } + + goto redo; +} + +int auxthen; + +int getstringkey(StringMap *map, const char *str, int n) +{ + int *keys = map->keys; + StringEntry *vals = map->vals; + + int key, hash = strnhash(str, n); + int i, j = (hash << 3) & (map->keyscap - 1); + + char *newstr; + + for (i = 0; i < 8; ++i, ++j) { + key = keys[j]; + if (!key) + break; + + assert(key > 0); + if (n == vals[key - 1].len && + memcmp(str, vals[key - 1].str, n) == 0) + { + return key; + } + } + + key = map->valslen + 1; + putstringkey(map, key, hash); + + if (key > map->valscap) { + int cap = map->valscap * 3 / 2 + 1; + vals = map->vals = realloc(vals, cap * sizeof(StringEntry)); + assert(vals); + map->valscap = cap; + } + + newstr = calloc(n + 1, sizeof (char*)); + assert(newstr); + memcpy(newstr, str, n); + + vals[key - 1].len = n; + vals[key - 1].str = newstr; + ++map->valslen; + + return key; +} + +#define getstring(map, key) ((map).vals[(key) - 1].str) +#define getlength(map, key) ((map).vals[(key) - 1].len) + +enum { + ASTMT, + ADO, + ADECL, + ALOOP, + ALOOPUNTIL, + AWHILE, + AFOR, + ACONTINUE, + ABREAK, + ASCOPE, + ARETURN, + AGOTO, + ALABEL, + AIF, + ASWITCH, + ACASE +}; + +const char *astnames[] = { + "Statement", + "Do", + "Declaration", + "Loop", + "Loop-Until", + "While", + "For", + "Continue", + "Break", + "Scope", + "Return", + "Goto", + "Label", + "If", + "Switch", + "Case" +}; + +/* +Node kinds: + '@' - Annotation + ';' ',' ':' '{' '}' ']' ')' - Delimiters + 'A' - Statement + 'I' - Identifier + 'K' - Keyword + 'N' - Number-literal + 'O' - Operator + 'S' - String-literal +*/ +struct Node { + char kind; + + Type *type; + + union { + int id; + + double d; + uint64_t u; + int64_t s; + + struct { + int id; + Node *cond; + } cond; + + struct { + int id; + Node *init; + } decl; + } u; + + Node *lhs, *rhs; + Node *prev, *next; +}; + +FILE *filein; +const char *filename; +const int tabwidth = 8; +char stringbuf[1024]; + +int currcol, lastcol, lastindent, lastkind; +Node tok; + +int warn(const char *fmt, ...) { + va_list ap; + int n; + + va_start(ap, fmt); + n = fprintf(stderr, "%s:%i:%i: warning: ", + filename, lastline, lastcol + 1); + n += vfprintf(stderr, fmt, ap); + n += fprintf(stderr, "\n"); + va_end(ap); + + return n; +} + +int error(const char *fmt, ...) { + va_list ap; + int n; + + va_start(ap, fmt); + n = fprintf(stderr, "%s:%i:%i: error: ", + filename, lastline, lastcol + 1); + n += vfprintf(stderr, fmt, ap); + n += fprintf(stderr, "\n"); + va_end(ap); + + return n; +} + +#define nextindent(indent) \ + ((indent) + tabwidth - ((indent) % tabwidth)) + +int gettok(bool haslhs) { + register int c0 = line[currcol]; + static bool hasnewline = false; + + lastkind = tok.kind; + +skipwhite: + if (hasnewline) { + if (!mygetline(filein)) { + lastindent = 0; + return tok.kind = 0; + } + c0 = line[(currcol = 0)]; + } + + if (currcol) { + while (isspace(c0)) + c0 = line[++currcol]; + } else { + lastindent = 0; + while (isspace(c0)) { + if (c0 == '\t') { + lastindent = nextindent(lastindent); + } else { + ++lastindent; + } + + c0 = line[++currcol]; + } + } + + tok.type = prim; + tok.u.u = 0; + tok.lhs = NULL; + tok.rhs = NULL; + tok.prev = NULL; + tok.next = NULL; + lastcol = currcol; + + /* get line */ + if (!c0 || c0 == '#') { + if (hasnewline) { + goto skipwhite; + } else { + hasnewline = true; + return tok.kind = '\n'; + } + } + + hasnewline = false; + + /* identifier or keyword */ + if (isalpha(c0) || c0 == '_') { + int keyword; + + while (isalnum(c0) || c0 == '_') + c0 = line[++currcol]; + + keyword = getkeyword(line + lastcol, currcol - lastcol); + if (tok.kind != '@' && keyword >= 0 && + (tok.kind != 'O' || tok.u.id != ODISP)) { + if (keywords[keyword].isop) { + tok.u.id = keywords[keyword].opid; + return tok.kind = 'O'; + } else if (keywords[keyword].istype) { + tok.u.id = keywords[keyword].typeid; + tok.type = prim + tok.u.id; + return tok.kind = 'T'; + } + tok.u.id = keyword; + return tok.kind = 'K'; + } + + tok.u.id = getstringkey(&idents, + line + lastcol, currcol - lastcol); + return tok.kind = 'I'; + } + + /* number literal */ + if (isdigit(c0) || c0 == '.' && isdigit(line[currcol+1])) { + int l = c0, t = line[currcol+1], i, j; + bool hasdec = false, hasexp = false; + char *end; + advancenum: + while (isalnum(c0) || c0 == '_' || + c0 == '.' && line[currcol+1] != '.' && !hasdec) + { + if (c0 != '_') + l = c0; + if (c0 == '.') + hasdec = true; + c0 = line[++currcol]; + } + + if (hasdec && !hasexp && (c0 == '+' || c0 == '-')) { + t = tolower(t); + l = tolower(l); + if (l == 'e' && t != 'x' || l == 'p' && t == 'x') { + c0 = line[++currcol]; + hasexp = true; + goto advancenum; + } + } + + /* remove underscores */ + for (j = 0, i = lastcol; i < currcol; ++i) { + if (line[i] != '_') { + if (j >= lengthof(stringbuf) - 1) { + error("number-literal is too long"); + tok.u.u = 0; + tok.type = prim + TINT; + return tok.kind = 'N'; + } + stringbuf[j++] = line[i]; + } + } + stringbuf[j] = 0; + + if (strpbrk(stringbuf, ".pPrR") || + !strpbrk(stringbuf, "xX") && strpbrk(stringbuf, "eEfF")) + { + tok.u.d = strtod(stringbuf, &end); + tok.type = prim + TDOUBLE; + + if (*end != 0) { + /* FIXME(m21c): r-suffix might conflict with radix */ + if ((*end == 'f' || *end == 'F') && !end[1]) { + tok.type = prim + TFLOAT; + } else if (*end == 'l' || *end == 'L') { + tok.type = prim + TDOUBLE; + if (end[1]) + goto errorfloat; + } else if (!mystrcasecmp(end, "f32") || + !mystrcasecmp(end, "r32")) + { + tok.type = prim + TF32; + } else if (!mystrcasecmp(end, "f64") || + !mystrcasecmp(end, "r64")) + { + tok.type = prim + TF64; + } else { + errorfloat: + error("invalid floating-point format"); + } + } + } else { + int typeid = TINT - TUINT; + + if (mystrncasecmp(stringbuf, "0b", 2) == 0) { + tok.u.u = strtoull(stringbuf + 2, &end, 2); + } else { + tok.u.u = strtoull(stringbuf, &end, 0); + } + + switch (*end) { + case 0: + typeid = TINT; + break; + case 'u': case 'U': + typeid = 0; + case 's': case 'S': + case 'i': case 'I': + ++end; + if (*end == 0) { + typeid += TUINT; + break; + } else if (*end == '8') { + typeid += TU8; + if (end[1]) + goto errorint; + break; + } else if (!strcmp(end, "16")) { + typeid += TU16; + break; + } else if (!strcmp(end, "32")) { + typeid += TU32; + break; + } else if (!strcmp(end, "64")) { + typeid += TU64; + break; + } else if (!mystrcasecmp(end, "sz")) { + typeid += TUSIZE; + break; + } + default: + if (!mystrcasecmp(end, "ll")) { + typeid += TULLONG; + } else if (*end == 'l' || *end == 'L') { + typeid += TULONG; + if (end[1]) + goto errorint; + } else { + errorint: + error("invalid integer format"); + typeid = TINT; + } + } + tok.type = prim + typeid; + } + + return tok.kind = 'N'; + } + + /* string & character-literal */ + if (c0 == '"' || c0 == '\'') { + int delim = c0, j; + c0 = line[++currcol]; + lastcol = currcol; + + j = currcol; + while (c0 != delim && c0 != 0) { + if (c0 == '\\') { + switch ((c0 = line[++currcol])) { + case '\\': + c0 = '\\'; + break; + case 'n': + c0 = '\n'; + break; + case 'r': + c0 = '\r'; + break; + case 't': + c0 = '\t'; + break; + case '\'': + c0 = '\''; + break; + case '"': + c0 = '"'; + break; + /* TODO(m21c): read more escape sequences */ + case 0: + goto stringeof; + default: + error("invalid escape sequence '\\%c'", c0); + } + } + line[j++] = c0; + c0 = line[++currcol]; + } + ++currcol; + line[j++] = 0; + + if (c0 == 0) { + stringeof: + error("unexpected end-of-file"); + return tok.kind = 0; + } + + /* TODO(m21c): read '\''-token as character-literal 'C' */ + + tok.u.id = getstringkey(&strings, line + lastcol, j - lastcol); + return tok.kind = 'S'; + } + + /* delimiters */ + switch (c0) { + case ';': + case '@': + case ',': + case ':': + case '{': + case '}': + case ']': + case ')': + goto joindelim; + case '[': + if (haslhs) + c0 = 'O', tok.u.id = OARRAY; + goto joindelim; + case '(': + if (haslhs) + c0 = 'O', tok.u.id = OCALL; + joindelim: + ++currcol; + return tok.kind = c0; + } + + /* operators */ +#define select(ch, then, otherwise) \ + (line[currcol] == (ch) ? ++currcol, (then) : (otherwise)) + switch (line[currcol++]) { + case '.': + /* tok.u.id = select('.', ORANGE, ODISP); */ + tok.u.id = ODISP; + goto joinop; + case '*': + tok.u.id = select('=', OMULA, (haslhs ? OMUL : ODEREF)); + goto joinop; + case '/': + tok.u.id = select('=', ODIVA, ODIV); + goto joinop; + case '%': + tok.u.id = select('=', OMODA, OMOD); + goto joinop; + case '<': + tok.u.id = select('=', OLEQ, + select('<', + select('=', OLSHA, OLSH), + OLET)); + goto joinop; + case '>': + tok.u.id = select('=', OGEQ, + select('>', + select('>', + select('=', OARSHA, OARSH), + select('=', ORSHA, ORSH)), + OGRT)); + goto joinop; + case '&': + tok.u.id = select('=', OANDA, select('&', OLAND, + (haslhs ? OBAND : OADDR))); + goto joinop; + case '+': + tok.u.id = select('=', OADDA, select('+', + (haslhs ? OSUFINC : OINC), + (haslhs ? OADD : OPLUS))); + goto joinop; + case '-': + tok.u.id = select('=', OSUBA, select('-', + (haslhs ? OSUFDEC : ODEC), + (haslhs ? OSUB : OMINUS))); + goto joinop; + case '|': + tok.u.id = select('=', OORA, select('|', OLOR, OBOR)); + goto joinop; + case '^': + tok.u.id = select('=', OXORA, OXOR); + goto joinop; + case '!': + tok.u.id = select('=', ONEQ, OLNOT); + goto joinop; + case '~': + tok.u.id = select('=', OFLIP, OBNOT); + goto joinop; + case '=': + tok.u.id = select('=', select('=', OIDENT, OEQU), OASS); + joinop: + return tok.kind = 'O'; + + default: + error("invalid input character '%c'", c0); + return 'Z'; + } +#undef select +} + +#define skipnewline() \ + (tok.kind == '\n' ? (void) gettok(false) : (void) 0) + +/* - environment & declaration */ + +typedef enum DeclKind { + DMODULE = 0, + DTYPE, /* NOTE(m21c): maybe be the same as void-module ? */ + DVAR, + /* + DFUNCTION, + DMACRO, + DENFOLD + */ +} DeclKind; + +typedef struct Decl Decl; +typedef struct Env Env; + +struct Decl { + DeclKind kind; + + Env *env; + Node *declnode; + + int key; + Decl *prev, *next; +}; + +typedef enum EnvKind { + STOPLEVEL = 0, + SFUNCTION, + SSCOPE, + /* + SUNION, + SSTRUCT, + SENUM, + */ +} EnvKind; + +struct Env { + EnvKind kind; + + uint8_t keycache[64]; + + Decl *head, *tail; + Env *below; +}; + +Decl declbuf[4096]; +int decltop; + +Env envbuf[4096]; +int envtop; + +Env *currenv; + +Decl *makedecl(int key, DeclKind kind) +{ + const int cacheindex = (key >> 3) & 0x3f; + const int cachebit = 1 << (key & 0x03); + + Decl *decl = declbuf + decltop++; + + decl->kind = kind; + decl->key = key; + + assert(currenv); + currenv->keycache[cacheindex] |= cachebit; + + decl->env = currenv; + + if (currenv->tail) { + currenv->tail->next = decl; + decl->prev = currenv->tail; + } else { + assert(currenv->head == NULL); + currenv->head = decl; + } + + currenv->tail = decl; + + return decl; +} + +Decl *finddeclaration(int key) +{ + const int cacheindex = (key >> 3) & 0x3f; + const int cachebit = 1 << (key & 0x03); + + Env *env; + + for (env = currenv; env; env = env->below) { + Decl *decl; + + if (currenv->keycache[cacheindex] & cachebit == 0) + continue; + + for (decl = env->head; decl; decl = decl->next) { + if (decl->key == key) + return decl; + } + } + + return NULL; +} + +Env *pushenv(EnvKind kind) +{ + Env *env = envbuf + envtop++; + + env->kind = kind; + env->below = currenv; + + currenv = env; + + return env; +} + +Env *popenv(void) +{ + Env *env = currenv; + + if (currenv) + currenv = currenv->below; + + return env; +} + +Env *getfuncenv(void) +{ + Env *env; + + for (env = currenv; env; env = env->below) { + if (env->kind == SFUNCTION) + return env; + } + + return NULL; +} + +/* - ast-node - */ + +Node nodebuf[4096]; +int nodetop; + +Node *makenode(Node *lhs) +{ + Node *node = nodebuf + nodetop++; + *node = tok; + node->lhs = lhs; + + return node; +} + +int printnode(FILE *out, Node *node) { + int n = 0, len, i; + const char *str; + + if (!node) + return fprintf(out, "<null>"); + + switch (node->kind) { + case 'T': + return fprintf(out, "Type"); + case 'I': + return fprintf(out, "%s", getstring(idents, node->u.id)); + case 'O': + return fprintf(out, "%s", ops[node->u.id].str); + case 'K': + return fprintf(out, "%s", keywords[node->u.id].str); + case 'N': + if (node->type->kind == TFLOAT || + node->type->kind == TDOUBLE || + node->type->kind == TLDOUBLE) + return fprintf(out, "%f", node->u.d); + else if (node->type->kind & (TUINT - TINT)) + return fprintf(out, "%li", node->u.s); + else + return fprintf(out, "%lu", node->u.u); + case 'S': + str = getstring(strings, node->u.id); + len = getlength(strings, node->u.id); + n += fprintf(out, "\""); + for (i = 0; i < len; ++i) { + switch (str[i]) { + case '\\': + n += printf("\\\\"); + break; + case '\n': + n += printf("\\n"); + break; + case '\r': + n += printf("\\r"); + break; + case '\t': + n += printf("\\t"); + break; + case '\"': + n += printf("\\\""); + break; + case '\'': + n += printf("\\\'"); + break; + case 0: + n += printf("\\0"); + break; + default: + putc(str[i], out); + ++n; + } + } + n += printf("\""); + } + + return n; +} + +int printast(Node *node, int indent) { + int n, i; + bool newline = false; + + assert(node); + + if (node->kind == 'O') + n += printf("%s(", ops[node->u.id].debugstr); + else if (node->kind == 'A') + n += printf("%s(", astnames[node->u.id]); + else + printnode(stdout, node); + + if (node->kind == 'A' && ( + node->u.id == AIF || + node->u.id == AWHILE || + node->u.id == ALOOPUNTIL + ) && node->u.cond.cond) + { + newline = true; + n += printf("\n"); + for (i = 0; i <= indent; ++i) + n += printf(" "); + n += printf("cond: "); + n += printast(node->u.cond.cond, indent + 1); + } else if (node->kind == 'A' && node->u.id == ADECL && node->u.decl.init) { + newline = true; + n += printf("\n"); + for (i = 0; i <= indent; ++i) + n += printf(" "); + n += printf("init: "); + n += printast(node->u.decl.init, indent + 1); + } + + if (node->lhs) { + newline = true; + n += printf("\n"); + for (i = 0; i <= indent; ++i) + n += printf(" "); + n += printf("lhs: "); + n += printast(node->lhs, indent + 1); + } + + if (node->rhs) { + newline = true; + n += printf("\n"); + for (i = 0; i <= indent; ++i) + n += printf(" "); + n += printf("rhs: "); + n += printast(node->rhs, indent + 1); + } + + if (newline) { + n += printf("\n"); + for (i = 0; i < indent; ++i) + n += printf(" "); + } + n += printf(")"); + + if (0 && node->prev) { + n += printf("prev:\n"); + for (i = 0; i <= indent; ++i) + n += printf(" "); + n += printast(node->prev, indent); + } + + if (node->next) { + n += printf("next:\n"); + for (i = 0; i <= indent; ++i) + n += printf(" "); + n += printast(node->next, indent); + } + + return n; +} + +/* - parser - */ + +bool expect(int kind, bool nexthaslhs, const char *fmt, ...) +{ + va_list ap; + + if (tok.kind != kind) { + va_start(ap, fmt); + fprintf(stderr, "%s:%i:%i: error: ", + filename, lastline, lastcol + 1); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + return false; + } + + gettok(nexthaslhs); + return true; +} + +enum { + QINTERN = 0x0001, + QEXTERN = 0x0002, + + QSTATIC = 0x0010, + + QCONST = 0x0200, + + QVAR = 0x1000, + + /* masks */ + QALL = QINTERN | QEXTERN | QSTATIC | QCONST | QVAR, + QVISIB = QEXTERN | QINTERN, + QSTORAGE = QSTATIC, + QTYPE = QCONST, + QINFER = QVAR, +}; + +int qualifiers(int allowmask) { + int flags = 0, mask = allowmask; + + while (tok.kind == 'K') { + int f, m; + + switch (tok.u.id) { + case KEXTERN: + f = QEXTERN, m = ~QVISIB; + break; + case KINTERN: + f = QINTERN, m = ~QVISIB; + break; + case KSTATIC: + f = QSTATIC, m = ~QSTORAGE; + break; + case KCONST: + f = QCONST; + break; + case KVAR: + f = QVAR, m = ~(QTYPE | QINFER); + break; + default: + goto finish; + } + + if (f & ~allowmask) { + const char *str = keywords[tok.u.id].str; + error("invalid qualifier '%s'", str); + } else if (f & flags & QTYPE) { + const char *str = keywords[tok.u.id].str; + warn("redundant qualifier '%s'", str); + } else if (f & ~mask) { + const char *str = keywords[tok.u.id].str; + error("redundant qualifier '%s'", str); + } + + flags |= f & allowmask & mask; + mask &= m; + gettok(false); + } + +finish: + return flags; +} + +Node *expr(int minprec); + +Node *getbasetype(int flags) { + Node *result; + + if (tok.kind == 'I') { + /* TODO(m21c): check/read type identifier */ + return NULL; + } else if (tok.kind != 'T') { + return NULL; + } + + result = makenode(NULL); + result->kind = 'T'; + + gettok(false); + + return result; +} + +Node *gettype(Node *basetype) { + int flags; + Type *ty; + + assert(basetype); + assert(basetype->kind == 'T'); + + ty = basetype->type; + +advance: + flags = qualifiers(QTYPE); + + if (tok.kind == '[') { + Type *tmp = maketype(); + tmp->kind = TARRAY; + tmp->target = ty, ty = tmp; + gettok(false); + if (tok.kind != ']') + ty->u.val = expr(PSTART); + expect(']', false, "expect ']'"); + goto advance; + } + + if (tok.kind == 'O') { + if (tok.u.id == ODEREF) { + Type *tmp = maketype(); + tmp->kind = TPTR; + tmp->target = ty, ty = tmp; + gettok(false); + goto advance; + } + } + + basetype->type = ty; + return basetype; +} + +Node *exprlist(bool isparam, Node *paramtype); +Node *stmtlist(int indent); + +Node *declaration(Node *typenode) { + bool has_self_param = false; + Node *result = typenode; + + if (tok.kind == 'I') { + result = makenode(typenode); + result->kind = 'A'; + result->u.decl.id = ADECL; + result->rhs = makenode(NULL); + gettok(true); + } else if (tok.kind == 'T') { + Node *module = gettype(getbasetype(0)); + if (tok.kind == 'O' && tok.u.id == ODISP || tok.kind == ':') { + has_self_param = tok.kind == ':'; + gettok(false); + } else { + error("expected '.' or ':'"); + } + + if (tok.kind == 'I') { + module = makenode(module); + module->kind = 'O'; + module->u.id = ODISP; + module->rhs = makenode(NULL); + result = makenode(typenode); + result->kind = 'A'; + result->u.decl.id = ADECL; + result->rhs = module; + gettok(true); + } else { + error("expected identifier"); + } + } else { + return result; + } + + if (tok.kind == 'O' && tok.u.id == OCALL) { + Node *params; + gettok(false); + params = exprlist(true, NULL); + expect(')', true, "expected ')'"); + + if (tok.kind != 'O' || tok.u.id != OASS) { + Node *stmts = stmtlist(lastindent); + result->u.decl.init = stmts; + } + } + + if (tok.kind == 'O' && tok.u.id == OASS) { + gettok(false); + result->u.decl.init = expr(PSTART); + } else { + result->u.decl.init = NULL; + } + + return result; +} + +Node *tailof(Node *head) { + while (head->next) + head = head->next; + return head; +} + +bool isatom(void) { + switch (tok.kind) { + case 0: + case '\n': case ';': + case ',': case ':': + case ')': case ']': case '}': + return false; + case 'O': + if (ops[tok.u.id].prec != PUNARY) + return false; + return true; + case 'K': + switch (tok.u.id) { + case KELSE: + case KUNTIL: + return false; + } + return true; + } + + return true; +} + +Node *stmtlist(int indent) { + Node *result = NULL, *lhs = NULL; + int needindent = nextindent(indent); + /* printf("needident: %d, currindent: %d, lastindent: %d\n", needindent, currindent, lastindent); */ + + for (;;) { + Node *stmt; + + if (tok.kind == '\n') { + gettok(false); + if (tok.kind == ';') + error("expected expression"); + } + + if (lastkind == '\n' && lastindent < needindent) + break; + + if (tok.kind == ';') { + gettok(false); + + /* NOTE(m21c): used for REPL */ + if (tok.kind == ';' || tok.kind == '\n') { + /* TODO(m21c): output an error-message if not in REPL-mode */ + } + } + + if (!isatom()) + break; + + if (lhs && lastkind != '\n' && lastkind != ';') + error("expected line delimiter"); + + stmt = exprlist(false, NULL); + + stmt = makenode(stmt); + stmt->kind = 'A'; + stmt->u.id = ASTMT; + + if (!lhs) { + result = lhs = stmt; + } else { + lhs->next = stmt; + lhs->next->prev = lhs; + lhs = lhs->next; + } + } + + return result; +} + +Node *lastis; + +Node *atom(int flags) { + Node *lhs = NULL, *savedis = lastis; + int indent; + /* int flags; */ + + /* unary 'is'-operator */ + if (tok.kind == 'K' && tok.u.id == KIS) { + if (!lastis) { + error("there is no left-hand-side for 'is'"); + lhs = makenode(NULL); + } else { + lhs = makenode(lastis->lhs); + } + + gettok(false); + + lhs->kind = 'O'; + if (tok.kind == 'K' && tok.u.id == KNOT) + gettok(false), lhs->u.id = ONEQ; + else + lhs->u.id = OEQU; + lhs->rhs = expr(PRELAT); + + return lhs; + } + + /* unary prefix operators */ + if (tok.kind == 'O' && ops[tok.u.id].prec == PUNARY) { + lhs = makenode(NULL); + gettok(false); + lhs->lhs = atom(0); + return lhs; + } + + if ((lhs = getbasetype(0))) + return declaration(gettype(lhs)); + + if (flags & ~(QINFER | QCONST)) { + error("invalid use of qualifiers"); + flags = flags & (QINFER | QCONST); + } + + if (flags) { + lhs = atom(flags); + return lhs; + } + + /* actual atom */ + switch (tok.kind) { + case '(': + #if 0 + gettok(false); + skipnewline(); + lhs = exprlist(false, NULL), lastis = savedis; + if (lhs->kind == 'T') { + /* NOTE(m21c): expecting that the type is also set in lhs->type */ + lhs->kind = 'O'; + lhs->u.id = OCAST; + skipnewline(); + expect(')', true, "expected ')'"); + + lhs->lhs = atom(0); + break; + } + skipnewline(); + expect(')', true, "expected ')'"); + #else + gettok(false); + if (tok.kind == '\n') { + lhs = stmtlist(lastindent), lastis = savedis; + } else { + lhs = exprlist(false, NULL), lastis = savedis; + if (lhs->kind == 'T') { + /* NOTE(m21c): expecting that the type is also set in lhs->type */ + lhs->kind = 'O'; + lhs->u.id = OCAST; + skipnewline(); + expect(')', true, "expected ')'"); + + lhs->lhs = atom(0); + break; + } + skipnewline(); + } + expect(')', true, "expected ')'"); + #endif + break; + + case 'I': + case 'T': + case 'N': + case 'S': + case 'C': + lhs = makenode(NULL); + gettok(true); + if (flags & QCONST) { + /* TODO(m21c): const - conversion */ + } + break; + case 'K': + switch (tok.u.id) { + case KNOT: + lhs = makenode(NULL); + gettok(false); + lhs->kind = 'O'; + lhs->u.id = OLNOT; + lhs->lhs = expr(PRELAT); + break; + case KBREAK: + case KCONTINUE: + lhs = makenode(NULL); + lhs->kind = 'A'; + lhs->u.id = tok.u.id == KBREAK ? ABREAK : ACONTINUE; + gettok(true); + if (tok.kind == ':') { + gettok(false); + skipnewline(); + if (tok.kind == 'I') { + lhs->lhs = makenode(NULL); + gettok(false); + } else { + error("expected identifier"); + } + } + break; + case KRETURN: + lhs = makenode(NULL); + gettok(true); + lhs->kind = 'A'; + lhs->u.id = ARETURN; + if (tok.kind == ':') { + gettok(false); + skipnewline(); + if (tok.kind == 'I') { + lhs->lhs = makenode(NULL); + gettok(false); + } else { + error("expected identifier"); + } + } + if (isatom()) + lhs->rhs = exprlist(false, NULL); + break; + case KDO: + indent = lastindent; + lhs = makenode(NULL); + gettok(false); + lhs->kind = 'A'; + lhs->u.id = ADO; + lhs->lhs = stmtlist(indent); + break; + case KLOOP: + indent = lastindent; + lhs = makenode(NULL); + gettok(false); + lhs->kind = 'A'; + lhs->u.id = ALOOP; + lhs->lhs = stmtlist(indent); + + if (tok.kind == 'K' && tok.u.id == KUNTIL && + lastindent >= indent) + { + lhs->u.cond.id = ALOOPUNTIL; + gettok(false); + lhs->u.cond.cond = expr(POR); + } + if (lhs->u.id != ALOOP) + goto joinelse; + else + break; + case KWHILE: + indent = lastindent; + lhs = makenode(NULL); + gettok(false); + lhs->kind = 'A'; + lhs->u.cond.id = AWHILE; + lhs->u.cond.cond = expr(POR); + lhs->lhs = stmtlist(indent); + goto joinelse; + case KIF: + indent = lastindent; + lhs = makenode(NULL); + gettok(false); + lhs->kind = 'A'; + lhs->u.cond.id = AIF; + lhs->u.cond.cond = expr(POR); + skipnewline(); + if (tok.kind == 'I' && tok.u.id == auxthen) + gettok(false); + lhs->lhs = stmtlist(indent); + joinelse: + if (tok.kind == 'K' && tok.u.id == KELSE && + lastindent >= indent) + { + gettok(false); + lhs->rhs = stmtlist(indent); + } + break; + default: + goto joinerror; + } + break; + case 'O': + default: + joinerror: + error("expected expression"); + lhs = makenode(NULL); + gettok(true); + } + + /* unary postfix operators */ + while (tok.kind == 'O' && ops[tok.u.id].prec == PUNSUF) { + lhs = makenode(lhs); + if (tok.u.id == ODISP) { + gettok(false); + skipnewline(); + if (tok.kind != 'I') + error("expected identifier"); + lhs->rhs = makenode(NULL); + } else if (tok.u.id == OCALL) { + gettok(false); + if (tok.kind != ')') + lhs->rhs = exprlist(false, NULL), lastis = savedis; + expect(')', true, "expected ')'"); + continue; + } else if (tok.u.id == OARRAY) { + gettok(false); + lhs->rhs = exprlist(false, NULL), lastis = savedis; + expect(']', true, "expected ']'"); + continue; + } + gettok(true); + } + + /* 'not'-suffix for the binary 'is'-operator (i.e. 'is not') */ + while (tok.kind == 'K' && tok.u.id == KIS) { + lhs = makenode(lhs); + gettok(false); + + lhs->kind = 'O'; + if (tok.kind == 'K' && tok.u.id == KNOT) + gettok(false), lhs->u.id = ONEQ; + else + lhs->u.id = OEQU; + + lastis = lhs; + lhs->rhs = expr(PRELAT); + } + + return lhs; +} + +Node *expr(int minprec) { + Node *lhs = atom(0), *last = NULL; + + /* only binary expr */ + while (tok.kind == 'O' && ops[tok.u.id].prec >= minprec) { + lhs = makenode(lhs); + gettok(false); + skipnewline(); + if (ops[lhs->u.id].rassoc) + lhs->rhs = expr(ops[lhs->u.id].prec); + else + lhs->rhs = expr(ops[lhs->u.id].prec + 1); + + switch (ops[lhs->u.id].prec) { + case PRELAT: + if (last) { + lhs = makenode(lhs); + + lhs->rhs = lhs->lhs; + lhs->kind = 'O'; + lhs->u.id = OLAND; + + lhs->lhs = lhs->rhs->lhs; + lhs->rhs->lhs = last->rhs; /* copy */ + last = lhs->rhs; + } else { + last = lhs; + } + break; + default: + last = NULL; + break; + } + } + + return lhs; +} + +Node *todeclaration(Node *curr, Node **ty) { + if (*ty) { + if (curr->kind == 'I') { + Node *decl = makenode(*ty); + curr->kind = 'A'; + curr->u.decl.id = ADECL; + decl->rhs = curr; + curr = decl; + } else if (curr->kind == 'O' && curr->u.id == OASS && + curr->lhs && curr->lhs->kind == 'I') + { + curr->kind = 'A'; + curr->u.decl.id = ADECL; + curr->u.decl.init = curr->rhs; + curr->rhs = curr->lhs; + curr->lhs = *ty; + } + } + + if (curr->kind == 'A' && curr->u.id == ADECL) + *ty = curr->lhs; + + return curr; +} + +Node *exprlist(bool isparam, Node *paramtype) { + Node *head, *tail; + bool isdeclaration, typetuple; + + /* tail = todeclaration(tail, &paramtype); */ + + if (paramtype && tok.kind == 'I') { + head = declaration(paramtype), tail = head; + } else { + head = expr(PSTART), tail = head; + } + + if (isparam && (tail->kind != 'A' || tail->u.id != ADECL)) + error("expected declaration"); + + isdeclaration = tail->kind == 'A' && tail->u.id == ADECL; + typetuple = tail->kind == 'T'; + + while (tok.kind == ',') { + Node *curr; + gettok(false); + + if (tok.kind == 'I' && isdeclaration) { + assert(paramtype); + curr = declaration(paramtype); + typetuple = false; + } else { + curr = expr(PSTART); + typetuple &= curr->kind == 'T'; + /* curr = todeclaration(curr, &paramtype); */ + } + + if ((paramtype || isparam) && + (curr->kind != 'A' || curr->u.id != ADECL)) + error("expected declaration"); + + isdeclaration &= curr->kind == 'A' && curr->u.id == ADECL; + + tail->next = curr; + tail->next->prev = tail; + tail = tail->next; + } + + lastis = NULL; + return head; +} + +/* - main-routine - */ + +int main(int argc, char **argv) { + initkeywords(); + initstrmap(&idents); + initstrmap(&strings); + + auxthen = getstringkey(&idents, "then", 4); + + if (argc >= 2) { + filein = fopen(argv[1], "rb"); + filename = argv[1]; + assert(filein); + } else { + filein = stdin; + filename = "<stdin>"; + printf("> "); + } + + gettok(false); + if (tok.kind == '\n') + gettok(false); + while (tok.kind != 0) { + /* printf("token:%i:%i: %c '%.*s'\n", lastline, lastcol + 1, tok.u.id, currcol - lastcol, line + lastcol);*/ + Node *ast; + + ast = exprlist(false, NULL); + printast(ast, 0); + printf("\n"); + + if (tok.kind == '\n') { + if (filein == stdin) + printf("> "); + gettok(false); + } else if (tok.kind == ';') { + gettok(false); + } + + if (lastkind != ';' && lastkind != '\n') { + error("expected new line"); + while (tok.kind != ';' && tok.kind != '\n' && tok.kind != 0) + gettok(false); + if (filein == stdin) + printf("> "); + if (tok.kind != 0) + gettok(false); + } + } + + /* fclose(filein); */ + /* disposestrmap(&strings); */ + /* disposestrmap(&idents); */ +}