summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rw-r--r--err.c2
-rw-r--r--infer.c16
-rw-r--r--main.c1
-rw-r--r--parse.c55
-rw-r--r--scope.c13
-rw-r--r--test.toc9
-rw-r--r--toc.c1
-rw-r--r--types.c130
-rw-r--r--types.h39
10 files changed, 178 insertions, 90 deletions
diff --git a/README.md b/README.md
index 9d588e1..be43683 100644
--- a/README.md
+++ b/README.md
@@ -28,7 +28,7 @@ See `docs` for more information (in progress).
`tests` has some test programs written in `toc`.
-To compile the compiler on a Unix-y system, just run `build.sh`. You can supply a compiler by running `CC=tcc build.sh`, or built it in release mode with `./build.sh release` (which will help speed up compiling large programs).
+To compile the compiler on a Unix-y system, just run `./build.sh release`. You can supply a compiler by running `CC=tcc ./build.sh release`, or built it in debug mode without the `release`.
On other systems, you can just compile main.c with a C compiler. toc uses several C99 and a couple of C11 features, so it might not work on all compilers. But it does compile on quite a few, including `clang`, `gcc`, and `tcc`. It can also be compiled as if it were C++, but it does break the standard in a few places\*. So, MSVC can also compile it. The *outputted* code should be C99-compliant.
diff --git a/err.c b/err.c
index 66ffd8f..f2b51aa 100644
--- a/err.c
+++ b/err.c
@@ -66,7 +66,7 @@ static void info_print_header_(Location where) {
#if ERR_EMACS
err_fprint("%s:%lu: " TEXT_INFO("info") ":\n", where.ctx->filename, (unsigned long)where.line);
#else
- err_fprint(TEXT_ERROR("info") " at line %lu of %s:\n", (unsigned long)where.line, where.ctx->filename);
+ err_fprint(TEXT_INFO("info") " at line %lu of %s:\n", (unsigned long)where.line, where.ctx->filename);
#endif
}
diff --git a/infer.c b/infer.c
new file mode 100644
index 0000000..ac32f81
--- /dev/null
+++ b/infer.c
@@ -0,0 +1,16 @@
+/* infers */
+static bool infer_expr(Typer *tr, Expression *e, Declaration *decls,
+ Expression *exprs) {
+ e->kind = EXPR_VAL;
+ Value *val = &e->val;
+ val->type = malloc(sizeof *val->type);
+ memset(val->type, 0, sizeof *val->type);
+ val->type->kind = TYPE_BUILTIN;
+ val->type->builtin = BUILTIN_I64;
+ val->type->flags = TYPE_IS_RESOLVED;
+ memset(&e->type, 0, sizeof e->type);
+ e->type.kind = TYPE_TYPE;
+ e->type.flags = TYPE_IS_RESOLVED;
+
+ return true;
+}
diff --git a/main.c b/main.c
index 8268ac7..7a5e3db 100644
--- a/main.c
+++ b/main.c
@@ -19,7 +19,6 @@
/*
TODO:
-check for duplicate params
inferred const params
packages
X ::= newtype(int); or something
diff --git a/parse.c b/parse.c
index 769f2eb..ef49dc9 100644
--- a/parse.c
+++ b/parse.c
@@ -8,6 +8,7 @@ static bool parse_stmt(Parser *p, Statement *s);
enum {
PARSE_DECL_ALLOW_CONST_WITH_NO_EXPR = 0x01,
PARSE_DECL_ALLOW_SEMI_CONST = 0x02,
+ PARSE_DECL_ALLOW_INFER
};
static bool parse_decl(Parser *p, Declaration *d, DeclEndKind ends_with, uint16_t flags);
static bool parse_decl_list(Parser *p, Declaration **decls, DeclEndKind decl_end);
@@ -826,7 +827,7 @@ static bool parse_decl_list(Parser *p, Declaration **decls, DeclEndKind decl_end
!token_is_kw(t->token - 1, KW_LBRACE)))) {
first = false;
Declaration *decl = parser_arr_add(p, decls);
- if (!parse_decl(p, decl, decl_end, PARSE_DECL_ALLOW_CONST_WITH_NO_EXPR | PARSE_DECL_ALLOW_SEMI_CONST)) {
+ if (!parse_decl(p, decl, decl_end, PARSE_DECL_ALLOW_CONST_WITH_NO_EXPR | PARSE_DECL_ALLOW_SEMI_CONST | PARSE_DECL_ALLOW_INFER)) {
ret = false;
/* skip to end of list */
while (t->token->kind != TOKEN_EOF && !ends_decl(t->token, decl_end))
@@ -882,6 +883,10 @@ static bool parse_fn_expr(Parser *p, FnExpr *f) {
err_print(d->where, "Named return values cannot be constant.");
return false;
}
+ if (d->flags & DECL_INFER) {
+ err_print(d->where, "Can't infer the value of a return declaration!");
+ return false;
+ }
}
--t->token; /* move back to { */
/* just set return type to void. the actual return type will be set by types.c:type_of_fn */
@@ -1790,27 +1795,37 @@ static bool parse_decl(Parser *p, Declaration *d, DeclEndKind ends_with, U16 fla
if (token_is_kw(t->token, KW_EQ)) {
++t->token;
- d->flags |= DECL_HAS_EXPR;
- uint16_t expr_flags = 0;
- if (ends_with == DECL_END_RPAREN_COMMA)
- expr_flags |= EXPR_CAN_END_WITH_COMMA;
- if (ends_with == DECL_END_LBRACE_COMMA)
- expr_flags |= EXPR_CAN_END_WITH_LBRACE;
- Token *end = expr_find_end(p, expr_flags);
- if (!end || !ends_decl(end, ends_with)) {
- t->token = end;
- tokr_err(t, "Expected %s at end of declaration.", end_str);
- goto ret_false;
- }
- if (!parse_expr(p, &d->expr, end)) {
- t->token = end; /* move to ; */
- goto ret_false;
- }
- if (ends_decl(t->token, ends_with)) {
+ if ((flags & PARSE_DECL_ALLOW_INFER) && ends_decl(t->token, ends_with)) {
+ /* inferred expression */
+ d->flags |= DECL_INFER;
+ if (!(d->flags & DECL_IS_CONST)) {
+ tokr_err(t, "Inferred parameters must be constant.");
+ goto ret_false;
+ }
++t->token;
} else {
- tokr_err(t, "Expected %s at end of declaration.", end_str);
- goto ret_false;
+ d->flags |= DECL_HAS_EXPR;
+ uint16_t expr_flags = 0;
+ if (ends_with == DECL_END_RPAREN_COMMA)
+ expr_flags |= EXPR_CAN_END_WITH_COMMA;
+ if (ends_with == DECL_END_LBRACE_COMMA)
+ expr_flags |= EXPR_CAN_END_WITH_LBRACE;
+ Token *end = expr_find_end(p, expr_flags);
+ if (!end || !ends_decl(end, ends_with)) {
+ t->token = end;
+ tokr_err(t, "Expected %s at end of declaration.", end_str);
+ goto ret_false;
+ }
+ if (!parse_expr(p, &d->expr, end)) {
+ t->token = end; /* move to ; */
+ goto ret_false;
+ }
+ if (ends_decl(t->token, ends_with)) {
+ ++t->token;
+ } else {
+ tokr_err(t, "Expected %s at end of declaration.", end_str);
+ goto ret_false;
+ }
}
} else if (ends_decl(t->token, ends_with)) {
++t->token;
diff --git a/scope.c b/scope.c
index 5416883..2fc6041 100644
--- a/scope.c
+++ b/scope.c
@@ -10,7 +10,7 @@ enum {
static void val_free(Value *v, Type *t);
-static bool add_ident_decls(Block *b, Declaration *d, U16 flags) {
+static bool DEBUG_UNDERSCORE(add_ident_decls)(SOURCE_LOCATION_PARAMS Block *b, Declaration *d, U16 flags) {
bool ret = true;
arr_foreach(d->idents, Identifier, ident) {
IdentDecl *decls = (*ident)->decls;
@@ -20,11 +20,20 @@ static bool add_ident_decls(Block *b, Declaration *d, U16 flags) {
if (prev->scope == b) {
err_print(d->where, "Re-declaration of identifier in the same block.");
info_print(prev->decl->where, "Previous declaration was here.");
+#ifdef TOC_DEBUG
+ info_print(d->where, "First declaration was done by %s:%d, second was done by %s:%d.", prev->src_file, prev->src_line, src_file, src_line);
+#endif
ret = false;
continue;
}
}
- ident_add_decl(*ident, d, b);
+ IdentDecl *idecl = ident_add_decl(*ident, d, b);
+#ifdef TOC_DEBUG
+ idecl->src_file = src_file;
+ idecl->src_line = src_line;
+#else
+ (void)idecl;
+#endif
}
return ret;
}
diff --git a/test.toc b/test.toc
index a24df5a..74980c0 100644
--- a/test.toc
+++ b/test.toc
@@ -1,8 +1,7 @@
-f ::= fn(y:f64=19.2, x :f64=3.4) f64 {
- x + y
+f ::= fn(t::=, x :t) t {
+ x + 1
};
main ::= fn() {
- f(y = 13, 23);
-};
-// \ No newline at end of file
+ f(13);
+}; \ No newline at end of file
diff --git a/toc.c b/toc.c
index e109c18..6d68950 100644
--- a/toc.c
+++ b/toc.c
@@ -34,6 +34,7 @@
#include "eval.c"
+#include "infer.c"
#include "types.c"
static bool cgen_decls_file(CGenerator *g, ParsedFile *f);
static bool typedefs_file(CGenerator *g, ParsedFile *f);
diff --git a/types.c b/types.c
index 27fd838..fa8c626 100644
--- a/types.c
+++ b/types.c
@@ -289,18 +289,11 @@ static bool type_of_fn(Typer *tr, FnExpr *f, Location where, Type *t, U16 flags)
}
++idx;
}
-
- if (param->flags & DECL_IS_CONST) {
- /* allow constant declarations to be used in other parameters, e.g. fn(x @ int, y := x) */
- arr_foreach(param->idents, Identifier, ident) {
- ident_add_decl(*ident, param, &f->body);
- }
- }
}
if (f->ret_decls && !generic && f->ret_type.kind == TYPE_VOID /* haven't found return type yet */) {
/* find return type */
-
+
arr_foreach(f->ret_decls, Declaration, d) {
if (!types_decl(tr, d)) {
success = false;
@@ -337,15 +330,6 @@ static bool type_of_fn(Typer *tr, FnExpr *f, Location where, Type *t, U16 flags)
if (entered_fn) {
fn_exit(f);
tr->fn = prev_fn;
-
- /* remove declarations from parameters we've already dealt with */
- for (size_t i = 0; i < param_idx; ++i) {
- Declaration *p = &f->params[i];
- if (p->flags & DECL_IS_CONST)
- arr_foreach(p->idents, Identifier, ident)
- arr_remove_last(&(*ident)->decls);
- }
-
}
return success;
}
@@ -1103,7 +1087,7 @@ static bool types_expr(Typer *tr, Expression *e) {
return false;
}
param_idx = index;
- } else if ((param->flags & DECL_HAS_EXPR) && param < last_param_without_default_value) {
+ } else if ((param->flags & (DECL_HAS_EXPR | DECL_INFER)) && param < last_param_without_default_value) {
/* this param must be named; so this is referring to a later parameter */
--arg;
} else {
@@ -1152,7 +1136,7 @@ static bool types_expr(Typer *tr, Expression *e) {
assert(fn_decl); /* we can only miss an arg if we're using named/optional args */
arr_foreach(fn_decl->params, Declaration, param) {
- bool is_required = !(param->flags & DECL_HAS_EXPR);
+ bool is_required = !(param->flags & (DECL_HAS_EXPR|DECL_INFER));
int ident_idx = 0;
arr_foreach(param->idents, Identifier, ident) {
@@ -1223,31 +1207,73 @@ static bool types_expr(Typer *tr, Expression *e) {
/* eval compile time arguments */
for (i = 0; i < nparams; ++i) {
bool should_be_evald = arg_is_const(&arg_exprs[i], fn_type->constness[i]);
-
- if (should_be_evald && params_set[i]) {
- Expression *expr = &arg_exprs[i];
- Value *arg_val = typer_arr_add(tr, &table_index.tuple);
- if (!eval_expr(tr->evalr, expr, arg_val)) {
- if (tr->evalr->enabled) {
- info_print(arg_exprs[i].where, "(error occured while trying to evaluate compile-time argument, argument #%lu)", 1+(unsigned long)i);
+ if (should_be_evald) {
+ if (params_set[i]) {
+ Expression *expr = &arg_exprs[i];
+ Value *arg_val = typer_arr_add(tr, &table_index.tuple);
+ if (!eval_expr(tr->evalr, expr, arg_val)) {
+ if (tr->evalr->enabled) {
+ info_print(arg_exprs[i].where, "(error occured while trying to evaluate compile-time argument, argument #%lu)", 1+(unsigned long)i);
+ }
+ return false;
}
- return false;
- }
- Type *type = &expr->type;
- *(Type *)typer_arr_add(tr, &table_index_type.tuple) = *type;
+ Type *type = &expr->type;
+ *(Type *)typer_arr_add(tr, &table_index_type.tuple) = *type;
- arg_exprs[i].kind = EXPR_VAL;
- arg_exprs[i].flags = EXPR_FOUND_TYPE;
- copy_val(tr->allocr, &arg_exprs[i].val, arg_val, type);
- arg_exprs[i].val = *arg_val;
- copy_val(tr->allocr, &param_decl->val, arg_val, type);
- param_decl->flags |= DECL_FOUND_VAL;
-
- } else if (should_be_evald) {
- /* leave gap for this */
- typer_arr_add(tr, &table_index.tuple);
- typer_arr_add(tr, &table_index_type.tuple);
+ arg_exprs[i].kind = EXPR_VAL;
+ arg_exprs[i].flags = EXPR_FOUND_TYPE;
+ copy_val(tr->allocr, &arg_exprs[i].val, arg_val, type);
+ arg_exprs[i].val = *arg_val;
+ copy_val(tr->allocr, &param_decl->val, arg_val, type);
+ param_decl->flags |= DECL_FOUND_VAL;
+ if (!(param_decl->flags & DECL_ANNOTATES_TYPE)) {
+ param_decl->type = *type;
+ }
+ } else if (param_decl->flags & DECL_INFER) {
+ arg_exprs[i].kind = EXPR_VAL;
+ arg_exprs[i].flags = EXPR_FOUND_TYPE;
+ {
+ for (Declaration *p = fn->params; p < param_decl; ++p) {
+ if (p->flags & DECL_FOUND_VAL)
+ if (!add_ident_decls(&fn->body, p, SCOPE_CHECK_REDECL)) {
+ for (Declaration *q = fn->params; q < p; ++q)
+ if (q->flags & DECL_FOUND_VAL)
+ remove_ident_decls(&fn->body, q);
+ return false;
+ }
+ }
+ }
+ bool success = infer_expr(tr, &arg_exprs[i], fn->params, arg_exprs);
+ for (Declaration *p = fn->params; p < param_decl; ++p) {
+ if (p->flags & DECL_FOUND_VAL)
+ remove_ident_decls(&fn->body, p);
+ }
+ if (!success) return false;
+ copy_val(tr->allocr, &param_decl->val, &arg_exprs[i].val, &arg_exprs[i].type);
+
+
+ if (param_decl->flags & DECL_ANNOTATES_TYPE) {
+ if (!type_resolve(tr, &param_decl->type, param_decl->where))
+ return false;
+ Type *expected = &arg_exprs[i].type;
+ Type *got = &param_decl->type;
+ if (!type_eq(expected, got)) {
+ char *estr = type_to_str(expected);
+ char *gstr = type_to_str(got);
+ err_print(param_decl->where, "Expected annotated type %s for this argument, but it was annotated as %s.", estr, gstr);
+ free(estr); free(gstr);
+ return false;
+ }
+ }
+ param_decl->type = arg_exprs[i].type;
+ param_decl->flags |= DECL_FOUND_VAL|DECL_FOUND_TYPE;
+ params_set[i] = true;
+ } else {
+ /* leave gap for this (default argument) */
+ typer_arr_add(tr, &table_index.tuple);
+ typer_arr_add(tr, &table_index_type.tuple);
+ }
}
if (fn_type->constness[i] == CONSTNESS_SEMI) {
@@ -1284,9 +1310,11 @@ static bool types_expr(Typer *tr, Expression *e) {
Value *arg_val = &table_index.tuple[i+1];
copy_val(tr->allocr, arg_val, &param->expr.val, &param->expr.type);
table_index_type.tuple[i+1] = param->expr.type;
+ params_set[i] = true;
}
++i;
}
+
}
ret_type = f->type.fn.types;
@@ -1296,6 +1324,7 @@ static bool types_expr(Typer *tr, Expression *e) {
/* check types of arguments */
for (size_t p = 0; p < nparams; ++p) {
Expression *arg = &arg_exprs[p];
+
Type *expected = &param_types[p];
Type *got = &arg->type;
if (!type_eq(expected, got)) {
@@ -1305,7 +1334,6 @@ static bool types_expr(Typer *tr, Expression *e) {
return false;
}
}
-
if (fn_type->constness) {
bool instance_already_exists;
@@ -1322,7 +1350,7 @@ static bool types_expr(Typer *tr, Expression *e) {
/* if anything happens, make sure we let the user know that this happened while generating a fn */
ErrCtx *err_ctx = e->where.ctx;
*(Location *)typer_arr_add(tr, &err_ctx->instance_stack) = e->where;
- bool success = types_fn(tr, &c->instance->fn, &f->type, e->where, c->instance);
+ bool success = types_fn(tr, &c->instance->fn, &f->type, e->where, c->instance);
arr_remove_last(&err_ctx->instance_stack);
if (!success) return false;
arr_clear(&table_index_type.tuple);
@@ -1371,7 +1399,7 @@ static bool types_expr(Typer *tr, Expression *e) {
case EXPR_UNARY_OP: {
Expression *of = e->unary.of;
Type *of_type = &of->type;
- if (!types_expr(tr, e->unary.of)) return false;
+ if (!types_expr(tr, e->unary.of)) return false;
if (of_type->kind == TYPE_UNKNOWN) {
return true;
}
@@ -1624,7 +1652,7 @@ static bool types_expr(Typer *tr, Expression *e) {
}
break;
default: {
- char *s = type_to_str(lhs_type);
+ char *s = type_to_str(lhs_type);
err_print(e->where, "Trying to take index of non-array type %s.", s);
free(s);
return false;
@@ -1744,7 +1772,7 @@ static bool types_expr(Typer *tr, Expression *e) {
assert(0);
return false;
}
- t->flags |= TYPE_IS_RESOLVED;
+ t->flags |= TYPE_IS_RESOLVED;
return true;
}
@@ -1804,6 +1832,11 @@ static bool types_block(Typer *tr, Block *b) {
static bool types_decl(Typer *tr, Declaration *d) {
bool success = true;
if (d->flags & DECL_FOUND_TYPE) return true;
+ if (d->flags & DECL_INFER) {
+ d->type.kind = TYPE_UNKNOWN;
+ d->type.flags = 0;
+ return true;
+ }
Declaration **dptr = typer_arr_add(tr, &tr->in_decls);
*dptr = d;
if (d->flags & DECL_ANNOTATES_TYPE) {
@@ -1832,7 +1865,7 @@ static bool types_decl(Typer *tr, Declaration *d) {
goto ret;
}
d->type = d->expr.type;
- d->type.flags &= (DeclFlags)~(DeclFlags)TYPE_IS_FLEXIBLE; /* x := 5; => x is not flexible */
+ d->type.flags &= (TypeFlags)~(TypeFlags)TYPE_IS_FLEXIBLE; /* x := 5; => x is not flexible */
}
if ((d->flags & DECL_IS_CONST) || (tr->block == NULL && tr->fn == NULL)) {
if (!(d->flags & DECL_FOUND_VAL)) {
@@ -1847,7 +1880,6 @@ static bool types_decl(Typer *tr, Declaration *d) {
}
}
-
for (size_t i = 0; i < arr_len(d->idents); ++i) {
Type *t = d->type.kind == TYPE_TUPLE ? &d->type.tuple[i] : &d->type;
if (t->kind == TYPE_TYPE) {
@@ -1893,7 +1925,7 @@ static bool types_decl(Typer *tr, Declaration *d) {
/* use unknown type if we didn't get the type */
d->type.flags = TYPE_IS_RESOLVED;
d->type.was_expr = NULL;
- d->type.kind = TYPE_UNKNOWN;
+ d->type.kind = TYPE_UNKNOWN;
tr->evalr->enabled = false; /* disable evaluator completely so that it doesn't accidentally try to access this declaration */
}
arr_remove_last(&tr->in_decls);
diff --git a/types.h b/types.h
index dd00a94..9937b88 100644
--- a/types.h
+++ b/types.h
@@ -52,7 +52,18 @@ typedef double F64;
typedef U32 IdentID; /* identifier ID for cgen (anonymous variables) */
-typedef uint32_t LineNo;
+typedef U32 LineNo;
+
+/* for keeping track of whence something came */
+#ifdef TOC_DEBUG
+#define SOURCE_LOCATION char *src_file; int src_line;
+#define SOURCE_LOCATION_PARAMS char *src_file, int src_line,
+#define DEBUG_UNDERSCORE(x) x##_
+#else
+#define SOURCE_LOCATION
+#define SOURCE_LOCATION_PARAMS
+#define DEBUG_UNDERSCORE(x) x
+#endif
typedef struct Location {
LineNo line;
@@ -141,6 +152,7 @@ typedef struct IdentDecl {
};
struct Block *scope; /* NULL for file scope */
Value val;
+ SOURCE_LOCATION
IdentDeclKind kind;
U16 flags;
} IdentDecl;
@@ -361,10 +373,11 @@ enum {
TYPE_IS_FLEXIBLE = 0x01,
TYPE_IS_RESOLVED = 0x02,
};
+typedef U16 TypeFlags;
typedef struct Type {
Location where;
TypeKind kind;
- uint16_t flags;
+ TypeFlags flags;
struct Expression *was_expr; /* if non-NULL, indicates that this type used to be an expression (TYPE_EXPR) */
union {
BuiltinType builtin;
@@ -624,17 +637,18 @@ typedef struct Argument {
} Argument;
enum {
- DECL_ANNOTATES_TYPE = 0x01,
- DECL_IS_CONST = 0x02,
- DECL_SEMI_CONST = 0x04,
- DECL_HAS_EXPR = 0x08,
- DECL_FOUND_TYPE = 0x10,
- DECL_ERRORED_ABOUT_SELF_REFERENCE = 0x20, /* has there been an error about this decl referencing itself? */
- DECL_FOUND_VAL = 0x40,
- DECL_IS_PARAM = 0x80
+ DECL_ANNOTATES_TYPE = 0x0001,
+ DECL_IS_CONST = 0x0002,
+ DECL_SEMI_CONST = 0x0004,
+ DECL_HAS_EXPR = 0x0008,
+ DECL_FOUND_TYPE = 0x0010,
+ DECL_ERRORED_ABOUT_SELF_REFERENCE = 0x0020, /* has there been an error about this decl referencing itself? */
+ DECL_FOUND_VAL = 0x0040,
+ DECL_IS_PARAM = 0x0080,
+ DECL_INFER = 0x0100, /* infer the value (e.g. fn(t::Type=, x:t)) */
};
-typedef U16 DeclFlags;
+typedef U32 DeclFlags;
/* OPTIM: Instead of using dynamic arrays, do two passes. */
typedef struct Declaration {
@@ -723,3 +737,6 @@ typedef struct CGenerator {
Identifiers *idents;
} CGenerator;
+#ifdef TOC_DEBUG
+#define add_ident_decls(b, d, flags) add_ident_decls_(__FILE__, __LINE__, b, d, flags)
+#endif