15dfecf96Smrg;
25dfecf96Smrg;; Copyright (c) 2002 by The XFree86 Project, Inc.
35dfecf96Smrg;;
45dfecf96Smrg;; Permission is hereby granted, free of charge, to any person obtaining a
55dfecf96Smrg;; copy of this software and associated documentation files (the "Software"),
65dfecf96Smrg;; to deal in the Software without restriction, including without limitation
75dfecf96Smrg;; the rights to use, copy, modify, merge, publish, distribute, sublicense,
85dfecf96Smrg;; and/or sell copies of the Software, and to permit persons to whom the
95dfecf96Smrg;; Software is furnished to do so, subject to the following conditions:
105dfecf96Smrg;;
115dfecf96Smrg;; The above copyright notice and this permission notice shall be included in
125dfecf96Smrg;; all copies or substantial portions of the Software.
135dfecf96Smrg;;
145dfecf96Smrg;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
155dfecf96Smrg;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
165dfecf96Smrg;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
175dfecf96Smrg;; THE XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
185dfecf96Smrg;; WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
195dfecf96Smrg;; OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
205dfecf96Smrg;; SOFTWARE.
215dfecf96Smrg;;
225dfecf96Smrg;; Except as contained in this notice, the name of the XFree86 Project shall
235dfecf96Smrg;; not be used in advertising or otherwise to promote the sale, use or other
245dfecf96Smrg;; dealings in this Software without prior written authorization from the
255dfecf96Smrg;; XFree86 Project.
265dfecf96Smrg;;
275dfecf96Smrg;; Author: Paulo César Pereira de Andrade
285dfecf96Smrg;;
295dfecf96Smrg;;
305dfecf96Smrg;; $XFree86: xc/programs/xedit/lisp/modules/indent.lsp,v 1.6 2003/01/16 03:50:46 paulo Exp $
315dfecf96Smrg;;
325dfecf96Smrg
335dfecf96Smrg(provide "indent")
345dfecf96Smrg(require "xedit")
355dfecf96Smrg(in-package "XEDIT")
365dfecf96Smrg
375dfecf96Smrg(defconstant indent-spaces '(#\Tab #\Space))
385dfecf96Smrg
395dfecf96Smrg;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
405dfecf96Smrg;; The final indentation function.
415dfecf96Smrg;; Parameters:
425dfecf96Smrg;;	indent
435dfecf96Smrg;;		Number of spaces to insert
445dfecf96Smrg;;	offset
455dfecf96Smrg;;		Offset to where indentation should be added
465dfecf96Smrg;;	no-tabs
475dfecf96Smrg;;		If set, tabs aren't inserted
485dfecf96Smrg;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
495dfecf96Smrg(defun indent-text (indent offset &optional no-tabs
505dfecf96Smrg		    &aux start line length index current tabs spaces string
515dfecf96Smrg			 barrier base result (point (point))
525dfecf96Smrg		   )
535dfecf96Smrg
545dfecf96Smrg    ;; Initialize
555dfecf96Smrg    (setq
565dfecf96Smrg	start	(scan offset :eol :left)
575dfecf96Smrg	line	(read-text start (- offset start))
585dfecf96Smrg	length	(length line)
595dfecf96Smrg	index	(1- length)
605dfecf96Smrg	current	0
615dfecf96Smrg	base	0
625dfecf96Smrg    )
635dfecf96Smrg
645dfecf96Smrg    (and (minusp indent) (setq indent 0))
655dfecf96Smrg
665dfecf96Smrg    ;; Skip any spaces after offset, "paranoia check"
675dfecf96Smrg    (while (member (char-after offset) indent-spaces)
685dfecf96Smrg	(incf offset)
695dfecf96Smrg    )
705dfecf96Smrg
715dfecf96Smrg    ;; Check if there are only spaces before `offset' and the line `start'
725dfecf96Smrg    (while (and (>= index 0) (member (char line index) indent-spaces))
735dfecf96Smrg	(decf index)
745dfecf96Smrg    )
755dfecf96Smrg
765dfecf96Smrg    ;; `index' will be zero if there are only spaces in the `line'
775dfecf96Smrg    (setq barrier (+ start (incf index)))
785dfecf96Smrg
795dfecf96Smrg    ;; Calculate `base' unmodifiable indentation, if any
805dfecf96Smrg    (dotimes (i index)
815dfecf96Smrg	(if (char= (char line i) #\Tab)
825dfecf96Smrg	    (incf base (- 8 (rem base 8)))
835dfecf96Smrg	    (incf base)
845dfecf96Smrg	)
855dfecf96Smrg    )
865dfecf96Smrg
875dfecf96Smrg    ;; If any non blank character would need to be deleted
885dfecf96Smrg    (and (> base indent) (return-from indent-text nil))
895dfecf96Smrg
905dfecf96Smrg    ;; Calculate `current' indentation
915dfecf96Smrg    (setq current base)
925dfecf96Smrg    (while (< index length)
935dfecf96Smrg	(if (char= (char line index) #\Tab)
945dfecf96Smrg	    (incf current (- 8 (rem current 8)))
955dfecf96Smrg	    (incf current)
965dfecf96Smrg	)
975dfecf96Smrg	(incf index)
985dfecf96Smrg    )
995dfecf96Smrg
1005dfecf96Smrg    ;; Maybe could also "optimize" the indentation even if it is already
1015dfecf96Smrg    ;; correct, removing spaces "inside" tabs.
1025dfecf96Smrg    (when (/= indent current)
1035dfecf96Smrg	(if no-tabs
1045dfecf96Smrg	    (setq
1055dfecf96Smrg		length	(- indent base)
1065dfecf96Smrg		result	(+ barrier length)
1075dfecf96Smrg		string	(make-string length :initial-element #\Space)
1085dfecf96Smrg	    )
1095dfecf96Smrg	    (progn
1105dfecf96Smrg		(multiple-value-setq (tabs spaces) (floor (- indent base) 8))
1115dfecf96Smrg		(setq
1125dfecf96Smrg		    length	(+ tabs spaces)
1135dfecf96Smrg		    result	(+ barrier length)
1145dfecf96Smrg		    string	(make-string length :initial-element #\Tab)
1155dfecf96Smrg		)
1165dfecf96Smrg		(fill string #\Space :start tabs)
1175dfecf96Smrg	    )
1185dfecf96Smrg	)
1195dfecf96Smrg
1205dfecf96Smrg	(replace-text barrier offset string)
1215dfecf96Smrg	(and (>= offset point) (>= point barrier) (goto-char result))
1225dfecf96Smrg    )
1235dfecf96Smrg)
1245dfecf96Smrg(compile 'indent-text)
1255dfecf96Smrg
1265dfecf96Smrg;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1275dfecf96Smrg;; Helper function, returns indentation of a given offset
1285dfecf96Smrg;; If `align' is set, stop once a non blank character is seen, that
1295dfecf96Smrg;; is, use `offset' only as a line identifier
1305dfecf96Smrg;; If `resolve' is set, it means that the offset is just a hint, it
1315dfecf96Smrg;; maybe anywhere in the line
1325dfecf96Smrg;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1335dfecf96Smrg(defun offset-indentation (offset &key resolve align
1345dfecf96Smrg			   &aux
1355dfecf96Smrg			   char
1365dfecf96Smrg			   line
1375dfecf96Smrg			   (start (scan offset :eol :left))
1385dfecf96Smrg			   (indent 0))
1395dfecf96Smrg    (if resolve
1405dfecf96Smrg	(loop
1415dfecf96Smrg	    (if (characterp (setq char (char-after start)))
1425dfecf96Smrg		(if (char= char #\Tab)
1435dfecf96Smrg		    (incf indent (- 8 (rem indent 8)))
1445dfecf96Smrg		    ;; Not a tab, check if is a space
1455dfecf96Smrg		    (if (char= char #\Space)
1465dfecf96Smrg			(incf indent)
1475dfecf96Smrg			;; Not a tab neither a space
1485dfecf96Smrg			(return indent)
1495dfecf96Smrg		    )
1505dfecf96Smrg		)
1515dfecf96Smrg		;; EOF found
1525dfecf96Smrg		(return indent)
1535dfecf96Smrg	    )
1545dfecf96Smrg	    ;; Increment offset to check next character
1555dfecf96Smrg	    (incf start)
1565dfecf96Smrg	)
1575dfecf96Smrg	(progn
1585dfecf96Smrg	    (setq line (read-text start (- offset start)))
1595dfecf96Smrg	    (dotimes (i (length line) indent)
1605dfecf96Smrg		(if (char= (setq char (char line i)) #\Tab)
1615dfecf96Smrg		    (incf indent (- 8 (rem indent 8)))
1625dfecf96Smrg		    (progn
1635dfecf96Smrg			(or align (member char indent-spaces)
1645dfecf96Smrg			    (return indent)
1655dfecf96Smrg			)
1665dfecf96Smrg			(incf indent)
1675dfecf96Smrg		    )
1685dfecf96Smrg		)
1695dfecf96Smrg	    )
1705dfecf96Smrg	)
1715dfecf96Smrg    )
1725dfecf96Smrg)
1735dfecf96Smrg(compile 'offset-indentation)
1745dfecf96Smrg
1755dfecf96Smrg;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1765dfecf96Smrg;;  A default/fallback indentation function, just copy indentation
1775dfecf96Smrg;; of previous line.
1785dfecf96Smrg;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1795dfecf96Smrg(defun default-indent (syntax syntable)
1805dfecf96Smrg    (let
1815dfecf96Smrg	(
1825dfecf96Smrg	(offset (scan (point) :eol :left))
1835dfecf96Smrg	start
1845dfecf96Smrg	left
1855dfecf96Smrg	right
1865dfecf96Smrg	)
1875dfecf96Smrg
1885dfecf96Smrg	syntable	;; XXX hack to not generate warning about unused
1895dfecf96Smrg			;; variable, should be temporary (until unused
1905dfecf96Smrg			;; variables can be declared as such)
1915dfecf96Smrg
1925dfecf96Smrg	(if
1935dfecf96Smrg	    (or
1945dfecf96Smrg		;; if indentation is disabled
1955dfecf96Smrg		(and
1965dfecf96Smrg		    (hash-table-p (syntax-options syntax))
1975dfecf96Smrg		    (gethash :disable-indent (syntax-options syntax))
1985dfecf96Smrg		)
1995dfecf96Smrg		;; or if not at the start of a new line
2005dfecf96Smrg		(> (scan offset :eol :right) offset)
2015dfecf96Smrg	    )
2025dfecf96Smrg	    (return-from default-indent)
2035dfecf96Smrg	)
2045dfecf96Smrg
2055dfecf96Smrg	(setq left offset)
2065dfecf96Smrg	(loop
2075dfecf96Smrg	    (setq
2085dfecf96Smrg		start left
2095dfecf96Smrg		left (scan start :eol :left :count 2)
2105dfecf96Smrg		right (scan left :eol :right)
2115dfecf96Smrg	    )
2125dfecf96Smrg	    ;; if start of file reached
2135dfecf96Smrg	    (and (>= left start) (return))
2145dfecf96Smrg	    (when
2155dfecf96Smrg		(setq
2165dfecf96Smrg		    start
2175dfecf96Smrg		    (position-if-not
2185dfecf96Smrg			#'(lambda (char) (member char indent-spaces))
2195dfecf96Smrg			(read-text left (- right left))
2205dfecf96Smrg		    )
2215dfecf96Smrg		)
2225dfecf96Smrg
2235dfecf96Smrg		;; indent the current line
2245dfecf96Smrg		(indent-text (offset-indentation (+ left start) :align t) offset)
2255dfecf96Smrg		(return)
2265dfecf96Smrg	    )
2275dfecf96Smrg	)
2285dfecf96Smrg    )
2295dfecf96Smrg)
2305dfecf96Smrg(compile 'default-indent)
2315dfecf96Smrg
2325dfecf96Smrg;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2335dfecf96Smrg;; Helper function
2345dfecf96Smrg;;   Clear line before cursor if it is empty
2355dfecf96Smrg;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2365dfecf96Smrg(defun indent-clear-empty-line (&aux left offset right line index)
2375dfecf96Smrg    (setq
2385dfecf96Smrg	offset	(scan (point) :eol :left)
2395dfecf96Smrg	left	(scan offset :eol :left :count 2)
2405dfecf96Smrg	right	(scan left :eol :right)
2415dfecf96Smrg    )
2425dfecf96Smrg
2435dfecf96Smrg    ;; If not at the first line in the file and line is not already empty
2445dfecf96Smrg    (when (and (/= offset left) (/= left right))
2455dfecf96Smrg	(setq
2465dfecf96Smrg	    line	(read-text left (- right left))
2475dfecf96Smrg	    index	(1- (length line))
2485dfecf96Smrg	)
2495dfecf96Smrg	(while (and (>= index 0) (member (char line index) indent-spaces))
2505dfecf96Smrg	    (decf index)
2515dfecf96Smrg	)
2525dfecf96Smrg	;; If line was only spaces
2535dfecf96Smrg	(and (minusp index) (replace-text left right ""))
2545dfecf96Smrg    )
2555dfecf96Smrg)
2565dfecf96Smrg
2575dfecf96Smrg;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2585dfecf96Smrg;;  Macro to be called whenever an indentation rule decides that
2595dfecf96Smrg;; the parser is done.
2605dfecf96Smrg;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2615dfecf96Smrg(defmacro indent-macro-terminate (&optional result)
2625dfecf96Smrg    `(return-from ind-terminate-block ,result)
2635dfecf96Smrg)
2645dfecf96Smrg
2655dfecf96Smrg;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2665dfecf96Smrg;; Like indent-terminate, but "rejects" the input for the current line
2675dfecf96Smrg;; and terminates the loop.
2685dfecf96Smrg;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2695dfecf96Smrg(defmacro indent-macro-reject (&optional result)
2705dfecf96Smrg   `(progn
2715dfecf96Smrg	(setq ind-state ind-prev-state)
2725dfecf96Smrg	(return-from ind-terminate-block ,result)
2735dfecf96Smrg    )
2745dfecf96Smrg)
2755dfecf96Smrg
2765dfecf96Smrg;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2775dfecf96Smrg;; Like indent-reject, but "rejects" anything before the current token
2785dfecf96Smrg;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2795dfecf96Smrg(defmacro indent-macro-reject-left (&optional result)
2805dfecf96Smrg   `(progn
2815dfecf96Smrg	(setq ind-state ind-matches)
2825dfecf96Smrg	(return-from ind-terminate-block ,result)
2835dfecf96Smrg    )
2845dfecf96Smrg)
2855dfecf96Smrg
2865dfecf96Smrg
2875dfecf96Smrg(defstruct indtoken
2885dfecf96Smrg    regex			;; a string, character or regex
2895dfecf96Smrg    token			;; the resulting token, nil or a keyword
2905dfecf96Smrg    begin			;; begin a new table
2915dfecf96Smrg    switch			;; switch to another table
2925dfecf96Smrg    ;; begin and switch fields are used like the ones for the syntax highlight
2935dfecf96Smrg    ;; syntoken structure.
2945dfecf96Smrg    label			;; filed at compile time
2955dfecf96Smrg    code			;; code to execute when it matches
2965dfecf96Smrg)
2975dfecf96Smrg
2985dfecf96Smrg(defstruct indtable
2995dfecf96Smrg    label			;; a keyword, name of the table
3005dfecf96Smrg    tokens			;; list of indtoken structures
3015dfecf96Smrg    tables			;; list of indtable structures
3025dfecf96Smrg    augments			;; augment list
3035dfecf96Smrg)
3045dfecf96Smrg
3055dfecf96Smrg(defstruct indaugment
3065dfecf96Smrg    labels			;; list of keywords labeling tables
3075dfecf96Smrg)
3085dfecf96Smrg
3095dfecf96Smrg(defstruct indinit
3105dfecf96Smrg    variables			;; list of variables and optional initialization
3115dfecf96Smrg    ;; Format of variables must be suitable to LET*, example of call:
3125dfecf96Smrg    ;;	(indinit
3135dfecf96Smrg    ;;	    var1		;; initialized to NIL
3145dfecf96Smrg    ;;	    (var2 (afun))	;; initialized to the value returned by AFUN
3155dfecf96Smrg    ;;	)
3165dfecf96Smrg)
3175dfecf96Smrg
3185dfecf96Smrg(defstruct indreduce
3195dfecf96Smrg    token			;; reduced token
3205dfecf96Smrg    rules			;; list of rules
3215dfecf96Smrg    label			;; unique label associated with rule, this
3225dfecf96Smrg				;; field is automatically filled in the
3235dfecf96Smrg				;; compilation process. this field exists
3245dfecf96Smrg				;; to allow several indreduce definitions
3255dfecf96Smrg				;; that result in the same token
3265dfecf96Smrg    check			;; FORM evaluated, if T apply reduce rule
3275dfecf96Smrg    code			;; PROGN to be called when a rule matches
3285dfecf96Smrg)
3295dfecf96Smrg
3305dfecf96Smrg;; NOTE, unlike "reduce" rules, "resolve" rules cannot be duplicated
3315dfecf96Smrg(defstruct indresolve
3325dfecf96Smrg    match			;; the matched token (or a list of tokens)
3335dfecf96Smrg    code			;; PROGN to apply for this token
3345dfecf96Smrg)
3355dfecf96Smrg
3365dfecf96Smrg(defstruct indent
3375dfecf96Smrg    reduces			;; list of indreduce structures
3385dfecf96Smrg    tables			;; list of indtable structures
3395dfecf96Smrg    inits			;; initialization list
3405dfecf96Smrg    resolves			;; list of indresolve structures
3415dfecf96Smrg    token-code			;; code to execute when a token matches
3425dfecf96Smrg    check-code			;; code to execute before applying a reduce rule
3435dfecf96Smrg    reduce-code			;; code to execute after reduce rule
3445dfecf96Smrg    resolve-code		;; code to execute when matching a token
3455dfecf96Smrg)
3465dfecf96Smrg
3475dfecf96Smrg(defmacro defindent (variable label &rest lists)
3485dfecf96Smrg   `(if (boundp ',variable)
3495dfecf96Smrg	,variable
3505dfecf96Smrg	(progn
3515dfecf96Smrg	    (proclaim '(special ,variable))
3525dfecf96Smrg	    (setq ,variable (compile-indent-table ,label ,@lists))
3535dfecf96Smrg	)
3545dfecf96Smrg    )
3555dfecf96Smrg)
3565dfecf96Smrg
3575dfecf96Smrg
3585dfecf96Smrg;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3595dfecf96Smrg;; Create an indent token.
3605dfecf96Smrg;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3615dfecf96Smrg(defmacro indtoken (pattern token
3625dfecf96Smrg		    &key icase nospec begin switch code (nosub t))
3635dfecf96Smrg    (setq pattern (re-comp (eval pattern) :icase icase :nospec nospec :nosub nosub))
3645dfecf96Smrg    (when (consp (re-exec pattern "" :notbol t :noteol t))
3655dfecf96Smrg	(error "INDTOKEN: regex ~A matches empty string" pattern)
3665dfecf96Smrg    )
3675dfecf96Smrg
3685dfecf96Smrg    ;; result of macro, return token structure
3695dfecf96Smrg    (make-indtoken
3705dfecf96Smrg	:regex	pattern
3715dfecf96Smrg	:token	token
3725dfecf96Smrg	:begin	begin
3735dfecf96Smrg	:switch	switch
3745dfecf96Smrg	:code	code
3755dfecf96Smrg    )
3765dfecf96Smrg)
3775dfecf96Smrg
3785dfecf96Smrg
3795dfecf96Smrg;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3805dfecf96Smrg;; Create an indentation table. Basically a list of indentation tokens.
3815dfecf96Smrg;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3825dfecf96Smrg(defun indtable (label &rest definitions)
3835dfecf96Smrg    ;; check for simple errors
3845dfecf96Smrg    (unless (keywordp label)
3855dfecf96Smrg	(error "INDTABLE: ~A is not a keyword" label)
3865dfecf96Smrg    )
3875dfecf96Smrg    (dolist (item definitions)
3885dfecf96Smrg	(unless
3895dfecf96Smrg	    (or
3905dfecf96Smrg		(atom item)
3915dfecf96Smrg		(indtoken-p item)
3925dfecf96Smrg		(indtable-p item)
3935dfecf96Smrg		(indaugment-p item)
3945dfecf96Smrg	    )
3955dfecf96Smrg	    (error "INDTABLE: invalid indent table argument ~A" item)
3965dfecf96Smrg	)
3975dfecf96Smrg    )
3985dfecf96Smrg
3995dfecf96Smrg    ;; return indent table structure
4005dfecf96Smrg    (make-indtable
4015dfecf96Smrg	:label		label
4025dfecf96Smrg	:tokens		(remove-if-not #'indtoken-p definitions)
4035dfecf96Smrg	:tables		(remove-if-not #'indtable-p definitions)
4045dfecf96Smrg	:augments	(remove-if-not #'indaugment-p definitions)
4055dfecf96Smrg    )
4065dfecf96Smrg)
4075dfecf96Smrg
4085dfecf96Smrg
4095dfecf96Smrg;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4105dfecf96Smrg;; Add identifier to list of augment tables.
4115dfecf96Smrg;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4125dfecf96Smrg(defun indaugment (&rest keywords)
4135dfecf96Smrg    (dolist (keyword keywords)
4145dfecf96Smrg	(unless (keywordp keyword)
4155dfecf96Smrg	    (error "INDAUGMENT: bad indent table label ~A" keyword)
4165dfecf96Smrg	)
4175dfecf96Smrg    )
4185dfecf96Smrg
4195dfecf96Smrg    ;; return augment list structure
4205dfecf96Smrg    (make-indaugment :labels keywords)
4215dfecf96Smrg)
4225dfecf96Smrg
4235dfecf96Smrg
4245dfecf96Smrg;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4255dfecf96Smrg;; Add variables to initialization list
4265dfecf96Smrg;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4275dfecf96Smrg(defmacro indinit (&rest variables)
4285dfecf96Smrg    (make-indinit :variables variables)
4295dfecf96Smrg)
4305dfecf96Smrg
4315dfecf96Smrg
4325dfecf96Smrg;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4335dfecf96Smrg;; Create a "reduction rule"
4345dfecf96Smrg;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4355dfecf96Smrg(defmacro indreduce (token check rules &rest code &aux nullp consp)
4365dfecf96Smrg    ;; check for simple errors
4375dfecf96Smrg    (unless (or (keywordp token) (null token))
4385dfecf96Smrg	(error "INDREDUCE: ~A is not a keyword" token)
4395dfecf96Smrg    )
4405dfecf96Smrg    (dolist (rule rules)
4415dfecf96Smrg	(or (listp rule) (error "INDREDUCE: invalid indent rule ~A" rule))
4425dfecf96Smrg	;; XXX This test is not enough, maybe should add some sort of
4435dfecf96Smrg	;; runtime check to avoid circularity.
4445dfecf96Smrg	(and (eq token (car rule)) (null (cdr rule))
4455dfecf96Smrg	    (error "INDREDUCE: ~A reduces to ~A" token)
4465dfecf96Smrg	)
4475dfecf96Smrg	(dolist (item rule)
4485dfecf96Smrg	    (and (or nullp consp) (not (keywordp item))
4495dfecf96Smrg		(error "INDREDUCE: a keyword must special pattern")
4505dfecf96Smrg	    )
4515dfecf96Smrg	    (if (consp item)
4525dfecf96Smrg		(progn
4535dfecf96Smrg		    (unless
4545dfecf96Smrg			(or
4555dfecf96Smrg			    (and
4565dfecf96Smrg				(eq (car item) 'not)
4575dfecf96Smrg				(keywordp (cadr item))
4585dfecf96Smrg				(null (cddr item))
4595dfecf96Smrg			    )
4605dfecf96Smrg			    (and
4615dfecf96Smrg				(eq (car item) 'or)
4625dfecf96Smrg				(null (member-if-not #'keywordp (cdr item)))
4635dfecf96Smrg			    )
4645dfecf96Smrg			)
4655dfecf96Smrg			(error "INDREDUCE: syntax error parsing ~A" item)
4665dfecf96Smrg		    )
4675dfecf96Smrg		    (setq consp t)
4685dfecf96Smrg		)
4695dfecf96Smrg		(progn
4705dfecf96Smrg		    (setq nullp (null item) consp nil)
4715dfecf96Smrg		    (unless (or (keywordp item) nullp (eq item t))
4725dfecf96Smrg			(error "INDREDUCE: ~A is not a keyword" item)
4735dfecf96Smrg		    )
4745dfecf96Smrg		)
4755dfecf96Smrg	    )
4765dfecf96Smrg	)
4775dfecf96Smrg;	(and consp
4785dfecf96Smrg;	    (error "INDREDUCE: pattern must be followed by keyword")
4795dfecf96Smrg;	)
4805dfecf96Smrg    )
4815dfecf96Smrg
4825dfecf96Smrg    ;; result of macro, return indent reduce structure
4835dfecf96Smrg    (make-indreduce
4845dfecf96Smrg	:token	token
4855dfecf96Smrg	:check	check
4865dfecf96Smrg	:rules	(remove-if #'null rules)
4875dfecf96Smrg	:code	code
4885dfecf96Smrg    )
4895dfecf96Smrg)
4905dfecf96Smrg
4915dfecf96Smrg;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4925dfecf96Smrg;; Create a "resolve rule"
4935dfecf96Smrg;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4945dfecf96Smrg(defmacro indresolve (match &rest code)
4955dfecf96Smrg    ;; check for simple errors
4965dfecf96Smrg    (if (consp match)
4975dfecf96Smrg	(dolist (token match)
4985dfecf96Smrg	    (or (keywordp token) (error "INDRESOLVE: ~A is not a keyword" token))
4995dfecf96Smrg	)
5005dfecf96Smrg	(or (keywordp match) (error "INDRESOLVE: ~A is not a keyword" match))
5015dfecf96Smrg    )
5025dfecf96Smrg
5035dfecf96Smrg    ;; result of macro, return indent resolve structure
5045dfecf96Smrg    (make-indresolve
5055dfecf96Smrg	:match	match
5065dfecf96Smrg	:code	code
5075dfecf96Smrg    )
5085dfecf96Smrg)
5095dfecf96Smrg
5105dfecf96Smrg
5115dfecf96Smrg;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
5125dfecf96Smrg;; Helper function for compile-indent-table. Returns a list of all
5135dfecf96Smrg;; tables and tokens for a given table, including tokens and tables
5145dfecf96Smrg;; of children.
5155dfecf96Smrg;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
5165dfecf96Smrg(defun list-indtable-elements (table &aux result sub-result)
5175dfecf96Smrg    (setq result (cons (indtable-tokens table) (indtable-tables table)))
5185dfecf96Smrg    (dolist (child (indtable-tables table))
5195dfecf96Smrg	(setq sub-result (list-indtable-elements child))
5205dfecf96Smrg	(rplaca result (append (car result) (car sub-result)))
5215dfecf96Smrg	(rplacd result (append (cdr result) (cdr sub-result)))
5225dfecf96Smrg    )
5235dfecf96Smrg    ;; Return pair of all nested tokens and tables
5245dfecf96Smrg    result
5255dfecf96Smrg)
5265dfecf96Smrg
5275dfecf96Smrg
5285dfecf96Smrg;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
5295dfecf96Smrg;; First pass adding augumented tokens to a table, done in two passes
5305dfecf96Smrg;; to respect inheritance order.
5315dfecf96Smrg;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
5325dfecf96Smrg(defun compile-indent-augment-list (table table-list &aux labels augment tokens)
5335dfecf96Smrg
5345dfecf96Smrg    ;; Create a list of all augment tables.
5355dfecf96Smrg    (dolist (augment (indtable-augments table))
5365dfecf96Smrg	(setq labels (append labels (indaugment-labels augment)))
5375dfecf96Smrg    )
5385dfecf96Smrg
5395dfecf96Smrg    ;;  Remove duplicates and references to "itself", without warnings?
5405dfecf96Smrg    (setq
5415dfecf96Smrg	labels
5425dfecf96Smrg	(remove (indtable-label table) (remove-duplicates labels :from-end t))
5435dfecf96Smrg    )
5445dfecf96Smrg
5455dfecf96Smrg    ;; Check if the specified indent tables exists!
5465dfecf96Smrg    (dolist (label labels)
5475dfecf96Smrg	(unless
5485dfecf96Smrg	    (setq augment (car (member label table-list :key #'indtable-label)))
5495dfecf96Smrg	    (error "COMPILE-INDENT-AUGMENT-LIST: Cannot augment ~A in ~A"
5505dfecf96Smrg		label
5515dfecf96Smrg		(indtable-label table)
5525dfecf96Smrg	    )
5535dfecf96Smrg	)
5545dfecf96Smrg
5555dfecf96Smrg	;; Increase list of tokens.
5565dfecf96Smrg	(setq tokens (append tokens (indtable-tokens augment)))
5575dfecf96Smrg    )
5585dfecf96Smrg
5595dfecf96Smrg    ;;  Store the tokens in the augment list. They will be added
5605dfecf96Smrg    ;; to the indent table in the second pass.
5615dfecf96Smrg    (setf (indtable-augments table) tokens)
5625dfecf96Smrg
5635dfecf96Smrg    ;;  Recurse on every child table.
5645dfecf96Smrg    (dolist (child (indtable-tables table))
5655dfecf96Smrg	(compile-indent-augment-list child table-list)
5665dfecf96Smrg    )
5675dfecf96Smrg)
5685dfecf96Smrg
5695dfecf96Smrg
5705dfecf96Smrg;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
5715dfecf96Smrg;; Last pass adding augmented tokens to a table.
5725dfecf96Smrg;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
5735dfecf96Smrg(defun link-indent-augment-list (table)
5745dfecf96Smrg    (setf
5755dfecf96Smrg	(indtable-tokens table)
5765dfecf96Smrg	(remove-duplicates
5775dfecf96Smrg	    (nconc (indtable-tokens table) (indtable-augments table))
5785dfecf96Smrg	    :key	#'indtoken-regex
5795dfecf96Smrg	    :test	#'equal
5805dfecf96Smrg	    :from-end	t
5815dfecf96Smrg	)
5825dfecf96Smrg
5835dfecf96Smrg	;;  Don't need to keep this list anymore.
5845dfecf96Smrg	(indtable-augments table)
5855dfecf96Smrg	()
5865dfecf96Smrg    )
5875dfecf96Smrg
5885dfecf96Smrg    (dolist (child (indtable-tables table))
5895dfecf96Smrg	(link-indent-augment-list child)
5905dfecf96Smrg    )
5915dfecf96Smrg)
5925dfecf96Smrg
5935dfecf96Smrg;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
5945dfecf96Smrg;; Compile the indent reduction rules
5955dfecf96Smrg;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
5965dfecf96Smrg(defun compile-indent-reduces (reduces
5975dfecf96Smrg			       &aux need label check rules reduce
5985dfecf96Smrg				    check-code reduce-code)
5995dfecf96Smrg    (dolist (item reduces)
6005dfecf96Smrg	(setq
6015dfecf96Smrg	    label	(indreduce-label item)
6025dfecf96Smrg	    check	(indreduce-check item)
6035dfecf96Smrg	    rules	(indreduce-rules item)
6045dfecf96Smrg	    reduce	(indreduce-code  item)
6055dfecf96Smrg	    need	(and
6065dfecf96Smrg			    rules
6075dfecf96Smrg			    (not label)
6085dfecf96Smrg			    (or
6095dfecf96Smrg				reduce
6105dfecf96Smrg				(null check)
6115dfecf96Smrg				(not (constantp check))
6125dfecf96Smrg			    )
6135dfecf96Smrg			)
6145dfecf96Smrg	)
6155dfecf96Smrg	(when need
6165dfecf96Smrg	    (and (null label) (setq label (intern (string (gensym)) 'keyword)))
6175dfecf96Smrg
6185dfecf96Smrg	    (setf (indreduce-label item) label)
6195dfecf96Smrg
6205dfecf96Smrg	    (and
6215dfecf96Smrg		(or (null check)
6225dfecf96Smrg		    (not (constantp check))
6235dfecf96Smrg		)
6245dfecf96Smrg		(setq
6255dfecf96Smrg		    check	(list (list 'eq '*ind-label* label) check)
6265dfecf96Smrg		    check-code	(nconc check-code (list check))
6275dfecf96Smrg		)
6285dfecf96Smrg	    )
6295dfecf96Smrg
6305dfecf96Smrg	    (and reduce
6315dfecf96Smrg		(setq
6325dfecf96Smrg		    reduce	(cons (list 'eq '*ind-label* label) reduce)
6335dfecf96Smrg		    reduce-code	(nconc reduce-code (list reduce))
6345dfecf96Smrg		)
6355dfecf96Smrg	    )
6365dfecf96Smrg	)
6375dfecf96Smrg    )
6385dfecf96Smrg
6395dfecf96Smrg    ;; XXX Instead of using COND, could/should use CASE
6405dfecf96Smrg    ;; TODO Implement a smart CASE in the bytecode compiler, if
6415dfecf96Smrg    ;;	    possible, should generate a hashtable, or a table
6425dfecf96Smrg    ;;	    of indexes (for example when all elements in the cases
6435dfecf96Smrg    ;;	    are characters) and then jump directly to the code.
6445dfecf96Smrg    (if check-code
6455dfecf96Smrg	(setq check-code (cons 'cond (nconc check-code '((t t)))))
6465dfecf96Smrg	(setq check-code t)
6475dfecf96Smrg    )
6485dfecf96Smrg    (and reduce-code (setq reduce-code (cons 'cond reduce-code)))
6495dfecf96Smrg
6505dfecf96Smrg    (values check-code reduce-code)
6515dfecf96Smrg)
6525dfecf96Smrg
6535dfecf96Smrg
6545dfecf96Smrg;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
6555dfecf96Smrg;; Compile the indent resolve code
6565dfecf96Smrg;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
6575dfecf96Smrg(defun compile-indent-resolves (resolves &aux match resolve resolve-code)
6585dfecf96Smrg    (and
6595dfecf96Smrg	(/=
6605dfecf96Smrg	    (length resolves)
6615dfecf96Smrg	    (length (remove-duplicates resolves :key #'indresolve-match))
6625dfecf96Smrg	)
6635dfecf96Smrg	;; XXX Could do a more complete job and tell what is wrong...
6645dfecf96Smrg	(error "COMPILE-INDENT-RESOLVES: duplicated labels")
6655dfecf96Smrg    )
6665dfecf96Smrg
6675dfecf96Smrg    (dolist (item resolves)
6685dfecf96Smrg	(when (setq resolve (indresolve-code item))
6695dfecf96Smrg	    (setq
6705dfecf96Smrg		match
6715dfecf96Smrg		(indresolve-match item)
6725dfecf96Smrg
6735dfecf96Smrg		resolve
6745dfecf96Smrg		(cons
6755dfecf96Smrg		    (if (listp match)
6765dfecf96Smrg			(list 'member '*ind-token* `',match :test `#'eq)
6775dfecf96Smrg			(list 'eq '*ind-token* match)
6785dfecf96Smrg		    )
6795dfecf96Smrg		    resolve
6805dfecf96Smrg		)
6815dfecf96Smrg
6825dfecf96Smrg		resolve-code
6835dfecf96Smrg		(nconc resolve-code (list resolve))
6845dfecf96Smrg	    )
6855dfecf96Smrg	)
6865dfecf96Smrg    )
6875dfecf96Smrg
6885dfecf96Smrg    (and resolve-code (cons 'cond resolve-code))
6895dfecf96Smrg)
6905dfecf96Smrg
6915dfecf96Smrg
6925dfecf96Smrg;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
6935dfecf96Smrg;; Create an indentation table
6945dfecf96Smrg;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
6955dfecf96Smrg(defun compile-indent-table (name &rest lists
6965dfecf96Smrg			     &aux main elements switches begins tables symbols
6975dfecf96Smrg				  label code token-code check-code reduce-code
6985dfecf96Smrg				  (inits (remove-if-not #'indinit-p lists))
6995dfecf96Smrg				  (reduces (remove-if-not #'indreduce-p lists))
7005dfecf96Smrg				  (resolves (remove-if-not #'indresolve-p lists))
7015dfecf96Smrg			    )
7025dfecf96Smrg    (setq
7035dfecf96Smrg	lists	 (delete-if
7045dfecf96Smrg		    #'(lambda (object)
7055dfecf96Smrg			(or
7065dfecf96Smrg			    (indinit-p object)
7075dfecf96Smrg			    (indreduce-p object)
7085dfecf96Smrg			    (indresolve-p object)
7095dfecf96Smrg			)
7105dfecf96Smrg		    )
7115dfecf96Smrg		    lists)
7125dfecf96Smrg	main	 (apply #'indtable name lists)
7135dfecf96Smrg	elements (list-indtable-elements main)
7145dfecf96Smrg	switches (remove-if #'null (car elements) :key #'indtoken-switch)
7155dfecf96Smrg	begins   (remove-if #'null (car elements) :key #'indtoken-begin)
7165dfecf96Smrg	tables	 (cons main (cdr elements))
7175dfecf96Smrg    )
7185dfecf96Smrg
7195dfecf96Smrg    ;; Check for typos in the keywords, or for not defined indent tables.
7205dfecf96Smrg    (dolist (item (mapcar #'indtoken-switch switches))
7215dfecf96Smrg	(unless
7225dfecf96Smrg	    (or	(and (integerp item) (minusp item))
7235dfecf96Smrg		(member item tables :key #'indtable-label)
7245dfecf96Smrg	    )
7255dfecf96Smrg	    (error "COMPILE-INDENT-TABLE: SWITCH ~A cannot be matched" item)
7265dfecf96Smrg	)
7275dfecf96Smrg    )
7285dfecf96Smrg    (dolist (item (mapcar #'indtoken-begin begins))
7295dfecf96Smrg	(unless (member item tables :key #'indtable-label)
7305dfecf96Smrg	    (error "COMPILE-INDENT-TABLE: BEGIN ~A cannot be matched" item)
7315dfecf96Smrg	)
7325dfecf96Smrg    )
7335dfecf96Smrg
7345dfecf96Smrg    ;; Build augment list.
7355dfecf96Smrg    (compile-indent-augment-list main tables)
7365dfecf96Smrg    (link-indent-augment-list main)
7375dfecf96Smrg
7385dfecf96Smrg    ;; Change switch and begin fields to point to the indent table
7395dfecf96Smrg    (dolist (item switches)
7405dfecf96Smrg	(if (keywordp (indtoken-switch item))
7415dfecf96Smrg	    (setf
7425dfecf96Smrg		(indtoken-switch item)
7435dfecf96Smrg		(car (member (indtoken-switch item) tables :key #'indtable-label))
7445dfecf96Smrg	    )
7455dfecf96Smrg	)
7465dfecf96Smrg    )
7475dfecf96Smrg    (dolist (item begins)
7485dfecf96Smrg	(setf
7495dfecf96Smrg	    (indtoken-begin item)
7505dfecf96Smrg	    (car (member (indtoken-begin item) tables :key #'indtable-label))
7515dfecf96Smrg	)
7525dfecf96Smrg    )
7535dfecf96Smrg
7545dfecf96Smrg    ;; Build initialization list
7555dfecf96Smrg    (dolist (init inits)
7565dfecf96Smrg	(setq symbols (nconc symbols (indinit-variables init)))
7575dfecf96Smrg    )
7585dfecf96Smrg
7595dfecf96Smrg    ;; Build token code
7605dfecf96Smrg    (dolist (item (car elements))
7615dfecf96Smrg	(when (setq code (indtoken-code item))
7625dfecf96Smrg	    (setf
7635dfecf96Smrg		label
7645dfecf96Smrg		(intern (string (gensym)) 'keyword)
7655dfecf96Smrg
7665dfecf96Smrg		(indtoken-label item)
7675dfecf96Smrg		label
7685dfecf96Smrg
7695dfecf96Smrg		code
7705dfecf96Smrg		(list (list 'eq '*ind-label* label) code)
7715dfecf96Smrg
7725dfecf96Smrg		token-code
7735dfecf96Smrg		(nconc token-code (list code))
7745dfecf96Smrg	    )
7755dfecf96Smrg	)
7765dfecf96Smrg    )
7775dfecf96Smrg
7785dfecf96Smrg    (multiple-value-setq
7795dfecf96Smrg	(check-code reduce-code)
7805dfecf96Smrg	(compile-indent-reduces reduces)
7815dfecf96Smrg    )
7825dfecf96Smrg
7835dfecf96Smrg    (make-indent
7845dfecf96Smrg	:tables		tables
7855dfecf96Smrg	:inits		symbols
7865dfecf96Smrg	:reduces	reduces
7875dfecf96Smrg	:resolves	resolves
7885dfecf96Smrg	:token-code	(and token-code (cons 'cond token-code))
7895dfecf96Smrg	:check-code	check-code
7905dfecf96Smrg	:reduce-code	reduce-code
7915dfecf96Smrg	:resolve-code	(compile-indent-resolves resolves)
7925dfecf96Smrg    )
7935dfecf96Smrg)
7945dfecf96Smrg
7955dfecf96Smrg
7965dfecf96Smrg;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
7975dfecf96Smrg;; Search rule-pattern in match-pattern
7985dfecf96Smrg;; Returns offset of match, and it's length, if any
7995dfecf96Smrg;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
8005dfecf96Smrg(defun indent-search-rule (rule-pattern match-pattern
8015dfecf96Smrg			   &aux start rule rulep matchp test offset length)
8025dfecf96Smrg    (if (member-if-not #'keywordp rule-pattern)
8035dfecf96Smrg	;; rule has wildcards
8045dfecf96Smrg	(progn
8055dfecf96Smrg	    (setq
8065dfecf96Smrg		rulep	rule-pattern
8075dfecf96Smrg		matchp	match-pattern
8085dfecf96Smrg		start	match-pattern
8095dfecf96Smrg	    )
8105dfecf96Smrg	    (loop
8115dfecf96Smrg		(setq rule (car rulep))
8125dfecf96Smrg		(cond
8135dfecf96Smrg		    ;; Special pattern
8145dfecf96Smrg		    ((consp rule)
8155dfecf96Smrg			(if (eq (car rule) 'not)
8165dfecf96Smrg			    (progn
8175dfecf96Smrg				(setq
8185dfecf96Smrg				    test	(cadr rule)
8195dfecf96Smrg				    rulep	(cdr rulep)
8205dfecf96Smrg				    rule	(car rulep)
8215dfecf96Smrg				)
8225dfecf96Smrg				(while
8235dfecf96Smrg				    (and
8245dfecf96Smrg					;; something to match
8255dfecf96Smrg					matchp
8265dfecf96Smrg					;; NOT match is true
8275dfecf96Smrg					(not (eq (car matchp) test))
8285dfecf96Smrg					;; next match is not true
8295dfecf96Smrg					(not (eq (car matchp) rule))
8305dfecf96Smrg				    )
8315dfecf96Smrg				    (setq matchp (cdr matchp))
8325dfecf96Smrg				)
8335dfecf96Smrg				(if (eq (car matchp) rule)
8345dfecf96Smrg				    ;; rule matched
8355dfecf96Smrg				    (setq
8365dfecf96Smrg					matchp	(cdr matchp)
8375dfecf96Smrg					rulep	(cdr rulep)
8385dfecf96Smrg				    )
8395dfecf96Smrg				    ;; failed
8405dfecf96Smrg				    (setq
8415dfecf96Smrg					rulep	rule-pattern
8425dfecf96Smrg					matchp	(cdr start)
8435dfecf96Smrg					start	matchp
8445dfecf96Smrg				    )
8455dfecf96Smrg				)
8465dfecf96Smrg			    )
8475dfecf96Smrg			    ;; (eq (car rule) 'or)
8485dfecf96Smrg			    (progn
8495dfecf96Smrg				(if (member (car matchp) (cdr rule) :test #'eq)
8505dfecf96Smrg				    (setq rulep (cdr rulep) matchp (cdr matchp))
8515dfecf96Smrg				    ;; failed
8525dfecf96Smrg				    (progn
8535dfecf96Smrg					;; end of match found!
8545dfecf96Smrg					(and (null matchp) (return))
8555dfecf96Smrg					;; reset search
8565dfecf96Smrg					(setq
8575dfecf96Smrg					    rulep	rule-pattern
8585dfecf96Smrg					    matchp	(cdr start)
8595dfecf96Smrg					    start	matchp
8605dfecf96Smrg					)
8615dfecf96Smrg				    )
8625dfecf96Smrg				)
8635dfecf96Smrg			    )
8645dfecf96Smrg			)
8655dfecf96Smrg		    )
8665dfecf96Smrg
8675dfecf96Smrg		    ;; Skip until end of match-pattern or rule is found
8685dfecf96Smrg		    ((null rule)
8695dfecf96Smrg			(setq rulep (cdr rulep))
8705dfecf96Smrg			;; If matches everything
8715dfecf96Smrg			(if (null rulep)
8725dfecf96Smrg			    (progn (setq matchp nil) (return))
8735dfecf96Smrg			    ;; If next token cannot be matched
8745dfecf96Smrg			    (unless
8755dfecf96Smrg				(setq
8765dfecf96Smrg				    matchp
8775dfecf96Smrg				    (member (car rulep) matchp :test #'eq)
8785dfecf96Smrg				)
8795dfecf96Smrg				(setq rulep rule-pattern)
8805dfecf96Smrg				(return)
8815dfecf96Smrg			    )
8825dfecf96Smrg			)
8835dfecf96Smrg			(setq rulep (cdr rulep) matchp (cdr matchp))
8845dfecf96Smrg		    )
8855dfecf96Smrg
8865dfecf96Smrg		    ;; Matched
8875dfecf96Smrg		    ((eq rule t)
8885dfecf96Smrg			;; If there isn't a rule to skip
8895dfecf96Smrg			(and (null matchp) (return))
8905dfecf96Smrg			(setq rulep (cdr rulep) matchp (cdr matchp))
8915dfecf96Smrg		    )
8925dfecf96Smrg
8935dfecf96Smrg		    ;; Matched
8945dfecf96Smrg		    ((eq rule (car matchp))
8955dfecf96Smrg			(setq rulep (cdr rulep) matchp (cdr matchp))
8965dfecf96Smrg		    )
8975dfecf96Smrg
8985dfecf96Smrg		    ;; No match
8995dfecf96Smrg		    (t
9005dfecf96Smrg			;; end of match found!
9015dfecf96Smrg			(and (null matchp) (return))
9025dfecf96Smrg			;; reset search
9035dfecf96Smrg			(setq
9045dfecf96Smrg			    rulep	rule-pattern
9055dfecf96Smrg			    matchp	(cdr start)
9065dfecf96Smrg			    start	matchp
9075dfecf96Smrg			)
9085dfecf96Smrg		    )
9095dfecf96Smrg		)
9105dfecf96Smrg
9115dfecf96Smrg		;; if everything matched
9125dfecf96Smrg		(or rulep (return))
9135dfecf96Smrg	    )
9145dfecf96Smrg
9155dfecf96Smrg	    ;; All rules matched
9165dfecf96Smrg	    (unless rulep
9175dfecf96Smrg		;; Calculate offset and length of match
9185dfecf96Smrg		(setq offset 0 length 0)
9195dfecf96Smrg		(until (eq match-pattern start)
9205dfecf96Smrg		    (setq
9215dfecf96Smrg			offset		(1+ offset)
9225dfecf96Smrg			match-pattern	(cdr match-pattern)
9235dfecf96Smrg		    )
9245dfecf96Smrg		)
9255dfecf96Smrg		(until (eq match-pattern matchp)
9265dfecf96Smrg		    (setq
9275dfecf96Smrg			length		(1+ length)
9285dfecf96Smrg			match-pattern	(cdr match-pattern)
9295dfecf96Smrg		    )
9305dfecf96Smrg		)
9315dfecf96Smrg	    )
9325dfecf96Smrg	)
9335dfecf96Smrg	;; no wildcards
9345dfecf96Smrg	(and (setq offset (search rule-pattern match-pattern :test #'eq))
9355dfecf96Smrg	     (setq length (length rule-pattern))
9365dfecf96Smrg	)
9375dfecf96Smrg    )
9385dfecf96Smrg
9395dfecf96Smrg    (values offset length)
9405dfecf96Smrg)
9415dfecf96Smrg(compile 'indent-search-rule)
9425dfecf96Smrg
9435dfecf96Smrg;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
9445dfecf96Smrg;; Indentation parser
9455dfecf96Smrg;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
9465dfecf96Smrg(defmacro indent-macro (ind-definition ind-offset &optional ind-no-tabs)
9475dfecf96Smrg   `(prog*
9485dfecf96Smrg	(
9495dfecf96Smrg	;; Current indentation table
9505dfecf96Smrg	(ind-table (car (indent-tables ,ind-definition)))
9515dfecf96Smrg
9525dfecf96Smrg	;; The parser rules
9535dfecf96Smrg	(ind-reduces (indent-reduces ,ind-definition))
9545dfecf96Smrg
9555dfecf96Smrg	;; Token list for the table
9565dfecf96Smrg	(ind-tokens (indtable-tokens ind-table))
9575dfecf96Smrg
9585dfecf96Smrg	;; Stack of nested tables/states
9595dfecf96Smrg	ind-stack
9605dfecf96Smrg
9615dfecf96Smrg	;; indentation to be used
9625dfecf96Smrg	(*indent* 0)
9635dfecf96Smrg
9645dfecf96Smrg	;; offset to apply indentation
9655dfecf96Smrg	*offset*
9665dfecf96Smrg
9675dfecf96Smrg	;; Number of lines read
9685dfecf96Smrg	(*ind-lines* 1)
9695dfecf96Smrg
9705dfecf96Smrg	;; Matched token
9715dfecf96Smrg	*ind-token*
9725dfecf96Smrg
9735dfecf96Smrg	;; list of tokens after current match, should not be changed
9745dfecf96Smrg	*ind-token-list*
9755dfecf96Smrg
9765dfecf96Smrg	;; label associated with rule
9775dfecf96Smrg	*ind-label*
9785dfecf96Smrg
9795dfecf96Smrg	;; offset of match
9805dfecf96Smrg	*ind-offset*
9815dfecf96Smrg
9825dfecf96Smrg	;; length of match
9835dfecf96Smrg	*ind-length*
9845dfecf96Smrg
9855dfecf96Smrg	;; insert position
9865dfecf96Smrg	(*ind-point* (point))
9875dfecf96Smrg
9885dfecf96Smrg	(ind-from (scan ,ind-offset :eol :left))
9895dfecf96Smrg	(ind-to ,ind-offset)
9905dfecf96Smrg	(ind-line (read-text ind-from (- ind-to ind-from)))
9915dfecf96Smrg
9925dfecf96Smrg	;; start of current line
9935dfecf96Smrg	(*ind-start* ind-from)
9945dfecf96Smrg
9955dfecf96Smrg	;; State information
9965dfecf96Smrg	ind-state
9975dfecf96Smrg
9985dfecf96Smrg	;; For use with (indent-macro-reject)
9995dfecf96Smrg	ind-prev-state
10005dfecf96Smrg
10015dfecf96Smrg	;; Matches for the current line
10025dfecf96Smrg	ind-matches
10035dfecf96Smrg
10045dfecf96Smrg	;; Matched tokens not yet used
10055dfecf96Smrg	ind-cache
10065dfecf96Smrg
10075dfecf96Smrg	;; Pattern being tested
10085dfecf96Smrg	ind-token
10095dfecf96Smrg
10105dfecf96Smrg	;; Used when searching for a regex
10115dfecf96Smrg	ind-match
10125dfecf96Smrg
10135dfecf96Smrg	;; Table to change
10145dfecf96Smrg	ind-change
10155dfecf96Smrg
10165dfecf96Smrg	;; Length of ind-line
10175dfecf96Smrg	(ind-length (length ind-line))
10185dfecf96Smrg
10195dfecf96Smrg	;; Don't parse after this offset
10205dfecf96Smrg	(ind-end ind-length)
10215dfecf96Smrg
10225dfecf96Smrg	;; Temporary variables used during loops
10235dfecf96Smrg	ind-left
10245dfecf96Smrg	ind-right
10255dfecf96Smrg	ind-tleft
10265dfecf96Smrg	ind-tright
10275dfecf96Smrg
10285dfecf96Smrg	;; Set  when start of file is found
10295dfecf96Smrg	ind-startp
10305dfecf96Smrg
10315dfecf96Smrg	;; Flag for regex search
10325dfecf96Smrg	(ind-noteol (< ind-to (scan ind-from :eol :right)))
10335dfecf96Smrg
10345dfecf96Smrg	;; Initialization variables expanded here
10355dfecf96Smrg	,@(indent-inits (eval ind-definition))
10365dfecf96Smrg	)
10375dfecf96Smrg
10385dfecf96Smrg	;; Initial input already read
10395dfecf96Smrg	(go :ind-loop)
10405dfecf96Smrg
1041f14f4646Smrg	;; Just to avoid a warning about unused variable, as this
1042f14f4646Smrg	;; variable is somewhat redundant as code should already
1043f14f4646Smrg	;; know before entering indent parser, but useful inside
1044f14f4646Smrg	;; indent macros.
1045f14f4646Smrg	*ind-point*
1046f14f4646Smrg
10475dfecf96Smrg;------------------------------------------------------------------------
10485dfecf96Smrg; Read a text line
10495dfecf96Smrg:ind-read
10505dfecf96Smrg	(setq
10515dfecf96Smrg	    ind-to	ind-from
10525dfecf96Smrg	    ind-from	(scan ind-from :eol :left :count 2)
10535dfecf96Smrg	)
10545dfecf96Smrg	;; If start of file reached
10555dfecf96Smrg	(and (= ind-to ind-from) (setq ind-startp t) (go :ind-process))
10565dfecf96Smrg
10575dfecf96Smrg	(setq
10585dfecf96Smrg	    *ind-lines*		(1+ *ind-lines*)
10595dfecf96Smrg	    ind-to		(scan ind-from :eol :right)
10605dfecf96Smrg	    ind-line		(read-text ind-from (- ind-to ind-from))
10615dfecf96Smrg	    ind-length		(length ind-line)
10625dfecf96Smrg	    ind-end		ind-length
10635dfecf96Smrg	    ind-noteol		nil
10645dfecf96Smrg	    ind-cache		nil
10655dfecf96Smrg	    ind-prev-state	ind-state
10665dfecf96Smrg	)
10675dfecf96Smrg
10685dfecf96Smrg;------------------------------------------------------------------------
10695dfecf96Smrg; Loop parsing backwards
10705dfecf96Smrg:ind-loop
10715dfecf96Smrg	(setq ind-matches nil)
10725dfecf96Smrg	(dolist (token ind-tokens)
10735dfecf96Smrg	    ;; Prepare to loop
10745dfecf96Smrg	    (setq
10755dfecf96Smrg		ind-token	(indtoken-regex token)
10765dfecf96Smrg		ind-left	0
10775dfecf96Smrg	    )
10785dfecf96Smrg	    ;; While the pattern matches
10795dfecf96Smrg	    (loop
10805dfecf96Smrg		(setq ind-right ind-left)
10815dfecf96Smrg		(if
10825dfecf96Smrg		    (consp
10835dfecf96Smrg			(setq
10845dfecf96Smrg			    ind-match
10855dfecf96Smrg			    (re-exec
10865dfecf96Smrg				ind-token
10875dfecf96Smrg				ind-line
10885dfecf96Smrg				:start	ind-left
10895dfecf96Smrg				:end	ind-end
10905dfecf96Smrg				:notbol (> ind-left 0)
10915dfecf96Smrg				:noteol ind-noteol
10925dfecf96Smrg			    )
10935dfecf96Smrg			)
10945dfecf96Smrg		    )
10955dfecf96Smrg
10965dfecf96Smrg		    ;; Remember about match
10975dfecf96Smrg		    (setq
10985dfecf96Smrg			ind-match   (car ind-match)
10995dfecf96Smrg			ind-left    (cdr ind-match)
11005dfecf96Smrg			ind-matches (cons (cons token ind-match) ind-matches)
11015dfecf96Smrg		    )
11025dfecf96Smrg
11035dfecf96Smrg		    ;; No match
11045dfecf96Smrg		    (return)
11055dfecf96Smrg		)
11065dfecf96Smrg		;; matched an empty string
11075dfecf96Smrg		(and (= ind-left ind-right) (incf ind-left))
11085dfecf96Smrg
11095dfecf96Smrg		;; matched a single eol or bol
11105dfecf96Smrg		(and (>= ind-left ind-end) (return))
11115dfecf96Smrg	    )
11125dfecf96Smrg	)
11135dfecf96Smrg
11145dfecf96Smrg	;; Add new matches to cache
11155dfecf96Smrg	(when ind-matches
11165dfecf96Smrg	    (setq
11175dfecf96Smrg		ind-cache
11185dfecf96Smrg		(stable-sort
11195dfecf96Smrg		    (nconc (nreverse ind-matches) ind-cache) #'< :key #'cadr
11205dfecf96Smrg		)
11215dfecf96Smrg	    )
11225dfecf96Smrg	)
11235dfecf96Smrg
11245dfecf96Smrg	;; If nothing in the cache
11255dfecf96Smrg	(or ind-cache (go :ind-process))
11265dfecf96Smrg
11275dfecf96Smrg	(setq
11285dfecf96Smrg	    ind-left	(cadar ind-cache)
11295dfecf96Smrg	    ind-right	(cddar ind-cache)
11305dfecf96Smrg	    ind-matches	(cdr ind-cache)
11315dfecf96Smrg	)
11325dfecf96Smrg
11335dfecf96Smrg	;; If only one element in the cache
11345dfecf96Smrg	(or ind-matches	(go :ind-parse))
11355dfecf96Smrg
11365dfecf96Smrg	(setq
11375dfecf96Smrg	    ind-tleft	(cadar ind-matches)
11385dfecf96Smrg	    ind-tright	(cddar ind-matches)
11395dfecf96Smrg	)
11405dfecf96Smrg
11415dfecf96Smrg	;; Remove overlaps
11425dfecf96Smrg	(loop
11435dfecf96Smrg	    (if (or (>= ind-tleft ind-right) (<= ind-tright ind-left))
11445dfecf96Smrg		;; No overlap
11455dfecf96Smrg		(progn
11465dfecf96Smrg		    (setq
11475dfecf96Smrg			ind-left    ind-tleft
11485dfecf96Smrg			ind-right   ind-tright
11495dfecf96Smrg			ind-matches (cdr ind-matches)
11505dfecf96Smrg		    )
11515dfecf96Smrg		    ;; If everything checked
11525dfecf96Smrg		    (or ind-matches (return))
11535dfecf96Smrg		)
11545dfecf96Smrg		;; Overlap found
11555dfecf96Smrg		(progn
11565dfecf96Smrg		    (if (consp (cdr ind-matches))
11575dfecf96Smrg			;; There are yet items to be checked
11585dfecf96Smrg			(progn
11595dfecf96Smrg			    (rplaca ind-matches (cadr ind-matches))
11605dfecf96Smrg			    (rplacd ind-matches (cddr ind-matches))
11615dfecf96Smrg			)
11625dfecf96Smrg			;; Last item
11635dfecf96Smrg			(progn
11645dfecf96Smrg			    (rplacd (last ind-cache 2) nil)
11655dfecf96Smrg			    (return)
11665dfecf96Smrg			)
11675dfecf96Smrg		    )
11685dfecf96Smrg		)
11695dfecf96Smrg	    )
11705dfecf96Smrg
11715dfecf96Smrg	    ;; Prepare for next check
11725dfecf96Smrg	    (setq
11735dfecf96Smrg		ind-tleft   (cadar ind-matches)
11745dfecf96Smrg		ind-tright  (cddar ind-matches)
11755dfecf96Smrg	    )
11765dfecf96Smrg	)
11775dfecf96Smrg
11785dfecf96Smrg;------------------------------------------------------------------------
11795dfecf96Smrg; Process the matched tokens
11805dfecf96Smrg:ind-parse
11815dfecf96Smrg	(setq ind-cache (nreverse ind-cache))
11825dfecf96Smrg
11835dfecf96Smrg:ind-parse-loop
11845dfecf96Smrg	(or (setq ind-match (car ind-cache)) (go :ind-process))
11855dfecf96Smrg
11865dfecf96Smrg	(setq
11875dfecf96Smrg	    ind-cache (cdr ind-cache)
11885dfecf96Smrg	    ind-token (car ind-match)
11895dfecf96Smrg	)
11905dfecf96Smrg
11915dfecf96Smrg	(or (member ind-token ind-tokens :test #'eq)
11925dfecf96Smrg	    (go :ind-parse-loop)
11935dfecf96Smrg	)
11945dfecf96Smrg
11955dfecf96Smrg	;; If a state should be added
11965dfecf96Smrg	(when (setq ind-change (indtoken-token ind-token))
11975dfecf96Smrg	    (setq
11985dfecf96Smrg		ind-left    (cadr ind-match)
11995dfecf96Smrg		ind-right   (cddr ind-match)
12005dfecf96Smrg
12015dfecf96Smrg		*ind-offset*
12025dfecf96Smrg		(+ ind-from ind-left)
12035dfecf96Smrg
12045dfecf96Smrg		*ind-length*
12055dfecf96Smrg		(- ind-right ind-left)
12065dfecf96Smrg
12075dfecf96Smrg		ind-state
12085dfecf96Smrg		(cons
12095dfecf96Smrg		    (cons ind-change (cons *ind-offset* *ind-length*))
12105dfecf96Smrg		    ind-state
12115dfecf96Smrg		)
12125dfecf96Smrg
12135dfecf96Smrg		*ind-label*
12145dfecf96Smrg		(indtoken-label ind-token)
12155dfecf96Smrg	    )
12165dfecf96Smrg
12175dfecf96Smrg	    ;; Expand token code
12185dfecf96Smrg	    ,(indent-token-code (eval ind-definition))
12195dfecf96Smrg	)
12205dfecf96Smrg
12215dfecf96Smrg	;; Check if needs to switch to another table
12225dfecf96Smrg	(when (setq ind-change (indtoken-switch ind-token))
12235dfecf96Smrg	    ;; Need to switch to a previous table
12245dfecf96Smrg	    (if (integerp ind-change)
12255dfecf96Smrg		;; Relative switch
12265dfecf96Smrg		(while (and ind-stack (minusp ind-change))
12275dfecf96Smrg		    (setq
12285dfecf96Smrg			ind-table	(pop ind-stack)
12295dfecf96Smrg			ind-change	(1+ ind-change)
12305dfecf96Smrg		    )
12315dfecf96Smrg		)
12325dfecf96Smrg		;; Search table in the stack
12335dfecf96Smrg		(until
12345dfecf96Smrg		    (or
12355dfecf96Smrg			(null ind-stack)
12365dfecf96Smrg			(eq
12375dfecf96Smrg			    (setq ind-table (pop ind-stack))
12385dfecf96Smrg			    ind-change
12395dfecf96Smrg			)
12405dfecf96Smrg		    )
12415dfecf96Smrg		)
12425dfecf96Smrg	    )
12435dfecf96Smrg
12445dfecf96Smrg	    ;; If no match or stack became empty
12455dfecf96Smrg	    (and (null ind-table)
12465dfecf96Smrg		(setq
12475dfecf96Smrg		    ind-table
12485dfecf96Smrg		    (car (indent-tables ,ind-definition))
12495dfecf96Smrg		)
12505dfecf96Smrg	    )
12515dfecf96Smrg	)
12525dfecf96Smrg
12535dfecf96Smrg	;; Check if needs to start a new table
12545dfecf96Smrg	;; XXX use ind-tleft to reduce number of local variables
12555dfecf96Smrg	(when (setq ind-tleft (indtoken-begin ind-token))
12565dfecf96Smrg	    (setq
12575dfecf96Smrg		ind-change  ind-tleft
12585dfecf96Smrg		ind-stack   (cons ind-table ind-stack)
12595dfecf96Smrg		ind-table   ind-change
12605dfecf96Smrg	    )
12615dfecf96Smrg	)
12625dfecf96Smrg
12635dfecf96Smrg	;; If current "indent pattern table" changed
12645dfecf96Smrg	(when ind-change
12655dfecf96Smrg	    (setq
12665dfecf96Smrg		ind-tokens  (indtable-tokens ind-table)
12675dfecf96Smrg		ind-cache   (nreverse ind-cache)
12685dfecf96Smrg		ind-end     (cadr ind-match)
12695dfecf96Smrg		ind-noteol  (> ind-length ind-end)
12705dfecf96Smrg	    )
12715dfecf96Smrg	    (go :ind-loop)
12725dfecf96Smrg	)
12735dfecf96Smrg
12745dfecf96Smrg	(and ind-cache (go :ind-parse-loop))
12755dfecf96Smrg
12765dfecf96Smrg;------------------------------------------------------------------------
12775dfecf96Smrg; Everything checked, process result
12785dfecf96Smrg:ind-process
12795dfecf96Smrg
12805dfecf96Smrg	;; If stack is not empty, don't apply rules
12815dfecf96Smrg	(and ind-stack (not ind-startp) (go :ind-read))
12825dfecf96Smrg
12835dfecf96Smrg	(block ind-terminate-block
12845dfecf96Smrg	    (setq ind-cache nil ind-tleft 0 ind-change (mapcar #'car ind-state))
12855dfecf96Smrg	    (dolist (entry ind-reduces)
12865dfecf96Smrg		(setq
12875dfecf96Smrg		    *ind-token* (indreduce-token entry)
12885dfecf96Smrg		    *ind-label* (indreduce-label entry)
12895dfecf96Smrg		)
12905dfecf96Smrg		(dolist (rule (indreduce-rules entry))
12915dfecf96Smrg		    (loop
12925dfecf96Smrg			;; Check if reduction can be applied
12935dfecf96Smrg			(or
12945dfecf96Smrg			    (multiple-value-setq
12955dfecf96Smrg				(ind-match ind-length)
12965dfecf96Smrg				(indent-search-rule rule ind-change)
12975dfecf96Smrg			    )
12985dfecf96Smrg			    (return)
12995dfecf96Smrg			)
13005dfecf96Smrg
13015dfecf96Smrg			(setq
13025dfecf96Smrg			    ;; First element matched
13035dfecf96Smrg			    ind-matches		(nthcdr ind-match ind-state)
13045dfecf96Smrg
13055dfecf96Smrg			    ;; Offset of match
13065dfecf96Smrg			    *ind-offset*	(cadar ind-matches)
13075dfecf96Smrg
13085dfecf96Smrg			    *ind-token-list*	(nthcdr ind-match ind-change)
13095dfecf96Smrg
13105dfecf96Smrg			    ;; Length of match, note that *ind-length*
13115dfecf96Smrg			    ;; Will be transformed to zero bellow if
13125dfecf96Smrg			    ;; the rule is deleting entries.
13135dfecf96Smrg			    *ind-length*
13145dfecf96Smrg			    (if (> ind-length 1)
13155dfecf96Smrg				(progn
13165dfecf96Smrg				    (setq
13175dfecf96Smrg					;; XXX using ind-tright, to reduce
13185dfecf96Smrg					;; number of local variables...
13195dfecf96Smrg					ind-tright
13205dfecf96Smrg					(nth (1- ind-length) ind-matches)
13215dfecf96Smrg
13225dfecf96Smrg					ind-right
13235dfecf96Smrg					(+  (cadr ind-tright)
13245dfecf96Smrg					    (cddr ind-tright)
13255dfecf96Smrg					)
13265dfecf96Smrg				    )
13275dfecf96Smrg				    (- ind-right *ind-offset*)
13285dfecf96Smrg				)
13295dfecf96Smrg				(cddar ind-matches)
13305dfecf96Smrg			    )
13315dfecf96Smrg			)
13325dfecf96Smrg
13335dfecf96Smrg			;; XXX using ind-tleft as a counter, to reduce
13345dfecf96Smrg			;; number of used variables...
13355dfecf96Smrg			(and (>= (incf ind-tleft) 1000)
13365dfecf96Smrg			    ;; Should never apply so many reduce rules on
13375dfecf96Smrg			    ;; every iteration, if needs to, something is
13385dfecf96Smrg			    ;; wrong in the indentation definition...
13395dfecf96Smrg			    (error "~D INDREDUCE iterations, ~
13405dfecf96Smrg				   now checking (~A ~A)"
13415dfecf96Smrg				ind-tleft *ind-token* rule
13425dfecf96Smrg			    )
13435dfecf96Smrg			)
13445dfecf96Smrg
13455dfecf96Smrg			;; Check if should apply the reduction
13465dfecf96Smrg			(or
13475dfecf96Smrg			    ;; Expand check code
13485dfecf96Smrg			    ,(indent-check-code (eval ind-definition))
13495dfecf96Smrg			    (return)
13505dfecf96Smrg			)
13515dfecf96Smrg
13525dfecf96Smrg			(if (null *ind-token*)
13535dfecf96Smrg			    ;; Remove match
13545dfecf96Smrg			    (progn
13555dfecf96Smrg				(setq *ind-length* 0)
13565dfecf96Smrg				(if (= ind-match 0)
13575dfecf96Smrg				    ;; Matched the first entry
13585dfecf96Smrg				    (setq
13595dfecf96Smrg					ind-state
13605dfecf96Smrg					(nthcdr ind-length ind-matches)
13615dfecf96Smrg				    )
13625dfecf96Smrg				    (progn
13635dfecf96Smrg					(setq
13645dfecf96Smrg					    ind-matches
13655dfecf96Smrg					    (nthcdr (1- ind-match) ind-state)
13665dfecf96Smrg					)
13675dfecf96Smrg					(rplacd
13685dfecf96Smrg					    ind-matches
13695dfecf96Smrg					    (nthcdr (1+ ind-length) ind-matches)
13705dfecf96Smrg					)
13715dfecf96Smrg				    )
13725dfecf96Smrg				)
13735dfecf96Smrg			    )
13745dfecf96Smrg
13755dfecf96Smrg			    ;; Substitute/simplify
13765dfecf96Smrg			    (progn
13775dfecf96Smrg				(rplaca (car ind-matches) *ind-token*)
13785dfecf96Smrg				(when (> ind-length 1)
13795dfecf96Smrg				    (rplacd (cdar ind-matches) *ind-length*)
13805dfecf96Smrg				    (rplacd
13815dfecf96Smrg					ind-matches
13825dfecf96Smrg					(nthcdr ind-length ind-matches)
13835dfecf96Smrg				    )
13845dfecf96Smrg				)
13855dfecf96Smrg			    )
13865dfecf96Smrg			)
13875dfecf96Smrg			(setq
13885dfecf96Smrg			    ind-cache	    t
13895dfecf96Smrg			    ind-change	    (mapcar #'car ind-state)
13905dfecf96Smrg			)
13915dfecf96Smrg
13925dfecf96Smrg			;; Expand reduce code
13935dfecf96Smrg			,(indent-reduce-code (eval ind-definition))
13945dfecf96Smrg		    )
13955dfecf96Smrg		)
13965dfecf96Smrg	    )
13975dfecf96Smrg
13985dfecf96Smrg	    ;; ind-cache will be T if at least one change was done
13995dfecf96Smrg	    (and ind-cache (go :ind-process))
14005dfecf96Smrg
14015dfecf96Smrg	    ;; Start of file reached
14025dfecf96Smrg	    (or ind-startp (go :ind-read))
14035dfecf96Smrg
14045dfecf96Smrg	)    ;; end of ind-terminate-block
14055dfecf96Smrg
14065dfecf96Smrg
14075dfecf96Smrg	(block ind-terminate-block
14085dfecf96Smrg	    (setq *ind-token-list* (mapcar #'car ind-state))
14095dfecf96Smrg	    (dolist (item ind-state)
14105dfecf96Smrg		(setq
14115dfecf96Smrg		    *ind-token*		(car item)
14125dfecf96Smrg		    *ind-offset*	(cadr item)
14135dfecf96Smrg		    *ind-length*	(cddr item)
14145dfecf96Smrg		)
14155dfecf96Smrg		;; Expand resolve code
14165dfecf96Smrg		,(indent-resolve-code (eval ind-definition))
14175dfecf96Smrg		(setq *ind-token-list* (cdr *ind-token-list*))
14185dfecf96Smrg	    )
14195dfecf96Smrg	)
14205dfecf96Smrg
14215dfecf96Smrg	(and (integerp *indent*)
14225dfecf96Smrg	     (integerp *offset*)
14235dfecf96Smrg	    (indent-text *indent* *offset* ,ind-no-tabs)
14245dfecf96Smrg	)
14255dfecf96Smrg    )
14265dfecf96Smrg)
1427