浏览代码

added macros.

Nicolas Cannasse 19 年之前
父节点
当前提交
816bc2ca8d
共有 1 个文件被更改,包括 93 次插入27 次删除
  1. 93 27
      std/haxe/Template.hx

+ 93 - 27
std/haxe/Template.hx

@@ -31,12 +31,23 @@ private enum TemplateExpr {
 	OpStr( str : String );
 	OpBlock( l : List<TemplateExpr> );
 	OpForeach( expr : Void -> Dynamic, loop : TemplateExpr );
+	OpMacro( name : String, params : List<TemplateExpr> );
 }
 
+private signature Token {
+	var s : Bool;
+	var p : String;
+	var l : Array<String>;
+}
+
+private signature ExprToken {
+	var s : Bool;
+	var p : String;
+}
 
 class Template {
 
-	static var splitter = ~/::[A-Za-z0-9_ ()&|!+=\/><*.-]+::/;
+	static var splitter = ~/(::[A-Za-z0-9_ ()&|!+=\/><*.-]+::|\$\$([A-Za-z0-9_-]+)\()/;
 	static var expr_splitter = ~/(\(|\)|[!+=\/><*.&|-]+)/;
 	static var expr_trim = ~/^[ ]*([^ ]+)[ ]*$/;
 	static var expr_int = ~/^[0-9]+$/;
@@ -46,6 +57,7 @@ class Template {
 
 	var expr : TemplateExpr;
 	var context : Dynamic;
+	var macros : Dynamic;
 	var stack : List<Dynamic>;
 	var buf : StringBuf;
 
@@ -56,7 +68,8 @@ class Template {
 			throw "Unexpected '"+tokens.first().s+"'";
 	}
 
-	public function execute( context : Dynamic ) {
+	public function execute( context : Dynamic, ?macros : Dynamic ) {
+		this.macros = macros;
 		this.context = context;
 		stack = new List();
 		buf = new StringBuf();
@@ -74,21 +87,42 @@ class Template {
 	}
 
 	function parseTokens( data : String ) {
-		var tokens = new List();
+		var tokens = new List<Token>();
 		while( splitter.match(data) ) {
 			var p = splitter.matchedPos();
 			if( p.pos > 0 )
-				tokens.add({ p : data.substr(0,p.pos), s : true });
-			tokens.add({ p : data.substr(p.pos + 2,p.len - 4), s : false });
-			var k = p.pos + p.len;
-			data = data.substr(k,data.length-k);
+				tokens.add({ p : data.substr(0,p.pos), s : true, l : null });
+
+			// : ?
+			if( data.charCodeAt(p.pos) == 58 ) {
+				tokens.add({ p : data.substr(p.pos + 2,p.len - 4), s : false, l : null });
+				data = splitter.matchedRight();
+				continue;
+			}
+
+			// macro parse
+			var parp = p.pos + p.len;
+			var npar = 1;
+			while( npar > 0 ) {
+				var c = data.charCodeAt(parp);
+				if( c == 40 )
+					npar++;
+				else if( c == 41 )
+					npar--;
+				else if( c == null )
+					throw "Unclosed macro parenthesis";
+				parp++;
+			}
+			var params = data.substr(p.pos+p.len,parp - (p.pos+p.len) - 1).split(",");			
+			tokens.add({ p : splitter.matched(2), s : false, l : params });
+			data = data.substr(parp,data.length - parp);
 		}
 		if( data.length > 0 )
-			tokens.add({ p : data, s : true });
+			tokens.add({ p : data, s : true, l : null });
 		return tokens;
 	}
 
-	function parseBlock( tokens : List<{ p : String, s : Bool }> ) {
+	function parseBlock( tokens : List<Token> ) {
 		var l = new List();
 		while( true ) {
 			var t = tokens.first();
@@ -103,11 +137,18 @@ class Template {
 		return OpBlock(l);
 	}
 
-	function parse( tokens : List<{ p : String, s : Bool }> ) {
+	function parse( tokens : List<Token> ) {
 		var t = tokens.pop();
 		var p = t.p;
 		if( t.s )
 			return OpStr(p);
+		// macro
+		if( t.l != null ) {
+			var pe = new List();
+			for( p in t.l )
+				pe.add(parseBlock(parseTokens(p)));
+			return OpMacro(p,pe);
+		}
 		// 'end' , 'else', 'elseif' can't be found here
 		if( p.substr(0,3) == "if " ) {
 			p = p.substr(3,p.length - 3);
@@ -147,7 +188,7 @@ class Template {
 	}
 
 	function parseExpr( data : String ) {
-		var l = new List();
+		var l = new List<ExprToken>();
 		var expr = data;
 		while( expr_splitter.match(data) ) {
 			var p = expr_splitter.matchedPos();
@@ -155,7 +196,7 @@ class Template {
 			if( p.pos != 0 )
 				l.add({ p : data.substr(0,p.pos), s : true });
 			l.add({ p : expr_splitter.matched(0), s : false });
-			data = data.substr(k,data.length - k);
+			data = expr_splitter.matchedRight();
 		}
 		if( data.length != 0 )
 			l.add({ p : data, s : true });
@@ -191,7 +232,7 @@ class Template {
 		return function() { return me.resolve(v); };
 	}
 
-	function makePath( e : Void -> Dynamic, l : List<{ p : String, s : Bool }> ) {
+	function makePath( e : Void -> Dynamic, l : List<ExprToken> ) {
 		var p = l.first();
 		if( p == null || p.p != "." )
 			return e;
@@ -209,7 +250,7 @@ class Template {
 		return makePath(makeExpr2(l),l);
 	}
 
-	function makeExpr2( l : List<{ p : String, s : Bool }> ) : Void -> Dynamic {
+	function makeExpr2( l : List<ExprToken> ) : Void -> Dynamic {
 		var p = l.pop();
 		if( p == null )
 			throw "<eof>";
@@ -228,19 +269,19 @@ class Template {
 			if( p2 == null || p2.p != ")" )
 				throw p2.p;
 			return switch( p.p ) {
-			case "+": function() { return untyped e1() + e2(); };
-			case "-": function() { return untyped e1() - e2(); };
-			case "*": function() { return untyped e1() * e2(); };
-			case "/": function() { return untyped e1() / e2(); };
-			case ">": function() { return untyped e1() > e2(); };
-			case "<": function() { return untyped e1() < e2(); };
-			case ">=": function() { return untyped e1() >= e2(); };
-			case "<=": function() { return untyped e1() <= e2(); };
-			case "==": function() { return untyped e1() == e2(); };
-			case "!=": function() { return untyped e1() != e2(); };
-			case "&&": function() { return untyped e1() && e2(); };
-			case "||": function() { return untyped e1() || e2(); };
-			default: throw "operation "+p.p;
+			case "+": function() { return cast e1() + e2(); };
+			case "-": function() { return cast e1() - e2(); };
+			case "*": function() { return cast e1() * e2(); };
+			case "/": function() { return cast e1() / e2(); };
+			case ">": function() { return cast e1() > e2(); };
+			case "<": function() { return cast e1() < e2(); };
+			case ">=": function() { return cast e1() >= e2(); };
+			case "<=": function() { return cast e1() <= e2(); };
+			case "==": function() { return cast e1() == e2(); };
+			case "!=": function() { return cast e1() != e2(); };
+			case "&&": function() { return cast e1() && e2(); };
+			case "||": function() { return cast e1() || e2(); };
+			default: throw "Unknown operation "+p.p;
 			}
 		case "!":
 			var e = makeExpr(l);
@@ -289,6 +330,31 @@ class Template {
 				run(loop);
 			}
 			context = stack.pop();
+		case OpMacro(m,params):
+			var v : Dynamic = Reflect.field(macros,m);
+			var pl = new Array<Dynamic>();
+			var old = buf;
+			pl.push(resolve);
+			for( p in params ) {
+				switch( p ) {
+				case OpVar(v): pl.push(resolve(v));
+				default:
+					buf = new StringBuf();
+					run(p);
+					pl.push(buf.toString());
+				}
+			}
+			buf = old;
+			try {				
+				buf.add(Reflect.callMethod(null,v,pl));
+			} catch( e : Dynamic ) {
+				var msg = "Macro call "+m+" failed ("+Std.string(e)+")";
+				#if neko
+				untyped __dollar__rethrow(msg);
+				#else true
+				throw msg;
+				#end
+			}
 		}
 	}