From 19eafbc01a492e8f1df25cef52678ed8f76d3e63 Mon Sep 17 00:00:00 2001 From: Leo Tenenbaum Date: Tue, 25 Feb 2020 20:35:46 -0500 Subject: fixed bugs with new arg order system also now struct parameters are "officially" part of the language --- .gitignore | 2 + README.html | 103 ------------------------------------------------ README.md | 3 +- docs/00.html | 61 ---------------------------- docs/01.html | 18 --------- infer.c | 19 +++++++-- tests/arr/arr.toc | 11 +++++- tests/arr2/arr2.toc | 10 ++++- tests/bf/bf.toc | 5 ++- tests/params/params.toc | 5 ++- tests/test.sh | 2 + toc.c | 7 +++- types.c | 28 ++++++------- 13 files changed, 64 insertions(+), 210 deletions(-) delete mode 100644 README.html delete mode 100644 docs/00.html delete mode 100644 docs/01.html diff --git a/.gitignore b/.gitignore index 19c015b..2935f6d 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,5 @@ compatibility std/*.c std/*.o *.so +README.html +docs/*.html diff --git a/README.html b/README.html deleted file mode 100644 index e3ea45d..0000000 --- a/README.html +++ /dev/null @@ -1,103 +0,0 @@ -

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 (in progress).

- -

tests has some test programs written in toc.

- -

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 build it in debug mode without the release. To disable compile time foreign function support (which you will need to do if you don't have ffcall/dl), prefix this with COMPILE_TIME_FOREIGN_FN_SUPPORT=no.

- -

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++, so, MSVC and g++ can also compile it (it does rely on implicit casting of void * though). The outputted code should be C99-compliant.

- -

Why it compiles to C

- -

toc compiles to C. Here are some reasons why:

- - - -
- -

toc Compiler Source Code

- -

Most of the source code for the toc compiler is licensed under the GNU General Public License, version 3, and the rest (some small general utilities) is in the public domain. Each source file begins with a comment explaining its license.

- -

See LICENSE for the GNU General Public License.

- -

toc is written in C, for speed and portability. It has no dependencies, other than the C runtime library. If you want to be able to call external C functions at compile time, however, you will need libffcall and libdl (so this is only currently supported on Unix-y systems).

- -

Build system

- -

toc is set up as a unity build, meaning that there is only one translation unit. So, main.c #includes toc.c, which #includes all of toc's files.

- -
Why?
- -

This improves compilation speeds (especially from scratch), since you don't have to include headers a bunch of times for each translation unit. This is more of a problem in C++, where, for example, doing #include <map> ends up turning into 25,000 lines after preprocessing. All of toc's source code, which includes most of the C standard library, at the time of this writing (Dec 2019) is only 22,000 lines after preprocessing; imagine including all of that once for each translation unit which includes map. It also obviates the need for fancy build systems like CMake.

- -

New features

- -

Here are all the C99 features which toc depends on (I might have forgotten some...):

- - - -

And here are all of its C11 features:

- - - -

More

- -

See main.c for a bit more information.

- -
- -

Version history

- -

Here are the major versions of toc.

- - - - - - -
VersionDescriptionDate
0.0Initial version.2019 Dec 6
0.1Constant parameter inference.2019 Dec 15
0.2Foreign functions and #include.2020 Jan 29
- -
- -

Report a bug

- -

If you find a bug, you can report it through GitHub's issue tracker, or by emailing pommicket@gmail.com.

- -

Just send me the toc source code which results in the bug, and I'll try to fix it.

diff --git a/README.md b/README.md index 4cea135..e60fee7 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,8 @@ Here are the major versions of `toc`. VersionDescriptionDate 0.0Initial version.2019 Dec 6 0.1Constant parameter inference.2019 Dec 15 -0.2Foreign functions and #include.2020 Jan 29 +0.2Foreign functions and #include.2020 Jan 29 +0.3struct parameters2020 Feb 25 --- diff --git a/docs/00.html b/docs/00.html deleted file mode 100644 index cd0c352..0000000 --- a/docs/00.html +++ /dev/null @@ -1,61 +0,0 @@ -

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 integer, 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:

- - - -

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.

- -

To make declarations constant, use :: instead of :. e.g.

- -

-x ::= 5+3;
-y :: float = 5.123; -

- -

Here, "constant" means constant at compile time, not read-only as it does in C. One interesting thing about toc is that normal functions can run at compile time, so pretty much any expression is a valid initializer for a constant, e.g. doing x ::= some_function(); runs some_function at compile time, not at run time.

diff --git a/docs/01.html b/docs/01.html deleted file mode 100644 index 633295b..0000000 --- a/docs/01.html +++ /dev/null @@ -1,18 +0,0 @@ -

A first program

- -

The main function in toc corresponds to the main function in C. This function is called when your program is run. So, this is a valid toc program which does nothing:

- -

-main ::= fn() { -}; -

- -

It declares a constant, main, which is a function with an empty body. Note that the syntax for declaring functions is the same as the syntax for declaring constants (it isn't something like fn main() { ... }).

- -

Assuming you have compiled the compiler (see README.md for instructions about that), you can compile it with

- -

-toc <your filename> -

- -

You will get a file called out.c, which you can then put through your C compiler to get an executable file which does nothing. Congratulations! You've written your first toc program.

diff --git a/infer.c b/infer.c index bdffc1f..8cd7064 100644 --- a/infer.c +++ b/infer.c @@ -105,14 +105,20 @@ static bool infer_from_expr(Typer *tr, Expression *match, Expression *to, Expres } Argument *m_args = match->call.args; + size_t nargs = arr_len(m_args); Expression *t_args = to->call.arg_exprs; I16 *order = NULL; Expression *f = match->call.fn; Identifier ident = f->ident; bool is_direct_fn = f->kind == EXPR_IDENT && ident->decl_kind == IDECL_DECL && (ident->decl->flags & DECL_HAS_EXPR) && ident->decl->expr.kind == EXPR_FN; + if (!types_expr(tr, f)) + return false; + if (f->type.kind != TYPE_FN) { + char *s = type_to_str(&f->type); + err_print(f->where, "Calling non-function type %s.", s); + return false; + } if (is_direct_fn) { - if (!types_expr(tr, f)) - return false; FnExpr *fn_decl = ident->decl->expr.fn; if (!call_arg_param_order(fn_decl, &f->type, m_args, match->where, &order)) { free(order); @@ -120,9 +126,14 @@ static bool infer_from_expr(Typer *tr, Expression *match, Expression *to, Expres } } size_t nparams = arr_len(f->type.fn.types) - 1; + if (!order && nparams != nargs) { + /* wrong number of parameters? let typing deal with it... */ + free(order); + return true; + } for (size_t i = 0; i < nparams; ++i) { - if (order[i] != -1) { - Argument *m_arg = &m_args[order[i]]; + if (!order || order[i] != -1) { + Argument *m_arg = &m_args[order ? (size_t)order[i] : i]; Expression *t_arg; if (is_direct_fn) { t_arg = &t_args[i]; diff --git a/tests/arr/arr.toc b/tests/arr/arr.toc index f7c6707..a682283 100644 --- a/tests/arr/arr.toc +++ b/tests/arr/arr.toc @@ -1,9 +1,16 @@ puti ::= fn(x: int) { - #C("extern int printf(const char *fmt, ...)"); +//tcc's giving me "incompatible types for redefinition of 'printf'" for some reason (even though the declarations have the exact same type) + #C("#ifndef __TINYC__ +extern int printf(const char *fmt, ...); +#endif +"); #C("printf(\"%ld\\n\", (long)x)"); }; putf ::= fn(x: float) { - #C("extern int printf(const char *fmt, ...)"); + #C("#ifndef __TINYC__ +extern int printf(const char *fmt, ...); +#endif +"); #C("printf(\"%f\\n\", (double)x)"); }; diff --git a/tests/arr2/arr2.toc b/tests/arr2/arr2.toc index e28d355..e016cf2 100644 --- a/tests/arr2/arr2.toc +++ b/tests/arr2/arr2.toc @@ -1,10 +1,16 @@ puti ::= fn(x: int) { - #C("extern int printf(const char *fmt, ...)"); +#C("#ifndef __TINYC__ +extern int printf(const char *fmt, ...); +#endif +"); #C("printf(\"%ld\\n\", (long)x); "); }; putf ::= fn(x: float) { - #C("extern int printf(const char *fmt, ...)"); +#C("#ifndef __TINYC__ +extern int printf(const char *fmt, ...); +#endif +"); #C("printf(\"%f\\n\", (double)x); "); }; diff --git a/tests/bf/bf.toc b/tests/bf/bf.toc index c2f9970..a3f731a 100644 --- a/tests/bf/bf.toc +++ b/tests/bf/bf.toc @@ -32,7 +32,10 @@ getstdin ::= fn() []char { }; puti ::= fn(x: int) { - #C("extern int printf(const char *fmt, ...)"); + #C("#ifndef __TINYC__ +extern int printf(const char *fmt, ...); +#endif +"); #C("printf(\"%ld\\n\", x)"); }; diff --git a/tests/params/params.toc b/tests/params/params.toc index 3d8800d..1946739 100644 --- a/tests/params/params.toc +++ b/tests/params/params.toc @@ -7,7 +7,10 @@ do_foo ::= fn (x := 3) y := x { }; puti ::= fn(x: int) { - #C("extern int printf(const char *fmt, ...)"); + #C("#ifndef __TINYC__ +extern int printf(const char *fmt, ...); +#endif +"); #C("printf(\"%ld\\n\", x)"); }; diff --git a/tests/test.sh b/tests/test.sh index 9224ee3..4852218 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -10,6 +10,8 @@ compile() { EXTRA_FLAGS="-Wno-builtin-declaration-mismatch" elif [ "$CC" = "clang -O3 -s" ]; then EXTRA_FLAGS="-Wno-builtin-requires-header" + elif [ "$CC" = "tcc" ]; then + EXTRA_FLAGS="-w" fi $CC $CFLAGS $EXTRA_FLAGS -o $DIR/$1/$1.bin $DIR/$1/$1.c || exit 1 } diff --git a/toc.c b/toc.c index 3502986..b314187 100644 --- a/toc.c +++ b/toc.c @@ -34,9 +34,14 @@ #endif #if __STDC_VERSION__ >= 201112 +#ifdef __GNUC__ +/* GCC supports non-string literals as the message for a static assertion */ #define possibly_static_assert(cond) static_assert(cond, "Assertion " #cond " failed.") #else -#define possibly_static_assert assert +#define possibly_static_assert(cond) static_assert(cond, "Assertion failed") +#endif +#else +#define possibly_static_assert(cond) assert(cond) #endif diff --git a/types.c b/types.c index 3e4d6ce..d6a0d7b 100644 --- a/types.c +++ b/types.c @@ -870,6 +870,7 @@ static bool types_fn(Typer *tr, FnExpr *f, Type *t, Instance *instance) { /* puts a dynamic array of the argument indices of the parameters into order. *order must be freed, even if function fails */ static bool call_arg_param_order(FnExpr *fn, Type *fn_type, Argument *args, Location where, I16 **orderp) { *orderp = NULL; + assert(fn_type->flags & TYPE_IS_RESOLVED); size_t nparams = arr_len(fn_type->fn.types)-1; size_t nargs = arr_len(args); if (nargs > nparams) { @@ -901,13 +902,6 @@ static bool call_arg_param_order(FnExpr *fn, Type *fn_type, Argument *args, Loca } int p = 0; /* counter for sequential parameters */ - - Declaration *last_param_without_default_value = NULL; - arr_foreach(fn->params, Declaration, param) { - if (!(param->flags & DECL_HAS_EXPR)) { - last_param_without_default_value = param; - } - } Declaration *param = fn->params; size_t ident_idx = 0; I16 arg_idx = -1; @@ -941,18 +935,21 @@ static bool call_arg_param_order(FnExpr *fn, Type *fn_type, Argument *args, Loca } param_idx = index; } else { + /* move past inferred parameters because they must be named */ + while (param < (Declaration *)arr_end(fn->params) && (param->flags & DECL_INFER)) { + ++p; + ++ident_idx; + if (ident_idx == arr_len(param->idents)) { + ++param; + ident_idx = 0; + } + } if (param > (Declaration *)arr_last(fn->params)) { err_print(arg->where, "Too many arguments to function!"); info_print(fn->where, "Declaration is here."); return false; } - - 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 { - param_idx = p; - } + param_idx = p; } if (param_idx != -1) { @@ -979,7 +976,7 @@ static bool call_arg_param_order(FnExpr *fn, Type *fn_type, Argument *args, Loca if (order[param_idx] == -1) { if (!(decl->flags & DECL_HAS_EXPR) && !(decl->flags & DECL_INFER)) { char *s = ident_to_str(*ident); - err_print(where, "Parameter #%lu (%s) was not set in function call.", param_idx-1, s); + err_print(where, "Parameter #%lu (%s) was not set in function call.", param_idx+1, s); free(s); return false; } @@ -1751,7 +1748,6 @@ static bool types_expr(Typer *tr, Expression *e) { *p = ¶m->type; Type **q = typer_arr_add(tr, &arg_types); *q = &arg_exprs[i].type; - print_expr(arg_exprs + i); } ++i; } -- cgit v1.2.3