-
Notifications
You must be signed in to change notification settings - Fork 0
/
hare-mode.el
250 lines (205 loc) · 8.42 KB
/
hare-mode.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
;;; hare-mode.el --- Hare mode -*- lexical-binding: t; -*-
;; Copyright (C) 2020 Benjamín Buccianti
;; Copyright (C) 2020 Amin Bandali
;; Copyright (C) 2020 Theodor Thornhill
;; Copyright (C) 2022 Sebastian
;; Author: Benjamín Buccianti <[email protected]>
;; Amin Bandali <[email protected]>
;; Theodor Thornhill <[email protected]>
;; Sebastian <[email protected]>
;; Keywords: languages
;; URL: https://git.sr.ht/~bbuccianti/hare-mode
;; Version: 0.1.0
;; Hare mode is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published
;; by the Free Software Foundation, either version 3 of the License,
;; or (at your option) any later version.
;; Hare mode is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with Hare mode. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; This is a major mode for editing `hare' files in GNU Emacs.
;;; Code:
(require 'smie)
(defvar hare-mode-keywords
'("as" "break" "const" "continue" "def" "else" "export" "fn" "for"
"if" "is" "let" "match" "return" "size" "static" "switch" "use"
"_" "defer" "yield" "case" "type")
"Keywords used in `hare-mode'.")
(defvar hare-mode-types
'("u8" "u16" "u32" "u64" "i8" "i16" "i32" "i64" "int" "uint"
"uintptr" "f32" "f64" "bool" "char" "str" "void" "struct" "union"
"nullable" "null" "valist" "rune" "enum")
"Types used in `hare-mode'.")
(defvar hare-mode-constants
'("true" "false")
"Constants used in `hare-mode'.")
(defvar hare-mode-builtins
'("@init" "@symbol" "@test" "@fini" "@offset" "@noreturn" "len"
"offset" "free" "alloc" "assert" "vastart" "vaarg" "vaend" "append"
"insert" "delete" "abort")
"Built in identifiers used in `hare-mode'.")
(defconst hare-mode--regexp-declaration-line-beginning
(concat "^" (regexp-opt hare-mode-keywords))
"Regexp matching `hare-mode-keywords' on line beginning.")
(defconst hare-mode--regexp-declaration-end
(regexp-opt '("};" ");"))
"Regexp matching declaration endings.")
(defconst hare-mode-indent-offset 8
"Indent hare code by this number of spaces.")
(defvar hare-mode-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "<tab>") 'hare-mode-indent-forward)
(define-key map (kbd "<backtab>") 'hare-mode-indent-backward)
map)
"Keymap for `hare-mode'.")
(defvar hare-mode-font-lock-defaults
`((("\"\\.\\*\\?" . font-lock-string-face)
(,(regexp-opt hare-mode-keywords 'symbols) . font-lock-keyword-face)
(,(regexp-opt hare-mode-constants 'symbols) . font-lock-constant-face)
(,(regexp-opt hare-mode-builtins 'symbols) . font-lock-builtin-face)
(,(regexp-opt hare-mode-types 'symbols) . font-lock-type-face))))
(defconst hare-mode-syntax-table
(let ((st (make-syntax-table)))
;; strings and characters
(modify-syntax-entry ?\" "\"" st)
(modify-syntax-entry ?\' "\"" st)
(modify-syntax-entry ?\\ "\\" st)
;; comments
(modify-syntax-entry ?/ ". 12" st)
(modify-syntax-entry ?\n ">" st)
;; @ is part of symbols in Hare
(modify-syntax-entry ?@ "_" st)
;; return our modified syntax table
st))
(defvar hare-mode-imenu-generic-expression
`(;; Functions
(nil
,(concat
(rx line-start)
(rx (opt "export" (1+ blank)))
(rx (opt "@" (or "test" "init" "fini") (1+ blank)))
(rx (opt "@symbol(" (regexp "\".*\"") ")" (1+ blank)))
(rx "fn" (1+ blank))
(rx (group (or letter "_") (0+ (or letter "_" digit)))) ;; identifier
(rx (0+ (syntax whitespace)))
;; Optional parameter list
(rx (opt (syntax open-parenthesis)
(0+ (any letter ":" "*" "," "_" "[" "]" digit whitespace))
(syntax close-parenthesis)))
(rx (0+ (syntax whitespace)))
(rx (opt (1+ letter))) ;; Optional nullable
(rx (0+ (syntax whitespace)))
(rx (opt (1+ letter))) ;; Optional const
(rx (0+ (syntax whitespace)))
(rx (opt (or "*" "&")) (or (1+ (any letter ":")))) ;; result type
(rx (0+ (syntax whitespace)))
"="
) 1)))
(defun hare-mode-beginning-of-defun (&optional arg)
"Jump to the beginning of a \"defun\".
Search backwards for ARG amount of occurrences of identifiers
represented by `hare-mode--regexp-declaration-line-beginning'."
(interactive "p")
(unless arg (setq arg 1))
(re-search-backward hare-mode--regexp-declaration-line-beginning nil t arg))
(defun hare-mode-end-of-defun (&optional arg)
"Jump to the end of a \"defun\".
Search backwards for ARG amount of occurrences of identifiers
represented by `hare-mode--regexp-declaration-end'."
(interactive "p")
(unless arg (setq arg 1))
(re-search-forward hare-mode--regexp-declaration-end nil t arg))
(defun hare-mode--do-indent (indent)
"Helper to indent a line to the column at INDENT."
(if (<= (current-column) (current-indentation))
(ignore-errors (indent-line-to indent))
(save-excursion (ignore-errors (indent-line-to indent)))))
(defun hare-mode-indent-forward (&optional arg)
"Indent line to the next ARG tabstop."
(interactive "p")
(or arg (setq arg 1))
(hare-mode--do-indent
(indent-next-tab-stop (* arg (current-indentation)))))
(defun hare-mode-indent-backward (&optional arg)
"Indent backwards to the nearest tabstop.
ARG tabstops from the `current-column'"
(interactive "p")
(or arg (setq arg 1))
(hare-mode--do-indent
(indent-next-tab-stop
(save-excursion (back-to-indentation) (current-column)) t)))
(defun hare-mode--backward-token ()
"Same as `smie-default-backward-token'.
Expand this further on to get better parsing. Put things in here
before changing the `hare-mode--smie-grammar'."
(forward-comment (- (point)))
(buffer-substring-no-properties
(point)
(progn
(if (zerop (skip-syntax-backward "."))
(skip-syntax-backward "w_'"))
(point))))
(defun hare-mode--forward-token ()
"Same as `smie-default-forward-token'.
Expand this further on to get better parsing. Put things in here
before changing the `hare-mode--smie-grammar'."
(forward-comment (point-max))
(buffer-substring-no-properties
(point)
(progn
(if (zerop (skip-syntax-forward "."))
(skip-syntax-forward "w_'"))
(point))))
(defconst hare-mode--smie-grammar
(smie-prec2->grammar
(smie-merge-prec2s
(smie-bnf->prec2
'((id)
(branches (branches "|" branches))
(toplevel (toplevel "," toplevel)
(toplevel ";" toplevel)))
'((assoc "|"))
'((assoc ";") (assoc ",")))
(smie-precs->prec2
'((assoc "+" "-" "^")
(assoc "/" "*" "%")
(nonassoc "==" "!="))))))
(defun hare-mode-smie-rules (kind token)
"Rules for the smie grammar.
Argument KIND is one of \":elem\" \":before\" \":after\"
\":list-intro\", and is used to designate how to indent.
Argument TOKEN is the token in question, either defined by SMIE
or a custom made one. See info manual for the most thorough
documentation of this feature."
(pcase (cons kind token)
(`(:elem . basic) hare-mode-indent-offset)
(`(:after . ",") (smie-rule-separator kind))
(`(:before . ";") (smie-rule-parent))
(`(:after . ";") (smie-rule-separator kind))
(`(:before . ,(or "(" "[" "{")) (if (smie-rule-hanging-p) (smie-rule-parent)))
(`(:before . "=") (if (smie-rule-hanging-p) hare-mode-indent-offset))))
;;;###autoload
(define-derived-mode hare-mode prog-mode "Hare"
"Major mode for editing `hare' files."
:syntax-table hare-mode-syntax-table
(setq-local tab-width hare-mode-indent-offset)
(setq-local indent-tabs-mode t)
(when (boundp 'electric-indent-inhibit) (setq electric-indent-inhibit t))
(smie-setup hare-mode--smie-grammar #'hare-mode-smie-rules
:forward-token #'hare-mode--forward-token
:backward-token #'hare-mode--backward-token)
(setq-local beginning-of-defun-function #'hare-mode-beginning-of-defun)
(setq-local end-of-defun-function #'hare-mode-end-of-defun)
(setq-local font-lock-defaults hare-mode-font-lock-defaults)
(setq-local comment-start "//")
(setq-local comment-end "")
(setq imenu-generic-expression hare-mode-imenu-generic-expression)
(font-lock-ensure))
;;;###autoload
(add-to-list 'auto-mode-alist '("\\.ha\\'" . hare-mode))
(provide 'hare-mode)
;;; hare-mode.el ends here