]> code.delx.au - gnu-emacs-elpa/blobdiff - packages/wisi/wisi.el
* wcheck-mode: New package.
[gnu-emacs-elpa] / packages / wisi / wisi.el
index faa1e3d2db4ff797d0c93f0738901ba5fffcc74e..02a2b6ecdcd1fb2a5f9d00cf5e6e50d78add517d 100755 (executable)
@@ -1,11 +1,11 @@
 ;;; wisi.el --- Utilities for implementing an indentation/navigation engine using a generalized LALR parser
 ;;
-;; Copyright (C) 2012, 2013  Free Software Foundation, Inc.
+;; Copyright (C) 2012 - 2014  Free Software Foundation, Inc.
 ;;
 ;; Author: Stephen Leake <stephen_leake@member.fsf.org>
-;; Version: 1.0
+;; Version: 1.0.4
+;; package-requires: ((cl-lib "0.4") (emacs "24.2"))
 ;; URL: http://stephe-leake.org/emacs/ada-mode/emacs-ada-mode.html
-;; Package-Requires: ((cl-lib "0"))
 ;;
 ;; This file is part of GNU Emacs.
 ;;
@@ -38,8 +38,8 @@
 ;; that lets us find statement indent points from arbitrary places in
 ;; the code.
 ;;
