Rewrite as standard macro

This commit is contained in:
~keith 2022-02-22 04:50:53 +00:00
parent 9535f8c6ab
commit 9b6614fc45
Signed by: keith
GPG Key ID: 5BEBEEAB2C73D520
3 changed files with 40 additions and 48 deletions

View File

@ -3,13 +3,13 @@
Syntactic sugar for object-oriented Lisp.
`objective-lisp` provides a simple, concise, and (slightly) more conventional
syntax for accessing the slots and methods of objects. It defines a reader
macro for the `[` and `]` characters (although you can change these in the
code).
syntax for accessing the slots and methods of objects. It defines a macro named
`O!`, and a reader macro for `#[...]` (although you can change these characters
in the code).
## Usage
**TL;DR:** `[object (method args)]` is like `object.method(args)` in C++.
**TL;DR:** `#[object (method args)]` is like `object.method(args)` in C++.
First, to enable `objective-lisp`'s syntax, just load the system:
``` common-lisp
@ -21,37 +21,44 @@ in square brackets rather than parentheses. Each expression within acts upon the
result of the previous one, like a chain of `.` (dot) operators in C-like
languages.
```common-lisp
[foo (bar) (baz) (quux)]
#[foo (bar) (baz) (quux)]
;; C++: foo.bar().baz().quux()
```
To call a method, just write it after the object:
``` common-lisp
[object (method args...)]
#[object (method args...)]
;; => (method object args...)
```
Under the hood, this just passes `object` as the first argument to `method`, so
you can do stuff like this (I won't kinkshame you, but your coworkers might):
```common-lisp
[object (slot-value 'slot-name) (setf value)]
;; => [(slot-value object 'slot-name) (setf value)]
#[object (slot-value 'slot-name) (setf value)]
;; => #[(slot-value object 'slot-name) (setf value)]
;; => (setf (slot-value object 'slot-name) value)
```
Slot accessors, and other methods that don't take additional arguments, can be
written without enclosing parentheses:
``` common-lisp
[object get-something]
#[object get-something]
;; => (get-something object)
```
To access slots directly, use the `:slot` keyword:
``` common-lisp
[object :slot slot-name]
#[object :slot slot-name]
;; => (slot-value object 'slot-name)
```
You can also just use the `O!` macro directly:
``` common-lisp
(O! object :slot foo (do-something args...))
;; => (do-something (slot-value object 'foo) args...)
```
## License
`objective-lisp` is public domain (CC0). You can do whatever you want with it. I
don't really care about credit, it's just a silly little thing I wrote in a few

View File

@ -3,7 +3,7 @@
(defsystem "objective-lisp"
:description "Syntactic sugar for object-oriented Lisp."
:version "2.0"
:version "3.0"
:author "~keith"
:homepage "https://bytes.keithhacks.cyou/keith/objective-lisp"
:license "Public Domain/CC0"

View File

@ -3,49 +3,34 @@
(defpackage objective-lisp
(:use common-lisp)
(:export +open-char+ +close-char+
read-construct-item
read-construct
read-unexpected))
(:export O!))
(in-package objective-lisp)
(defconstant +open-char+ #\[)
(defconstant +close-char+ #\])
(defun read-construct-item (sexpr stream)
"Recursively read and rewrite an objective-lisp construct."
(if (char= (peek-char t stream t nil t) +close-char+)
; We've hit the end, return the sexpr we built
(progn (read-char stream t nil t)
sexpr)
(let ((item (read stream t nil t)))
(cond
; Method call [object (method args...)]
((consp item)
(read-construct-item `(,(car item) ,sexpr ,@(cdr item))
stream))
; Slot access [object :slot slot-name]
((eq item :slot)
(read-construct-item `(slot-value ,sexpr ',(read stream t nil t))
stream))
; Consless method call [object method]
((symbolp item)
(read-construct-item `(,item ,sexpr)
stream))
; Something else
(t (error "Unexpected item ~S" item))
))))
(defmacro O! (root &rest forms)
(let ((expr root))
(loop for entry on forms
for form = (car entry)
do (setf expr
(cond ((consp form) `(,(car form) ,expr ,@(cdr form)))
((eq form :slot) (prog1
`(slot-value ,expr ',(cadr entry))
(unless (cdr entry)
(error "Unexpected end of list (expected slot name)"))
(rplacd entry (cddr entry))))
((symbolp form) `(,form ,expr))
(t (error "Unexpected form ~S" form))
)))
expr))
(defun read-construct (stream char)
(defun read-construct (stream char arg)
"Read an objective-lisp construct."
(declare (ignore char))
(read-construct-item (read stream t nil t) ; 1st sexpr is the object, don't rewrite it
stream))
(declare (ignore char arg))
(macroexpand-1 (cons 'O! (read-delimited-list +close-char+ stream))))
(defun read-unexpected (stream char)
(declare (ignore stream))
(error "Unexpected character ~S" char))
(set-macro-character +open-char+ 'read-construct)
(set-macro-character +close-char+ 'read-unexpected)
;(set-macro-character +open-char+ 'read-construct)
(set-dispatch-macro-character #\# +open-char+ #'read-construct)
(set-macro-character +close-char+ (get-macro-character #\)))