It's A Bit Too Early To Declare Victory... BUT...
...Prepare To Get Excited!!!
I have a system booting...that can run UPARSE and do HTTPS requests (so it's non-trivially booting).
...AND it can do this:
internals: func [a <local> b] [
b: "internal-B"
let c: "internal-C"
print interpolate "$(a) $(b) $(c)"
]
>> internals "argument-A"
argument-A internal-B internal-C
It can also do this:
externals: func [str a <local> b] [
b: "internal-B"
let c: "internal-C"
print interpolate str
]
>> a: "global-A"
>> b: "global-B"
>> c: "global-C"
>> externals "$(a) $(b) $(c)" "argument-A"
global-A global-B global-C
This demonstrates the requested feature...for strings to capture a kind of "binding environment" and carry it along with them (much like traditional WORD!s would have a binding that would "stick" to them).
The INTERPOLATE is Mostly Usermode
There's a little bit of UPARSE code to break up the string:
breaker: func [return: [block!] text [text!]] [
let capturing
let inner
return uparse text [collect [while [
not <end>
(capturing: false)
keep opt between <here> ["$(" (capturing: true) | <end>]
:(if capturing '[
inner: between <here> ")"
keep (as word! inner)
])
]]]
]
It gives you a block of WORD!s and TEXT! bits:
>> breaker "abc$(def)ghi"
== ["abc" def "ghi"]
Then the INTERPOLATE function relies on a new weird native called GET-IN-STRING:
interpolate: lambda [text [text!]] [
unspaced map-each item (breaker text) [
if text? item [
item
] else [
get-in-string text (ensure word! item)
]
]
]
GET-IN-STRING takes a WORD! and an ANY-STRING! and it will look in the string--as if it were a context of some kind.
(I could have made IN accept TEXT! as a context, so you could write get in text item, but this is all very speculative so I kept it separate. But it would presumably become something like that.)
The Implications Are Pretty Profound
What's kind of astonishing about the above is how a powerful feature like string interpolation is being constructed in userspace. Very few languages put you on the same level as the language designers, to add new features of this type.
You can imagine powerful variations like what CSCAPE does. If you use $() then it assumes you want to repeat the line several times, with the last repetition not repeating whatever comes after it (good for comma lists):
block: [one two three]
cscape {
enum {
$(block),
};
}
== {
enum {
one,
two,
three
};
}
You can thank CSCAPE for why I've not been willing to compromise on going after this feature.
Should REWORD Be Our "INTERPOLATE"?
I like REWORD as a shorter name than INTERPOLATE. (Most languages don't name it explicitly...because interpolation is a built in feature of strings...or they call it format() or fmt(). The humorous language LOLCODE calls it "SMOOSH" )
REWORD has a historical quirk that it doesn't require terminating the substitutions. It's only able to do this because you've given it the explicit list of the substitutions you're interested in:
>> reword "$abcdef" [abc "123"]
== "123def" ; knew you weren't looking for $abcd, $abcde, $abcdef...
I don't know how interesting that "feature" is. :-/ But beyond having it enclosed in delimiters, it seems having the option to put spaces around things is nice to have by default:
>> abc: 123
>> reword "foo$(abc)bar"
== "foo123bar"
>> reword "foo $abc bar"
== "foo 123 bar"
If you want to parameterize the REWORD, you would thus do it by means of manipulating the binding of the string:
>> abc: 123
>> def: 456
>> reword (bind "foo $abc bar $def" [def: 789])
== "foo 123 bar 789"
...or at least that's one idea. There are plenty of details to work out.
So How Real Is The Implementation?
Fairly Kind of sort of real. It takes some liberties, with the hopes that those things can be pinned down better as things go on.
Please bear in mind that this didn't come out of nowhere. I didn't just write this in a week. The foundations that make this possible have been evolving and trying to form a richer basis for binding... ultimately what I have called "virtual binding".
But after many days of hacking through getting a booting system under fairly new binding rules...I tried the INTERPOLATE immediately. Because I was tired of filtering through crashes.
So that means that now--with the proof of concept going--there are thousands of tests to filter through to see what all breaks.
Performance isn't bad enough to be unusable. It seems all right. I don't want to look too much into it until things are further.
I'll know more in a bit. But it's very promising...and there will almost certainly be other features that can be built from the new foundations.