Reputation: 347
I have a file (furniture.lisp) that looks basically like this (with many more entries):
(addgv :furniture 'stove
(make-instance 'stove
:pose (tf:make-pose-stamped
"map" ; frame-id
0.0
(tf:make-3d-vector -3.1 -0.9 0) ; translation/origin
(tf:euler->quaternion :az 0))))
(addgv :furniture 'drawers-cupboard
(make-instance 'cupboard
:pose (tf:make-pose-stamped
"map"
0.0
(tf:make-3d-vector -3.1 0.1 0)
(tf:euler->quaternion :az 0))))
Now, I'd like to have a function (get-locations "furniture.lisp" "locations.txt")
that extracts the objects coordinates in the 3d-vector and writes its output to a file:
(location stove -3.1 -0.9 9)
(location drawers-cupboard -3.1 0.1 0)
...
I started by writing an expression that reads in the file (so far without parametrization) line by line:
(ql:quickload "split-sequence")
(with-open-file (stream "furniture.lisp")
(do ((line (read-line stream nil)
(read-line stream nil)))
((null line))
(princ (split-sequence::split-sequence #\Space line)) ; Just for demonstration
))
But I realized that I have no chance/idea to "connect" the name of the object (e.g. stove) and its coordinates. I'd need the second symbol after "(addgv " for the name and variable "distance of words" for the coordinates. So I tried to read the file into one big list:
(defun make-list-from-text (fn)
(with-open-file (stream fn)
(loop for line = (read-line stream nil nil)
while line
collect
(split-sequence::split-sequence #\Space line))))
Whereby every line is a sublist (I don't know if this substructure is a advantage, perhaps I should 'flatten' the result). Now I'm stuck. Furthermore, I have the feeling, that my approach is somehow inelegant.
EDIT:
I followed Svante's approach and finally got the desired output! Besides creating a dummy package, I also had to create dummy exports for the package (e.g. :export :make-3d-vector
). Additionally,:key #'car
did not work, as my list was a 'mixed' list, consisting of sublists (e.g. (make-instance ...)
) and symbols (e.g. addgv
). So I created a helper function:
(defun find-helper (list-or-symbol)
(if (listp list-or-symbol)
(car list-or-symbol)
list-or-symbol))
And replaced #'car
by #'find-helper
.
Upvotes: 4
Views: 262
Reputation: 51501
My idea would be to create a dummy tf
package, then read
the forms and parse whatever you need from them. Something like this (untested):
(eval-when (:compile-toplevel :load-toplevel :execute)
(unless (find-package #:tf)
(defpackage #:tf)))
(defun extract-location-file ()
(let ((*read-eval* nil))
(with-open-file (in "furniture.lisp")
(with-open-file (out "locations.txt"
:direction :output
:if-exists :supersede
:if-does-not-exist :create)
(loop :for form := (read in nil)
:while form
:do (print (extract-location form) out)
(terpri)))))
(defun extract-location (form)
`(location ,(third form)
,@(rest (find 'tf::make-3d-vector
(find 'tf::make-pose-stamped
(find 'make-instance
form
:key #'car)
:key #'car)
:key #'car))))
Be sure not to omit to bind *read-eval*
to nil
.
Upvotes: 1
Reputation: 139251
Unfortunately this would be a non-portable solution:
For example in LispWorks I could do something like this (just a sketch):
CL-USER 60 > (defun test ()
(handler-bind ((conditions:package-not-found-reader
(lambda (c)
(continue c)))
(conditions:simple-reader-error
(lambda (c)
(continue c))))
(read-from-string "'(foo27:bar19 bar18:foo44)")))
TEST
CL-USER 61 > (test)
(QUOTE (FOO27::BAR19 BAR18::FOO44))
It calls the continue restarts for the missing package error and then for the error that the symbol is not exported. The restarts create the package and the other one is returning a non-exported symbol...
Upvotes: 0
Reputation: 9451
The general way would be:
(cl-ppcre:regex-replace-all "tf::?" content "")
, i.e. replace all references to package tf
to avoid package-related errors '()
around the contentsread
it and assign to a variableUpvotes: 0