Common Lisp: relative path to absolute - common-lisp

May be it is a really dumb question, but after playing around with all built-in pathname-family functions and cl-fad/pathname-utils packages I still can't figure out how to convert a relative path to absolute (with respect to $PWD):
; let PWD be "/very/long/way"
(abspath "../road/home"); -> "/very/long/road/home"
Where the hypothetical function abspath works just like os.path.abspath() in Python.

The variable *DEFAULT-PATHNAME-DEFAULTS* usually contains your initial working directory, you can merge the pathname with that;
(defun abspath (pathname)
(merge-pathnames pathname *default-pathname-defaults*))
And since this is the default for the second argument to merge-pathnames, you can simply write:
(defun abspath (pathname)
(merge-pathnames pathname))

UIOP
Here is what the documentation of UIOP says about cl-fad :-)
UIOP completely replaces it with better design and implementation
A good number of implementations ship with UIOP (used by ASDF3), so it's basically already available when you need it (see "Using UIOP" in the doc.). One of the many functions defined in the library is uiop:parse-unix-namestring, which understands the syntax of Unix filenames without checking if the path designates an existing file or directory. However the double-dot is parsed as :back or :up which is not necessarily supported by your implementation. With SBCL, it is the case and the path is simplified. Note that pathnames allows to use both :back and :up components; :back can be simplified easily by looking at the pathname only (it is a syntactic up directory), whereas :up is the semantic up directory, meaning that it depends on the actual file system. You have a better chance to obtain a canonical file name if the file name exists.
Truename
You can also call TRUENAME, which will probably get rid of the ".." components in your path. See also 20.1.3 Truenames which explains that you can point to the same file by using different pathnames, but that there is generally one "canonical" name.

