emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[nongnu] elpa/gnuplot 18dccc2 058/184: Merge branch 'context' into devel


From: ELPA Syncer
Subject: [nongnu] elpa/gnuplot 18dccc2 058/184: Merge branch 'context' into devel
Date: Sun, 29 Aug 2021 11:03:15 -0400 (EDT)

branch: elpa/gnuplot
commit 18dccc2d97e29e1a5a19fb72b076deab0152f32b
Merge: f710394 0d2079a
Author: joddie <jonxfield@gmail.com>
Commit: joddie <jonxfield@gmail.com>

    Merge branch 'context' into devel
---
 .gitignore               |    4 +-
 gnuplot-context.el       | 2295 ++++++++++++++++++++++++++++++++++++++++++++++
 gnuplot-debug-context.el |  115 +++
 gnuplot-test-context.el  |  419 +++++++++
 gnuplot.el               |   10 +-
 5 files changed, 2837 insertions(+), 6 deletions(-)

diff --git a/.gitignore b/.gitignore
index 8acbc2b..ed79435 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,5 +4,7 @@
 *.pdf
 *.dvi
 *~
-#*#
+/#*#
+*/#*#
 /hacks.el
+.#*
diff --git a/gnuplot-context.el b/gnuplot-context.el
new file mode 100644
index 0000000..1954b28
--- /dev/null
+++ b/gnuplot-context.el
@@ -0,0 +1,2295 @@
+;;; gnuplot-context.el -- context-sensitive help and completion for gnuplot
+
+;; Copyright (C) 2012 Jonathan Oddie <j.j.oddie@gmail.com>
+
+;; Author:     Jonathan Oddie <j.j.oddie@gmail.com>
+;; Maintainer: Jonathan Oddie <j.j.oddie@gmail.com>
+;; Created:    Wednesday, 08 February 2012
+;; Updated:    Friday, 20 April 2012
+;; Version:    0.6.1
+;; Keywords:   gnuplot, plotting
+
+;; This file is not part of GNU Emacs.
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+;;
+;; This lisp script is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+;;
+;; Permission is granted to distribute copies of this lisp script
+;; provided the copyright notice and this permission are preserved in
+;; all copies.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with this program; if not, you can either send email to this
+;; program's maintainer or write to: The Free Software Foundation,
+;; Inc.; 675 Massachusetts Avenue; Cambridge, MA 02139, USA.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; send bug reports to the author (j.j.oddie@gmail.com)
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;;; Commentary:
+
+;;
+;; This file enhances gnuplot-mode by providing context-sensitive
+;; completion, ElDoc support, and info page lookup for gnuplot script
+;; and shell buffers.
+;; 
+;; Usage 
+;; =====
+;;
+;; Put this file somewhere in your load path, byte compile it
+;; (important!) and (require 'gnuplot-context).  This will give you
+;; context-sensitive TAB-completion and info page lookup, provided
+;; that you have the correct version of the Gnuplot Info file
+;; installed somewhere in your Info path (see `Info-directory-list').
+;;
+;; Summary of key bindings:
+;;     C-c C-d            read info page for construction at point
+;;     C-u C-c C-d        prompt for info page to read
+;;     C-c M-h, C-c C-/   pop up multi-line Eldoc string for construction
+;;          at point (requires additional installation steps)
+;;         
+;; This file rebinds "C-c C-d" in both `gnuplot-mode' and
+;; `gnuplot-comint-mode' to a new function, `gnuplot-info-at-point',
+;; which does a partial parse of the current line in order to find the
+;; most relevant info page node. If given a "C-u" prefix argument (or
+;; if it fails to parse the command at point), `gnuplot-info-at-point'
+;; will instead prompt for the Info node to read.
+;;
+;; This file also redefines `gnuplot-completion-at-point' to use the
+;; same parsing engine for finding completions, which should hopefully
+;; be more accurate than matching against the entire (long) list of
+;; gnuplot keywords.  Completion is bound to TAB in the gnuplot comint
+;; buffer, and usually to M-TAB in other buffers.  In recent Emacs
+;; versions, you can make TAB choose intelligently between indentation
+;; and context-sensitive completion in other buffers by setting the
+;; variable `tab-always-indent' to `complete'.  This allows entering,
+;; for example, "set cntrparam" into a gnuplot script with the
+;; keystrokes "s e TAB SPC c n TAB".
+;; 
+;; ElDoc support (one-line help displayed in the mode line) has to be
+;; compiled from the source for the Gnuplot documentation in the
+;; Gnuplot source tree.  Running "doc2texi.el" in the "docs" directory
+;; should produce an Elisp "gnuplot-eldoc.el" file with ElDoc strings
+;; extracted from the gnuplot.doc documentation source.  Install this
+;; somewhere in your load path.  You can then toggle ElDoc on and off
+;; by typing M-x `eldoc-mode' in a gnuplot/gnuplot-comint buffer.  Put
+;; it in the relevant load hook if you always want it on.
+;;
+;; ElDoc only displays one line of information automatically.  To pop
+;; up a fuller multi-line syntax description of a construct in the
+;; echo area, type C-c M-h or C-c C-/ (`gnuplot-help-function'). This
+;; works whether ElDoc mode is currently enabled or not.
+;; 
+;; 
+;;
+;; Internal details
+;; ================
+;; 
+;; Gnuplot's command language has a fair amount of syntactic
+;; complexity, and the only way I could think of to support these
+;; features was to do a complete parse of the command line. So that's
+;; what this package does. Instead of building a parse tree, it
+;; matches up until the token at point, and then either makes a list
+;; of possible completions, or sets the variables `gnuplot-eldoc' and
+;; `gnuplot-info-at-point' based on where it is in the grammar at that
+;; point.
+;;
+;; The parsing/matching process happens in two phases: tokenizing
+;; (`gnuplot-tokenize') and matching (`gnuplot-match-pattern').  In
+;; order to be able to construct a full list of possible completions
+;; via backtracking, the matching algorithm simulates a simple stack
+;; machine with continuations.  At byte-compile time, the PEG-like
+;; grammar in S-expression notation (`gnuplot-grammar') is compiled
+;; down into a vector of "machine code" for the parsing machine (see
+;; `gnuplot-compile-pattern', `gnuplot-compile-grammar' and
+;; `gnuplot-compiled-grammar'). This is complicated, but it seems to
+;; work well enough, and it saves on the Emacs call stack.
+;;
+;; Compiling the grammar does require increasing `max-lisp-eval-depth'
+;; modestly. This shouldn't cause any problems on modern machines, and
+;; it only needs to be done once, at byte-compilation time.
+;;
+;; The parsing machine and compiler are partially based on the
+;; description in Medeiros and Ierusalimschy 2008, "A Parsing Machine
+;; for PEGs" (http://dl.acm.org/citation.cfm?doid=1408681.1408683).
+;;
+;; The pattern-matching language
+;; =============================
+;;
+;; The gnuplot-mode grammar (see `gnuplot-compiled-grammar') is a list
+;; of rules (RULE PATTERN), with each pattern written in S-expression
+;; notation as follows:
+;;
+;;    any
+;;     Match any token
+;;
+;;    name, number, string
+;;     Match a token of the given type
+;;
+;;    Any other symbol
+;;     Match another named rule in the grammar. May be recursive.
+;;
+;;    "STRING"
+;;     Match literally: a token with exactly the text "STRING".
+;;
+;;   (kw KEYWORD ALIASES ...)
+;;     Match abbreviated Gnuplot keywords. KEYWORD can be a string or
+;;     a cons (PREFIX . SUFFIX). In the latter case, this pattern
+;;     will match PREFIX plus any number of characters from the
+;;     beginning of SUFFIX.  Any literal string from ALIASES will
+;;     also match. The token-id of the matching token is mutated to
+;;     the canonical value of KEYWORD. 
+;;     Example:
+;;      (kw ("linew" ."idth") "lw") matches "linew", "linewi",
+;;     ... "linewidth" as well as "lw". Any of these tokens will
+;;     appear as "linewidth" in subsequent processing. (This is
+;;     important for the "info-keyword" form, see below).
+;;
+;; The other pattern forms combine simpler patterns, much like regular
+;; expressions or PEGs (parsing expression grammars):
+;;
+;;    (sequence { (:eldoc "eldoc string") }
+;;              { (:info "info page") }
+;;              { (:no-info) }
+;;          PATTERN PATTERN... )
+;;     Match all the PATTERNs in sequence or fail. Sequences can also
+;;     have optional ElDoc strings and info pages associated with
+;;     them; the innermost ElDoc or info page around point is the one
+;;     shown to the user. Alternatively, either property may be a
+;;     symbol, which should be a function to be called to get the
+;;     real value.  Finally, if no ElDoc string is specified but the
+;;     variable `gnuplot-eldoc-hash' contains a value for the name of
+;;     the info page at point, that value is used as the ElDoc string
+;;     instead.
+;;
+;;     For better readability, sequence forms can also be written as
+;;     a vector, omitting the `sequence': [PATTERN PATTERN ...]
+;;      
+;;    (either PATTERN PATTERN...)
+;;     Match the first PATTERN to succeed, or fail if none
+;;     matches. Like regexp `|'.
+;;
+;;    (many PATTERN)
+;;     Match PATTERN zero or more times, greedily; like regexp
+;;     `*'. Unlike a regular expression matcher, the parsing machine
+;;     will not backtrack and try to match fewer times if a later
+;;     part of the pattern fails. This applies equally to the other
+;;     non-deterministic forms "either" and "maybe".
+;;
+;;    (maybe PATTERN)
+;;     Match PATTERN zero or one times, like regexp `?'.
+;;
+;;    (capture NAME PATTERN)
+;;     Match PATTERN, capturing the tokens in a capture group named
+;;     NAME. Capture groups are stored in `gnuplot-captures'
+;;     and can be retrieved using `gnuplot-capture-group'. This is
+;;     used to store the plotting style, which we need in order to
+;;     give the correct ElDoc string for "using" clauses, and for
+;;     info keywords (see below)
+;;     
+;;    (info-keyword PATTERN)
+;;     Match PATTERN, and use whatever the value of the first token
+;;     it matches is to look up info pages for this pattern. Most
+;;     Gnuplot info pages have the same name as the keyword they
+;;     document, so by using this we only have to put :info
+;;     properties on the few that don't, such as "set".
+;;  
+;;    For convenience, "many", "maybe", "capture" and "info-keyword"
+;;    wrap the rest of their arguments in an implicit "sequence" form,
+;;    so we can write (maybe "," expression) instead of
+;;    (maybe (sequence "," expression))
+;;
+;;    (delimited-list PATTERN SEPARATOR)
+;;     Match a list of PATTERNs separated by SEPARATOR. Sugar for:
+;;     (sequence PATTERN (many (sequence SEPARATOR PATTERN)))
+;;
+;;   (assert LISP-FORM)
+;;     Evaluate LISP-FORM and fail if it returns NIL. We need this in
+;;     the patterns for "plot" and "splot" to check whether the
+;;     command at point should be parsed in parametric mode or
+;;     not. See `gnuplot-guess-parametric-p'.
+;;
+;;
+;; Bugs, TODOs, etc.
+;; =======================
+;;
+;; It would be useful to complete on user-defined functions and
+;; variables as well as built-ins.
+;;
+;; Completion probably will not work in continuation lines entered
+;; into the gnuplot interaction buffer.
+;;
+;; It would be better to pop up longer syntax descriptions in a
+;; temporary window, rather than making the echo area grow to fit
+;; many lines.
+;;
+;; In ElDoc mode, we parse the whole line every time the user stops
+;; typing. This is wasteful; should cache things in text properties
+;; instead.
+;;
+;; The pattern matching engine uses backtracking, which can take
+;; exponential time. So far it seems "fast enough" in actual use.
+;;
+;; The patterns don't really distinguish between "plot" and "splot"
+;; for things like plot styles, binary arguments, etc.
+;;
+;; Some other the patterns are probably not quite right, especially for
+;; things like abbreviated keywords, and features that I don't use
+;; myself like "fit". Hopefully anyone bothered by this will submit
+;; patches ;-)
+;;
+;; It would be possible to provide more helpful ElDoc strings for
+;; sub-parts of complicated options like "cntrparam". This is a time
+;; and maintenance issue rather than a technical one.
+;;
+;;
+
+(eval-when-compile (require 'cl))
+
+;; Prevent compiler warnings about undefined functions
+(eval-when-compile (require 'gnuplot))
+
+;; We need ElDoc support
+(require 'eldoc)
+
+;; Load external ElDoc strings if we can find them.
+(defvar gnuplot-eldoc-hash nil
+  "ElDoc strings for gnuplot-mode.
+
+These have to be compiled from the Gnuplot source tree using
+`doc2texi.el'.")
+
+(condition-case nil
+    (load-library "gnuplot-eldoc")
+  (error
+   (setq gnuplot-eldoc-hash (make-hash-table))))
+
+
+;;;; Customization interface, etc.
+(defun gnuplot-context-sensitive-mode (&optional enable)
+  "Turn gnuplot-mode context-sensitive completion and help on and off.
+Works like a minor mode: with argument, turn context-sensitive
+mode on if ENABLE is positive, otherwise turn it off. With no
+argument, toggle context-sensitive mode."
+  (interactive "P")
+  (setq gnuplot-context-sensitive-mode
+        (if (null enable) (not gnuplot-context-sensitive-mode)
+            (> (prefix-numeric-value enable) 0)))
+  
+  (if gnuplot-context-sensitive-mode
+      (progn
+        (when (called-interactively-p 'any)
+          (message "Gnuplot context-sensitive help & completion enabled."))
+        (ad-enable-advice 'gnuplot-completion-at-point
+                          'around 'gnuplot-context)
+        (ad-activate 'gnuplot-completion-at-point)
+
+        (dolist (keymap (list gnuplot-mode-map gnuplot-comint-mode-map))
+          (define-key keymap (kbd "C-c M-h") 'gnuplot-help-function)
+          (define-key keymap (kbd "C-c C-/") 'gnuplot-help-function)
+          (define-key keymap (kbd "C-c C-d") 'gnuplot-info-at-point))
+        (define-key gnuplot-comint-mode-map (kbd "TAB") 
'comint-dynamic-complete)
+        
+        (add-hook 'gnuplot-mode-hook 'gnuplot-setup-eldoc)
+        (add-hook 'gnuplot-comint-mode-hook 'gnuplot-setup-eldoc))
+
+    ;; Turn off
+    (when (called-interactively-p 'any)
+      (message "Gnuplot context-sensitive help & completion disabled."))
+    (dolist (keymap (list gnuplot-mode-map gnuplot-comint-mode-map))
+      (define-key keymap (kbd "C-c M-h") 'undefined)
+      (define-key keymap (kbd "C-c C-/") 'undefined)
+      (define-key keymap (kbd "C-c C-d") 'gnuplot-info-lookup-symbol))
+    (ad-disable-advice 'gnuplot-completion-at-point
+                       'around 'gnuplot-context)
+    (ad-activate 'gnuplot-completion-at-point)
+
+    (remove-hook 'gnuplot-mode-hook 'gnuplot-setup-eldoc)
+    (remove-hook 'gnuplot-comint-mode-hook 'gnuplot-setup-eldoc)))
+
+;; Has to be defined here. Grumble.
+(defadvice gnuplot-completion-at-point (around gnuplot-context disable)
+  ;; This around-advice gets activated/deactivated when turning
+  ;; context-sensitivity on and off
+  (setq ad-return-value (gnuplot-context-completion-at-point)))
+
+(defcustom gnuplot-context-sensitive-mode nil
+  "Whether context-sensitive completion and help for gnuplot are enabled.
+
+With context-sensitive mode on, gnuplot-mode's tab completion and
+info file lookup try to parse the current command line to find
+the most useful completions or info pages.
+
+Don't set this variable from Lisp code; instead, use Customize or
+call the `gnuplot-context-sensitive-mode' function, which behaves
+like a minor mode."
+  :group 'gnuplot
+  :type 'boolean
+  :set 'custom-set-minor-mode
+  :link '(emacs-commentary-link "gnuplot-context"))
+
+(defcustom gnuplot-eldoc-mode nil
+  "Whether to enable ElDoc mode by default in Gnuplot buffers.
+ElDoc support requires `gnuplot-context-sensitive-mode' to be
+on."
+  :group 'gnuplot
+  :type 'boolean)
+  
+(defcustom gnuplot-tab-completion nil
+  "Whether the TAB key should perform completion in gnuplot-mode buffers.
+
+Setting this to `t' sets the `tab-always-indent' variable to the
+symbol `complete' in gnuplot-mode buffers."
+  :group 'gnuplot
+  :type 'boolean)
+    
+(defun gnuplot-setup-eldoc ()
+  (set (make-local-variable 'eldoc-documentation-function)
+       'gnuplot-eldoc-function)
+
+  ;; Check for ElDoc after doing completion
+  (eldoc-add-command 'completion-at-point)
+  (eldoc-add-command 'comint-dynamic-complete)
+
+  (when gnuplot-eldoc-mode
+    (eldoc-mode))
+  (when gnuplot-tab-completion
+    (set (make-local-variable 'tab-always-indent) 'complete)))
+
+
+
+;;;; The tokenizer.
+
+(defstruct gnuplot-token
+  start            ; Buffer start position
+  end      ; Buffer end position
+  id       ; Text
+  type)            ; name, number, string, operator, end-of-command
+
+(defvar gnuplot-operator-regexp
+  (eval-when-compile
+    (regexp-opt
+     '("(" ")" "(" ")" "{" "," "}" "[" ":" "]" "!" "**" "-" "+" "~" "!" "*" "/"
+       "%" "+" "-" "." "<" "<=" ">" ">=" "==" "!=" "eq" "ne" "&" "^" "|" "&&" 
"||"
+       "?" ":" "=" "$")))
+  "Regexp to match Gnuplot operators for tokenizing.")
+
+(eval-when-compile
+  (defmacro gnuplot-tokenize-by-regexps (&rest rules)
+    `(cond ,@(mapcar
+             (lambda (rule)
+               (let ((regexp (car rule))
+                     (token-type (cadr rule)))
+                 `((looking-at ,regexp)
+                   (let ((str (match-string-no-properties 0)))
+                     (forward-char (length str))
+                     (make-gnuplot-token :id str
+                                         :type ',token-type
+                                         :start (match-beginning 0)
+                                         :end (match-end 0))))))
+             rules))))
+
+(defun gnuplot-tokenize (&optional completing-p)
+  "Tokenize the Gnuplot command at point. Returns a list of `gnuplot-token' 
objects.
+
+If COMPLETING-P is non-nil, omits the token at point if it is a
+name; otherwise continues tokenizing up to the token at point. FIXME"
+  (let ((tokens '())
+       (stop-point (min (point)
+                        (gnuplot-point-at-end-of-command))))
+    (save-excursion
+      (gnuplot-beginning-of-command)
+      (while
+         ;; Skip whitespace and continuation lines
+         (progn
+           (skip-syntax-forward "-" stop-point)
+           (while (looking-at "\\\\\n")
+             (forward-line)
+             (skip-syntax-forward "-" stop-point))
+           ;; Don't tokenize anything starting after point
+           (and (not (looking-at "#"))
+                (< (point) stop-point)))
+       (let* ((from (point))
+              (token
+               (cond  
+                ((gnuplot-tokenize-by-regexps
+                  ("[A-Za-z_][A-Za-z0-9_]*" name)
+                  
("[0-9]+\\(\\.[0-9]*\\)?\\([eE][+-]?[0-9]+\\)?\\|\\.[0-9]+\\([eE][+-]?[0-9]+\\)?"
 number)
+                  (gnuplot-operator-regexp operator)))
+
+                ((looking-at "['\"]")
+                 (let* ((bounds (bounds-of-thing-at-point 'sexp))
+                        (to (or (cdr bounds) stop-point)))
+                   (goto-char to)
+                   (make-gnuplot-token
+                    :id (buffer-substring-no-properties from to)
+                    :type 'string
+                    :start from :end to)))
+
+                (t (error
+                    "gnuplot-tokenize: bad token beginning %s"
+                    (buffer-substring-no-properties (point) stop-point))))))
+         
+         (push token tokens))))
+    
+    ;; If we are looking for completions, AND if the last token
+    ;; read is a name, AND if point is within the bounds of the
+    ;; last token, then discard it. The matching function
+    ;; generates a list of all possible tokens that could appear
+    ;; in that position for completion.
+    (if (and completing-p
+            tokens
+            (eq (gnuplot-token-type (car tokens)) 'name)
+            (<= (point) (gnuplot-token-end (car tokens))))
+       (pop tokens))
+    
+    (nreverse tokens)))
+
+
+
+;;;; The pattern and grammar compiler
+;;
+;; These functions compile the source S-expression grammar into a
+;; vector of instructions for the parsing machine,
+;; `gnuplot-match-pattern'. Its state consists of a program counter
+;; (PC), a position in the list of tokens, a call stack, and a second
+;; stack of backtracking entries (continuations). Its "machine
+;; instructions" are the following:
+;;
+;;    (any)
+;;     Match any token (fails only at end of command).
+;;
+;;    (literal LITERAL NO-COMPLETE)
+;;     Match token with `gnuplot-token-id' LITERAL or fail. If we
+;;     have reached the token before point, include LITERAL in the
+;;     completion list unless NO-COMPLETE is non-`nil'.
+;;
+;;    (token-type TYPE)
+;;     Match a token with `gnuplot-token-type' TYPE, or fail.
+;;
+;;    (keyword REGEXP NAME)
+;;     Match any token whose `gnuplot-token-id' matches REGEXP. Use
+;;     NAME for the completion list.
+;;
+;;    (jump OFFSET FIXED)
+;;     Jump to (set PC to) OFFSET if FIXED is non-nil, otherwise to
+;;     PC + OFFSET
+;;
+;;    (call OFFSET FIXED) 
+;;     Like "jump", but push a return address onto the stack for
+;;     (return). (The compiler adds the name of the rule being called
+;;     as a fourth element on the end of the list, but this is just a
+;;     comment for debugging purposes).
+;;
+;;    (return) 
+;;     Return to the PC address on top of the stack, or finish
+;;     matching if stack is empty. (Usually this doesn't happen,
+;;     because the machine stops as soon as it gets to the token at
+;;     point).
+;;
+;;    (choice OFFSET)
+;;     Push a backtracking entry for location PC + OFFSET onto the
+;;     backtracking stack. Backtracking entries save the contents of
+;;     the call stack, position in the token list, the values of
+;;     capture groups, and the record of loop progress (see below).
+;;
+;;    (check-progress)
+;;      Break out of infinite loops, like (many (many ...)).  Checks
+;;      an alist of conses (pc . tokens) for the position in the token
+;;      stream the last time this instruction was reached, and breaks
+;;      out of the loop if stuck in the same place; otherwise pushes a
+;;      new entry onto the list.
+;;
+;;    (fail)
+;;     Pop the most recent backtracking entry and continue from
+;;     there, or fail the whole match if out of backtrack
+;;     points. Failing to match returns the remainder of the token
+;;     list, although we don't currently use this for anything.
+;;
+;;    (commit OFFSET)
+;;     Discard one backtracking point and jump to PC + OFFSET. This
+;;     is used to make the (either) form non-deterministic.
+;;
+;;    (push TYPE VALUE)
+;;     Push an entry for an eldoc or info string (specified by TYPE)
+;;     onto the stack.
+;;
+;;    (pop TYPE)
+;;     Pop something off the stack; checks that it has the expected
+;;     TYPE, for safety.
+;;
+;;    (save-start NAME)
+;;     Open a capture group named NAME. Pushes an entry onto
+;;     `gnuplot-captures' with current position in token list as the
+;;     start of the group.
+;;
+;;    (save-end NAME)
+;;     Close the capture group named NAME. Finds the topmost entry in
+;;     `gnuplot-captures' with this name and sets its endpoint to the
+;;     current position in token list. Error if no group with that
+;;     name is found.
+;;
+;;    (label NAME)
+;;     This should never be reached and will cause an error. The
+;;     compiler inserts it at the beginning of compiled rules only
+;;     for debugging purposes.
+;;
+
+
+(eval-when-compile
+  ;; Compile a single pattern into a list of instructions. Leaves
+  ;; calls to other rules as symbolic instructions (call SYMBOL) and
+  ;; jumps, commits etc. as relative offsets; these are resolved into
+  ;; absolute locations by `gnuplot-compile-grammar', below.
+  (defun gnuplot-compile-pattern (pat)
+    (cond
+     ;; Strings match a single token literally
+     ((stringp pat)
+      ;; Don't add non-words to completion lists
+      (let ((wordp (string-match-p "^\\sw\\(\\sw\\|\\s_\\)*$" pat)))
+       `((literal ,pat ,(not wordp)))))
+     
+     ;; Symbols match token types or calls to other patterns
+     ((symbolp pat)
+      (case pat
+       ((any) `((any)))
+       ((name number string) `((token-type ,pat)))
+       (t `((call ,pat)))))
+     
+     ;; Syntactic sugar: write sequences (sequence ...) as vectors [...]
+     ((vectorp pat)
+      (gnuplot-compile-pattern
+       (append '(sequence) pat '())))
+
+     ;; Other forms combine simpler patterns
+     (t
+      (let ((type (car pat)))
+       (case type
+         ;; (sequence...): concatenate patterns, with optional eldoc
+         ;; and info strings
+         ((sequence)
+          (destructuring-bind
+              (subpats eldoc info)
+              (gnuplot-filter-arg-list (cdr pat))
+            (let ((eldoc-push '()) (eldoc-pop '())
+                  (info-push '()) (info-pop '())
+                  (compiled
+                   (mapcar 'gnuplot-compile-pattern subpats)))
+              (if eldoc
+                  (setq eldoc-push `((push eldoc ,eldoc))
+                        eldoc-pop `((pop eldoc))))
+              (if info
+                  (if (eq info :no-info)
+                      (setq info-push '((push no-scan t))
+                            info-pop '((pop no-scan)))
+                    (setq info-push `((push info ,info))
+                          info-pop `((pop info)))))
+              (apply 'append
+                     `(,info-push
+                       ,eldoc-push
+                       ,@compiled
+                       ,eldoc-pop
+                       ,info-pop)))))
+
+         ;; (either...): choose between patterns
+         ((either)
+          (cond
+           ((= (length pat) 2)         ; trivial case
+            (gnuplot-compile-pattern (cadr pat)))
+
+           ((> (length pat) 3)         ; could be more efficient...
+            (gnuplot-compile-pattern (gnuplot-either-helper pat)))
+
+           (t                          ; two patterns
+            (let* ((pat1 (cadr pat))
+                   (pat2 (caddr pat))
+                   (pat1-c (gnuplot-compile-pattern pat1))
+                   (pat2-c (gnuplot-compile-pattern pat2))
+                   (pat1-l (length pat1-c))
+                   (pat2-l (length pat2-c)))
+              `((choice ,(+ pat1-l 2))
+                ,@pat1-c
+                (commit ,(+ pat2-l 1))
+                ,@pat2-c)))))
+
+         ;; Repetition (*)
+         ((many)
+          (let* ((pat1 (cons 'sequence (cdr pat)))
+                 (pat1-c (gnuplot-compile-pattern pat1))
+                 (pat1-l (length pat1-c)))
+            `((choice ,(+ pat1-l 3))
+              (check-progress)         ; bail out of infinite loops
+              ,@pat1-c
+              (commit ,(- (+ pat1-l 2))))))
+
+         ;; Repetition (+)
+         ((many1)
+          (let* ((pat1 (cdr pat)))
+            (gnuplot-compile-pattern
+             `(sequence ,@pat1 (many ,@pat1)))))
+                 
+
+         ;; Optional (?)
+         ((maybe)
+          (let* ((pat1 (cons 'sequence (cdr pat)))
+                 (pat1-c (gnuplot-compile-pattern pat1))
+                 (pat1-l (length pat1-c)))
+            `((choice ,(+ pat1-l 1))
+              ,@pat1-c)))
+
+         ;; Syntactic sugar for delimited lists
+         ((delimited-list)
+          (let* ((item (cadr pat))
+                 (sep (caddr pat)))
+            (gnuplot-compile-pattern
+             `(sequence ,item (many (sequence ,sep ,item))))))
+
+         ;; keywords
+         ((kw)
+          (destructuring-bind (regex name)
+              (gnuplot-keyword-helper (cdr pat))
+            `((keyword ,regex ,name))))
+
+         ;; Capturing groups
+         ((capture)
+          (let* ((name (cadr pat))
+                 (pat1 (cons 'sequence (cddr pat)))
+                 (pat1-c (gnuplot-compile-pattern pat1)))
+            `((save-start ,name)
+              ,@pat1-c
+              (save-end ,name))))
+         
+         ;; Use the first token as an info keyword
+         ((info-keyword)
+          (let* ((pat1 (cons 'sequence (cdr pat)))
+                 (pat1-c (gnuplot-compile-pattern pat1)))
+            `((push info first-token)
+              ,@pat1-c
+              (pop info))))
+
+         ;; Assertions
+         ((assert)
+          (let* ((form (cadr pat)))
+            `((assert ,form))))
+
+         (t
+          (error "gnuplot-compile-pattern: bad pattern form %s" pat)))))))
+
+  ;; Helper function for destructuring (sequence ...) forms in patterns
+  ;; Takes the cdr of the sequence form, returns a list (PATTERNS ELDOC
+  ;; INFO).
+  (defun gnuplot-filter-arg-list (args)  
+    (let ((accum '())
+         (eldoc nil) (info nil))
+      (dolist (item args)
+       (let ((type (car-safe item)))
+         (case type
+           ((:eldoc) (setq eldoc (cadr item)))
+           ((:no-info) (setq info :no-info)) ; inhibit stack scanning
+           ((:info) (setq info (cadr item)))
+           (t (push item accum)))))
+      (list (reverse accum) eldoc info)))
+
+  ;; Helper function for compiling (kw...) patterns
+  ;; Takes the cdr of the kw form, returns a list (REGEXP KEYWORD)
+  (defun gnuplot-keyword-helper (args)
+    (let ((keyword (car args)) (aliases (cdr args)))
+      (when (consp keyword)
+       (let ((pre (car keyword)) (suf (cdr keyword)))
+         (setq keyword (concat pre suf))
+         (while (progn
+                  (push pre aliases)
+                  (not (zerop (length suf))))
+           (setq pre (concat pre (substring suf 0 1))
+                 suf (substring suf 1)))))
+      (let ((regex
+            (concat "^"
+                    (regexp-opt (cons keyword aliases))
+                    "$")))
+       (list regex keyword))))
+
+  ;; Helper function for compiling (either ...) patterns. Rewrites
+  ;; alternates (either A B C) into (either A (either B (either C D)))
+  (defun gnuplot-either-helper (pat)
+    (if (= (length pat) 3)
+       pat
+      `(either ,(cadr pat)
+              ,(gnuplot-either-helper
+                (cons 'either (cddr pat))))))
+  
+  ;; Compile the grammar (a list of rule-pattern pairs (RULE PATTERN))
+  ;; into a single vector of matching-machine instructions. Compiles
+  ;; each pattern individually, then "links" them into one vector,
+  ;; converting symbolic (call ...) instructions into numeric offsets
+  (defun gnuplot-compile-grammar (grammar start-symbol)
+    (let ((compiled-pats '())        ; Alist of (name . instructions)
+         ;; Reserve space for a jump to the start symbol
+         (code-length 1))
+
+      ;; Compile each rule and find the total number of instructions
+      (dolist (item grammar)
+       (let* ((name (car item))
+              (pat (cadr item))
+              (code (gnuplot-compile-pattern pat)))
+         (push (cons name code) compiled-pats)
+         ;; Reserve space for a label at the beginning and (return) at
+         ;; the end
+         (setq code-length (+ code-length 2 (length code)))))
+
+      ;; Copy instructions into a single vector
+      (let ((object-code (make-vector code-length nil))
+           (name->offset (make-hash-table))
+           (i 1))
+       (setf (aref object-code 0) `(jump ,start-symbol))
+       (dolist (chunk compiled-pats)
+         (let ((name (car chunk))
+               (code (cdr chunk)))
+           (setf (aref object-code i) `(label ,name))
+           (incf i)
+           (puthash name i name->offset)
+           (while code
+             (setf (aref object-code i) (car code)
+                   code (cdr code)
+                   i (1+ i)))
+           (setf (aref object-code i) '(return)
+                 i (1+ i))))
+
+       ;; Resolve symbolic and relative jumps 
+       (let ((pattern-name nil))
+         (dotimes (i (length object-code))
+           (let ((inst (aref object-code i)))
+             (case (car inst)
+               ((label)
+                (setq pattern-name (cadr inst)))
+
+               ((jump call choice commit)
+                (cond
+                 ((symbolp (cadr inst))
+                  (let* ((name (cadr inst))
+                         (location (gethash name name->offset)))
+                    (if (not location)
+                        (error
+                         (concat "gnuplot-compile-grammar: "
+                                 "No rule found for symbol `%s' in pattern 
`%s'")
+                         name pattern-name))
+                    (setcdr inst `(,location ,name))))
+
+                 ((numberp (cadr inst))
+                  (let* ((offset (cadr inst))
+                         (location (+ offset i)))
+                    (setcdr inst `(,location))))
+
+                 (t
+                  (error "gnuplot-compile-grammar: bad instruction %s" 
inst))))))))
+       object-code))))
+
+;;; The grammar.
+(defvar gnuplot-compiled-grammar
+  (eval-when-compile
+    (let ((max-lisp-eval-depth 600))
+      (gnuplot-compile-grammar
+       '((expression
+         [infix-expression (maybe "?" expression ":" expression)])
+        
+        (prefix-operator
+         (either "!" "~" "-" "+"))
+
+        (infix-operator
+         (either "**" "*" "/" "%" "+" "-" "." "<" "<=" ">" ">=" "==" "!=" "eq" 
"ne"
+                 "&" "^" "|" "&&" "||"))
+
+        (infix-expression
+         [(many prefix-operator)
+          primary-expression
+          (many infix-operator expression)])
+
+        (primary-expression
+         [(either number string parenthesized-expression
+                  column-ref complex-number function-call name)
+          (many "!")
+          (maybe "**" infix-expression)
+          (maybe substring-range)])
+
+        (function-call
+         (either
+          (info-keyword
+           [(either "abs" "acos" "acosh" "arg" "asin" "asinh" "atan" "atan2" 
"atanh"
+                    "besj0" "besj1" "besy0" "besy1" "ceil" "column" 
"columnhead"
+                    "cos" "cosh" "defined" "erf" "erfc" "exists" "exp" "floor"
+                    "gamma" "gprintf" "ibeta" "igamma" "imag" "int" "inverf"
+                    "invnorm" "lambertw" "lgamma" "log" "log10" "norm" "real"
+                    "sgn" "sin" "sinh" "sprintf" "sqrt" "strftime" 
"stringcolumn"
+                    "strlen" "strptime" "strstrt" "substr" "tan" "tanh" 
"timecolumn"
+                    "tm_hour" "tm_mday" "tm_min" "tm_mon" "tm_sec" "tm_wday"
+                    "tm_yday" "tm_year" "valid" "value" "word" "words" "rand")
+            parenthesized-expression])
+          [(:info "elliptic_integrals")
+           (either "EllipticK" "EllipticE" "EllipticPi")
+           parenthesized-expression]
+          [name
+           parenthesized-expression]))
+
+        (parenthesized-expression
+         ["(" comma-list ")"])
+
+        (complex-number
+         ["{" (maybe "-") number "," (maybe "-") number "}"])
+
+        (column-ref
+         ["$" number])
+
+        (substring-range-component
+         (maybe (either "*" expression)))
+
+        (substring-range
+         ["[" (delimited-list substring-range-component ":" 2 2) "]"])
+
+;;; Assignments
+        (lhs
+         [name (maybe "(" (delimited-list name "," 1) ")")])
+
+        (assignment
+         [lhs "=" (either assignment expression)])
+
+;;; Lists of expressions
+        (comma-list
+         (delimited-list (either assignment expression) ","))
+
+        (colon-list
+         (delimited-list expression ":"))
+
+        (tuple
+         ["(" (delimited-list expression "," 2 3) ")"])
+
+;;; Commands
+        (command
+         (info-keyword
+          (either plot-command splot-command replot-command fit-command 
print-command
+                  set-command cd-command call-command simple-command
+                  eval-command load-command lower-raise-command pause-command
+                  save-command system-command test-command undefine-command
+                  update-command assignment)))
+
+;;; PLOT, SPLOT commands
+        (plot-command
+         [(kw ("pl" . "ot"))
+          
+          (either
+           ;; Parametric ranges
+           [(assert (gnuplot-guess-parametric-p))
+            (maybe t-axis-range) (maybe x-axis-range) (maybe y-axis-range)]
+
+           ;; Non-parametric ranges
+           [(maybe x-axis-range) (maybe y-axis-range)])
+
+          plot-body])
+
+        (splot-command
+         [ ;; This capturing group lets `gnuplot-find-using-eldoc' know
+          ;; that this is an splot command
+          (capture :splot-command (kw ("spl" . "ot")))
+
+          (either
+           ;; Parametric ranges
+           [(assert (gnuplot-guess-parametric-p))
+            (maybe u-axis-range) (maybe v-axis-range)
+            (maybe x-axis-range) (maybe y-axis-range) (maybe z-axis-range)]
+
+           ;; Non-parametric ranges
+           [(maybe x-axis-range) (maybe y-axis-range) (maybe z-axis-range)])
+
+          plot-body])
+
+         (replot-command [(kw "replot") plot-body])
+        
+        ;; Axis ranges
+        (axis-range-component
+         (maybe (either "*" expression)))
+
+        (axis-range-body
+         (delimited-list axis-range-component ":" 2 3))
+        
+        (axis-range
+         [(:info "ranges")
+          "[" (maybe (maybe name "=") axis-range-body) "]"])
+        
+        (x-axis-range [(:eldoc "X RANGE: [{<dummy>=}<min>:<max>]") axis-range])
+        (y-axis-range [(:eldoc "Y RANGE: [{<dummy>=}<min>:<max>]") axis-range])
+        (z-axis-range [(:eldoc "Z RANGE: [{<dummy>=}<min>:<max>]") axis-range])
+        (t-axis-range [(:eldoc "T RANGE: [{<dummy>=}<min>:<max>]") axis-range])
+        (u-axis-range [(:eldoc "U RANGE: [{<dummy>=}<min>:<max>]") axis-range])
+        (v-axis-range [(:eldoc "V RANGE: [{<dummy>=}<min>:<max>]") axis-range])
+
+        ;; Body of a plot/splot command. Should really be different for
+        ;; parametric vs non-parametric, but that's too hard.
+        (plot-body
+         (delimited-list
+          [(maybe iteration-spec) plot-expression plot-modifiers]
+          ","))
+
+        ;; Iteration: for [... ]
+        (iteration-spec
+         [(:info "iteration")
+          "for" "[" name
+          (either ["=" (delimited-list expression ":")]
+                  ["in" expression])
+          "]"])
+        
+        ;; Expressions to plot can be preceded by any number of
+        ;; assignments, with or without commas
+        (plot-expression
+         [(many [(:no-info) assignment (maybe ",")])
+          expression])
+
+;;; Plot/splot modifiers
+        ;; These should probably be more different for plot and splot ...
+        (plot-modifiers (many (either plot-modifier datafile-modifier)))
+
+        (plot-modifier
+         (info-keyword
+          (either
+           ;; simple one-word modifiers
+           (kw "nohidden3d") (kw "nocontours") (kw "nosurface")
+           
+           ;; word followed by expression
+           [(either
+             (kw ("lines" . "tyle") "ls")
+             (kw ("linet" . "ype") "lt")
+             (kw ("linew" . "idth") "lw")
+             (kw ("pointt" . "ype") "pt")
+             (kw ("points" . "ize") "ps")
+             (kw ("pointi" . "nterval") "pi"))
+            expression]
+
+           ;; others defined below
+           title-modifier notitle-modifier axes-modifier with-modifier
+           linecolor-modifier fillstyle-modifier)))
+
+        (title-modifier
+         [(kw ("t" . "itle")) expression])
+        
+        (notitle-modifier
+         [(:info "title")
+          (kw ("not" . "itle"))
+          (maybe string)])
+
+        (axes-modifier
+         [(kw ("ax" . "es")) (either "x1y1" "x1y2" "x2y1" "x2y2")])
+
+        (linecolor-modifier
+         [(kw ("linec" . "olor") "lc") color-spec])
+
+        (fillstyle-modifier
+         [(kw "fillstyle" "fs")
+          ;; fill-style also used by "set style fill"
+          fill-style])
+        
+        (fill-style
+         [(either
+           "empty"
+           [(maybe "transparent")
+            (either "pattern" "solid")
+            (maybe (either fill-style-border-clause expression))])
+          (maybe fill-style-border-clause)])
+
+        (fill-style-border-clause
+         (either "noborder" [(kw ("bo" . "rder")) expression]))
+
+        (color-spec
+         [(:info "colorspec")
+          (either
+           (kw ("var" . "iable"))
+
+           [(kw ("pal" . "ette"))
+            (either "z"
+                    [(either "frac" "cb") expression])]
+
+           [(kw ("rgb" . "color"))
+            (either (kw ("var" . "iable")) string)])])
+
+        (with-modifier
+         [(:info "plotting_styles")
+          (kw ("w" . "ith"))
+          
+          ;; plotting-style also used for "set style data"
+          (capture :with-style plotting-style)])
+
+        (plotting-style
+         (info-keyword
+          (either
+           ;; Simple styles that take no arguments
+           (kw ("l" . "ines")) (kw ("i" . "mpulses")) (kw ("p" . "oints"))
+           (kw ("linesp" . "oints") "lp") (kw ("d" . "ots")) (kw ("yerrorl" . 
"ines"))
+           (kw ("errorl" . "ines")) (kw ("xerrorl" . "ines")) (kw ("xyerrorl" 
. "ines"))
+           (kw ("ye" . "rrorbars")) (kw ("e" . "rrorbars")) (kw ("xe" . 
"rrorbars"))
+           (kw ("xye" . "rrorbars")) (kw "boxes") (kw ("hist" . "ograms"))
+           (kw ("boxer" . "rorbars")) (kw ("boxx" . "yerrorbars")) (kw ("st" . 
"eps"))
+           (kw ("fs" . "teps")) (kw ("his" . "teps")) (kw ("fin" . "ancebars"))
+           (kw ("can" . "dlesticks")) (kw ("pm" . "3d"))
+           (kw ("cir" . "cles"))
+           
+           ;; Image styles all use the same info page
+           [(:info "image")
+            (either (kw ("ima" . "ge"))
+                    (kw ("rgbima" . "ge"))
+                    (kw ("rgba" . "lpha")))]
+           
+           ;; More complicated styles defined below
+           labels-style-clause
+           filledcurves-style-clause
+           vectors-style-clause)))
+
+        (labels-style-clause
+         [(kw "labels")
+          (maybe textcolor-spec)])
+          
+        (filledcurves-style-clause
+         [(kw ("filledc" . "urves"))
+          (maybe
+           (either
+            "closed"
+            
+            ["xy" "=" expression "," expression]
+
+            [(maybe (either "above" "below"))
+             (maybe [(either "x1" "x2" "y1" "y2")
+                     (maybe "=" expression)])]))])
+
+        (vectors-style-clause
+         [(kw ("vec" . "tors"))
+          (many
+           (either
+            "nohead" "head" "heads" "filled" "empty" "nofilled" "front" "back"
+            [(kw "arrowstyle" "as") expression]
+            ["size" (delimited-list expression ",")]
+            linestyle-spec))])
+
+;;; Various style specifiers, used in different places
+        (linestyle-spec
+         (many1
+          (either
+           [(kw ("lines" . "tyle") "ls") expression]
+           [(kw ("linet" . "ype") "lt") expression]
+           [(kw ("linew" . "idth") "lw") expression])))
+        
+        (textcolor-spec
+         [(kw "textcolor" "tc")
+          (either "default"
+                  ["lt" expression]
+                  color-spec)])
+
+        (pointsize-spec [(kw "pointsize" "ps") expression])
+
+;;; Datafile modifiers
+        (datafile-modifier
+         (info-keyword
+          (either binary-modifier
+                  [(maybe "nonuniform") (kw ("mat" . "rix"))]
+                  index-modifier every-modifier
+                  thru-modifier using-modifier
+                  smooth-modifier
+                  "volatile" "noautoscale")))
+        
+        (index-modifier
+         [(kw ("i" . "ndex"))
+          (either string (delimited-list expression ":" 0 2))])
+
+        (every-modifier
+         [(kw ("ev" . "ery")) (delimited-list (maybe expression) ":")])
+        
+        (thru-modifier
+         [(kw "thru") expression])
+        
+        (using-modifier
+         [(:eldoc gnuplot-find-using-eldoc) 
+          (kw ("u" . "sing"))
+          (either
+           string
+           [colon-list (maybe string)])])
+
+        (smooth-modifier
+         [(kw ("s" . "mooth"))
+          (either (kw ("a" . "csplines")) (kw ("b" . "ezier")) (kw ("c" . 
"splines"))
+                  (kw ("s" . "bezier")) (kw ("u" . "nique")) (kw ("f" . 
"requency"))
+                  (kw ("cum" . "ulative")) (kw ("k" . "density")))])
+
+;;; Binary datafile modifiers
+        (binary-modifier 
+         ["binary" (many binary-keyword)])
+
+        (binary-keyword
+         (either
+          ;; All of these binary keywords are described on the same
+          ;; info page
+          [(:info "keywords")
+           (either
+            "transpose" "flipx" "flipy" "flipz"
+            ["flip" "=" (either "x" "y" "z")]
+            ["scan" "=" name]
+            [(either "dx" "dy" "dz") "=" number]
+            [(either "origin" "center" "perpendicular") "="
+             (delimited-list tuple ":")]
+            [(kw ("rot" . "ate") "rotation") "="
+             (sequence expression (maybe (kw ("d" . "eg")) (kw ("p" . 
"i"))))])]
+
+          ;; remaining binary keywords have their own info pages
+          (info-keyword
+           (either
+            [(either "array" "record")
+             "="
+             (delimited-list expression ":")]
+
+            [(either "skip")
+             "="
+             (delimited-list expression ":")]
+           
+            [(either "format" "endian" "filetype")
+             "="
+             expression]))))
+        
+;;; "fit" command
+        (fit-command
+         [(:info "fit")
+          (kw "fit")
+          (many axis-range)
+          expression
+          string
+          (many plot-modifier)
+          (kw "via")
+          (either string (delimited-list name ","))])
+
+;;; print command
+        (print-command
+         [(kw ("pr" . "int")) (delimited-list expression ",")])
+
+;;; set commands
+        (set-command
+         [(:eldoc "set ...")
+          (:info "set-show")
+          (either (kw "set") (kw "unset") (kw "show"))
+          (maybe iteration-spec)
+          (info-keyword
+           (either set-angles-clause set-arrow-clause
+                   set-autoscale-clause set-bars-clause
+                   set-border-clause set-boxwidth-clause
+                   set-clabel-clause set-clip-clause
+                   set-cntrparam-clause set-colorbox-clause
+                   set-contour-clause set-datafile-clause
+                   set-decimalsign-clause set-dgrid3d-clause
+                   set-dummy-clause set-encoding-clause
+                   set-fit-clause set-fontpath-clause
+                   set-format-clause set-grid-clause
+                   set-hidden3d-clause set-historysize-clause
+                   set-isosamples-clause set-key-clause
+                   set-label-clause set-loadpath-clause
+                   set-locale-clause set-logscale-clause
+                   set-mapping-clause set-margin-clause
+                   set-multiplot-clause set-mxtics-clause
+                   set-object-clause set-offsets-clause
+                   set-origin-clause set-output-clause
+                   set-parametric-clause set-pm3d-clause
+                   set-palette-clause set-pointsize-clause
+                   set-polar-clause set-print-clause
+                   set-samples-clause set-size-clause
+                   set-style-clause
+                   set-surface-clause set-table-clause
+                   set-terminal-clause set-termoption-clause
+                   set-tics-clause set-tics-clause-2
+                   set-xtics-clause
+                   set-timestamp-clause set-timefmt-clause
+                   set-title-clause set-view-clause
+                   set-data-clause set-dtics-clause
+                   set-xlabel-clause
+                   set-mtics-clause set-range-clause
+                   set-xyplane-clause set-zero-clause
+                   set-zeroaxis-clause))])
+        
+;;; positions and coordinate systems for set options
+        (position-system
+         (either "first" "second" "graph" "screen" "character"))
+
+        (dimension [(maybe position-system) expression])
+        
+        (position
+         [dimension "," dimension (maybe "," dimension)])
+        
+        (to (either "to" "rto"))
+        
+;;; all the different "set ... " options
+        (set-angles-clause
+         ["angles" (either "degrees" "radians")])
+        
+        (set-arrow-clause
+         ["arrow" (maybe number) 
+          (many
+           (either ["from" position] [to position]
+                   [(kw "arrowstyle" "as") expression]
+                   "nohead" "head" "backhead" "heads"
+                   ["size" dimension "," expression (maybe "," expression)]
+                   "filled" "empty" "nofilled" "front" "back"
+                   linecolor-modifier linestyle-spec))])
+
+        (set-autoscale-clause
+         ["autoscale"
+          (either "fix"
+                  "keepfix"
+                  "x" "y" "z" "cb" "x2" "y2" "xy"
+                  "xmin" "ymin" "zmin" "cbmin" "x2min" "y2min" 
+                  "xmax" "ymax" "zmax" "cbmax" "x2max" "y2max" 
+                  "xfix" "yfix" "zfix" "cbfix" "x2fix" "y2fix"
+                  "xfixmax" "yfixmax" "zfixmax" "cbfixmax" "x2fixmax" 
"y2fixmax"
+                  "xfixmin" "yfixmin" "zfixmin" "cbfixmin" "x2fixmin" 
"y2fixmin")])
+
+        (set-bars-clause
+         ["bars"
+          (either expression "small" "large" "fullwidth")
+          (either "front" "back")])
+
+        (set-border-clause
+         ["border"
+          (maybe number)
+          (maybe (either "front" "back"))
+          (maybe (kw "linewidth" "lw") expression)
+          (maybe
+           (either (kw "linestyle" "ls") (kw "linetype" "lt"))
+           expression)])
+
+        (set-boxwidth-clause
+         ["boxwidth"
+          (maybe expression)
+          (maybe (either (kw ("abs" . "olute")) "relative"))])
+
+        (set-clabel-clause
+         ["clabel" (maybe string)])
+
+        (set-clip-clause
+         ["clip" (maybe (either "points" "one" "two"))])
+
+        (set-cntrparam-clause
+         [(kw "cntrparam")
+          (either
+           "linear" "cubicspline" "bspline"
+           
+           [(either "points" "order") number]
+           
+           [(kw "levels")
+            (either
+             number
+             (sequence (kw "auto") (maybe number))
+             (sequence
+              (kw "discrete") comma-list)
+             (sequence
+              (kw "incremental") (delimited-list expression "," 2 3)))])])
+
+        (set-colorbox-clause
+         [(:info "color_box")
+          (kw ("colorb" . "ox"))
+          (many
+           (either
+            (kw ("vert" . "ical")) (kw ("horiz" . "ontal"))
+            "default" "user"
+            ["origin" expression "," expression]
+            ["size" expression "," expression]
+            "front" "back"
+            "noborder" "bdefault"
+            ["border" expression]))])
+
+        (set-contour-clause
+         ["contour" (either "base" "surface" "both")])
+
+        (set-datafile-clause
+         ["datafile"
+          (either [(:info "set_datafile_fortran")
+                   "fortran"]
+                  [(:info "set_datafile_nofpe_trap")
+                   "nofpe_trap"]
+                  [(:info "set_datafile_missing")
+                   "missing" (maybe string)]
+                  [(:info "set_datafile_separator")
+                   "separator" (either "whitespace" string)]
+                  [(:info "set_datafile_commentschars")
+                   "commentschars" (maybe string)]
+                  [(:info "set_datafile_binary")
+                   "binary" (many binary-keyword)])])
+
+        (set-decimalsign-clause
+         ["decimalsign"
+          (either string ["locale" (maybe string)])])
+
+        (set-dgrid3d-clause
+         ["dgrid3d"
+          (maybe expression)           ; fixme
+          (maybe "," expression)
+          (either
+           "splines"
+           ["qnorm" expression]
+           [(either "gauss" "cauchy" "exp" "box" "hann")
+            (maybe expression)
+            (maybe "," expression)])])
+
+        (set-dummy-clause
+         ["dummy"
+          name (maybe "," name)])
+
+        (set-encoding-clause
+         ["encoding"
+          (either "default" "iso_8859_1" "iso_8859_15" "iso_8859_2" 
"iso_8859_9"
+                  "koi8r" "koi8u" "cp437" "cp850" "cp852" "cp1250" "cp1251" 
"cp1254"
+                  "utf8" "locale")])
+
+        (set-fit-clause
+         [(:info "fit_")
+          "fit"
+          (either
+           ["logfile" string]
+           "errorvariables" "noerrorvariables")])
+
+        (set-fontpath-clause
+         ["fontpath" (many string)])
+
+        (set-format-clause
+         [(:info "format_")
+          "format"
+          (maybe (either "x" "y" "xy" "x2" "y2" "z" "cb"))
+          string])
+
+        (set-grid-clause
+         ["grid"
+          (either "nomxtics" "mxtics" "noxtics" "xtics" "nomytics" "mytics"
+                  "noytics" "ytics" "nomztics" "mztics" "noztics" "ztics"
+                  "nomx2tics" "mx2tics" "nox2tics" "x2tics" "nomy2tics"
+                  "my2tics" "noy2tics" "y2tics" "nomcbtics" "mcbtics"
+                  "nocbtics" "cbtics" "layerdefault" "front" "back"
+                  [linestyle-spec (maybe "," linestyle-spec)])])
+
+        (set-hidden3d-clause
+         [(kw ("hidden" . "3d"))
+          (many
+           (either
+            "defaults" "front" "back"
+            ["offset" expression] "nooffset"
+            ["trianglepattern"
+             (either "0" "1" "2" "3" "4" "5" "6" "7")]
+            ["undefined" (either "1" "2" "3")]
+            ["noundefined"]
+            "altdiagonal" "noaltdiagonal"
+            "bentover" "nobentover"))])
+        
+        (set-historysize-clause
+         ["historysize" number])
+
+        (set-isosamples-clause
+         [(kw ("isosam" . "ples")) number (maybe "," number)])
+
+        (set-key-clause
+         ["key"
+          (many
+           (either "on" "off" "default"
+                   [(either "inside" "outside")
+                    (either "lmargin" "rmargin" "tmargin" "bmargin")]
+                   ["at" expression "," expression]
+                   "left" "right" "center" "top" "bottom" "vertical"
+                   "horizontal" "Left" "Right" "reverse" "noreverse" "invert"
+                   "noinvert" "above" "over" "below" "under"
+                   ["samplen" number]
+                   ["spacing" number]
+                   ["width" number]
+                   [(either "autotitle" "noautotitle") (maybe "columnheader")]
+                   ["title" expression] "enhanced" "noenhanced" ["font" string]
+                   textcolor-spec
+                   [(either "box" "nobox") linestyle-spec]
+                   ["maxcols" (either expression "auto")]
+                   ["maxrows" (either expression "auto")]))])
+
+        (set-label-clause
+         ["label"
+          (maybe number)
+          (either label-clause-component expression)
+          (many label-clause-component)])
+
+        (label-clause-component
+         (either
+          ["at" position]
+          "left" "center" "right"
+          (either "norotate" ["rotate" "by" expression])
+          ["font" string]
+          "noenhanced"
+          "front" "back"
+          textcolor-spec
+          "nopoint" ["point" (many (either pointsize-spec linestyle-spec))]
+          ["offset" position]))
+
+        (set-loadpath-clause
+         ["loadpath" (many string)])
+
+        (set-locale-clause
+         ["locale" (maybe string)])
+
+        (set-logscale-clause
+         ["logscale"
+          (either "x" "y" "xy" "x2" "y2" "z" "cb" name)])
+
+        (set-mapping-clause
+         ["mapping" (either "cartesian" "spherical" "cylindrical")])
+
+        (set-margin-clause
+         [(either "bmargin" "lmargin" "rmargin" "tmargin")
+          (maybe "at" "screen") expression])
+
+        ;; TODO: set-mouse-clause
+
+        (set-multiplot-clause
+         ["multiplot"
+          (maybe
+           ["layout" number "," number
+            (maybe (either "rowsfirst" "columnsfirst"))
+            (maybe (either "downwards" "upwards"))
+            (maybe "title" string)
+            (maybe "scale" number (maybe "," number))
+            (maybe "offset" number (maybe "," number))])])
+
+        (set-mxtics-clause
+         [(:info "mxtics")
+          (either "mxtics" "mytics" "mztics" "mx2tics" "my2tics" "mcbtics")
+          (either "default" number)])
+
+        ;; "set object", objects, dimensions, positions
+        (set-object-clause
+         ["object"
+          (maybe number)
+          (info-keyword
+           (either rectangle-object ellipse-object circle-object 
polygon-object))
+          (maybe (either "front" "back" "behind"))
+          (maybe (kw "fillcolor" "fc") color-spec)
+          (maybe "fs" expression)
+          (maybe "default")
+          (maybe (kw "linewidth" "lw") expression)])
+        
+        (rectangle-object
+         [(kw ("rect" . "angle"))
+          (maybe
+           (either
+            ["from" position (either "to" "rto") position]
+            ["center" position "size" dimension "," dimension]
+            ["at" position "size" dimension "," dimension]))])
+
+        (ellipse-object
+         ["ellipse"
+          (either "at" "center") position
+          "size" dimension "," dimension
+          (maybe "angle" number)])
+
+        (circle-object
+         ["circle"
+          (either "at" "center") position
+          "size" dimension
+          (maybe "arc" "[" number ":" number "]")])
+
+        (polygon-object
+         ["polygon"
+          "from" position (many (either "to" "rto") position)])
+
+        ;; "set offsets"
+        (set-offsets-clause
+         ["offsets"
+          (delimited-list [(maybe "graph") expression] "," 4 4)])
+
+        (set-origin-clause
+         ["origin" expression "," expression])
+
+        (set-output-clause
+         ["output" (maybe string)])
+
+        (set-parametric-clause
+         [(:info "parametric_")
+          (kw ("param" . "etric"))])
+
+        (set-pm3d-clause
+         ["pm3d"
+          (many
+           (either
+            ["at" name]
+            ["interpolate" number "," number]
+            (either "scansautomatic" "scansforward" "scansbackward" 
"depthorder")
+            ["flush" (either "begin" "center" "end")]
+            (either "ftriangles" "noftriangles")
+            (either "clip1in" "clip4in")
+            ["corners2color"
+             (either "mean" "geomean" "median" "min" "max" "c1" "c2" "c3" 
"c4")]
+            ["hidden3d" number]
+            "nohidden3d"
+            "implicit" "explicit" "map"))])
+
+        (set-palette-clause
+         ["palette"
+          (many
+           (either
+            "gray" "color"
+            ["gamma" number]
+            ["rgbformulae" number "," number "," number]
+            "defined"                  ; not complete
+            ["functions" expression "," expression "," expression]
+            ["file" string (many datafile-modifier)]
+            "RGB" "HSV" "CMY" "YIQ" "XYZ"
+            "positive" "negative"
+            "nops_allcF" "ps_allcF"
+            ["maxcolors" number]))])
+
+        (set-pointsize-clause pointsize-spec)
+
+        (set-polar-clause "polar")
+
+        (set-print-clause
+         [(:info "print_")
+          "print"
+          (maybe string)])
+
+        (set-samples-clause
+         ["samples" expression (maybe "," expression)])
+
+        (set-size-clause
+         ["size"
+          (either
+           "square" "nosquare"
+           ["ratio" expression]
+           "noratio"
+           [expression "," expression])])
+
+        (set-style-clause
+         ["style"
+          (either style-arrow-clause style-data-clause style-fill-clause
+                  style-function-clause style-increment-clause
+                  style-line-clause style-circle-clause 
style-rectangle-clause)])
+
+        ;; begin subclauses of "set style ..."
+        (style-arrow-clause
+         [(:info "set_style_arrow")
+          "arrow"
+          number
+          (either
+           "default"
+           (many
+            (either "nohead" "head" "heads"
+                    "filled" "empty" "nofilled"
+                    "front" "back"
+                    ["size" dimension "," number (maybe "," number)]
+                    linestyle-spec)))])
+
+        (style-data-clause
+         [(:info "set_style_data")
+          "data" plotting-style])
+
+        (style-fill-clause
+         [(:info "set_style_fill")
+          "fill" fill-style])
+
+        (style-function-clause
+         [(:info "set_style_function")
+          "function" plotting-style])
+
+        (style-increment-clause
+         [(:info "set_style_increment")
+          "increment"
+          (either (kw ("d" . "efault")) (kw ("u" . "serstyles")))])
+
+        (style-line-clause
+         [(:info "set_style_line")
+          "line"
+          expression
+          (either
+           "default"
+           (many
+            (either
+             "palette"
+             [(kw ("linet" . "ype") "lt")
+              (either expression color-spec)]
+             [(kw ("linec" . "olor") "lc") color-spec]
+             [(either (kw ("linew" . "idth") "lw")
+                      (kw ("pointt" . "ype") "pt")
+                      (kw ("points" . "ize") "ps")
+                      (kw ("pointi" . "nterval") "pi"))
+              expression])))])
+
+        (style-circle-clause
+         [(:info "set_style_circle")
+          "circle" "radius" dimension])
+
+        (style-rectangle-clause
+         [(:info "set_style_rectangle")
+          "rectangle"
+          (many
+           (either
+            "front" "back"
+            [(kw ("linew" . "idth") "lw") expression]
+            [(kw "fillcolor" "fc") color-spec]
+            ["fs" expression]))])
+        ;; end of "set style ..." clauses
+
+        (set-surface-clause "surface")
+        
+        (set-table-clause ["table" (maybe string)])
+
+        (set-terminal-clause           ; not sure how to do this...
+         ["terminal" (maybe (either "push" "pop"))])
+
+        (set-termoption-clause
+         ["termoption"
+          (either
+           "enhanced" "noenhanced"
+           ["font" string]
+           "solid" "dashed"
+           [(kw "linewidth" "lw") expression])])
+
+        (set-tics-clause
+         ["tics"
+          (many
+           (either
+            "axis" "border" "mirror" "nomirror" "in" "out"
+            ["scale" (either "default" [expression (maybe "," expression)])]
+            [(either "rotate" "norotate") (maybe "by" expression)]
+            ["offset" expression] "nooffset"
+            ["format" string]
+            ["font" string]
+            textcolor-spec))])
+
+        (set-tics-clause-2
+         ["tics" (either "front" "back")])
+        
+        (set-xtics-clause
+         [(:info "xtics")
+          (either "xtics" "ytics" "ztics" "x2tics" "y2tics" "cbtics")
+          (many
+           (either
+            "axis" "border" "mirror" "nomirror" "in" "out"
+            ["scale" (either "default" [expression (maybe "," expression)])]
+            [(either "rotate" "norotate") (maybe "by" expression)]
+            ["offset" position] "nooffset"
+            "add" "autofreq"
+            ["(" (delimited-list [(maybe string) expression (maybe number)] 
",") ")"]
+            ["format" string]
+            ["font" string]
+            "rangelimited"
+            textcolor-spec
+            (delimited-list expression ",")))])
+
+        (set-timestamp-clause
+         ["timestamp"
+          (maybe string)
+          (maybe (either "top" "bottom"))
+          (maybe (either "rotate" "norotate"))
+          (maybe "offset" position)
+          (maybe "font" string)])
+
+        (set-timefmt-clause
+         ["timefmt" string])
+
+        (set-title-clause
+         [(:info "title_")
+          "title" 
+          (maybe expression)
+          (many
+           (either
+            ["offset" position] 
+            ["font" string]
+            textcolor-spec
+            "enhanced" "noenhanced"))])
+
+        (set-view-clause
+         ["view"
+          (either
+           "map"
+           [(either "equal" "noequal") (maybe (either "xy" "xyz"))]
+           (delimited-list (maybe expression) ","))])
+
+        (set-data-clause
+         [(:info "xdata")
+          (either "xdata" "ydata" "zdata" "x2data" "y2data" "cbdata")
+          (maybe (either "time" "geographic"))])
+
+        (set-dtics-clause
+         [(:info "xdtics")
+          (either "xdtics" "ydtics" "zdtics" "x2dtics" "y2dtics" "cbdtics")])
+
+        (set-xlabel-clause
+         [(:info "xlabel")
+          (either (kw ("xlab" . "el")) (kw ("ylab" . "el"))
+                  (kw ("zlab" . "el")) (kw ("x2lab" . "el"))
+                  (kw ("y2lab" . "el")) (kw ("cblab" . "el")))
+          (maybe expression)
+          (many
+           (either
+            ["offset" position] 
+            ["font" string]
+            textcolor-spec
+            "enhanced" "noenhanced"))])
+        
+        (set-mtics-clause
+         [(:info "xmtics")
+          (either "xmtics" "ymtics" "zmtics" "x2mtics" "y2mtics" "cbmtics")])
+        
+        (set-range-clause
+         [(:info "xrange")
+          (either (kw ("xr" . "ange")) (kw ("yr" . "ange"))
+                   (kw ("x2r" . "ange")) (kw ("y2r" . "ange"))
+                  (kw ("zr" . "ange")) (kw ("tr" . "ange"))
+                  (kw ("ur" . "ange")) (kw ("vr" . "ange"))
+                  (kw ("rr" . "ange")) (kw ("cbr" . "ange")))
+          (either
+           "restore"
+           ["[" (maybe
+                 [(maybe axis-range-component) ":"
+                  (maybe axis-range-component)])
+            "]"
+            (many (either "reverse" "noreverse" "writeback" "nowriteback"))])])
+            
+        (set-xyplane-clause
+         ["xyplane" (either "at" "relative") expression])
+
+        (set-zero-clause
+         ["zero" expression])
+
+        (set-zeroaxis-clause
+         [(:info "zeroaxis")
+          (either "zeroaxis" "xzeroaxis" "x2zeroaxis" "yzeroaxis" "y2zeroaxis"
+                  "zzeroaxis")
+          (maybe linestyle-spec)])
+        
+
+;;; Other commands
+        (cd-command
+         ["cd" string])
+
+        (call-command
+         ["call" string (many expression)])
+
+        (simple-command
+         (either "clear" "exit" "quit" "pwd" "refresh" "reread" "reset"
+                 "shell"))
+
+        (eval-command
+         ["eval" expression])
+
+        (load-command
+         ["load" string])
+
+        (lower-raise-command [(either "lower" "raise") number])
+
+        (pause-command
+         ["pause"
+          (either
+           expression
+           ["mouse" (maybe endcondition (maybe "," endcondition))])
+          string])
+
+        (endcondition (either "keypress" "button1" "button2" "button3" "close" 
"any"))
+
+        (save-command
+         ["save"
+          (either "functions" "variables" "terminal" "set")
+          string])
+
+        (system-command
+         ["system" string])
+
+        (test-command
+         ["test"
+          (either
+           "terminal"
+           ["palette"
+            (maybe
+             (either "rgb" "rbg" "grb" "gbr" "brg" "bgr"))])])
+
+        (undefine-command
+         ["undefine" (many name)])
+
+        (update-command
+         ["update" string (maybe string)]))
+       
+       ;; This is the start symbol
+       'command))))
+
+
+;; The following macros are used for debugging; load
+;; gnuplot-debug-context.el and then re-load this file to enable
+;; them. For normal use, they compile to no-ops.
+(eval-when-compile
+  (when (not (featurep 'gnuplot-debug-context))
+    (defmacro with-gnuplot-trace-buffer (&rest args) "No-op." '(progn nil))
+    (defmacro gnuplot-trace (&rest args) "No-op." '(progn nil))
+    (defmacro gnuplot-debug (&rest args) "No-op." '(progn nil))))
+
+
+
+;;;; Variables to be set via pattern matching
+(defvar gnuplot-completions nil
+  "List of possible gnuplot-mode completions at point.
+This is filled in by `gnuplot-match-pattern' when it reaches the
+token before point.")
+
+(defvar gnuplot-info-at-point nil
+  "Relevant page of the Gnuplot info manual for the construction at point.
+
+Set by `gnuplot-match-pattern' using information from
+`gnuplot-compiled-grammar'. `gnuplot-match-pattern' pushes ElDoc
+and info strings onto the stack as it runs, and scans the stack
+for the topmost entry when it reaches the token at point.")
+
+(defvar gnuplot-eldoc nil
+  "ElDoc documentation string for the Gnuplot construction at point.
+
+Set by `gnuplot-match-pattern'. See also `gnuplot-info-at-point'.")
+
+(defvar gnuplot-captures nil
+  "Alist of named capture groups for gnuplot-mode completion code.
+
+Each entry is of the form (NAME BEGIN END), where NAME is the
+name specified in the (capture NAME PATTERN) form in the
+`gnuplot-compiled-grammar' source, BEGIN is the tail of the token
+list beginning the capture group, and END is the tail of the
+token list just after the end of the capture group.")
+
+
+;;;; The pattern matching machine 
+(defun gnuplot-match-pattern (instructions tokens completing-p
+                                  &optional start-symbol)
+  "Parse TOKENS, setting completions, info and ElDoc information.
+
+This function parses TOKENS by simulating a stack machine with
+unlimited backtracking. If COMPLETING-P is non-nil, it stops
+before the token at point and collects a list of the next tokens
+that it would accept in `gnuplot-completions'. If COMPLETING-P is
+nil, it parses up to the token at point and sets `gnuplot-eldoc'
+and `gnuplot-info-at-point' based on the contents of the stack
+there."
+  (catch 'return
+    (let ((pc 0)                       ; Program counter
+         ;; Stack of return addresses (return PC), eldoc strings
+         ;; (eldoc STRING) and info pages (info STRING)
+         (stack '())
+         ;; Stack of backtracking records:
+         ;; ((STACK TOKENS RESUME-PC CAPTURES PROGRESS) ...)
+         (backtrack '())
+         ;; Match failure flag, set to `t' to cause backtracking
+         (fail nil)
+         ;; Flag set by JUMP and CALL instructions to stop PC advance
+         (jump nil)
+         ;; Record of progress made within (many ...) loops, an alist
+         ;; of conses (pc . tokens)
+         (progress '()))
+
+      (with-gnuplot-trace-buffer (erase-buffer))
+
+      (when start-symbol               ; HACK FIXME
+       (let ((look-for `(label ,start-symbol)))
+         (while (not (equal (aref instructions pc) look-for))
+           (incf pc))
+         (incf pc)))
+
+      (setq gnuplot-completions nil
+           gnuplot-eldoc nil
+           gnuplot-info-at-point nil
+           gnuplot-captures nil)
+
+      (flet ((advance
+             ()
+             (pop tokens)
+             (if (and (null tokens) (not completing-p))
+                 (gnuplot-scan-stack stack tokens)))
+            (fail () (setq fail t)))
+       
+       ;; Main loop
+       (while t
+         (let* ((inst (aref instructions pc))
+                (opcode (car inst))
+                (token (car tokens))
+                (end-of-tokens (null tokens)))
+           (gnuplot-trace "%s\t%s\t%s\n" pc inst (and token (gnuplot-token-id 
token)))
+           
+           (case opcode
+             ;; (literal LITERAL NO-COMPLETE)
+             ((literal)
+              (let ((expect (cadr inst))
+                    (no-complete (caddr inst)))
+                (cond (end-of-tokens
+                       (unless no-complete
+                         (gnuplot-trace "\tpushing \"%s\" to completions\n" 
expect)
+                         (push expect gnuplot-completions))
+                       (fail))
+                      
+                      ((not (equal (gnuplot-token-id token) expect))
+                       (fail))
+
+                      ;; otherwise succeed
+                      (t (advance)))))
+
+             ;; (token-type TYPE)
+             ((token-type)
+              (let ((expect (cadr inst)))
+                (if (or end-of-tokens
+                        (not (eq (gnuplot-token-type token) expect)))
+                    (fail)
+                  (advance))))
+
+             ;; (keyword REGEXP NAME): match any token whose ID
+             ;; regexp-matches REGEXP, use NAME for completions
+             ((keyword)
+              (let ((regexp (cadr inst))
+                    (name (caddr inst)))
+                (cond (end-of-tokens
+                       (gnuplot-trace "\tpushing \"%s\" to completions\n" name)
+                       (push name gnuplot-completions)
+                       (fail))
+                      
+                      ((not (string-match-p regexp (gnuplot-token-id token)))
+                       (fail))
+
+                      ;; otherwise succeed
+                      (t
+                       (setf (gnuplot-token-id token) name)
+                       (advance)))))
+
+             ;; (any): match any token
+             ((any)
+              (if end-of-tokens
+                  (fail)
+                (advance)))
+             
+             ;; (jump LOCATION): jump to instruction at LOCATION
+             ((jump)
+              (let ((location (cadr inst)))
+                (setq jump location)))
+
+             ;; (call LOCATION): push the next instruction as a
+             ;; return location and jump
+             ((call)
+              (let ((location (cadr inst)))
+                (push `(return ,(+ pc 1)) stack)
+                (setq jump location)))
+
+             ;; (return): return to address at topmost RETURN record on
+             ;; stack, or stop matching and return if stack is empty
+             ((return)
+              (while (and stack
+                          (not (eq (caar stack) 'return)))
+                (pop stack))
+              (if (not stack)
+                  ;; Successful match
+                  (throw 'return (list tokens))
+                ;; Otherwise, return to caller
+                (let* ((r (pop stack))
+                       (r-pc (cadr r)))
+                  (setq jump r-pc))))
+
+             ;; (choice LOCATION): push LOCATION onto the stack of
+             ;; backtracking points and continue at next instruction
+             ((choice)
+              (let ((location (cadr inst)))
+                (push `(,stack ,tokens ,location ,gnuplot-captures
+                               ,progress)
+                      backtrack)))
+
+             ;; (commit LOCATION): discard most recent backtrack point
+             ;; and jump to LOCATION
+             ((commit)
+              (let ((location (cadr inst)))
+                (if (not backtrack)
+                    (error "no more backtrack points in commit"))
+                (pop backtrack)
+                (setq jump location)))
+
+             ;; (fail): force this match to fail, going back to most
+             ;; recent backtrack point
+             ((fail)
+              (fail))
+
+             ;; (assert): run Lisp code and fail if it returns NIL
+             ((assert)
+              (let ((form (cadr inst)))
+                (if (not (eval form)) (fail))))
+
+             ;; (push TYPE VALUE): push an info page or eldoc string
+             ;; onto the stack
+             ((push)
+              (let* ((type (cadr inst))
+                     (value (caddr inst)))
+                (push `(,type ,value ,tokens) stack)))
+
+             ;; (pop TYPE): pop something off the stack
+             ((pop)
+              (let ((type (cadr inst)))
+                (if (not (and stack
+                              (eq (caar stack) type)))
+                    (error "Expected a %s on the stack but found %s" type 
stack))
+                (pop stack)))
+
+             ;; (save-start NAME): save current token pointer as
+             ;; beginning of capture group NAME
+             ((save-start)
+              (let ((name (cadr inst)))
+                (push `(,name ,tokens nil) gnuplot-captures)))
+
+             ;; (save-end NAME): save current token pointer as end of
+             ;; capture group NAME
+             ((save-end)
+              (let* ((name (cadr inst))
+                     (record (assoc name gnuplot-captures)))
+                (if (not record)
+                    (error "gnuplot-match-tokens: no open capture group named 
%s" name)
+                  (setf (caddr record) tokens)
+                  (gnuplot-debug (gnuplot-dump-captures)))))
+
+             ;; (check-progress): make sure not stuck in an infinite loop
+             ((check-progress)
+              (let ((prev-progress (cdr (assoc pc progress))))
+                (if (and prev-progress (eq prev-progress tokens))
+                    (fail)
+                  (push (cons pc tokens) progress))))
+
+             (t
+              (error "bad instruction: %s" inst)))
+
+           ;; Increment PC or jump
+           (setq pc (or jump (1+ pc))
+                 jump nil)
+                 
+           ;; Backtrack on failure
+           (when fail
+             (if (not backtrack)       ; Out of backtracking stack: failed 
match
+                 (throw 'return nil)
+               (gnuplot-trace "\t*fail*\t%s\n" (length backtrack))
+               (gnuplot-debug (gnuplot-dump-backtrack backtrack))
+               ;; If we got as far as token-at-point before failing,
+               ;; scan the stack for eldoc and info strings
+               (when (and end-of-tokens (not completing-p))
+                 (gnuplot-scan-stack stack tokens))
+               
+               (destructuring-bind
+                   (bt-stack bt-tokens bt-pc bt-captures bt-progress)
+                   (pop backtrack)
+                 (setq stack bt-stack
+                       tokens bt-tokens
+                       pc bt-pc
+                       gnuplot-captures bt-captures
+                       progress bt-progress
+                       fail nil)
+                 (gnuplot-debug (gnuplot-dump-progress progress)))))))))))
+
+(defun gnuplot-scan-stack (stack tokens)
+  "Scan STACK for the most recently pushed eldoc and info strings"
+  (gnuplot-trace "\t* scanning stack *\n")
+  (gnuplot-debug (gnuplot-backtrace))
+  (gnuplot-debug (gnuplot-dump-captures))
+
+  (catch 'no-scan
+    (while (and stack
+               (not (and gnuplot-info-at-point gnuplot-eldoc)))
+      (let* ((item (car stack))
+            (type (car item))
+            (position (caddr item))) ; must progress by at least one token
+       (if (and (memq type '(info eldoc no-scan))
+                (not (eq position tokens)))
+           (case type
+             ((no-scan)
+              (throw 'no-scan nil))
+
+             ((info)
+              (when (not gnuplot-info-at-point)
+                (let ((info (cadr item)))
+                  (setq gnuplot-info-at-point
+                        (cond
+                         ((eq info 'first-token)
+                          (gnuplot-token-id (car position)))
+                         ((functionp info) (funcall info))
+                         (t info)))
+                  (when gnuplot-info-at-point
+                    (gnuplot-trace "\tset info to \"%s\"\n" 
gnuplot-info-at-point)
+                    (when (not gnuplot-eldoc)
+                      (let ((eldoc
+                             (car (gethash gnuplot-info-at-point 
gnuplot-eldoc-hash))))
+                        (when eldoc
+                          (setq gnuplot-eldoc eldoc)
+                          (gnuplot-trace "\tand set eldoc to \"%s\"\n" 
eldoc))))))))
+
+             ((eldoc)
+              (when (not gnuplot-eldoc)
+                (let ((eldoc (cadr item)))
+                  (setq gnuplot-eldoc
+                        (if (functionp eldoc) (funcall eldoc) eldoc))
+                  (gnuplot-trace "\tset eldoc to \"%s\"\n" gnuplot-eldoc)))))))
+      (pop stack))))
+
+(defun gnuplot-capture-group (name)
+  "Return capture group NAME from the most recent parse, as a list of tokens."
+  (let ((record (assoc name gnuplot-captures)))
+    (if (not record) nil
+      (let ((begin (cadr record))
+           (end (caddr record))
+           (accum '()))
+       (while (and begin (not (eq begin end)))
+         (push (pop begin) accum))
+       (nreverse accum)))))
+
+(defun gnuplot-capture-group->string (name)
+  (let ((tokens (gnuplot-capture-group name)))
+    (and tokens
+        (mapconcat 'gnuplot-token-id tokens " "))))
+
+
+;;; Interface to the matching machine
+(defun gnuplot-parse-at-point (completing-p)
+  (let ((tokens (gnuplot-tokenize completing-p)))
+    (gnuplot-match-pattern gnuplot-compiled-grammar tokens completing-p)))
+
+;; Completions
+(defun gnuplot-completions ()
+  (gnuplot-parse-at-point t)
+  gnuplot-completions)
+
+(defun gnuplot-context-completion-at-point ()
+  "Return completions of keyword preceding point, using context."
+  (let* ((end (point))
+        (beg
+         (save-excursion
+           (skip-syntax-backward "w_" (gnuplot-point-at-beginning-of-command))
+           (point)))
+        (word nil)
+        (completions (gnuplot-completions)))
+    (unless (= beg end)
+      (setq word (buffer-substring beg end)
+           completions (all-completions word completions)))
+
+    (if completions
+       (list beg end completions)
+      (message "No gnuplot keywords complete '%s'" word)
+      nil)))
+
+;; Eldoc help
+(defun gnuplot-eldoc-function ()
+  "Return the ElDoc string for the Gnuplot construction at point."
+  (gnuplot-parse-at-point nil)  
+  gnuplot-eldoc)
+
+(defun gnuplot-help-function ()
+  "Pop up the extended documentation for the construction at point."
+  (interactive)
+  (gnuplot-parse-at-point nil)
+  (if gnuplot-info-at-point
+      (let ((eldoc
+            (cadr (gethash gnuplot-info-at-point gnuplot-eldoc-hash))))
+       (if eldoc (message eldoc)))))
+
+;; Info lookup
+(defun gnuplot-info-at-point (&optional query)
+  "Open the relevant gnuplot info page for the construction at point."
+  (interactive "P")
+  (setq gnuplot-info-at-point nil)
+  (unless query
+    (gnuplot-parse-at-point nil))
+  (if (or query (not gnuplot-info-at-point))
+      (let ((info
+            (info-lookup-interactive-arguments 'symbol)))
+       (setq gnuplot-info-at-point (car info))))
+  (and gnuplot-info-at-point
+       (info-other-window (format "(gnuplot)%s" gnuplot-info-at-point))))
+
+(defun gnuplot-info-look-guess ()
+  (gnuplot-parse-at-point nil)
+  gnuplot-info-at-point)
+
+
+
+
+;;; Some context-sensitive hacks 
+
+;; ElDoc strings for "using" specs, which depend on other information
+;; from the parsed command
+
+(defvar gnuplot-using-eldoc
+  '(("boxerrorbars" . "x:y:ydelta{:xdelta} | x:y:ylow:yhigh{:xdelta}")
+    ("boxes" . "x:y{:x_width}")
+    ("boxxyerrorbars" . "x:y:xdelta:ydelta | x:y:xlow:xhigh:ylow:yhigh")
+    ("candlesticks" . "x:box_min:whisker_min:whisker_high:box_high | 
date:open:low:high:close")
+    ("circles" . "x:y:radius")
+    ("dots" . "x{:y{:z}}")
+    ("filledcurves" . "x:y | x:y1:y2")
+    ("financebars" . "date:open:low:high:close")
+    ("fsteps" . "y | x:y")
+    ("histeps" . "y | x:y")
+    ("histograms" . "y:yerr | y:ymin:ymax")
+    ("image" . "x:y:value")
+    ("rgbimage" . "x:y:r:g:b")
+    ("rgbalpha" . "x:y:r:g:b:a")
+    ("impulses" . "x{:y{:z}}")
+    ("labels" . "x:y:string")
+    ("lines" . "y | x:y")
+    ("steps" . "y | x:y")
+    ("vectors" . "x:y:xdelta:ydelta")
+    ("xerrorbars" . "x:y:xdelta | x:y:xlow:xhigh")
+    ("xyerrorbars" . "x:y:xdelta:ydelta | x:y:xlow:xhigh:ylow:yhigh")
+    ("yerrorbars" . "x:y:ydelta | x:y:ylow:yhigh")
+    ("yerrorlines" . "x:y:ydelta | x:y:ylow:yhigh")
+    ("xerrorlines" "x:y:xdelta | x:y:xlow:xhigh")
+    ("xyerrorlines" . "x:y:xdelta:ydelta | x:y:xlow:xhigh:ylow:yhigh"))
+  "Alist of ElDoc strings for Gnuplot \"using\" clauses in \"plot\" commands.")
+
+(defvar gnuplot-using-3d-eldoc
+  (append
+   '(("fsteps" . "z | x:y:z")
+     ("histeps" . "z | x:y:z")
+     ("histograms" . "y:yerr | y:ymin:ymax")
+     ("image" . "x:y:z:value")
+     ("rgbimage" . "x:y:z:r:g:b")
+     ("rgbalpha" . "x:y:z:r:g:b:a")
+     ("labels" . "x:y:z:string")
+     ("lines" . "z | x:y:z")
+     ("steps" . "z | x:y:z")
+     ("vectors" . "x:y:z:xdelta:ydelta:zdelta"))
+   gnuplot-using-eldoc)
+  "Alist of ElDoc strings for Gnuplot \"using\" clauses in \"splot\" 
commands.")
+
+(defun gnuplot-find-using-eldoc ()
+  "Return ElDoc string for a Gnuplot \"using\" clause, based on plotting style.
+
+This will fail if the \"using\" clause comes before the \"with\"
+clause."
+  (let ((with-style (gnuplot-capture-group :with-style))
+       (3d-p (gnuplot-capture-group :splot-command))
+       (column-description nil))
+    (if with-style
+       (let ((with-style-string (gnuplot-token-id (car with-style))))
+         (setq column-description
+               (or (and 3d-p
+                        (cdr (assoc with-style-string gnuplot-using-3d-eldoc)))
+                   (cdr (assoc with-style-string gnuplot-using-eldoc))
+                   "<columns>"))))
+    (format "using %s {'format'}" column-description)))
+
+;;; Needed for correctly parsing plot commands
+(defun gnuplot-guess-parametric-p (&optional start)
+  "Guess whether the command beginning at START is in parametric mode.
+
+Searches backward in current buffer for an \"(un)set parametric\"
+command."
+  (save-excursion
+    (and start (goto-char start))
+    (catch 'result
+      (while
+         (search-backward-regexp "reset\\|set\\s-+parametric" (point-min) t)
+       (gnuplot-beginning-of-command)
+       (cond ((looking-at "reset\\|unset\\s-+parametric") (throw 'result nil))
+             ((looking-at "set\\s-+parametric") (throw 'result t))))
+      nil)))
+
+
+
+;;; All done!
+(provide 'gnuplot-context)
+
+;;; gnuplot-context.el ends here
\ No newline at end of file
diff --git a/gnuplot-debug-context.el b/gnuplot-debug-context.el
new file mode 100644
index 0000000..7f975c2
--- /dev/null
+++ b/gnuplot-debug-context.el
@@ -0,0 +1,115 @@
+;;
+;; debugging utilities for the gnuplot-mode context matcher
+;;
+
+(require 'gnuplot-test-context)        ; for gnuplot-simplify-tokens
+
+(defun gnuplot-unload ()
+  (interactive)
+  (mapatoms
+   (lambda (sym)
+     (when (string-match
+           "gnuplot"
+           (symbol-name sym))
+       (unintern sym obarray)))))
+
+(defun gnuplot-reload (&optional context)
+  (interactive "p")
+  (condition-case nil
+      (gnuplot-unload)
+    (error nil))
+  (require 'gnuplot)
+  (when context
+    (if (= context 16)
+       (require 'gnuplot-debug-context))
+    (require 'gnuplot-context)))
+
+(defmacro gnuplot-recompile ()
+  '(save-current-buffer
+     (save-window-excursion
+       (find-file "gnuplot-context.el")
+       (delete-file "gnuplot-context.elc")
+       (emacs-lisp-byte-compile)
+       (load-file "gnuplot-context.elc"))))
+  
+(defun gnuplot-nodebug ()
+  (interactive)
+  (when (featurep 'gnuplot-debug-context)
+      (let ((savef (symbol-function 'gnuplot-debug-on)))
+       (unload-feature 'gnuplot-debug-context)
+       (fset 'gnuplot-debug-on savef)))
+  (gnuplot-recompile))
+
+(defun gnuplot-debug-on ()
+  (interactive)
+  (unless (featurep 'gnuplot-debug-context)
+    (load-library "gnuplot-debug-context"))
+  (gnuplot-recompile))
+
+(defmacro with-gnuplot-trace-buffer (&rest body)
+  `(with-current-buffer (get-buffer-create "gnuplot-trace")
+     ,@body))
+
+(defmacro gnuplot-debug (&rest args)
+  `(progn ,@args))
+
+(defmacro gnuplot-trace (&rest args)
+  `(with-gnuplot-trace-buffer (insert (format ,@args))))
+
+(defun gnuplot-backtrace ()
+  (if stack
+      (with-gnuplot-trace-buffer
+       (insert "\n-- * backtrace: * --\n")
+       (dolist (x stack)
+        (insert (format "%s\n"
+                        (if (eq (car x) 'return)
+                            x
+                          (list (car x) (cadr x)
+                                (gnuplot-simplify-tokens (caddr x)))))))
+       (insert "-- end backtrace  --\n"))))
+
+(defun gnuplot-dump-backtrack (backtrack)
+  (if backtrack
+      (with-gnuplot-trace-buffer
+       (insert "\n-- * backtrack records: * --\n")
+       (dolist (x backtrack)
+        (insert (format "%s\t%s\n" (caddr x) (gnuplot-simplify-tokens (cadr 
x)))))
+       (insert "-- end backtrack records  --\n\n"))))
+
+(defun gnuplot-dump-progress (progress)
+  (if progress
+      (with-gnuplot-trace-buffer
+       (insert "\n-- * progress records: * --\n")
+       (dolist (x progress)
+        (insert (format "%s\t%s\n" (car x) (gnuplot-simplify-tokens (cdr x)))))
+       (insert "-- end progress records  --\n\n"))))
+
+(defun gnuplot-dump-code (&optional inst)
+  (interactive)
+  (let ((inst (or inst gnuplot-compiled-grammar)))
+    (with-gnuplot-trace-buffer
+     (insert "\n-- * compiled code: * --\n")
+     (dotimes (i (length inst))
+       (insert (format "%s\t%s\n" i (aref inst i))))
+     (insert "--  end compiled code --\n\n")
+     (pop-to-buffer (current-buffer)))))
+
+(defun gnuplot-dump-captures ()
+  (interactive)
+  (if gnuplot-captures
+      (with-gnuplot-trace-buffer
+       (insert "\n-- * capture groups: * --\n")
+       (loop for c on gnuplot-captures
+            do
+            (let ((name (caar c))
+                  (gnuplot-captures c))
+              (insert (format "%s\t%s\n"
+                              name
+                              (mapconcat 'gnuplot-token-id
+                                         (gnuplot-capture-group name)
+                                         " ")))))
+       (insert "-- end capture groups  --\n\n"))))
+
+(provide 'gnuplot-debug-context)
+
+(gnuplot-debug-on)
diff --git a/gnuplot-test-context.el b/gnuplot-test-context.el
new file mode 100644
index 0000000..ba7a165
--- /dev/null
+++ b/gnuplot-test-context.el
@@ -0,0 +1,419 @@
+;;
+;; automated tests for gnuplot-mode context matching
+;;
+
+(require 'gnuplot-context)
+(require 'ert)
+
+(eval-when-compile
+  (if (not (fboundp 'deftest))
+      (defalias 'deftest 'ert-deftest)))
+
+
+(defun gnuplot-tokenize-string (string)
+  (with-temp-buffer
+    (gnuplot-mode)
+    (insert string)
+    (goto-char (point-max))
+    (gnuplot-tokenize)))
+  
+(defmacro with-gnuplot-tokens-from-string (binding &rest body)
+  (declare (indent 1))
+  `(with-temp-buffer
+     (gnuplot-mode)
+     (insert ,(cadr binding))
+     (let ((,(car binding) (gnuplot-tokenize)))
+       ,@body)))
+        
+(defun gnuplot-simplify-tokens (tokens)
+  (mapcar
+   (lambda (token)
+     (case (gnuplot-token-type token)
+       (number
+       (string-to-number (gnuplot-token-id token)))
+
+       (string
+       (gnuplot-token-id token))
+
+       (end-of-command 'end-of-command)
+       
+       (otherwise
+       (intern (gnuplot-token-id token)))))
+       tokens))
+
+;; compile a single pattern to usable form
+(eval-and-compile
+  (defun gnuplot-compile-pattern-1 (pattern)
+    (gnuplot-compile-grammar `((rule ,pattern)) 'rule)))
+ 
+;; match a string 
+(defun gnuplot-match-string (string rule)
+  (if (vectorp rule)
+      (gnuplot-match-pattern
+       rule (gnuplot-tokenize-string string) nil)
+    (gnuplot-match-pattern
+     gnuplot-compiled-grammar
+     (gnuplot-tokenize-string string)
+     nil rule)))
+
+;; with-gensyms
+(defmacro with-gensyms (symbols &rest forms)
+  (declare (indent 1))
+  `(let ,(mapcar
+         (lambda (sym)
+           `(,sym (make-symbol ,(symbol-name sym))))
+         symbols)
+     ,@forms))
+
+;; test-defining macro
+(defmacro should-match (rule &rest pairs)
+  (declare (indent 1))
+  (with-gensyms (tokens result rest)
+    `(let ((rule ,(if (symbolp rule)
+                     `(quote ,rule)
+                   (gnuplot-compile-pattern-1 rule))))
+       ,@(mapcar
+         (lambda (pair)
+           (if (stringp pair)
+               (setq pair (list pair)))
+
+           (let ((string (car pair))
+                 (rest (cadr pair)))
+             (if (eq rest :none)               ; Shouldn't match anything
+                 `(should
+                   (null
+                    (gnuplot-match-string ,string rule)))
+               `(should (equal
+                         (gnuplot-simplify-tokens
+                          (car (gnuplot-match-string ,string rule)))
+                         ,rest)))))
+         pairs))))
+
+
+;; Number
+(deftest gnuplot-number ()
+  (should-match [number]
+    ("123")
+    (".05")
+    ("1e7")))
+
+;; name
+(deftest gnuplot-name ()
+  (should-match [name]
+    ("foo")
+    ("name_with_underscores")
+    ("var123")))
+
+;; string-constant
+
+;; Note that the id of a string constant token includes the delimiters
+(deftest gnuplot-string-constant ()
+  (should-match [string]
+    ("\"double quoted string\"")
+    ("'single quoted'")))
+
+;; sequence
+(deftest gnuplot-sequence ()
+  (should-match [number name]
+    ("1.34 name garbage" '(garbage))
+    ("2.718 xy")
+    ("1e9 123 2.718281828459045" :none)))
+
+;; either
+(deftest gnuplot-either ()
+  (should-match
+      (either number name)
+    ("1359, 349" '(\, 349))
+    ("a_name . something" '(\. something))
+    ("'quoted string constant' name" :none)))
+
+;; many
+(deftest gnuplot-many ()
+  (should-match (many number)
+    ("123 456 789")
+    ("not a number" '(not a number))
+    (".89 3.1415 foo" '(foo)))
+  (should-match (many name)
+    ("foo bar baz")
+    ("tom dick harry 1.34" '(1.34))))
+
+;; maybe
+(deftest gnuplot-maybe ()
+  (should-match (maybe name)
+    ("foo bar baz" '(bar baz))
+    ("1.23" '(1.23))
+    ("'string' quux" '("'string'" quux))))
+
+;; delimited list
+(deftest gnuplot-delimited-list ()
+  (should-match (delimited-list number ":")
+    ("1:2:3")
+    ("1e2:2.78")
+    ("9")
+    ("17:xy" '(: xy))
+    ("nan" :none))
+  (should-match (delimited-list name ",")
+    ("foo,bar,baz")
+    ("x,y")
+    ("x"))
+  (should-match (delimited-list number "-")
+    ("1 - 2 - 3, garbage" '(\, garbage))
+    ("x - 2 - 3" :none)
+    ("1 - 2 - y" '(- y))))
+
+;; keyword
+(deftest gnuplot-keyword ()
+  (should-match (either (kw ("w" . "ord"))
+                       (kw ("ot" . "her_word") "ow" "alt"))
+    ("word")
+    ("w")
+    ("wo")
+    ("wor")
+    ("word thing" '(thing))
+    ("o" :none)
+    ("ot")
+    ("oth")
+    ("othx" :none)
+    ("ow")
+    ("alt")))
+
+;; primary-expression
+(deftest gnuplot-primary-expression ()
+  (should-match primary-expression
+    ("name")
+    ("123")
+    ("{3,5}")
+    ("$23")
+    ("\"string\"")
+    ("5!! + 2" '(+ 2))
+    ("5 ** 9")
+    ("foo[3:5]")
+    ("(1,2,3)")
+    ("fun(3.14,x)")
+    ("3!!**2 ," '(\,))
+    ("," :none)
+    ("]" :none)))
+
+(deftest gnuplot-function-call ()
+  (should-match function-call
+    "abs(2)"
+    "sin(pi*2)"
+    "non_built_in(5+2)"
+    "sprintf('%s*', columnheader(1))"
+    "y(n)"))
+
+;; expression
+(deftest gnuplot-infix-expression ()
+  (should-match expression
+    ("-2")
+    ("!~foo ^ bar , " '(\,))
+    ("1+2%7 >= 9")
+    ("f && g ? 1 + 2 : 5**2")
+    ("t ? y(n) : n")
+    ("f ? g ? 1 : 2 : 3 + x")
+    ("f ? fun(1, 3+5 ** 7) : g > h ? pi:e : garbage"
+     '(: garbage))))
+
+;; assignments
+(deftest gnuplot-assignment ()
+  (should-match lhs
+    ("x")
+    ("long_identifier")
+    ("1.9" :none)
+    ("x(y)")
+    ("fun(x_, y_) = " '(=))
+    ("no_thunks()" '(\( \))))
+  (should-match assignment
+    ("x=2")
+    ("x=y=3, garbage" '(\, garbage))
+    ("f(a) = y(x) = 5")))
+
+;; parenthesized exprs (including assignments)
+(deftest gnuplot-parenthesized-expression ()
+  (should-match parenthesized-expression
+    ("(sum = sum + $2, sum/2)")))
+
+;; axis ranges
+(deftest gnuplot-axis-range ()
+  (should-match axis-range
+     ("[-pi:pi]")
+     ("[-1:1]")
+     ("[t = -10 :30]")
+     ("[ ]") 
+     ("[-2:sin(5)*-8]")
+     ("[:200]")
+     ("[foo=:200]")
+     ("[-pi:]")
+     ("[bar=-pi:]")
+     ("[baz=1:100*2:3/2]")
+     ("[-pi:pi:0.2]")
+     ("[\"1/6/93 12:00\":\"5/6/93 12:00\"]")))
+
+;; iteration
+(deftest gnuplot-iteration-spec ()
+  (should-match iteration-spec
+    ("for [x = 1:9]")
+    ("for [y=-2*pi:2*pi:0.1]")
+    ("for[1:2:3]" :none)))
+
+;; plot expression, ignoring assignments
+(deftest gnuplot-plot-expression ()
+  (should-match plot-expression
+    ("sin(x) + 2")
+    ("a=5, foo")
+    ("b=9 5+2")
+    ("i=3, j=sin(x)+9 k = 1**2!! f(x) garbage" '(garbage))))
+
+;; plot modifiers
+(deftest gnuplot-plot-modifier ()
+  (should-match plot-modifier
+    ("lines 5 + 2")
+    ("lw 9")
+
+    ("titl 'string'[2:3]")
+    ("notitle 'ignored'")
+    ("notitle with lines" '(with lines))
+
+    ("axes x1y2")
+    ("axes" :none)
+    ("axes 2 + 3" :none)))
+
+(deftest gnuplot-with-modifier ()
+  (should-match with-modifier
+    ("with impulses")
+    ("w points")
+    ("with l")
+    ("w i")
+    ("with boxes")
+    ("w lines")
+    ("w errorbars")))
+
+(deftest gnuplot-filledcurves ()
+  (should-match filledcurves-style-clause
+    ("filledcurves closed")
+    ("filledcurves x1")
+    ("filledcurves x2")
+    ("filledcurves y1=0")
+    ("filledcurves below y2=42")
+    ("filledcurves xy=10,20")))
+
+(deftest gnuplot-plot-command ()
+  (should-match plot-command
+    ("plot sin(x) with impulses")
+     ("plot x w points, x**2")
+     ("plot [ ] [-2:5] tan(x), 'data.1' with l")
+     ("plot 'leastsq.dat' w i")
+     ("plot 'exper.dat' w lines, 'exper.dat' notitle w errorbars")
+     ("plot sin(x) with linesp lt 1 pt 3, cos(x) with linesp lt 1 pt 4")
+     ("plot 'data' with points pointtype 3 pointsize 2")
+     ("plot 'data' using 1:2:4 with points pt 5 pointsize variable")
+     ("plot 'd1' t \"good\" w l lt 2 lw 3, 'd2' t \"bad\" w l lt 2 lw 1")
+     ("plot x*x with filledcurve closed, 40 with filledcurve y1=10")
+     ("plot x*x, (x>=-5 && x<=5 ? 40 : 1/0) with filledcurve y1=10 lt 8")))
+
+;;; set cntrparam
+(deftest gnuplot-cntrparam ()
+  (should-match set-cntrparam-clause
+    ("cntrparam bspline")
+    ("cntrparam points 7")
+    ("cntrparam order 10")
+    ("cntrparam levels auto 5")
+    ("cntrparam levels discrete .1,1/exp(1),.9")
+    ("cntrparam levels incremental  0,1,4")
+    ("cntrparam levels 10")
+    ("cntrparam levels incremental 100,50")))
+            
+
+;;
+;; test by parsing all the demos
+;;
+
+(defvar gnuplot-test-result-buffer "*gnuplot parse test results*")
+(defvar gnuplot-test-count 0)
+(defvar gnuplot-test-success-count 0)
+
+
+(defun gnuplot-test-parse-all-demos ()
+  (interactive)
+  (let* ((bufname "*gnuplot parse test results*")
+        (gnuplot-test-result-buffer
+         (progn
+           (and bufname (get-buffer bufname)
+                (kill-buffer bufname))
+           (get-buffer-create bufname)))
+        (gnuplot-test-count 0)
+        (gnuplot-test-success-count 0)
+        (demo-dir "~/dev/gnuplot/demo/")
+        (demo-files (directory-files demo-dir t "^[^.].*\\.dem$"))
+        (n-files (length demo-files))
+        (n 0))
+    
+    (switch-to-buffer-other-window gnuplot-test-result-buffer)
+
+    (catch 'done
+      (dolist (fname demo-files)
+       
+       (with-temp-buffer
+         (insert-file-contents fname)
+         (gnuplot-mode)
+         (message "Testing on file %s of %s: %s..."
+                  (incf n) n-files fname)
+         (condition-case err
+             (gnuplot-test-parse-buffer (current-buffer) fname)
+           (error
+            (with-current-buffer gnuplot-test-result-buffer
+              (insert (format "ERROR in %s: %s" fname err)))))
+         (message "Testing on file %s of %s: %s... done"
+                  n n-files fname)
+         (with-current-buffer gnuplot-test-result-buffer
+           (goto-char (point-max))
+           (recenter)))))
+    
+    (with-current-buffer gnuplot-test-result-buffer
+      (insert (format "\n\nPassed %s out of %s tests (%.2f%%)\n"
+                     gnuplot-test-success-count
+                     gnuplot-test-count
+                     (* 100 (/ (+ gnuplot-test-success-count 0.0)
+                               gnuplot-test-count))))
+      (compilation-mode))))
+
+(defun gnuplot-test-parse-buffer (&optional buffer fname)
+  (interactive nil)
+  (let ((buffer (or buffer (current-buffer)))
+       (fname (or fname (buffer-file-name))))
+    (with-current-buffer buffer
+      (goto-char (point-min))
+      (while (not (eobp))
+       (let ((ln (line-number-at-pos))
+             (tokens (progn
+                       (gnuplot-end-of-command)
+                       (gnuplot-tokenize))))
+         (when (> (length tokens) 1)
+           (let ((result
+                  (gnuplot-match-pattern
+                   gnuplot-compiled-grammar
+                   tokens nil)))
+             (incf gnuplot-test-count)
+             (if (equal result '(nil))
+                 (incf gnuplot-test-success-count)
+               (let ((cmd
+                      (buffer-substring
+                       (gnuplot-point-at-beginning-of-command)
+                       (gnuplot-point-at-end-of-command))))
+                 (with-current-buffer
+                     (get-buffer-create gnuplot-test-result-buffer)
+                   (insert
+                    (format "FAILED at %s:%s\n\t%s\n" fname ln cmd))
+                   (when (not (null result))
+                     (insert
+                      (format "\tUNMATCHED TOKENS were: %s\n"
+                              (gnuplot-simplify-tokens (car result)))))))))))
+       (gnuplot-beginning-of-defun -1)))))
+
+(add-to-list 'compilation-error-regexp-alist-alist
+            '(gnuplot-test-errors
+              "^FAILED at \\([^:]*\\):\\([0-9]*\\)" 1 2))
+
+(add-to-list 'compilation-error-regexp-alist 'gnuplot-test-errors)
+
+(provide 'gnuplot-test-context)
diff --git a/gnuplot.el b/gnuplot.el
index 9760322..a58e044 100644
--- a/gnuplot.el
+++ b/gnuplot.el
@@ -58,7 +58,7 @@
 ;;    C-c C-c       comment region
 ;;    C-c C-o       set arguments for command at point
 ;;   S-mouse-2      set arguments for command under mouse cursor
-;;    C-c C-h       read the gnuplot info file
+;;    C-c C-d       read the gnuplot info file
 ;;    C-c C-e       show-gnuplot-buffer
 ;;    C-c C-k       kill gnuplot process
 ;;    C-c C-u       submit a bug report about gnuplot-mode
@@ -640,10 +640,10 @@ you're not using that musty old thing, are you..."
   (define-key gnuplot-mode-map "\C-c\C-b" 'gnuplot-send-buffer-to-gnuplot)
   (define-key gnuplot-mode-map "\C-c\C-c" 'comment-region) ; <RF>
   (define-key gnuplot-mode-map "\C-c\C-o" 'gnuplot-gui-set-options-and-insert)
-  (define-key gnuplot-mode-map "\C-c\C-d" 'gnuplot-show-version)
+  (define-key gnuplot-mode-map "\C-c\C-w" 'gnuplot-show-version)
   (define-key gnuplot-mode-map "\C-c\C-e" 'gnuplot-show-gnuplot-buffer)
   (define-key gnuplot-mode-map "\C-c\C-f" 'gnuplot-send-file-to-gnuplot)
-  (define-key gnuplot-mode-map "\C-c\C-h"    'gnuplot-info-lookup-symbol)
+  (define-key gnuplot-mode-map "\C-c\C-d" 'gnuplot-info-lookup-symbol)
   (define-key gnuplot-mode-map "\C-c\C-i" 'gnuplot-insert-filename)
   (define-key gnuplot-mode-map "\C-c\C-j" 'gnuplot-forward-script-line)
   (define-key gnuplot-mode-map "\C-c\C-k" 'gnuplot-kill-gnuplot-buffer)
@@ -2080,8 +2080,8 @@ buffer."
 (define-key gnuplot-comint-mode-map "\C-d"     'gnuplot-delchar-or-maybe-eof)
 (define-key gnuplot-comint-mode-map "\M-\r"    'comint-dynamic-complete)
 (define-key gnuplot-comint-mode-map "\M-\t"    'comint-dynamic-complete)
-(define-key gnuplot-comint-mode-map "\C-c\C-d" 'gnuplot-show-version)
-(define-key gnuplot-comint-mode-map "\C-c\C-h" 'gnuplot-info-lookup-symbol)
+(define-key gnuplot-comint-mode-map "\C-c\C-d"  'gnuplot-info-lookup-symbol)
+(define-key gnuplot-comint-mode-map "\C-c\C-w" 'gnuplot-show-version)
 (define-key gnuplot-comint-mode-map "\C-c\C-i" 'gnuplot-insert-filename)
 (define-key gnuplot-comint-mode-map "\C-c\C-n" 'gnuplot-negate-option)
 (define-key gnuplot-comint-mode-map "\C-c\C-p" 'gnuplot-show-gnuplot-version)



reply via email to

[Prev in Thread] Current Thread [Next in Thread]