-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathlisp-form-builder.lisp
114 lines (108 loc) · 3.86 KB
/
lisp-form-builder.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
;; A BibTeX re-implementation in Common Lisp - construct beautiful Lisp forms
;; Copyright 2001, 2002 Matthias Koeppe <[email protected]>
;;
;; This code is free software; you can redistribute it and/or
;; modify it under the terms of version 2.1 of the GNU Lesser
;; General Public License as published by the Free Software
;; Foundation or any later version, as clarified by the preamble
;; found in COPYING-preamble.txt. This preamble is in the style
;; of the Franz Inc. preamble at http://opensource.franz.com/preamble.html
;; with names and copyright holders altered accordingly.
(in-package bibtex-compiler)
(defun build-if-form (val-form then-form else-form)
"Build a Lisp form equivalent to `(IF ,VAL-FORM ,THEN-FORM ,ELSE-FORM)
but try to beautify the result by using COND, UNLESS, and WHEN, rather
than IF in certain cases."
(let ((then-operator (and (consp then-form) (car then-form)))
(else-operator (and (consp else-form) (car else-form))))
(labels ((clauses (form)
(let ((operator (and (consp form) (car form))))
(case operator
(progn `((t ,@(cdr form))))
(if `((,(cadr form)
,(caddr form))
(t
,(cadddr form))))
(cond (cdr form))
(t `((t ,form))))))
(body (form)
(let ((operator (and (consp form) (car form))))
(if (eql operator 'progn)
(cdr form)
(list form)))))
(cond
((equal then-form '(values)) ; we have, in fact, an `unless'
`(unless ,val-form
,@(body else-form)))
((equal else-form '(values)) ; we have, in fact, a `when'
`(when ,val-form
,@(body then-form)))
((member else-operator '(if cond)) ; beautify using `cond'
`(cond
(,val-form ,@(body then-form))
,@(clauses else-form)))
((member then-operator '(if cond)) ; beautify using reverse `cond'
`(cond
(,(build-not-form val-form)
,@(body else-form))
,@(clauses then-form)))
((or (eql else-operator 'progn)
(eql then-operator 'progn)) ; beautify using `cond'
`(cond
(,val-form ,@(body then-form))
,@(clauses else-form)))
(t ; normal if
`(if ,val-form
,then-form
,else-form))))))
(defun build-associative-form (operators form1 form2)
"Build the form `(,@OPERATORS FORM1 FORM2) but if FORM1 and FORM2
are of this form, use the associativity of the operation to build
`(,@OPERATORS FORMS...) instead."
(labels ((operation-p (form)
(and (consp form)
(let ((index (mismatch operators form :test 'equal)))
(or (not index)
(= index (length operators))))))
(args (form)
(subseq form (length operators)))
(arg-forms (form)
(if (operation-p form)
(args form)
(list form))))
`(,@operators ,@(arg-forms form1) ,@(arg-forms form2))))
(defun build-not-form (form)
"Build the form `(not ,@FORM) but simplify that if possible."
(labels ((operator (f)
(and (consp f) (car f))))
(let ((op (operator form)))
(case op
(not (cadr form))
((> < >= <=)
(if (= (length form) 3)
(let ((negop (ecase op (> '<=) (< '>=) (<= '>) (>= '<))))
`(,negop ,@(cdr form)))
`(not ,form)))
(string= `(string/= ,@(cdr form)))
(string/= `(string= ,@(cdr form)))
((and or)
;; de-Morgan-ize if at least one negated subform is found
(if (find 'not (cdr form) :key #'operator)
(let ((negop (ecase op (and 'or) (or 'and))))
`(,negop ,@(mapcar #'build-not-form (cdr form))))
`(not ,form)))
(t `(not ,form))))))
(defun build-values-body (result-list)
"Build a Lisp body containing one form, `(values ,@RESULT-LIST).
For zero or one values, make simpler constructions."
(case (length result-list)
(0 ())
(1 (list (car result-list)))
(t (list `(values ,@result-list)))))
(defun build-progn-form (body)
"Build a Lisp form equivalent to `(progn ,@BODY).
For the special case of an empty body, use `(values)."
(case (length body)
(0 `(values))
(1 (car body))
(t `(progn ,@body))))