diff --git a/Makefile b/Makefile index b22a556e..dba4a9fd 100755 --- a/Makefile +++ b/Makefile @@ -169,11 +169,13 @@ lib/%$(SO): lib/%.c $(INCLUDES) $(CHIBI) tools/chibi-doc $< > $@ doc/lib/chibi/%.html: lib/chibi/%.module tools/chibi-doc chibi-scheme$(EXE) - $(CHIBI) tools/chibi-doc $< > $@ + $(CHIBI) tools/chibi-doc chibi.$* > $@ -MODULE_DOCS := doc/lib/chibi/match.html +MODULE_DOCS := ast disasm equiv filesystem generic heap-stats io loop \ + match mime modules net pathname process repl scribble stty \ + system test time type-inference uri weak -doc: doc/chibi.html $(MODULE_DOCS) +doc: doc/chibi.html $(MODULE_DOCS:%=doc/lib/chibi/%.html) clean: rm -f *.o *.i *.s *.8 diff --git a/doc/chibi.scrbl b/doc/chibi.scrbl index f2bf69f7..9b8c1b4c 100755 --- a/doc/chibi.scrbl +++ b/doc/chibi.scrbl @@ -963,117 +963,52 @@ SRFIs used in standard Chibi modules Additional non-standard modules are put in the @scheme{(chibi)} module namespace. -@subsection{Internals Interface} +@itemlist[ -The @scheme{(chibi ast)} module provides access to the Abstract Syntax Tree and -other internal data structures not typically needed for everyday programs. +@item{@hyperlink["lib/chibi/ast.html"]{(chibi ast) - Abstract Syntax Tree and other internal data types}} -@subsection{Disassember} +@item{@hyperlink["lib/chibi/disasm.html"]{(chibi disasm) - Disassembler for the virtual machine}} -The @scheme{(chibi disasm)} module provides a disassembler for the virtual -machine. +@item{@hyperlink["lib/chibi/equiv.html"]{(chibi equiv) - A version of @scheme{equal?} which is guaranteed to terminate}} -@subsection{Printed Equivalence} +@item{@hyperlink["lib/chibi/filesystem.html"]{(chibi filesystem) - Interface to the filesystem and file descriptor objects}} -The @scheme{(chibi equiv)} module provides the @scheme{equiv?} procedure, which -is similar to @scheme{equal?} but is guaranteed to terminate. It takes two -arguments and returns true iff they would print the same using SRFI-38 style -read/write syntax. +@item{@hyperlink["lib/chibi/generic.html"]{(chibi generic) - Generic methods for CLOS-style object oriented programming}} -@subsection{Filesystem Interface} +@item{@hyperlink["lib/chibi/heap-stats.html"]{(chibi heap-stats) - Utilities for gathering statistics on the heap}} -The @scheme{(chibi filesystem)} module provides access to the filesystem and -file descriptor objects. +@item{@hyperlink["lib/chibi/io.html"]{(chibi io) - Various I/O extensions and custom ports}} -@subsection{Generic Functions} +@item{@hyperlink["lib/chibi/loop.html"]{(chibi loop) - Fast and extensible loop syntax}} -The @scheme{(chibi generic)} module provides generic methods for CLOS-style -object oriented programming. +@item{@hyperlink["lib/chibi/match.html"]{(chibi match) - Intuitive and widely supported pattern matching syntax}} -@subsection{Heap Introspection} +@item{@hyperlink["lib/chibi/mime.html"]{(chibi mime) - Parse MIME files into SXML}} -The @scheme{(chibi heap-stats)} module provides utilities for gathering -statistics on the heap. +@item{@hyperlink["lib/chibi/modules.html"]{(chibi modules) - Introspection for the module system itself}} -@subsection{Input/Output Extensions} +@item{@hyperlink["lib/chibi/net.html"]{(chibi net) - Simple networking interface}} -The @scheme{(chibi io)} module provides various I/O extensions, including -custom ports. +@item{@hyperlink["lib/chibi/pathname.html"]{(chibi pathname) - Utilities to decompose and manipulate pathnames}} -@subsection{Extensible Loop Syntax} +@item{@hyperlink["lib/chibi/process.html"]{(chibi process) - Interface to spawn processes and handle signals}} -The @scheme{(chibi loop)} module provides a fast and extensible loop syntax. +@item{@hyperlink["lib/chibi/repl.html"]{(chibi repl) - A full-featured Read/Eval/Print Loop}} -@subsection{Pattern-Matching} +@item{@hyperlink["lib/chibi/scribble.html"]{(chibi scribble) - A parser for the scribble syntax used to write this manual}} -The @scheme{(chibi match)} module provides an intuitive and widely supported -pattern matching syntax. +@item{@hyperlink["lib/chibi/stty.html"]{(chibi stty) - A high-level interface to ioctl}} -@subsection{RFC2045 MIME} +@item{@hyperlink["lib/chibi/system.html"]{(chibi system) - Access to the host system and current user information}} -The @scheme{(chibi mime)} module provides utilities for parsing MIME -files into an SXML format. +@item{@hyperlink["lib/chibi/test.html"]{(chibi test) - A simple unit testing framework}} -@subsection{Module Introspection} +@item{@hyperlink["lib/chibi/time.html"]{(chibi time) - An interface to the current system time}} -The @scheme{(chibi modules)} module provides introspection utilities for the -module system itself. +@item{@hyperlink["lib/chibi/type-inference.html"]{(chibi type-inference) - An easy-to-use type inference system}} -@subsection{Networking Interface} +@item{@hyperlink["lib/chibi/uri.html"]{(chibi uri) - Utilities to parse and construct URIs}} -The @scheme{(chibi net)} module provides a simple networking interface. - -@subsection{Pathname Utilities} - -The @scheme{(chibi pathname)} module provides utilities to decompose and -manipulate pathnames. - -@subsection{Processes and Signals} - -The @scheme{(chibi process)} module provides utilities to spawn processes and -send and handle signals between processes. - -@subsection{Read/Eval/Print Loop} - -The @scheme{(chibi repl)} module provides a more full-featured repl than the -chibi-scheme executable. - -@subsection{Scribble Syntax} - -The @scheme{(chibi scribble)} module provides a parser for the scribble syntax -used to write this manual. - -@subsection{Stty Interface} - -The @scheme{(chibi stty)} provides a high-level interface to ioctl. - -@subsection{System Information} - -The @scheme{(chibi system)} module provides access to the host system and -current user information. - -@subsection{Line Editing} - -The @scheme{(chibi term edit-line)} provides an @scheme{edit-line} procedure -for interactive line editing. - -@subsection{Testing} - -The @scheme{(chibi test)} provides a simple unit testing framework. - -@subsection{Times and Dates} - -The @scheme{(chibi time)} provides an interface to the current system time. - -@subsection{Type Inference} - -The @scheme{(chibi type-inference)} is an easy-to-use type inference system. - -@subsection{URI Utilities} - -The @scheme{(chibi uri)} module provides utilities to parse and construct URIs. - -@subsection{Weak References} - -The @scheme{(chibi weak)} module provides data structures with weak references. +@item{@hyperlink["lib/chibi/weak.html"]{(chibi weak) - Data structures with weak references}} +] diff --git a/doc/lib/chibi/match.html b/doc/lib/chibi/match.html index 94b304dc..1a073107 100755 --- a/doc/lib/chibi/match.html +++ b/doc/lib/chibi/match.html @@ -1,10 +1,10 @@ - +(chibi match) -

