chicken-hackers
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Chicken-hackers] [PATCH] Add `include-relative` form to "chicken" modul


From: Evan Hanson
Subject: [Chicken-hackers] [PATCH] Add `include-relative` form to "chicken" module
Date: Tue, 9 Aug 2016 23:55:39 +1200

Adds a new include form that searches for files relative to the
including file rather than the CWD. In all other ways it behaves like
the normal `include`.

Because `load-relative` and `include-relative` are so similar, this
change also moves some pathname handling code into a group of shared
procedures in eval.scm and uses them for both.

Also slightly simplifies the filename handling code in `load` and fixes
a segfault in the following program caused by the use of
`##sys#current-load-path` as a string when it's false:

  (load (open-input-string "(load-relative \"foo.scm\")"))
---
 NEWS                                         |  2 +
 batch-driver.scm                             | 12 ++--
 chicken-syntax.scm                           |  9 ++-
 core.scm                                     | 13 +++--
 csc.scm                                      |  4 +-
 eval.scm                                     | 87 +++++++++++++++-------------
 expand.scm                                   |  5 +-
 manual/Non-standard macros and special forms | 13 ++++-
 tests/runtests.bat                           | 20 +++++++
 tests/runtests.sh                            | 15 +++++
 10 files changed, 122 insertions(+), 58 deletions(-)

diff --git a/NEWS b/NEWS
index d4eb49d..27e6be5 100644
--- a/NEWS
+++ b/NEWS
@@ -38,6 +38,8 @@
     a single bidirectional port.
   - New `input-port-open?` and `output-port-open?` procedures have been
     added for testing whether a port is open in a specific direction.
+  - An `include-relative` form has been added to the chicken module.
+    This works like `load-relative` but for textual inclusion.
 
 - Module system
   - The compiler has been modularised, for improved namespacing.  This
