From a06011858998d84fa1102e89e7f3e124f9afb8de Mon Sep 17 00:00:00 2001 From: shmyga Date: Mon, 13 Jul 2015 14:13:38 +0300 Subject: [PATCH] view builder update --- haxework/gui/Builder.hx | 160 ---------------- haxework/gui/GuiBuilder.hx | 210 -------------------- haxework/gui/ViewBuilder.hx | 198 +------------------ haxework/gui/build/Builder.hx | 181 ++++++++++++++++++ haxework/gui/build/BuilderUtil.hx | 30 +++ haxework/gui/build/PositionJsonParser.hx | 233 +++++++++++++++++++++++ 6 files changed, 445 insertions(+), 567 deletions(-) delete mode 100644 haxework/gui/Builder.hx delete mode 100755 haxework/gui/GuiBuilder.hx create mode 100755 haxework/gui/build/Builder.hx create mode 100755 haxework/gui/build/BuilderUtil.hx create mode 100755 haxework/gui/build/PositionJsonParser.hx diff --git a/haxework/gui/Builder.hx b/haxework/gui/Builder.hx deleted file mode 100644 index 379b45c..0000000 --- a/haxework/gui/Builder.hx +++ /dev/null @@ -1,160 +0,0 @@ -package haxework.gui; - -import haxework.resources.Resources.ResMap; -import haxework.resources.IResources; -import haxework.provider.Provider; -import openfl.Assets; -import haxework.locale.LString; -import haxe.Json; -import flash.errors.TypeError; -import flash.errors.Error; - -enum Value { - SIMPLE(v:Dynamic); - RESOURCE(res:ResMap, key:String); - LINK(key:String); - BUILDER(v:Builder); - ARRAY(v:Array); -} - -class Field { - - public var key(default, null):String; - public var value(default, null):Value; - - public function new(key:String, value:Dynamic) { - this.key = key; - this.value = fieldValue(value); - } - - public static function buildValue(value:Value, ?links:Dynamic):Dynamic { - return switch (value) { - case Value.SIMPLE(v): - v; - case Value.BUILDER(v): - v.build(links); - case Value.ARRAY(v): - v.map(function(i) return buildValue(i, links)); - case Value.RESOURCE(r, k): - r.get(k); - case Value.LINK(k): - Reflect.field(links, k); - } - } - - private static function specialValue(value):Value { - var a = value.split(":"); - return switch (a[0]) { - case "asset": - switch (a[1]) { - case "image": - Value.SIMPLE(Assets.getBitmapData(a[2])); - case _: - Value.SIMPLE(value); - } - case "res": - //Reflect.field(Provider.get(IResources), a[1]).bind(a[2], object, field); - Value.RESOURCE(Reflect.field(Provider.get(IResources), a[1]), a[2]); - case "locale": - Value.SIMPLE(new LString(a[1])); - case "class": - Value.SIMPLE(Type.resolveClass(a[1])); - case "layout": - var data = Json.parse(Assets.getText(a[1])); - Value.BUILDER(new Builder(data)); - case "link": - Value.LINK(a[1]); //ToDo - case _: - throw new Error("Unsupported prefix \"" + a[0] + "\""); - //Value.SIMPLE(value); - } - } - - private static function fieldValue(value:Dynamic):Value { - return if (Std.is(value, Array)) { - Value.ARRAY(value.map(fieldValue)); - } else if (Std.is(value, String)) { - if (value.charAt(0) == "@") { - specialValue(value.substr(1)); - } else if (~/0x[A-Fa-f\d]{6}/.match(value)) { - Value.SIMPLE(Std.parseInt(value)); - } else { - Value.SIMPLE(value); - } - } else if (Std.is(value, Float) || (Std.is(value, Bool))) { - Value.SIMPLE(value); - } else if (value != null) { - if (Reflect.hasField(value, "type")) { - Value.BUILDER(new Builder(value)); - } else { - Value.SIMPLE(value); - } - } else { - Value.SIMPLE(value); - } - } -} - -@:deprecated("Use haxework.gui.ViewBuilder") -class Builder { - - private var factory:Class; - private var fields:Map; - - public function new(data:Dynamic) { - if (Reflect.hasField(data, "type")) { - var type = Reflect.field(data, "type"); - this.factory = Type.resolveClass(type); - if (factory == null) throw new TypeError("Class \"" + type + "\" not found"); - } - this.fields = new Map(); - if (Reflect.hasField(data, "style")) { - var style = Provider.get(IResources).styles.get(Reflect.field(data, "style")); - for (key in Reflect.fields(style)) { - fields.set(key, new Field(key, Reflect.field(style, key))); - } - } - for (key in Reflect.fields(data)) { - switch (key) { - case "type": - case "style": - case _: fields.set(key, new Field(key, Reflect.field(data, key))); - } - } - } - - public function build(?links:Dynamic):Dynamic { - var instance:Dynamic = Type.createInstance(factory, []); - instance = fill(instance, links); - var initMethod:Dynamic = Reflect.field(instance, "init"); - if (initMethod != null) Reflect.callMethod(instance, initMethod, []); - return instance; - } - - public function fill(instance:Dynamic, ?links:Dynamic):Dynamic { - for (field in fields) { - Reflect.setProperty(instance, field.key, Field.buildValue(field.value, links)); - switch (field.value) { - case Value.RESOURCE(r, k): - r.bind(k, instance, field.key); - case _: - } - } - return instance; - } - - - private static var cache:Map = new Map(); - - public static function createFromAsset(asset:String, ?key:String):Dynamic { - var cacheKey = asset + ":" + key; - if (!cache.exists(cacheKey)) { - var data = Json.parse(openfl.Assets.getText(asset)); - if (key != null) { - data = Reflect.field(data, key); - } - cache.set(cacheKey, new Builder(data)); - } - return cache.get(cacheKey); - } -} \ No newline at end of file diff --git a/haxework/gui/GuiBuilder.hx b/haxework/gui/GuiBuilder.hx deleted file mode 100755 index 8206234..0000000 --- a/haxework/gui/GuiBuilder.hx +++ /dev/null @@ -1,210 +0,0 @@ -package haxework.gui; - - -//ToDo: -import haxework.locale.LString; -import haxe.Json; -import flash.errors.Error; -import openfl.Assets; -import flash.errors.TypeError; -import flash.errors.ArgumentError; -import haxework.resources.IResources; -import haxework.provider.Provider; -import haxework.gui.View; -import haxework.gui.SpriteView; -import haxework.gui.MovieView; -import haxework.gui.GroupView; -import haxework.gui.HGroupView; -import haxework.gui.VGroupView; -import haxework.gui.TextView; -import haxework.gui.InputView; -import haxework.gui.LabelView; -import haxework.gui.ButtonView; -import haxework.gui.ToggleButtonView; -import haxework.gui.ProgressView; -import haxework.gui.AnimateView; -import haxework.gui.skin.ColorSkin; -import haxework.gui.skin.BitmapSkin; -import haxework.gui.skin.ButtonColorSkin; -import haxework.gui.skin.ButtonBitmapSkin; -import haxework.gui.skin.ProgressSkin; -import haxework.gui.frame.FrameSwitcher; - -@:deprecated("Use haxework.gui.ViewBuilder") -class GuiBuilder { - - private static var cache:Map = new Map(); - - private static function getResource(resource:String):Dynamic { - if (!cache.exists(resource)) { - cache.set(resource, Json.parse(Assets.getText(resource))); - } - return cache.get(resource); - } - - public static function build(data:Dynamic, ?links:Dynamic):Dynamic { - return new GuiB(data, links, data._includes).build(); - } - - public static function fill(object:Dynamic, data:Dynamic, ?links:Dynamic):Void { - new GuiF(object, data, links, data._includes).fill(); - } - - public static function buildFromAssets(resource:String, ?key:String = null, ?links:Dynamic = null):Dynamic { - var form:Dynamic = getResource(resource); - if (key != null) form = Reflect.field(form, key); - return build(form, links); - } - - public static function fillFromAssets(object:Dynamic, resource:String, ?key:String = null, ?links:Dynamic = null):Void { - var form:Dynamic = getResource(resource); - if (key != null) form = Reflect.field(form, key); - fill(object, form, links); - } -} - -class GuiB { - - private var data:Dynamic; - private var links:Dynamic; - private var includes:Dynamic; - - public function new(data:Dynamic, ?links:Dynamic, ?includes:Dynamic) { - this.data = data; - this.links = links; - this.includes = includes; - } - - public function build():Dynamic { - if (Reflect.hasField(data, "type")) { - var type:String = data.type; - //Reflect.deleteField(data, "type"); - if (type.charAt(0) == "~") return Type.resolveClass(type.substr(1)); - var object:Dynamic = instance(type); - new GuiF(object, data, links, includes).fill(); - var initMethod:Dynamic = Reflect.field(object, "init"); - if (initMethod != null) Reflect.callMethod(object, initMethod, []); - return object; - } else if (Std.is(data, String)) { - return GuiF.convertString(data, links); - } else { - new GuiF(data, data, links, includes).fill(); - return data; - } - } - - private static function instance(type:String):Dynamic { - var clazz:Class = Type.resolveClass(type); - if (clazz == null) throw new TypeError("Class \"" + type + "\" not found"); - var instance:Dynamic = Type.createInstance(clazz, []); - return instance; - } -} - -class GuiF { - - private var object:Dynamic; - private var data:Dynamic; - private var links:Dynamic; - private var includes:Dynamic; - - public function new(object:Dynamic, data:Dynamic, ?links:Dynamic, ?includes:Dynamic) { - this.object = object; - this.data = data; - this.links = links; - this.includes = includes; - } - - public static function convertString(value:Dynamic, ?links:Dynamic):Dynamic { - var s:String = cast(value, String); - var c:String = s.charAt(0); - if (c == "#") { - value = Reflect.field(links, s.substr(1)); - } else if (c == "~") { - var a:Array = s.substr(1).split(":"); - var e:Enum = Type.resolveEnum(a[0]); - value = Type.createEnum(e, a[1]); - } else if (c == "@") { - if (s.charAt(1) == "~") { - var a:Array = s.substr(2).split(":"); - switch (a[0]) { - case "image": value = Assets.getBitmapData(a[1]); - case "layout": value = GuiBuilder.buildFromAssets(a[1], null, links); - } - } else { - var a:Array = s.substr(1).split(":"); - value = Reflect.field(Provider.get(IResources), a[0]).get(a[1]); - } - } else if (~/0x[A-Fa-f\d]{6}/.match(value)) { - value = Std.parseInt(value); - } - return value; - } - - public function fill() { - if (Reflect.hasField(data, "style")) { - var style = Provider.get(IResources).styles.get(Reflect.field(data, "style")); - for (key in Reflect.fields(style)) if (!Reflect.hasField(data, key)) { - Reflect.setField(data, key, Reflect.field(style, key)); - } - } - var fields:Array = Reflect.fields(data); - for (field in fields) { - if (field == "type" || field == "style") continue; - if (field == "_includes") continue; - var value:Dynamic = Reflect.field(data, field); - if (field == "_include") { - var data:Dynamic = includes == null ? null : Reflect.field(includes, value); - if (data != null) { - new GuiF(object, data, links).fill(); - } - continue; - } - if (Std.is(value, Array)) { - var a:Array = []; - for (o in cast(value, Array)) a.push(new GuiB(o, links, includes).build()); - value = a; - } else if (Std.is(value, String)) { - var s:String = cast(value, String); - var c:String = s.charAt(0); - if (c == "#") { - value = Reflect.field(links, s.substr(1)); - } else if (c == "~") { - var a:Array = s.substr(1).split(":"); - var e:Enum = Type.resolveEnum(a[0]); - value = Type.createEnum(e, a[1]); - } else if (c == "@") { - var c1 = s.charAt(1); - if (c1 == "~") { - var a:Array = s.substr(2).split(":"); - switch (a[0]) { - case "image": value = Assets.getBitmapData(a[1]); - } - } else if (c1 == "^") { - value = new LString(s.substr(2)); - } else { - var a:Array = s.substr(1).split(":"); - //value = Reflect.field(Provider.get(IResources), a[0]).get(a[1]); - Reflect.field(Provider.get(IResources), a[0]).bind(a[1], object, field); - continue; - } - } else if (~/0x[A-Fa-f\d]{6}/.match(value)) { - value = Std.parseInt(value); - } - } else if (Std.is(value, Float)) { - - } else if (Std.is(value, Bool)) { - - } else { - var o:Dynamic = new GuiB(value, links, includes).build(); - new GuiF(o, value, links).fill(); - value = o; - } - try { - Reflect.setProperty(object, field, value); - } catch(error:Dynamic) { - L.e("GuiBuilder", "", error); - } - } - } -} \ No newline at end of file diff --git a/haxework/gui/ViewBuilder.hx b/haxework/gui/ViewBuilder.hx index 3ddd23f..75d5733 100755 --- a/haxework/gui/ViewBuilder.hx +++ b/haxework/gui/ViewBuilder.hx @@ -1,206 +1,10 @@ package haxework.gui; -import haxe.Json; -import haxe.macro.Expr; -import haxe.macro.Context; +import haxework.gui.build.Builder; @:remove @:autoBuild(haxework.gui.ViewBuilderImpl.build()) extern interface ViewBuilder {} -#if macro -private class BuilderUtil { - - public static function loadFile(path:String, json:Bool = true) { - Context.registerModuleDependency(Context.getLocalModule(), path); - var content = sys.io.File.getContent(path); - return json ? Json.parse(content) : content; - } - - public static function getMeta(key:String):Array { - 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 []; - } -} - -private class Builder { - - private var templateFile:String; - private var templateKey:String; - private var styleFile:String; - - private var template:Dynamic; - private var fields:Array; - private var exprs:Array; - private var style:Dynamic; - - private var i:Int; - - public function new() { - var templateMeta = BuilderUtil.getMeta(":template"); - var templatePath = templateMeta[0].split("@"); - templateFile = Context.resolvePath(templatePath[0]); - templateKey = templatePath[1]; - - template = BuilderUtil.loadFile(templateFile); - if (templateKey != null) template = Reflect.field(template, templateKey); - - if (templateMeta[1] != null) { - styleFile = Context.resolvePath(templateMeta[1]); - style = BuilderUtil.loadFile(styleFile); - } - - fields = Context.getBuildFields(); - exprs = []; - i = 0; - } - - private function getPosition():Position { - return Context.makePosition({min:0, max:0, file:templateFile + (templateKey != null ? "@" + templateKey : "")}); - } - - private function specialValue(name:String, key:String, a:Array):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())); - //res + ".get(\"" + a[2] + "\")"; - null; - case "locale": - "new haxework.locale.LString(\"" + a[1] + "\")"; - case "class": - a[1]; - case "layout": - var template = BuilderUtil.loadFile(a[1]); - return getValue(name, key, template); - case "link": - "(links == null) ? untyped this : Reflect.field(links, \"" + a[1] + "\")"; - case _: - throw "Unsupported prefix \"" + a[0] + "\""; - } - } - - private function getValue(name:String, key:String, value:Dynamic):Dynamic { - return if (Std.is(value, Array)) { - value.map(function(v) { return getValue(null, null, v); }); - } else if (Std.is(value, String)) { - if (value.charAt(0) == "@") { - specialValue(name, key, value.substring(1, value.length).split(":")); - } 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) { - if (Reflect.hasField(value, "type")) { - var n = "a" + i++; - var type = Reflect.field(value, "type"); - exprs.push(Context.parse("var " + n + " = new " + type + "()", getPosition())); - createElement(value, n); - n; - } else { - value; - } - } else { - value; - } - } - - private function createElement(template:Dynamic, name:String):String { - if (Reflect.hasField(template, "style")) { - var s = Reflect.field(style, Reflect.field(template, "style")); - for (key in Reflect.fields(s)) if (!Reflect.hasField(template, key)) { - Reflect.setField(template, key, Reflect.field(s, key)); - } - } - - if (Reflect.hasField(template, "id")) { - var id = Reflect.field(template, "id"); - var type = Reflect.field(template, "type"); - var expr = Context.parse("var a:" + type, getPosition()); - var type = switch (expr.expr) { - case EVars(vars): vars[0].type; - case _: null; - } - fields.push({ - name: id, - access: [APublic], - pos: getPosition(), - kind: FProp("default", "null", type) - }); - exprs.push(Context.parse("this." + id + " = " + name, getPosition())); - } - - for (key in Reflect.fields(template)) { - if (["type", "style"].indexOf(key) > -1) continue; - var value = getValue(name, key, Reflect.field(template, key)); - if (value != null) { - exprs.push(Context.parse(name + "." + key + " = " + value, getPosition())); - } - } - return name; - } - - - public function build():Array { - 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 - class ViewBuilderImpl { #if macro public static function build() { diff --git a/haxework/gui/build/Builder.hx b/haxework/gui/build/Builder.hx new file mode 100755 index 0000000..cd7beac --- /dev/null +++ b/haxework/gui/build/Builder.hx @@ -0,0 +1,181 @@ +package haxework.gui.build; +#if macro + +import haxe.macro.Context; +import haxework.gui.build.PositionJsonParser; +import haxe.macro.Expr; +import haxe.macro.Expr.Field; + +class Builder { + + private var templateFile:String; + private var templateKey:String; + private var styleFile:String; + + private var template:Dynamic; + private var fields:Array; + private var exprs:Array; + private var style:Dynamic; + + private var i:Int; + + public function new() { + var templateMeta = BuilderUtil.getMeta(":template"); + var templatePath = templateMeta[0].split("@"); + templateFile = Context.resolvePath(templatePath[0]); + templateKey = templatePath[1]; + + template = BuilderUtil.loadFile(templateFile); + if (templateKey != null) template = Reflect.field(template, templateKey); + + if (templateMeta[1] != null) { + styleFile = Context.resolvePath(templateMeta[1]); + 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; + return Context.makePosition({min:min, max:max, file:templateFile}); + } + + private function specialValue(name:String, key:String, a:Array):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())); +//res + ".get(\"" + a[2] + "\")"; + null; + case "locale": + "new haxework.locale.LString(\"" + a[1] + "\")"; + case "class": + a[1]; + case "layout": + var template = BuilderUtil.loadFile(a[1]); + return getValue(name, key, template); + case "link": + "(links == null) ? untyped this : Reflect.field(links, \"" + a[1] + "\")"; + case _: + throw "Unsupported prefix \"" + a[0] + "\""; + } + } + + private function getValue(name:String, key:String, value:Dynamic):Dynamic { + return if (Std.is(value, Array)) { + value.map(function(v) { return getValue(null, null, v); }); + } else if (Std.is(value, String)) { + if (value.charAt(0) == "@") { + specialValue(name, key, value.substring(1, value.length).split(":")); + } 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) { + if (Reflect.hasField(value, "type")) { + var n = "a" + i++; + var type = Reflect.field(value, "type"); + exprs.push(Context.parse("var " + n + " = new " + type + "()", getPosition())); + createElement(value, n); + n; + } else { + value; + } + } else { + value; + } + } + + private function createElement(template:Dynamic, name:String):String { + if (Reflect.hasField(template, "style")) { + var s = Reflect.field(style, Reflect.field(template, "style")); + for (key in Reflect.fields(s)) if (!Reflect.hasField(template, key)) { + Reflect.setField(template, key, Reflect.field(s, key)); + } + } + + if (Reflect.hasField(template, "id")) { + var id = Reflect.field(template, "id"); + var type = Reflect.field(template, "type"); + var expr = Context.parse("var a:" + type, getPosition()); + var type = switch (expr.expr) { + case EVars(vars): vars[0].type; + case _: null; + } + fields.push({ + name: id, + access: [APublic], + pos: getPosition(), + kind: FProp("default", "null", type) + }); + exprs.push(Context.parse("this." + id + " = " + name, getPosition())); + } + + for (key in Reflect.fields(template)) { + if (key.charAt(0) == "$" || ["type", "style"].indexOf(key) > -1) continue; + var position = Reflect.field(template, "$" + key); + var value = getValue(name, key, Reflect.field(template, key)); + if (value != null) { + exprs.push(Context.parse(name + "." + key + " = " + value, getPosition(position))); + } + } + return name; + } + + + public function build():Array { + 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 \ No newline at end of file diff --git a/haxework/gui/build/BuilderUtil.hx b/haxework/gui/build/BuilderUtil.hx new file mode 100755 index 0000000..b13c729 --- /dev/null +++ b/haxework/gui/build/BuilderUtil.hx @@ -0,0 +1,30 @@ +package haxework.gui.build; +#if macro + +import haxe.macro.Context; +import haxe.macro.Expr.Constant; +import haxe.macro.Expr.ExprDef; + +class BuilderUtil { + + public static function loadFile(path:String, json:Bool = true) { + Context.registerModuleDependency(Context.getLocalModule(), path); + var content = sys.io.File.getContent(path); + Context.parse(content, Context.makePosition({min:0, max:0, file:path})); + return json ? PositionJsonParser.parse(content) : content; + } + + public static function getMeta(key:String):Array { + 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 []; + } +} +#end \ No newline at end of file diff --git a/haxework/gui/build/PositionJsonParser.hx b/haxework/gui/build/PositionJsonParser.hx new file mode 100755 index 0000000..bb6d45f --- /dev/null +++ b/haxework/gui/build/PositionJsonParser.hx @@ -0,0 +1,233 @@ +package haxework.gui.build; + +typedef JsonKeyPosition = { + var min:Int; + var max:Int; +} + +class PositionJsonParser { + + static public inline function parse(str : String) : Dynamic { + return new PositionJsonParser(str).parseRec(); + } + + var str : String; + var pos : Int; + + function new( str : String ) { + this.str = str; + this.pos = 0; + } + + function getKeyPosition():JsonKeyPosition { + return { + min: pos, + max: pos + } + } + + function parseRec() : Dynamic { + while( true ) { + var c = nextChar(); + switch( c ) { + case ' '.code, '\r'.code, '\n'.code, '\t'.code: +// loop + case '{'.code: + var obj = {}, field = null, comma : Null = null; + var position = null; + while( true ) { + var c = nextChar(); + switch( c ) { + case ' '.code, '\r'.code, '\n'.code, '\t'.code: +// loop + case '}'.code: + if( field != null || comma == false ) + invalidChar(); + return obj; + case ':'.code: + if( field == null ) + invalidChar(); + Reflect.setField(obj,field,parseRec()); + Reflect.setField(obj,"$" + field,position); + field = null; + comma = true; + case ','.code: + if( comma ) comma = false else invalidChar(); + case '"'.code: + if( comma ) invalidChar(); + position = getKeyPosition(); + field = parseString(); + default: + invalidChar(); + } + } + case '['.code: + var arr = [], comma : Null = null; + while( true ) { + var c = nextChar(); + switch( c ) { + case ' '.code, '\r'.code, '\n'.code, '\t'.code: +// loop + case ']'.code: + if( comma == false ) invalidChar(); + return arr; + case ','.code: + if( comma ) comma = false else invalidChar(); + default: + if( comma ) invalidChar(); + pos--; + arr.push(parseRec()); + comma = true; + } + } + case 't'.code: + var save = pos; + if( nextChar() != 'r'.code || nextChar() != 'u'.code || nextChar() != 'e'.code ) { + pos = save; + invalidChar(); + } + return true; + case 'f'.code: + var save = pos; + if( nextChar() != 'a'.code || nextChar() != 'l'.code || nextChar() != 's'.code || nextChar() != 'e'.code ) { + pos = save; + invalidChar(); + } + return false; + case 'n'.code: + var save = pos; + if( nextChar() != 'u'.code || nextChar() != 'l'.code || nextChar() != 'l'.code ) { + pos = save; + invalidChar(); + } + return null; + case '"'.code: + return parseString(); + case '0'.code, '1'.code,'2'.code,'3'.code,'4'.code,'5'.code,'6'.code,'7'.code,'8'.code,'9'.code,'-'.code: + return parseNumber(c); + default: + invalidChar(); + } + } + } + + function parseString() { + var start = pos; + var buf = null; + while( true ) { + var c = nextChar(); + if( c == '"'.code ) + break; + if( c == '\\'.code ) { + if (buf == null) { + buf = new StringBuf(); + } + buf.addSub(str,start, pos - start - 1); + c = nextChar(); + switch( c ) { + case "r".code: buf.addChar("\r".code); + case "n".code: buf.addChar("\n".code); + case "t".code: buf.addChar("\t".code); + case "b".code: buf.addChar(8); + case "f".code: buf.addChar(12); + case "/".code, '\\'.code, '"'.code: buf.addChar(c); + case 'u'.code: + var uc = Std.parseInt("0x" + str.substr(pos, 4)); + pos += 4; +#if (neko || php || cpp) + if( uc <= 0x7F ) + buf.addChar(uc); + else if( uc <= 0x7FF ) { + buf.addChar(0xC0 | (uc >> 6)); + buf.addChar(0x80 | (uc & 63)); + } else if( uc <= 0xFFFF ) { + buf.addChar(0xE0 | (uc >> 12)); + buf.addChar(0x80 | ((uc >> 6) & 63)); + buf.addChar(0x80 | (uc & 63)); + } else { + buf.addChar(0xF0 | (uc >> 18)); + buf.addChar(0x80 | ((uc >> 12) & 63)); + buf.addChar(0x80 | ((uc >> 6) & 63)); + buf.addChar(0x80 | (uc & 63)); + } +#else + buf.addChar(uc); +#end + default: + throw "Invalid escape sequence \\" + String.fromCharCode(c) + " at position " + (pos - 1); + } + start = pos; + } +#if (neko || php || cpp) +// ensure utf8 chars are not cut + else if( c >= 0x80 ) { + pos++; + if( c >= 0xFC ) pos += 4; + else if( c >= 0xF8 ) pos += 3; + else if( c >= 0xF0 ) pos += 2; + else if( c >= 0xE0 ) pos++; + } +#end + else if( StringTools.isEof(c) ) + throw "Unclosed string"; + } + if (buf == null) { + return str.substr(start, pos - start - 1); + } + else { + buf.addSub(str,start, pos - start - 1); + return buf.toString(); + } + } + + inline function parseNumber( c : Int ) : Dynamic { + var start = pos - 1; + var minus = c == '-'.code, digit = !minus, zero = c == '0'.code; + var point = false, e = false, pm = false, end = false; + while( true ) { + c = nextChar(); + switch( c ) { + case '0'.code : + if (zero && !point) invalidNumber(start); + if (minus) { + minus = false; zero = true; + } + digit = true; + case '1'.code,'2'.code,'3'.code,'4'.code,'5'.code,'6'.code,'7'.code,'8'.code,'9'.code : + if (zero && !point) invalidNumber(start); + if (minus) minus = false; + digit = true; zero = false; + case '.'.code : + if (minus || point) invalidNumber(start); + digit = false; point = true; + case 'e'.code, 'E'.code : + if (minus || zero || e) invalidNumber(start); + digit = false; e = true; + case '+'.code, '-'.code : + if (!e || pm) invalidNumber(start); + digit = false; pm = true; + default : + if (!digit) invalidNumber(start); + pos--; + end = true; + } + if (end) break; + } + var f = Std.parseFloat(str.substr(start, pos - start)); + var i = Std.int(f); + return if( i == f ) i else f; + } + + inline function nextChar() { + return StringTools.fastCodeAt(str,pos++); + } + + function invalidChar() { + pos--; // rewind + throw "Invalid char "+StringTools.fastCodeAt(str,pos)+" at position "+pos; + } + + function invalidNumber( start : Int ) { + throw "Invalid number at position "+start+": " + str.substr(start, pos - start); + } +}