(module (chibi match) - (export match match-lambda match-lambda* match-let match-letrec match-let*) - (import-immutable (scheme)) - (include "match/match.scm")) -

\ No newline at end of file +

(chibi match)

+This is a full superset of the popular match +package by Andrew Wright, written in fully portable syntax-rules +and thus preserving hygiene. +The most notable extensions are the ability to use non-linear +patterns - patterns in which the same identifier occurs multiple +times, tail patterns after ellipsis, and the experimental tree patterns. +

Patterns

+Patterns are written to look like the printed representation of +the objects they match. The basic usage is +(match expr (pat body ...) ...) +where the result of expr is matched against each pattern in +turn, and the corresponding body is evaluated for the first to +succeed. Thus, a list of three elements matches a list of three +elements. +

(let ((ls (list 1 2 3))) (match ls ((1 2 3) #)))
=> #t

+If no patterns match an error is signalled. +Identifiers will match anything, and make the corresponding +binding available in the body. +

(match (list 1 2 3) ((a b c) b))
=> 2

+If the same identifier occurs multiple times, the first instance +will match anything, but subsequent instances must match a value +which is equal? to the first. +

(match (list 1 2 1) ((a a b) 1) ((a b a) 2))
=> 2

+The special identifier _ matches anything, no matter how +many times it is used, and does not bind the result in the body. +

(match (list 1 2 1) ((_ _ b) 1) ((a b a) 2))
=> 1

+To match a literal identifier (or list or any other literal), use +quote. +

(match 'a ('b 1) ('a 2))
=> 2

+Analogous to its normal usage in scheme, quasiquote can +be used to quote a mostly literally matching object with selected +parts unquoted. +

(match (list 1 2 3) (`(1 ,b ,c) (list b c)))
=> (2 3)

+Often you want to match any number of a repeated pattern. Inside +a list pattern you can append ... after an element to +match zero or more of that pattern (like a regexp Kleene star). +

(match (list 1 2) ((1 2 3 ...) #))
=> #t

+

(match (list 1 2 3) ((1 2 3 ...) #))
=> #t

+

(match (list 1 2 3 3 3) ((1 2 3 ...) #))
=> #t

+Pattern variables matched inside the repeated pattern are bound to +a list of each matching instance in the body. +

(match (list 1 2) ((a b c ...) c))
=> ()

+

(match (list 1 2 3) ((a b c ...) c))
=> (3)

+

(match (list 1 2 3 4 5) ((a b c ...) c))
=> (3 4 5)

+More than one ... may not be used in the same list, since +this would require exponential backtracking in the general case. +However, ... need not be the final element in the list, +and may be succeeded by a fixed number of patterns. +

(match (list 1 2 3 4) ((a b c ... d e) c))
=> ()

+

(match (list 1 2 3 4 5) ((a b c ... d e) c))
=> (3)

+

(match (list 1 2 3 4 5 6 7) ((a b c ... d e) c))
=> (3 4 5)

+___ is provided as an alias for ... when it is +inconvenient to use the ellipsis (as in a syntax-rules template). +The ..1 syntax is exactly like the ... except +that it matches one or more repetitions (like a regexp "+"). +

(match (list 1 2) ((a b c ..1) c))
ERROR: match: "no matching pattern" +

+

(match (list 1 2 3) ((a b c ..1) c))
=> (3)

+The boolean operators and, or and not +can be used to group and negate patterns analogously to their +Scheme counterparts. +The and operator ensures that all subpatterns match. +This operator is often used with the idiom (and x pat) to +bind x to the entire value that matches pat +(c.f. "as-patterns" in ML or Haskell). Another common use is in +conjunction with not patterns to match a general case +with certain exceptions. +

(match 1 ((and) #))
=> #t

+

(match 1 ((and x) x))
=> 1

+

(match 1 ((and x 1) x))
=> 1

+The or operator ensures that at least one subpattern +matches. If the same identifier occurs in different subpatterns, +it is matched independently. All identifiers from all subpatterns +are bound if the or operator matches, but the binding is +only defined for identifiers from the subpattern which matched. +

(match 1 ((or) #) (else #))
=> #f

+

(match 1 ((or x) x))
=> 1

+

(match 1 ((or x 2) x))
=> 1

+The not operator succeeds if the given pattern doesn't +match. None of the identifiers used are available in the body. +

(match 1 ((not 2) #))
=> #t

+The more general operator ? can be used to provide a +predicate. The usage is (? predicate pat ...) where +predicate is a Scheme expression evaluating to a predicate +called on the value to match, and any optional patterns after the +predicate are then matched as in an and pattern. +

(match 1 ((? odd? x) x))
=> 1

+The field operator = is used to extract an arbitrary +field and match against it. It is useful for more complex or +conditional destructuring that can't be more directly expressed in +the pattern syntax. The usage is (= field pat), where +field can be any expression, and should result in a +procedure of one argument, which is applied to the value to match +to generate a new value to match against pat. +Thus the pattern (and (= car x) (= cdr y)) is equivalent +to (x . y), except it will result in an immediate error +if the value isn't a pair. +

(match '(1 . 2) ((= car x) x))
=> 1

+

(match 4 ((= sqrt x) x))
=> 2

+The record operator $ is used as a concise way to match +records defined by SRFI-9 (or SRFI-99). The usage is +($ rtd field ...), where rtd should be the record +type descriptor specified as the first argument to +define-record-type, and each field is a subpattern +matched against the fields of the record in order. Not all fields +must be present. +

(let ()
+  (define-record-type employee
+    (make-employee name title)
+    employee?
+    (name get-name)
+    (title get-title))
+  (match (make-employee "Bob" "Doctor")
+    (($ employee n t) (list t n))))
+
=> ("Doctor" "Bob")

+The set! and get! operators are used to bind an +identifier to the setter and getter of a field, respectively. The +setter is a procedure of one argument, which mutates the field to +that argument. The getter is a procedure of no arguments which +returns the current value of the field. +

(let ((x (cons 1 2))) (match x ((1 . (set! s)) (s 3) x)))
=> (1 . 3)

+

(match '(1 . 2) ((1 . (get! g)) (g)))
=> 2

+The new operator *** can be used to search a tree for +subpatterns. A pattern of the form (x *** y) represents +the subpattern y located somewhere in a tree where the path +from the current object to y can be seen as a list of the +form (x ...). y can immediately match the current +object in which case the path is the empty list. In a sense it's +a 2-dimensional version of the ... pattern. +As a common case the pattern (_ *** y) can be used to +search for y anywhere in a tree, regardless of the path +used. +

(match '(a (a (a b))) ((x *** 'b) x))
=> (a a a)

+

(match '(a (b) (c (d e) (f g))) ((x *** 'g) x))
=> (a c f)

Syntax

+

(match expr (pattern . body) ...)

+(match expr (pattern (=> failure) . body) ...)

+The result of expr is matched against each pattern in +turn, according to the pattern rules described in the previous +section, until the the first pattern matches. When a match is +found, the corresponding bodys are evaluated in order, +and the result of the last expression is returned as the result +of the entire match. If a failure is provided, +then it is bound to a procedure of no arguments which continues, +processing at the next pattern. If no pattern matches, +an error is signalled.

(match-lambda (pattern . body) ...)

Shortcut for lambda + match. Creates a +procedure of one argument, and matches that argument against each +clause.

(match-lambda* (pattern . body) ...)

Similar to match-lambda. Creates a procedure of any +number of arguments, and matches the argument list against each +clause.

(match-let ((var value) ...) . body)

(match-let loop ((var init) ...) . body)

Matches each var to the corresponding expression, and evaluates +the body with all match variables in scope. Raises an error if +any of the expressions fail to match. Syntax analogous to named +let can also be used for recursive functions which match on their +arguments as in match-lambda*.

(match-letrec ((var value) ...) . body)

Similar to match-let, but analogously to letrec +matches and binds the variables with all match variables in scope.

(match-let* ((var value) ...) body ...)

+Similar to match-let, but analogously to let* +matches and binds the variables in sequence, with preceding match +variables in scope.

\ No newline at end of file diff --git a/lib/chibi/modules.module b/lib/chibi/modules.module index fb32bda5..a5cb2e23 100755 --- a/lib/chibi/modules.module +++ b/lib/chibi/modules.module @@ -1,6 +1,6 @@ (module (chibi modules) - (export module-name module-dir module-includes + (export module-name module-dir module-includes module-shared-includes module-ast module-ast-set! module-ref module-contains? analyze-module containing-module load-module module-exports procedure-analysis) diff --git a/lib/chibi/modules.scm b/lib/chibi/modules.scm index 8272e166..f1e6e4c5 100644 --- a/lib/chibi/modules.scm +++ b/lib/chibi/modules.scm @@ -30,19 +30,31 @@ "" (module-name-prefix name)))) +(define (module-metas mod metas) + (let ((mod (if (module? mod) mod (find-module mod)))) + (let lp ((ls (module-meta-data mod)) (res '())) + (cond + ((not (pair? ls)) (reverse res)) + ((and (pair? (car ls)) (memq (caar ls) metas)) + (lp (cdr ls) (append (reverse (cdar ls)) res))) + (else (lp (cdr ls) res)))))) + (define (module-includes mod) (let* ((mod (if (module? mod) mod (find-module mod))) (dir (module-dir mod))) (define (module-file f) (find-module-file (string-append dir f))) - (let lp ((ls (module-meta-data mod)) (res '())) - (cond - ((not (pair? ls)) - (reverse res)) - ((and (pair? (car ls)) (eq? 'include (caar ls))) - (lp (cdr ls) (append (map module-file (reverse (cdar ls))) res))) - (else - (lp (cdr ls) res)))))) + (map module-file (module-metas mod '(include))))) + +(define (module-shared-includes mod) + (let* ((mod (if (module? mod) mod (find-module mod))) + (dir (module-dir mod))) + (define (module-file f) + (find-module-file (string-append dir f ".stub"))) + (let lp ((ls (module-metas mod '(include-shared))) (res '())) + (cond ((null? ls) (reverse res)) + ((module-file (car ls)) => (lambda (x) (lp (cdr ls) (cons x res)))) + (else (lp (cdr ls) res)))))) (define (analyze-module-source name mod recursive?) (let ((env (module-env mod)) diff --git a/lib/chibi/process.module b/lib/chibi/process.module index 851333ff..2263e771 100644 --- a/lib/chibi/process.module +++ b/lib/chibi/process.module @@ -12,7 +12,7 @@ signal/alarm signal/term signal/user1 signal/user2 signal/child signal/continue signal/stop signal/tty-stop signal/tty-input - signal/tty-output) + signal/tty-output wait/no-hang) (import-immutable (scheme)) (cond-expand (threads (import (srfi 18))) (else #f)) (include-shared "process") diff --git a/lib/chibi/process.stub b/lib/chibi/process.stub index 85751a4b..88081140 100644 --- a/lib/chibi/process.stub +++ b/lib/chibi/process.stub @@ -1,9 +1,15 @@ +;;> An interface to spawning processes and sending and +;;> receiving signals between processes. + (c-system-include "sys/types.h") (c-system-include "sys/wait.h") (c-system-include "signal.h") (c-system-include "unistd.h") +;;> The siginfo_t struct is used to return info about the status, +;;> process and user info of a called signal handler. + (define-c-type siginfo_t predicate: signal-info? (int si_signo signal-number) @@ -16,9 +22,6 @@ ;;(clock_t si_stime signal-system-time) ) -(define-c-type sigset_t - predicate: signal-set?) - (define-c-const int (signal/hang-up "SIGHUP")) (define-c-const int (signal/interrupt "SIGINT")) (define-c-const int (signal/quit "SIGQUIT")) @@ -41,8 +44,23 @@ (c-include "signal.c") +;;> @subsubsubsection{@rawcode{(set-signal-action! signal handler)}} + +;;> Sets the signal handler for @var{signal} to @var{handler} +;;> and returns the old handler. @var{handler} should be a procedure +;;> of one argument, the signal number, the value @scheme{#t} for +;;> the default signal handler, or @scheme{#f} for no handler. + +;;> Signal handlers are queued run in a dedicated thread after the +;;> system handler has returned. + (define-c sexp (set-signal-action! "sexp_set_signal_action") - ((value ctx sexp) (value self sexp) sexp sexp)) + ((value ctx sexp) (value self sexp) sexp sexp)) + +;;> The sigset_t struct represents a set of signals for masking. + +(define-c-type sigset_t + predicate: signal-set?) (define-c errno (make-signal-set "sigemptyset") ((pointer result sigset_t))) (define-c errno (signal-set-fill! "sigfillset") ((pointer sigset_t))) @@ -59,15 +77,50 @@ (define-c errno (current-signal-mask "sigprocmask") ((value SIG_BLOCK int) (pointer value NULL sigset_t) (pointer result sigset_t))) +;;> Send a @var{signal/alarm} signal to the current process +;;> after @var{unsigned-int} seconds have elapsed. + (define-c unsigned-int alarm (unsigned-int)) + +;;> Suspend the current process for @var{unsigned-int} seconds. +;;> See SRFI-18 @scheme{thread-sleep!} for a light-weight sleep +;;> for only the current thread. + (define-c unsigned-int sleep (unsigned-int)) +;;> Fork the current process. Returns @rawcode{0} for the newly +;;> created process, and the process id of the new process for +;;> the parent. + (define-c pid_t fork ()) + +(define-c-const int (wait/no-hang "WNOHANG")) + ;;(define-c pid_t wait ((result int))) + +;;> @subsubsubsection{@rawcode{(waitpid pid options)}} + +;;> Wait on the process @var{pid}, or any child process if @var{pid} +;;> is @rawcode{-1}. @var{options} should be 0, or @var{wait/no-hang} +;;> to return immediately if no processes have reported status. Returns +;;> a list whose first element is the actual @var{pid} reporting, and +;;> the second element is the integer status. + (define-c pid_t waitpid (int (result int) int)) + +;;> Send a signal to the given process. + (define-c errno kill (int int)) + ;;(define-c errno raise (int)) + +;;> Exits the current process immediately. Finalizers are not run. + (define-c void exit (int)) + +;;> Replace the current process with the given command. Finalizers +;;> are not run. + (define-c int (execute execvp) (string (array string))) (cond-expand diff --git a/tools/chibi-doc b/tools/chibi-doc index 9c5c5b7b..5a808c69 100755 --- a/tools/chibi-doc +++ b/tools/chibi-doc @@ -160,9 +160,15 @@ (define (expand-section tag) (lambda (sxml env) - (let ((body (map (lambda (x) (expand x env)) (cdr sxml)))) - `(div (a (^ (name . ,(section-name tag (sxml-strip (cons tag body)))))) - (,tag ,@body))))) + (if (null? (cdr sxml)) + (error "section must not be empty" sxml) + (let* ((name (and (eq? 'tag: (cadr sxml)) + (pair? (cddr sxml)) + (sxml-strip (caddr sxml)))) + (body (map (lambda (x) (expand x env)) + (if name (cdddr sxml) (cdr sxml)))) + (name (or name (sxml-strip (cons tag body))))) + `(div (a (^ (name . ,(section-name tag name)))) (,tag ,@body)))))) (define (expand-url sxml env) (if (not (= 2 (length sxml))) @@ -171,11 +177,10 @@ `(a (^ (href . ,url)) ,url)))) (define (expand-hyperlink sxml env) - (if (not (= 3 (length sxml))) - (error "hyperlink expects two arguments" sxml) - (let ((url (expand (cadr sxml) env)) - (text (expand (caddr sxml) env))) - `(a (^ (href . ,url)) ,text)))) + (if (not (>= (length sxml) 3)) + (error "hyperlink expects at least two arguments" sxml) + (let ((url (expand (cadr sxml) env))) + `(a (^ (href . ,url)) ,(map (lambda (x) (expand x env)) (cddr sxml)))))) (define (expand-note sxml env) `(div (^ (id . "notes")) @@ -286,10 +291,10 @@ (style (^ (type . "text/css")) " body {color: #000; background-color: #FFF} -div#menu {font-size: smaller; position: absolute; top: 0; left: 0; width: 180px; height: 100%} -div#menu ol {margin-left: 10px; padding-left: 10px} +div#menu {font-size: smaller; position: absolute; top: 50px; left: 0; width: 180px; height: 100%} div#main {position: absolute; top: 0; left: 200px; width: 520px; height: 100%} div#notes {position: relative; top: 2em; left: 550px; max-width: 200px; height: 0px; font-size: smaller;} +div#footer {padding-bottom: 50px} .result { color: #000; background-color: #FFEADF; width: 100%; padding: 3px} .command { color: #000; background-color: #FFEADF; width: 100%; padding: 5px} " @@ -303,7 +308,8 @@ div#notes {position: relative; top: 2em; left: 550px; max-width: 200px; height: (if (and (pair? x) (eq? 'title (car x))) (cons 'h1 (cdr x)) x)) - x))))) + x) + (div (^ (id . "footer"))))))) (define (fix-paragraphs x) (let lp ((ls x) (p '()) (res '())) @@ -349,13 +355,44 @@ div#notes {position: relative; top: 2em; left: 550px; max-width: 200px; height: (define (get-signature proc source form) (match form (('define (name . args) . body) - (cons name args)) + (list (cons name args))) (('define-syntax name ('syntax-rules () (clause . body) ...)) (map (lambda (x) (cons name (cdr x))) (filter external-clause? clause))) + ((procedure? proc) + (procedure-signature proc)) (else - (or (and (procedure? proc) (procedure-signature proc)) - (procedure-name proc))))) + '()))) + +(define (get-ffi-signatures form) + (match form + (('define-c ret-type (or (name _) name) (args ...)) + (list (cons name (map (lambda (x) (if (pair? x) (last x) x)) args)))) + (('define-c-const type (or (name _) name)) + (list (list 'const: type name))) + (((or 'define-c-struct 'define-c-class 'define-c-type) name . rest) + (let lp ((ls rest) (res '())) + (cond + ((null? ls) + (reverse res)) + ((eq? 'predicate: (car ls)) + (lp (cddr ls) (cons (list (cadr ls) 'obj) res))) + ((eq? 'constructor: (car ls)) + (lp (cddr ls) + (cons (if (pair? (cadr ls)) (cadr ls) (list (cadr ls))) res))) + ((pair? (car ls)) + (lp (cdr ls) + (append (if (pair? (cdddar ls)) + (list (list (car (cdddar ls)) name (caar ls))) + '()) + (list (list (caddar ls) name)) + res))) + ((symbol? (car ls)) + (lp (cddr ls) res)) + (else + (lp (cdr ls) res))))) + (else + #f))) (define section-number (let ((sections '(section subsection subsubsection subsubsubsection))) @@ -380,27 +417,42 @@ div#notes {position: relative; top: 2em; left: 550px; max-width: 200px; height: (symbol->string name)))) (define (insert-signature orig-ls name sig) - (let lp ((ls orig-ls) (rev-pre '())) - (cond - ((or (null? ls) (section>=? (car ls) (section-number 'subsubsubsection))) - `(,@(reverse rev-pre) - ,@(if (and (pair? ls) - (section-describes? - (extract-sxml 'subsubsubsection (car ls)) - name)) - '() - `((subsubsubsection - (rawcode ,@(intersperse (map write-to-string sig) '(br)))))) - ,@ls)) - (else - (lp (cdr ls) (cons (car ls) rev-pre)))))) + (cond + ((not (pair? sig)) + orig-ls) + (else + (let ((name + (or name (if (eq? 'const: (caar sig)) (caddar sig) (caar sig))))) + (let lp ((ls orig-ls) (rev-pre '())) + (cond + ((or (null? ls) + (section>=? (car ls) (section-number 'subsubsubsection))) + `(,@(reverse rev-pre) + ,@(if (and (pair? ls) + (section-describes? + (extract-sxml 'subsubsubsection (car ls)) + name)) + '() + `((subsubsubsection + tag: ,(write-to-string name) + (rawcode + ,@(if (eq? 'const: (caar sig)) + `((i ,(write-to-string (cadar sig)) ": ") + ,(write-to-string (caddar sig))) + (intersperse (map write-to-string sig) '(br))))))) + ,@ls)) + (else + (lp (cdr ls) (cons (car ls) rev-pre))))))))) -(define (extract-docs file defs res) +(define (extract-docs file defs . o) (call-with-input-file file (lambda (in) - (let ((defs (map (lambda (x) `(,(car x) ,(cadr x) ,(cdaddr x))) - (filter (lambda (x) (equal? file (caaddr x))) defs)))) - (let lp ((lines '()) (cur '()) (res res)) + (let ((lang (or (and (pair? o) (car o)) 'scheme)) + (defs (map (lambda (x) `(,(car x) ,(cadr x) ,(cdaddr x))) + (filter (lambda (x) (and (pair? (caddr x)) + (equal? file (caaddr x)))) + defs)))) + (let lp ((lines '()) (cur '()) (res '())) (define (collect) (if (pair? lines) (append @@ -435,6 +487,9 @@ div#notes {position: relative; top: 2em; left: 550px; max-width: 200px; height: (procs (filter (lambda (x) (<= line1 (caddr x) line2)) (filter caddr defs)))) (cond + ((and (eq? lang 'ffi) (get-ffi-signatures x)) + => (lambda (sigs) + (lp '() '() (append (insert-signature cur #f sigs) res)))) ((= 1 (length procs)) (let* ((sig (or (get-signature (caar procs) (cdar procs) x) '())) @@ -573,11 +628,12 @@ div#notes {position: relative; top: 2em; left: 550px; max-width: 200px; height: (filter (lambda (x) (or (procedure? (cdr x)) (macro? (cdr x)))) (map (lambda (x) (cons x (module-ref mod-name x))) exports))))) - (let lp ((includes (module-includes mod)) - (res `((title ,(write-to-string mod-name))))) - (if (null? includes) - (output (reverse res)) - (lp (cdr includes) (extract-docs (car includes) defs res)))))) + (output + `((title ,(write-to-string mod-name)) + ,@(reverse (append-map (lambda (x) (extract-docs x defs)) + (module-includes mod))) + ,@(reverse (append-map (lambda (x) (extract-docs x defs 'ffi)) + (module-shared-includes mod))))))) (define (main args) (case (length args)