Why don't people like lisp?
Peter Seibel
peter at javamonkey.com
Wed Oct 22 01:57:54 EDT 2003
"Andrew Dalke" <adalke at mindspring.com> writes:
> Me:
> > Python has the ability to do exactly what you're saying (domain
> > language -> AST -> Python code or AST -> compiler). It's rarely
> > needed (I've used it twice now in my six years or so of Python),
> > so why should a language cater to make that easy at the expense of
> > making frequent things harder?
>
> As an example, here's a quick hack of a way to parse a simple
> stack-based language and make a native Python function out of it. I
> am the first to admit that it's ugly code, but it does work.
>
> I am curious to see the equivalent code in Lisp. Here's the spec
>
> All tokens are separated by whitespace (space, tab, newline, and
> include vertical tab and form feed if you want).
>
> Numbers are the standard IEEE floats (excepting NaN, Inf, etc)
> and represented "as usual".
>
> The operators are addition ("+"), subtraction ("-"), multiplication
> ("*"), division ("/"), and exponentiation ("**"). These are binary
> operators only, so to get -b use 0 b - .
>
> The variable names are of the form [A-Za-Z_][A-Za-z0-9_]*
> and must be passed to the function when making the function call.
> The order of names is the same as the order the names appear
> in the RPN expression, so in "b a +" the function created takes
> "b" as the first argument and "a" as the second.
Okay, here's a Common Lisp version. Because Lisp functions take either
keyword or positional arguments, I choose to make the generated
functions use keyword arguments. But the variables are gathered in the
right order, so take out the '&key' below to use positional arguments:
(defun compile-rpn (input)
(compile nil (parse input)))
(defun parse (input)
(let (stack variables (idx 0))
(loop
(multiple-value-bind (tok next-idx) (read-from-string input nil nil :start idx)
(labels
((line-number () (1+ (count #\Newline input :end idx)))
(pop-arg ()
(or (pop stack)
(error "Not enough arguments for ~a at position ~d (line ~d)." tok idx (line-number)))))
(cond
((not tok) (return `(lambda (&key ,@(nreverse variables)) (prog1 , at stack))))
((member tok '(+ * - /)) (push (cons tok (reverse (list (pop-arg) (pop-arg)))) stack))
((eq tok '**) (push (cons 'expt (reverse (list (pop-arg) (pop-arg)))) stack))
((numberp tok) (push tok stack))
((variablep tok) (push tok stack) (pushnew tok variables))
(t (error "Invalid token ~a at position ~d (line ~d)." tok idx (line-number))))
(setq idx next-idx))))))
(defun variablep (sym)
(and (symbolp sym) (every #'alpha-char-p (string sym))))
Here's how it works (FUNCALL is the Common Lisp operator to calls a
function object such as the one returned by compile-rpn):
* (funcall (compile-rpn "1 2 3 4 5 + + + + a **") :a 2)
225
Compile-time error messages:
* (compile-rpn "a + 2 * b")
Error: Not enough arguments for + at position 2 (line 1).
* (compile-rpn "5t")
Error: Invalid token 5T at position 0 (line 1).
Runtime error handling:
* (funcall (compile-rpn "a b +") :a "foo" :b 2)
Error: `"foo"' is not of the expected type `NUMBER'
And for grins here's the x86 machine code generated for the first function:
* (disassemble (compile-rpn "1 2 3 4 5 + + + + a **"))
;; disassembly of #<Function (:ANONYMOUS-LAMBDA 51) @ #x7608e53a>
;; formals: &KEY A
;; constant vector:
0: NIL
1: 1
2: :A
3: EXPT
;; code start: #x7608e4d4:
0: 55 pushl ebp
1: 8b ec movl ebp,esp
3: 56 pushl esi
4: 83 ec 2c subl esp,$44
7: 89 45 08 movl [ebp+8],eax
10: 89 55 0c movl [ebp+12],edx
13: 8d 09 leal ecx,[ecx+0]
15: 8d 45 08 leal eax,[ebp+8]
18: 8d 55 e0 leal edx,[ebp-32]
21: 33 db xorl ebx,ebx
23: b3 48 movb bl,$72
25: ff 57 4f call *[edi+79] ; SYS::KWDSCAN
28: 8d 5d e0 leal ebx,[ebp-32]
31: 8b 13 movl edx,[ebx+0]
33: 3b fa cmpl edi,edx
35: 75 1a jnz 63
37: 8b d7 movl edx,edi
39: 80 7f 97 00 cmpb [edi-105],$0 ; SYS::C_INTERRUPT
43: 74 02 jz 47
45: cd 64 int $100 ; EXCL::TRAP-SIGNAL-HIT
47: 8b 5e 1e movl ebx,[esi+30] ; EXPT
50: 33 c0 xorl eax,eax
52: b0 3c movb al,$60
54: ff 57 27 call *[edi+39] ; SYS::TRAMP-TWO
57: f8 clc
58: c9 leave
59: 8b 75 fc movl esi,[ebp-4]
62: c3 ret
63: 8b 53 04 movl edx,[ebx+4]
66: eb e3 jmp 39
-Peter
--
Peter Seibel peter at javamonkey.com
Lisp is the red pill. -- John Fraser, comp.lang.lisp
More information about the Python-list
mailing list