From ee053e35c9498ba1fb9059d9781dbdac7f165ecd Mon Sep 17 00:00:00 2001 From: alex-eg Date: Mon, 21 Sep 2026 00:56:26 +0300 Subject: [PATCH] look past comments in a public form's header --- semen.scm | 19 ++++--------------- sex-modules.scm | 26 ++++++++++++++------------ tests/modules/Makefile | 3 +++ tests/modules/greet.sex | 9 +++++++-- tools/sextest/utils.module.scm | 1 + utils.module.scm | 1 + utils.scm | 15 +++++++++++++++ 7 files changed, 45 insertions(+), 29 deletions(-) diff --git a/semen.scm b/semen.scm index f18113e..1b4ad85 100644 --- a/semen.scm +++ b/semen.scm @@ -204,21 +204,10 @@ acc))) (define (strip-fn-header-comments fn-form) - ;; Remove comment forms from the function header - ;; ([pub|extern] fn name arglist rettype) so the positional accessors - ;; below are not shifted. Comments in the body are left in place as - ;; ordinary statements and preserved into the generated C. - (let ((header-count (fn-header-length fn-form))) - ;; This always rebuilds the list, so the location has to be carried - ;; over explicitly -- otherwise every function loses it - (copy-form-source! - fn-form - (let loop ((form fn-form) (kept 0) (acc (list))) - (cond - ((null? form) (reverse acc)) - ((= kept header-count) (append (reverse acc) form)) - ((comment-form? (car form)) (loop (cdr form) kept acc)) - (else (loop (cdr form) (+ kept 1) (cons (car form) acc)))))))) + ;; ([pub|extern] fn name arglist rettype). Comments in the body are + ;; left in place as ordinary statements and preserved into the + ;; generated C. + (strip-header-comments fn-form (fn-header-length fn-form))) (define (process-fn sex-fn-raw acc) (let-values (((doc sex-fn) diff --git a/sex-modules.scm b/sex-modules.scm index fd07f6e..9939bd8 100644 --- a/sex-modules.scm +++ b/sex-modules.scm @@ -72,18 +72,19 @@ (list) raw-forms))) -(define (public-fn-interface form) +(define (public-fn-interface raw-form) ;; (pub fn name args ret . body) -> prototype, keeping a docstring so ;; the importer can emit it above the declaration - (match form - (('pub 'fn name args ret) - form) - (('pub 'fn name args ret ('comment . _) . rest) - (public-fn-interface `(pub fn ,name ,args ,ret ,@rest))) - (('pub 'fn name args ret (? string? doc) . _) - `(pub fn ,name ,args ,ret ,doc)) - (('pub 'fn name args ret . _) - `(pub fn ,name ,args ,ret)))) + (let ((form (strip-header-comments raw-form 5))) + (match form + (('pub 'fn name args ret) + form) + (('pub 'fn name args ret ('comment . _) . rest) + (public-fn-interface `(pub fn ,name ,args ,ret ,@rest))) + (('pub 'fn name args ret (? string? doc) . _) + `(pub fn ,name ,args ,ret ,doc)) + (('pub 'fn name args ret . _) + `(pub fn ,name ,args ,ret))))) ;;; TODO: use semen facilities to analyze modules (define (process-public-interface-form form acc) @@ -92,8 +93,9 @@ ;; with external linkage (('pub 'fn . _) (cons (copy-form-source! form (public-fn-interface form)) acc)) - (('pub 'var name type . _) - (cons (copy-form-source! form `(extern var ,name ,type)) acc)) + (('pub 'var . _) + (match-let ((('pub 'var name type . _) (strip-header-comments form 4))) + (cons (copy-form-source! form `(extern var ,name ,type)) acc))) (('pub (or 'define 'defmacro 'enum 'import 'include 'struct 'typedef 'union) . _) (cons (copy-form-source! form (cdr form)) acc)) (('pub . _) diff --git a/tests/modules/Makefile b/tests/modules/Makefile index 1821967..bd1e966 100644 --- a/tests/modules/Makefile +++ b/tests/modules/Makefile @@ -11,6 +11,9 @@ # It also checks what only a second translation unit can check: that an # imported type reaches the type database, by expanding a macro that # reads the imported struct's fields. +# +# The public forms carry comments in their headers, which the reduction +# to a prototype and to an extern both have to look past. SEXC ?= ../../sexc diff --git a/tests/modules/greet.sex b/tests/modules/greet.sex index 96e36ff..50c1aa0 100644 --- a/tests/modules/greet.sex +++ b/tests/modules/greet.sex @@ -7,7 +7,11 @@ (include stdio.h) -(pub var greet-count int 0) +(pub var greet-count ;; a comment in the header of a public form is + ;; not part of it: what the importer is given has + ;; to be `extern int greet-count', not a form + ;; counted off by one + int 0) (pub struct greeting "A greeting to print." @@ -25,7 +29,8 @@ (lambda (name field-type) `(printf "%s " ,(symbol->string name)))))) -(pub fn greet ((name (* const char))) void +(pub fn greet ;; ...and here the prototype would lose its return type + ((name (* const char))) void "Print a greeting for NAME." (++ greet-count) (printf "hello, %s\n" name)) diff --git a/tools/sextest/utils.module.scm b/tools/sextest/utils.module.scm index 51c6c26..8f41e2f 100644 --- a/tools/sextest/utils.module.scm +++ b/tools/sextest/utils.module.scm @@ -3,6 +3,7 @@ set-working-directory to-absolute-pathname comment-form? + strip-header-comments list-split list-join recons diff --git a/utils.module.scm b/utils.module.scm index e4a91aa..e9f6678 100644 --- a/utils.module.scm +++ b/utils.module.scm @@ -3,6 +3,7 @@ set-working-directory to-absolute-pathname comment-form? + strip-header-comments list-split list-join recons diff --git a/utils.scm b/utils.scm index d97c043..1dbc60e 100644 --- a/utils.scm +++ b/utils.scm @@ -45,6 +45,21 @@ (define (comment-form? form) (and (pair? form) (eq? (car form) 'comment))) +;;; Remove the comment forms from the first COUNT elements of FORM -- +;;; its header -- so that the positional accessors reading it are not +;;; shifted by one +(define (strip-header-comments form count) + ;; This always rebuilds the list, so the location has to be carried + ;; over explicitly -- otherwise every form loses it + (copy-form-source! + form + (let loop ((rest form) (kept 0) (acc (list))) + (cond + ((null? rest) (reverse acc)) + ((= kept count) (append (reverse acc) rest)) + ((comment-form? (car rest)) (loop (cdr rest) kept acc)) + (else (loop (cdr rest) (+ kept 1) (cons (car rest) acc))))))) + (define (list-split src-list split-elt) ;; split '(1 2 / 3 4 / 5 6) by '/ -> '((1 2) (3 4) (5 6)) (fold (lambda (elt acc)