summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2020-11-03 16:09:38 -0800
committerH. Peter Anvin <hpa@zytor.com>2020-11-03 16:09:38 -0800
commite10d2f49cfacadd2355b2b8d5ed319ce57103a35 (patch)
tree9df8594fe578bd6fe86a66c55b44ddd48bc986a0
parentd15b0ff6251bfa5ef60a994e4676e21363ccdc2e (diff)
downloadabc80-1m-sram-baspp.el.tar.gz
abc80-1m-sram-baspp.el.tar.xz
abc80-1m-sram-baspp.el.zip
baspp.el: auto-indentation supportbaspp.el
Automatically indent lines based on block constructs. NOTE: FOR and NEXT on the same line are not yet handled properly.
-rw-r--r--sw/baspp.el133
1 files changed, 108 insertions, 25 deletions
diff --git a/sw/baspp.el b/sw/baspp.el
index 4238517..b514149 100644
--- a/sw/baspp.el
+++ b/sw/baspp.el
@@ -112,37 +112,41 @@
("<[^\\>]*>" nil nil (0 'font-lock-string-face)))
;; Line numbers and labels
- ("^\\s-*\\([0-9]+\\s-*:?\\)\\_>" 1 'baspp-line-number-face)
+ ("^\\s-*\\([[:digit:]]+\\s-*:?\\)\\_>" 1 'baspp-line-number-face)
- ("^\\s-*\\([[:alpha:]_][[:alnum:]_]*\\)\\(\\s-*:\\)"
+ ("^\\s-*\\([[:alpha:]_]\\w*\\)\\(\\s-*:\\)"
(1 'baspp-label-face)
(2 'baspp-label-colon-face))
;; For THEN and ELSE we need to watch out for implicit LET
- ("\\_<\\(then\\|else\\)\\_>\\s-+\\(fn\\)?\\([[:alpha:]_][[:alnum:]_]*\\)\\s-*="
+ ("\\_<\\(then\\|else\\)\\_>\\s-+\\(fn\\)?\\([[:alpha:]_]\\w*\\)\\s-*="
(1 'font-lock-keyword-face)
(2 'font-lock-warning-face keep t)
(3 'font-lock-variable-name-face))
;; Line numbers/labels after GOTO-type statements
- ("\\_<\\(go\\(?:to\\|sub\\)\\|on\\s-*error\\s-*goto\\|resume\\|then\\|else\\)\\_>\\s-+\\(?:\\([0-9]+\\)\\|\\([[:alpha:]_][[:alnum:]_]*\\)\\)\\_>"
+ ("\\_<\\(go\\(?:to\\|sub\\)\\|on\\s-*error\\s-*goto\\|resume\\|then\\|else\\)\\_>\\s-+\\(?:\\([[:digit:]]+\\)\\|\\([[:alpha:]_]\\w*\\)\\)\\_>"
(1 'font-lock-keyword-face)
(2 'baspp-line-number-face keep t)
(3 'baspp-label-face keep t)
- ("\\=\\s-*,\\s-*\\(?:\\([0-9]+\\)\\|\\([[:alpha:]_][[:alnum:]_]*\\)\\)\\_>"
+ ("\\=\\s-*,\\s-*\\(?:\\([[:digit:]]+\\)\\|\\([[:alpha:]_]\\w*\\)\\)\\_>"
nil nil
(1 'baspp-line-number-face keep t)
(2 'baspp-label-face keep t)))
;; Numeric constants
- ("\\_<\\(0[byoqdthx][0-9a-f_]+\\|[0-9][0-9_]*\\)[%.]?\\_>"
+ ("\\_<\\(0[byoqdthx][[:xdigit:]_]+\\|[[:digit:]][[:digit:]_]*\\)[%.]?\\_>"
. 'baspp-numeric-face)
- ("\\_<[0-9][0-9._]*\\(e[+-]?[0-9]+\\)?\\.?\\_>"
- . 'baspp-numeric-face)
+ ("\\_<[[:digit:]][[:digit:]._]*\\(e[+-]?[[:digit:]]+\\)?\\.?\\_>"
+ . 'baspp-numeric-face)
+
+ ;; Invalid numeric constants (valid forms are as above)
+ ("\\_<[[:digit:]][[:word:]%$¤.]+" 0 'font-lock-warning-face)
;; Functions and variables
- ("\\_<fn[[:alpha:]_][[:alnum:]_]*[%.$¤]?\\_>" . 'font-lock-function-name-face)
- ("\\_<[[:alpha:]_][[:alnum:]_]*[%.$¤]?\\_>" . 'font-lock-variable-name-face)
+ ("\\_<\\(fn\\)\\([[:digit:]]\\|\\W\\)" 1 'font-lock-warning-face)
+ ("\\_<fn\\w+\\s_?\\_>" . 'font-lock-function-name-face)
+ ("\\_<[[:alpha:]_]\\w*\\s_?\\_>" . 'font-lock-variable-name-face)
;; Operators
(,(regexp-opt
@@ -189,14 +193,9 @@
(modify-syntax-entry ?\) ")(" table)
table))
-;; The actual syntax table is constructed from font-lock-defaults for
-;; some reason... this just contains the pairs.
-(setq baspp-mode-font-lock-syntax-table
- '((?\" . "\"")
- (?\' . "\"")
- (?\! . "<")
- (?\n . ">")))
-
+;;
+;; Keyboard mappings
+;;
(defun baspp-insert-block ()
"Insert a ■ character, the Unicode version of an ABC delete"
(interactive)
@@ -205,7 +204,6 @@
(setq baspp-mode-map
(let ((map (make-sparse-keymap)))
(set-keymap-parent map prog-mode-map)
- (define-key map "\t" 'tab-to-tab-stop)
(define-key map "\d" 'backward-delete-char-untabify)
(define-key map (kbd "C-<") 'baspp-insert-block)
(define-key map (kbd "C->") 'baspp-insert-block)
@@ -218,18 +216,103 @@
(define-abbrev table "endif" "END IF" nil :system t)
table))
-(defvar baspp-basic-indent 4
+;;
+;; Indentation
+;;
+(defvar baspp-basic-offset 4
"Indentation level in baspp mode")
+(defun baspp-mode-looking-at-label-p ()
+ "Does this line have a label or line number?"
+ (looking-at "\\s-*\\([[:digit:]]+\\(\\s-\\|:\\)\\|\\w+\\s-*:\\)"))
+
+;;
+;; Rules for indentation:
+;;
+;; 1. Labels and line numbers temporary decrease indentation level by 1
+;; 2. For the current line: each WEND, NEXT, ELSE IF, END IF,
+;; or lone ELSE reduce indentation by 1
+;; 3. For the *previous* line: each WHILE, FOR, terminal THEN, or lone ELSE
+;; increase indentation by 1.
+;; 4. If a ! comment starts in column 0, don't re-indent.
+;;
+(defun baspp-mode-indent-line ()
+ "Indent the current like as Baspp code"
+ (interactive)
+ (beginning-of-line)
+
+ ;; Rule 4: do nothing
+ (unless (looking-at "!")
+ (let ((starting-point (point))
+ (indent-base (+ (prog-first-column) baspp-basic-offset))
+ (delta-indent 0)
+ (scanning t)
+ (no-indent-base t)
+ lep
+ scanning)
+
+ ;; Calculate any indent adjustment
+ (setq lep (line-end-position))
+ (setq scanning t)
+
+ ;; Rule 1: adjust indent due to labels
+ (when (baspp-mode-looking-at-label-p)
+ (setq delta-indent -1)
+ (goto-char (match-end 0)))
+
+ (when (> (line-beginning-position) (point-min))
+
+ ;; Rule 2: decrease indentation based on the current line contents
+ (while (and scanning (< (point) lep)
+ (re-search-forward "\\_<\\(wend\\|next\\|else\\(?:\\s-*if\\)?\\|end\\s-*if\\|rem\\|data\\)\\_>" lep t))
+ (let ((keywd (match-string 1)))
+ (unless (syntax-ppss-context (syntax-ppss))
+ (if (string-match-p "^\\(rem\\|data\\)$" keywd)
+ (setq scanning nil)
+ (when (or (not (string-match-p "^else$" keywd))
+ (looking-at "\\s-*\\(!\\|$\\)"))
+ (setq delta-indent (1- delta-indent)))))))
+
+ ;; Rule 3: look back for suitable indentation base and forward
+ (while no-indent-base
+ (beginning-of-line 0)
+ (if (looking-at "\\s-*\\(!\\|$\\)")
+ (setq no-indent-base (/= (point) (point-min)))
+
+ (setq lep (line-end-position))
+ (setq no-indent-base nil)
+ (setq indent-base (current-indentation))
+ (setq scanning t)
+
+ (when (baspp-mode-looking-at-label-p)
+ (setq delta-indent (1+ delta-indent))
+ (goto-char (match-end 0)))
+
+ (while (and scanning (< (point) lep)
+ (re-search-forward "\\_<\\(while\\|for\\|then\\|else\\|rem\\|data\\)\\_>" lep t))
+ (let ((keywd (match-string 1)))
+ (unless (syntax-ppss-context (syntax-ppss))
+ (if (string-match-p "^\\(rem\\|data\\)$" keywd)
+ (setq scanning nil)
+ (when (or (not (string-match-p "^\\(then\\|else\\)$" keywd))
+ (looking-at "\\s-*\\(!\\|$\\)"))
+ (setq delta-indent (1+ delta-indent))))))))))
+
+ (goto-char starting-point)
+ (delete-horizontal-space)
+ (indent-to (max 0 (+ indent-base (* baspp-basic-offset delta-indent)))))))
+
+;;
+;; Final mode definition
+;;
(define-derived-mode baspp-mode prog-mode "Baspp"
"Major mode for preprocessed ABC80 BASIC"
(setq-local case-fold-search t)
(setq-local imenu-case-fold-search t)
(setq-local indent-tabs-mode nil)
- (setq-local tabs-stop-list (list baspp-basic-indent))
- (setq-local auto-fill-mode nil)
- (setq-local comment-start "!!")
+ (setq-local indent-line-function 'baspp-mode-indent-line)
+ (auto-fill-mode 0) ;; Makes no sense, except perhaps in comments
+ (setq-local comment-start "!")
(setq-local font-lock-defaults
`(,baspp-mode-font-lock-keywords
- nil t nil))
- )
+ nil t nil)))