1
0
forked from alex-eg/sex

5 Commits

Author SHA1 Message Date
Pavel Kulyov
434086ade8 tests: add docstring cases 2026-09-21 22:24:47 +03:00
Pavel Kulyov
1685b0c31f semen: process fn docstrings 2026-09-21 22:24:04 +03:00
Pavel Kulyov
720bff3437 semen: add and use fn core prototype getter 2026-09-20 15:40:29 +03:00
Pavel Kulyov
8588f5a531 semen: extract counting header length into a function 2026-09-20 15:33:09 +03:00
Pavel Kulyov
05929951cb modules: save fn docstring in prototype 2026-09-20 00:39:05 +03:00
3 changed files with 62 additions and 65 deletions

View File

@@ -149,11 +149,11 @@
(and (pair? f) (eq? (car f) 'comment))) (and (pair? f) (eq? (car f) 'comment)))
(define (fn-header-length fn-form) (define (fn-header-length fn-form)
(if (memq (car fn-form) '(pub extern)) 5 4)) (if (memq (first fn-form) '(pub extern)) 5 4))
(define (fn-core form) (define (fn-core form)
;; The (fn name args rettype . body) list, without pub/extern ;; The (fn name args rettype . body) list, without pub/extern
(if (memq (car form) '(pub extern)) (if (memq (first form) '(pub extern))
(cdr form) (cdr form)
form)) form))
@@ -161,46 +161,42 @@
;; If FORMS starts with a string, possibly after comment forms, return ;; If FORMS starts with a string, possibly after comment forms, return
;; that string and FORMS without it. Otherwise #f and FORMS unchanged ;; that string and FORMS without it. Otherwise #f and FORMS unchanged
(let loop ((fs forms) (prefix (list))) (let loop ((fs forms) (prefix (list)))
(cond (match fs
((null? fs) (() (values #f forms))
(values #f forms)) (((and cmt ('comment . _)) . rest)
((comment-form? (car fs)) (loop rest (cons cmt prefix)))
(loop (cdr fs) (cons (car fs) prefix))) (((? string? doc) . rest)
((string? (car fs)) (values doc (append (reverse prefix) rest)))
(values (car fs) (append (reverse prefix) (cdr fs)))) (_ (values #f forms)))))
(else
(values #f forms)))))
(define (extract-fn-docstring fn-form) (define (extract-fn-docstring fn-form)
(let ((n (fn-header-length fn-form))) (let ((lift
(if (< (length fn-form) n) (lambda (proto body)
(values #f fn-form) (let-values (((doc rest) (take-leading-docstring body)))
(let-values (((doc body) (take-leading-docstring (drop fn-form n)))) (if doc
(if doc (values doc (copy-form-source! fn-form (append proto rest)))
(values doc (values #f fn-form))))))
(copy-form-source! fn-form (match fn-form
(append (take fn-form n) body))) (('pub 'fn name args ret . body)
(values #f fn-form)))))) (lift `(pub fn ,name ,args ,ret) body))
(('extern 'fn name args ret . body)
(lift `(extern fn ,name ,args ,ret) body))
(('fn name args ret . body)
(lift `(fn ,name ,args ,ret) body))
(_ (values #f fn-form)))))
(define (extract-aggregate-docstring form) (define (extract-aggregate-docstring form)
;; ([pub] struct|union|enum name "doc" (fields ...) . attrs)
;; A string immediately after the name is the docstring; comments ;; A string immediately after the name is the docstring; comments
;; between name and fields are not skipped, they already confuse the ;; between name and fields are not skipped, they already confuse the
;; writer ;; writer
(let* ((pub? (eq? (car form) 'pub)) (match form
(core (if pub? (cdr form) form))) (('pub (and kind (or 'struct 'union 'enum))
(if (and (pair? (cdr core)) (? symbol? name) (? string? doc) . rest)
(symbol? (cadr core)) (values doc (copy-form-source! form `(pub ,kind ,name ,@rest))))
(pair? (cddr core)) (((and kind (or 'struct 'union 'enum))
(string? (caddr core))) (? symbol? name) (? string? doc) . rest)
(let ((new-core (cons (car core) (values doc (copy-form-source! form `(,kind ,name ,@rest))))
(cons (cadr core) (cdddr core))))) (_ (values #f form))))
(values (caddr core)
(copy-form-source! form
(if pub?
(cons 'pub new-core)
new-core))))
(values #f form))))
(define (with-docstring doc form acc) (define (with-docstring doc form acc)
;; acc is newest-first; FORM is consed last so the final reverse ;; acc is newest-first; FORM is consed last so the final reverse
@@ -299,27 +295,28 @@
(define (sex-fn? form) (define (sex-fn? form)
"The `form` must be toplevel. "The `form` must be toplevel.
Returns #f if the form is not a function, returns the form otherwise" Returns #f if the form is not a function, returns the form otherwise"
(and (non-empty-list? form) (match form
(let ((core (fn-core form))) ((fn . _) form)
(and (pair? core) (eq? (car core) 'fn) form)))) ((pub fn . _) form)
(else #f)))
(define (sex-fn-public? fn-form) (define (sex-fn-public? fn-form)
(eq? (car fn-form) 'pub)) (eq? (first fn-form) 'pub))
(define (sex-fn-name fn-form) (define (sex-fn-name fn-form)
(assert (sex-fn? fn-form) (assert (sex-fn? fn-form)
(fmt #f "Form " fn-form " is not a function")) (fmt #f "Form " fn-form " is not a function"))
(cadr (fn-core fn-form))) (second (fn-core fn-form)))
(define (sex-fn-arglist fn-form) (define (sex-fn-arglist fn-form)
(assert (sex-fn? fn-form) (assert (sex-fn? fn-form)
(fmt #f "Form " fn-form " is not a function")) (fmt #f "Form " fn-form " is not a function"))
(caddr (fn-core fn-form))) (third (fn-core fn-form)))
(define (sex-fn-return-type fn-form) (define (sex-fn-return-type fn-form)
(assert (sex-fn? fn-form) (assert (sex-fn? fn-form)
(fmt #f "Form " fn-form " is not a function")) (fmt #f "Form " fn-form " is not a function"))
(cadddr (fn-core fn-form))) (fourth (fn-core fn-form)))
(define (sex-fn-prototype fn-form) (define (sex-fn-prototype fn-form)
"Returns all except body" "Returns all except body"

View File

@@ -8,6 +8,7 @@
(chicken process-context) (chicken process-context)
(chicken string) (chicken string)
fmt fmt
matchable
reader reader
srfi-1 srfi-1
utils) utils)
@@ -74,31 +75,30 @@
(define (public-fn-interface form) (define (public-fn-interface form)
;; (pub fn name args ret . body) -> prototype, keeping a docstring so ;; (pub fn name args ret . body) -> prototype, keeping a docstring so
;; the importer can emit it above the declaration ;; the importer can emit it above the declaration
(let ((header (take form 5))) (match form
(let loop ((body (drop form 5))) (('pub 'fn name args ret)
(cond form)
((null? body) header) (('pub 'fn name args ret ('comment . _) . rest)
((and (pair? (car body)) (eq? (caar body) 'comment)) (public-fn-interface `(pub fn ,name ,args ,ret ,@rest)))
(loop (cdr body))) (('pub 'fn name args ret (? string? doc) . _)
((string? (car body)) (append header (list (car body)))) `(pub fn ,name ,args ,ret ,doc))
(else header))))) (('pub 'fn name args ret . _)
`(pub fn ,name ,args ,ret))))
;;; TODO: use semen facilities to analyze modules ;;; TODO: use semen facilities to analyze modules
(define (process-public-interface-form form acc) (define (process-public-interface-form form acc)
(case (car form) (match form
((pub) ;; Reduced to a prototype, still `pub', so the importer declares it
(case (cadr form) ;; with external linkage
;; A function is reduced to a prototype and keeps its `pub', so (('pub 'fn . _)
;; the importing unit declares it with external linkage (cons (copy-form-source! form (public-fn-interface form)) acc))
((fn) (('pub 'var name type . _)
(cons (copy-form-source! form (public-fn-interface form)) acc)) (cons (copy-form-source! form `(extern var ,name ,type)) acc))
;; A variable becomes an `extern' declaration (('pub (or 'define 'defmacro 'enum 'import 'include 'struct 'typedef 'union) . _)
((var) (cons (copy-form-source! form (cdr form)) acc))
(cons (copy-form-source! form (cons 'extern (take (cdr form) 3))) acc)) (('pub . _)
((define defmacro enum import include struct typedef union) (sex-error form "pub must be followed by a definition" form))
(cons (copy-form-source! form (cdr form)) acc)) (_ acc)))
(else (sex-error form "pub must be followed by a definition" form))))
(else acc)))
(define (load-persistent-module-paths) (define (load-persistent-module-paths)
(let ((sex-module-path-env-var (let ((sex-module-path-env-var

View File

@@ -115,4 +115,4 @@
(union t-doc-val ((i int) (f float)))) (union t-doc-val ((i int) (f float))))
(semen-process (semen-process
'((union t-doc-val "Either." ((i int) (f float)))))) '((union t-doc-val "Either." ((i int) (f float))))))
) )