|
From: parth.malwankar on 17 Jun 2008 14:13 On Jun 17, 12:22 am, t...(a)sevak.isi.edu (Thomas A. Russ) wrote: > parth.malwan...(a)gmail.com writes: > > Since a lot of commentary has already occurred, I'll just pick and > choose a few items (some of which are a bit redundant). > > > Source Code Listing > > =================== > > (defun concat-strings-from-list (l) > > "take all strings in the list and concatenate them > > adding a space in between. > > e.g.: ('aa' 'bb' 'cc') => 'aa bb cc'" > > (reduce #'(lambda (x y) (concatenate 'string x " " y)) > > l)) > > There is also the FORMAT option, already mentioned: > > (format nil "~{~A~^ ~}" list) > > This has the additional advantage that it will work with lists of > arbitrary objects and not just strings. So symbols, numbers, etc will > also work easily. > > If you want a more general purpose function, you could write something > like the following, which is actually a bit simpler to write using the > reduce method: > > (defun format-list (list &optional (delimiter " ")) > "Formats LIST items separated by DELIMITER" > (reduce #'(lambda (x y) (concatenate 'string x delimiter y)) > list :initial-value "")) > > This then can be used more generally. Also, if you add the > :INITIAL-VALUE keyword, then the function will work correctly even if > you pass in NIL as the list to concatenate. It will then return just > the empty string instead of signaling an error. > > To do the same thing in format is a bit uglier, since you would need to > construct the format string as part of applying the function: > > ... (format (concatenate 'string "~{~A~^" delimiter "~}") ...) > > > (defun find-last-subst-pos-re (sub str) > > "returns position of last sub-string. > > e.g.: 'xx' 'abcxxyy' => 3" > > (let ((m (all-matches sub str))) > > (first (last (butlast m))))) > > If this is truly just looking for the substring rather than the > occurrence of of a regular expression, there is a more straightforward > method using built-in Common Lisp forms. And if you really mean it to > be regular expressions, the documentation string should say that rather > than "sub-string". > > (search sub str :from-end t) > > > ;;; ================================ > > ;;; File Extension and Type Handling > > ;;; ================================ > > > (defun extention-p (ext name) > > "return if name ends in ext. used to check file extension" > > (let ((ext-len (length ext))) > > (equal ext (subseq name (- (length name) ext-len))))) > > Should be called something like HAS-EXTENSION or EXTENSION-MATCHES. > > I would also write another helper function called GET-EXTENSION that > attempts to just return the extension of a file. This would typically > just look for the last "." in the name and return whatever starts there: > > (defun get-extension (name) > "Returns the extension part of "name". > (let ((extension-start (position #/. name :from-end t))) > (when extension-start > (subseq name extension-start)))) > > Or even better, you could use the Common Lisp pathname functions and use > PATHNAME-TYPE to get the extension. That will also insulate more of > your code from the underlying operating system's syntax for indicating > file types. Then you would simply be able to write > > (defun has-extension (name ext) > (string= ext (pathname-type name))) > > Note that you would then have to use "c" rather than ".c" as the > extension name. But this is a lot simpler. > > > (defun filetype-p (name type) > > "check if file is of a 'type'" > > (cond ((equal type 'cfile) (extention-p ".c" name)) > > ((equal type 'ofile) (extention-p ".o" name)) > > ((equal type 'hfile) (extention-p ".h" name)) > > (t nil))) > > > (defun filetype? (name) > > "return the 'type' of file based on extension" > > (cond ((extention-p ".c" name) 'cfile) > > ((extention-p ".o" name) 'ofile) > > ((extention-p ".h" name) 'hfile) > > (t nil))) > > As has been noted, this would be better handled by using a declarative > data structure such as an association list to store the correspondences: > > (defparameter *extension-mapping* > '(("c" . cfile) ("o" . ofile) ("h" . hfile))) > > This then lets you write some very simple functions to get items out of > the data structure. By using this declarative approach, you are also > assured that any changes to make will always be reflected in the > underlying code, since you won't have to remember to update both > functions. > > (defun get-file-type (name) > (cdr (assoc (pathname-type name) *extension-mapping* > :test #'string=))) > > (defun get-file-extension (type) > (car (rassoc type *extension-mapping*))) > > (defun has-file-type (name type) > (eql (get-file-type name) type)) > > The general theme is to break a lot of this down to very simple > functions that are generally applicable and that also build on one > another. > > > ;;; =================== > > ;;; File Representation > > ;;; =================== > > If you change the direct file representation to be pathnames instead of > just strings, then some of these manipulations would be easier. > > > (defun make-file (&key name (deps nil) (type nil)) > > (list :name name :deps deps :type (filetype? name))) > > I would use this to convert NAME to a pathname object with > (pathname name) > > > > > (defun make-file-list (names) > > (mapcar #'(lambda (x) (get-intermediate-file (make-file :name x))) > > names)) > > > (defun get-intermediate-file (file) > > "takes a file and generates a plist for the possible intermediate > > file once this is processed. For example, for a test.c plist > > it will create a test.o plist. When there is no intermediate file, > > input is returned" > > (let ((name (getf file :name)) > > (type (getf file :type))) > > (cond ((equal type 'cfile) (make-file > > :name (search-replace-last-re "\.c" > > "\.o" name) > > :deps (list file) > > :type 'ofile)) > > (t file)))) > > Here you might also want to build a data structure similar to the one > used to map file extensions to types and use that to contain the mapping > from files to successor types. That then makes the function easily > extensible and keeps the correspondence between successors where you can > easily see it, instead of buried inside the code for this function. > > Note that this assume you convert file names to pathnames. > > (defparameter *file-successors* '((cfile . ofile))) > > (defun change-file-type (name new-type) > (let ((new-name (pathname (namestring name)))) > (setf (pathname-type new-name) (get-file-extension new-type)) > new-name)) > > (defun get-successor-file (file) > (let ((name (getf file :name)) > (next-type (cdr (assoc (getf file :type) *file-successors*)))) > (if next-type > (make-file :name (change-file-type name next-type) > :deps (list file) > :type next-type) > file))) > > Thanks Thomas. Coming from other languages pathnames doesn't come naturally to be (yet!) but this makes a lot of sense. :) I have updated the code to use pathnames and also generalized using *file-successors*. Was able to get rid of regex as this was being used only for file extension which is easier and more reliable with pathname. Parth > > > > > ; Usage: > > ; (setf *p* (program "test" '("a.c" "b.c" "c.d"))) > > ; (build *p*) > > > (defun program (target sources) > > "top level program creation function. returns a tree that represents > > files and their dependencies leading to the toplevel program." > > (let ((target-file (make-file :name target))) > > (setf (getf target-file :deps) (make-file-list sources)) > > (setf (getf target-file :type) 'exe) > > target-file)) > > > (defun build (program) > > (let ((deps (getf program :deps)) > > (name (getf program :name)) > > (type (getf program :type))) > > (cond ((null deps) (format t "[build] ~A: " name) > > (build-file program)) > > (t (mapcar #'build deps) > > (format t "[build] ~A: " name) > > (build-file program))))) > > > ; "test.c" => "test.o" > > (defun cfile->ofile (name) > > (search-replace-last-re "\.c" "\.o" name)) > > > ; deps-(<file0>, <file1>, <file2>) => ("depname0" "depname1" > > "depname2") > > (defun get-dep-names (file) > > (let ((deps (getf file :deps))) > > (mapcar #'(lambda (x) (getf x :name)) deps))) > > > (defun build-file (file) > > "print the string needed to build the file based on file type" > > (let ((type (getf file :type)) > > (name (getf file :name)) > > (deps (getf file :deps))) > > (cond ((equal type 'cfile) > > (format t "~A~%" (concat-strings-from-list > > (list "gcc -o" (cfile->ofile name) > > name)))) > > ((equal type 'ofile) > > (format t "nothing to do: ~A~%" name)) > > ((equal type 'exe) > > (format t "~A~%" (concatenate 'string > > "gcc -o " > > name " " > > (concat-strings-from-list > > (get-dep-names file))))) > > (t (format t "nothing to do: ~A~%" name))))) > > -- > Thomas A. Russ, USC/Information Sciences Institute
First
|
Prev
|
Pages: 1 2 Prev: acl compile-file can't find a macro Next: OS X USB/RS232 problems (somewhat OT) |