(import scheme brev-separate (chicken base) (chicken file) (chicken load) (chicken pathname) (chicken process-context) (chicken string) fmt reader srfi-1 utils) (define +persistent-module-paths+ (list)) ;;; for guarding against multiple imports (sort of mandatory #pragma ;;; once) (define +imported-modules+ (list)) (define (get-modules-public-forms module-list) ;; Module list is a list of symbols ;; How Sex handles modules: ;; For each module in a list, construct path, find module by path in ;; module path directories, extract public definitions from the ;; module, paste them in current one in emulation of C include ;; directives. (fold-right append (list) (map (fn (import-module (symbol->string x))) module-list))) (define (import-module name) (let ((module-path (locate-module name))) (assert module-path (fmt #f "Failed to find module " name " in " (get-module-paths))) (if (member module-path +imported-modules+) (list) (begin (set! +imported-modules+ (cons module-path +imported-modules+)) (read-public-interface module-path))))) (define (get-module-paths) (cons (current-directory) +persistent-module-paths+)) (define (locate-module name) ;; Module locations: relative to file being compiled, or in what was ;; in SEX_MODULE_PATH env var at the start of the process (see ;; load-persistent-module-paths function) (let ((search-paths (get-module-paths))) (let loop ((paths search-paths)) (if (null? paths) #f (or (module-exists? name (car paths)) (loop (cdr paths))))))) (define (module-exists? name module-dir) ;; returns absolute path to module, if it exists (and (directory-exists? module-dir) (let ((module-path (make-absolute-pathname module-dir name "sex"))) (and (file-exists? module-path) (file-readable? module-path) module-path)))) (define (read-public-interface module-path) ;; pub fns are reduced to prototypes, other pub forms are just pasted (let ((raw-forms (read-from-file module-path))) (fold process-public-interface-form (list) raw-forms))) ;;; TODO: use semen facilities to analyze modules (define (process-public-interface-form form acc) (case (car form) ((pub) (case (cadr form) ;; A function is reduced to a prototype and keeps its `pub', so ;; the importing unit declares it with external linkage ((fn) (cons (copy-form-source! form (take form 5)) acc)) ;; A variable becomes an `extern' declaration ((var) (cons (copy-form-source! form (cons 'extern (take (cdr form) 3))) acc)) ((define defmacro enum import include struct typedef union) (cons (copy-form-source! form (cdr form)) acc)) (else (sex-error form "pub must be followed by a definition" form)))) (else acc))) (define (load-persistent-module-paths) (let ((sex-module-path-env-var (get-env-var "SEX_MODULE_PATH"))) (when sex-module-path-env-var (set! +persistent-module-paths+ (map (lambda (p) (make-absolute-pathname p #f #f)) (string-split sex-module-path-env-var ":"))))))