diff options
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | err.c | 2 | ||||
-rw-r--r-- | infer.c | 16 | ||||
-rw-r--r-- | main.c | 1 | ||||
-rw-r--r-- | parse.c | 55 | ||||
-rw-r--r-- | scope.c | 13 | ||||
-rw-r--r-- | test.toc | 9 | ||||
-rw-r--r-- | toc.c | 1 | ||||
-rw-r--r-- | types.c | 130 | ||||
-rw-r--r-- | types.h | 39 |
10 files changed, 178 insertions, 90 deletions
@@ -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. @@ -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 } @@ -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; +} @@ -19,7 +19,6 @@ /* TODO: -check for duplicate params inferred const params packages X ::= newtype(int); or something @@ -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; @@ -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; } @@ -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 @@ -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); @@ -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, ¶m_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, ¶m_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, ¶m_decl->val, &arg_exprs[i].val, &arg_exprs[i].type); + + + if (param_decl->flags & DECL_ANNOTATES_TYPE) { + if (!type_resolve(tr, ¶m_decl->type, param_decl->where)) + return false; + Type *expected = &arg_exprs[i].type; + Type *got = ¶m_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, ¶m->expr.val, ¶m->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 = ¶m_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); @@ -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 |