[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] externals/parser-generator 043e375 095/434: Refactored LR-parser
From: |
ELPA Syncer |
Subject: |
[elpa] externals/parser-generator 043e375 095/434: Refactored LR-parser into stand-alone file |
Date: |
Mon, 29 Nov 2021 15:59:16 -0500 (EST) |
branch: externals/parser-generator
commit 043e3757c9e50e750326a0e7d5a4f2c2b4809542
Author: Christian Johansson <christian@cvj.se>
Commit: Christian Johansson <christian@cvj.se>
Refactored LR-parser into stand-alone file
---
Makefile | 11 +-
parser-lr.el | 369 +++++++++++++++++++++++++++++++++++++++++++++++++
parser.el | 354 +----------------------------------------------
test/parser-lr-test.el | 170 +++++++++++++++++++++++
test/parser-test.el | 114 +--------------
5 files changed, 552 insertions(+), 466 deletions(-)
diff --git a/Makefile b/Makefile
index 4db76fa..951c98d 100644
--- a/Makefile
+++ b/Makefile
@@ -15,6 +15,13 @@ clean:
compile:
$(EMACS_CMD) -f batch-byte-compile $(EL)
-.PHONY: tests
-tests:
+.PHONY: test
+test:
$(EMACS_CMD) -l test/parser-test.el -f "parser-test"
+
+.PHONY: test-lr
+test-lr:
+ $(EMACS_CMD) -l test/parser-lr-test.el -f "parser-lr-test"
+
+.PHONY: tests
+tests: test test-lr
diff --git a/parser-lr.el b/parser-lr.el
new file mode 100644
index 0000000..5562ad8
--- /dev/null
+++ b/parser-lr.el
@@ -0,0 +1,369 @@
+;;; parser-el.el --- LR(k) Parser -*- lexical-binding: t -*-
+
+
+;;; Commentary:
+
+
+;;; Code:
+
+
+(require 'parser)
+
+
+;;; Variables:
+
+
+(defvar parser-lr--goto-tables
+ nil
+ "GOTO-tables for grammar.")
+
+(defvar parser-lr--items
+ nil
+ "Hash-table for distinct LR-items in grammar.")
+
+
+;; Main Algorithms
+
+;; Algorithm 5.9, p. 389
+(defun parser-lr--generate-goto-tables ()
+ "Calculate set of valid LR(k) items for grammar and a GOTO-table."
+ (unless (or
+ parser-lr--goto-tables
+ parser-lr--items)
+ (setq parser--goto-table nil)
+ (setq parser--table-lr-items (make-hash-table :test 'equal))
+ (let ((lr-item-set-new-index 0)
+ (goto-table)
+ (unmarked-lr-item-sets)
+ (marked-lr-item-sets (make-hash-table :test 'equal))
+ (symbols (append (parser--get-grammar-non-terminals)
(parser--get-grammar-terminals))))
+
+ (let ((e-set (parser-lr--items-for-prefix parser--e-identifier)))
+ ;;(1) Place V(e) in S. The set V(e) is initially unmarked.
+ (push `(,lr-item-set-new-index ,e-set) unmarked-lr-item-sets)
+ (setq lr-item-set-new-index (1+ lr-item-set-new-index)))
+
+ ;; (2) If a set of items a in S is unmarked
+ ;; (3) Repeat step (2) until all sets of items in S are marked.
+ (let ((popped-item)
+ (lr-item-set-index)
+ (lr-items)
+ (goto-table-table))
+ (while unmarked-lr-item-sets
+
+ (setq popped-item (pop unmarked-lr-item-sets))
+ (setq lr-item-set-index (car popped-item))
+ (setq lr-items (car (cdr popped-item)))
+ (parser--debug
+ (message "lr-item-set-index: %s" lr-item-set-index)
+ (message "lr-items: %s" lr-items)
+ (message "popped-item: %s" popped-item))
+
+ ;; (2) Mark a
+ (puthash lr-items lr-item-set-index marked-lr-item-sets)
+
+ (puthash lr-item-set-index lr-items parser--table-lr-items)
+ (setq goto-table-table nil)
+
+ ;; (2) By computing for each X in N u E, GOTO (a, X). (Algorithm 5.8
can be used here.)
+ ;; V(X1,...,Xi) = GOTO(V(X1,...,Xi-1), Xi)
+ (dolist (symbol symbols)
+ (parser--debug
+ (message "symbol: %s" symbol))
+
+ (let ((prefix-lr-items (parser-lr--items-for-goto lr-items
symbol)))
+
+ ;; If a' = GOTO(a, X) is nonempty
+ (when prefix-lr-items
+
+ (parser--debug
+ (message "GOTO(%s, %s) = %s" lr-items symbol prefix-lr-items))
+
+ ;; and is not already in S
+ (let ((goto (gethash prefix-lr-items marked-lr-item-sets)))
+ (if goto
+ (progn
+ (parser--debug
+ (message "Set already exists in: %s" goto))
+ (push `(,symbol ,goto) goto-table-table))
+
+ (parser--debug
+ (message "Set is new"))
+
+ ;; Note that GOTO(a, X) will always be empty if all items
in a
+ ;; have the dot at the right end of the production
+
+ ;; then add a' to S as an unmarked set of items
+ (push `(,symbol ,lr-item-set-new-index) goto-table-table)
+ (push `(,lr-item-set-new-index ,prefix-lr-items)
unmarked-lr-item-sets)
+ (setq lr-item-set-new-index (1+
lr-item-set-new-index)))))))
+
+ (setq goto-table-table (sort goto-table-table 'parser--sort-list))
+ (push `(,lr-item-set-index ,goto-table-table) goto-table)))
+ (setq parser--goto-table (sort goto-table 'parser--sort-list)))
+ (unless
+ (parser-lr--items-valid-p
+ (parser--hash-values-to-list parser--table-lr-items t))
+ (error "Inconsistent grammar!")))
+ t)
+
+;; Algorithm 5.10, p. 391
+(defun parser-lr--items-valid-p (lr-item-sets)
+ "Return whether the set collection LR-ITEM-SETS is valid or not."
+ (parser--debug
+ (message "lr-item-sets: %s" lr-item-sets))
+ (let ((valid-p t)
+ (set-index 0)
+ (set)
+ (sets-length (length lr-item-sets))
+ (set-length 0)
+ (a)
+ (a-look-ahead)
+ (a-follow)
+ (a-index 0)
+ (b)
+ (b-suffix)
+ (b-follow)
+ (b-suffix-follow)
+ (b-suffix-follow-eff)
+ (b-index 0))
+
+ ;; Iterate each set
+ (while (and
+ valid-p
+ (< set-index sets-length))
+ (setq set (nth set-index lr-item-sets))
+ (parser--debug
+ (message "set: %s" set))
+
+ ;; Iterate each set
+ (setq a-index 0)
+ (setq b-index 0)
+ (setq set-length (length set))
+ (while (and
+ valid-p
+ (< a-index set-length))
+ (setq a (nth a-index set))
+ (setq a-look-ahead (nth 2 a))
+
+ (parser--debug
+ (message "a: %s" a)
+ (message "a-look-ahead: %s" a-look-ahead))
+
+ ;; The only sets of LR items which need to be tested are those that
contain a dot at the right end of a production
+ (unless a-look-ahead
+ (setq a-follow (nth 3 a))
+
+ (parser--debug
+ (message "a-follow: %s" a-follow))
+
+ ;; Iterate each set again
+ (while (and
+ valid-p
+ (< b-index set-length))
+ (unless (= a-index b-index)
+ (setq b (nth b-index set))
+ (setq b-suffix (nth 2 b))
+ (setq b-follow (nth 3 b))
+ (setq b-suffix-follow (append b-suffix b-follow))
+ (setq b-suffix-follow-eff (parser--e-free-first b-suffix-follow))
+
+ (parser--debug
+ (message "b: %s" b)
+ (message "b-suffix: %s" b-suffix)
+ (message "b-follow: %s" b-follow)
+ (message "b-suffix-follow: %s" b-suffix-follow)
+ (message "b-suffix-follow-eff: %s" b-suffix-follow-eff))
+
+ (dolist (b-suffix-follow-eff-item b-suffix-follow-eff)
+ (when (equal a-follow b-suffix-follow-eff-item)
+ (parser--debug
+ (message "Inconsistent grammar! %s conflicts with %s" a b))
+ (setq valid-p nil))))
+ (setq b-index (1+ b-index))))
+ (setq a-index (1+ a-index)))
+ (setq set-index (1+ set-index)))
+
+ valid-p))
+
+;; Algorithm 5.8, p. 386
+(defun parser-lr--items-for-prefix (γ)
+ "Calculate valid LR-items for the viable prefix Γ."
+ (let ((start (parser--get-grammar-start)))
+ (unless (listp γ)
+ (setq γ (list γ)))
+ (unless (parser--valid-sentential-form-p γ)
+ (error "Invalid sentential form γ!"))
+
+ (let ((lr-item-exists (make-hash-table :test 'equal)))
+
+ ;; 1
+
+ ;; Iterate all productions in grammar
+ (let ((lr-items-e)
+ (start-productions (parser--get-grammar-rhs start)))
+
+ ;; (a)
+ (dolist (rhs start-productions)
+ ;; Add [S -> . α] to V(e)
+ (push `(,start nil ,rhs (e)) lr-items-e)
+ (puthash `(,parser--e-identifier ,start nil ,rhs
(,parser--e-identifier)) t lr-item-exists))
+
+ ;; (b) Iterate every item in v-set(e), if [A -> . Bα, u] is an item
and B -> β is in P
+ ;; then for each x in FIRST(αu) add [B -> . β, x] to v-set(e),
provided it is not already there
+ (let ((found-new t))
+
+ ;; Repeat this until no new item is found
+ (while found-new
+ (setq found-new nil)
+
+ ;; Iterate every item in V(e)
+ (dolist (item lr-items-e)
+ (let ((prefix (nth 1 item))
+ (rhs (nth 2 item))
+ (suffix (nth 3 item)))
+
+ ;; Without prefix
+ (unless prefix
+
+ ;; Check if RHS starts with a non-terminal
+ (let ((rhs-first (car rhs)))
+ (parser--debug
+ (message "rhs-first: %s" rhs-first))
+ (when (parser--valid-non-terminal-p rhs-first)
+ (let ((rhs-rest (append (cdr rhs) suffix)))
+ (let ((rhs-rest-first (parser--first rhs-rest)))
+ (parser--debug
+ (message "rhs-rest-first: %s" rhs-rest-first))
+ (unless rhs-rest-first
+ (setq rhs-rest-first `((,parser--e-identifier))))
+ (let ((sub-production (parser--get-grammar-rhs
rhs-first)))
+ (parser--debug
+ (message "sub-production: %s" sub-production))
+
+ ;; For each production with B as LHS
+ (dolist (sub-rhs sub-production)
+
+ ;; Set follow to nil if it's the e-identifier
+ (when (and
+ (= (length sub-rhs) 1)
+ (parser--valid-e-p (car sub-rhs)))
+ (setq sub-rhs nil))
+
+ (parser--debug
+ (message "sub-rhs: %s" sub-rhs))
+
+ ;; For each x in FIRST(αu)
+ (dolist (f rhs-rest-first)
+ (parser--debug
+ (message "f: %s" f))
+
+ ;; Add [B -> . β, x] to V(e), provided it is
not already there
+ (unless (gethash `(e ,rhs-first nil ,sub-rhs
,f) lr-item-exists)
+ (puthash `(e ,rhs-first nil ,sub-rhs ,f) t
lr-item-exists)
+ (push `(,rhs-first nil ,sub-rhs ,f)
lr-items-e)
+
+ ;; (c) Repeat (b) until no more items can be
added to V(e)
+ (setq found-new t))))))))))))))
+
+ (parser--debug
+ (message "V(e) = %s" lr-items-e))
+
+ (setq lr-items-e (sort lr-items-e 'parser--sort-list))
+
+ ;; 2 Suppose that we have constructed V(X1,X2,...,Xi-1) we construct
V(X1,X2,...,Xi) as follows:
+ ;; Only do this step if prefix is not the e-identifier
+ (let ((prefix-previous lr-items-e))
+ (unless (and
+ (= (length γ) 1)
+ (parser--valid-e-p (car γ)))
+ (dolist (prefix γ)
+ (let ((lr-new-item))
+ (setq lr-new-item (parser-lr--items-for-goto prefix-previous
prefix))
+
+ (parser--debug
+ (message "prefix: %s" prefix)
+ (message "prefix-previous: %s" prefix-previous)
+ (message "lr-new-item: %s" lr-new-item))
+
+ (setq prefix-previous lr-new-item))))
+
+ (parser--debug
+ (message "γ: %s" γ))
+ prefix-previous)))))
+
+(defun parser-lr--items-for-goto (previous-lr-item x)
+ "Calculate LR-items for GOTO(PREVIOUS-LR-ITEM, X)."
+ (let ((lr-new-item)
+ (lr-item-exists (make-hash-table :test 'equal)))
+ (parser--debug (message "x: %s" x))
+
+ (dolist (lr-item previous-lr-item)
+ (let ((lr-item-lhs (nth 0 lr-item))
+ (lr-item-prefix (nth 1 lr-item))
+ (lr-item-suffix (nth 2 lr-item))
+ (lr-item-look-ahead (nth 3 lr-item)))
+ (let ((lr-item-suffix-first (car lr-item-suffix))
+ (lr-item-suffix-rest (cdr lr-item-suffix)))
+
+ ;; (a) If [A -> a . XiB, u] is in V(X1,...,Xi-1)
+ (when (eq lr-item-suffix-first x)
+
+ ;; Add [A -> aXi . B, u] to V(X1,...,Xi)
+ (let ((combined-prefix (append lr-item-prefix (list x))))
+ (parser--debug
+ (message "lr-new-item-1: %s" `(,lr-item-lhs ,combined-prefix
,lr-item-suffix-rest ,lr-item-look-ahead)))
+ (push `(,lr-item-lhs ,combined-prefix ,lr-item-suffix-rest
,lr-item-look-ahead) lr-new-item))))))
+
+ ;; (c) Repeat step (2b) until no more new items can be added to
V(X1,...,Xi)
+ (let ((added-new t))
+ (while added-new
+ (setq added-new nil)
+ (dolist (lr-item lr-new-item)
+ (let ((lr-item-suffix (nth 2 lr-item)))
+ (let ((lr-item-suffix-first (car lr-item-suffix))
+ (lr-item-suffix-rest (cdr lr-item-suffix)))
+
+ ;; (b) If [A -> a . Bb, u] has been placed in V(X1,...,Xi)
+ ;; and B -> D is in P
+ (when (parser--valid-non-terminal-p lr-item-suffix-first)
+
+ (let ((lr-item-suffix-rest-first (parser--first
lr-item-suffix-rest)))
+ (unless lr-item-suffix-rest-first
+ (setq lr-item-suffix-rest-first (list nil)))
+ (let ((sub-production (parser--get-grammar-rhs
lr-item-suffix-first)))
+
+ ;; For each production with B as LHS
+ (dolist (sub-rhs sub-production)
+
+ ;; Transform e-productions into nil
+ (when (and
+ (= (length sub-rhs) 1)
+ (parser--valid-e-p (car sub-rhs)))
+ (setq sub-rhs nil))
+
+ ;; For each x in FIRST(αu)
+ (dolist (f lr-item-suffix-rest-first)
+
+ ;; then add [B -> . D, x] to V(X1,...,Xi) for each x
in FIRST(bu)
+ ;; provided it is not already there
+ (let ((lr-item-to-add `(,lr-item-suffix-first nil
,sub-rhs ,f)))
+ (unless (gethash lr-item-to-add lr-item-exists)
+ (setq added-new t)
+ (parser--debug (message "lr-item-to-add: %s"
lr-item-to-add))
+ (puthash lr-item-to-add t lr-item-exists)
+ (push lr-item-to-add lr-new-item)))))))))))))
+
+ (setq lr-new-item (sort lr-new-item 'parser--sort-list))
+ lr-new-item))
+
+;; Algorithm 5.11, p. 393
+(defun parser-lr--generate-action-tables ()
+ "Generate action-tables for lr-grammar."
+ ;; TODO This
+ t)
+
+
+(provide 'parser-lr)
+
+;;; parser-lr.el ends here
diff --git a/parser.el b/parser.el
index bd8c3c2..8b25813 100644
--- a/parser.el
+++ b/parser.el
@@ -1,4 +1,4 @@
-;;; parser.el --- LR(k) Parser -*- lexical-binding: t -*-
+;;; parser.el --- Parser library -*- lexical-binding: t -*-
;;; Commentary:
@@ -26,18 +26,10 @@
nil
"Generated F-sets for grammar.")
-(defvar parser--goto-table
- nil
- "GOTO-table for grammar.")
-
(defvar parser--look-ahead-number
nil
"Current look-ahead number used.")
-(defvar parser--table-lr-items
- nil
- "Hash-table for distinct LR-items in grammar.")
-
(defvar parser--table-non-terminal-p
nil
"Hash-table of terminals for quick checking.")
@@ -65,9 +57,7 @@
(defun parser--clear-cache ()
"Clear cache."
- (setq parser--f-sets nil)
- (setq parser--goto-table nil)
- (setq parser--table-lr-items nil))
+ (setq parser--f-sets nil))
(defun parser--distinct (elements)
"Return distinct of ELEMENTS."
@@ -698,346 +688,6 @@
(setq follow-set (parser--distinct follow-set)))
follow-set))
-;; Algorithm 5.9, p. 389
-(defun parser--generate-tables-for-lr ()
- "Calculate set of valid LR(k) items for grammar and a GOTO-table."
- (unless (or
- parser--goto-table
- parser--table-lr-items)
- (setq parser--goto-table nil)
- (setq parser--table-lr-items (make-hash-table :test 'equal))
- (let ((lr-item-set-new-index 0)
- (goto-table)
- (unmarked-lr-item-sets)
- (marked-lr-item-sets (make-hash-table :test 'equal))
- (symbols (append (parser--get-grammar-non-terminals)
(parser--get-grammar-terminals))))
-
- (let ((e-set (parser--lr-items-for-prefix parser--e-identifier)))
- ;;(1) Place V(e) in S. The set V(e) is initially unmarked.
- (push `(,lr-item-set-new-index ,e-set) unmarked-lr-item-sets)
- (setq lr-item-set-new-index (1+ lr-item-set-new-index)))
-
- ;; (2) If a set of items a in S is unmarked
- ;; (3) Repeat step (2) until all sets of items in S are marked.
- (let ((popped-item)
- (lr-item-set-index)
- (lr-items)
- (goto-table-table))
- (while unmarked-lr-item-sets
-
- (setq popped-item (pop unmarked-lr-item-sets))
- (setq lr-item-set-index (car popped-item))
- (setq lr-items (car (cdr popped-item)))
- (parser--debug
- (message "lr-item-set-index: %s" lr-item-set-index)
- (message "lr-items: %s" lr-items)
- (message "popped-item: %s" popped-item))
-
- ;; (2) Mark a
- (puthash lr-items lr-item-set-index marked-lr-item-sets)
-
- (puthash lr-item-set-index lr-items parser--table-lr-items)
- (setq goto-table-table nil)
-
- ;; (2) By computing for each X in N u E, GOTO (a, X). (Algorithm 5.8
can be used here.)
- ;; V(X1,...,Xi) = GOTO(V(X1,...,Xi-1), Xi)
- (dolist (symbol symbols)
- (parser--debug
- (message "symbol: %s" symbol))
-
- (let ((prefix-lr-items (parser--lr-items-for-goto lr-items
symbol)))
-
- ;; If a' = GOTO(a, X) is nonempty
- (when prefix-lr-items
-
- (parser--debug
- (message "GOTO(%s, %s) = %s" lr-items symbol prefix-lr-items))
-
- ;; and is not already in S
- (let ((goto (gethash prefix-lr-items marked-lr-item-sets)))
- (if goto
- (progn
- (parser--debug
- (message "Set already exists in: %s" goto))
- (push `(,symbol ,goto) goto-table-table))
-
- (parser--debug
- (message "Set is new"))
-
- ;; Note that GOTO(a, X) will always be empty if all items
in a
- ;; have the dot at the right end of the production
-
- ;; then add a' to S as an unmarked set of items
- (push `(,symbol ,lr-item-set-new-index) goto-table-table)
- (push `(,lr-item-set-new-index ,prefix-lr-items)
unmarked-lr-item-sets)
- (setq lr-item-set-new-index (1+
lr-item-set-new-index)))))))
-
- (setq goto-table-table (sort goto-table-table 'parser--sort-list))
- (push `(,lr-item-set-index ,goto-table-table) goto-table)))
- (setq parser--goto-table (sort goto-table 'parser--sort-list)))
- (unless
- (parser--lr-items-valid-p
- (parser--hash-values-to-list parser--table-lr-items t))
- (error "Inconsistent grammar!")))
- t)
-
-;; Algorithm 5.10, p. 391
-(defun parser--lr-items-valid-p (lr-item-sets)
- "Return whether the set collection LR-ITEM-SETS is valid or not."
- (parser--debug
- (message "lr-item-sets: %s" lr-item-sets))
- (let ((valid-p t)
- (set-index 0)
- (set)
- (sets-length (length lr-item-sets))
- (set-length 0)
- (a)
- (a-look-ahead)
- (a-follow)
- (a-index 0)
- (b)
- (b-suffix)
- (b-follow)
- (b-suffix-follow)
- (b-suffix-follow-eff)
- (b-index 0))
-
- ;; Iterate each set
- (while (and
- valid-p
- (< set-index sets-length))
- (setq set (nth set-index lr-item-sets))
- (parser--debug
- (message "set: %s" set))
-
- ;; Iterate each set
- (setq a-index 0)
- (setq b-index 0)
- (setq set-length (length set))
- (while (and
- valid-p
- (< a-index set-length))
- (setq a (nth a-index set))
- (setq a-look-ahead (nth 2 a))
-
- (parser--debug
- (message "a: %s" a)
- (message "a-look-ahead: %s" a-look-ahead))
-
- ;; The only sets of LR items which need to be tested are those that
contain a dot at the right end of a production
- (unless a-look-ahead
- (setq a-follow (nth 3 a))
-
- (parser--debug
- (message "a-follow: %s" a-follow))
-
- ;; Iterate each set again
- (while (and
- valid-p
- (< b-index set-length))
- (unless (= a-index b-index)
- (setq b (nth b-index set))
- (setq b-suffix (nth 2 b))
- (setq b-follow (nth 3 b))
- (setq b-suffix-follow (append b-suffix b-follow))
- (setq b-suffix-follow-eff (parser--e-free-first b-suffix-follow))
-
- (parser--debug
- (message "b: %s" b)
- (message "b-suffix: %s" b-suffix)
- (message "b-follow: %s" b-follow)
- (message "b-suffix-follow: %s" b-suffix-follow)
- (message "b-suffix-follow-eff: %s" b-suffix-follow-eff))
-
- (dolist (b-suffix-follow-eff-item b-suffix-follow-eff)
- (when (equal a-follow b-suffix-follow-eff-item)
- (parser--debug
- (message "Inconsistent grammar! %s conflicts with %s" a b))
- (setq valid-p nil))))
- (setq b-index (1+ b-index))))
- (setq a-index (1+ a-index)))
- (setq set-index (1+ set-index)))
-
- valid-p))
-
-;; Algorithm 5.8, p. 386
-(defun parser--lr-items-for-prefix (γ)
- "Calculate valid LR-items for the viable prefix Γ."
- (let ((start (parser--get-grammar-start)))
- (unless (listp γ)
- (setq γ (list γ)))
- (unless (parser--valid-sentential-form-p γ)
- (error "Invalid sentential form γ!"))
-
- (let ((lr-item-exists (make-hash-table :test 'equal)))
-
- ;; 1
-
- ;; Iterate all productions in grammar
- (let ((lr-items-e)
- (start-productions (parser--get-grammar-rhs start)))
-
- ;; (a)
- (dolist (rhs start-productions)
- ;; Add [S -> . α] to V(e)
- (push `(,start nil ,rhs (e)) lr-items-e)
- (puthash `(,parser--e-identifier ,start nil ,rhs
(,parser--e-identifier)) t lr-item-exists))
-
- ;; (b) Iterate every item in v-set(e), if [A -> . Bα, u] is an item
and B -> β is in P
- ;; then for each x in FIRST(αu) add [B -> . β, x] to v-set(e),
provided it is not already there
- (let ((found-new t))
-
- ;; Repeat this until no new item is found
- (while found-new
- (setq found-new nil)
-
- ;; Iterate every item in V(e)
- (dolist (item lr-items-e)
- (let ((prefix (nth 1 item))
- (rhs (nth 2 item))
- (suffix (nth 3 item)))
-
- ;; Without prefix
- (unless prefix
-
- ;; Check if RHS starts with a non-terminal
- (let ((rhs-first (car rhs)))
- (parser--debug
- (message "rhs-first: %s" rhs-first))
- (when (parser--valid-non-terminal-p rhs-first)
- (let ((rhs-rest (append (cdr rhs) suffix)))
- (let ((rhs-rest-first (parser--first rhs-rest)))
- (parser--debug
- (message "rhs-rest-first: %s" rhs-rest-first))
- (unless rhs-rest-first
- (setq rhs-rest-first `((,parser--e-identifier))))
- (let ((sub-production (parser--get-grammar-rhs
rhs-first)))
- (parser--debug
- (message "sub-production: %s" sub-production))
-
- ;; For each production with B as LHS
- (dolist (sub-rhs sub-production)
-
- ;; Set follow to nil if it's the e-identifier
- (when (and
- (= (length sub-rhs) 1)
- (parser--valid-e-p (car sub-rhs)))
- (setq sub-rhs nil))
-
- (parser--debug
- (message "sub-rhs: %s" sub-rhs))
-
- ;; For each x in FIRST(αu)
- (dolist (f rhs-rest-first)
- (parser--debug
- (message "f: %s" f))
-
- ;; Add [B -> . β, x] to V(e), provided it is
not already there
- (unless (gethash `(e ,rhs-first nil ,sub-rhs
,f) lr-item-exists)
- (puthash `(e ,rhs-first nil ,sub-rhs ,f) t
lr-item-exists)
- (push `(,rhs-first nil ,sub-rhs ,f)
lr-items-e)
-
- ;; (c) Repeat (b) until no more items can be
added to V(e)
- (setq found-new t))))))))))))))
-
- (parser--debug
- (message "V(e) = %s" lr-items-e))
-
- (setq lr-items-e (sort lr-items-e 'parser--sort-list))
-
- ;; 2 Suppose that we have constructed V(X1,X2,...,Xi-1) we construct
V(X1,X2,...,Xi) as follows:
- ;; Only do this step if prefix is not the e-identifier
- (let ((prefix-previous lr-items-e))
- (unless (and
- (= (length γ) 1)
- (parser--valid-e-p (car γ)))
- (dolist (prefix γ)
- (let ((lr-new-item))
- (setq lr-new-item (parser--lr-items-for-goto prefix-previous
prefix))
-
- (parser--debug
- (message "prefix: %s" prefix)
- (message "prefix-previous: %s" prefix-previous)
- (message "lr-new-item: %s" lr-new-item))
-
- (setq prefix-previous lr-new-item))))
-
- (parser--debug
- (message "γ: %s" γ))
- prefix-previous)))))
-
-(defun parser--lr-items-for-goto (previous-lr-item x)
- "Calculate LR-items for GOTO(PREVIOUS-LR-ITEM, X)."
- (let ((lr-new-item)
- (lr-item-exists (make-hash-table :test 'equal)))
- (parser--debug (message "x: %s" x))
-
- (dolist (lr-item previous-lr-item)
- (let ((lr-item-lhs (nth 0 lr-item))
- (lr-item-prefix (nth 1 lr-item))
- (lr-item-suffix (nth 2 lr-item))
- (lr-item-look-ahead (nth 3 lr-item)))
- (let ((lr-item-suffix-first (car lr-item-suffix))
- (lr-item-suffix-rest (cdr lr-item-suffix)))
-
- ;; (a) If [A -> a . XiB, u] is in V(X1,...,Xi-1)
- (when (eq lr-item-suffix-first x)
-
- ;; Add [A -> aXi . B, u] to V(X1,...,Xi)
- (let ((combined-prefix (append lr-item-prefix (list x))))
- (parser--debug
- (message "lr-new-item-1: %s" `(,lr-item-lhs ,combined-prefix
,lr-item-suffix-rest ,lr-item-look-ahead)))
- (push `(,lr-item-lhs ,combined-prefix ,lr-item-suffix-rest
,lr-item-look-ahead) lr-new-item))))))
-
- ;; (c) Repeat step (2b) until no more new items can be added to
V(X1,...,Xi)
- (let ((added-new t))
- (while added-new
- (setq added-new nil)
- (dolist (lr-item lr-new-item)
- (let ((lr-item-suffix (nth 2 lr-item)))
- (let ((lr-item-suffix-first (car lr-item-suffix))
- (lr-item-suffix-rest (cdr lr-item-suffix)))
-
- ;; (b) If [A -> a . Bb, u] has been placed in V(X1,...,Xi)
- ;; and B -> D is in P
- (when (parser--valid-non-terminal-p lr-item-suffix-first)
-
- (let ((lr-item-suffix-rest-first (parser--first
lr-item-suffix-rest)))
- (unless lr-item-suffix-rest-first
- (setq lr-item-suffix-rest-first (list nil)))
- (let ((sub-production (parser--get-grammar-rhs
lr-item-suffix-first)))
-
- ;; For each production with B as LHS
- (dolist (sub-rhs sub-production)
-
- ;; Transform e-productions into nil
- (when (and
- (= (length sub-rhs) 1)
- (parser--valid-e-p (car sub-rhs)))
- (setq sub-rhs nil))
-
- ;; For each x in FIRST(αu)
- (dolist (f lr-item-suffix-rest-first)
-
- ;; then add [B -> . D, x] to V(X1,...,Xi) for each x
in FIRST(bu)
- ;; provided it is not already there
- (let ((lr-item-to-add `(,lr-item-suffix-first nil
,sub-rhs ,f)))
- (unless (gethash lr-item-to-add lr-item-exists)
- (setq added-new t)
- (parser--debug (message "lr-item-to-add: %s"
lr-item-to-add))
- (puthash lr-item-to-add t lr-item-exists)
- (push lr-item-to-add lr-new-item)))))))))))))
-
- (setq lr-new-item (sort lr-new-item 'parser--sort-list))
- lr-new-item))
-
-;; Algorithm 5.11, p. 393
-(defun parser--generate-action-tables-for-lr-grammar ()
- "Generate action-tables for lr-grammar."
- ;; TODO This
- t)
-
-
(provide 'parser)
;;; parser.el ends here
diff --git a/test/parser-lr-test.el b/test/parser-lr-test.el
new file mode 100644
index 0000000..d2483f0
--- /dev/null
+++ b/test/parser-lr-test.el
@@ -0,0 +1,170 @@
+;;; parser-lr-test.el --- Tests for LR(k) Parser -*- lexical-binding: t -*-
+
+
+;;; Commentary:
+
+
+;;; Code:
+
+(require 'parser-lr)
+(require 'ert)
+
+(defun parser-lr-test--generate-goto-tables ()
+ "Test `parser-lr--generate-goto-tables'."
+ (message "Starting tests for (parser-lr--generate-goto-tables)")
+
+ ;; Example 5.30, p. 389
+ (parser--set-grammar '((Sp S) (a b) ((Sp S) (S (S a S b)) (S e)) Sp))
+ (parser--set-look-ahead-number 1)
+
+ (parser-lr--generate-goto-tables)
+
+ ;; (message "GOTO-table: %s" parser--goto-table)
+ ;; (message "LR-items: %s" (parser--hash-to-list parser--table-lr-items))
+
+ (should
+ (equal
+ '((0 ((S 1)))
+ (1 ((a 2)))
+ (2 ((S 3)))
+ (3 ((a 4) (b 5)))
+ (4 ((S 6)))
+ (5 nil)
+ (6 ((a 4) (b 7)))
+ (7 nil))
+ parser--goto-table))
+
+ (should
+ (equal
+ '((0 ((S nil (S a S b) (a)) (S nil (S a S b) (e)) (S nil nil (a)) (S nil
nil (e)) (Sp nil (S) (e))))
+ (1 ((S (S) (a S b) (a)) (S (S) (a S b) (e)) (Sp (S) nil (e))))
+ (2 ((S (S a) (S b) (a)) (S (S a) (S b) (e)) (S nil (S a S b) (a)) (S nil
(S a S b) (b)) (S nil nil (a)) (S nil nil (b))))
+ (3 ((S (S) (a S b) (a)) (S (S) (a S b) (b)) (S (S a S) (b) (a)) (S (S a
S) (b) (e))))
+ (4 ((S (S a) (S b) (a)) (S (S a) (S b) (b)) (S nil (S a S b) (a)) (S nil
(S a S b) (b)) (S nil nil (a)) (S nil nil (b))))
+ (5 ((S (S a S b) nil (a)) (S (S a S b) nil (e))))
+ (6 ((S (S) (a S b) (a)) (S (S) (a S b) (b)) (S (S a S) (b) (a)) (S (S a
S) (b) (b))))
+ (7 ((S (S a S b) nil (a)) (S (S a S b) nil (b)))))
+ (parser--hash-to-list parser--table-lr-items)))
+
+ (message "Passed LR-items for example 5.30")
+
+ (message "Passed tests for (parser-r--generate-goto-tables)"))
+
+(defun parser-lr-test--items-for-prefix ()
+ "Test `parser-lr--items-for-prefix'."
+ (message "Starting tests for (parser-lr--items-for-prefix)")
+
+ ;; Example 5.29 p 387
+ (parser--set-grammar '((Sp S) (a b) ((Sp S) (S (S a S b)) (S e)) Sp))
+ (parser--set-look-ahead-number 1)
+
+ (should
+ (equal
+ '((S nil (S a S b) (a))
+ (S nil (S a S b) (e))
+ (S nil nil (a))
+ (S nil nil (e))
+ (Sp nil (S) (e)))
+ (parser-lr--items-for-prefix 'e)))
+ (message "Passed V(e)")
+
+ (should
+ (equal
+ '((S (S) (a S b) (a))
+ (S (S) (a S b) (e))
+ (Sp (S) nil (e)))
+ (parser-lr--items-for-prefix 'S)))
+ (message "Passed V(S)")
+
+ (should
+ (equal
+ nil
+ (parser-lr--items-for-prefix 'a)))
+ (message "Passed V(a)")
+
+ (should
+ (equal
+ nil
+ (parser-lr--items-for-prefix 'b)))
+ (message "Passed V(b)")
+
+ (should
+ (equal
+ '((S (S a) (S b) (a))
+ (S (S a) (S b) (e))
+ (S nil (S a S b) (a))
+ (S nil (S a S b) (b))
+ (S nil nil (a))
+ (S nil nil (b)))
+ (parser-lr--items-for-prefix '(S a))))
+ (message "Passed V(Sa)")
+
+ (should
+ (equal
+ nil
+ (parser-lr--items-for-prefix '(S S))))
+ (message "Passed V(SS)")
+
+ (should
+ (equal
+ nil
+ (parser-lr--items-for-prefix '(S b))))
+ (message "Passed V(Sb)")
+
+ ;; a3 p. 390
+ (should
+ (equal
+ '((S (S) (a S b) (a))
+ (S (S) (a S b) (b))
+ (S (S a S) (b) (a))
+ (S (S a S) (b) (e)))
+ (parser-lr--items-for-prefix '(S a S))))
+ (message "Passed V(SaS)")
+
+ (should
+ (equal
+ nil
+ (parser-lr--items-for-prefix '(S a a))))
+ (message "Passed V(Saa)")
+
+ (should
+ (equal
+ nil
+ (parser-lr--items-for-prefix '(S a b))))
+ (message "Passed V(Sab)")
+
+ (message "Passed tests for (parser-lr--items-for-prefix)"))
+
+(defun parser-lr-test--items-valid-p ()
+ "Test `parser-lr--items-valid-p'."
+ (message "Started tests for (parser-lr--items-valid-p)")
+
+ (parser--set-grammar '((Sp S) (a b) ((Sp S) (S (S a S b)) (S e)) Sp))
+ (parser--set-look-ahead-number 1)
+ (parser-lr--generate-goto-tables)
+ (should
+ (equal
+ t
+ (parser-lr--items-valid-p (parser--hash-values-to-list
parser--table-lr-items t))))
+
+ (message "Passed first")
+
+ (should
+ (equal
+ nil
+ (parser-lr--items-valid-p
+ '(((S nil (S a S b) (a)) (S nil (S a S b) (e)) (S nil nil (a)) (S nil nil
(e)) (Sp nil (S) (e))) ((S (S) (a S b) (a)) (S (S) (a S b) (e)) (Sp (S) nil
(e))) ((S (S a) (S b) (a)) (S (S a) (S b) (e)) (S nil (S a S b) (a)) (S nil (S
a S b) (b)) (S nil nil (a)) (S nil nil (b))) ((S (S) (a S b) (a)) (S (S) (a S
b) (b)) (S (S a S) (b) (a)) (S (S a S) (b) (e))) ((S (S a S b) nil (a)) (S (S a
S b) (a) (a)) (S (S a S b) nil (e))) ((S (S a) (S b) (a)) (S (S a) (S b) (b))
(S nil (S a S b) (a)) [...]
+
+ (message "Passed tests for (parser-lr--items-valid-p)"))
+
+(defun parser-lr-test ()
+ "Run test."
+ ;; (setq debug-on-error t)
+
+ (parser-lr-test--items-for-prefix)
+ (parser-lr-test--generate-goto-tables)
+ (parser-lr-test--items-valid-p))
+
+(provide 'parser-lr-test)
+
+;;; parser-lr-test.el ends here
diff --git a/test/parser-test.el b/test/parser-test.el
index c8fdfed..a8467b3 100644
--- a/test/parser-test.el
+++ b/test/parser-test.el
@@ -1,4 +1,4 @@
-;;; parser-test.el --- Tests for parser -*- lexical-binding: t -*-
+;;; parser-test.el --- Tests for Parser -*- lexical-binding: t -*-
;;; Commentary:
@@ -264,91 +264,6 @@
(message "Passed tests for (parser--generate-tables-for-lr)"))
-(defun parser-test--lr-items-for-prefix ()
- "Test `parser--lr-items-for-prefix'."
- (message "Starting tests for (parser--lr-items-for-prefix)")
-
- ;; Example 5.29 p 387
- (parser--set-grammar '((Sp S) (a b) ((Sp S) (S (S a S b)) (S e)) Sp))
- (parser--set-look-ahead-number 1)
-
- (should
- (equal
- '((S nil (S a S b) (a))
- (S nil (S a S b) (e))
- (S nil nil (a))
- (S nil nil (e))
- (Sp nil (S) (e)))
- (parser--lr-items-for-prefix 'e)))
- (message "Passed V(e)")
-
- (should
- (equal
- '((S (S) (a S b) (a))
- (S (S) (a S b) (e))
- (Sp (S) nil (e)))
- (parser--lr-items-for-prefix 'S)))
- (message "Passed V(S)")
-
- (should
- (equal
- nil
- (parser--lr-items-for-prefix 'a)))
- (message "Passed V(a)")
-
- (should
- (equal
- nil
- (parser--lr-items-for-prefix 'b)))
- (message "Passed V(b)")
-
- (should
- (equal
- '((S (S a) (S b) (a))
- (S (S a) (S b) (e))
- (S nil (S a S b) (a))
- (S nil (S a S b) (b))
- (S nil nil (a))
- (S nil nil (b)))
- (parser--lr-items-for-prefix '(S a))))
- (message "Passed V(Sa)")
-
- (should
- (equal
- nil
- (parser--lr-items-for-prefix '(S S))))
- (message "Passed V(SS)")
-
- (should
- (equal
- nil
- (parser--lr-items-for-prefix '(S b))))
- (message "Passed V(Sb)")
-
- ;; a3 p. 390
- (should
- (equal
- '((S (S) (a S b) (a))
- (S (S) (a S b) (b))
- (S (S a S) (b) (a))
- (S (S a S) (b) (e)))
- (parser--lr-items-for-prefix '(S a S))))
- (message "Passed V(SaS)")
-
- (should
- (equal
- nil
- (parser--lr-items-for-prefix '(S a a))))
- (message "Passed V(Saa)")
-
- (should
- (equal
- nil
- (parser--lr-items-for-prefix '(S a b))))
- (message "Passed V(Sab)")
-
- (message "Passed tests for (parser--lr-items-for-prefix)"))
-
(defun parser-test--valid-grammar-p ()
"Test function `parser--valid-grammar-p'."
(message "Starting tests for (parser--valid-grammar-p)")
@@ -467,28 +382,6 @@
(message "Passed tests for (parser--get-grammar-rhs)"))
-(defun parser-test--lr-items-valid-p ()
- "Test `parser--lr-items-valid-p'."
- (message "Started tests for (parser--lr-items-valid-p)")
-
- (parser--set-grammar '((Sp S) (a b) ((Sp S) (S (S a S b)) (S e)) Sp))
- (parser--set-look-ahead-number 1)
- (parser--generate-tables-for-lr)
- (should
- (equal
- t
- (parser--lr-items-valid-p (parser--hash-values-to-list
parser--table-lr-items t))))
-
- (message "Passed first")
-
- (should
- (equal
- nil
- (parser--lr-items-valid-p
- '(((S nil (S a S b) (a)) (S nil (S a S b) (e)) (S nil nil (a)) (S nil nil
(e)) (Sp nil (S) (e))) ((S (S) (a S b) (a)) (S (S) (a S b) (e)) (Sp (S) nil
(e))) ((S (S a) (S b) (a)) (S (S a) (S b) (e)) (S nil (S a S b) (a)) (S nil (S
a S b) (b)) (S nil nil (a)) (S nil nil (b))) ((S (S) (a S b) (a)) (S (S) (a S
b) (b)) (S (S a S) (b) (a)) (S (S a S) (b) (e))) ((S (S a S b) nil (a)) (S (S a
S b) (a) (a)) (S (S a S b) nil (e))) ((S (S a) (S b) (a)) (S (S a) (S b) (b))
(S nil (S a S b) (a)) [...]
-
- (message "Passed tests for (parser--lr-items-valid-p)"))
-
(defun parser-test ()
"Run test."
;; (setq debug-on-error t)
@@ -505,10 +398,7 @@
;; Algorithms
(parser-test--first)
(parser-test--e-free-first)
- (parser-test--follow)
- (parser-test--lr-items-for-prefix)
- (parser-test--generate-tables-for-lr)
- (parser-test--lr-items-valid-p))
+ (parser-test--follow))
(provide 'parser-test)
- [elpa] externals/parser-generator 8fb8676 376/434: More work on Infix math example, passing another test, (continued)
- [elpa] externals/parser-generator 8fb8676 376/434: More work on Infix math example, passing another test, ELPA Syncer, 2021/11/29
- [elpa] externals/parser-generator 32e2c15 024/434: Fixed bug with e-production, ELPA Syncer, 2021/11/29
- [elpa] externals/parser-generator 26bf153 037/434: Setting look-ahead-number is now separated from setting grammar, ELPA Syncer, 2021/11/29
- [elpa] externals/parser-generator a54061c 055/434: Debugging of new algorithm, ELPA Syncer, 2021/11/29
- [elpa] externals/parser-generator 62d06a0 063/434: Passing unit test for V(Sa), ELPA Syncer, 2021/11/29
- [elpa] externals/parser-generator 186d7bb 065/434: Renamed function lr-items to lr-items-for-prefix, ELPA Syncer, 2021/11/29
- [elpa] externals/parser-generator 9792eeb 069/434: Added TODO items, ELPA Syncer, 2021/11/29
- [elpa] externals/parser-generator 08b40cd 071/434: Updated header levels in README, ELPA Syncer, 2021/11/29
- [elpa] externals/parser-generator 5da1b28 079/434: Added TODO item, ELPA Syncer, 2021/11/29
- [elpa] externals/parser-generator 79565f4 089/434: Fixed sorting of columns in GOTO-table, ELPA Syncer, 2021/11/29
- [elpa] externals/parser-generator 043e375 095/434: Refactored LR-parser into stand-alone file,
ELPA Syncer <=
- [elpa] externals/parser-generator 4f81d98 107/434: Sorting each row in action-table, ELPA Syncer, 2021/11/29
- [elpa] externals/parser-generator 62f54f1 110/434: Added failing unit test for e-free-first function, ELPA Syncer, 2021/11/29
- [elpa] externals/parser-generator ee0ef5d 115/434: Added failing unit test for Algorithm 5.7, ELPA Syncer, 2021/11/29
- [elpa] externals/parser-generator b0e9111 125/434: Started on lex-analyzer function, ELPA Syncer, 2021/11/29
- [elpa] externals/parser-generator 0416ca9 134/434: Added information about lex-analyzer in README, ELPA Syncer, 2021/11/29
- [elpa] externals/parser-generator b756e1a 135/434: Added example of parsing using LR algorithm, ELPA Syncer, 2021/11/29
- [elpa] externals/parser-generator cee559d 139/434: Added separate document for lexical analysis documentation, ELPA Syncer, 2021/11/29
- [elpa] externals/parser-generator de0ed95 142/434: Updated README.md, ELPA Syncer, 2021/11/29
- [elpa] externals/parser-generator fa7089e 144/434: Re-factored lex analyzer function to not use length argument, ELPA Syncer, 2021/11/29
- [elpa] externals/parser-generator 7eb9a4a 156/434: Fixed issue with indexing productions when they have SDT, ELPA Syncer, 2021/11/29