close Warning: Can't synchronize with repository "(default)" (The repository directory has changed, you should resynchronize the repository with: trac-admin $ENV repository resync '(default)'). Look in the Trac log for more information.

source: branches/f4grobner/infix-printer.lisp@ 4429

Last change on this file since 4429 was 4423, checked in by Marek Rychlik, 9 years ago

* empty log message *

File size: 6.9 KB
Line 
1;;; -*- Mode: Lisp -*-
2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3;;;
4;;; Copyright (C) 1999, 2002, 2009, 2015 Marek Rychlik <rychlik@u.arizona.edu>
5;;;
6;;; This program is free software; you can redistribute it and/or modify
7;;; it under the terms of the GNU General Public License as published by
8;;; the Free Software Foundation; either version 2 of the License, or
9;;; (at your option) any later version.
10;;;
11;;; This program is distributed in the hope that it will be useful,
12;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
13;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14;;; GNU General Public License for more details.
15;;;
16;;; You should have received a copy of the GNU General Public License
17;;; along with this program; if not, write to the Free Software
18;;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19;;;
20;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
21
22(defpackage "INFIX-PRINTER"
23 (:use "COMMON-LISP"
24 "INFIX" ;for operator-lessp
25 )
26 (:export infix-print infix-print-to-string infix-print-to-stream))
27
28(in-package "INFIX-PRINTER")
29
30;;(proclaim '(optimize (speed 0) (space 0) (safety 3) (debug 3)))
31
32(defun find-alt-op (op)
33 "Find ALT-OP, used for arguments above 1. Example:
34We should have
35 (+ X (- Y) Z) --> X - Y + Z
36so when an argument of an expression with operator '+ has an operator '-, we
37should use '- as a separator, before we render Y. Thus ALT-OP for '+ is '-"
38 (ecase op
39 (+ '-)
40 (- '-)
41 (* '/)
42 (/ '/)))
43
44(defun inverse-op (op)
45 (ecase op
46 (- '+)
47 (/ '*)))
48
49(defun infix-print-separated-list (lst sep stream op print-level
50 &optional
51 (alt-op nil)
52 &aux
53 (beg t)
54 (count 0)
55 true-sep)
56 "Print a list LST using SEP as separator, to stream STREAM. Every
57argument is printed usin OP as main operator. PRINT-LEVEL is used to
58control printing nested expressions as expected: subexpressions at
59level exceeding PRINT-LEVEL are printed as ellipsis. The argument BEG
60indicates whether this is the start of a sequence of arguments with
61the main operator OP. The argument ALT-OP, if not NIL, replaces
62operator OP for list elements beyond the first one."
63
64 (cond
65 ((endp lst) nil)
66
67 ;; Handle *print-level*
68 ((and (numberp *print-level*)
69 (> print-level *print-level*))
70 (format stream "#"))
71
72 (t
73 (dolist (arg lst)
74 (setf true-sep sep)
75 (incf count)
76
77 ;; Treat negative number X as '(- (- X))
78 (when (and (realp arg) (minusp arg))
79 (setf arg (list '- (- arg))))
80
81 ;; The following code handles unary minus
82 ;; Thus:
83 ;; (+ x (- y z)) --> X + Y - Z
84 ;; (+ x (- y)) --> X - Y
85 ;; (- x (- y)) --> X + Y
86 (when (and alt-op
87 (> count 1)
88 (consp arg)
89 (endp (cddr arg))
90 (eq alt-op (car arg)))
91 (psetf arg (cadr arg)
92 true-sep (if (eq op alt-op) (inverse-op op) alt-op)))
93
94 ;; Unless at the beginning, print the separator
95 (cond
96 (beg
97 (setf beg nil))
98 (t
99 (format stream "~a" true-sep)))
100
101 ;; If *print-length* exceeded, print ellipsis
102 (when (and (numberp *print-length*) (> count *print-length*))
103 (format stream "...")
104 (return-from infix-print-separated-list (values)))
105
106 (infix-print-to-stream arg stream op print-level)
107
108 )))
109 (values))
110
111(defun infix-print-arg-list (lst stream print-level)
112 "Print a comma-separated list."
113 (infix-print-separated-list lst '\, stream '\, print-level))
114
115(defun infix-print-to-stream (expr &optional (stream t) (op nil) (print-level 0))
116 "Print an expression EXPR in infix notation to stream STREAM.
117If OP is not nil, the expression is parenthesized if its operator
118has lower precedence than OP. Returns (VALUES)."
119 (cond
120 ;; Handle *print-level*
121 ((and (numberp *print-level*)
122 (> print-level *print-level*))
123 (format stream "#"))
124
125 ;; Null expression is an error
126 ((null expr)
127 (error "Null expression."))
128
129 ;; Atoms are printed using ~A format directive
130 ((atom expr)
131 (format stream "~a" expr))
132
133 ;; Check if the operator of this expression has lower precedence
134 ;; than the surrounding operator, and parenthesize if necessary
135 ((and op
136 (operator-lessp (car expr) op))
137 (format stream "(")
138 (infix-print expr stream nil (1+ print-level))
139 (format stream ")"))
140
141 ;; Unary minus needs special handling
142 ((and (eq (car expr) '-) (endp (cddr expr)))
143 (format stream "-")
144
145 ;; Print the second element in product context
146 (infix-print (cadr expr) stream '* (1+ print-level)))
147
148 ;; All other operators
149 (t
150 (case (car expr)
151
152 ;; Arithmetic operators
153 ((+ - * /)
154
155 (let ((op (car expr))
156 (args (cdr expr)))
157 (infix-print-separated-list
158 args op stream op
159 (1+ print-level)
160 (find-alt-op op))
161 )
162 )
163
164 ;; Exponentials
165 (expt
166 (unless (= (length (cdr expr)) 2)
167 (error "expt must take 2 arguments."))
168 (infix-print-separated-list
169 (cdr expr)
170 '^
171 stream
172 '^
173 (1+ print-level)))
174
175 ;; Assuming function call
176 (otherwise
177 (cond
178
179 ;; Handle array references
180 ((eq (car expr) 'aref)
181 ;; Special syntax for subscripted variables
182 ;; consistent with the infix package.
183 (format stream "~a[" (cadr expr))
184 (infix-print-separated-list (cddr expr) '\, stream '\, (1+ print-level))
185 (format stream "]"))
186
187 ;; Handle lists
188 ((and (symbolp (car expr))
189 (string= (symbol-name (car expr)) "["))
190 (format stream "[")
191 (infix-print-separated-list (cdr expr) '\, stream '\, (1+ print-level))
192 (format stream "]"))
193
194 ;; Handle generic function call syntax
195 (t
196 (format stream "~a(" (car expr))
197 (infix-print-arg-list (cdr expr) stream (1+ print-level))
198 (format stream ")")))))))
199 (values))
200
201(defun infix-print-to-string (expr &optional (op nil) (print-level 0)
202 &aux (fstr (make-array '(0) :element-type 'base-char
203 :fill-pointer 0 :adjustable t)))
204 "Print an expression EXPR in infix notation to a string. If OP is
205not nil, the expression is parenthesized if its operator has lower
206precedence than OP. Returns the string containing the printed
207expression."
208 (with-output-to-string (s fstr)
209 (infix-print-to-stream expr s op print-level))
210 fstr)
211
212(defun infix-print (expr &optional (stream t) (op nil) (print-level 0))
213 "Print an expression EXPR in infix notation to stream STREAM or to
214string if (NULL STREAM). If OP is not nil, the expression is
215parenthesized if its operator has lower precedence than OP. Returns
216the string containing the printed expression, or (VALUES) if (NULL
217STREAM) is true."
218 (cond
219 ((null stream)
220 (infix-print-to-string expr op print-level))
221 (t (infix-print-to-stream expr stream op print-level))))
Note: See TracBrowser for help on using the repository browser.