diff options
-rw-r--r-- | cgen.c | 30 | ||||
-rw-r--r-- | eval.c | 35 | ||||
-rw-r--r-- | main.c | 4 | ||||
-rw-r--r-- | parse.c | 36 | ||||
-rw-r--r-- | test.toc | 19 | ||||
-rw-r--r-- | tests/bools.toc | 22 | ||||
-rw-r--r-- | tests/bools_expected | 2 | ||||
-rw-r--r-- | types.c | 18 | ||||
-rw-r--r-- | types.h | 9 |
9 files changed, 150 insertions, 25 deletions
@@ -1286,10 +1286,9 @@ static void cgen_expr(CGenerator *g, Expression *e) { } } break; case EXPR_BINARY_OP: { - const char *s = ""; + const char *s = NULL; Expression *lhs = e->binary.lhs, *rhs = e->binary.rhs; Type *lhs_type = &lhs->type; - bool handled = false; BinaryOp op = e->binary.op; switch (op) { case BINARY_SUB: @@ -1304,7 +1303,6 @@ static void cgen_expr(CGenerator *g, Expression *e) { s = "%"; break; case BINARY_SET: cgen_set(g, lhs, NULL, rhs, NULL); - handled = true; break; case BINARY_GT: s = ">"; break; @@ -1316,6 +1314,14 @@ static void cgen_expr(CGenerator *g, Expression *e) { s = "<="; break; case BINARY_EQ: s = "=="; break; + case BINARY_AND: + case BINARY_OR: + cgen_write(g, "("); + cgen_truthiness(g, lhs); + cgen_write(g, " %s ", op == BINARY_AND ? "&&" : "||"); + cgen_truthiness(g, rhs); + cgen_write(g, ")"); + break; case BINARY_NE: s = "!="; break; case BINARY_SET_ADD: @@ -1368,7 +1374,6 @@ static void cgen_expr(CGenerator *g, Expression *e) { assert(0); break; } - handled = true; } break; case BINARY_DOT: { Type *struct_type = lhs_type; @@ -1387,7 +1392,6 @@ static void cgen_expr(CGenerator *g, Expression *e) { cgen_write(g, is_ptr ? "->" :"."); cgen_write(g, "data"); } - handled = true; } break; } if (lhs_type->kind == TYPE_PTR && type_is_void(lhs_type->ptr)) { @@ -1410,10 +1414,10 @@ static void cgen_expr(CGenerator *g, Expression *e) { cgen_expr(g, rhs); cgen_write(g, ";}"); } - handled = true; + s = NULL; } - if (handled) break; + if (!s) break; cgen_write(g, "("); cgen_expr(g, lhs); cgen_write(g, "%s", s); @@ -1421,8 +1425,7 @@ static void cgen_expr(CGenerator *g, Expression *e) { cgen_write(g, ")"); } break; case EXPR_UNARY_OP: { - const char *s = ""; - bool handled = false; + const char *s = NULL; Type *of_type = &e->unary.of->type; switch (e->unary.op) { case UNARY_MINUS: @@ -1432,7 +1435,10 @@ static void cgen_expr(CGenerator *g, Expression *e) { case UNARY_ADDRESS: s = "&"; break; case UNARY_NOT: - s = "!"; break; + cgen_write(g, "(!"); + cgen_truthiness(g, e->unary.of); + cgen_write(g, ")"); + break; case UNARY_SIZEOF: case UNARY_ALIGNOF: cgen_write(g, "%s(", e->unary.op == UNARY_SIZEOF ? "sizeof" : "_Alignof"); @@ -1440,7 +1446,6 @@ static void cgen_expr(CGenerator *g, Expression *e) { 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_LEN: { bool is_ptr = of_type->kind == TYPE_PTR; @@ -1457,7 +1462,6 @@ static void cgen_expr(CGenerator *g, Expression *e) { break; default: assert(0); break; } - handled = true; } break; case UNARY_TYPEOF: case UNARY_DSIZEOF: @@ -1465,7 +1469,7 @@ static void cgen_expr(CGenerator *g, Expression *e) { assert(0); return; } - if (handled) break; + if (!s) break; cgen_write(g, "("); cgen_write(g, "%s", s); cgen_expr(g, e->unary.of); @@ -1150,10 +1150,11 @@ static Status eval_expr(Evaluator *ev, Expression *e, Value *v) { } break; case EXPR_BINARY_OP: { Value lhs, rhs; + BinaryOp o = e->binary.op; Expression *lhs_expr = e->binary.lhs, *rhs_expr = e->binary.rhs; - if (e->binary.op != BINARY_SET) + if (o != BINARY_SET) if (!eval_expr(ev, lhs_expr, &lhs)) return false; - if (e->binary.op != BINARY_DOT) + if (o != BINARY_DOT && o != BINARY_AND && o != BINARY_OR) if (!eval_expr(ev, rhs_expr, &rhs)) return false; switch (e->binary.op) { case BINARY_DOT: { @@ -1162,6 +1163,22 @@ static Status eval_expr(Evaluator *ev, Expression *e, Value *v) { return false; eval_deref(v, ptr, &e->type); } break; + case BINARY_AND: { + if (!val_truthiness(lhs, &lhs_expr->type)) { + v->boolv = false; + break; + } + if (!eval_expr(ev, rhs_expr, &rhs)) return false; + v->boolv = val_truthiness(rhs, &rhs_expr->type); + } break; + case BINARY_OR: { + if (val_truthiness(lhs, &lhs_expr->type)) { + v->boolv = true; + break; + } + if (!eval_expr(ev, rhs_expr, &rhs)) return false; + v->boolv = val_truthiness(rhs, &rhs_expr->type); + } break; case BINARY_ADD: case BINARY_SUB: case BINARY_MUL: @@ -1223,6 +1240,13 @@ static Status eval_expr(Evaluator *ev, Expression *e, Value *v) { IfExpr *i = e->if_; if (i->cond) { Value cond; + if (i->cond->type.kind == TYPE_UNKNOWN) { + if (!i->cond->where.file->ctx->have_errored) { + err_print(i->cond->where, "Couldn't determine type of if condition, but need to evaluate it."); + return false; + } + return false; + } if (!eval_expr(ev, i->cond, &cond)) return false; if (val_truthiness(cond, &i->cond->type)) { if (!eval_block(ev, &i->body, v)) return false; @@ -1238,6 +1262,13 @@ static Status eval_expr(Evaluator *ev, Expression *e, Value *v) { WhileExpr *w = e->while_; while (1) { if (w->cond) { + if (w->cond->type.kind == TYPE_UNKNOWN) { + if (!w->cond->where.file->ctx->have_errored) { + err_print(w->cond->where, "Couldn't determine type of while condition, but need to evaluate it."); + return false; + } + return false; + } if (!eval_expr(ev, w->cond, &cond)) return false; Type *cond_type = &w->cond->type; if (!val_truthiness(cond, cond_type)) @@ -8,7 +8,6 @@ /* @TODO: -&&, || #no_warn start making a standard library... (printf; stringbuilder would be nice to have) improve type_to_str: @@ -18,6 +17,7 @@ switch - #fallthrough enums unions +bitwise operations --- either detect circular #includes or set a #include limit (maybe sometimes you want finite circular includes with #if) switch to / add as an alternative: libffi @@ -29,6 +29,7 @@ use point #except x; optional -Wshadow format errors so that vim/emacs can jump to them show include stack--especially for redeclarations with #include #force +stuff like __builtin_sqrt --- make sure that floating point literals are as exact as possible have some way of doing Infinity and s/qNaN (you can @@ -36,6 +37,7 @@ make sure that floating point literals are as exact as possible once you have a bunch of test code: - analyze memory usage by secretly passing __FILE__, __LINE__ to allocr_m/c/realloc - try making more Expression members pointers +- try making Value.slice a pointer - should val_stack be on the allocator? what about temporary arrays? -->on the contrary, should in_decls be off the allocator? error on x ::= {return; 3} @@ -102,6 +102,8 @@ static const char *binary_op_to_str(BinaryOp b) { case BINARY_NE: return "!="; case BINARY_DOT: return "."; case BINARY_MOD: return "%"; + case BINARY_AND: return "&&"; + case BINARY_OR: return "||"; } assert(0); return ""; @@ -589,6 +591,17 @@ static Status parse_type(Parser *p, Type *type, Location *where) { Location ptr_where; if (!parse_type(p, type->ptr, &ptr_where)) return false; } break; + case KW_ANDAND: { + /* pointer to pointer */ + type->kind = TYPE_PTR; + Type *ptr = type->ptr = parser_malloc(p, sizeof *type->ptr); + ptr->flags = 0; + ptr->kind = TYPE_PTR; + ptr->ptr = parser_malloc(p, sizeof *ptr->ptr); + ++t->token; /* move past && */ + Location ptrptr_where; + if (!parse_type(p, ptr->ptr, &ptrptr_where)) return false; + } break; case KW_STRUCT: { /* struct */ type->kind = TYPE_STRUCT; @@ -678,6 +691,9 @@ static bool parser_is_definitely_type(Parser *p, Token **end) { switch (t->token->kind) { case TOKEN_KW: switch (t->token->kw) { + case KW_ANDAND: + /* &&int */ + return true; case KW_STRUCT: ret = true; if (end) { @@ -982,12 +998,14 @@ static int op_precedence(Keyword op) { case KW_PERCENT_EQ: return 0; case KW_COMMA: return 1; - case KW_LT: return 3; - case KW_GT: return 3; - case KW_LE: return 3; - case KW_GE: return 3; - case KW_EQ_EQ: return 3; - case KW_NE: return 3; + case KW_OROR: return 2; + case KW_ANDAND: return 3; + case KW_LT: return 4; + case KW_GT: return 4; + case KW_LE: return 4; + case KW_GE: return 4; + case KW_EQ_EQ: return 4; + case KW_NE: return 4; case KW_SIZEOF: case KW_ALIGNOF: return 5; @@ -1855,6 +1873,12 @@ static Status parse_expr(Parser *p, Expression *e, Token *end) { case KW_PERCENT_EQ: op = BINARY_SET_MOD; break; + case KW_ANDAND: + op = BINARY_AND; + break; + case KW_OROR: + op = BINARY_OR; + break; 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; @@ -1,5 +1,22 @@ #include "std/io.toc"; +foo ::= fn() total := 0{ + if "foo" || (1/0) { + total += 1; + } + if !"foo" && (1/0) { + total += 100; + } + if "foo" || "bar" && 0 { + total += 10; + } + if !"foo" || !"bar" { + total += 1000; + } +} main ::= fn() { - puts("hello"); + a := foo(); + b ::= foo(); + puti(a); + puti(b); } diff --git a/tests/bools.toc b/tests/bools.toc new file mode 100644 index 0000000..b181972 --- /dev/null +++ b/tests/bools.toc @@ -0,0 +1,22 @@ +#include "std/io.toc"; + +foo ::= fn() total := 0{ + if "foo" || (1/0) { + total += 1; + } + if !"foo" && (1/0) { + total += 100; + } + if "foo" || "bar" && 0 { + total += 10; + } + if !"foo" || !"bar" { + total += 1000; + } +} +main ::= fn() { + a := foo(); + b ::= foo(); + puti(a); + puti(b); +} diff --git a/tests/bools_expected b/tests/bools_expected new file mode 100644 index 0000000..47e3206 --- /dev/null +++ b/tests/bools_expected @@ -0,0 +1,2 @@ +11 +11 @@ -3038,6 +3038,24 @@ static Status types_expr(Typer *tr, Expression *e) { break; } + case BINARY_AND: + case BINARY_OR: { + bool success = true; + if (!type_can_be_truthy(lhs_type)) { + char *s = type_to_str(lhs_type); + success = false; + err_print(lhs->where, "Cannot use operator %s on type %s.", binary_op_to_str(o), s); + free(s); + } + if (!type_can_be_truthy(rhs_type)) { + char *s = type_to_str(rhs_type); + success = false; + err_print(lhs->where, "Cannot use operator %s on type %s.", binary_op_to_str(o), s); + free(s); + } + if (!success) return false; + t->kind = TYPE_BUILTIN; t->builtin = BUILTIN_BOOL; + } break; case BINARY_AT_INDEX: if (type_is_slicechar(rhs_type)) { /* switch to BINARY_DOT (point["x"] => point.x) */ @@ -274,6 +274,8 @@ typedef enum { KW_LT, KW_GE, KW_GT, + KW_ANDAND, + KW_OROR, KW_PLUS, KW_MINUS, KW_ASTERISK, @@ -328,7 +330,7 @@ typedef enum { static const char *const keywords[KW_COUNT] = { ";", ":", ",", "(", ")", "{", "}", "[", "]", "==", "+=", "-=", "*=", "/=", "%=", - "!=", "<=", "<", ">=", ">", + "!=", "<=", "<", ">=", ">", "&&", "||", "+", "-", "*", "!", "&", "/", "%", "..", ".", "=", "if", "elif", "else", "while", "for", "return", "break", @@ -339,6 +341,7 @@ static const char *const keywords[KW_COUNT] = { "typeof", "sizeof", "alignof", "null" }; + typedef enum { NUM_LITERAL_INT, NUM_LITERAL_FLOAT @@ -614,7 +617,9 @@ typedef enum { BINARY_EQ, BINARY_NE, BINARY_AT_INDEX, /* e.g. x[i] */ - BINARY_DOT + BINARY_DOT, + BINARY_AND, /* && */ + BINARY_OR /* || */ } BinaryOp; typedef struct CallExpr { |