forked from LeenkxTeam/LNXSDK
		
	
		
			
				
	
	
		
			565 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Haxe
		
	
	
	
	
	
			
		
		
	
	
			565 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Haxe
		
	
	
	
	
	
| /*
 | |
|  * Copyright (C)2005-2019 Haxe Foundation
 | |
|  *
 | |
|  * Permission is hereby granted, free of charge, to any person obtaining a
 | |
|  * copy of this software and associated documentation files (the "Software"),
 | |
|  * to deal in the Software without restriction, including without limitation
 | |
|  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 | |
|  * and/or sell copies of the Software, and to permit persons to whom the
 | |
|  * Software is furnished to do so, subject to the following conditions:
 | |
|  *
 | |
|  * The above copyright notice and this permission notice shall be included in
 | |
|  * all copies or substantial portions of the Software.
 | |
|  *
 | |
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | |
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | |
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | |
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | |
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 | |
|  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 | |
|  * DEALINGS IN THE SOFTWARE.
 | |
|  */
 | |
| 
 | |
| package haxe.macro;
 | |
| 
 | |
| import haxe.macro.Expr;
 | |
| 
 | |
| using Lambda;
 | |
| using StringTools;
 | |
| 
 | |
| /**
 | |
| 	This class provides some utility methods to convert elements from the
 | |
| 	macro context to a human-readable String representation.
 | |
|  */
 | |
