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