[gui] support yaml in builder style

This commit is contained in:
2018-02-15 23:56:46 +03:00
parent 5b4e8dbb5e
commit 97b7686694
3 changed files with 223 additions and 210 deletions

View File

@@ -4,7 +4,7 @@
"license": "BSD", "license": "BSD",
"tags": ["flash", "openfl"], "tags": ["flash", "openfl"],
"description": "Framework.", "description": "Framework.",
"version": "0.7.0", "version": "0.7.1",
"releasenote": "Update.", "releasenote": "Update.",
"contributors": ["shmyga"], "contributors": ["shmyga"],
"classPath": "src/main", "classPath": "src/main",

View File

@@ -1,6 +1,7 @@
package haxework.gui.build; package haxework.gui.build;
#if macro #if macro
import haxework.gui.build.BuilderUtil;
import haxe.macro.Context; import haxe.macro.Context;
import haxework.gui.build.PositionJsonParser; import haxework.gui.build.PositionJsonParser;
import haxe.macro.Expr; import haxe.macro.Expr;
@@ -8,201 +9,204 @@ import haxe.macro.Expr.Field;
class Builder { class Builder {
private var templateFile:String; private var templateFile:String;
private var templateKey:String; private var templateKey:String;
private var styleFile:String; private var styleFile:String;
private var template:Dynamic; private var template:Dynamic;
private var fields:Array<Field>; private var fields:Array<Field>;
private var exprs:Array<Expr>; private var exprs:Array<Expr>;
private var style:Dynamic; private var style:Dynamic;
private var i:Int; private var i:Int;
public function new() { public function new() {
var templateMeta = BuilderUtil.getMeta(":template"); var templateMeta = BuilderUtil.getMeta(":template");
var templatePath = templateMeta[0].split("@"); var templatePath = templateMeta[0].split("@");
templateFile = Context.resolvePath(templatePath[0]); templateFile = Context.resolvePath(templatePath[0]);
templateKey = templatePath[1]; templateKey = templatePath[1];
var ext = templateFile.split('.').pop(); template = BuilderUtil.loadFile(templateFile);
template = switch(ext) { if (templateKey != null) template = Reflect.field(template, templateKey);
case 'json': BuilderUtil.loadJsonFile(templateFile);
case 'yml' | 'yaml': BuilderUtil.loadYamlFile(templateFile);
case x: throw 'Unsupported template format: "${x}"';
}
if (templateKey != null) template = Reflect.field(template, templateKey);
if (templateMeta[1] != null) { if (templateMeta[1] != null) {
styleFile = Context.resolvePath(templateMeta[1]); styleFile = Context.resolvePath(templateMeta[1]);
style = BuilderUtil.loadJsonFile(styleFile); style = BuilderUtil.loadFile(styleFile);
}
fields = Context.getBuildFields();
exprs = [];
i = 0;
}
private function getPosition(?position:JsonKeyPosition):Position {
var min = position == null ? 1 : position.min + 32; // :-(
var max = position == null ? 1 : position.max + 32;
var file = position == null || position.file == null ? templateFile : position.file;
return Context.makePosition({min:min, max:max, file:file});
}
private function specialValue(name:String, key:String, a:Array<String>, position:JsonKeyPosition):Dynamic {
return switch (a[0]) {
case "asset":
switch (a[1]) {
case "image":
"openfl.Assets.getBitmapData(\"" + a[2] + "\")";
case _:
a[2];
} }
case "res":
var res = "haxework.provider.Provider.get(haxework.resources.IResources)." + a[1]; fields = Context.getBuildFields();
var bindExpr = res + ".bind(\"" + a[2] + "\", " + name + ", \"" + key + "\")"; exprs = [];
exprs.push(Context.parse(bindExpr, getPosition(position))); i = 0;
//res + ".get(\"" + a[2] + "\")";
null;
case "locale":
"new haxework.locale.LString(\"" + a[1] + "\")";
case "class":
a[1];
case "layout":
var template = BuilderUtil.loadJsonFile(a[1]);
return getValue(name, key, template, position);
case "link":
"(links == null) ? untyped this : Reflect.field(links, \"" + a[1] + "\")";
case _:
Context.error("Unsupported prefix \"" + a[0] + "\"", getPosition(position));
} }
}
private function getType(value:Dynamic, position:Position):String { private static function getSpecField(object:Dynamic, field:String):Dynamic {
if (Reflect.hasField(value, "@type")) { if (Reflect.hasField(object, "@" + field)) {
return Reflect.field(value, "@type"); return Reflect.field(object, "@" + field);
} else if (Reflect.hasField(value, "$type")) { } else if (Reflect.hasField(object, "$" + field)) {
return Reflect.field(value, "$type"); return Reflect.field(object, "$" + field);
} else {
Context.error("Need @type field", position);
return null;
}
}
private function getValue(name:String, key:String, value:Dynamic, position:JsonKeyPosition):Dynamic {
return if (Std.is(value, Array)) {
value.map(function(v) {
return getValue(null, null, v, position);
});
} else if (Std.is(value, String)) {
if (value.charAt(0) == "@") {
specialValue(name, key, value.substring(1, value.length).split(":"), position);
} else if (~/(0x|#)[A-Fa-f\d]{6}/.match(value)) {
Std.parseInt(StringTools.replace(Std.string(value), "#", "0x"));
} else {
"\"" + value + "\"";
}
} else if (Std.is(value, Float) || (Std.is(value, Bool))) {
value;
} else if (value != null) {
var type = getType(value, getPosition(position));
if (type != null) {
var n = "a" + i++;
if (type == "Dynamic") {
//ToDo:
exprs.push(Context.parse("var " + n + " = cast {}", getPosition(position)));
} else { } else {
exprs.push(Context.parse("var " + n + " = new " + type + "()", getPosition(position))); return null;
} }
createElement(value, n);
n;
} else {
null;
}
} else {
value;
} }
}
private function createElement(template:Dynamic, name:String):String { private function getPosition(?position:JsonKeyPosition):Position {
if (Reflect.hasField(template, "@style")) { var min = position == null ? 1 : position.min + 32; // :-(
var s = Reflect.field(style, Reflect.field(template, "@style")); var max = position == null ? 1 : position.max + 32;
for (key in Reflect.fields(s)) { var file = position == null || position.file == null ? templateFile : position.file;
if (key.charAt(0) != "$" && !Reflect.hasField(template, key)) { return Context.makePosition({min:min, max:max, file:file});
Reflect.setField(template, key, Reflect.field(s, key)); }
Reflect.setField(template, "$" + key, Reflect.field(s, "$" + key));
private function specialValue(name:String, key:String, a:Array<String>, position:JsonKeyPosition):Dynamic {
return switch (a[0]) {
case "asset":
switch (a[1]) {
case "image":
"openfl.Assets.getBitmapData(\"" + a[2] + "\")";
case _:
a[2];
}
case "res":
var res = "haxework.provider.Provider.get(haxework.resources.IResources)." + a[1];
var bindExpr = res + ".bind(\"" + a[2] + "\", " + name + ", \"" + key + "\")";
exprs.push(Context.parse(bindExpr, getPosition(position)));
//res + ".get(\"" + a[2] + "\")";
null;
case "locale":
"new haxework.locale.LString(\"" + a[1] + "\")";
case "class":
a[1];
case "layout":
var template = BuilderUtil.loadJsonFile(a[1]);
return getValue(name, key, template, position);
case "link":
"(links == null) ? untyped this : Reflect.field(links, \"" + a[1] + "\")";
case _:
Context.error("Unsupported prefix \"" + a[0] + "\"", getPosition(position));
} }
}
} }
if (Reflect.hasField(template, "id")) { private static function getType(value:Dynamic, position:Position):String {
var id = Reflect.field(template, "id"); var type:String = getSpecField(value, "type");
var type = getType(template, getPosition()); if (type == null) {
var expr = Context.parse("var a:" + type, getPosition()); Context.error("Need @type field", position);
var complexType = switch (expr.expr) { }
case EVars(vars): vars[0].type; return type;
case _: null;
}
fields.push({
name: id,
access: [APublic],
pos: getPosition(),
kind: FProp("default", "null", complexType)
});
exprs.push(Context.parse("this." + id + " = " + name, getPosition()));
} }
for (key in Reflect.fields(template)) { private function getValue(name:String, key:String, value:Dynamic, position:JsonKeyPosition):Dynamic {
if (key.charAt(0) == "$" || key.charAt(0) == "@") continue; return if (Std.is(value, Array)) {
var position = Reflect.field(template, "$" + key); value.map(function(v) {
var value = getValue(name, key, Reflect.field(template, key), position); return getValue(null, null, v, position);
if (value != null) { });
exprs.push(Context.parse(name + "." + key + " = " + value, getPosition(position))); } else if (Std.is(value, String)) {
} if (value.charAt(0) == "@" || value.charAt(0) == "$") {
} specialValue(name, key, value.substring(1, value.length).split(":"), position);
return name; } else if (~/(0x|#)[A-Fa-f\d]{6}/.match(value)) {
} Std.parseInt(StringTools.replace(Std.string(value), "#", "0x"));
} else {
"\"" + value + "\"";
public function build():Array<Field> { }
createElement(template, "this"); } else if (Std.is(value, Float) || (Std.is(value, Bool))) {
value;
var init = false; } else if (value != null) {
for (f in fields) if (f.name == "init") { var type = getType(value, getPosition(position));
init = true; if (type != null) {
break; var n = "a" + i++;
if (type == "Dynamic") {
//ToDo:
exprs.push(Context.parse("var " + n + " = cast {}", getPosition(position)));
} else {
exprs.push(Context.parse("var " + n + " = new " + type + "()", getPosition(position)));
}
createElement(value, n);
n;
} else {
null;
}
} else {
value;
}
} }
fields.push({ private function createElement(template:Dynamic, name:String):String {
name: "build", var s = getSpecField(template, "style");
access: [APublic], if (s != null) {
pos: getPosition(), var s = Reflect.field(style, s);
kind: FFun({ for (key in Reflect.fields(s)) {
args: [{name:"links", type:TPath({name:"Dynamic", pack:[], params:[]}), opt:true, value:null}], if (key.charAt(0) != "$" && !Reflect.hasField(template, key)) {
expr: macro $b{exprs}, Reflect.setField(template, key, Reflect.field(s, key));
params: [], Reflect.setField(template, "$" + key, Reflect.field(s, "$" + key));
ret: null }
}) }
}); }
var contstrExprs = []; if (Reflect.hasField(template, "id")) {
contstrExprs.push(macro super()); var id = Reflect.field(template, "id");
contstrExprs.push(macro build(links)); var type = getType(template, getPosition());
if (init) contstrExprs.push(macro init()); var expr = Context.parse("var a:" + type, getPosition());
var complexType = switch (expr.expr) {
case EVars(vars): vars[0].type;
case _: null;
}
fields.push({
name: id,
access: [APublic],
pos: getPosition(),
kind: FProp("default", "null", complexType)
});
exprs.push(Context.parse("this." + id + " = " + name, getPosition()));
}
fields.push({ for (key in Reflect.fields(template)) {
name: "new", if (key.charAt(0) == "$" || key.charAt(0) == "@") continue;
access: [APublic], var position = Reflect.field(template, "$" + key);
pos: getPosition(), var value = getValue(name, key, Reflect.field(template, key), position);
kind: FFun({ if (value != null) {
args: [{name:"links", type:TPath({name:"Dynamic", pack:[], params:[]}), opt:true, value:null}], exprs.push(Context.parse(name + "." + key + " = " + value, getPosition(position)));
expr: macro $b{contstrExprs}, }
params: [], }
ret: null return name;
}) }
});
return fields;
} public function build():Array<Field> {
createElement(template, "this");
var init = false;
for (f in fields) if (f.name == "init") {
init = true;
break;
}
fields.push({
name: "build",
access: [APublic],
pos: getPosition(),
kind: FFun({
args: [{name:"links", type:TPath({name:"Dynamic", pack:[], params:[]}), opt:true, value:null}],
expr: macro $b{exprs},
params: [],
ret: null
})
});
var contstrExprs = [];
contstrExprs.push(macro super());
contstrExprs.push(macro build(links));
if (init) contstrExprs.push(macro init());
fields.push({
name: "new",
access: [APublic],
pos: getPosition(),
kind: FFun({
args: [{name:"links", type:TPath({name:"Dynamic", pack:[], params:[]}), opt:true, value:null}],
expr: macro $b{contstrExprs},
params: [],
ret: null
})
});
return fields;
}
} }
#end #end

View File

@@ -9,44 +9,53 @@ import haxe.macro.Expr.ExprDef;
class BuilderUtil { class BuilderUtil {
public static function loadJsonFile(path:String) { public static function loadJsonFile(path:String):Dynamic {
Context.registerModuleDependency(Context.getLocalModule(), path); Context.registerModuleDependency(Context.getLocalModule(), path);
var content = sys.io.File.getContent(path); var content = sys.io.File.getContent(path);
var json = null; var json = null;
try { try {
json = PositionJsonParser.parse(content, path); json = PositionJsonParser.parse(content, path);
} catch(error:Dynamic) { } catch (error:Dynamic) {
Context.error(error, Context.makePosition({min:0, max:0, file:path})); Context.error(error, Context.makePosition({min:0, max:0, file:path}));
}
Context.parse(content, Context.makePosition({min:0, max:0, file:path}));
return json;
} }
Context.parse(content, Context.makePosition({min:0, max:0, file:path}));
return json;
}
public static function loadYamlFile(path:String) { public static function loadYamlFile(path:String):Dynamic {
Context.registerModuleDependency(Context.getLocalModule(), path); Context.registerModuleDependency(Context.getLocalModule(), path);
var content = sys.io.File.getContent(path); var content = sys.io.File.getContent(path);
var result = null; var result = null;
try { try {
// ToDo: extract poisiton info // ToDo: extract poisiton info
result = Yaml.parse(content, Parser.options().useObjects()); result = Yaml.parse(content, Parser.options().useObjects());
} catch(error:Dynamic) { } catch (error:Dynamic) {
Context.error(error, Context.makePosition({min:0, max:0, file:path})); Context.error(error, Context.makePosition({min:0, max:0, file:path}));
}
//Context.parse(content, Context.makePosition({min:0, max:0, file:path}));
return result;
} }
//Context.parse(content, Context.makePosition({min:0, max:0, file:path}));
return result;
}
public static function getMeta(key:String):Array<String> { public static function loadFile(path:String):Dynamic {
var c = Context.getLocalClass().get(); var ext = path.split('.').pop();
for (meta in c.meta.get()) { return switch(ext) {
if (meta.name == key) { case 'json': BuilderUtil.loadJsonFile(path);
return meta.params.map(function(param) return switch(param.expr) { case 'yml' | 'yaml': BuilderUtil.loadYamlFile(path);
case ExprDef.EConst(Constant.CString(value)): value; case x: throw 'Unsupported template format: "${x}"';
case _: null; }
}); }
}
public static function getMeta(key:String):Array<String> {
var c = Context.getLocalClass().get();
for (meta in c.meta.get()) {
if (meta.name == key) {
return meta.params.map(function(param) return switch(param.expr) {
case ExprDef.EConst(Constant.CString(value)): value;
case _: null;
});
}
}
return [];
} }
return [];
}
} }
#end #end