Merge pull request #2863 from kugelfuhr/kugelfuhr/fix-2859

Fix broken boolean not operator in ca65
This commit is contained in:
Bob Andrews
2025-11-18 16:39:08 +01:00
committed by GitHub
2 changed files with 48 additions and 42 deletions

View File

@@ -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 (); NextTok ();
break; 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: case TOK_PLUS:
NextTok (); NextTok ();
N = Factor (); N = Factor ();
@@ -1159,7 +1171,7 @@ static ExprNode* Factor (void)
case TOK_LPAREN: case TOK_LPAREN:
NextTok (); NextTok ();
N = Expr0 (); N = Expr1 ();
ConsumeRParen (); ConsumeRParen ();
break; 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) ExprNode* Expression (void)
/* Evaluate an expression, build the expression tree on the heap and return /* Evaluate an expression, build the expression tree on the heap and return
** a pointer to the root of the tree. ** a pointer to the root of the tree.
*/ */
{ {
return Expr0 (); return Expr1 ();
} }

33
test/asm/val/bug2859.s Normal file
View File

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