tutorial.scm 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. #|
  2. Note: The code herein is mostly following tutorials I found online:
  3. - https://hyperdev.fr/blog/getting-started-with-guile-parser-combinators.html
  4. - another one I cannot find currently =(
  5. |#
  6. (use-modules (parser-combinators)
  7. (srfi srfi-41))
  8. ;; writing your own parse character function
  9. #;(define (parse-c-char stream)
  10. (if (eqv? (stream-car stream) #\c)
  11. (parse-result #\c (stream-cdr stream))))
  12. #;(parse-c-char (list->stream '(#\c #\c #\c)))
  13. ;; using a library function
  14. #;((parse-char #\c) (list->stream '(#\c #\c #\c)))
  15. ;; EXAMPLE 1
  16. ;; implementing your own parse-char function
  17. (define (parse-char char)
  18. "Create a parser that succeeds when the next character in the stream
  19. is CHAR."
  20. (lambda (stream) ; we return a function which accepts
  21. (stream-match stream ; using
  22. (() %parse-failure) ; if empty list then fail
  23. ((head . tail) (if (equal? head char)
  24. ; if there is head and tail
  25. ; check if the character is
  26. ; equal to the char
  27. (parse-result head tail)
  28. ; the parsing result is the
  29. ; head and the remaining tail
  30. %parse-failure
  31. ; otherwise parsing fails
  32. )))))
  33. (let ([my-char-parser (parse-char #\c)])
  34. (my-char-parser (list->stream '(#\c #\c #\c))))
  35. ;; EXAMPLE 2
  36. ;; set of characters
  37. #;((parse-char-set '(#\a #\b #\c)) (list->stream '(#\c #\c #\c))) ; does not work
  38. ;; EXAMPLE 3
  39. ;; parse a string part
  40. (define string->stream (compose list->stream string->list))
  41. ((parse-string "ccc") (string->stream "ccc means chaos computer club"))
  42. ;; EXAMPLE 4
  43. ;; any char
  44. (parse-any-char (string->stream "scheme is awesome"))
  45. (parse-any-char (string->stream "\nthis starts with a newline"))
  46. ;; ===============
  47. ;; CONTROL PARSERS
  48. ;; ===============
  49. ;; EXAMPLE 5
  50. ;; (parse-any parser ...)
  51. ;; (parse-each parser ...)
  52. ;; (parse-zero-or-more parser)
  53. ;; (parse-one-or-more parser)
  54. ;; general any char matched
  55. ((parse-each parse-any-char parse-any-char parse-any-char) (string->stream "gnu"))
  56. ;; only parsing g then n then u
  57. (define parse-gnu
  58. (parse-each (parse-char #\g)
  59. (parse-char #\n)
  60. (parse-char #\u)))
  61. ;; try on "gnu"
  62. (parse-gnu (string->stream "gnu"))
  63. ;; try on "ccc"
  64. (parse-gnu (string->stream "ccc"))
  65. ;; using parse-zero-or-more
  66. (define parse-aaagnu
  67. (parse-each
  68. (parse-zero-or-more (parse-char #\a))
  69. (parse-char #\g)
  70. (parse-char #\n)
  71. (parse-char #\u)))
  72. (parse-aaagnu (string->stream "aagnu")) ; works with arbitrary amount of a in front
  73. (parse-aaagnu (string->stream "gnu")) ; also with 0 a
  74. (define parse-must-have-aaa-gnu
  75. (parse-each
  76. (parse-one-or-more (parse-char #\a))
  77. (parse-char #\g)
  78. (parse-char #\n)
  79. (parse-char #\u)))
  80. (parse-must-have-aaa-gnu (string->stream "aaagnu")) ; this succeeds
  81. (parse-must-have-aaa-gnu (string->stream "gnu")) ; this fails
  82. (define parse-a-or-b
  83. (parse-any (parse-char #\a)
  84. (parse-char #\b)))
  85. (parse-a-or-b (string->stream "a"))
  86. (parse-a-or-b (string->stream "b"))
  87. (parse-a-or-b (string->stream "c"))
  88. ;; ======================
  89. ;; OUTPUT BUILDER PARSERS
  90. ;; ======================
  91. ;; They take a parser as argument.
  92. ;; They process the parser's output.
  93. ;; Then they return. (what?)
  94. ;; EXAMPLE 6
  95. ;; (parse-map proc parser)
  96. ;; (parse-match parser matcher ...)
  97. (define (parse-char char)
  98. "Create a parser that succeeds when the next character in the stream
  99. is CHAR."
  100. (lambda (stream) ; we return a function which accepts
  101. (stream-match stream ; using
  102. (() %parse-failure) ; if empty list then fail
  103. ((head . tail) (if (equal? head char)
  104. ; if there is head and tail
  105. ; check if the character is
  106. ; equal to the char
  107. (parse-result head tail)
  108. ; the parsing result is the
  109. ; head and the remaining tail
  110. %parse-failure
  111. ; otherwise parsing fails
  112. )))))
  113. (define parse-gnu
  114. (parse-each (parse-char #\g)
  115. (parse-char #\n)
  116. (parse-char #\u)))
  117. (define string->stream (compose list->stream string->list))
  118. (define parse-gnu*
  119. (parse-map
  120. ;; map takes a procedure, as usual
  121. (lambda (lst)
  122. `(b ,(list->string lst))) ; sxml expression
  123. ;; and a parser, which will generate a list which is mapped
  124. ;; (the parse-gnu parser from above, copied so that the example is standalone)
  125. parse-gnu))
  126. (parse-gnu* (string->stream "gnu is awesome"))
  127. ;; ============================
  128. ;; EXAMPLE 7: SIMPLE CSV PARSER
  129. ;; ============================
  130. ;; define a test-check form for making unit tests
  131. (define-syntax test-check
  132. (syntax-rules ()
  133. ((_ title tested-expression expected-result)
  134. (begin
  135. (format #t "* Checking ~s\n" title)
  136. (let* ((expected expected-result)
  137. (produced tested-expression))
  138. (if (not (equal? expected produced))
  139. (begin (format #t "Expected: ~s\n" expected)
  140. (format #t "Computed: ~s\n" produced))))))))
  141. ;; defining the unit tests / checks for the csv parser
  142. (when (or (getenv "CHECK")
  143. (getenv "CHECK_MARKDOWN"))
  144. (test-check "single line"
  145. (csv "a;b;c;")
  146. (list (list "a" "b" "c")))
  147. (test-check "multi line"
  148. (csv (string-append "a;b;c;\n"
  149. "d;e;f;"))
  150. (list (list "a" "b" "c") (list "d" "e" "f"))))
  151. ;; interlude
  152. ;; (parse-unless predicate parser)
  153. (define (parse-unless predicate parser)
  154. (lambda (stream)
  155. (match (predicate stream)
  156. ((? parse-failure?) (parser stream))
  157. (_ %parse-failure))))
  158. ;; defining the csv parser