Fix operator precedence and boolification

This commit is contained in:
Bruce Hill 2024-02-11 23:09:00 -05:00
parent 7d6af5f4c7
commit ddd9f6e5d5
4 changed files with 72 additions and 55 deletions

2
ast.c
View File

@ -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

View File

@ -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,

View File

@ -1,5 +1,6 @@
#pragma once
#include <err.h>
#include <gc.h>
#include <gc/cord.h>
#include <stdbool.h>
@ -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

74
parse.c
View File

@ -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) {