module stri; import std.format : format; import std.algorithm : map; auto extractIdentifiers(string s) { import std.string; import std.algorithm : find; import std.typecons; string fmt = s; string[] ids; auto subs = s.find("${"); // "${def}gh${i}..." while (!subs.empty) { auto ends = subs.find("}"); // "}gh${i}..." if (ends.empty) { // TODO assert here break; } auto id = subs[2..$-ends.length]; // "def" ids ~= id; // [..., "def"] auto quote = subs[0..$+1-ends.length]; // "${def}" fmt = fmt.replace(quote, "%s"); subs = ends.find("${"); // "${i}..." } return tuple!("ids", "fmt")(ids, fmt); } mixin template s(string sfmt) { enum _ret = extractIdentifiers(sfmt); mixin(format!`immutable str = format!"%s"(%-(%s, %));`(_ret.fmt, _ret.ids)); } unittest { import std.stdio; auto a = 1; enum _a0 = "D-lang"; mixin s!"${a} is one. ${_a0} is nice. ${a}" i; assert(i.str == "1 is one. D-lang is nice. 1"); writeln(i.str); }