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

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

[nongnu] elpa/gnuplot de13740 038/184: Added experimental context-sensit


From: ELPA Syncer
Subject: [nongnu] elpa/gnuplot de13740 038/184: Added experimental context-sensitive completion and help code.
Date: Sun, 29 Aug 2021 11:03:11 -0400 (EDT)

branch: elpa/gnuplot
commit de137406c8674b85eec6dcfa7b6a2aec8e7031db
Author: Jonathan Oddie <j.j.oddie@gmail.com>
Commit: Jonathan Oddie <j.j.oddie@gmail.com>

    Added experimental context-sensitive completion and help code.
---
 .gitignore               |    3 +-
 gnuplot-context.el       | 2113 ++++++++++++++++++++++++++++++++++++++++++++++
 gnuplot-debug-context.el |   64 ++
 3 files changed, 2179 insertions(+), 1 deletion(-)

diff --git a/.gitignore b/.gitignore
index 8acbc2b..c4d293f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,5 +4,6 @@
 *.pdf
 *.dvi
 *~
-#*#
+/#*#
+*/#*#
 /hacks.el
diff --git a/gnuplot-context.el b/gnuplot-context.el
new file mode 100644
index 0000000..4f3ecea
--- /dev/null
+++ b/gnuplot-context.el
@@ -0,0 +1,2113 @@
+;;;; 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:    Wednesday, 08 February 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 provides context-sensitive completion, ElDoc support and
+;; info page links for gnuplot-mode buffers.
+;; 
+;; Usage 
+;; =====
+;;
+;; Put this file somewhere in your load path, byte compile and
+;; (require 'gnuplot-context).  This will give you context-sensitive
+;; completion and info page links, assuming you have the correct
+;; version of the Gnuplot documentation installed somewhere in your
+;; info path.
+;; 
+;; ElDoc support (one-line help displayed in the mode line) has to be
+;; compiled from 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
+;; buffer, or put it in a load hook if you always want it on.
+;; 
+;;
+;; 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 (see
+;; `gnuplot-tokenize') and matching (`gnuplot-match-pattern'). The
+;; simplest way I could think of to build a complete list of
+;; completions was to backtrack through the grammar and try every
+;; possible path. This is implemented in the parsing function
+;; `gnuplot-match-pattern' by simululating a stack machine with
+;; continuations. The 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 saves on the
+;; Emacs call stack.
+;;
+;; Compiling the grammar does require increasing `max-lisp-eval-depth'
+;; modestly, which shouldn't cause any problems on modern machines, and
+;; only needs to be done once, when byte-compiling.
+;;
+;; 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:
+;;
+;;    (sequence { :eldoc "eldoc string" }
+;;              { :info "info page" }
+;;          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.
+;;      
+;;    (either PATTERN PATTERN...)
+;;     Match the first PATTERN to succeed or fail. Like regexp `|'.
+;;
+;;    (many PATTERN)
+;;     Match PATTERN as many times as possible, like regexp
+;;     `*'. Backtracks if a later part of the pattern fails.
+;;    
+;;    (lazy-many PATTERN)
+;;     Similar to "many", but match as few times as possible, like
+;;     regexp `*?'. This has the side effect of preferring whatever
+;;     pattern comes afterwards for determining ElDoc and info
+;;     strings; see the rule for "plot-expression" for an example.
+;;
+;;    (maybe PATTERN)
+;;     Match PATTERN 0 or 1 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 (like "set").
+;;  
+;;    For convenience, "many", "lazy-many", "maybe", "capture" and
+;;    "info-keyword" wrap the rest of their arguments in an implicit
+;;    "sequence", 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.
+;; =======================
+;;
+;; The tokenizer should stop before or at point, instead of going to the
+;; end of the line.
+;;
+;; 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.
+;;
+;; It might be useful to complete on user-defined functions and
+;; variables as well as built-ins.
+;;
+
+(eval-when-compile (require 'cl))
+
+;; Prevent compiler warnings about undefined functions
+(eval-when-compile (require 'gnuplot))
+
+;; 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))))
+
+
+;;;; The tokenizer.
+
+(defstruct gnuplot-token
+  start            ; Buffer start position
+  end      ; Buffer end position
+  id       ; Text
+  type)            ; name, number, string, operator, end-of-input
+
+(defvar gnuplot-token-at-point nil
+  "Gnuplot token at point, set by `gnuplot-tokenize'.")
+
+(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 ()
+  "Tokenize the Gnuplot command at point. Returns a list of `gnuplot-token' 
objects."
+  (let ((p (point)))
+    (save-excursion
+      (gnuplot-beginning-of-command)
+      (let ((tokens '())
+           (parse-limit (gnuplot-point-at-end-of-command)))
+
+       (setq gnuplot-token-at-point nil)
+
+       (while (progn
+                (skip-syntax-forward "-")
+                (while (looking-at "\\\\\n")
+                  (forward-line)
+                  (skip-syntax-forward "-"))
+                (< (point) parse-limit))
+
+         (if (looking-at "#")
+             (goto-char parse-limit)
+           
+           (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))
+                            (from (point))
+                            (to (or (cdr bounds)
+                                    parse-limit)))
+                       (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) 
parse-limit))))))
+             (if (and (not gnuplot-token-at-point)
+                      (eq (gnuplot-token-type token) 'name)
+                      (<= p (gnuplot-token-end token)))
+                 (setq gnuplot-token-at-point token))
+             (push token tokens))))
+
+       (let ((end-of-input
+              (make-gnuplot-token :type 'end-of-input
+                                  :start parse-limit
+                                  :end parse-limit)))
+         (when (not gnuplot-token-at-point)
+           (setq gnuplot-token-at-point end-of-input))
+
+         (nreverse (cons end-of-input tokens)))))))
+
+(defun gnuplot-end-of-tokens-p (tokens)
+  (or (not tokens)
+      (equal (gnuplot-token-type (car tokens)) 'end-of-input)))
+
+
+
+;;;; 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, and the values of
+;;     capture groups.
+;;
+;;    (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 any
+  ;; calls to other rules as symbolic instructions (call SYMBOL); these
+  ;; are resolved into offsets 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)))))
+   
+     ;; 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
+                  (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
+                (jump ,(+ 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 2))
+              ,@pat1-c
+              (jump ,(- (+ pat1-l 1))))))
+
+         ;; Non-greedy-repetition (*?)
+         ((lazy-many)
+          (let* ((pat1 (cons 'sequence (cdr pat)))
+                 (pat1-c (gnuplot-compile-pattern pat1))
+                 (pat1-l (length pat1-c)))
+            `((choice 2)
+              (jump ,(+ pat1-l 2))
+              ,@pat1-c
+              (jump ,(- (+ pat1-l 2))))))
+
+         ;; 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)  
+    (destructuring-bind
+       (&rest arg-list &key eldoc &key info &allow-other-keys) args
+      (let ((accum '()))
+       (while arg-list
+         (if (memq (car arg-list) '(:eldoc :info))
+             (pop arg-list)
+           (push (car arg-list) accum))
+         (pop arg-list))
+       (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 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)
+                (if (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 t ,name)))))))))
+       object-code))))
+
+;;; The grammar.
+(defvar gnuplot-compiled-grammar
+  (eval-when-compile
+    (let ((max-lisp-eval-depth 600))
+      (gnuplot-compile-grammar
+       '((expression
+         (sequence infix-expression
+                   (maybe "?" expression ":" expression)))
+    
+        (prefix-operator
+         (either "!" "~" "-" "+"))
+
+        (infix-operator
+         (either "**" "*" "/" "%" "+" "-" "." "<" "<=" ">" ">=" "==" "!=" "eq" 
"ne"
+                 "&" "^" "|" "&&" "||"))
+
+        (infix-expression
+         (sequence
+          (many prefix-operator)
+          primary-expression
+          (many infix-operator expression)))
+
+        (primary-expression
+         (sequence
+          (either number string parenthesized-expression
+                  column-ref complex-number function-call name)
+          (many "!")
+          (maybe "**" infix-expression)
+          (maybe substring-range)))
+
+        (function-call
+         (either
+          (info-keyword
+           (sequence 
+            (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))
+          (sequence :info "elliptic_integrals"
+                    (either "EllipticK" "EllipticE" "EllipticPi")
+                    parenthesized-expression)))
+
+        (parenthesized-expression
+         (sequence "(" comma-list ")"))
+
+        (complex-number
+         (sequence
+          "{" number "," number "}"))
+
+        (column-ref
+         (sequence "$" number))
+
+        (substring-range-component
+         (maybe (either "*" expression)))
+
+        (substring-range
+         (sequence "[" (delimited-list substring-range-component ":" 2 2) "]"))
+
+;;; Assignments
+        (lhs
+         (sequence name
+                   (maybe "(" (delimited-list name "," 1) ")")))
+
+        (assignment
+         (sequence lhs "=" (either lhs expression)))
+
+;;; Lists of expressions
+        (comma-list
+         (delimited-list expression ","))
+
+        (colon-list
+         (delimited-list expression ":"))
+
+        (tuple
+         (sequence "(" (delimited-list expression "," 2 3) ")"))
+
+;;; Commands
+        (command
+         (info-keyword
+          (either plot-command splot-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)))
+
+;;; PLOT, SPLOT commands
+        (plot-command
+         (sequence
+          (kw ("pl" . "ot"))
+      
+          (either
+           ;; Parametric ranges
+           (sequence
+            (assert (gnuplot-guess-parametric-p))
+            (maybe t-axis-range) (maybe x-axis-range) (maybe y-axis-range))
+
+           ;; Non-parametric ranges
+           (sequence (maybe x-axis-range) (maybe y-axis-range)))
+
+          plot-body))
+
+        (splot-command
+         (sequence 
+          ;; This capturing group lets `gnuplot-find-using-eldoc' know
+          ;; that this is an splot command
+          (capture :splot-command (kw ("spl" . "ot")))
+
+          (either
+           ;; Parametric ranges
+           (sequence
+            (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
+           (sequence
+            (maybe x-axis-range) (maybe y-axis-range) (maybe z-axis-range)))
+
+          plot-body))
+    
+        ;; Axis ranges
+        (axis-range-component
+         (maybe (either "*" expression)))
+
+        (axis-range-body
+         (delimited-list axis-range-component ":" 2 3))
+    
+        (axis-range
+         (sequence
+          :info "ranges"
+          "[" (maybe (maybe name "=") axis-range-body) "]"))
+    
+        (x-axis-range (sequence :eldoc "X RANGE: [{<dummy>=}<min>:<max>]" 
axis-range))
+        (y-axis-range (sequence :eldoc "Y RANGE: [{<dummy>=}<min>:<max>]" 
axis-range))
+        (z-axis-range (sequence :eldoc "Z RANGE: [{<dummy>=}<min>:<max>]" 
axis-range))
+        (t-axis-range (sequence :eldoc "T RANGE: [{<dummy>=}<min>:<max>]" 
axis-range))
+        (u-axis-range (sequence :eldoc "U RANGE: [{<dummy>=}<min>:<max>]" 
axis-range))
+        (v-axis-range (sequence :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
+          (sequence (maybe iteration-spec) plot-expression plot-modifiers)
+          ","))
+
+        ;; Iteration: for [ ... ]
+        (iteration-spec
+         (sequence
+                                       ; :eldoc "for [<var> = <min> : <max> 
{:<step>}]"
+          :info "iteration"
+          "for" "[" name "=" (delimited-list expression ":" 2 3) "]"))
+   
+        ;; Expressions to plot can be preceded by any number of
+        ;; assignments, with or without commas
+        (plot-expression
+         (sequence
+          (lazy-many (sequence 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
+           (sequence
+            (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
+         (sequence 
+          (kw ("t" . "itle")) expression))
+    
+        (notitle-modifier
+         (sequence
+          :info "title"
+          (kw ("not" . "itle"))
+          (maybe string)))
+
+        (axes-modifier
+         (sequence
+          (kw ("ax" . "es")) (either "x1y1" "x1y2" "x2y1" "x2y2")))
+
+        (linecolor-modifier
+         (sequence (kw "linecolor" "lc") color-spec))
+
+        (fillstyle-modifier
+         (sequence
+          (kw "fillstyle" "fs")
+          (either
+           "empty"
+           (sequence
+            "transparent"
+            (maybe (either "pattern" "solid") expression)
+            (maybe (either "noborder" (sequence "border" expression)))))))
+
+        (color-spec
+         (either
+          "variable"
+
+          (sequence
+           "palette"
+           (either "z" (sequence (either "frac" "cb") expression)))
+
+          (sequence
+           "rgbcolor"
+           (either "variable" string))))
+
+        (with-modifier
+         (sequence
+          :info "plotting_styles"
+          (kw ("w" . "ith"))
+      
+          (capture
+           :with-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 "labels") 
+             (kw ("cir" . "cles"))
+        
+             ;; Image styles all use the same info page
+             (sequence
+              :info "image"
+              (either (kw ("ima" . "ge"))
+                      (kw ("rgbima" . "ge"))
+                      (kw ("rgba" . "lpha"))))
+        
+             ;; More complicated styles defined below
+             filledcurves-style-clause
+             vectors-style-clause)))))
+
+        (filledcurves-style-clause
+         (sequence
+          (kw ("filledc" . "urves"))
+          (maybe
+           (either
+            "closed"
+       
+            (sequence
+             (maybe (either "above" "below"))
+             (either "x1" "x2" "y1" "y2")
+             (maybe "=" expression))
+
+            (sequence "xy" "=" expression "," expression)))))
+
+        (vectors-style-clause
+         (sequence
+          (kw ("vec" . "tors"))
+          (many
+           (either
+            "nohead" "head" "heads" "filled" "empty" "nofilled" "front" "back"
+
+            (sequence
+             (either (kw "linestyle" "ls")
+                     (kw "linetype" "lt")
+                     (kw "linewidth" "lw"))
+             expression)
+
+            (sequence "size"
+                      (delimited-list expression "," 2 3))))))
+
+;;; Datafile modifiers
+        (datafile-modifier
+         (info-keyword
+          (either binary-modifier
+                  (sequence (maybe "nonuniform") (kw ("mat" . "rix")))
+                  index-modifier every-modifier
+                  thru-modifier using-modifier
+                  smooth-modifier
+                  "volatile" "noautoscale")))
+    
+        (index-modifier
+         (sequence 
+          (kw ("i" . "ndex"))
+          (either string (delimited-list expression ":" 0 2))))
+
+        (every-modifier
+         (sequence 
+          (kw ("ev" . "ery")) (delimited-list expression ";" 0)))
+    
+        (thru-modifier
+         (sequence
+          (kw "thru") expression))
+    
+        (using-modifier
+         (sequence
+          :eldoc gnuplot-find-using-eldoc 
+          (kw ("u" . "sing"))
+          (either
+           string
+           (sequence colon-list (maybe string)))))
+
+        (smooth-modifier
+         (sequence
+          (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 
+         (sequence
+          "binary" (many binary-keyword)))
+
+        (binary-keyword
+         (either
+          ;; All of these binary keywords are described on the same
+          ;; info page
+          (sequence
+           :info "keywords"
+           (either
+            "transpose" "flipx" "flipy" "flipz"
+            (sequence "flip" "=" (either "x" "y" "z"))
+            (sequence "scan" "=" name)
+            (sequence (either "dx" "dy" "dz") "=" number)
+            (sequence
+             (either "origin" "center" "perpendicular") "="
+             (delimited-list tuple ":"))
+            (sequence
+             (kw ("rot" . "ate") "rotation") "="
+             (sequence expression (maybe (kw ("d" . "eg")) (kw ("p" . 
"i")))))))
+
+          ;; remaining binary keywords have their own info pages
+          (info-keyword
+           (sequence
+            (either "array" "record")
+            "="
+            (either
+             (delimited-list tuple ":")
+             (delimited-list expression ":")))
+
+           (sequence
+            (either "skip")
+            "="
+            (delimited-list expression ":"))
+       
+           (sequence
+            (either "format" "endian" "filetype")
+            "="
+            expression))))
+    
+;;; "fit" command
+        (fit-command
+         (sequence
+          :info "fit"
+          (kw "fit")
+          (many axis-range)
+          expression
+          string
+          (many plot-modifier)
+          (kw "via")
+          (either string (delimited-list name ","))))
+
+;;; print command
+        (print-command
+         (sequence "print" (delimited-list expression ",")))
+
+;;; set commands
+        (set-command
+         (sequence
+          :eldoc "set ..."
+          :info "set-show"
+          (either (kw "set") (kw "unset") (kw "show"))
+          (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-surface-clause set-table-clause
+                   set-terminal-clause set-termoption-clause
+                   set-tics-clause set-tics-clause-2
+                   set-timestamp-clause set-timefmt-clause
+                   set-title-clause set-view-clause
+                   set-data-clause set-dtics-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 (sequence (maybe position-system) number))
+        
+        (position
+         (sequence dimension "," dimension (maybe "," dimension)))
+        
+        (to (either "to" "rto"))
+         
+;;; all the different "set ... " options
+        (set-angles-clause
+         (sequence
+          "angles" (either "degrees" "radians")))
+        
+        (set-arrow-clause
+         (sequence
+          "arrow" (maybe number)
+          (maybe "from" position)
+          (maybe to position)
+          (maybe
+           (either
+            (sequence (kw "arrowstyle" "as") number)
+            (sequence
+             (maybe (either "nohead" "head" "backhead" "heads"))
+             (maybe "size" dimension "," number
+                    (maybe "," number))
+             (maybe (either "filled" "empty" "nofilled"))
+             (maybe (either "front" "back"))
+             (maybe linestyle-spec))))))
+
+        (set-autoscale-clause
+         (sequence 
+          "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
+         (sequence
+          "bars"
+          (either number "small" "large" "fullwidth")
+          (either "front" "back")))
+
+        (set-border-clause
+         (sequence
+          "border"
+          (maybe number)
+          (maybe (either "front" "back"))
+          (maybe (kw "linewidth" "lw") expression)
+          (maybe
+           (either (kw "linestyle" "ls") (kw "linetype" "lt"))
+           expression)))
+
+        (set-boxwidth-clause
+         (sequence
+          "boxwidth" (maybe expression) (maybe (either "absolute" 
"relative"))))
+
+        (set-clabel-clause
+         (sequence
+          "clabel" (maybe string)))
+
+        (set-clip-clause
+         (sequence
+          "clip" (maybe (either "points" "one" "two"))))
+
+        (set-cntrparam-clause
+         (sequence
+          (kw "cntrparam")
+          (either
+           "linear" "cubicspline" "bspline"
+       
+           (sequence (either "points" "order") number)
+       
+           (sequence
+            (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
+         (sequence
+          :info "color_box"
+          "colorbox"
+          (many
+           (either
+            "vertical" "horizontal"
+            "default" "user"
+            (sequence "origin" expression "," expression)
+            (sequence "size" expression "," expression)
+            "front" "back"
+            "noborder" "bdefault"
+            (sequence "border" expression)))))
+
+        (set-contour-clause
+         (sequence
+          "contour" (either "base" "surface" "both")))
+
+        (set-datafile-clause
+         (sequence
+          "datafile"
+          (either (sequence :info "set_datafile_fortran"
+                            "fortran")
+                  (sequence :info "set_datafile_nofpe_trap"
+                            "nofpe_trap")
+                  (sequence :info "set_datafile_missing"
+                            "missing" (maybe string))
+                  (sequence :info "set_datafile_separator"
+                            "separator" (either "whitespace" string))
+                  (sequence :info "set_datafile_commentschars"
+                            "commentschars" (maybe string))
+                  (sequence :info "set_datafile_binary"
+                            "binary" (many binary-keyword)))))
+
+        (set-decimalsign-clause
+         (sequence
+          "decimalsign"
+          (either string (sequence "locale" (maybe string)))))
+
+        (set-dgrid3d-clause
+         (sequence
+          "dgrid3d"
+          (maybe expression)           ; fixme
+          (maybe "," expression)
+          (either
+           "splines"
+           (sequence "qnorm" expression)
+           (sequence (either "gauss" "cauchy" "exp" "box" "hann")
+                     (maybe expression)
+                     (maybe "," expression)))))
+
+        (set-dummy-clause
+         (sequence
+          "dummy"
+          name (maybe "," name)))
+
+        (set-encoding-clause
+         (sequence
+          "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
+         (sequence
+          :info "fit_"
+          "fit"
+          (either
+           (sequence "logfile" string)
+           "errorvariables" "noerrorvariables")))
+
+        (set-fontpath-clause
+         (sequence
+          "fontpath" (many string)))
+
+        (set-format-clause
+         (sequence
+          :info "format_"
+          "format"
+          (maybe (either "x" "y" "xy" "x2" "y2" "z" "cb"))
+          string))
+
+        (set-grid-clause
+         (sequence
+          "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"
+                  (sequence linestyle-spec (maybe "," linestyle-spec)))))
+
+        (linestyle-spec
+         (either
+          (sequence (kw ("lines" . "tyle") "ls") expression)
+          (sequence (maybe (kw ("linet" . "ype") "lt") expression)
+                    (maybe (kw ("linew" . "idth") "lw") expression))))
+           
+        (set-hidden3d-clause
+         (sequence
+          "hidden3d"
+          (either
+           "defaults" "front" "back"
+           (sequence "offset" expression) "nooffset"
+           (sequence "trianglepattern"
+                     (either "0" "1" "2" "3" "4" "5" "6" "7"))
+           (sequence "undefined" (either "1" "2" "3"))
+           (sequence "noundefined")
+           "altdiagonal" "noaltdiagonal"
+           "bentover" "nobentover")))
+     
+        (set-historysize-clause
+         (sequence
+          "historysize" number))
+
+        (set-isosamples-clause
+         (sequence
+          "isosamples" number (maybe "," number)))
+
+        (set-key-clause
+         (sequence
+          "key"
+          (maybe (either "on" "off"))
+          (maybe "default")
+          (maybe
+           (either
+            "inside"  "outside"
+            "lmargin" "rmargin" "tmargin" "bmargin"
+            (sequence "at" expression "," expression)))
+          (maybe
+           (either 
+                   "left" "right" "center"))
+          (maybe
+           (either
+            "top" "bottom" "center"))
+          (maybe (either "vertical" "horizontal"))
+          (maybe (either "Left" "Right"))
+          (maybe (either "reverse" "noreverse" "invert" "noinvert"))
+          (maybe "samplen" number)
+          (maybe "spacing" number)
+          (maybe "width" number)
+          (maybe "width" number)
+          (maybe (either "autotitle" "noautotitle") (maybe "columnheader"))
+          (maybe "title" expression)
+          (maybe (either "enhanced" "noenhanced"))
+          (maybe "font" string)
+          (maybe "textcolor" color-spec)
+          (maybe (either "box" "nobox")
+                 linestyle-spec)
+          (maybe "maxcols" (either number "auto"))
+          (maybe "maxrows" (either number "auto"))))
+
+        (set-label-clause
+         (sequence
+          "label"
+          (maybe number)
+          (maybe string)
+          (maybe "at" expression "," expression)
+          (maybe (either "left" "center" "right"))
+          (maybe (either "norotate" (sequence "rotate" "by" expression)))
+          (maybe "font" string)
+          (maybe "noenhanced")
+          (maybe (either "front" "back"))
+          (maybe "textcolor" color-spec)
+;         (maybe (either "nopoint" (sequence "point" point-style)))
+          (maybe "offset" position "," position)))
+
+        (set-loadpath-clause
+         (sequence
+          "loadpath" (many string)))
+
+        (set-locale-clause
+         (sequence
+          "locale" (maybe string)))
+
+        (set-logscale-clause
+         (sequence
+          "logscale"
+          (either "x" "y" "xy" "x2" "y2" "z" "cb" name)))
+
+        (set-mapping-clause
+         (sequence
+          "mapping" (either "cartesian" "spherical" "cylindrical")))
+
+        (set-margin-clause
+         (sequence
+          (either "bmargin" "lmargin" "rmargin" "tmargin")
+          (maybe "at" "screen") expression))
+
+        ;; TODO: set-mouse-clause
+
+        (set-multiplot-clause
+         (sequence
+          "multiplot"
+          (maybe
+           (sequence 
+            "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
+         (sequence
+          :info "mxtics"
+          (either "mxtics" "mytics" "mztics" "mx2tics" "my2tics" "mcbtics")
+          (either "default" number)))
+
+        ;; "set object", objects, dimensions, positions
+        (set-object-clause
+         (sequence
+          "object"
+          (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
+         (sequence
+          "rectangle"
+          (either
+           (sequence "from" position (either "to" "rto") position)
+           (sequence "center" position "size" dimension "," dimension)
+           (sequence "at" position "size" dimension "," dimension))))
+
+        (ellipse-object
+         (sequence
+          "ellipse"
+          (either "at" "center") position
+          "size" dimension "," dimension
+          (maybe "angle" number)))
+
+        (circle-object
+         (sequence
+          "circle"
+          (either "at" "center") position
+          "size" dimension
+          (maybe "arc" "[" number ":" number "]")))
+
+        (polygon-object
+         (sequence
+          "polygon"
+          "from" position (many (either "to" "rto") position)))
+
+        ;; "set offsets"
+        (set-offsets-clause
+         (sequence
+          "offsets"
+          (delimited-list (sequence (maybe "graph") expression) "," 4 4)))
+
+        (set-origin-clause
+         (sequence
+          "origin" expression "," expression))
+
+        (set-output-clause
+         (sequence
+          "output" (maybe string)))
+
+        (set-parametric-clause
+         (sequence
+          "parametric"))
+
+        (set-pm3d-clause
+         (sequence
+          "pm3d"
+          (many
+           (either
+            (sequence "at" name)
+            (sequence "interpolate" number "," number)
+            (either "scansautomatic" "scansforward" "scansbackward" 
"depthorder")
+            (sequence "flush" (either "begin" "center" "end"))
+            (either "ftriangles" "noftriangles")
+            (either "clip1in" "clip4in")
+            (sequence "corners2color"
+                      (either "mean" "geomean" "median" "min" "max" "c1" "c2" 
"c3" "c4"))
+            (sequence "hidden3d" number)
+            "nohidden3d"
+            "implicit" "explicit" "map"))))
+
+        (set-palette-clause
+         (sequence
+          "palette"
+          (many
+           (either
+            "gray" "color"
+            (sequence "gamma" number)
+            (sequence "rgbformulae" number "," number "," number)
+            "defined"                  ; not complete
+            (sequence "functions" expression "," expression "," expression)
+            (sequence "file" string (many datafile-modifier))
+            (sequence (either "RGB" "HSV" "CMY" "YIQ" "XYZ"))
+            "positive" "negative"
+            "nops_allcF" "ps_allcF"
+            (sequence "maxcolors" number)))))
+
+        (set-pointsize-clause
+         (sequence
+          "pointsize" number))
+
+        (set-polar-clause "polar")
+
+        (set-print-clause
+         (sequence
+          "print"
+          (maybe string)))
+
+        (set-samples-clause
+         (sequence
+          "samples" expression (maybe "," expression)))
+
+        (set-size-clause
+         (sequence
+          "size"
+          (either
+           "square" "nosquare"
+           (sequence "ratio" expression)
+           "noratio"
+           (sequence expression "," expression))))
+
+        ;; TODO: set style  *
+
+        (set-surface-clause "surface")
+        
+        (set-table-clause (sequence "table" (maybe string)))
+
+        (set-terminal-clause           ; not sure how to do this...
+         (sequence "terminal" (maybe (either "push" "pop"))))
+
+        (set-termoption-clause
+         (sequence
+          "termoption"
+          (either
+           "enhanced" "noenhanced"
+           (sequence "font" string)
+           "solid" "dashed"
+           (sequence (kw "linewidth" "lw") expression))))
+
+        (set-tics-clause
+         (sequence
+          (either "tics" "xtics" "ytics" "ztics" "x2tics" "y2tics" "cbtics")
+          (many
+           (either
+            "axis" "border" "mirror" "nomirror" "in" "out"
+            (sequence "scale" (either "default" expression "," expression))
+            (sequence "rotate" "by" expression) "norotate"
+            (sequence "offset" expression) "nooffset"
+            (sequence "format" string)
+            (sequence "font" string)
+            (sequence "textcolor" color-spec)))))
+
+        (set-tics-clause-2
+         (sequence
+          "tics" (either "front" "back")))
+
+        (set-timestamp-clause
+         (sequence
+          "timestamp"
+          (maybe string)
+          (maybe (either "top" "bottom"))
+          (maybe (either "rotate" "norotate"))
+          (maybe "offset" position "," position)
+          (maybe "font" string)))
+
+        (set-timefmt-clause
+         (sequence
+          "timefmt" string))
+
+        (set-title-clause
+         (sequence
+          "title" 
+          (maybe string)
+          (maybe "offset" position) 
+          (maybe "font" string)
+          (maybe (kw "textcolor" "tc")
+                           (either "default" color-spec))
+          (maybe (either "enhanced" "noenhanced"))))
+
+        (set-view-clause
+         (sequence
+          "view"
+          (either
+           "map"
+           (sequence (either "equal" "noequal") (maybe (either "xy" "xyz")))
+           (delimited-list expression ","))))
+
+        (set-data-clause
+         (sequence
+          :info "xdata"
+          (either "xdata" "ydata" "zdata" "x2data" "y2data" "cbdata")
+          (maybe "time")))
+
+        (set-dtics-clause
+         (sequence
+          :info "xdtics"
+          (either "xdtics" "ydtics" "zdtics" "x2dtics" "y2dtics" "cbdtics")))
+                       
+         ;; TODO set xlabel etc.
+        
+        (set-mtics-clause
+         (sequence
+          :info "xmtics"
+          (either "xmtics" "ymtics" "zmtics" "x2mtics" "y2mtics" "cbmtics")))
+    
+        (set-range-clause
+         (sequence
+          :info "xrange"
+          (either "xrange" "yrange" "zrange" "trange" "urange" "vrange"
+                  "rrange")
+          (either
+           "restore"
+           (sequence
+            "[" (maybe
+                 (sequence
+                  (maybe axis-range-component) ":"
+                  (maybe axis-range-component)))
+            "]"
+            (maybe (either "reverse" "noreverse" "writeback" 
"nowriteback"))))))
+
+        ;; TODO set xtics etc.
+
+        (set-xyplane-clause
+         (sequence
+          "xyplane" (either "at" "relative") expression))
+
+        (set-zero-clause
+         (sequence
+          "zero" expression))
+
+        (set-zeroaxis-clause
+         (sequence
+          :info "zeroaxis"
+          (either "xzeroaxis" "x2zeroaxis" "yzeroaxis" "y2zeroaxis" 
"zzeroaxis")
+          linestyle-spec))
+         
+
+       ;;; Other commands
+       (cd-command
+       (sequence "cd" string))
+
+       (call-command
+       (sequence "call" string (many expression)))
+
+       (simple-command
+       (either "clear" "exit" "quit" "pwd" "refresh" "replot" "reread" "reset"
+               "shell"))
+
+       (eval-command
+       (sequence "eval" expression))
+
+       (load-command
+       (sequence "load" string))
+
+       (lower-raise-command (sequence (either "lower" "raise") number))
+
+       (pause-command
+       (sequence
+        "pause"
+        (either
+         number
+         (sequence "mouse" (maybe endcondition (maybe "," endcondition))))
+        string))
+
+       (endcondition (either "keypress" "button1" "button2" "button3" "close" 
"any"))
+
+       (save-command
+       (sequence
+        "save"
+        (either "functions" "variables" "terminal" "set")
+        string))
+
+       (system-command
+       (sequence "system" string))
+
+       (test-command
+       (sequence
+        "test"
+        (either
+         "terminal"
+         (sequence
+          "palette"
+          (maybe
+           (either "rgb" "rbg" "grb" "gbr" "brg" "bgr"))))))
+
+       (undefine-command
+       (sequence "undefine" (many name)))
+
+       (update-command
+       (sequence "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
+`gnuplop-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 (inst 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)
+         (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))
+
+      (with-gnuplot-trace-buffer (erase-buffer))
+
+      (when start-symbol               ; HACK FIXME
+       (while (not (equal (aref inst pc)
+                          '(label start-symbol)))
+         (incf pc))
+       (incf pc))
+
+      (setq gnuplot-completions nil
+           gnuplot-eldoc nil
+           gnuplot-info-at-point nil
+           gnuplot-captures nil)
+
+      (flet ((advance
+             ()
+             (if (eq (pop tokens) gnuplot-token-at-point)
+                 (gnuplot-scan-stack stack tokens)))
+            (fail () (setq fail t)))
+
+       ;; Main loop
+       (while t
+         (let* ((inst (aref inst pc))
+                (opcode (car inst))
+                (token (car tokens))
+                (end-of-tokens
+                 (or (gnuplot-end-of-tokens-p tokens)
+                     (and completing-p
+                          ;; (<= (point) (gnuplot-token-end token))
+                          (eq token gnuplot-token-at-point)))))
+           (gnuplot-trace "%s\t%s\t%s\n" pc inst (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 (advance)))))
+
+             ;; (any): match any token
+             ((any)
+              (if end-of-tokens
+                  (fail)
+                (advance)))
+             
+             ;; (jump OFFSET FIXED): jump to instruction at PC + OFFSET,
+             ;; or directly to OFFSET if FIXED is non-nil
+             ((jump)
+              (let ((offset (cadr inst))
+                    (fixed (caddr inst)))
+                (setq pc (if fixed offset (+ pc offset))
+                      jump t)))
+
+             ;; (call OFFSET FIXED): push the next instruction as a
+             ;; return location and jump like (jump), above
+             ((call)
+              (let ((offset (cadr inst))
+                    (fixed (caddr inst)))
+                (push `(return ,(+ pc 1)) stack)
+                (setq pc (if fixed offset (+ pc offset))
+                      jump t)))
+
+             ;; (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)          ; end of pattern
+                  (throw 'return tokens)
+                (let* ((r (pop stack))
+                       (r-pc (cadr r)))
+                  (setq pc r-pc
+                        jump t))))
+
+             ;; (choice OFFSET): push PC + OFFSET onto the stack of
+             ;; backtracking points and continue at next instruction
+             ((choice)
+              (let ((offset (cadr inst)))
+                (push `(,stack ,tokens ,(+ pc offset) ,gnuplot-captures)
+                      backtrack)))
+
+             ;; (commit OFFSET): discard most recent backtrack point
+             ;; and jump to PC + OFFSET
+             ((commit)
+              (let ((offset (cadr inst)))
+                (if (not backtrack)
+                    (error "no more backtrack points in commit"))
+                (pop backtrack)
+                (setq pc (+ pc offset)
+                      jump t)))
+
+             ;; (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)))))
+
+             (t
+              (error "bad instruction: %s" inst)))
+
+           ;; Increment PC unless a jump occurred
+           (if jump
+               (setq jump nil)
+             (incf pc))
+
+           ;; Backtrack on failure
+           (when fail
+             (if (not backtrack)       ; Out of backtracking stack
+                 (throw 'return nil)
+               (gnuplot-trace "\t*fail*\t%s\n" (length backtrack))
+               ;; If we got as far as token-at-point before failing,
+               ;; scan the stack for eldoc and info strings
+               (when end-of-tokens
+                 (gnuplot-scan-stack stack tokens))
+
+               (destructuring-bind
+                   (bt-stack bt-tokens bt-pc bt-captures)
+                   (pop backtrack)
+                 (setq stack bt-stack
+                       tokens bt-tokens
+                       pc bt-pc
+                       gnuplot-captures bt-captures
+                       fail nil))))))))))
+
+(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))
+
+  (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))
+              (not (eq position tokens)))
+         (case type
+           ((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)))
+    (gnuplot-match-pattern gnuplot-compiled-grammar tokens completing-p)))
+
+(defun gnuplot-completions ()
+  (gnuplot-parse-at-point t)
+  gnuplot-completions)
+
+(defun gnuplot-completion-at-point ()
+  "Return completions of keyword preceding point."
+  (let* ((end (point))
+        (beg (max
+              (save-excursion (skip-syntax-backward "w_") (point))
+              (gnuplot-point-at-beginning-of-command)))
+        (word nil)
+        (completions (gnuplot-completions)))
+    (unless (= beg end)
+      (setq word (buffer-substring beg end)
+           completions (all-completions word completions)))
+
+    (when (= (length completions) 1)
+      (setq completions (list (concat (car completions) " ")))
+      (gnuplot-completions))
+
+    (if completions
+       (list beg end completions)
+      (message "No gnuplot keywords complete '%s'" word)
+      nil)))
+
+(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)))))
+
+(define-key gnuplot-mode-map (kbd "C-c h") 'gnuplot-help-function)
+
+(defun gnuplot-info-at-point ()
+  "Open the relevant gnuplot info page for the construction at point."
+  (interactive)
+  (gnuplot-parse-at-point nil)
+  (and gnuplot-info-at-point
+       (info-other-window (format "(gnuplot)%s" gnuplot-info-at-point))))
+
+(define-key gnuplot-mode-map (kbd "C-c C-/") 'gnuplot-info-at-point)
+
+(add-hook
+ 'gnuplot-mode-hook
+ (lambda ()
+   (set (make-local-variable 'eldoc-documentation-function)
+       'gnuplot-eldoc-function)))
+
+
+;;; 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..61f7123
--- /dev/null
+++ b/gnuplot-debug-context.el
@@ -0,0 +1,64 @@
+
+(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 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 ()
+  (with-gnuplot-trace-buffer
+   (insert "\n-- * backtrace: * --\n\n")
+   (dolist (x stack)
+     (insert (format "%s\n\n" x)))
+   (insert "\n-- end backtrace  --\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\n")
+     (dotimes (i (length inst))
+       (insert (format "%s\t%s\n" i (aref inst i))))
+     (insert "\n--  end compiled code --\n\n")
+     (pop-to-buffer (current-buffer)))))
+
+(defun gnuplot-dump-captures ()
+  (interactive)
+  (with-gnuplot-trace-buffer
+   (insert "\n-- * capture groups: * --\n\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 "\n-- end capture groups  --\n\n")))
+
+(provide 'gnuplot-debug-context)
\ No newline at end of file



reply via email to

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