spl

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 4e954d398a4ac44ed59ad4fbc8b3ed26f454bd6a
Author: Brian Swetland <swetland@frotz.net>
Date:   Sat, 13 May 2023 23:23:20 -0700

initial

Diffstat:
AMakefile | 42++++++++++++++++++++++++++++++++++++++++++
Acompiler0.c | 1472+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ainc/builtin.type.h | 11+++++++++++
Ainc/library.impl.c | 11+++++++++++
Ainc/library.impl.h | 3+++
Atest/1000-return-42.log | 1+
Atest/1000-return-42.src | 3+++
Atest/1010-func-call.log | 1+
Atest/1010-func-call.src | 7+++++++
Atest/1011-func-call-forward.log | 1+
Atest/1011-func-call-forward.src | 8++++++++
Atest/1012-func-call-builtin.log | 4++++
Atest/1012-func-call-builtin.src | 6++++++
Atest/1020-math-1.log | 10++++++++++
Atest/1020-math-1.src | 22++++++++++++++++++++++
Atest/1021-numbers.log | 24++++++++++++++++++++++++
Atest/1021-numbers.src | 27+++++++++++++++++++++++++++
Atest/1024-xorshift32.log | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/1024-xorshift32.src | 20++++++++++++++++++++
Atest/1025-fibonacci.log | 25+++++++++++++++++++++++++
Atest/1025-fibonacci.src | 17+++++++++++++++++
Atest/1026.fib.iter.log | 25+++++++++++++++++++++++++
Atest/1026.fib.iter.src | 23+++++++++++++++++++++++
Atest/1030-flow-control.log | 14++++++++++++++
Atest/1030-flow-control.src | 45+++++++++++++++++++++++++++++++++++++++++++++
Atest/1031-logical-and-or.log | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/1031-logical-and-or.src | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/1040-structs.log | 13+++++++++++++
Atest/1040-structs.src | 50++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/1041-arrays.log | 18++++++++++++++++++
Atest/1041-arrays.src | 26++++++++++++++++++++++++++
Atest/1042-arrays2.log | 9+++++++++
Atest/1042-arrays2.src | 16++++++++++++++++
Atest/1050-enums.log | 10++++++++++
Atest/1050-enums.src | 20++++++++++++++++++++
Atest/2000-err-decl-mismatch-type.src | 6++++++
Atest/2001-err-decl-mismatch-return.src | 5+++++
Atest/2002-err-decl-mismatch-count.src | 6++++++
Atest/2003-err-decl-mismatch-array.src | 8++++++++
Atest/2010-err-array-init-too-large.src | 2++
Atest/2011-err-array-oob-const.src | 6++++++
Atest/2020-err-u32-overflow.src | 3+++
Atest/2021-err-u32-hex-overflow.src | 3+++
Atest/2022-err-u32-bin-overflow.src | 3+++
Atest/2030-err-bad-break.src | 4++++
Atest/2031-err-bad-continue.src | 4++++
Atools/compile | 4++++
Atools/runtest | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
48 files changed, 2276 insertions(+), 0 deletions(-)

