forked from LeenkxTeam/LNXSDK
Update Files
This commit is contained in:
508
Kha/Tools/macos/std/haxe/Template.hx
Normal file
508
Kha/Tools/macos/std/haxe/Template.hx
Normal file
@ -0,0 +1,508 @@
|
||||
/*
|
||||
* 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user