source: CGBLisp/src/RCS/infix.lisp,v@ 1

Last change on this file since 1 was 1, checked in by Marek Rychlik, 15 years ago

First import of a version circa 1997.

File size: 41.0 KB
Line 
1head 1.4;
2access;
3symbols;
4locks; strict;
5comment @;;; @;
6
7
81.4
9date 2009.01.22.04.02.59; author marek; state Exp;
10branches;
11next 1.3;
12
131.3
14date 2009.01.19.09.26.04; author marek; state Exp;
15branches;
16next 1.2;
17
181.2
19date 2009.01.19.07.53.56; author marek; state Exp;
20branches;
21next 1.1;
22
231.1
24date 2009.01.19.06.48.18; author marek; state Exp;
25branches;
26next ;
27
28
29desc
30@@
31
32
331.4
34log
35@*** empty log message ***
36@
37text
38@;;; -*- Mode: Lisp -*-
39;;; $Id$
40;;; ****************************************************************
41;;; CHANGE LOG:
42;;; * (1997/12/6) Fixed code for valid-numberp, so that "" is no longer
43;;; recognized as valid number
44;;; * (1997/11/29) Modified by M. Rychlik(rychlik@@math.arizona.edu):
45;;; The role of operators ^^ (exponentiation) and ^ (logxior)
46;;; of the original version was reversed
47;;; ****************************************************************
48;;;
49
50;;; Wed Jan 18 13:13:59 1995 by Mark Kantrowitz <mkant@@FLATHEAD.OZ.CS.CMU.EDU>
51;;; infix.cl -- 40545 bytes
52
53;;; **************************************************************************
54;;; Infix ********************************************************************
55;;; **************************************************************************
56;;;
57;;; This is an implementation of an infix reader macro. It should run in any
58;;; valid Common Lisp and has been tested in Allegro CL 4.1, Lucid CL 4.0.1,
59;;; MCL 2.0 and CMU CL. It allows the user to type arithmetic expressions in
60;;; the traditional way (e.g., 1+2) when writing Lisp programs instead of
61;;; using the normal Lisp syntax (e.g., (+ 1 2)). It is not intended to be a
62;;; full replacement for the normal Lisp syntax. If you want a more complete
63;;; alternate syntax for Lisp, get a copy Apple's MLisp or Pratt's CGOL.
64;;;
65;;; Although similar in concept to the Symbolics infix reader (#<DIAMOND>),
66;;; no real effort has been made to ensure compatibility beyond coverage
67;;; of at least the same set of basic arithmetic operators. There are several
68;;; differences in the syntax beyond just the choice of #I as the macro
69;;; character. (Our syntax is a little bit more C-like than the Symbolics
70;;; macro in addition to some more subtle differences.)
71;;;
72;;; We initially chose $ as a macro character because of its association
73;;; with mathematics in LaTeX, but unfortunately that character is already
74;;; used in MCL. We switched to #I() because it was one of the few options
75;;; remaining.
76;;;
77;;; Written by Mark Kantrowitz, School of Computer Science,
78;;; Carnegie Mellon University, March 1993.
79;;;
80;;; Copyright (c) 1993 by Mark Kantrowitz. All rights reserved.
81;;;
82;;; Use and copying of this software and preparation of derivative works
83;;; based upon this software are permitted, so long as the following
84;;; conditions are met:
85;;; o no fees or compensation are charged for use, copies,
86;;; distribution or access to this software
87;;; o this copyright notice is included intact.
88;;; This software is made available AS IS, and no warranty is made about
89;;; the software or its performance.
90;;;
91;;; In no event will the author(s) or their institutions be liable to you for
92;;; damages, including lost profits, lost monies, or other special, incidental
93;;; or consequential damages, arising out of or in connection with the use or
94;;; inability to use (including but not limited to loss of data or data being
95;;; rendered inaccurate or losses sustained by third parties or a failure of
96;;; the program to operate as documented) the program, or for any claim by
97;;; any other party, whether in an action of contract, negligence, or
98;;; other tortious action.
99;;;
100;;; Please send bug reports, comments and suggestions to mkant@@cs.cmu.edu.
101;;;
102;;; The current version of this software and a variety of related utilities
103;;; may be obtained from the Lisp Repository by anonymous ftp
104;;; from ftp.cs.cmu.edu [128.2.206.173] in the directory
105;;; user/ai/lang/lisp/code/syntax/infix/
106;;; If your site runs the Andrew File System, you can cd to the AFS directory
107;;; /afs/cs.cmu.edu/project/ai-repository/ai/lang/lisp/code/syntax/infix/
108;;;
109;;; If you wish to be added to the Lisp-Utilities@@cs.cmu.edu mailing list,
110;;; send email to Lisp-Utilities-Request@@cs.cmu.edu with your name, email
111;;; address, and affiliation. This mailing list is primarily for
112;;; notification about major updates, bug fixes, and additions to the Lisp
113;;; Utilities Repository. The mailing list is intended to have low traffic.
114;;;
115
116
117;;; ********************************
118;;; Documentation ******************
119;;; ********************************
120;;;
121;;; Syntax:
122;;;
123;;; Begin the reader macro with #I( and end it with ). For example,
124;;; #I( x^2 + y^2 )
125;;; is equivalent to the Lisp form
126;;; (+ (expt x 2) (expt y 2))
127;;; but much easier to read according to some folks.
128;;;
129;;; If you want to see the expansion, type a quote before the #I form
130;;; at the Lisp prompt:
131;;; > '#I(if x<y<=z then f(x)=x^2+y^2 else f(x)=x^2-y^2)
132;;; (IF (AND (< X Y) (<= Y Z))
133;;; (SETF (F X) (+ (EXPT X 2) (EXPT Y 2)))
134;;; (SETF (F X) (- (EXPT X 2) (EXPT Y 2))))
135;;;
136;;;
137;;; Operators:
138;;;
139;;; NOTE: == is equality, = is assignment (C-style).
140;;;
141;;; \ quoting character: x\-y --> x-y
142;;; ! lisp escape !(foo bar) --> (foo bar)
143;;; ; comment
144;;; x = y assignment (setf x y)
145;;; x += y increment (incf x y)
146;;; x -= y decrement (decf x y)
147;;; x *= y multiply and store (setf x (* x y))
148;;; x /= y divide and store (setf x (/ x y))
149;;; x|y bitwise logical inclusive or (logior x y)
150;;; x^^y bitwise logical exclusive or (logxor x y)
151;;; x&y bitwise logical and (logand x y)
152;;; x<<y left shift (ash x y)
153;;; x>>y right shift (ash x (- y))
154;;; ~x ones complement (unary) (lognot x)
155;;; x and y conjunction (and x y)
156;;; x && y conjunction (and x y)
157;;; x or y disjunction (or x y)
158;;; x || y disjunction (or x y)
159;;; not x negation (not x)
160;;; x^y exponentiation (expt x y)
161;;; x,y sequence (progn x y)
162;;; (x,y) sequence (progn x y)
163;;; also parenthesis (x+y)/z --> (/ (+ x y) z)
164;;; f(x,y) functions (f x y)
165;;; a[i,j] array reference (aref a i j)
166;;; x+y x*y arithmetic (+ x y) (* x y)
167;;; x-y x/y arithmetic (- x y) (/ x y)
168;;; -y value negation (- y)
169;;; x % y remainder (mod x y)
170;;; x<y x>y inequalities (< x y) (> x y)
171;;; x <= y x >= y inequalities (<= x y) (>= x y)
172;;; x == y equality (= x y)
173;;; x != y equality (not (= x y))
174;;; if p then q conditional (when p q)
175;;; if p then q else r conditional (if p q r)
176;;;
177
178
179;;; Precedence:
180;;;
181;;; The following precedence conventions are obeyed by the infix operators:
182;;; [ ( !
183;;; ^
184;;; ~
185;;; * / %
186;;; + -
187;;; << >>
188;;; < == > <= != >=
189;;; &
190;;; ^^
191;;; |
192;;; not
193;;; and
194;;; or
195;;; = += -= *= /=
196;;; ,
197;;; if
198;;; then else
199;;; ] )
200;;;
201;;; Note that logical negation has lower precedence than numeric comparison
202;;; so that "not a<b" becomes (not (< a b)), which is different from the
203;;; C precedence conventions. You can change the precedence conventions by
204;;; modifying the value of the variable *operator-ordering*.
205;;;
206
207
208;;; ********************************
209;;; To Do **************************
210;;; ********************************
211;;;
212;;; Write some more test cases.
213;;; Write some more syntactic optimizations.
214;;; Would really like ~x to be (not x), but need it for (lognot x).
215;;; Support for multiple languages, such as a Prolog parser, a
216;;; strictly C compatible parser, etc.
217
218;;; Create a more declarative format, where there is one big table of
219;;; operators with all the info on them, and also NOT have the list of
220;;; operators in the comment, where they are likely to become wrong when
221;;; changes are made to the code. For example, something like:
222#|
223(define-infix-operators
224 ([ 30 :matchfix aref :end ])
225 (* 20 :infix * )
226 (+ 10 :infix + :prefix + )
227 (& 10 :infix and )
228 (+= 10 :infix #'+=-operator )
229 ...)
230|#
231
232;;; ********************************
233;;; Change Log *********************
234;;; ********************************
235;;;
236;;; 9-MAR-93 mk Created
237;;; 12-MAR-93 mk Fixed defpackage form for Lucid.
238;;; 1.1:
239;;; 14-OCT-93 mk Changed macro character from #$ to #I(). Suggested by
240;;; Scott McKay.
241;;; 1.2:
242;;; 18-JAN-95 norvig Added *print-infix-copyright*, string->prefix, support
243;;; for #I"..." in addition to #i(...) which lets one
244;;; type #i"a|b" which doesn't confuse editors that aren't
245;;; |-aware. Also added := as a synonym for =, so that
246;;; '#i"car(a) := b" yields (SETF (CAR A) B).
247;;;
248;;; 1.3:
249;;; 28-JUN-96 mk Modified infix reader to allow whitespace between the #I
250;;; and the start of the expression.
251
252
253
254
255;;; ********************************
256;;; Implementation Notes ***********
257;;; ********************************
258;;;
259;;; Initially we tried implementing everything within the Lisp reader,
260;;; but found this to not be workable. Parameters had to be passed in
261;;; global variables, and some of the processing turned out to be
262;;; indelible, so it wasn't possible to use any kind of lookahead.
263;;; Center-embedded constructions were also a problem, due to the lack
264;;; of an explicit stack.
265;;;
266;;; So we took another tack, that used below. The #I macro binds the
267;;; *readtable* to a special readtable, which is used solely for tokenization
268;;; of the input. Then the problem is how to correctly parenthesize the input.
269;;; We do that with what is essentially a recursive-descent parser. An
270;;; expression is either a prefix operator followed by an expression, or an
271;;; expression followed by an infix operator followed by an expression. When
272;;; the latter expression is complex, the problem becomes a little tricky.
273;;; For example, suppose we have
274;;; exp1 op1 exp2 op2
275;;; We need to know whether to parenthesize it as
276;;; (exp1 op1 exp2) op2
277;;; or as
278;;; exp1 op1 (exp2 op2 ...)
279;;; The second case occurs either when op2 has precedence over op1 (e.g.,
280;;; * has precedence over +) or op2 and op1 are the same right-associative
281;;; operator (e.g., exponentiation). Thus the algorithm is as follows:
282;;; When we see op1, we want to gobble up exp2 op2 exp3 op3 ... opn expn+1
283;;; into an expression where op2 through opn all have higher precedence
284;;; than op1 (or are the same right-associative operator), and opn+1 doesn't.
285;;; This algorithm is implemented by the GATHER-SUPERIORS function.
286;;;
287;;; Because + and - are implemented in the infix readtable as terminating
288;;; macro cahracters, the exponentiation version of Lisp number syntax
289;;; 1e-3 == 0.001
290;;; doesn't work correctly -- it parses it as (- 1e 3). So we add a little
291;;; cleverness to GATHER-SUPERIORS to detect when the tokenizer goofed.
292;;; Since this requires the ability to lookahead two tokens, we use a
293;;; stack to implement the lookahead in PEEK-TOKEN and READ-TOKEN.
294;;;
295;;; Finally, the expression returned by GATHER-SUPERIORS sometimes needs to
296;;; be cleaned up a bit. For example, parsing a<b<c would normally return
297;;; (< (< a b) c), which obviously isn't correct. So POST-PROCESS-EXPRESSION
298;;; detects this and similar cases, replacing the expression with (< a b c).
299;;; For cases like a<b<=c, it replaces it with (and (< a b) (<= b c)).
300;;;
301
302
303;;; ********************************
304;;; Package Cruft ******************
305;;; ********************************
306
307(defpackage "INFIX"
308 (:use #-:lucid "COMMON-LISP"
309 #+:lucid "LISP" #+:lucid "LUCID-COMMON-LISP")
310 (:export test-infix string->prefix))
311
312(in-package "INFIX")
313
314(pushnew :infix *features*)
315
316#+debug(proclaim '(optimize (speed 0) (debug 3)))
317#-debug(proclaim '(optimize (speed 3) (debug 0)))
318
319(eval-when (compile load eval)
320 (defparameter *version* "1.3 28-JUN-96")
321 (defparameter *print-infix-copyright* t
322 "If non-NIL, prints a copyright notice upon loading this file.")
323
324 (defun infix-copyright (&optional (stream *standard-output*))
325 "Prints an INFIX copyright notice and header upon startup."
326 (format stream "~%;;; ~V,,,'*A" 73 "*")
327 (format stream "~%;;; Infix notation for Common Lisp.")
328 (format stream "~%;;; Version ~A." *version*)
329 (format stream "~%;;; Written by Mark Kantrowitz, ~
330 CMU School of Computer Science.")
331 (format stream "~%;;; Copyright (c) 1993-95. All rights reserved.")
332 (format stream "~%;;; May be freely redistributed, provided this ~
333 notice is left intact.")
334 (format stream "~%;;; This software is made available AS IS, without ~
335 any warranty.")
336 (format stream "~%;;; ~V,,,'*A~%" 73 "*")
337 (force-output stream))
338
339 ;; What this means is you can either turn off the copyright notice
340 ;; by setting the parameter, or you can turn it off by including
341 ;; (setf (get :infix :dont-print-copyright) t) in your lisp init file.
342 (when (and *print-infix-copyright*
343 (not (get :infix :dont-print-copyright)))
344 (infix-copyright)))
345
346;;; ********************************
347;;; Readtable **********************
348;;; ********************************
349
350(defparameter *infix-readtable* (copy-readtable nil))
351(defparameter *normal-readtable* (copy-readtable nil))
352
353(defmacro infix-error (format-string &rest args)
354 `(let ((*readtable* *normal-readtable*))
355 (error ,format-string ,@@args)))
356
357(defun infix-reader (stream subchar arg)
358 ;; Read either #I(...) or #I"..."
359 (declare (ignore arg subchar))
360 (let ((first-char (peek-char nil stream t nil t)))
361 (cond ((char= first-char #\space)
362 (read-char stream) ; skip over whitespace
363 (infix-reader stream nil nil))
364 ((char= first-char #\")
365 ;; Read double-quote-delimited infix expressions.
366 (string->prefix (read stream t nil t)))
367 ((char= first-char #\()
368 (read-char stream) ; get rid of opening left parenthesis
369 (let ((*readtable* *infix-readtable*)
370 (*normal-readtable* *readtable*))
371 (read-infix stream)))
372 (t
373 (infix-error "Infix expression starts with ~A" first-char)))))
374
375(set-dispatch-macro-character #\# #\I #'infix-reader *readtable*) ; was #\# #\$
376
377(defun string->prefix (string)
378 "Convert a string to a prefix s-expression using the infix reader.
379 If the argument is not a string, just return it as is."
380 (if (stringp string)
381 (with-input-from-string (stream (concatenate 'string "#I(" string ")"))
382 (read stream))
383 string))
384
385
386(defun read-infix (stream)
387 (let* ((result (gather-superiors '\) stream)) ; %infix-end-token%
388 (next-token (read-token stream)))
389 (unless (same-token-p next-token '\)) ; %infix-end-token%
390 (infix-error "Infix expression ends with ~A." next-token))
391 result))
392
393(defun read-regular (stream)
394 (let ((*readtable* *normal-readtable*))
395 (read stream t nil t)))
396
397
398
399;;; ********************************
400;;; Reader Code ********************
401;;; ********************************
402
403(defun same-operator-p (x y)
404 (same-token-p x y))
405
406(defun same-token-p (x y)
407 (and (symbolp x)
408 (symbolp y)
409 (string-equal (symbol-name x) (symbol-name y))))
410
411;;; Peeking Token Reader
412
413(defvar *peeked-token* nil)
414(defun read-token (stream)
415 (if *peeked-token*
416 (pop *peeked-token*)
417 (read stream t nil t)))
418(defun peek-token (stream)
419 (unless *peeked-token*
420 (push (read stream t nil t) *peeked-token*))
421 (car *peeked-token*))
422
423;;; Hack to work around + and - being terminating macro characters,
424;;; so 1e-3 doesn't normally work correctly.
425
426(defun fancy-number-format-p (left operator stream)
427 (when (and (symbolp left)
428 (find operator '(+ -) :test #'same-operator-p))
429 (let* ((name (symbol-name left))
430 (length (length name)))
431 (when (and (valid-numberp (subseq name 0 (1- length)))
432 ;; Exponent, Single, Double, Float, or Long
433 (find (subseq name (1- length))
434 '("e" "s" "d" "f" "l")
435 :test #'string-equal))
436 (read-token stream)
437 (let ((right (peek-token stream)))
438 (cond ((integerp right)
439 ;; it is one of the fancy numbers, so return it
440 (read-token stream)
441 (let ((*readtable* *normal-readtable*))
442 (read-from-string (format nil "~A~A~A"
443 left operator right))))
444 (t
445 ;; it isn't one of the fancy numbers, so unread the token
446 (push operator *peeked-token*)
447 ;; and return nil
448 nil)))))))
449
450(defun valid-numberp (string)
451 (let ((saw-dot nil)
452 (char-list (coerce string 'list)))
453 (unless char-list (return-from valid-numberp nil))
454 (dolist (char char-list t)
455 (cond ((char= char #\.)
456 (if saw-dot
457 (return nil)
458 (setq saw-dot t)))
459 ((not (find char "01234567890" :test #'char=))
460 (return nil))))))
461
462;;; Gobbles an expression from the stream.
463
464(defun gather-superiors (previous-operator stream)
465 "Gathers an expression whose operators all exceed the precedence of
466 the operator to the left."
467 (let ((left (get-first-token stream)))
468 (loop
469 (setq left (post-process-expression left))
470 (let ((peeked-token (peek-token stream)))
471 (let ((fancy-p (fancy-number-format-p left peeked-token stream)))
472 (when fancy-p
473 ;; i.e., we've got a number like 1e-3 or 1e+3 or 1f-1
474 (setq left fancy-p
475 peeked-token (peek-token stream))))
476 (unless (or (operator-lessp previous-operator peeked-token)
477 (and (same-operator-p peeked-token previous-operator)
478 (operator-right-associative-p previous-operator)))
479 ;; The loop should continue when the peeked operator is
480 ;; either superior in precedence to the previous operator,
481 ;; or the same operator and right-associative.
482 (return left)))
483 (setq left (get-next-token stream left)))))
484
485(defun get-first-token (stream)
486 (let ((token (read-token stream)))
487 (if (token-operator-p token)
488 ;; It's an operator in a prefix context.
489 (apply-token-prefix-operator token stream)
490 ;; It's a regular token
491 token)))
492
493(defun apply-token-prefix-operator (token stream)
494 (let ((operator (get-token-prefix-operator token)))
495 (if operator
496 (funcall operator stream)
497 (infix-error "~A is not a prefix operator" token))))
498
499(defun get-next-token (stream left)
500 (let ((token (read-token stream)))
501 (apply-token-infix-operator token left stream)))
502
503(defun apply-token-infix-operator (token left stream)
504 (let ((operator (get-token-infix-operator token)))
505 (if operator
506 (funcall operator stream left)
507 (infix-error "~A is not an infix operator" token))))
508
509;;; Fix to read-delimited-list so that it works with tokens, not
510;;; characters.
511
512(defun infix-read-delimited-list (end-token delimiter-token stream)
513 (do ((next-token (peek-token stream) (peek-token stream))
514 (list nil))
515 ((same-token-p next-token end-token)
516 ;; We've hit the end. Remove the end-token from the stream.
517 (read-token stream)
518 ;; and return the list of tokens.
519 ;; Note that this does the right thing with [] and ().
520 (nreverse list))
521 ;; Ignore the delimiters.
522 (when (same-token-p next-token delimiter-token)
523 (read-token stream))
524 ;; Gather the expression until the next delimiter.
525 (push (gather-superiors delimiter-token stream) list)))
526
527
528
529;;; ********************************
530;;; Precedence *********************
531;;; ********************************
532
533(defparameter *operator-ordering*
534 '(( \[ \( \! ) ; \[ is array reference
535 ( ^ ) ; exponentiation
536 ( ~ ) ; lognot
537 ( * / % ) ; % is mod
538 ( + - )
539 ( << >> )
540 ( < == > <= != >= )
541 ( & ) ; logand
542 ( ^^ ) ; logxor
543 ( \| ) ; logior
544 ( not )
545 ( and )
546 ( or )
547 ;; Where should setf and friends go in the precedence?
548 ( = |:=| += -= *= /= )
549 ( \, ) ; progn (statement delimiter)
550 ( if )
551 ( then else )
552 ( \] \) )
553 ( %infix-end-token% )) ; end of infix expression
554 "Ordered list of operators of equal precedence.")
555
556(defun operator-lessp (op1 op2)
557 (dolist (ops *operator-ordering* nil)
558 (cond ((find op1 ops :test #'same-token-p)
559 (return nil))
560 ((find op2 ops :test #'same-token-p)
561 (return t)))))
562
563(defparameter *right-associative-operators* '(^ =))
564(defun operator-right-associative-p (operator)
565 (find operator *right-associative-operators*))
566
567
568
569;;; ********************************
570;;; Define Operators ***************
571;;; ********************************
572
573(defvar *token-operators* nil)
574(defvar *token-prefix-operator-table* (make-hash-table))
575(defvar *token-infix-operator-table* (make-hash-table))
576(defun token-operator-p (token)
577 (find token *token-operators*))
578(defun get-token-prefix-operator (token)
579 (gethash token *token-prefix-operator-table*))
580(defun get-token-infix-operator (token)
581 (gethash token *token-infix-operator-table*))
582
583(eval-when (compile load eval)
584 (defmacro define-token-operator (operator-name &key
585 (prefix nil prefix-p)
586 (infix nil infix-p))
587 `(progn
588 (pushnew ',operator-name *token-operators*)
589 ,(when prefix-p
590 `(setf (gethash ',operator-name *token-prefix-operator-table*)
591 #'(lambda (stream)
592 ,@@(cond ((and (consp prefix)
593 (eq (car prefix) 'infix-error))
594 ;; To avoid ugly compiler warnings.
595 `((declare (ignore stream))
596 ,prefix))
597 (t
598 (list prefix))))))
599 ,(when infix-p
600 `(setf (gethash ',operator-name *token-infix-operator-table*)
601 #'(lambda (stream left)
602 ,@@(cond ((and (consp infix)
603 (eq (car infix) 'infix-error))
604 ;; To avoid ugly compiler warnings.
605 `((declare (ignore stream left))
606 ,infix))
607 (t
608 (list infix)))))))))
609
610;;; Readtable definitions for characters, so that the right token is returned.
611(eval-when (compile load eval)
612 (defmacro define-character-tokenization (char function)
613 `(set-macro-character ,char ,function nil *infix-readtable*)))
614
615
616
617;;; ********************************
618;;; Operator Definitions ***********
619;;; ********************************
620
621(define-token-operator and
622 :infix `(and ,left ,(gather-superiors 'and stream)))
623(define-token-operator or
624 :infix `(or ,left ,(gather-superiors 'or stream)))
625(define-token-operator not
626 :prefix `(not ,(gather-superiors 'not stream)))
627
628(define-token-operator if
629 :prefix (let* ((test (gather-superiors 'if stream))
630 (then (cond ((same-token-p (peek-token stream) 'then)
631 (read-token stream)
632 (gather-superiors 'then stream))
633 (t
634 (infix-error "Missing THEN clause."))))
635 (else (when (same-token-p (peek-token stream) 'else)
636 (read-token stream)
637 (gather-superiors 'else stream))))
638 (cond ((and test then else)
639 `(if ,test ,then ,else))
640 ((and test then)
641 ;; no else clause
642 `(when ,test ,then))
643 ((and test else)
644 ;; no then clause
645 `(unless ,test ,else))
646 (t
647 ;; no then and else clauses --> always NIL
648 nil))))
649
650(define-token-operator then
651 :prefix (infix-error "THEN clause without an IF."))
652(define-token-operator else
653 :prefix (infix-error "ELSE clause without an IF."))
654
655(define-character-tokenization #\+
656 #'(lambda (stream char)
657 (declare (ignore char))
658 (cond ((char= (peek-char nil stream t nil t) #\=)
659 (read-char stream t nil t)
660 '+=)
661 (t
662 '+))))
663(define-token-operator +
664 :infix `(+ ,left ,(gather-superiors '+ stream))
665 :prefix (gather-superiors '+ stream))
666(define-token-operator +=
667 :infix `(incf ,left ,(gather-superiors '+= stream)))
668
669(define-character-tokenization #\-
670 #'(lambda (stream char)
671 (declare (ignore char))
672 (cond ((char= (peek-char nil stream t nil t) #\=)
673 (read-char stream t nil t)
674 '-=)
675 (t
676 '-))))
677(define-token-operator -
678 :infix `(- ,left ,(gather-superiors '- stream))
679 :prefix `(- ,(gather-superiors '- stream)))
680(define-token-operator -=
681 :infix `(decf ,left ,(gather-superiors '-= stream)))
682
683(define-character-tokenization #\*
684 #'(lambda (stream char)
685 (declare (ignore char))
686 (cond ((char= (peek-char nil stream t nil t) #\=)
687 (read-char stream t nil t)
688 '*=)
689 (t
690 '*))))
691(define-token-operator *
692 :infix `(* ,left ,(gather-superiors '* stream)))
693(define-token-operator *=
694 :infix `(,(if (symbolp left)
695 'setq
696 'setf)
697 ,left
698 (* ,left ,(gather-superiors '*= stream))))
699
700(define-character-tokenization #\/
701 #'(lambda (stream char)
702 (declare (ignore char))
703 (cond ((char= (peek-char nil stream t nil t) #\=)
704 (read-char stream t nil t)
705 '/=)
706 (t
707 '/))))
708(define-token-operator /
709 :infix `(/ ,left ,(gather-superiors '/ stream))
710 :prefix `(/ ,(gather-superiors '/ stream)))
711(define-token-operator /=
712 :infix `(,(if (symbolp left)
713 'setq
714 'setf)
715 ,left
716 (/ ,left ,(gather-superiors '/= stream))))
717
718(define-character-tokenization #\^
719 #'(lambda (stream char)
720 (declare (ignore char))
721 (cond ((char= (peek-char nil stream t nil t) #\^)
722 (read-char stream t nil t)
723 '^^)
724 (t
725 '^))))
726(define-token-operator ^
727 :infix `(expt ,left ,(gather-superiors '^ stream)))
728(define-token-operator ^^
729 :infix `(logxor ,left ,(gather-superiors '^^ stream)))
730
731(define-character-tokenization #\|
732 #'(lambda (stream char)
733 (declare (ignore char))
734 (cond ((char= (peek-char nil stream t nil t) #\|)
735 (read-char stream t nil t)
736 'or)
737 (t
738 '\|))))
739(define-token-operator \|
740 :infix `(logior ,left ,(gather-superiors '\| stream)))
741
742(define-character-tokenization #\&
743 #'(lambda (stream char)
744 (declare (ignore char))
745 (cond ((char= (peek-char nil stream t nil t) #\&)
746 (read-char stream t nil t)
747 'and)
748 (t
749 '\&))))
750(define-token-operator \&
751 :infix `(logand ,left ,(gather-superiors '\& stream)))
752
753(define-character-tokenization #\%
754 #'(lambda (stream char)
755 (declare (ignore stream char))
756 '\%))
757(define-token-operator \%
758 :infix `(mod ,left ,(gather-superiors '\% stream)))
759
760(define-character-tokenization #\~
761 #'(lambda (stream char)
762 (declare (ignore stream char))
763 '\~))
764(define-token-operator \~
765 :prefix `(lognot ,(gather-superiors '\~ stream)))
766
767(define-character-tokenization #\,
768 #'(lambda (stream char)
769 (declare (ignore stream char))
770 '\,))
771(define-token-operator \,
772 :infix `(progn ,left ,(gather-superiors '\, stream)))
773
774(define-character-tokenization #\=
775 #'(lambda (stream char)
776 (declare (ignore char))
777 (cond ((char= (peek-char nil stream t nil t) #\=)
778 (read-char stream t nil t)
779 '==)
780 (t
781 '=))))
782(define-token-operator ==
783 :infix `(= ,left ,(gather-superiors '== stream)))
784(define-token-operator =
785 :infix `(,(if (symbolp left)
786 'setq
787 'setf)
788 ,left
789 ,(gather-superiors '= stream)))
790
791(define-character-tokenization #\:
792 #'(lambda (stream char)
793 (declare (ignore char))
794 (cond ((char= (peek-char nil stream t nil t) #\=)
795 (read-char stream t nil t)
796 '|:=|)
797 (t
798 '|:|))))
799(define-token-operator |:=|
800 :infix `(,(if (symbolp left)
801 'setq
802 'setf)
803 ,left
804 ,(gather-superiors '|:=| stream)))
805
806(define-character-tokenization #\<
807 #'(lambda (stream char)
808 (declare (ignore char))
809 (cond ((char= (peek-char nil stream t nil t) #\=)
810 (read-char stream t nil t)
811 '<=)
812 ((char= (peek-char nil stream t nil t) #\<)
813 (read-char stream t nil t)
814 '<<)
815 (t
816 '<))))
817(define-token-operator <
818 :infix `(< ,left ,(gather-superiors '< stream)))
819(define-token-operator <=
820 :infix `(<= ,left ,(gather-superiors '<= stream)))
821(define-token-operator <<
822 :infix `(ash ,left ,(gather-superiors '<< stream)))
823
824(define-character-tokenization #\>
825 #'(lambda (stream char)
826 (declare (ignore char))
827 (cond ((char= (peek-char nil stream t nil t) #\=)
828 (read-char stream t nil t)
829 '>=)
830 ((char= (peek-char nil stream t nil t) #\>)
831 (read-char stream t nil t)
832 '>>)
833 (t
834 '>))))
835(define-token-operator >
836 :infix `(> ,left ,(gather-superiors '> stream)))
837(define-token-operator >=
838 :infix `(>= ,left ,(gather-superiors '>= stream)))
839(define-token-operator >>
840 :infix `(ash ,left (- ,(gather-superiors '>> stream))))
841
842(define-character-tokenization #\!
843 #'(lambda (stream char)
844 (declare (ignore char))
845 (cond ((char= (peek-char nil stream t nil t) #\=)
846 (read-char stream t nil t)
847 '!=)
848 (t
849 '!))))
850(define-token-operator !=
851 :infix `(not (= ,left ,(gather-superiors '!= stream))))
852(define-token-operator !
853 :prefix (read-regular stream))
854
855(define-character-tokenization #\[
856 #'(lambda (stream char)
857 (declare (ignore stream char))
858 '\[))
859(define-token-operator \[
860 :infix (let ((indices (infix-read-delimited-list '\] '\, stream)))
861 (if (null indices)
862 (infix-error "No indices found in array reference.")
863 `(aref ,left ,@@indices)))
864 :prefix (let ((list-members (infix-read-delimited-list '\] '\, stream)))
865 `(:[ ,@@list-members)))
866
867(define-character-tokenization #\(
868 #'(lambda (stream char)
869 (declare (ignore stream char))
870 '\())
871(define-token-operator \(
872 :infix `(,left ,@@(infix-read-delimited-list '\) '\, stream))
873 :prefix (let ((list (infix-read-delimited-list '\) '\, stream)))
874 (if (null (rest list))
875 ;; only one element in list. works correctly if list is NIL
876 (first list)
877 ;; several elements in list
878 `(progn ,@@list))))
879
880(define-character-tokenization #\]
881 #'(lambda (stream char)
882 (declare (ignore stream char))
883 '\]))
884(define-token-operator \]
885 :infix (infix-error "Extra close brace \"]\" in infix expression"))
886
887(define-character-tokenization #\)
888 #'(lambda (stream char)
889 (declare (ignore stream char))
890 '\)))
891(define-token-operator \)
892 :infix (infix-error "Extra close paren \")\" in infix expression"))
893
894#|
895;;; Commented out because no longer using $ as the macro character.
896(define-character-tokenization #\$
897 #'(lambda (stream char)
898 (declare (ignore stream char))
899 '%infix-end-token%))
900(define-token-operator %infix-end-token%
901 :infix (infix-error "Prematurely terminated infix expression")
902 :prefix (infix-error "Prematurely terminated infix expression"))
903|#
904
905(define-character-tokenization #\;
906 #'(lambda (stream char)
907 (declare (ignore char))
908 (do ((char (peek-char nil stream t nil t)
909 (peek-char nil stream t nil t)))
910 ((or (char= char #\newline) (char= char #\return)
911 ;; was #\$
912; (char= char #\))
913 )
914 ;; Gobble characters until the end of the line or the
915 ;; end of the input.
916 (cond ((or (char= char #\newline) (char= char #\return))
917 (read-char stream)
918 (read stream t nil t))
919 (t
920 ;; i.e., return %infix-end-token%
921 (read stream t nil t))))
922 (read-char stream))))
923
924
925
926;;; ********************************
927;;; Syntactic Modifications ********
928;;; ********************************
929
930;;; Post processes the expression to remove some unsightliness caused
931;;; by the way infix processes the input. Note that it is also required
932;;; for correctness in the a<b<=c case.
933
934(defun post-process-expression (expression)
935 (if (and (consp expression)
936 (= (length expression) 3))
937 (destructuring-bind (operator left right) expression
938 (cond ((and (consp left)
939 (same-operator-p (first left) operator)
940 (find operator '(+ * / - and or < > <= >= progn)
941 :test #'same-operator-p))
942 ;; Flatten the expression if possible
943 (cond ((and (eq operator '-)
944 (= (length left) 2))
945 ;; -a-b --> (+ (- a) (- b)).
946 `(+ ,left (- ,right)))
947 ((and (eq operator '/)
948 (= (length left) 2))
949 ;; ditto with /
950 `(/ (* ,(second left) ,right)))
951 (t
952 ;; merges a+b+c as (+ a b c).
953 (append left (list right)))))
954 ((and (consp left)
955 (eq operator '-)
956 (eq (first left) '+))
957 ;; merges a+b-c as (+ a b (- c)).
958 (append left (list `(- ,right))))
959 ((and (consp left)
960 (find operator '(< > <= >=))
961 (find (first left) '(< > <= >=)))
962 ;; a<b<c --> a<b and b<c
963 `(and ,left
964 (,operator ,(first (last left))
965 ,right)))
966 (t
967 expression)))
968 expression))
969
970
971
972;;; ********************************
973;;; Test Infix *********************
974;;; ********************************
975
976;;; Invoke with (infix:test-infix).
977;;; Prints out all the tests that fail and a count of the number of failures.
978
979(defparameter *test-cases*
980 ;; Note that in strings, we have to slashify \ as \\.
981 '(("1 * +2" (* 1 2))
982 ("1 * -2" (* 1 (- 2)))
983 ("1 * /2" (* 1 (/ 2)))
984 ("/2" (/ 2))
985 ("not true" (not true))
986 ("foo\\-bar" foo-bar)
987 ("a + b-c" (+ a b (- c)))
988 ("a + b\\-c" (+ a b-c))
989 ("f\\oo" |FoO|)
990 ("!foo-bar * 2" (* foo-bar 2))
991 ("!(foo bar baz)" (foo bar baz))
992 ("!foo-bar " foo-bar)
993 ;; The following now longer gives an eof error, since the close
994 ;; parenthesis terminates the token.
995 ("!foo-bar" foo-bar) ; eof error -- ! eats the close $
996 ("a+-b" (+ a (- b)))
997 ("a+b" (+ a b))
998 ("a+b*c" (+ a (* b c)))
999 ("a+b+c" (+ a b c))
1000 ("a+b-c" (+ a b (- c)))
1001 ("a+b-c+d" (+ a b (- c) d))
1002 ("a+b-c-d" (+ a b (- c) (- d)))
1003 ("a-b" (- a b))
1004 ("a*b" (* a b))
1005 ("a*b*c" (* a b c))
1006 ("a*b+c" (+ (* a b) c))
1007 ("a/b" (/ a b))
1008 ("a^b" (expt a b))
1009 ("foo/-bar" (/ foo (- bar)))
1010 ("1+2*3^4" (+ 1 (* 2 (expt 3 4))))
1011 ("1+2*3^4+5" (+ 1 (* 2 (expt 3 4)) 5))
1012 ("2*3^4+1" (+ (* 2 (expt 3 4)) 1))
1013 ("2+3^4*5" (+ 2 (* (expt 3 4) 5)))
1014 ("2^3^4" (expt 2 (expt 3 4)))
1015 ("x^2 + y^2" (+ (expt x 2) (expt y 2)))
1016 ("(1+2)/3" (/ (+ 1 2) 3))
1017 ("(a=b)" (setq a b))
1018 ("(a=b,b=c)" (progn (setq a b) (setq b c)))
1019 ("1*(2+3)" (* 1 (+ 2 3)))
1020 ("1+2/3" (+ 1 (/ 2 3)))
1021 ("a,b" (progn a b))
1022 ("a,b,c" (progn a b c))
1023 ("foo(a,b,(c,d))" (foo a b (progn c d)))
1024 ("foo(a,b,c)" (foo a b c))
1025 ("(a+b,c)" (progn (+ a b) c))
1026 ("1" 1)
1027 ("-1" (- 1))
1028 ("+1" 1)
1029 ("1." 1)
1030 ("1.1" 1.1)
1031 ("1e3" 1000.0)
1032 ("1e-3" 0.001)
1033 ("1f-3" 1f-3)
1034 ("1e-3e" (- 1e 3e))
1035 ("!1e-3 " 0.001)
1036 ("a and b and c" (and a b c))
1037 ("a and b or c" (or (and a b) c))
1038 ("a and b" (and a b))
1039 ("a or b and c" (or a (and b c)))
1040 ("a or b" (or a b))
1041 ("a<b and b<c" (and (< a b) (< b c)))
1042 ("if (if a then b else c) then e" (when (if a b c) e))
1043 ("if 1 then 2 else 3+4" (if 1 2 (+ 3 4)))
1044 ("(if 1 then 2 else 3)+4" (+ (if 1 2 3) 4))
1045 ("if a < b then b else a" (if (< a b) b a))
1046 ("if a and b then c and d else e and f"
1047 (if (and a b) (and c d) (and e f)))
1048 ("if a or b then c or d else e or f" (if (or a b) (or c d) (or e f)))
1049 ("if a then (if b then c else d) else e" (if a (if b c d) e))
1050 ("if a then (if b then c) else d" (if a (when b c) d))
1051 ("if a then b else c" (if a b c))
1052 ("if a then b" (when a b))
1053 ("if a then if b then c else d else e" (if a (if b c d) e))
1054 ("if a then if b then c else d" (when a (if b c d)))
1055 ("if if a then b else c then e" (when (if a b c) e))
1056 ("if not a and not b then c" (when (and (not a) (not b)) c))
1057 ("if not a then not b else not c and d"
1058 (if (not a) (not b) (and (not c) d)))
1059 ("not a and not b" (and (not a) (not b)))
1060 ("not a or not b" (or (not a) (not b)))
1061 ("not a<b and not b<c" (and (not (< a b)) (not (< b c))))
1062 ("not a<b" (not (< a b)))
1063 ("a[i,k]*b[j,k]" (* (aref a i k) (aref b j k)))
1064 ("foo(bar)=foo[bar,baz]" (setf (foo bar) (aref foo bar baz)))
1065 ("foo(bar,baz)" (foo bar baz))
1066 ("foo[bar,baz]" (aref foo bar baz))
1067 ("foo[bar,baz]=barf" (setf (aref foo bar baz) barf))
1068 ("max = if a < b then b else a" (setq max (if (< a b) b a)))
1069 ("a < b < c" (< A B C))
1070 ("a < b <= c" (and (< a b) (<= b c)))
1071 ("a <= b <= c" (<= A B C))
1072 ("a <= b <= c" (<= A B C))
1073 ("a!=b and b<c" (and (not (= a b)) (< b c)))
1074 ("a!=b" (not (= a b)))
1075 ("a<b" (< a b))
1076 ("a==b" (= a b))
1077 ("a*b(c)+d" (+ (* a (b c)) d))
1078 ("a+b(c)*d" (+ a (* (b c) d)))
1079 ("a+b(c)+d" (+ a (b c) d))
1080 ("d+a*b(c)" (+ d (* a (b c))))
1081 ("+a+b" (+ a b))
1082 ("-a+b" (+ (- a) b))
1083 ("-a-b" (+ (- a) (- b)))
1084 ("-a-b-c" (+ (- a) (- b) (- c)))
1085 ("a*b/c" (/ (* a b) c))
1086 ("a+b-c" (+ a b (- c)))
1087 ("a-b-c" (- a b c))
1088 ("a/b*c" (* (/ a b) c))
1089 ("a/b/c" (/ a b c))
1090 ("/a/b" (/ (* a b)))
1091 ("a^b^c" (expt a (expt b c)))
1092 ("a(d)^b^c" (expt (a d) (expt b c)))
1093 ("a<b+c<d" (< a (+ b c) d))
1094 ("1*~2+3" (+ (* 1 (lognot 2)) 3))
1095 ("1+~2*3" (+ 1 (* (lognot 2) 3)))
1096 ("1+~2+3" (+ 1 (lognot 2) 3))
1097 ("f(a)*=g(b)" (setf (f a) (* (f a) (g b))))
1098 ("f(a)+=g(b)" (incf (f a) (g b)))
1099 ("f(a)-=g(b)" (decf (f a) (g b)))
1100 ("f(a)/=g(b)" (setf (f a) (/ (f a) (g b))))
1101 ("a&b" (logand a b))
1102 ("a^^b" (logxor a b))
1103 ("a|b" (logior a b))
1104 ("a<<b" (ash a b))
1105 ("a>>b" (ash a (- b)))
1106 ("~a" (lognot a))
1107 ("a&&b" (and a b))
1108 ("a||b" (or a b))
1109 ("a%b" (mod a b))
1110
1111 ;; Comment character -- must have carriage return after semicolon.
1112 ("x^2 ; the x coordinate
1113 + y^2 ; the y coordinate" :error)
1114 ("x^2 ; the x coordinate
1115 + y^2 ; the y coordinate
1116 " (+ (expt x 2) (expt y 2)))
1117
1118 ;; Errors
1119 ("foo(bar,baz" :error) ; premature termination
1120 ;; The following no longer gives an error
1121 ("foo(bar,baz))" (foo bar baz)) ; extra close parenthesis
1122 ("foo[bar,baz]]" :error) ; extra close bracket
1123 ("[foo,bar]" :error) ; AREF is not a prefix operator
1124 ("and a" :error) ; AND is not a prefix operator
1125 ("< a" :error) ; < is not a prefix operator
1126 ("=bar" :error) ; SETF is not a prefix operator
1127 ("*bar" :error) ; * is not a prefix operator
1128 ("a not b" :error) ; NOT is not an infix operator
1129 ("a if b then c" :error) ; IF is not an infix operator
1130 ("" :error) ; premature termination (empty clause)
1131 (")a" :error) ; left parent is not a prefix operator
1132 ("]a" :error) ; left bracket is not a prefix operator
1133 ))
1134
1135(defun test-infix (&optional (tests *test-cases*))
1136 (let ((count 0))
1137 (dolist (test tests)
1138 (destructuring-bind (string result) test
1139 (unless (test-infix-case string result)
1140 (incf count))))
1141 (format t "~&~:(~R~) test~p failed." count count)
1142 (values)))
1143
1144(defun test-infix-case (string result)
1145 (multiple-value-bind (value error)
1146 (let ((*package* (find-package "INFIX")))
1147 (ignore-errors
1148 (values (read-from-string (concatenate 'string "#I(" string ")")
1149 t nil))))
1150 (cond (error
1151 (cond ((eq result :error)
1152 t)
1153 (t
1154 (format t "~&Test #I(~A) failed with ERROR." string)
1155 nil)))
1156 ((eq result :error)
1157 (format t "~&Test #I(~A) failed. ~
1158 ~& Expected ERROR ~
1159 ~& but got ~A."
1160 string value)
1161 nil)
1162 ((not (equal value result))
1163 (format t "~&Test #I(~A) failed. ~
1164 ~& Expected ~A ~
1165 ~& but got ~A."
1166 string result value)
1167 nil)
1168 (t
1169 t))))
1170
1171;;; *EOF*
1172@
1173
1174
11751.3
1176log
1177@*** empty log message ***
1178@
1179text
1180@d274 2
1181a275 2
1182;;(proclaim '(optimize (speed 0) (debug 3)))
1183(proclaim '(optimize (speed 3) (debug 0)))
1184@
1185
1186
11871.2
1188log
1189@*** empty log message ***
1190@
1191text
1192@d274 2
1193a275 1
1194(proclaim '(optimize (speed 0) (debug 3)))
1195@
1196
1197
11981.1
1199log
1200@Initial revision
1201@
1202text
1203@d2 1
1204a2 1
1205;;; $Id: infix.lisp,v 1.10 1997/12/07 03:32:51 marek Exp $
1206d274 2
1207@
Note: See TracBrowser for help on using the repository browser.