/
source-form.lisp
1454 lines (1246 loc) · 58.4 KB
/
source-form.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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
;;;; -*- coding:utf-8 -*-
;;;;**************************************************************************
;;;;FILE: source-form.lisp
;;;;LANGUAGE: Common-Lisp
;;;;SYSTEM: Common-Lisp
;;;;USER-INTERFACE: NONE
;;;;DESCRIPTION
;;;;
;;;; This package exports functions to parse and manipulate
;;;; Common Lisp sources as lisp forms (such as in macros).
;;;;
;;;;AUTHORS
;;;; <PJB> Pascal J. Bourguignon <pjb@informatimago.com>
;;;;MODIFICATIONS
;;;; 2021-06-09 <PJB> Added remove-whole-parameter and remove-environment-parameter.
;;;; 2016-01-16 <PJB> Added parameter-parameter-list and make-parameter-list.
;;;; 2014-11-05 <PJB> make-parameter-list now returns also
;;;; parameters from
;;;; sub-destructuring-lambda-lists.
;;;; 2010-02-06 <PJB> Corrected the superclass of orakawbe-ll.
;;;; preqvars instanciated the wrong parameter class.
;;;; bodyvar poped the body parameter name.
;;;; 2006-05-25 <PJB> Created
;;;;BUGS
;;;;
;;;; "3.4.4.1 Destructuring by Lambda Lists" seems to apply only
;;;; to macro lambda lists (therefore to destructuring lambda
;;;; lists only when used in a macro lambda list). Unless "and
;;;; supports destructuring in the same way." in "3.4.5
;;;; Destructuring Lambda Lists" means that 3.4.4.1 also applies
;;;; to destructuring lambda lists.
;;;;
;;;; (parse-lambda-list '(a b . r) :destructuring) is not implemented yet.
;;;;
;;;;LEGAL
;;;; AGPL3
;;;;
;;;; Copyright Pascal J. Bourguignon 2006 - 2021
;;;;
;;;; This program is free software: you can redistribute it and/or modify
;;;; it under the terms of the GNU Affero General Public License as published by
;;;; the Free Software Foundation, either version 3 of the License, or
;;;; (at your option) any later version.
;;;;
;;;; This program is distributed in the hope that it will be useful,
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;;;; GNU Affero General Public License for more details.
;;;;
;;;; You should have received a copy of the GNU Affero General Public License
;;;; along with this program. If not, see <http://www.gnu.org/licenses/>
;;;;**************************************************************************
(eval-when (:compile-toplevel :load-toplevel :execute)
(setf *readtable* (copy-readtable nil)))
(defpackage "COM.INFORMATIMAGO.COMMON-LISP.LISP-SEXP.SOURCE-FORM"
(:use "COMMON-LISP")
(:export
;; Parameter Classes:
"PARAMETER" "ENVIRONMENT-PARAMETER" "WHOLE-PARAMETER"
"REST-PARAMETER" "BODY-PARAMETER"
"SPECIALIZED-PARAMETER" "AUXILIARY-PARAMETER"
"OPTIONAL-PARAMETER" "GENERIC-OPTIONAL-PARAMETER"
"KEYWORD-PARAMETER" "GENERIC-KEYWORD-PARAMETER"
;; Parameter Methods:
"PARAMETER-NAME" "PARAMETER-LABEL" #|"PARAMETER-HELP-LABEL"|#
"PARAMETER-LAMBDA-LIST-KEYWORD"
"PARAMETER-SPECIFIER" "PARAMETER-INDICATOR" "PARAMETER-INDICATOR-P"
"PARAMETER-INITFORM" "PARAMETER-INITFORM-P" "PARAMETER-KEYWORD"
"PARAMETER-KEYWORD-P" "ENSURE-PARAMETER-KEYWORD"
"PARAMETER-SPECIALIZER" "PARAMETER-SPECIALIZER-P"
"PARAMETER-PARAMETER-LIST"
;; Lambda-List Classes:
"LAMBDA-LIST" "ORDINARY-LAMBDA-LIST" "BOA-LAMBDA-LIST"
"SPECIALIZED-LAMBDA-LIST" "MODIFY-MACRO-LAMBDA-LIST" "GENERIC-LAMBDA-LIST"
"MACRO-LAMBDA-LIST" "TYPE-LAMBDA-LIST" "DESTRUCTURING-LAMBDA-LIST"
"SETF-LAMBDA-LIST" "METHOD-COMBINATION-LAMBDA-LIST"
;; Lambda-List Methods:
"ORIGINAL-LAMBDA-LIST" "LAMBDA-LIST-PARAMETERS"
"LAMBDA-LIST-MANDATORY-PARAMETERS" "LAMBDA-LIST-OPTIONAL-PARAMETERS"
"LAMBDA-LIST-REST-PARAMETER" "LAMBDA-LIST-ALLOW-OTHER-KEYS-P" "LAMBDA-LIST-KEY-P"
"LAMBDA-LIST-KEYWORD-PARAMETERS" "LAMBDA-LIST-ENVIRONMENT-PARAMETER"
"LAMBDA-LIST-AUXILIARY-PARAMETERS" "LAMBDA-LIST-WHOLE-PARAMETER"
"LAMBDA-LIST-ENVIRONMENT-PARAMETER" "LAMBDA-LIST-BODY-PARAMETER"
"LAMBDA-LIST-KIND" "LAMBDA-LIST-ALLOWED-KEYWORDS"
"LAMBDA-LIST-MANDATORY-PARAMETER-COUNT"
"LAMBDA-LIST-OPTIONAL-PARAMETER-COUNT" "LAMBDA-LIST-REST-P"
"LAMBDA-LIST-MANDATORY-PARAMETERS-P" "LAMBDA-LIST-OPTIONAL-PARAMETERS-P"
"LAMBDA-LIST-REST-PARAMETER-P" "LAMBDA-LIST-AUXILIARY-PARAMETERS-P"
"LAMBDA-LIST-WHOLE-PARAMETER-P" "LAMBDA-LIST-BODY-PARAMETER-P"
"LAMBDA-LIST-ENVIRONMENT-PARAMETER-P"
;; Parsing lambda-lists:
"PARSE-LAMBDA-LIST" "PARSE-ORIGINAL-LAMBDA-LIST"
;; Generating information from a lambda-list instance:
"MAKE-HELP"
"MAKE-ARGUMENT-LIST" "MAKE-ARGUMENT-LIST-FORM"
"MAKE-FLAT-ARGUMENT-LIST" "MAKE-FLAT-ARGUMENT-LIST-FORM"
"MAKE-LAMBDA-LIST" "MAKE-PARAMETER-LIST"
;; Parsing sources:
"EXTRACT-DOCUMENTATION" "EXTRACT-DECLARATIONS" "EXTRACT-BODY"
"PARSE-BODY"
"DECLARATIONS-HASH-TABLE"
"EXTRACT-METHOD-QUALIFIERS" "EXTRACT-METHOD-LAMBDA-LIST"
"EXTRACT-METHOD-DDL" "EXTRACT-METHOD-DOCUMENTATION"
"EXTRACT-METHOD-DECLARATIONS" "EXTRACT-METHOD-BODY"
;; "DEFUN""DEFGENERIC""DEFMETHOD"
;; *CALL-STACK*" ;; not yet
"LIST-SOURCE-FORM")
(:export "REMOVE-WHOLE-PARAMETER" "REMOVE-ENVIRONMENT-PARAMETER")
(:documentation "
This package exports functions to parse and manipulate
Common Lisp sources as lisp forms (such as in macros).
Copyright Pascal J. Bourguignon 2003 - 2021
This package is provided under the GNU Affero General Public License.
See the source file for details.
"))
(in-package "COM.INFORMATIMAGO.COMMON-LISP.LISP-SEXP.SOURCE-FORM")
;;;----------------------------------------
;;; Parameter specifications in lambda-lists
;;;----------------------------------------
;; Syntax of parameter specifications:
;;
;; name
;; | (name [ specializer ]) ; for specialized lambda-lists
;; | (name [ init-form [ indicator ]]) ; for &key &optional
;; | ((name keyword) [ init-form [ indicator ]]) ; for &key
(defmacro define-default-generic (name class default-value)
`(defgeneric ,name (self)
(:method ((self ,class)) (declare (ignore self)) ,default-value)))
;;;--------------------
(defgeneric parameter-name-p (self))
(defgeneric parse-parameter (self form))
(defgeneric parse-parameter-name (self form))
(defgeneric ensure-parameter-keyword (self))
(defgeneric lambda-list-mandatory-parameter-count (self))
(defgeneric lambda-list-optional-parameter-count (self))
(defgeneric parse-optvars (self current slot lambda-list-keyword class))
(defgeneric auxvars (self current))
(defgeneric optvars (self current))
(defgeneric goptvars (self current))
(defgeneric parse-keyvars (self current class))
(defgeneric keyvars (self current))
(defgeneric gkeyvars (self current))
(defgeneric parse-reqvars (self current class))
(defgeneric reqvars (self current))
(defgeneric sreqvars (self current))
(defgeneric preqvars (self current))
(defgeneric parse-original-lambda-list (self))
(defgeneric make-help (self))
(defgeneric make-argument-list (self))
(defgeneric make-argument-list-form (self))
(defgeneric make-flat-argument-list (self))
(defgeneric make-flat-argument-list-form (self))
(defgeneric make-lambda-list (self))
(defgeneric make-parameter-list (ll)
(:documentation "Return a list of all the symbols naming parameters in the lambda list."))
(defgeneric parameter-parameter-list (parameter)
(:documentation "Return a list of symbols naming parameters for a parameter object.
In the case of &optional or &key parameters, the result may contain two symbols:
one for the parameter and one for the indicator."))
;;;--------------------
(defclass parameter ()
((name :accessor parameter-name
:initarg :name
:type symbol
:documentation "The name of the parameter."))
(:documentation "A generic parameter."))
(defmethod parameter-name-p ((self parameter))
(slot-boundp self 'name))
(defmethod parameter-parameter-list ((parameter parameter))
(if (parameter-indicator-p parameter)
(list (parameter-name parameter)
(parameter-indicator parameter))
(list (parameter-name parameter))))
(define-default-generic parameter-indicator parameter nil)
(define-default-generic parameter-indicator-p parameter nil)
(define-default-generic parameter-initform parameter nil)
(define-default-generic parameter-initform-p parameter nil)
(define-default-generic parameter-keyword parameter nil)
(define-default-generic parameter-keyword-p parameter nil)
(define-default-generic parameter-specializer parameter nil)
(define-default-generic parameter-specializer-p parameter nil)
(defmethod parse-parameter-name ((self parameter) form)
(if (symbolp form)
(setf (parameter-name self) form)
(error "Invalid parameter name: ~S" form))
self)
(defmethod parse-parameter ((self parameter) form)
(parse-parameter-name self form))
(defmethod print-object ((self parameter) stream)
(print-unreadable-object (self stream :identity t)
(format stream "~A ~S"
(parameter-lambda-list-keyword self)
(parameter-specifier self))))
;;;--------------------
(defclass environment-parameter (parameter)
()
(:documentation "An &ENVIRONMENT parameter."))
(defclass whole-parameter (parameter)
()
(:documentation "A &WHOLE parameter."))
(defclass rest-parameter (parameter)
()
(:documentation "A &REST parameter."))
(defclass body-parameter (rest-parameter)
()
(:documentation "A &BODY parameter."))
;;;--------------------
(defclass specialized-parameter (parameter)
((specializer :accessor parameter-specializer
:initarg :specializer
:type (or symbol cons)
:documentation "
A specializer can be either NIL (no specializer),p
a symbol denoting a class, or
a cons (eql object) denoting an EQL specializer."))
(:documentation "A specialized parameter."))
(defmethod parameter-specializer-p ((self specialized-parameter))
(slot-boundp self 'specializer))
(defmethod parse-parameter ((self specialized-parameter) form)
(etypecase form
(symbol (call-next-method))
(cons (call-next-method self (first form))
(when (cdr form)
(setf (parameter-specializer self) (second form))
(when (cddr form)
(error "~A specification must be a ~
list of two elements at most, not ~S"
(parameter-label self) form)))))
self)
;;;--------------------
(defclass parameter-with-initform ()
((initform :accessor parameter-initform
:initarg :initform
:documentation "The initial form for the parameter."))
(:documentation "A mixin for a parameter that may have an initform."))
(defmethod parameter-initform-p ((self parameter-with-initform))
(slot-boundp self 'initform))
(defmethod parse-parameter ((self parameter-with-initform) form)
(etypecase form
(symbol (call-next-method))
(cons (call-next-method self (first form))
(when (cdr form)
(setf (parameter-initform self) (second form)))))
self)
;;;--------------------
(defclass auxiliary-parameter (parameter-with-initform parameter)
;; The order of the superclasses is important
;; to find the methods in the right order!
()
(:documentation "An auxiliary parameter."))
(defmethod parse-parameter ((self auxiliary-parameter) form)
(etypecase form
(symbol (call-next-method))
(cons (call-next-method)
(when (cddr form)
(error "~A specification must be a ~
list of two elements at most, not ~S"
(parameter-label self) form))))
self)
;;;--------------------
(defclass optional-parameter (parameter-with-initform parameter)
;; The order of the superclasses is important
;; to find the methods in the right order!
((indicator :accessor parameter-indicator
:initarg :indicator
:type symbol
:documentation "NIL, or the name of the indicator parameter."))
(:documentation "An optional parameter.
Note that while auxiliary-parameter and optional-parameter have the
same initform attribute, an optional-parameter is a different kind from
an auxiliary-parameter, semantically."))
(defmethod parameter-initform-p ((self optional-parameter))
(slot-boundp self 'initform))
(defmethod parameter-indicator-p ((self optional-parameter))
(slot-boundp self 'indicator))
(defmethod parse-parameter ((self optional-parameter) form)
(etypecase form
(symbol (call-next-method))
(cons (call-next-method)
(when (cddr form)
(setf (parameter-indicator self) (third form))
(when (cdddr form)
(error "~A specification must be a ~
list of three elements at most, not ~S"
(parameter-label self) form)))))
self)
;;;--------------------
(defclass generic-optional-parameter (parameter)
()
(:documentation "An optional parameter in generic lambda-lists."))
(defmethod parse-parameter ((self generic-optional-parameter) form)
(etypecase form
(symbol (call-next-method))
(cons (call-next-method self (first form))
(when (cdr form)
(error "~A specification must be a ~
list of one element at most, not ~S"
(parameter-label self) form)))))
;;;--------------------
(defclass parameter-with-keyword ()
((keyword :accessor parameter-keyword
:initarg :keyword
:type symbol
:documentation "NIL, or the keyword specified for the parameter."))
(:documentation "A mixin for keyword parameters."))
(defmethod parameter-keyword-p ((self parameter-with-keyword))
(slot-boundp self 'keyword))
(defmethod parse-parameter-name ((self parameter-with-keyword) form)
(etypecase form
(symbol (call-next-method))
(cons (if (= 2 (length form))
(progn
(call-next-method self (second form))
(setf (parameter-keyword self) (first form)))
(error "~A specification must be a ~
list of two elements, not ~S"
(parameter-label self) form))))
self)
(defmethod ensure-parameter-keyword ((self parameter-with-keyword))
(if (parameter-keyword-p self)
(parameter-keyword self)
(intern (string (parameter-name self)) "KEYWORD")))
;;;--------------------
(defclass keyword-parameter (parameter-with-keyword optional-parameter)
;; The order of the superclasses is important
;; to find the methods in the right order!
()
(:documentation "A keyword parameter."))
;;;--------------------
(defclass generic-keyword-parameter (parameter-with-keyword
generic-optional-parameter)
;; The order of the superclasses is important
;; to find the methods in the right order!
()
(:documentation "A generic keyword parameter."))
;;;--------------------
(defgeneric parameter-label (parameter)
(:method ((self parameter)) (declare (ignorable self)) "A mandatory parameter")
(:method ((self environment-parameter)) (declare (ignorable self)) "An environment parameter")
(:method ((self whole-parameter)) (declare (ignorable self)) "A whole parameter")
(:method ((self rest-parameter)) (declare (ignorable self)) "A rest parameter")
(:method ((self body-parameter)) (declare (ignorable self)) "A body parameter")
(:method ((self specialized-parameter)) (declare (ignorable self)) "A specialized parameter")
(:method ((self auxiliary-parameter)) (declare (ignorable self)) "An auxiliary parameter")
(:method ((self optional-parameter)) (declare (ignorable self)) "An optional parameter")
(:method ((self generic-optional-parameter)) (declare (ignorable self)) "A generic optional parameter")
(:method ((self keyword-parameter)) (declare (ignorable self)) "A keyword parameter")
(:method ((self generic-keyword-parameter)) (declare (ignorable self)) "A generic keyword parameter"))
(defgeneric parameter-lambda-list-keyword (parameter)
(:method ((self parameter)) (declare (ignorable self)) '&mandatory)
(:method ((self environment-parameter)) (declare (ignorable self)) '&environment)
(:method ((self whole-parameter)) (declare (ignorable self)) '&whole)
(:method ((self rest-parameter)) (declare (ignorable self)) '&rest)
(:method ((self body-parameter)) (declare (ignorable self)) '&body)
(:method ((self specialized-parameter)) (declare (ignorable self)) '&specialized)
(:method ((self auxiliary-parameter)) (declare (ignorable self)) '&aux)
(:method ((self optional-parameter)) (declare (ignorable self)) '&optional)
(:method ((self generic-optional-parameter)) (declare (ignorable self)) '&generic-optional)
(:method ((self keyword-parameter)) (declare (ignorable self)) '&key)
(:method ((self generic-keyword-parameter)) (declare (ignorable self)) '&generic-key))
(defgeneric parameter-specifier (parameter)
(:documentation "Return a parameter specifier sexp, which can be used to build a lambda list.")
(:method ((self parameter))
(parameter-name self))
(:method ((self specialized-parameter))
(cons (parameter-name self)
(when (parameter-specializer-p self)
(list (parameter-specializer self)))))
(:method ((self auxiliary-parameter))
(if (parameter-initform-p self)
(list (parameter-name self) (parameter-initform self))
(parameter-name self)))
(:method ((self parameter-with-initform))
(if (parameter-initform-p self)
(cons (parameter-name self)
(cons (parameter-initform self)
(when (parameter-indicator-p self)
(list (parameter-indicator self)))))
(parameter-name self)))
(:method ((self parameter-with-keyword))
(if (or (parameter-keyword-p self) (parameter-initform-p self))
(cons (if (parameter-keyword-p self)
(list (parameter-keyword self) (parameter-name self))
(parameter-name self))
(when (parameter-initform-p self)
(cons (parameter-initform self)
(when (parameter-indicator-p self)
(list (parameter-indicator self))))))
(parameter-name self)))
(:method ((self generic-keyword-parameter))
(if (parameter-keyword-p self)
(list (list (parameter-keyword self) (parameter-name self)))
(parameter-name self))))
;;;--------------------
(defclass or-ll ()
((mandatories :accessor lambda-list-mandatory-parameters
:initarg :mandatory-parameters
:initform '()
:type list)
(optionals :accessor lambda-list-optional-parameters
:initarg :optional-parameters
:initform '()
:type list)
(rest :accessor lambda-list-rest-parameter
:initarg :rest-parameter
:type (or null rest-parameter)))
(:documentation
"This class and its subclasses are mixin declaring formally
the attributes for the various lambda-list classes. Semantically,
some constraints may be different from one lambda-list to the other."))
(defgeneric lambda-list-mandatory-parameters-p (self)
(:method ((self or-ll)) (not (not (lambda-list-mandatory-parameters self))))
(:method ((self t)) (declare (ignorable self)) nil))
(defgeneric lambda-list-optional-parameters-p (self)
(:method ((self or-ll)) (not (not (lambda-list-optional-parameters self))))
(:method ((self t)) (declare (ignorable self)) nil))
(defgeneric lambda-list-rest-parameter-p (self)
(:method ((self or-ll)) (slot-boundp self 'rest))
(:method ((self t)) (declare (ignorable self)) nil))
(define-default-generic lambda-list-allow-other-keys-p or-ll nil)
(define-default-generic lambda-list-key-p or-ll nil)
(define-default-generic lambda-list-keyword-parameters or-ll nil)
(define-default-generic lambda-list-environment-parameter or-ll nil)
(define-default-generic lambda-list-auxiliary-parameters or-ll nil)
(define-default-generic lambda-list-whole-parameter or-ll nil)
(define-default-generic lambda-list-body-parameter or-ll nil)
(defclass orak-ll (or-ll)
((allow-other-keys-p :accessor lambda-list-allow-other-keys-p
:initarg :allow-other-keys-p
:initform nil
:type boolean
:documentation "Whether &ALLOW-OTHER-KEYS is present.")
(key-p :accessor lambda-list-key-p
:initarg :key-p
:initform nil
:type boolean
:documentation "Whether &KEY is present.")
;; We can have &KEY &ALLOW-OTHER-KEYS without any keyword.
(keys :accessor lambda-list-keyword-parameters
:initarg :keyword-parameters
:initform '()
:type list)))
(defgeneric lambda-list-keyword-parameters-p (self)
(:method ((self or-ll)) (not (not (lambda-list-keyword-parameters self)))))
(defclass orake-ll (orak-ll)
((environment :accessor lambda-list-environment-parameter
:initarg :environment-parameter
:type environment-parameter)))
(defclass oraka-ll (orak-ll)
((aux :accessor lambda-list-auxiliary-parameters
:initarg :auxiliary-parameters
:initform '()
:type list)))
(defclass orakawb-ll (oraka-ll)
((whole :accessor lambda-list-whole-parameter
:initarg :whole-parameter
:type whole-parameter)
(body :accessor lambda-list-body-parameter
:accessor lambda-list-rest-parameter
:initarg :body-parameter
:type rest-parameter)))
(defclass orakawbe-ll (orakawb-ll)
((environment :accessor lambda-list-environment-parameter
:initarg :environment-parameter
:type environment-parameter)))
(defgeneric lambda-list-auxiliary-parameters-p (self)
(:method ((self oraka-ll)) (not (not (lambda-list-auxiliary-parameters self))))
(:method ((self t)) (declare (ignorable self)) nil))
(defgeneric lambda-list-whole-parameter-p (self)
(:method ((self orakawb-ll)) (slot-boundp self 'whole))
(:method ((self t)) (declare (ignorable self)) nil))
(defgeneric lambda-list-body-parameter-p (self)
(:method ((self orakawb-ll)) (slot-boundp self 'body))
(:method ((self t)) (declare (ignorable self)) nil))
(defgeneric lambda-list-environment-parameter-p (self)
(:method ((self orakawbe-ll)) (slot-boundp self 'environment))
(:method ((self orake-ll)) (slot-boundp self 'environment))
(:method ((self t)) (declare (ignorable self)) nil))
(defgeneric remove-environment-parameter (ll)
(:method ((ll orake-ll)) (slot-makunbound ll 'environment))
(:method ((ll orakawbe-ll)) (slot-makunbound ll 'environment))
(:method ((ll t))))
(defgeneric remove-whole-parameter (ll)
(:method ((ll orakawb-ll)) (slot-makunbound ll 'whole))
(:method ((ll t))))
;;;----------------------------------------
(defclass lambda-list ()
((original :accessor original-lambda-list
:initarg :lambda-list
:type list))
(:documentation "An abstract lambda-list."))
(defgeneric lambda-list-parameters (lambda-list)
(:documentation "An ordered list of the parameters or destructuring-lambda-list instances."))
(defclass ordinary-lambda-list (lambda-list oraka-ll) ())
(defclass boa-lambda-list (lambda-list oraka-ll) ())
(defclass specialized-lambda-list (lambda-list oraka-ll) ())
(defclass modify-macro-lambda-list (lambda-list or-ll) ())
(defclass generic-lambda-list (lambda-list orak-ll) ())
(defclass macro-lambda-list (lambda-list orakawbe-ll) ())
(defclass type-lambda-list (lambda-list orakawbe-ll) ())
(defclass destructuring-lambda-list (lambda-list orakawb-ll) ())
(defclass setf-lambda-list (lambda-list orake-ll) ())
(defclass method-combination-lambda-list (lambda-list oraka-ll) ())
(defgeneric lambda-list-kind (lambda-list)
(:method ((self ordinary-lambda-list)) (declare (ignorable self)) :ordinary)
(:method ((self boa-lambda-list)) (declare (ignorable self)) :boa)
(:method ((self specialized-lambda-list)) (declare (ignorable self)) :specialized)
(:method ((self modify-macro-lambda-list)) (declare (ignorable self)) :modify-macro)
(:method ((self generic-lambda-list)) (declare (ignorable self)) :generic)
(:method ((self macro-lambda-list)) (declare (ignorable self)) :macro)
(:method ((self type-lambda-list)) (declare (ignorable self)) :type)
(:method ((self destructuring-lambda-list)) (declare (ignorable self)) :destructuring)
(:method ((self setf-lambda-list)) (declare (ignorable self)) :setf)
(:method ((self method-combination-lambda-list)) (declare (ignorable self)) :method-combination))
(defgeneric lambda-list-allowed-keywords (lambda-list)
(:method ((self ordinary-lambda-list))
(declare (ignorable self))
'(&optional &rest &allow-other-keys &key &aux))
(:method ((self boa-lambda-list))
(declare (ignorable self))
'(&optional &rest &allow-other-keys &key &aux))
(:method ((self specialized-lambda-list))
(declare (ignorable self))
'(&optional &rest &allow-other-keys &key &aux))
(:method ((self modify-macro-lambda-list))
(declare (ignorable self))
'(&optional &rest))
(:method ((self generic-lambda-list))
(declare (ignorable self))
'(&optional &rest &allow-other-keys &key))
(:method ((self macro-lambda-list))
(declare (ignorable self))
'(&optional &rest &allow-other-keys &key &aux &whole &body &environment))
(:method ((self type-lambda-list))
(declare (ignorable self))
'(&optional &rest &allow-other-keys &key &aux &whole &body &environment))
(:method ((self destructuring-lambda-list))
(declare (ignorable self))
'(&optional &rest &allow-other-keys &key &aux &whole &body))
(:method ((self setf-lambda-list))
(declare (ignorable self))
'(&optional &rest &allow-other-keys &key &environment))
(:method ((self method-combination-lambda-list))
(declare (ignorable self))
'(&optional &rest &allow-other-keys &key &aux &whole)))
(defmethod lambda-list-mandatory-parameter-count ((self or-ll))
"RETURN: The number of mandatory parameters."
(length (lambda-list-mandatory-parameters self)))
(defmethod lambda-list-optional-parameter-count ((self or-ll))
"RETURN: The number of optional parameters."
(length (lambda-list-mandatory-parameters self)))
(defgeneric lambda-list-rest-p (self)
(:documentation "RETURN: Whether &REST or &BODY parameters are present.")
(:method ((self or-ll)) (lambda-list-rest-parameter-p self))
(:method ((self orakawb-ll)) (or (lambda-list-rest-parameter-p self)
(lambda-list-body-parameter-p self))))
;; auxvars ::= [&aux {var | (var [init-form])}*]
;; optvars ::= [&optional {var | (var [init-form [supplied-p-parameter]])}*]
;; goptvars ::= [&optional {var | (var)}*]
(defmethod parse-optvars ((self or-ll) current slot lambda-list-keyword class)
"
DO: Parses optional parameters.
RETURN: The remaining tokens.
"
(when (eq (car current) lambda-list-keyword)
(pop current)
(setf (slot-value self slot)
(loop
:while (and current (not (member (car current) lambda-list-keywords)))
:collect (parse-parameter (make-instance class) (pop current)))))
current)
(defmethod auxvars ((self or-ll) current)
(parse-optvars self current 'aux '&aux 'auxiliary-parameter))
(defmethod optvars ((self or-ll) current)
(parse-optvars self current 'optionals '&optional 'optional-parameter))
(defmethod goptvars ((self or-ll) current)
(parse-optvars self current 'optionals '&optional 'generic-optional-parameter))
;; keyvars ::= [&key {var | ({var | (keyword-name var)} [init-form [supplied-p-parameter]])}* [&allow-other-keys]]
;; gkeyvars ::= [&key {var | ({var | (keyword-name var)})}* [&allow-other-keys]])
(defmethod parse-keyvars ((self orak-ll) current class)
"
DO: Parses keywork parameters.
RETURN: The remaining tokens.
"
(when (eq '&key (car current))
(pop current)
(setf (lambda-list-key-p self) t
(lambda-list-keyword-parameters self)
(loop
:while (and current (not (member (car current) lambda-list-keywords)))
:collect (parse-parameter (make-instance class) (pop current)))
(lambda-list-allow-other-keys-p self)
(and (eq '&allow-other-keys (car current)) (pop current) t)))
current)
(defmethod keyvars ((self orak-ll) current)
(parse-keyvars self current 'keyword-parameter))
(defmethod gkeyvars ((self orak-ll) current)
(parse-keyvars self current 'generic-keyword-parameter))
;; reqvars ::= var*
;; sreqvars ::= {var | (var [specializer])}*
;; preqvars ::= {var | destructuring-lambda-list}*
(defmethod parse-reqvars ((self or-ll) current class)
"
DO: Parses required parameters.
RETURN: (values list-of-parameters following)
"
(setf (lambda-list-mandatory-parameters self)
(loop
:while (and current (not (member (car current) lambda-list-keywords)))
:collect (parse-parameter (make-instance class) (pop current))))
current)
(defmethod reqvars ((self or-ll) current)
(parse-reqvars self current 'parameter))
(defmethod sreqvars ((self or-ll) current)
(parse-reqvars self current 'specialized-parameter))
(defmethod preqvars ((self or-ll) current)
"
DO: Parses required parameters or patterns.
RETURN: (values list-of-parameters following)
"
(setf (lambda-list-mandatory-parameters self)
(loop
:while (and current (not (member (car current) lambda-list-keywords)))
:collect (if (consp (car current))
(parse-original-lambda-list
(make-instance 'destructuring-lambda-list
:lambda-list (pop current)))
(parse-parameter
(make-instance 'parameter)
(pop current)))))
current)
;; bodyvar ::= [{&rest | &body} var]
;; restvar ::= [&rest var]
;; wholevar ::= [&whole var]
;; envvar ::= [&environment var]
(defun bodyvar (self current)
"
RETURN: (values parameter following)
"
(flet ((check-duplicate (lambda-list-keyword)
(when (lambda-list-rest-p self)
(error "~:[&BODY~;&REST~] parameter already given before ~A in ~S"
(lambda-list-rest-parameter-p self)
lambda-list-keyword
(original-lambda-list self)))))
(case (car current)
((&rest)
(check-duplicate (pop current))
(setf (lambda-list-rest-parameter self)
(parse-parameter (make-instance 'rest-parameter) (pop current))))
((&body)
(check-duplicate (pop current))
(setf (lambda-list-body-parameter self)
(parse-parameter (make-instance 'body-parameter) (pop current)))))
current))
(defun parse-var (self current slot lambda-list-keyword class )
"
RETURN: (values parameter following)
"
(when (eq (car current) lambda-list-keyword)
(pop current)
(when (slot-boundp self slot)
(error "~A parameter duplicated in ~S"
lambda-list-keyword (original-lambda-list self)))
(setf (slot-value self slot)
(parse-parameter (make-instance class) (pop current))))
current)
(defun restvar (self current)
(parse-var self current 'rest '&rest 'rest-parameter))
(defun wholevar (self current)
(parse-var self current 'whole '&whole 'whole-parameter))
(defun envvar (self current)
(parse-var self current 'environment '&environment 'environment-parameter))
;; macro-lambda-list ::= (wholevar envvar preqvars envvar optvars envvar
;; bodyvar envvar keyvars envvar auxvars envvar)
;; | (wholevar envvar preqvars envvar optvars envvar . var)
;;
;; destructuring-lambda-list ::= (wholevar preqvars optvars bodyvar keyvars auxvars)
;; | (wholevar preqvars optvars . var)
;;
;; type-lambda-list ::= macro-lambda-list
;;
;;
;; ordinary-lambda-list ::= (reqvars optvars restvar keyvars auxvars)
;; boa-lambda-list ::= ordinary-lambda-list
;; specialized-lambda-list ::= (sreqvars optvars restvar keyvars auxvars)
;; generic-lambda-list ::= (reqvars goptvars restvar gkeyvars)
;; setf-lambda-list ::= (reqvars optvars restvar keyvars envvar)
;; modify-macro-lambda-list ::= (reqvars optvars restvar)
;; method-combination-lambda-list ::= (wholevar reqvars optvars restvar keyvars auxvars)
(defun parse-rest (self current syntax)
(if (listp current)
(dolist (fun syntax current)
(setf current (funcall fun self current)))
(restvar self (list '&rest current))))
(defun destructuring-rest (self current)
(parse-rest self current '(bodyvar keyvars auxvars)))
(defun macro-rest (self current)
(parse-rest self current '(bodyvar envvar keyvars envvar auxvars envvar)))
(defgeneric lambda-list-syntax (self)
(:method ((self ordinary-lambda-list))
(declare (ignorable self))
'(reqvars optvars restvar keyvars auxvars))
(:method ((self boa-lambda-list))
(declare (ignorable self))
'(reqvars optvars restvar keyvars auxvars))
(:method ((self specialized-lambda-list))
(declare (ignorable self))
'(sreqvars optvars restvar keyvars auxvars))
(:method ((self generic-lambda-list))
(declare (ignorable self))
'(reqvars goptvars restvar gkeyvars))
(:method ((self setf-lambda-list))
(declare (ignorable self))
'(reqvars optvars restvar keyvars envvar))
(:method ((self modify-macro-lambda-list))
(declare (ignorable self))
'(reqvars optvars restvar))
(:method ((self method-combination-lambda-list))
(declare (ignorable self))
'(wholevar reqvars optvars restvar keyvars auxvars))
(:method ((self macro-lambda-list))
(declare (ignorable self))
'(wholevar envvar preqvars envvar optvars envvar macro-rest))
(:method ((self type-lambda-list))
(declare (ignorable self))
'(wholevar envvar preqvars envvar optvars envvar macro-rest))
(:method ((self destructuring-lambda-list))
(declare (ignorable self))
'(wholevar preqvars optvars destructuring-rest)))
(defmethod parse-original-lambda-list ((self lambda-list))
(let ((current (original-lambda-list self)))
(dolist (fun (lambda-list-syntax self))
(setf current (funcall fun self current)))
(when current
(error "Syntax error in ~(~A~) at: ~S~%in ~S"
(class-name (class-of self)) current (original-lambda-list self)))
self))
(defun parse-lambda-list (lambda-list &optional (kind :ordinary))
"
DO: Parse a lambda-list of the specified kind.
KIND: (MEMBER :ORDINARY :BOA :SPECIALIZED :MODIFY-MACRO :GENERIC
:MACRO :TYPE :DESTRUCTURING :SETF :METHOD-COMBINATION)
RETURN: A lambda-list instance.
NOTE: In the case of :macro, :destructuring lambda lists, some
parameter lists may further contain destructuring-lambda-list
instances instead of lambda-list-parameter instances.
"
(parse-original-lambda-list
(make-instance
(or (cdr (assoc
kind
'((:ordinary . ordinary-lambda-list)
(:boa . boa-lambda-list)
(:specialized . specialized-lambda-list)
(:modify-macro . modify-macro-lambda-list)
(:generic . generic-lambda-list)
(:macro . macro-lambda-list)
(:type . type-lambda-list)
(:destructuring . destructuring-lambda-list)
(:setf . setf-lambda-list)
(:method-combination . method-combination-lambda-list))))
(error "Invalid lambda-list kind ~S" kind))
:lambda-list lambda-list)))
;;------------------------------------------------------------------------
(defgeneric parameter-help-label (self)
(:method ((self parameter))
(format nil "~A" (parameter-name self)))
(:method ((self optional-parameter))
(format nil "[~A]" (parameter-name self)))
(:method ((self rest-parameter))
(format nil "~A..." (parameter-name self)))
(:method ((self body-parameter))
(format nil "~A..." (parameter-name self)))
(:method ((self keyword-parameter))
(format nil "~A" (ensure-parameter-keyword self))))
(defmethod make-help ((self lambda-list))
"
RETURN: A list describing the lambda-list for the user. Each item is a cons:
(lambda-list-keyword . description) where
- the lambda-list-keyword is either
:mandatory, :optional, :rest, :body, :key, or :allow-other-keys.
- the description is a string indicating the name of the parameter,
and whether it's optional '[n]' or takes several arguments 'n...'.
"
(append
;; mandatory:
(mapcar (lambda (par) (cons :mandatory (parameter-help-label par)))
(lambda-list-mandatory-parameters self))
;; optional:
(mapcar (lambda (par) (cons :optional (parameter-help-label par)))
(lambda-list-optional-parameters self))
(when (lambda-list-rest-parameter-p self)
(list (cons :rest (parameter-help-label (lambda-list-rest-parameter self)))))
(when (lambda-list-body-parameter-p self)
(list (cons :body (parameter-help-label (lambda-list-body-parameter self)))))
;; keywords:
(mapcar (lambda (par) (cons :key (parameter-help-label par)))
(lambda-list-keyword-parameters self))
(when (lambda-list-allow-other-keys-p self)
(list (cons :allow-other-keys "(other keys allowed)")))))
(defmethod make-argument-list ((self lambda-list))
"
RETURN: A list of arguments taken from the parameters usable with apply
to call a function with the same lambda-list.
NOTE: If no there is no &rest parameter in the lambda-list,
then a NIL is put at the end of the result, for APPLY.
EXAMPLE: `(apply ,@(make-argument-list ll))
"
(let ((rest (lambda-list-rest-p self)))
(append
(mapcar (function parameter-name) (lambda-list-mandatory-parameters self))
(mapcar (function parameter-name) (lambda-list-optional-parameters self))
(when (lambda-list-key-p self)
(mapcan (lambda (par) (list (ensure-parameter-keyword par)
(parameter-name par)))
(lambda-list-keyword-parameters self)))
(list (if rest
(parameter-name (lambda-list-rest-parameter self))
'())))))
(defgeneric parameters-by-category (ll)
(:method ((self lambda-list))
(flet ((destructp (parameter)
(typep parameter 'destructuring-lambda-list)))
(let* ((mandatories (lambda-list-mandatory-parameters self))
(destructs (remove-if-not (function destructp) mandatories))
(desman '())
(desopt '())
(desres '())
(deskey '()))
(dolist (destruct destructs)
(multiple-value-bind (man opt res key) (parameters-by-category destruct)
(setf desman (nconc desman man)
desopt (nconc desopt opt)
desres (nconc desres res)
deskey (nconc deskey key))))
(values (append (remove-if (function destructp) mandatories) desman)
(append (lambda-list-optional-parameters self) desopt)
(append (when (lambda-list-rest-p self)
(list (lambda-list-rest-parameter self))) desres)
(append (when (lambda-list-key-p self)
(lambda-list-keyword-parameters self)) deskey))))))
(defmethod make-flat-argument-list ((self lambda-list))
"
RETURN: A list of arguments taken from the parameters usable with apply
to call a function with the same lambda-list.
NOTE: If no there is no &rest parameter in the lambda-list,
then a NIL is put at the end of the result, for APPLY.
NOTE: If the lambda-list is a macro-lambda-list or a
destructuring-lambda-list, some of the mandatory parameters
may be sub- destructuring-lambda-lists (and recursively). The
arguments collected from those sub- lambda lists are appended