1;;>| Utility Parsing Combinators2;;>3;;> Additional high-level combinators implemented using the aforementioned4;;> [basic combinators][basic section]. The provided high-level combinators,5;;> return more useful parser error messages then the basic ones.6;;>7;;> [basic section]: #section-basic-parsing-combinators89;;> Parser which always fails with the given error message.1011(define (parse-fail msg)12 (lambda (source index sk fk)13 (fk source index msg)))1415;;> Bind a constant `value` to a given `parser`.16;;> That is, always return this value if the parser succeeds.1718(define (parse-bind value parser)19 (parse-map20 parser21 (lambda (x) value)))2223;;> Run a parser `f`, which *must* return a list, and convert24;;> its return value to a string using the `list->string` procedure.2526(define (parse-as-string parser)27 (parse-map28 parser29 list->string))3031;;> Parse one or more digits (i.e. `0-9`), interpret them as a32;;> decimal number, and return this number.3334(define parse-digits35 (parse-with-failure-reason36 (parse-map37 (parse-token char-set:digit)38 string->number)39 "expected digits"))4041;;> Parse an ASCII lowercase character (i.e. `a-z`).4243(define parse-lowercase44 (parse-with-failure-reason45 (parse-char char-set:lower-case)46 "expected lowercase character"))4748;;> Attempt parsing using the given parser `f`, if it fails return a default value `def`.4950(define (parse-default f def)51 (parse-map52 (parse-optional f)53 (lambda (x)54 (or x def))))5556;;> Parse a newline character.5758(define parse-newline59 (parse-with-failure-reason60 (parse-char #\newline)61 "expected newline"))6263;;> Parse a single blank character (i.e. horizontal whitespace).6465(define parse-blank66 (parse-with-failure-reason67 (parse-char char-set:blank)68 "expected whitespace"))6970;;> Parse one or more blank characters.7172(define parse-blanks+73 (parse-with-failure-reason74 (parse-token char-set:blank)75 "expected whitespaces"))7677;;> Parse zero or more blank characters.7879(define parse-blanks80 (parse-optional parse-blanks+))8182;;> Invokes parser `f` between the parsers `lhs` and `rhs`.8384(define (parse-between lhs f rhs)85 (parse-map86 (parse-seq lhs f rhs)87 cadr))8889;;> Returns the result of parser `f` but allows preceding its input90;;> with a backslash character to escape it in the parsed input format.9192(define (parse-esc f)93 (parse-map94 (parse-seq95 (parse-char #\\)96 f)97 cadr))9899;;> Parse an `alist` mapping chars to values which should be returned for each char.100101(define (parse-alist alist)102 (apply103 parse-or104 (map105 (lambda (x)106 (parse-map107 (parse-char (car x))108 (lambda (_)109 (cdr x))))110 alist)))111112;; Utility procedure for parsing ed(1) BRE addresses.113114(define (%parse-regex-lit ch end)115 (parse-with-failure-reason116 (parse-atomic117 (parse-as-string118 (parse-between119 (parse-char ch)120 (parse-repeat (parse-or121 (parse-esc (parse-char ch))122 (parse-char (char-set-complement (char-set ch #\newline)))))123 end)))124 "expected regex"))125126;;> Parse a regex literal which is enclosed by the character `ch`.127128(define (parse-regex-lit ch)129 (%parse-regex-lit130 ch131 (parse-char ch)))132133;;> Parse a regex literal which starts with character `ch` and is134;;> terminated by the same character or the end of line.135136(define (parse-regex-lit* ch)137 (%parse-regex-lit138 ch139 (parse-or140 (parse-char ch)141 parse-end-of-line)))142143;;> Invoke given parser and strip trailing blanks (if any).144145(define (parse-strip-blanks parser)146 (parse-map147 (parse-seq148 parser149 parse-blanks)150 car))151152;;> Like [parse-seq](#parse-seq) but skip blanks *before* each parser.153154(define (parse-blanks-seq . o)155 (define (%parse-blanks-seq lst)156 (parse-seq-list157 (apply append158 (zip (make-list159 (length lst)160 (parse-ignore parse-blanks))161 lst))))162163 (%parse-blanks-seq o))164165;;> Parse a single line (excluding the terminating newline character).166167(define parse-line168 (parse-atomic169 (parse-or170 (parse-bind "" parse-newline) ;; empty line171 (parse-map172 (parse-seq173 (parse-token (lambda (x) (not (char=? x #\newline))))174 ;; XXX: parse-end-of-line does _not_ consume the end-of-file.175 ;; This is crucial for parse-input-mode to work correctly.176 (parse-or parse-newline parse-end-of-line))177 car))))178179;;> Feed the result of the parser `ctx` to a single argument procedure `f`.180;;> The procedure must then return a new parser which is executed181;;> afterwards on the same index as `ctx`.182183(define (parse-with-context ctx f)184 (lambda (source index sk fk)185 (let* ((yield (lambda (r s i fk) r))186 (value (call-with-parse ctx source index yield fk)))187 ((f value) source index sk fk))))