Skip to content

Instantly share code, notes, and snippets.

@dymk
Last active December 17, 2015 09:38
Show Gist options
  • Save dymk/bbf6c87e92d5aca3c737 to your computer and use it in GitHub Desktop.
Save dymk/bbf6c87e92d5aca3c737 to your computer and use it in GitHub Desktop.
import
std.stdio,
std.string,
std.array,
std.range,
std.algorithm;
uint leftIndexOfAny(Char1, Char2)(const(Char1)[] s, const(Char2)[][] subs) {
auto indexes_of = map!((a) { return s.countUntil(a); })(subs);
auto min_index = -1U;
foreach(index_of; indexes_of) {
if(index_of != -1) {
if(min_index == -1) {
min_index = index_of;
} else {
min_index = min(min_index, index_of);
}
}
}
return min_index;
}
unittest {
auto a = "1, 2, 3, 4";
assert(a.leftIndexOfAny(["1", "2"]) == 0);
assert(a.leftIndexOfAny(["4", "2"]) == 3);
assert(a.leftIndexOfAny(["5", "1"]) == 0);
assert(a.leftIndexOfAny(["5", "6"]) == -1);
}
string EmbeddedDFunc(Context = void)(string template_string) {
enum OPEN_DELIM = "<%";
enum OPEN_DELIM_STR = "<%=";
enum CLOSE_DELIM = "%>";
auto function_body = "";
auto indent_level = 0;
void push_line(string[] stmts...) {
foreach(i; 0..indent_level) {
function_body ~= '\t';
}
foreach(stmt; stmts) {
function_body ~= stmt;
}
function_body ~= '\n';
}
void indent() { indent_level++; }
void outdent() { indent_level--; }
//generates something like
/+
(Ctx __context) {
alias __context.a a;
auto d_code = "";
//generated code
return d_code;
}+/
const bool context_given = !is(Context == void);
static if(!context_given) {
push_line("() {");
} else {
enum ContextType = __traits(identifier, Context);
push_line("(", ContextType, " __context) {");
}
indent();
push_line("import std.conv;");
push_line("import std.array;");
push_line("auto __buff = appender!string();");
//generate local bindings to context fields
if(context_given) {
push_line("with(__context) {");
indent();
}
while(!template_string.empty) {
auto open = template_string.leftIndexOfAny([OPEN_DELIM, OPEN_DELIM_STR]);
if(open != -1) {
//pragma(msg, "Found open delimer @" ~ open ~ " in '" ~ template_string ~ "': " ~ template_string[open..$]);
if(template_string[0..open].length) {
//Append everything before the open delimer onto the string
push_line(`__buff.put("` ~ template_string[0..open] ~ `");`);
}
//find the next close delimer
auto close = template_string[open..$].countUntil(CLOSE_DELIM);
assert(close != -1, "Missing close delimer '" ~ CLOSE_DELIM ~ "'. (" ~ template_string ~ ")");
close += open; //add index position lost by slicing from 0..open
string delim_type;
if(template_string[open..open+OPEN_DELIM_STR.length] == OPEN_DELIM_STR) {
delim_type = OPEN_DELIM_STR;
} else if(template_string[open..open+OPEN_DELIM.length] == OPEN_DELIM) {
delim_type = OPEN_DELIM;
} else {
assert(false, "Unknown delimer at " ~ template_string[open..open+5]);
}
auto inbetween_delims = template_string[open+delim_type.length ..close];
if(delim_type == OPEN_DELIM_STR) {
//Was an evaluate + output string delimer
push_line(`__buff.put(to!string((` ~ inbetween_delims ~ `)));`);
} else {
//Was an evaluate delimer
push_line(inbetween_delims);
}
template_string = template_string[close + CLOSE_DELIM.length .. $];
}
else {
//no more open delimers, append rest to buffer
push_line(`__buff.put("` ~ template_string[0..$] ~ `");`);
break;
}
}
if(context_given) {
outdent();
push_line("}");
}
push_line("return __buff.data();");
outdent();
push_line("}");
return function_body;
}
unittest {
//Test delimer parsing
const render = mixin(EmbeddedDFunc("<% if(true) { %>foo<% } %>"));
static assert(render() == "foo");
}
unittest {
//Test to!string of eval delimers
const render = mixin(EmbeddedDFunc(`<%= "foo" %>`));
static assert(render() == "foo");
}
unittest {
//Test raw text with no delimers
const render = mixin(EmbeddedDFunc(`foo`));
static assert(render() == "foo");
}
unittest {
//Assert that it's invalid if no context is used
static assert(!__traits(compiles, mixin(EmbeddedDFunc(`<%= test %>`))));
}
unittest {
//Assert that it's invalid if invalid context fields are used
struct Ctx {
string foo;
}
static assert(!__traits(compiles, mixin(EmbeddedDFunc(`<%= bar %>`))));
}
unittest {
//Test static context fields
struct Ctx {
static string test = "static value";
}
const render = mixin(EmbeddedDFunc!Ctx(`<%= test %>`));
assert(render(Ctx()) == "static value");
}
unittest {
//Test member context fields
struct Ctx {
string test;
}
const render = mixin(EmbeddedDFunc!Ctx(`<%= test %>`));
Ctx ctx = {test: "member value"};
assert(render(ctx) == "member value");
}
unittest {
//Test looping
const templ = `<% foreach(i; 0..3) { %>foo<% } %>`;
const render = mixin(EmbeddedDFunc(templ));
static assert(render() == "foofoofoo");
}
unittest {
//Test looping
const templ = `<% foreach(i; 0..3) { %><%= i %><% } %>`;
const render = mixin(EmbeddedDFunc(templ));
static assert(render() == "012");
}
unittest {
//Test method calling & context state on contexts
struct Ctx {
int foo() {
return n_foo++;
}
int n_foo;
}
const templ = `<% foreach(i; 0..3) { %><%= foo() %><% } %>`;
const render = mixin(EmbeddedDFunc!Ctx(templ));
assert(render(Ctx()) == "012");
}
void main() {
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment