%---------------------------------------------------------------------------- % Magic tutorial number S-1 %---------------------------------------------------------------------------- \NeedsTeXFormat{LaTeX2e}[1994/12/01] \documentclass[letterpaper,twoside,12pt]{article} \usepackage{epsfig,times} \setlength{\textwidth}{8.5in} \addtolength{\textwidth}{-2.0in} \setlength{\textheight}{11.0in} \addtolength{\textheight}{-2.0in} \setlength{\oddsidemargin}{0in} \setlength{\evensidemargin}{0pt} \setlength{\topmargin}{-0.5in} \setlength{\headheight}{0.2in} \setlength{\headsep}{0.3in} \setlength{\topskip}{0pt} \def\hinch{\hspace*{0.5in}} \def\starti{\begin{center}\begin{tabbing}\hinch\=\hinch\=\hinch\=hinch\hinch\=\kill} \def\endi{\end{tabbing}\end{center}} \def\ii{\>\>\>} \def\q{\special{ps:(") show}\hspace*{0.6em}} \def\mytitle{Magic Tutorial \#S-1: The scheme command-line interpreter} \def\bk{\special{ps:/bksp 2 string def bksp 0 92 put bksp show}\hspace*{0.4em}} %---------------------------------------------------------------------------- \begin{document} \makeatletter \newcommand{\ps@magic}{% \renewcommand{\@oddhead}{\mytitle\hfil\today}% \renewcommand{\@evenhead}{\today\hfil\mytitle}% \renewcommand{\@evenfoot}{\hfil\textrm{--{\thepage}--}\hfil}% \renewcommand{\@oddfoot}{\@evenfoot}} \newcommand{\ps@mplain}{% \renewcommand{\@oddhead}{}% \renewcommand{\@evenhead}{}% \renewcommand{\@evenfoot}{\hfil\textrm{--{\thepage}--}\hfil}% \renewcommand{\@oddfoot}{\@evenfoot}} \makeatother \pagestyle{magic} \thispagestyle{mplain} \begin{center} {\bfseries \Large \mytitle} \\ \vspace*{0.5in} {\itshape Rajit Manohar} \\ \vspace*{0.5in} Department of Computer Science \\ California Institute of Technology \\ Pasadena, CA 91125 \\ \vspace*{0.25in} This tutorial corresponds to Magic version 7. \\ \end{center} \vspace*{0.5in} {\noindent\bfseries\large Tutorials to read first:} \starti \> Read reference \cite{sussman} \endi {\noindent\bfseries\large Commands introduced in this tutorial:} \starti \> :scm-echo-result, :eval, lots of scheme functions \endi {\noindent\bfseries\large Macros introduced in this tutorial:} \starti \> {\itshape (None)} \endi \vspace*{0.75in} \section{Introduction} Magic's original command-line interpreter has some limitations. Some of these include the absence of definitions with arguments, block structure, the ability to define complex functions. We describe an extension which is almost completely backward compatible with the existing magic command-line syntax, but permits the use of Scheme on the command-line. \section{Backward compatibility} To permit existing magic source files to work within the scheme interpreter, we have had to sacrifice one feature of the magic command-line syntax. Single quotes can only be used to quote a single character. The reason for this limitation is that {\itshape unmatched} quotes are used by scheme to stop evaluation of the next input symbol. Parentheses are used by the scheme interpreter. If you use parentheses outside single or double quotes in your magic source files, you might find that the source files don't work properly. To circumvent this problem, simply put your parentheses in double quotes. You can also use backslashes to quote parentheses as in: \starti \ii {\bfseries :macro {\bk}( {\q}echo hello{\q}} \endi Another thing you may notice is that floating-point numbers are parsed as such, and therefore a command such as \starti \ii {\bfseries :echo 5.3} \endi would display the string {\bfseries 5.300000}. If you really want the string {\bfseries 5.3}, use: \starti \ii {\bfseries :echo {\q}5.3{\q}} \endi If this difference is undesirable, the scheme interpreter can be turned off at compile-time. Talk to your local magic maintainer if you want this done. We feel that the minor trouble taken in modifying existing magic source files will be outweighed by the advantage of using a more powerful layout language. \section{The scheme interpreter} The interpreter supports a subset of the scheme language. The features of scheme that are missing include character types, vector types, file input/output, complex numbers, the distinction between exact and inexact arithmetic, quasi-quotations, and continuations. \subsection{Command-line interaction} When interacting with the command-line of magic, the interpreter implicitly parenthesizes its input. For example, the command \starti \ii {\bfseries :paint poly} \endi would be interpreted as the scheme expression \starti \ii {\bfseries (paint poly)} \endi This has exactly the same effect as the original expression, because all existing magic command-line functions are also scheme functions. Since the valid magic commands vary from window to window, the return value of the function is a boolean that indicates whether the command was valid for the current window. The boolean {\bfseries scm-echo-result} controls whether or not the result of the evaluation is displayed. If the variable does not exist, or the variable is not boolean-valued, the result of evaluation is not echoed. Since the input is implicitly parenthesized, typing in \starti \ii {\bfseries :scm-echo-result} \endi would not display the value of the variable, since it would be evaluated as: \starti \ii {\bfseries (scm-echo-result)} \endi To display the value of a variable, use the built-in procedure {\bfseries eval} as follows: \starti \ii {\bfseries :eval scm-echo-result} \endi This would result in the expression: \starti \ii {\bfseries (eval scm-echo-result)} \endi which would have the desired effect (note that for this to actually display anything, the value of {\bfseries scm-echo-result} must be {\bfseries \#t}, and so examining its value is really a futile exercise---which is why it is an example, of course!). \subsection{Types of arguments} Since scheme expressions are typed, we may need to examine the type of a particular expression. The following functions return booleans, and can be used to determine the type of an object. {\bfseries \#t} and {\bfseries \#f} are constants representing the booleans true and false. A standard scheme convention is to name functions that return booleans with a name ending with ``?''. The built-in functions conform to this convention. The expression {\itshape expr} is evaluated, and the type of the result of evaluation is checked. \begin{center} \begin{tabular}{|l|l|} \hline {\bfseries (boolean?} {\itshape expr}{\bfseries )} & {\bfseries \#t} if {\itshape expr} is a boolean \\ \hline {\bfseries (symbol?} {\itshape expr}{\bfseries )} & {\bfseries \#t} if {\itshape expr} is a symbol \\ \hline {\bfseries (list?} {\itshape expr}{\bfseries )} & {\bfseries \#t} if {\itshape expr} is a list \\ \hline {\bfseries (pair?} {\itshape expr}{\bfseries )} & {\bfseries \#t} if {\itshape expr} is a pair \\ \hline {\bfseries (number?} {\itshape expr}{\bfseries )} & {\bfseries \#t} if {\itshape expr} is a number \\ \hline {\bfseries (string?} {\itshape expr}{\bfseries )} & {\bfseries \#t} if {\itshape expr} is a string \\ \hline {\bfseries (procedure?} {\itshape expr}{\bfseries )} & {\bfseries \#t} if {\itshape expr} is a procedure \\ \hline \end{tabular} \end{center} For example, \starti \ii {\bfseries (boolean? \#t)}{\itshape $=>$ \#t} \\ \ii {\bfseries (number? \#t)}{\itshape $=>$ \#f} \endi \subsection{Lists and pairs} A pair is a record structure with two fields, called the car and cdr fields (for historical reasons). Pairs are used primarily to represent lists. A list can be defined recursively as either the empty list, or a pair whose cdr field is a list. The following functions are used to extract these fields and to construct new pairs and lists. \begin{center} \begin{tabular}{|l|l|} \hline {\bfseries (car} {\itshape pair}{\bfseries )} & the car field of {\itshape pair} \\ \hline {\bfseries (cdr} {\itshape pair}{\bfseries )}& the cdr field {\itshape pair} \\ \hline {\bfseries (cons} {\itshape obj1 obj2}{\bfseries )} & a new pair whose car field is {\itshape obj1} and cdr field is {\itshape obj2} \\ \hline {\bfseries (list} {\itshape arg1 \dots}{\bfseries )} & a new list consisting of its arguments \\ \hline {\bfseries (null?} {\itshape list}{\bfseries )} & {\bfseries \#t} if {\itshape list} is the empty list \\ \hline {\bfseries (length} {\itshape list}{\bfseries )} & the number of elements in {\itshape list} \\ \hline \end{tabular} \end{center} For example, \starti \ii {\bfseries (car '(a b c))}{\itshape =$>$ a} \\ \ii {\bfseries (cdr '(a b c))}{\itshape =$>$ (b c)} \\ \ii {\bfseries (cons 'a '(b c))}{\itshape =$>$ (a b c)} \\ \ii {\bfseries (list 'a 'b 'c)}{\itshape =$>$ (a b c)} \\ \ii {\bfseries (null? '(a b))}{\itshape =$>$ \#f} \\ \ii {\bfseries (null? ())}{\itshape =$>$ \#t} \endi The car field and cdr field of a pair can be set using the following two functions. \begin{center} \begin{tabular}{|l|l|} \hline {\bfseries (set-car!} {\itshape pair obj}{\bfseries )} & sets the car field of {\itshape pair} to {\itshape obj}. It returns the new pair \\ \hline {\bfseries (set-cdr!} {\itshape pair obj}{\bfseries )} & sets the cdr field of {\itshape pair} to {\itshape obj}. It returns the new pair \\ \hline \end{tabular} \end{center} These two functions have {\itshape side-effects}, another feature that distinguishes scheme from pure lisp. Another naming convention followed is that functions that have side-effects end in ``!''. Try the following sequence in magic: \starti \ii {\bfseries (define x '(a b))}{\itshape =$>$ (a b)} \\ \ii {\bfseries (set-car! x 'c)}{\itshape =$>$ (c b)} \\ \ii {\bfseries (set-cdr! x '(q))}{\itshape =$>$ (c q)} \\ \ii {\bfseries (set-cdr! x 'q)}{\itshape =$>$ (c . q)} \\ \endi After the last statement, the value of x is no longer a list but a pair. \subsection{Arithmetic} The interpreter supports both floating-point and integer arithmetic. The basic arithmetic functions are supported. \begin{center} \begin{tabular}{|l|l|} \hline {\bfseries (+ }{\itshape num1 num2}{\bfseries )} & the sum {\itshape num1}+{\itshape num2} \\ \hline {\bfseries (- }{\itshape num1 num2}{\bfseries )} & the difference {\itshape num1}-{\itshape num2} \\ \hline {\bfseries (* }{\itshape num1 num2}{\bfseries )} & the product {\itshape num1}*{\itshape num2} \\ \hline {\bfseries (/ }{\itshape num1 num2}{\bfseries )} & the quotient {\itshape num1}/{\itshape num2} \\ \hline {\bfseries (truncate }{\itshape num}{\bfseries )} & the integer part of {\itshape num} \\ \hline \end{tabular} \end{center} The division operator checks for division by zero, and promotes integers to floating-point if deemed necessary. Floating-point numbers can be converted into integers by truncation. The range of a number can be checked using the following predicates: \begin{center} \begin{tabular}{|l|l|} \hline {\bfseries (zero? }{\itshape num}{\bfseries )} & {\bfseries \#t} if {\itshape num} is zero \\ \hline {\bfseries (positive? }{\itshape num}{\bfseries )} & {\bfseries \#t} if {\itshape num} is positive \\ \hline {\bfseries (negative? }{\itshape num}{\bfseries )} & {\bfseries \#t} if {\itshape num} is negative \\ \hline \end{tabular} \end{center} \subsection{Strings} The interpreter supports string manipulation. String manipulation can be useful for interaction with the user as well as constructing names for labels. \begin{center} \begin{tabular}{|l|p{0.6\columnwidth}|} \hline {\bfseries (string-append }{\itshape str1 str2}{\bfseries )} & the string formed by concatenating {\itshape str1} and {\itshape str2} \\ \hline {\bfseries (string-length }{\itshape str}{\bfseries )} & the length of string {\itshape str} \\ \hline {\bfseries (string-compare }{\itshape str1 str2}{\bfseries )} & a positive, zero, or negative number depending on whether {\itshape str1} is lexicographically greater, equal to, or less than {\itshape str2} \\ \hline {\bfseries (string-ref }{\itshape str int}{\bfseries )} & the numerical value of the character stored at position {\itshape int} in {\itshape str} (The first character is at position 0.) \\ \hline {\bfseries (string-set! }{\itshape str int1 int2}{\bfseries )} & sets character in string {\itshape str} at position {\itshape int1} to {\itshape int2} \\ \hline {\bfseries (substring }{\itshape str int1 int2}{\bfseries )} & returns substring of {\itshape str} from position {\itshape int1} (inclusive) to {\itshape int2} (exclusive) \\ \hline \end{tabular} \end{center} Strings can be used to convert to and from various types. \begin{center} \begin{tabular}{|l|l|} \hline {\bfseries (number-$>$string }{\itshape num}{\bfseries )} & the string corresponding to the representation of {\itshape num} \\ \hline {\bfseries (string-$>$number }{\itshape str}{\bfseries )} & the number corresponding to {\itshape str} \\ \hline {\bfseries (string-$>$symbol }{\itshape str}{\bfseries )} & a symbol named {\itshape str} \\ \hline {\bfseries (symbol$->$string }{\itshape sym}{\bfseries )} & the string corresponding to the name of {\itshape sym} \\ \hline \end{tabular} \end{center} \subsection{Bindings and functions} An object (more accurately, the {\itshape location} where the object is stored) can be bound to a symbol using the following two functions: \begin{center} \begin{tabular}{|l|l|} \hline {\bfseries (define }{\itshape sym expr}{\bfseries )} & bind {\itshape expr} to {\itshape sym}, creating a new symbol if necessary and return {\itshape expr} \\ \hline {\bfseries (set! }{\itshape sym expr}{\bfseries )} & bind {\itshape expr} to an existing symbol {\itshape sym} and return {\itshape expr} \\ \hline \end{tabular} \\ (Note: these functions do not evaluate their first argument.) \end{center} The difference between the two is that {\bfseries define} introduces a new binding, whereas {\bfseries set!} modifies an existing binding. In both cases, {\itshape expr} is evaluated, and the result is bound to the symbol {\itshape sym}. The result of the evaluation is also returned. \starti \ii {\bfseries (define x 4)}{\itshape $=>$ 4} \endi Functions can be defined using lambda expressions. Typically a function is bound to a variable. If required, a lambda expression or built-in function can be applied to a list. \begin{center} \begin{tabular}{|l|l|} \hline {\bfseries (lambda }{\itshape list obj}{\bfseries )} & a new function \\ \hline \end{tabular} \\ (Note: a lambda does not evaluate its arguments.) \end{center} {\itshape list} is a list of symbol names, and {\itshape obj} is the expression that corresponds to the body of the function. For example, \starti \ii {\bfseries (lambda (x y z) (+ (+ x y) z))}{\itshape $=>$ \#proc} \endi is a function that takes three arguments and returns their sum. It can be bound to a symbol using {\bfseries define}. \starti \ii {\bfseries (define sum3 (lambda (x y z) (+ (+ x y) z)))}{\itshape $=>$ \#proc} \endi Now, we can use {\bfseries sum3} like any other function. \starti \ii {\bfseries (sum3 5 3 8)}{\itshape $=>$ 16} \endi A function can be applied to a list using {\bfseries apply}. \begin{center} \begin{tabular}{|l|l|} \hline {\bfseries (apply }{\itshape proc list}{\bfseries )} & apply {\itshape proc} to {\itshape list} \\ \hline \end{tabular} \\ (Note: both {\itshape proc} and {\itshape list} are evaluated before application.) \end{center} {\itshape list} is used as the list of arguments for the function. For instance, an alternative way to sum the three numbers in the example above is: \starti \ii {\bfseries (apply sum3 '(3 5 8))}{\itshape $=>$ 16} \endi An alternative method for creating bindings is provided by the {\bfseries let} mechanism. \begin{center} \begin{tabular}{|l|l|} \hline {\bfseries (let }{\itshape binding-list expr}{\bfseries )} & evaluate {\itshape expr} after the bindings have been performed \\ \hline {\bfseries (let* }{\itshape binding-list expr}{\bfseries )} & evaluate {\itshape expr} after the bindings have been performed \\ \hline {\bfseries (letrec }{\itshape binding-list expr}{\bfseries )} & evaluate {\itshape expr} after the bindings have been performed \\ \hline \end{tabular} \end{center} The {\itshape binding-list} is a list of bindings. Each binding is a list containing a symbol and an expression. The expression is evaluated and bound to the symbol. In the case of {\bfseries let}, all the expressions are evaluated before binding them to any symbol; {\bfseries let*}, on the other hand, evaluates an expression and binds it to the symbol before evaluating the next expression. {\bfseries letrec} permits bindings to refer to each other, permitting mutually recursive function definitions. The evaluation order is defined to be from left to right in all cases. After performing the bindings, {\itshape expr} is evaluated with the new bindings in effect and the result is returned. {\bfseries let} bindings can be used in interesting ways. An example of their use is provided later. Scheme is an eager language, and only a few functions do not evaluate all their arguments (definitions and conditionals). Evaluation can be controlled to some degree using the following two functions: \begin{center} \begin{tabular}{|l|l|} \hline {\bfseries (quote }{\itshape obj}{\bfseries )} & the unevaluated object {\itshape obj} \\ \hline {\bfseries (eval }{\itshape obj}{\bfseries )} & evaluates object {\itshape obj} \\ \hline \end{tabular} \end{center} \subsection{Control structures} Since scheme is a functional programming language, functions that are usually written using loops are written using recursion. Conditional constructs are used to terminate the recursion. These constructs are slightly different in that they do not evaluate all their arguments (otherwise recursive functions would not terminate!). \begin{center} \begin{tabular}{|l|l|} \hline {\bfseries (if }{\itshape expr arg1 arg2}{\bfseries )} & evaluate {\itshape expr} and evaluate one of {\itshape arg1} or {\itshape arg2} \\ \hline \end{tabular} \end{center} The {\bfseries if} construct evaluates its first argument (which must result in a boolean), and if the result is {\bfseries \#t} evaluates {\itshape arg1} and returns the result; otherwise {\itshape arg2} is evaluated and returned. For instance, the standard factorial function might be written as: \starti \ii {\bfseries (define fact (lambda (x) (if (positive? x) (* x (fact (- x 1))) 1)))} \endi A more complicated form of conditional behavior is provided by {\bfseries cond}. \begin{center} \begin{tabular}{|l|l|} \hline {\bfseries (cond }{\itshape arg1 arg2 ...}{\bfseries )} & generalized conditional \\ \hline \end{tabular} \end{center} Each argument consists of a list which contains two expressions. The first expression is evaluated (and must evaluate to a boolean), and if it is true the second expression is evaluated and returned as the result of the entire expression. If the result was false, the next argument is examined and the above procedure is repeated. If all arguments evaluate to false, the result is undefined. For instance if {\bfseries x} was a list, the expression \starti \ii {\bfseries (cond ((null? x) x) ((list? x) (car x)) (\#t (echo {\q}error{\q})))} \endi would return the empty list if {\bfseries x} was the empty list and the first element from the list otherwise. When {\bfseries x} is not a list, an error message is displayed. Note that {\bfseries echo} is a standard magic command. Often one needs to evaluate a number of expressions in sequence (since the language has side-effects). The {\bfseries begin} construct can be used for this purpose. \begin{center} \begin{tabular}{|l|l|} \hline {\bfseries (begin }{\itshape arg1 arg2 \dots}{\bfseries )} & sequencing construct \\ \hline \end{tabular} \end{center} {\bfseries begin} evaluates each of its arguments in sequence, and returns the result of evaluating its last argument. \subsection{Interaction with layout} All standard magic commands are also scheme functions. This permits one to write scheme functions that interact with the layout directly. Apart from the standard magic commands, the following scheme functions are provided so as to enable the user to edit layout. \begin{center} \begin{tabular}{|l|p{0.6\columnwidth}|} \hline {\bfseries (getbox)} & a list containing four members (llx lly urx ury) \\ \hline {\bfseries (getpaint }{\itshape str}{\bfseries )} & a list containing the boxes from layer {\itshape str} under the current box that have paint in them \\ \hline {\bfseries (getlabel }{\itshape str}{\bfseries )} & a list containing the labels under the current box that match {\itshape str} \\ \hline {\bfseries (magic }{\itshape sym}{\bfseries )} & forces {\itshape sym} to be interpreted as a magic command \\ \hline \end{tabular} \end{center} The pairs (llx,lly) and (urx,ury) correspond to magic coordinates for the lower left and upper right corner of the current box. {\bfseries getpaint} returns a list of boxes (llx lly urx ury), and {\bfseries getlabel} returns a list of tagged boxes (label llx lly urx ury) which contain the label string. {\bfseries magic} can be used to force the scheme interpreter to interpret a symbol as a magic procedure. The evaluation returns the specified magic command. \subsection{Miscellaneous} Some additional functions are provided to enable the user to debug functions. \begin{center} \begin{tabular}{|l|l|} \hline {\bfseries (showframe)} & display the current list of bindings \\ \hline {\bfseries (display-object }{\itshape obj}{\bfseries )} & display the type and value of {\itshape obj} \\ \hline {\bfseries (error }{\itshape str}{\bfseries )} & display error message and abort evaluation \\ \hline {\bfseries (eqv? }{\itshape obj1 obj2}{\bfseries )} & checks if two objects are equal \\ \hline {\bfseries (collect-garbage)} & force garbage collection \\ \hline \end{tabular} \end{center} The following is a complete list of the built-in scheme variables that can be used to control the interpreter. \begin{center} \begin{tabular}{|l|p{0.6\columnwidth}|} \hline {\bfseries scm-library-path} & a colon-separated path string \\ \hline {\bfseries scm-echo-result} & a boolean used to determine if the result of evaluation should be displayed \\ \hline {\bfseries scm-trace-magic} & controls display of actual magic commands \\ \hline {\bfseries scm-echo-parser-input} & controls display of the string sent to the scheme parser \\ \hline {\bfseries scm-echo-parser-output} & controls display of the result of parsing \\ \hline {\bfseries scm-stack-display-depth} & controls the number of frames displayed in the stack trace output when an error occurs during evaluation \\ \hline {\bfseries scm-gc-frequency} & controls the frequency of garbage collection \\ \hline \end{tabular} \end{center} \subsection{Libraries} The following function loads in a file and evaluates its contents in order. \begin{center} \begin{tabular}{|l|l|} \hline {\bfseries (load-scm }{\itshape str}{\bfseries )} & reads scheme commands in from the named file \\ \hline {\bfseries (save-scm }{\itshape str obj}{\bfseries )} & appends {\itshape obj} to the file {\itshape str}, creating a new file if necessary \\ \hline \end{tabular} \end{center} The file can be in the current directory, or in any of the locations specified by a string containing a colon-separated list of directory names stored in {\bfseries scm-library-path}. The format of these files differs from standard magic source files because the contents of a line are not implicitly parenthesized. In addition, semicolons are used as a comment character; everything following a semicolon to the end of the current line is treated as a comment. For instance, \starti \ii {\bfseries define f (lambda (x) x)} \endi would define {\bfseries f} to be the identity function when placed in a magic source file (so as to provide backward compatibility). The same definition would result in an error if placed in a scheme source file. \starti \ii {\bfseries (define f (lambda (x) x))} \endi The above expression should be used in the scheme file to achieve the same effect. \begin{thebibliography}{2} \bibitem{sussman} H. Abelson and G.J. Sussman, \newblock {\itshape Structure and Interpretation of Computer Programs}. \bibitem{abelson} H. Abelson {\itshape et al.}, \newblock {\itshape Revised Report on the Algorithmic Language Scheme}. \end{thebibliography} \end{document}