Files
tink_macro/src/tink/macro/Printer.hx
2013-06-26 21:59:22 +02:00

247 lines
8.3 KiB
Haxe

package tink.macro.tools;
import haxe.macro.Context;
import haxe.macro.Expr;
using Lambda;
using tink.macro.tools.ExprTools;
class Printer {
static var binops = '+,*,/,-,=,==,!=,>,>=,<,<=,&,|,^,&&,||,<<,>>,>>>,%,NONE,...,=>'.split(',');
static var unops = '++,--,!,-,~'.split(',');
static public function binoperator(b:Binop) {
return
switch (b) {
case OpAssignOp(op):
binoperator(op) + '=';
default:
binops[Type.enumIndex(b)];
}
}
static public function unoperator(u:Unop)
return unops[Type.enumIndex(u)];
static public function binop(?indent:String = '', b:Binop, e1:Expr, e2:Expr):String {
function rec(e)
return printExpr(indent, e);
return '(' + rec(e1) + ' ' + binoperator(b) + ' ' + rec(e2) +')';
}
static public function printExprList(indent:String, list:Iterable<Expr>, ?sep = ', ', ?border:Array<String>):String {
return printList(list.map(printExpr.bind(indent)), sep, border);
}
static public function printList(list:Iterable<String>, ?sep = ', ', ?border:Array<String>) {
if (border == null) border = '()'.split('');
return border[0] + list.list().join(sep) + border[1];
}
static public function printPath(indent:String, p:TypePath) {
var a = p.pack.copy();
a.push(p.name);
if (p.sub != null) a.push(p.sub);
var ret = a.join('.');
if (p.params.length > 0) {
var a = [];
for (p in p.params)
a.push(printParam(indent, p));
ret += '<' + a.join(', ') + '>';
}
return ret;
}
static public function printParam(indent:String, t:TypeParam) {
return
switch (t) {
case TPType(t): printType(indent, t);
case TPExpr(e): printExpr(indent, e);
}
}
static public function printType(indent:String, t:ComplexType) {
return
switch (t) {
case TOptional(t): '?' + printType(indent, t);
case TPath(p):
printPath(indent, p);
case TFunction(args, ret):
if (args.length == 0) 'Void -> ' + printType(indent, ret);
else args.concat([ret]).map(printType.bind(indent)).array().join(' -> ');
case TAnonymous(fields):
printFields(indent, fields);
case TParent(t):
'(' + printType(indent, t) + ')';
case TExtend(p, fields):
printFields(indent, fields, p);
}
}
static public function printFields(indent:String, fields:Array<Field>, ?extend:TypePath) {
var ret = '{ ';
if (extend != null) ret += '> ' + printPath(indent, extend) + '; ';
var a = [];
for (field in fields)
a.push(printField('\t' + indent, field));
if (a.length > 0)
ret += '\n\t' + indent + a.join(';\n\t' + indent) + ';\n' + indent;
return ret +'}';
}
static public function printFunction(f:Function, ?name:String, ?indent = '') {
function rec(e)
return printExpr(indent, e);
var ret = 'function ';
if (name != null) ret += name;
var args = [];
for (arg in f.args) {
var s = if (arg.opt) '?' else '';
s += arg.name;
s += typify(indent, arg.type);
if (arg.value != null) s += ' = ' + rec(arg.value);
args.push(s);
}
var params = [];
for (p in f.params) {
var constraints = [];
for (c in p.constraints)
constraints.push(printType(indent, c));
var ret = p.name;
ret +=
switch (constraints.length) {
case 0: '';
case 1: ':' + constraints[0];
default: ':(' + constraints.join(', ') + ')';
}
params.push(ret);
}
if (params.length > 0)
ret += '<' + params.join(', ') + '>';
ret += '(' + args.join(', ') + ')';
if (f.ret != null)
ret += ':' + printType(indent, f.ret);
if (f.expr != null)
ret += ' ' + rec(f.expr);
return ret;
}
static public function printInitializer(indent:String, e:Expr) {
return
if (e == null) '';
else ' = ' + printExpr(indent, e);
}
static function typify(indent:String, t:ComplexType) {
return
if (t == null) '';
else ':' + printType(indent, t);
}
static public function printField(indent:String, field:Field) {
var ret = '';
if (field.access != null)
for (a in field.access)
ret += Type.enumConstructor(a).substr(1).toLowerCase() + ' ';
ret +=
switch (field.kind) {
case FVar(t, e): 'var ' + field.name + typify(indent, t) + printInitializer(indent, e);
case FProp(get, set, t, e): 'var ' + field.name + '(' + get + ', ' + set + ')' + typify(indent, t) + printInitializer(indent, e);
case FFun(f): printFunction(f, field.name, indent);
}
return ret;
}
static public function print(e:Expr):String {
return printExpr('', e);
}
static public function printExpr(indent:String, e:Expr):String {
function rec(e)
return printExpr(indent, e);
return
if (e == null) '#NULL';
else if (e.expr == null) '#MALFORMED';
else
switch (e.expr) {
case EConst(c):
switch (c) {
case CInt(s), CFloat(s), CIdent(s): s;
case CString(s): '"' + s + '"';
case CRegexp(r, opt): '~/' + r + '/' + opt;
}
case EVars(vars):
var ret = [];
for (v in vars)
ret.push(v.name + typify(indent, v.type) + printInitializer(indent, v.expr));
'var ' + ret.join(', ');
case ECheckType(e, t): '(' + rec(e) + '/* ' + typify(indent, t) + ' */)';
case ECast(e, t):
'cast(' + rec(e) + ((t == null) ? '' : ', ' + printType(indent, t)) + ')';
case EArray(e1, e2):
rec(e1) + '[' + rec(e2) + ']';
case EField(e, field):
rec(e) + '.' + field;
case EParenthesis(e):
'(' + rec(e) + ')';
case ECall(e, params):
rec(e) + printExprList(indent, params);
case EIf(econd, eif, eelse):
'if (' + rec(econd) + ') ' + rec(eif) +
if (eelse == null)
''
else
('\n' + indent + 'else ' + rec(eelse));
case EBlock(exprs):
if (exprs.length == 0)
'{}';
else
printExprList(indent + '\t', exprs, ';\n\t' + indent, ['{\n\t' + indent, ';\n' + indent + '}']);
case EIn(e1, e2):
rec(e1) + ' in ' + rec(e2);
case EFor(it, expr):
'for (' + rec(it) + ')\n\t' + indent + printExpr(indent + '\t', expr);
case EWhile(econd, e, normalWhile):
if (normalWhile)
'while (' + rec(econd) + ') ' + rec(e);
else
'do ' + rec(e) + '\n' + indent + 'while (' + rec(econd) + ')';
case EBreak: 'break';
case EContinue: 'continue';
case EUntyped(e): '(untyped ' + rec(e) + ')';
case EThrow(e): 'throw ' + rec(e);
case EReturn(e): 'return' + if (e == null) '' else (' ' + rec(e));
case EDisplay(e, isCall):
rec(e) + (isCall ? '(/*DISPLAY*/)' : '/*.DISPLAY*/');
case EDisplayNew(t):
'new ' + printPath(indent, t) + '(/*DISPLAY*/)';
case ETernary(econd, eif, eelse):
'((' + rec(econd) + ') ? ' + rec(eif) + ' : ' + rec(eelse) + ')';
case ESwitch(e, cases, edef):
var ret = [];
for (c in cases)
ret.push(
'case ' + printExprList(indent, c.values, ['', ''])
+ (if (c.guard == null) '' else ' if '+rec(c.guard))
+ ': ' + printExpr(indent + '\t', c.expr));
if (edef != null)
ret.push('default: ' + rec(edef));
'switch (' + rec(e) + ') {\n\t' + indent + ret.join('\n\t' + indent) + '\n' + indent + '}';
case EUnop(op, postFix, e):
var op = unops[Type.enumIndex(op)];
var e = rec(e);
var inner =
if (postFix)
e + op;
else
op + e;
'(' + inner + ')';
case ETry(e, catches):
var ret = [];
for (c in catches)
ret.push('catch (' + c.name +':' + printType(indent, c.type) + ') ' + rec(c.expr));
'try ' + rec(e) + '\n' + indent + ret.join('\n' + indent);
case ENew(t, params):
'new ' + printPath(indent, t) + printExprList(indent, params);
case EBinop(op, e1, e2):
binop(indent, op, e1, e2);
case EArrayDecl(values):
printExprList(indent, values, '[]'.split(''));
case EObjectDecl(fields):
var ret = [];
for (field in fields)
ret.push(field.field + ' : ' + rec(field.expr));
printList(ret, '{}'.split(''));
case EFunction(name, f):
printFunction(f, name, indent);
case EMeta(s, e):
'@' + s.name + printExprList(indent, s.params) + ' ' + rec(e);
//default: '#UNSUPPORTED_' + Type.enumConstructor(e.expr);
}
}
}