;; --------------------------------------------------------------------------- ;; Author: Stuart Robinson ;; Date: 10 September 2007 ;; Description: This file provides an fst mode for emacs ;; Versions: 0.1 -- initial implementation for fst files with the suffix ;; .infile ;; 0.2 -- cleaned up regex for built-ins, fixed highlighting ;; glitches observed by Lauri Karttunen , added .script ;; file suffix for mode ;; 0.3 -- fixed built-in highlighting for 'regex' and 'push' ;; 0.4 -- backslashes are handled properly (not as escape char) ;; 0.5 -- various minor fixes (added .p./.P./.o.) ;; Notes: * original implementation based on code found at ;; http://two-wugs.net/emacs/mode-tutorial.html ;; * the pre-defined font categories for emacs need to be thought ;; out in terms of fst: for example, commands and built-in ;; variables alike are treated as builtins, and it's not clear ;; what a keyword would be; there are also probably more constants ;; Bugs: * curly braces should be treated as string delimiteres (like ;; quotes) ;; * pound sign in .#. treated as beginning of comment ;; --------------------------------------------------------------------------- (defvar fst-mode-hook nil) (defvar fst-mode-map (let ((fst-mode-map (make-keymap))) (define-key fst-mode-map "\C-j" 'newline-and-indent) fst-mode-map) "Keymap for fst major mode") ;; built-ins & variables (defconst fst-font-lock-keywords-1 (list ;; NOTE: The regex is wrapped in \\< and \\> to ensure that it only matches words ;; (i.e., it doesn't match substrings). It is generated by running the program ;; keyword-list-to-emacs-regex.rb on the file fst-keywords-no-spaces.txt. '("\\<\\(for\\|add properties\\|ambiguity net\\|\\(apply \\(patterns \\)?\\)?\\(up\\|down\\)\\|cleanup net\\|clear\\( stack\\)?\\|close sigma\\|collect epsilon-loops\\|compact \\(net\\|sigma\\)\\|compile-replace \\(upper\\|lower\\)\\|complete net\\|compose\\( net\\)?\\|compose-apply \\(up\\|down\\)\\|concatenate\\( net\\)?\\|continue script\\|crossproduct net\\|determinize net\\|edit properties\\|eliminate flag\\|epsilon-remove net\\|extract-compile-replace \\(upper\\|lower\\)\\|factorize \\(up\\|down\\)\\|ignore net\\|inspect net\\|interrupt script\\|intersect\\( net\\)?\\|invert\\( net\\)?\\|label net\\|load\\( \\(defined\\|stack\\)\\)?\\|lower-side net\\|minimize net\\|minus net\\|multi char sigma net\\|name net\\|negate\\( net\\)?\\|one-plus net\\|optimize net\\|paste net labels\\|pop stack\\|print \\(aliases\\|arc-tally\\|defined\\|directory\\|eqv-labels\\|file-info\\|flags\\|label-maps\\|label-tally\\|labels\\|list\\|lists\\|longest-string\\(-size\\)?\\|lower-words\\|name\\|net\\|nth-lower\\|nth-upper\\|num-lower\\|num-upper\\|random-lower\\|random-upper\\|random-words\\|shortest-string\\(-size\\)?\\|sigma\\(\\(-word\\)?-tally\\)?\\|size\\|stack\\|storage\\|upper-words\\|words\\)\\|prune net\\|push\\( \\(defined\\|epsilons\\)\\)?\\|\\(read \\)?regex\\|read \\(lec\\|lexc\\|prolog\\|properties\\|spaced-text\\|text\\)\\|reduce labelset\\|reverse\\( net\\)?\\|rotate stack\\|save\\( \\(defined\\|stack\\)\\)?\\|share arcs\\|shuffle net\\|sigma net\\|single char sigma net\\|sort net\\|sub-string net\\|substitute \\(defined\\|label\\|symbol\\)\\|substring net\\|\\(test \\)?equivalent\\|test \\(lower-bounded\\|lower-universal\\|non-null\\|null\\|overlap\\|sublanguage\\|unambiguous-down\\|unambiguous-up\\|upper-bounded\\|upper-universal\\)\\|turn stack\\|twosided flag-diacritics\\|uncompact net\\|unfactorize down\\|unfactorize up\\|union\\( net\\)?\\|unoptimize\\( net\\)?\\|unreduce labelset\\|unshare arcs\\|unvectorize net\\|upper-side net\\|vectorize net\\|virtual \\(compose\\|concatenate\\|copy\\|determinize\\|intersect\\|lower\\|minus\\|negate\\|one-plus\\|option\\|priority-union\\|union\\|upper\\|zero-plus\\)\\|write \\(definition\\|dot\\|prolog\\|properties\\|spaced-text\\|text\\)\\|zero-plus net\\|alias\\|apropos\\|assert\\|char-encoding\\|completion\\|copyright-owner\\|count-patterns\\|define\\|delete-patterns\\|directory\\|echo\\|extract-patterns\\|fail-safe-composition\\|flag-is-epsilon\\|help\\|label-map\\|license-type\\|list\\|locate-patterns\\|mark-\\(patterns\\|version\\)\\|max-\\(context-length\\|state-visits\\)\\|minimal\\|name-nets\\|need-separators\\|obey-flags\\|print-\\(pairs\\|sigma\\|space\\)\\|process-in-order\\|quit\\(-on-fail\\)?\\|quote-special\\|random-seed\\|recode-cp1252\\|recursive-\\(apply\\|define\\)\\|retokenize\\|seq-\\(final-arcs\\|intern-arcs\\|string-one\\)\\|set\\|show\\(-flags\\)?\\|sort-arcs\\|source\\|system\\|undefine\\|unlist\\|use-mmap\\|use-timer\\|vectorize-n\\|verbose\\|virtual-to-real\\)\\>" . font-lock-builtin-face) '("\\('\\w*'\\)" . font-lock-variable-name-face)) "Minimal highlighting expressions for FST mode") ;; keywords and constants (defconst fst-font-lock-keywords-2 (append fst-font-lock-keywords-1 (list '("\\(\\$\\|?\\|~\\|@\\||\\|->\\|<-\\|&\\|_\\|*\\|\\\\\\|0\\|\\.#\\.\\|\\.P\\.\\|\\.p\\.\\|\\.o\\.\\|@->\\|->@\\|@>\\|>@\\)" . font-lock-keyword-face) '("\\<\\(ON\\|OFF\\|NONE\\)\\>" . font-lock-constant-face))) "Additional Keywords to highlight in FST mode") ;; don't understand why this is necessary... (defconst fst-font-lock-keywords-3 (append fst-font-lock-keywords-2 (list '("" . font-lock-constant-face))) "Balls-out highlighting in FST mode") (defvar fst-font-lock-keywords fst-font-lock-keywords-3 "Default highlighting expressions for FST mode") ;; Indentation not being handled properly--needs to be updated (defun fst-indent-line () "Indent current line as FST code" (interactive) (beginning-of-line) ; Check for rule 1 (if (bobp) (indent-line-to 0) (let ((not-indented t) cur-indent) ; Check for rule 2 (if (looking-at "^[ \t]*END_") (progn (save-excursion (forward-line -1) (setq cur-indent (- (current-indentation) default-tab-width))) (if (< cur-indent 0) (setq cur-indent 0))) (save-excursion (while not-indented (forward-line -1) ; Check for rule 3 (if (looking-at "^[ \t]*END_") (progn (setq cur-indent (current-indentation)) (setq not-indented nil)) ; Check for rule 4 (if (looking-at "^[ \t]*\\(PARTICIPANT\\|MODEL\\|APPLICATION\\|WORKFLOW\\|ACTIVITY\\|DATA\\|TOOL_LIST\\|TRANSITION\\)") (progn (setq cur-indent (+ (current-indentation) default-tab-width)) (setq not-indented nil)) ; Check for rule 5 (if (bobp) (setq not-indented nil))))))) ; If we didn't see an indentation hint, then allow no indentation (if cur-indent (indent-line-to cur-indent) (indent-line-to 0))))) ;; Change the interpretation of particular chars in Emacs' syntax table (defvar fst-mode-syntax-table (let ( (fst-mode-syntax-table (make-syntax-table) ) ) (modify-syntax-entry ?# "<" fst-mode-syntax-table) ; start comment (modify-syntax-entry ?\n ">" fst-mode-syntax-table) ; end comment (modify-syntax-entry ?\\ "_" fst-mode-syntax-table) ; don't escape quote (modify-syntax-entry ?% "/" fst-mode-syntax-table) ; functions as escape char fst-mode-syntax-table ) "Syntax table for fst-mode" ) (defun fst-mode () "Major mode for editing fst scripts" (interactive) (kill-all-local-variables) (set-syntax-table fst-mode-syntax-table) (use-local-map fst-mode-map) (set (make-local-variable 'font-lock-defaults) '(fst-font-lock-keywords)) (set (make-local-variable 'indent-line-function) 'fst-indent-line) (setq major-mode 'fst-mode) (setq mode-name "FST") (run-hooks 'fst-mode-hook)) ;; Make FST mode available for .infile and .script files (provide 'fst-mode) (add-to-list 'auto-mode-alist '("\\.infile\\'" . fst-mode)) (add-to-list 'auto-mode-alist '("\\.script\\'" . fst-mode))