// Code written by Reiner Pope. import std.stdio; import std.metastrings; void main() { auto myAA = AAInit!(int, char[], ` "11" => 11; // Comment /* Another comment */ "fruit /+ this isn't a comment+/ " => 42; "apple" => -1; `); foreach (key, value; myAA) { writefln("[%s => %s]", key, value); } } /** Wraps a pretty initializer for inbuilt AAs in D. Format of initializer: key => value; // Comments allowed /+ (of all kinds) +/ */ T[K] AAInit(T, K, char[] code)() { T[K] aa; mixin(ParseAAInit!(code).Code); return aa; } private template ParseAAInit(char[] code) { const int MiddleStartIndex = BracketedIndexOf!("=>", code).Value; const int EndIndex = BracketedIndexOf!(";", code).Value; static assert(EndIndex >= MiddleStartIndex, `Error in syntax: ';' must occur after '=>'`); static if (MiddleStartIndex == -1 || EndIndex == -1) const char[] ThisCode = ""; else const char[] ThisCode = "aa[" ~ code[0 .. MiddleStartIndex] ~ "] = " ~ code[MiddleStartIndex + 2 .. EndIndex] ~ ";"; static if (ThisCode == "") const char[] Code = ""; else const char[] Code = ThisCode ~ "\n" ~ ParseAAInit!(code[EndIndex + 1 .. $]).Code; } private enum QuoteStatus { None, RDoubleQuote, DoubleQuote, TickQuote, SingleQuote, SlashStarComment, SlashSlashComment } private template BracketedIndexOf(char[] needle, char[] haystack, int nestedcommentlevel = 0, int bracketcount = 0, QuoteStatus status = QuoteStatus.None) { static if (haystack.length == 0) const int Value = -1; else static if (nestedcommentlevel == 0 && bracketcount == 0 && status == QuoteStatus.None && StartsWith!(haystack, needle)) const int Value = 0; else { static assert(nestedcommentlevel > -1 && bracketcount > -1, "Syntax error: have a close nest before an open nest"); static assert(nestedcommentlevel == 0 || status == QuoteStatus.None, "Error: can't both be in a quote and a comment at once"); static if (status == QuoteStatus.None && StartsWith!(haystack, "/+")) const int Value = Recurse!(2, needle, haystack, nestedcommentlevel + 1, bracketcount, status); else static if (nestedcommentlevel > 0 && StartsWith!(haystack, "+/")) const int Value = Recurse!(2, needle, haystack, nestedcommentlevel - 1, bracketcount, status); else static if (status == QuoteStatus.None && nestedcommentlevel == 0) // We're in plain code { // Escape plain literals like \" floating around the place (outside quotes) static if (StartsWith!(haystack, `\`)) const int Value = Recurse!(2, needle, haystack, nestedcommentlevel, bracketcount, status); else static if (StartsWith!(haystack, `r"`)) const int Value = Recurse!(2, needle, haystack, nestedcommentlevel, bracketcount, QuoteStatus.RDoubleQuote); else static if (StartsWith!(haystack, `"`)) const int Value = Recurse!(1, needle, haystack, nestedcommentlevel, bracketcount, QuoteStatus.DoubleQuote); else static if (StartsWith!(haystack, "`")) const int Value = Recurse!(1, needle, haystack, nestedcommentlevel, bracketcount, QuoteStatus.TickQuote); else static if (StartsWith!(haystack, "'")) const int Value = Recurse!(1, needle, haystack, nestedcommentlevel, bracketcount, QuoteStatus.SingleQuote); else static if (StartsWith!(haystack, "/*")) const int Value = Recurse!(2, needle, haystack, nestedcommentlevel, bracketcount, QuoteStatus.SlashStarComment); else static if (StartsWith!(haystack, "//")) const int Value = Recurse!(2, needle, haystack, nestedcommentlevel, bracketcount, QuoteStatus.SlashSlashComment); // Now we do all the brackets: else static if (StartsWith!(haystack, "{") || StartsWith!(haystack, "(") || StartsWith!(haystack, "[")) const int Value = Recurse!(1, needle, haystack, nestedcommentlevel, bracketcount + 1, status); else static if (StartsWith!(haystack, "}") || StartsWith!(haystack, ")") || StartsWith!(haystack, "]")) const int Value = Recurse!(1, needle, haystack, nestedcommentlevel, bracketcount - 1, status); else const int Value = Recurse!(1, needle, haystack, nestedcommentlevel, bracketcount, status); } else static if (nestedcommentlevel == 0 && status != QuoteStatus.None) { static if ((status == QuoteStatus.DoubleQuote || status == QuoteStatus.SingleQuote) && StartsWith!(haystack, `\`)) const int Value = Recurse!(2, needle, haystack, nestedcommentlevel, bracketcount, status); else static if (status == QuoteStatus.RDoubleQuote && StartsWith!(haystack, `"`)) const int Value = Recurse!(1, needle, haystack, nestedcommentlevel, bracketcount, QuoteStatus.None); else static if (status == QuoteStatus.DoubleQuote && StartsWith!(haystack, `"`)) const int Value = Recurse!(1, needle, haystack, nestedcommentlevel, bracketcount, QuoteStatus.None); else static if (status == QuoteStatus.TickQuote && StartsWith!(haystack, "`")) const int Value = Recurse!(1, needle, haystack, nestedcommentlevel, bracketcount, QuoteStatus.None); else static if (status == QuoteStatus.SingleQuote && StartsWith!(haystack, `'`)) const int Value = Recurse!(1, needle, haystack, nestedcommentlevel, bracketcount, QuoteStatus.None); else static if (status == QuoteStatus.SlashStarComment && StartsWith!(haystack, "*/")) const int Value = Recurse!(2, needle, haystack, nestedcommentlevel, bracketcount, QuoteStatus.None); else static if (status == QuoteStatus.SlashSlashComment && (StartsWith!(haystack, "\r") || StartsWith!(haystack, "\n"))) const int Value = Recurse!(1, needle, haystack, nestedcommentlevel, bracketcount, QuoteStatus.None); else const int Value = Recurse!(1, needle, haystack, nestedcommentlevel, bracketcount, status); } else const int Value = Recurse!(1, needle, haystack, nestedcommentlevel, bracketcount, status); } } private template StartsWith(char[] haystack, char[] needle) { const bool StartsWith = (haystack.length >= needle.length && haystack[0..needle.length] == needle); } private template Recurse(uint Length, char[] needle, char[] haystack, int nestedcommentlevel = 0, int bracketcount = 0, QuoteStatus status = QuoteStatus.None) { static if (BracketedIndexOf!(needle, haystack[Length..$], nestedcommentlevel, bracketcount, status).Value == -1) const int Recurse = -1; else const int Recurse = BracketedIndexOf!(needle, haystack[Length..$], nestedcommentlevel, bracketcount, status).Value + Length; }