From cd3016b6b8faa904a48a2d22840db311f3475300 Mon Sep 17 00:00:00 2001 From: alex-eg Date: Tue, 29 Sep 2026 21:38:36 +0300 Subject: [PATCH] =?UTF-8?q?fix=20(=C2=A4=20int=20N)=20expanding=20to=20int?= =?UTF-8?q?=20N=20a[]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now only C type keywords will stay as a part of type, i.e. (¤ unsigned int) -> unsigned int a[] was and is okay --- fmt-c-writer.scm | 23 ++++++++++++++++++++++- tests/codegen.scm | 21 ++++++++++++++++++++- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/fmt-c-writer.scm b/fmt-c-writer.scm index 570ec0d..4ca7c43 100644 --- a/fmt-c-writer.scm +++ b/fmt-c-writer.scm @@ -292,6 +292,27 @@ forms, and what remains." (list) (walk-expr (drop form 3)))))) ; optional init expression +;;; `(¤ int N)' is N of int +;;; `(¤ unsigned int)' is an unsized array of unsigned int +;;; An aggregate is the exception -- `(¤ struct point)' ends in a tag, +;;; which is part of the type and not a bound. + +(define +c-type-words+ + '(void char short int long float double signed unsigned + bool _Bool complex _Complex _Atomic const volatile restrict)) + +(define (array-bound? array-type) + (and (> (length array-type) 1) + (let ((bound (last array-type)) + (preceding (last (drop-right array-type 1)))) + (cond + ((not (symbol? bound)) #t) + ((memq bound +c-type-words+) #f) + ;; a tag always follows its keyword, so `(¤ * struct tt)' ends + ;; in a name belonging to the type + ((memq preceding '(struct union enum)) #f) + (else #t))))) + (define (walk-type form) ;; int -> int ;; (const int) -> const int @@ -302,7 +323,7 @@ forms, and what remains." ;; (fn ((int) (float)) void) -> (%fun void ((int) (float))) (match form (('¤ . array-type) - (if (integer? (last array-type)) + (if (array-bound? array-type) ;; sized array (let* ((type-list (drop-right array-type 1)) (type (maybe-unwrap-type type-list)) diff --git a/tests/codegen.scm b/tests/codegen.scm index e6860b6..4124dbd 100644 --- a/tests/codegen.scm +++ b/tests/codegen.scm @@ -345,4 +345,23 @@ compiles." (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")))) + (emits? (in-fn "(var n int (+ (- a) b))") "-a + b"))) + + ;; An array bound was taken only when it was an integer literal, so a + ;; symbolic one fell into the type: `(¤ int N)' came out `int N a[]'. + (test-group "array bounds" + (test-assert "a symbolic bound" + (emits? "(define N 4) (struct s ((a (¤ int N))))" "int a[N]")) + (test-assert "an expression bound" + (emits? "(define N 4) (struct s ((a (¤ char (* 2 N)))))" "char a[2 * N]")) + (test-assert "an integer bound still works" + (emits? "(struct s ((a (¤ int 4))))" "int a[4]")) + ;; a multi-word type is keywords all the way down, so a trailing + ;; keyword belongs to the type and leaves the array unsized + (test-assert "a multi-word type is not a bound" + (emits? "(struct s ((a (¤ unsigned int))))" "unsigned int a[]")) + ;; ...and a tag always follows its keyword + (test-assert "nor is an aggregate tag" + (emits? "(struct t ((z int))) (struct s ((a (¤ struct t))))" "struct t a[]")) + (test-assert "nor one behind a pointer" + (emits? "(struct t ((z int))) (struct s ((a (¤ * struct t))))" "struct t* a[]"))))