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. Syntactic sugar for object-oriented Lisp.
`objective-lisp` provides a simple, concise, and (slightly) more conventional `objective-lisp` provides a simple, concise, and (slightly) more conventional
syntax for accessing the slots and methods of objects. It defines a reader syntax for accessing the slots and methods of objects. It defines a macro named
macro for the `[` and `]` characters (although you can change these in the `O!`, and a reader macro for `#[...]` (although you can change these characters
code). in the code).
## Usage ## 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: First, to enable `objective-lisp`'s syntax, just load the system:
``` common-lisp ``` 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 result of the previous one, like a chain of `.` (dot) operators in C-like
languages. languages.
```common-lisp ```common-lisp
[foo (bar) (baz) (quux)] #[foo (bar) (baz) (quux)]
;; C++: foo.bar().baz().quux() ;; C++: foo.bar().baz().quux()
``` ```
To call a method, just write it after the object: To call a method, just write it after the object:
``` common-lisp ``` common-lisp
[object (method args...)] #[object (method args...)]
;; => (method object args...) ;; => (method object args...)
``` ```
Under the hood, this just passes `object` as the first argument to `method`, so 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): you can do stuff like this (I won't kinkshame you, but your coworkers might):
```common-lisp ```common-lisp
[object (slot-value 'slot-name) (setf value)] #[object (slot-value 'slot-name) (setf value)]
;; => [(slot-value object 'slot-name) (setf value)] ;; => #[(slot-value object 'slot-name) (setf value)]
;; => (setf (slot-value object 'slot-name) value) ;; => (setf (slot-value object 'slot-name) value)
``` ```
Slot accessors, and other methods that don't take additional arguments, can be Slot accessors, and other methods that don't take additional arguments, can be
written without enclosing parentheses: written without enclosing parentheses:
``` common-lisp ``` common-lisp
[object get-something] #[object get-something]
;; => (get-something object) ;; => (get-something object)
``` ```
To access slots directly, use the `:slot` keyword: To access slots directly, use the `:slot` keyword:
``` common-lisp ``` common-lisp
[object :slot slot-name] #[object :slot slot-name]
;; => (slot-value object '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 ## License
`objective-lisp` is public domain (CC0). You can do whatever you want with it. I `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 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" (defsystem "objective-lisp"
:description "Syntactic sugar for object-oriented Lisp." :description "Syntactic sugar for object-oriented Lisp."
:version "2.0" :version "3.0"
:author "~keith" :author "~keith"
:homepage "https://bytes.keithhacks.cyou/keith/objective-lisp" :homepage "https://bytes.keithhacks.cyou/keith/objective-lisp"
:license "Public Domain/CC0" :license "Public Domain/CC0"

View file

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