emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] master 031b4de: Add gle-mode package


From: Stefan Monnier
Subject: [elpa] master 031b4de: Add gle-mode package
Date: Sun, 26 Nov 2017 22:57:16 -0500 (EST)

branch: master
commit 031b4de9142990dcefd20084f0fc18c9fa2f5f52
Author: Stefan Monnier <address@hidden>
Commit: Stefan Monnier <address@hidden>

    Add gle-mode package
---
 packages/gle-mode/gle-mode.el | 320 ++++++++++++++++++++++++++++++++++++++++++
 packages/gle-mode/samples.gle |  42 ++++++
 2 files changed, 362 insertions(+)

diff --git a/packages/gle-mode/gle-mode.el b/packages/gle-mode/gle-mode.el
new file mode 100644
index 0000000..2462157
--- /dev/null
+++ b/packages/gle-mode/gle-mode.el
@@ -0,0 +1,320 @@
+;;; gle-mode.el --- Major mode to edit Graphics Layout Engine files  -*- 
lexical-binding: t; -*-
+
+;; Copyright (C) 2017  Free Software Foundation, Inc
+
+;; Author: Stefan Monnier <address@hidden>
+;; Package-Requires: ((cl-lib "0.5"))
+;; Version: 1.0
+
+;; This program 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.
+
+;; This program 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 this program.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This is a major mode for files using the GLE (Graphics Layout Engine)
+;; language.  See http://glx.sourceforge.net/
+;; [ Not sure why the site uses "glx", while everything else seems to use
+;;   "gle" instead.  ]
+
+;; It provides:
+;; - Rudimentary code highlighting.
+;; - Automatic indentation.
+;; - Flymake support (requires Emacs-26's fymake).
+;; - Imenu support
+
+;;;; TODO
+;; - Fix highlighting of function calls?
+;; - provide a completion-at-point-function
+;; - auto-complete the `end`s and `next`s
+
+;;; Code:
+
+(require 'smie)
+(require 'cl-lib)
+
+(defgroup 'gle-mode ()
+  "Major mode for GLE (Graphics Layout Engine) files."
+  :group 'tools)
+
+;;;; Syntax table
+
+(defvar gle-mode-syntax-table
+  (let ((st (make-syntax-table)))
+    (modify-syntax-entry ?! "<" st)
+    (modify-syntax-entry ?\n ">" st)
+    (modify-syntax-entry ?\" "\"" st)
+    (modify-syntax-entry ?\' "\"" st)
+    ;; Backslash isn't used to escape a double quote in a string.
+    (modify-syntax-entry ?\\ "." st)
+    st))
+
+(defconst gle-syntax-propertize
+  (syntax-propertize-rules
+   ;; The doc says that doubled quotes are used to escape quotes in a string,
+   ;; tho running gle-4.2.5 on those strings gives me errors :-(
+   ("\"\"\\|''"
+    (0 (if (save-excursion (nth 3 (syntax-ppss (match-beginning 0))))
+           (string-to-syntax ".")
+         ;; If match-beg is not within a string, maybe it starts a string,
+         ;; and maybe the second " doesn't end the string!
+         (goto-char (1+ (match-beginning 0)))
+         nil)))))
+
+;;;; SMIE support
+
+(defvar gle-smie-grammar
+  (smie-prec2->grammar
+   (smie-merge-prec2s
+    (smie-bnf->prec2
+     '((var)
+       (exp)
+       (inst-else-inst (inst) (inst "else bloc" inst))
+       (for-body (exp ";" inst))
+       (var=exp (var "=" exp))
+       (for-head (var=exp "to" exp-step))
+       (exp-step (exp) (exp "step" exp))
+       (until-body (exp ";" inst))
+       (inst (inst ";" inst)
+             ("begin" inst "end <thing>")
+             ;; You can have "single-line" ifs (with inst right after "then"),
+             ;; which can be extended with single line "else if"s.
+             ;; Or you can have "if ... end if" blocs.
+             ("if bloc" inst-else-inst "end if")
+             ("sub" inst "end sub")
+             ("for" for-body "next <var>")
+             ("until" until-body "next")
+             ("while" until-body "next")
+             ("gsave" inst "grestore")))
+     '((assoc ";"))))))
+
+(defun gle-smie--disambiguate-if ()
+  "Expects point to be right after `if`."
+  (save-excursion
+    (let ((eol (line-end-position))
+          (lasttok nil)
+          (tok nil))
+      (while (<= (point) eol)
+        (setq lasttok tok)
+        (forward-comment (point-max))
+        (setq tok (buffer-substring (point)
+                                    (progn
+                                      (or (> (skip-syntax-forward "w_") 0)
+                                          (> (skip-syntax-forward ".") 0)
+                                          (forward-char 1))
+                                      (point)))))
+      (if (equal lasttok "then")
+          "if bloc"
+        "if line"))))
+
+(defun gle-smie-forward-token ()
+  (let ((start (point)))
+    (forward-comment (point-max))
+    (if (and (not (eq ?\n (char-before start)))
+             (< start (line-beginning-position)))
+        (progn (goto-char start)
+               (forward-line 1)
+               ";")
+      (let ((bolp (save-excursion
+                    (skip-chars-backward " \t")
+                    (memq (char-before) '(nil ?\n ?\;)))))
+        (if (not bolp)
+            (cond
+             ((not (zerop (skip-chars-forward "^ \t\n;!"))) "<exp>")
+             ((eobp) "")
+             (t (cl-assert (looking-at ";"))
+                (forward-char 1)
+                ";"))
+          (let ((tok (buffer-substring (point)
+                                       (progn (skip-chars-forward "^ \t\n;!")
+                                              (point)))))
+            (cond
+             ((looking-at "[ \t]*=") "<var>")
+             ((equal tok "end")
+              (cond
+               ((looking-at "[ \t]+sub") (goto-char (match-end 0)) "end sub")
+               ((looking-at "[ \t]+if") (goto-char (match-end 0)) "end if")
+               ((looking-at "[ \t]+\\w+")
+                (goto-char (match-end 0)) "end <thing>")
+               (t tok)))
+             ((equal tok "next")
+              (cond
+               ((looking-at "[ \t]+\\w+")
+                (goto-char (match-end 0)) "next <var>")
+               (t tok)))
+             ((equal tok "if") (gle-smie--disambiguate-if))
+             ((equal tok "else")
+              (if (looking-at "[ \t]+if") "else line" "else bloc"))
+             (t tok))))))))
+              
+(defun gle-smie-backward-token ()
+  (let ((start (point)))
+    (forward-comment (- (point)))
+    (if (> start (line-end-position))
+        ";"
+      (let* ((end (point))
+             (assign (looking-at "[ \t]*="))
+             (tok (buffer-substring (progn (skip-chars-backward "^ \t\n;")
+                                           (point))
+                                    end))
+             (bolp (save-excursion
+                     (skip-chars-backward " \t")
+                     (memq (char-before) '(nil ?\n ?\;)))))
+        (cond
+         ((and bolp (equal tok ""))
+          (if (bobp) tok
+            (cl-assert (eq (char-before) ?\;))
+            (forward-char -1)
+            ";"))
+         (assign "<var>")
+         (bolp
+          (cond
+           ((equal tok "if")
+            (save-excursion (forward-char 2) (gle-smie--disambiguate-if)))
+           ((equal tok "else")
+            (if (looking-at "else[ \t]+if") "else line" "else bloc"))
+           (t tok)))
+         ((save-excursion
+            (skip-chars-backward " \t")
+            (and (memq (char-before) '(?d ?t))
+                 (looking-back "^[ \t]*\\(end\\|nex\\(t\\)\\)"
+                               (line-beginning-position))))
+          (goto-char (match-beginning 1))
+          (cond
+           ((match-beginning 2) "next <var>")
+           ((equal tok "sub") "end sub")
+           ((equal tok "if") "end if")
+           (t "end <thing>")))
+         (t "<exp>"))))))
+
+(defun gle-smie-rules (kind token)
+  (pcase (cons kind token)
+    (`(:after . ";")
+     (cond
+      ((smie-rule-parent-p "for" "while" "sub" "begin" "gsave" "if bloc")
+       (smie-rule-parent smie-indent-basic))))
+    (`(:before . "else bloc") (smie-rule-parent 0))))
+
+;;;; Font-lock
+
+(defvar gle-font-lock-keywords
+  '(("^[ \t]*\\(\\(?:\\sw\\|\\s_\\)+\\)[ \t]*="
+     (1 font-lock-variable-name-face))
+    ("^[ \t]*if[ \t][^!\n;]*[ \t]\\(then\\)\\_>"
+     (1 font-lock-keyword-face))
+    ("^[ \t]*for[ \t][^!\n;]*[ \t]\\(to\\)\\_>\\(?:[^!\n;]*[ 
\t]\\(step\\)\\_>\\)?"
+     (1 font-lock-keyword-face) (2 font-lock-keyword-face nil t))
+    ("^[ \t]*else[ \t]+\\(if\\)[ \t][^!\n;]*[ \t]\\(then\\)\\_>"
+     (1 font-lock-keyword-face) (2 font-lock-keyword-face))
+    ("^[ \t]*end[ \t]+\\(\\(?:\\sw\\|\\s_\\)+\\)"
+     (1 font-lock-keyword-face))
+    ("^[ \t]*sub[ \t]*\\(\\(?:\\sw\\|\\s_\\)+\\)"
+     (1 font-lock-function-name-face))
+    ;; FIXME: Actually, this can also be a function call!
+    ("^[ \t]*\\(\\(?:\\sw\\|\\s_\\)+\\)" (1 font-lock-keyword-face))))
+
+;;;; Flymake
+
+(defcustom gle-program-name "gle"
+  "Name of the `gle' program."
+  :type 'string)
+
+(defvar-local gle--flymake-proc nil)
+
+(defun gle--flymake (report-fn &rest _args)
+  "GLE backend for Flymake.
+See `flymake-diagnostic-functions' for documentation of REPORT-FN."
+  ;; Code largely inspired from `ruby-flymake'.
+  (unless (executable-find gle-program-name)
+    (error "Cannot find `gle' executable"))
+
+  (when (process-live-p gle--flymake-proc)
+    (delete-process gle--flymake-proc))
+
+  (let ((source (current-buffer)))
+    (save-restriction
+      (widen)
+      (setq
+       gle--flymake-proc
+       (make-process
+        :name "gle-flymake" :noquery t :connection-type 'pipe
+        :buffer (generate-new-buffer " *gle-flymake*")
+        :command (list gle-program-name "-nosave" "-")
+        :sentinel
+        (lambda (proc _event)
+          (when (eq 'exit (process-status proc))
+            (let ((diagnostics '()))
+              (unwind-protect
+                  (if (with-current-buffer source
+                        (not (eq proc gle--flymake-proc)))
+                      (message "Skipping obsolete check for %s" proc)
+                    (with-current-buffer (process-buffer proc)
+                      (goto-char (point-min))
+                      (while (search-forward-regexp
+                              "^>> .*? (\\([0-9]+\\)) |\\(.*\\)|\n>>.*\n>> 
*\\(\\w+\\): *\\(.*\\)"
+                              nil t)
+                        (let ((line (string-to-number (match-string 1)))
+                              (txt (match-string 2))
+                              (kind (intern
+                                     (format ":%s"
+                                             (downcase (match-string 3)))))
+                              (msg (match-string 4)))
+                          (with-current-buffer source
+                            (save-excursion
+                              (goto-char (point-min))
+                              (forward-line (1- line))
+                              (push
+                               (if (search-forward txt (line-end-position) t)
+                                   (flymake-make-diagnostic source
+                                                            (match-beginning 0)
+                                                            (match-end 0)
+                                                            kind
+                                                            msg)
+                                 (skip-chars-forward " \t")
+                                 (flymake-make-diagnostic source
+                                                          (point)
+                                                          (line-end-position)
+                                                          kind
+                                                          msg))
+                               diagnostics)))))))
+                (kill-buffer (process-buffer proc)))
+              (funcall report-fn diagnostics))))))
+      (process-send-region gle--flymake-proc (point-min) (point-max))
+      (process-send-eof gle--flymake-proc))))
+
+;;;; Imenu
+
+(defvar gle-imenu-generic-expression
+  '(("Funs" "^[ \t]*sub[ \t]+\\(\\(?:\\s_\\|\\sw\\)+\\)" 1)
+    ("Vars" "^[ \t]*\\(\\(?:\\s_\\|\\sw\\)+\\)[ \t]*=" 1)))
+
+;;;; Top-level
+
+;;;###autoload
+(add-to-list 'auto-mode-alist '("\\.gle\\'" . gle-mode))
+
+;;;###autoload
+(define-derived-mode gle-mode prog-mode "GLE"
+  "Major mode to edit Graphics Layout Engine files."
+  (setq-local comment-start "!")
+  (setq-local syntax-propertize-function gle-syntax-propertize)
+  (smie-setup gle-smie-grammar #'gle-smie-rules
+              :forward-token #'gle-smie-forward-token
+              :backward-token #'gle-smie-backward-token)
+  (setq-local font-lock-defaults
+              '(gle-font-lock-keywords))
+  (setq-local imenu-generic-expression gle-imenu-generic-expression)
+  (add-hook 'flymake-diagnostic-functions 'gle--flymake nil 'local)
+  )
+
+(provide 'gle-mode)
+;;; gle-mode.el ends here
diff --git a/packages/gle-mode/samples.gle b/packages/gle-mode/samples.gle
new file mode 100644
index 0000000..d0f22af
--- /dev/null
+++ b/packages/gle-mode/samples.gle
@@ -0,0 +1,42 @@
+
+print "Double quoted string"
+print 'Double quoted string'
+print "Between ""double quotes"""
+print 'Between ''single quotes'''
+print "Backslash at the end\"
+print """"
+
+gsave
+    ! Ha!
+    sub toto x                !Hi
+        adsf
+        adsf
+    end sub
+    
+    sub titi x                !Hi
+        adsf
+        adsf
+    end sub
+
+    total = 56
+    median = total/2
+    
+    for x = bla to bla step 5
+        mylength = sfg
+        asfdg
+    next x
+    
+    if x = 3 then
+        print "there"
+    else
+        print "hello"
+    end if
+    
+    if x = 3 then return
+    else if x = 4 then return
+    print "toto"
+    
+    while I'm here'
+        do this
+    next
+grestore



reply via email to

[Prev in Thread] Current Thread [Next in Thread]