diff options
-rw-r--r-- | cgen.c | 5 | ||||
-rw-r--r-- | eval.c | 8 | ||||
-rw-r--r-- | main.c | 6 | ||||
-rw-r--r-- | std/base.toc | 2 | ||||
-rw-r--r-- | std/io.toc | 67 | ||||
-rw-r--r-- | test.toc | 13 | ||||
-rw-r--r-- | types.c | 30 | ||||
-rw-r--r-- | types.h | 5 |
8 files changed, 109 insertions, 27 deletions
@@ -121,7 +121,7 @@ static inline void cgen_char(CGenerator *g, char c) { if (isprint(c) && c != '"') cgen_write(g, "%c", c); else - cgen_write(g, "\\x%02x", c); + cgen_write(g, "\\%03o", c); /* can't use hex escape sequences, because they can be more than 2 characters "\xbafoo" is '\xbaf', 'o', 'o' */ } /* should this declaration be a direct function declaration C? (as opposed to using a function pointer or not being a function) */ static bool cgen_fn_is_direct(CGenerator *g, Declaration *d) { @@ -1250,6 +1250,9 @@ static void cgen_expr(CGenerator *g, Expression *e) { case BUILTIN_PLATFORM: cgen_write(g, "platform__"); break; + case BUILTIN_DEBUG: /* substituted for its value */ + assert(0); + break; } break; case EXPR_CAST: { @@ -6,7 +6,7 @@ static Status eval_block(Evaluator *ev, Block *b); static Status eval_address_of(Evaluator *ev, Expression *e, void **ptr); -static Value get_builtin_val(BuiltinVal val); +static Value get_builtin_val(GlobalCtx *gctx, BuiltinVal val); static void evalr_create(Evaluator *ev, Typer *tr, Allocator *allocr) { ev->returning = NULL; @@ -605,7 +605,7 @@ static Status eval_val_ptr_at_index(Location where, Value *arr, U64 i, Type *arr switch (arr_type->kind) { case TYPE_ARR: { U64 arr_sz = (U64)arr_type->arr.n; - if (i >= arr_sz) { + if (i > arr_sz) { /* this is INTENTIONALLY > and not >=, because it's okay to have a pointer to one past the end of an array */ err_print(where, "Array out of bounds (index = %lu, array size = %lu)\n", (unsigned long)i, (unsigned long)arr_sz); return false; } @@ -615,7 +615,7 @@ static Status eval_val_ptr_at_index(Location where, Value *arr, U64 i, Type *arr case TYPE_SLICE: { Slice slice = *(Slice *)arr_ptr; U64 slice_sz = (U64)slice.len; - if (i >= slice_sz) { + if (i > slice_sz) { /* see above for why it's not >= */ err_print(where, "Slice out of bounds (index = %lu, slice size = %lu)\n", (unsigned long)i, (unsigned long)slice_sz); return false; } @@ -1252,7 +1252,7 @@ static Status eval_expr(Evaluator *ev, Expression *e, Value *v) { err_print(e->where, "Cannot run C code at compile time."); return false; case EXPR_BUILTIN: - *v = get_builtin_val(e->builtin.which.val); + *v = get_builtin_val(ev->typer->gctx, e->builtin.which.val); break; case EXPR_CALL: { FnExpr *fn; @@ -124,6 +124,7 @@ int main(int argc, char **argv) { const char *out_filename = "out.c"; bool verbose = false; + bool debug_build = true; ErrCtx err_ctx = {0}; err_ctx.enabled = true; @@ -156,6 +157,10 @@ int main(int argc, char **argv) { } else if (streq(arg, "-v") || streq(arg, "-verbose")) { printf("Verbose mode enabled\n"); verbose = true; + } else if (streq(arg, "-d") || streq(arg, "-debug")) { + debug_build = true; + } else if (streq(arg, "-r") || streq(arg, "-release")) { + debug_build = false; } else { if (arg[0] == '-') { fprintf(stderr, "Unrecognized option: %s.\n", argv[i]); @@ -215,6 +220,7 @@ int main(int argc, char **argv) { str_hash_table_create(&global_ctx.included_files, sizeof(IncludedFile), &main_allocr); global_ctx.main_file = &file; global_ctx.err_ctx = &err_ctx; + global_ctx.debug_build = debug_build; Parser p; parser_create(&p, &globals, &t, &main_allocr, &global_ctx); diff --git a/std/base.toc b/std/base.toc index 950f817..a8fc990 100644 --- a/std/base.toc +++ b/std/base.toc @@ -6,6 +6,8 @@ PLATFORM_FREEBSD ::= 4; PLATFORM_OPENBSD ::= 5; PLATFORM_MISC_UNIX ::= 6; +DEBUG ::= #builtin("debug"); + PLATFORM ::= #builtin("platform"); #if PLATFORM == PLATFORM_LINUX { libc ::= "libc.so.6"; @@ -18,6 +18,7 @@ FILE_ERR_MISC ::= 1; the raw file interface: raw_stdout - standard output raw_stderr - standard error +raw_stdin - standard input raw_file_read - read from a raw file - unlike raw_file_write, it's okay if the number of bytes read doesn't match the number of bytes requested. raw_file_write - write to a raw file raw_file_open_write - open a raw file for writing @@ -65,6 +66,9 @@ raw_file_close - close a raw file raw_stderr ::= fn() RawFile { return 2; } + raw_stdin ::= fn() RawFile { + return 0; + } raw_file_open_write ::= fn(name: []char) f: RawFile, err := FILE_ERR_OK { cstr := str_to_cstr(name); defer mem.dels(cstr); @@ -109,6 +113,9 @@ raw_file_close - close a raw file raw_stderr ::= fn() RawFile { return #builtin("stderr"); } + raw_stdin ::= fn() RawFile { + return #builtin("stdin"); + } raw_file_open_write ::= fn(name: []char) f: RawFile, err := FILE_ERR_OK { cstr := base.str_to_cstr(name); defer mem.dels(cstr); @@ -134,20 +141,30 @@ raw_file_close - close a raw file } } +// file modes +FileMode ::= u8; +// @TODO: enum +MODE_READ :: FileMode = 1; +MODE_WRITE :: FileMode = 2; + + +IO_DEBUG ::= base.DEBUG; + // @TODO: flush for read files -- discard buffer // @TODO: error flag // @TODO: locking? -// @TODO: keep track of mode the file was opened in, #if DEBUG, check mode before read/writing File ::= struct { BUFSZ ::= 4096; raw : RawFile; - buffer_used, buffer_len : int; // ranges from 0 to FILE_BUFSZ-1 + buffer_used, buffer_len : int; // range from 0 to FILE_BUFSZ-1 + mode : FileMode; 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, mode : FileMode, f: &File) { f.raw = raw; + f.mode = mode; } std_in, std_out, std_err : File; @@ -157,13 +174,19 @@ fopen_write ::= fn(name: []char) f: &File, error: FileError { raw, error = raw_file_open_write(name); if !error { f = mem.new(File); - raw_file_to_file(raw, f); + raw_file_to_file(raw, MODE_WRITE, f); } } flush ::= fn(use f: &File) err: FileError { - err = raw_file_write(raw, &buffer[0], buffer_used); - buffer_used = 0; + if mode == MODE_READ { + // discard buffer + buffer_used = 0; + buffer_len = 0; + } else { + err = raw_file_write(raw, &buffer[0], buffer_used); + buffer_used = 0; + } } fclose ::= fn(f: &File) err: FileError { @@ -173,6 +196,12 @@ fclose ::= fn(f: &File) err: FileError { } fwrites ::= fn(use f: &File, s : []char) FileError { + #if IO_DEBUG { + if mode != MODE_WRITE { + base.error("Writing to file which wasn't opened for writing."); + } + } + if f.nobuffer { return raw_file_write(raw, &s[0], s.len); } @@ -209,6 +238,11 @@ puts ::= fn(s: []char) FileError { // read into out, set its length appropriately fread ::= fn(use f: &File, out: &[]char) FileError { + #if IO_DEBUG { + if mode != MODE_READ { + base.error("Reading from file which wasn't opened for reading."); + } + } to_read := out.len; buffer_left := buffer_len - buffer_used; if to_read <= buffer_left { @@ -247,19 +281,28 @@ fread ::= fn(use f: &File, out: &[]char) FileError { } -// read a line of standard input. does not include newline -gets ::= fn(out: &[]char) { - fread(&std_in, out); - if out[out.len-1] == '\n' { +// read a line from in (default: standard input). does not include newline +gets ::= fn(out: &[]char, in := &std_in) err : FileError { + err = fread(in, out); + if out.len && out[out.len-1] == '\n' { out.len -= 1; } } +// read a line of file in (default: standard input), at most max_len (default: 256) characters +// a read error might happen, but this function just ignores it +read_line ::= fn(max_len := 256, in := &std_in) []char { + ret := mem.news(char, max_len); + gets(&ret, in); + return ret; +} + io_init ::= fn() { - raw_file_to_file(raw_stdout(), &std_out); + raw_file_to_file(raw_stdout(), MODE_WRITE, &std_out); std_out.nobuffer = true; - raw_file_to_file(raw_stderr(), &std_err); + raw_file_to_file(raw_stderr(), MODE_WRITE, &std_err); std_err.nobuffer = true; + raw_file_to_file(raw_stdin(), MODE_READ, &std_in); } #init io_init(); @@ -1,11 +1,16 @@ +#include "std/mem.toc", mem; #include "std/io.toc", io; #include "std/base.toc", base; main ::= fn() { - s: [64]char; - buf := s[:]; + file, _ := io.fopen_write("test.txt"); io.writes("What is your name? "); - io.gets(&buf); + b : [64]char; + name := b[:]; + err := io.gets(&name, file); + if err { + base.error("File error"); + } io.writes("Hello, "); - io.puts(buf); + io.puts(name); } @@ -1453,7 +1453,7 @@ static Status parameterized_struct_arg_order(StructDef *struc, Argument *args, I return true; } -static Value get_builtin_val(BuiltinVal val) { +static Value get_builtin_val(GlobalCtx *gctx, BuiltinVal val) { Value v; switch (val) { case BUILTIN_STDOUT: @@ -1471,6 +1471,9 @@ static Value get_builtin_val(BuiltinVal val) { case BUILTIN_PLATFORM: v.i64 = platform__; break; + case BUILTIN_DEBUG: + v.boolv = gctx->debug_build; + break; } return v; } @@ -1496,6 +1499,10 @@ static void get_builtin_val_type(Allocator *a, BuiltinVal val, Type *t) { t->kind = TYPE_BUILTIN; t->builtin = BUILTIN_I64; break; + case BUILTIN_DEBUG: + t->kind = TYPE_BUILTIN; + t->builtin = BUILTIN_BOOL; + break; } } @@ -2199,8 +2206,11 @@ static Status types_expr(Typer *tr, Expression *e) { if (counter < 0) break; ++decl; } - err_print(decl->where, "Could not infer value of declaration."); - info_print(e->where, "While processing this call"); + + if (!tr->gctx->err_ctx->have_errored) { /* something could've gone wrong elsewhere causing a strange error message here. for example, one of the arguments could be TYPE_UNKNOWN, because its declaration had a typing error */ + err_print(decl->where, "Could not infer value of declaration."); + info_print(e->where, "While processing this call"); + } return false; } ++type; @@ -2428,9 +2438,19 @@ static Status types_expr(Typer *tr, Expression *e) { err_print(e->where, "Unrecognized builtin value: %s.", builtin_name); return false; } - e->builtin.which.val = (BuiltinVal)which; - get_builtin_val_type(tr->allocr, e->builtin.which.val, t); + get_builtin_val_type(tr->allocr, which, t); assert(t->flags & TYPE_IS_RESOLVED); + switch (which) { + /* immediately evaluate (things which do not differ between compile time & run time) */ + case BUILTIN_DEBUG: + e->kind = EXPR_VAL; + e->val = get_builtin_val(tr->gctx, which); + break; + /* stuff that's different between compile & run time */ + default: + e->builtin.which.val = (BuiltinVal)which; + break; + } } break; case EXPR_UNARY_OP: { Expression *of = e->unary.of; @@ -716,12 +716,13 @@ typedef enum { BUILTIN_STDERR, BUILTIN_STDIN, BUILTIN_COMPILING, + BUILTIN_DEBUG, BUILTIN_PLATFORM #define BUILTIN_VAL_COUNT (BUILTIN_PLATFORM+1) } BuiltinVal; const char *const builtin_val_names[BUILTIN_VAL_COUNT] = - {"stdout", "stderr", "stdin", "compiling", "platform"}; + {"stdout", "stderr", "stdin", "compiling", "debug", "platform"}; typedef struct Namespace { Block body; @@ -1005,6 +1006,7 @@ typedef struct { File *main_file; /* this is the file which the compiler is invoked on. needed for checking for circular includes. */ StatementWithCtx *static_ifs; /* all the #ifs */ ErrCtx *err_ctx; + bool debug_build; } GlobalCtx; typedef struct Parser { @@ -1085,4 +1087,5 @@ typedef struct CGenerator { Identifier main_ident; Identifiers *globals; char **nms_prefixes; /* dynamic (null-terminated) array of namespace prefixes */ + GlobalCtx *gctx; } CGenerator; |