Here's the final solution (based on the previous two answers):
(defun abspath
(path-string)
(uiop:unix-namestring
(uiop:merge-pathnames*
(uiop:parse-unix-namestring path-string))))
uiop:parse-unix-namestring converts the string argument to a pathname, replacing . and .. references; uiop:merge-pathnames* translates a relative pathname to absolute; uiop:unix-namestring converts the pathname back to a string.
Also, if you know for sure what kind of file the path points to, you can use either:
(uiop:unix-namestring (uiop:file-exists-p path))
or
(uiop:unix-namestring (uiop:directory-exists-p path))
because both file-exists-p and directory-exists-p return absolute pathnames (or nil, if file does not exist).
UPDATE:
Apparently in some implementations (like ManKai Common Lisp) uiop:merge-pathnames* does not prepend the directory part if the given pathname lacks ./ prefix (for example if you feed it #P"main.c" rather than #P"./main.c"). So the safer solution is:
(defun abspath
(path-string &optional (dir-name (uiop:getcwd)))
(uiop:unix-namestring
(uiop:ensure-absolute-pathname
(uiop:merge-pathnames*
(uiop:parse-unix-namestring path-string))
dir-name)))

Related

Is there a easy way to get the "directory" function to put the values in alphabetical order?

Retrieving all paths for the files with .pas ending in my subfolder "testfiles" with the directory function. So far so good. The problem is that the paths is not in alphabetical order.
The paths in the list is not strings or list, so the sort function does not work for me either.
Is there a easy way to fix this?
Code:
(setq test (directory "testfiles/*.pas"))
The sequence functions in Common Lisp are powerful, through the various keyword parameters (:key, :test, etc., depending on the function). SORT takes an optional :key parameter.
(sort (directory "testfiles/*.pas") #'string< :key #'pathname-name)
Note: SORT is a destructive operation. But this is not a problem here, because DIRECTORY always returns a freshly consed-up list.

Finding a Module's path, using the Module object

What is the sane way to go from a Module object to a path to the file in which it was declared?
To be precise, I am looking for the file where the keyword module occurs.
The indirect method is to find the location of the automatically defined eval method in each module.
moduleloc(mm::Module) = first(functionloc(mm.eval, (Symbol,)))
for example
moduleloc(mm::Module) = first(functionloc(mm.eval, (Symbol,)))
using DataStructures
moduleloc(DataStructures)
Outputs:
/home/oxinabox/.julia/v0.6/DataStructures/src/DataStructures.jl
This indirect method works, but it feels like a bit of a kludge.
Have I missed some inbuilt function to do this?
I will remind answered that Modules are not the same thing as packages.
Consider the existence of submodules, or even modules that are being loaded via includeing some abolute path that is outside the package directory or loadpath.
Modules simply do not store the file location where they were defined. You can see that for yourself in their definition in C. Your only hope is to look through the bindings they hold.
Methods, on the other hand, do store their file location. And eval is the one function that is defined in every single module (although not baremodules). Slightly more correct might be:
moduleloc(mm::Module) = first(functionloc(mm.eval, (Any,)))
as that more precisely mirrors the auto-defined eval method.
If you aren't looking for a programmatic way of doing it you can use the methods function.
using DataFrames
locations = methods(DataFrames.readtable).ms
It's for all methods but it's hardly difficult to find the right one unless you have an enormous number of methods that differ only in small ways.
There is now pathof:
using DataStructures
pathof(DataStructures)
"/home/ederag/.julia/packages/DataStructures/59MD0/src/DataStructures.jl"
See also: pkgdir.
pkgdir(DataStructures)
"/home/ederag/.julia/packages/DataStructures/59MD0"
Tested with julia-1.7.3
require obviously needs to perform that operation. Looking into loading.jl, I found that finding the module path has changed a bit recently: in v0.6.0, there is a function
load_hook(prefix::String, name::String, ::Void)
which you can call "manually":
julia> Base.load_hook(Pkg.dir(), "DataFrames", nothing)
"/home/philipp/.julia/v0.6/DataFrames/src/DataFrames.jl"
However, this has changed to the better in the current master; there's now a function find_package, which we can copy:
macro return_if_file(path)
quote
path = $(esc(path))
isfile(path) && return path
end
end
function find_package(name::String)
endswith(name, ".jl") && (name = chop(name, 0, 3))
for dir in [Pkg.dir(); LOAD_PATH]
dir = abspath(dir)
#return_if_file joinpath(dir, "$name.jl")
#return_if_file joinpath(dir, "$name.jl", "src", "$name.jl")
#return_if_file joinpath(dir, name, "src", "$name.jl")
end
return nothing
end
and add a little helper:
find_package(m::Module) = find_package(string(module_name(m)))
Basically, this takes Pkg.dir() and looks in the "usual locations".
Additionally, chop in v0.6.0 doesn't take these additional arguments, which we can fix by adding
chop(s::AbstractString, m, n) = SubString(s, m, endof(s)-n)
Also, if you're not on Unix, you might want to care about the definitions of isfile_casesensitive above the linked code.
And if you're not so concerned about corner cases, maybe this is enough or can serve as a basis:
function modulepath(m::Module)
name = string(module_name(m))
Pkg.dir(name, "src", "$name.jl")
end
julia> Pkg.dir("DataStructures")
"/home/liso/.julia/v0.7/DataStructures"
Edit: I now realized that you want to use Module object!
julia> m = DataStructures
julia> Pkg.dir(repr(m))
"/home/liso/.julia/v0.7/DataStructures"
Edit2: I am not sure if you are trying to find path to module or to object defined in module (I hope that parsing path from next result is easy):
julia> repr(which(DataStructures.eval, (String,)))
"eval(x) in DataStructures at /home/liso/.julia/v0.7/DataStructures/src/DataStructures.jl:3"

Defining aliases to standard Common Lisp functions?

Lisp is said to enable redefinitions of its core functions.
I want to define an alias to the function cl:documentation function, such that
(doc 'write 'function) === (documentation 'write 'function)
How can this be done and made permanent in SBCL?
Creating an Alias
You are not trying to redefine (i.e., change the definition of) the system function documentation, you want to define your own function with a shorter name which would do the same thing as the system function.
This can be done using fdefinition:
(setf (fdefinition 'doc) #'documentation)
How to make your change "permanent" in common lisp
There is no standard way, different implementation may do it differently, but, generally speaking, there are two common ways.
Add code to an init file - for beginners and casual users
SBCL
CLISP
Clozure
ECL
The code in question will be evaluated anew every time lisp starts.
Pro:
Easy to modify (just edit file)
Takes little disk space
Normal lisp invocation captures the change
Con:
Evaluated every time you start lisp (so, slows start up time if the code is slow)
Save image - for heavy-weight professionals
SBCL
CLISP
Clozure
ECL - not supported
The modified lisp world is saved to disk.
Pro:
Start uptime is unaffected
Con:
Requires re-dumping the world on each change
Lisp image is usually a large file (>10MB)
Must specify the image at invocation time
Even though #sds has already answered pretty thoroughly I just wanted to add that the utility library serapeum has defalias
I use a simple macro for this:
(defmacro alias (to fn)
`(setf (fdefinition ',to) #',fn))
e.g.
(alias neg -) => #<Compiled-function ... >
(neg 10) => -10
Other answers include detail about how to make this permanent.

Common Lisp - how to say if a pathname points to a regular file or a directory?

Is it possible to 'stat' a file and find its file type - regular or directory?
Read the chapter about a portable pathname library from Peter Seibel's Practical Common Lisp book. It's available for free. It has a function file-exists-p that will return a pathname when the file exists or nil if it doesn't. The returned pathname will be in directory form if it's a directory. He also gives another function for checking if the pathname is indeed in directory form.
BTW the whole book is really worth reading so check it out if you haven't already.
CL-FAD has a function DIRECTORY-EXISTS-P which, when used in combination with PATHNAME-AS-DIRECTORY canonicalizes the pathname (prevents failure when handed a string like "/path/dir-without-trailing-slash") and achives what you're asking for.
(CL-FAD:DIRECTORY-PATHNAME-P (CL-FAD:PATHNAME-AS-DIRECTORY (PROBE-FILE "/path/missing-slash")))
I think there are several ways. probe-file followed by checking the returned true name to determine that it has a directory name but not a filename and type should do it. e.g. for a directory
(pathname-name (probe-file filespec))
-> NIL
CLISP has a function EXT:PROBE-DIRECTORY, which tells you whether a file exists and is a directory.
Note that this function is specific to CLISP and not standard Common Lisp.

Organizing Clojure Code

I have a program that draws shapes on an image. I have a separate namespace for each shape, and they are in separate files.
com/name/box.clj --> has com.name.box namespace.
com/name/triangle.clj --> has com.name.triangle namespace.
They all share a common function called generate that draws them on screen, so if I use use, function names clash.
For now, I load them with load-file. Is there a better way of doing this? Looking through the Clojure API, it seems there are multiple ways of including files. Which one is a better option for a project with lots of files?
I too started by using load-file. According to the Clojure documentation on libs,
Clojure defines conventions for naming and structuring libs:
* A lib name is a symbol that will typically contain two or more parts separated by periods.
* A lib's container is a Java resource whose classpath-relative path is derived from the lib name:
o The path is a string
o Periods in the lib name are replaced by slashes in the path
o Hyphens in the lib name are replaced by underscores in the path
o The path ends with ".clj"
* A lib begins with an "ns" form that
o creates the Clojure namespace that shares its name, and
o declares its dependencies on Java classes, Clojure's core facilities, and/or other libs
The Clojure documentation further provides the following example namespace declaration (which I'm sure you already know, but I'm providing it here for completeness):
(ns com.my-company.clojure.examples.my-utils
(:import java.util.Date)
(:use [clojure.contrib.def :only (defvar-)])
(:require [clojure.contrib.shell-out :as shell]))
So, my answer would be to use libs for your project -- it'll help simplify all of those folders. To "include" a lib, you'll use require, like this:
(require 'clojure.contrib.def 'clojure.contrib.except 'clojure.contrib.sql)
(require '(clojure.contrib def except sql))
As long as the documentation is correct and your project is classpath-relative, everything should happily load. Hope that answers your question. :D
Along with using namespace libs as already has been suggested perhaps your common 'generate' function is a candidate for a multimethod? http://clojure.org/multimethods
This would help avoid the function name clash and add a common abstraction to your 'shapes', I guess it depends on whether an appropriate dispatch function can be found.

Resources