fix (. (* p) x) expanding to *(p).x

Was an fmt-c-writer bug: unary-op must parenthize itself, and not it's
arg
This commit is contained in:
2026-09-29 21:37:18 +03:00
parent 8a3f51e833
commit d3f05cecae
2 changed files with 37 additions and 2 deletions

View File

@@ -1015,9 +1015,24 @@
(cat nl (make-space (+ 2 (fmt-col st))) str " "))
st))))))))))
;; `-' arrives as the symbol binary minus uses, and so with binary
;; precedence: `(+ (- b) b)' asked under that spelling comes out
;; `(-b) + b'.
(define (unary-operator op)
(case op
((-) 'unary-)
((+) 'unary+)
((*) 'unary-*)
((&) 'unary-&)
(else op)))
;; Parenthesises the whole expression rather than the operand: the
;; other way round, `(. (* p) x)' came out `*(p).x', which C reads as
;; `*(p.x)'.
(define (c-unary-op op x)
(c-wrap-stmt
(cat (display-to-string op) (c-maybe-paren op (c-expr x)))))
(c-maybe-paren (unary-operator op)
(cat (display-to-string op) (c-expr x)))))
;; some convenience definitions

View File

@@ -325,4 +325,24 @@ compiles."
(test-assert "a closure passed as an argument stays a value"
(emits? "(fn g ((c (closure ((int)) int))) int (return 0))
(fn f ((c (closure ((int)) int))) int (return (g c)))"
"g(c)"))))
"g(c)")))
;; A unary expression parenthesised its operand rather than itself, so
;; the parens landed inside: `*(p).x', which C reads as `*(p.x)'.
(test-group "unary operand precedence"
(test-assert "member access through a dereference"
(emits? "(struct pt ((x int))) (fn f ((p (* (struct pt)))) int (return (. (* p) x)))"
"(*p).x"))
(test-assert "and not with the parens inside"
(not (emits? "(struct pt ((x int))) (fn f ((p (* (struct pt)))) int (return (. (* p) x)))"
"*(p).x")))
(test-assert "member access through a cast"
(emits? (in-fn "(var n int (. (* (cast a (* (struct s)))) f))")
"(*(struct s*)a).f"))
;; ...without gaining parens where none are due
(test-assert "a bare dereference is left alone"
(emits? (in-fn "(var p (* int) 0) (= a (* p))") "a = *p"))
(test-assert "so is address-of in an argument"
(emits? (in-fn "(g (& a))") "g(&a)"))
(test-assert "and negation beside a binary operator"
(emits? (in-fn "(var n int (+ (- a) b))") "-a + b"))))