summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--README.html103
-rw-r--r--README.md3
-rw-r--r--docs/00.html61
-rw-r--r--docs/01.html18
-rw-r--r--infer.c19
-rw-r--r--tests/arr/arr.toc11
-rw-r--r--tests/arr2/arr2.toc10
-rw-r--r--tests/bf/bf.toc5
-rw-r--r--tests/params/params.toc5
-rwxr-xr-xtests/test.sh2
-rw-r--r--toc.c7
-rw-r--r--types.c28
13 files changed, 64 insertions, 210 deletions
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 @@
-<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 &lt;map&gt;</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>
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`.
<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>
-&lt;name&gt; :[:] [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 &lt;your filename&gt;
-</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>
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 = &param->type;
Type **q = typer_arr_add(tr, &arg_types);
*q = &arg_exprs[i].type;
- print_expr(arg_exprs + i);
}
++i;
}