forked from LeenkxTeam/LNXSDK
		
	
		
			
				
	
	
		
			315 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			Haxe
		
	
	
	
	
	
			
		
		
	
	
			315 lines
		
	
	
		
			9.5 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;
 | |
| 
 | |
| /**
 | |
| 	This class provides some utility methods to work with expressions. It is
 | |
| 	best used through 'using haxe.macro.ExprTools' syntax and then provides
 | |
| 	additional methods on haxe.macro.Expr instances.
 | |
| 
 | |
| 	While mainly intended to be used in macros, it works in non-macro code as
 | |
| 	well.
 | |
| **/
 | |
| class ExprTools {
 | |
| 	/**
 | |
| 		Converts expression `e` to a human-readable String representation.
 | |
| 
 | |
| 		The result is guaranteed to be valid Haxe code, but there may be
 | |
| 		differences from the original lexical syntax.
 | |
| 	**/
 | |
| 	static public function toString(e:Expr):String
 | |
| 		return new Printer().printExpr(e);
 | |
| 
 | |
| 	/**
 | |
| 		Calls function `f` on each sub-expression of `e`.
 | |
| 
 | |
| 		If `e` has no sub-expressions, this operation has no effect.
 | |
| 
 | |
| 		Otherwise `f` is called once per sub-expression of `e`, with the
 | |
| 		sub-expression as argument. These calls are done in order of the
 | |
| 		sub-expression declarations.
 | |
| 
 | |
| 		This method does not call itself recursively. It should instead be used
 | |
| 		in a recursive function which handles the expression nodes of interest.
 | |
| 
 | |
| 		Usage example:
 | |
| 		```haxe
 | |
| 		function findStrings(e:Expr) {
 | |
| 			switch(e.expr) {
 | |
| 				case EConst(CString(s)):
 | |
| 					// handle s
 | |
| 				case _:
 | |
| 					ExprTools.iter(e, findStrings);
 | |
| 			}
 | |
| 		}
 | |
| 		```
 | |
| 	**/
 | |
| 	static public function iter(e:Expr, f:Expr->Void):Void {
 | |
| 		switch (e.expr) {
 | |
| 			case EConst(_), EContinue, EBreak, EDisplayNew(_):
 | |
| 			case EField(e, _), EParenthesis(e), EUntyped(e), EThrow(e), EDisplay(e, _), ECheckType(e, _), EUnop(_, _, e), ECast(e, _), EIs(e, _) | EMeta(_, e):
 | |
| 				f(e);
 | |
| 			case EArray(e1, e2), EWhile(e1, e2, _), EBinop(_, e1, e2), EFor(e1, e2):
 | |
| 				f(e1);
 | |
| 				f(e2);
 | |
| 			case EVars(vl):
 | |
| 				for (v in vl)
 | |
| 					opt2(v.expr, f);
 | |
| 			case ETry(e, cl):
 | |
| 				f(e);
 | |
| 				for (c in cl)
 | |
| 					f(c.expr);
 | |
| 			case ETernary(e1, e2, e3) | EIf(e1, e2, e3):
 | |
| 				f(e1);
 | |
| 				f(e2);
 | |
| 				opt2(e3, f);
 | |
| 			case EArrayDecl(el), ENew(_, el), EBlock(el):
 | |
| 				ExprArrayTools.iter(el, f);
 | |
| 			case EObjectDecl(fl):
 | |
| 				for (fd in fl)
 | |
| 					f(fd.expr);
 | |
| 			case ECall(e, el):
 | |
| 				f(e);
 | |
| 				ExprArrayTools.iter(el, f);
 | |
| 			case EReturn(e):
 | |
| 				opt2(e, f);
 | |
| 			case EFunction(_, func):
 | |
| 				for (arg in func.args)
 | |
| 					opt2(arg.value, f);
 | |
| 				opt2(func.expr, f);
 | |
| 			case ESwitch(e, cl, edef):
 | |
| 				f(e);
 | |
| 				for (c in cl) {
 | |
| 					ExprArrayTools.iter(c.values, f);
 | |
| 					opt2(c.guard, f);
 | |
| 					opt2(c.expr, f);
 | |
| 				}
 | |
| 				if (edef != null && edef.expr != null)
 | |
| 					f(edef);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 		Transforms the sub-expressions of `e` by calling `f` on each of them.
 | |
| 
 | |
| 		If `e` has no sub-expressions, this operation returns `e` unchanged.
 | |
| 
 | |
| 		Otherwise `f` is called once per sub-expression of `e`, with the
 | |
| 		sub-expression as argument. These calls are done in order of the
 | |
| 		sub-expression declarations.
 | |
| 
 | |
| 		This method does not call itself recursively. It should instead be used
 | |
| 		in a recursive function which handles the expression nodes of interest.
 | |
| 
 | |
| 		Usage example:
 | |
| 		```haxe
 | |
| 		function capitalizeStrings(e:Expr) {
 | |
| 			return switch(e.expr) {
 | |
| 				case EConst(CString(s)):
 | |
| 					{ expr: EConst(CString(s.toUpperCase())), pos: e.pos };
 | |
| 				case _:
 | |
| 					ExprTools.map(e, capitalizeStrings);
 | |
| 			}
 | |
| 		}
 | |
| 		```
 | |
| 	**/
 | |
| 	static public function map(e:Expr, f:Expr->Expr):Expr {
 | |
| 		return {
 | |
| 			pos: e.pos,
 | |
| 			expr: switch (e.expr) {
 | |
| 				case EConst(_): e.expr;
 | |
| 				case EArray(e1, e2): EArray(f(e1), f(e2));
 | |
| 				case EBinop(op, e1, e2): EBinop(op, f(e1), f(e2));
 | |
| 				case EField(e, field): EField(f(e), field);
 | |
| 				case EParenthesis(e): EParenthesis(f(e));
 | |
| 				case EObjectDecl(fields):
 | |
| 					var ret = [];
 | |
| 					for (field in fields)
 | |
| 						ret.push({field: field.field, expr: f(field.expr), quotes: field.quotes});
 | |
| 					EObjectDecl(ret);
 | |
| 				case EArrayDecl(el): EArrayDecl(ExprArrayTools.map(el, f));
 | |
| 				case ECall(e, params): ECall(f(e), ExprArrayTools.map(params, f));
 | |
| 				case ENew(tp, params): ENew(tp, ExprArrayTools.map(params, f));
 | |
| 				case EUnop(op, postFix, e): EUnop(op, postFix, f(e));
 | |
| 				case EVars(vars):
 | |
| 					var ret = [];
 | |
| 					for (v in vars) {
 | |
| 						var v2:Var = {name: v.name, type: v.type, expr: opt(v.expr, f)};
 | |
| 						if (v.isFinal != null)
 | |
| 							v2.isFinal = v.isFinal;
 | |
| 						ret.push(v2);
 | |
| 					}
 | |
| 					EVars(ret);
 | |
| 				case EBlock(el): EBlock(ExprArrayTools.map(el, f));
 | |
| 				case EFor(it, expr): EFor(f(it), f(expr));
 | |
| 				case EIf(econd, eif, eelse): EIf(f(econd), f(eif), opt(eelse, f));
 | |
| 				case EWhile(econd, e, normalWhile): EWhile(f(econd), f(e), normalWhile);
 | |
| 				case EReturn(e): EReturn(opt(e, f));
 | |
| 				case EUntyped(e): EUntyped(f(e));
 | |
| 				case EThrow(e): EThrow(f(e));
 | |
| 				case ECast(e, t): ECast(f(e), t);
 | |
| 				case EIs(e, t): EIs(f(e), t);
 | |
| 				case EDisplay(e, dk): EDisplay(f(e), dk);
 | |
| 				case ETernary(econd, eif, eelse): ETernary(f(econd), f(eif), f(eelse));
 | |
| 				case ECheckType(e, t): ECheckType(f(e), t);
 | |
| 				case EDisplayNew(_), EContinue, EBreak:
 | |
| 					e.expr;
 | |
| 				case ETry(e, catches):
 | |
| 					var ret = [];
 | |
| 					for (c in catches)
 | |
| 						ret.push({name: c.name, type: c.type, expr: f(c.expr)});
 | |
| 					ETry(f(e), ret);
 | |
| 				case ESwitch(e, cases, edef):
 | |
| 					var ret = [];
 | |
| 					for (c in cases)
 | |
| 						ret.push({expr: opt(c.expr, f), guard: opt(c.guard, f), values: ExprArrayTools.map(c.values, f)});
 | |
| 					ESwitch(f(e), ret, edef == null || edef.expr == null ? edef : f(edef));
 | |
| 				case EFunction(kind, func):
 | |
| 					var ret = [];
 | |
| 					for (arg in func.args)
 | |
| 						ret.push({
 | |
| 							name: arg.name,
 | |
| 							opt: arg.opt,
 | |
| 							type: arg.type,
 | |
| 							value: opt(arg.value, f)
 | |
| 						});
 | |
| 					EFunction(kind, {
 | |
| 						args: ret,
 | |
| 						ret: func.ret,
 | |
| 						params: func.params,
 | |
| 						expr: f(func.expr)
 | |
| 					});
 | |
| 				case EMeta(m, e): EMeta(m, f(e));
 | |
| 			}
 | |
| 		};
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 		Returns the value `e` represents.
 | |
| 
 | |
| 		Supported expressions are:
 | |
| 
 | |
| 		 - `Int`, `Float` and `String` literals
 | |
| 		 - identifiers `true`, `false` and `null`
 | |
| 		 - structure declarations if all their fields are values
 | |
| 		 - array declarations if all their elements are values
 | |
| 		 - unary operators `-`, `!` and `~` if the operand is a value
 | |
| 		 - binary operators except `=>`, `...` and assignments
 | |
| 
 | |
| 		Parentheses, metadata and the `untyped` keyword are ignored.
 | |
| 
 | |
| 		If any non-value is encountered, an exception of type `String` is
 | |
| 		thrown.
 | |
| 
 | |
| 		If `e` is null, the result is unspecified.
 | |
| 	**/
 | |
| 	static public function getValue(e:Expr):Dynamic {
 | |
| 		return switch (e.expr) {
 | |
| 			case EConst(CInt(v)): Std.parseInt(v);
 | |
| 			case EConst(CFloat(v)): Std.parseFloat(v);
 | |
| 			case EConst(CString(s)): s;
 | |
| 			case EConst(CIdent("true")): true;
 | |
| 			case EConst(CIdent("false")): false;
 | |
| 			case EConst(CIdent("null")): null;
 | |
| 			case EParenthesis(e1) | EUntyped(e1) | EMeta(_, e1): getValue(e1);
 | |
| 			case EObjectDecl(fields):
 | |
| 				var obj = {};
 | |
| 				for (field in fields) {
 | |
| 					Reflect.setField(obj, field.field, getValue(field.expr));
 | |
| 				}
 | |
| 				obj;
 | |
| 			case EArrayDecl(el): el.map(getValue);
 | |
| 			case EIf(econd, eif, eelse) | ETernary(econd, eif, eelse):
 | |
| 				if (eelse == null) {
 | |
| 					throw "If statements only have a value if the else clause is defined";
 | |
| 				} else {
 | |
| 					var econd:Dynamic = getValue(econd);
 | |
| 					econd ? getValue(eif) : getValue(eelse);
 | |
| 				}
 | |
| 			case EUnop(op, false, e1):
 | |
| 				var e1:Dynamic = getValue(e1);
 | |
| 				switch (op) {
 | |
| 					case OpNot: !e1;
 | |
| 					case OpNeg: -e1;
 | |
| 					case OpNegBits: ~e1;
 | |
| 					case _: throw 'Unsupported expression: $e';
 | |
| 				}
 | |
| 			case EBinop(op, e1, e2):
 | |
| 				var e1:Dynamic = getValue(e1);
 | |
| 				var e2:Dynamic = getValue(e2);
 | |
| 				switch (op) {
 | |
| 					case OpAdd: e1 + e2;
 | |
| 					case OpSub: e1 - e2;
 | |
| 					case OpMult: e1 * e2;
 | |
| 					case OpDiv: e1 / e2;
 | |
| 					case OpMod: e1 % e2;
 | |
| 					case OpEq: e1 == e2;
 | |
| 					case OpNotEq: e1 != e2;
 | |
| 					case OpLt: e1 < e2;
 | |
| 					case OpLte: e1 <= e2;
 | |
| 					case OpGt: e1 > e2;
 | |
| 					case OpGte: e1 >= e2;
 | |
| 					case OpOr: e1 | e2;
 | |
| 					case OpAnd: e1 & e2;
 | |
| 					case OpXor: e1 ^ e2;
 | |
| 					case OpBoolAnd: e1 && e2;
 | |
| 					case OpBoolOr: e1 || e2;
 | |
| 					case OpShl: e1 << e2;
 | |
| 					case OpShr: e1 >> e2;
 | |
| 					case OpUShr: e1 >>> e2;
 | |
| 					case _: throw 'Unsupported expression: $e';
 | |
| 				}
 | |
| 			case _: throw 'Unsupported expression: $e';
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	static inline function opt(e:Null<Expr>, f:Expr->Expr):Expr
 | |
| 		return e == null ? null : f(e);
 | |
| 
 | |
| 	static inline function opt2(e:Null<Expr>, f:Expr->Void):Void
 | |
| 		if (e != null)
 | |
| 			f(e);
 | |
| }
 | |
| 
 | |
| /**
 | |
| 	This class provides functions on expression arrays for convenience. For a
 | |
| 	detailed reference on each method, see the documentation of ExprTools.
 | |
| **/
 | |
| class ExprArrayTools {
 | |
| 	static public function map(el:Array<Expr>, f:Expr->Expr):Array<Expr> {
 | |
| 		var ret = [];
 | |
| 		for (e in el)
 | |
| 			ret.push(f(e));
 | |
| 		return ret;
 | |
| 	}
 | |
| 
 | |
| 	static public function iter(el:Array<Expr>, f:Expr->Void):Void {
 | |
| 		for (e in el)
 | |
| 			f(e);
 | |
| 	}
 | |
| }
 |