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

Consider allowing hotword expressions nested in bracketed expressions #59

Open
gilch opened this issue Oct 26, 2021 · 6 comments
Open

Comments

@gilch
Copy link
Owner

gilch commented Oct 26, 2021

While parsing a bracketed expression, we could recursively switch back to the base Hebigo parser when encountering a macro character that Python doesn't use, like !, until we finish the next Hebigo expression, then place the result of compiling that back into the Python string we're building. This would effectively be a builtin reader macro. For example,

def: fibonacci: n
  :@ functools..lru_cache: None  # Qualified identifier in decorator.
  if: (n <= 1)
    :then: n
    :else: (!fibonacci:(n - 1) + !fibonacci:(n - 2))

Maybe a bad example, since Python's expression syntax can handle this part fine.

    :else: (fibonacci(n - 1) + fibonacci(n - 2))

But suppose we needed a macro.

(!macro:spam + !macro:eggs)

We'd currently have to do something like

!let: :,: s e
  :be hebi.bootstrap..entuple: macro:spam macro:eggs
  (s + e)

or

operator..add: macro:spam macro:eggs

On the other hand, we might want to encourage using hotword expressions instead of Python expressions, because it's much easier to write macros to work with those.

@gilch
Copy link
Owner Author

gilch commented Oct 26, 2021

This actually seems a bit more difficult than I first thought. We're relying on Python's parser in bracketed expressions, but the macro character ! probably shouldn't be valid inside of a string literal. We can probably recognize literal strings with the lexing regex though. Hmm. It does still seem doable.

@gilch
Copy link
Owner Author

gilch commented Oct 26, 2021

F-strings though. We'd want to be able to turn the macro back on in those.

@gilch
Copy link
Owner Author

gilch commented Oct 26, 2021

As convenient as this would be, it kind of doesn't seem worth it.

The thing I miss the most in bracketed expressions so far is the module literals. It would be nice if we could at least preprocess those, but it seems about as difficult to do as the full expression macros. One can certainly write __import__ calls by hand, but that's so much longer it doesn't seem worth it compared to using !let.

Python is capable of abbreviating this kind of thing.

class Importer:
    def __init__(self, module_path=()):
        self.mpath = module_path
    def __getattribute__(self, attr):
        return Importer(object.__getattribute__(self,'mpath') + (attr,))
    def __call__(self):
        return import_module('.'.join(object.__getattribute__(self,'mpath')))
>>> M = Importer()
>>> M.collections.abc().Iterable
<class 'collections.abc.Iterable'>

That's almost as good. You need a prefix like the M., and (). instead of ... There's also slightly more runtime overhead, which could add up.

@gilch
Copy link
Owner Author

gilch commented Oct 27, 2021

Given f-strings, ! isn't a good macro character choice, because it does have meaning there. ; might be the best option. I'm pretty sure it's not in format specifiers. Normally, it would end a statement, so it can't be in an expression either, except in string literals, which would have to be dealt with somehow anyway.

@gilch
Copy link
Owner Author

gilch commented Sep 5, 2022

A macro like this wouldn't be too hard to write:

!where: (spam+eggs) spam macro:spam  eggs macro:eggs

It feels more compact than the !let anyway.

It could also be done positionally with an anaphor.

!where: (X[0]+X[1]) macro:spam macro:eggs

Or both at once.

This seems like the 80% solution. Getting the parser to handle this properly seems a lot more difficult.

@gilch
Copy link
Owner Author

gilch commented Nov 24, 2024

Hissp has the mix macro now, which would serve this role. It depends on fragment tokens to work, but a macro could theoretically strip brackets.

Usage might end up looking something like the following:

!mix: macro:spam (+) macro:eggs

You couldn't simply inject unbalanced parentheses this way, but a text macro could expand to an arbitrary fragment. Macros operate on the Hissp level. There's no need to change the parser here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant