diff options
Diffstat (limited to 'README.md')
-rw-r--r-- | README.md | 337 |
1 files changed, 79 insertions, 258 deletions
diff --git a/README.md b/README.md index 2d590a0564ae..7a42289f482e 100644 --- a/README.md +++ b/README.md @@ -1,258 +1,79 @@ -quasiquote-2.0 -============== - -Why should it be hard to write macros that write other macros? -Well, it shouldn't! - -quasiquote-2.0 defines slightly different rules for quasiquotation, -that make writing macro-writing macros very smooth experience. - -NOTE: quasiquote-2.0 does horrible things to shared structure!!! -(it does a lot of COPY-TREE's, so shared-ness is destroyed). -So, it's indeed a tool to construct code (where it does not matter much if the -structure is shared or not) and not the data (or, at least, not the data with shared structure) - - -```lisp -(quasiquote-2.0:enable-quasiquote-2.0) - -(defmacro define-my-macro (name args &body body) - `(defmacro ,name ,args - `(sample-thing-to-expand-to - ,,@body))) ; note the difference from usual way - -(define-my-macro foo (x y) - ,x ; now here injections of quotation constructs work - ,y) - -(define-my-macro bar (&body body) - ,@body) ; splicing is also easy -``` - -The "injections" in macros FOO and BAR work as naively expected, as if I had written -```lisp -(defmacro foo (x y) - `(sample-thing-to-expand-to ,x ,y)) - -(defmacro bar (&body body) - `(sample-thing-to-expand-to ,@body)) - -(macroexpand-1 '(foo a b)) - - '(SAMPLE-THING-TO-EXPAND-TO A B) - -(macroexpand-1 '(bar a b c)) - - '(SAMPLE-THING-TO-EXPAND-TO A B C) -``` - - -So, how is this effect achieved? - - -DIG, INJECT and SPLICE -------------------------- - -The transformations of backquote occur at macroexpansion-time and not at read-time. -It is totally possible not to use any special reader syntax, but just -underlying macros directly! - -At the core is a macro DIG, which expands to the code that generates the -expression according to the rules, which are roughly these: - * each DIG increases "depth" by one (hence the name) - * each INJECT or SPLICE decreases "depth" by one - * if depth is 0, evaluation is turned on - * if depth if not zero (even if it's negative!) evaluation is off - * SPLICE splices the form, similarly to ordinary `,@`, INJECT simply injects, same as `,` - -```lisp -;; The example using macros, without special reader syntax - -(dig ; depth is 1 here - (a b - (dig ; depth is 2 here - ((inject c) ; this inject is not evaluated, because depth is nonzero - (inject (d ;depth becomes 1 here again - (inject e) ; and this inject is evaluated, because depth becomes zero - )) - (inject 2 f) ; this inject with level specification is evaluated, because it - ; decreases depth by 2 - )))) - - -;; the same example using ENABLE-QUASIQUOTE-2.0 syntax is written as -`(a b `(,c ,(d ,e) ,,f)) ; note double comma acts different than usually -``` - - -The ENABLE-QUASIQUOTE-2.0 macro just installs reader that reads -`FORM as (DIG FORM), ,FORM as (INJECT FORM) and ,@FORM as (SPLICE FORM). -You can just as well type DIG's, INJECT's and SPLICE's directly, -(in particular, when writing utility functions that generate macro-generating code) -or roll your own convenient reader syntax (pull requests are welcome). - -So, these two lines (with ENABLE-QUASIQUOTE-2.0) read the same -```lisp -`(a (,b `,,c) d) - -(dig (a ((inject b) (dig (inject 2 c))) d)) -``` - -You may notice the (INJECT 2 ...) form appearing, which is described below. - - -At "level 1", i.e. when only \` , and ,@ are used, and not, say \`\` ,, ,', ,,@ ,',@ -this behaves exactly as usual quasiquotation. - - -The optional N argument --------------- - -All quasiquote-2.0 operators accept optional "depth" argument, -which goes before the form for human readability. - -Namely, (DIG N FORM) increases depth by N instead of one and -(INJECT N FORM) decreases depth by N instead of one. - -```lisp -(DIG 2 (INJECT 2 A)) - -; gives the same result as - -(DIG (INJECT A)) -``` - - -In fact, with ENABLE-QUASIQUOTE-2.0, say, ,,,,,FORM (5 quotes) reads as (INJECT 5 FORM) -and ,,,,,@FORM as (SPLICE 5 FORM) - - -More examples -------------- - -For fairly complicated example, which uses ,,,@ and OINJECT (see below), - see DEFINE-BINOP-DEFINER macro -in CG-LLVM (https://github.com/mabragor/cg-llvm/src/basics.lisp), -desire to write which was the initial impulse for this project. - - -For macro, that is not a macro-writing macro, yet benefits from -ability to inject using `,` and `,@`, consider JOINING-WITH-COMMA-SPACE macro -(also from CG-LLVM) - -```lisp -(defmacro joining-with-comma-space (&body body) - ;; joinl just joins strings in the list with specified string - `(joinl ", " (mapcar #'emit-text-repr - (remove-if-not #'identity `(,,@body))))) - -;; the macro can be then used uniformly over strings and lists of strings -(defun foo (x y &rest z) - (joining-with-comma-space ,x ,y ,@z)) - -(foo "a" "b" "c" "d") - ;; produces - "a, b, c, d" -``` - - -ODIG and OINJECT and OSPLICE ----------------------------- - -Sometimes you don't want DIG's macroexpansion to look further into the structure of -some INJECT or SPLICE or DIG in its subform, -if the depth does not match. In these cases you need "opaque" versions of -DIG, INJECT and SPLICE, named, respectively, ODIG, OINJECT and OSPLICE. - -```lisp -;; here injection of B would occur -(defun foo (b) - (dig (dig (inject (a (inject b)))))) - -;; and here not, because macroexpansion does not look into OINJECT form -(defun bar (b) - (dig (dig (oinject (a (inject b)))))) - -(foo 1) - - '(DIG (INJECT (A 1))) - -(bar 1) - - '(DIG (OINJECT (A (INJECT B)))) -``` - -MACRO-INJECT and MACRO-SPLICE ------------------------------ - -Sometimes you just want to abstract-out some common injection patterns... -That is, you want macros, that expand into common injection patterns. -However, you want this only sometimes, and only in special circumstances. -So it won't do, if INJECT and SPLICE just expanded something, whenever it -turned out to be macro. For that, use MACRO-INJECT and MACRO-SPLICE. - -```lisp -;; with quasiquote-2.0 syntax turned on -(defmacro inject-n-times (form n) - (make-list n :initial-element `(inject ,form))) - -(let (x 0) - `(dig (a (macro-inject (inject-n-times (incf x) 3))))) -;; yields -'(a (1 2 3)) - -;;and same with MACRO-SPLICE -(let (x 0) - `(dig (a (macro-splice (inject-n-times (incf x) 3))))) -;; yields -'(a 1 2 3) -``` - -OMACRO-INJECT and OMACRO-SPLICE are, as usual, opaque variants of MACRO-INJECT and MACRO-SPLICE. - -Both MACRO-INJECT and MACRO-SPLICE expand their subform exactly once (using MACROEXPAND-1), -before plugging it into list. -If you want to expand as much as it's possible, use MACRO-INJECT-ALL and MACRO-SPLICE-ALL, -which expand using MACROEXPAND before injecting/splicing, respectively. -That implies, that while subform of MACRO-INJECT and MACRO-SPLICE is checked to be -macro-form, the subform of MACRO-INJECT-ALL is not. - - -Terse syntax of the ENABLE-QUASIQUOTE-2.0 ------------------------------------------ - -Of course, typing all those MACRO-INJECT-ALL, or OMACRO-SPLICE-ALL or whatever explicitly -every time you want this special things is kind of clumsy. For that, default reader -of quasiquote-2.0 provides extended syntax - -```lisp -',,,,!oma@x - -;; reads as -'(OMACRO-SPLICE-ALL 4 X) -``` - -That is, the regexp of the syntax is -[,]+![o][m][a][@]<whatever> - -As usual, number of commas determine the anti-depth of the injector, exclamation mark -turns on the syntax, if `o` is present, opaque version of injector will be used, -if `m` is present, macro-expanding version of injector will be used and if -`a` is present, macro-all version of injector will be used. - -Note: it's possible to write ,!ax, which will read as (INJECT-ALL X), but -this will not correspond to the actual macro name. - -Note: it was necessary to introduce special escape-char for extended syntax, -since usual idioms like `,args` would otherwise be completely screwed. - - -TODO ----- - -* WITH-QUASIQUOTE-2.0 read-macro-token for local enabling of ` and , overloading -* wrappers for convenient definition of custom overloading schemes -* some syntax for opaque operations - -P.S. Name "quasiquote-2.0" comes from "patronus 2.0" spell from www.hpmor.com - and has nothing to do with being "the 2.0" version of quasiquote. \ No newline at end of file +depot +===== + +[![builds.sr.ht status](https://builds.sr.ht/~tazjin/depot/master.svg)](https://builds.sr.ht/~tazjin/depot/master?) + +This repository is the [monorepo][] for my personal tools and infrastructure. +Everything in here is built using [Nix][] with an automatic attribute-set layout +that mirrors the filesystem layout of the repository (this might feel familiar +to users of Bazel). + +This repository used to be hosted on GitHub, but for a variety of reasons I have +decided to take over the management of personal infrastructure - of which this +repository is a core component. + +If you've ended up here and have no idea who I am, feel free to follow me [on +Twitter][]. + +# Highlights + +## Tools + +* `tools/emacs` contains my personal Emacs configuration (packages & config) +* `fun/aoc2019` contains solutions for a handful of Advent of Code 2019 + challenges, before I ran out of interest +* `tools/blog_cli` contains my tool for writing new blog posts and storing them + in the DNS zone +* `ops/kms_pass.nix` is a tiny tool that emulates the user-interface of `pass`, + but actually uses Google Cloud KMS for secret decryption +* `ops/kontemplate` contains my Kubernetes resource templating tool (with which + the services in this repository are deployed!) + +## Packages / Libraries + +* `nix/buildGo` implements a Nix library that can build Go software in the style + of Bazel's `rules_go`. Go programs in this repository are built using this + library. +* `tools/emacs-pkgs` contains various Emacs libraries that my Emacs setup uses, + for example: + * `dottime.el` provides [dottime][] in the Emacs modeline + * `nix-util.el` provides editing utilities for Nix files + * `term-switcher.el` is an ivy-function for switching between vterm buffers +* `net/alcoholic_jwt` contains an easy-to-use JWT-validation library for Rust +* `net/crimp` contains a high-level HTTP client using cURL for Rust + +## Services + +Services in this repository are deployed on a Google Kubernetes Engine cluster +using [Nixery](). + +* `web/tazblog` contains my blog software (serving at [tazj.in][]) +* `web/cgit-taz` contains a slightly patched version of `cgit` that serves my + git web interface at [git.tazj.in][] +* `ops/sync-gcsr` contains a tiny service that synchronises a Google Cloud + Source Repository with a local disk path. My `cgit` setup uses this + under-the-hood. +* `ops/journaldriver` contains a small Rust daemon that can forward logs from + journald to Stackdriver Logging + +## Miscellaneous + +Presentations I've given in the past are in the `presentations` folder, these +cover a variety of topics and some of them have links to recordings. + +There's a few fun things in the `fun/` folder, often with context given in the +README. Check out my [list of the best tools][best-tools] for example. + +# Contributing + +If you'd like to contribute to any of the tools in here, please check out the +[contribution guidelines](/tree/docs/CONTRIBUTING.md). + +[monorepo]: https://en.wikipedia.org/wiki/Monorepo +[Nix]: https://nixos.org/nix +[on Twitter]: https://twitter.com/tazjin +[Nixery]: https://github.com/google/nixery +[tazj.in]: https://tazj.in +[git.tazj.in]: https://git.tazj.in +[best-tools]: /about/fun/best-tools/README.md +[dottime]: https://dotti.me |