Rewrite as standard macro
This commit is contained in:
parent
9535f8c6ab
commit
9b6614fc45
3 changed files with 40 additions and 48 deletions
27
README.md
27
README.md
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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)
|
|
||||||
|
|
Loading…
Reference in a new issue