Thursday, February 04, 2010

Slide Title Extractor Part 2 of 3

So, I was unsatisfied with my last snippet of code. I had been reading a lot of common lisp, but not writing any. I rectified that with this poorly written code to get the frametitles out of a beamer slide that uses latex.

so, my first lisp version which I was not happy with. I am not going to comment much, as I want to spend more time on part 3.
I am going to just go down the code, with some comments as I feel like making them.

This is the main function, extract-slide-titles. I use a let to hold some variables common to the functions that are coming up.

(defun extract-slide-titles (stream)
(let ((out-paragraph "")
(steps-left 10)
(command-string "")

Decide next step is the first subfunction that is called on the stream. Depending on the current character, it decides to do different things. I use the variable cur-state to hold the next function to call.

(defun decide-next-step (c)
((not c) (setf out-paragraph (concatenate 'string out-paragraph (string #\newline)))
(return-from extract-slide-titles out-paragraph)) ;end of file
((char= #\% c) (setf cur-state #'throw-away-until-newline)) ;comments
((char= #\\ c) (setf cur-state #'check-for-frame-title)) ;look for latex commands
(t ())))

check-for-frame-title works when we know that a latex command has been found (when you have a "\" character. Then it checks if the command is the same as "frametitle".

(defun check-for-frame-title (c)
(setf steps-left (- steps-left 1))
(setf command-string (concatenate 'string command-string (string c)))
((char= #\{ c)
(setf steps-left 10)
(setf command-string "")
(setf cur-state #'decide-next-step))
((eq 0 steps-left)
(setf steps-left 10)
(if (equal command-string "frametitle")
(setf cur-state #'store-char)
(setf cur-state #'decide-next-step))
(setf command-string ""))
(t (setf cur-state #'check-for-frame-title)))

Here, I store the characters that I want to output to a string, until I reach the end of the latex command which is signaled by a "{".

(defun store-char (c)
;here is where to add support for nested commands
((and (char= #\ c) (char= #\ previous-char)) ())
((char= #\{ c) ())
((char= #\newline c) ())
((char= #\} c)
(setf out-paragraph (concatenate 'string out-paragraph ". "))
(setf cur-state #'decide-next-step))
(setf out-paragraph (concatenate 'string out-paragraph (string c)))
(setf cur-state #'store-char)))
(setf previous-char c))

As before, I need to get rid of comments, so this just throws everything away until it hits a newline.

(defun throw-away-until-newline (c)
((char= #\newline c) (setf cur-state #'decide-next-step)) ;end of line, switch back
(t (setf cur-state #'throw-away-until-newline)))) ;keep going, throwing out c

Here, I start this thing by setting the cur-state "pointer" to the first function that I want called. Then I start the loop which will read characters from the stream until the end of the file. It will apply the function that cur-state refers to to the current character.

(setf cur-state #'decide-next-step)
(loop for char = (read-char stream nil) do
(funcall cur-state char))))

This is just to get the file into lisp, and to print the output to the screen.

(with-open-file (infile "test.tex" :if-does-not-exist nil)
(princ (extract-slide-titles infile)))


Like I said, this code is ugly. I cleaned it up and hopefully made it clearer in the next iteration. The code for extract-frame-titles is much shorter the next time around.

No comments: