diff options
-rw-r--r-- | README.html | 33 | ||||
-rw-r--r-- | README.md | 33 | ||||
-rw-r--r-- | cgen.c | 1 | ||||
-rw-r--r-- | copy.c | 21 | ||||
-rw-r--r-- | docs/00.md | 50 | ||||
-rw-r--r-- | eval.c | 25 | ||||
-rw-r--r-- | instance_table.c | 5 | ||||
-rw-r--r-- | main.c | 1 | ||||
-rw-r--r-- | parse.c | 9 | ||||
-rw-r--r-- | toc.c | 15 | ||||
-rw-r--r-- | types.c | 14 | ||||
-rw-r--r-- | types.h | 13 |
12 files changed, 190 insertions, 30 deletions
diff --git a/README.html b/README.html new file mode 100644 index 0000000..d3476a5 --- /dev/null +++ b/README.html @@ -0,0 +1,33 @@ +<h2>toc</h2> + +<p><code>toc</code> is a language which compiles to C.</p> + +<hr /> + +<h3>About</h3> + +<p><code>toc</code> is currently in development. <strong>It is not a stable language, +and there are almost definitely bugs right now.</strong> +I would recommend against using it for anything big or important. +Many parts of it may change in the future.</p> + +<p><code>toc</code> improves on C's syntax (and semantics) in many ways, +To declare <code>x</code> as an integer and set it to 5, +you can do:</p> + +<p><code> +x := 5; // Declare x and set x to 5 (infer type) <br /> +x : int = 5; // Explicitly make the type int. <br /> +x : int; x = 5; // Declare x as an integer, then set it to 5. +</code></p> + +<p><code>toc</code> is statically typed and has many of C's features, but +it is just as fast in theory.</p> + +<p>See <code>docs</code> for more information.</p> + +<hr /> + +<h3>Help</h3> + +<p>If you find a bug in <code>toc</code>, please <a href="https://github.com/pommicket/toc/issues">report an issue</a> on GitHub.</p> 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 @@ -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); @@ -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: +``` +<name> : [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. @@ -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; @@ -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 @@ -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); @@ -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) { @@ -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); @@ -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; |