HaxeFoundation / HaxeFoundation/haxe

MacroStringTools.formatString or alternative to unambiguously identify interpolated expressions

Open
#5,964 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Haxe
Stars
6.9k
Forks
715
Avg merge
2d 2h
Merged PRs (30d)
11

Description

For more details, see https://gist.github.com/binki/3f9ec47fed01deacba9184a417ec8790

Basically, the trouble I am having is that I cannot figure out how, in a macro, to figure out what comes from a `${}` interpolation versus not. These two forms produce the same expression when processed through `MacroStringTools.formatString()`:

```haxe
'asdf${''}${'bsdf'}';
'asdf${''}bsdf';
```

I want to be able to clearly identify what is part of the top level non-interpolated part of the interpolated string and what is actually being interpolated into it. I want to be able to be able to extract interpolated strings I have marked for processing with a macro and store just the uninterpolated parts for my own review/tooling purposes or be able to automatically escape interpolated expressions (e.g., tiny HTML templates). I find the expression trees built by `formatString` too confusing. Also, since the expression tree represents the code that would be generated, even if the output did unambiguously identify the interpolated parts by convention, that would seem too fragile and likely to be broken in a future release. (E.g., you could make that API unambiguous by always wrapping each interpolated expression in `EParenthesis`, and as a workaround now I can just make my macro require the developer (me!) to just put all interpolated expressions in parenthesis too for now).

I am thinking of something like [JavaScript’s Tagged Template Literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals) which, unfortunately, operates at runtime but at least gives you a list of uninterpolated parts and interpolated parts:

```javascript
const t = function (uninterpolated) { console.log({uninterpolated: uninterpolated, interpolated: [].slice.call(arguments, 1),}) }
t`asdf${''}${'bsdf'}` // { uninterpolated: [ 'asdf', '', '' ], interpolated: [ '', 'bsdf' ] }
t`asdf${''}bsdf` // { uninterpolated: [ 'asdf', 'bsdf' ], interpolated: [ '' ] }
```

C#’s [interpolated string support is diferent](https://gist.github.com/binki/eadb3986c19dfea1e6369f698b60b212), but it is sufficient to unambiguously distinguish the interpolated and uninterpolated parts of the string.

Could a new macro-time API be added which keeps uninterpolated and interpolated parts of interpolated strings separate? E.g.:

```haxe
class MacroStringTools {
typedef InterpolatedStringExpr = {
/**
The uninterpolated parts of the string. Note two adjacent interpolations
will have an empty uninterpolated string between them and an interpolation
at the beginning or end will have an empty string before or after it respectively.
**/
strings:Array,
/**
The interpolated expressions. The length of this array is one less than the length
of `strings`.
**/
expressions:Array,
};
static formatStringUnambiguous(s:String, p:Position):InterpolatedStringExpr;
}
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.