match.scm 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. (library (match-definitions)
  2. (export trie-merge compile-pattern interpret-tree)
  3. (import (chezscheme))
  4. (define (push-box! b v)
  5. (set-box! b (cons v (unbox b))))
  6. (define (length=? l n)
  7. (if (null? l)
  8. (= n 0)
  9. (if (pair? l)
  10. (length=? (cdr l) (- n 1))
  11. #f)))
  12. (define syntax-car
  13. (lambda (ls)
  14. (syntax-case ls ()
  15. ((x . y) #'x))))
  16. (define syntax-cdr
  17. (lambda (ls)
  18. (syntax-case ls ()
  19. ((x . y) #'y))))
  20. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  21. ;; This is the code for merging a list of instructions into a trie
  22. (define terminator (cons '() '()))
  23. (define (trie-merge seqs)
  24. (cond ((null? seqs)
  25. '())
  26. ((member '() seqs)
  27. (cons terminator
  28. (trie-merge (filter pair? seqs))))
  29. (else (let* ((p (partition-by-head (caar seqs) seqs))
  30. (tails (car p))
  31. (rest (cdr p)))
  32. (cons `(,(caar seqs) . ,(trie-merge tails))
  33. (trie-merge rest))))))
  34. (define (partition-by-head head seqs)
  35. (cond ((null? seqs)
  36. terminator)
  37. (else (let* ((p (partition-by-head head (cdr seqs)))
  38. (l (car p))
  39. (r (cdr p)))
  40. (if (equal? (syntax->datum head)
  41. (syntax->datum (caar seqs)))
  42. (cons (cons (cdar seqs) l) r)
  43. (cons l (cons (car seqs) r)))))))
  44. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  45. ;; This is the interpreter for trees of matching instructions.
  46. ;; matching instructions are as defined in compile-pattern,
  47. ;; and the trees terminate with an (execute <code>) command
  48. ;; The interpreter handles scope explicitly and manages a
  49. ;; fail continuation to do backtracking.
  50. (define-syntax interpret-tree
  51. (syntax-rules (execute bind compare-equal? guard decons)
  52. ((interpret-tree scope () stack failure)
  53. failure)
  54. ((interpret-tree scope (((execute <body>) (())) <alternatives> ...) stack failure)
  55. (let* scope <body>))
  56. ((interpret-tree scope (((bind <var>) <then> ...) <alternatives> ...) stack failure)
  57. (let ((top (car stack))
  58. (new-stack (cdr stack)))
  59. (interpret-tree ((<var> top) . scope) (<then> ...) new-stack
  60. (interpret-tree scope (<alternatives> ...) stack failure))))
  61. ((interpret-tree scope (((compare-equal? <s-expr>) <then> ...) <alternatives> ...)
  62. stack failure)
  63. (let ((top (car stack))
  64. (new-stack (cdr stack)))
  65. (if (let* scope (equal? top <s-expr>))
  66. (interpret-tree scope (<then> ...) new-stack failure)
  67. (interpret-tree scope (<alternatives> ...) stack failure))))
  68. ((interpret-tree scope (((guard <predicate>) <then> ...) <alternatives> ...)
  69. stack failure)
  70. (if (let* scope <predicate>)
  71. (interpret-tree scope (<then> ...) stack failure)
  72. (interpret-tree scope (<alternatives> ...) stack failure)))
  73. ((interpret-tree scope (((decons) <then> ...) <alternatives> ...) stack failure)
  74. (let ((top (car stack))
  75. (new-stack (cdr stack))
  76. (fail-thunk (lambda ()
  77. (interpret-tree scope (<alternatives> ...) stack failure))))
  78. (if (pair? top)
  79. (let ((stack (cons (car top) (cons (cdr top) new-stack))))
  80. (interpret-tree scope (<then> ...) stack (fail-thunk)))
  81. (fail-thunk))))))
  82. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  83. ;; This part compiles a pattern into a sequence of matching instructions
  84. ;; that performs the testing and binding operations
  85. ;;
  86. ;; Update this part to change or extend the pattern language.
  87. ;; <pat> ::= <var> | '<data> | `<qq>
  88. ;; | (list <pat> ...)
  89. ;;
  90. ;; <qq> ::= <atom> | ,<pat> | (<qq> . <qq>)
  91. (define (identifier-mem? x ls)
  92. (and (not (null? ls))
  93. (or (bound-identifier=? x (car ls))
  94. (identifier-mem? x (cdr ls)))))
  95. (define (var? s) (symbol? (syntax->datum s)))
  96. (define (atomic? s)
  97. (let ((s (syntax->datum s)))
  98. (or (null? s)
  99. (symbol? s) (number? s)
  100. (boolean? s) (string? s))))
  101. (define (quoted? s)
  102. (let ((s (syntax->datum s)))
  103. (and (length=? s 2) (eq? 'quote (car s)))))
  104. (define (quasiquoted? s)
  105. (let ((s (syntax->datum s)))
  106. (and (length=? s 2) (eq? 'quasiquote (car s)))))
  107. (define (unquote? s)
  108. (let ((s (syntax->datum s)))
  109. (and (length=? s 2) (eq? 'unquote (car s)))))
  110. (define (compile-pattern box pat rest)
  111. (cond ((var? pat) (compile-var box pat rest))
  112. ((quoted? pat)
  113. (let ((pat (syntax-car (syntax-cdr pat))))
  114. (compile-quoted box pat rest)))
  115. ((quasiquoted? pat)
  116. (let ((pat (syntax-car (syntax-cdr pat))))
  117. (compile-quasiquoted box pat rest)))
  118. (else (error 'compile-pattern "Invalid pattern" pat))))
  119. (define (compile-var box var rest)
  120. (if (identifier-mem? var (unbox box))
  121. (cons #`(compare-equal? #,var) (rest))
  122. (begin
  123. (push-box! box var)
  124. (cons #`(bind #,var) (rest)))))
  125. (define (compile-quoted box dat rest)
  126. (cons #`(compare-equal? '#,dat) (rest)))
  127. (define (compile-quasiquoted box pat rest)
  128. (cond ((atomic? pat) (compile-quoted box pat rest))
  129. ((unquote? pat) (compile-pattern box (syntax-car (syntax-cdr pat)) rest))
  130. (else (let ((x (syntax-car pat))
  131. (y (syntax-cdr pat)))
  132. (cons #'(decons)
  133. (compile-quasiquoted
  134. box x
  135. (lambda ()
  136. (compile-quasiquoted
  137. box y
  138. rest))))))))
  139. )
  140. (library (match)
  141. (export match)
  142. (import (chezscheme) (match-definitions))
  143. (define-syntax (match stx)
  144. (define (compile-pattern^ entry)
  145. (compile-pattern (box '())
  146. (car entry)
  147. (lambda ()
  148. (list #`(execute #,(cadr entry))))))
  149. (syntax-case stx (else)
  150. ((match <exp> (<pattern> <body> ...) ... (else <fail> ...))
  151. (let* ((rules #'((<pattern> (begin <body> ...)) ...))
  152. (instructions (map compile-pattern^ (syntax->list rules)))
  153. (trie (trie-merge instructions)))
  154. #`(let ((stack (list <exp>)))
  155. (interpret-tree ()
  156. #,trie
  157. stack
  158. (begin <fail> ...)))))
  159. ((match <exp> (<pattern> <body> ...) ...)
  160. #'(match <exp> (<pattern> <body> ...) ... (else (error 'match "Failed to match"))))))
  161. )