diff options
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | README.html | 103 | ||||
-rw-r--r-- | README.md | 3 | ||||
-rw-r--r-- | docs/00.html | 61 | ||||
-rw-r--r-- | docs/01.html | 18 | ||||
-rw-r--r-- | infer.c | 19 | ||||
-rw-r--r-- | tests/arr/arr.toc | 11 | ||||
-rw-r--r-- | tests/arr2/arr2.toc | 10 | ||||
-rw-r--r-- | tests/bf/bf.toc | 5 | ||||
-rw-r--r-- | tests/params/params.toc | 5 | ||||
-rwxr-xr-x | tests/test.sh | 2 | ||||
-rw-r--r-- | toc.c | 7 | ||||
-rw-r--r-- | types.c | 28 |
13 files changed, 64 insertions, 210 deletions
@@ -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 @@ -<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 nearly as fast in theory.</p> - -<p>See <code>docs</code> for more information (in progress).</p> - -<p><code>tests</code> has some test programs written in <code>toc</code>.</p> - -<p>To compile the compiler on a Unix-y system, just run <code>./build.sh release</code>. You can supply a compiler by running <code>CC=tcc ./build.sh release</code>, or build it in debug mode without the <code>release</code>. To disable compile time foreign function support (which you will need to do if you don't have ffcall/dl), prefix this with <code>COMPILE_TIME_FOREIGN_FN_SUPPORT=no</code>.</p> - -<p>On other systems, you can just compile main.c with a C compiler. <code>toc</code> 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 <code>clang</code>, <code>gcc</code>, and <code>tcc</code>. It can also be compiled as if it were C++, so, MSVC and <code>g++</code> can also compile it (it does rely on implicit casting of <code>void *</code> though). The <em>outputted</em> code should be C99-compliant.</p> - -<h4>Why it compiles to C</h4> - -<p><code>toc</code> compiles to C. Here are some reasons why:</p> - -<ul> -<li>Speed. C is one of the most performant programming languages out there. It also has compilers which are very good at optimizing (better than anything I could write). </li> -<li>Portability. C is probably the most portable language. It has existed for >30 years and can run on practically anything. Furthermore, all major languages nowadays can call functions written in C.</li> -</ul> - -<hr /> - -<h3><code>toc</code> Compiler Source Code</h3> - -<p>Most of the source code for the <code>toc</code> 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.</p> - -<p>See <code>LICENSE</code> for the GNU General Public License.</p> - -<p><code>toc</code> 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 <code>libffcall</code> and <code>libdl</code> (so this is only currently supported on Unix-y systems).</p> - -<h4>Build system</h4> - -<p><code>toc</code> is set up as a unity build, meaning that there is only one translation unit. So, <code>main.c</code> <code>#include</code>s <code>toc.c</code>, which <code>#include</code>s all of <code>toc</code>'s files.</p> - -<h5>Why?</h5> - -<p>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 <code>#include <map></code> 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 <code>map</code>. It also obviates the need for fancy build systems like CMake.</p> - -<h4>New features</h4> - -<p>Here are all the C99 features which <code>toc</code> depends on (I might have forgotten some...):</p> - -<ul> -<li>Declare anywhere</li> -<li><code>inttypes.h</code></li> -<li>Non-constant struct literal initializers (e.g. <code>int x[2] = {y, z};</code>)</li> -<li>Flexible array members</li> -</ul> - -<p>And here are all of its C11 features:</p> - -<ul> -<li>Anonymous structures/unions</li> -<li><code>max_align_t</code> - It can still compile without this, and will almost definitely work, but it won't technically be standard-compliant</li> -</ul> - -<h4>More</h4> - -<p>See <code>main.c</code> for a bit more information.</p> - -<hr /> - -<h3>Version history</h3> - -<p>Here are the major versions of <code>toc</code>.</p> - -<table> -<tr><th>Version</th><th>Description</th><th>Date</th></tr> -<tr><td>0.0</td><td>Initial version.</td><td>2019 Dec 6</td></tr> -<tr><td>0.1</td><td>Constant parameter inference.</td><td>2019 Dec 15</td></tr> -<tr><td>0.2</td><td>Foreign functions and #include.</td><td>2020 Jan 29</td></tr> -</table> - -<hr /> - -<h3>Report a bug</h3> - -<p>If you find a bug, you can report it through <a href="https://github.com/pommicket/toc/issues">GitHub's issue tracker</a>, or by emailing pommicket@gmail.com.</p> - -<p>Just send me the <code>toc</code> source code which results in the bug, and I'll try to fix it. </p> @@ -83,7 +83,8 @@ Here are the major versions of `toc`. <tr><th>Version</th><th>Description</th><th>Date</th></tr> <tr><td>0.0</td><td>Initial version.</td><td>2019 Dec 6</td></tr> <tr><td>0.1</td><td>Constant parameter inference.</td><td>2019 Dec 15</td></tr> -<tr><td>0.2</td><td>Foreign functions and #include.</td><td>2020 Jan 29</td></tr> +<tr><td>0.2</td><td>Foreign functions and <code>#include</code>.</td><td>2020 Jan 29</td></tr> +<tr><td>0.3</td><td><code>struct</code> parameters</td><td>2020 Feb 25</td></tr> </table> --- 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 @@ -<h2>Declarations</h2> - -<p>In toc, declarations have the following syntax: -<code> -<name> :[:] [type] [= expression]; -</code></p> - -<p>The square brackets (<code>[]</code>) indicate something optional.</p> - -<p>All of the following statements -declare an new variable <code>x</code> which is an integer, and has a value of 0: -<code> -x : int; -x : int = 0; -x := 0; -</code> -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 <code>x</code> uninitialized.</p> - -<p>If you wanted x to be a floating-point number, you could use: -<code> -x : float; -x : float = 0; -x := 0.0; -</code></p> - -<p>Note that <code>0</code> can be used as both a <code>float</code> and an <code>int</code>eger, but -when no type is specified, it defaults to an <code>int</code>, whereas <code>0.0</code> -defaults to a <code>float</code>.</p> - -<p>Here are all of toc's builtin types and their ranges of values:</p> - -<ul> -<li><code>int</code> - A 64-bit signed integer (always), -9223372036854775808 to 9223372036854775807</li> -<li><code>i8</code> - An 8-bit signed integer, -128 to 128</li> -<li><code>i16</code> - 16-bit signed integer, -32768 to 32767</li> -<li><code>i32</code> - 32-bit signed integer, -2147483648 to 2147483647</li> -<li><code>i64</code> - 64-bit signed integer (same as <code>int</code>, but more explicit about the size), -9223372036854775808 to 9223372036854775807</li> -<li><code>u8</code> - An 8-bit unsigned integer, 0 to 255</li> -<li><code>u16</code> - 16-bit unsigned integer, 0 to 65535</li> -<li><code>u32</code> - 32-bit unsigned integer, 0 to 4294967295</li> -<li><code>u64</code> - 64-bit unsigned integer, 0 to 18446744073709551615</li> -<li><code>float</code> - A 32-bit floating-point number, -3.40282347e+38 to 3.40282347e+38</li> -<li><code>f32</code> - A 32-bit floating-point number (same as <code>float</code>, but more explicit about the size)</li> -<li><code>f64</code> - A 64-bit floating-point number, -1.7976931348623157e+308 to 1.7976931348623157e+308</li> -<li><code>bool</code> - A boolean value, either <code>false</code> or <code>true</code>.</li> -<li><code>char</code> - A character. The specific values are technically platform-dependent, but usually there are 256 of them.</li> -</ul> - -<p>At the moment, it is not technically guaranteed that <code>f32</code>/<code>float</code> is actually 32-bit and that <code>f64</code> 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.</p> - -<p>To make declarations constant, use <code>::</code> instead of <code>:</code>. e.g.</p> - -<p><code> -x ::= 5+3; <br /> -y :: float = 5.123; -</code></p> - -<p>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 <code>x ::= some_function();</code> runs <code>some_function</code> at compile time, not at run time.</p> 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 @@ -<h3>A first program</h3> - -<p>The <code>main</code> function in toc corresponds to the <code>main</code> function in C. This function is called when your program is run. So, this is a valid toc program which does nothing:</p> - -<p><code> -main ::= fn() { -}; -</code></p> - -<p>It declares a constant, <code>main</code>, 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 <code>fn main() { ... }</code>).</p> - -<p>Assuming you have compiled the compiler (see <code>README.md</code> for instructions about that), you can compile it with</p> - -<p><code> -toc <your filename> -</code></p> - -<p>You will get a file called <code>out.c</code>, 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.</p> @@ -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 } @@ -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 @@ -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; } |