parse.scm 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. ;;; nyacc/parse.scm
  2. ;; Copyright (C) 2014-2018 Matthew R. Wette
  3. ;;
  4. ;; This library is free software; you can redistribute it and/or
  5. ;; modify it under the terms of the GNU Lesser General Public
  6. ;; License as published by the Free Software Foundation; either
  7. ;; version 3 of the License, or (at your option) any later version.
  8. ;;
  9. ;; This library is distributed in the hope that it will be useful,
  10. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. ;; Lesser General Public License for more details.
  13. ;;
  14. ;; You should have received a copy of the GNU Lesser General Public License
  15. ;; along with this library; if not, see <http://www.gnu.org/licenses/>
  16. ;;; Description:
  17. ;; procedures to generate parsers, given a lexical analyzer
  18. ;; one for files; one for interactive use: newline is possible end of input
  19. ;;; Code:
  20. (define-module (nyacc parse)
  21. #:export (make-lalr-parser
  22. make-lalr-parser/sym
  23. make-lalr-parser/num))
  24. (use-modules (ice-9 pretty-print))
  25. (define pp pretty-print)
  26. (define $default 1) ; sync w/ lalr.scm
  27. (define $error 2) ; sync w/ lalr.scm
  28. (define (vector-map proc vec) ; see (srfi srfi-43)
  29. (let* ((ln (vector-length vec)) (res (make-vector ln)))
  30. (let iter ((ix 0))
  31. (unless (= ix ln)
  32. (vector-set! res ix (proc ix (vector-ref vec ix)))
  33. (iter (1+ ix))))
  34. res))
  35. (define (wrap-action actn) ; see util.scm
  36. (define (mkarg i) (string->symbol (string-append "$" (number->string i))))
  37. (define (make-arg-list n) (let iter ((r '(. $rest)) (i 1))
  38. (if (> i n) r (iter (cons (mkarg i) r) (1+ i)))))
  39. (cons* 'lambda (make-arg-list (car actn)) (cdr actn)))
  40. (define (make-xct av)
  41. (if (procedure? (vector-ref av 0))
  42. av
  43. (vector-map (lambda (ix f) (eval f (current-module)))
  44. (vector-map (lambda (ix actn) (wrap-action actn)) av))))
  45. (define (sferr fmt . args)
  46. (apply simple-format (current-error-port) fmt args))
  47. (define (dmsg/n s t a)
  48. (cond
  49. ((not a) (sferr "state ~S, token ~S\t=> parse error\n" s t))
  50. ((positive? a) (sferr "state ~S, token ~S\t=> shift, goto ~S\n" s t a))
  51. ((negative? a) (sferr "state ~S, token ~S\t=> reduce ~S\n" s t (- a)))
  52. ((zero? a) (sferr "state ~S, token ~S\t=> accept\n" s t))
  53. (else (error "coding error in (nyacc parse)"))))
  54. (define (dmsg/s s t a)
  55. (case (car a)
  56. ((error) (sferr "state ~S, token ~S\t=> parse error\n" s t))
  57. ((shift) (sferr "state ~S, token ~S\t=> shift, goto ~S\n" s t (cdr a)))
  58. ((reduce) (sferr "state ~S, token ~S\t=> reduce ~S\n" s t (cdr a)))
  59. ((accept) (sferr "state ~S, token ~S\t=> accept\n" s t))
  60. (else (error "coding error in (nyacc parse)"))))
  61. (define (parse-error state laval)
  62. (let ((fn (or (port-filename (current-input-port)) "(unknown)"))
  63. (ln (1+ (port-line (current-input-port)))))
  64. (throw 'nyacc-error
  65. "~A:~A: parse failed at state ~A, on input ~S\n"
  66. fn ln (car state) (cdr laval))))
  67. (define* (make-lalr-parser/sym mach #:key (skip-if-unexp '()) interactive)
  68. (let* ((len-v (assq-ref mach 'len-v))
  69. (rto-v (assq-ref mach 'rto-v))
  70. (pat-v (assq-ref mach 'pat-v))
  71. (xct-v (make-xct (assq-ref mach 'act-v)))
  72. (start (assq-ref (assq-ref mach 'mtab) '$start)))
  73. (lambda* (lexr #:key debug)
  74. (let iter ((state (list 0)) ; state stack
  75. (stack (list '$@)) ; semantic value stack
  76. (nval #f) ; non-terminal from prev reduction
  77. (lval #f)) ; lexical value (from lex'er)
  78. (cond
  79. ((and interactive nval
  80. (eqv? (car nval) start)
  81. (zero? (car state))) ; done
  82. (cdr nval))
  83. ((not (or nval lval))
  84. (if (eqv? '$default (caar (vector-ref pat-v (car state))))
  85. (iter state stack (cons '$default #f) lval) ; default reduction
  86. (iter state stack nval (lexr)))) ; reload
  87. (else
  88. (let* ((laval (or nval lval))
  89. (tval (car laval))
  90. (sval (cdr laval))
  91. (stxl (vector-ref pat-v (car state)))
  92. (stx (or (assq-ref stxl tval) (assq-ref stxl '$default)
  93. (cons '$error #f))))
  94. (if debug (dmsg/s (car state) (if nval tval sval) stx))
  95. (cond
  96. ((eq? '$error (car stx)) ; error ???
  97. (if (memq tval skip-if-unexp)
  98. (iter state stack #f #f)
  99. (parse-error state laval)))
  100. ((eq? 'reduce (car stx)) ; reduce
  101. (let* ((gx (cdr stx))
  102. (gl (vector-ref len-v gx))
  103. ($$ (apply (vector-ref xct-v gx) stack)))
  104. (iter (list-tail state gl)
  105. (list-tail stack gl)
  106. (cons (vector-ref rto-v gx) $$)
  107. lval)))
  108. ((eq? 'shift (car stx)) ; shift
  109. (iter (cons (cdr stx) state) (cons sval stack)
  110. #f (if nval lval #f)))
  111. (else ; accept
  112. (car stack))))))))))
  113. (define* (make-lalr-parser/num mach #:key (skip-if-unexp '()) interactive)
  114. (let* ((len-v (assq-ref mach 'len-v))
  115. (rto-v (assq-ref mach 'rto-v))
  116. (pat-v (assq-ref mach 'pat-v))
  117. (xct-v (make-xct (assq-ref mach 'act-v)))
  118. (start (assq-ref (assq-ref mach 'mtab) '$start)))
  119. (lambda* (lexr #:key debug)
  120. (let iter ((state (list 0)) ; state stack
  121. (stack (list '$@)) ; semantic value stack
  122. (nval #f) ; non-terminal from prev reduction
  123. (lval #f)) ; lexical value (from lex'r)
  124. (cond
  125. ((and interactive nval
  126. (eqv? (car nval) start)
  127. (zero? (car state))) ; done
  128. (cdr nval))
  129. ((not (or nval lval))
  130. (if (eqv? $default (caar (vector-ref pat-v (car state))))
  131. (iter state stack (cons $default #f) lval) ; default reduction
  132. (iter state stack nval (lexr)))) ; reload
  133. (else
  134. (let* ((laval (or nval lval))
  135. (tval (car laval))
  136. (sval (cdr laval))
  137. (stxl (vector-ref pat-v (car state)))
  138. (stx (or (assq-ref stxl tval)
  139. (and (not (memq tval skip-if-unexp))
  140. (assq-ref stxl $default)))))
  141. (if debug (dmsg/n (car state) (if nval tval sval) stx))
  142. (cond
  143. ((eq? #f stx) ; error
  144. (if (memq tval skip-if-unexp)
  145. (iter state stack #f #f)
  146. (parse-error state laval)))
  147. ((negative? stx) ; reduce
  148. (let* ((gx (abs stx))
  149. (gl (vector-ref len-v gx))
  150. ($$ (apply (vector-ref xct-v gx) stack)))
  151. (iter (list-tail state gl)
  152. (list-tail stack gl)
  153. (cons (vector-ref rto-v gx) $$)
  154. lval)))
  155. ((positive? stx) ; shift
  156. (iter (cons stx state) (cons sval stack) #f (if nval lval #f)))
  157. (else ; accept
  158. (car stack))))))))))
  159. ;; @deffn {Procedure} make-lalr-parser mach [options] => parser
  160. ;; Generate a procedure for parsing a language, where @var{mach} is
  161. ;; a machine generated by @code{make-lalr-machine}.
  162. ;; This generates a procedure that takes one argument, a lexical analyzer:
  163. ;; @example
  164. ;; (parser lexical-analyzer #:debug #t)
  165. ;; @end example
  166. ;; @noindent
  167. ;; and is used as
  168. ;; @example
  169. ;; (define xyz-parse (make-lalr-parser xyz-mach))
  170. ;; (with-input-from-file "sourcefile.xyz"
  171. ;; (lambda () (xyz-parse (gen-lexer))))
  172. ;; @end example
  173. ;; @noindent
  174. ;; The generated parser is reentrant. Options are:
  175. ;; @table @code
  176. ;; @item #:skip-if-unexp
  177. ;; This is a list of tokens to skip if not expected. It is used
  178. ;; to allow comments to be skipped. The default is @code{'()}.
  179. ;; @item #:interactive
  180. ;; If @code{#t}, this tells the parserthat this is being called
  181. ;; interactively, so that the token @code{$end} is not expected.
  182. ;; The default value is @code{#f}.
  183. ;; @end table
  184. ;; @noindent
  185. ;; @end deffn
  186. (define* (make-lalr-parser mach #:key (skip-if-unexp '()) interactive)
  187. "- Procedure: make-lalr-parser mach [options] => parser
  188. Generate a procedure for parsing a language, where MACH is a
  189. machine generated by 'make-lalr-machine'. This generates a
  190. procedure that takes one argument, a lexical analyzer:
  191. (parser lexical-analyzer #:debug #t)
  192. and is used as
  193. (define xyz-parse (make-lalr-parser xyz-mach))
  194. (with-input-from-file \"sourcefile.xyz\"
  195. (lambda () (xyz-parse (gen-lexer))))
  196. The generated parser is reentrant. Options are:
  197. '#:skip-if-unexp'
  198. This is a list of tokens to skip if not expected. It is used
  199. to allow comments to be skipped. The default is ''()'.
  200. '#:interactive'
  201. If '#t', this tells the parserthat this is being called
  202. interactively, so that the token '$end' is not expected. The
  203. default value is '#f'."
  204. (let* ((mtab (assq-ref mach 'mtab))
  205. (siu (map (lambda (n) (assoc-ref mtab n)) skip-if-unexp))
  206. (iact interactive))
  207. (if (number? (caar (vector-ref (assq-ref mach 'pat-v) 0)))
  208. ;; hashed:
  209. (make-lalr-parser/num mach #:skip-if-unexp siu #:interactive iact)
  210. ;; not hashed:
  211. (make-lalr-parser/sym mach #:skip-if-unexp siu #:interactive iact))))
  212. ;; == deprecated =============================================================
  213. (define* (old-make-lalr-parser mach)
  214. (define (dmsg s t a) (sferr "state ~S, token ~S\t=> ~S\n" s t a))
  215. (let* ((len-v (assq-ref mach 'len-v)) ; production RHS length
  216. (rto-v (assq-ref mach 'rto-v)) ; reduce to
  217. (pat-v (assq-ref mach 'pat-v)) ; parse action (shift, reduce) table
  218. (actn-v (assq-ref mach 'act-v)) ; symbolic actions
  219. (mtab (assq-ref mach 'mtab))
  220. (xact-v (if (procedure? (vector-ref actn-v 0)) actn-v
  221. (vector-map
  222. ;; Turn symbolic action into executable procedures:
  223. (lambda (ix f) (eval f (current-module)))
  224. (vector-map
  225. (lambda (ix actn) (wrap-action actn))
  226. actn-v))))
  227. ;;
  228. (hashed (number? (caar (vector-ref pat-v 0)))) ; been hashified?
  229. (def (if hashed $default '$default))
  230. (end (assq-ref mtab '$end))
  231. (err (assq-ref mtab '$error))
  232. (comm (list (assq-ref mtab '$lone-comm) (assq-ref mtab '$code-comm)))
  233. ;; predicate to test for shift action:
  234. (shift? (if hashed
  235. (lambda (a) (positive? a))
  236. (lambda (a) (eq? 'shift (car a)))))
  237. ;; On shift, transition to this state:
  238. (shift-to (if hashed (lambda (x) x) (lambda (x) (cdr x))))
  239. ;; Predicate to test for reduce action:
  240. (reduce? (if hashed
  241. (lambda (a) (negative? a))
  242. (lambda (a) (eq? 'reduce (car a)))))
  243. ;; On reduce, reduce this production-rule:
  244. (reduce-pr (if hashed abs cdr))
  245. ;; If error, make the right packet.
  246. (other (if hashed 0 '(other . 0)))
  247. )
  248. (lambda* (lexr #:key debug)
  249. (let iter ((state (list 0)) ; state stack
  250. (stack (list '$@)) ; sval stack
  251. (nval #f) ; prev reduce to non-term val
  252. (lval (lexr))) ; lexical value (from lex'er)
  253. (let* ((tval (car (if nval nval lval))) ; token (syntax value)
  254. (sval (cdr (if nval nval lval))) ; semantic value
  255. (stxl (vector-ref pat-v (car state))) ; state transition xtra
  256. (oact #f) ;; if not shift/reduce, then accept, error or skip
  257. (stx (cond ;; state transition
  258. ((assq-ref stxl tval)) ; shift/reduce in table
  259. ((memq tval comm) (set! oact 'skip) other)
  260. ((assq-ref stxl err)) ; error recovery
  261. ((assq-ref stxl def)) ; default action
  262. (else (set! oact 'error) other))))
  263. (if debug (dmsg (car state) (if nval tval sval) stx))
  264. (cond
  265. ((shift? stx)
  266. ;; We could check here to determine if next transition only has a
  267. ;; default reduction and, if so, go ahead and process the reduction
  268. ;; without reading another input token. Needed for interactive.
  269. (iter (cons (shift-to stx) state) (cons sval stack)
  270. #f (if nval lval (lexr))))
  271. ((reduce? stx)
  272. (let* ((gx (reduce-pr stx)) (gl (vector-ref len-v gx))
  273. ($$ (apply (vector-ref xact-v gx) stack)))
  274. (iter (list-tail state gl)
  275. (list-tail stack gl)
  276. (cons (vector-ref rto-v gx) $$)
  277. lval)))
  278. (else ;; other action: skip, error, or accept
  279. (case oact
  280. ((skip) (iter state stack nval (lexr)))
  281. ((error) (throw 'nyacc-error
  282. "parse failed at state ~A, on input ~S"
  283. (car state) sval))
  284. (else ;; accept
  285. (car stack))))))))))
  286. ;;; --- last line ---