using-parsers.scm 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. ;;;; using-parsers.scm --- utilities to make using parsers easier
  2. ;;;;
  3. ;;;; Copyright (C) 2010, 2011 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. (define-module (ice-9 peg using-parsers)
  20. #:use-module (ice-9 peg simplify-tree)
  21. #:use-module (ice-9 peg codegen)
  22. #:use-module (ice-9 peg cache)
  23. #:use-module (srfi srfi-9)
  24. #:export (match-pattern define-peg-pattern search-for-pattern
  25. prec make-prec peg:start peg:end peg:string
  26. peg:tree peg:substring peg-record?))
  27. (define-record-type prec
  28. (make-prec start end string tree)
  29. peg-record?
  30. (start prec-start)
  31. (end prec-end)
  32. (string prec-string)
  33. (tree prec-tree))
  34. (define (peg:start pm)
  35. (and pm (prec-start pm)))
  36. (define (peg:end pm)
  37. (and pm (prec-end pm)))
  38. (define (peg:string pm)
  39. (and pm (prec-string pm)))
  40. (define (peg:tree pm)
  41. (and pm (prec-tree pm)))
  42. (define (peg:substring pm)
  43. (and pm (substring (prec-string pm) (prec-start pm) (prec-end pm))))
  44. ;;;
  45. ;;; Helper Macros
  46. ;;;
  47. (define-syntax until
  48. (syntax-rules ()
  49. "Evaluate TEST. If it is true, return its value. Otherwise,
  50. execute the STMTs and try again."
  51. ((_ test stmt stmt* ...)
  52. (let lp ()
  53. (or test
  54. (begin stmt stmt* ... (lp)))))))
  55. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  56. ;;;;; FOR DEFINING AND USING NONTERMINALS
  57. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  58. ;; Parses STRING using NONTERM
  59. (define (match-pattern nonterm string)
  60. ;; We copy the string before using it because it might have been modified
  61. ;; in-place since the last time it was parsed, which would invalidate the
  62. ;; cache. Guile uses copy-on-write for strings, so this is fast.
  63. (let ((res (nonterm (string-copy string) (string-length string) 0)))
  64. (if (not res)
  65. #f
  66. (make-prec 0 (car res) string (string-collapse (cadr res))))))
  67. ;; Defines a new nonterminal symbol accumulating with ACCUM.
  68. (define-syntax define-peg-pattern
  69. (lambda (x)
  70. (syntax-case x ()
  71. ((_ sym accum pat)
  72. (let ((matchf (compile-peg-pattern #'pat (syntax->datum #'accum)))
  73. (accumsym (syntax->datum #'accum)))
  74. ;; CODE is the code to parse the string if the result isn't cached.
  75. (let ((syn (wrap-parser-for-users x matchf accumsym #'sym)))
  76. #`(define sym #,(cg-cached-parser syn))))))))
  77. (define (peg-like->peg pat)
  78. (syntax-case pat ()
  79. (str (string? (syntax->datum #'str)) #'(peg str))
  80. (else pat)))
  81. ;; Searches through STRING for something that parses to PEG-MATCHER. Think
  82. ;; regexp search.
  83. (define-syntax search-for-pattern
  84. (lambda (x)
  85. (syntax-case x ()
  86. ((_ pattern string-uncopied)
  87. (let ((pmsym (syntax->datum #'pattern)))
  88. (let ((matcher (compile-peg-pattern (peg-like->peg #'pattern) 'body)))
  89. ;; We copy the string before using it because it might have been
  90. ;; modified in-place since the last time it was parsed, which would
  91. ;; invalidate the cache. Guile uses copy-on-write for strings, so
  92. ;; this is fast.
  93. #`(let ((string (string-copy string-uncopied))
  94. (strlen (string-length string-uncopied))
  95. (at 0))
  96. (let ((ret (until (or (>= at strlen)
  97. (#,matcher string strlen at))
  98. (set! at (+ at 1)))))
  99. (if (eq? ret #t) ;; (>= at strlen) succeeded
  100. #f
  101. (let ((end (car ret))
  102. (match (cadr ret)))
  103. (make-prec
  104. at end string
  105. (string-collapse match))))))))))))