diff --git a/fmt-c-writer.scm b/fmt-c-writer.scm index 4174219..6733687 100644 --- a/fmt-c-writer.scm +++ b/fmt-c-writer.scm @@ -114,6 +114,12 @@ forms, and what remains." ((attribute) '%attribute) ((ยค) 'vector-ref) ((include) '%include) + ;; a `|' inside a symbol has to be escaped to be written in + ;; a Scheme source, so we just rename it in fmt-c compatible + ;; way + ((|\||) 'bit-or) + ((|\|\||) '%or) + ((|\|=|) 'bit-or=) ;; uh things we do for c89 compatibility ((bool) 'int) ((true) 1) @@ -190,8 +196,6 @@ forms, and what remains." (let-values (((comments slots _) (take-slots rest 2))) (list '%cast (walk-type (cadr slots)) (walk-expr (car slots))))) (('enum . _) (walk-enum form)) - ;; | is problematic... And c-or/bit-or/etc are actually - ;; procedures, so we have to call the procedure itself (('c-or . rest) (apply c-or (map walk-expr rest))) (('c-bit-or . rest) (apply c-bit-or (map walk-expr rest))) (('c-bit-or= . rest) (apply c-bit-or= (map walk-expr rest))) diff --git a/tests/codegen.scm b/tests/codegen.scm index 44ca91e..e278d3a 100644 --- a/tests/codegen.scm +++ b/tests/codegen.scm @@ -123,4 +123,34 @@ ;; Bodies are a statement sequence, so comments there stay put. (test-assert "a comment in a body stays in the body" (emits? (in-fn "(while (< a b) ;; inside\n (g 1))") - "while (a < b) {")))) + "while (a < b) {"))) + ;; `|', `||' and `|=' read as ordinary symbols -- our own reader has + ;; no |symbol| syntax for them to collide with -- but fmt-c cannot + ;; dispatch on a symbol whose name it cannot write in Scheme source, + ;; so they used to fall through to the function-call path and emit + ;; `|\||(a, b)'. The writer renames them to heads fmt-c spells with + ;; a string. + (test-group "bitwise and logical operators" + (test-assert "bit-or" + (emits? (in-fn "(var x int (| a b))") "int x = a | b")) + (test-assert "logical or" + (emits? (in-fn "(var x int (|| a b))") "int x = a || b")) + (test-assert "or-assign" + (emits? (in-fn "(|= a b)") "a |= b")) + (test-assert "bit-and" + (emits? (in-fn "(var x int (& a b))") "int x = a & b")) + (test-assert "logical and" + (emits? (in-fn "(var x int (&& a b))") "int x = a && b")) + + ;; Precedence too: the operator reaches fmt-c as a string it looks + ;; up, not as a symbol in its table + (test-assert "parenthesised where C needs it" + (emits? (in-fn "(var x int (& (| a b) a))") "(a | b) & a")) + (test-assert "and left alone where it does not" + (emits? (in-fn "(var x int (| a (& a b)))") "int x = a | a & b")) + + ;; The spelling from before they could be written directly + (test-assert "c-or is still accepted" + (emits? (in-fn "(var x int (c-or a b))") "a || b")) + (test-assert "c-bit-or is still accepted" + (emits? (in-fn "(var x int (c-bit-or a b))") "a | b"))))