commit 174f6e54521a65a23b336a4260faaf4e166d3cf8 Author: ~keith Date: Wed May 29 10:28:38 2024 -0400 Initial commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..da45dc0 --- /dev/null +++ b/README.md @@ -0,0 +1,8 @@ +# syrup: assorted syntactic sugar for Lisp + +Syrup is a simple package providing syntax enhancements to Common Lisp. + +Right now it doesn't do much, but I'll add new features as I think of them. + +- `#^sym` to export a symbol inline +- `[]` and `{}` can be used in place of normal parentheses diff --git a/superfluous-parentheses.el b/superfluous-parentheses.el new file mode 100644 index 0000000..32b353e --- /dev/null +++ b/superfluous-parentheses.el @@ -0,0 +1,25 @@ +;;; superfluous-parentheses --- Emacs support for [] and {} in lisp-mode + +;;; Code: +(defun superfluous-modify-font-lock-keywords (rule-table) + (dolist (rule rule-table) + (when (and (consp rule) (stringp (car rule))) + (when (string-prefix-p "(" (car rule)) + (rplaca rule (concat "\\s(" (substring (car rule) 1))))))) + +(add-hook! lisp-mode + (modify-syntax-entry ?\[ "(]" lisp-mode-syntax-table) + (modify-syntax-entry ?\] ")[" lisp-mode-syntax-table) + (modify-syntax-entry ?\{ "(}" lisp-mode-syntax-table) + (modify-syntax-entry ?\} "){" lisp-mode-syntax-table) + (superfluous-modify-font-lock-keywords lisp-cl-font-lock-keywords-1) + (superfluous-modify-font-lock-keywords lisp-cl-font-lock-keywords-2) + (superfluous-modify-font-lock-keywords lisp-cl-font-lock-keywords)) + +;;; Commentary: +;; This package modifies the lisp-mode syntax table and font-lock keywords to +;; provide proper support for using square brackets and curly braces in Common +;; Lisp code, as per superfluous-parentheses. + +(provide 'superfluous-parentheses) +;;; superfluous-parentheses.el ends here diff --git a/syrup.asd b/syrup.asd new file mode 100644 index 0000000..5f512f5 --- /dev/null +++ b/syrup.asd @@ -0,0 +1,7 @@ +(asdf:defsystem "syrup" + :description "Assorted syntactic sugar for Lisp." + :version "1.0" + :author "~keith " + :homepage "https://bytes.keithhacks.cyou/keith/syrup" + :license "Public Domain/CC0" + :components ((:file "syrup"))) diff --git a/syrup.lisp b/syrup.lisp new file mode 100644 index 0000000..fafe26d --- /dev/null +++ b/syrup.lisp @@ -0,0 +1,61 @@ +(defpackage #:syrup + (:use #:cl)) +(in-package #:syrup) + +(define-condition simple-reader-error (simple-condition reader-error) ()) + +;; #^ - Inline symbol export macro +;; Use #^SYMBOL or #^(SYMBOL) to export a symbol at the location it's defined. +(defun read-exported-symbol (stream char arg) + "Read a symbol (optionally wrapped in parens) and export it" + (declare (ignore char arg)) + (let ((sym (read stream t nil t))) + (when (and (consp sym) (not (cdr sym))) + (setf sym (car sym))) ;; allow wrapping symbol in parens + (unless (symbolp sym) + (error 'simple-reader-error + :stream stream + :format-control "Not a symbol: ~S" + :format-arguments (list sym))) + (export sym) + sym)) +(set-dispatch-macro-character #\# #\^ #'read-exported-symbol) + +;; Use [] and {} like normal parentheses +;; NOTE: You'll have to add superfluous-parentheses.el to Emacs to get it to recognize this syntax +(defun read-bracketed-list (stream begin-char) + (let* ((end-char (cond ((char= begin-char #\[) #\]) + ((char= begin-char #\{) #\}) + (t (error 'simple-reader-error + :stream stream + :format-control "Invalid bracket ~S" + :format-arguments (list begin-char))))) + (list-body (loop for next-char = (peek-char t stream t nil t) + until (or (char= next-char end-char) (char= next-char #\.)) + collect (read stream t nil t))) + (last-char (read-char stream t nil t))) ;; discard the last peeked character + (cond ((char= last-char end-char) list-body) + ((char= last-char #\.) + (unless list-body + (error 'simple-reader-error + :stream stream + :format-control "Nothing before . in list." + :format-arguments nil)) + ;; set the CDR of the list + (rplacd (last list-body) (read stream t nil t)) + ;; skip past whitespace + (peek-char t stream t nil t) + (unless (char= (read-char stream t nil t) end-char) + (error 'simple-reader-error + :stream stream + :format-control "Multiple objects after . in list." + :format-arguments nil)) + list-body) + (t (error 'simple-reader-error + :stream stream + :format-control "Expected ~S or ., but got ~S." + :format-arguments (list end-char last-char)))))) +(set-macro-character #\[ #'read-bracketed-list) +(set-macro-character #\] (get-macro-character #\))) +(set-macro-character #\{ #'read-bracketed-list) +(set-macro-character #\} (get-macro-character #\)))