Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove synexpand and friends #251

Merged
merged 2 commits into from
Jan 13, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
240 changes: 6 additions & 234 deletions src/hissp/macros.lissp
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ subexpressions as arguments.

;;; Don't let the size of this file intimidate you. Most of the lines
;;; are documentation. After you remove the comments, docstrings, and
;;; blank lines, only about 400 lines of actual code remain.
;;; blank lines, only about 300 lines of actual code remain.


_#"
Expand Down Expand Up @@ -759,7 +759,7 @@ Hidden doctest adds bundled macros for REPL-consistent behavior.
;;
;; See also:
;; `en#<enQzHASH_>`, `O#<OQzHASH_>`, `XY#<XYQzHASH_>`, `getattr`,
;; `operator.attrgetter`, `lambda`, `^#<QzHAT_QzHASH_>`.
;; `operator.attrgetter`, `lambda`.
;;
`(lambda ,'X ,e))

Expand All @@ -777,7 +777,7 @@ Hidden doctest adds bundled macros for REPL-consistent behavior.
... 'abcd')
'dcba'

See also: `X#<XQzHASH_>`, `XYZ#<XYZQzHASH_>`, `^^#<QzHAT_QzHAT_QzHASH_>`.
See also: `X#<XQzHASH_>`, `XYZ#<XYZQzHASH_>`.
"
`(lambda ,'XY ,e))

Expand All @@ -794,8 +794,7 @@ Hidden doctest adds bundled macros for REPL-consistent behavior.
;; ... Z=__import__('math').tau)
;; True
;;
;; See also: `XY#<XYQzHASH_>`, `XYZW#<XYZWQzHASH_>`,
;; `^^^#<QzHAT_QzHAT_QzHAT_QzHASH_>`.
;; See also: `XY#<XYQzHASH_>`, `XYZW#<XYZWQzHASH_>`.
;;
`(lambda ,'XYZ ,e))

Expand All @@ -814,7 +813,6 @@ Hidden doctest adds bundled macros for REPL-consistent behavior.
;; 'Hello'
;;
;; See also: `XYZ#<XYZQzHASH_>`, `en#<enQzHASH_>`, `X#<XQzHASH_>`.
;; `^^^^#<QzHAT_QzHAT_QzHAT_QzHAT_QzHASH_>`.
;;
`(lambda ,'XYZW ,e))

Expand Down Expand Up @@ -939,8 +937,8 @@ Hidden doctest adds bundled macros for REPL-consistent behavior.
;; ... QzPLUS_=(1)))
;; 1
;;
;; See also: `operator.getitem`, `[#<QzLSQB_QzHASH_>`,
;; `set!<setQzBANG_>`, `^*#<QzHAT_QzSTAR_QzHASH_>`.
;; See also:
;; `operator.getitem`, `[#<QzLSQB_QzHASH_>`, `set!<setQzBANG_>`.
;;
`(op#itemgetter ,e))

Expand Down Expand Up @@ -2585,232 +2583,6 @@ except ModuleNotFoundError:pass"
"``nil#`` evaluates as ``x or ()``. Adapter for 'nil punning'."
`(ors ,x ()))

