;;;; peg.scm --- Parsing Expression Grammar (PEG) parser generator ;;;; ;;;; Copyright (C) 2010, 2011 Free Software Foundation, Inc. ;;;; ;;;; This library is free software; you can redistribute it and/or ;;;; modify it under the terms of the GNU Lesser General Public ;;;; License as published by the Free Software Foundation; either ;;;; version 3 of the License, or (at your option) any later version. ;;;; ;;;; This library 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 ;;;; Lesser General Public License for more details. ;;;; ;;;; You should have received a copy of the GNU Lesser General Public ;;;; License along with this library; if not, write to the Free Software ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ;;;; (define-module (ice-9 peg) #:export (peg-parse ; define-nonterm ; define-nonterm-f peg-match) ; #:export-syntax (define-nonterm) #:use-module (ice-9 peg codegen) #:use-module (ice-9 peg string-peg) #:use-module (ice-9 peg simplify-tree) #:use-module (ice-9 peg match-record) #:re-export (peg-sexp-compile define-grammar define-grammar-f define-nonterm keyword-flatten context-flatten peg:start peg:end peg:string peg:tree peg:substring peg-record?)) ;;; ;;; Helper Macros ;;; (define-syntax until (syntax-rules () "Evaluate TEST. If it is true, return its value. Otherwise, execute the STMTs and try again." ((_ test stmt stmt* ...) (let lp () (or test (begin stmt stmt* ... (lp))))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;; FOR DEFINING AND USING NONTERMINALS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Parses STRING using NONTERM (define (peg-parse nonterm string) ;; We copy the string before using it because it might have been modified ;; in-place since the last time it was parsed, which would invalidate the ;; cache. Guile uses copy-on-write for strings, so this is fast. (let ((res (nonterm (string-copy string) (string-length string) 0))) (if (not res) #f (make-prec 0 (car res) string (string-collapse (cadr res)))))) ;; Searches through STRING for something that parses to PEG-MATCHER. Think ;; regexp search. (define-syntax peg-match (lambda (x) (syntax-case x () ((_ pattern string-uncopied) (let ((pmsym (syntax->datum #'pattern))) (let ((matcher (if (string? (syntax->datum #'pattern)) (peg-string-compile #'pattern 'body) (peg-sexp-compile #'pattern 'body)))) ;; We copy the string before using it because it might have been ;; modified in-place since the last time it was parsed, which would ;; invalidate the cache. Guile uses copy-on-write for strings, so ;; this is fast. #`(let ((string (string-copy string-uncopied)) (strlen (string-length string-uncopied)) (at 0)) (let ((ret (until (or (>= at strlen) (#,matcher string strlen at)) (set! at (+ at 1))))) (if (eq? ret #t) ;; (>= at strlen) succeeded #f (let ((end (car ret)) (match (cadr ret))) (make-prec at end string (string-collapse match))))))))))))