509 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Haxe
		
	
	
	
	
	
		
		
			
		
	
	
			509 lines
		
	
	
		
			12 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;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import haxe.ds.List;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								using StringTools;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								private enum TemplateExpr {
							 | 
						||
| 
								 | 
							
									OpVar(v:String);
							 | 
						||
| 
								 | 
							
									OpExpr(expr:Void->Dynamic);
							 | 
						||
| 
								 | 
							
									OpIf(expr:Void->Dynamic, eif:TemplateExpr, eelse:TemplateExpr);
							 | 
						||
| 
								 | 
							
									OpStr(str:String);
							 | 
						||
| 
								 | 
							
									OpBlock(l:List<TemplateExpr>);
							 | 
						||
| 
								 | 
							
									OpForeach(expr:Void->Dynamic, loop:TemplateExpr);
							 | 
						||
| 
								 | 
							
									OpMacro(name:String, params:List<TemplateExpr>);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								private typedef Token = {
							 | 
						||
| 
								 | 
							
									var s:Bool;
							 | 
						||
| 
								 | 
							
									var p:String;
							 | 
						||
| 
								 | 
							
									var l:Array<String>;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								private typedef ExprToken = {
							 | 
						||
| 
								 | 
							
									var s:Bool;
							 | 
						||
| 
								 | 
							
									var p:String;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
									`Template` provides a basic templating mechanism to replace values in a source
							 | 
						||
| 
								 | 
							
									String, and to have some basic logic.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									A complete documentation of the supported syntax is available at:
							 | 
						||
| 
								 | 
							
									<https://haxe.org/manual/std-template.html>
							 | 
						||
| 
								 | 
							
								**/
							 | 
						||
| 
								 | 
							
								class Template {
							 | 
						||
| 
								 | 
							
									static var splitter = ~/(::[A-Za-z0-9_ ()&|!+=\/><*."-]+::|\$\$([A-Za-z0-9_-]+)\()/;
							 | 
						||
| 
								 | 
							
									static var expr_splitter = ~/(\(|\)|[ \r\n\t]*"[^"]*"[ \r\n\t]*|[!+=\/><*.&|-]+)/;
							 | 
						||
| 
								 | 
							
									static var expr_trim = ~/^[ ]*([^ ]+)[ ]*$/;
							 | 
						||
| 
								 | 
							
									static var expr_int = ~/^[0-9]+$/;
							 | 
						||
| 
								 | 
							
									static var expr_float = ~/^([+-]?)(?=\d|,\d)\d*(,\d*)?([Ee]([+-]?\d+))?$/;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/**
							 | 
						||
| 
								 | 
							
										Global replacements which are used across all `Template` instances. This
							 | 
						||
| 
								 | 
							
										has lower priority than the context argument of `execute()`.
							 | 
						||
| 
								 | 
							
									**/
							 | 
						||
| 
								 | 
							
									public static var globals:Dynamic = {};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// To avoid issues with DCE, keep the array iterator.
							 | 
						||
| 
								 | 
							
									@:ifFeature("haxe.Template.run") static var hxKeepArrayIterator = [].iterator();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									var expr:TemplateExpr;
							 | 
						||
| 
								 | 
							
									var context:Dynamic;
							 | 
						||
| 
								 | 
							
									var macros:Dynamic;
							 | 
						||
| 
								 | 
							
									var stack:List<Dynamic>;
							 | 
						||
| 
								 | 
							
									var buf:StringBuf;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/**
							 | 
						||
| 
								 | 
							
										Creates a new `Template` instance from `str`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										`str` is parsed into tokens, which are stored for internal use. This
							 | 
						||
| 
								 | 
							
										means that multiple `execute()` operations on a single `Template` instance
							 | 
						||
| 
								 | 
							
										are more efficient than one `execute()` operations on multiple `Template`
							 | 
						||
| 
								 | 
							
										instances.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										If `str` is `null`, the result is unspecified.
							 | 
						||
| 
								 | 
							
									**/
							 | 
						||
| 
								 | 
							
									public function new(str:String) {
							 | 
						||
| 
								 | 
							
										var tokens = parseTokens(str);
							 | 
						||
| 
								 | 
							
										expr = parseBlock(tokens);
							 | 
						||
| 
								 | 
							
										if (!tokens.isEmpty())
							 | 
						||
| 
								 | 
							
											throw "Unexpected '" + tokens.first().s + "'";
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/**
							 | 
						||
| 
								 | 
							
										Executes `this` `Template`, taking into account `context` for
							 | 
						||
| 
								 | 
							
										replacements and `macros` for callback functions.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										If `context` has a field `name`, its value replaces all occurrences of
							 | 
						||
| 
								 | 
							
										`::name::` in the `Template`. Otherwise `Template.globals` is checked instead,
							 | 
						||
| 
								 | 
							
										If `name` is not a field of that either, `::name::` is replaced with `null`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										If `macros` has a field `name`, all occurrences of `$$name(args)` are
							 | 
						||
| 
								 | 
							
										replaced with the result of calling that field. The first argument is
							 | 
						||
| 
								 | 
							
										always the `resolve()` method, followed by the given arguments.
							 | 
						||
| 
								 | 
							
										If `macros` has no such field, the result is unspecified.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										If `context` is `null`, the result is unspecified. If `macros` is `null`,
							 | 
						||
| 
								 | 
							
										no macros are used.
							 | 
						||
| 
								 | 
							
									**/
							 | 
						||
| 
								 | 
							
									public function execute(context:Dynamic, ?macros:Dynamic):String {
							 | 
						||
| 
								 | 
							
										this.macros = if (macros == null) {} else macros;
							 | 
						||
| 
								 | 
							
										this.context = context;
							 | 
						||
| 
								 | 
							
										stack = new List();
							 | 
						||
| 
								 | 
							
										buf = new StringBuf();
							 | 
						||
| 
								 | 
							
										run(expr);
							 | 
						||
| 
								 | 
							
										return buf.toString();
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function resolve(v:String):Dynamic {
							 | 
						||
| 
								 | 
							
										if (v == "__current__")
							 | 
						||
| 
								 | 
							
											return context;
							 | 
						||
| 
								 | 
							
										if (Reflect.isObject(context)) {
							 | 
						||
| 
								 | 
							
											var value = Reflect.getProperty(context, v);
							 | 
						||
| 
								 | 
							
											if (value != null || Reflect.hasField(context, v))
							 | 
						||
| 
								 | 
							
												return value;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										for (ctx in stack) {
							 | 
						||
| 
								 | 
							
											var value = Reflect.getProperty(ctx, v);
							 | 
						||
| 
								 | 
							
											if (value != null || Reflect.hasField(ctx, v))
							 | 
						||
| 
								 | 
							
												return value;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										return Reflect.field(globals, v);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function parseTokens(data:String) {
							 | 
						||
| 
								 | 
							
										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, 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;
							 | 
						||
| 
								 | 
							
											var params = [];
							 | 
						||
| 
								 | 
							
											var part = "";
							 | 
						||
| 
								 | 
							
											while (true) {
							 | 
						||
| 
								 | 
							
												var c = data.charCodeAt(parp);
							 | 
						||
| 
								 | 
							
												parp++;
							 | 
						||
| 
								 | 
							
												if (c == 40) {
							 | 
						||
| 
								 | 
							
													npar++;
							 | 
						||
| 
								 | 
							
												} else if (c == 41) {
							 | 
						||
| 
								 | 
							
													npar--;
							 | 
						||
| 
								 | 
							
													if (npar <= 0)
							 | 
						||
| 
								 | 
							
														break;
							 | 
						||
| 
								 | 
							
												} else if (c == null) {
							 | 
						||
| 
								 | 
							
													throw "Unclosed macro parenthesis";
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
												if (c == 44 && npar == 1) {
							 | 
						||
| 
								 | 
							
													params.push(part);
							 | 
						||
| 
								 | 
							
													part = "";
							 | 
						||
| 
								 | 
							
												} else {
							 | 
						||
| 
								 | 
							
													part += String.fromCharCode(c);
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											params.push(part);
							 | 
						||
| 
								 | 
							
											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, l: null});
							 | 
						||
| 
								 | 
							
										return tokens;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function parseBlock(tokens:List<Token>) {
							 | 
						||
| 
								 | 
							
										var l = new List();
							 | 
						||
| 
								 | 
							
										while (true) {
							 | 
						||
| 
								 | 
							
											var t = tokens.first();
							 | 
						||
| 
								 | 
							
											if (t == null)
							 | 
						||
| 
								 | 
							
												break;
							 | 
						||
| 
								 | 
							
											if (!t.s && (t.p == "end" || t.p == "else" || t.p.substr(0, 7) == "elseif "))
							 | 
						||
| 
								 | 
							
												break;
							 | 
						||
| 
								 | 
							
											l.add(parse(tokens));
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										if (l.length == 1)
							 | 
						||
| 
								 | 
							
											return l.first();
							 | 
						||
| 
								 | 
							
										return OpBlock(l);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									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);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										function kwdEnd(kwd:String):Int {
							 | 
						||
| 
								 | 
							
											var pos = -1;
							 | 
						||
| 
								 | 
							
											var length = kwd.length;
							 | 
						||
| 
								 | 
							
											if (p.substr(0, length) == kwd) {
							 | 
						||
| 
								 | 
							
												pos = length;
							 | 
						||
| 
								 | 
							
												for (c in p.substr(length)) {
							 | 
						||
| 
								 | 
							
													switch c {
							 | 
						||
| 
								 | 
							
														case ' '.code: pos++;
							 | 
						||
| 
								 | 
							
														case _: break;
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											return pos;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										// 'end' , 'else', 'elseif' can't be found here
							 | 
						||
| 
								 | 
							
										var pos = kwdEnd("if");
							 | 
						||
| 
								 | 
							
										if (pos > 0) {
							 | 
						||
| 
								 | 
							
											p = p.substr(pos, p.length - pos);
							 | 
						||
| 
								 | 
							
											var e = parseExpr(p);
							 | 
						||
| 
								 | 
							
											var eif = parseBlock(tokens);
							 | 
						||
| 
								 | 
							
											var t = tokens.first();
							 | 
						||
| 
								 | 
							
											var eelse;
							 | 
						||
| 
								 | 
							
											if (t == null)
							 | 
						||
| 
								 | 
							
												throw "Unclosed 'if'";
							 | 
						||
| 
								 | 
							
											if (t.p == "end") {
							 | 
						||
| 
								 | 
							
												tokens.pop();
							 | 
						||
| 
								 | 
							
												eelse = null;
							 | 
						||
| 
								 | 
							
											} else if (t.p == "else") {
							 | 
						||
| 
								 | 
							
												tokens.pop();
							 | 
						||
| 
								 | 
							
												eelse = parseBlock(tokens);
							 | 
						||
| 
								 | 
							
												t = tokens.pop();
							 | 
						||
| 
								 | 
							
												if (t == null || t.p != "end")
							 | 
						||
| 
								 | 
							
													throw "Unclosed 'else'";
							 | 
						||
| 
								 | 
							
											} else { // elseif
							 | 
						||
| 
								 | 
							
												t.p = t.p.substr(4, t.p.length - 4);
							 | 
						||
| 
								 | 
							
												eelse = parse(tokens);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											return OpIf(e, eif, eelse);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										var pos = kwdEnd("foreach");
							 | 
						||
| 
								 | 
							
										if (pos >= 0) {
							 | 
						||
| 
								 | 
							
											p = p.substr(pos, p.length - pos);
							 | 
						||
| 
								 | 
							
											var e = parseExpr(p);
							 | 
						||
| 
								 | 
							
											var efor = parseBlock(tokens);
							 | 
						||
| 
								 | 
							
											var t = tokens.pop();
							 | 
						||
| 
								 | 
							
											if (t == null || t.p != "end")
							 | 
						||
| 
								 | 
							
												throw "Unclosed 'foreach'";
							 | 
						||
| 
								 | 
							
											return OpForeach(e, efor);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										if (expr_splitter.match(p))
							 | 
						||
| 
								 | 
							
											return OpExpr(parseExpr(p));
							 | 
						||
| 
								 | 
							
										return OpVar(p);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function parseExpr(data:String) {
							 | 
						||
| 
								 | 
							
										var l = new List<ExprToken>();
							 | 
						||
| 
								 | 
							
										var expr = data;
							 | 
						||
| 
								 | 
							
										while (expr_splitter.match(data)) {
							 | 
						||
| 
								 | 
							
											var p = expr_splitter.matchedPos();
							 | 
						||
| 
								 | 
							
											var k = p.pos + p.len;
							 | 
						||
| 
								 | 
							
											if (p.pos != 0)
							 | 
						||
| 
								 | 
							
												l.add({p: data.substr(0, p.pos), s: true});
							 | 
						||
| 
								 | 
							
											var p = expr_splitter.matched(0);
							 | 
						||
| 
								 | 
							
											l.add({p: p, s: p.indexOf('"') >= 0});
							 | 
						||
| 
								 | 
							
											data = expr_splitter.matchedRight();
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										if (data.length != 0) {
							 | 
						||
| 
								 | 
							
											for (i => c in data) {
							 | 
						||
| 
								 | 
							
												switch c {
							 | 
						||
| 
								 | 
							
													case ' '.code:
							 | 
						||
| 
								 | 
							
													case _:
							 | 
						||
| 
								 | 
							
														l.add({p: data.substr(i), s: true});
							 | 
						||
| 
								 | 
							
														break;
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										var e:Void->Dynamic;
							 | 
						||
| 
								 | 
							
										try {
							 | 
						||
| 
								 | 
							
											e = makeExpr(l);
							 | 
						||
| 
								 | 
							
											if (!l.isEmpty())
							 | 
						||
| 
								 | 
							
												throw l.first().p;
							 | 
						||
| 
								 | 
							
										} catch (s:String) {
							 | 
						||
| 
								 | 
							
											throw "Unexpected '" + s + "' in " + expr;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										return function() {
							 | 
						||
| 
								 | 
							
											try {
							 | 
						||
| 
								 | 
							
												return e();
							 | 
						||
| 
								 | 
							
											} catch (exc:Dynamic) {
							 | 
						||
| 
								 | 
							
												throw "Error : " + Std.string(exc) + " in " + expr;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function makeConst(v:String):Void->Dynamic {
							 | 
						||
| 
								 | 
							
										expr_trim.match(v);
							 | 
						||
| 
								 | 
							
										v = expr_trim.matched(1);
							 | 
						||
| 
								 | 
							
										if (v.charCodeAt(0) == 34) {
							 | 
						||
| 
								 | 
							
											var str = v.substr(1, v.length - 2);
							 | 
						||
| 
								 | 
							
											return function() return str;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										if (expr_int.match(v)) {
							 | 
						||
| 
								 | 
							
											var i = Std.parseInt(v);
							 | 
						||
| 
								 | 
							
											return function() {
							 | 
						||
| 
								 | 
							
												return i;
							 | 
						||
| 
								 | 
							
											};
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										if (expr_float.match(v)) {
							 | 
						||
| 
								 | 
							
											var f = Std.parseFloat(v);
							 | 
						||
| 
								 | 
							
											return function() {
							 | 
						||
| 
								 | 
							
												return f;
							 | 
						||
| 
								 | 
							
											};
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										var me = this;
							 | 
						||
| 
								 | 
							
										return function() {
							 | 
						||
| 
								 | 
							
											return me.resolve(v);
							 | 
						||
| 
								 | 
							
										};
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function makePath(e:Void->Dynamic, l:List<ExprToken>) {
							 | 
						||
| 
								 | 
							
										var p = l.first();
							 | 
						||
| 
								 | 
							
										if (p == null || p.p != ".")
							 | 
						||
| 
								 | 
							
											return e;
							 | 
						||
| 
								 | 
							
										l.pop();
							 | 
						||
| 
								 | 
							
										var field = l.pop();
							 | 
						||
| 
								 | 
							
										if (field == null || !field.s)
							 | 
						||
| 
								 | 
							
											throw field.p;
							 | 
						||
| 
								 | 
							
										var f = field.p;
							 | 
						||
| 
								 | 
							
										expr_trim.match(f);
							 | 
						||
| 
								 | 
							
										f = expr_trim.matched(1);
							 | 
						||
| 
								 | 
							
										return makePath(function() {
							 | 
						||
| 
								 | 
							
											return Reflect.field(e(), f);
							 | 
						||
| 
								 | 
							
										}, l);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function makeExpr(l) {
							 | 
						||
| 
								 | 
							
										return makePath(makeExpr2(l), l);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function skipSpaces(l:List<ExprToken>) {
							 | 
						||
| 
								 | 
							
										var p = l.first();
							 | 
						||
| 
								 | 
							
										while (p != null) {
							 | 
						||
| 
								 | 
							
											for (c in p.p) {
							 | 
						||
| 
								 | 
							
												if (c != " ".code) {
							 | 
						||
| 
								 | 
							
													return;
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											l.pop();
							 | 
						||
| 
								 | 
							
											p = l.first();
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function makeExpr2(l:List<ExprToken>):Void->Dynamic {
							 | 
						||
| 
								 | 
							
										skipSpaces(l);
							 | 
						||
| 
								 | 
							
										var p = l.pop();
							 | 
						||
| 
								 | 
							
										skipSpaces(l);
							 | 
						||
| 
								 | 
							
										if (p == null)
							 | 
						||
| 
								 | 
							
											throw "<eof>";
							 | 
						||
| 
								 | 
							
										if (p.s)
							 | 
						||
| 
								 | 
							
											return makeConst(p.p);
							 | 
						||
| 
								 | 
							
										switch (p.p) {
							 | 
						||
| 
								 | 
							
											case "(":
							 | 
						||
| 
								 | 
							
												skipSpaces(l);
							 | 
						||
| 
								 | 
							
												var e1:Dynamic = makeExpr(l);
							 | 
						||
| 
								 | 
							
												skipSpaces(l);
							 | 
						||
| 
								 | 
							
												var p = l.pop();
							 | 
						||
| 
								 | 
							
												if (p == null || p.s)
							 | 
						||
| 
								 | 
							
													throw p;
							 | 
						||
| 
								 | 
							
												if (p.p == ")")
							 | 
						||
| 
								 | 
							
													return e1;
							 | 
						||
| 
								 | 
							
												skipSpaces(l);
							 | 
						||
| 
								 | 
							
												var e2:Dynamic = makeExpr(l);
							 | 
						||
| 
								 | 
							
												skipSpaces(l);
							 | 
						||
| 
								 | 
							
												var p2 = l.pop();
							 | 
						||
| 
								 | 
							
												skipSpaces(l);
							 | 
						||
| 
								 | 
							
												if (p2 == null || p2.p != ")")
							 | 
						||
| 
								 | 
							
													throw p2;
							 | 
						||
| 
								 | 
							
												return switch (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:Void->Dynamic = makeExpr(l);
							 | 
						||
| 
								 | 
							
												return function() {
							 | 
						||
| 
								 | 
							
													var v:Dynamic = e();
							 | 
						||
| 
								 | 
							
													return (v == null || v == false);
							 | 
						||
| 
								 | 
							
												};
							 | 
						||
| 
								 | 
							
											case "-":
							 | 
						||
| 
								 | 
							
												var e = makeExpr(l);
							 | 
						||
| 
								 | 
							
												return function() {
							 | 
						||
| 
								 | 
							
													return -e();
							 | 
						||
| 
								 | 
							
												};
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										throw p.p;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function run(e:TemplateExpr) {
							 | 
						||
| 
								 | 
							
										switch (e) {
							 | 
						||
| 
								 | 
							
											case OpVar(v):
							 | 
						||
| 
								 | 
							
												buf.add(Std.string(resolve(v)));
							 | 
						||
| 
								 | 
							
											case OpExpr(e):
							 | 
						||
| 
								 | 
							
												buf.add(Std.string(e()));
							 | 
						||
| 
								 | 
							
											case OpIf(e, eif, eelse):
							 | 
						||
| 
								 | 
							
												var v:Dynamic = e();
							 | 
						||
| 
								 | 
							
												if (v == null || v == false) {
							 | 
						||
| 
								 | 
							
													if (eelse != null)
							 | 
						||
| 
								 | 
							
														run(eelse);
							 | 
						||
| 
								 | 
							
												} else
							 | 
						||
| 
								 | 
							
													run(eif);
							 | 
						||
| 
								 | 
							
											case OpStr(str):
							 | 
						||
| 
								 | 
							
												buf.add(str);
							 | 
						||
| 
								 | 
							
											case OpBlock(l):
							 | 
						||
| 
								 | 
							
												for (e in l)
							 | 
						||
| 
								 | 
							
													run(e);
							 | 
						||
| 
								 | 
							
											case OpForeach(e, loop):
							 | 
						||
| 
								 | 
							
												var v:Dynamic = e();
							 | 
						||
| 
								 | 
							
												try {
							 | 
						||
| 
								 | 
							
													var x:Dynamic = v.iterator();
							 | 
						||
| 
								 | 
							
													if (x.hasNext == null)
							 | 
						||
| 
								 | 
							
														throw null;
							 | 
						||
| 
								 | 
							
													v = x;
							 | 
						||
| 
								 | 
							
												} catch (e:Dynamic)
							 | 
						||
| 
								 | 
							
													try {
							 | 
						||
| 
								 | 
							
														if (v.hasNext == null)
							 | 
						||
| 
								 | 
							
															throw null;
							 | 
						||
| 
								 | 
							
													} catch (e:Dynamic) {
							 | 
						||
| 
								 | 
							
														throw "Cannot iter on " + v;
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
												stack.push(context);
							 | 
						||
| 
								 | 
							
												var v:Iterator<Dynamic> = v;
							 | 
						||
| 
								 | 
							
												for (ctx in v) {
							 | 
						||
| 
								 | 
							
													context = ctx;
							 | 
						||
| 
								 | 
							
													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(Std.string(Reflect.callMethod(macros, v, pl)));
							 | 
						||
| 
								 | 
							
												} catch (e:Dynamic) {
							 | 
						||
| 
								 | 
							
													var plstr = try pl.join(",") catch (e:Dynamic) "???";
							 | 
						||
| 
								 | 
							
													var msg = "Macro call " + m + "(" + plstr + ") failed (" + Std.string(e) + ")";
							 | 
						||
| 
								 | 
							
													#if neko
							 | 
						||
| 
								 | 
							
													neko.Lib.rethrow(msg);
							 | 
						||
| 
								 | 
							
													#else
							 | 
						||
| 
								 | 
							
													throw msg;
							 | 
						||
| 
								 | 
							
													#end
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 |