From a48fdca56dfb7cab319789fb51b2d8959cf04c84 Mon Sep 17 00:00:00 2001 From: Leo Tenenbaum Date: Mon, 16 Mar 2020 19:24:02 -0400 Subject: sizeof, alignof --- cgen.c | 9 +++ eval.c | 6 ++ main.c | 3 +- parse.c | 214 ++++++++++++++++++++++++++++---------------------- test.toc | 16 +++- tests/sizeof.toc | 17 ++++ tests/sizeof_expected | 2 + tests/test.sh | 8 +- types.c | 19 ++++- types.h | 8 +- 10 files changed, 197 insertions(+), 105 deletions(-) create mode 100644 tests/sizeof.toc create mode 100644 tests/sizeof_expected 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 { -- cgit v1.2.3