tremor-script 0.9.4

Tremor Script Interpreter
;; a tremor major mode, tremor-mode

;;;###autoload
;;(require 'smie)

;; Font-locking definitions and helpers
(defconst tremor-mode-keywords
  '("match" "end" "let" "when" "case" "of" "merge" "patch" "erase" "emit" "drop" "once" "default"  "or" "and" "not" "for" "null" "present" "absent" "event"))

(defconst tremor-special-types
  '("false" "null" "true"))

(defvar tremor-mode-indent-offset 2
  "*Indentation offset for `tremor-mode'.")

(defvar tremor-indent-basic 2
  "*Indentation offset for `tremor-mode'.")


(defconst tremor-re-ident "[[:word:][:multibyte:]_][[:word:][:multibyte:]_[:digit:]]*")
(defconst tremor-re-lc-ident "$?[[:lower:][:multibyte:]_][[:word:][:multibyte:]_[:digit:]]*")

(defun tremor-re-word (inner) (concat "\\<" inner "\\>"))
(defun tremor-re-grab (inner) (concat "\\(" inner "\\)"))
(defun tremor-re-shy (inner) (concat "\\(?:" inner "\\)"))
(defconst tremor-re-generic
  (concat "<[[:space:]]*'" tremor-re-ident "[[:space:]]*>"))
(defun tremor-re-item-def (itype)
  (concat (tremor-re-word itype)
	        "[[:space:]]+" (tremor-re-grab tremor-re-ident)))


(setq tremor-mode-syntax-table
      (let ( (table (make-syntax-table)))
        ;; Operators
        (dolist (i '(?+ ?* ?/ ?% ?& ?| ?^ ?! ?< ?~ ?@ ?.))
          (modify-syntax-entry i "." table))
        ;; Strings
        (modify-syntax-entry ?\" "\"" table)
        (modify-syntax-entry ?` "\"" table)
        (modify-syntax-entry ?| "\"" table)
        (modify-syntax-entry ?\\ "\\" table)
        ;;
        ;; Comments
        (modify-syntax-entry ?# "<" table)
        (modify-syntax-entry ?\n ">" table)
   table))


(defun tremor-path-font-lock-matcher (re-ident)
  "Match occurrences of RE-IDENT followed by a double-colo-n.
Examples include to match names like \"foo::\" or \"Foo::\".
Does not match type annotations of the form \"foo::<\"."
  `(lambda (limit)
     (catch 'tremor-path-font-lock-matcher
       (while t
         (let* ((symbol-then-colons (rx-to-string '(seq (group (regexp ,re-ident)) "::")))
                (match (re-search-forward symbol-then-colons limit t)))
           (cond
            ;; If we didn't find a match, there are no more occurrences
            ;; of foo::, so return.
            ((null match) (throw 'tremor-path-font-lock-matcher nil))
            ;; If this isn't a type annotation foo::<, we've found a
            ;; match, so a return it!
            ((not (looking-at (rx (0+ space) "<")))
	           (throw 'tremore-path-font-lock-matcher match))))))))


(defvar tremor-mode-font-lock-keywords
  (append
   `(
     ;; Keywords proper
     (,(regexp-opt tremor-mode-keywords 'symbols) . font-lock-keyword-face)

     ;; Special types
     (,(regexp-opt tremor-special-types 'symbols) . font-lock-type-face)

     ;; Field names like `foo:`, highlight excluding the :
     (,(concat (tremor-re-grab tremor-re-ident) ":[^:]") 1 font-lock-variable-name-face)

     ;; Module names like `foo::`, highlight excluding the ::
     (,(tremor-path-font-lock-matcher tremor-re-lc-ident) 1 font-lock-constant-face)

     )
   ))

(defun tremor-mode-syntactic-face-function (state)
  "Return face which distinguishes doc and normal comments in the given syntax STATE."
  (if (nth 3 state) 'font-lock-string-face
    (save-excursion
      (goto-char (nth 8 state))
      (if (looking-at "#[^#!]")
          'font-lock-comment-face
        'font-lock-doc-face
        ))))

(defun tremor--syntax-propertize-raw-string (end)
  "A helper for tremor-syntax-propertize.
If point is already in a raw string, this will apply the
appropriate string syntax to the character up to the end of the
raw string, or to END, whichever comes first."
  (let ((str-start (nth 8 (syntax-ppss))))
    (when str-start
      (when (save-excursion
	            (goto-char str-start)
	            (looking-at "r\\(#*\\)\\(\"\\)"))
	      ;; In a raw string, so try to find the end.
	      (let ((hashes (match-string 1)))
	        ;; Match \ characters at the end of the string to suppress
	        ;; their normal character-quote syntax.
	        (when (re-search-forward (concat "\\(\\\\*\\)\\(\"" hashes "\\)") end t)
	          (put-text-property (match-beginning 1) (match-end 1)
			                         'syntax-table (string-to-syntax "_"))
	          (put-text-property (1- (match-end 2)) (match-end 2)
			                         'syntax-table (string-to-syntax "|"))
	          (goto-char (match-end 0))))))))

(defun tremor-ordinary-lt-gt-p ()
  "Test whether the `<' or `>' at point is an ordinary operator of some kind.
This returns t if the `<' or `>' is an ordinary operator (like
less-than) or part of one (like `->'); and nil if the character
should be considered a paired angle bracket."
  nil)


(eval-and-compile
  (defconst tremor--char-literal-rx
    (rx (seq
	       (group "'")
	       (or
	        (seq
	         "\\"
	         (or
	          (: "u{" (** 1 6 xdigit) "}")
	          (: "x" (= 2 xdigit))
	          (any "'nrt0\"\\")))
	        (not (any "'\\"))
	        )
	       (group "'")))
    "A regular expression matching a character literal."))

(defun tremor-syntax-propertize (start end)
  "A `syntax-propertize-function' to apply properties from START to END."
  (goto-char start)
  (tremor--syntax-propertize-raw-string end)
  (funcall
   (syntax-propertize-rules
    ("[<>]"
     (0 (ignore
	       (when (save-match-data
		             (save-excursion
		               (goto-char (match-beginning 0))
		               (tremor-ordinary-lt-gt-p)))
	         (put-text-property (match-beginning 0) (match-end 0)
			                        'syntax-table (string-to-syntax "."))
	         (goto-char (match-end 0)))))))
   (point) end))



(defvar tremor-keywords-regexp
  (regexp-opt '("+" "*" "," ";" ">" ">=" "<" "<=" ":=" "=" "++")))

;; (defvar tremor-smie-grammar
;;   (smie-prec2->grammar
;;    (smie-bnf->prec2
;;     '((id)
;;       (inst
;;        ("let" id ":=" exp)
;;        ("erase" id)
;;        ("match" exp "of" cases "end")
;;        ("{" kvs "}")
;;        )
;;       (insts (inst ";" inst))

;;       ;;(kvs (id ":" exp) (kvs "," kvs))
;;       (cases (cases ";" cases)
;;              ("case" caselabel
;;               "=>" exp)
;;              ("case" caselabel "when" exp
;;               "=>" exp))
;;       (exp (inst))
;;       (exp (exp "+" exp)
;;            (exp "-" exp)
;;            (exp "*" exp)
;;            (exp "/" exp)
;;            (id)
;;            (inst)
;;            ("(" exps ")")))
;;     '((assoc ":"))
;;     '((assoc ","))
;;     '((assoc ";"))4
;;     '((assoc "_" "->"))
;;     '((assoc "+") (assoc "-") (assoc "*") (assoc "/")))))


;; (defvar tremor-smie-grammar
;;   (smie-prec2->grammar
;;    (smie-bnf->prec2
;;     '((id)
;;       (stmt
;;        ("let" id ":=" expr)
;;        ("erase" id)
;;        ("extend" expr "match" expr "->" cases "end")
;;        (expr))
;;       (stmts (stmt ";" stmt))
;;       (expr
;;        ("match" expr "->" cases "end")
;;        ("[" items "]")
;;        ("{" oitems "}")
;;        (id))
;;       (aitems (aitems "," aitems), expr)
;;       (oitems (oitems "," oitems), (expr ":" expr))
;;       ;;("case" caselabel "when" exp "=>" stmts)
;;       (cases (cases ";" cases)
;;              ("case" caselabel "=>" stmts)))
;;     '((assoc "extend" "match"))
;;     '((assoc  "=>"))
;;     '((assoc ";"))
;;     '((assoc ","))
;;     '((assoc ":"))
;;     ;;'((assoc ":"))
;;     ;;'((assoc "_" "->"))
;;     '((assoc "+") (assoc "-") (assoc "*") (assoc "/")))))



;; (defvar tremor-smie-grammar
;;   (smie-prec2->grammar
;;    (smie-bnf->prec2
;;     '((id)
;;       (stmt
;;        ("let" id "=" expr)
;;        (expr))
;;       (stmts (stmt ";" stmt))
;;       (case-stmts (stmt "," stmt))
;;       (expr
;;        ("match" expr "of" cases "default" effectors "end")
;;        ("patch" expr "of" patches "end")
;;        ("for" expr "of" cases "end")
;;        ("merge" expr "of" expr "end")
;;        ("[" items "]")
;;        ("{" oitems "}")
;;        (id))
;;       (aitems (aitems "," aitems), expr)
;;       (oitems (oitems "," oitems), (expr ":" expr))
;;       (cases (cases " " cases)
;;              ("case" caselabel effectors))
;;       (effectors ("=>" case-stmts)))
;;     '((assoc "=>"))
;;     '((assoc ";"))
;;     '((assoc ","))
;;     '((assoc ":"))
;;     '((assoc "+") (assoc "-") (assoc "*") (assoc "/")))))

;; (defun tremor-smie-rules (kind token)
;;   (pcase (cons kind token)
;;     (`(:elem . basic) tremor-indent-basic)
;;     (`(,_ . ",") (smie-rule-separator kind))
;;     ;; (`(,_ . ";") (smie-rule-separator kind))
;;     (`(,_ . ":") (smie-rule-separator kind))
;;     ;; (`(,_ . "when") (smie-rule-separator kind))
;;     ;; (`(:after . ":") tremor-indent-basic)
;;     (`(:before . "=") tremor-indent-basic)
;;     ;;(`(:before . "case") tremor-indent-basic)
;;     ;;(`(:before . "default") tremor-indent-basic)
;;     ;; (`(:before . "=>") tremor-indent-basic)
;;     ;; (`(:before . "when") tremor-indent-basic)
;;     ;; (`(:list-intro . "") tremor-indent-basic)
;;     (`(:before . ,(or `"[" `"(" `"{"))
;;      (if (smie-rule-hanging-p) (smie-rule-parent)))
;;     ;; (`(:before . "extend") (smie-rule-parent))
;;     ;; (`(:before . "match") (smie-rule-parent))
;;     ;; (`(:before . "->") tremor-indent-basic)
;;     (`(:after . "of") tremor-indent-basic)
;;     ;; (`(:before . "with") tremor-indent-basic)
;;     ;; (`(:before . "end") (smie-rule-parent))
;;     ))

;;;###autoload
(define-derived-mode tremor-mode prog-mode "Tremor"
  "Major mode for Tremor code.
\\{tremor-mode-map}"
  :group 'tremor-mode
  :syntax-table tremor-mode-syntax-table

  ;; Syntax.
  (setq-local syntax-propertize-function #'tremor-syntax-propertize)

  ;; Indentation
  ;;(setq-local indent-line-function 'tremor-mode-indent-line)
  ;;(smie-setup nil #'tremor-smie-rules)
  ;;(setq-local smie-indent-basic tremor-indent-basic)
  ;;(setq-local smie-grammar tremor-smie-grammar)

  ;; Fonts
  (setq-local font-lock-defaults '(tremor-mode-font-lock-keywords
                                   nil nil nil nil
                                   (font-lock-syntactic-face-function . tremor-mode-syntactic-face-function)))

  (setq-local comment-use-syntax t)
  (setq-local comment-start "#")
  (setq-local comment-end "")
  (setq-local indent-tabs-mode nil)

  ;; ;; Misc
  ;; (setq-local comment-start "// ")
  ;; (setq-local comment-end   "")
  ;; (setq-local indent-tabs-mode nil)
  ;; (setq-local open-paren-in-column-0-is-defun-start nil)

  ;; ;; Auto indent on }
  ;; (setq-local
  ;;  electric-indent-chars (cons ?} (and (boundp 'electric-indent-chars)
  ;;                                      electric-indent-chars)))

  ;; ;; Allow paragraph fills for comments
  ;; ;; (setq-local comment-start-skip "\\(?://[/!]*\\|/\\*[*!]?\\)[[:space:]]*")
  ;; (setq-local paragraph-start
  ;;      (concat "[[:space:]]*\\(?:" comment-start-skip "\\|\\*/?[[:space:]]*\\|\\)$"))
  ;; (setq-local paragraph-separate paragraph-start)
  ;; (setq-local normal-auto-fill-function 'tremor-do-auto-fill)
  ;; (setq-local fill-paragraph-function 'tremor-fill-paragraph)
  ;; (setq-local fill-forward-paragraph-function 'tremor-fill-forward-paragraph)
  ;; (setq-local adaptive-fill-function 'tremor-find-fill-prefix)
  ;; (setq-local adaptive-fill-first-line-regexp "")
  ;; (setq-local comment-multi-line t)
  ;; (setq-local comment-line-break-function 'tremor-comment-indent-new-line)
  ;; (setq-local imenu-generic-expr ession tremor-imenu-generic-expression)
  ;; (setq-local imenu-syntax-alist '((?! . "w"))) ; For macro_rules!
  ;; (setq-local beginning-of-defun-function 'tremor-beginning-of-defun)
  ;; (setq-local end-of-defun-function 'tremor-end-of-defun)
  ;; (setq-local parse-sexp-lookup-properties t)
  ;; (setq-local electric-pair-inhibit-predicate 'tremor-electric-pair-inhibit-predicate-wrap)

  ;; (add-hook 'before-save-hook 'tremor-before-save-hook nil t)
  ;; (add-hook 'after-save-hook 'tremor-after-save-hook nil t)

  ;; (setq-local tremor-buffer-project nil)

  ;; (when tremor-always-locate-project-on-open
  ;;   (tremor-update-buffer-project)))
)

(add-to-list 'auto-mode-alist '("\\.tremor\\'" . tremor-mode))

(provide 'tremor-mode)