From c4cd57533139e92485e9f97a439d9630a4153953 Mon Sep 17 00:00:00 2001 From: Kugel Fuhr <98353208+kugelfuhr@users.noreply.github.com> Date: Tue, 18 Nov 2025 14:35:29 +0100 Subject: [PATCH 1/2] Fix parsing boolean not (.not/!). --- src/ca65/expr.c | 57 +++++++++++++------------------------------------ 1 file changed, 15 insertions(+), 42 deletions(-) diff --git a/src/ca65/expr.c b/src/ca65/expr.c index 1391eddbd..cb77d0e1d 100644 --- a/src/ca65/expr.c +++ b/src/ca65/expr.c @@ -146,7 +146,7 @@ static void FreeExprNode (ExprNode* E) -static ExprNode* Expr0 (void); +static ExprNode* Expr1 (void); @@ -1106,6 +1106,18 @@ static ExprNode* Factor (void) NextTok (); break; + case TOK_BOOLNOT: + NextTok (); + L = Expr1 (); + if (IsEasyConst (L, &Val)) { + FreeExpr (L); + N = GenLiteralExpr (!Val); + } else { + N = NewExprNode (EXPR_BOOLNOT); + N->Left = L; + } + break; + case TOK_PLUS: NextTok (); N = Factor (); @@ -1159,7 +1171,7 @@ static ExprNode* Factor (void) case TOK_LPAREN: NextTok (); - N = Expr0 (); + N = Expr1 (); ConsumeRParen (); break; @@ -1652,51 +1664,12 @@ static ExprNode* Expr1 (void) -static ExprNode* Expr0 (void) -/* Boolean operators: NOT */ -{ - ExprNode* Root; - - /* Handle booleans */ - if (CurTok.Tok == TOK_BOOLNOT) { - - long Val; - ExprNode* Left; - - /* Skip the operator token */ - NextTok (); - - /* Read the argument */ - Left = Expr0 (); - - /* If the argument is const, evaluate it directly */ - if (IsEasyConst (Left, &Val)) { - FreeExpr (Left); - Root = GenLiteralExpr (!Val); - } else { - Root = NewExprNode (EXPR_BOOLNOT); - Root->Left = Left; - } - - } else { - - /* Read left hand side */ - Root = Expr1 (); - - } - - /* Return the expression tree we've created */ - return Root; -} - - - ExprNode* Expression (void) /* Evaluate an expression, build the expression tree on the heap and return ** a pointer to the root of the tree. */ { - return Expr0 (); + return Expr1 (); } From 1924e4cc6340a2f2fdcf4153986ad7b04d9e39d3 Mon Sep 17 00:00:00 2001 From: Kugel Fuhr <98353208+kugelfuhr@users.noreply.github.com> Date: Tue, 18 Nov 2025 14:36:16 +0100 Subject: [PATCH 2/2] Added a test for #2859. --- test/asm/val/bug2859.s | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 test/asm/val/bug2859.s diff --git a/test/asm/val/bug2859.s b/test/asm/val/bug2859.s new file mode 100644 index 000000000..cb26191be --- /dev/null +++ b/test/asm/val/bug2859.s @@ -0,0 +1,33 @@ +; Tests for the bug reported in #2859. Boolean not had the correct precedence +; (as specified in the docs) but worked only correctly at the start of a full +; expression. + +; This one assembles ok since ! is at the start +.if !.defined(__DOCART__) && .defined(__C64__) +.byte 1 +.endif + +; This one assembles too since a parenthesis restarts a full expression +.if .defined(__C64__) && (!.defined(__DOCART__)) +.byte 2 +.endif + +; This one doesn't work since ! is somewhere in between +.if .defined(__C64__) && !.defined(__DOCART__) +.byte 3 +.endif + + + .import _exit + .export _main + + +; The following code is an indirect test for the precedence of .not. +; .not has the lowest precedence, so the expression that is loaded into A +; evaluates to zero. This has of course to be changed when the precedence +; of .not is changed. +_main: + lda #.not 0 + 1 ; Means: .not (0 + 1) + ldx #0 + jmp _exit +