From ddd9f6e5d542fca3186558e4d6603c4b05967121 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sun, 11 Feb 2024 23:09:00 -0500 Subject: [PATCH] Fix operator precedence and boolification --- ast.c | 2 +- compile.c | 23 +++++++++++------ nextlang.h | 28 +++++++++++++-------- parse.c | 74 +++++++++++++++++++++++++++--------------------------- 4 files changed, 72 insertions(+), 55 deletions(-) diff --git a/ast.c b/ast.c index 17f46f2..6cb1fd6 100644 --- a/ast.c +++ b/ast.c @@ -125,7 +125,7 @@ CORD ast_to_cord(ast_t *ast) T(EnumDef, "(%s, tags=%r, namespace=%r)", data.name, tags_to_cord(data.tags), ast_to_cord(data.namespace)) T(Index, "(indexed=%r, index=%r)", ast_to_cord(data.indexed), ast_to_cord(data.index)) T(FieldAccess, "(fielded=%r, field=%s)", ast_to_cord(data.fielded), data.field) - T(DocTest, "(expr=%r, output=%s)", ast_to_cord(data.expr), data.output) + T(DocTest, "(expr=%r, output=%s)", ast_to_cord(data.expr), data.output ? data.output : "") T(Use, "(%s)", data.path) T(LinkerDirective, "(%s)", data.directive) #undef T diff --git a/compile.c b/compile.c index 96431eb..7c3871c 100644 --- a/compile.c +++ b/compile.c @@ -20,7 +20,7 @@ CORD compile_statement(ast_t *ast) { switch (ast->tag) { case If: case For: case While: case FunctionDef: case Return: case StructDef: case EnumDef: - case Declare: case Assign: case UpdateAssign: + case Declare: case Assign: case UpdateAssign: case DocTest: return compile(ast); default: return CORD_asprintf("(void)%r;", compile(ast)); @@ -61,12 +61,12 @@ CORD compile(ast_t *ast) case BINOP_MINUS: return CORD_asprintf("(%r - %r)", lhs, rhs); case BINOP_LSHIFT: return CORD_asprintf("(%r << %r)", lhs, rhs); case BINOP_RSHIFT: return CORD_asprintf("(%r >> %r)", lhs, rhs); - case BINOP_EQ: return CORD_asprintf("(%r == %r)", lhs, rhs); - case BINOP_NE: return CORD_asprintf("(%r != %r)", lhs, rhs); - case BINOP_LT: return CORD_asprintf("(%r < %r)", lhs, rhs); - case BINOP_LE: return CORD_asprintf("(%r <= %r)", lhs, rhs); - case BINOP_GT: return CORD_asprintf("(%r > %r)", lhs, rhs); - case BINOP_GE: return CORD_asprintf("(%r >= %r)", lhs, rhs); + case BINOP_EQ: return CORD_asprintf("__eq(%r, %r)", lhs, rhs); + case BINOP_NE: return CORD_asprintf("__ne(%r, %r)", lhs, rhs); + case BINOP_LT: return CORD_asprintf("__lt(%r, %r)", lhs, rhs); + case BINOP_LE: return CORD_asprintf("__le(%r, %r)", lhs, rhs); + case BINOP_GT: return CORD_asprintf("__gt(%r, %r)", lhs, rhs); + case BINOP_GE: return CORD_asprintf("__ge(%r, %r)", lhs, rhs); case BINOP_AND: return CORD_asprintf("and(%r, %r)", lhs, rhs); case BINOP_OR: return CORD_asprintf("or(%r, %r)", lhs, rhs); case BINOP_XOR: return CORD_asprintf("xor(%r, %r)", lhs, rhs); @@ -285,6 +285,15 @@ CORD compile(ast_t *ast) code = CORD_cat(code, "} __data;\n};\n"); return code; } + case DocTest: { + auto test = Match(ast, DocTest); + CORD src = heap_strn(test->expr->start, (size_t)(test->expr->end - test->expr->start)); + return CORD_asprintf( + "__test(%r, %r, %r);\n", + compile(WrapAST(test->expr, StringLiteral, .cord=src)), + compile(test->expr), + compile(WrapAST(test->expr, StringLiteral, .cord=test->output))); + } // Index, FieldAccess, // DocTest, // Use, diff --git a/nextlang.h b/nextlang.h index 8f74995..f310384 100644 --- a/nextlang.h +++ b/nextlang.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -52,28 +53,35 @@ .data=memcpy(GC_MALLOC(sizeof(__items)), __items, sizeof(__items)), \ .copy_on_write=1}; }) -#define not(x) _Generic(x, bool: !(x), default: ~(x)) -#define and(x, y) _Generic(x, bool: (x) && (y), default: (x) & (y)) -#define or(x, y) _Generic(x, bool: (x) || (y), default: (x) | (y)) -#define xor(x, y) ((x) ^ (y)) +#define not(x) _Generic(x, bool: (bool)!(x), default: ~(x)) +#define and(x, y) _Generic(x, bool: (bool)((x) && (y)), default: ((x) & (y))) +#define or(x, y) _Generic(x, bool: (bool)((x) || (y)), default: ((x) | (y))) +#define xor(x, y) _Generic(x, bool: (bool)((x) ^ (y)), default: ((x) ^ (y))) #define mod(x, n) ((x) % (n)) #define mod1(x, n) (((x) % (n)) + (__typeof(x))1) #define __cmp(x, y) (_Generic(x, CORD: CORD_cmp(x, y), char*: strcmp(x, y), const char*: strcmp(x, y), default: (x > 0) - (y > 0))) -#define __lt(x, y) (_Generic(x, int8_t: x < y, int16_t: x < y, int32_t: x < y, int64_t: x < y, float: x < y, double: x < y, char: x < y, bool: x < y, \ +#define __lt(x, y) (bool)(_Generic(x, int8_t: x < y, int16_t: x < y, int32_t: x < y, int64_t: x < y, float: x < y, double: x < y, char: x < y, bool: x < y, \ default: __cmp(x, y) < 0)) -#define __le(x, y) (_Generic(x, int8_t: x <= y, int16_t: x <= y, int32_t: x <= y, int64_t: x <= y, float: x <= y, double: x <= y, char: x <= y, bool: x <= y, \ +#define __le(x, y) (bool)(_Generic(x, int8_t: x <= y, int16_t: x <= y, int32_t: x <= y, int64_t: x <= y, float: x <= y, double: x <= y, char: x <= y, bool: x <= y, \ default: __cmp(x, y) <= 0)) -#define __ge(x, y) (_Generic(x, int8_t: x >= y, int16_t: x >= y, int32_t: x >= y, int64_t: x >= y, float: x >= y, double: x >= y, char: x >= y, bool: x >= y, \ +#define __ge(x, y) (bool)(_Generic(x, int8_t: x >= y, int16_t: x >= y, int32_t: x >= y, int64_t: x >= y, float: x >= y, double: x >= y, char: x >= y, bool: x >= y, \ default: __cmp(x, y) >= 0)) -#define __gt(x, y) (_Generic(x, int8_t: x > y, int16_t: x > y, int32_t: x > y, int64_t: x > y, float: x > y, double: x > y, char: x > y, bool: x > y, \ +#define __gt(x, y) (bool)(_Generic(x, int8_t: x > y, int16_t: x > y, int32_t: x > y, int64_t: x > y, float: x > y, double: x > y, char: x > y, bool: x > y, \ default: __cmp(x, y) > 0)) -#define __eq(x, y) (_Generic(x, int8_t: x == y, int16_t: x == y, int32_t: x == y, int64_t: x == y, float: x == y, double: x == y, char: x == y, bool: x == y, \ +#define __eq(x, y) (bool)(_Generic(x, int8_t: x == y, int16_t: x == y, int32_t: x == y, int64_t: x == y, float: x == y, double: x == y, char: x == y, bool: x == y, \ default: __cmp(x, y) == 0)) -#define __ne(x, y) (_Generic(x, int8_t: x != y, int16_t: x != y, int32_t: x != y, int64_t: x != y, float: x != y, double: x != y, char: x != y, bool: x != y, \ +#define __ne(x, y) (bool)(_Generic(x, int8_t: x != y, int16_t: x != y, int32_t: x != y, int64_t: x != y, float: x != y, double: x != y, char: x != y, bool: x != y, \ default: __cmp(x, y) != 0)) #define min(x, y) ({ __declare(__min_lhs, x); __declare(__min_rhs, y); __le(__min_lhs, __min_rhs) ? __min_lhs : __min_rhs; }) #define max(x, y) ({ __declare(__min_lhs, x); __declare(__min_rhs, y); __ge(__min_lhs, __min_rhs) ? __min_lhs : __min_rhs; }) #define say(str) puts(CORD_to_const_char_star(__cord(str))) +#define __test(src, expr, expected) do { \ + CORD __result = __cord(expr); \ + say(CORD_catn(5, USE_COLOR ? "\x1b[33m>>>\x1b[0;1m " : ">>> ", src, USE_COLOR ? "\n\x1b[0;33m===\x1b[0;35m " : "\n=== ", __result, "\x1b[m")); \ + if (expected && CORD_cmp(__result, expected)) { \ + errx(1, "I expected:\n%s but got:\n%s", CORD_to_const_char_star(expected), CORD_to_const_char_star(__result)); \ + } \ + } while (0) // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/parse.c b/parse.c index 6488b2c..37c33eb 100644 --- a/parse.c +++ b/parse.c @@ -30,16 +30,17 @@ extern void builtin_fail(const char *fmt, ...); #define STUB_PARSER(name) PARSER(name) { (void)ctx; (void)pos; return NULL; } int op_tightness[] = { - [BINOP_POWER]=1, - [BINOP_MULT]=2, [BINOP_DIVIDE]=2, [BINOP_MOD]=2, [BINOP_MOD1]=2, - [BINOP_PLUS]=3, [BINOP_MINUS]=3, - [BINOP_CONCAT]=4, + [BINOP_POWER]=9, + [BINOP_MULT]=8, [BINOP_DIVIDE]=8, [BINOP_MOD]=8, [BINOP_MOD1]=8, + [BINOP_PLUS]=7, [BINOP_MINUS]=7, + [BINOP_CONCAT]=6, [BINOP_LSHIFT]=5, [BINOP_RSHIFT]=5, - [BINOP_MIN]=6, [BINOP_MAX]=6, - [BINOP_EQ]=7, [BINOP_NE]=7, - [BINOP_LT]=8, [BINOP_LE]=8, [BINOP_GT]=8, [BINOP_GE]=8, - [BINOP_AND]=9, [BINOP_OR]=9, [BINOP_XOR]=9, + [BINOP_MIN]=4, [BINOP_MAX]=4, + [BINOP_EQ]=3, [BINOP_NE]=3, + [BINOP_LT]=2, [BINOP_LE]=2, [BINOP_GT]=2, [BINOP_GE]=2, + [BINOP_AND]=1, [BINOP_OR]=1, [BINOP_XOR]=1, }; +#define MAX_TIGHTNESS 9 static const char *keywords[] = { "yes", "xor", "while", "use", "then", "struct", "stop", "skip", "return", @@ -1172,39 +1173,38 @@ static ast_t *parse_infix_expr(parse_ctx_t *ctx, const char *pos, int min_tightn if (!lhs) return NULL; spaces(&pos); - binop_e op = match_binary_operator(&pos); - if (op == BINOP_UNKNOWN || op_tightness[op] < min_tightness) - return lhs; + for (binop_e op; (op=match_binary_operator(&pos)) != BINOP_UNKNOWN && op_tightness[op] >= min_tightness; spaces(&pos)) { + ast_t *key = NULL; + if (op == BINOP_MIN || op == BINOP_MAX) { + key = NewAST(ctx->file, pos, pos, Var, .name=op == BINOP_MIN ? "_min_" : "_max_"); + for (bool progress = true; progress; ) { + ast_t *new_term; + progress = (false + || (new_term=parse_index_suffix(ctx, key)) + || (new_term=parse_field_suffix(ctx, key)) + || (new_term=parse_fncall_suffix(ctx, key, NORMAL_FUNCTION)) + ); + if (progress) key = new_term; + } + if (key->tag == Var) key = NULL; + else pos = key->end; - ast_t *key = NULL; - if (op == BINOP_MIN || op == BINOP_MAX) { - key = NewAST(ctx->file, pos, pos, Var, .name=op == BINOP_MIN ? "_min_" : "_max_"); - for (bool progress = true; progress; ) { - ast_t *new_term; - progress = (false - || (new_term=parse_index_suffix(ctx, key)) - || (new_term=parse_field_suffix(ctx, key)) - || (new_term=parse_fncall_suffix(ctx, key, NORMAL_FUNCTION)) - ); - if (progress) key = new_term; } - if (key->tag == Var) key = NULL; - else pos = key->end; + spaces(&pos); + ast_t *rhs = parse_infix_expr(ctx, pos, op_tightness[op] + 1); + if (!rhs) break; + pos = rhs->end; + + if (op == BINOP_MIN) { + return NewAST(ctx->file, lhs->start, rhs->end, Min, .lhs=lhs, .rhs=rhs, .key=key); + } else if (op == BINOP_MAX) { + return NewAST(ctx->file, lhs->start, rhs->end, Max, .lhs=lhs, .rhs=rhs, .key=key); + } else { + lhs = NewAST(ctx->file, lhs->start, rhs->end, BinaryOp, .lhs=lhs, .op=op, .rhs=rhs); + } } - spaces(&pos); - ast_t *rhs = parse_infix_expr(ctx, pos, op_tightness[op]); - if (!rhs) return lhs; - pos = rhs->end; - - switch (op) { - case BINOP_MIN: - return NewAST(ctx->file, lhs->start, rhs->end, Min, .lhs=lhs, .rhs=rhs, .key=key); - case BINOP_MAX: - return NewAST(ctx->file, lhs->start, rhs->end, Max, .lhs=lhs, .rhs=rhs, .key=key); - default: - return NewAST(ctx->file, lhs->start, rhs->end, BinaryOp, .lhs=lhs, .rhs=rhs, .op=op); - } + return lhs; } ast_t *parse_expr(parse_ctx_t *ctx, const char *pos) {