From 76ff5d914def4984d5b599177032ded71632a5ec Mon Sep 17 00:00:00 2001 From: Leo Tenenbaum Date: Thu, 28 Nov 2019 15:47:10 -0500 Subject: started dealing with calling "type functions" --- README.html | 33 +++++++++++++++++++++++++++++++++ README.md | 33 +++++++++++++++++++++++++++++++++ cgen.c | 1 + copy.c | 21 ++++++++++++++++----- docs/00.md | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ eval.c | 25 ++++++++++++++++--------- instance_table.c | 5 ++++- main.c | 1 + parse.c | 9 +++++++++ toc.c | 15 +++++++++++---- types.c | 14 +++++++++----- types.h | 13 +++++++------ 12 files changed, 190 insertions(+), 30 deletions(-) create mode 100644 README.html create mode 100644 README.md create mode 100644 docs/00.md diff --git a/README.html b/README.html new file mode 100644 index 0000000..d3476a5 --- /dev/null +++ b/README.html @@ -0,0 +1,33 @@ +

toc

+ +

toc is a language which compiles to C.

+ +
+ +

About

+ +

toc is currently in development. It is not a stable language, +and there are almost definitely bugs right now. +I would recommend against using it for anything big or important. +Many parts of it may change in the future.

+ +

toc improves on C's syntax (and semantics) in many ways, +To declare x as an integer and set it to 5, +you can do:

+ +

+x := 5; // Declare x and set x to 5 (infer type)
+x : int = 5; // Explicitly make the type int.
+x : int; x = 5; // Declare x as an integer, then set it to 5. +

+ +

toc is statically typed and has many of C's features, but +it is just as fast in theory.

+ +

See docs for more information.

+ +
+ +

Help

+ +

If you find a bug in toc, please report an issue on GitHub.

