diff options
Diffstat (limited to 'src/interp/util.lisp')
-rw-r--r-- | src/interp/util.lisp | 1118 |
1 files changed, 1118 insertions, 0 deletions
diff --git a/src/interp/util.lisp b/src/interp/util.lisp new file mode 100644 index 00000000..730b1df5 --- /dev/null +++ b/src/interp/util.lisp @@ -0,0 +1,1118 @@ +;; Copyright (c) 1991-2002, The Numerical ALgorithms Group Ltd. +;; All rights reserved. +;; Copyright (C) 2007, Gabriel Dos Reis. +;; All rights reserved. +;; +;; Redistribution and use in source and binary forms, with or without +;; modification, are permitted provided that the following conditions are +;; met: +;; +;; - Redistributions of source code must retain the above copyright +;; notice, this list of conditions and the following disclaimer. +;; +;; - Redistributions in binary form must reproduce the above copyright +;; notice, this list of conditions and the following disclaimer in +;; the documentation and/or other materials provided with the +;; distribution. +;; +;; - Neither the name of The Numerical ALgorithms Group Ltd. nor the +;; names of its contributors may be used to endorse or promote products +;; derived from this software without specific prior written permission. +;; +;; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +;; IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +;; TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +;; PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +;; OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +;; EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +;; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +;; PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +;; LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +;; This file is a collection of utility functions that are useful +;; for system level work. A couple of the functions, `build-depsys' +;; and `build-interpsys' interface to the src/interp/Makefile. + +;; A second group of related functions allows us to rebuild portions +;; of the system from the command prompt. This varies from rebuilding +;; individual files to whole directories. The most complex functions +;; like `makespad' can rebuild the whole algebra tree. + +;; A third group of related functions are used to set up the +;; `autoload' mechanism. These enable whole subsystems to +;; be kept out of memory until they are used. + +;; A fourth group of related functions are used to construct and +;; search Emacs TAGS files. + +;; A fifth group of related functions are some translated boot +;; functions we need to define here so they work and are available +;; at load time. + + + +(IMPORT-MODULE "vmlisp") +(import-module "parsing") + +(in-package "BOOT") +(export '($directory-list $current-directory reroot + make-absolute-filename |$msgDatabaseName| |$defaultMsgDatabaseName|)) + +(defun our-write-date (file) (and #+kcl (probe-file file) + (file-write-date file))) + +(defun make-directory (direc) + (setq direc (namestring direc)) + (if (string= direc "") (|systemRootDirectory|) + (if (or (memq :unix *features*) + (memq 'unix *features*)) + (progn + (if (char/= (char direc 0) #\/) + (setq direc (concat (|systemRootDirectory|) "/" direc))) + (if (char/= (char direc (1- (length direc))) #\/) + (setq direc (concat direc "/"))) + direc) + (progn ;; Assume Windows conventions + (if (not (or (char= (char direc 0) #\/) + (char= (char direc 0) #\\) + (find #\: direc))) + (setq direc (concat (|systemRootDirectory|) "\\" direc))) + (if (not (or (char= (char direc (1- (length direc))) #\/) + (char= (char direc (1- (length direc))) #\\ ))) + (setq direc (concat direc "\\"))) + direc)))) + +(defun interp-make-directory (direc) + (setq direc (namestring direc)) + (if (string= direc "") $current-directory + (if (or (memq :unix *features*) + (memq 'unix *features*)) + (progn + (if (char/= (char $current-directory (1-(length $current-directory))) #\/) + (setq $current-directory (concat $current-directory "/"))) + (if (char/= (char direc 0) #\/) + (setq direc (concat $current-directory direc))) + (if (char/= (char direc (1- (length direc))) #\/) + (setq direc (concat direc "/"))) + direc) + (progn ;; Assume Windows conventions + (if (not (or (char= (char $current-directory (1- (length $current-directory))) #\/) + (char= (char $current-directory (1- (length $current-directory))) #\\ ))) + (setq $current-directory (concat $current-directory "\\"))) + (if (not (or (char= (char direc 0) #\/) + (char= (char direc 0) #\\) + (find #\: direc))) + (setq direc (concat $current-directory direc))) + (if (not (or (char= (char direc (1- (length direc))) #\/) + (char= (char direc (1- (length direc))) #\\ ))) + (setq direc (concat direc "\\"))) + direc)))) + +;; Various lisps use different ``extensions'' on the filename to indicate +;; that a file has been compiled. We set this variable correctly depending +;; on the system we are using. +(defvar *bin-path* + #+kcl "o" + #+lucid "bbin" + #+symbolics "bin" + #+cmulisp "fasl" + #+:ccl "not done this way at all") + +(defun load-directory (dir) + (let* ((direc (make-directory dir)) + (pattern (make-pathname :directory (pathname-directory direc) + :name :wild :type *bin-path*)) + (files (directory pattern))) + (mapcar #'load files))) + +(defun compspadfiles (filelist ;; should be a file containing files to compile + &optional (*default-pathname-defaults* + (pathname (concat (|systemRootDirectory|) + "nalgebra/")))) + (with-open-file (stream filelist) + (do ((fname (read-line stream nil nil) (read-line stream nil nil))) + ((null fname) 'done) + (setq fname (string-right-trim " *" fname)) + (when (not (equal (elt fname 0) #\*)) + (spad fname (concat (pathname-name fname) ".out")))))) + +(defun recompile-all-algebra-files (dir) ;; a desperation measure + (let* ((direc (make-directory dir)) + (pattern (make-pathname :directory (pathname-directory direc) + :name :wild :type "spad")) + (files (directory pattern)) + (*default-pathname-defaults* (pathname direc))) + (mapcar + #'(lambda (fname) (spad fname (concat (pathname-name fname) ".out"))) + files))) + +(defun fe (function file &optional (compflag nil) &aux (fn (pathname-name file))) + (let ((tbootfile (concat "/tmp/" fn ".boot")) + (tlispfile (concat "/tmp/" fn ".lisp"))) + (system::run-aix-program "fc" + :arguments (list (string function) + (namestring + (merge-pathnames file + (concat (|systemRootDirectory|) + "nboot/.boot")))) + :if-output-exists :supersede :output tbootfile) + (boot tbootfile tlispfile) + (if compflag (progn (compile-file tlispfile) + (load (make-pathname :type *bin-path* :defaults tlispfile))) + (load tlispfile)))) +(defun fc (function file) (fe function file t)) + +;; This function will compile any lisp code that has changed in a directory. +(defun recompile-directory (dir) + (let* ((direc (make-directory dir)) + (pattern (make-pathname :directory (pathname-directory direc) + :name :wild :type "lisp")) + (files (directory pattern))) + (mapcan #'recompile-file-if-necessary files))) + +;; This is a helper function that checks the time stamp between +;; the given file and its compiled binary. If the file has changed +;; since it was last compiled this function will recompile it. +(defun recompile-file-if-necessary (lfile) + (let* ((bfile (make-pathname :type *bin-path* :defaults lfile)) + (bdate (our-write-date bfile)) + (ldate (our-write-date lfile))) + (if (and bdate ldate (> bdate ldate)) nil + (progn + (format t "compiling ~a~%" lfile) + (compile-file lfile) + (list bfile))))) + +;; Force recompilation of all lisp files in a directory. +(defun recompile-all-files (dir) + (let* ((direc (make-directory dir)) + (pattern (make-pathname :directory (pathname-directory direc) + :name :wild :type "lisp")) + (files (directory pattern))) + (mapcar #'compile-file files))) + + +;; Recompile library lisp code if necessary. +(defun recompile-lib-directory (dir) + (let* ((direc (make-directory dir)) + (pattern (make-pathname :directory (pathname-directory direc) + :name :wild :type "NRLIB")) + (files (directory pattern))) + (mapcan #'recompile-NRLIB-if-necessary files))) + +(defun recompile-all-libs (dir) + (let* ((direc (make-directory dir)) + (pattern (make-pathname :directory (pathname-directory direc) + :name :wild :type "NRLIB")) + (files (directory pattern))) + (mapcar + #'(lambda (lib) (compile-lib-file (concat (namestring lib) "/code.lsp"))) + files))) + +;; Recompile a single library's lisp file if it is out of date. +;; The {\bf recompile-lib-file-if-necessary} is defined in nlib.lisp. +(defun recompile-NRLIB-if-necessary (lib) + (recompile-lib-file-if-necessary (concat (namestring lib) "/code.lsp")) + (lift-NRLIB-name (namestring lib))) + + +;; We used to use FOO.NRLIB/code.o files for algebra. However there +;; was no need for this additional level of indirection since the rest +;; of the information in an NRLIB is now kept in the daase files. Thus +;; we lift the FOO.NRLIB/code.o to FOO.o in the final system. +(defun lift-NRLIB-name (f) + (obey (concat "cp " f "/code.o " (subseq f 0 (position #\. f)) ".o")) + nil) + +;; Translate a directory of boot code to common lisp if the boot code +;; is newer. +(defun retranslate-directory (dir) + (let* ((direc (make-directory dir)) + (pattern (make-pathname :directory (pathname-directory direc) + :name :wild :type "boot")) + (files (directory pattern))) + (mapcan #'retranslate-file-if-necessary files))) + + +;; Retranslate a single boot file if it has been changed. +(defun retranslate-file-if-necessary (bootfile) + (let* ((lfile (make-pathname :type "lisp" :defaults bootfile)) + (ldate (our-write-date lfile)) + (binfile (make-pathname :type *bin-path* :defaults bootfile)) + (bindate (our-write-date binfile)) + (bootdate (our-write-date bootfile))) + (if (and ldate bootdate (> ldate bootdate)) nil + (if (and bindate bootdate (> bindate bootdate)) nil + (progn (format t "translating ~a~%" bootfile) + (boot bootfile lfile) (list bootfile)))))) + + +;; TAGS are useful for finding functions if you run Emacs. We have a +;; set of functions that construct TAGS files for Axiom. +(defun make-tags-file () +#+:gcl (system:chdir "/tmp") +#-:gcl (obey (concatenate 'string "cd " "/tmp")) + (obey (concat "etags " (make-absolute-filename "../../src/interp/*.lisp"))) + (spadtags-from-directory "../../src/interp" "boot") + (obey "cat /tmp/boot.TAGS >> /tmp/TAGS")) + +(defun spadtags-from-directory (dir type) + (let* ((direc (make-directory dir)) + (pattern (make-pathname :directory (pathname-directory direc) + :name :wild :type type)) + (files (directory pattern))) + (with-open-file + (tagstream (concatenate 'string "/tmp/" type ".TAGS") :direction :output + :if-exists :supersede :if-does-not-exist :create) + (dolist (file files (namestring tagstream)) + (print (list "processing:" file)) + (write-char #\page tagstream) + (terpri tagstream) + (write-string (namestring file) tagstream) + (write-char #\, tagstream) + (princ (spadtags-from-file file) tagstream) + (terpri tagstream) + (with-open-file (stream "/tmp/*TAGS") + (do ((line (read-line stream nil nil) + (read-line stream nil nil))) + ((null line) nil) + (write-line line tagstream))))))) + +(defun spadtags-from-file (spadfile) + (with-open-file (tagstream "/tmp/*TAGS" :direction :output + :if-exists :supersede :if-does-not-exist :create) + (with-open-file (stream spadfile) + (do ((char-count 0 (file-position stream)) + (line (read-line stream nil nil) (read-line stream nil nil)) + (line-count 1 (1+ line-count))) + ((null line) (file-length tagstream)) + (if (/= (length line) 0) + (let ((firstchar (elt line 0)) (end nil) + (len (length line))) + (cond ((member firstchar '(#\space #\{ #\} #\tab ) + :test #'char= ) "skip") + ((string= line ")abb" :end1 (min 4 len)) + (setq end (position #\space line :from-end t + :test-not #'eql) + end (and end (position #\space line :from-end t + :end end))) + (write-tag-line line tagstream end + line-count char-count)) + ((char= firstchar #\)) "skip") + ((and (> len 1) (string= line "--" :end1 2)) "skip") + ((and (> len 1) (string= line "++" :end1 2)) "skip") + ((search "==>" line) "skip") + ((and (setq end (position #\space line) + end (or (position #\( line :end end) end) + end (or (position #\: line :end end) end) + end (or (position #\[ line :end end) end)) + (equal end 0)) "skip") + ((position #\] line :end end) "skip") + ((string= line "SETANDFILEQ" :end1 end) "skip") + ((string= line "EVALANDFILEACTQ" :end1 end) "skip") + (t (write-tag-line line tagstream + (if (numberp end) (+ end 1) end) + line-count char-count)) ))))))) + +(defun write-tag-line (line tagstream endcol line-count char-count) + (write-string line tagstream :end endcol) + (write-char #\rubout tagstream) + (princ line-count tagstream) + (write-char #\, tagstream) + (princ char-count tagstream) + (terpri tagstream)) + +(defun blankcharp (c) (char= c #\Space)) + +(defun findtag (tag &optional (tagfile (concat (|systemRootDirectory|) "/../../src/interp/TAGS")) ) + ;; tag is an identifier + (with-open-file (tagstream tagfile) + (do ((tagline (read-line tagstream nil nil) + (read-line tagstream nil nil)) + (*package* (symbol-package tag)) + (sourcefile) + (stringtag (string tag)) + (pos) + (tpos) + (type)) + ((null tagline) ()) + (cond ((char= (char tagline 0) #\Page) + (setq tagline (read-line tagstream nil nil)) + (setq sourcefile (subseq tagline 0 + (position #\, tagline))) + (setq type (pathname-type sourcefile))) + ((string= type "lisp") + (if (match-lisp-tag tag tagline) + (return (cons sourcefile tagline)))) + ((> (mismatch ")abb" tagline) 3) + (setq pos (position #\Space tagline :start 3)) + (setq pos (position-if-not #'blankcharp tagline + :start pos)) + (setq pos (position #\Space tagline :start pos)) + (setq pos (position-if-not #'blankcharp tagline + :start pos)) + (setq tpos (mismatch stringtag tagline :start2 pos)) + (if (and (= tpos (length (string tag))) + (member (char tagline (+ pos tpos)) '(#\Space #\Rubout))) + (return (cons sourcefile tagline)))) + ((setq pos (mismatch stringtag tagline)) + (if (and (= pos (length stringtag)) + (> (length tagline) pos) + (member (char tagline pos) + '( #\Space #\( #\:) )) + (return (cons sourcefile tagline)))))))) + +(defun match-lisp-tag (tag tagline &optional (prefix nil) + &aux (stringtag (string tag)) pos tpos) + (when (and (if prefix + (= (mismatch prefix tagline :test #'char-equal) + (length prefix)) + t) + (numberp (setq pos (position #\Space tagline))) + (numberp (setq pos (position-if-not #'blankcharp tagline + :start pos)))) + (if (char= (char tagline pos) #\') (incf pos)) + (if (member (char tagline pos) '( #\\ #\|)) + (setq tpos (1+ pos)) + (setq tpos pos)) + (and (= (mismatch stringtag tagline :start2 tpos :test #'char-equal) + (length stringtag)) + (eq tag (read-from-string tagline nil nil :start pos))) )) + +;; Translate a single boot file to common lisp, compile it +;; and load it. +(defun compile-boot-file (file) + "compile and load a boot file" + (boot (concat file ".boot") (concat file ".lisp")) +#+:AKCL + (compile-file (concat file ".lisp")) +#+:AKCL + (load (concat file "." *bin-path*)) +#+:CCL + (load (concat file ".lisp")) +) + + +;; Translate a single boot file to common lisp +(defun translate (file) ;; translates a single boot file +#+:CCL + (setq *package* (find-package "BOOT")) +#+:AKCL + (in-package "BOOT") + (let (*print-level* *print-length* (fn (pathname-name file)) + (bootfile (merge-pathnames file (concat (|systemRootDirectory|) "nboot/.boot")))) + (declare (special *print-level* *print-length*)) + (boot bootfile (make-pathname :type "lisp" :defaults bootfile)))) + + +;; Translate a list of boot files to common lisp. +(defun translist (fns) + (mapcar #'(lambda (f) (format t "translating ~a~%" (concat f ".boot")) + (translate f)) + fns)) + + +;; The relative directory list specifies a search path for files +;; for the current directory structure. It has been changed from the +;; NAG distribution back to the original form. +(defvar $relative-directory-list + '("/../../src/input/" + "/share/msgs/" + "/../../src/algebra/" + "/../../src/interp/" ; for boot and lisp files (helps fd) + "/doc/spadhelp/" )) + +;; The relative directory list specifies how to find the algebra +;; directory from the current {\bf AXIOM} shell variable. +(defvar $relative-library-directory-list '("/algebra/")) + +(in-package "OLD-BOOT") + +(defun boot (file) ;; translates a single boot file +#+:CCL + (setq *package* (find-package "BOOT")) +#+:AKCL + (in-package "BOOT") + (let (*print-level* + *print-length* + (fn (pathname-name file)) + (*print-pretty* t)) + (declare (special *print-level* *print-length*)) + (boot::boot + file + (merge-pathnames (make-pathname :type "clisp") file)))) + + +(in-package "BOOT") + +;; This is a little used subsystem to generate {\bf ALDOR} code +;; from {\bf Spad} code. Frankly, I'd be amazed if it worked. +(setq translate-functions '( +;; .spad to .as translator, in particular +;; loadtranslate + |spad2AsTranslatorAutoloadOnceTrigger| + )) + +;; This is part of the {\bf ALDOR subsystem}. These will be loaded +;; if you compile a {\bf .as} file rather than a {\bf .spad} file. +;; {\bf ALDOR} is an external compiler that gets automatically called +;; if the file extension is {\bf .as}. +(setq asauto-functions '( + loadas +;; |as| ;; now in as.boot +;; |astran| ;; now in as.boot + |spad2AxTranslatorAutoloadOnceTrigger| + |sourceFilesToAxcliqueAxFile| + |sourceFilesToAxFile| + |setExtendedDomains| + |makeAxFile| + |makeAxcliqueAxFile| + |nrlibsToAxFile| + |attributesToAxFile| )) + +;; These are some {\bf debugging} functions that I use. I can't imagine +;; why you might autoload them but they don't need to be in a running +;; system. +(setq debug-functions '( + loaddebug + |showSummary| + |showPredicates| + |showAttributes| + |showFrom| + |showImp|)) + +;; The {\bf ANNA} subsystem, invoked thru {\bf hypertex}, is an +;; expert system that understands the Numerical Algorithms Group (NAG) +;; fortran library. +(setq anna-functions '( + |annaInt| + |annaMInt| + |annaOde| + |annaOpt| + |annaOpt2| + |annaPDESolve| + |annaOptDefaultSolve1| + |annaOptDefaultSolve2| + |annaOptDefaultSolve3| + |annaOptDefaultSolve4| + |annaOptDefaultSolve5| + |annaOpt2DefaultSolve| + |annaFoo| + |annaBar| + |annaJoe| + |annaSue| + |annaAnn| + |annaBab| + |annaFnar| + |annaDan| + |annaBlah| + |annaTub| + |annaRats| + |annaMInt| + |annaOdeDefaultSolve1| + |annaOdeDefaultSolve2|)) + +;; The Numerical Algorithms Group (NAG) fortran library has a set +;; of cover functions. These functions need to be loaded if you use +;; the NAG library. +(setq nagbr-functions '( + loadnag + |c02aff| |c02agf| + |c05adf| |c05nbf| |c05pbf| + |c06eaf| |c06ebf| |c06ecf| |c06ekf| |c06fpf| |c06fqf| |c06frf| + |c06fuf| |c06gbf| |c06gcf| |c06gqf| |c06gsf| + |d01ajf| |d01akf| |d01alf| |d01amf| |d01anf| |d01apf| |d01aqf| + |d01asf| |d01bbf| |d01fcf| |d01gaf| |d01gbf| + |d02bbf| |d02bhf| |d02cjf| |d02ejf| |d02gaf| |d02gbf| |d02kef| + |d02raf| + |d03edf| |d03eef| |d03faf| + |e01baf| |e01bef| |e01bff| |e01bgf| |e01bhf| |e01daf| |e01saf| + |e01sbf| |e01sef| + |e02adf| |e02aef| |e02agf| |e02ahf| |e02ajf| |e02akf| |e02baf| + |e02bbf| |e02bcf| |e02bdf| |e02bef| |e02daf| |e02dcf| + |e02ddf| |e02def| |e02dff| |e02gaf| |e02zaf| + |e04dgf| |e04fdf| |e04gcf| |e04jaf| |e04mbf| |e04naf| |e04ucf| + |e04ycf| + |f01brf| |f01bsf| |f01maf| |f01mcf| |f01qcf| |f01qdf| |f01qef| + |f01rcf| |f01rdf| |f01ref| + |f02aaf| |f02abf| |f02adf| |f02aef| |f02aff| |f02agf| |f02ajf| + |f02akf| |f02awf| |f02axf| |f02bbf| |f02bjf| |f02fjf| + |f02wef| |f02xef| + |f04adf| |f04arf| |f04asf| |f04atf| |f04axf| |f04faf| |f04jgf| + |f04maf| |f04mbf| |f04mcf| |f04qaf| + |f07adf| |f07aef| |f07fdf| |f07fef| + |s01eaf| |s13aaf| |s13acf| |s13adf| |s14aaf| |s14abf| |s14baf| + |s15adf| |s15aef| |s17acf| |s17adf| |s17aef| |s17aff| + |s17agf| |s17ahf| |s17ajf| |s17akf| |s17dcf| |s17def| + |s17dgf| |s17dhf| |s17dlf| |s18acf| |s18adf| |s18aef| + |s18aff| |s18dcf| |s18def| |s19aaf| |s19abf| |s19acf| + |s19adf| |s20acf| |s20adf| |s21baf| |s21bbf| |s21bcf| + |s21bdf| + )) + + +;; This function is called by {\bf build-interpsys}. It takes two lists. +;; The first is a list of functions that need to be used as +;; ``autoload triggers''. The second is a list of files to load if one +;; of the trigger functions is called. At system build time each of the +;; functions in the first list is set up to load every file in the second +;; list. In this way we will automatically load a whole subsystem if we +;; touch any function in that subsystem. We call a helper function +;; called {\bf setBootAutoLoadProperty} to set up the autoload trigger. +;; This helper function is listed below. +(defun |setBootAutloadProperties| (fun-list file-list) +#+:AKCL + (mapc #'(lambda (fun) (|setBootAutoLoadProperty| fun file-list)) fun-list) +#+:CCL + (mapc #'(lambda (fun) (lisp::set-autoload fun file-list)) fun-list) +) + + +;; This function knows where the {\bf autoload} subdirectory lives. +;; It is called by {\bf mkBootAutoLoad} above to find the necessary +;; files. +(defun boot-load (file) + (let ((name (concat (|systemRootDirectory|) + "/autoload/" + (pathname-name file)))) + (if |$printLoadMsgs| + (format t " Loading ~A.~%" name)) + (load name))) + +;; This is a helper function to set up the autoload trigger. It sets +;; the function cell of each symbol to {\bf mkBootAutoLoad} which is +;; listed below. +(defun |setBootAutoLoadProperty| (func file-list) + (setf (symbol-function func) (|mkBootAutoLoad| func file-list)) ) + +;; This is how the autoload magic happens. Every function named in the +;; autoload lists is actually just another name for this function. When +;; the named function is called we call {\bf boot-load} on all of the +;; files in the subsystem. This overwrites all of the autoload triggers. +;; We then look up the new (real) function definition and call it again +;; with the real arguments. Thus the subsystem loads and the original +;; call succeeds. +(defun |mkBootAutoLoad| (fn file-list) + (function (lambda (&rest args) + (mapc #'boot-load file-list) + (unless (string= (subseq (string fn) 0 4) "LOAD") + (apply (symbol-function fn) args))))) + +;############################################################################ +;# autoload dependencies +;# +;# if you are adding a file which is to be autoloaded the following step +;# information is useful: +;# there are 2 cases: +;# 1) adding files to currently autoloaded parts +;# (as of 2/92: browser old parser and old compiler) +;# 2) adding new files +;# case 1: +;# a) you have to add the file to the list of files currently there +;# (e.g. see BROBJS above) +;# b) add an autolaod rule +;# (e.g. ${AUTO}/parsing.${O}: ${OUT}/parsing.${O}) +;# c) edit util.lisp to add the 'external' function (those that +;# should trigger the autoload +;# case 2: +;# build-interpsys (in util.lisp) needs an extra argument for the +;# new autoload things and several functions in util.lisp need hacking. +;############################################################################ + +;; The `build-interpsys' function takes a list of files to load +;; into the image (`load-files'). It also takes several lists of files, +;; one for each subsystem which will be autoloaded. Autoloading is explained +;; below. This function is called in the src/interp/Makefile. + +;; This function calls `reroot' to set up pathnames we need. Next +;; it sets up the lisp system memory (at present only for AKCL/GCL). Next +;; it loads all of the named files, resets a few global state variables, +;; loads the databases, sets up autoload triggers and clears out hash tables. +;; After this function is called the image is clean and can be saved. + +(defun build-interpsys (load-files + translate-files nagbr-files asauto-files) + (reroot) + #+:AKCL + (init-memory-config :cons 500 :fixnum 200 :symbol 500 :package 8 + :array 400 :string 500 :cfun 100 :cpages 1000 + :rpages 1000 :hole 2000) + #+:AKCL + (setq compiler::*suppress-compiler-notes* t) + (mapcar #'|AxiomCore|::|importModule| load-files) + (|resetWorkspaceVariables|) + (|initHist|) + (|initNewWorld|) + (compressopen) + (interpopen) + (create-initializers) + (|start| :fin) +#+:CCL + (resethashtables) + (setq *load-verbose* nil) + (|setBootAutloadProperties| translate-functions translate-files) + (|setNAGBootAutloadProperties| nagbr-functions nagbr-files) + (|setBootAutloadProperties| asauto-functions asauto-files) + (setf (symbol-function 'boot::|addConsDB|) #'identity) + (resethashtables) ; the databases into core, then close the streams + ) + + +;; This is a further refinement of the autoload scheme. Since the +;; Numerical Algorithms Group (NAG) fortran library contains many +;; functions we subdivide the NAG library subsystem into chapters. +;; We use a different helper function {\bf get-NAG-chapter} to decide +;; which files to load. +(defun |setNAGBootAutloadProperties| (function-list file-list) + (mapcar + #'(lambda (f) + (|setBootAutloadProperties| + (get-NAG-chapter (chapter-name f) function-list) + (nag-files f file-list))) + file-list)) + +;; This function is used to find the names of the files to load. +;; On solaris 9 under GCL the original implementation will fail because +;; the max number of arguments is 63. We rewrite it to get around this +;; problem. +(defun get-NAG-chapter (chapter function-list) + (let ((l (length chapter)) r) + (dolist (f function-list) + (when (equalp chapter (subseq (string f) 0 l)) + (push f r))) + (nreverse r))) + + +;; We analyze the function names to decide which chapter we are in. +;; We load files based on the chapter. +(defun nag-files (filename filelist) + (apply 'append (mapcar + #'(lambda (f) + (cond ((equalp (chapter-name filename) (chapter-name f)) (list f))) ) + filelist))) + +;; The library names follow a convention that allows us to extract +;; the chapter name. +(defun chapter-name (f) +#+:AKCL + (apply + #'(lambda (s) + (cond ((equalp (aref s 0) #\s) "s") (T (reverse (subseq s 0 3))))) + (list (string-left-trim "a.o" (reverse f) )) ) +#+:CCL + (subseq (string-downcase (string f)) 4 (length (string f))) +) + + +;; The `depsys' image is one of the two images we build from +;; the src/interp subdirectory (the other is `interpsys'). We +;; use `depsys' as a compile-time image as it contains all of +;; the necessary functions and macros to compile any file. The +;; `depsys' image is almost the same as an `interpsys' +;; image but it does not have any autoload triggers or databases +;; loaded. + +(defun build-depsys (load-files) +#+:CCL + (setq *package* (find-package "BOOT")) +#+:AKCL + (in-package "BOOT") + (mapcar #'load load-files) + (reroot) + #+:AKCL + (init-memory-config :cons 1000 :fixnum 400 :symbol 1000 :package 16 + :array 800 :string 1000 :cfun 200 :cpages 2000 + :rpages 2000 :hole 4000) ) +;; (init-memory-config :cons 500 :fixnum 200 :symbol 500 :package 8 +;; :array 400 :string 500 :cfun 100 :cpages 1000 +;; :rpages 1000 :hole 2000) ) + + +(DEFUN |string2BootTree| (S) + (init-boot/spad-reader) + (LET* ((BOOT-LINE-STACK (LIST (CONS 1 S))) + ($BOOT T) + ($SPAD NIL) + (XTOKENREADER 'GET-BOOT-TOKEN) + (LINE-HANDLER 'NEXT-BOOT-LINE) + (PARSEOUT (PROGN (|PARSE-Expression|) (POP-STACK-1)))) + (DECLARE (SPECIAL BOOT-LINE-STACK $BOOT $SPAD XTOKENREADER LINE-HANDLER)) + (DEF-RENAME (|new2OldLisp| PARSEOUT)))) + +(DEFUN |string2SpadTree| (LINE) + (DECLARE (SPECIAL LINE)) + (if (and (> (LENGTH LINE) 0) (EQ (CHAR LINE 0) #\) )) + (|processSynonyms|)) + (ioclear) + (LET* ((BOOT-LINE-STACK (LIST (CONS 1 LINE))) + ($BOOT NIL) + ($SPAD T) + (XTOKENREADER 'GET-BOOT-TOKEN) + (LINE-HANDLER 'NEXT-BOOT-LINE) + (PARSEOUT (PROG2 (|PARSE-NewExpr|) (POP-STACK-1)))) + (DECLARE (SPECIAL BOOT-LINE-STACK $BOOT $SPAD XTOKENREADER LINE-HANDLER)) + PARSEOUT)) + + +;;--------------------> NEW DEFINITION (see i-syscmd.boot.pamphlet) +(defun |processSynonyms| () nil) ;;dummy def for depsys, redefined later + + +;; the following are for conditional reading +#+:ieee-floating-point (setq $ieee t) +#-:ieee-floating-point (setq $ieee nil) +(setq |$opSysName| '"shell") +#+:CCL (defun machine-type () "unknown") +(setq |$machineType| (machine-type)) +; spad-clear-input patches around fact that akcl clear-input leaves newlines chars +(defun spad-clear-input (st) (clear-input st) (if (listen st) (read-char st))) + +;; We need a way of distinguishing different versions of the system. +;; There used to be a way to touch the src/timestamp file whenever +;; you checked in a change to the change control subsystem. +;; During make PART=interp (the default for make) we set timestamp +;; to the filename of this timestamp file. This function converts it +;; to a luser readable string and sets the *yearweek* variable. +;; The result of this function is a string that is printed as a banner +;; when Axiom starts. The actual printing is done by the function +;; [[spadStartUpMsgs]] in [[src/interp/msgdb.boot]]. It uses a +;; format string from the file [[src/doc/msgs/s2-us.msgs]]. +(defun yearweek () + "set *yearweek* to the current time string for the version banner" + (declare (special timestamp) (special *yearweek*)) + (if (and (boundp 'timestamp) (probe-file timestamp)) + (let (sec min hour date month year day dayvec monvec) + (setq dayvec '("Monday" "Tuesday" "Wednesday" "Thursday" + "Friday" "Saturday" "Sunday")) + (setq monvec '("January" "February" "March" "April" "May" "June" + "July" "August" "September" "October" "November" + "December")) + (multiple-value-setq (sec min hour date month year day) + (decode-universal-time + (file-write-date timestamp))) + (setq *yearweek* + (copy-seq + (format nil "~a ~a ~d, ~d at ~2,'0d:~2,'0d:~2,'0d " + (elt dayvec day) + (elt monvec (1- month)) date year hour min sec)))) + (setq *yearweek* "no timestamp"))) + +(defun sourcepath (f) + "find the sourcefile in the system directories" + (let (axiom algebra naglink) + (setq axiom (|systemRootDirectory|)) + (setq algebra (concatenate 'string axiom "/../../src/algebra/" f ".spad")) + (setq naglink (concatenate 'string axiom "/../../src/naglink/" f ".spad")) + (cond + ((probe-file algebra) algebra) + ((probe-file naglink) naglink) + ('else nil)))) + +(defun srcabbrevs (sourcefile) + "read spad source files and return the constructor names and abbrevs" + (let (expr point mark names longnames) + (catch 'done + (with-open-file (in sourcefile) + (loop + (setq expr (read-line in nil 'done)) + (when (eq expr 'done) (throw 'done nil)) + (when (and (> (length expr) 4) (string= ")abb" (subseq expr 0 4))) + (setq expr (string-right-trim '(#\space #\tab) expr)) + (setq point (position #\space expr :from-end t :test #'char=)) + (push (subseq expr (1+ point)) longnames) + (setq expr (string-right-trim '(#\space #\tab) + (subseq expr 0 point))) + (setq mark (position #\space expr :from-end t)) + (push (subseq expr (1+ mark)) names))))) + (values longnames names))) + + +#+(and :AKCL (not (or :dos :win32))) +(in-package "COMPILER") +#+(and :AKCL (not (or :dos :win32))) +(defun gazonk-name ( &aux tem) + "return the name of the intermediate compiler file" + (dotimes (i 1000) + (setq tem (merge-pathnames (format nil "/tmp/gazonk~d.lsp" i))) + (unless (probe-file tem) + (return-from gazonk-name (pathname tem)))) + (error "1000 gazonk names used already!")) + +(in-package "BOOT") + +(defun |tr| (fn) + (|spad2AsTranslatorAutoloadOnceTrigger|) + (|convertSpadFile| fn) ) + + +;; Make will not compare dates across directories. +;; Rather than copy all of the code.lsp files to the MNT directory +;; we run this function to compile the files that are out of date +;; this function assumes that the shell variables INT and MNT are set. +;; Also of note: on the rt some files (those in the nooptimize list) +;; need to be compiled without optimize due to compiler bugs +(defun makelib (mid out stype btype) + "iterate over the NRLIBs, compiling ones that are out of date. + mid is the directory containing code.lsp + out is the directory containing code.o" + (let (libs lspdate odate nooptimize (alphabet #\space)) +#+(and :akcl :rt) + (setq nooptimize '("FFCAT-.NRLIB" "CHVAR.NRLIB" "PFO.NRLIB" "SUP.NRLIB" + "INTG0.NRLIB" "FSPRMELT.NRLIB" "VECTOR.NRLIB" + "EUCDOM-.NRLIB")) + (if (and mid out) + (format t "doing directory on ~s...~%" (concatenate 'string mid "/*")) + (error "makelib:MID=~a OUT=~a~% these are not set properly~%" mid out)) +#+:akcl (compiler::emit-fn nil) +#+:akcl (si::chdir mid) +#-:akcl (obey (concatenate 'string "cd " mid)) + (setq libs (directory "*.NRLIB")) + (unless libs + (format t "makelib:directory of ~a returned NIL~%" mid) + (bye -1)) + (princ "checking ") + (dolist (lib libs) + (unless (char= (schar (pathname-name lib) 0) alphabet) + (setq alphabet (schar (pathname-name lib) 0)) + (princ alphabet) + (finish-output)) + (let (dotlsp doto mntlib intkaf mntkaf intkafdate mntkafdate) + (setq dotlsp + (concatenate 'string mid "/" (file-namestring lib) "/code." stype)) + (setq doto + (concatenate 'string out "/" (pathname-name lib) ".NRLIB/code." btype)) + (setq mntlib + (concatenate 'string out "/" (pathname-name lib) ".NRLIB")) + (setq intkaf + (concatenate 'string mid "/" (file-namestring lib) "/index.KAF*")) + (setq mntkaf + (concatenate 'string out "/" (pathname-name lib) ".NRLIB/index.KAF*")) + (unless (probe-file mntlib) + (format t "creating directory ~a~%" mntlib) + (obey (concatenate 'string "cp -pr " (namestring lib) " " out)) + (when (probe-file (concatenate 'string mntlib "/code." stype)) + (delete-file (concatenate 'string mntlib "/code." stype)))) + (setq intkafdate (and (probe-file intkaf) (file-write-date intkaf))) + (setq mntkafdate (and (probe-file mntkaf) (file-write-date mntkaf))) + (when intkafdate + (unless (and mntkafdate (> mntkafdate intkafdate)) + (format t "~©ing ~s to ~s" intkaf mntkaf) + (obey + (concatenate 'string "cp " + (namestring intkaf) " " (namestring mntkaf))))) + (setq lspdate (and (probe-file dotlsp) (file-write-date dotlsp))) + (setq odate (and (probe-file doto) (file-write-date doto))) + (when lspdate + (unless (and odate (> odate lspdate)) +#+(and :akcl :rt) + (if (member (file-namestring lib) nooptimize :test #'string=) + (setq compiler::*speed* 0) + (setq compiler::*speed* 3)) + (compile-lib-file dotlsp :output-file doto))))))) + + +;; Make will not compare dates across directories. +;; In particular, it cannot compare the algebra files because there +;; is a one-to-many correspondence. This function will walk over +;; all of the algebra NRLIB files and find all of the spad files +;; that are out of date and need to be recompiled. This function +;; creates a file "/tmp/compile.input" to be used later in the +;; makefile. +;; Note that the file /tmp/compile.input is not currently used +;; as algebra source recompiles are not necessarily something +;; we want done automatically. Nevertheless, in the quest for +;; quality we check anyway. +(defun makespad (src mid stype) + "iterate over the spad files, compiling ones that are out of date. + src is the directory containing .spad + mid is the directory containing code.lsp + out is the directory containing code.o" + (let (mntlibs spadwork (alphabet #\space)) + (labels ( + (findsrc (mid libname) + "return a string name of the source file given the library file + name (eg PI) as a string" + (let (kaffile index alist) + (setq kaffile + (concatenate 'string mid "/" libname ".NRLIB/index.KAF*")) + (with-open-file (kaf kaffile) + (setq index (read kaf)) + (file-position kaf index) + (setq alist (read kaf)) + (setq index (third (assoc "sourceFile" alist :test #'string=))) + (file-position kaf index) + (pathname-name (pathname (read kaf index))))))) + (format t "makespad:src=~s mid=~s stype=~s~%" src mid stype) + (if (and src mid) + (format t "doing directory on ~s...~%" (concatenate 'string src "/*")) + (error "makespad:SRC=~a MID=~a not set properly~%" src mid)) +#+:akcl (si::chdir mid) +#-:akcl (obey (concatenate 'string "cd " mid)) + (setq mntlibs (directory "*.NRLIB")) + (unless mntlibs + (format t "makespad:directory of ~a returned NIL~%" src) + (bye 1)) + (princ "checking ") + (dolist (lib mntlibs) + (unless (char= (schar (pathname-name lib) 0) alphabet) + (setq alphabet (schar (pathname-name lib) 0)) + (princ alphabet) + (finish-output)) + (let (spad spaddate lsp lspdate) + (setq spad + (concatenate 'string src "/" (findsrc mid (pathname-name lib)) ".spad")) + (setq spaddate + (and (probe-file spad) (file-write-date spad))) + (setq lsp + (concatenate 'string mid "/" (pathname-name lib) ".NRLIB/code." stype)) + (setq lspdate + (and (probe-file lsp) (file-write-date lsp))) + (cond + ((and spaddate lspdate (<= spaddate lspdate))) + ((and spaddate lspdate (> spaddate lspdate)) + (setq spadwork (adjoin spad spadwork :test #'string=))) + ((and spaddate (not lspdate)) + (setq spadwork (adjoin spad spadwork :test #'string=))) + ((and (not spaddate) lspdate) + (format t "makespad:missing spad file ~a for lisp file ~a~%" spad lsp)) + ((and (not spaddate) (not lspdate)) + (format t "makespad:NRLIB ~a exist but is spad ~a and lsp ~a don't~%" + lib spad lsp))))) + (with-open-file (tmp "/tmp/compile.input" :direction :output) + (dolist (spad spadwork) + (format t "~a is out of date~%" spad) + (format tmp ")co ~a~%" spad)))))) + + +;; We need to ensure that the INTERP.EXPOSED list, which is a list +;; of the exposed constructors, is consistent with the actual libraries. +(defun libcheck (int) + "check that INTERP.EXPOSED and NRLIBs are consistent" + (let (interp nrlibs) + (labels ( + (CONSTRUCTORNAME (nrlib) + "find the long name of a constructor given an abbreviation string" + (let (file sourcefile name) + (setq file (findsrc nrlib)) + (setq sourcefile + (concatenate 'string int "/" file ".spad")) + (when (and file (probe-file sourcefile)) + (setq name (searchsource sourcefile nrlib))))) + (NOCAT (longnames) + "remove the categories from the list of long names" + (remove-if + #'(lambda (x) + (let ((c (schar x (1- (length x))))) + (or (char= c #\&) (char= c #\-)))) longnames)) + (FINDSRC (libname) + "return a string name of the source file given the library file + name (eg PI) as a string" + (let (kaffile index alist result) + (setq kaffile + (concatenate 'string int "/" libname ".NRLIB/index.KAF*")) + (if (probe-file kaffile) + (with-open-file (kaf kaffile) + (setq index (read kaf)) + (file-position kaf index) + (setq alist (read kaf)) + (setq index (third (assoc "sourceFile" alist :test #'string=))) + (file-position kaf index) + (setq result (pathname-name (pathname (read kaf index)))))) + (format t "~a does not exist~%" kaffile) + result)) + (READINTERP () + "read INTERP.EXPOSED and return a sorted abbreviation list" + (let (expr names longnames) + (with-open-file (in (concatenate 'string int "/INTERP.EXPOSED")) + (catch 'eof + (loop + (setq expr (read-line in nil 'eof)) + (when (eq expr 'eof) (throw 'eof nil)) + (when + (and + (> (length expr) 58) + (char= (schar expr 0) #\space) + (not (char= (schar expr 8) #\space))) + (push (string-trim '(#\space) (subseq expr 8 57)) longnames) + (push (string-right-trim '(#\space) (subseq expr 58)) names))))) + (setq longnames (sort longnames #'string<)) + (setq names (sort names #'string<)) + (values names longnames))) + (READLIBS (algebra) + "read the NRLIB directory and return a sorted abbreviation list" + (let (libs nrlibs) +#+:akcl (si::chdir algebra) +#-:akcl (obey (concatenate 'string "cd " algebra)) + (setq nrlibs (directory "*.NRLIB")) + (unless nrlibs + (error "libcheck: (directory ~s) returned NIL~%" + (concatenate 'string algebra "/*.NRLIB"))) + (dolist (lib nrlibs) + (push (pathname-name lib) libs)) + (sort libs #'string<))) + (SEARCHSOURCE (sourcefile nrlib) + "search a sourcefile for the long constructor name of the nrlib string" + (let (in expr start) + (setq nrlib (concatenate 'string " " nrlib " ")) + (catch 'done + (with-open-file (in sourcefile) + (loop + (setq expr (read-line in nil 'done)) + (when (eq expr 'done) (throw 'done nil)) + (when (and (> (length expr) 4) + (string= ")abb" (subseq expr 0 4)) + (search nrlib expr :test #'string=) + (setq start (position #\space expr :from-end t :test #'char=))) + (throw 'done (string-trim '(#\space) (subseq expr start))))))))) + (SRCABBREVS (sourcefile) + (let (in expr start end names longnames) + (catch 'done + (with-open-file (in sourcefile) + (loop + (setq expr (read-line in nil 'done)) + (when (eq expr 'done) (throw 'done nil)) + (when (and (> (length expr) 4) + (string= ")abb" (subseq expr 0 4))) + (setq point (position #\space expr :from-end t :test #'char=)) + (push (string-trim '(#\space) (subseq expr point)) longnames) + (setq mark + (position #\space + (string-right-trim '(#\space) + (subseq expr 0 (1- point))) :from-end t)) + (push (string-trim '(#\space) (subseq expr mark point)) names))))) + (values names longnames))) + (SRCSCAN () + (let (longnames names) +#+:gcl (system::chdir int) +#-:gcl (obey (concatenate 'string "cd " int)) + (setq spads (directory "*.spad")) + (dolist (spad spads) + (multiple-value-setq (short long) (srcabbrevs spad)) + (setq names (nconc names short)) + (setq longnames (nconc longnames long))) + (setq names (sort names #'string<)) + (setq longnames (sort longnames #'string<)) + (values names longnames)))) + (multiple-value-setq (abbrevs constructors) (readinterp)) + (setq nrlibs (readlibs int)) + (dolist (lib (set-difference nrlibs abbrevs :test #'string=)) + (format t "libcheck:~a/~a.NRLIB is not in INTERP.EXPOSED~%" int lib)) + (dolist (expose (set-difference abbrevs nrlibs :test #'string=)) + (format t "libcheck:~a is in INTERP.EXPOSED with no NRLIB~%" expose)) + (multiple-value-setq (srcabbrevs srcconstructors) (srcscan)) + (setq abbrevs (nocat abbrevs)) + (setq constructors (nocat constructors)) + (dolist (item (set-difference srcabbrevs abbrevs :test #'string=)) + (format t "libcheck:~a is in ~a but not in INTERP.EXPOSED~%" item + (findsrc item))) + (dolist (item (set-difference abbrevs srcabbrevs :test #'string=)) + (format t "libcheck:~a is in INTERP.EXPOSED but has no spad sourcfile~%" + item)) + (dolist (item (set-difference srcconstructors constructors :test #'string=)) + (format t "libcheck:~a is not in INTERP.EXPOSED~%" item)) + (dolist (item (set-difference constructors srcconstructors :test #'string=)) + (format t "libcheck:~a has no spad source file~%" item))))) + + |