Next: , Up: parser logic   [Index]


11.1 Introductory examples

Parsing a string of selected characters

As first usage example, let’s see a simple parser using a full Scheme string as argument and accepting lexemes being the empty string or strings of characters ‘#\a’ and ‘\#b’; the result of a call to the parser is the list of characters or #f if the input is invalid:

#!r6rs
(import (vicare)
  (vicare parser-logic))

(module (parse-abba)

  (define (parse-abba input-string)
    (assert (string? input-string))
    (%parse-string input-string
                   (string-length input-string)
                   0    ;start index
                   '()  ;start value for ACCUMULATOR
                   ))

  (define-parser-logic define-string->abba-parser ch next fail
    (%parse-string (accumulator)
       ((:end-of-input)
        (reverse accumulator))
       ((#\a #\b)
        (next %parse-string (cons ch accumulator)))))

  (define-string->abba-parser string->token-or-false
    (%parse-string))

  #| end of module |# )

(parse-abba "")         ⇒ ()
(parse-abba "a")        ⇒ (#\a)
(parse-abba "b")        ⇒ (#\b)
(parse-abba "1")        ⇒ #f)
(parse-abba "ciao")     ⇒ #f)
(parse-abba "abb")      ⇒ (#\a #\b #\b)

notice the use of next to recursively tail–call parse-string.

The macro string->token-or-false is exported by (vicare parser-logic); it implements the device logic for a full input Scheme string representing a lexeme; it is to be used in a parser returning #f when the input is invalid.

The macros in the module combine their output and expand to the definition of a function %parse-string equivalent to the following:

(define (%parse-string input.string input.length input.index
                       accumulator)
  (if (fx=? input.index input.length)
      (reverse accumulator)
    (let ((ch (string-ref input.string input.index)))
      (cond ((or (char=? #\a ch)
                 (char=? #\b ch))
             (%parse-string input.string input.length
                            (fx+ 1 input.index)
                            (cons ch accumulator)))
            (else #f)))))

Parsing a string of intermixed selected characters

Let’s see a parser using a full Scheme string as argument and accepting lexemes being the empty string or strings of characters ‘#\a’ and ‘\#b’:

the result of a call to the parser is the list of characters or #f if the input is invalid:

#!r6rs
(import (vicare)
  (vicare parser-logic))

(module (parse-abab)

  (define (parse-abab input-string)
    (assert (string? input-string))
    (%parse-string input-string
                   (string-length input-string)
                   0    ;start index
                   '()  ;start value for ACCUMULATOR
                   ))

  ;;Parser logic to convert a string of intermixed
  ;;#\a and #\b into a list of characters.
  (define-parser-logic define-string->abab-parser ch next fail
    (%parse-string (accumulator)
       ((:end-of-input)
        (reverse accumulator))
       ((#\a #\b)
        (if (or (null? accumulator)
                (case ch
                  ((#\a) (char=? #\b (car accumulator)))
                  ((#\b) (char=? #\a (car accumulator)))))
            (next %parse-string (cons ch accumulator))
          (fail)))))

  ;;Actual parser drawing characters from an input string.
  (define-string->abab-parser string->token-or-false
    (%parse-string))

  #| end of module |# )

(parse-abab "")         ⇒ ()
(parse-abab "a")        ⇒ (#\a)
(parse-abab "b")        ⇒ (#\b)
(parse-abab "1")        ⇒ #f
(parse-abab "ciao")     ⇒ #f
(parse-abab "abb")      ⇒ #f
(parse-abab "baa")      ⇒ #f
(parse-abab "abab")     ⇒ (#\a #\b #\a #\b)
(parse-abab "baba")     ⇒ (#\b #\a #\b #\a)

notice the use of fail to signal an input error from inside an operator clause.

The macros in the module combine their output and expand to the definition of a function %parse-string equivalent to the following:

(define (%parse-string input.string input.length input.index
                       accumulator)
  (if (fx=? input.index input.length)
      (reverse accumulator)
    (let ((ch (string-ref input.string input.index)))
      (cond ((or (char=? #\a ch)
                 (char=? #\b ch))
             (if (or (null? accumulator)
                     (case ch
                       ((#\a) (char=? #\b (car accumulator)))
                       ((#\b) (char=? #\a (car accumulator)))))
                 (%parse-string input.string input.length
                                (fx+ 1 input.index)
                                (cons ch accumulator))
               #f))
            (else #f)))))

Parsing exact integers in base 10

Let’s see a parser using a full Scheme string as argument and accepting lexemes representing exact integers in base 10; the result of a call to the parser is the exact integer object or #f if the input is invalid:

#!r6rs
(import (vicare)
  (vicare parser-logic))

(define (parse-integer input-string)

  (define (%digit ch)
    ;;Given a character argument: return the corresponding
    ;;fixnum if the character is between #\0 and #\9, else
    ;;return false.
    ;;
    (let ((N (fx- (char->integer ch) (char->integer #\0))))
      (and (fx>= N 0)
           (fx<  N 10)
           N)))

  ;;Parser logic to convert a string into an exact integer
  ;;in base 10.
  (define-parser-logic define-string->integer-parser ch next fail
    (%parse-integer ()
       ((%digit) => D
        (next %parse-digit+ D)))
    (%parse-digit+ (accumulator)
       ((:end-of-input)
        accumulator)
       ((%digit) => D
        (next %parse-digit+ (+ D (* 10 accumulator))))))

  ;;Actual parser drawing characters from an input string.
  (define-string->integer-parser string->token-or-false
    (%parse-integer))

  (assert (string? input-string))
  (%parse-integer input-string (string-length input-string) 0))

(parse-integer "")              ⇒ #f
(parse-integer "1")             ⇒ 1
(parse-integer "123")           ⇒ 123
(parse-integer "ciao")          ⇒ #f
(parse-integer "123ciao")       ⇒ #f

The macros in the body of parse-integer combine their output and expand to the definition of two functions %parse-integer and %parse-digit+ equivalent to the following:

(define (%parse-integer input.string input.length input.index)
  (if (fx=? input.index input.length)
      #f
    (let ((ch (string-ref input.string input.index)))
      (cond ((%digit ch)
             => (lambda (D)
                  (%parse-digit+ input.string input.length
                                 (fx+ 1 input.index) D)))
            (else #f)))))

(define (%parse-digit+ input.string input.length input.index
                       accumulator)
  (if (fx=? input.index input.length)
      accumulator
    (let ((ch (string-ref input.string input.index)))
      (cond ((%digit ch)
             => (lambda (D)
                  (%parse-digit+ input.string input.length
                                 (fx+ 1 input.index)
                                 (+ D (* 10 accumulator)))))
            (else #f)))))

Next: , Up: parser logic   [Index]