]> code.delx.au - gnu-emacs-elpa/blob - packages/shen-mode/shen-mode.el
Merge commit '0cda39255827f283e7578cd469ae42daad9556a2' from js2-mode
[gnu-emacs-elpa] / packages / shen-mode / shen-mode.el
1 ;;; shen-mode.el --- A major mode for editing shen source code
2
3 ;; Copyright (C) 2011, 2013 Free Software Foundation, Inc.
4
5 ;; Author: Eric Schulte <schulte.eric@gmail.com>
6 ;; Version: 0.1
7 ;; Keywords: languages, shen
8 ;; Description: A major mode for editing shen source code
9
10 ;; This file is part of GNU Emacs.
11
12 ;; GNU Emacs is free software: you can redistribute it and/or modify
13 ;; it under the terms of the GNU General Public License as published by
14 ;; the Free Software Foundation, either version 3 of the License, or
15 ;; (at your option) any later version.
16
17 ;; GNU Emacs is distributed in the hope that it will be useful,
18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 ;; GNU General Public License for more details.
21
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
24
25 ;;; Commentary:
26
27 ;; A major mode for editing Shen source code. Shen is a modern Lisp
28 ;; dialect with support for functional and declarative programming,
29 ;; pattern matching and a very powerful type system. See the
30 ;; following for more information on Shen. http://www.shenlanguage.org
31
32 ;;; Code:
33 (eval-when-compile (require 'cl))
34 (require 'lisp-mode)
35 (require 'imenu)
36
37 (defcustom shen-mode-hook '(turn-on-eldoc-mode)
38 "Normal hook run when entering `shen-mode'."
39 :type 'hook
40 :group 'shen)
41
42 (defvar shen-mode-map
43 (let ((map (make-sparse-keymap)))
44 (set-keymap-parent map lisp-mode-shared-map)
45 map)
46 "Currently just inherits from `lisp-mode-shared-map'.")
47
48 (eval-and-compile
49 (defconst shen-functions
50 '((* "number --> number --> number" "Number multiplication.")
51 (+ "number --> number --> number" "Number addition.")
52 (- "number --> number --> number" "Number subtraction.")
53 (/ "number --> number --> number" "Number division.")
54 (/. "_" "Abstraction builder, receives a variable and an expression; does the job of --> in the lambda calculus.")
55 (< "number --> number --> boolean" "Less than.")
56 (<-vector nil nil)
57 (<= "number --> number --> boolean" "Less than or equal to.")
58 (<e> nil nil)
59 (= "A --> A --> boolean" "Equal to.")
60 (== "A --> B --> boolean" "Equal to.")
61 (> "number --> number --> boolean" "Greater than.")
62 (>= "number --> number --> boolean" "Greater than or equal to.")
63 (@p "_" "Takes two inputs and forms an ordered pair.")
64 (@s "_" "Takes two or more inputs and forms a string.")
65 (@v "_" "Takes two or more inputs and forms a vector.")
66 (abort nil "throw a simple error")
67 (adjoin nil "add arg1 to list arg2 if not already a member")
68 (and "boolean --> boolean --> boolean" "Boolean and.")
69 (append "(list A) --> (list A) --> (list A)" "Appends two lists into one list.")
70 (apply "(A --> B) --> (A --> B)" "Applies a function to an input.")
71 (arity nil nil)
72 (assoc nil nil)
73 (assoc-type "symbol --> variable --> symbol" "Associates a Qi type (first input) with Lisp type (second input)..")
74 (average nil "return the average of two numbers")
75 (bind nil nil)
76 (boolean\? "A --> boolean" "Recognisor for booleans.")
77 (bound\? nil "check is a symbol is bound")
78 (byte->string nil "return the string represented by bytes")
79 (call nil nil)
80 (cd "string --> string" "Changes the home directory. (cd \"My Programs\") will cause (load \"hello_world.txt\") to load MyPrograms/hello_world.txt. (cd \"\") is the default.")
81 (character\? "A --> boolean" "Recognisor for characters.")
82 (compile nil nil)
83 (complex\? "A --> boolean" "Recognisor for complex numbers.")
84 (concat "symbol --> symbol --> symbol" "Concatenates two symbols.")
85 (congruent\? "A --> A --> boolean" "Retrns true if objects are identical or else if they are strings or characters which are identical differing at most in case or numbers of equal value (e.g. 1 and 1.0) or tuples composed of congruent elements.")
86 (cons "_" "A special form that takes an object e of type A and a list l of type (list A) and produces a list of type (list A) by adding e to the front of l.")
87 (cons\? "--> boolean" "Returns true iff the input is a non-empty list.")
88 (core nil nil)
89 (cut nil nil)
90 (debug "A --> string" "The input is ignored and debugging is returned; but all terminal output is echoed to the file debug.txt until the undebug function is executed.")
91 (declare "_" "Takes a function name f and a type t expressed as a list and gives f the type t.")
92 (define "_" "Define a function, takes a name, an optional type and a pattern matching body.")
93 (delete-file "string --> string" "The file named in the string is deleted and the string returned.")
94 (destroy "_" "Receives the name of a function and removes it and its type from the environment.")
95 (difference "(list A) --> (list A) --> (list A)" "Subtracts the elements of the second list from the first")
96 (do "_" "A special form: receives n well-typed expressions and evaluates each one, returning the normal form of the last one.")
97 (dump "string --> string" "Dumps all user-generated Lisp from the file f denoted by the argument into a file f.lsp.")
98 (echo "string --> string" "Echoes all terminal input/output to a file named by string (which is either appended to if it exists or created if not) until the command (echo \"\") is received which switches echo off.")
99 (element\? "A -> (list A) --> boolean" "Returns true iff the first input is an element in the second.")
100 (empty\? "--> boolean" "Returns true iff the input is [].")
101 (error "_" "A special form: takes a string followed by n (n --> 0) expressions. Prints error string.")
102 (eval "_" "Evaluates the input.")
103 (explode "A --> (list character)" "Explodes an object to a list of characters.")
104 (fail nil nil)
105 (fix "(A --> A) --> (A --> A)" "Applies a function to generate a fixpoint.")
106 (float\? "A --> boolean" "Recognisor for floating point numbers.")
107 (floor nil nil)
108 (format nil "takes a stream, a format string and args, formats and prints to the stream")
109 (freeze "A --> (lazy A)" "Returns a frozen version of its input.")
110 (fst "(A * B) --> A" "Returns the first element of a tuple.")
111 (fwhen nil nil)
112 (gensym "_" "Generates a fresh symbol or variable from a string..")
113 (get nil "gets property arg2 from object arg1")
114 (get-array "(array A) --> (list number) --> A --> A" "3-place function that takes an array of elements of type A, an index to that array as a list of natural numbers and an expression E of type A. If an object is stored at the index, then it is returned, otherwise the normal form of E is returned.")
115 (get-prop "_" "3-place function that takes a symbol S, a pointer P (which can be a string, symbol or number), and an expression E of any kind and returns the value pointed by P from S (if one exists) or the normal form of E otherwise.")
116 (hash nil "hash an object")
117 (hdv nil nil)
118 (head "(list A) --> A" "Returns the first element of a list.")
119 (identical nil nil)
120 (if "boolean --> A --> A" "takes a boolean b and two expressions x and y and evaluates x if b evaluates to true and evaluates y if b evaluates to false.")
121 (if-with-checking "string --> (list A)" "If type checking is enabled, raises the string as an error otherwise returns the empty list..")
122 (if-without-checking "string --> (list A)" "If type checking is disabled, raises the string as an error otherwise returns the empty list.")
123 (include "(list symbol) --> (list symbol)" "Includes the datatype theories or synonyms for use in type checking.")
124 (include-all-but "(list symbol) --> (list symbol)" "Includes all loaded datatype theories and synonyms for use in type checking apart from those entered.")
125 (inferences "A --> number" "The input is ignored. Returns the number of logical inferences executed since the last call to the top level.")
126 (input "_" "0-place function. Takes a user input i and returns the normal form of i.")
127 (input+ "_" "Special form. Takes inputs of the form : <expr>. Where d(<expr>) is the type denoted by the choice of expression (e.g. \"number\" denotes the type number). Takes a user input i and returns the normal form of i given i is of the type d(<expr>).")
128 (integer\? "A --> boolean" "Recognisor for integers.")
129 (interror nil nil)
130 (intersection "(list A) --> (list A) --> (list A)" "Computes the intersection of two lists.")
131 (intmake-string nil nil)
132 (intoutput nil nil)
133 (lambda "_" "Lambda operator from lambda calculus.")
134 (length "(list A) --> integer" "Returns the number of elements in a list.")
135 (let nil nil)
136 (limit nil nil)
137 (lineread "_" "Top level reader of read-evaluate-print loop. Reads elements into a list. lineread terminates with carriage return when brackets are balanced. ^ aborts lineread.")
138 (list "A .. A --> (list A)" "A special form. Assembles n (n --> 0) inputs into a list.")
139 (load "string --> symbol" "Takes a file name and loads the file, returning loaded as a symbol.")
140 (macroexpand nil nil)
141 (make-string "string A1 - An --> string" "A special form: takes a string followed by n (n --> 0) well-typed expressions; assembles and returns a string.")
142 (map "(A --> B) --> (list A) --> (list B)" "The first input is applied to each member of the second input and the results consed into one list..")
143 (mapcan "(A --> (list B)) --> (list A) --> (list B)" "The first input is applied to each member of the second input and the results appended into one list.")
144 (maxinferences "number --> number" "Returns the input and as a side-effect, sets a global variable to a number that limits the maximum number of inferences that can be expended on attempting to typecheck a program. The default is 1,000,000.")
145 (mod nil "arg1 mod arg2")
146 (newsym "symbol --> symbol" "Generates a fresh symbol from a symbol.")
147 (newvar "variable --> variable" "Generates a fresh variable from a variable")
148 (nl nil nil)
149 (not "boolean --> boolean" "Boolean not.")
150 (nth "number --> (list A) --> A" "Gets the nth element of a list numbered from 1.")
151 (number\? "A --> boolean" "Recognisor for numbers.")
152 (occurences "A --> B --> number" "Returns the number of times the first argument occurs in the second.")
153 (occurrences nil "returns the number of occurrences of arg1 in arg2")
154 (occurs-check "symbol --> boolean" "Receives either + or - and enables/disables occur checking in Prolog, datatype definitions and rule closures. The default is +.")
155 (opaque "symbol --> symbol" "Applied to a Lisp macro makes it opaque to Qi.")
156 (or "boolean --> (boolean --> boolean)" "Boolean or.")
157 (output "string A1 - An --> string" "A special form: takes a string followed by n (n --> 0) well-typed expressions; prints a message to the screen and returns an object of type string (the string \"done\").")
158 (preclude "(list symbol) --> (list symbol)" "Removes the mentioned datatype theories and synonyms from use in type checking.")
159 (preclude-all-but "(list symbol) --> (list symbol)" "Removes all the datatype theories and synonyms from use in type checking apart from the ones given.")
160 (print "A --> A" "Takes an object and prints it, returning it as a result.")
161 (profile "(A --> B) --> (A --> B)" "Takes a function represented by a function name and inserts profiling code returning the function as an output.")
162 (profile-results "A --> symbol" "The input is ignored. Returns a list of profiled functions and their timings since profile-results was last used.")
163 (ps "_" "Receives a symbol denoting a Qi function and prints the Lisp source code associated with the function.")
164 (put nil "puts value of arg3 as property arg2 in object arg1")
165 (put-array "(array A) --> (list number) --> A --> A" "3-place function that takes an array of elements of type A, an index to that array as a list of natural numbers and an expression E of type A. The normal form of E is stored at that index and then returned.")
166 (put-prop "_" "3-place function that takes a symbol S, a pointer P (a string symbol or number), and an expression E. The pointer P is set to point from S to the normal form of E which is then returned.")
167 (quit "_" "0-place function that exits Qi.")
168 (random "number --> number" "Given a positive number n, generates a random number between 0 and n-1.")
169 (rational\? "A --> boolean" "Recognisor for rational numbers.")
170 (read nil nil)
171 (read-char "A --> character" "The input is discarded and the character typed by the user is returned.")
172 (read-chars-as-stringlist "(list character) --> (character --> boolean) --> (list string)" "Returns a list of strings whose components are taken from the character list. The second input acts as a tokeniser. Thus (read-chars-as-stringlist [#\\H #\\i #\\Space #\\P #\\a #\\t] (/. X (= X #\\Space))) will produce [\"Hi\" \"Pat\"].")
173 (read-file "string --> (list unit)" "Returns the contents of an ASCII file designated by a string. Returns a list of units, where unit is an unspecified type.")
174 (read-file-as-charlist "string --> (list character)" "Returns the list of characters from the contents of an ASCII file designated by a string.")
175 (read-file-as-string nil nil)
176 (real\? "A --> boolean" "Recognisor for real numbers.")
177 (remove "A --> (list A) --> (list A)" "Removes all occurrences of an element from a list.")
178 (return nil nil)
179 (reverse "(list A)--> ?(list A)" "Reverses a list.")
180 (round "number--> ?number" "Rounds a number.")
181 (save "_" "0 place function. Saves a Qi image.")
182 (snd "(A * B) --> B" "Returns the second element of a tuple.")
183 (specialise "symbol --> symbol" "Receives the name of a function and turns it into a special form. Special forms are not curried during evaluation or compilation.")
184 (speed "number --> number" "Receives a value 0 to 3 and sets the performance of the generated Lisp code, returning its input. 0 is the lowest setting.")
185 (spy "symbol --> boolean" "Receives either + or - and respectively enables/disables tracing the operation of T*.")
186 (sqrt "number --> number" "Returns the square root of a number.")
187 (step "symbol --> boolean" "Receives either + or - and enables/disables stepping in the trace.")
188 (stinput nil nil)
189 (string\? "A --> boolean" "Recognisor for strings.")
190 (strong-warning "symbol --> boolean" "Takes + or -; if + then warnings are treated as error messages.")
191 (subst nil nil)
192 (sugar "symbol --> (A --> B) --> number --> (A --> B)" "Receives either in or out as first argument, a function f and an integer greater than 0 and returns f as a result. The function f is placed on the sugaring list at a position determined by the number.")
193 (sugar-list "symbol --> (list symbol)" "Receives either in or out as first argument, and returns the list of sugar functions.")
194 (sum nil "sum a list of numbers")
195 (symbol\? "A --> boolean" "Recognisor for symbols.")
196 (systemf nil nil)
197 (tail "(list A) --> (list A)" "Returns all but the first element of a non-empty list.")
198 (tc "symbol --> boolean" "Receives either + or - and respectively enables/disables static typing.")
199 (tc\? nil "return true if type checking")
200 (thaw "(lazy A) --> A" "Receives a frozen input and evaluates it to get the unthawed result..")
201 (time "A --> A" "Prints the run time for the evaluation of its input and returns its normal form.")
202 (tlv nil nil)
203 (track "symbol --> symbol" "Tracks the I/O behaviour of a function.")
204 (transparent "symbol --> symbol" "Applied to a Lisp macro makes it transparent to Qi.")
205 (tuple\? "A --> boolean" "Recognisor for tuples.")
206 (type "_" "Returns a type for its input (if any) or false if the input has no type.")
207 (unassoc-type "symbol --> symbol" "Removes any associations with the Qi type in the type association table.")
208 (undebug "A --> string" "The input is ignored, undebugging is returned and all terminal output is closed to the file debug.txt.")
209 (unify nil nil)
210 (unify! nil nil)
211 (union "(list A) --> (list A) --> (list A)" "Forms the union of two lists.")
212 (unprofile "(A --> B) --> (A --> B)" "Unprofiles a function.")
213 (unspecialise "symbol --> symbol" "Receives the name of a function and deletes its special form status.")
214 (unsugar "symbol --> (A --> B) --> (A --> B)" "Receives either out or in and the name of a function and removes its status as a sugar function.")
215 (untrack "symbol --> symbol" "Untracks a function.")
216 (value "_" "Applied to a symbol, returns the global value assigned to it.")
217 (variable\? "A --> boolean" "Applied to a variable, returns true.")
218 (vector nil nil)
219 (vector-> nil nil)
220 (vector\? nil nil)
221 (version "string --> string" "Changes the version string displayed on startup.")
222 (warn "string --> string" "Prints the string as a warning and returns \"done\". See strong-warning")
223 (write-to-file "string --> A --> string" "Writes the second input into a file named in the first input. If the file does not exist, it is created, else it is overwritten. If the second input is a string then it is written to the file without the enclosing quotes. The first input is returned.")
224 (y-or-n\? "string --> boolean" "Prints the string as a question and returns true for y and false for n."))
225 "Shen functions taken largely from the Qi documentation by Dr. Mark Tarver."))
226
227 \f
228 ;;; Fontification
229 (defconst shen-font-lock-keywords
230 (eval-when-compile
231 `(;; definitions
232 (,(concat "(\\("
233 (regexp-opt
234 '("define" "defmacro" "defprolog" "/." "synonyms" "defcc"))
235 "\\)\\>"
236 "[ \t]*(?"
237 "\\(\\sw+\\)?")
238 (1 font-lock-keyword-face)
239 (2 font-lock-function-name-face nil t))
240 ("(\\(lambda\\)\\>[ \t]*(?\\(\\sw+\\)?"
241 (1 font-lock-keyword-face)
242 (2 font-lock-variable-name-face nil t))
243 ;; data types
244 ("(\\(datatype\\)\\>[ \t]*(?\\(\\sw+\\)?"
245 (1 font-lock-keyword-face)
246 (2 font-lock-type-face nil t))
247 ;; variables
248 ("\\<\\([A-Z]\\w*\\)\\>" . font-lock-variable-name-face)
249 ;; control structures
250 (,(concat
251 "("
252 (regexp-opt
253 (append
254 '("let" "=" "eval-without-reader-macros" "freeze" "type") ; generic
255 '("if" "and" "or" "cond")) t) ; boolean
256 "\\>") . 1)
257 ;; errors
258 ("(\\(error\\)\\>" 1 font-lock-warning-face)
259 ;; built-in
260 (,(concat
261 "("
262 (regexp-opt
263 (mapcar
264 (lambda (it) (format "%s" it))
265 (append
266 '(intern function) ; symbols
267 '(pos tlstr cn str string?) ; strings
268 '(set value) ; assignment
269 '(cons hd tl cons?) ; lists
270 '(absvector address-> <-address absvector?) ; vector
271 '(pr read-byte open close) ; stream
272 '(get-time) ; time
273 '(+ - * / > < >= <= number?) ; arithmetic
274 '(fst snd tupple?) ; tuple
275 '(@s @v @p)
276 '(put get) ; property lists
277 '(simple-error trap-error error-to-string) ; error
278 ;; predicates
279 (mapcar
280 (lambda (it) (format "%s?" it))
281 '(boolean character complex congruent cons element empty float
282 integer number provable rational solved string symbol
283 tuple variable))
284 ;; misc functions
285 (mapcar #'car shen-functions)))
286 t)
287 "\\>")
288 1 font-lock-builtin-face)
289 ;; external global variables
290 (,(concat
291 (regexp-opt
292 (mapcar
293 (lambda (cnst) (format "*%s*" cnst))
294 '("language" "implementation" "port" "porters"
295 "stinput" "home-directory" "version"
296 "maximum-print-sequence-size" "printer" "macros")) t)
297 "\\>")
298 1 font-lock-builtin-face)))
299 "Default expressions to highlight in Shen mode.")
300
301 (defvar shen-mode-syntax-table
302 (let ((table (make-syntax-table)))
303 (dolist (pair '((?@ . "w")
304 (?_ . "w")
305 (?- . "w")
306 (?+ . "w")
307 (?? . "w")
308 (?! . "w")
309 (?< . "w")
310 (?> . "w")
311 (?/ . "w")
312 ;; comment delimiters
313 (?\\ . ". 14")
314 (?* . ". 23")))
315 (modify-syntax-entry (car pair) (cdr pair) table))
316 table)
317 "Syntax table to use in shen-mode.")
318
319 \f
320 ;;; Indentation
321 ;; Copied from qi-mode, which in turn is from scheme-mode and from lisp-mode
322 (defun shen-indent-function (indent-point state)
323 (let ((normal-indent (current-column)))
324 (goto-char (1+ (elt state 1)))
325 (parse-partial-sexp (point) calculate-lisp-indent-last-sexp 0 t)
326 (if (and (elt state 2)
327 (not (looking-at "\\sw\\|\\s_")))
328 ;; car of form doesn't seem to be a symbol
329 (progn
330 (if (not (> (save-excursion (forward-line 1) (point))
331 calculate-lisp-indent-last-sexp))
332 (progn (goto-char calculate-lisp-indent-last-sexp)
333 (beginning-of-line)
334 (parse-partial-sexp (point)
335 calculate-lisp-indent-last-sexp 0 t)))
336 ;; Indent under the list or under the first sexp on the same
337 ;; line as calculate-lisp-indent-last-sexp. Note that first
338 ;; thing on that line has to be complete sexp since we are
339 ;; inside the innermost containing sexp.
340 (backward-prefix-chars)
341 (current-column))
342 (let ((function (buffer-substring (point)
343 (progn (forward-sexp 1) (point))))
344 method)
345 (setq method (or (get (intern-soft function) 'shen-indent-function)
346 (get (intern-soft function) 'shen-indent-hook)))
347 (cond ((or (eq method 'defun)
348 (and (null method)
349 (> (length function) 3)
350 (string-match "\\`def" function)))
351 (lisp-indent-defform state indent-point))
352 ((integerp method)
353 (lisp-indent-specform method state
354 indent-point normal-indent))
355 (method
356 (funcall method state indent-point normal-indent)))))))
357
358 (defun shen-let-indent (state indent-point normal-indent)
359 (let ((edge (- (current-column) 2)))
360 (goto-char indent-point) (skip-chars-forward " \t")
361 (if (looking-at "[-a-zA-Z0-9+*/?!@$%^&_:~]")
362 ;; deeper indent because we're still defining local variables
363 (lisp-indent-specform 5 state indent-point normal-indent)
364 ;; shallow indent because we're in the body
365 edge)))
366
367 (defun shen-package-indent (_state _indent-point _normal-indent)
368 (- (current-column) 8))
369
370 (put 'let 'shen-indent-function 'shen-let-indent)
371 (put 'lambda 'shen-indent-function 1)
372 (put 'package 'shen-indent-function 'shen-package-indent)
373 (put 'datatype 'shen-indent-function 1)
374
375 \f
376 ;;; Function documentation
377 (defun shen-current-function ()
378 (ignore-errors
379 (save-excursion
380 (backward-up-list)
381 (forward-char 1)
382 (thing-at-point 'word))))
383
384 (defun shen-mode-eldoc ()
385 (let ((func (assoc (intern (or (shen-current-function) "")) shen-functions)))
386 (when func
387 (format "%s%s:%s"
388 (propertize (symbol-name (car func))
389 'face 'font-lock-function-name-face)
390 (if (cadr func) (concat "[" (cadr func) "]") "")
391 (if (caddr func) (concat " " (caddr func)) "")))))
392
393 (defvar shen-imenu-generic-expression
394 '(("Functions" "^\\s-*(\\(define\\)" 1)))
395
396 \f
397 ;;; Major mode definition
398 ;; apparently some versions of Emacs don't have `prog-mode' defined
399 (unless (fboundp 'prog-mode)
400 (defalias 'prog-mode 'fundamental-mode))
401
402 ;;;###autoload
403 (define-derived-mode shen-mode prog-mode "shen"
404 "Major mode for editing Shen code."
405 :syntax-table shen-mode-syntax-table
406 ;; Set a variety of local variables.
407 (dolist (pair `((adaptive-fill-mode . nil)
408 (fill-paragraph-function . lisp-fill-paragraph)
409 (indent-line-function . lisp-indent-line)
410 (lisp-indent-function . shen-indent-function)
411 (parse-sexp-ignore-comments . t)
412 (comment-start . "\\* ")
413 (comment-end . " *\\")
414 (comment-add . 0)
415 (comment-column . 32)
416 (parse-sexp-ignore-comments . t)
417 (comment-use-global-state . nil)
418 (comment-multi-line . t)
419 (eldoc-documentation-function . shen-mode-eldoc)
420 (imenu-case-fold-search . t)
421 (imenu-generic-expression . ,shen-imenu-generic-expression)
422 (mode-name . "Shen")
423 (font-lock-defaults . (shen-font-lock-keywords))))
424 (set (make-local-variable (car pair)) (cdr pair))))
425
426 ;;;###autoload
427 (add-to-list 'auto-mode-alist '("\\.shen\\'" . shen-mode))
428
429 (provide 'shen-mode)
430 ;;; shen-mode.el ends here