From: goose
Subject: writing an interpreter
Date: 
Message-ID: <1148915580.525429.129440@u72g2000cwu.googlegroups.com>
Hello all

I want to write an interpreter (sort of) in lisp for a
TeX-like language. I'm not sure yet how what I will do with
the output (either store it in a list, or display it as the
data is parsed); I simply want to read in a subset of TeX.

Basically, my language will be as follows:

1. atom : prints the atom itself
      e.g. Hello World
               will merely print "Hello World"

2. {atoms} : prints each atom, same as #1
      e.g. {Hello World}
               will print "Hello World"

3. \key{atoms} : prints the atoms in the manner specified
   by the key.
      e.g. \bold{Hello World}
               will print out a bolded "Hello World"

4. \key[options]{atoms} : same as #3, with options controlling
   a few variables associated with that key.
      e.g. \paragraph[In the beginning] {
               Once upon a time, in a land far far
               away, there lived the deranged developer.
           }
               will result in the above paragraph with the
               title "In the beginning".

I can possibly do this with a state machine, but I was wondering
if there is a better way. I'm guessing that a modification to the
readtable might make it possible to read in a file of TeX and
have each "\key" be a function call, with optional arguments
represented with "[...]" and a rest argument being represented
with "{...}" so that when the reader encounters

   \paragraph[Welcome]{
      \textbf{Hello to the \textit{entire} World}
   }

it will read it in as

   (paragraph :option "Welcome"
    (textbf "Hello to the " (textit "entire") " World"))

and then I can just write the functions (paragraph ...),
(textbf ...) etc, but I don't know enough about the readtable
to even know where to begin, never mind what the changes should
be.

Is this possible by just changing the reader syntax? Is it
difficult enough that I should rather just write a state-machine
instead? The only problem I see with the above is that free-
standing atoms would get ignored.


Thanks in advance
goose

(ps. I actually *am* intending to read in TeX, and display
(as faithfully as possible, anyway) the rendered TeX).

From: James Crippen
Subject: Re: writing an interpreter
Date: 
Message-ID: <1148919313.914274.323250@j33g2000cwa.googlegroups.com>
I recommend you make friends with CL-YACC, or something like it.

http://www.cliki.net/CL-Yacc
http://www.pps.jussieu.fr/~jch/software/cl-yacc/
From: Nicolay Giraldo
Subject: Re: writing an interpreter
Date: 
Message-ID: <1148931669.390146.99800@y43g2000cwc.googlegroups.com>
You may also want to see the code of JSMath, a javascript latex parser
and layout engine.

http://www.math.union.edu/~dpvc/jsMath/welcome.html
From: Andreas Hinze
Subject: Re: writing an interpreter
Date: 
Message-ID: <447B5749.7030602@snafu.de>
goose wrote:
> Hello all
> 
> I want to write an interpreter (sort of) in lisp for a
> TeX-like language. I'm not sure yet how what I will do with
> the output (either store it in a list, or display it as the
> data is parsed); I simply want to read in a subset of TeX.
> 
Hi,
you mean like this ? Sorry for the spare commented src but it's just a
quick hack. However, any comments are highly appreciated.

Please note that \paragraph{ foo bar } will become (paragraph (foo
bar)). I make this because there might be a limit on the number of
arguments given to a function.

Regards
AHz


(defun read-list-if-character (char stream terminator)
   (let ((next-char (read-char stream)))  ; TODO: Eat whitespaces
     (if (char= next-char char)
	(read-delimited-list terminator stream t)
       (unread-char next-char stream))))

