www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Code Construction

Heres a module I just started working on, completely 
incompletely, but demonstrates an ideas that might be very useful 
in D: Code Construction.

The idea is very simple: We have code strings like "class 
%%name%% { }"

and %%name%% is replaced with the name of a type T.

The idea is that we can map a type to a code string and have the 
type "fill in the blanks" rather than having to do it all in D, 
which is far more verbose and terse, we can do it using a 
different method.

Eventually the idea is that we can take any type T, map it in to 
a code string, then map that string to a new type that relates to 
T.

What I use this for is to simplify generating related members.

	enum eState
		{
			Ready,
			Starting,
			Running,
			Pausing,
			Paused,
			Unpausing,
			Stopping,	
			Stopped,
			Finished,
		}


Can be used to generate new stuff:

		mixin(sCodeConstruction.MemberMap!("\t\tsMultiCallback!callbackSignature
Callback_%%name%%;\n", eState));

Generates the following code:

		sMultiCallback!callbackSignature Callback_Ready;
		sMultiCallback!callbackSignature Callback_Starting;
		sMultiCallback!callbackSignature Callback_Running;
		sMultiCallback!callbackSignature Callback_Pausing;
		sMultiCallback!callbackSignature Callback_Paused;
		sMultiCallback!callbackSignature Callback_Unpausing;
		sMultiCallback!callbackSignature Callback_Stopping;
		sMultiCallback!callbackSignature Callback_Stopped;
		sMultiCallback!callbackSignature Callback_Finished;


and if the enum changes, one does not have to regenerate the 
callbacks. (Obviously this is a simple case and D can handle this 
reasonably easily, but the more complex cases are far more 
verbose than what can be done by substitutions)

Using a substitution grammar is a lot easier IMO and D needs 
something like. Unfortunately, I won't be finishing it anytime 
soon so I thought I'd mention the idea and maybe someone else 
could tackle it.



module mCodeConstruction;
import std.meta, std.traits, std.string, std.algorithm, 
std.array, std.conv;



/*
     Code construction that simplifies common tasks.	
*/

struct sCodeConstruction
{
		//[ For examples, let X in myModule.myClass.X represents the 
member `int X = 4;` and Y in myModule.myClass.Y be `bool Y(double 
q)`]
	public static:
	enum Tokens : string
	{
		Name				= "%%name%%",					// The member name (X => "X", Y => 
"Y")
		FullName			= "%%fullName%%",				// The full member name (X => 
"myClass.X", Y => "myClass.Y")
		CompleteName		= "%%completeName%%",			// The complete member 
name (X => "myModule.myClass.X", Y => "myModule.myClass.Y")
		Value				= "%%value%%",					// The default value of the member 
(X => 4, Y => "")
		Type				= "%%type%%",					// The type of the member (X => int, 
Y => bool delegate(double))
		Module				= "%%module%%",					// The module the member is in (X 
=> "myModule", Y => "myModule")
		ParentType			= "%%parentType%%",				// The type of the parent 
(X => "class", Y => "class")
		MethodReturn		= "%%method/return%%",			// The return type of a 
member if it has one (X => "", Y => "bool")
		MethodParamNName	= "%%methodParam/N/Name%%",		// The Nth 
parameter's name (Y => "q")
		MethodParamNType	= "%%methodParam/N/type%%",		// The Nth 
parameter's type (Y => "double")
		DoublePercent		= "%%%",						// the %% symbol
	}


	/*
		A Resolver is a function takes a grammar symbol and returns the 
corresponding element for it from T.
		The following list of resolvers are provided for common use:

		MemberResolver: Resolves member information: 
MemeberResolver("%%name%%", myClass.X) = "X".
	*/
	auto MemberResolver(string S, alias T)()
	{
		mixin("import "~moduleName!(T)~";");
		mixin("alias val = " ~ fullyQualifiedName!(T) ~ ";");
		
		switch(S)
		{
			case Tokens.Name : return to!string( __traits(identifier, T));

		}
		
		//pragma(msg, name);
		static if (is(typeof(T) == function) || is(typeof(*T) == 
function))
		{
		
			// Get the parameters in a (w)string array.
			enum p = split(Parameters!(member).stringof[1..$-1], 
",").map!(n => strip(n));
			Tuple!(S,S)[] params;
			foreach(a; aliasSeqOf!(p))
			{
			}
		}	

		return "";
	}

	auto StandardResolver(string S, alias T)()
	{		
		return MemberResolver!(S, T)();
	}


	/*
		Reverses a type or declaration in to it's meta symbols. E.g., 
`int foo(int x)` becomes "%%method/return%% 
%%name%%(%%method/0/type%% %%method/0/name%%).
	*/
	auto ReverseGenerate(alias T)()
	{

	}




	/*
		Substitutes the values in T for the grammar symbols in S using 
the resolver R. That is, the meta tokens in S are replaced by 
their corresponding information from T using the resolver R.
	*/
	string GrammarSub(string S, alias T, R = void)()
	{
		string s = S;
		static if (is(R == void)) { alias R = StandardResolver!(S, T); }
		
		// Iterate over the grammar(token symbols)
		foreach(tokenName; __traits(allMembers, Tokens))
		{
			mixin("alias tokenSymbol = " ~ fullyQualifiedName!(Tokens) ~ 
"." ~ tokenName ~ ";");
			s = s.replace(cast(string)tokenSymbol, 
R!(cast(string)tokenSymbol, T)());	
		}
		return s;
	}





	/*
		Maps each member of T in to a string by substituting the 
symbols in to it. E.g., "Given enum Enum {X,Y,Z}; 
MemberMap!(Enum, "int foo(int %%name%% = %%value%%);")" becomes 
"int foo(int X = 0); int foo(int Y = 1); int foo(int Z = 2); "}
	*/
	string MemberMap(string S, T)()
	{
		string str = "";

		// Import the module containing T so we can resolve symbols 
correctly.
		mixin("import "~moduleName!(T)~";");

		
		// Iterate over each member, applying token mapping
		foreach(memberName; AliasSeq!(__traits(allMembers, T)))
		{
			mixin("alias member = " ~ fullyQualifiedName!(T) ~ "." ~ 
memberName ~ ";");		
			str ~= GrammarSub!(S, member);
		}

		//str ~= replace(S, "%%%", "%%");
		return str;
	}
}
Jul 14 2017