combinator.ss
#lang scheme/base
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; PARSEQ.PLT
;; A Parser Combinator library.
;;
;; Bonzai Lab, LLC.  All rights reserved.
;;
;; Licensed under LGPL.
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; combinator.ss - higher level combinator for parsers...
;; yc 12/31/2009 - first version
;; yc 1/5/2010 - moved delimited, bracket, and alternate to token.ss
(require "depend.ss"
         mzlib/defmacro
         (for-syntax scheme/base
                     "depend.ss"
                     scheme/match
                     )
         "primitive.ss" 
         "input.ss" 
         ) 

;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Parser COMBINATORS

;; bind
;; Parser a -> (a -> Parser b) -> Parser b
;; this is the function version of the monad - use this when you want to
;; create higher combinators dynamically...
(define (bind parser v->parser) 
  (lambda (in)
    (let-values (((v in)
                  (parser in)))
      ((v->parser v) in))))

;; result
;; allows the transformation of the result of the parser...
(define (result parser transform)
  (bind parser 
        (lambda (v) 
          (if (succeeded? v) 
              (return (transform v))  
              fail))))

(define (result* parser transform)
  (bind parser 
        (lambda (v) 
          (if (and (succeeded? v) (list? v))
              (return (apply transform v))
              fail))))

;; seq
;; the macro-based monad for stringing multiple parsers together...
;; (seq parser) => parser
;; (seq v <- parser exp ...) => (bind paser (lambda (v) (if v (seq exp ...) fail))
(define-macro (seq . exps) 
  (define *in (gensym 'in)) ;; represents the input
  (define *v (gensym 'v)) ;; represents the value
  (define literal 'literal) 
  ;; sequence body for creating a sequence combinator...
  (define (body exps) 
    (match exps 
      ((list exp) 
       `((,literal ,exp) ,*in))
      ((list-rest var '<- exp rest)
        `(let-values (((,var ,*in)
                       ((,literal ,exp) ,*in)))
           (if (succeeded? ,var) 
               ,(body rest)
               (fail in))))
      ((list-rest exp rest) 
       (body `(,*v <- ,exp . ,rest)))
      ))
  `(lambda (in) 
     (let ((,*in in))
       ,(body exps))))

;; sequence
;; a functional version of seq
(define (sequence parsers) 
  (lambda (IN) 
    (define (helper parsers in acc) 
      (if (null? parsers) 
          ((return (reverse acc)) in)
          (let-values (((v in) 
                        ((car parsers) in))) 
            (if (succeeded? v) 
                (helper (cdr parsers) in (cons v acc))
                (fail IN)))))
    (helper (map literal parsers) IN '()))) 

;; sequence*
(define (sequence* . parsers)
  (sequence parsers))

;; #|
;; choice
;; (choice parser) => (bind parser (lambda (v) (if v (return v) fail))
;; (choice parser rest ...) => (bind parser (lambda (v) (if v (choice rest ...) fail)))
(define-macro (choice . exps) 
  (define *in (gensym 'in)) ;; represents the input
  (define *v (gensym 'v)) ;; represents the value
  (define (body exps) 
    (match exps 
      ((list) 
       `(fail ,*in))
      ((list-rest exp rest) 
       `(let-values (((,*v ,*in)
                      ((literal ,exp) ,*in)))
          (if (succeeded? ,*v)
               ((return ,*v) ,*in)
               ,(body rest))))
      ))
  `(lambda (,*in)
     ,(body exps)))
;;|#

;; one-of
;; a function version of choice
(define (one-of parsers)
  (lambda (in) 
    (define (helper parsers) 
      (if (null? parsers)
          (fail in) 
          (let-values (((v in) 
                        ((car parsers) in))) 
            (if (succeeded? v) 
                ((return v) in)
                (helper (cdr parsers))))))
    (helper (map literal parsers))))

;; one-of*
(define (one-of* . parsers)
  (one-of parsers)) 

;; all-of
(define (all-of parsers) 
  (lambda (in) 
    (define (helper parsers v)
      (if (null? parsers)
          ((return v) in) 
          (let-values (((v IN) 
                        ((car parsers) in)))
            (if (succeeded? v) 
                (helper (cdr parsers) v)
                (fail in)))))
    (helper (map literal parsers) (make-failed 0))))

;; all-of*
(define (all-of* . parsers) 
  (all-of parsers)) 

;; repeat
;; returns when # of occurence falls within the min and max range
;; default to [1,+inf]
(define (repeat parser (min 1) (max +inf.0)) 
  (define (make parser) 
    (lambda (IN) 
      (define (helper prev-in acc count)
        (let-values (((v in)
                      (parser prev-in)))
          (if (succeeded? v) 
              (if (< count max) 
                  (helper in (cons v acc) (add1 count))
                  ((return (reverse acc)) prev-in))
              (if (< count min) 
                  (fail IN)
                  ((return (reverse acc)) in)))))
      (helper IN '() 0)))
  (make (literal parser))) 

;; zero-many
;; returns the matched values if zero or more matches
;; (this means that this parser will always match)
(define (zero-many parser) 
  (repeat parser 0)) 

;; one-many
;; matches if parser parses one or more times
(define (one-many parser)
  (repeat parser)) 

;; zero-one
;; returns if the parser matches zero or one times
;; when the parser does not match, it defaults to fail, but you can pass in a
;; default value so it does not fail.
(define (zero-one parser default) 
  (lambda (in) 
    (let-values (((v in)
                  ((literal parser) in))) 
      ((return (if (succeeded? v) v default)) in))))

(provide bind
         result 
         result* 
         seq
         sequence
         sequence*
         choice
         one-of
         one-of* 
         all-of
         all-of* 
         repeat 
         zero-many
         one-many
         zero-one
         )