diff --git a/batch-driver.scm b/batch-driver.scm
index 3848f3b..e33e38c 100644
--- a/batch-driver.scm
+++ b/batch-driver.scm
@@ -437,7 +437,10 @@
     (let ([extends (collect-options 'extend)])
       (dribble "Loading compiler extensions...")
       (for-each
-       (lambda (f) (load (##sys#resolve-include-filename f #f #t))) 
+       (lambda (e)
+        (let ((f (##sys#resolve-include-filename e #f #t #f)))
+          (when (not f) (quit-compiling "cannot load extension: ~a" e))
+          (load f)))
        extends) )
     (set! ##sys#features (delete #:compiler-extension ##sys#features))
     (set! ##sys#features (cons '#:compiling ##sys#features))
@@ -561,7 +564,9 @@
           (print-expr "source" '|1| forms)
           (begin-time)
           ;; Canonicalize s-expressions
-          (let* ((exps0 (map canonicalize-expression
+          (let* ((exps0 (map (lambda (x)
+                               (fluid-let ((##sys#current-source-filename 
filename))
+                                 (canonicalize-expression x)))
                              (let ((forms (append initforms forms)))
                                (if (not module-name)
                                    forms
@@ -647,8 +652,7 @@
                   (for-each
                    (lambda (id)
                      (and-let* ((ifile (##sys#resolve-include-filename
-                                        (symbol->string id) '(".inline") #t))
-                                ((file-exists? ifile)))
+                                        (symbol->string id) '(".inline") #t 
#f)))
                        (dribble "Loading inline file ~a ..." ifile)
                        (load-inline-file ifile)))
                    mreq))
diff --git a/chicken-syntax.scm b/chicken-syntax.scm
index b30e820..1ec885c 100644
--- a/chicken-syntax.scm
+++ b/chicken-syntax.scm
@@ -169,7 +169,14 @@
  (##sys#er-transformer
   (lambda (form r c)
     (##sys#check-syntax 'include form '(_ string))
-    `(##core#include ,(cadr form)))))
+    `(##core#include ,(cadr form) #f))))
+
+(##sys#extend-macro-environment
+ 'include-relative '()
+ (##sys#er-transformer
+  (lambda (form r c)
+    (##sys#check-syntax 'include-relative form '(_ string))
+    `(##core#include ,(cadr form) ,##sys#current-source-filename))))
 
 (##sys#extend-macro-environment
  'assert '()
diff --git a/core.scm b/core.scm
index 806d7cf..6ba3107 100644
--- a/core.scm
+++ b/core.scm
@@ -111,7 +111,7 @@
 ; (##core#lambda ({<variable>}+ [. <variable>]) <body>)
 ; (##core#set! <variable> <exp>)
 ; (##core#begin <exp> ...)
-; (##core#include <string>)
+; (##core#include <string> <string> | #f)
 ; (##core#loop-lambda <llist> <body>)
 ; (##core#undefined)
 ; (##core#primitive <name>)
@@ -902,11 +902,12 @@
                                 bs) ) ) ) )
 
                       ((##core#include)
-                       (walk
-                        `(##core#begin
-                          ,@(fluid-let ((##sys#default-read-info-hook 
read-info-hook))
-                              (##sys#include-forms-from-file (cadr x))))
-                        e se dest ldest h ln))
+                       (fluid-let ((##sys#default-read-info-hook 
read-info-hook))
+                         (##sys#include-forms-from-file
+                          (cadr x)
+                          (caddr x)
+                          (lambda (forms)
+                            (walk `(##core#begin ,@forms) e se dest ldest h 
ln)))))
 
                       ((##core#let-module-alias)
                        (##sys#with-module-aliases
diff --git a/csc.scm b/csc.scm
index d12dba2..ea59f06 100644
--- a/csc.scm
+++ b/csc.scm
@@ -314,8 +314,8 @@
 (define (find-object-files name)
 
   (define (locate-object-file filename repo)
-    (let ((f (##sys#resolve-include-filename filename '() repo)))
-      (and (file-exists? f) (list f))))
+    (and-let* ((f (##sys#resolve-include-filename filename '() repo #f)))
+      (list f)))
 
   (define (static-extension-information name)
     (and-let* ((info  (extension-information name))
diff --git a/eval.scm b/eval.scm
index fba6d5d..7ee369a 100644
--- a/eval.scm
+++ b/eval.scm
@@ -626,10 +626,11 @@
                           e #f tf cntr se))
 
                         ((##core#include)
-                         (compile
-                          `(##core#begin
-                            ,@(##sys#include-forms-from-file (cadr x)))
-                          e #f tf cntr se))
+                         (##sys#include-forms-from-file
+                          (cadr x)
+                          (caddr x)
+                          (lambda (forms)
+                            (compile `(##core#begin ,@forms) e #f tf cntr 
se))))
 
                         ((##core#let-module-alias)
                          (##sys#with-module-aliases
@@ -919,12 +920,30 @@
                          (fx+ argc 1) ) ] ) ) ) ) )
 
 
+;;; Pathname helpers:
+
+(define path-separators
+  (if ##sys#windows-platform '(#\\ #\/) '(#\/)))
+
+(define (path-separator-index/right s)
+  (let loop ((i (fx- (##sys#size s) 1)))
+    (if (memq (##core#inline "C_subchar" s i) path-separators)
+       i
+       (and (fx< 0 i) (loop (fx- i 1))))))
+
+(define (make-relative-pathname from file)
+  (let ((i (and (string? from)
+               (positive? (##sys#size file)) ; XXX probably an error?
+               (not (memq (##core#inline "C_subchar" file 0) path-separators))
+               (path-separator-index/right from))))
+    (if (not i) file (string-append (##sys#substring from 0 i) "/" file))))
+
+
 ;;; Loading source/object files:
 
 (define load-verbose (make-parameter (##sys#fudge 13)))
 
-(define ##sys#current-source-filename #f)
-(define ##sys#current-load-path "")
+(define ##sys#current-load-filename #f)
 (define ##sys#dload-disabled #f)
 
 (define-foreign-variable _dlerror c-string "C_dlerror")
@@ -967,17 +986,10 @@
       (define evalproc
        (or evaluator eval))
 
-      (define (has-slash? str)
-       (let loop ((i (fx- (##sys#size str) 1)))
-         (if (memq (##core#inline "C_subchar" str i) '(#\\ #\/))
-             i
-             (and (fx< 0 i)
-                  (loop (fx- i 1))))))
-
       ;; dload doesn't consider filenames without slashes to be paths,
       ;; so we prepend a dot to force a relative pathname.
       (define (dload-path path)
-       (if (has-slash? path)
+       (if (path-separator-index/right path)
            path
            (##sys#string-append "./" path)))
 
@@ -996,17 +1008,12 @@
              ((not (string? input))
               (##sys#signal-hook #:type-error 'load "bad argument type - not a 
port or string" input))
              ((##sys#file-exists? input #t #f 'load) input)
+             ((let ((f (##sys#string-append input 
##sys#load-dynamic-extension)))
+                (and dload? (##sys#file-exists? f #t #f 'load) f)))
+             ((let ((f (##sys#string-append input source-file-extension)))
+                (and (##sys#file-exists? f #t #f 'load) f)))
              (else
-              (let ((fname2 (##sys#string-append input 
##sys#load-dynamic-extension)))
-                (if (and dload? (##sys#file-exists? fname2 #t #f 'load))
-                    fname2
-                    (let ((fname3 (##sys#string-append input 
source-file-extension)))
-                      (if (##sys#file-exists? fname3 #t #f 'load)
-                          fname3
-                          input)))))))
-
-      (when (and (string? input) (not fname))
-       (##sys#signal-hook #:file-error 'load "cannot open file" input))
+              (##sys#signal-hook #:file-error 'load "cannot open file" 
input))))
 
       (when (and (load-verbose) fname)
        (display "; loading ")
@@ -1018,11 +1025,8 @@
          (call-with-current-continuation
           (lambda (abrt)
             (fluid-let ((##sys#read-error-with-line-number #t)
-                        (##sys#current-source-filename fname)
-                        (##sys#current-load-path
-                         (and fname
-                              (let ((i (has-slash? fname)))
-                                (if i (##sys#substring fname 0 (fx+ i 1)) 
"")))))
+                        (##sys#current-load-filename fname)
+                        (##sys#current-source-filename fname))
               (let ((in (if fname (open-input-file fname) input)))
                 (##sys#dynamic-wind
                  (lambda () #f)
@@ -1060,9 +1064,7 @@
 
 (define (load-relative filename . evaluator)
   (load/internal
-   (if (memq (string-ref filename 0) '(#\\ #\/))
-       filename
-       (##sys#string-append ##sys#current-load-path filename))
+   (make-relative-pathname ##sys#current-load-filename filename)
    (optional evaluator #f)))
 
 (define (load-noisily filename #!key (evaluator #f) (time #f) (printer #f))
@@ -1128,16 +1130,19 @@
   (let ((with-input-from-file with-input-from-file)
        (read read)
        (reverse reverse))
-    (lambda (fname)
-      (let ((path (##sys#resolve-include-filename fname #t #f)))
-       (when (load-verbose) (print "; including " path " ..."))
+    (lambda (filename source k)
+      (let ((path (##sys#resolve-include-filename filename #t #f source)))
+       (when (not path)
+         (##sys#signal-hook #:file-error 'include "cannot open file" filename))
+       (when (load-verbose)
+         (print "; including " path " ..."))
        (with-input-from-file path
          (lambda ()
            (fluid-let ((##sys#current-source-filename path))
              (do ((x (read) (read))
-                  (xs '() (cons x xs)) )
-                 ((eof-object? x) 
-                  (reverse xs))) ) ) ) ) ) ) )
+                  (xs '() (cons x xs)))
+                 ((eof-object? x)
+                  (k (reverse xs)))))))))))
 
 
 ;;; Extensions:
@@ -1390,7 +1395,7 @@
   (let ((string-append string-append) )
     (define (exists? fname)
       (##sys#file-exists? fname #t #f #f))
-    (lambda (fname exts repo)
+    (lambda (fname exts repo source)
       (define (test-extensions fname lst)
        (if (null? lst)
            (and (exists? fname) fname)
@@ -1408,7 +1413,7 @@
                (list ##sys#load-dynamic-extension source-file-extension))
               (else                   ; prefer source
                (list source-file-extension ##sys#load-dynamic-extension)))))
-      (or (test fname)
+      (or (test (make-relative-pathname source fname))
          (let loop ((paths (if repo
                                (##sys#append 
                                 ##sys#include-pathnames 
@@ -1417,7 +1422,7 @@
                                       (list (##sys#repository-path))
                                       '())))
                                ##sys#include-pathnames) ) )
-           (cond ((eq? paths '()) fname)
+           (cond ((eq? paths '()) #f)
                  ((test (string-append (##sys#slot paths 0)
                                        "/"
                                        fname) ) )
diff --git a/expand.scm b/expand.scm
index d96477d..29ef3fa 100644
--- a/expand.scm
+++ b/expand.scm
@@ -74,6 +74,9 @@
 (define-inline (putp sym prop val)
   (##core#inline_allocate ("C_a_i_putprop" 8) sym prop val))
 
+;;; Source file tracking
+
+(define ##sys#current-source-filename #f)
 
 ;;; Syntactic environments
 
@@ -1521,7 +1524,7 @@
                     (if (and (pair? body) 
                              (null? (cdr body))
                              (string? (car body)))
-                        `((##core#include ,(car body)))
+                        `((##core#include ,(car body) 
,##sys#current-source-filename))
                         body))))))))))
 
 (##sys#extend-macro-environment
diff --git a/manual/Non-standard macros and special forms b/manual/Non-standard 
macros and special forms
index 3f45734..78a56f5 100644
--- a/manual/Non-standard macros and special forms      
+++ b/manual/Non-standard macros and special forms      
@@ -612,9 +612,16 @@ The following table should make this clearer:
 
 Include toplevel-expressions from the given source file in the currently
 compiled/interpreted program.  If the included file has the extension
-{{.scm}}, then it may be omitted.  The file is searched in the
-current directory and, if not found, in all directories specified in the
-{{-include-path}} option.
+{{.scm}}, then it may be omitted. The file is searched for in the
+current directory and all directories specified by the {{-include-path}}
+option.
+
+==== include-relative
+
+<macro>(include-relative STRING)</macro>
+
+Works like {{include}}, but the filename is searched for relative to the
+including file rather than the current directory.
 
 ==== nth-value
 
diff --git a/tests/runtests.bat b/tests/runtests.bat
index 07ec3b6..8e488d0 100644
--- a/tests/runtests.bat
+++ b/tests/runtests.bat
@@ -511,6 +511,26 @@ if errorlevel 1 exit /b 1
 echo ======================================== syntax-rules stress test ...
 %interpret% -bnq syntax-rule-stress-test.scm
 
+echo "======================================== include test ..."
+mkdir a\b
+echo > a\b\ok.scm
+echo '(include "a/b/ok.scm")' > a\b\include.scm
+%compile% -analyze-only a\b\include.scm
+if errorlevel 1 exit /b 1
+echo '(include "b/ok.scm")' > a\b\include.scm
+%compile% -analyze-only a\b\include.scm -include-path a
+if errorlevel 1 exit /b 1
+echo '(include-relative "ok.scm")' > a\b\include.scm
+%compile% -analyze-only a\b\include.scm
+if errorlevel 1 exit /b 1
+echo '(include-relative "b/ok.scm")' > a\include.scm
+%compile% -analyze-only a\include.scm
+if errorlevel 1 exit /b 1
+echo '(include-relative "b/ok.scm")' > a\b\include.scm
+%compile% -analyze-only a\b\include.scm -include-path a
+if errorlevel 1 exit /b 1
+del /f /s /q a
+
 echo "======================================== executable tests ..."
 %compile% executable-tests.scm
 if errorlevel 1 exit /b 1
diff --git a/tests/runtests.sh b/tests/runtests.sh
index 72cb56e..279f70f 100755
--- a/tests/runtests.sh
+++ b/tests/runtests.sh
@@ -435,6 +435,21 @@ $compile locative-stress-test.scm
 echo "======================================== syntax-rules stress test ..."
 time $interpret -bnq syntax-rule-stress-test.scm
 
+echo "======================================== include test ..."
+mkdir -p a/b
+echo > a/b/ok.scm
+echo '(include "a/b/ok.scm")' > a/b/include.scm
+$compile -analyze-only a/b/include.scm
+echo '(include "b/ok.scm")' > a/b/include.scm
+$compile -analyze-only a/b/include.scm -include-path a
+echo '(include-relative "ok.scm")' > a/b/include.scm
+$compile -analyze-only a/b/include.scm
+echo '(include-relative "b/ok.scm")' > a/include.scm
+$compile -analyze-only a/include.scm
+echo '(include-relative "b/ok.scm")' > a/b/include.scm
+$compile -analyze-only a/b/include.scm -include-path a
+rm -r a
+
 echo "======================================== executable tests ..."
 $compile executable-tests.scm
 ./a.out "$TEST_DIR/a.out"
-- 
2.1.4




reply via email to

[Prev in Thread] Current Thread [Next in Thread]