(defmacro ^*\# e
"``^*#`` 'synexpand' concatenative mini-language expressions

Walks e expanding any mini-language syntax found.

The mini-language supports higher-order function manipulation
including composition, partial application, and point-free data flow.

A mini-language expression is a tuple beginning with an element
containing at least one ``^`` or ``:`` character that is not the first
character (to avoid detecting all control words). The remainder form
the initial stack.

The first element must read to a `str` (typically a symbol, control
word, or injected string literal), is demunged, and is split into
terms on magic characters.

Syntax expansion builds an expression from the stack of expressions
operated on by the mini-language terms. The result is the stack
spliced into a `prog1` (or the element itself if only one remains).

The
`^#<QzHAT_QzHASH_>`,
`^^#<QzHAT_QzHAT_QzHASH_>`,
`^^^#<QzHAT_QzHAT_QzHAT_QzHASH_>`, and
`^^^^#<QzHAT_QzHAT_QzHAT_QzHAT_QzHASH_>` macros apply to terms and
wrap a ``^*#`` expression in a lambda of arity 1-4 (respectively)
using their parameters as the initial stack.

The terms are applied right-to-left, like function calls.
Magic characters are

``,`` -data (Suffix)
Interprets callable term as data.
``^`` -depth (Suffix)
Increases arity of a term. Assume depth 1 otherwise. Can be repeated.
Write after -data.
``/`` DROP (term)
Removes expression (at depth).
``&`` PICK (term)
Copies expression (at depth) and pushes.
``@`` ROLL (term, default depth 2)
Pops expression (at depth) and pushes.
``>`` MARK (term, default depth 0)
Inserts a sentinel object for PACK (at depth).
``<`` PACK (term)
Pops to the first MARK (if any) and pushes as tuple.
With depth, looks tuple up on the next expression.
Used for invocations.
``*`` SPLAT (term)
Splices an iterable (in-place, at depth).
``:`` NOP (no depth)
Has no effect. A separator when no other magic applies.

They can be escaped with a backtick (:literal:`\\``).

Other terms are either callables or data, and read as Lissp.

Callables (default depth 1) pop args to their depth and push their
result. Combine with a datum for partial application.

.. code-block:: REPL

#> (define decrement ^#sub^@1)
>>> # define
... __import__('builtins').globals().update(
... decrement=(lambda _QzX7FS3TFJz_x:
... # hissp.macros.._macro_.QzHAT_QzSTAR_QzHASH_
... sub(
... _QzX7FS3TFJz_x,
... (1))))

#> (decrement 5)
>>> decrement(
... (5))
4

Data terms just push themselves on the stack (default depth 0).

.. code-block:: REPL

#> ^*#(<`@,1:2:3)
>>> # QzAT_
... (lambda *xs:[*xs])(
... (1),
... (2),
... (3))
[1, 2, 3]

Increasing the depth of data to 1 implies a lookup on the next
expression. Methods always need a self, so they can be converted to
attribute lookups at the default depth of 1. Combine them to drill
into complex data structures.

.. code-block:: REPL

#> (^#.__class__.__name__:'spam^ (dict : spam 'eggs))
>>> (lambda _QzX7FS3TFJz_x:
... # hissp.macros.._macro_.QzHAT_QzSTAR_QzHASH_
... __import__('operator').attrgetter(
... '__class__.__name__')(
... __import__('operator').getitem(
... _QzX7FS3TFJz_x,
... 'spam')))(
... dict(
... spam='eggs'))
'str'

Terms are categorized as callable or data at read time. Literals are
always data, but a term that reads as a `tuple` or `str` type may be
ambiguous, in which case it's presumed callable, unless it ends with a
``,``.

.. code-block:: REPL

#> (define prod ^#reduce^mul,)
>>> # define
... __import__('builtins').globals().update(
... prod=(lambda _QzX7FS3TFJz_x:
... # hissp.macros.._macro_.QzHAT_QzSTAR_QzHASH_
... reduce(
... mul,
... _QzX7FS3TFJz_x)))

#> (en#prod 1 2 3)
>>> (lambda *_Qz6RFWTTVXz_xs:
... prod(
... _Qz6RFWTTVXz_xs))(
... (1),
... (2),
... (3))
6

#> (define geomean ^#pow^prod@truediv^1:len&)
>>> # define
... __import__('builtins').globals().update(
... geomean=(lambda _QzX7FS3TFJz_x:
... # hissp.macros.._macro_.QzHAT_QzSTAR_QzHASH_
... pow(
... prod(
... _QzX7FS3TFJz_x),
... truediv(
... (1),
... len(
... _QzX7FS3TFJz_x)))))

#> (geomean '(1 10))
>>> geomean(
... ((1),
... (10),))
3.1622776601683795

"
(if-else (ands e (op#is_ tuple (type e)))
(let-from (sym : :* args) e
(if-else (ands (op#is_ str (type sym))
(re..search ".[:^]" (hissp..demunge sym)))
(._rewrite _macro_
(re..findall <<#;([/&@<>*:]|(?:[^,^`/&@<>*:]|`[,^/&@<>*:])+)(,?\^*)
(hissp..demunge sym))
: :* (map X#(.^*\# _macro_ X) args))
`(,@(map X#(.^*\# _macro_ X) e))))
e))

(defmacro _rewrite (program : :* exprs)
(if-else (not program)
(case (len exprs) `(prog1 ,@exprs)
(1) (get#0 exprs))
my#
(let-from (cmd suffix) (.pop program)
(attach my
: reader (hissp..reader.Lissp : ns (.get hissp.compiler..NS))
is? XY#(op#is_ X (type Y))
str? X#(my.is? str X)
startswith? XY#(ands (my.str? X) (.startswith X Y))
literal? X#(ors (op#eq () X) (not (op#contains (# tuple str) (type X))))
quotation? X#(ands (my.is? tuple X) (op#eq 'quote (get#0 X)))
control-word? X#(my.startswith? X ":")
module-handle? X#(ands (my.str? X) (.endswith X "."))
method? X#(my.startswith? X ".")
exprs (list exprs)
arity (.count suffix "^")
mark (getattr unittest.mock..sentinel "hissp.>"))
(attach my
: iexprs (iter my.exprs)
obj (next (.reads my.reader (.replace cmd "`" "")))
arity+1 (op#add 1 my.arity))
(set@ my.result
(case cmd (@ (if-else (ors (my.literal? my.obj)
(.startswith suffix ",")
(hissp.reader..is_lissp_string my.obj)
(my.control-word? my.obj)
(my.module-handle? my.obj)
(my.quotation? my.obj))
(if-else my.arity `(op#getitem ,(next my.iexprs) ,my.obj) my.obj)
(if-else (ors my.arity (not (my.method? my.obj)))
`(,my.obj ,@(i#islice my.iexprs my.arity+1))
`((op#attrgetter ',([#1:] my.obj))
,(next my.iexprs)))))
|/| (progn (.pop my.exprs my.arity) ())
|&| (@ (op#getitem my.exprs my.arity))
|@| (@ (.pop my.exprs my.arity+1))
|>| nil#(.insert my.exprs my.arity my.mark)
|<| (@ (let (x (tuple (i#takewhile X#(op#ne X my.mark) my.iexprs)))
(if-else suffix
`(op#getitem ,(next my.iexprs) x)
x)))
|*| (@ :* (i#islice my.iexprs my.arity) :* (next my.iexprs))
: ()))
(set@ my.result
(._rewrite _macro_ program : :* my.result :* my.iexprs))
my.result)))

.#`(progn ,@(map X#(let (args (get#(slice X) `($#x $#y $#z $#w))
name (.format "{}#" (op#mul X "^")))
`(defmacro ,(hissp..munge name) (,'terms)
',(.format "``{}`` 'synexpand-{X}'.

Creates a lambda of arity {X} containing a `^*#<QzHAT_QzSTAR_QzHASH_>`
(synexpand) applied with terms to a tuple of the parameters.
"
name : X X)
`(lambda ,',args
(^*\# (,,'terms ,@',args)))))
(range 1 5)))

(defmacro _spy (expr file)
`(let ($#e ,expr)
(print (pprint..pformat ',expr : sort_dicts 0)
Expand Down
Loading