commit aea94da4800cedfc179539b45fc8c425f2dc7591
Author: m21c <ho*******@gmail.com>
Date: Sun, 28 Mar 2021 16:01:16 +0200
initial commit
Diffstat:
| A | .gitignore | | | 7 | +++++++ |
| A | aria.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, ¶mtype); */
+
+ 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, ¶mtype); */
+ }
+
+ 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); */
+}