summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cgen.c9
-rw-r--r--eval.c6
-rw-r--r--main.c3
-rw-r--r--parse.c214
-rw-r--r--test.toc16
-rw-r--r--tests/sizeof.toc17
-rw-r--r--tests/sizeof_expected2
-rwxr-xr-xtests/test.sh8
-rw-r--r--types.c19
-rw-r--r--types.h8
10 files changed, 197 insertions, 105 deletions
diff --git a/cgen.c b/cgen.c
index ccbeb7e..176a2be 100644
--- a/cgen.c
+++ b/cgen.c
@@ -1508,6 +1508,15 @@ static void cgen_expr(CGenerator *g, Expression *e) {
s = "&"; break;
case UNARY_NOT:
s = "!"; break;
+ case UNARY_SIZEOF:
+ case UNARY_ALIGNOF:
+ cgen_write(g, "%s(", e->unary.op == UNARY_SIZEOF ? "sizeof" : "_Alignof");
+ assert(e->unary.of->kind == EXPR_VAL);
+ cgen_type_pre(g, e->unary.of->val.type);
+ cgen_type_post(g, e->unary.of->val.type);
+ cgen_write(g, ")");
+ handled = true;
+ break;
case UNARY_DEL:
cgen_write(g, "free_(");
cgen_expr(g, e->unary.of);
diff --git a/eval.c b/eval.c
index 0ce09a3..474e9ee 100644
--- a/eval.c
+++ b/eval.c
@@ -1152,6 +1152,12 @@ static Status eval_expr(Evaluator *ev, Expression *e, Value *v) {
default: assert(0); break;
}
break;
+ case UNARY_SIZEOF:
+ v->i64 = (I64)compiler_sizeof(of.type);
+ break;
+ case UNARY_ALIGNOF:
+ v->i64 = (I64)compiler_alignof(of.type);
+ break;
case UNARY_DSIZEOF:
case UNARY_DALIGNOF:
case UNARY_TYPEOF:
diff --git a/main.c b/main.c
index 8b01d85..9e971fc 100644
--- a/main.c
+++ b/main.c
@@ -8,8 +8,7 @@
/*
TODO:
-make #sizeof always take a Type
-sizeof (not #sizeof)
+cast varargs, not integer literals in cgen
- make new(s) and del functions!
defer
&&, ||
diff --git a/parse.c b/parse.c
index b101afe..7d2628c 100644
--- a/parse.c
+++ b/parse.c
@@ -64,6 +64,8 @@ static const char *unary_op_to_str(UnaryOp u) {
case UNARY_LEN: return "len";
case UNARY_DSIZEOF: return "#sizeof";
case UNARY_DALIGNOF: return "#alignof";
+ case UNARY_SIZEOF: return "sizeof";
+ case UNARY_ALIGNOF: return "alignof";
case UNARY_TYPEOF: return "typeof";
}
assert(0);
@@ -1020,6 +1022,8 @@ static void fprint_expr(FILE *out, Expression *e);
/* cast/new aren't really operators since they operate on types, not exprs. */
#define CAST_PRECEDENCE 2
#define NEW_PRECEDENCE 22
+#define DSIZEOF_PRECEDENCE 5
+#define DALIGNOF_PRECEDENCE 5
static int op_precedence(Keyword op) {
switch (op) {
case KW_EQ:
@@ -1036,7 +1040,11 @@ static int op_precedence(Keyword op) {
case KW_GE: return 3;
case KW_EQ_EQ: return 3;
case KW_NE: return 3;
- case KW_TYPEOF: return 5;
+ case KW_SIZEOF:
+ case KW_ALIGNOF:
+ return 5;
+ case KW_TYPEOF:
+ return 6;
case KW_PLUS: return 10;
case KW_MINUS: return 20;
case KW_AMPERSAND: return 25;
@@ -1608,24 +1616,34 @@ static Status parse_expr(Parser *p, Expression *e, Token *end) {
}
break;
case KW_DOT:
- if (paren_level == 0 && brace_level == 0 && square_level == 0)
+ if (paren_level == 0 && brace_level == 0 && square_level == 0) {
dot = token;
+ }
break;
- default: { /* OPTIM: use individual cases for each op */
- if (paren_level == 0 && brace_level == 0 && square_level == 0) {
- int precedence;
- switch (token->kw) {
- case KW_AS: precedence = CAST_PRECEDENCE; break;
- case KW_NEW: precedence = NEW_PRECEDENCE; break;
- default: precedence = op_precedence(token->kw); break;
- }
- if (precedence == NOT_AN_OP) break; /* nvm it's not an operator */
- if (lowest_precedence == NOT_AN_OP || precedence <= lowest_precedence) {
- lowest_precedence = precedence;
- lowest_precedence_op = token;
- }
+ default: break;
+ }
+ }
+
+ if (paren_level == 0 && brace_level == 0 && square_level == 0) {
+ int precedence = NOT_AN_OP;
+ if (token->kind == TOKEN_KW) {
+ switch (token->kw) {
+ case KW_AS: precedence = CAST_PRECEDENCE; break;
+ case KW_NEW: precedence = NEW_PRECEDENCE; break;
+ default: precedence = op_precedence(token->kw); break;
+ }
+ } else if (token->kind == TOKEN_DIRECT) {
+ switch (token->direct) {
+ case DIRECT_SIZEOF: precedence = DSIZEOF_PRECEDENCE; break;
+ case DIRECT_ALIGNOF: precedence = DALIGNOF_PRECEDENCE; break;
+ default: break;
+ }
+ }
+ if (precedence != NOT_AN_OP) {
+ if (lowest_precedence == NOT_AN_OP || precedence <= lowest_precedence) {
+ lowest_precedence = precedence;
+ lowest_precedence_op = token;
}
- } break;
}
}
}
@@ -1681,72 +1699,94 @@ static Status parse_expr(Parser *p, Expression *e, Token *end) {
/* Unary */
UnaryOp op;
bool is_unary = true;
- switch (t->token->kw) {
- case KW_PLUS:
- /* unary + is ignored entirely */
- ++t->token;
- /* re-parse this expression without + */
- return parse_expr(p, e, end);
- case KW_MINUS:
- op = UNARY_MINUS;
- break;
- case KW_AMPERSAND:
- op = UNARY_ADDRESS;
- break;
- case KW_ASTERISK:
- op = UNARY_DEREF;
- break;
- case KW_EXCLAMATION:
- op = UNARY_NOT;
- break;
- case KW_NEW:
- e->kind = EXPR_NEW;
- ++t->token;
- if (!token_is_kw(t->token, KW_LPAREN)) {
- tokr_err(t, "Expected ( to follow new.");
- return false;
- }
- ++t->token;
- if (!parse_type(p, &e->new.type, NULL)) return false;
- if (token_is_kw(t->token, KW_COMMA)) {
- /* new(int, 5) */
+ if (t->token->kind == TOKEN_KW) {
+ switch (t->token->kw) {
+ case KW_PLUS:
+ /* unary + is ignored entirely */
++t->token;
- Token *n_end = expr_find_end(p, 0);
- e->new.n = parser_new_expr(p);
- if (!parse_expr(p, e->new.n, n_end))
+ /* re-parse this expression without + */
+ return parse_expr(p, e, end);
+ case KW_MINUS:
+ op = UNARY_MINUS;
+ break;
+ case KW_AMPERSAND:
+ op = UNARY_ADDRESS;
+ break;
+ case KW_ASTERISK:
+ op = UNARY_DEREF;
+ break;
+ case KW_EXCLAMATION:
+ op = UNARY_NOT;
+ break;
+ case KW_SIZEOF:
+ op = UNARY_SIZEOF;
+ break;
+ case KW_ALIGNOF:
+ op = UNARY_ALIGNOF;
+ break;
+ case KW_NEW:
+ e->kind = EXPR_NEW;
+ ++t->token;
+ if (!token_is_kw(t->token, KW_LPAREN)) {
+ tokr_err(t, "Expected ( to follow new.");
return false;
- } else e->new.n = NULL;
- if (!token_is_kw(t->token, KW_RPAREN)) {
- tokr_err(t, "Expected ).");
- return false;
- }
- ++t->token;
- if (e->new.type.kind == TYPE_TUPLE) {
- err_print(e->where, "You cannot new a tuple.");
- return false;
+ }
+ ++t->token;
+ if (!parse_type(p, &e->new.type, NULL)) return false;
+ if (token_is_kw(t->token, KW_COMMA)) {
+ /* new(int, 5) */
+ ++t->token;
+ Token *n_end = expr_find_end(p, 0);
+ e->new.n = parser_new_expr(p);
+ if (!parse_expr(p, e->new.n, n_end))
+ return false;
+ } else e->new.n = NULL;
+ if (!token_is_kw(t->token, KW_RPAREN)) {
+ tokr_err(t, "Expected ).");
+ return false;
+ }
+ ++t->token;
+ if (e->new.type.kind == TYPE_TUPLE) {
+ err_print(e->where, "You cannot new a tuple.");
+ return false;
+ }
+ if (t->token == end)
+ goto success;
+ /* otherwise, there's more stuff after the new (e.g. new(int, 5).len)*/
+ t->token = start;
+ goto not_an_op;
+ case KW_DEL:
+ if (!token_is_kw(t->token + 1, KW_LPAREN)) {
+ /* for the future, when del could be a function */
+ err_print(e->where, "Expected ( after del.");
+ return false;
+ }
+ op = UNARY_DEL;
+ break;
+ case KW_TYPEOF:
+ op = UNARY_TYPEOF;
+ break;
+ default:
+ is_unary = false;
+ break;
}
- if (t->token == end)
- goto success;
- /* otherwise, there's more stuff after the new (e.g. new(int, 5).len)*/
- t->token = start;
- goto not_an_op;
- case KW_DEL:
- if (!token_is_kw(t->token + 1, KW_LPAREN)) {
- /* for the future, when del could be a function */
- err_print(e->where, "Expected ( after del.");
- return false;
+ } else if (t->token->kind == TOKEN_DIRECT) {
+ switch (t->token->direct) {
+ case DIRECT_SIZEOF:
+ op = UNARY_DSIZEOF;
+ break;
+ case DIRECT_ALIGNOF:
+ op = UNARY_DALIGNOF;
+ break;
+ default:
+ is_unary = false;
+ break;
}
- op = UNARY_DEL;
- break;
- case KW_TYPEOF:
- op = UNARY_TYPEOF;
- break;
- default:
+ } else {
is_unary = false;
- break;
}
if (!is_unary) {
- tokr_err(t, "%s is not a unary operator.", keywords[lowest_precedence_op->kw]);
+ tokr_err(t, "This is not a unary operator, but it's being used as one.");
return false;
}
e->unary.op = op;
@@ -1858,12 +1898,9 @@ static Status parse_expr(Parser *p, Expression *e, Token *end) {
case KW_PERCENT_EQ:
op = BINARY_SET_MOD;
break;
- case KW_AMPERSAND:
- case KW_EXCLAMATION:
- case KW_DEL:
+ default:
err_print(token_location(p->file, lowest_precedence_op), "Unary operator '%s' being used as a binary operator!", kw_to_str(lowest_precedence_op->kw));
return false;
- default: assert(0); return false;
}
e->binary.op = op;
e->kind = EXPR_BINARY_OP;
@@ -1900,16 +1937,6 @@ static Status parse_expr(Parser *p, Expression *e, Token *end) {
e->kind = EXPR_BUILTIN;
single_arg = e->builtin.which.expr = parser_new_expr(p);
break;
- case DIRECT_SIZEOF:
- e->kind = EXPR_UNARY_OP;
- e->unary.op = UNARY_DSIZEOF;
- single_arg = e->unary.of = parser_new_expr(p);
- break;
- case DIRECT_ALIGNOF:
- e->kind = EXPR_UNARY_OP;
- e->unary.op = UNARY_DALIGNOF;
- single_arg = e->unary.of = parser_new_expr(p);
- break;
case DIRECT_FOREIGN: {
e->kind = EXPR_FN;
@@ -1993,12 +2020,7 @@ static Status parse_expr(Parser *p, Expression *e, Token *end) {
fn->where.end = t->token;
return true;
}
- case DIRECT_ERROR:
- case DIRECT_WARN:
- case DIRECT_INFO:
- case DIRECT_EXPORT:
- case DIRECT_INCLUDE:
- case DIRECT_FORCE:
+ default:
tokr_err(t, "Unrecognized expression.");
return false;
case DIRECT_COUNT: assert(0); break;
@@ -2006,7 +2028,6 @@ static Status parse_expr(Parser *p, Expression *e, Token *end) {
if (single_arg) {
++t->token;
if (!token_is_kw(t->token, KW_LPAREN)) {
- printf("%d\n",t->token->direct);
tokr_err(t, "Expected ( to follow #%s.", directives[t->token[-1].direct]);
return false;
}
@@ -2192,6 +2213,7 @@ static Status parse_expr(Parser *p, Expression *e, Token *end) {
return false;
goto success;
}
+
tokr_err(t, "Unrecognized expression.");
return false;
}
diff --git a/test.toc b/test.toc
index a525e75..f1cf021 100644
--- a/test.toc
+++ b/test.toc
@@ -6,8 +6,18 @@ Point3D ::= struct {
main ::= fn() {
p: Point3D;
- io.puti(#sizeof(Point3D));
- // io.puti(#sizeof(p));
- io.puti(#sizeof(typeof p));
+ io.puti(#sizeof Point3D);
+ // io.puti(#sizeof p);
+ io.puti(#sizeof typeof p);
+ io.puti(sizeof Point3D);
+ // io.puti(sizeof p);
+ io.puti(sizeof typeof p);
+
+ io.puti(#alignof Point3D);
+ // io.puti(#alignof p);
+ io.puti(#alignof typeof p);
+ io.puti(alignof Point3D);
+ // io.puti(alignof p);
+ io.puti(alignof typeof p);
}
main(); \ No newline at end of file
diff --git a/tests/sizeof.toc b/tests/sizeof.toc
new file mode 100644
index 0000000..0bc806b
--- /dev/null
+++ b/tests/sizeof.toc
@@ -0,0 +1,17 @@
+#include "io.toc", io;
+
+Point3D ::= struct {
+ x, y, z: f32;
+}
+
+calculation ::= fn() int {
+ p: Point3D;
+ (#sizeof Point3D) * (#sizeof typeof p) * (sizeof Point3D) * (sizeof typeof p)
+ * (#alignof Point3D) * (#alignof typeof p) * (alignof Point3D) * (alignof typeof p)
+}
+
+main ::= fn() {
+ x ::= calculation();
+ io.puti(x);
+ io.puti(calculation());
+} \ No newline at end of file
diff --git a/tests/sizeof_expected b/tests/sizeof_expected
new file mode 100644
index 0000000..d93e228
--- /dev/null
+++ b/tests/sizeof_expected
@@ -0,0 +1,2 @@
+5308416
+5308416
diff --git a/tests/test.sh b/tests/test.sh
index 4016151..5e4535a 100755
--- a/tests/test.sh
+++ b/tests/test.sh
@@ -2,6 +2,7 @@
tests='bf
control_flow
+sizeof
arr
arr2
arr3
@@ -30,6 +31,8 @@ compile_c() {
$CC $CFLAGS $EXTRA_FLAGS -Werror -o a.out out.c || exit 1
}
+failed=false
+
do_tests() {
valgrind -q --exit-on-first-error=yes --error-exitcode=1 $TOC "$1.toc" -o out.c || exit 1
for CC in "gcc -O0 -g" "tcc" "clang -O3 -s"; do
@@ -40,7 +43,7 @@ do_tests() {
printf '\x1b[92mpassed!\x1b[0m\n'
else
printf '\x1b[91mfailed!\x1b[0m\n'
- exit 1
+ failed=true
fi
done
}
@@ -49,3 +52,6 @@ for x in $tests; do
done
rm got a.out out.c
+if $failed; then
+ exit 1
+fi
diff --git a/types.c b/types.c
index 712c8d8..eabb79d 100644
--- a/types.c
+++ b/types.c
@@ -2605,7 +2605,8 @@ static Status types_expr(Typer *tr, Expression *e) {
case UNARY_DALIGNOF: {
Type *queried_type;
if (!type_is_builtin(&of->type, BUILTIN_TYPE)) {
- err_print(e->where, "Argument of #sizeof or #alignof must be a Type. Did you mean #sizeof(typeof ...)?");
+ char *s = e->unary.op == UNARY_DSIZEOF ? "sizeof" : "alignof";
+ err_print(e->where, "Argument of #%s must be a Type. Did you mean #%s(typeof ...)?", s, s);
return false;
}
Value val;
@@ -2620,6 +2621,22 @@ static Status types_expr(Typer *tr, Expression *e) {
t->kind = TYPE_BUILTIN;
t->builtin = BUILTIN_I64;
} break;
+ case UNARY_SIZEOF:
+ case UNARY_ALIGNOF: {
+ /* eval of */
+ if (!type_is_builtin(&of->type, BUILTIN_TYPE)) {
+ char *s = e->unary.op == UNARY_SIZEOF ? "sizeof" : "alignof";
+ err_print(e->where, "Argument of %s must be a Type. Did you mean %s(typeof ...)?", s, s);
+ return false;
+ }
+ Value val;
+ if (!eval_expr(tr->evalr, of, &val))
+ return false;
+ of->kind = EXPR_VAL;
+ of->val = val;
+ t->kind = TYPE_BUILTIN;
+ t->builtin = BUILTIN_I64;
+ } break;
}
} break;
case EXPR_BINARY_OP: {
diff --git a/types.h b/types.h
index 9c6545f..40165f5 100644
--- a/types.h
+++ b/types.h
@@ -323,6 +323,8 @@ typedef enum {
KW_FALSE,
KW_NMS,
KW_TYPEOF,
+ KW_SIZEOF,
+ KW_ALIGNOF,
KW_COUNT
} Keyword;
@@ -338,7 +340,7 @@ static const char *const keywords[KW_COUNT] =
"int", "i8", "i16", "i32", "i64",
"u8", "u16", "u32", "u64", "float", "f32", "f64", "Type",
"Namespace",
- "char", "bool", "true", "false", "nms", "typeof"};
+ "char", "bool", "true", "false", "nms", "typeof", "sizeof", "alignof"};
typedef enum {
NUM_LITERAL_INT,
@@ -581,7 +583,9 @@ typedef enum {
UNARY_TYPEOF, /* typeof x */
UNARY_LEN, /* x.len ; replaces BINARY_DOT len when typing */
UNARY_DSIZEOF,
- UNARY_DALIGNOF
+ UNARY_DALIGNOF,
+ UNARY_SIZEOF,
+ UNARY_ALIGNOF
} UnaryOp;
typedef enum {