Commit fe9f9ff2ecb7d9e19f073d5b8db4a50818c9f4c6
- Diff rendering mode:
- inline
- side by side
src/ebnf-example.lisp
(52 / 0)
|   | |||
| 1 | (defpackage #:yacc-ebnf-example | ||
| 2 | (:export #:ebnf-example) | ||
| 3 | (:use #:cl #:yacc #:yacc-ebnf)) | ||
| 4 | |||
| 5 | (in-package #:yacc-ebnf-example) | ||
| 6 | |||
| 7 | ;;; The lexer | ||
| 8 | |||
| 9 | (define-condition lexer-error (yacc-runtime-error) | ||
| 10 | ((character :initarg :character :reader lexer-error-character)) | ||
| 11 | (:report (lambda (e stream) | ||
| 12 | (format stream "Lexing failed~@[: unexpected character ~S~]" | ||
| 13 | (lexer-error-character e))))) | ||
| 14 | |||
| 15 | (defun lexer-error (char) | ||
| 16 | (error (make-condition 'lexer-error :character char))) | ||
| 17 | |||
| 18 | (defun lexer (&optional (stream *standard-input*)) | ||
| 19 | (loop | ||
| 20 | (let ((c (read-char stream nil nil))) | ||
| 21 | (cond | ||
| 22 | ((member c '(nil #\Newline)) (return-from lexer (values nil nil))) | ||
| 23 | ((member c '(#\- #\.)) | ||
| 24 | (let ((symbol (intern (string c) '#.*package*))) | ||
| 25 | (return-from lexer (values symbol symbol)))) | ||
| 26 | ((digit-char-p c) | ||
| 27 | (return-from lexer (values 'digit c))) | ||
| 28 | (t | ||
| 29 | (lexer-error c)))))) | ||
| 30 | |||
| 31 | ;;; The parser | ||
| 32 | |||
| 33 | (define-ebnf-parser *expression-ebnf-parser* | ||
| 34 | (:start-symbol S) | ||
| 35 | (:terminals (|.| - digit)) | ||
| 36 | (:precedence nil) | ||
| 37 | (S ((:option -) (:plus D) (:option |.| (:plus D))) ()) | ||
| 38 | (D digit)) | ||
| 39 | |||
| 40 | ;;; The toplevel loop | ||
| 41 | |||
| 42 | (defun ebnf-example () | ||
| 43 | (format t "A EBNF example: | ||
| 44 | S := '-'? D+ ('.' D+)? | ||
| 45 | D := '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'~%") | ||
| 46 | (loop | ||
| 47 | (with-simple-restart (abort "Return to ebnf-example toplevel.") | ||
| 48 | (format t "? ") | ||
| 49 | (let ((e (parse-with-lexer #'lexer *expression-ebnf-parser*))) | ||
| 50 | (when (null e) | ||
| 51 | (return-from ebnf-example)) | ||
| 52 | (format t " => ~A~%" (print e)))))) |
src/package.lisp
(1 / 0)
|   | |||
| 23 | 23 | ;;; THE SOFTWARE. | |
| 24 | 24 | ||
| 25 | 25 | (defpackage #:yacc-ebnf | |
| 26 | (:export #:define-ebnf-parser) | ||
| 26 | 27 | (:use #:cl #:yacc #:alexandria)) |
src/yacc-ebnf.lisp
(91 / 3)
|   | |||
| 26 | 26 | ||
| 27 | 27 | (defstruct ebnf-prod symbol derives) | |
| 28 | 28 | ||
| 29 | |||
| 30 | ;; (defun make-repeat (symbol derives) | ||
| 31 | ;; (append | ||
| 32 | ;; (expand-ebnf symbol derives :operation :option) | ||
| 33 | ;; (list (make-ebnf-prod :symbol symbol :derives (list symbol))))) | ||
| 34 | |||
| 29 | 35 | (defun make-repeat (symbol derives) | |
| 30 | (append | ||
| 31 | (expand-ebnf symbol derives :operation :option) | ||
| 32 | (list (make-ebnf-prod :symbol symbol :derives (list symbol))))) | ||
| 36 | (append (list (make-ebnf-prod :symbol symbol :derives '())) | ||
| 37 | (list (make-ebnf-prod :symbol symbol :derives (append derives (list symbol)))))) | ||
| 33 | 38 | ||
| 39 | |||
| 34 | 40 | (defun make-option (symbol derives) | |
| 35 | 41 | (append | |
| 36 | 42 | (expand-ebnf symbol derives) | |
| 37 | 43 | (list (make-ebnf-prod :symbol symbol :derives '())))) | |
| 38 | 44 | ||
| 45 | (defun make-plus (symbol derives) | ||
| 46 | (append | ||
| 47 | (expand-ebnf symbol derives) | ||
| 48 | (list (make-ebnf-prod :symbol symbol :derives (append derives (list symbol)))))) | ||
| 49 | |||
| 50 | |||
| 39 | 51 | (defun expand-ebnf (symbol derives &key (operation '())) | |
| 40 | 52 | (let ((add-prods '())) | |
| 41 | 53 | (append | |
| … | … | ||
| 56 | 56 | (make-repeat symbol derives)) | |
| 57 | 57 | (:option | |
| 58 | 58 | (make-option symbol derives)) | |
| 59 | (:plus | ||
| 60 | (make-plus symbol derives)) | ||
| 59 | 61 | (otherwise | |
| 60 | 62 | (list | |
| 61 | 63 | (make-ebnf-prod | |
| … | … | ||
| 81 | 81 | :action action | |
| 82 | 82 | :action-form action-form)) | |
| 83 | 83 | (expand-ebnf symbol derives))) | |
| 84 | |||
| 85 | (defun make-ebnf-grammar(&key name (start-symbol (required-argument)) | ||
| 86 | terminals precedence productions) | ||
| 87 | (declare (symbol name start-symbol) (list terminals productions)) | ||
| 88 | (setq productions | ||
| 89 | ;; Plato Wu,2009/12/05: yacc package need its own s-prime | ||
| 90 | (append (make-ebnf-production 'yacc::s-prime (list start-symbol) | ||
| 91 | :action #'identity :action-form '#'identity) | ||
| 92 | productions)) | ||
| 93 | (do* ((i 0 (+ i 1)) (ps productions (cdr ps)) (p (car ps) (car ps))) | ||
| 94 | ((null ps)) | ||
| 95 | (setf (yacc::production-id p) i)) | ||
| 96 | (yacc::%make-grammar :name name :terminals terminals :precedence precedence | ||
| 97 | :productions productions)) | ||
| 98 | |||
| 99 | (defun parse-ebnf-production (form) | ||
| 100 | (let ((symbol (car form)) | ||
| 101 | (productions '())) | ||
| 102 | (dolist (stuff (cdr form)) | ||
| 103 | (cond | ||
| 104 | ((and (symbolp stuff) (not (null stuff))) | ||
| 105 | |||
| 106 | (appendf productions (make-ebnf-production symbol (list stuff) | ||
| 107 | :action #'identity :action-form '#'identity) | ||
| 108 | )) | ||
| 109 | ((listp stuff) | ||
| 110 | (let ((l (car (last stuff)))) | ||
| 111 | ;; Plato Wu,2009/12/05: function in other package is list | ||
| 112 | (let ((rhs (if (or (symbolp l) (not (eq (car l) 'function))) stuff (butlast stuff))) | ||
| 113 | (action (if (or (symbolp l) (not (eq (car l) 'function))) '#'list l))) | ||
| 114 | (appendf productions (make-ebnf-production symbol rhs | ||
| 115 | :action (eval action) | ||
| 116 | :action-form action))))) | ||
| 117 | (t (error "Unexpected production ~S" stuff)))) | ||
| 118 | productions)) | ||
| 119 | |||
| 120 | (defun parse-ebnf-grammar (forms) | ||
| 121 | (let ((options '()) (make-options '()) (productions '())) | ||
| 122 | (dolist (form forms) | ||
| 123 | (cond | ||
| 124 | ((member (car form) | ||
| 125 | '(:muffle-conflicts | ||
| 126 | :print-derives-epsilon :print-first-terminals | ||
| 127 | :print-states :print-goto-graph :print-lookaheads)) | ||
| 128 | (unless (null (cddr form)) | ||
| 129 | (error "Malformed option ~S" form)) | ||
| 130 | (push (car form) make-options) | ||
| 131 | (push (cadr form) make-options)) | ||
| 132 | ((keywordp (car form)) | ||
| 133 | (unless (null (cddr form)) | ||
| 134 | (error "Malformed option ~S" form)) | ||
| 135 | (push (car form) options) | ||
| 136 | (push (cadr form) options)) | ||
| 137 | ((symbolp (car form)) | ||
| 138 | (setq productions (nconc (parse-ebnf-production form) productions))) | ||
| 139 | (t | ||
| 140 | (error "Unexpected grammar production ~S" form)))) | ||
| 141 | (values (nreverse options) (nreverse make-options) | ||
| 142 | (nreverse productions)))) | ||
| 143 | |||
| 144 | (defmacro define-ebnf-parser (name &body body) | ||
| 145 | "DEFINE-GRAMMAR NAME OPTION... PRODUCTION... | ||
| 146 | PRODUCTION ::= (SYMBOL RHS...) | ||
| 147 | RHS ::= SYMBOL | (SYMBOL... [ACTION]) | ||
| 148 | Defines the special variable NAME to be a parser. Options are as in | ||
| 149 | MAKE-GRAMMAR and MAKE-PARSER." | ||
| 150 | (multiple-value-bind (options make-options productions) (parse-ebnf-grammar body) | ||
| 151 | `(defparameter ,name | ||
| 152 | ',(apply #'make-parser | ||
| 153 | (apply #'make-ebnf-grammar | ||
| 154 | :name name | ||
| 155 | :productions productions | ||
| 156 | options) | ||
| 157 | make-options)))) |

