optargs.scm 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500
  1. ;;;; optargs.scm -- support for optional arguments
  2. ;;;;
  3. ;;;; Copyright (C) 1997, 1998, 1999, 2001, 2002, 2004, 2006 Free Software Foundation, Inc.
  4. ;;;;
  5. ;;;; This library is free software; you can redistribute it and/or
  6. ;;;; modify it under the terms of the GNU Lesser General Public
  7. ;;;; License as published by the Free Software Foundation; either
  8. ;;;; version 3 of the License, or (at your option) any later version.
  9. ;;;;
  10. ;;;; This library is distributed in the hope that it will be useful,
  11. ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. ;;;; Lesser General Public License for more details.
  14. ;;;;
  15. ;;;; You should have received a copy of the GNU Lesser General Public
  16. ;;;; License along with this library; if not, write to the Free Software
  17. ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  18. ;;;;
  19. ;;;; Contributed by Maciej Stachowiak <mstachow@alum.mit.edu>
  20. ;;; Commentary:
  21. ;;; {Optional Arguments}
  22. ;;;
  23. ;;; The C interface for creating Guile procedures has a very handy
  24. ;;; "optional argument" feature. This module attempts to provide
  25. ;;; similar functionality for procedures defined in Scheme with
  26. ;;; a convenient and attractive syntax.
  27. ;;;
  28. ;;; exported macros are:
  29. ;;; let-optional
  30. ;;; let-optional*
  31. ;;; let-keywords
  32. ;;; let-keywords*
  33. ;;; lambda*
  34. ;;; define*
  35. ;;; define*-public
  36. ;;; defmacro*
  37. ;;; defmacro*-public
  38. ;;;
  39. ;;;
  40. ;;; Summary of the lambda* extended parameter list syntax (brackets
  41. ;;; are used to indicate grouping only):
  42. ;;;
  43. ;;; ext-param-list ::= [identifier]* [#:optional [ext-var-decl]+]?
  44. ;;; [#:key [ext-var-decl]+ [#:allow-other-keys]?]?
  45. ;;; [[#:rest identifier]|[. identifier]]?
  46. ;;;
  47. ;;; ext-var-decl ::= identifier | ( identifier expression )
  48. ;;;
  49. ;;; The characters `*', `+' and `?' are not to be taken literally; they
  50. ;;; mean respectively, zero or more occurences, one or more occurences,
  51. ;;; and one or zero occurences.
  52. ;;;
  53. ;;; Code:
  54. (define-module (mes optargs)
  55. #:use-module (system base pmatch)
  56. #:replace (lambda*)
  57. #:export-syntax (let-optional
  58. let-optional*
  59. let-keywords
  60. let-keywords*
  61. define*
  62. define*-public
  63. defmacro*
  64. defmacro*-public))
  65. ;; let-optional rest-arg (binding ...) . body
  66. ;; let-optional* rest-arg (binding ...) . body
  67. ;; macros used to bind optional arguments
  68. ;;
  69. ;; These two macros give you an optional argument interface that is
  70. ;; very "Schemey" and introduces no fancy syntax. They are compatible
  71. ;; with the scsh macros of the same name, but are slightly
  72. ;; extended. Each of binding may be of one of the forms <var> or
  73. ;; (<var> <default-value>). rest-arg should be the rest-argument of
  74. ;; the procedures these are used from. The items in rest-arg are
  75. ;; sequentially bound to the variable namess are given. When rest-arg
  76. ;; runs out, the remaining vars are bound either to the default values
  77. ;; or to `#f' if no default value was specified. rest-arg remains
  78. ;; bound to whatever may have been left of rest-arg.
  79. ;;
  80. (defmacro let-optional (REST-ARG BINDINGS . BODY)
  81. (let-optional-template REST-ARG BINDINGS BODY 'let))
  82. (defmacro let-optional* (REST-ARG BINDINGS . BODY)
  83. (let-optional-template REST-ARG BINDINGS BODY 'let*))
  84. ;; let-keywords rest-arg allow-other-keys? (binding ...) . body
  85. ;; let-keywords* rest-arg allow-other-keys? (binding ...) . body
  86. ;; macros used to bind keyword arguments
  87. ;;
  88. ;; These macros pick out keyword arguments from rest-arg, but do not
  89. ;; modify it. This is consistent at least with Common Lisp, which
  90. ;; duplicates keyword args in the rest arg. More explanation of what
  91. ;; keyword arguments in a lambda list look like can be found below in
  92. ;; the documentation for lambda*. Bindings can have the same form as
  93. ;; for let-optional. If allow-other-keys? is false, an error will be
  94. ;; thrown if anything that looks like a keyword argument but does not
  95. ;; match a known keyword parameter will result in an error.
  96. ;;
  97. (defmacro let-keywords (REST-ARG ALLOW-OTHER-KEYS? BINDINGS . BODY)
  98. (let-keywords-template REST-ARG ALLOW-OTHER-KEYS? BINDINGS BODY 'let))
  99. (defmacro let-keywords* (REST-ARG ALLOW-OTHER-KEYS? BINDINGS . BODY)
  100. (let-keywords-template REST-ARG ALLOW-OTHER-KEYS? BINDINGS BODY 'let*))
  101. ;; some utility procedures for implementing the various let-forms.
  102. (define (let-o-k-template REST-ARG BINDINGS BODY let-type proc)
  103. (let ((bindings (map (lambda (x)
  104. (if (list? x)
  105. x
  106. (list x #f)))
  107. BINDINGS)))
  108. `(,let-type ,(map proc bindings) ,@BODY)))
  109. (define (let-optional-template REST-ARG BINDINGS BODY let-type)
  110. (if (null? BINDINGS)
  111. `(let () ,@BODY)
  112. (let-o-k-template REST-ARG BINDINGS BODY let-type
  113. (lambda (optional)
  114. `(,(car optional)
  115. (cond
  116. ((not (null? ,REST-ARG))
  117. (let ((result (car ,REST-ARG)))
  118. ,(list 'set! REST-ARG
  119. `(cdr ,REST-ARG))
  120. result))
  121. (else
  122. ,(cadr optional))))))))
  123. (define (let-keywords-template REST-ARG ALLOW-OTHER-KEYS? BINDINGS BODY let-type)
  124. (if (null? BINDINGS)
  125. `(let () ,@BODY)
  126. (let* ((kb-list-gensym (gensym "kb:G"))
  127. (bindfilter (lambda (key)
  128. `(,(car key)
  129. (cond
  130. ((assq ',(car key) ,kb-list-gensym)
  131. => cdr)
  132. (else
  133. ,(cadr key)))))))
  134. `(let ((,kb-list-gensym ((if (not mes?) (@@ (mes optargs) rest-arg->keyword-binding-list)
  135. rest-arg->keyword-binding-list)
  136. ,REST-ARG ',(map (lambda (x) (symbol->keyword (if (pair? x) (car x) x)))
  137. BINDINGS)
  138. ,ALLOW-OTHER-KEYS?)))
  139. ,(let-o-k-template REST-ARG BINDINGS BODY let-type bindfilter)))))
  140. (define (rest-arg->keyword-binding-list rest-arg keywords allow-other-keys?)
  141. (if (null? rest-arg)
  142. '()
  143. (let loop ((first (car rest-arg))
  144. (rest (cdr rest-arg))
  145. (accum '()))
  146. (let ((next (lambda (a)
  147. (if (null? (cdr rest))
  148. a
  149. (loop (cadr rest) (cddr rest) a)))))
  150. (if (keyword? first)
  151. (cond
  152. ((memq first keywords)
  153. (if (null? rest)
  154. (error "Keyword argument has no value:" first)
  155. (next (cons (cons (keyword->symbol first)
  156. (car rest)) accum))))
  157. ((not allow-other-keys?)
  158. (error "Unknown keyword in arguments:" first))
  159. (else (if (null? rest)
  160. accum
  161. (next accum))))
  162. (if (null? rest)
  163. accum
  164. (loop (car rest) (cdr rest) accum)))))))
  165. ;; lambda* args . body
  166. ;; lambda extended for optional and keyword arguments
  167. ;;
  168. ;; lambda* creates a procedure that takes optional arguments. These
  169. ;; are specified by putting them inside brackets at the end of the
  170. ;; paramater list, but before any dotted rest argument. For example,
  171. ;; (lambda* (a b #:optional c d . e) '())
  172. ;; creates a procedure with fixed arguments a and b, optional arguments c
  173. ;; and d, and rest argument e. If the optional arguments are omitted
  174. ;; in a call, the variables for them are bound to `#f'.
  175. ;;
  176. ;; lambda* can also take keyword arguments. For example, a procedure
  177. ;; defined like this:
  178. ;; (lambda* (#:key xyzzy larch) '())
  179. ;; can be called with any of the argument lists (#:xyzzy 11)
  180. ;; (#:larch 13) (#:larch 42 #:xyzzy 19) (). Whichever arguments
  181. ;; are given as keywords are bound to values.
  182. ;;
  183. ;; Optional and keyword arguments can also be given default values
  184. ;; which they take on when they are not present in a call, by giving a
  185. ;; two-item list in place of an optional argument, for example in:
  186. ;; (lambda* (foo #:optional (bar 42) #:key (baz 73)) (list foo bar baz))
  187. ;; foo is a fixed argument, bar is an optional argument with default
  188. ;; value 42, and baz is a keyword argument with default value 73.
  189. ;; Default value expressions are not evaluated unless they are needed
  190. ;; and until the procedure is called.
  191. ;;
  192. ;; lambda* now supports two more special parameter list keywords.
  193. ;;
  194. ;; lambda*-defined procedures now throw an error by default if a
  195. ;; keyword other than one of those specified is found in the actual
  196. ;; passed arguments. However, specifying #:allow-other-keys
  197. ;; immediately after the keyword argument declarations restores the
  198. ;; previous behavior of ignoring unknown keywords. lambda* also now
  199. ;; guarantees that if the same keyword is passed more than once, the
  200. ;; last one passed is the one that takes effect. For example,
  201. ;; ((lambda* (#:key (heads 0) (tails 0)) (display (list heads tails)))
  202. ;; #:heads 37 #:tails 42 #:heads 99)
  203. ;; would result in (99 47) being displayed.
  204. ;;
  205. ;; #:rest is also now provided as a synonym for the dotted syntax rest
  206. ;; argument. The argument lists (a . b) and (a #:rest b) are equivalent in
  207. ;; all respects to lambda*. This is provided for more similarity to DSSSL,
  208. ;; MIT-Scheme and Kawa among others, as well as for refugees from other
  209. ;; Lisp dialects.
  210. (defmacro lambda* (ARGLIST . BODY)
  211. (parse-arglist
  212. ARGLIST
  213. (lambda (non-optional-args optionals keys aok? rest-arg)
  214. ;; Check for syntax errors.
  215. (if (not (every? symbol? non-optional-args))
  216. (error "Syntax error in fixed argument declaration."))
  217. (if (not (every? ext-decl? optionals))
  218. (error "Syntax error in optional argument declaration."))
  219. (if (not (every? ext-decl? keys))
  220. (error "Syntax error in keyword argument declaration."))
  221. (if (not (or (symbol? rest-arg) (eq? #f rest-arg)))
  222. (error "Syntax error in rest argument declaration."))
  223. ;; generate the code.
  224. (let ((rest-gensym (or rest-arg (gensym "lambda*:G")))
  225. (lambda-gensym (gensym "lambda*:L")))
  226. (if (not (and (null? optionals) (null? keys)))
  227. `(let ((,lambda-gensym
  228. (lambda (,@non-optional-args . ,rest-gensym)
  229. ;; Make sure that if the proc had a docstring, we put it
  230. ;; here where it will be visible.
  231. ,@(if (and (not (null? BODY))
  232. (string? (car BODY)))
  233. (list (car BODY))
  234. '())
  235. (let-optional*
  236. ,rest-gensym
  237. ,optionals
  238. (let-keywords* ,rest-gensym
  239. ,aok?
  240. ,keys
  241. ,@(if (and (not rest-arg) (null? keys))
  242. `((if (not (null? ,rest-gensym))
  243. (error "Too many arguments.")))
  244. '())
  245. (let ()
  246. ,@BODY))))))
  247. (set-procedure-property! ,lambda-gensym 'arglist
  248. '(,non-optional-args
  249. ,optionals
  250. ,keys
  251. ,aok?
  252. ,rest-arg))
  253. ,lambda-gensym)
  254. `(lambda (,@non-optional-args . ,(if rest-arg rest-arg '()))
  255. ,@BODY))))))
  256. (define (every? pred lst)
  257. (or (null? lst)
  258. (and (pred (car lst))
  259. (every? pred (cdr lst)))))
  260. (define (ext-decl? obj)
  261. (or (symbol? obj)
  262. (and (list? obj) (= 2 (length obj)) (symbol? (car obj)))))
  263. ;; XXX - not tail recursive
  264. (define (improper-list-copy obj)
  265. (if (pair? obj)
  266. (cons (car obj) (improper-list-copy (cdr obj)))
  267. obj))
  268. (define (parse-arglist arglist cont)
  269. (define (split-list-at val lst cont)
  270. (cond
  271. ((memq val lst)
  272. => (lambda (pos)
  273. (if (memq val (cdr pos))
  274. (error (with-output-to-string
  275. (lambda ()
  276. (map display `(,val
  277. " specified more than once in argument list.")))))
  278. (cont (reverse (cdr (memq val (reverse lst)))) (cdr pos) #t))))
  279. (else (cont lst '() #f))))
  280. (define (parse-opt-and-fixed arglist keys aok? rest cont)
  281. (split-list-at
  282. #:optional arglist
  283. (lambda (before after split?)
  284. (if (and split? (null? after))
  285. (error "#:optional specified but no optional arguments declared.")
  286. (cont before after keys aok? rest)))))
  287. (define (parse-keys arglist rest cont)
  288. (split-list-at
  289. #:allow-other-keys arglist
  290. (lambda (aok-before aok-after aok-split?)
  291. (if (and aok-split? (not (null? aok-after)))
  292. (error "#:allow-other-keys not at end of keyword argument declarations.")
  293. (split-list-at
  294. #:key aok-before
  295. (lambda (key-before key-after key-split?)
  296. (cond
  297. ((and aok-split? (not key-split?))
  298. (error "#:allow-other-keys specified but no keyword arguments declared."))
  299. (key-split?
  300. (cond
  301. ((null? key-after) (error "#:key specified but no keyword arguments declared."))
  302. ((memq #:optional key-after) (error "#:optional arguments declared after #:key arguments."))
  303. (else (parse-opt-and-fixed key-before key-after aok-split? rest cont))))
  304. (else (parse-opt-and-fixed arglist '() #f rest cont)))))))))
  305. (define (parse-rest arglist cont)
  306. (cond
  307. ((null? arglist) (cont '() '() '() #f #f))
  308. ((not (pair? arglist)) (cont '() '() '() #f arglist))
  309. ((not (list? arglist))
  310. (let* ((copy (improper-list-copy arglist))
  311. (lp (last-pair copy))
  312. (ra (cdr lp)))
  313. (set-cdr! lp '())
  314. (if (memq #:rest copy)
  315. (error "Cannot specify both #:rest and dotted rest argument.")
  316. (parse-keys copy ra cont))))
  317. (else (split-list-at
  318. #:rest arglist
  319. (lambda (before after split?)
  320. (if split?
  321. (case (length after)
  322. ((0) (error "#:rest not followed by argument."))
  323. ((1) (parse-keys before (car after) cont))
  324. (else (error "#:rest argument must be declared last.")))
  325. (parse-keys before #f cont)))))))
  326. (parse-rest arglist cont))
  327. ;; define* args . body
  328. ;; define*-public args . body
  329. ;; define and define-public extended for optional and keyword arguments
  330. ;;
  331. ;; define* and define*-public support optional arguments with
  332. ;; a similar syntax to lambda*. They also support arbitrary-depth
  333. ;; currying, just like Guile's define. Some examples:
  334. ;; (define* (x y #:optional a (z 3) #:key w . u) (display (list y z u)))
  335. ;; defines a procedure x with a fixed argument y, an optional agument
  336. ;; a, another optional argument z with default value 3, a keyword argument w,
  337. ;; and a rest argument u.
  338. ;; (define-public* ((foo #:optional bar) #:optional baz) '())
  339. ;; This illustrates currying. A procedure foo is defined, which,
  340. ;; when called with an optional argument bar, returns a procedure that
  341. ;; takes an optional argument baz.
  342. ;;
  343. ;; Of course, define*[-public] also supports #:rest and #:allow-other-keys
  344. ;; in the same way as lambda*.
  345. (defmacro define* (ARGLIST . BODY)
  346. (define*-guts 'define ARGLIST BODY))
  347. (defmacro define*-public (ARGLIST . BODY)
  348. (define*-guts 'define-public ARGLIST BODY))
  349. ;; The guts of define* and define*-public.
  350. (define (define*-guts DT ARGLIST BODY)
  351. (define (nest-lambda*s arglists)
  352. (if (null? arglists)
  353. BODY
  354. `((lambda* ,(car arglists) ,@(nest-lambda*s (cdr arglists))))))
  355. (define (define*-guts-helper ARGLIST arglists)
  356. (let ((first (car ARGLIST))
  357. (al (cons (cdr ARGLIST) arglists)))
  358. (if (symbol? first)
  359. `(,DT ,first ,@(nest-lambda*s al))
  360. (define*-guts-helper first al))))
  361. (if (symbol? ARGLIST)
  362. `(,DT ,ARGLIST ,@BODY)
  363. (define*-guts-helper ARGLIST '())))
  364. ;; defmacro* name args . body
  365. ;; defmacro*-public args . body
  366. ;; defmacro and defmacro-public extended for optional and keyword arguments
  367. ;;
  368. ;; These are just like defmacro and defmacro-public except that they
  369. ;; take lambda*-style extended paramter lists, where #:optional,
  370. ;; #:key, #:allow-other-keys and #:rest are allowed with the usual
  371. ;; semantics. Here is an example of a macro with an optional argument:
  372. ;; (defmacro* transmorgify (a #:optional b)
  373. (defmacro defmacro* (NAME ARGLIST . BODY)
  374. `(define-macro ,NAME #f (lambda* ,ARGLIST ,@BODY)))
  375. (defmacro defmacro*-public (NAME ARGLIST . BODY)
  376. `(begin
  377. (defmacro* ,NAME ,ARGLIST ,@BODY)
  378. (export-syntax ,NAME)))
  379. ;;; Support for optional & keyword args with the interpreter.
  380. (define *uninitialized* (list 'uninitialized))
  381. (define (parse-lambda-case spec inits predicate args)
  382. (pmatch spec
  383. ((,nreq ,nopt ,rest-idx ,nargs ,allow-other-keys? ,kw-indices)
  384. (define (req args prev tail n)
  385. (cond
  386. ((zero? n)
  387. (if prev (set-cdr! prev '()))
  388. (let ((slots-tail (make-list (- nargs nreq) *uninitialized*)))
  389. (opt (if prev (append! args slots-tail) slots-tail)
  390. slots-tail tail nopt inits)))
  391. ((null? tail)
  392. #f) ;; fail
  393. (else
  394. (req args tail (cdr tail) (1- n)))))
  395. (define (opt slots slots-tail args-tail n inits)
  396. (cond
  397. ((zero? n)
  398. (rest-or-key slots slots-tail args-tail inits rest-idx))
  399. ((null? args-tail)
  400. (set-car! slots-tail (apply (car inits) slots))
  401. (opt slots (cdr slots-tail) '() (1- n) (cdr inits)))
  402. (else
  403. (set-car! slots-tail (car args-tail))
  404. (opt slots (cdr slots-tail) (cdr args-tail) (1- n) (cdr inits)))))
  405. (define (rest-or-key slots slots-tail args-tail inits rest-idx)
  406. (cond
  407. (rest-idx
  408. ;; it has to be this way, vars are allocated in this order
  409. (set-car! slots-tail args-tail)
  410. (if (pair? kw-indices)
  411. (key slots (cdr slots-tail) args-tail inits)
  412. (rest-or-key slots (cdr slots-tail) '() inits #f)))
  413. ((pair? kw-indices)
  414. ;; fail early here, because once we're in keyword land we throw
  415. ;; errors instead of failing
  416. (and (or (null? args-tail) rest-idx (keyword? (car args-tail)))
  417. (key slots slots-tail args-tail inits)))
  418. ((pair? args-tail)
  419. #f) ;; fail
  420. (else
  421. (pred slots))))
  422. (define (key slots slots-tail args-tail inits)
  423. (cond
  424. ((null? args-tail)
  425. (if (null? inits)
  426. (pred slots)
  427. (begin
  428. (if (eq? (car slots-tail) *uninitialized*)
  429. (set-car! slots-tail (apply (car inits) slots)))
  430. (key slots (cdr slots-tail) '() (cdr inits)))))
  431. ((not (keyword? (car args-tail)))
  432. (if rest-idx
  433. ;; no error checking, everything goes to the rest..
  434. (key slots slots-tail '() inits)
  435. (error "bad keyword argument list" args-tail)))
  436. ((and (keyword? (car args-tail))
  437. (pair? (cdr args-tail))
  438. (assq-ref kw-indices (car args-tail)))
  439. => (lambda (i)
  440. (list-set! slots i (cadr args-tail))
  441. (key slots slots-tail (cddr args-tail) inits)))
  442. ((and (keyword? (car args-tail))
  443. (pair? (cdr args-tail))
  444. allow-other-keys?)
  445. (key slots slots-tail (cddr args-tail) inits))
  446. (else (error "unrecognized keyword" args-tail))))
  447. (define (pred slots)
  448. (cond
  449. (predicate
  450. (if (apply predicate slots)
  451. slots
  452. #f))
  453. (else slots)))
  454. (let ((args (list-copy args)))
  455. (req args #f args nreq)))
  456. (else (error "unexpected spec" spec))))