pattern.scm 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. ;;; Ported from Scheme 48 1.9. See file COPYING for notices and license.
  2. ;;;
  3. ;;; Port Author: Andrew Whatson
  4. ;;;
  5. ;;; Original Authors: Richard Kelsey
  6. ;;;
  7. ;;; scheme48-1.9.2/ps-compiler/simp/pattern.scm
  8. ;;;
  9. ;;;(define (simplify-subtract call)
  10. ;;; (simplify-args call 0)
  11. ;;; ((pattern-simplifier
  12. ;;; ((- 'a 'b) '(- a b)) ; constant folding
  13. ;;; ((- x 'a) (+ '(- 0 a) x)) ; convert to a normal form
  14. ;;; ((- 'a (+ 'b x)) (- '(- a b) x)) ; merging constants
  15. ;;; ((- 'a (- 'b x)) (+ x '(- a b))) ; ditto
  16. ;;; ((- x (+ 'a y)) (+ '(- 0 a) (- x y))) ; convert to a normal form
  17. ;;; ((- (+ 'a x) (+ 'b y)) (- (+ '(- a b) x) y)))
  18. ;;; call))
  19. ;;;
  20. ;;; (pattern-simplifier pattern-spec ...)
  21. ;;; =>
  22. ;;; (lambda (call-node) ...)
  23. ;;; The resulting procedure replaces instances of IN-PATTERNs with the
  24. ;;; corresponding OUT-PATTERNs.
  25. ;;;
  26. ;;; <pattern-spec> ::= (in-pattern out-pattern) |
  27. ;;; (in-pattern boolean-expression out-pattern)
  28. ;;;
  29. ;;; All of the IN-PATTERNs for a particular simplifier must be calls to the
  30. ;;; same primop. If the boolean-expression is present it is evaluated after
  31. ;;; the in-pattern is matched and in an environment where the symbols of the
  32. ;;; the in-pattern are bound to the corresponding values from the call.
  33. ;;;
  34. ;;; x matches anything
  35. ;;; 'x matches any literal
  36. ;;; (x ...) matches a call to primop X
  37. ;;; 5 matches the literal 5
  38. ;;;
  39. ;;; The patterns are matched in order.
  40. ;;;
  41. ;;;----------------
  42. ;;; Call MATCH-CALLS with a continuation that makes code to construct the
  43. ;;; right-hand side of the specification. This assumes that the left-hand side
  44. ;;; of all of the specifications will be calls to the same primitive. The
  45. ;;; initial CASE is removed from the code returned by MATCH-CALLS.
  46. (define-module (ps-compiler simp pattern)
  47. #:use-module (system syntax)
  48. #:use-module (prescheme scheme48)
  49. #:use-module (prescheme s48-defrecord)
  50. #:use-module (prescheme record-discloser)
  51. #:use-module (prescheme syntax-utils)
  52. #:use-module (ps-compiler node let-nodes)
  53. #:export (pattern-simplifier))
  54. ;; FIXME: make proper use of syntax objects
  55. (define-syntax pattern-simplifier
  56. (lambda (x)
  57. (syntax-case x ()
  58. ((_ spec ...)
  59. (set! *generate-symbol-index* 0)
  60. (let* ((specs (syntax->datum #'(spec ...)))
  61. (initial (generate-symbol 'initial))
  62. (exp (match-calls (map (lambda (spec)
  63. (make-pattern (car spec) (cdr spec)))
  64. specs)
  65. initial
  66. #f
  67. (lambda (patterns)
  68. (if (null? patterns)
  69. (error "no patterns matched" specs)
  70. (check-predicates patterns initial))))))
  71. (datum->syntax x `(lambda (,initial)
  72. ,(cadar (cddr exp))))))))) ;; strip off initial CASE
  73. (define (name=? a b)
  74. (eq? a b))
  75. (define-record-type pattern
  76. (spec ;; the specification this pattern is to match
  77. (env) ;; an a-list mapping atoms in the pattern to the identifiers
  78. ;; that will be bound to the value matched by the atom
  79. parent ;; if this pattern is an argument in another pattern, this
  80. ;; field contains the other pattern
  81. predicate ;; predicate call or #F
  82. build-spec ;; specification for the transformed pattern
  83. )
  84. ())
  85. (define-record-discloser type/pattern
  86. (lambda (p)
  87. (list 'pattern (pattern-spec p))))
  88. ;; Returns the pattern for the I'th argument in PATTERN.
  89. (define (pattern-arg pattern i)
  90. (list-ref (pattern-spec pattern) (+ i 1)))
  91. (define (make-pattern spec specs)
  92. (receive (build-spec predicate)
  93. (if (null? (cdr specs))
  94. (values (car specs) #f)
  95. (values (cadr specs) (car specs)))
  96. (pattern-maker spec '() #f predicate build-spec)))
  97. ;; For each pattern in PATTERN, extend the environment with the I'th argument
  98. ;; of the pattern bound to ID.
  99. (define (extend-pattern-envs patterns i id)
  100. (map (lambda (pattern)
  101. (let ((arg (pattern-arg pattern i)))
  102. (set-pattern-env! pattern
  103. (cons (if (pair? arg)
  104. (list (cadr arg) id #t)
  105. (list arg id #f))
  106. (pattern-env pattern)))))
  107. patterns))
  108. ;; Return the parent of PATTERN, setting the environment of the parent to be
  109. ;; the environment of PATTERN. This is only used once we are done with PATTERN
  110. ;; and want to continue with the next argument in the parent.
  111. (define (get-pattern-parent pattern)
  112. (let ((p (pattern-parent pattern)))
  113. (set-pattern-env! p (pattern-env pattern))
  114. p))
  115. ;; Sort PATTERNS by the primop being called, and for each set of patterns
  116. ;; matching the same primop, call MATCH-CALL-ARGS to generate code for
  117. ;; those patterns. FINISH-CALL-MATCH builds the clauses that this generates
  118. ;; into a CASE expression.
  119. ;; CALL-VAR is the identifier that will be bound to the call being matched.
  120. ;; FAIL-VAR is either #f or a variable that should be called if no pattern
  121. ;; matches.
  122. ;; MORE is a procedure that finishes with the patterns after this call has
  123. ;; been matched.
  124. (define (match-calls patterns call-var fail-var more)
  125. (let ((primop-var (generate-symbol 'primop)))
  126. (let loop ((patterns patterns) (res '()))
  127. (if (null? patterns)
  128. (finish-call-match res call-var primop-var fail-var)
  129. (let ((primop (car (pattern-spec (car patterns)))))
  130. (receive (same other)
  131. (partition-list (lambda (p)
  132. (name=? primop (car (pattern-spec p))))
  133. (cdr patterns))
  134. (loop other
  135. (cons `(,(if (number? primop) 'else `(,primop))
  136. ,(match-call-args (cons (car patterns) same)
  137. 0
  138. call-var
  139. fail-var
  140. more))
  141. res))))))))
  142. (define (finish-call-match clauses call-var primop-var fail-var)
  143. (receive (elses other)
  144. (partition-list (lambda (c)
  145. (name=? (car c) 'else))
  146. clauses)
  147. `(case (primop-id (call-primop ,call-var))
  148. ,@(reverse other)
  149. (else ,(cond ((null? elses)
  150. (if fail-var `(,fail-var) #f))
  151. ((null? (cdr elses))
  152. `(let ((,primop-var (call-primop ,call-var)))
  153. ,(cadar elses))) ;; strip of uneeded ELSE
  154. (else
  155. (error "more than one ELSE clause" elses)))))))
  156. ;; Similar to MATCH-CALLS, except that this is matching the I'th argument of a
  157. ;; call. All patterns with similar I'th arguments are grouped together and
  158. ;; passed to MATCH-CALL-ARG. The clauses that are returned are made into a
  159. ;; COND expression by FINISH-MATCH-CALL-ARGS.
  160. ;; If there are fewer than I arguments, MORE is called to continue matching
  161. ;; other parts of the patterns.
  162. ;; Patterns that always match the I'th argument are handled separately.
  163. ;; They are used to generate the ELSE clause of the conditional returned.
  164. ;; If there are no such patterns, then the passed-in FAIL-VAR is called
  165. ;; if no patterns match.
  166. (define (match-call-args patterns i call-var fail-var more)
  167. (if (>= i (length (cdr (pattern-spec (car patterns)))))
  168. (more patterns)
  169. (receive (atom-patterns other-patterns)
  170. (partition-list (lambda (p)
  171. (atom? (pattern-arg p i)))
  172. patterns)
  173. (let* ((arg-var (generate-symbol 'arg))
  174. (else-code (cond ((null? atom-patterns)
  175. #f)
  176. (else
  177. (extend-pattern-envs atom-patterns i arg-var)
  178. (match-call-args atom-patterns (+ i 1)
  179. call-var fail-var more))))
  180. (fail-var (if else-code (generate-symbol 'fail) fail-var))
  181. (more (lambda (patterns)
  182. (match-call-args patterns (+ i 1)
  183. call-var fail-var more))))
  184. (let loop ((patterns other-patterns) (clauses '()))
  185. (if (null? patterns)
  186. (finish-match-call-args i call-var arg-var fail-var
  187. else-code clauses)
  188. (let ((first (car patterns)))
  189. (receive (same other)
  190. (partition-list (lambda (p)
  191. (same-arg-pattern? first p i))
  192. (cdr patterns))
  193. (loop other
  194. (cons (match-call-arg (cons first same)
  195. i
  196. arg-var
  197. fail-var
  198. more)
  199. clauses))))))))))
  200. ;; If ELSE-CODE exists this binds FAIL-VAR to a failure procedure containing it.
  201. ;; The CLAUSES are put in a COND.
  202. (define (finish-match-call-args i call-var arg-var fail-var else-code clauses)
  203. `(let ((,arg-var (call-arg ,call-var ,i)))
  204. ,(if else-code
  205. `(let ((,fail-var (lambda () ,else-code)))
  206. (cond ,@clauses (else (,fail-var))))
  207. `(cond ,@clauses (else ,(if fail-var `(,fail-var) #f))))))
  208. ;; Are the I'th arguments of patterns P1 and P2 the same as far as matching
  209. ;; arguments is concerned?
  210. (define (same-arg-pattern? p1 p2 i)
  211. (let ((a1 (pattern-arg p1 i))
  212. (a2 (pattern-arg p2 i)))
  213. (cond ((atom? a1)
  214. (atom? a2))
  215. ((atom? a2)
  216. #f)
  217. ((name=? (car a1) 'quote)
  218. (name=? (car a2) 'quote))
  219. ((name=? (car a2) 'quote)
  220. #f)
  221. (else #t))))
  222. ;; Dispatch on the type of the I'th argument of PATTERNS (all of which have
  223. ;; similar I'th arguments) and generate the appropriate code.
  224. ;; ARG-VAR is the identifier that will be bound to the actual argument.
  225. ;; MORE is a procedure that generates code for the rest of the patterns.
  226. ;; Atoms always match and require that the environments of the patterns
  227. ;; be extended.
  228. ;; Code for literals and calls are generated by other procedures.
  229. (define (match-call-arg patterns i arg-var fail-var more)
  230. (let ((arg (pattern-arg (car patterns) i)))
  231. (cond ((name=? (car arg) 'quote)
  232. `((literal-node? ,arg-var)
  233. ,(match-literal patterns i arg-var fail-var more)))
  234. (else
  235. `((call-node? ,arg-var)
  236. ,(match-calls (map (lambda (p)
  237. (pattern-maker (pattern-arg p i)
  238. (pattern-env p)
  239. p
  240. (pattern-predicate p)
  241. (pattern-build-spec p)))
  242. patterns)
  243. arg-var
  244. fail-var
  245. (lambda (patterns)
  246. (more (map get-pattern-parent patterns)))))))))
  247. ;; Again we sort the patterns into similar groups and build a clause for
  248. ;; each group. Patterns with symbols have their environments extended.
  249. ;; FINISH-MATCH-LITERAL puts the clauses into a CASE expression.
  250. (define (match-literal patterns i arg-var fail-var more)
  251. (receive (symbols numbers)
  252. (partition-list (lambda (p)
  253. (name? (cadr (pattern-arg p i))))
  254. patterns)
  255. (extend-pattern-envs symbols i arg-var)
  256. (if (null? numbers)
  257. (more symbols)
  258. (let loop ((patterns numbers) (clauses '()))
  259. (if (null? patterns)
  260. (finish-match-literal clauses
  261. (if (null? symbols)
  262. (if fail-var `(,fail-var) #f)
  263. (more symbols))
  264. arg-var)
  265. (receive (same other)
  266. (partition-list (lambda (p)
  267. (= (cadr (pattern-arg (car patterns) i))
  268. (cadr (pattern-arg p i))))
  269. (cdr patterns))
  270. (loop other
  271. (cons `((,(cadr (pattern-arg (car patterns) i)))
  272. ,(more (cons (car patterns) same)))
  273. clauses))))))))
  274. ;; Not great, but what to do? I don't think the real NAME? is available.
  275. (define (name? x)
  276. (not (or (pair? x)
  277. (number? x))))
  278. (define (finish-match-literal clauses else arg-var)
  279. (if (null? clauses)
  280. else
  281. `(case (literal-value ,arg-var)
  282. ,@(reverse clauses)
  283. (else ,else))))
  284. ;;------------------------------------------------------------------------------
  285. ;; GENSYM utility
  286. (define *generate-symbol-index* 0)
  287. (define (generate-symbol sym)
  288. (let ((i *generate-symbol-index*))
  289. (set! *generate-symbol-index* (+ i 1))
  290. (concatenate-symbol sym "." i)))
  291. ;;------------------------------------------------------------------------------
  292. ;; Add code to check the predicate if any.
  293. (define (check-predicates patterns initial)
  294. (let label ((patterns patterns))
  295. (cond ((null? (cdr patterns))
  296. (let ((pattern (car patterns)))
  297. (if (pattern-predicate pattern)
  298. (make-predicate-check pattern initial #f)
  299. (make-builder pattern initial))))
  300. ((pattern-predicate (car patterns))
  301. (make-predicate-check (car patterns)
  302. initial
  303. (label (cdr patterns))))
  304. (else
  305. (error "multiple patterns matched ~S"
  306. patterns)))))
  307. (define (make-predicate-check pattern initial rest)
  308. `(if (let ,(map (lambda (p)
  309. `(,(car p) ,(if (caddr p)
  310. `(literal-value ,(cadr p))
  311. (cadr p))))
  312. (pattern-env pattern))
  313. ,(pattern-predicate pattern))
  314. ,(make-builder pattern initial)
  315. ,rest))
  316. ;;------------------------------------------------------------------------------
  317. ;; Building the result of a pattern match
  318. ;; A new environment is made as the builder must keep track of how many times
  319. ;; each node in the matched pattern is used.
  320. ;; CLAUSES is a list of LET-NODES clauses for making the call nodes in the
  321. ;; produced pattern. VALUE is what will replace the original pattern in the
  322. ;; node tree. Any nodes that are used in the result are DETACHed.
  323. (define (make-builder pattern initial)
  324. (let ((env (map (lambda (p)
  325. (list (car p) (cadr p) #f))
  326. (pattern-env pattern)))
  327. (pattern (pattern-build-spec pattern))
  328. (sym (generate-symbol 'result)))
  329. (let ((clauses (if (and (pair? pattern)
  330. (not (name=? (car pattern) 'quote)))
  331. (reverse (build-call sym pattern env))
  332. '()))
  333. (value (cond ((not (pair? pattern))
  334. (lookup-pattern pattern env))
  335. ((name=? (car pattern) 'quote)
  336. `(make-literal-node ,(build-literal (cadr pattern) env)
  337. (node-type ,initial)))
  338. (else
  339. sym))))
  340. `(begin
  341. ,@(filter-map (lambda (data)
  342. (if (caddr data)
  343. `(detach ,(cadr data))
  344. #f))
  345. env)
  346. (let-nodes ,clauses
  347. (replace ,initial ,value))))))
  348. ;; Go down the arguments in PATTERN making the appropriate LET-NODES spec
  349. ;; for each.
  350. (define (build-call id pattern env)
  351. (let loop ((arg-patterns (cdr pattern)) (args '()) (clauses '()))
  352. (if (null? arg-patterns)
  353. `((,id (,(car pattern) 0 . ,(reverse args)))
  354. . ,clauses)
  355. (let ((arg (car arg-patterns)))
  356. (cond ((atom? arg)
  357. (loop (cdr arg-patterns)
  358. (cons (lookup-pattern arg env) args)
  359. clauses))
  360. ((name=? (car arg) 'quote)
  361. (loop (cdr arg-patterns)
  362. (cons `'(,(build-literal (cadr arg) env)
  363. type/unknown)
  364. args)
  365. clauses))
  366. (else
  367. (let ((sym (generate-symbol 'new)))
  368. (loop (cdr arg-patterns)
  369. (cons sym args)
  370. (append (build-call sym arg env) clauses)))))))))
  371. ;; A literal specification is either a number, a symbol which will bound to a
  372. ;; number, or an expression to be evaluated.
  373. (define (build-literal spec env)
  374. (cond ((number? spec)
  375. spec)
  376. ((name? spec)
  377. `(literal-value ,(lookup-literal spec env)))
  378. (else
  379. `(,(car spec)
  380. . ,(map (lambda (a)
  381. (build-literal a env))
  382. (cdr spec))))))
  383. ;; Get the identifier that will be bound to the value of PATTERN.
  384. (define (lookup-literal pattern env)
  385. (cond ((assoc pattern env)
  386. => cadr)
  387. (else
  388. (error "pattern ~S not found in env" pattern))))
  389. ;; Get the identifier that will be bound to the node value of PATTERN.
  390. ;; Annotate the environment to mark that the node has been used.
  391. (define (lookup-pattern pattern env)
  392. (cond ((assoc pattern env)
  393. => (lambda (data)
  394. (if (caddr data)
  395. (error "node ~S is used more than once" (car data)))
  396. (set-car! (cddr data) 1)
  397. (cadr data)))
  398. (else
  399. (error "pattern ~S not found in env" pattern))))