diff --git a/README.md b/README.md new file mode 100644 index 0000000..d946dce --- /dev/null +++ b/README.md @@ -0,0 +1,33 @@ +## toc + +`toc` is a language which compiles to C. + +--- + +### About + +`toc` is currently in development. **It is not a stable language, +and there are almost definitely bugs right now.** +I would recommend against using it for anything big or important. +Many parts of it may change in the future. + +`toc` improves on C's syntax (and semantics) in many ways, +To declare `x` as an integer and set it to 5, +you can do: + +``` +x := 5; // Declare x and set x to 5 (infer type) +x : int = 5; // Explicitly make the type int. +x : int; x = 5; // Declare x as an integer, then set it to 5. +``` + +`toc` is statically typed and has many of C's features, but +it is nearly as fast in theory. + +See `docs` for more information. + +--- + +### Help + +If you find a bug in `toc`, please [report an issue](https://github.com/pommicket/toc/issues) on GitHub. \ No newline at end of file diff --git a/cgen.c b/cgen.c index 1b959d2..44222b3 100644 --- a/cgen.c +++ b/cgen.c @@ -231,6 +231,7 @@ static bool cgen_uses_ptr(Type *t) { case TYPE_TYPE: return false; case TYPE_USER: + case TYPE_CALL: return cgen_uses_ptr(type_user_underlying(t)); } assert(0); diff --git a/copy.c b/copy.c index d6654f9..3062115 100644 --- a/copy.c +++ b/copy.c @@ -36,6 +36,7 @@ static void copy_val(Allocator *a, Value *out, Value *in, Type *t) { memcpy(out->struc, in->struc, bytes); } break; case TYPE_USER: + case TYPE_CALL: copy_val(a, out, in, type_user_underlying(t)); break; case TYPE_TYPE: @@ -53,8 +54,10 @@ static void copy_val(Allocator *a, Value *out, Value *in, Type *t) { } } +/* does not work on resolved types */ static void copy_type(Copier *c, Type *out, Type *in) { - /* needs to handle resolved and unresolved types properly */ + assert(!(in->flags & TYPE_IS_RESOLVED)); + *out = *in; switch (in->kind) { case TYPE_BUILTIN: @@ -63,6 +66,14 @@ static void copy_type(Copier *c, Type *out, Type *in) { case TYPE_UNKNOWN: case TYPE_USER: break; + case TYPE_CALL: { + copy_type(c, out->call.calling = allocr_malloc(c->allocr, sizeof *out->call.calling), + in->call.calling); + out->call.args = NULL; + arr_foreach(in->call.args, Expression, arg) { + copy_expr(c, arr_add(&out->call.args), arg); + } + } break; case TYPE_FN: { size_t ntypes = arr_len(in->fn.types); out->fn.types = NULL; @@ -80,10 +91,10 @@ static void copy_type(Copier *c, Type *out, Type *in) { } } break; case TYPE_ARR: - if (!(in->flags & TYPE_IS_RESOLVED)) { - out->arr.n_expr = allocr_malloc(c->allocr, sizeof *out->arr.n_expr); - copy_expr(c, out->arr.n_expr, in->arr.n_expr); - } + /* if (!(in->flags & TYPE_IS_RESOLVED)) { */ + out->arr.n_expr = allocr_malloc(c->allocr, sizeof *out->arr.n_expr); + copy_expr(c, out->arr.n_expr, in->arr.n_expr); + /* } */ out->arr.of = allocr_malloc(c->allocr, sizeof *out->arr.of); copy_type(c, out->arr.of, in->arr.of); break; diff --git a/docs/00.md b/docs/00.md new file mode 100644 index 0000000..e4e8d41 --- /dev/null +++ b/docs/00.md @@ -0,0 +1,50 @@ +## Declarations in toc + +Declarations have the following syntax: +``` + : [type] [= expression]; +``` + +The square brackets (`[]`) indicate something optional. + +All of the following statements +declare an new variable `x` which is an integer, and has a value of 0: +``` +x : int; +x : int = 0; +x := 0; +``` +Note that in the first of those statements, although no expression +is specified, it defaults to 0. This is not true in C, +and there will eventually probably be an option to +leave `x` uninitialized. + +If you wanted x to be a floating-point number, you could use: +``` +x : float; +x : float = 0; +x := 0.0; +``` + +Note that `0` can be used as both a `float` and an `int`eger, but +when no type is specified, it defaults to an `int`, whereas `0.0` +defaults to a `float`. + +Here are all of toc's builtin types and their ranges of values: + +- `int` - A 64-bit signed integer (always), -9223372036854775808 to 9223372036854775807 +- `i8` - An 8-bit signed integer, -128 to 128 +- `i16` - 16-bit signed integer, -32768 to 32767 +- `i32` - 32-bit signed integer, -2147483648 to 2147483647 +- `i64` - 64-bit signed integer (same as `int`, but more explicit about the size), -9223372036854775808 to 9223372036854775807 +- `u8` - An 8-bit unsigned integer, 0 to 255 +- `u16` - 16-bit unsigned integer, 0 to 65535 +- `u32` - 32-bit unsigned integer, 0 to 4294967295 +- `u64` - 64-bit unsigned integer, 0 to 18446744073709551615 +- `float` - A 32-bit floating-point number, +- `f32` +- `f64` +- `bool` +- `char` + +At the moment, it is not technically guaranteed that `f32`/`float` is actually 32-bit and that `f64` is actually 64-bit; they are platform dependent. Perhaps someday there will be a version of toc which does not compile to C, where that could be guaranteed. diff --git a/eval.c b/eval.c index 2ac4170..e06dd9e 100644 --- a/eval.c +++ b/eval.c @@ -71,6 +71,7 @@ static size_t compiler_alignof(Type *t) { case TYPE_TYPE: return sizeof(Type *); case TYPE_USER: + case TYPE_CALL: return compiler_alignof(type_user_underlying(t)); case TYPE_STRUCT: { /* assume the align of a struct is (at most) the greatest align out of its children's */ @@ -129,6 +130,7 @@ static size_t compiler_sizeof(Type *t) { case TYPE_TYPE: return sizeof(Type *); case TYPE_USER: + case TYPE_CALL: return compiler_sizeof(type_user_underlying(t)); case TYPE_STRUCT: { eval_struct_find_offsets(t); @@ -169,6 +171,7 @@ static bool val_truthiness(Value *v, Type *t) { case TYPE_FN: return v->fn != NULL; case TYPE_ARR: return t->arr.n > 0; case TYPE_SLICE: return v->slice.n > 0; + case TYPE_CALL: case TYPE_USER: case TYPE_TYPE: case TYPE_TUPLE: @@ -246,6 +249,7 @@ static void *val_get_ptr(Value *v, Type *t) { case TYPE_TYPE: return v; case TYPE_USER: + case TYPE_CALL: return val_get_ptr(v, type_user_underlying(t)); case TYPE_ARR: return v->arr; @@ -320,6 +324,7 @@ static void fprint_val_ptr(FILE *f, void *p, Type *t) { fprint_type(f, *(Type **)p); break; case TYPE_USER: + case TYPE_CALL: fprint_val_ptr(f, p, type_user_underlying(t)); break; case TYPE_STRUCT: @@ -365,6 +370,7 @@ static void *val_ptr_to_free(Value *v, Type *t) { case TYPE_STRUCT: return v->struc; case TYPE_USER: + case TYPE_CALL: return val_ptr_to_free(v, type_user_underlying(t)); } assert(0); return NULL; @@ -452,7 +458,8 @@ static void val_cast(Value *vin, Type *from, Value *vout, Type *to) { vout->boolv = val_truthiness(vin, from); return; } - if (from->kind == TYPE_USER || to->kind == TYPE_USER) { + if (from->kind == TYPE_USER || to->kind == TYPE_USER + || from->kind == TYPE_CALL || to->kind == TYPE_CALL) { *vout = *vin; return; } @@ -462,6 +469,7 @@ static void val_cast(Value *vin, Type *from, Value *vout, Type *to) { case TYPE_UNKNOWN: case TYPE_TUPLE: case TYPE_USER: + case TYPE_CALL: case TYPE_TYPE: case TYPE_STRUCT: assert(0); break; @@ -484,6 +492,7 @@ static void val_cast(Value *vin, Type *from, Value *vout, Type *to) { } break; case TYPE_USER: + case TYPE_CALL: case TYPE_STRUCT: case TYPE_SLICE: case TYPE_VOID: @@ -506,8 +515,7 @@ static void val_cast(Value *vin, Type *from, Value *vout, Type *to) { vout->fn = vin->fn; break; case TYPE_USER: - *vout = *vin; - break; + case TYPE_CALL: case TYPE_SLICE: case TYPE_UNKNOWN: case TYPE_TUPLE: @@ -542,8 +550,7 @@ static void val_cast(Value *vin, Type *from, Value *vout, Type *to) { vout->fn = vin->ptr; break; case TYPE_USER: - *vout = *vin; - break; + case TYPE_CALL: case TYPE_SLICE: case TYPE_UNKNOWN: case TYPE_TUPLE: @@ -564,8 +571,7 @@ static void val_cast(Value *vin, Type *from, Value *vout, Type *to) { vout->arr = vin->arr; break; case TYPE_USER: - *vout = *vin; - break; + case TYPE_CALL: case TYPE_SLICE: case TYPE_FN: case TYPE_UNKNOWN: @@ -589,8 +595,7 @@ static void val_cast(Value *vin, Type *from, Value *vout, Type *to) { vout->slice = vin->slice; break; case TYPE_USER: - *vout = *vin; - break; + case TYPE_CALL: case TYPE_FN: case TYPE_UNKNOWN: case TYPE_TUPLE: @@ -635,6 +640,7 @@ static void eval_deref(Value *v, void *ptr, Type *type) { v->type = *(Type **)ptr; break; case TYPE_USER: + case TYPE_CALL: eval_deref(v, ptr, type_user_underlying(type)); break; case TYPE_VOID: @@ -674,6 +680,7 @@ static void eval_deref_set(void *set, Value *to, Type *type) { *(Type **)set = to->type; break; case TYPE_USER: + case TYPE_CALL: eval_deref_set(set, to, type_user_underlying(type)); break; case TYPE_VOID: diff --git a/instance_table.c b/instance_table.c index 948e4e5..63879ce 100644 --- a/instance_table.c +++ b/instance_table.c @@ -97,7 +97,9 @@ static U64 val_ptr_hash(void *v, Type *t) { } case TYPE_PTR: return (U64)*(void **)v; case TYPE_TYPE: return (U64)*(Type **)v; - case TYPE_USER: return val_ptr_hash(v, type_inner(t)); + case TYPE_USER: + case TYPE_CALL: + return val_ptr_hash(v, type_inner(t)); case TYPE_ARR: { U32 x = 1; U64 hash = 0; @@ -162,6 +164,7 @@ static bool val_ptr_eq(void *u, void *v, Type *t) { case TYPE_FN: return *(FnExpr **)u == *(FnExpr **)v; case TYPE_USER: + case TYPE_CALL: return val_ptr_eq(u, v, type_inner(t)); case TYPE_PTR: return *(void **)u == *(void **)v; diff --git a/main.c b/main.c index 87f5037..1866e5f 100644 --- a/main.c +++ b/main.c @@ -3,6 +3,7 @@ TODO: struct parameters - struct type parameters test ArrInt @= Arr(int); + packages don't allow while {3; 5} (once break is added) any odd number of "s for a string diff --git a/parse.c b/parse.c index 1452dcc..df47ee9 100644 --- a/parse.c +++ b/parse.c @@ -256,6 +256,15 @@ static size_t type_to_str_(Type *t, char *buffer, size_t bufsize) { free(ident_str); return ret; } + case TYPE_CALL: { + size_t written = 0; + written += type_to_str_(t->call.calling, buffer + written, bufsize - written); + written += str_copy(buffer + written, bufsize - written, "("); + /* TODO: show values if resolved */ + written += str_copy(buffer + written, bufsize - written, "..."); + written += str_copy(buffer + written, bufsize - written, ")"); + return written; + } } assert(0); diff --git a/toc.c b/toc.c index 05697a3..cd931ed 100644 --- a/toc.c +++ b/toc.c @@ -13,12 +13,19 @@ #include "types.h" +/* can be used on TYPE_USERs and TYPE_CALLs */ static Type *type_user_underlying(Type *t) { - assert(t->kind == TYPE_USER); assert(t->flags & TYPE_IS_RESOLVED); - Declaration *d = t->user.decl; - assert(d->flags & DECL_FOUND_VAL); - return (d->type.kind == TYPE_TUPLE ? d->val.tuple[t->user.index] : d->val).type; + switch (t->kind) { + case TYPE_USER: { + Declaration *d = t->user.decl; + assert(d->flags & DECL_FOUND_VAL); + return (d->type.kind == TYPE_TUPLE ? d->val.tuple[t->user.index] : d->val).type; + } + case TYPE_CALL: + return &t->call.instance->type; + default: assert(0); + } } static Type *type_inner(Type *t) { diff --git a/types.c b/types.c index d4d0414..f5b8e5e 100644 --- a/types.c +++ b/types.c @@ -28,9 +28,9 @@ static bool type_eq(Type *a, Type *b) { assert(a->flags & TYPE_IS_RESOLVED); assert(b->flags & TYPE_IS_RESOLVED); - if (a->kind == TYPE_USER && a->user.is_alias) + if ((a->kind == TYPE_USER || a->kind == TYPE_CALL) && a->user.is_alias) return type_eq(type_user_underlying(a), b); - if (b->kind == TYPE_USER && b->user.is_alias) + if ((b->kind == TYPE_USER || a->kind == TYPE_CALL) && b->user.is_alias) return type_eq(a, type_user_underlying(b)); @@ -57,6 +57,8 @@ static bool type_eq(Type *a, Type *b) { case TYPE_TYPE: return true; case TYPE_USER: return a->user.decl == b->user.decl && a->user.index == b->user.index; + case TYPE_CALL: + return a->call.instance == b->call.instance; case TYPE_BUILTIN: return a->builtin == b->builtin; case TYPE_STRUCT: return false; @@ -541,6 +543,7 @@ static bool type_can_be_truthy(Type *t) { case TYPE_ARR: case TYPE_TYPE: case TYPE_USER: + case TYPE_CALL: case TYPE_STRUCT: return false; case TYPE_FN: @@ -563,13 +566,13 @@ typedef enum { static Status type_cast_status(Type *from, Type *to) { if (to->kind == TYPE_UNKNOWN) return STATUS_NONE; - if (from->kind == TYPE_USER) { + if (from->kind == TYPE_USER || from->kind == TYPE_CALL) { if (from->user.is_alias) { return type_cast_status(type_user_underlying(from), to); } return type_eq(type_user_underlying(from), to) ? STATUS_NONE : STATUS_ERR; } - if (to->kind == TYPE_USER) { + if (to->kind == TYPE_USER || to->kind == TYPE_CALL) { if (to->user.is_alias) { return type_cast_status(from, type_user_underlying(to)); } @@ -604,7 +607,7 @@ static Status type_cast_status(Type *from, Type *to) { case TYPE_STRUCT: case TYPE_ARR: case TYPE_VOID: - case TYPE_USER: /* handled above */ + case TYPE_USER: case TYPE_CALL: /* handled above */ return STATUS_ERR; } break; @@ -642,6 +645,7 @@ static Status type_cast_status(Type *from, Type *to) { return STATUS_NONE; return STATUS_ERR; case TYPE_USER: + case TYPE_CALL: break; } assert(0); diff --git a/types.h b/types.h index ab41519..599ca67 100644 --- a/types.h +++ b/types.h @@ -291,7 +291,8 @@ typedef enum { TYPE_SLICE, TYPE_TYPE, TYPE_USER, /* user-defined type */ - TYPE_STRUCT + TYPE_STRUCT, + TYPE_CALL /* "calling" a type function, e.g. Arr(int) */ } TypeKind; typedef enum { @@ -367,9 +368,9 @@ typedef struct Type { } struc; struct { - Type *calling; - Expression *arguments; - struct Instance *instance; /* instance of struct, NULL if this is not an instance. set during resolution. */ + struct Type *calling; + struct Expression *args; + struct Instance *instance; /* instance of struct. set during type resolution. */ } call; /* "calling" a function returning a type */ }; } Type; @@ -529,10 +530,10 @@ typedef struct FnExpr { } FnExpr; /* an expression such as fn(x: int) int { 2 * x } */ typedef struct Instance { - Value val; + Value val; /* key into hash table */ union { FnExpr fn; /* the typed function */ - Type struc; /* the structure, resolved */ + Type type; /* the type, resolved */ }; struct { U64 id; -- cgit v1.2.3