Skip to content

Commit

Permalink
Add mix macro
Browse files Browse the repository at this point in the history
  • Loading branch information
gilch committed Sep 29, 2024
1 parent eb4b636 commit 8a30e99
Showing 1 changed file with 42 additions and 17 deletions.
59 changes: 42 additions & 17 deletions src/hissp/macros.lissp
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
;;; Copyright 2019, 2020, 2021, 2022, 2023, 2024 Matthew Egan Odendahl
;;; SPDX-License-Identifier: Apache-2.0

"Hissp's bundled macros.
"Hissp's bundled `tag` and `macro` metaprograms.

To help keep macro definitions and `expansion`\\ s manageable in
complexity, these macros lack some of the extra features their
equivalents have in Python or in other Lisps. These are not intended to
To help keep definitions and `expansion`\\ s manageable in
complexity, these metaprograms lack some of the extra features their
equivalents have in Python or in other Lisps. They are not intended to
be a standard library for general use, but do bring Hissp up to a basic
standard of utility without adding dependencies, which may suffice in
some cases.
Expand All @@ -27,19 +27,22 @@ As a convenience, the bundled macros are automatically made available
Hissp module with better alternatives need not use the bundled macros at
all.

They also eschew expansions of any subexpression to a `Python injection`
(except the standard `symbol` or `string literal fragment` cases),
relying only on the built-in special forms ``quote`` and ``lambda``,
which makes their expansions compatible with advanced rewriting macros
that process the Hissp expansions of other macros. (The -``[#`` tags are
something of an exception, as one subexpression is written in Python to
begin with.)

This doesn't apply to inlined helper functions that contain no user
code, because that's no worse than using an opaque name imported from
somewhere else. `if-else <ifQzH_else>` is one such example, expanding to
a lambda containing an injected `conditional expression <if_expr>`,
immediately called with the subexpressions as arguments.
With the exception of `mix`, which is a `text macro` specifically made
for creating a `Python injection`, the other metaprograms eschew
expansions of any of their arguments to a `Python injection` (other than
the standard `symbol` or `string literal fragment` cases), relying only
on the built-in special forms ``quote`` and ``lambda``, which makes
their expansions compatible with advanced rewriting macros that process
the Hissp expansions of other macros.

(The -``[#`` and `my# <myQzHASH_>` tags are also something of an
exception, as one argument is written in Python to begin with.)

That only goes for arguments and doesn't apply to inlined helper
functions in the expansions that contain no user code, because that's no
worse than using an opaque name imported from somewhere else. `if-else
<ifQzH_else>` is one such example, expanding to a lambda containing an
injected `conditional expression <if_expr>`, that is called immediately.
"

;;; This module is not necessarily a good example of how you should
Expand Down Expand Up @@ -3215,6 +3218,28 @@ except ModuleNotFoundError: pass"

;;;; Advanced

(defmacro mix (: :* args)
<#; Injection. Compiles each arg to Python and concatenates the fragments.
;;
;; Lissp features like munging and fully-qualified identifiers can be
;; freely mixed with Python expressions like slicing, infix operators
;; and list comprehensions by using `fragment token`\ s:
;;
;; .. code-block:: REPL
;;
;; #> (mix |[|%|+|(.lower %)| for |%| in |string..ascii_uppercase|[:10]]|)
;; >>> # mix
;; ... [QzPCENT_+QzPCENT_.lower() for QzPCENT_ in __import__('string').ascii_uppercase[:10]]
;; ['Aa', 'Bb', 'Cc', 'Dd', 'Ee', 'Ff', 'Gg', 'Hh', 'Ii', 'Jj']
;;
;; Beware that a `str atom` like ``|:|`` is still a `control word`,
;; and like ``|foo.|`` is still a `module handle`, even when made with
;; a `fragment token`. However, Python allows whitespace in many areas
;; where it is not conventional to do so, making fragments like
;; ``| :|`` or ``|foo .|`` viable workarounds in such cases.
;;
(.join "" (map hissp..readerless args)))

(defmacro my\# (targets_or_scope : expr None scope () :** kwargs)
<#;``my#`` Anaphoric. Injection. `let` ``my`` be a fresh
;; `types.SimpleNamespace` in a lexical scope.
Expand Down

0 comments on commit 8a30e99

Please sign in to comment.