(defun tex-reader-function (stream char)
   (declare (ignore char))
   (let ((*readtable* (copy-readtable)))
	(no-char-fct (get-macro-character #\A)))
     (set-macro-character #\[ no-char-fct)
     (set-macro-character #\] no-char-fct)
     (let* ((function (list (read stream)))
	   (options  (read-list-if-character #\[ stream #\]))
	   (value    (read-list-if-character #\{ stream #\})))
       (when value
	(push value function))
       (when options
	(push :options function)
	(push options  function))
       (reverse function))))

(defun tex-read-list (stream char)
   (declare (ignore char))
   (read-delimited-list #\} stream t))

(defconstant +macro-characters+ '(#\# #\, #\` #\' #\( #\) ))
(defconstant +terminating-characters+ '(#\. #\, #\? #\! ))

(defun tex-read (&optional input-stream eof-error-p eof-value recursive-p)
   "Read input from a tex-like file"
   (let ((*readtable* (copy-readtable))
	(start-list (get-macro-character #\( ))
	(end-list (get-macro-character #\)   ))
	(no-char-fct (get-macro-character #\A)))

     (dolist (c +macro-characters+)  ; Kill default macro chars
       (set-syntax-from-char  c  #\A))

     (dolist (c +terminating-characters+) ; make new terminating chars
       (set-macro-character c no-char-fct))

     (set-macro-character #\\ #'tex-reader-function)
     (set-macro-character #\{ #'tex-read-list)
     (set-macro-character #\} end-list)
     (setf (readtable-case *readtable*) :preserve)
     (read input-stream eof-error-p eof-value recursive-p)))

;; some spare tests
(prin1 (tex-read (make-string-input-stream "foo")))
(prin1 (tex-read (make-string-input-stream "{foo bar}")))
(prin1 (tex-read (make-string-input-stream "\\foo bar")))
(prin1 (tex-read (make-string-input-stream "\\foo{ bar }")))
(prin1 (tex-read (make-string-input-stream "\\foo[what ever you want]{
bar }")))
(prin1 (tex-read (make-string-input-stream "\\paragraph[In the beginning]{
                Once upon a time, in a land far far
                away, there lived the deranged developer.
            }")))
From: goose
Subject: Re: writing an interpreter
Date: 
Message-ID: <1148982518.049605.232290@j55g2000cwa.googlegroups.com>
Hello

this:

> (dolist (c +terminating-characters+) ; make new terminating chars
>            (set-macro-character c no-char-fct))

gives the following error in clisp:

*** - SET-MACRO-CHARACTER: undefined function NIL

A similar complaint is raised by cmucl. I don't really
understand readtables, so I cannot figure out what it
is supposed to be doing at that point (I'm not even
sure  why set-macro-character wants a function).

I am slowing working my way through this function
to try and figure out how it accomplishes what it does.

HAND,
goose
From: Andreas
Subject: Re: writing an interpreter
Date: 
Message-ID: <4e2iq3F1bojscU2@uni-berlin.de>
Hi again,

the code that i sent is not very portable.
Here is a version that run at least on LWW and
CMUCL.

Regards
AHz

(defun read-list-if-character (char stream terminator)
   (let ((next-char (read-char stream)))  ; TODO: Eat whitespaces
     (if (char= next-char char)
         (read-delimited-list terminator stream t)
       (unread-char next-char stream))))

(defun tex-read-self (stream char)
   (declare (ignore stream))
   char)

(defun tex-reader-function (stream char)
   (declare (ignore char))
   (let ((*readtable* (copy-readtable)))
     (set-macro-character #\[ #'tex-read-self)
     (set-macro-character #\] #'tex-read-self)
     (let* ((function (list (read stream)))
            (options  (read-list-if-character #\[ stream #\]))
            (value    (read-list-if-character #\{ stream #\})))
       (when value
         (push value function))
       (when options
         (push :options function)
         (push options  function))

       (reverse function))))

(defun tex-read-list (stream char)
   (declare (ignore char))
   (read-delimited-list #\} stream t))

(defconstant +macro-characters+ '(#\# #\, #\` #\' #\( #\) ))
(defconstant +terminating-characters+ '(#\. #\, #\? #\! ))

(defun tex-read (&optional input-stream eof-error-p eof-value recursive-p)
   "Read input from a tex-like file"
   (let ((*readtable* (copy-readtable))
         (start-list (get-macro-character #\( ))
         (end-list (get-macro-character #\)   )))

     (dolist (c +macro-characters+)  ; Kill default macro chars
       (set-macro-character c #'tex-read-self))

     (dolist (c +terminating-characters+) ; make new terminating chars
       (set-macro-character c #'tex-read-self))

     (set-macro-character #\\ #'tex-reader-function)
     (set-macro-character #\{ #'tex-read-list)
     (set-macro-character #\} end-list)
     (setf (readtable-case *readtable*) :preserve)
     (read input-stream eof-error-p eof-value recursive-p)))

(prin1 (tex-read (make-string-input-stream "foo")))
(prin1 (tex-read (make-string-input-stream "{foo bar}")))
(prin1 (tex-read (make-string-input-stream "\\foo bar")))
(prin1 (tex-read (make-string-input-stream "\\foo{ bar }")))
(prin1 (tex-read (make-string-input-stream "\\foo[what ever you want]{
bar }")))
(prin1 (tex-read (make-string-input-stream "\\paragraph[In the beginning]{
                Once upon a time, in a land far far
                away, there lived the deranged developer.
            }")))
From: Andreas
Subject: Re: writing an interpreter
Date: 
Message-ID: <4e2ireF1bojscU3@uni-berlin.de>
Hi again,

the code that i sent is not very portable.
Here is a version that run at least on LWW and
CMUCL.

Regards
AHz

(defun read-list-if-character (char stream terminator)
   (let ((next-char (read-char stream)))  ; TODO: Eat whitespaces
     (if (char= next-char char)
         (read-delimited-list terminator stream t)
       (unread-char next-char stream))))

(defun tex-read-self (stream char)
   (declare (ignore stream))
   char)

(defun tex-reader-function (stream char)
   (declare (ignore char))
   (let ((*readtable* (copy-readtable)))
     (set-macro-character #\[ #'tex-read-self)
     (set-macro-character #\] #'tex-read-self)
     (let* ((function (list (read stream)))
            (options  (read-list-if-character #\[ stream #\]))
            (value    (read-list-if-character #\{ stream #\})))
       (when value
         (push value function))
       (when options
         (push :options function)
         (push options  function))

       (reverse function))))

(defun tex-read-list (stream char)
   (declare (ignore char))
   (read-delimited-list #\} stream t))

(defconstant +macro-characters+ '(#\# #\, #\` #\' #\( #\) ))
(defconstant +terminating-characters+ '(#\. #\, #\? #\! ))

(defun tex-read (&optional input-stream eof-error-p eof-value recursive-p)
   "Read input from a tex-like file"
   (let ((*readtable* (copy-readtable))
         (start-list (get-macro-character #\( ))
         (end-list (get-macro-character #\)   )))

     (dolist (c +macro-characters+)  ; Kill default macro chars
       (set-macro-character c #'tex-read-self))

     (dolist (c +terminating-characters+) ; make new terminating chars
       (set-macro-character c #'tex-read-self))

     (set-macro-character #\\ #'tex-reader-function)
     (set-macro-character #\{ #'tex-read-list)
     (set-macro-character #\} end-list)
     (setf (readtable-case *readtable*) :preserve)
     (read input-stream eof-error-p eof-value recursive-p)))

(prin1 (tex-read (make-string-input-stream "foo")))
(prin1 (tex-read (make-string-input-stream "{foo bar}")))
(prin1 (tex-read (make-string-input-stream "\\foo bar")))
(prin1 (tex-read (make-string-input-stream "\\foo{ bar }")))
(prin1 (tex-read (make-string-input-stream "\\foo[what ever you want]{
bar }")))
(prin1 (tex-read (make-string-input-stream "\\paragraph[In the beginning]{
                Once upon a time, in a land far far
                away, there lived the deranged developer.
            }")))