DIP78

From D Wiki
Revision as of 20:24, 26 May 2015 by Kagamin (talk | contribs)
Jump to: navigation, search
Title: AST Macros Lite
DIP: 78
Version: 1
Status: Draft
Created: 2015-05-26
Last Modified: 2015-05-26
Links: NG DiscussionDIP50

Abstract

Proposal for a macro system without syntactical extensions to the language. Hence it doesn't allow arbitrary syntax.

Rationale

Like in DIP50, macros are implemented as almost normal functions executed at compile time. Syntax tree is handled as plain data in imperative style. The only difference from normal functions is how their arguments are prepared and how their return values are handled at the point of invocation, function itself runs as usual on current CTFE engine.

Description

Macro functions integrate with the compiler using a new special type, say, core.macros.Auto.

module core.macros;

// well, declare it in any way which makes the compiler happy
alias Auto = __auto_ast_converter__;

// or
struct Auto
{
	Node node;
	// converts to and from ast node automatically
	alias node this;
}

/// Root for ast nodes type hierarchy
class Node
{
}

This type makes the compiler convert expressions into a graph of objects from core.macros module to pass it to the macro function as arguments. It also makes the returned ast to automatically mix into code at the point of invocation.

import core.macros;

Auto myAssert(Auto condition, Auto message)
{
	// condition and message are automatically converted to Node
	return
		new IfStatement(
			new NotExpression(condition),
			new CallStatement(new Identifier("fail"), message));
}

void f()
{
	myAssert(a==b, "test");
}

// is translated to
void f()
{
	if(!(a==b))fail("test");
}

When the function being invoked accepts a parameter of type core.macros.Auto automatic conversion of expressions happens roughly as follows:

void f()
{
	mixin(myAssert(parse(`a==b`), parse(`"test"`)).toString());
}

This can be optimized by the compiler, say, by having a macro component before CTFE engine, which would prepare a deep copy of syntax tree for function arguments from the compiler internal ast presentation and/or convert the return value back.

Other than handling the core.macros.Auto type at the call site, macro functions have no other special treatment. Parameters and return value can be typed core.macros.Auto independently:

// essentially stringifies passed identifier
string identifierName(Auto id)
{
	// cast is ok because happens at compile time
	return (cast(IdentifierExpression)id).name;
}

// use:
enum string name = identifierName(myid);
assert(name=="myid");


// mixes the passed ast into code
Auto mix(Node node)
{
	return node;
}

// use:
//another function builds the tree as plain data
enum Node node = myBuildTree();
mix(node); //mix the result

Examples of ast hierarchies: System.Linq.Expressions in .Net, macros module in Nim.

Future directions

Attribute macro receives the declaration it's applied to as the last parameter:

@attributeMacro
Auto test1(Auto decl)
{
	return handle(decl);
}

//use:
@test1 int myvar;

Statement macro is similar:

@statementMacro
Auto myforeach(Auto i, Auto c, Auto statement)
{
	return handle(i,c,statement);
}

//use (currently accepted as valid syntax):
	myforeach(i,e in a) = {
		int b;
		c=0;
	};

//or use attribute syntax? (currently rejected as invalid syntax)
	@myforeach(i,e in a)
	{
		int b;
		c=0;
	}

Copyright

This document has been placed in the Public Domain.