summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cgen.c5
-rw-r--r--eval.c8
-rw-r--r--main.c6
-rw-r--r--std/base.toc2
-rw-r--r--std/io.toc67
-rw-r--r--test.toc13
-rw-r--r--types.c30
-rw-r--r--types.h5
8 files changed, 109 insertions, 27 deletions
diff --git a/cgen.c b/cgen.c
index 2fd5cbf..b27f2fa 100644
--- a/cgen.c
+++ b/cgen.c
@@ -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: {
diff --git a/eval.c b/eval.c
index 69b2785..4292f65 100644
--- a/eval.c
+++ b/eval.c
@@ -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;
diff --git a/main.c b/main.c
index 1fb6f34..1863a1c 100644
--- a/main.c
+++ b/main.c
@@ -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";
diff --git a/std/io.toc b/std/io.toc
index 03a82e4..ebdff8d 100644
--- a/std/io.toc
+++ b/std/io.toc
@@ -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();
diff --git a/test.toc b/test.toc
index 6f17a38..d672753 100644
--- a/test.toc
+++ b/test.toc
@@ -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);
}
diff --git a/types.c b/types.c
index f181f30..fc9bd23 100644
--- a/types.c
+++ b/types.c
@@ -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;
diff --git a/types.h b/types.h
index 11efddb..bfbec3c 100644
--- a/types.h
+++ b/types.h
@@ -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;