Skip to content

Commit

Permalink
Added working REPL
Browse files Browse the repository at this point in the history
  • Loading branch information
soegaard committed Mar 11, 2012
1 parent 6f05d4c commit 3541c19
Show file tree
Hide file tree
Showing 2 changed files with 214 additions and 0 deletions.
54 changes: 54 additions & 0 deletions latex.rkt
@@ -0,0 +1,54 @@
#reader scribble/reader
#lang racket
(require racket/system racket/gui/base)
(provide latex)

(define str string-append)

(define TEMPLATE
@str{\documentclass[a0,landscape]{article}
\usepackage[mathletters]{ucs}
\usepackage[utf8x]{inputenc}
\usepackage{amsmath}
\pagestyle{empty}
\usepackage{breqn}
\begin{document}
\hsize=150mm
\begin{dmath*}[style={\huge}]
~a
\end{dmath*}
\end{document}})

(define COMMANDS
@str{/usr/texbin/pdflatex x.tex
/opt/local/bin/convert -density 96x96 x.pdf -trim +repage x.png})

(define (latex . strs)
(define latex (make-temporary-file "latex~a" 'directory))
(define (run)
(parameterize ([current-directory latex]
[current-input-port (open-input-bytes #"")]
[current-output-port (open-output-string)])
(call-with-output-file* "x.tex" #:exists 'truncate
(lambda (o) (fprintf o TEMPLATE (string-append* strs))))
(unless (system (regexp-replace #rx"\n+" COMMANDS " \\&\\& "))
(display (get-output-string (current-output-port))
(current-error-port))
(error 'latex
"commands did not run successfully, see above output"))
(make-object image-snip% "x.png")))
(define (cleanup) (delete-directory/files latex))
; (display-lines (list latex))
(dynamic-wind void run cleanup))

;; Examples
;@latex{\sum_{i=0}^{\infty}\lambda_i}
;(let ([self @str{\lambda x . x x}])
; @latex{(@self) (@self)})

;;; An example to test breaking lines with various page width
;@latex{
;x^{20}+20\,x^{19}+190\,x^{18}+1140\,x^{17}+4845\,x^{16}+15504\,x^{
; 15}+38760\,x^{14}+77520\,x^{13}+125970\,x^{12}+167960\,x^{11}+184756
; \,x^{10}+167960\,x^9+125970\,x^8+77520\,x^7+38760\,x^6+15504\,x^5+
; 4845\,x^4+1140\,x^3+190\,x^2+20\,x+1}
160 changes: 160 additions & 0 deletions racket-maxima.rkt
@@ -0,0 +1,160 @@
#lang at-exp racket
(require racket/tcp racket/gui/base
(planet jaymccarthy/slideshow-latex/latex2bitmap))

;;;
;;; Maxima in Racket
;;;

;; This module starts an external Maxima process.
;; The function send will send a command to Maxima.
;; The function receive will get the output from Maxima as a list of strings.
;; The various send-* and receive-* functions sends and receives to and from Maxima.
;; The various read-* and display-* functions reads and displays to Racket (DrRacket).


;;; Configuration: Change maxima, latex and dvipng paths here.
(define PORT 8087)
(define MAXIMA-PATH "/Applications/Maxima.app/Contents/Resources/maxima.sh")

(define latex-enabled? #t) ; set to #f to disable latex rendering
(latex-path "/usr/texbin/latex")
(dvipng-path "/usr/texbin/dvipng")
(latex-debug? #f) ; if #t prints errors from LaTeX.

;;; Parameters

(define out (make-parameter #f)) ; output port for sending
(define in (make-parameter #f)) ; input port for receiving

;;; Sending

(define (send str)
(sync (out))
(display str (out))
(flush-output (out)))

(define (send-command str)
(send str))

;; Receiving

(define (receive-line)
(read-line (in)))

(define (receive-welcome-message)
; Due to the flag --very-quiet the welcome is
; a single line containg the pid.
(list (receive-line)))

(define (maybe-receive-line)
(if (sync/timeout 0 (in))
(receive-line)
#f))

(define (receive)
(let ([first-line (receive-line)])
(let loop ([lines (list first-line)])
(let ([line (maybe-receive-line)])
(if line
(loop (cons line lines))
(reverse lines))))))

(define (receive-whitespace)
(let ([c (read-char (in))])
(when (not (char-whitespace? c))
(error 'read-whitespace "expected to receive whitespace " c))))

;; String utilities

(define (blank-line? line)
(andmap char-whitespace? (string->list line)))

(define (labeled-line? line)
(regexp-match #rx"^(\\(.+\\)) (.*)$" line))

(define (remove-$$ str)
(second (regexp-match #px"^\\$\\$(.*)\\$\\$" str)))

(define (string-begins-with-$$? str)
(regexp-match #rx"^\\$\\$.*$" str))

(define (string-ends-with-$$? str)
(regexp-match #rx"^.*\\$\\$$" str))

(define (maybe-add-$$ str)
(string-append
(if (string-begins-with-$$? str)
"" "$$")
str
(if (string-ends-with-$$? str)
"" "$$")))

(define (string-ref-last str)
(if (string=? "" str)
#f
(string-ref str (sub1 (string-length str)))))

;; List utilies

(define (remove-last xs)
(if (empty? xs) xs (drop-right xs 1)))

;; Displaying

(define (display-line datum)
(display datum)
(newline))

(define (display-prompt prompt)
(display prompt)
(display " "))

(define (display-output lines)
(unless (empty? lines)
(display-lines
(if (and latex-enabled? (string-begins-with-$$? (first lines)))
(list (latex-lines (remove-last lines)))
lines))))

(define (latex-lines lines)
(latex (string-append* lines)))

(define (latex str)
(parameterize ([latex-preamble "\\usepackage{breqn}"]
[latex-dpi 90])
(make-object image-snip%
(latex->bitmap
@string-append{\begin{dmath*}[style={\huge}]@str \end{dmath*}}))))


;; Reading

(define (read-command)
(let loop ([lines '()])
(let ([line (read-line)])
(if (memv (string-ref-last line) '(#\$ #\;))
(string-append* (reverse (cons line lines)))
(loop (cons line lines))))))

;; REPL

(define (read-send-receive-loop)
(display-prompt ">")
(send-command (read-command))
(display-output (receive))
(newline)
(read-send-receive-loop))


;; Start Maxima and REPL

(let ([listener (tcp-listen PORT 3 #t)])
(match-let
([(list pin pout pid perr status)
(process* MAXIMA-PATH "--very-quiet" "-s" (format "~a" PORT))])
(let-values ([(lin lout) (tcp-accept listener)])
(parameterize ([in lin] [out pout])
(receive-welcome-message)
(display "Enter a Maxima command. Terminate a command with either ; or $ .\n")
(read-send-receive-loop)))))

0 comments on commit 3541c19

Please sign in to comment.