diff --git a/sex-fmt-c.scm b/sex-fmt-c.scm index 8d78d58..fd05daf 100644 --- a/sex-fmt-c.scm +++ b/sex-fmt-c.scm @@ -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 diff --git a/tests/codegen.scm b/tests/codegen.scm index d103dd4..e6860b6 100644 --- a/tests/codegen.scm +++ b/tests/codegen.scm @@ -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"))))