diff --git a/Makefile b/Makefile @@ -0,0 +1,42 @@ + + +.NOTINTERMEDIATE: %.c %.h + +all: bin/compiler0 + +test: out/test/summary.txt + +bin/compiler0: compiler0.c + @mkdir -p bin + gcc -Wall -O0 -g -o bin/compiler0 compiler0.c + +clean:: + rm -rf bin out + + +# have to have two rules here otherwise tests without .log files +# fail to be compiled by the rule that depends on src+log *or* +# we fail to depend on the .log for tests with both... + +TESTDEPS := bin/compiler0 tools/runtest tools/compile +TESTDEPS += $(wildcard inc/*.h) $(wildcard inc/*.c) + +out/test/%.txt: test/%.src test/%.log $(TESTDEPS) + @mkdir -p out/test + @rm -f $@ + @tools/runtest $< $@ + +out/test/%.txt: test/%.src $(TESTDEPS) + @mkdir -p out/test + @rm -f $@ + @tools/runtest $< $@ + + +SRCTESTS := $(sort $(wildcard test/*.src)) +ALLTESTS := $(patsubst test/%.src,out/test/%.txt,$(SRCTESTS)) + +out/test/summary.txt: $(ALLTESTS) + @cat $(ALLTESTS) > $@ + +%: test/%.src + @$(MAKE) $(patsubst %.src,out/%.txt,$<) diff --git a/compiler0.c b/compiler0.c @@ -0,0 +1,1472 @@ +// Copyright 2022, Brian Swetland <swetland@frotz.net> +// Licensed under the Apache License, Version 2.0. + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <stdint.h> +#include <stdbool.h> +#include <string.h> + +#include <fcntl.h> +#include <unistd.h> + +// builtin types +#define nil 0 +typedef uint32_t u32; +typedef int32_t i32; +typedef uint8_t u8; + +typedef uint32_t token_t; + +void error(const char *fmt, ...); + +// ------------------------------------------------------------------ +// structures + +typedef struct String String; +typedef struct Symbol Symbol; +typedef struct Scope Scope; +typedef struct Type Type; + +struct String { + String *next; + u32 len; + char text[0]; +}; + +struct Symbol { + Symbol *next; + String *name; + Type *type; + u32 kind; +}; +enum { + SYMBOL_VAR, + SYMBOL_FLD, // struct field + SYMBOL_PTR, // struct *field +}; + +struct Scope { + Scope *parent; + Symbol *first; + Symbol *last; + u32 kind; +}; +enum { + SCOPE_GLOBAL, + SCOPE_FUNC, + SCOPE_BLOCK, + SCOPE_LOOP, + SCOPE_STRUCT, +}; + +struct Type { + Type *next; + String *name; + Type *of; // for: slice, array, ptr + Symbol *fields; // for: struct + u32 kind; + u32 count; // for: arrays +}; +enum { + TYPE_VOID, + TYPE_BOOL, + TYPE_U8, + TYPE_U32, +// TYPE_NIL, +// TYPE_POINTER, + TYPE_ARRAY, + TYPE_SLICE, + TYPE_STRUCT, +// TYPE_FUNC, + TYPE_ENUM, + TYPE_UNDEFINED, +}; + +typedef struct Ctx Ctx; + +// ------------------------------------------------------------------ +// compiler global context + +struct Ctx { + const char* filename; // filename of active source + const char* outname; // base name for output files + int fd; + + FILE *fp_decl; // output files + FILE *fp_type; + FILE *fp_impl; + + int nl_decl; // flag to update #line + int nl_type; + int nl_impl; + + u8 iobuffer[1024]; // scanner file io buffer + u32 ionext; + u32 iolast; + + u32 linenumber; // line number of most recent line + u32 lineoffset; // position of start of most recent line + u32 byteoffset; // position of the most recent character + u32 flags; + u32 cc; // scanner: next character + + token_t tok; // most recent token + u32 num; // used for tNUM + char tmp[256]; // used for tIDN, tSTR; + String *ident; // used for tIDN + + String *stringlist; // all strings + Type *typelist; // all types + + Scope *scope; // scope stack + Scope *fn; // args of active function being parsed + + Scope global; + + String *idn_if; // identifier strings + String *idn_for; + String *idn_var; + String *idn_nil; + String *idn_case; + String *idn_func; + String *idn_else; + String *idn_enum; + String *idn_true; + String *idn_type; + String *idn_break; + String *idn_while; + String *idn_false; + String *idn_switch; + String *idn_struct; + String *idn_return; + String *idn_continue; + + Type *type_void; // base types + Type *type_u32; + Type *type_i32; + Type *type_u8; +}; + +Ctx ctx; + +// ------------------------------------------------------------------ + +String *string_make(const char* text, u32 len) { + // obviously this wants to be a hash table + String *str = ctx.stringlist; + while (str != nil) { + if ((str->len == len) && (memcmp(text, str->text, len) == 0)) { + return str; + } + str = str->next; + } + + str = malloc(sizeof(String) + len + 1); + str->len = len; + memcpy(str->text, text, len); + str->text[len] = 0; + str->next = ctx.stringlist; + ctx.stringlist = str; + + return str; +} + +Scope *scope_push(u32 kind) { + Scope *scope = malloc(sizeof(Scope)); + scope->first = nil; + scope->last = nil; + scope->parent = ctx.scope; + scope->kind = kind; + ctx.scope = scope; + return scope; +} + +Scope *scope_pop(void) { + Scope *scope = ctx.scope; + ctx.scope = scope->parent; + return scope; +} + +Scope *scope_find(u32 scope_kind) { + Scope *scope = ctx.scope; + while (scope != nil) { + if (scope->kind == scope_kind) { + return scope; + } + scope = scope->parent; + } + return nil; +} + +Symbol *symbol_find_in(String *name, Scope *scope) { + for (Symbol *sym = scope->first; sym != nil; sym = sym->next) { + if (sym->name == name) { + return sym; + } + } + return nil; +} + +// find the first surrounding scope of a specified kind +Symbol *symbol_find(String *name) { + for (Scope *scope = ctx.scope; scope != nil; scope = scope->parent) { + Symbol *sym = symbol_find_in(name, scope); + if (sym != nil) { + return sym; + } + } + return nil; +} + +Symbol *symbol_make_in_scope(String *name, Type *type, Scope *scope) { + Symbol *sym = malloc(sizeof(Symbol)); + sym->name = name; + sym->type = type; + sym->next = nil; + sym->kind = SYMBOL_VAR; + if (scope->first == nil) { + scope->first = sym; + } else { + scope->last->next = sym; + } + scope->last = sym; + return sym; +} + +Symbol *symbol_make_global(String *name, Type *type) { + return symbol_make_in_scope(name, type, &ctx.global); +} + +Symbol *symbol_make(String *name, Type *type) { + return symbol_make_in_scope(name, type, ctx.scope); +} + +Type *type_make(String *name, u32 kind, Type *of, Symbol *fields, u32 count) { + Type *type = malloc(sizeof(Type)); + type->name = name; + type->of = of; + type->fields = fields; + type->kind = kind; + type->count = count; + if (name != nil) { + type->next = ctx.typelist; + ctx.typelist = type; + } else { + type->next = nil; + } + return type; +} + +Type *type_find(String *name) { + for (Type *t = ctx.typelist; t != nil; t = t->next) { + if (t->name == name) { + return t; + } + } + return nil; +} + +Symbol *type_find_field(Type *type, String *name) { + if (type->kind != TYPE_STRUCT) { + error("not a struct"); + } + for (Symbol *s = type->fields; s != nil; s = s->next) { + if (s->name == name) { + return s; + } + } + error("struct has no such field '%s'", name->text); + return nil; +} + +// ================================================================ + +enum { + cfVisibleEOL = 1, + cfAbortOnError = 2, + cfTraceCodeGen = 3, +}; + +void ctx_init() { + memset(&ctx, 0, sizeof(ctx)); + + // pre-intern keywords + ctx.idn_if = string_make("if", 2); + ctx.idn_for = string_make("for", 3); + ctx.idn_var = string_make("var", 3); + ctx.idn_nil = string_make("nil", 3); + ctx.idn_case = string_make("case", 4); + ctx.idn_func = string_make("func", 4); + ctx.idn_else = string_make("else", 4); + ctx.idn_enum = string_make("enum", 4); + ctx.idn_true = string_make("true", 4); + ctx.idn_type = string_make("type", 4); + ctx.idn_break = string_make("break", 5); + ctx.idn_while = string_make("while", 5); + ctx.idn_false = string_make("false", 5); + ctx.idn_switch = string_make("switch", 6); + ctx.idn_struct = string_make("struct", 6); + ctx.idn_return = string_make("return", 6); + ctx.idn_continue = string_make("continue", 8); + + ctx.type_void = type_make(string_make("void", 4), TYPE_VOID, nil, nil, 0); + ctx.type_u32 = type_make(string_make("u32", 3), TYPE_U32, nil, nil, 0); + ctx.type_i32 = type_make(string_make("i32", 3), TYPE_U32, nil, nil, 0); + ctx.type_u8 = type_make(string_make("u8", 2), TYPE_U8, nil, nil, 0); + + ctx.scope = &(ctx.global); +} + +void dump_file_line(const char* fn, u32 offset); +void dump_error_ctxt(); + +void error(const char *fmt, ...) { + va_list ap; + + fprintf(stderr,"\n%s:%d: ", ctx.filename, ctx.linenumber); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + if (ctx.linenumber > 0) { + // dump_file_line(ctx.filename, ctx.lineoffset); + } + fprintf(stderr, "\n"); + +#if 0 + dump_error_ctxt(); +#endif + + if (ctx.flags & cfAbortOnError) { + abort(); + } else { + exit(1); + } +} + +#define DECL ctx.fp_decl +#define TYPE ctx.fp_type +#define IMPL ctx.fp_impl + +void emit(FILE* fp, const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + vfprintf(fp, fmt, ap); + va_end(ap); +} + +void emit_type(FILE *fp, Type *type) { + if ((type->kind == TYPE_SLICE) || (type->kind == TYPE_ARRAY)) { + emit(fp, "t$s$"); + emit_type(fp, type->of); +#if 0 + } else if (type->kind == TYPE_POINTER) { + emit(fp, "t$%s*", type->of->name->text); +#endif + } else { + emit(fp, "t$%s", type->name->text); + } +} + +void emit_vref(FILE *fp, Symbol *sym) { + if (sym->type->kind == TYPE_STRUCT) { + emit(fp, "(&v$%s)", sym->name->text); + } else { + emit(fp, "v$%s", sym->name->text); + } +} + + +void ctx_open_source(const char* filename) { + ctx.filename = filename; + ctx.linenumber = 0; + + if (ctx.fd >= 0) { + close(ctx.fd); + } + ctx.fd = open(filename, O_RDONLY); + if (ctx.fd < 0) { + error("cannot open file '%s'", filename); + } + ctx.ionext = 0; + ctx.iolast = 0; + ctx.linenumber = 1; + ctx.lineoffset = 0; + ctx.byteoffset = 0; +} + +void ctx_open_output(void) { + char tmp[1024]; + ctx.nl_decl = 1; + ctx.nl_type = 1; + ctx.nl_impl = 1; + + sprintf(tmp, "%s.decl.h", ctx.outname); + if ((ctx.fp_decl = fopen(tmp, "w")) == NULL) { + error("cannot open output '%s'", tmp); + } + + sprintf(tmp, "%s.type.h", ctx.outname); + if ((ctx.fp_type = fopen(tmp, "w")) == NULL) { + error("cannot open output '%s'", tmp); + } + + sprintf(tmp, "%s.impl.c", ctx.outname); + if ((ctx.fp_impl = fopen(tmp, "w")) == NULL) { + error("cannot open output '%s'", tmp); + } + + emit(IMPL,"#include <builtin.type.h>\n"); + emit(IMPL,"#include \"%s.type.h\"\n", ctx.outname); + emit(IMPL,"#include \"%s.decl.h\"\n", ctx.outname); +} + + +// ================================================================ +// lexical scanner + +// token classes (tok & tcMASK) +enum { + tcRELOP = 0x08, tcADDOP = 0x10, tcMULOP = 0x18, + tcAEQOP = 0x20, tcMEQOP = 0x28, tcMASK = 0xF8, +}; + +enum { + // EndMarks, Braces, Brackets Parens + tEOF, tEOL, tOBRACE, tCBRACE, tOBRACK, tCBRACK, tOPAREN, tCPAREN, + // RelOps (do not reorder) + tEQ, tNE, tLT, tLE, tGT, tGE, tx0E, tx0F, + // AddOps (do not reorder) + tPLUS, tMINUS, tPIPE, tCARET, tx14, tx15, tx16, tx17, + // MulOps (do not reorder) + tSTAR, tSLASH, tPERCENT, tAMP, tLEFT, tRIGHT, tx1E, tx1F, + // AsnOps (do not reorder) + tADDEQ, tSUBEQ, tOREQ, tXOREQ, tx24, tx25, tx26, tx27, + tMULEQ, tDIVEQ, tMODEQ, tANDEQ, tLSEQ, tRSEQ, t2E, t2F, + // Various, UnaryNot, LogicalOps, + tSEMI, tCOLON, tDOT, tCOMMA, tNOT, tAND, tOR, tBANG, + tASSIGN, tINC, tDEC, + // Keywords + tTYPE, tFUNC, tSTRUCT, tVAR, tENUM, + tIF, tELSE, tWHILE, + tBREAK, tCONTINUE, tRETURN, + tFOR, tSWITCH, tCASE, + tTRUE, tFALSE, tNIL, + tIDN, tNUM, tSTR, + // used internal to the lexer but never returned + tSPC, tINV, tDQT, tSQT, tMSC, +}; + +const char *tnames[] = { + "<EOF>", "<EOL>", "{", "}", "[", "]", "(", ")", + "==", "!=", "<", "<=", ">", ">=", "", "", + "+", "-", "|", "^", "", "", "", "", + "*", "/", "%", "&", "<<", ">>", "", "", + "+=", "-=", "|=", "^=", "", "", "", "", + "*=", "/=", "%=", "&=", "<<=", ">>=", "", "", + ";", ":", ".", ",", "~", "&&", "||", "!", + "=", "++", "--", + "type", "func", "struct", "var", "enum", + "if", "else", "while", + "break", "continue", "return", + "for", "switch", "case", + "true", "false", "nil", + "<ID>", "<NUM>", "<STR>", + "<SPC>", "<INV>", "<DQT>", "<SQT>", "<MSC>", +}; + +u8 lextab[256] = { + tEOF, tINV, tINV, tINV, tINV, tINV, tINV, tINV, + tINV, tSPC, tEOL, tSPC, tINV, tSPC, tINV, tINV, + tINV, tINV, tINV, tINV, tINV, tINV, tINV, tINV, + tINV, tINV, tINV, tINV, tINV, tINV, tINV, tINV, + tSPC, tBANG, tDQT, tMSC, tMSC, tPERCENT, tAMP, tSQT, + tOPAREN, tCPAREN, tSTAR, tPLUS, tCOMMA, tMINUS, tDOT, tSLASH, + tNUM, tNUM, tNUM, tNUM, tNUM, tNUM, tNUM, tNUM, + tNUM, tNUM, tCOLON, tSEMI, tLT, tASSIGN, tGT, tMSC, + tMSC, tIDN, tIDN, tIDN, tIDN, tIDN, tIDN, tIDN, + tIDN, tIDN, tIDN, tIDN, tIDN, tIDN, tIDN, tIDN, + tIDN, tIDN, tIDN, tIDN, tIDN, tIDN, tIDN, tIDN, + tIDN, tIDN, tIDN, tOBRACK, tMSC, tCBRACK, tCARET, tIDN, + tMSC, tIDN, tIDN, tIDN, tIDN, tIDN, tIDN, tIDN, + tIDN, tIDN, tIDN, tIDN, tIDN, tIDN, tIDN, tIDN, + tIDN, tIDN, tIDN, tIDN, tIDN, tIDN, tIDN, tIDN, + tIDN, tIDN, tIDN, tOBRACE, tPIPE, tCBRACE, tNOT, tINV, + tINV, tINV, tINV, tINV, tINV, tINV, tINV, tINV, + tINV, tINV, tINV, tINV, tINV, tINV, tINV, tINV, + tINV, tINV, tINV, tINV, tINV, tINV, tINV, tINV, + tINV, tINV, tINV, tINV, tINV, tINV, tINV, tINV, + tINV, tINV, tINV, tINV, tINV, tINV, tINV, tINV, + tINV, tINV, tINV, tINV, tINV, tINV, tINV, tINV, + tINV, tINV, tINV, tINV, tINV, tINV, tINV, tINV, + tINV, tINV, tINV, tINV, tINV, tINV, tINV, tINV, + tINV, tINV, tINV, tINV, tINV, tINV, tINV, tINV, + tINV, tINV, tINV, tINV, tINV, tINV, tINV, tINV, + tINV, tINV, tINV, tINV, tINV, tINV, tINV, tINV, + tINV, tINV, tINV, tINV, tINV, tINV, tINV, tINV, + tINV, tINV, tINV, tINV, tINV, tINV, tINV, tINV, + tINV, tINV, tINV, tINV, tINV, tINV, tINV, tINV, + tINV, tINV, tINV, tINV, tINV, tINV, tINV, tINV, + tINV, tINV, tINV, tINV, tINV, tINV, tINV, tINV, +}; + +i32 unhex(u32 ch) { + if ((ch >= '0') && (ch <= '9')) { + return ch - '0'; + } + if ((ch >= 'a') && (ch <= 'f')) { + return ch - 'a' + 10; + } + if ((ch >= 'A') && (ch <= 'F')) { + return ch - 'A' + 10; + } + return -1; +} + +u32 scan() { + while (ctx.ionext == ctx.iolast) { + if (ctx.fd < 0) { + ctx.cc = 0; + return ctx.cc; + } + int r = read(ctx.fd, ctx.iobuffer, sizeof(ctx.iobuffer)); + if (r <= 0) { + ctx.fd = -1; + } else { + ctx.iolast = r; + ctx.ionext = 0; + } + } + ctx.cc = ctx.iobuffer[ctx.ionext]; + ctx.ionext++; + ctx.byteoffset++; + return ctx.cc; +} + +u32 unescape(u32 n) { + if (n == 'n') { + return 10; + } else if (n == 'r') { + return 13; + } else if (n == 't') { + return 9; + } else if (n == '"') { + return '"'; + } else if (n == '\'') { + return '\''; + } else if (n == '\\') { + return '\\'; + } else if (n == 'x') { + int x0 = unhex(scan()); + int x1 = unhex(scan()); + if ((x0 < 0) || (x1 < 0)) { + error("invalid hex escape"); + } + return (x0 << 4) | x1; + } else { + error("invalid escape 0x%02x", n); + return 0; + } +} + +token_t scan_string(u32 cc, u32 nc) { + u32 n = 0; + while (true) { + if (nc == '"') { + break; + } else if (nc == 0) { + error("unterminated string"); + } else if (nc == '\\') { + ctx.tmp[n] = unescape(scan()); + } else { + ctx.tmp[n] = nc; + } + nc = scan(); + n++; + if (n == 255) { + error("constant string too large"); + } + } + ctx.tmp[n] = 0; + return tSTR; +} + +token_t scan_keyword(u32 len) { + ctx.tmp[len] = 0; + String *idn = string_make(ctx.tmp, len); + ctx.ident = idn; + + if (len == 2) { + if (idn == ctx.idn_if) { return tIF; }; + } else if (len == 3) { + if (idn == ctx.idn_for) { return tFOR; } + if (idn == ctx.idn_var) { return tVAR; } + if (idn == ctx.idn_nil) { return tNIL; } + } else if (len == 4) { + if (idn == ctx.idn_case) { return tCASE; } + if (idn == ctx.idn_func) { return tFUNC; } + if (idn == ctx.idn_else) { return tELSE; } + if (idn == ctx.idn_enum) { return tENUM; } + if (idn == ctx.idn_true) { return tTRUE; } + if (idn == ctx.idn_type) { return tTYPE; } + } else if (len == 5) { + if (idn == ctx.idn_break) { return tBREAK; } + if (idn == ctx.idn_while) { return tWHILE; } + if (idn == ctx.idn_false) { return tFALSE; } + } else if (len == 6) { + if (idn == ctx.idn_switch) { return tSWITCH; } + if (idn == ctx.idn_struct) { return tSTRUCT; } + if (idn == ctx.idn_return) { return tRETURN; } + } else if (len == 8) { + if (idn == ctx.idn_continue) { return tCONTINUE; } + } + return tIDN; +} + +token_t scan_number(u32 cc, u32 nc) { + u32 n = 1; + u32 val = cc - '0'; + + if ((cc == '0') && (nc == 'b')) { // binary + nc = scan(); + while ((nc == '0') || (nc == '1')) { + val = (val << 1) | (nc - '0'); + nc = scan(); + n++; + if (n == 34) { + error("binary constant too large"); + } + } + } else if ((cc == '0') && (nc == 'x')) { // hex + nc = scan(); + while (true) { + int tmp = unhex(nc); + if (tmp == -1) { + break; + } + val = (val << 4) | tmp; + nc = scan(); + n++; + if (n == 10) { + error("hex constant too large"); + } + } + } else { // decimal + while (lextab[nc] == tNUM) { + u32 tmp = (val * 10) + (nc - '0'); + if (tmp <= val) { + error("decimal constant too large"); + } + val = tmp; + nc = scan(); + n++; + } + } + ctx.num = val; + return tNUM; +} + +token_t scan_ident(u32 cc, u32 nc) { + ctx.tmp[0] = cc; + u32 n = 1; + + while (true) { + u32 tok = lextab[nc]; + if ((tok == tIDN) || (tok == tNUM)) { + ctx.tmp[n] = nc; + n++; + if (n == 32) { error("identifier too large"); } + nc = scan(); + } else { + break; + } + } + return scan_keyword(n); +} + +token_t _next() { + u8 nc = ctx.cc; + while (true) { + u8 cc = nc; + nc = scan(); + u32 tok = lextab[cc]; + if (tok == tNUM) { // 0..9 + return scan_number(cc, nc); + } else if (tok == tIDN) { // _ A..Z a..z + return scan_ident(cc, nc); + } else if (tok == tDQT) { // " + return scan_string(cc, nc); + } else if (tok == tSQT) { // ' + ctx.num = nc; + if (nc == '\\') { + ctx.num = unescape(scan()); + } + nc = scan(); + if (nc != '\'') { + error("unterminated character constant"); + } + nc = scan(); + return tNUM; + } else if (tok == tPLUS) { + if (nc == '+') { tok = tINC; nc = scan(); } + } else if (tok == tMINUS) { + if (nc == '-') { tok = tDEC; nc = scan(); } + } else if (tok == tAMP) { + if (nc == '&') { tok = tAND; nc = scan(); } + } else if (tok == tPIPE) { + if (nc == '|') { tok = tOR; nc = scan(); } + } else if (tok == tGT) { + if (nc == '=') { tok = tGE; nc = scan(); } + else if (nc == '>') { tok = tRIGHT; nc = scan(); } + } else if (tok == tLT) { + if (nc == '=') { tok = tLE; nc = scan(); } + else if (nc == '<') { tok = tLEFT; nc = scan(); } + } else if (tok == tASSIGN) { + if (nc == '=') { tok = tEQ; nc = scan(); } + } else if (tok == tBANG) { + if (nc == '=') { tok = tNE; nc = scan(); } + } else if (tok == tSLASH) { + if (nc == '/') { + // comment -- consume until EOL or EOF + while ((nc != '\n') && (nc != 0)) { + nc = scan(); + } + continue; + } + } else if (tok == tEOL) { + ctx.linenumber++; + ctx.lineoffset = ctx.byteoffset; + if (ctx.flags & cfVisibleEOL) { + return tEOL; + } + continue; + } else if (tok == tSPC) { + continue; + } else if ((tok == tMSC) || (tok == tINV)) { + error("unknown character 0x%02x", cc); + } + + // if we're an AddOp or MulOp, followed by an '=' + if (((tok & 0xF0) == 0x20) && (nc == '=')) { + nc = scan(); + // transform us to a XEQ operation + tok = tok + 0x10; + } + + return tok; + } +} + +token_t next() { + return (ctx.tok = _next()); +} + +void token_printstr(void) { + u32 n = 0; + printf("\""); + while (n < 256) { + u32 ch = ctx.tmp[n]; + if (ch == 0) { + break; + } else if ((ch < ' ') || (ch > '~')) { + printf("\\x%02x", ch); + } else if ((ch == '"') || (ch == '\\')) { + printf("\\%c", ch); + } else { + printf("%c", ch); + } + n++; + } + printf("\""); +} + +void token_print(void) { + if (ctx.tok == tNUM) { + printf("#%u ", ctx.num); + } else if (ctx.tok == tIDN) { + printf("@%s ", ctx.tmp); + } else if (ctx.tok == tEOL) { + printf("\n"); + } else if (ctx.tok == tSTR) { + token_printstr(); + } else { + printf("%s ", tnames[ctx.tok]); + } +} + +void expected(const char* what) { + error("expected %s, found %s", what, tnames[ctx.tok]); +} + +void expect(token_t tok) { + if (ctx.tok != tok) { + error("expected %s, found %s", tnames[tok], tnames[ctx.tok]); + } +} + +void require(token_t tok) { + expect(tok); + next(); +} + +String *parse_name(const char* what) { + if (ctx.tok != tIDN) { + error("expected %s, found %s %u", what, tnames[ctx.tok], ctx.tok); + } + String *str = ctx.ident; + next(); + return str; +} + +void parse_expr(void); + +void parse_ident(void) { + String *name = ctx.ident; + Symbol *sym = symbol_find(name); + next(); + + if ((sym == nil) && (ctx.tok != tOPAREN)) { + error("undefined identifier '%s'", name->text); + } + + if (ctx.tok == tOPAREN) { // function call + next(); + emit(IMPL,"f$%s(", name->text); + while (ctx.tok != tCPAREN) { + parse_expr(); + if (ctx.tok != tCPAREN) { + require(tCOMMA); + emit(IMPL,", "); + } + } + next(); + emit(IMPL,")"); + } else if (ctx.tok == tDOT) { // field access + next(); + String *fieldname = parse_name("field name"); + Symbol *field = type_find_field(sym->type, fieldname); + emit(IMPL,"(v$%s->f$%s)", name->text, fieldname->text); + } else if (ctx.tok == tOBRACK) { // array access + next(); + // XXX handle slices + if (sym->type->kind != TYPE_ARRAY) { + error("cannot access '%s' as an array", name->text); + } + emit(IMPL,"(v$%s[", name->text); + parse_expr(); + emit(IMPL,"])"); + } else { // variable access + emit(IMPL,"v$%s", sym->name->text); + } +} + +void parse_primary_expr(void) { + if (ctx.tok == tNUM) { + emit(IMPL,"0x%08x", ctx.num); + } else if (ctx.tok == tSTR) { + error("<TODO> string const"); + } else if (ctx.tok == tTRUE) { + emit(IMPL,"1"); + } else if (ctx.tok == tFALSE) { + emit(IMPL,"0"); + } else if (ctx.tok == tNIL) { + emit(IMPL,"0"); + } else if (ctx.tok == tOPAREN) { + next(); + parse_expr(); + require(tCPAREN); + return; + } else if (ctx.tok == tIDN) { + parse_ident(); + return; + } else { + error("invalid expression"); + } + next(); +} + +void parse_unary_expr(void) { + u32 op = ctx.tok; + if (op == tPLUS) { + next(); + parse_unary_expr(); + } else if (op == tMINUS) { + emit(IMPL,"(-"); + next(); + parse_unary_expr(); + emit(IMPL,")"); + } else if (op == tBANG) { + emit(IMPL,"(!"); + next(); + parse_unary_expr(); + emit(IMPL,")"); + } else if (op == tNOT) { + emit(IMPL,"(~"); + next(); + parse_unary_expr(); + emit(IMPL,")"); + } else if (op == tAMP) { + error("dereference not supported"); + next(); + parse_unary_expr(); + } else { + return parse_primary_expr(); + } +} + +void parse_mul_expr(void) { + emit(IMPL,"("); + parse_unary_expr(); + while ((ctx.tok & tcMASK) == tcMULOP) { + emit(IMPL," %s ", tnames[ctx.tok]); + next(); + parse_unary_expr(); + } + emit(IMPL,")"); +} + +void parse_add_expr(void) { + emit(IMPL,"("); + parse_mul_expr(); + while ((ctx.tok & tcMASK) == tcADDOP) { + emit(IMPL," %s ", tnames[ctx.tok]); + next(); + parse_mul_expr(); + } + emit(IMPL,")"); +} + +void parse_rel_expr(void) { + emit(IMPL,"("); + parse_add_expr(); + if ((ctx.tok & tcMASK) == tcRELOP) { + emit(IMPL," %s ", tnames[ctx.tok]); + next(); + parse_add_expr(); + } + emit(IMPL,")"); +} + +void parse_and_expr(void) { + emit(IMPL,"("); + parse_rel_expr(); + if (ctx.tok == tAND) { + while (ctx.tok == tAND) { + emit(IMPL," && "); + next(); + parse_rel_expr(); + } + } + emit(IMPL,")"); +} + +void parse_expr(void) { + emit(IMPL,"("); + parse_and_expr(); + if (ctx.tok == tOR) { + while (ctx.tok == tOR) { + emit(IMPL," || "); + next(); + parse_and_expr(); + } + } + emit(IMPL,")"); +} + +// fwd_ref_ok indicates that an undefined typename +// may be treated as a forward reference. This is +// only used for pointers (because their size does +// not depend on their target). +Type *parse_type(bool fwd_ref_ok); + +Type *parse_struct_type(String *name) { + Type *rectype = type_make(name, TYPE_STRUCT, nil, nil, 0); + scope_push(SCOPE_STRUCT); + require(tOBRACE); + emit(TYPE,"typedef struct t$%s t$%s;\n", name->text, name->text); + emit(TYPE,"struct t$%s {\n", name->text); + while (true) { + if (ctx.tok == tCBRACE) { + next(); + break; + } + String *fname = parse_name("field name"); + bool ptr = (ctx.tok == tSTAR); + if (ptr) next(); + Type *type = parse_type(false); + emit(TYPE," t$%s %sf$%s;\n", type->name->text, ptr ? "*" : "", fname->text); + Symbol *sym = symbol_make(fname, type); + sym->kind = ptr ? SYMBOL_PTR : SYMBOL_FLD; + if (ctx.tok != tCBRACE) { + require(tCOMMA); + } + } + emit(TYPE,"};\n"); + rectype->fields = scope_pop()->first; + return rectype; +} + +Type *parse_array_type(void) { + if (ctx.tok == tCBRACK) { + next(); + return type_make(nil, TYPE_SLICE, parse_type(false), nil, 0); + } else { + if (ctx.tok != tNUM) { + error("array size must be numeric"); + } + u32 nelem = ctx.num; + next(); + require(tCBRACK); + return type_make(nil, TYPE_ARRAY, parse_type(false), nil, nelem); + } +} + +Type *parse_type(bool fwd_ref_ok) { + if (ctx.tok == tSTAR) { // pointer-to + error("pointer types not supported"); + //next(); + //return type_make(nil, TYPE_POINTER, parse_type(true), nil, 0); + } else if (ctx.tok == tOBRACK) { // array-of + next(); + return parse_array_type(); + } else if (ctx.tok == tFUNC) { + error("func types not supported"); + //next(); + //return parse_func_type(); + } else if (ctx.tok == tSTRUCT) { + error ("anonymous struct types not supported"); + //next(); + //return parse_struct_type(nil); + } else if (ctx.tok == tIDN) { + String *name = ctx.ident; + next(); + Type *type = type_find(name); + if (type == nil) { + if (fwd_ref_ok) { + type = type_make(name, TYPE_UNDEFINED, nil, nil, 0); + } else { + error("undefined type '%s' not usable here", name->text); + } + } + return type; + } else { + expected("type"); + } + return nil; +} + +void parse_block(void); + +void parse_while(void) { + emit(IMPL,"while "); + parse_expr(); + require(tOBRACE); + scope_push(SCOPE_LOOP); + emit(IMPL,"{\n"); + parse_block(); + scope_pop(); + emit(IMPL,"\n}\n"); +} + +void parse_if(void) { + // if expr { block } + emit(IMPL,"if "); + parse_expr(); + emit(IMPL," {\n"); + require(tOBRACE); + scope_push(SCOPE_BLOCK); + parse_block(); + scope_pop(); + while (ctx.tok == tELSE) { + next(); + // ... else ... + if (ctx.tok == tIF) { + // ... if expr { block } + emit(IMPL,"\n} else if "); + next(); + parse_expr(); + require(tOBRACE); + emit(IMPL," {\n"); + scope_push(SCOPE_BLOCK); + parse_block(); + scope_pop(); + } else { + // ... { block } + emit(IMPL,"\n} else {\n"); + require(tOBRACE); + scope_push(SCOPE_BLOCK); + parse_block(); + scope_pop(); + break; + } + } + emit(IMPL,"\n}\n"); +} + +void parse_return(void) { + if (ctx.tok == tSEMI) { + // error("function requires return type"); + next(); + emit(IMPL,"return;\n"); + } else { + // error("return types do not match"); + emit(IMPL,"return "); + parse_expr(); + emit(IMPL,";\n"); + require(tSEMI); + } +} + +void parse_break(void) { + // XXX break-to-labeled-loop support + Scope *scope = scope_find(SCOPE_LOOP); + if (scope == nil) { + error("break must be used from inside a looping construct"); + } + require(tSEMI); + emit(IMPL,"break;\n"); +} + +void parse_continue(void) { + // XXX continue-to-labeled-loop support + Scope *scope = scope_find(SCOPE_LOOP); + if (scope == nil) { + error("continue must be used from inside a looping construct"); + } + require(tSEMI); + emit(IMPL,"continue;\n"); +} + +#if 0 +u32 parse_array_init(Symbol var, u32ptr data, u32 dmax, u32 sz) { + memset(data, 0, dmax); + u32 n = 0; + while (true) { + if (ctx.tok == tCBRACE) { + next(); + break; + } + if (n >= dmax) { + error("initializer too large"); + } + Ast expr = parse_expr(); + i32 v = ast_get_const_i32(expr); + + // VALIDATE type compat/fit + STORE(v, data, n, sz); + n += sz; + if (ctx.tok != tCBRACE) { + require(tCOMMA); + } + } + return n; +} +#endif + +void parse_struct_init(Symbol *var) { + while (true) { + if (ctx.tok == tCBRACE) { + next(); + break; + } + String *name = parse_name("field name"); + Symbol *field = var->type->fields; + while (true) { + if (field == nil) { + error("structure has no '%s' field", name->text); + } + if (field->name == name) { + break; + } + field = field->next; + } + require(tCOLON); + if (ctx.tok == tOBRACE) { + next(); + emit(IMPL,"{"); + parse_struct_init(field); + emit(IMPL,"}"); + } else { + parse_expr(); + //emit(IMPL, "0x%x", ctx.num); + } + emit(IMPL, ","); + if (ctx.tok != tCBRACE) { + require(tCOMMA); + } + } +} + +void parse_var(void) { + String *name = parse_name("variable name"); + Type *type = parse_type(false); + Symbol *var = symbol_make(name, type); + + if (ctx.tok == tASSIGN) { + next(); + if (ctx.tok == tOBRACE) { + next(); + emit(IMPL,"t$%s v$$%s = {\n", type->name->text, name->text); + parse_struct_init(var); + emit(IMPL,"\n};\nt$%s *v$%s = &v$$%s;\n", + type->name->text, name->text, name->text); + } else { + emit(IMPL,"t$%s %sv$%s = ", type->name->text, + (type->kind == TYPE_STRUCT) ? "*" : "", + name->text); + parse_expr(); + emit(IMPL,";\n"); + } + } else { + emit(IMPL,"t$%s %sv$%s = 0;", type->name->text, + (type->kind == TYPE_STRUCT) ? "*" : "", + name->text); + } + require(tSEMI); + +} + +void parse_expr_statement(void) { + parse_expr(); + if (ctx.tok == tASSIGN) { + emit(IMPL," = "); + next(); + parse_expr(); + } else if ((ctx.tok & tcMASK) == tcAEQOP) { + emit(IMPL," %s ", tnames[ctx.tok]); + next(); + parse_expr(); + } else if ((ctx.tok & tcMASK) == tcMEQOP) { + emit(IMPL," %s ", tnames[ctx.tok]); + next(); + parse_expr(); + } else if ((ctx.tok == tINC) || (ctx.tok == tDEC)) { + emit(IMPL," %s", tnames[ctx.tok]); + next(); + } + require(tSEMI); + emit(IMPL,";\n"); +} + +void parse_block(void) { + while (true) { + if (ctx.tok == tCBRACE) { + next(); + break; + } else if (ctx.tok == tRETURN) { + next(); + parse_return(); + } else if (ctx.tok == tBREAK) { + next(); + parse_break(); + } else if (ctx.tok == tCONTINUE) { + next(); + parse_continue(); + } else if (ctx.tok == tWHILE) { + next(); + parse_while(); + } else if (ctx.tok == tIF) { + next(); + parse_if(); + } else if (ctx.tok == tVAR) { + next(); + parse_var(); + } else if (ctx.tok == tSEMI) { + next(); + // empty statement + continue; + } else { + parse_expr_statement(); + } + } +} + +Symbol *parse_param(String *fname) { + String *pname = parse_name("parameter name"); + Type *ptype = parse_type(false); + + // arrays and structs are always passed as reference parameters + //if ((ptype->kind == TYPE_ARRAY) || (ptype->kind == TYPE_RECORD)) { + // ptype = type_make_ptr(ptype); + //} + + if (symbol_find(pname)) { + error("duplicate parameter name '%s'", pname->text); + } + + return symbol_make(pname, ptype); +} + +void parse_function(void) { + String *fname = parse_name("function name"); + Type *rtype = ctx.type_void; + + scope_push(SCOPE_FUNC); + + require(tOPAREN); + if (ctx.tok != tCPAREN) { + parse_param(fname); + while (ctx.tok == tCOMMA) { + next(); + parse_param(fname); + } + } + require(tCPAREN); + + if (ctx.tok != tOBRACE) { + rtype = parse_type(false); + } + + emit(DECL,"t$%s f$%s(", rtype->name->text, fname->text); + emit(IMPL,"t$%s f$%s(", rtype->name->text, fname->text); + for (Symbol *s = ctx.scope->first; s != nil; s = s->next) { + emit(DECL,"t$%s %sv$%s%s", + s->type->name->text, + s->type->kind == TYPE_STRUCT ? "*" : "", + s->name->text, s->next ? "," : ""); + emit(IMPL,"t$%s %sv$%s%s", + s->type->name->text, + s->type->kind == TYPE_STRUCT ? "*" : "", + s->name->text, s->next ? "," : ""); + } + emit(DECL,"%s);\n", ctx.scope->first ? "" : "void"); + emit(IMPL,"%s) {\n", ctx.scope->first ? "" : "void"); + + require(tOBRACE); + + scope_push(SCOPE_BLOCK); + parse_block(); + scope_pop(); + + emit(IMPL,"\n}\n"); + + scope_pop(); +} + +void parse_enum_def(void) { + require(tOBRACE); + u32 val = 0; + while (ctx.tok != tCBRACE) { + String *name = parse_name("enum tag name"); + Symbol *sym = symbol_find(name); + if (sym != nil) { + error("cannot redefine %s as enum tag\n", name->text); + } + if (ctx.tok == tASSIGN) { + next(); + if (ctx.tok != tNUM) { + error("enum value not a number"); + } + val = ctx.num; + } + require(tCOMMA); + symbol_make_global(name, ctx.type_u32); + emit(DECL,"static const t$u32 v$%s = 0x%08x;\n", name->text, val); + val++; + } + require(tCBRACE); + require(tSEMI); +} + +void parse_program() { + emit(IMPL,"\n#include <library.impl.h>\n"); + next(); + for (;;) { + if (ctx.tok == tENUM) { + next(); + parse_enum_def(); + } else if (ctx.tok == tSTRUCT) { + next(); + String *name = parse_name("struct name"); + parse_struct_type(name); + require(tSEMI); + } else if (ctx.tok == tFUNC) { + next(); + parse_function(); + } else if (ctx.tok == tVAR) { + next(); + parse_var(); + } else if (ctx.tok == tEOF) { + emit(IMPL,"\n#include <library.impl.c>\n"); + return; + } else { + expected("function, variable, or type definition"); + } + } + +} + +// ================================================================ + +int main(int argc, char **argv) { + char *srcname = nil; + bool scan_only = false; + + ctx_init(); + ctx.filename = "<commandline>"; + ctx.outname = nil; + + while (argc > 1) { + if (!strcmp(argv[1],"-o")) { + if (argc < 2) { + error("option -o requires argument"); + } + ctx.outname = argv[2]; + argc--; + argv++; + } else if (!strcmp(argv[1], "-s")) { + scan_only = true; + } else if (!strcmp(argv[1], "-A")) { + ctx.flags |= cfAbortOnError; + } else if (argv[1][0] == '-') { + error("unknown option: %s", argv[1]); + } else { + if (srcname != nil) { + error("multiple source files disallowed"); + } else { + srcname = argv[1]; + } + } + argc--; + argv++; + } + + if (srcname == nil) { + printf( +"usage: compiler [ <option> | <sourcefilename> ]*\n" +"\n" +"options: -o <filename> output base name (default source name)\n" +" -s scan only\n" +" -A abort on error\n"); + return 0; + } + ctx.filename = srcname; + if (ctx.outname == nil) { + ctx.outname = srcname; + } + + ctx_open_source(srcname); + ctx.linenumber = 1; + ctx.lineoffset = 0; + + ctx_open_output(); + // prime the lexer + scan(); + + if (scan_only) { + ctx.flags |= 1; + while (true) { + next(); + token_print(); + if (ctx.tok == tEOF) { + printf("\n"); + return 0; + } + } + } + + parse_program(); + return 0; +} diff --git a/inc/builtin.type.h b/inc/builtin.type.h @@ -0,0 +1,11 @@ + +#include <stdint.h> + +typedef void t$void; + +typedef uint32_t t$u32; +typedef int32_t t$i32; +typedef uint16_t t$u16; +typedef int16_t t$i16; +typedef uint8_t t$u8; +typedef int8_t t$i8; diff --git a/inc/library.impl.c b/inc/library.impl.c @@ -0,0 +1,11 @@ + +void f$_hexout_(int x) { + printf("D %08x\n", x); +} + +int main(int argc, char** argv) { + int x = f$start(); + printf("X %08x\n", x); + return 0; +} + diff --git a/inc/library.impl.h b/inc/library.impl.h @@ -0,0 +1,3 @@ +#include <stdio.h> + +t$void f$_hexout_(t$i32 x); diff --git a/test/1000-return-42.log b/test/1000-return-42.log @@ -0,0 +1 @@ +X 0000002a diff --git a/test/1000-return-42.src b/test/1000-return-42.src @@ -0,0 +1,3 @@ +func start() i32 { + return 42; +} diff --git a/test/1010-func-call.log b/test/1010-func-call.log @@ -0,0 +1 @@ +X 0000002a diff --git a/test/1010-func-call.src b/test/1010-func-call.src @@ -0,0 +1,7 @@ +func add(a i32, b i32) i32 { + return a + b; +} + +func start() i32 { + return add(20, 22); +} diff --git a/test/1011-func-call-forward.log b/test/1011-func-call-forward.log @@ -0,0 +1 @@ +X 0000002a diff --git a/test/1011-func-call-forward.src b/test/1011-func-call-forward.src @@ -0,0 +1,8 @@ +func start() i32 { + return add(20, 22); +} + +func add(a i32, b i32) i32 { + return a + b; +} + diff --git a/test/1012-func-call-builtin.log b/test/1012-func-call-builtin.log @@ -0,0 +1,4 @@ +D 10203040 +D cafef00d +D ffffffff +X 00000000 diff --git a/test/1012-func-call-builtin.src b/test/1012-func-call-builtin.src @@ -0,0 +1,6 @@ +func start() i32 { + _hexout_(0x10203040); + _hexout_(0xcafef00d); + _hexout_(-1); + return 0; +} diff --git a/test/1020-math-1.log b/test/1020-math-1.log @@ -0,0 +1,10 @@ +D ffd23137 +D 88d050a1 +D 00000002 +D 00000080 +D 00000020 +D ffffffe0 +D 0000000e +D 003c8c00 +D fffe0c00 +X 00000000 diff --git a/test/1020-math-1.src b/test/1020-math-1.src @@ -0,0 +1,22 @@ + +func print(n i32) { + _hexout_(n); +} + +func test(v2 i32, v5 i32, v31 i32, v128000 i32) { + // quick check hex and bin consts + print(0xFFD23137); + print(0b10001000110100000101000010100001); + print(v2); + print(v128000 / 1000); + print(1 << v5); + print(~v31); + print(v31 % 17); + print(v128000 * v31); + print(-v128000); +} + +func start() i32 { + test(2, 5, 31, 128000); + return 0; +} diff --git a/test/1021-numbers.log b/test/1021-numbers.log @@ -0,0 +1,24 @@ +D ffffffff +D 80000000 +D ffffffff +D 80000000 +D ffffffff +D 80000000 +D ffffffff +D fffffffe +D 80000000 +D 7fffffff +D 88aacc55 +D 88aacc55 +D 88aacc55 +D 00000061 +D 00000009 +D 0000000a +D 0000000d +D 0000005c +D 00000027 +D 00000022 +D 00000042 +D 000000ff +D 00000080 +X 00000000 diff --git a/test/1021-numbers.src b/test/1021-numbers.src @@ -0,0 +1,27 @@ + +func start() i32 { + _hexout_(4294967295); // maxu32 + _hexout_(2147483648); // highbit + _hexout_(0xffffffff); // maxu32 + _hexout_(0x80000000); // highbit + _hexout_(0b11111111111111111111111111111111); // maxu32 + _hexout_(0b10000000000000000000000000000000); // highbit + _hexout_(-1); + _hexout_(-2); + _hexout_(-2147483648); // minint32 + _hexout_(2147483647); // maxint32 + _hexout_(0b10001000101010101100110001010101); + _hexout_(2292894805); + _hexout_(0x88aacc55); + _hexout_('a'); // char consts + _hexout_('\t'); + _hexout_('\n'); + _hexout_('\r'); + _hexout_('\\'); + _hexout_('\''); + _hexout_('"'); + _hexout_('\x42'); + _hexout_('\xFF'); + _hexout_('\x80'); + return 0; +} diff --git a/test/1024-xorshift32.log b/test/1024-xorshift32.log @@ -0,0 +1,65 @@ +D 8df0839b +D 2df6061a +D 4b932080 +D dfe5df61 +D e53061c5 +D c81e1d61 +D 7df52fb8 +D d84a3499 +D 45f22555 +D 2a483ad9 +D 8f728650 +D 28a5d80c +D e7262a1e +D 6eda9eac +D 9cef054b +D f48d3668 +D 1a43f648 +D f5d245ad +D 1190d8de +D 5be8aefb +D 88cad5e0 +D 9ca127bb +D b2956990 +D eb4bae23 +D 4f7fade4 +D e2ad6025 +D 9b950951 +D 6d59b5ce +D 27eb5ffe +D ce838774 +D 73c65002 +D 810fd0c1 +D 19ee640a +D 7886991d +D df08f52f +D f403cad9 +D 264d22d5 +D c0efb81e +D ca601ee8 +D fe123cf6 +D 8818a630 +D 0703e3bf +D 15f3fea5 +D 2eca1d76 +D c1f99344 +D 81bc3b0c +D dd6ab422 +D f6388b75 +D 0d9d431e +D 1a24edc1 +D 740d4fef +D 63e8e3f7 +D 8d51bbbc +D feeab34f +D b8e3e6ce +D 4372deb3 +D 0c32eec1 +D 6cb620b4 +D bcbe6864 +D 47e8c71d +D be625b98 +D 57350310 +D 1db86fdb +D 1823f8ba +X 00000040 diff --git a/test/1024-xorshift32.src b/test/1024-xorshift32.src @@ -0,0 +1,20 @@ + +// todo: u32 and LSR support + +var state i32 = 0xd3f56332; + +func xorshift32() i32 { + state = state ^ (state << 13); + state = state ^ ((state >> 17) & 0x7FFF); + state = state ^ (state << 5); + return state; +} + +func start() i32 { + var n i32 = 0; + while (n < 64) { + _hexout_(xorshift32()); + n++; + } + return 64; +} diff --git a/test/1025-fibonacci.log b/test/1025-fibonacci.log @@ -0,0 +1,25 @@ +D 00000000 +D 00000001 +D 00000001 +D 00000002 +D 00000003 +D 00000005 +D 00000008 +D 0000000d +D 00000015 +D 00000022 +D 00000037 +D 00000059 +D 00000090 +D 000000e9 +D 00000179 +D 00000262 +D 000003db +D 0000063d +D 00000a18 +D 00001055 +D 00001a6d +D 00002ac2 +D 0000452f +D 00006ff1 +X 00000007 diff --git a/test/1025-fibonacci.src b/test/1025-fibonacci.src @@ -0,0 +1,17 @@ + +func fib(n i32) i32 { + if (n < 2) { + return n; + } else { + return fib(n - 1) + fib(n - 2); + } +} + +func start() i32 { + var n i32 = 0; + while n < 24 { + _hexout_(fib(n)); + n++; + } + return 7; +} diff --git a/test/1026.fib.iter.log b/test/1026.fib.iter.log @@ -0,0 +1,25 @@ +D 00000000 +D 00000001 +D 00000001 +D 00000002 +D 00000003 +D 00000005 +D 00000008 +D 0000000d +D 00000015 +D 00000022 +D 00000037 +D 00000059 +D 00000090 +D 000000e9 +D 00000179 +D 00000262 +D 000003db +D 0000063d +D 00000a18 +D 00001055 +D 00001a6d +D 00002ac2 +D 0000452f +D 00006ff1 +X 00000007 diff --git a/test/1026.fib.iter.src b/test/1026.fib.iter.src @@ -0,0 +1,23 @@ + +func fib(n i32) i32 { + var a i32 = 0; + var b i32 = 1; + var z i32 = 0; + while (n != z) { + var t i32 = a + b; + a = b; + b = t; + n = n - 1; + z = 0; + } + return a; +} + +func start() i32 { + var n i32 = 0; + while n < 24 { + _hexout_(fib(n)); + n++; + } + return 7; +} diff --git a/test/1030-flow-control.log b/test/1030-flow-control.log @@ -0,0 +1,14 @@ +D 10101010 +D 40404040 +D 00000002 +D 00000001 +D 00000007 +D 00000006 +D 00000005 +D 00000004 +D 00000003 +D 00000006 +D 00000004 +D 00000002 +D 00000000 +X 00000001 diff --git a/test/1030-flow-control.src b/test/1030-flow-control.src @@ -0,0 +1,45 @@ +func hex(n i32) { + _hexout_(n); +} + +func count(n i32) { + while (n > 0) { + hex(n); + if (n == 3) { + break; + } + n = n - 1; + } +} + +func count_even(n i32) { + while (n > 0) { + n--; + if ((n & 1) == 1) { + continue; + } + _hexout_(n); + } +} + +func start() i32 { + if (true) { + hex(0x10101010); + } else { + hex(0x20202020); + } + + if (false) { + hex(0x30303030); + } else { + hex(0x40404040); + } + + count(2); + + count(7); + + count_even(7); + + return 1; +} diff --git a/test/1031-logical-and-or.log b/test/1031-logical-and-or.log @@ -0,0 +1,64 @@ +D 00000200 +D 00000201 +D 00000202 +D 00000103 +D 00000204 +D 00000205 +D 00000106 +D 00000207 +D 00000208 +D 00000109 +D 00000333 +D 00000666 +D 00000999 +D 00002200 +D 00000333 +D 00000666 +D 00000999 +D 00002201 +D 00000333 +D 00000666 +D 00000999 +D 00002202 +D 00000333 +D 00001103 +D 00000333 +D 00000666 +D 00000999 +D 00002204 +D 00000333 +D 00000666 +D 00000999 +D 00002205 +D 00000333 +D 00000666 +D 00001106 +D 00000333 +D 00000666 +D 00000999 +D 00002207 +D 00000333 +D 00000666 +D 00000999 +D 00002208 +D 00000333 +D 00000666 +D 00000999 +D 00001109 +D 00000400 +D 00000401 +D 00000402 +D 00000303 +D 00000404 +D 00000405 +D 00000406 +D 00000307 +D 00000408 +D 00000409 +D 0000040a +D 0000040b +D 0000040c +D 0000040d +D 0000040e +D 0000040f +X 00000042 diff --git a/test/1031-logical-and-or.src b/test/1031-logical-and-or.src @@ -0,0 +1,56 @@ + +func is_three(n i32) bool { + _hexout_(0x333); + return n == 3; +} +func is_six(n i32) bool { + _hexout_(0x666); + return n == 6; +} +func is_nine(n i32) bool { + _hexout_(0x999); + return n == 9; +} + +func or_test2(n i32) { + if (is_three(n) || is_six(n) || is_nine(n)) { + _hexout_(n | 0x1100); + } else { + _hexout_(n | 0x2200); + } +} + +func or_test(n i32) { + if (n == 3 || n == 6 || n == 9) { + _hexout_(n | 0x100); + } else { + _hexout_(n | 0x200); + } +} +func and_test(n i32) { + if ((n & 1 == 1) && (n & 2 == 2) && (n < 8)) { + _hexout_(n | 0x300); + } else { + _hexout_(n | 0x400); + } +} + +func start() i32 { + var n i32 = 0; + while n < 10 { + or_test(n); + n = n + 1; + } + n = 0; + while n < 10 { + or_test2(n); + n = n + 1; + } + n = 0; + while n < 16 { + and_test(n); + n = n + 1; + } + return 0x42; +} + diff --git a/test/1040-structs.log b/test/1040-structs.log @@ -0,0 +1,13 @@ +D 0000002d +D 00000011 +D 0000007b +D 000001c8 +D 00000080 +D 000001cb +D 000000fb +D 00000393 +D 10203040 +D 50607080 +D 10203040 +D 50607080 +X 00000000 diff --git a/test/1040-structs.src b/test/1040-structs.src @@ -0,0 +1,50 @@ + +struct Point { + x i32, + y i32, +}; + +struct Line { + start Point, + end Point, +}; + +func add(a Point, b Point) { + a.x = a.x + b.x; + a.y = a.y + b.y; +} + +func print(p Point) { + _hexout_(p.x); + _hexout_(p.y); +} + +var p0 Point = { x: 45, y: 17 }; +var p1 Point = { x: 5, y: 3 }; + +var line Line = { + start: { x: 1, y: 2 }, + end: { x: 42, y: 17 } +}; + +// var p2 struct { x i32, y i32, } = { x: 7, y: 6 }; + +func start() i32 { + _hexout_(p0.x); + _hexout_(p0.y); + p0.x = 123; + p0.y = 456; + _hexout_(p0.x); + _hexout_(p0.y); + add(p1, p0); + print(p1); + add(p1, p0); + print(p1); + var z Point = p1; + z.x = 0x10203040; + z.y = 0x50607080; + print(z); + _hexout_(z.x); + _hexout_(z.y); + return 0; +} diff --git a/test/1041-arrays.log b/test/1041-arrays.log @@ -0,0 +1,18 @@ +D 00000001 +D 00000002 +D 00000004 +D 00000008 +D 00000010 +D 00000020 +D 00000040 +D 00000080 +D 000000ff +D 00000010 +D 00000020 +D 00000030 +D 00000040 +D 00000050 +D 00000060 +D 00000070 +D 00000080 +X 00000008 diff --git a/test/1041-arrays.src b/test/1041-arrays.src @@ -0,0 +1,26 @@ + +var numbers [8]byte = { 1, 2, 4, 8, 16, 32, 64, 128 }; + +var data [8]byte = { 0x10, 0x20, 0x30, 0x40, + 0x50, 0x60, 0x70, 0x80 }; + +func dump(x [8]byte) { + var n i32 = 0; + while (n < 8) { + _hexout_(x[n]); + n++; + } +} + +func start() i32 { + var n i32 = 0; + var m i32 = 0; + while (n < 8) { + _hexout_(numbers[n]); + m = m + numbers[n]; + n++; + } + _hexout_(m); + dump(data); + return n; +} diff --git a/test/1042-arrays2.log b/test/1042-arrays2.log @@ -0,0 +1,9 @@ +D 00000000 +D 00000010 +D 00000020 +D 00000030 +D 00000040 +D 00000050 +D 00000060 +D 00000070 +X 00000008 diff --git a/test/1042-arrays2.src b/test/1042-arrays2.src @@ -0,0 +1,16 @@ + +var data [8]byte; + +func start() i32 { + var n i32 = 0; + while n < 8 { + data[n] = n << 4; + n++; + } + n = 0; + while n < 8 { + _hexout_(data[n]); + n++; + } + return n; +} diff --git a/test/1050-enums.log b/test/1050-enums.log @@ -0,0 +1,10 @@ +D 00000000 +D 00000001 +D 00000002 +D 00000005 +D 00000006 +D 10000000 +D 10000001 +D 00000064 +D 00000002 +X 00000000 diff --git a/test/1050-enums.src b/test/1050-enums.src @@ -0,0 +1,20 @@ + +var i i32 = 3; + +enum { ZERO, ONE, TWO, }; + +enum { A = 5, B, C = 0x10000000, D, E = 50 + 50, F = ONE * TWO, }; + +func start() i32 { + _hexout_(ZERO); + _hexout_(ONE); + _hexout_(TWO); + _hexout_(A); + _hexout_(B); + _hexout_(C); + _hexout_(D); + _hexout_(E); + _hexout_(F); + return 0; +} + diff --git a/test/2000-err-decl-mismatch-type.src b/test/2000-err-decl-mismatch-type.src @@ -0,0 +1,6 @@ + +func hello(a i32, b i32) bool; + +func hello(a i32, b bool) bool { + return false; +} diff --git a/test/2001-err-decl-mismatch-return.src b/test/2001-err-decl-mismatch-return.src @@ -0,0 +1,5 @@ + +func hello(a i32, b i32) bool; + +func hello(a i32, b i32) { +} diff --git a/test/2002-err-decl-mismatch-count.src b/test/2002-err-decl-mismatch-count.src @@ -0,0 +1,6 @@ + +func hello(a i32, b i32) bool; + +func hello(a i32, b i32, c i32) bool { + return false; +} diff --git a/test/2003-err-decl-mismatch-array.src b/test/2003-err-decl-mismatch-array.src @@ -0,0 +1,8 @@ + +func a1a1a1(aaa [17]i32) { +} + +func bad() { + var z [10]i32; + a1a1a1(z); +} diff --git a/test/2010-err-array-init-too-large.src b/test/2010-err-array-init-too-large.src @@ -0,0 +1,2 @@ +var magic [4]i32 = { 3, 1, 3, 3, 7 }; + diff --git a/test/2011-err-array-oob-const.src b/test/2011-err-array-oob-const.src @@ -0,0 +1,6 @@ + +var blarg [256]byte; + +func bad() { + var n i32 = blarg[256]; +} diff --git a/test/2020-err-u32-overflow.src b/test/2020-err-u32-overflow.src @@ -0,0 +1,3 @@ +func start() i32 { + return 4294967296; +} diff --git a/test/2021-err-u32-hex-overflow.src b/test/2021-err-u32-hex-overflow.src @@ -0,0 +1,3 @@ +func start() i32 { + return 0x123456789; +} diff --git a/test/2022-err-u32-bin-overflow.src b/test/2022-err-u32-bin-overflow.src @@ -0,0 +1,3 @@ +func start() i32 { + return 0b100010001000100010001000100010001; +} diff --git a/test/2030-err-bad-break.src b/test/2030-err-bad-break.src @@ -0,0 +1,4 @@ + +func nogood() { + break; +} diff --git a/test/2031-err-bad-continue.src b/test/2031-err-bad-continue.src @@ -0,0 +1,4 @@ + +func fail() { + continue; +} diff --git a/tools/compile b/tools/compile @@ -0,0 +1,4 @@ +#!/bin/bash -e + +bin/compiler0 -o $2 $1 +gcc -Wall -I. -Iinc -Iout -o $2.bin $2.impl.c diff --git a/tools/runtest b/tools/runtest @@ -0,0 +1,53 @@ +#!/bin/bash + +## Copyright 2020, Brian Swetland <swetland@frotz.net> +## Licensed under the Apache License, Version 2.0. + +src="$1" +txt="$2" +base="${txt%.txt}" +bin="${txt%.txt}.bin" +lst="${txt%.txt}.lst" +log="${txt%.txt}.log" +msg="${txt%.txt}.msg" +gold="${src%.src}.log" + +#echo "RUNTEST: $src: compiling..." +if tools/compile "$src" "$base" 2> "$msg"; then + # success! + if [[ "$txt" == *"-err"* ]]; then + # but this was an error test, so... + echo "RUNTEST: $src: FAIL: compiler did not detect error" + echo "FAIL: $src" > "$txt" + else + #echo "RUNTEST: $src: running..." + if "$bin" > "$log"; then + if diff "$log" "$gold" >/dev/null ; then + echo "RUNTEST: $src: PASS" + echo "PASS: $src" > "$txt" + else + echo "RUNTEST: $src: FAIL: output differs from expected" + diff "$log" "$gold" | head + echo "FAIL: %src" > "$txt" + fi + else + echo "RUNTEST: $src: FAIL: emulator crashed" + echo "FAIL: %src" > "$txt" + fi + fi +else + if [[ $? > 127 ]]; then + echo "RUNTEST: $src: FAIL: compiler crashed" + cat "$msg" + # failure + elif [[ "$txt" == *"-err"* ]]; then + # but this was an error test, so... + echo "RUNTEST: $src: PASS" + echo "PASS: $src" > "$txt" + else + echo "RUNTEST: $src: FAIL: compiler error" + echo "FAIL: $src" > "$txt" + cat "$msg" + fi +fi +