summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cgen.c30
-rw-r--r--eval.c35
-rw-r--r--main.c4
-rw-r--r--parse.c36
-rw-r--r--test.toc19
-rw-r--r--tests/bools.toc22
-rw-r--r--tests/bools_expected2
-rw-r--r--types.c18
-rw-r--r--types.h9
9 files changed, 150 insertions, 25 deletions
diff --git a/cgen.c b/cgen.c
index 4b3e3d7..6fc6cb6 100644
--- a/cgen.c
+++ b/cgen.c
@@ -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);
diff --git a/eval.c b/eval.c
index 830e574..01f7625 100644
--- a/eval.c
+++ b/eval.c
@@ -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))
diff --git a/main.c b/main.c
index 6fb48e7..313267b 100644
--- a/main.c
+++ b/main.c
@@ -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}
diff --git a/parse.c b/parse.c
index a39fbf2..dd3a4b7 100644
--- a/parse.c
+++ b/parse.c
@@ -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;
diff --git a/test.toc b/test.toc
index 97c30b9..b181972 100644
--- a/test.toc
+++ b/test.toc
@@ -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
diff --git a/types.c b/types.c
index 9970d90..536ddf6 100644
--- a/types.c
+++ b/types.c
@@ -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) */
diff --git a/types.h b/types.h
index f8a45d0..07d9bfb 100644
--- a/types.h
+++ b/types.h
@@ -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 {