| class Printer {
 | |
| 	var tabs:String;
 | |
| 	var tabString:String;
 | |
| 
 | |
| 	public function new(?tabString = "\t") {
 | |
| 		tabs = "";
 | |
| 		this.tabString = tabString;
 | |
| 	}
 | |
| 
 | |
| 	public function printUnop(op:Unop)
 | |
| 		return switch (op) {
 | |
| 			case OpIncrement: "++";
 | |
| 			case OpDecrement: "--";
 | |
| 			case OpNot: "!";
 | |
| 			case OpNeg: "-";
 | |
| 			case OpNegBits: "~";
 | |
| 			case OpSpread: "...";
 | |
| 		}
 | |
| 
 | |
| 	public function printBinop(op:Binop)
 | |
| 		return switch (op) {
 | |
| 			case OpAdd: "+";
 | |
| 			case OpMult: "*";
 | |
| 			case OpDiv: "/";
 | |
| 			case OpSub: "-";
 | |
| 			case OpAssign: "=";
 | |
| 			case OpEq: "==";
 | |
| 			case OpNotEq: "!=";
 | |
| 			case OpGt: ">";
 | |
| 			case OpGte: ">=";
 | |
| 			case OpLt: "<";
 | |
| 			case OpLte: "<=";
 | |
| 			case OpAnd: "&";
 | |
| 			case OpOr: "|";
 | |
| 			case OpXor: "^";
 | |
| 			case OpBoolAnd: "&&";
 | |
| 			case OpBoolOr: "||";
 | |
| 			case OpShl: "<<";
 | |
| 			case OpShr: ">>";
 | |
| 			case OpUShr: ">>>";
 | |
| 			case OpMod: "%";
 | |
| 			case OpInterval: "...";
 | |
| 			case OpArrow: "=>";
 | |
| 			case OpIn: "in";
 | |
| 			case OpAssignOp(op):
 | |
| 				printBinop(op) + "=";
 | |
| 		}
 | |
| 
 | |
| 	function escapeString(s:String, delim:String) {
 | |
| 		return delim
 | |
| 			+ s.replace("\n", "\\n")
 | |
| 				.replace("\t", "\\t")
 | |
| 				.replace("\r", "\\r")
 | |
| 				.replace("'", "\\'")
 | |
| 				.replace('"', "\\\"") #if sys .replace("\x00", "\\x00") #end + delim;
 | |
| 	}
 | |
| 
 | |
| 	public function printFormatString(s:String) {
 | |
| 		return escapeString(s, "'");
 | |
| 	}
 | |
| 
 | |
| 	public function printString(s:String) {
 | |
| 		return escapeString(s, '"');
 | |
| 	}
 | |
| 
 | |
| 	public function printConstant(c:Constant)
 | |
| 		return switch (c) {
 | |
| 			case CString(s, SingleQuotes): printFormatString(s);
 | |
| 			case CString(s, _): printString(s);
 | |
| 			case CIdent(s), CInt(s), CFloat(s):
 | |
| 				s;
 | |
| 			case CRegexp(s, opt): '~/$s/$opt';
 | |
| 		}
 | |
| 
 | |
| 	public function printTypeParam(param:TypeParam)
 | |
| 		return switch (param) {
 | |
| 			case TPType(ct): printComplexType(ct);
 | |
| 			case TPExpr(e): printExpr(e);
 | |
| 		}
 | |
| 
 | |
| 	public function printTypePath(tp:TypePath)
 | |
| 		return (tp.pack.length > 0 ? tp.pack.join(".") + "." : "")
 | |
| 			+ tp.name
 | |
| 			+ (tp.sub != null ? '.${tp.sub}' : "")
 | |
| 			+ (tp.params == null ? "" : tp.params.length > 0 ? "<" + tp.params.map(printTypeParam).join(", ") + ">" : "");
 | |
| 
 | |
| 	// TODO: check if this can cause loops
 | |
| 	public function printComplexType(ct:ComplexType)
 | |
| 		return switch (ct) {
 | |
| 			case TPath(tp): printTypePath(tp);
 | |
| 			case TFunction(args, ret):
 | |
| 				var wrapArgumentsInParentheses = switch args {
 | |
| 					// type `:(a:X) -> Y` has args as [TParent(TNamed(...))], i.e `a:X` gets wrapped in `TParent()`. We don't add parentheses to avoid printing `:((a:X)) -> Y`
 | |
| 					case [TParent(t)]: false;
 | |
| 					// this case catches a single argument that's a type-path, so that `X -> Y` prints `X -> Y` not `(X) -> Y`
 | |
| 					case [TPath(_) | TOptional(TPath(_))]: false;
 | |
| 					default: true;
 | |
| 				}
 | |
| 				var argStr = args.map(printComplexType).join(", ");
 | |
| 				(wrapArgumentsInParentheses ? '($argStr)' : argStr) + " -> " + (switch ret {
 | |
| 					// wrap return type in parentheses if it's also a function
 | |
| 					case TFunction(_): '(${printComplexType(ret)})';
 | |
| 					default: (printComplexType(ret): String);
 | |
| 				});
 | |
| 			case TAnonymous(fields): "{ " + [for (f in fields) printField(f) + "; "].join("") + "}";
 | |
| 			case TParent(ct): "(" + printComplexType(ct) + ")";
 | |
| 			case TOptional(ct): "?" + printComplexType(ct);
 | |
| 			case TNamed(n, ct): n + ":" + printComplexType(ct);
 | |
| 			case TExtend(tpl, fields):
 | |
| 				var types = [for (t in tpl) "> " + printTypePath(t) + ", "].join("");
 | |
| 				var fields = [for (f in fields) printField(f) + "; "].join("");
 | |
| 				'{${types}${fields}}';
 | |
| 			case TIntersection(tl): tl.map(printComplexType).join(" & ");
 | |
| 		}
 | |
| 
 | |
| 	public function printMetadata(meta:MetadataEntry)
 | |
| 		return '@${meta.name}' + ((meta.params != null && meta.params.length > 0) ? '(${printExprs(meta.params, ", ")})' : "");
 | |
| 
 | |
| 	public function printAccess(access:Access)
 | |
| 		return switch (access) {
 | |
| 			case AStatic: "static";
 | |
| 			case APublic: "public";
 | |
| 			case APrivate: "private";
 | |
| 			case AOverride: "override";
 | |
| 			case AInline: "inline";
 | |
| 			case ADynamic: "dynamic";
 | |
| 			case AMacro: "macro";
 | |
| 			case AFinal: "final";
 | |
| 			case AExtern: "extern";
 | |
| 			case AAbstract: "abstract";
 | |
| 			case AOverload: "overload";
 | |
| 		}
 | |
| 
 | |
| 	public function printField(field:Field) {
 | |
| 		inline function orderAccess(access: Array<Access>) {
 | |
| 			// final should always be printed last
 | |
| 			// (does not modify input array)
 | |
| 			return access.has(AFinal) ? access.filter(a -> !a.match(AFinal)).concat([AFinal]) : access;
 | |
| 		}
 | |
| 		return (field.doc != null
 | |
| 			&& field.doc != "" ? "/**\n"
 | |
| 				+ tabs
 | |
| 				+ tabString
 | |
| 				+ StringTools.replace(field.doc, "\n", "\n" + tabs + tabString)
 | |
| 				+ "\n"
 | |
| 				+ tabs
 | |
| 				+ "**/\n"
 | |
| 				+ tabs : "")
 | |
| 			+ (field.meta != null && field.meta.length > 0 ? field.meta.map(printMetadata).join('\n$tabs') + '\n$tabs' : "")
 | |
| 			+ (field.access != null && field.access.length > 0 ? orderAccess(field.access).map(printAccess).join(" ") + " " : "")
 | |
| 			+ switch (field.kind) {
 | |
| 				case FVar(t, eo): ((field.access != null && field.access.has(AFinal)) ? '' : 'var ') + '${field.name}' + opt(t, printComplexType, " : ") + opt(eo, printExpr, " = ");
 | |
| 				case FProp(get, set, t, eo): 'var ${field.name}($get, $set)' + opt(t, printComplexType, " : ") + opt(eo, printExpr, " = ");
 | |
| 				case FFun(func): 'function ${field.name}' + printFunction(func);
 | |
| 			}
 | |
| 	}
 | |
| 
 | |
| 	public function printTypeParamDecl(tpd:TypeParamDecl)
 | |
| 		return (tpd.meta != null && tpd.meta.length > 0 ? tpd.meta.map(printMetadata).join(" ") + " " : "")
 | |
| 			+ tpd.name
 | |
| 			+ (tpd.params != null && tpd.params.length > 0 ? "<" + tpd.params.map(printTypeParamDecl).join(", ") + ">" : "")
 | |
| 			+ (tpd.constraints != null && tpd.constraints.length > 0 ? ":(" + tpd.constraints.map(printComplexType).join(", ") + ")" : "");
 | |
| 
 | |
| 	public function printFunctionArg(arg:FunctionArg)
 | |
| 		return (arg.opt ? "?" : "") + arg.name + opt(arg.type, printComplexType, ":") + opt(arg.value, printExpr, " = ");
 | |
| 
 | |
| 	public function printFunction(func:Function, ?kind:FunctionKind) {
 | |
| 		var skipParentheses = switch func.args {
 | |
| 			case [{ type:null }]: kind == FArrow;
 | |
| 			case _: false;
 | |
| 		}
 | |
| 		return (func.params == null ? "" : func.params.length > 0 ? "<" + func.params.map(printTypeParamDecl).join(", ") + ">" : "")
 | |
| 			+ (skipParentheses ? "" : "(")
 | |
| 			+ func.args.map(printFunctionArg).join(", ")
 | |
| 			+ (skipParentheses ? "" : ")")
 | |
| 			+ (kind == FArrow ? " ->" : "")
 | |
| 			+ opt(func.ret, printComplexType, ":")
 | |
| 			+ opt(func.expr, printExpr, " ");
 | |
| 	}
 | |
| 
 | |
| 	public function printVar(v:Var) {
 | |
| 		var s = v.name + opt(v.type, printComplexType, ":") + opt(v.expr, printExpr, " = ");
 | |
| 		return switch v.meta {
 | |
| 			case null|[]: s;
 | |
| 			case meta: meta.map(printMetadata).join(" ") + " " + s;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public function printObjectFieldKey(of:ObjectField) {
 | |
| 		return switch (of.quotes) {
 | |
| 			case null | Unquoted: of.field;
 | |
| 			case Quoted: '"${of.field}"'; // TODO: Have to escape that?
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public function printObjectField(of:ObjectField) {
 | |
| 		return '${printObjectFieldKey(of)} : ${printExpr(of.expr)}';
 | |
| 	}
 | |
| 
 | |
| 	public function printExpr(e:Expr)
 | |
| 		return e == null ? "#NULL" : switch (e.expr) {
 | |
| 			case EConst(c): printConstant(c);
 | |
| 			case EArray(e1, e2): '${printExpr(e1)}[${printExpr(e2)}]';
 | |
| 			case EBinop(op, e1, e2): '${printExpr(e1)} ${printBinop(op)} ${printExpr(e2)}';
 | |
| 			case EField(e1, n): '${printExpr(e1)}.$n';
 | |
| 			case EParenthesis(e1): '(${printExpr(e1)})';
 | |
| 			case EObjectDecl(fl):
 | |
| 				"{ " + fl.map(function(fld) return printObjectField(fld)).join(", ") + " }";
 | |
| 			case EArrayDecl(el): '[${printExprs(el, ", ")}]';
 | |
| 			case ECall(e1, el): '${printExpr(e1)}(${printExprs(el, ", ")})';
 | |
| 			case ENew(tp, el): 'new ${printTypePath(tp)}(${printExprs(el, ", ")})';
 | |
| 			case EUnop(op, true, e1): printExpr(e1) + printUnop(op);
 | |
| 			case EUnop(op, false, e1): printUnop(op) + printExpr(e1);
 | |
| 			case EFunction(FNamed(no,inlined), func): (inlined ? 'inline ' : '') + 'function $no' + printFunction(func);
 | |
| 			case EFunction(kind, func): (kind != FArrow ? "function" : "") + printFunction(func, kind);
 | |
| 			case EVars(vl): "var " + vl.map(printVar).join(", ");
 | |
| 			case EBlock([]): '{ }';
 | |
| 			case EBlock(el):
 | |
| 				var old = tabs;
 | |
| 				tabs += tabString;
 | |
| 				var s = '{\n$tabs' + printExprs(el, ';\n$tabs');
 | |
| 				tabs = old;
 | |
| 				s + ';\n$tabs}';
 | |
| 			case EFor(e1, e2): 'for (${printExpr(e1)}) ${printExpr(e2)}';
 | |
| 			case EIf(econd, eif, null): 'if (${printExpr(econd)}) ${printExpr(eif)}';
 | |
| 			case EIf(econd, eif, eelse): 'if (${printExpr(econd)}) ${printExpr(eif)} else ${printExpr(eelse)}';
 | |
| 			case EWhile(econd, e1, true): 'while (${printExpr(econd)}) ${printExpr(e1)}';
 | |
| 			case EWhile(econd, e1, false): 'do ${printExpr(e1)} while (${printExpr(econd)})';
 | |
| 			case ESwitch(e1, cl, edef):
 | |
| 				var old = tabs;
 | |
| 				tabs += tabString;
 | |
| 				var s = 'switch ${printExpr(e1)} {\n$tabs'
 | |
| 					+ cl.map(function(c) return 'case ${printExprs(c.values, ", ")}' + (c.guard != null ? ' if (${printExpr(c.guard)}):' : ":")
 | |
| 						+ (c.expr != null ? (opt(c.expr, printExpr)) + ";" : ""))
 | |
| 						.join('\n$tabs');
 | |
| 				if (edef != null)
 | |
| 					s += '\n${tabs}default:' + (edef.expr == null ? "" : printExpr(edef) + ";");
 | |
| 				tabs = old;
 | |
| 				s + '\n$tabs}';
 | |
| 			case ETry(e1, cl):
 | |
| 				'try ${printExpr(e1)}' + cl.map(function(c) return ' catch(${c.name}${c.type == null ? '' : (':' + printComplexType(c.type))}) ${printExpr(c.expr)}').join("");
 | |
| 			case EReturn(eo): "return" + opt(eo, printExpr, " ");
 | |
| 			case EBreak: "break";
 | |
| 			case EContinue: "continue";
 | |
| 			case EUntyped(e1): "untyped " + printExpr(e1);
 | |
| 			case EThrow(e1): "throw " + printExpr(e1);
 | |
| 			case ECast(e1, cto) if (cto != null): 'cast(${printExpr(e1)}, ${printComplexType(cto)})';
 | |
| 			case ECast(e1, _): "cast " + printExpr(e1);
 | |
| 			case EIs(e1, ct): '${printExpr(e1)} is ${printComplexType(ct)}';
 | |
| 			case EDisplay(e1, _): '#DISPLAY(${printExpr(e1)})';
 | |
| 			case EDisplayNew(tp): '#DISPLAY(${printTypePath(tp)})';
 | |
| 			case ETernary(econd, eif, eelse): '${printExpr(econd)} ? ${printExpr(eif)} : ${printExpr(eelse)}';
 | |
| 			case ECheckType(e1, ct): '(${printExpr(e1)} : ${printComplexType(ct)})';
 | |
| 			case EMeta({ name:":implicitReturn" }, { expr:EReturn(e1) }): printExpr(e1);
 | |
| 			case EMeta(meta, e1): printMetadata(meta) + " " + printExpr(e1);
 | |
| 		}
 | |
| 
 | |
| 	public function printExprs(el:Array<Expr>, sep:String) {
 | |
| 		return el.map(printExpr).join(sep);
 | |
| 	}
 | |
| 
 | |
| 	function printExtension(tpl:Array<TypePath>, fields:Array<Field>) {
 | |
| 		return '{\n$tabs>'
 | |
| 			+ tpl.map(printTypePath).join(',\n$tabs>')
 | |
| 			+ ","
 | |
| 			+ (fields.length > 0 ? ('\n$tabs' + fields.map(printField).join(';\n$tabs') + ";\n}") : ("\n}"));
 | |
| 	}
 | |
| 
 | |
| 	function printStructure(fields:Array<Field>) {
 | |
| 		return fields.length == 0 ? "{ }" : '{\n$tabs' + fields.map(printField).join(';\n$tabs') + ";\n}";
 | |
| 	}
 | |
| 
 | |
| 	public function printTypeDefinition(t:TypeDefinition, printPackage = true):String {
 | |
| 		var old = tabs;
 | |
| 		tabs = tabString;
 | |
| 
 | |
| 		var str = t == null ? "#NULL" : (printPackage && t.pack.length > 0 && t.pack[0] != "" ? "package " + t.pack.join(".") + ";\n" : "")
 | |
| 			+ (t.doc != null && t.doc != "" ? "/**\n" + tabString + StringTools.replace(t.doc, "\n", "\n" + tabString) + "\n**/\n" : "")
 | |
| 			+ (t.meta != null && t.meta.length > 0 ? t.meta.map(printMetadata).join(" ") + " " : "")
 | |
| 			+ (t.isExtern ? "extern " : "")
 | |
| 			+ switch (t.kind) {
 | |
| 				case TDEnum:
 | |
| 					"enum "
 | |
| 					+ t.name
 | |
| 					+ ((t.params != null && t.params.length > 0) ? "<" + t.params.map(printTypeParamDecl).join(", ") + ">" : "")
 | |
| 					+ " {\n"
 | |
| 					+ [
 | |
| 						for (field in t.fields)
 | |
| 							tabs
 | |
| 							+ (field.doc != null
 | |
| 								&& field.doc != "" ? "/**\n"
 | |
| 									+ tabs
 | |
| 									+ tabString
 | |
| 									+ StringTools.replace(field.doc, "\n", "\n" + tabs + tabString)
 | |
| 									+ "\n"
 | |
| 									+ tabs
 | |
| 									+ "**/\n"
 | |
| 									+ tabs : "")
 | |
| 							+ (field.meta != null && field.meta.length > 0 ? field.meta.map(printMetadata).join(" ") + " " : "")
 | |
| 							+ (switch (field.kind) {
 | |
| 								case FVar(t, _): field.name + opt(t, printComplexType, ":");
 | |
| 								case FProp(_, _, _, _): throw "FProp is invalid for TDEnum.";
 | |
| 								case FFun(func): field.name + printFunction(func);
 | |
| 							})
 | |
| 							+ ";"].join("\n")
 | |
| 					+ "\n}";
 | |
| 				case TDStructure:
 | |
| 					"typedef "
 | |
| 					+ t.name
 | |
| 					+ ((t.params != null && t.params.length > 0) ? "<" + t.params.map(printTypeParamDecl).join(", ") + ">" : "")
 | |
| 					+ " = {\n"
 | |
| 					+ [
 | |
| 						for (f in t.fields) {
 | |
| 							tabs + printField(f) + ";";
 | |
| 						}
 | |
| 					].join("\n")
 | |
| 					+ "\n}";
 | |
| 				case TDClass(superClass, interfaces, isInterface, isFinal, isAbstract):
 | |
| 					(isFinal ? "final " : "")
 | |
| 						+ (isAbstract ? "abstract " : "")
 | |
| 						+ (isInterface ? "interface " : "class ")
 | |
| 						+ t.name
 | |
| 						+ (t.params != null && t.params.length > 0 ? "<" + t.params.map(printTypeParamDecl).join(", ") + ">" : "")
 | |
| 						+ (superClass != null ? " extends " + printTypePath(superClass) : "")
 | |
| 						+ (interfaces != null ? (isInterface ? [for (tp in interfaces) " extends " + printTypePath(tp)] : [
 | |
| 							for (tp in interfaces)
 | |
| 								" implements " + printTypePath(tp)
 | |
| 						]).join("") : "")
 | |
| 						+ " {\n"
 | |
| 						+ [
 | |
| 							for (f in t.fields) {
 | |
| 								tabs + printFieldWithDelimiter(f);
 | |
| 							}
 | |
| 						].join("\n")
 | |
| 						+ "\n}";
 | |
| 				case TDAlias(ct):
 | |
| 					"typedef "
 | |
| 					+ t.name
 | |
| 					+ ((t.params != null && t.params.length > 0) ? "<" + t.params.map(printTypeParamDecl).join(", ") + ">" : "")
 | |
| 					+ " = "
 | |
| 					+ (switch (ct) {
 | |
| 						case TExtend(tpl, fields): printExtension(tpl, fields);
 | |
| 						case TAnonymous(fields): printStructure(fields);
 | |
| 						case _: printComplexType(ct);
 | |
| 					})
 | |
| 					+ ";";
 | |
| 				case TDAbstract(tthis, from, to):
 | |
| 					"abstract "
 | |
| 					+ t.name
 | |
| 					+ ((t.params != null && t.params.length > 0) ? "<" + t.params.map(printTypeParamDecl).join(", ") + ">" : "")
 | |
| 					+ (tthis == null ? "" : "(" + printComplexType(tthis) + ")")
 | |
| 					+ (from == null ? "" : [for (f in from) " from " + printComplexType(f)].join(""))
 | |
| 					+ (to == null ? "" : [for (t in to) " to " + printComplexType(t)].join(""))
 | |
| 					+ " {\n"
 | |
| 					+ [
 | |
| 						for (f in t.fields) {
 | |
| 							tabs + printFieldWithDelimiter(f);
 | |
| 						}
 | |
| 					].join("\n")
 | |
| 					+ "\n}";
 | |
| 				case TDField(kind, access):
 | |
| 					tabs = old;
 | |
| 					(access != null && access.length > 0 ? access.map(printAccess).join(" ") + " " : "")
 | |
| 					+ switch (kind) {
 | |
| 						case FVar(type, eo): ((access != null && access.has(AFinal)) ? '' : 'var ') + '${t.name}' + opt(type, printComplexType, " : ") + opt(eo, printExpr, " = ") + ";";
 | |
| 						case FProp(get, set, type, eo): 'var ${t.name}($get, $set)' + opt(type, printComplexType, " : ") + opt(eo, printExpr, " = ") + ";";
 | |
| 						case FFun(func): 'function ${t.name}' + printFunction(func) + switch func.expr { case {expr: EBlock(_)}: ""; case _: ";"; };
 | |
| 					}
 | |
| 			} tabs = old;
 | |
| 
 | |
| 		return str;
 | |
| 	}
 | |
| 
 | |
| 	function printFieldWithDelimiter(f:Field):String {
 | |
| 		return printField(f) + switch (f.kind) {
 | |
| 			case FVar(_, _), FProp(_, _, _, _): ";";
 | |
| 			case FFun({expr: null}): ";";
 | |
| 			case FFun({expr: {expr: EBlock(_)}}): "";
 | |
| 			case FFun(_): ";";
 | |
| 			case _: "";
 | |
| 		};
 | |
| 	}
 | |
| 
 | |
| 	function opt<T>(v:T, f:T->String, prefix = "")
 | |
| 		return v == null ? "" : (prefix + f(v));
 | |
| 
 | |
| 	public function printExprWithPositions(e:Expr) {
 | |
| 		var buffer = new StringBuf();
 | |
| 		function format4(i:Int) {
 | |
| 			return StringTools.lpad(Std.string(i), " ", 4);
 | |
| 		}
 | |
| 		function loop(tabs:String, e:Expr) {
 | |
| 			function add(s:String, ?p = null) {
 | |
| 				if (p == null) {
 | |
| 					p = e.pos;
 | |
| 				}
 | |
| 				var p = #if macro haxe.macro.Context.getPosInfos(p) #else e.pos #end;
 | |
| 				buffer.add('${format4(p.min)}-${format4(p.max)} $tabs$s\n');
 | |
| 			}
 | |
| 			function loopI(e:Expr)
 | |
| 				loop(tabs + tabString, e);
 | |
| 			switch (e.expr) {
 | |
| 				case EConst(c):
 | |
| 					add(printConstant(c));
 | |
| 				case EArray(e1, e2):
 | |
| 					add("EArray");
 | |
| 					loopI(e1);
 | |
| 					loopI(e2);
 | |
| 				case EBinop(op, e1, e2):
 | |
| 					add("EBinop " + printBinop(op));
 | |
| 					loopI(e1);
 | |
| 					loopI(e2);
 | |
| 				case EField(e, field):
 | |
| 					add("EField " + field);
 | |
| 					loopI(e);
 | |
| 				case EParenthesis(e):
 | |
| 					add("EParenthesis");
 | |
| 					loopI(e);
 | |
| 				case EObjectDecl(fields):
 | |
| 					add("EObjectDecl");
 | |
| 					for (field in fields) {
 | |
| 						add(field.field); // TODO: we don't have the field pos?
 | |
| 						loopI(field.expr);
 | |
| 					}
 | |
| 				case EArrayDecl(values):
 | |
| 					add("EArrayDecl");
 | |
| 					values.iter(loopI);
 | |
| 				case ECall(e, params):
 | |
| 					add("ECall");
 | |
| 					loopI(e);
 | |
| 					params.iter(loopI);
 | |
| 				case ENew(tp, params):
 | |
| 					add("ENew " + printTypePath(tp));
 | |
| 					params.iter(loopI);
 | |
| 				case EUnop(op, postFix, e):
 | |
| 					add("EUnop " + printUnop(op));
 | |
| 					loopI(e);
 | |
| 				case EVars(vars):
 | |
| 					add("EVars");
 | |
| 					for (v in vars) {
 | |
| 						if (v.expr != null) {
 | |
| 							add(v.name);
 | |
| 							loopI(v.expr);
 | |
| 						}
 | |
| 					}
 | |
| 				case EFunction(_, f):
 | |
| 					add("EFunction");
 | |
| 					if (f.expr != null) {
 | |
| 						loopI(f.expr);
 | |
| 					}
 | |
| 				case EBlock(exprs):
 | |
| 					add("EBlock");
 | |
| 					exprs.iter(loopI);
 | |
| 				case EFor(it, expr):
 | |
| 					add("EFor");
 | |
| 					loopI(it);
 | |
| 					loopI(expr);
 | |
| 				case EIf(econd, eif, eelse):
 | |
| 					add("EIf");
 | |
| 					loopI(econd);
 | |
| 					loopI(eif);
 | |
| 					if (eelse != null) {
 | |
| 						loopI(eelse);
 | |
| 					}
 | |
| 				case EWhile(econd, e, normalWhile):
 | |
| 					add("EWhile");
 | |
| 					loopI(econd);
 | |
| 					loopI(e);
 | |
| 				case ESwitch(e, cases, edef):
 | |
| 					add("ESwitch");
 | |
| 					loopI(e);
 | |
| 					for (c in cases) {
 | |
| 						for (pat in c.values) {
 | |
| 							loop(tabs + tabString + tabString, pat);
 | |
| 						}
 | |
| 						if (c.expr != null) {
 | |
| 							loop(tabs + tabString + tabString + tabString, c.expr);
 | |
| 						}
 | |
| 					}
 | |
| 					if (edef != null) {
 | |
| 						loop(tabs + tabString + tabString + tabString, edef);
 | |
| 					}
 | |
| 				case ETry(e, catches):
 | |
| 					add("ETry");
 | |
| 					loopI(e);
 | |
| 					for (c in catches) {
 | |
| 						loop(tabs + tabString + tabString, c.expr);
 | |
| 					}
 | |
| 				case EReturn(e):
 | |
| 					add("EReturn");
 | |
| 					if (e != null) {
 | |
| 						loopI(e);
 | |
| 					}
 | |
| 				case EBreak:
 | |
| 					add("EBreak");
 | |
| 				case EContinue:
 | |
| 					add("EContinue");
 | |
| 				case EUntyped(e):
 | |
| 					add("EUntyped");
 | |
| 					loopI(e);
 | |
| 				case EThrow(e):
 | |
| 					add("EThrow");
 | |
| 					loopI(e);
 | |
| 				case ECast(e, t):
 | |
| 					add("ECast");
 | |
| 					loopI(e);
 | |
| 				case EIs(e, t):
 | |
| 					add("EIs");
 | |
| 					loopI(e);
 | |
| 				case EDisplay(e, displayKind):
 | |
| 					add("EDisplay");
 | |
| 					loopI(e);
 | |
| 				case EDisplayNew(t):
 | |
| 					add("EDisplayNew");
 | |
| 				case ETernary(econd, eif, eelse):
 | |
| 					add("ETernary");
 | |
| 					loopI(econd);
 | |
| 					loopI(eif);
 | |
| 					loopI(eelse);
 | |
| 				case ECheckType(e, t):
 | |
| 					add("ECheckType");
 | |
| 					loopI(e);
 | |
| 				case EMeta(s, e):
 | |
| 					add("EMeta " + printMetadata(s));
 | |
| 					loopI(e);
 | |
| 			}
 | |
| 		}
 | |
| 		loop("", e);
 | |
| 		return buffer.toString();
 | |
| 	}
 | |
| }
 |