[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[nongnu] elpa/haskell-tng-mode 41a29dd 066/385: backward lexer
From: |
ELPA Syncer |
Subject: |
[nongnu] elpa/haskell-tng-mode 41a29dd 066/385: backward lexer |
Date: |
Tue, 5 Oct 2021 23:59:03 -0400 (EDT) |
branch: elpa/haskell-tng-mode
commit 41a29dd344fb96ec784a55c388e2056b28ca25db
Author: Tseen She <ts33n.sh3@gmail.com>
Commit: Tseen She <ts33n.sh3@gmail.com>
backward lexer
---
haskell-tng-smie.el | 80 ++++++++++++++++++++++------
test/haskell-tng-smie-test.el | 118 +++++++++++++++++++++++++++++-------------
test/src/medley.hs.lexer | 2 +-
3 files changed, 149 insertions(+), 51 deletions(-)
diff --git a/haskell-tng-smie.el b/haskell-tng-smie.el
index 0c3db4b..ed89f69 100644
--- a/haskell-tng-smie.el
+++ b/haskell-tng-smie.el
@@ -34,6 +34,13 @@
;; read-only navigation.
(defvar-local haskell-tng-smie:last nil)
+;; syntax-tables supported by SMIE
+(defconst haskell-tng-smie:fast-syntax
+ (rx (| (syntax open-parenthesis)
+ (syntax close-parenthesis)
+ (syntax string-quote)
+ (syntax string-delimiter))))
+
(defun haskell-tng-smie:state-invalidation (_beg _end _pre-length)
"For use in `after-change-functions' to invalidate the state of
the lexer."
@@ -52,12 +59,13 @@ the lexer."
;; Note that this implementation is stateful as it can play back multiple
;; virtual tokens at a single point. This lexer could be made stateless if SMIE
;; were to support a 4th return type: a list of any of the above.
+;;
+;; Any changes to this function must be reflected in
+;; `haskell-tng-smie:backward-token'.
(defun haskell-tng-smie:forward-token ()
(unwind-protect
(let (case-fold-search)
- (when (and haskell-tng-smie:state
- (not (equal haskell-tng-smie:last `(forward . ,(point)))))
- (setq haskell-tng-smie:state nil))
+ (haskell-tng-smie:check-last 'forward)
(if (consp haskell-tng-smie:state)
;; continue replaying virtual tokens
@@ -77,12 +85,10 @@ the lexer."
(haskell-tng-smie:state
(haskell-tng-smie:replay-virtual))
+ ((eobp) nil)
+
;; syntax tables (supported by `smie-indent-forward-token')
- ((looking-at (rx (| (syntax open-parenthesis)
- (syntax close-parenthesis)
- (syntax string-quote)
- (syntax string-delimiter))))
- nil)
+ ((looking-at haskell-tng-smie:fast-syntax) nil)
;; regexps
((or
@@ -98,17 +104,62 @@ the lexer."
(string (char-before))))))
;; save the state
- (setq haskell-tng-smie:last `(forward . ,(point)))))
+ (haskell-tng-smie:set-last 'forward)))
+
+;; Implementation of `smie-backward-token' for Haskell, matching
+;; `haskell-tng-smie:forward-token'.
+(defun haskell-tng-smie:backward-token ()
+ (unwind-protect
+ (let (case-fold-search)
+ (haskell-tng-smie:check-last 'backward)
+
+ (if (consp haskell-tng-smie:state)
+ (haskell-tng-smie:replay-virtual 'reverse)
+
+ (setq haskell-tng-smie:state
+ (unless haskell-tng-smie:state
+ (haskell-tng-layout:virtuals-at-point)))
+
+ (if haskell-tng-smie:state
+ (haskell-tng-smie:replay-virtual 'reverse)
+
+ (forward-comment (- (point)))
+ (cond
+ ((bobp) nil)
+ ((looking-back haskell-tng-smie:fast-syntax (- (point) 1)) nil)
+ ((or
+ (looking-back haskell-tng:regexp:reserved (- (point) 8))
+ (looking-back (rx (+ (| (syntax word) (syntax symbol))))
+ (line-beginning-position) 't))
+ (haskell-tng-smie:last-match 'reverse))
+ (t
+ (forward-char -1)
+ (string (char-after)))))))
+
+ (haskell-tng-smie:set-last 'backward)))
+
+(defun haskell-tng-smie:set-last (direction)
+ (setq haskell-tng-smie:last (cons direction (point))))
+
+(defun haskell-tng-smie:check-last (direction)
+ (when (and haskell-tng-smie:state
+ (not (equal haskell-tng-smie:last (cons direction (point)))))
+ (setq haskell-tng-smie:state nil)))
-(defun haskell-tng-smie:replay-virtual ()
+(defun haskell-tng-smie:replay-virtual (&optional reverse)
";; read a virtual token from state, set 't when all done"
(unwind-protect
- (pop haskell-tng-smie:state)
+ (if reverse
+ (unwind-protect
+ (car (last haskell-tng-smie:state))
+ (setq haskell-tng-smie:state
+ (butlast haskell-tng-smie:state)))
+ (pop haskell-tng-smie:state))
(unless haskell-tng-smie:state
(setq haskell-tng-smie:state 't))))
-(defun haskell-tng-smie:last-match ()
- (goto-char (match-end 0))
+(defun haskell-tng-smie:last-match (&optional reverse)
+ (goto-char (if reverse (match-beginning 0) (match-end 0)))
(match-string-no-properties 0))
;; TODO a haskell grammar
@@ -148,8 +199,7 @@ the lexer."
haskell-tng-smie:grammar
haskell-tng-smie:rules
:forward-token #'haskell-tng-smie:forward-token
- ;; FIXME :backward-token #'haskell-tng-smie:backward-token
- ))
+ :backward-token #'haskell-tng-smie:backward-token))
(provide 'haskell-tng-smie)
;;; haskell-tng-smie.el ends here
diff --git a/test/haskell-tng-smie-test.el b/test/haskell-tng-smie-test.el
index f23ddb4..4d5457a 100644
--- a/test/haskell-tng-smie-test.el
+++ b/test/haskell-tng-smie-test.el
@@ -17,6 +17,7 @@
(let ((tok (funcall smie-forward-token-function)))
(cond
((< 0 (length tok)) tok)
+ ((eobp) nil)
((looking-at (rx (| (syntax open-parenthesis)
(syntax close-parenthesis))))
(concat "_" (haskell-tng-smie:last-match)))
@@ -25,45 +26,76 @@
(let ((start (point)))
(forward-sexp 1)
(concat "_" (buffer-substring-no-properties start (point)))))
- ((eobp) nil)
(t (error "Bumped into unknown token")))))
-(defun haskell-tng-smie-test:forward-tokens ()
- "Forward lex the current buffer using SMIE lexer and return the list of
lines,
+;; same as above, but for `smie-indent-backward-token'
+(defun haskell-tng-smie-test:indent-backward-token ()
+ (let ((tok (funcall smie-backward-token-function)))
+ (cond
+ ((< 0 (length tok)) tok)
+ ((bobp) nil)
+ ((looking-back (rx (| (syntax open-parenthesis)
+ (syntax close-parenthesis)))
+ (- (point) 1))
+ (concat "_" (haskell-tng-smie:last-match 'reverse)))
+ ((looking-back (rx (| (syntax string-quote)
+ (syntax string-delimiter)))
+ (- (point) 1))
+ (let ((start (point)))
+ (backward-sexp 1)
+ (concat "_" (buffer-substring-no-properties (point) start))))
+ (t (error "Bumped into unknown token")))))
+
+(defun haskell-tng-smie-test:tokens (&optional reverse)
+ "Lex the current buffer using SMIE and return the list of lines,
where each line is a list of tokens.
When called interactively, shows the tokens in a buffer."
- (defvar smie-forward-token-function)
- (let* ((lines '(())))
- (goto-char (point-min))
- (while (not (eobp))
+ (let ((lines (list nil))
+ quit)
+ (goto-char (if reverse (point-max) (point-min)))
+ (while (not quit)
(let* ((start (point))
- (token (haskell-tng-smie-test:indent-forward-token)))
+ (token (if reverse
+ (haskell-tng-smie-test:indent-backward-token)
+ (haskell-tng-smie-test:indent-forward-token))))
(let ((line-diff (- (line-number-at-pos (point))
(line-number-at-pos start))))
- (unless (<= line-diff 0)
- (setq lines (append (-repeat line-diff nil) lines))))
- (unless (s-blank? token)
- (push token (car lines)))))
- (reverse (--map (reverse it) lines))))
+ (unless (= line-diff 0)
+ (setq lines (append (-repeat (abs line-diff) nil) lines))))
+ (if (and (not token) (if reverse (bobp) (eobp)))
+ (setq quit 't)
+ (unless (s-blank? token)
+ (push token (car lines))))))
+ (if reverse
+ lines
+ (reverse (--map (reverse it) lines)))))
(defun haskell-tng-smie-test:tokens-to-string (lines)
(concat (s-join "\n" (--map (s-join " " it) lines)) "\n"))
-(defun haskell-tng-smie-test:parse-to-string ()
- (haskell-tng-smie-test:tokens-to-string
- (haskell-tng-smie-test:forward-tokens)))
-
(defun have-expected-forward-lex (file)
(haskell-tng-testutils:assert-file-contents
file
#'haskell-tng-mode
- #'haskell-tng-smie-test:parse-to-string
+ (lambda () (haskell-tng-smie-test:tokens-to-string
+ (haskell-tng-smie-test:tokens)))
+ "lexer"))
+
+(defun have-expected-backward-lex (file)
+ (haskell-tng-testutils:assert-file-contents
+ file
+ #'haskell-tng-mode
+ (lambda () (haskell-tng-smie-test:tokens-to-string
+ (haskell-tng-smie-test:tokens 'reverse)))
"lexer"))
(ert-deftest haskell-tng-smie-file-tests ()
- (should (have-expected-forward-lex (testdata "src/medley.hs")))
- (should (have-expected-forward-lex (testdata "src/layout.hs")))
+ ;;(should (have-expected-forward-lex (testdata "src/medley.hs")))
+ ;;(should (have-expected-forward-lex (testdata "src/layout.hs")))
+
+ (should (have-expected-backward-lex (testdata "src/medley.hs")))
+ (should (have-expected-backward-lex (testdata "src/layout.hs")))
)
(ert-deftest haskell-tng-smie-state-invalidation-tests ()
@@ -75,44 +107,60 @@ When called interactively, shows the tokens in a buffer."
;; token, then move the point for another token.
(goto-char 317)
(should (equal (haskell-tng-smie-test:indent-forward-token) ";"))
- (should (= 317 (point)))
(should (equal (haskell-tng-smie-test:indent-forward-token) "stkToLst"))
- (should (= 325 (point)))
(should (equal (haskell-tng-smie-test:indent-forward-token) "_("))
- (should (= 327 (point)))
;; repeating the above, but with a user edit, should reset the state
(goto-char 317)
(should (equal (haskell-tng-smie-test:indent-forward-token) ";"))
- (should (= 317 (point)))
(save-excursion
(goto-char (point-max))
(insert " "))
- (should (= 317 (point)))
(should (equal (haskell-tng-smie-test:indent-forward-token) ";"))
- (should (= 317 (point)))
(should (equal (haskell-tng-smie-test:indent-forward-token) "stkToLst"))
- (should (= 325 (point)))
(should (equal (haskell-tng-smie-test:indent-forward-token) "_("))
- (should (= 327 (point)))
;; repeating again, but jumping the lexer, should reset the state
(goto-char 317)
(should (equal (haskell-tng-smie-test:indent-forward-token) ";"))
- (should (= 317 (point)))
(goto-char 327)
(should (equal (haskell-tng-smie-test:indent-forward-token) "MkStack"))
- (should (= 334 (point)))
(goto-char 317)
(should (equal (haskell-tng-smie-test:indent-forward-token) ";"))
- (should (= 317 (point)))
(should (equal (haskell-tng-smie-test:indent-forward-token) "stkToLst"))
- (should (= 325 (point)))
(should (equal (haskell-tng-smie-test:indent-forward-token) "_("))
- (should (= 327 (point)))
- ))
-;; TODO the backwards test should assert consistency with forward
+ ;; repeating those tests, but for the backward lexer
+ (goto-char 317)
+ (should (equal (haskell-tng-smie-test:indent-backward-token) ";"))
+ (should (equal (haskell-tng-smie-test:indent-backward-token) "_]"))
+ (should (equal (haskell-tng-smie-test:indent-backward-token) "_["))
+
+ (goto-char 317)
+ (should (equal (haskell-tng-smie-test:indent-backward-token) ";"))
+ (save-excursion
+ (goto-char (point-max))
+ (insert " "))
+ (should (equal (haskell-tng-smie-test:indent-backward-token) ";"))
+ (should (equal (haskell-tng-smie-test:indent-backward-token) "_]"))
+ (should (equal (haskell-tng-smie-test:indent-backward-token) "_["))
+
+ (goto-char 317)
+ (should (equal (haskell-tng-smie-test:indent-backward-token) ";"))
+ (goto-char 327)
+ (should (equal (haskell-tng-smie-test:indent-backward-token) "_("))
+ (goto-char 317)
+ (should (equal (haskell-tng-smie-test:indent-backward-token) ";"))
+ (should (equal (haskell-tng-smie-test:indent-backward-token) "_]"))
+ (should (equal (haskell-tng-smie-test:indent-backward-token) "_["))
+
+ ;; jumping between forward and backward at point should reset state
+ (goto-char 317)
+ (should (equal (haskell-tng-smie-test:indent-forward-token) ";"))
+ (should (equal (haskell-tng-smie-test:indent-backward-token) ";"))
+ (should (equal (haskell-tng-smie-test:indent-forward-token) ";"))
+ (should (equal (haskell-tng-smie-test:indent-backward-token) ";"))
+ ))
;; ideas for an indentation tester
;;
https://github.com/elixir-editors/emacs-elixir/blob/master/test/test-helper.el#L52-L63
diff --git a/test/src/medley.hs.lexer b/test/src/medley.hs.lexer
index a948522..c2ee1a8 100644
--- a/test/src/medley.hs.lexer
+++ b/test/src/medley.hs.lexer
@@ -131,4 +131,4 @@ where { baz = _
; _( + _) = _
-}
+} }
- [nongnu] elpa/haskell-tng-mode 502cc26 085/385: document a failure mode, (continued)
- [nongnu] elpa/haskell-tng-mode 502cc26 085/385: document a failure mode, ELPA Syncer, 2021/10/06
- [nongnu] elpa/haskell-tng-mode 08f924c 088/385: simplify the grammar rules, better s-exps, ELPA Syncer, 2021/10/06
- [nongnu] elpa/haskell-tng-mode 71cf945 048/385: lexer test based on Haskell2010, ELPA Syncer, 2021/10/06
- [nongnu] elpa/haskell-tng-mode 4d6bbfc 050/385: feedback from Stefan, improving lexing, ELPA Syncer, 2021/10/06
- [nongnu] elpa/haskell-tng-mode 96609e4 052/385: thoughts on layout inference, ELPA Syncer, 2021/10/06
- [nongnu] elpa/haskell-tng-mode 06b357c 054/385: hacky closing braces, ELPA Syncer, 2021/10/06
- [nongnu] elpa/haskell-tng-mode 64ad4a8 057/385: refactored to centralise state, ELPA Syncer, 2021/10/06
- [nongnu] elpa/haskell-tng-mode 0ac5a2f 059/385: copyright years and move the test assertions, ELPA Syncer, 2021/10/06
- [nongnu] elpa/haskell-tng-mode a6bb27e 061/385: [ci skip] layout algorithm implemented and tested, ELPA Syncer, 2021/10/06
- [nongnu] elpa/haskell-tng-mode 7d2863e 065/385: tests for SMIE state invalidation, ELPA Syncer, 2021/10/06
- [nongnu] elpa/haskell-tng-mode 41a29dd 066/385: backward lexer,
ELPA Syncer <=
- [nongnu] elpa/haskell-tng-mode c48e7a5 069/385: starting to transcribe the expression table, ELPA Syncer, 2021/10/06
- [nongnu] elpa/haskell-tng-mode 1f1110a 073/385: transcribe the grammar rules, ELPA Syncer, 2021/10/06
- [nongnu] elpa/haskell-tng-mode 01789b1 075/385: y u no haskell-mode?, ELPA Syncer, 2021/10/06
- [nongnu] elpa/haskell-tng-mode b8f3e3f 079/385: back out incomplete grammar rules, ELPA Syncer, 2021/10/06
- [nongnu] elpa/haskell-tng-mode 9e19b2b 080/385: double down on simpler grammar, ELPA Syncer, 2021/10/06
- [nongnu] elpa/haskell-tng-mode 6e7a24f 083/385: lexer identifies conid / varid, ELPA Syncer, 2021/10/06
- [nongnu] elpa/haskell-tng-mode b12e49a 086/385: consym, ELPA Syncer, 2021/10/06
- [nongnu] elpa/haskell-tng-mode 7d6fa3d 091/385: thoughts on lexers, ELPA Syncer, 2021/10/06
- [nongnu] elpa/haskell-tng-mode 6a05d12 090/385: planning for indentation, ELPA Syncer, 2021/10/06
- [nongnu] elpa/haskell-tng-mode 2060f7a 092/385: thoughts on indentation testing, ELPA Syncer, 2021/10/06