From 8dd389a5f7db1c8ef9e60d900104fef99f25645f Mon Sep 17 00:00:00 2001 From: Leo Tenenbaum Date: Fri, 3 Jul 2020 18:49:28 -0400 Subject: io is working but slow --- .gitignore | 1 + cgen.c | 7 ++-- eval.c | 19 ++++++---- foreign64.c | 20 +++++++++++ main.c | 1 + parse.c | 1 + std/base.toc | 13 +++++++ std/io.toc | 115 ++++++++++++++++++++++++++++++++++++++++++++--------------- std/mem.toc | 15 ++++++-- test.toc | 23 ++++++++++-- toc.c | 1 + tokenizer.c | 4 +++ types.c | 20 +++++------ 13 files changed, 188 insertions(+), 52 deletions(-) diff --git a/.gitignore b/.gitignore index c5f345e..450a731 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ std/*.o *.obj *.exe .vs +test.txt diff --git a/cgen.c b/cgen.c index c4427e1..2fd5cbf 100644 --- a/cgen.c +++ b/cgen.c @@ -277,11 +277,12 @@ static void cgen_type_post(CGenerator *g, Type *t) { } static void cgen_ctype(CGenerator *g, CType *c) { - if (c->kind & CTYPE_UNSIGNED) { - c->kind &= (CTypeKind)~(CTypeKind)CTYPE_UNSIGNED; + CTypeKind k = c->kind; + if (k & CTYPE_UNSIGNED) { + k &= (CTypeKind)~(CTypeKind)CTYPE_UNSIGNED; cgen_write(g, "unsigned "); } - switch (c->kind) { + switch (k) { case CTYPE_CHAR: cgen_write(g, "char"); break; diff --git a/eval.c b/eval.c index e4b5313..69b2785 100644 --- a/eval.c +++ b/eval.c @@ -1035,7 +1035,8 @@ static Status eval_ident(Evaluator *ev, Identifier ident, Value *v, Location whe if (!success) return false; assert(d->type.flags & TYPE_IS_RESOLVED); #else - err_print(where, "Use of identifier at compile time before it is declared. This isn't allowed. Sorry."); + if (!where.file->ctx->have_errored) + err_print(where, "Use of identifier at compile time before it is declared. This isn't allowed. Sorry."); return false; #endif } @@ -1338,7 +1339,16 @@ static Status eval_expr(Evaluator *ev, Expression *e, Value *v) { return false; } if (fn->ret_decls) { + size_t nret_decls = 0; + arr_foreach(fn->ret_decls, Declaration, d) { + nret_decls += arr_len(d->idents); + } + Value *tuple = NULL; + if (nret_decls > 1) + tuple = err_malloc(nret_decls * sizeof *tuple); + size_t tuple_idx = 0; + arr_foreach(fn->ret_decls, Declaration, d) { int i = 0; arr_foreach(d->idents, Identifier, ident) { @@ -1349,16 +1359,13 @@ static Status eval_expr(Evaluator *ev, Expression *e, Value *v) { expr.ident = *ident; if (!eval_expr(ev, &expr, &this_one)) return false; - Value *element = arr_add_ptr(tuple); + Value *element = tuple ? &tuple[tuple_idx++] : v; Type *type = decl_type_at_index(d, i); copy_val(NULL, element, this_one, type); ++i; } } - if (arr_len(tuple) == 1) { - *v = tuple[0]; - arr_clear(tuple); - } else { + if (tuple) { v->tuple = tuple; } } diff --git a/foreign64.c b/foreign64.c index 0a0fc40..5aaf574 100644 --- a/foreign64.c +++ b/foreign64.c @@ -96,6 +96,14 @@ static Status val_to_word(Value v, Type *t, Location where, U64 *w) { static Status foreign_call(ForeignFnManager *ffmgr, FnExpr *fn, Type *ret_type, Type *arg_types, size_t arg_types_stride, Value *args, size_t nargs, Location call_where, Value *ret) { possibly_static_assert(sizeof(double) == 8); /* if either of these assertions fails, you'll need to use libffcall */ possibly_static_assert(sizeof(float) == 4); + +#if 0 +#define FOREIGN_DEBUGGING 1 +#endif +#if FOREIGN_DEBUGGING + printf("Foreign call: %s(", fn->foreign.name); +#endif + FnPtr fn_ptr = foreign_get_fn_ptr(ffmgr, fn, call_where); if (!fn_ptr) return false; /* @OPTIM: use alloca/_malloca if available */ @@ -104,6 +112,10 @@ static Status foreign_call(ForeignFnManager *ffmgr, FnExpr *fn, Type *ret_type, U64 *word = words; char *type = (char *)arg_types; for (size_t i = 0; i < nargs; ++i) { + #if FOREIGN_DEBUGGING + if (i) printf(", "); + fprint_val(stdout, args[i], (Type *)type); + #endif if (!val_to_word(args[i], (Type *)type, call_where, word)) return false; is_float[i] = type_is_float((Type *)type); @@ -181,6 +193,14 @@ static Status foreign_call(ForeignFnManager *ffmgr, FnExpr *fn, Type *ret_type, ret->f64 = foreign_calld(fn_ptr, words, (I64)nargs, is_float); break; } +#if FOREIGN_DEBUGGING + printf(") => "); + fprint_val(stdout, *ret, ret_type); +#if 1 + printf(" (errno: %d)", errno); +#endif + printf("\n"); +#endif free(words); free(is_float); return true; diff --git a/main.c b/main.c index fbd6029..1fb6f34 100644 --- a/main.c +++ b/main.c @@ -9,6 +9,7 @@ /* @TODO: if we do #include "foo.toc", bar; and foo.toc fails, bar should be declared as TYPE_UNKNOWN (right now it's undeclared) +fix #foreign not at global scope - right now the cgen'd definition doesn't use the proper type improve type_to_str: Foo ::= struct(t::Type) {} type_to_str(Foo(int)) diff --git a/parse.c b/parse.c index 53df60f..b723db0 100644 --- a/parse.c +++ b/parse.c @@ -2007,6 +2007,7 @@ static inline bool ends_decl(Token *t, U16 flags) { static Status parse_decl(Parser *p, Declaration *d, U16 flags) { Tokenizer *t = p->tokr; d->where = parser_mk_loc(p); + parser_set_end_to_token(p, &d->where, t->token+1); /* set temporary end in case this fails and we need to know the location of this declaration */ d->idents = NULL; d->flags = 0; d->val_stack = NULL; diff --git a/std/base.toc b/std/base.toc index 54d64d6..950f817 100644 --- a/std/base.toc +++ b/std/base.toc @@ -23,4 +23,17 @@ PLATFORM ::= #builtin("platform"); PLATFORM_IS_UNIX :: bool = PLATFORM == PLATFORM_LINUX || PLATFORM == PLATFORM_OSX || PLATFORM == PLATFORM_FREEBSD || PLATFORM == PLATFORM_OPENBSD || PLATFORM == PLATFORM_MISC_UNIX; +fwrite ::= #foreign("fwrite", libc) fn(#C &"const void", #C size_t, #C size_t, &void) #C size_t; +stderr_write ::= fn(s: []char) { + fwrite(&s[0], 1, s.len as #C size_t, #builtin("stderr")); +} + +exit ::= #foreign("exit", libc) fn(#C int); +error ::= fn(s: []char) { + stderr_write("Fatal error: "); + stderr_write(s); + // @TODO: backtrace + stderr_write("\nExiting.\n"); + exit(1); +} diff --git a/std/io.toc b/std/io.toc index acba673..932cf45 100644 --- a/std/io.toc +++ b/std/io.toc @@ -1,16 +1,36 @@ + #include "std/base.toc", base; #include "std/mem.toc", mem; str_to_cstr ::= fn(s: []char) []char { ret := mem.news(char, s.len+1); mem.mem_copy(&ret[0], &s[0], s.len); - ret + return ret; } +// @TODO(eventually): enum +FileError ::= int; +FILE_ERR_OK ::= 0; +FILE_ERR_MISC ::= 1; +// @TODO: more of these + +/* +the raw file interface: +raw_stdout - standard output +raw_stderr - standard error +raw_file_write - write to a raw file +raw_file_open_write - open a raw file for writing +raw_file_close - flush and close a raw file +*/ + + #if base.PLATFORM_IS_UNIX { - // @TODO: use syscall instead + // @TODO: use syscall instead (it'll allow other functions called write) write ::= #foreign("write", base.libc) fn(#C int, #C &"const void", #C size_t) #C long; - open ::= #foreign("open", base.libc) fn (#C &"const char", #C int) #C int; + open ::= #foreign("open", base.libc) fn (#C &"const char", #C int, #C unsigned) #C int; + close ::= #foreign("close", base.libc) fn (#C int) #C int; + + DEFAULT_MODE ::= 0o644; O_RDONLY ::= 0x00; O_WRONLY ::= 0x01; @@ -26,20 +46,27 @@ str_to_cstr ::= fn(s: []char) []char { size -= bytes_written; buf += bytes_written; } - true + return true; } raw_stdout ::= fn() RawFile { - 1 + return 1; } raw_stderr ::= fn() RawFile { - 2 + return 2; } - file_open_write ::= fn(name: []char) f: RawFile, success: bool { + raw_file_open_write ::= fn(name: []char) f: RawFile, err := FILE_ERR_OK { cstr := str_to_cstr(name); defer mem.dels(cstr); // @TODO: switch to bitwise or when that exists - f = open(&cstr[0], (O_WRONLY + O_CREAT) as #C int) as RawFile; - success = f != -1; + f = open(&cstr[0], (O_WRONLY + O_CREAT) as #C int, DEFAULT_MODE as #C unsigned) as RawFile; + if f == -1 { + err = FILE_ERR_MISC; // @TODO + } + } + raw_file_close ::= fn(f: RawFile) err := FILE_ERR_OK { + if close(f as #C int) == -1 { + err = FILE_ERR_MISC; // @TODO + } } } else { // @TODO: on windows, use WriteFile @@ -47,65 +74,97 @@ str_to_cstr ::= fn(s: []char) []char { RawFile ::= &void; raw_file_write ::= fn(file: RawFile, buf: &void, size: int) bool { bytes_written := fwrite(buf, 1, size, file) as int; - bytes_written == size + return bytes_written == size; } raw_stdout ::= fn() RawFile { - #builtin("stdout") + return #builtin("stdout"); } raw_stderr ::= fn() RawFile { - #builtin("stdout") + return #builtin("stderr"); } - file_open_write ::= fn(name: []char) f: RawFile, success: bool { + raw_file_open_write ::= fn(name: []char) f: RawFile, err := FILE_ERR_OK { cstr := base.str_to_cstr(name); defer mem.dels(cstr); mode := "w\0"; f = fopen(&cstr[0], &mode[0]); - success = f != null; + if f == null { + err = FILE_ERR_MISC; // @TODO + } + } + raw_file_close ::= fn(f: RawFile) err := FILE_ERR_OK { + if fclose(f) != 0 { + err = FILE_ERR_MISC; // @TODO + } } } - File ::= struct { BUFSZ ::= 4096; raw : RawFile; - written : int; // ranges from 0 to FILE_BUFSZ-1 + buffer_used : int; // ranges from 0 to FILE_BUFSZ-1 nobuffer : bool; // if true, flush after every write buffer : [BUFSZ]char; } -raw_file_to_file ::= fn(raw : RawFile) f: File { +raw_file_to_file ::= fn(raw : RawFile, f: &File) { f.raw = raw; } std_out, std_err : File; -file_flush ::= fn(use f: &File) { - raw_file_write(raw, &buffer[0], written); - written = 0; +fopen_write ::= fn(name: []char) f: &File, error: FileError { + raw : RawFile; + raw, error = raw_file_open_write(name); + if !error { + f = mem.new(File); + raw_file_to_file(raw, f); + } +} + +flush ::= fn(use f: &File) { + raw_file_write(raw, &buffer[0], buffer_used); + buffer_used = 0; +} + +fclose ::= fn(f: &File) err: FileError { + err = raw_file_close(f.raw); + mem.del(f); } -file_writes ::= fn(use f: &File, s : []char) { +fwrites ::= fn(use f: &File, s : []char) { if f.nobuffer { raw_file_write(raw, &s[0], s.len); return; } - if written + s.len > BUFSZ { - file_flush(f); + if buffer_used + s.len > BUFSZ { + flush(f); if s.len > BUFSZ { raw_file_write(raw, &s[0], s.len); } else { mem.mem_copy(&buffer[0], &s[0], s.len); - written = s.len; + buffer_used = s.len; } } else { - mem.mem_copy(&buffer[written], &s[0], s.len); - written += s.len; + mem.mem_copy(&buffer[buffer_used], &s[0], s.len); + buffer_used += s.len; } } +fputs ::= fn(f: &File, s: []char) { + fwrites(f, s); + fwrites(f, "\n"); +} + +puts ::= fn(s: []char) { + fputs(&std_out, s); +} + + io_init ::= fn() { - std_out = raw_file_to_file(raw_stdout()); + raw_file_to_file(raw_stdout(), &std_out); std_out.nobuffer = true; - std_err = raw_file_to_file(raw_stderr()); + raw_file_to_file(raw_stderr(), &std_err); std_err.nobuffer = true; } + +#init io_init(); diff --git a/std/mem.toc b/std/mem.toc index 9ea9b12..b6af0fe 100644 --- a/std/mem.toc +++ b/std/mem.toc @@ -1,6 +1,6 @@ #include "std/base.toc", base; -// TODO: check for failed calloc +// @TODO: check for failed calloc calloc ::= #foreign("calloc", base.libc) fn(#C size_t, #C size_t) #C &"void"; free ::= #foreign("free", base.libc) fn(#C &"void"); @@ -15,7 +15,6 @@ news ::= fn(t :: Type, n : int) []t { return s; } -// TODO(eventually): use type information to make this just one function del ::= fn(t::=, x: &t) { free(x); } @@ -23,3 +22,15 @@ del ::= fn(t::=, x: &t) { dels ::= fn(t::=, x: []t) { free(x.data); } + + +// @OPTIM @OPTIM @OPTIM +mem_copy ::= fn(out: &void, in: &void, n : int) { + out_u8 : &u8 = out; + in_u8 : &u8 = in; + for i := 0..n-1 { + *out_u8 = *in_u8; + out_u8 += 1; + in_u8 += 1; + } +} diff --git a/test.toc b/test.toc index d79f526..8523940 100644 --- a/test.toc +++ b/test.toc @@ -1,5 +1,22 @@ -f(); +#include "std/io.toc", io; +#include "std/base.toc", base; -f ::= fn( ) int { - return 3; +main ::= fn() { + file, err := io.fopen_write("test.txt"); + if err { + base.error("Couldn't open file!"); + } + io.fputs(file, "This file has some stuff in it."); + io.puts("Hello!"); + io.puts("yes"); + io.fputs(file, "here is more stuff for the file."); + for i := 1..1000 { + io.fwrites(file, "Here's a line in the file"); + for j := 1..i { + io.fwrites(file, "!"); + } + io.fwrites(file, "\n"); + } + io.fclose(file); } +main(); diff --git a/toc.c b/toc.c index 2afbc1c..c63dc36 100644 --- a/toc.c +++ b/toc.c @@ -15,6 +15,7 @@ #include #include #include +#include #ifndef COMPILE_TIME_FOREIGN_FN_SUPPORT #define COMPILE_TIME_FOREIGN_FN_SUPPORT 1 diff --git a/tokenizer.c b/tokenizer.c index 6b4cc1b..3f83e6f 100644 --- a/tokenizer.c +++ b/tokenizer.c @@ -375,6 +375,10 @@ static Status tokenize_file(Tokenizer *t, File *file) { base = 16; tokr_nextchar(t); break; + case 'o': + base = 8; + tokr_nextchar(t); + break; default: /* it's 0/0.something etc. */ break; diff --git a/types.c b/types.c index f103f5d..f50bebd 100644 --- a/types.c +++ b/types.c @@ -768,7 +768,6 @@ static Status type_of_fn(Typer *tr, FnExpr *f, Type *t, U16 flags) { /* doesn't do any translation on ident or check if it's declared or anything, so make sure it's in the right scope */ static Status type_of_ident(Typer *tr, Location where, Identifier i, Type *t) { -top:; Declaration *d = i->decl; assert(d); if (!(d->flags & DECL_IS_CONST)) { @@ -838,6 +837,7 @@ top:; free(s); return false; } + #if 0 Block *decl_block = i->idents->scope; if (block_is_at_top_level(decl_block)) { /* @@ -855,12 +855,12 @@ top:; tr->block = prev_block; goto top; } else { - char *s = ident_to_str(i); - err_print(where, "Use of %s before its declaration.", s); - info_print(d->where, "%s will be declared here.", s); - free(s); - return false; - } + #endif + char *s = ident_to_str(i); + err_print(where, "Use of %s before its declaration.", s); + info_print(d->where, "%s will be declared here.", s); + free(s); + return false; } } return true; @@ -2856,7 +2856,7 @@ static Status types_expr(Typer *tr, Expression *e) { Identifier i = rhs->ident = ident_get_with_len(&nms->body.idents, str.str, str.len); if (!i) { char *s = cstr(str.str, str.len); - err_print(e->where, "\"%s\" is not a member of this namespace.", s); + err_print(e->where, "'%s' is not a member of this namespace.", s); free(s); return false; } @@ -3194,7 +3194,7 @@ static Status types_decl(Typer *tr, Declaration *d) { d->expr.fn->flags |= FN_EXPR_EXPORT; } - if (typer_is_at_top_level(tr)) { + if (tr->block == NULL || tr->block->kind == BLOCK_NMS) { DeclWithCtx dctx = {d, tr->nms, tr->block}; typer_arr_add(tr, tr->all_globals, dctx); } @@ -3948,7 +3948,7 @@ top: } success: s->flags |= STMT_TYPED; - if (tr->block == NULL) { + if (tr->block == NULL || tr->block->kind == BLOCK_NMS) { /* evaluate statements at global scope */ switch (s->kind) { case STMT_DECL: -- cgit v1.2.3