-;; The grammar for Ada as represented by the EBNF in LRM Annex P is
-;; not LALR(1), so we use a generalized LALR(1) parser (see
+;; For example, the grammar for Ada as represented by the EBNF in LRM
+;; Annex P is not LALR(1), so we use a generalized LALR(1) parser (see
 ;; wisi-parse, wisi-compile).
 ;;
 ;; The parser actions cache indentation and other information as text
 ;;;; grammar compiler and parser
 ;;
 ;; Since we are using a generalized LALR(1) parser, we cannot use any
-;; of the wisent grammar functions.  We use the OpenToken Ada package
+;; of the wisent grammar functions.  We use OpenToken wisi-generate
 ;; to compile BNF to Elisp source (similar to
 ;; semantic-grammar-create-package), and wisi-compile-grammar to
 ;; compile that to the parser table.
 
 ;;; Code:
 
+(require 'cl-lib)
 (require 'wisi-parse)
-(eval-when-compile (require 'cl-lib))
+
+;; WORKAROUND: for some reason, this condition doesn't work in batch mode!
+;; (when (and (= emacs-major-version 24)
+;;        (= emacs-minor-version 2))
+  (require 'wisi-compat-24.2)
+;;)
 
 ;;;; lexer
 
 (defvar-local wisi-keyword-table nil)
 (defvar-local wisi-punctuation-table nil)
 (defvar-local wisi-punctuation-table-max-length 0)
-(defvar-local wisi-string-double-term nil) ;; string delimited by double quotes
-(defvar-local wisi-string-quote-escape-doubled nil)
+(defvar-local wisi-string-double-term nil);; string delimited by double quotes
+(defvar-local wisi-string-quote-escape-doubled nil
+  "Non-nil if a string delimiter is escaped by doubling it (as in Ada).")
+(defvar-local wisi-string-quote-escape nil
+  "Cons '(delim . character) where 'character' escapes quotes in strings delimited by 'delim'.")
 (defvar-local wisi-string-single-term nil) ;; string delimited by single quotes
 (defvar-local wisi-symbol-term nil)
 
@@ -213,9 +222,12 @@ If at end of buffer, returns `wisent-eoi-term'."
       (let ((delim (char-after (point)))
            (forward-sexp-function nil))
        (forward-sexp)
-       ;; point is now after the end quote; check for a doubled quote
-       (while (and wisi-string-quote-escape-doubled
-                   (eq (char-after (point)) delim))
+       ;; point is now after the end quote; check for an escaped quote
+       (while (or
+               (and wisi-string-quote-escape-doubled
+                    (eq (char-after (point)) delim))
+               (and (eq delim (car wisi-string-quote-escape))
+                    (eq (char-before (1- (point))) (cdr wisi-string-quote-escape))))
          (forward-sexp))
        (setq token-text (buffer-substring-no-properties start (point)))
        (setq token-id (if (= delim ?\") wisi-string-double-term wisi-string-single-term))))
@@ -320,7 +332,6 @@ wisi-forward-token, but does not look up symbol."
   "Non-nil when parse is needed - cleared when parse succeeds.")
 
 (defvar-local wisi-change-need-invalidate nil)
-(defvar-local wisi-change-jit-lock-mode nil)
 
 (defun wisi-invalidate-cache()
   "Invalidate the wisi token cache for the current buffer.
@@ -345,8 +356,7 @@ Also invalidate the Emacs syntax cache."
   (when (boundp 'jit-lock-mode)
     (when (memq 'wisi-after-change (memq 'jit-lock-after-change after-change-functions))
       (setq after-change-functions (delete 'wisi-after-change after-change-functions))
-      (add-hook 'after-change-functions 'wisi-after-change nil t)
-      (setq wisi-change-jit-lock-mode (1+ wisi-change-jit-lock-mode)))
+      (add-hook 'after-change-functions 'wisi-after-change nil t))
     )
 
   (save-excursion
@@ -372,9 +382,8 @@ Also invalidate the Emacs syntax cache."
 (defun wisi-after-change (begin end length)
   "For `after-change-functions'."
   ;; begin . end is range of text being inserted (may be empty)
-  ;; (syntax-ppss-flush-cache begin) is in before-change-functions
 
-  (syntax-ppss-flush-cache begin) ;; IMPROVEME: could check for whitespace
+  ;; (syntax-ppss-flush-cache begin) is in before-change-functions
 
   (cond
    (wisi-parse-failed
@@ -389,7 +398,7 @@ Also invalidate the Emacs syntax cache."
     )
 
    ((>= wisi-cache-max begin)
-    ;; The parse had succeeded paste the start of the inserted
+    ;; The parse had succeeded past the start of the inserted
     ;; text.
     (save-excursion
       (let ((need-invalidate t)
@@ -408,7 +417,9 @@ Also invalidate the Emacs syntax cache."
          ;; FIXME: insert newline in comment to create non-comment!?
          ;; or paste a chunk of code
          ;; => check that all of change region is comment or string
-         (setq need-invalidate nil))
+         (setq need-invalidate nil)
+         ;; no caches to remove
+         )
 
         ((progn
            (skip-syntax-forward " " end);; does not skip newlines
@@ -419,7 +430,8 @@ Also invalidate the Emacs syntax cache."
         )
 
        (if need-invalidate
-           ;; The inserted or deleted text could alter the parse
+           ;; The inserted or deleted text could alter the parse;
+           ;; wisi-invalidate-cache removes all 'wisi-cache.
            (wisi-invalidate-cache)
 
          ;; else move cache-max by the net change length. We don't
@@ -443,13 +455,6 @@ Also invalidate the Emacs syntax cache."
 If accessing cache at a marker for a token as set by `wisi-cache-tokens', POS must be (1- mark)."
   (get-text-property pos 'wisi-cache))
 
-(defvar wisi-debug 0
-  "wisi debug mode:
-0 : normal - ignore parse errors, for indenting new code
-1 : report parse errors (for running tests)
-2 : show parse states, position point at parse errors, debug-on-error works in parser
-3 : also show top 10 items of parser stack.")
-
 (defvar-local wisi-parse-error-msg nil)
 
 (defun wisi-goto-error ()
@@ -472,41 +477,42 @@ If accessing cache at a marker for a token as set by `wisi-cache-tokens', POS mu
 
 (defun wisi-validate-cache (pos)
   "Ensure cached data is valid at least up to POS in current buffer."
-  (when (and wisi-parse-try
-           (< wisi-cache-max pos))
-    (when (> wisi-debug 0)
-      (message "wisi: parsing ..."))
+  (let ((msg (format "wisi: parsing %s:%d ..." (buffer-name) (line-number-at-pos))))
+    (when (and wisi-parse-try
+              (< wisi-cache-max pos))
+      (when (> wisi-debug 0)
+       (message msg))
 
-    (setq wisi-parse-try nil)
-    (setq wisi-parse-error-msg nil)
-    (save-excursion
-      (goto-char wisi-cache-max)
-      (if (> wisi-debug 1)
-         ;; let debugger stop in wisi-parse
-         (progn
-           (wisi-parse wisi-parse-table 'wisi-forward-token)
-           (setq wisi-cache-max (point))
-           (setq wisi-parse-failed nil))
-       ;; else capture errors from bad syntax, so higher level functions can try to continue
-       (condition-case err
+      (setq wisi-parse-try nil)
+      (setq wisi-parse-error-msg nil)
+      (save-excursion
+       (goto-char wisi-cache-max)
+       (if (> wisi-debug 1)
+           ;; let debugger stop in wisi-parse
            (progn
              (wisi-parse wisi-parse-table 'wisi-forward-token)
              (setq wisi-cache-max (point))
              (setq wisi-parse-failed nil))
-         (wisi-parse-error
-          (setq wisi-parse-failed t)
-          (setq wisi-parse-error-msg (cdr err)))
-         )))
-    (if wisi-parse-error-msg
-       ;; error
+         ;; else capture errors from bad syntax, so higher level functions can try to continue
+         (condition-case err
+             (progn
+               (wisi-parse wisi-parse-table 'wisi-forward-token)
+               (setq wisi-cache-max (point))
+               (setq wisi-parse-failed nil))
+           (wisi-parse-error
+            (setq wisi-parse-failed t)
+            (setq wisi-parse-error-msg (cdr err)))
+           )))
+      (if wisi-parse-error-msg
+         ;; error
+         (when (> wisi-debug 0)
+           (message "%s error" msg)
+           (wisi-goto-error)
+           (error wisi-parse-error-msg))
+       ;; no msg; success
        (when (> wisi-debug 0)
-         (message "wisi: parsing ... error")
-         (wisi-goto-error)
-         (error wisi-parse-error-msg))
-      ;; no msg; success
-      (when (> wisi-debug 0)
-       (message "wisi: parsing ... done")))
-    ))
+         (message "%s done" msg)))
+      )))
 
 (defun wisi-get-containing-cache (cache)
   "Return cache from (wisi-cache-containing CACHE)."
@@ -531,6 +537,9 @@ Point must be at cache."
        (when region
          (goto-char (car region))
          (setq cache (wisi-get-cache (car region)))
+         (when (not cache)
+           ;; token is non-terminal; first terminal doesn't have cache.
+           (setq cache (wisi-forward-cache)))
          (while (and cache
                      (< (point) (cdr region)))
            (if (not (wisi-cache-end cache))
@@ -641,6 +650,20 @@ If CONTAINING-TOKEN is empty, the next token number is used."
   ;; wisi-tokens is is bound in action created by wisi-semantic-action
   (let* ((containing-region (cddr (nth (1- containing-token) wisi-tokens)))
         (contained-region (cddr (nth (1- contained-token) wisi-tokens))))
+
+    (unless containing-region ;;
+      (signal 'wisi-parse-error
+             (wisi-error-msg
+              "wisi-containing-action: containing-region '%s' is empty. grammar error; bad action"
+              (nth 1 (nth (1- containing-token) wisi-tokens)))))
+
+    (unless (or (not contained-region) ;; contained-token is empty
+               (wisi-get-cache (car containing-region)))
+      (signal 'wisi-parse-error
+             (wisi-error-msg
+              "wisi-containing-action: containing-token '%s' has no cache. grammar error; missing action"
+              (nth 1 (nth (1- containing-token) wisi-tokens)))))
+
     (while (not containing-region)
       ;; containing-token is empty; use next
       (setq containing-region (cddr (nth containing-token wisi-tokens))))
@@ -910,6 +933,9 @@ Return start cache."
     )
   cache)
 
+(defun wisi-goto-end-1 (cache)
+  (goto-char (1- (wisi-cache-end cache))))
+
 (defun wisi-goto-end ()
   "Move point to token at end of statement point is in or before."
   (interactive)
@@ -918,7 +944,7 @@ Return start cache."
                   (wisi-forward-cache))))
     (when (wisi-cache-end cache)
       ;; nil when cache is statement-end
-      (goto-char (1- (wisi-cache-end cache))))
+      (wisi-goto-end-1 cache))
     ))
 
 (defun wisi-next-statement-cache (cache)
@@ -1107,9 +1133,6 @@ correct. Must leave point at indentation of current line.")
   (syntax-propertize (point-max))
 
   (wisi-invalidate-cache)
-
-  ;; FIXME: debug counter
-  (setq wisi-change-jit-lock-mode 0)
   )
 
 (provide 'wisi)