1;;; gforth.el --- major mode for editing (G)Forth sources 2 3;; Copyright (C) 1995,1996,1997,1998,2000,2001,2003,2004,2007,2008 Free Software Foundation, Inc. 4 5;; This file is part of Gforth. 6 7;; GForth is distributed in the hope that it will be useful, 8;; but WITHOUT ANY WARRANTY. No author or distributor 9;; accepts responsibility to anyone for the consequences of using it 10;; or for whether it serves any particular purpose or works at all, 11;; unless he says so in writing. Refer to the GNU Emacs General Public 12;; License for full details. 13 14;; Everyone is granted permission to copy, modify and redistribute 15;; GNU Emacs, but only under the conditions described in the 16;; GNU Emacs General Public License. A copy of this license is 17;; supposed to have been given to you along with Gforth so you 18;; can know your rights and responsibilities. It should be in a 19;; file named COPYING. Among other things, the copyright notice 20;; and this notice must be preserved on all copies. 21 22;; Author: Goran Rydqvist <gorry@ida.liu.se> 23;; Maintainer: David K�hling <dvdkhlng@gmx.de> 24;; Created: 16 July 88 by Goran Rydqvist 25;; Keywords: forth, gforth 26 27;; Changes by anton 28;; This is a variant of forth.el that came with TILE. 29;; I left most of this stuff untouched and made just a few changes for 30;; the things I use (mainly indentation and syntax tables). 31;; So there is still a lot of work to do to adapt this to gforth. 32 33;; Changes by David 34;; Added a syntax-hilighting engine, rewrote auto-indentation engine. 35;; Added support for block files. 36;; Replaced forth-process code with comint-based implementation. 37 38;; Tested with Emacs 19.34, 20.5, 21 and XEmacs 21 39 40;;------------------------------------------------------------------- 41;; A Forth indentation, documentation search and interaction library 42;;------------------------------------------------------------------- 43;; 44;; Written by Goran Rydqvist, gorry@ida.liu.se, Summer 1988 45;; Started: 16 July 88 46;; Version: 2.10 47;; Last update: 5 December 1989 by Mikael Patel, mip@ida.liu.se 48;; Last update: 25 June 1990 by Goran Rydqvist, gorry@ida.liu.se 49;; 50;; Documentation: See forth-mode (^HF forth-mode) 51;;------------------------------------------------------------------- 52 53;;; Code: 54 55;(setq debug-on-error t) 56 57;; Code ripped from `version.el' for compatability with Emacs versions 58;; prior to 19.23. 59(if (not (boundp 'emacs-major-version)) 60 (defconst emacs-major-version 61 (progn (string-match "^[0-9]+" emacs-version) 62 (string-to-number (match-string 0 emacs-version))))) 63 64;; Code ripped from `subr.el' for compatability with Emacs versions 65;; prior to 20.1 66(eval-when-compile 67(defun forth-emacs-older (major minor) 68 (or (< emacs-major-version major) 69 (and (= emacs-major-version major) (< emacs-minor-version minor)))) 70 71 (if (forth-emacs-older 20 1) 72 (progn 73 (defmacro when (cond &rest body) 74 "If COND yields non-nil, do BODY, else return nil." 75 (list 'if cond (cons 'progn body))) 76 (defmacro unless (cond &rest body) 77 "If COND yields nil, do BODY, else return nil." 78 (cons 'if (cons cond (cons nil body))))))) 79 80;; `no-error' argument of require not supported in Emacs versions 81;; prior to 20.4 :-( 82(eval-and-compile 83(defun forth-require (feature) 84 (condition-case err (require feature) (error nil)))) 85 86(require 'font-lock) 87 88;; define `font-lock-warning-face' in emacs-versions prior to 20.1 89;; (ripped from `font-lock.el') 90(unless (boundp 'font-lock-warning-face) 91 (message "defining font-lock-warning-face") 92 (make-face 'font-lock-warning-face) 93 (defvar font-lock-warning-face 'font-lock-warning-face) 94 (set-face-foreground font-lock-warning-face "red") 95 (make-face-bold font-lock-warning-face)) 96 97;; define `font-lock-constant-face' in XEmacs (just copy 98;; `font-lock-preprocessor-face') 99(unless (boundp 'font-lock-constant-face) 100 (copy-face font-lock-preprocessor-face 'font-lock-constant-face)) 101 102 103;; define `regexp-opt' in emacs versions prior to 20.1 104;; (this implementation is extremely inefficient, though) 105(eval-and-compile (forth-require 'regexp-opt)) 106(unless (memq 'regexp-opt features) 107 (message (concat 108 "Warning: your Emacs version doesn't support `regexp-opt'. " 109 "Hilighting will be slow.")) 110 (defun regexp-opt (STRINGS &optional PAREN) 111 (let ((open (if PAREN "\\(" "")) (close (if PAREN "\\)" ""))) 112 (concat open (mapconcat 'regexp-quote STRINGS "\\|") close))) 113 (defun regexp-opt-depth (re) 114 (if (string= (substring re 0 2) "\\(") 1 0))) 115 116; todo: 117; 118 119; screen-height existiert nicht in XEmacs, frame-height ersetzen? 120; 121 122; W�rter ordentlich hilighten, die nicht auf Whitespace beginnen ( ..)IF 123; -- mit aktueller Konzeption nicht m�glich?? 124; 125; Konfiguration �ber customization groups 126; 127; Bereich nur auf Wortanfang/ende ausweiten, wenn Anfang bzw Ende in einem 128; Wort liegen (?) -- speed! 129; 130; 'forth-word' property muss eindeutig sein! 131; 132; Forth-Menu 133; 134; Interface zu GForth Prozessen (Patches von Michael Scholz) 135; 136; Byte-compile-Code rausschmei�en, Compilieren im Makefile �ber Emacs 137; batch-Modus 138; 139; forth-help Kram rausschmei�en 140; 141; XEmacs Kompatibilit�t? imenu/speedbar -> fume? 142; 143; Folding neuschreiben (neue Parser-Informationen benutzen) 144 145;;; Motion-hooking (dk) 146;;; 147(defun forth-idle-function () 148 "Function that is called when Emacs is idle to detect cursor motion 149in forth-block-mode buffers (which is mainly used for screen number 150display in). Currently ignores forth-mode buffers but that may change 151in the future." 152 (if (eq major-mode 'forth-block-mode) 153 (forth-check-motion))) 154 155(defvar forth-idle-function-timer nil 156 "Timer that runs `forth-idle-function' or nil if no timer installed.") 157 158(defun forth-install-motion-hook () 159 "Install the motion-hooking mechanism. Currently uses idle timers 160but might be transparently changed in the future." 161 (unless forth-idle-function-timer 162 ;; install idle function only once (first time forth-mode is used) 163 (setq forth-idle-function-timer 164 (run-with-idle-timer .05 t 'forth-idle-function)))) 165 166(defvar forth-was-point nil) 167 168(defun forth-check-motion () 169 "Run `forth-motion-hooks', if `point' changed since last call. This 170used to be called via `post-command-hook' but uses idle timers now as 171users complaint about lagging performance." 172 (when (or (eq forth-was-point nil) (/= forth-was-point (point))) 173 (setq forth-was-point (point)) 174 (run-hooks 'forth-motion-hooks))) 175 176 177;;; Hilighting and indentation engine (dk) 178;;; 179(defvar forth-disable-parser nil 180 "*Non-nil means to disable on-the-fly parsing of Forth-code. 181 182This will disable hilighting of forth-mode buffers and will decrease 183the smartness of the indentation engine. Only set it to non-nil, if 184your computer is very slow. To disable hilighting, set 185`forth-hilight-level' to zero.") 186 187(defvar forth-jit-parser nil 188 "*Non-nil means to parse Forth-code just-in-time. 189 190This eliminates the need for initially parsing forth-mode buffers and 191thus speeds up loading of Forth files. That feature is only available 192in Emacs21 (and newer versions).") 193 194(defvar forth-words nil 195 "List of words for hilighting and recognition of parsed text areas. 196 197Hilighting of object-oriented Forth code is achieved, by appending either 198`forth-objects-words' or `forth-oof-words' to the list, depending on the values of `forth-use-objects' or `forth-use-oof'. 199 200After `forth-words' changed, `forth-compile-words' must be called to 201make the changes take effect. 202 203Each item of `forth-words' has the form 204 (MATCHER TYPE HILIGHT . &optional PARSED-TEXT ...) 205 206MATCHER is either a list of strings to match, or a REGEXP. 207 If it's a REGEXP, it should not be surrounded by '\\<' or '\\>', since 208 that'll be done automatically by the search routines. 209 210TYPE should be one of 'definiton-starter', 'definition-ender', 'compile-only', 211 'immediate' or 'non-immediate'. Those information are required to determine 212 whether a word actually parses (and whether that parsed text needs to be 213 hilighted). 214 215HILIGHT is a cons cell of the form (FACE . MINIMUM-LEVEL) 216 Where MINIMUM-LEVEL specifies the minimum value of `forth-hilight-level', 217 that's required for matching text to be hilighted. 218 219PARSED-TEXT specifies whether and how a word parses following text. You can 220 specify as many subsequent PARSED-TEXT as you wish, but that shouldn't be 221 necessary very often. It has the following form: 222 (DELIM-REGEXP SKIP-LEADING-FLAG PARSED-TYPE HILIGHT) 223 224DELIM-REGEXP is a regular expression that should match strings of length 1, 225 which are delimiters for the parsed text. 226 227A non-nil value for PARSE-LEADING-FLAG means, that leading delimiter strings 228 before parsed text should be skipped. This is the parsing behaviour of the 229 Forth word WORD. Set it to t for name-parsing words, nil for comments and 230 strings. 231 232PARSED-TYPE specifies what kind of text is parsed. It should be on of 'name', 233 'string' or 'comment'.") 234(setq forth-words 235 '( 236 (("[") definition-ender (font-lock-keyword-face . 1)) 237 (("]" "]l") definition-starter (font-lock-keyword-face . 1)) 238 ((":") definition-starter (font-lock-keyword-face . 1) 239 "[ \t\n]" t name (font-lock-function-name-face . 3)) 240 (("immediate" "compile-only" "restrict") 241 immediate (font-lock-keyword-face . 1)) 242 (("does>") compile-only (font-lock-keyword-face . 1)) 243 ((":noname") definition-starter (font-lock-keyword-face . 1)) 244 ((";" ";code") definition-ender (font-lock-keyword-face . 1)) 245 (("include" "require" "needs" "use") 246 non-immediate (font-lock-keyword-face . 1) 247 "[\n\t ]" t string (font-lock-string-face . 1)) 248 (("included" "required" "thru" "load") 249 non-immediate (font-lock-keyword-face . 1)) 250 (("[char]") compile-only (font-lock-keyword-face . 1) 251 "[ \t\n]" t string (font-lock-string-face . 1)) 252 (("char") non-immediate (font-lock-keyword-face . 1) 253 "[ \t\n]" t string (font-lock-string-face . 1)) 254 (("s\"" "c\"") immediate (font-lock-string-face . 1) 255 "[\"\n]" nil string (font-lock-string-face . 1)) 256 ((".\"") compile-only (font-lock-string-face . 1) 257 "[\"\n]" nil string (font-lock-string-face . 1)) 258 (("abort\"") compile-only (font-lock-keyword-face . 1) 259 "[\"\n]" nil string (font-lock-string-face . 1)) 260 (("{") compile-only (font-lock-variable-name-face . 1) 261 "[\n}]" nil name (font-lock-variable-name-face . 1)) 262 ((".(" "(") immediate (font-lock-comment-face . 1) 263 ")" nil comment (font-lock-comment-face . 1)) 264 (("\\" "\\G") immediate (font-lock-comment-face . 1) 265 "[\n]" nil comment (font-lock-comment-face . 1)) 266 267 (("[if]" "[?do]" "[do]" "[for]" "[begin]" 268 "[endif]" "[then]" "[loop]" "[+loop]" "[next]" "[until]" "[repeat]" 269 "[again]" "[while]" "[else]") 270 immediate (font-lock-keyword-face . 2)) 271 (("[ifdef]" "[ifundef]") immediate (font-lock-keyword-face . 2) 272 "[ \t\n]" t name (font-lock-function-name-face . 3)) 273 (("if" "begin" "ahead" "do" "?do" "+do" "u+do" "-do" "u-do" "for" 274 "case" "of" "?dup-if" "?dup-0=-if" "then" "endif" "until" 275 "repeat" "again" "leave" "?leave" 276 "loop" "+loop" "-loop" "next" "endcase" "endof" "else" "while" "try" 277 "recover" "endtry" "iferror" "restore" "endtry-iferror" 278 "assert(" "assert0(" "assert1(" "assert2(" 279 "assert3(" ")" "<interpretation" "<compilation" "interpretation>" 280 "compilation>") 281 compile-only (font-lock-keyword-face . 2)) 282 283 (("true" "false" "c/l" "bl" "cell" "pi" "w/o" "r/o" "r/w") 284 non-immediate (font-lock-constant-face . 2)) 285 (("~~" "break:" "dbg") compile-only (font-lock-warning-face . 2)) 286 (("break\"") compile-only (font-lock-warning-face . 1) 287 "[\"\n]" nil string (font-lock-string-face . 1)) 288 (("postpone" "[is]" "defers" "[']" "[compile]") 289 compile-only (font-lock-keyword-face . 2) 290 "[ \t\n]" t name (font-lock-function-name-face . 3)) 291 (("is" "what's") immediate (font-lock-keyword-face . 2) 292 "[ \t\n]" t name (font-lock-function-name-face . 3)) 293 (("<is>" "'" "see") non-immediate (font-lock-keyword-face . 2) 294 "[ \t\n]" t name (font-lock-function-name-face . 3)) 295 (("[to]") compile-only (font-lock-keyword-face . 2) 296 "[ \t\n]" t name (font-lock-variable-name-face . 3)) 297 (("to") immediate (font-lock-keyword-face . 2) 298 "[ \t\n]" t name (font-lock-variable-name-face . 3)) 299 (("<to>") non-immediate (font-lock-keyword-face . 2) 300 "[ \t\n]" t name (font-lock-variable-name-face . 3)) 301 302 (("create" "variable" "constant" "2variable" "2constant" "fvariable" 303 "fconstant" "value" "field" "user" "vocabulary" 304 "create-interpret/compile") 305 non-immediate (font-lock-type-face . 2) 306 "[ \t\n]" t name (font-lock-variable-name-face . 3)) 307 ("\\S-+%" non-immediate (font-lock-type-face . 2)) 308 (("defer" "alias" "create-interpret/compile:") 309 non-immediate (font-lock-type-face . 1) 310 "[ \t\n]" t name (font-lock-function-name-face . 3)) 311 (("end-struct") non-immediate (font-lock-keyword-face . 2) 312 "[ \t\n]" t name (font-lock-type-face . 3)) 313 (("struct") non-immediate (font-lock-keyword-face . 2)) 314 ("-?[0-9]+\\(\\.[0-9]*e\\(-?[0-9]+\\)?\\|\\.?[0-9a-f]*\\)" 315 immediate (font-lock-constant-face . 3)) 316 )) 317 318(defvar forth-use-objects nil 319 "*Non-nil makes forth-mode also hilight words from the \"Objects\" package.") 320(defvar forth-objects-words 321 '(((":m") definition-starter (font-lock-keyword-face . 1) 322 "[ \t\n]" t name (font-lock-function-name-face . 3)) 323 (("m:") definition-starter (font-lock-keyword-face . 1)) 324 ((";m") definition-ender (font-lock-keyword-face . 1)) 325 (("[current]" "[parent]") compile-only (font-lock-keyword-face . 1) 326 "[ \t\n]" t name (font-lock-function-name-face . 3)) 327 (("current" "overrides") non-immediate (font-lock-keyword-face . 2) 328 "[ \t\n]" t name (font-lock-function-name-face . 3)) 329 (("[to-inst]") compile-only (font-lock-keyword-face . 2) 330 "[ \t\n]" t name (font-lock-variable-name-face . 3)) 331 (("[bind]") compile-only (font-lock-keyword-face . 2) 332 "[ \t\n]" t name (font-lock-type-face . 3) 333 "[ \t\n]" t name (font-lock-function-name-face . 3)) 334 (("bind") non-immediate (font-lock-keyword-face . 2) 335 "[ \t\n]" t name (font-lock-type-face . 3) 336 "[ \t\n]" t name (font-lock-function-name-face . 3)) 337 (("inst-var" "inst-value") non-immediate (font-lock-type-face . 2) 338 "[ \t\n]" t name (font-lock-variable-name-face . 3)) 339 (("method" "selector") 340 non-immediate (font-lock-type-face . 1) 341 "[ \t\n]" t name (font-lock-function-name-face . 3)) 342 (("end-class" "end-interface") 343 non-immediate (font-lock-keyword-face . 2) 344 "[ \t\n]" t name (font-lock-type-face . 3)) 345 (("public" "protected" "class" "exitm" "implementation" "interface" 346 "methods" "end-methods" "this") 347 non-immediate (font-lock-keyword-face . 2)) 348 (("object") non-immediate (font-lock-type-face . 2))) 349 "Hilighting description for words of the \"Objects\" package") 350 351 352(defvar forth-use-oof nil 353 "*Non-nil makes forth-mode also hilight words from the \"OOF\" package.") 354(defvar forth-oof-words 355 '((("class") non-immediate (font-lock-keyword-face . 2) 356 "[ \t\n]" t name (font-lock-type-face . 3)) 357 (("var") non-immediate (font-lock-type-face . 2) 358 "[ \t\n]" t name (font-lock-variable-name-face . 3)) 359 (("method" "early") non-immediate (font-lock-type-face . 2) 360 "[ \t\n]" t name (font-lock-function-name-face . 3)) 361 (("::" "super" "bind" "bound" "link") 362 immediate (font-lock-keyword-face . 2) 363 "[ \t\n]" t name (font-lock-function-name-face . 3)) 364 (("ptr" "asptr" "[]") 365 immediate (font-lock-keyword-face . 2) 366 "[ \t\n]" t name (font-lock-variable-name-face . 3)) 367 (("class;" "how:" "self" "new" "new[]" "definitions" "class?" "with" 368 "endwith") 369 non-immediate (font-lock-keyword-face . 2)) 370 (("object") non-immediate (font-lock-type-face . 2))) 371 "Hilighting description for words of the \"OOF\" package") 372 373(defvar forth-local-words nil 374 "List of Forth words to prepend to `forth-words'. Should be set by a 375 forth source, using a local variables list at the end of the file 376 (\"Local Variables: ... forth-local-words: ... End:\" construct).") 377 378(defvar forth-custom-words nil 379 "List of Forth words to prepend to `forth-words'. Should be set in your 380 .emacs.") 381 382(defvar forth-hilight-level 3 "*Level of hilighting of Forth code.") 383 384(defvar forth-compiled-words nil "Compiled representation of `forth-words'.") 385 386(defvar forth-indent-words nil 387 "List of words that have indentation behaviour. 388Each element of `forth-indent-words' should have the form 389 (MATCHER INDENT1 INDENT2 &optional TYPE) 390 391MATCHER is either a list of strings to match, or a REGEXP. 392 If it's a REGEXP, it should not be surrounded by `\\<` or `\\>`, since 393 that'll be done automatically by the search routines. 394 395TYPE might be omitted. If it's specified, the only allowed value is 396 currently the symbol `non-immediate', meaning that the word will not 397 have any effect on indentation inside definitions. (:NONAME is a good 398 example for this kind of word). 399 400INDENT1 specifies how to indent a word that's located at the beginning 401 of a line, following any number of whitespaces. 402 403INDENT2 specifies how to indent words that are not located at the 404 beginning of a line. 405 406INDENT1 and INDENT2 are indentation specifications of the form 407 (SELF-INDENT . NEXT-INDENT), where SELF-INDENT is a numerical value, 408 specifying how the matching line and all following lines are to be 409 indented, relative to previous lines. NEXT-INDENT specifies how to indent 410 following lines, relative to the matching line. 411 412 Even values of SELF-INDENT and NEXT-INDENT correspond to multiples of 413 `forth-indent-level'. Odd values get an additional 414 `forth-minor-indent-level' added/substracted. Eg a value of -2 indents 415 1 * forth-indent-level to the left, wheras 3 indents 416 1 * forth-indent-level + forth-minor-indent-level columns to the right.") 417 418(setq forth-indent-words 419 '((("if" "begin" "do" "?do" "+do" "-do" "u+do" 420 "u-do" "?dup-if" "?dup-0=-if" "case" "of" "try" "iferror" 421 "[if]" "[ifdef]" "[ifundef]" "[begin]" "[for]" "[do]" "[?do]") 422 (0 . 2) (0 . 2)) 423 ((":" ":noname" "code" "struct" "m:" ":m" "class" "interface") 424 (0 . 2) (0 . 2) non-immediate) 425 ("\\S-+%$" (0 . 2) (0 . 0) non-immediate) 426 ((";" ";m") (-2 . 0) (0 . -2)) 427 (("again" "then" "endif" "endtry" "endcase" "endof" 428 "[then]" "[endif]" "[loop]" "[+loop]" "[next]" 429 "[until]" "[again]" "loop") 430 (-2 . 0) (0 . -2)) 431 (("end-code" "end-class" "end-interface" "end-class-noname" 432 "end-interface-noname" "end-struct" "class;") 433 (-2 . 0) (0 . -2) non-immediate) 434 (("protected" "public" "how:") (-1 . 1) (0 . 0) non-immediate) 435 (("+loop" "-loop" "until") (-2 . 0) (-2 . 0)) 436 (("else" "recover" "restore" "endtry-iferror" "[else]") 437 (-2 . 2) (0 . 0)) 438 (("does>") (-1 . 1) (0 . 0)) 439 (("while" "[while]") (-2 . 4) (0 . 2)) 440 (("repeat" "[repeat]") (-4 . 0) (0 . -4)))) 441 442(defvar forth-local-indent-words nil 443 "List of Forth words to prepend to `forth-indent-words', when a forth-mode 444buffer is created. Should be set by a Forth source, using a local variables 445list at the end of the file (\"Local Variables: ... forth-local-words: ... 446End:\" construct).") 447 448(defvar forth-custom-indent-words nil 449 "List of Forth words to prepend to `forth-indent-words'. Should be set in 450 your .emacs.") 451 452(defvar forth-indent-level 4 453 "*Indentation of Forth statements.") 454(defvar forth-minor-indent-level 2 455 "*Minor indentation of Forth statements.") 456(defvar forth-compiled-indent-words nil) 457 458;(setq debug-on-error t) 459 460;; Filter list by predicate. This is a somewhat standard function for 461;; functional programming languages. So why isn't it already implemented 462;; in Lisp?? 463(defun forth-filter (predicate list) 464 (let ((filtered nil)) 465 (dolist (item list) 466 (when (funcall predicate item) 467 (if filtered 468 (nconc filtered (list item)) 469 (setq filtered (cons item nil))))) 470 filtered)) 471 472;; Helper function for `forth-compile-word': return whether word has to be 473;; added to the compiled word list, for syntactic parsing and hilighting. 474(defun forth-words-filter (word) 475 (let* ((hilight (nth 2 word)) 476 (level (cdr hilight)) 477 (parsing-flag (nth 3 word))) 478 (or parsing-flag 479 (<= level forth-hilight-level)))) 480 481;; Helper function for `forth-compile-word': translate one entry from 482;; `forth-words' into the form (regexp regexp-depth word-description) 483(defun forth-compile-words-mapper (word) 484 ;; warning: we cannot rely on regexp-opt's PAREN argument, since 485 ;; XEmacs will use shy parens by default :-( 486 (let* ((matcher (car word)) 487 (regexp 488 (concat "\\(" (cond ((stringp matcher) matcher) 489 ((listp matcher) (regexp-opt matcher)) 490 (t (error "Invalid matcher"))) 491 "\\)")) 492 (depth (regexp-opt-depth regexp)) 493 (description (cdr word))) 494 (list regexp depth description))) 495 496;; Read `words' and create a compiled representation suitable for efficient 497;; parsing of the form 498;; (regexp (subexp-count word-description) (subexp-count2 word-description2) 499;; ...) 500(defun forth-compile-wordlist (words) 501 (let* ((mapped (mapcar 'forth-compile-words-mapper words)) 502 (regexp (concat "\\<\\(" 503 (mapconcat 'car mapped "\\|") 504 "\\)\\>")) 505 (sub-count 2) 506 (sub-list (mapcar 507 (lambda (i) 508 (let ((sub (cons sub-count (nth 2 i)))) 509 (setq sub-count (+ sub-count (nth 1 i))) 510 sub 511 )) 512 mapped))) 513 (cons regexp sub-list))) 514 515(defun forth-compile-words () 516 "Compile the the words from `forth-words' and `forth-indent-words' into 517 the format that's later used for doing the actual hilighting/indentation. 518 Store the resulting compiled wordlists in `forth-compiled-words' and 519`forth-compiled-indent-words', respective" 520 (setq forth-compiled-words 521 (forth-compile-wordlist 522 (forth-filter 'forth-words-filter forth-words))) 523 (setq forth-compiled-indent-words 524 (forth-compile-wordlist forth-indent-words))) 525 526(defun forth-hack-local-variables () 527 "Parse and bind local variables, set in the contents of the current 528 forth-mode buffer. Prepend `forth-local-words' to `forth-words' and 529 `forth-local-indent-words' to `forth-indent-words'." 530 (hack-local-variables) 531 (setq forth-words (append forth-local-words forth-words)) 532 (setq forth-indent-words (append forth-local-indent-words 533 forth-indent-words))) 534 535(defun forth-customize-words () 536 "Add the words from `forth-custom-words' and `forth-custom-indent-words' 537 to `forth-words' and `forth-indent-words', respective. Add 538 `forth-objects-words' and/or `forth-oof-words' to `forth-words', if 539 `forth-use-objects' and/or `forth-use-oof', respective is set." 540 (setq forth-words (append forth-custom-words forth-words 541 (if forth-use-oof forth-oof-words nil) 542 (if forth-use-objects forth-objects-words nil))) 543 (setq forth-indent-words (append 544 forth-custom-indent-words forth-indent-words))) 545 546 547 548;; get location of first character of previous forth word that's got 549;; properties 550(defun forth-previous-start (pos) 551 (let* ((word (get-text-property pos 'forth-word)) 552 (prev (previous-single-property-change 553 (min (point-max) (1+ pos)) 'forth-word 554 (current-buffer) (point-min)))) 555 (if (or (= (point-min) prev) word) prev 556 (if (get-text-property (1- prev) 'forth-word) 557 (previous-single-property-change 558 prev 'forth-word (current-buffer) (point-min)) 559 (point-min))))) 560 561;; Get location of the last character of the current/next forth word that's 562;; got properties, text that's parsed by the word is considered as parts of 563;; the word. 564(defun forth-next-end (pos) 565 (let* ((word (get-text-property pos 'forth-word)) 566 (next (next-single-property-change pos 'forth-word 567 (current-buffer) (point-max)))) 568 (if word next 569 (if (get-text-property next 'forth-word) 570 (next-single-property-change 571 next 'forth-word (current-buffer) (point-max)) 572 (point-max))))) 573 574(defun forth-next-whitespace (pos) 575 (save-excursion 576 (goto-char pos) 577 (skip-syntax-forward "-" (point-max)) 578 (point))) 579(defun forth-previous-word (pos) 580 (save-excursion 581 (goto-char pos) 582 (re-search-backward "\\<" pos (point-min) 1) 583 (point))) 584 585;; Delete all properties, used by Forth mode, from `from' to `to'. 586(defun forth-delete-properties (from to) 587 (remove-text-properties 588 from to '(face nil fontified nil 589 forth-parsed nil forth-word nil forth-state nil))) 590 591;; Get the index of the branch of the most recently evaluated regular 592;; expression that matched. (used for identifying branches "a\\|b\\|c...") 593(defun forth-get-regexp-branch () 594 (let ((count 2)) 595 (while (not (condition-case err (match-beginning count) 596 (args-out-of-range t))) ; XEmacs requires error handling 597 (setq count (1+ count))) 598 count)) 599 600;; seek to next forth-word and return its "word-description" 601(defun forth-next-known-forth-word (to) 602 (if (<= (point) to) 603 (progn 604 (let* ((regexp (car forth-compiled-words)) 605 (pos (re-search-forward regexp to t))) 606 (if pos (let ((branch (forth-get-regexp-branch)) 607 (descr (cdr forth-compiled-words))) 608 (goto-char (match-beginning 0)) 609 (cdr (assoc branch descr))) 610 'nil))) 611 nil)) 612 613;; Set properties of forth word at `point', eventually parsing subsequent 614;; words, and parsing all whitespaces. Set point to delimiter after word. 615;; The word, including it's parsed text gets the `forth-word' property, whose 616;; value is unique, and may be used for getting the word's start/end 617;; positions. 618(defun forth-set-word-properties (state data) 619 (let* ((start (point)) 620 (end (progn (re-search-forward "[ \t]\\|$" (point-max) 1) 621 (point))) 622 (type (car data)) 623 (hilight (nth 1 data)) 624 (bad-word (and (not state) (eq type 'compile-only))) 625 (hlface (if bad-word font-lock-warning-face 626 (if (<= (cdr hilight) forth-hilight-level) 627 (car hilight) nil)))) 628 (when hlface (put-text-property start end 'face hlface)) 629 ;; if word parses in current state, process parsed range of text 630 (when (or (not state) (eq type 'compile-only) (eq type 'immediate)) 631 (let ((parse-data (nthcdr 2 data))) 632 (while parse-data 633 (let ((delim (nth 0 parse-data)) 634 (skip-leading (nth 1 parse-data)) 635 (parse-type (nth 2 parse-data)) 636 (parsed-hilight (nth 3 parse-data)) 637 (parse-start (point)) 638 (parse-end)) 639 (when skip-leading 640 (while (and (looking-at delim) (> (match-end 0) (point)) 641 (not (looking-at "\n"))) 642 (forward-char))) 643 (re-search-forward delim (point-max) 1) 644 (setq parse-end (point)) 645 (forth-delete-properties end parse-end) 646 (when (<= (cdr parsed-hilight) forth-hilight-level) 647 (put-text-property 648 parse-start parse-end 'face (car parsed-hilight))) 649 (put-text-property 650 parse-start parse-end 'forth-parsed parse-type) 651 (setq end parse-end) 652 (setq parse-data (nthcdr 4 parse-data)))))) 653 (put-text-property start end 'forth-word start))) 654 655;; Search for known Forth words in the range `from' to `to', using 656;; `forth-next-known-forth-word' and set their properties via 657;; `forth-set-word-properties'. 658(defun forth-update-properties (from to &optional loudly) 659 (save-excursion 660 (let ((msg-count 0) (state) (word-descr) (last-location)) 661 (goto-char (forth-previous-word (forth-previous-start 662 (max (point-min) (1- from))))) 663 (setq to (forth-next-end (min (point-max) (1+ to)))) 664 ;; `to' must be on a space delimiter, if a parsing word was changed 665 (setq to (forth-next-whitespace to)) 666 (setq state (get-text-property (point) 'forth-state)) 667 (setq last-location (point)) 668 (forth-delete-properties (point) to) 669 (put-text-property (point) to 'fontified t) 670 ;; hilight loop... 671 (while (setq word-descr (forth-next-known-forth-word to)) 672 (when loudly 673 (when (equal 0 (% msg-count 100)) 674 (message "Parsing Forth code...%s" 675 (make-string (/ msg-count 100) ?.))) 676 (setq msg-count (1+ msg-count))) 677 (forth-set-word-properties state word-descr) 678 (when state (put-text-property last-location (point) 'forth-state t)) 679 (let ((type (car word-descr))) 680 (if (eq type 'definition-starter) (setq state t)) 681 (if (eq type 'definition-ender) (setq state nil)) 682 (setq last-location (point)))) 683 ;; update state property up to `to' 684 (if (and state (< (point) to)) 685 (put-text-property last-location to 'forth-state t)) 686 ;; extend search if following state properties differ from current state 687 (if (< to (point-max)) 688 (if (not (equal state (get-text-property (1+ to) 'forth-state))) 689 (let ((extend-to (next-single-property-change 690 to 'forth-state (current-buffer) (point-max)))) 691 (forth-update-properties to extend-to)) 692 )) 693 ))) 694 695;; save-buffer-state borrowed from `font-lock.el' 696(eval-when-compile 697 (defmacro forth-save-buffer-state (varlist &rest body) 698 "Bind variables according to VARLIST and eval BODY restoring buffer state." 699 `(let* (,@(append varlist 700 '((modified (buffer-modified-p)) (buffer-undo-list t) 701 (inhibit-read-only t) (inhibit-point-motion-hooks t) 702 before-change-functions after-change-functions 703 deactivate-mark buffer-file-name buffer-file-truename))) 704 ,@body 705 (when (and (not modified) (buffer-modified-p)) 706 (set-buffer-modified-p nil))))) 707 708;; Function that is added to the `change-functions' hook. Calls 709;; `forth-update-properties' and keeps care of disabling undo information 710;; and stuff like that. 711(defun forth-change-function (from to len &optional loudly) 712 (save-match-data 713 (forth-save-buffer-state 714 () 715 (unless forth-disable-parser (forth-update-properties from to loudly)) 716 (forth-update-warn-long-lines)))) 717 718(defun forth-fontification-function (from) 719 "Function to be called from `fontification-functions' of Emacs 21." 720 (save-match-data 721 (forth-save-buffer-state 722 ((to (min (point-max) (+ from 100)))) 723 (unless (or forth-disable-parser (not forth-jit-parser) 724 (get-text-property from 'fontified)) 725 (forth-update-properties from to))))) 726 727(byte-compile 'forth-set-word-properties) 728(byte-compile 'forth-next-known-forth-word) 729(byte-compile 'forth-update-properties) 730(byte-compile 'forth-delete-properties) 731(byte-compile 'forth-get-regexp-branch) 732 733;;; imenu support 734;;; 735(defvar forth-defining-words 736 '("VARIABLE" "CONSTANT" "2VARIABLE" "2CONSTANT" "FVARIABLE" "FCONSTANT" 737 "USER" "VALUE" "field" "end-struct" "VOCABULARY" "CREATE" ":" "CODE" 738 "DEFER" "ALIAS") 739 "List of words, that define the following word. 740Used for imenu index generation.") 741 742(defvar forth-defining-words-regexp nil 743 "Regexp that's generated for matching `forth-defining-words'") 744 745(defun forth-next-definition-starter () 746 (progn 747 (let* ((pos (re-search-forward forth-defining-words-regexp (point-max) t))) 748 (if pos 749 (if (or (text-property-not-all (match-beginning 0) (match-end 0) 750 'forth-parsed nil) 751 (text-property-not-all (match-beginning 0) (match-end 0) 752 'forth-state nil)) 753 (forth-next-definition-starter) 754 t) 755 nil)))) 756 757(defun forth-create-index () 758 (let* ((forth-defining-words-regexp 759 (concat "\\<\\(" (regexp-opt forth-defining-words) "\\)\\>")) 760 (index nil)) 761 (goto-char (point-min)) 762 (while (forth-next-definition-starter) 763 (if (looking-at "[ \t]*\\([^ \t\n]+\\)") 764 (setq index (cons (cons (match-string 1) (point)) index)))) 765 index)) 766 767;; top-level require is executed at byte-compile and load time 768(eval-and-compile (forth-require 'speedbar)) 769 770;; this code is executed at load-time only 771(when (memq 'speedbar features) 772 (speedbar-add-supported-extension ".fs") 773 (speedbar-add-supported-extension ".fb")) 774 775;; (require 'profile) 776;; (setq profile-functions-list '(forth-set-word-properties forth-next-known-forth-word forth-update-properties forth-delete-properties forth-get-regexp-branch)) 777 778;;; Indentation 779;;; 780 781;; Return, whether `pos' is the first forth word on its line 782(defun forth-first-word-on-line-p (pos) 783 (save-excursion 784 (beginning-of-line) 785 (skip-chars-forward " \t") 786 (= pos (point)))) 787 788;; Return indentation data (SELF-INDENT . NEXT-INDENT) of next known 789;; indentation word, or nil if there is no word up to `to'. 790;; Position `point' at location just after found word, or at `to'. Parsed 791;; ranges of text will not be taken into consideration! 792(defun forth-next-known-indent-word (to) 793 (if (<= (point) to) 794 (progn 795 (let* ((regexp (car forth-compiled-indent-words)) 796 (pos (re-search-forward regexp to t))) 797 (if pos 798 (let* ((start (match-beginning 0)) 799 (end (match-end 0)) 800 (branch (forth-get-regexp-branch)) 801 (descr (cdr forth-compiled-indent-words)) 802 (indent (cdr (assoc branch descr))) 803 (type (nth 2 indent))) 804 ;; skip words that are parsed (strings/comments) and 805 ;; non-immediate words inside definitions 806 (if (or (text-property-not-all start end 'forth-parsed nil) 807 (and (eq type 'non-immediate) 808 (text-property-not-all start end 809 'forth-state nil))) 810 (forth-next-known-indent-word to) 811 (if (forth-first-word-on-line-p (match-beginning 0)) 812 (nth 0 indent) (nth 1 indent)))) 813 nil))) 814 nil)) 815 816;; Translate indentation value `indent' to indentation column. Multiples of 817;; 2 correspond to multiples of `forth-indent-level'. Odd numbers get an 818;; additional `forth-minor-indent-level' added (or substracted). 819(defun forth-convert-to-column (indent) 820 (let* ((sign (if (< indent 0) -1 1)) 821 (value (abs indent)) 822 (major (* (/ value 2) forth-indent-level)) 823 (minor (* (% value 2) forth-minor-indent-level))) 824 (* sign (+ major minor)))) 825 826;; Return the column increment, that the current line of forth code does to 827;; the current or following lines. `which' specifies which indentation values 828;; to use. 1 means the indentation of following lines relative to current 829;; line, 0 means the indentation of the current line relative to the previous 830;; line. Return `nil', if there are no indentation words on the current line. 831(defun forth-get-column-incr (which) 832 (save-excursion 833 (let ((regexp (car forth-compiled-indent-words)) 834 (word-indent) 835 (self-indent nil) 836 (next-indent nil) 837 (to (save-excursion (end-of-line) (point)))) 838 (beginning-of-line) 839 (while (setq word-indent (forth-next-known-indent-word to)) 840 (let* ((self-incr (car word-indent)) 841 (next-incr (cdr word-indent)) 842 (self-column-incr (forth-convert-to-column self-incr)) 843 (next-column-incr (forth-convert-to-column next-incr))) 844 (setq next-indent (if next-indent next-indent 0)) 845 (setq self-indent (if self-indent self-indent 0)) 846 (if (or (and (> next-indent 0) (< self-column-incr 0)) 847 (and (< next-indent 0) (> self-column-incr 0))) 848 (setq next-indent (+ next-indent self-column-incr)) 849 (setq self-indent (+ self-indent self-column-incr))) 850 (setq next-indent (+ next-indent next-column-incr)))) 851 (nth which (list self-indent next-indent))))) 852 853;; Find previous line that contains indentation words, return the column, 854;; to which following text should be indented to. 855(defun forth-get-anchor-column () 856 (save-excursion 857 (if (/= 0 (forward-line -1)) 0 858 (let ((indent)) 859 (while (not (or (setq indent (forth-get-column-incr 1)) 860 (<= (point) (point-min)))) 861 (forward-line -1)) 862 (+ (current-indentation) (if indent indent 0)))))) 863 864(defun forth-indent-line (&optional flag) 865 "Correct indentation of the current Forth line." 866 (let* ((anchor (forth-get-anchor-column)) 867 (column-incr (forth-get-column-incr 0))) 868 (forth-indent-to (if column-incr (+ anchor column-incr) anchor)))) 869 870(defun forth-current-column () 871 (- (point) (save-excursion (beginning-of-line) (point)))) 872(defun forth-current-indentation () 873 (- (save-excursion (beginning-of-line) (forward-to-indentation 0) (point)) 874 (save-excursion (beginning-of-line) (point)))) 875 876(defun forth-indent-to (x) 877 (let ((p nil)) 878 (setq p (- (forth-current-column) (forth-current-indentation))) 879 (forth-delete-indentation) 880 (beginning-of-line) 881 (indent-to x) 882 (if (> p 0) (forward-char p)))) 883 884(defun forth-delete-indentation () 885 (save-excursion 886 (delete-region 887 (progn (beginning-of-line) (point)) 888 (progn (back-to-indentation) (point))))) 889 890(defun forth-indent-command () 891 (interactive) 892 (forth-indent-line t)) 893 894;; remove trailing whitespaces in current line 895(defun forth-remove-trailing () 896 (save-excursion 897 (end-of-line) 898 (delete-region (point) (progn (skip-chars-backward " \t") (point))))) 899 900;; insert newline, removing any trailing whitespaces in the current line 901(defun forth-newline-remove-trailing () 902 (save-excursion 903 (delete-region (point) (progn (skip-chars-backward " \t") (point)))) 904 (newline)) 905; (let ((was-point (point-marker))) 906; (unwind-protect 907; (progn (forward-line -1) (forth-remove-trailing)) 908; (goto-char (was-point))))) 909 910;; workaround for bug in `reindent-then-newline-and-indent' 911(defun forth-reindent-then-newline-and-indent () 912 (interactive "*") 913 (indent-according-to-mode) 914 (forth-newline-remove-trailing) 915 (indent-according-to-mode)) 916 917 918;;; Block file encoding/decoding (dk) 919;;; 920 921(defconst forth-c/l 64 "Number of characters per block line") 922(defconst forth-l/b 16 "Number of lines per block") 923 924;; Check whether the unconverted block file line, point is in, does not 925;; contain `\n' and `\t' characters. 926(defun forth-check-block-line (line) 927 (let ((end (save-excursion (beginning-of-line) (forward-char forth-c/l) 928 (point)))) 929 (save-excursion 930 (beginning-of-line) 931 (when (search-forward "\n" end t) 932 (message "Warning: line %i contains newline character #10" line) 933 (ding t)) 934 (beginning-of-line) 935 (when (search-forward "\t" end t) 936 (message "Warning: line %i contains tab character #8" line) 937 (ding t))))) 938 939(defun forth-convert-from-block (from to) 940 "Convert block file format to stream source in current buffer." 941 (let ((line (count-lines (point-min) from))) 942 (save-excursion 943 (goto-char from) 944 (set-mark to) 945 (while (< (+ (point) forth-c/l) (mark t)) 946 (setq line (1+ line)) 947 (forth-check-block-line line) 948 (forward-char forth-c/l) 949 (forth-newline-remove-trailing)) 950 (when (= (+ (point) forth-c/l) (mark t)) 951 (forth-remove-trailing)) 952 (mark t)))) 953 954;; Pad a line of a block file up to `forth-c/l' characters, positioning `point' 955;; at the end of line. 956(defun forth-pad-block-line () 957 (save-excursion 958 (end-of-line) 959 (if (<= (current-column) forth-c/l) 960 (move-to-column forth-c/l t) 961 (message "Line %i longer than %i characters, truncated" 962 (count-lines (point-min) (point)) forth-c/l) 963 (ding t) 964 (move-to-column forth-c/l t) 965 (delete-region (point) (progn (end-of-line) (point)))))) 966 967;; Replace tab characters in current line by spaces. 968(defun forth-convert-tabs-in-line () 969 (save-excursion 970 (beginning-of-line) 971 (while (search-forward "\t" (save-excursion (end-of-line) (point)) t) 972 (backward-char) 973 (delete-region (point) (1+ (point))) 974 (insert-char ?\ (- tab-width (% (current-column) tab-width)))))) 975 976;; Delete newline at end of current line, concatenating it with the following 977;; line. Place `point' at end of newly formed line. 978(defun forth-delete-newline () 979 (end-of-line) 980 (delete-region (point) (progn (beginning-of-line 2) (point)))) 981 982(defun forth-convert-to-block (from to &optional original-buffer) 983 "Convert range of text to block file format in current buffer." 984 (let* ((lines 0)) ; I have to count lines myself, since `count-lines' has 985 ; problems with trailing newlines... 986 (save-excursion 987 (goto-char from) 988 (set-mark to) 989 ;; pad lines to full length (`forth-c/l' characters per line) 990 (while (< (save-excursion (end-of-line) (point)) (mark t)) 991 (setq lines (1+ lines)) 992 (forth-pad-block-line) 993 (forth-convert-tabs-in-line) 994 (forward-line)) 995 ;; also make sure the last line is padded, if `to' is at its end 996 (end-of-line) 997 (when (= (point) (mark t)) 998 (setq lines (1+ lines)) 999 (forth-pad-block-line) 1000 (forth-convert-tabs-in-line)) 1001 ;; remove newlines between lines 1002 (goto-char from) 1003 (while (< (save-excursion (end-of-line) (point)) (mark t)) 1004 (forth-delete-newline)) 1005 ;; append empty lines, until last block is complete 1006 (goto-char (mark t)) 1007 (let* ((required (* (/ (+ lines (1- forth-l/b)) forth-l/b) forth-l/b)) 1008 (pad-lines (- required lines))) 1009 (while (> pad-lines 0) 1010 (insert-char ?\ forth-c/l) 1011 (setq pad-lines (1- pad-lines)))) 1012 (point)))) 1013 1014(defun forth-detect-block-file-p () 1015 "Return non-nil if the current buffer is in block file format. Detection is 1016done by checking whether the first line has 1024 characters or more." 1017 (save-restriction 1018 (widen) 1019 (save-excursion 1020 (goto-char (point-min)) 1021 (end-of-line) 1022 (>= (current-column) 1024)))) 1023 1024;; add block file conversion routines to `format-alist' 1025(defconst forth-block-format-description 1026 '(forth-blocks "Forth block source file" nil 1027 forth-convert-from-block forth-convert-to-block 1028 t normal-mode)) 1029(unless (memq forth-block-format-description format-alist) 1030 (setq format-alist (cons forth-block-format-description format-alist))) 1031 1032;;; End block file encoding/decoding 1033 1034;;; Block file editing 1035;;; 1036(defvar forth-overlay-arrow-string ">>") 1037(defvar forth-block-base 1 "Number of first block in block file") 1038(defvar forth-show-screen nil 1039 "Non-nil means to show screen starts and numbers (for block files)") 1040(defvar forth-warn-long-lines nil 1041 "Non-nil means to warn about lines that are longer than 64 characters") 1042 1043(defvar forth-screen-marker nil) 1044(defvar forth-screen-number-string nil) 1045 1046(defun forth-update-show-screen () 1047 "If `forth-show-screen' is non-nil, put overlay arrow to start of screen, 1048`point' is in. If arrow now points to different screen than before, display 1049screen number." 1050 (if (not forth-show-screen) 1051 (setq overlay-arrow-string nil) 1052 (save-excursion 1053 (let* ((line (count-lines (point-min) (min (point-max) (1+ (point))))) 1054 (first-line (1+ (* (/ (1- line) forth-l/b) forth-l/b))) 1055 (scr (+ forth-block-base (/ first-line forth-l/b)))) 1056 (setq overlay-arrow-string forth-overlay-arrow-string) 1057 (goto-line first-line) 1058 (setq overlay-arrow-position forth-screen-marker) 1059 (set-marker forth-screen-marker 1060 (save-excursion (goto-line first-line) (point))) 1061 (setq forth-screen-number-string (format "%d" scr)))))) 1062 1063(add-hook 'forth-motion-hooks 'forth-update-show-screen) 1064 1065(defun forth-update-warn-long-lines () 1066 "If `forth-warn-long-lines' is non-nil, display a warning whenever a line 1067exceeds 64 characters." 1068 (when forth-warn-long-lines 1069 (when (> (save-excursion (end-of-line) (current-column)) forth-c/l) 1070 (message "Warning: current line exceeds %i characters" 1071 forth-c/l)))) 1072 1073(add-hook 'forth-motion-hooks 'forth-update-warn-long-lines) 1074 1075;;; End block file editing 1076 1077 1078(defvar forth-mode-abbrev-table nil 1079 "Abbrev table in use in Forth-mode buffers.") 1080 1081(define-abbrev-table 'forth-mode-abbrev-table ()) 1082 1083(defvar forth-mode-map nil 1084 "Keymap used in Forth mode.") 1085 1086(if (not forth-mode-map) 1087 (setq forth-mode-map (make-sparse-keymap))) 1088 1089;(define-key forth-mode-map "\M-\C-x" 'compile) 1090(define-key forth-mode-map "\C-x\\" 'comment-region) 1091(define-key forth-mode-map "\C-x~" 'forth-remove-tracers) 1092(define-key forth-mode-map "\C-x\C-m" 'forth-split) 1093(define-key forth-mode-map "\e " 'forth-reload) 1094(define-key forth-mode-map "\t" 'forth-indent-command) 1095(define-key forth-mode-map "\C-m" 'forth-reindent-then-newline-and-indent) 1096(define-key forth-mode-map "\M-q" 'forth-fill-paragraph) 1097(define-key forth-mode-map "\e." 'forth-find-tag) 1098 1099;; setup for C-h C-i to work 1100(eval-and-compile (forth-require 'info-look)) 1101(when (memq 'info-look features) 1102 (defvar forth-info-lookup '(symbol (forth-mode "\\S-+" t 1103 (("(gforth)Word Index")) 1104 "\\S-+"))) 1105 (unless (memq forth-info-lookup info-lookup-alist) 1106 (setq info-lookup-alist (cons forth-info-lookup info-lookup-alist))) 1107 ;; in X-Emacs C-h C-i is by default bound to Info-query 1108 (define-key forth-mode-map [?\C-h ?\C-i] 'info-lookup-symbol)) 1109 1110;; (info-lookup-add-help 1111;; :topic 'symbol 1112;; :mode 'forth-mode 1113;; :regexp "[^ 1114;; ]+" 1115;; :ignore-case t 1116;; :doc-spec '(("(gforth)Name Index" nil "`" "' ")))) 1117 1118(require 'etags) 1119 1120(defun forth-find-tag (tagname &optional next-p regexp-p) 1121 (interactive (find-tag-interactive "Find tag: ")) 1122 (unless (or regexp-p next-p) 1123 (setq tagname (concat "\\(^\\|\\s-+\\)\\(" (regexp-quote tagname) 1124 "\\)\\s-*\x7f"))) 1125 (switch-to-buffer 1126 (find-tag-noselect tagname next-p t))) 1127 1128(defvar forth-mode-syntax-table nil 1129 "Syntax table in use in Forth-mode buffers.") 1130 1131;; Important: hilighting/indentation now depends on a correct syntax table. 1132;; All characters, except whitespace *must* belong to the "word constituent" 1133;; syntax class. If different behaviour is required, use of Categories might 1134;; help. 1135(if (not forth-mode-syntax-table) 1136 (progn 1137 (setq forth-mode-syntax-table (make-syntax-table)) 1138 (let ((char 0)) 1139 (while (< char ?!) 1140 (modify-syntax-entry char " " forth-mode-syntax-table) 1141 (setq char (1+ char))) 1142 (while (< char 256) 1143 (modify-syntax-entry char "w" forth-mode-syntax-table) 1144 (setq char (1+ char)))) 1145 )) 1146 1147(defun forth-mode-variables () 1148 (set-syntax-table forth-mode-syntax-table) 1149 (setq local-abbrev-table forth-mode-abbrev-table) 1150 (make-local-variable 'paragraph-start) 1151 (setq paragraph-start (concat "^$\\|" page-delimiter)) 1152 (make-local-variable 'paragraph-separate) 1153 (setq paragraph-separate paragraph-start) 1154 (make-local-variable 'indent-line-function) 1155 (setq indent-line-function 'forth-indent-line) 1156; (make-local-variable 'require-final-newline) 1157; (setq require-final-newline t) 1158 (make-local-variable 'comment-start) 1159 (setq comment-start "\\ ") 1160 ;(make-local-variable 'comment-end) 1161 ;(setq comment-end " )") 1162 (make-local-variable 'comment-column) 1163 (setq comment-column 40) 1164 (make-local-variable 'comment-start-skip) 1165 (setq comment-start-skip "\\\\ ") 1166 (make-local-variable 'comment-indent-function) 1167 (setq comment-indent-function 'forth-comment-indent) 1168 (make-local-variable 'parse-sexp-ignore-comments) 1169 (setq parse-sexp-ignore-comments t) 1170 (setq case-fold-search t) 1171 (make-local-variable 'forth-was-point) 1172 (setq forth-was-point -1) 1173 (make-local-variable 'forth-words) 1174 (make-local-variable 'forth-compiled-words) 1175 (make-local-variable 'forth-compiled-indent-words) 1176 (make-local-variable 'forth-hilight-level) 1177 (make-local-variable 'after-change-functions) 1178 (make-local-variable 'forth-show-screen) 1179 (make-local-variable 'forth-screen-marker) 1180 (make-local-variable 'forth-warn-long-lines) 1181 (make-local-variable 'forth-screen-number-string) 1182 (make-local-variable 'forth-use-oof) 1183 (make-local-variable 'forth-use-objects) 1184 (setq forth-screen-marker (copy-marker 0)) 1185 (add-hook 'after-change-functions 'forth-change-function) 1186 (if (and forth-jit-parser (>= emacs-major-version 21)) 1187 (add-hook 'fontification-functions 'forth-fontification-function)) 1188 (setq imenu-create-index-function 'forth-create-index)) 1189 1190;;;###autoload 1191(defun forth-mode () 1192 "\ 1193Major mode for editing Forth code. Tab indents for Forth code. Comments 1194are delimited with \\ and newline. Paragraphs are separated by blank lines 1195only. Block files are autodetected, when read, and converted to normal 1196stream source format. See also `forth-block-mode'. 1197\\{forth-mode-map} 1198 1199Variables controlling syntax hilighting/recognition of parsed text: 1200 `forth-words' 1201 List of words that have a special parsing behaviour and/or should be 1202 hilighted. Add custom words by setting forth-custom-words in your 1203 .emacs, or by setting forth-local-words, in source-files' local 1204 variables lists. 1205 forth-use-objects 1206 Set this variable to non-nil in your .emacs, or in a local variables 1207 list, to hilight and recognize the words from the \"Objects\" package 1208 for object-oriented programming. 1209 forth-use-oof 1210 Same as above, just for the \"OOF\" package. 1211 forth-custom-words 1212 List of custom Forth words to prepend to `forth-words'. Should be set 1213 in your .emacs. 1214 forth-local-words 1215 List of words to prepend to `forth-words', whenever a forth-mode 1216 buffer is created. That variable should be set by Forth sources, using 1217 a local variables list at the end of file, to get file-specific 1218 hilighting. 1219 0 [IF] 1220 Local Variables: ... 1221 forth-local-words: ... 1222 End: 1223 [THEN] 1224 forth-hilight-level 1225 Controls how much syntax hilighting is done. Should be in the range 1226 0..3 1227 1228Variables controlling indentation style: 1229 `forth-indent-words' 1230 List of words that influence indentation. 1231 forth-local-indent-words 1232 List of words to prepend to `forth-indent-words', similar to 1233 forth-local-words. Should be used for specifying file-specific 1234 indentation, using a local variables list. 1235 forth-custom-indent-words 1236 List of words to prepend to `forth-indent-words'. Should be set in your 1237 .emacs. 1238 forth-indent-level 1239 Indentation increment/decrement of Forth statements. 1240 forth-minor-indent-level 1241 Minor indentation increment/decrement of Forth statemens. 1242 1243Variables controlling block-file editing: 1244 forth-show-screen 1245 Non-nil means, that the start of the current screen is marked by an 1246 overlay arrow, and screen numbers are displayed in the mode line. 1247 This variable is by default nil for `forth-mode' and t for 1248 `forth-block-mode'. 1249 forth-overlay-arrow-string 1250 String to display as the overlay arrow, when `forth-show-screen' is t. 1251 Setting this variable to nil disables the overlay arrow. 1252 forth-block-base 1253 Screen number of the first block in a block file. Defaults to 1. 1254 forth-warn-long-lines 1255 Non-nil means that a warning message is displayed whenever you edit or 1256 move over a line that is longer than 64 characters (the maximum line 1257 length that can be stored into a block file). This variable defaults to 1258 t for `forth-block-mode' and to nil for `forth-mode'. 1259 1260Variables controlling interaction with the Forth-process (also see 1261`run-forth'): 1262 forth-program-name 1263 Program invoked by the `run-forth' command (including arguments). 1264 inferior-forth-mode-hook 1265 Hook for customising inferior-forth-mode. 1266 forth-compile-command 1267 Default command to execute on `compile'. 1268" 1269 (interactive) 1270 (kill-all-local-variables) 1271 (use-local-map forth-mode-map) 1272 (setq mode-name "Forth") 1273 (setq major-mode 'forth-mode) 1274 (forth-install-motion-hook) 1275 ;; convert buffer contents from block file format, if necessary 1276 (when (forth-detect-block-file-p) 1277 (widen) 1278 (message "Converting from Forth block source...") 1279 (forth-convert-from-block (point-min) (point-max)) 1280 (message "Converting from Forth block source...done")) 1281 ;; if user switched from forth-block-mode to forth-mode, make sure the file 1282 ;; is now stored as normal strem source 1283 (when (equal buffer-file-format '(forth-blocks)) 1284 (setq buffer-file-format nil)) 1285 (forth-mode-variables) 1286; (if (not (forth-process-running-p)) 1287; (run-forth forth-program-name)) 1288 (run-hooks 'forth-mode-hook)) 1289 1290;;;###autoload 1291(define-derived-mode forth-block-mode forth-mode "Forth Block Source" 1292 "Major mode for editing Forth block source files, derived from 1293`forth-mode'. Differences to `forth-mode' are: 1294 * files are converted to block format, when written (`buffer-file-format' 1295 is set to `(forth-blocks)') 1296 * `forth-show-screen' and `forth-warn-long-lines' are t by default 1297 1298Note that the length of lines in block files is limited to 64 characters. 1299When writing longer lines to a block file, a warning is displayed in the 1300echo area and the line is truncated. 1301 1302Another problem is imposed by block files that contain newline or tab 1303characters. When Emacs converts such files back to block file format, 1304it'll translate those characters to a number of spaces. However, when 1305you read such a file, a warning message is displayed in the echo area, 1306including a line number that may help you to locate and fix the problem. 1307 1308So have a look at the *Messages* buffer, whenever you hear (or see) Emacs' 1309bell during block file read/write operations." 1310 (setq buffer-file-format '(forth-blocks)) 1311 (setq forth-show-screen t) 1312 (setq forth-warn-long-lines t) 1313 (setq forth-screen-number-string (format "%d" forth-block-base)) 1314 (setq mode-line-format (append (reverse (cdr (reverse mode-line-format))) 1315 '("--S" forth-screen-number-string "-%-")))) 1316 1317(add-hook 'forth-mode-hook 1318 '(lambda () 1319 (make-local-variable 'compile-command) 1320 (setq compile-command "gforth ") 1321 (forth-hack-local-variables) 1322 (forth-customize-words) 1323 (forth-compile-words) 1324 (unless (and forth-jit-parser (>= emacs-major-version 21)) 1325 (forth-change-function (point-min) (point-max) nil t)))) 1326 1327(defun forth-fill-paragraph () 1328 "Fill comments (starting with '\'; do not fill code (block style 1329programmers who tend to fill code won't use emacs anyway:-)." 1330 ; Currently only comments at the start of the line are filled. 1331 ; Something like lisp-fill-paragraph may be better. We cannot use 1332 ; fill-paragraph, because it removes the \ from the first comment 1333 ; line. Therefore we have to look for the first line of the comment 1334 ; and use fill-region. 1335 (interactive) 1336 (save-excursion 1337 (beginning-of-line) 1338 (while (and 1339 (= (forward-line -1) 0) 1340 (looking-at "[ \t]*\\\\g?[ \t]+"))) 1341 (if (not (looking-at "[ \t]*\\\\g?[ \t]+")) 1342 (forward-line 1)) 1343 (let ((from (point)) 1344 (to (save-excursion (forward-paragraph) (point)))) 1345 (if (looking-at "[ \t]*\\\\g?[ \t]+") 1346 (progn (goto-char (match-end 0)) 1347 (set-fill-prefix) 1348 (fill-region from to nil)))))) 1349 1350(defun forth-comment-indent () 1351 (save-excursion 1352 (beginning-of-line) 1353 (if (looking-at ":[ \t]*") 1354 (progn 1355 (end-of-line) 1356 (skip-chars-backward " \t\n") 1357 (1+ (current-column))) 1358 comment-column))) 1359 1360 1361;; Forth commands 1362 1363(defun forth-remove-tracers () 1364 "Remove tracers of the form `~~ '. Queries the user for each occurrence." 1365 (interactive) 1366 (query-replace-regexp "\\(~~ \\| ~~$\\)" "" nil)) 1367 1368(define-key forth-mode-map "\C-x\C-e" 'compile) 1369(define-key forth-mode-map "\C-x\C-n" 'next-error) 1370(require 'compile) 1371 1372(defvar forth-compile-command "gforth ") 1373;(defvar forth-compilation-window-percent-height 30) 1374 1375(defun forth-split () 1376 (interactive) 1377 (forth-split-1 "*forth*")) 1378 1379(defun forth-split-1 (buffer) 1380 (if (not (eq (window-buffer) (get-buffer buffer))) 1381 (progn 1382 (delete-other-windows) 1383 (split-window-vertically 1384 (/ (frame-height) 2)) 1385 (other-window 1) 1386 (switch-to-buffer buffer) 1387 (goto-char (point-max)) 1388 (other-window 1)))) 1389 1390;;; Forth menu 1391;;; Mikael Karlsson <qramika@eras70.ericsson.se> 1392 1393;; (dk) code commented out due to complaints of XEmacs users. After 1394;; all, there's imenu/speedbar, which uses much smarter scanning 1395;; rules. 1396 1397;; (cond ((string-match "XEmacs\\|Lucid" emacs-version) 1398;; (require 'func-menu) 1399 1400;; (defconst fume-function-name-regexp-forth 1401;; "^\\(:\\)[ \t]+\\([^ \t]*\\)" 1402;; "Expression to get word definitions in Forth.") 1403 1404;; (setq fume-function-name-regexp-alist 1405;; (append '((forth-mode . fume-function-name-regexp-forth) 1406;; ) fume-function-name-regexp-alist)) 1407 1408;; ;; Find next forth word in the buffer 1409;; (defun fume-find-next-forth-function-name (buffer) 1410;; "Searches for the next forth word in BUFFER." 1411;; (set-buffer buffer) 1412;; (if (re-search-forward fume-function-name-regexp nil t) 1413;; (let ((beg (match-beginning 2)) 1414;; (end (match-end 2))) 1415;; (cons (buffer-substring beg end) beg)))) 1416 1417;; (setq fume-find-function-name-method-alist 1418;; (append '((forth-mode . fume-find-next-forth-function-name)))) 1419 1420;; )) 1421;;; End Forth menu 1422 1423;;; File folding of forth-files 1424;;; uses outline 1425;;; Toggle activation with M-x fold-f (when editing a forth-file) 1426;;; Use f9 to expand, f10 to hide, Or the menubar in xemacs 1427;;; 1428;;; Works most of the times but loses sync with the cursor occasionally 1429;;; Could be improved by also folding on comments 1430 1431;; (dk) This code needs a rewrite; just too ugly and doesn't use the 1432;; newer and smarter scanning rules of `imenu'. Who needs it anyway?? 1433 1434;; (require 'outline) 1435 1436;; (defun f-outline-level () 1437;; (cond ((looking-at "\\`\\\\") 1438;; 0) 1439;; ((looking-at "\\\\ SEC") 1440;; 0) 1441;; ((looking-at "\\\\ \\\\ .*") 1442;; 0) 1443;; ((looking-at "\\\\ DEFS") 1444;; 1) 1445;; ((looking-at "\\/\\* ") 1446;; 1) 1447;; ((looking-at ": .*") 1448;; 1) 1449;; ((looking-at "\\\\G") 1450;; 2) 1451;; ((looking-at "[ \t]+\\\\") 1452;; 3))) 1453 1454;; (defun fold-f () 1455;; (interactive) 1456;; (add-hook 'outline-minor-mode-hook 'hide-body) 1457 1458;; ; outline mode header start, i.e. find word definitions 1459;; ;;; (setq outline-regexp "^\\(:\\)[ \t]+\\([^ \t]*\\)") 1460;; (setq outline-regexp "\\`\\\\\\|:\\|\\\\ SEC\\|\\\\G\\|[ \t]+\\\\\\|\\\\ DEFS\\|\\/\\*\\|\\\\ \\\\ .*") 1461;; (setq outline-level 'f-outline-level) 1462 1463;; (outline-minor-mode) 1464;; (define-key outline-minor-mode-map '(shift up) 'hide-sublevels) 1465;; (define-key outline-minor-mode-map '(shift right) 'show-children) 1466;; (define-key outline-minor-mode-map '(shift left) 'hide-subtree) 1467;; (define-key outline-minor-mode-map '(shift down) 'show-subtree)) 1468 1469 1470;;(define-key global-map '(shift up) 'fold-f) 1471 1472;;; end file folding 1473 1474;;; func-menu is a package that scans your source file for function definitions 1475;;; and makes a menubar entry that lets you jump to any particular function 1476;;; definition by selecting it from the menu. The following code turns this on 1477;;; for all of the recognized languages. Scanning the buffer takes some time, 1478;;; but not much. 1479;;; 1480;; (cond ((string-match "XEmacs\\|Lucid" emacs-version) 1481;; (require 'func-menu) 1482;; ;; (define-key global-map 'f8 'function-menu) 1483;; (add-hook 'find-fible-hooks 'fume-add-menubar-entry) 1484;; ; (define-key global-map "\C-cg" 'fume-prompt-function-goto) 1485;; ; (define-key global-map '(shift button3) 'mouse-function-menu) 1486;; )) 1487 1488;;; 1489;;; Inferior Forth interpreter 1490;;; -- mostly copied from `cmuscheme.el' of Emacs 21.2 1491;;; 1492 1493(eval-and-compile (forth-require 'comint)) 1494 1495 (defvar forth-program-name "gforth" 1496 "*Program invoked by the `run-forth' command, including program arguments") 1497 1498 (defcustom inferior-forth-mode-hook nil 1499 "*Hook for customising inferior-forth-mode." 1500 :type 'hook 1501 :group 'forth) 1502 1503 (defvar inferior-forth-mode-map 1504 (let ((m (make-sparse-keymap))) 1505 (define-key m "\r" 'comint-send-input) 1506 (define-key m "\M-\C-x" 'forth-send-paragraph-and-go) 1507 (define-key m "\C-c\C-l" 'forth-load-file) 1508 m)) 1509 ;; Install the process communication commands in the forth-mode keymap. 1510 (define-key forth-mode-map "\e\C-m" 'forth-send-paragraph-and-go) 1511 (define-key forth-mode-map "\eo" 'forth-send-buffer-and-go) 1512 1513 (define-key forth-mode-map "\M-\C-x" 'forth-send-paragraph-and-go) 1514 (define-key forth-mode-map "\C-c\C-r" 'forth-send-region) 1515 (define-key forth-mode-map "\C-c\M-r" 'forth-send-region-and-go) 1516 (define-key forth-mode-map "\C-c\C-z" 'forth-switch-to-interactive) 1517 (define-key forth-mode-map "\C-c\C-l" 'forth-load-file) 1518 1519 (defvar forth-process-buffer) 1520 1521 (define-derived-mode inferior-forth-mode comint-mode "Inferior Forth" 1522 "Major mode for interacting with an inferior Forth process. 1523 1524The following commands are available: 1525\\{inferior-forth-mode-map} 1526 1527A Forth process can be fired up with M-x run-forth. 1528 1529Customisation: Entry to this mode runs the hooks on comint-mode-hook and 1530inferior-forth-mode-hook (in that order). 1531 1532You can send text to the inferior Forth process from other buffers containing 1533Forth source. 1534 forth-switch-to-interactive switches the current buffer to the Forth 1535 process buffer. 1536 forth-send-paragraph sends the current paragraph to the Forth process. 1537 forth-send-region sends the current region to the Forth process. 1538 forth-send-buffer sends the current buffer to the Forth process. 1539 1540 forth-send-paragraph-and-go, forth-send-region-and-go, 1541 forth-send-buffer-and-go switch to the Forth process buffer after 1542 sending their text. 1543For information on running multiple processes in multiple buffers, see 1544documentation for variable `forth-process-buffer'. 1545 1546Commands: 1547Return after the end of the process' output sends the text from the 1548end of process to point. If you accidentally suspend your process, use 1549\\[comint-continue-subjob] to continue it. " 1550 ;; Customise in inferior-forth-mode-hook 1551 (setq comint-prompt-regexp "^") 1552 (setq mode-line-process '(":%s"))) 1553 1554 (defun forth-args-to-list (string) 1555 (let ((where (string-match "[ \t]" string))) 1556 (cond ((null where) (list string)) 1557 ((not (= where 0)) 1558 (cons (substring string 0 where) 1559 (forth-args-to-list (substring string (+ 1 where) 1560 (length string))))) 1561 (t (let ((pos (string-match "[^ \t]" string))) 1562 (if (null pos) 1563 nil 1564 (forth-args-to-list (substring string pos 1565 (length string))))))))) 1566 1567;;;###autoload 1568 (defun run-forth (cmd) 1569 "Run an inferior Forth process, input and output via buffer *forth*. 1570If there is a process already running in `*forth*', switch to that buffer. 1571With argument, allows you to edit the command line (default is value 1572of `forth-program-name'). Runs the hooks `inferior-forth-mode-hook' 1573\(after the `comint-mode-hook' is run). 1574\(Type \\[describe-mode] in the process buffer for a list of commands.)" 1575 1576 (interactive (list (if current-prefix-arg 1577 (read-string "Run Forth: " forth-program-name) 1578 forth-program-name))) 1579 (if (not (comint-check-proc "*forth*")) 1580 (let ((cmdlist (forth-args-to-list cmd))) 1581 (set-buffer (apply 'make-comint "forth" (car cmdlist) 1582 nil (cdr cmdlist))) 1583 (inferior-forth-mode))) 1584 (setq forth-program-name cmd) 1585 (setq forth-process-buffer "*forth*") 1586 (pop-to-buffer "*forth*")) 1587 1588 (defun forth-send-region (start end) 1589 "Send the current region to the inferior Forth process." 1590 (interactive "r") 1591 (comint-send-region (forth-proc) start end) 1592 (comint-send-string (forth-proc) "\n")) 1593 1594 (defun forth-end-of-paragraph () 1595 (if (looking-at "[\t\n ]+") (skip-chars-backward "\t\n ")) 1596 (if (not (re-search-forward "\n[ \t]*\n" nil t)) 1597 (goto-char (point-max)))) 1598 1599 (defun forth-send-paragraph () 1600 "Send the current or the previous paragraph to the Forth process" 1601 (interactive) 1602 (let (end) 1603 (save-excursion 1604 (forth-end-of-paragraph) 1605 (skip-chars-backward "\t\n ") 1606 (setq end (point)) 1607 (if (null (re-search-backward "\n[ \t]*\n" nil t)) 1608 (goto-char (point-min))) 1609 (skip-chars-forward "\t\n ") 1610 (forth-send-region (point) end)))) 1611 1612 (defun forth-send-paragraph-and-go () 1613 "Send the current or the previous paragraph to the Forth process. 1614Then switch to the process buffer." 1615 (interactive) 1616 (forth-send-paragraph) 1617 (forth-switch-to-interactive t)) 1618 1619 (defun forth-send-buffer () 1620 "Send the current buffer to the Forth process." 1621 (interactive) 1622 (if (eq (current-buffer) forth-process-buffer) 1623 (error "Not allowed to send this buffer's contents to Forth")) 1624 (forth-send-region (point-min) (point-max))) 1625 1626 (defun forth-send-buffer-and-go () 1627 "Send the current buffer to the Forth process. 1628Then switch to the process buffer." 1629 (interactive) 1630 (forth-send-buffer) 1631 (forth-switch-to-interactive t)) 1632 1633 1634 (defun forth-switch-to-interactive (eob-p) 1635 "Switch to the Forth process buffer. 1636With argument, position cursor at end of buffer." 1637 (interactive "P") 1638 (if (get-buffer forth-process-buffer) 1639 (pop-to-buffer forth-process-buffer) 1640 (error "No current process buffer. See variable `forth-process-buffer'")) 1641 (cond (eob-p 1642 (push-mark) 1643 (goto-char (point-max))))) 1644 1645 (defun forth-send-region-and-go (my-start end) 1646 "Send the current region to the inferior Forth process. 1647Then switch to the process buffer." 1648 (interactive "r") 1649 (forth-send-region my-start end) 1650 (forth-switch-to-interactive t)) 1651 1652 (defcustom forth-source-modes '(forth-mode forth-block-mode) 1653 "*Used to determine if a buffer contains Forth source code. 1654If it's loaded into a buffer that is in one of these major modes, it's 1655considered a Forth source file by `forth-load-file' and `forth-compile-file'. 1656Used by these commands to determine defaults." 1657 :type '(repeat function) 1658 :group 'forth) 1659 1660 (defvar forth-prev-l/c-dir/file nil 1661 "Caches the last (directory . file) pair. 1662Caches the last pair used in the last `forth-load-file' or 1663`forth-compile-file' command. Used for determining the default in the 1664next one.") 1665 1666 (defun forth-load-file (file-name) 1667 "Load a Forth file FILE-NAME into the inferior Forth process." 1668 (interactive (comint-get-source "Load Forth file: " forth-prev-l/c-dir/file 1669 forth-source-modes t)) ; T because LOAD 1670 ; needs an exact name 1671 (comint-check-source file-name) ; Check to see if buffer needs saved. 1672 (setq forth-prev-l/c-dir/file (cons (file-name-directory file-name) 1673 (file-name-nondirectory file-name))) 1674 (comint-send-string (forth-proc) 1675 (concat "s\" " file-name "\" included\n"))) 1676 1677 1678 (defvar forth-process-buffer nil "*The current Forth process buffer. 1679 1680See `scheme-buffer' for an explanation on how to run multiple Forth 1681processes.") 1682 1683 (defun forth-proc () 1684 "Return the current Forth process. See variable `forth-process-buffer'." 1685 (let ((proc (get-buffer-process (if (eq major-mode 'inferior-forth-mode) 1686 (current-buffer) 1687 forth-process-buffer)))) 1688 (or proc 1689 (error "No current process. See variable `forth-process-buffer'")))) 1690 1691(provide 'forth-mode) 1692 1693;;; gforth.el ends here 1694