aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2024-11-03 15:48:13 -0500
committerBruce Hill <bruce@bruce-hill.com>2024-11-03 15:48:13 -0500
commit3743913ce2c5bc37f899d437c09b60cbb3bc6dea (patch)
treecbbe7fe8debc7651c915b727f608bf2bcfa151ea
parent792743dff3b4af6df030a53e447b28a8a3e8b072 (diff)
Add unsigned integer shifts
-rw-r--r--ast.c4
-rw-r--r--ast.h2
-rw-r--r--compile.c34
-rw-r--r--parse.c21
-rw-r--r--test/integers.tm10
-rw-r--r--typecheck.c6
6 files changed, 64 insertions, 13 deletions
diff --git a/ast.c b/ast.c
index 65a8f405..5087e91f 100644
--- a/ast.c
+++ b/ast.c
@@ -15,7 +15,8 @@ static const char *OP_NAMES[] = {
[BINOP_UNKNOWN]="unknown",
[BINOP_POWER]="^", [BINOP_MULT]="*", [BINOP_DIVIDE]="/",
[BINOP_MOD]="mod", [BINOP_MOD1]="mod1", [BINOP_PLUS]="+", [BINOP_MINUS]="minus",
- [BINOP_CONCAT]="++", [BINOP_LSHIFT]="<<", [BINOP_RSHIFT]=">>", [BINOP_MIN]="min",
+ [BINOP_CONCAT]="++", [BINOP_LSHIFT]="<<", [BINOP_ULSHIFT]="<<[u]",
+ [BINOP_RSHIFT]=">>", [BINOP_URSHIFT]=">>[u]", [BINOP_MIN]="min",
[BINOP_MAX]="max", [BINOP_EQ]="==", [BINOP_NE]="!=", [BINOP_LT]="<",
[BINOP_LE]="<=", [BINOP_GT]=">", [BINOP_GE]=">=", [BINOP_CMP]="<>",
[BINOP_AND]="and", [BINOP_OR]="or", [BINOP_XOR]="xor",
@@ -25,6 +26,7 @@ const char *binop_method_names[BINOP_XOR+1] = {
[BINOP_POWER]="power", [BINOP_MULT]="times", [BINOP_DIVIDE]="divided_by",
[BINOP_MOD]="modulo", [BINOP_MOD1]="modulo1", [BINOP_PLUS]="plus", [BINOP_MINUS]="minus",
[BINOP_CONCAT]="concatenated_with", [BINOP_LSHIFT]="left_shifted", [BINOP_RSHIFT]="right_shifted",
+ [BINOP_ULSHIFT]="unsigned_left_shifted", [BINOP_URSHIFT]="unsigned_right_shifted",
[BINOP_AND]="bit_and", [BINOP_OR]="bit_or", [BINOP_XOR]="bit_xor",
};
diff --git a/ast.h b/ast.h
index 6546dd2d..bae74e56 100644
--- a/ast.h
+++ b/ast.h
@@ -61,7 +61,7 @@ typedef struct when_clause_s {
typedef enum {
BINOP_UNKNOWN,
BINOP_POWER=100, BINOP_MULT, BINOP_DIVIDE, BINOP_MOD, BINOP_MOD1, BINOP_PLUS,
- BINOP_MINUS, BINOP_CONCAT, BINOP_LSHIFT, BINOP_RSHIFT, BINOP_MIN,
+ BINOP_MINUS, BINOP_CONCAT, BINOP_LSHIFT, BINOP_ULSHIFT, BINOP_RSHIFT, BINOP_URSHIFT, BINOP_MIN,
BINOP_MAX, BINOP_EQ, BINOP_NE, BINOP_LT, BINOP_LE, BINOP_GT, BINOP_GE,
BINOP_CMP,
BINOP_AND, BINOP_OR, BINOP_XOR,
diff --git a/compile.c b/compile.c
index e226668f..383a4e03 100644
--- a/compile.c
+++ b/compile.c
@@ -28,6 +28,7 @@ static CORD compile_string(env_t *env, ast_t *ast, CORD color);
static CORD compile_arguments(env_t *env, ast_t *call_ast, arg_t *spec_args, arg_ast_t *call_args);
static CORD compile_maybe_incref(env_t *env, ast_t *ast);
static CORD compile_int_to_type(env_t *env, ast_t *ast, type_t *target);
+static CORD compile_unsigned_type(type_t *t);
static CORD promote_to_optional(type_t *t, CORD code);
static CORD compile_null(type_t *t);
@@ -198,6 +199,19 @@ CORD compile_declaration(type_t *t, CORD name)
}
}
+PUREFUNC CORD compile_unsigned_type(type_t *t)
+{
+ if (t->tag != IntType)
+ errx(1, "Not an int type, so unsigned doesn't make sense!");
+ switch (Match(t, IntType)->bits) {
+ case TYPE_IBITS8: return "uint8_t";
+ case TYPE_IBITS16: return "uint16_t";
+ case TYPE_IBITS32: return "uint32_t";
+ case TYPE_IBITS64: return "uint64_t";
+ default: errx(1, "Invalid integer bit size");
+ }
+}
+
CORD compile_type(type_t *t)
{
if (t == THREAD_TYPE) return "Thread_t";
@@ -1772,7 +1786,7 @@ CORD compile_math_method(env_t *env, binop_e op, ast_t *lhs, ast_t *rhs, type_t
}
break;
}
- case BINOP_LSHIFT: case BINOP_RSHIFT: {
+ case BINOP_LSHIFT: case BINOP_RSHIFT: case BINOP_ULSHIFT: case BINOP_URSHIFT: {
if (rhs_t->tag == IntType || rhs_t->tag == BigIntType) {
binding_t *b = get_namespace_binding(env, lhs, binop_method_names[op]);
if (binding_works(b, lhs_t, rhs_t, lhs_t))
@@ -2051,13 +2065,17 @@ CORD compile(env_t *env, ast_t *ast)
CORD lhs = compile(env, binop->lhs);
// Special case for bit shifting by an integer literal:
- if (binop->op == BINOP_LSHIFT || binop->op == BINOP_RSHIFT) {
+ if (binop->op == BINOP_LSHIFT || binop->op == BINOP_RSHIFT || binop->op == BINOP_ULSHIFT || binop->op == BINOP_URSHIFT) {
if (lhs_t->tag == IntType && rhs_t->tag == BigIntType && binop->rhs->tag == Int) {
CORD shift_amount = compile_int_to_type(env, binop->rhs, lhs_t);
if (binop->op == BINOP_LSHIFT)
return CORD_all("(", lhs, " << ", shift_amount, ")");
- else
+ else if (binop->op == BINOP_ULSHIFT)
+ return CORD_all("(", compile_type(lhs_t), ")((", compile_unsigned_type(lhs_t), ")", lhs, " << ", shift_amount, ")");
+ else if (binop->op == BINOP_RSHIFT)
return CORD_all("(", lhs, " >> ", shift_amount, ")");
+ else if (binop->op == BINOP_URSHIFT)
+ return CORD_all("(", compile_type(lhs_t), ")((", compile_unsigned_type(lhs_t), ")", lhs, " >> ", shift_amount, ")");
}
}
@@ -2120,6 +2138,16 @@ CORD compile(env_t *env, ast_t *ast)
code_err(ast, "Math operations are only supported for values of the same numeric type, not %T vs %T", lhs_t, rhs_t);
return CORD_all("(", lhs, " >> ", rhs, ")");
}
+ case BINOP_ULSHIFT: {
+ if (operand_t->tag != IntType && operand_t->tag != NumType && operand_t->tag != ByteType)
+ code_err(ast, "Math operations are only supported for values of the same numeric type, not %T vs %T", lhs_t, rhs_t);
+ return CORD_all("(", compile_type(operand_t), ")((", compile_unsigned_type(lhs_t), ")", lhs, " << ", rhs, ")");
+ }
+ case BINOP_URSHIFT: {
+ if (operand_t->tag != IntType && operand_t->tag != NumType && operand_t->tag != ByteType)
+ code_err(ast, "Math operations are only supported for values of the same numeric type, not %T vs %T", lhs_t, rhs_t);
+ return CORD_all("(", compile_type(operand_t), ")((", compile_unsigned_type(lhs_t), ")", lhs, " >> ", rhs, ")");
+ }
case BINOP_EQ: {
switch (operand_t->tag) {
case BigIntType:
diff --git a/parse.c b/parse.c
index 13c0a208..1e76f828 100644
--- a/parse.c
+++ b/parse.c
@@ -1704,13 +1704,26 @@ binop_e match_binary_operator(const char **pos)
case '*': *pos += 1; return BINOP_MULT;
case '/': *pos += 1; return BINOP_DIVIDE;
case '^': *pos += 1; return BINOP_POWER;
- case '<':
+ case '<': {
*pos += 1;
if (match(pos, "=")) return BINOP_LE;
else if (match(pos, ">")) return BINOP_CMP;
- else if (match(pos, "<")) return BINOP_LSHIFT;
- else return BINOP_LT;
- case '>': *pos += 1; return match(pos, "=") ? BINOP_GE : (match(pos, ">") ? BINOP_RSHIFT : BINOP_GT);
+ else if (match(pos, "<")) {
+ if (match(pos, "[u]"))
+ return BINOP_ULSHIFT;
+ return BINOP_LSHIFT;
+ } else return BINOP_LT;
+ }
+ case '>': {
+ *pos += 1;
+ if (match(pos, "=")) return BINOP_GE;
+ if (match(pos, ">")) {
+ if (match(pos, "[u]"))
+ return BINOP_URSHIFT;
+ return BINOP_RSHIFT;
+ }
+ return BINOP_GT;
+ }
default: {
if (match(pos, "!=")) return BINOP_NE;
else if (match(pos, "==") && **pos != '=') return BINOP_EQ;
diff --git a/test/integers.tm b/test/integers.tm
index 8d34dfc0..225baab5 100644
--- a/test/integers.tm
+++ b/test/integers.tm
@@ -17,6 +17,16 @@ func main():
>> 1 << 10
= 1024
+ !! Signed and unsigned bit shifting:
+ >> -2[64] << 1
+ = -4[64]
+ >> -2[64] <<[u] 1
+ = -4[64]
+ >> -2[64] >> 1
+ = -1[64]
+ >> -2[64] >>[u] 1
+ = 9223372036854775807[64]
+
>> 3 and 2
= 2
diff --git a/typecheck.c b/typecheck.c
index 7cc081de..e5255959 100644
--- a/typecheck.c
+++ b/typecheck.c
@@ -927,10 +927,8 @@ type_t *get_type(env_t *env, ast_t *ast)
return lhs_t;
break;
}
- case BINOP_LSHIFT: case BINOP_RSHIFT: {
- if (is_int_type(rhs_t) && binding_works(binop_method_names[binop->op], binop->lhs, lhs_t, rhs_t, lhs_t))
- return lhs_t;
- break;
+ case BINOP_LSHIFT: case BINOP_RSHIFT: case BINOP_ULSHIFT: case BINOP_URSHIFT: {
+ return lhs_t;
}
case BINOP_POWER: {
if (is_numeric_type(rhs_t) && binding_works(binop_method_names[binop->op], binop->lhs, lhs_t, rhs_t, lhs_t))