Skip to content

Commit

Permalink
Replace Pack with the simpler single Kwarg
Browse files Browse the repository at this point in the history
  • Loading branch information
gilch committed Dec 24, 2023
1 parent f72893a commit cac0003
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 76 deletions.
48 changes: 7 additions & 41 deletions docs/primer.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1469,56 +1469,23 @@ You indicate how many with the number of trailing ``#``\ s.
... )
Fraction(2, 3)
Reader tags may also take keyword arguments indicated by keyword prefixes,
Reader tags may also take keyword arguments,
made with a helper kwarg tag ending in ``=#``,
which can be helpful quick refinements for functions with optional arguments,
without the need to create a new reader macro for each specialization.

.. code-block:: REPL
#> builtins..int#.#"21" ; Normal base ten
#> builtins..int# .#"21" ; Normal base ten
>>> (21)
21
#> base=builtins..int##6 .#"21" ; base six via optional base= kwarg
#> builtins..int## base=#6 .#"21" ; base 6, via base=# kwarg tag
>>> (13)
13
The special prefixes ``*=`` and ``**=`` unpack the agument at that position,
The helper tags ``*=#`` and ``**=#`` unpack the argument at that position,
either as positional arguments or keyword arguments, respectively.
Prefixes pull from the reader stream in the order written.
Each prefix requires another ``#``.
Any leftover ``#``\ s each pull a positional argument after that.
An empty prefix (``=``) indicates a single positional argument.
These can be used to put positional arguments between or before kwargs.

Pack Objects
++++++++++++

Try to avoid using more than about 3 or 4 ``#``\ s in a tag,
because that gets hard to read.
You typically won't need more than that.
For too many homogeneous arguments,
(i.e., with the same semantic type)
consider using ``*=`` applied to a tuple instead.
For too many heterogeneous arguments, consider ``**=``.
For complicated expressions,
consider using inject (``.#``) on tuple expressions instead of using tags.

A tag can be empty if it has at least one prefix,
even the empty prefix (``=``).
An empty tag creates a `Pack` object,
which contains any args and kwargs given.
When a reader tag pulls one, it automatically unpacks it.

`Pack`\ s are used to order and group tag arguments in a hierarchical way,
for improved legibility.
They're another way to avoid using too many ``#``\ s in a row.
They allow you to write the keywords immediately before their values,
instead of up front.

`Pack`\ s are only meant for reader tags.
They should be consumed immediately at read time,
and are only allowed to survive past that for debugging purposes.

Unqualified Tags
++++++++++++++++
Expand All @@ -1532,9 +1499,8 @@ for attributes ending in ``#`` (i.e. ``QzHASH_``)
when it encounters an unqualified tag.
The ``#`` is only in an attribute name to distinguish them from normal compile-time macros,
not to indicate arity.
Prefixes should not be included in the attribute name either.
It is possible to use a tag name containing ``=`` or extra ``#``\ s,
but they must be escaped with a ``\``.
It is possible to use a tag name containing extra ``#``\ s,
or ending in ``=#`` if escaped with a ``\``.

Discard
+++++++
Expand Down
4 changes: 2 additions & 2 deletions src/hissp/macros.lissp
Original file line number Diff line number Diff line change
Expand Up @@ -2828,7 +2828,7 @@ Creates a lambda of arity {X} containing a `^*#<QzHAT_QzSTAR_QzHASH_>`

.. code-block:: REPL

#> (op#add 5 file=spy##sys..stdout(op#mul 7 3))
#> (op#add 5 spy##file=#sys..stdout(op#mul 7 3))
>>> __import__('operator').add(
... (5),
... # hissp.._macro_._spy
Expand Down Expand Up @@ -2861,7 +2861,7 @@ Creates a lambda of arity {X} containing a `^*#<QzHAT_QzSTAR_QzHASH_>`

.. code-block:: REPL

#> file=time##sys..stdout(time..sleep .05)
#> time##file=#sys..stdout(time..sleep .05)
>>> # hissp.macros.._macro_.let
... (lambda _QzPMWTVFTZz_time=__import__('time').time_ns:
... # hissp.macros.._macro_.letQz_from
Expand Down
53 changes: 20 additions & 33 deletions src/hissp/reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,18 +176,18 @@ def __repr__(self):
return f"Comment({self.token!r})"


class Pack:
"""Contains read-time arguments for reader macros.
class Kwarg:
"""Contains a read-time keyword argument for reader macros.
Normally made with empty tags, but can be constructed directly.
Normally made with kwarg tags, but can be constructed directly.
"""

def __init__(self, *args, **kwargs):
self.args = list(args)
self.kwargs = kwargs
def __init__(self, k, v):
self.k = k
self.v = v

def __repr__(self):
return f"Pack(*{self.args},**{self.kwargs})"
return f"Kwarg({self.k}, {self.v})"


class Lissp:
Expand Down Expand Up @@ -436,12 +436,11 @@ def _get_counter(self) -> int:

def _custom_macro(self, form, tag: str):
assert tag.endswith("#")
if re.fullmatch(r"(?:[^\\]|\\.)+=#", tag):
return Kwarg(tag[:-2], form)
arity = tag.replace(R"\#", "").count("#")
assert arity > 0
*keywords, label = re.findall(r"((?:[^=\\]|\\.)*(?:=|#+))", tag)
if len(keywords) > arity:
raise SyntaxError(f"Not enough # for each = in {tag!r}")
label = label[:-arity]
label = tag[:-arity]
label = force_munge(self.escape(label))
label = re.sub(r"(^\.)", lambda m: force_qz_encode(m[1]), label)
fn: Fn[[str], Fn] = self._fully_qualified if ".." in label else self._local
Expand All @@ -450,27 +449,17 @@ def _custom_macro(self, form, tag: str):
kwargs = {}
depth = len(self.depth)
with self.compiler.macro_context():
for i, [k, v] in enumerate(
zip_longest(
(k[:-1] for k in keywords), chain([form], self._filter_drop())
),
1,
):
if type(v) is Pack:
if k:
raise SyntaxError(
f"Can't apply prefix {k}= to {v}.", self.position(self._pos)
)
args.extend(v.args)
kwargs.update(v.kwargs)
elif k == "*":
args.extend(v)
elif k == "**":
kwargs.update(v)
elif k:
kwargs[force_munge(self.escape(k))] = v
for i, x in enumerate(chain([form], self._filter_drop()), 1):
if type(x) is Kwarg:
k, v = x.k, x.v
if k == "*":
args.extend(v)
elif k == "**":
kwargs.update(v)
else:
kwargs[force_munge(self.escape(k))] = v
else:
args.append(v)
args.append(x)
if i == arity:
break
else:
Expand All @@ -488,8 +477,6 @@ def _fully_qualified(tag: str) -> Fn:
return cast(Fn, reduce(getattr, function.split("."), import_module(module)))

def _local(self, tag: str) -> Fn:
if not tag:
return Pack
try:
return getattr(self.ns[C.MACROS], tag + munge("#"))
except (AttributeError, KeyError):
Expand Down

0 comments on commit cac0003

Please sign in to comment.