diff --git a/dist/xrfragment.js b/dist/xrfragment.js index 5736e1d..ca263c4 100644 --- a/dist/xrfragment.js +++ b/dist/xrfragment.js @@ -204,10 +204,11 @@ xrfragment_Query.prototype = { return this.q; } }; -var xrfragment_Value = $hx_exports["xrfragment"]["Value"] = function() { -}; -var xrfragment_Url = function() { }; +var xrfragment_Url = $hx_exports["xrfragment"]["Url"] = function() { }; xrfragment_Url.parse = function(qs) { + var Frag_h = Object.create(null); + Frag_h["pos"] = xrfragment_Type.isVector; + Frag_h["prio"] = xrfragment_Type.isInt; var fragment = qs.split("#"); var splitArray = fragment[1].split("&"); var regexPlus = new EReg("\\+","g"); @@ -222,28 +223,33 @@ xrfragment_Url.parse = function(qs) { if(splitByEqual.length > 1) { var s = regexPlus.split(splitByEqual[1]).join(" "); var value = decodeURIComponent(s.split("+").join(" ")); - xrfragment_Url.guessType(v,value); - if(value.split("|").length > 1) { - v.args = []; - var args = value.split("|"); - var _g2 = 0; - var _g3 = args.length; - while(_g2 < _g3) { - var i1 = _g2++; - var x = new xrfragment_Value(); - xrfragment_Url.guessType(x,args[i1]); - v.args.push(x); + if(Object.prototype.hasOwnProperty.call(Frag_h,key)) { + if(Frag_h[key].match(value)) { + xrfragment_Url.guessType(v,value); + if(value.split("|").length > 1) { + v.args = []; + var args = value.split("|"); + var _g2 = 0; + var _g3 = args.length; + while(_g2 < _g3) { + var i1 = _g2++; + var x = new xrfragment_Value(); + xrfragment_Url.guessType(x,args[i1]); + v.args.push(x); + } + } + resultMap[key] = v; + } else { + console.log("src/xrfragment/Url.hx:46:","[ i ] fragment '" + key + "' has incompatible value (" + value + ")"); } + } else { + console.log("src/xrfragment/Url.hx:47:","[ i ] fragment '" + key + "' does not exist or has no type defined (yet)"); } - resultMap[key] = v; } } return resultMap; }; xrfragment_Url.guessType = function(v,str) { - var isColor = new EReg("^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$",""); - var isInt = new EReg("^[0-9]+$",""); - var isFloat = new EReg("^[0-9]+\\.[0-9]+$",""); v.string = str; if(str.split(",").length > 1) { var xyz = str.split(","); @@ -254,19 +260,22 @@ xrfragment_Url.guessType = function(v,str) { v.y = parseFloat(xyz[1]); } if(xyz.length > 2) { - v.z = parseFloat(xyz[2]); + v.y = parseFloat(xyz[2]); } } - if(isColor.match(str)) { + if(xrfragment_Type.isColor.match(str)) { v.color = str; } - if(isFloat.match(str)) { + if(xrfragment_Type.isFloat.match(str)) { v.float = parseFloat(str); } - if(isInt.match(str)) { + if(xrfragment_Type.isInt.match(str)) { v.int = Std.parseInt(str); } }; +var xrfragment_Value = function() { +}; +var xrfragment_Type = function() { }; if(typeof(performance) != "undefined" ? typeof(performance.now) == "function" : false) { HxOverrides.now = performance.now.bind(performance); } @@ -299,5 +308,10 @@ var xrfragment_Query_ok = $hx_exports["xrfragment"]["Query"]["ok"] = } } ; +xrfragment_Url.error = ""; +xrfragment_Type.isColor = new EReg("^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$",""); +xrfragment_Type.isInt = new EReg("^[0-9]+$",""); +xrfragment_Type.isFloat = new EReg("^[0-9]+\\.[0-9]+$",""); +xrfragment_Type.isVector = new EReg("([,]+|\\w)",""); })({}); var xrfragment = $hx_exports["xrfragment"]; diff --git a/doc/url.md b/doc/url.md index 4a4d767..7a07066 100644 --- a/doc/url.md +++ b/doc/url.md @@ -1,5 +1,31 @@ +> version 1.0.0 -# URI Value types + +# Fragment (values) + +| param | type | category | example | +|---------|---------------|-------------------------|------------------| +| pos | 3D vector | HREF navigation/portals | `#pos=1,0,1` or `#pos=foo` | +| prio | int (-10..1) | Asset linking | `#prio=-5` | + + +# Url parser (the gist of it) + +1. fragment URI starts with `#` +1. fragments are split by `&` +1. fragment-values are urlencoded (space becomes `+` using `encodeUriComponent` e.g.) +1. `=` is used to split fragment key/values +1. `|` is used to split multiple/fallback values +1. `,` assumes 1D/2D/3D vector-values like x[,y[,z]] +1. parseFloat(..) and parseInt(..) is applied to vector/float and int values +1. anything else will be treated as string-value +1. incompatible value-types will be dropped / not used + +> the xrfragment specification should stay simple enough +> for anyone to write a parser using either regexes or grammar/lexers +> therefore expressions/comprehensions are not supported (max wildcard/comparison operators for queries e.g.) + +# Value types | type | info | format | example | |------|------|--------|----------------------------------| @@ -12,16 +38,7 @@ > rule for thumb: type-limitations will piggyback JSON limitations (IEEE floatsize e.g.) -# Url parser (the gist of it) - -1. fragment URI starts with `#` -1. fragments are split by `&` -1. fragment-values are urlencoded (` ` becomes `+` and so on) -1. `=` is used to indicate fragmentvalues -1. `|` is used to indicate multiple/fallback values 1. hex colors are detected using regex `/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/` 1. integers are detected using regex `/^[0-9]+$/` 1. floats are detected using regex `/^[0-9]+\.[0-9]+$/` -1. `,` is used to detect vector 1D/2D/3D values like x[,y[,z]] -1. anything else will be treated as string-value -1. last resort: inappropriate string values will be converted using parseInt/parseFloat +1. vectors are detected using regex `/[,]/` (but can also be an string referring to an entity-ID in the asset) diff --git a/make b/make index 645e5d0..3dfdc82 100755 --- a/make +++ b/make @@ -1,5 +1,6 @@ #!/bin/sh set -e +VERSION=1.0.0 try(){ set +e; "$@" 2>/dev/null; set -e; } @@ -23,16 +24,16 @@ install(){ } tests(){ - { - which python3 && python3 test/generated/test.py src/spec/*.json | awk '{ print "py: "$0 } END{ print "\n"}' - which node && node test/generated/test.js src/spec/*.json | awk '{ print "js: "$0 } END{ print "\n"}' - } | tee /tmp/log.txt - grep error /tmp/log.txt && exit 1 || exit 0 + { + which python3 && python3 test/generated/test.py src/spec/*.json | awk '{ print "py: "$0 } END{ print "\n"}' + which node && node test/generated/test.js src/spec/*.json | awk '{ print "js: "$0 } END{ print "\n"}' + } | awk '$2 ~ /src/ { $2=sprintf("%-30s",$2); print $0; } 1' | tee /tmp/log.txt + grep error /tmp/log.txt && exit 1 || exit } doc(){ - extract(){ cat $1 | awk '/\/\/ / { gsub(".*// ","",$0); gsub("# ","\n# ",$0);print $0; }'; } - extract src/xrfragment/Url.hx > doc/url.md + extract(){ cat $1 | awk '/\/\/ / { gsub(".*// ","",$0); gsub("# ","\n# ",$0);print $0; }'; } + { echo "> version $VERSION\n" && extract src/xrfragment/Url.hx; } > doc/url.md } test -z $1 && { try rm dist/* ; haxe build.hxml; exit $?; } diff --git a/src/Test.hx b/src/Test.hx index 5548475..e76ab4c 100644 --- a/src/Test.hx +++ b/src/Test.hx @@ -11,16 +11,18 @@ class Spec { class Test { + static var errors:Int = 0; + static public function main():Void { test( Spec.load("src/spec/url.json") ); test( Spec.load("src/spec/query.class.json") ); test( Spec.load("src/spec/query.rules.json") ); //test( Spec.load("src/spec/tmp.json") ); + if( errors > 1 ) trace("\n-----\n[ ❌] "+errors+" errors :/"); } static public function test(spec:Array):Void { var Query = xrfragment.Query; - var errors:Int = 0; for( i in 0...spec.length ){ var q:Query = null; var res:haxe.DynamicAccess = null; @@ -31,6 +33,7 @@ class Test { if( item.expect.fn == "test" ) valid = item.expect.out == q.test( item.expect.input[0] ); if( item.expect.fn == "testProperty" ) valid = item.expect.out == q.testProperty( item.expect.input[0], item.expect.input[1] ); if( item.expect.fn == "testPropertyExclude" ) valid = item.expect.out == q.testProperty( item.expect.input[0], item.expect.input[1], true ); + if( item.expect.fn == "testParsed" ) valid = item.expect.out == res.exists(item.expect.input); if( item.expect.fn == "equal.string" ) valid = item.expect.out == res.get(item.expect.input).string; if( item.expect.fn == "equal.xy" ) valid = item.expect.out == (Std.string(res.get(item.expect.input).x) + Std.string(res.get(item.expect.input).y) ); if( item.expect.fn == "equal.multi" ) valid = equalMulti(res, item); @@ -38,17 +41,18 @@ class Test { trace( ok + item.fn + ": '" + item.data + "'" + (item.label ? " (" + (item.label?item.label:item.expect.fn) +")" : "")); if( !valid ) errors += 1; } - if( errors > 1 ) trace("\n-----\n[ ❌] "+errors+" errors :/"); } static public function equalMulti(res:haxe.DynamicAccess, item:Dynamic):Bool { +trace(res); var target:Dynamic = res.get(item.expect.input); var str:String = ""; + if( !target ) return false; for( i in 0...target.args.length ){ str = str + "|" + target.args[i].string; } str = str.substr(1); - return str == item.expect.out; + return item.expect.out ? str == item.expect.out : false; } static public function testUrl():Void { diff --git a/src/spec/tmp.json b/src/spec/tmp.json new file mode 100644 index 0000000..271ba69 --- /dev/null +++ b/src/spec/tmp.json @@ -0,0 +1,5 @@ +[ + {"fn":"query","data":"tag:foo", "expect":{ "fn":"testProperty","input":["tag","foo"],"out":true}}, + {"fn":"query","data":"-tag:foo", "expect":{ "fn":"testProperty","input":["tag","foo"],"out":false}}, + {"fn":"query","data":"-tag:foo", "expect":{ "fn":"testPropertyExclude","input":["tag","foo"],"out":true}} +] diff --git a/src/spec/url.json b/src/spec/url.json index 5cdadf3..7a3aeac 100644 --- a/src/spec/url.json +++ b/src/spec/url.json @@ -1,5 +1,6 @@ [ - {"fn":"url","data":"http://foo.com?foo=1#bar=flop&a=1,2&b=c|d|1,2,3", "expect":{ "fn":"equal.string","input":"bar","out":"flop"}}, - {"fn":"url","data":"http://foo.com?foo=1#bar=flop&a=1.2,2.2&b=c|d|1,2,3", "expect":{ "fn":"equal.xy", "input":"a","out":"1.22.2"},"label":"a equal.xy"}, - {"fn":"url","data":"http://foo.com?foo=1#b=c|d|1,2,3", "expect":{ "fn":"equal.multi", "input":"b","out":"c|d|1,2,3"},"label":"b equal.multi"} + {"fn":"url","data":"http://foo.com?foo=1#pos=flop", "expect":{ "fn":"equal.string","input":"pos","out":"flop"}}, + {"fn":"url","data":"http://foo.com?foo=1#pos=1.2,2.2", "expect":{ "fn":"equal.xy", "input":"pos","out":"1.22.2"},"label":"a equal.xy"}, + {"fn":"url","data":"http://foo.com?foo=1#prio=foo", "expect":{ "fn":"testParsed", "input":"prio","out":false},"label":"drop incompatible type"}, + {"fn":"url","data":"http://foo.com?foo=1#pos=c|d|1,2,3", "expect":{ "fn":"equal.multi", "input":"pos","out":"c|d|1,2,3"},"label":"b equal.multi"} ] diff --git a/src/xrfragment/Url.hx b/src/xrfragment/Url.hx index 53f936b..fda9277 100644 --- a/src/xrfragment/Url.hx +++ b/src/xrfragment/Url.hx @@ -1,72 +1,91 @@ package xrfragment; -@:expose // <- makes the class reachable from plain JavaScript -@:keep // <- avoids accidental removal by dead code elimination - - // # URI Value types - // - // | type | info | format | example | -class Value { // |------|------|--------|----------------------------------| - public var x:Float; // |vector| x,y,z| comma-separated | #pos=1,2,3 | - public var y:Float; // - public var z:Float; // - public var color:String; // |string| color| FFFFFF (hex) | #fog=5m,FFAACC | - public var string:String; // |string| | | #q=-sun | - public var int:Int; // |int | | [-]x[xxxxx] | #price:>=100 | - public var float:Float; // |float | | [-]x[.xxxx] (ieee)| #prio=-20 - public var args:Array; // |array | mixed| \|-separated | #pos=0,0,0|90,0,0 | - public function new(){} // - // > rule for thumb: type-limitations will piggyback JSON limitations (IEEE floatsize e.g.) -} +@:expose // <- makes the class reachable from plain JavaScript +@:keep // <- avoids accidental removal by dead code elimination class Url { - @:keep // # Url parser (the gist of it) - public static function parse(qs:String):haxe.DynamicAccess { // - var fragment:Array = qs.split("#"); // 1. fragment URI starts with `#` - var splitArray:Array = fragment[1].split('&'); // 1. fragments are split by `&` - var regexPlus = ~/\+/g; // 1. fragment-values are urlencoded (` ` becomes `+` and so on) - var resultMap:haxe.DynamicAccess = {}; - for (i in 0...splitArray.length) { - var splitByEqual = splitArray[i].split('='); // 1. `=` is used to indicate fragmentvalues - var key:String = splitByEqual[0]; - var v:Value = new Value(); + public static var error:String = ""; - if (splitByEqual.length > 1) { - var value:String = StringTools.urlDecode(regexPlus.split(splitByEqual[1]).join(" ")); - guessType(v, value); + @:keep + public static function parse(qs:String):haxe.DynamicAccess { + // # Fragment (values) + // + // | param | type | category | example | + var Frag:Map = new Map(); // |---------|---------------|-------------------------|------------------| + Frag.set("pos", Type.isVector); // | pos | 3D vector | HREF navigation/portals | `#pos=1,0,1` or `#pos=foo` | + Frag.set("prio", Type.isInt); // | prio | int (-10..1) | Asset linking | `#prio=-5` | + // + // # Url parser (the gist of it) + // + var fragment:Array = qs.split("#"); // 1. fragment URI starts with `#` + var splitArray:Array = fragment[1].split('&'); // 1. fragments are split by `&` + var regexPlus = ~/\+/g; // 1. fragment-values are urlencoded (space becomes `+` using `encodeUriComponent` e.g.) + var resultMap:haxe.DynamicAccess = {}; + for (i in 0...splitArray.length) { + var splitByEqual = splitArray[i].split('='); // 1. `=` is used to split fragment key/values + var key:String = splitByEqual[0]; + var v:Value = new Value(); - // multiple/fallback values - if( value.split("|").length > 1 ){ // 1. `|` is used to indicate multiple/fallback values - v.args = new Array(); - var args:Array = value.split("|"); - for( i in 0...args.length){ - var x:Value = new Value(); - guessType(x, args[i]); - v.args.push( x ); - } - } - resultMap.set(key, v ); - } + if (splitByEqual.length > 1) { + var value:String = StringTools.urlDecode(regexPlus.split(splitByEqual[1]).join(" ")); + if( Frag.exists(key) ){ + if( Frag.get(key).match(value) ){ + guessType(v, value); + // multiple/fallback values + if( value.split("|").length > 1 ){ // 1. `|` is used to split multiple/fallback values + v.args = new Array(); + var args:Array = value.split("|"); + for( i in 0...args.length){ + var x:Value = new Value(); + guessType(x, args[i]); + v.args.push( x ); + } + } + resultMap.set(key, v ); + }else trace("[ i ] fragment '"+key+"' has incompatible value ("+value+")"); + }else trace("[ i ] fragment '"+key+"' does not exist or has no type defined (yet)"); } - return resultMap; + } + return resultMap; } @:keep public static function guessType(v:Value, str:String):Void { - var isColor:EReg = ~/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/; // 1. hex colors are detected using regex `/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/` - var isInt:EReg = ~/^[0-9]+$/; // 1. integers are detected using regex `/^[0-9]+$/` - var isFloat:EReg = ~/^[0-9]+\.[0-9]+$/; // 1. floats are detected using regex `/^[0-9]+\.[0-9]+$/` v.string = str; - if( str.split(",").length > 1){ // 1. `,` is used to detect vector 1D/2D/3D values like x[,y[,z]] - var xyz:Array = str.split(","); // 1. anything else will be treated as string-value - if( xyz.length > 0 ) v.x = Std.parseFloat(xyz[0]); // 1. last resort: inappropriate string values will be converted using parseInt/parseFloat - if( xyz.length > 1 ) v.y = Std.parseFloat(xyz[1]); - if( xyz.length > 2 ) v.z = Std.parseFloat(xyz[2]); - } - if( isColor.match(str) ) v.color = str; - if( isFloat.match(str) ) v.float = Std.parseFloat(str); - if( isInt.match(str) ) v.int = Std.parseInt(str); + if( str.split(",").length > 1){ // 1. `,` assumes 1D/2D/3D vector-values like x[,y[,z]] + var xyz:Array = str.split(","); // 1. parseFloat(..) and parseInt(..) is applied to vector/float and int values + if( xyz.length > 0 ) v.x = Std.parseFloat(xyz[0]); // 1. anything else will be treated as string-value + if( xyz.length > 1 ) v.y = Std.parseFloat(xyz[1]); // 1. incompatible value-types will be dropped / not used + if( xyz.length > 2 ) v.y = Std.parseFloat(xyz[2]); // + } // > the xrfragment specification should stay simple enough + // > for anyone to write a parser using either regexes or grammar/lexers + if( Type.isColor.match(str) ) v.color = str; // > therefore expressions/comprehensions are not supported (max wildcard/comparison operators for queries e.g.) + if( Type.isFloat.match(str) ) v.float = Std.parseFloat(str); + if( Type.isInt.match(str) ) v.int = Std.parseInt(str); } } + // # Value types + // + // | type | info | format | example | +class Value { // |------|------|--------|----------------------------------| + public var x:Float; // |vector| x,y,z| comma-separated | #pos=1,2,3 | + public var y:Float; // + public var z:Float; // + public var color:String; // |string| color| FFFFFF (hex) | #fog=5m,FFAACC | + public var string:String; // |string| | | #q=-sun | + public var int:Int; // |int | | [-]x[xxxxx] | #price:>=100 | + public var float:Float; // |float | | [-]x[.xxxx] (ieee)| #prio=-20 + public var args:Array; // |array | mixed| \|-separated | #pos=0,0,0|90,0,0 | + public function new(){} // + // > rule for thumb: type-limitations will piggyback JSON limitations (IEEE floatsize e.g.) +} + // Regexes: +class Type { // + static public var isColor:EReg = ~/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/; // 1. hex colors are detected using regex `/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/` + static public var isInt:EReg = ~/^[0-9]+$/; // 1. integers are detected using regex `/^[0-9]+$/` + static public var isFloat:EReg = ~/^[0-9]+\.[0-9]+$/; // 1. floats are detected using regex `/^[0-9]+\.[0-9]+$/` + static public var isVector:EReg = ~/([,]+|\w)/; // 1. vectors are detected using regex `/[,]/` (but can also be an string referring to an entity-ID in the asset) +} + diff --git a/test/generated/test.js b/test/generated/test.js index 835c156..5209464 100644 --- a/test/generated/test.js +++ b/test/generated/test.js @@ -130,13 +130,15 @@ StringTools.replace = function(s,sub,by) { var Test = function() { }; Test.__name__ = true; Test.main = function() { - Test.test([{ fn : "url", expect : { fn : "equal.string", input : "bar", out : "flop"}, data : "http://foo.com?foo=1#bar=flop&a=1,2&b=c|d|1,2,3"},{ fn : "url", expect : { fn : "equal.xy", input : "a", out : "1.22.2"}, label : "a equal.xy", data : "http://foo.com?foo=1#bar=flop&a=1.2,2.2&b=c|d|1,2,3"},{ fn : "url", expect : { fn : "equal.multi", input : "b", out : "c|d|1,2,3"}, label : "b equal.multi", data : "http://foo.com?foo=1#b=c|d|1,2,3"}]); + Test.test([{ fn : "url", expect : { fn : "equal.string", input : "pos", out : "flop"}, data : "http://foo.com?foo=1#pos=flop"},{ fn : "url", expect : { fn : "equal.xy", input : "pos", out : "1.22.2"}, label : "a equal.xy", data : "http://foo.com?foo=1#pos=1.2,2.2"},{ fn : "url", expect : { fn : "testParsed", input : "prio", out : false}, label : "drop incompatible type", data : "http://foo.com?foo=1#prio=foo"},{ fn : "url", expect : { fn : "equal.multi", input : "pos", out : "c|d|1,2,3"}, label : "b equal.multi", data : "http://foo.com?foo=1#pos=c|d|1,2,3"}]); Test.test([{ fn : "query", expect : { fn : "testProperty", input : ["class","bar"], out : true}, data : "class:bar"},{ fn : "query", expect : { fn : "testProperty", input : ["class","bar"], out : true}, label : ".bar shorthand", data : ".bar"},{ fn : "query", expect : { fn : "testProperty", input : ["class","foo"], out : false}, data : ".bar -.foo"},{ fn : "query", expect : { fn : "testProperty", input : ["class","foo"], out : true}, data : ".bar -.foo .foo"},{ fn : "query", expect : { fn : "testProperty", input : ["class","bar"], out : true}, data : ".bar -.bar .bar"},{ fn : "query", expect : { fn : "testProperty", input : ["class","foo"], out : true}, label : "class:foo", data : ".foo -.foo .foo"},{ fn : "query", expect : { fn : "testProperty", input : ["class","foo"], out : true}, label : "class:foo", data : ".foo -.foo bar:5 .foo"},{ fn : "query", expect : { fn : "testProperty", input : ["class","foo"], out : true}, label : "class:foo", data : ".foo -.foo bar:>5 .foo"},{ fn : "query", expect : { fn : "testProperty", input : ["class","foo"], out : true}, label : "class:foo", data : ".foo -.foo bar:>5 .foo"},{ fn : "query", expect : { fn : "testProperty", input : ["class","foo"], out : true}, label : "class:foo", data : ".foo -.foo .foo"},{ fn : "query", expect : { fn : "testProperty", input : ["id","foo"], out : false}, label : "!id:foo", data : ".foo -.foo .foo"},{ fn : "query", expect : { fn : "testProperty", input : ["id","foo"], out : true}, label : "id:foo?", data : "foo -foo foo"}]); Test.test([{ fn : "query", expect : { fn : "testProperty", input : ["price","10"], out : true}, data : "price:>=5"},{ fn : "query", expect : { fn : "testProperty", input : ["price","10"], out : false}, data : "price:>=15"},{ fn : "query", expect : { fn : "testProperty", input : ["price","4"], out : false}, data : "price:>=5"},{ fn : "query", expect : { fn : "testProperty", input : ["price","0"], out : false}, data : "price:>=5"},{ fn : "query", expect : { fn : "testProperty", input : ["price","1"], out : false}, label : "price=1", data : "price:>=5 price:0"},{ fn : "query", expect : { fn : "testProperty", input : ["price","0"], out : true}, label : "price=0", data : "price:>=5 price:0"},{ fn : "query", expect : { fn : "testProperty", input : ["price","6"], out : true}, label : "price=6", data : "price:>=5 price:0"},{ fn : "query", expect : { fn : "testProperty", input : ["tag","foo"], out : true}, data : "tag:foo"},{ fn : "query", expect : { fn : "testProperty", input : ["tag","foo"], out : false}, data : "-tag:foo"},{ fn : "query", expect : { fn : "testPropertyExclude", input : ["tag","foo"], out : true}, label : "testExclude", data : "-tag:foo"},{ fn : "query", expect : { fn : "test", input : [{ price : 5}], out : true}, data : ".foo price:5 -tag:foo"},{ fn : "query", expect : { fn : "test", input : [{ tag : "foo", price : 5}], out : false}, data : ".foo price:5 -tag:foo"}]); + if(Test.errors > 1) { + console.log("src/Test.hx:21:","\n-----\n[ ❌] " + Test.errors + " errors :/"); + } }; Test.test = function(spec) { var Query = xrfragment_Query; - var errors = 0; var _g = 0; var _g1 = spec.length; while(_g < _g1) { @@ -160,6 +162,9 @@ Test.test = function(spec) { if(item.expect.fn == "testPropertyExclude") { valid = item.expect.out == q.testProperty(item.expect.input[0],item.expect.input[1],true); } + if(item.expect.fn == "testParsed") { + valid = item.expect.out == Object.prototype.hasOwnProperty.call(res,item.expect.input); + } if(item.expect.fn == "equal.string") { valid = item.expect.out == res[item.expect.input].string; } @@ -170,18 +175,19 @@ Test.test = function(spec) { valid = Test.equalMulti(res,item); } var ok = valid ? "[ ✔ ] " : "[ ❌] "; - console.log("src/Test.hx:38:",ok + Std.string(item.fn) + ": '" + Std.string(item.data) + "'" + (item.label ? " (" + (item.label ? item.label : item.expect.fn) + ")" : "")); + console.log("src/Test.hx:41:",ok + Std.string(item.fn) + ": '" + Std.string(item.data) + "'" + (item.label ? " (" + (item.label ? item.label : item.expect.fn) + ")" : "")); if(!valid) { - ++errors; + Test.errors += 1; } } - if(errors > 1) { - console.log("src/Test.hx:41:","\n-----\n[ ❌] " + errors + " errors :/"); - } }; Test.equalMulti = function(res,item) { + console.log("src/Test.hx:47:",res); var target = res[item.expect.input]; var str = ""; + if(!target) { + return false; + } var _g = 0; var _g1 = target.args.length; while(_g < _g1) { @@ -189,7 +195,11 @@ Test.equalMulti = function(res,item) { str = str + "|" + target.args[i].string; } str = HxOverrides.substr(str,1,null); - return str == item.expect.out; + if(item.expect.out) { + return str == item.expect.out; + } else { + return false; + } }; var haxe_iterators_ArrayIterator = function(array) { this.current = 0; @@ -446,12 +456,12 @@ xrfragment_Query.prototype = { return qualify > 0; } }; -var xrfragment_Value = $hx_exports["xrfragment"]["Value"] = function() { -}; -xrfragment_Value.__name__ = true; -var xrfragment_Url = function() { }; +var xrfragment_Url = $hx_exports["xrfragment"]["Url"] = function() { }; xrfragment_Url.__name__ = true; xrfragment_Url.parse = function(qs) { + var Frag_h = Object.create(null); + Frag_h["pos"] = xrfragment_Type.isVector; + Frag_h["prio"] = xrfragment_Type.isInt; var fragment = qs.split("#"); var splitArray = fragment[1].split("&"); var regexPlus = new EReg("\\+","g"); @@ -466,28 +476,33 @@ xrfragment_Url.parse = function(qs) { if(splitByEqual.length > 1) { var s = regexPlus.split(splitByEqual[1]).join(" "); var value = decodeURIComponent(s.split("+").join(" ")); - xrfragment_Url.guessType(v,value); - if(value.split("|").length > 1) { - v.args = []; - var args = value.split("|"); - var _g2 = 0; - var _g3 = args.length; - while(_g2 < _g3) { - var i1 = _g2++; - var x = new xrfragment_Value(); - xrfragment_Url.guessType(x,args[i1]); - v.args.push(x); + if(Object.prototype.hasOwnProperty.call(Frag_h,key)) { + if(Frag_h[key].match(value)) { + xrfragment_Url.guessType(v,value); + if(value.split("|").length > 1) { + v.args = []; + var args = value.split("|"); + var _g2 = 0; + var _g3 = args.length; + while(_g2 < _g3) { + var i1 = _g2++; + var x = new xrfragment_Value(); + xrfragment_Url.guessType(x,args[i1]); + v.args.push(x); + } + } + resultMap[key] = v; + } else { + console.log("src/xrfragment/Url.hx:46:","[ i ] fragment '" + key + "' has incompatible value (" + value + ")"); } + } else { + console.log("src/xrfragment/Url.hx:47:","[ i ] fragment '" + key + "' does not exist or has no type defined (yet)"); } - resultMap[key] = v; } } return resultMap; }; xrfragment_Url.guessType = function(v,str) { - var isColor = new EReg("^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$",""); - var isInt = new EReg("^[0-9]+$",""); - var isFloat = new EReg("^[0-9]+\\.[0-9]+$",""); v.string = str; if(str.split(",").length > 1) { var xyz = str.split(","); @@ -498,25 +513,31 @@ xrfragment_Url.guessType = function(v,str) { v.y = parseFloat(xyz[1]); } if(xyz.length > 2) { - v.z = parseFloat(xyz[2]); + v.y = parseFloat(xyz[2]); } } - if(isColor.match(str)) { + if(xrfragment_Type.isColor.match(str)) { v.color = str; } - if(isFloat.match(str)) { + if(xrfragment_Type.isFloat.match(str)) { v.float = parseFloat(str); } - if(isInt.match(str)) { + if(xrfragment_Type.isInt.match(str)) { v.int = Std.parseInt(str); } }; +var xrfragment_Value = function() { +}; +xrfragment_Value.__name__ = true; +var xrfragment_Type = function() { }; +xrfragment_Type.__name__ = true; if(typeof(performance) != "undefined" ? typeof(performance.now) == "function" : false) { HxOverrides.now = performance.now.bind(performance); } String.__name__ = true; Array.__name__ = true; js_Boot.__toStr = ({ }).toString; +Test.errors = 0; var xrfragment_Query_ok = $hx_exports["xrfragment"]["Query"]["ok"] = // haxe workarounds Array.prototype.contains = Array.prototype.includes @@ -546,6 +567,11 @@ var xrfragment_Query_ok = $hx_exports["xrfragment"]["Query"]["ok"] = } } ; +xrfragment_Url.error = ""; +xrfragment_Type.isColor = new EReg("^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$",""); +xrfragment_Type.isInt = new EReg("^[0-9]+$",""); +xrfragment_Type.isFloat = new EReg("^[0-9]+\\.[0-9]+$",""); +xrfragment_Type.isVector = new EReg("([,]+|\\w)",""); Test.main(); })({}); var xrfragment = $hx_exports["xrfragment"]; diff --git a/test/generated/test.py b/test/generated/test.py index 99b4752..9b495fc 100644 --- a/test/generated/test.py +++ b/test/generated/test.py @@ -375,18 +375,19 @@ class StringTools: class Test: _hx_class_name = "Test" __slots__ = () - _hx_statics = ["main", "test", "equalMulti"] + _hx_statics = ["errors", "main", "test", "equalMulti"] @staticmethod def main(): - Test.test([_hx_AnonObject({'fn': "url", 'expect': _hx_AnonObject({'fn': "equal.string", 'input': "bar", 'out': "flop"}), 'data': "http://foo.com?foo=1#bar=flop&a=1,2&b=c|d|1,2,3"}), _hx_AnonObject({'fn': "url", 'expect': _hx_AnonObject({'fn': "equal.xy", 'input': "a", 'out': "1.22.2"}), 'label': "a equal.xy", 'data': "http://foo.com?foo=1#bar=flop&a=1.2,2.2&b=c|d|1,2,3"}), _hx_AnonObject({'fn': "url", 'expect': _hx_AnonObject({'fn': "equal.multi", 'input': "b", 'out': "c|d|1,2,3"}), 'label': "b equal.multi", 'data': "http://foo.com?foo=1#b=c|d|1,2,3"})]) + Test.test([_hx_AnonObject({'fn': "url", 'expect': _hx_AnonObject({'fn': "equal.string", 'input': "pos", 'out': "flop"}), 'data': "http://foo.com?foo=1#pos=flop"}), _hx_AnonObject({'fn': "url", 'expect': _hx_AnonObject({'fn': "equal.xy", 'input': "pos", 'out': "1.22.2"}), 'label': "a equal.xy", 'data': "http://foo.com?foo=1#pos=1.2,2.2"}), _hx_AnonObject({'fn': "url", 'expect': _hx_AnonObject({'fn': "testParsed", 'input': "prio", 'out': False}), 'label': "drop incompatible type", 'data': "http://foo.com?foo=1#prio=foo"}), _hx_AnonObject({'fn': "url", 'expect': _hx_AnonObject({'fn': "equal.multi", 'input': "pos", 'out': "c|d|1,2,3"}), 'label': "b equal.multi", 'data': "http://foo.com?foo=1#pos=c|d|1,2,3"})]) Test.test([_hx_AnonObject({'fn': "query", 'expect': _hx_AnonObject({'fn': "testProperty", 'input': ["class", "bar"], 'out': True}), 'data': "class:bar"}), _hx_AnonObject({'fn': "query", 'expect': _hx_AnonObject({'fn': "testProperty", 'input': ["class", "bar"], 'out': True}), 'label': ".bar shorthand", 'data': ".bar"}), _hx_AnonObject({'fn': "query", 'expect': _hx_AnonObject({'fn': "testProperty", 'input': ["class", "foo"], 'out': False}), 'data': ".bar -.foo"}), _hx_AnonObject({'fn': "query", 'expect': _hx_AnonObject({'fn': "testProperty", 'input': ["class", "foo"], 'out': True}), 'data': ".bar -.foo .foo"}), _hx_AnonObject({'fn': "query", 'expect': _hx_AnonObject({'fn': "testProperty", 'input': ["class", "bar"], 'out': True}), 'data': ".bar -.bar .bar"}), _hx_AnonObject({'fn': "query", 'expect': _hx_AnonObject({'fn': "testProperty", 'input': ["class", "foo"], 'out': True}), 'label': "class:foo", 'data': ".foo -.foo .foo"}), _hx_AnonObject({'fn': "query", 'expect': _hx_AnonObject({'fn': "testProperty", 'input': ["class", "foo"], 'out': True}), 'label': "class:foo", 'data': ".foo -.foo bar:5 .foo"}), _hx_AnonObject({'fn': "query", 'expect': _hx_AnonObject({'fn': "testProperty", 'input': ["class", "foo"], 'out': True}), 'label': "class:foo", 'data': ".foo -.foo bar:>5 .foo"}), _hx_AnonObject({'fn': "query", 'expect': _hx_AnonObject({'fn': "testProperty", 'input': ["class", "foo"], 'out': True}), 'label': "class:foo", 'data': ".foo -.foo bar:>5 .foo"}), _hx_AnonObject({'fn': "query", 'expect': _hx_AnonObject({'fn': "testProperty", 'input': ["class", "foo"], 'out': True}), 'label': "class:foo", 'data': ".foo -.foo .foo"}), _hx_AnonObject({'fn': "query", 'expect': _hx_AnonObject({'fn': "testProperty", 'input': ["id", "foo"], 'out': False}), 'label': "!id:foo", 'data': ".foo -.foo .foo"}), _hx_AnonObject({'fn': "query", 'expect': _hx_AnonObject({'fn': "testProperty", 'input': ["id", "foo"], 'out': True}), 'label': "id:foo?", 'data': "foo -foo foo"})]) Test.test([_hx_AnonObject({'fn': "query", 'expect': _hx_AnonObject({'fn': "testProperty", 'input': ["price", "10"], 'out': True}), 'data': "price:>=5"}), _hx_AnonObject({'fn': "query", 'expect': _hx_AnonObject({'fn': "testProperty", 'input': ["price", "10"], 'out': False}), 'data': "price:>=15"}), _hx_AnonObject({'fn': "query", 'expect': _hx_AnonObject({'fn': "testProperty", 'input': ["price", "4"], 'out': False}), 'data': "price:>=5"}), _hx_AnonObject({'fn': "query", 'expect': _hx_AnonObject({'fn': "testProperty", 'input': ["price", "0"], 'out': False}), 'data': "price:>=5"}), _hx_AnonObject({'fn': "query", 'expect': _hx_AnonObject({'fn': "testProperty", 'input': ["price", "1"], 'out': False}), 'label': "price=1", 'data': "price:>=5 price:0"}), _hx_AnonObject({'fn': "query", 'expect': _hx_AnonObject({'fn': "testProperty", 'input': ["price", "0"], 'out': True}), 'label': "price=0", 'data': "price:>=5 price:0"}), _hx_AnonObject({'fn': "query", 'expect': _hx_AnonObject({'fn': "testProperty", 'input': ["price", "6"], 'out': True}), 'label': "price=6", 'data': "price:>=5 price:0"}), _hx_AnonObject({'fn': "query", 'expect': _hx_AnonObject({'fn': "testProperty", 'input': ["tag", "foo"], 'out': True}), 'data': "tag:foo"}), _hx_AnonObject({'fn': "query", 'expect': _hx_AnonObject({'fn': "testProperty", 'input': ["tag", "foo"], 'out': False}), 'data': "-tag:foo"}), _hx_AnonObject({'fn': "query", 'expect': _hx_AnonObject({'fn': "testPropertyExclude", 'input': ["tag", "foo"], 'out': True}), 'label': "testExclude", 'data': "-tag:foo"}), _hx_AnonObject({'fn': "query", 'expect': _hx_AnonObject({'fn': "test", 'input': [_hx_AnonObject({'price': 5})], 'out': True}), 'data': ".foo price:5 -tag:foo"}), _hx_AnonObject({'fn': "query", 'expect': _hx_AnonObject({'fn': "test", 'input': [_hx_AnonObject({'tag': "foo", 'price': 5})], 'out': False}), 'data': ".foo price:5 -tag:foo"})]) + if (Test.errors > 1): + print(str((("\n-----\n[ ❌] " + Std.string(Test.errors)) + " errors :/"))) @staticmethod def test(spec): Query = xrfragment_Query - errors = 0 _g = 0 _g1 = len(spec) while (_g < _g1): @@ -406,6 +407,8 @@ class Test: valid = (Reflect.field(Reflect.field(item,"expect"),"out") == q.testProperty(HxOverrides.arrayGet(Reflect.field(Reflect.field(item,"expect"),"input"), 0),HxOverrides.arrayGet(Reflect.field(Reflect.field(item,"expect"),"input"), 1))) if (Reflect.field(Reflect.field(item,"expect"),"fn") == "testPropertyExclude"): valid = (Reflect.field(Reflect.field(item,"expect"),"out") == q.testProperty(HxOverrides.arrayGet(Reflect.field(Reflect.field(item,"expect"),"input"), 0),HxOverrides.arrayGet(Reflect.field(Reflect.field(item,"expect"),"input"), 1),True)) + if (Reflect.field(Reflect.field(item,"expect"),"fn") == "testParsed"): + valid = (Reflect.field(Reflect.field(item,"expect"),"out") == python_Boot.hasField(res,Reflect.field(Reflect.field(item,"expect"),"input"))) if (Reflect.field(Reflect.field(item,"expect"),"fn") == "equal.string"): valid = HxOverrides.eq(Reflect.field(Reflect.field(item,"expect"),"out"),Reflect.field(Reflect.field(res,Reflect.field(Reflect.field(item,"expect"),"input")),"string")) if (Reflect.field(Reflect.field(item,"expect"),"fn") == "equal.xy"): @@ -415,14 +418,18 @@ class Test: ok = ("[ ✔ ] " if valid else "[ ❌] ") print(str((((((("null" if ok is None else ok) + Std.string(Reflect.field(item,"fn"))) + ": '") + Std.string(Reflect.field(item,"data"))) + "'") + HxOverrides.stringOrNull(((((" (" + HxOverrides.stringOrNull(((Reflect.field(item,"label") if (Reflect.field(item,"label")) else Reflect.field(Reflect.field(item,"expect"),"fn"))))) + ")") if (Reflect.field(item,"label")) else "")))))) if (not valid): - errors = (errors + 1) - if (errors > 1): - print(str((("\n-----\n[ ❌] " + Std.string(errors)) + " errors :/"))) + _hx_local_0 = Test + _hx_local_1 = _hx_local_0.errors + _hx_local_0.errors = (_hx_local_1 + 1) + _hx_local_0.errors @staticmethod def equalMulti(res,item): + print(str(res)) target = Reflect.field(res,Reflect.field(Reflect.field(item,"expect"),"input")) _hx_str = "" + if (not target): + return False _g = 0 _g1 = Reflect.field(Reflect.field(target,"args"),"length") while (_g < _g1): @@ -430,7 +437,15 @@ class Test: _g = (_g + 1) _hx_str = ((("null" if _hx_str is None else _hx_str) + "|") + HxOverrides.stringOrNull(HxOverrides.arrayGet(Reflect.field(target,"args"), i).string)) _hx_str = HxString.substr(_hx_str,1,None) - return (_hx_str == Reflect.field(Reflect.field(item,"expect"),"out")) + if Reflect.field(Reflect.field(item,"expect"),"out"): + return (_hx_str == Reflect.field(Reflect.field(item,"expect"),"out")) + else: + return False + + +class haxe_IMap: + _hx_class_name = "haxe.IMap" + __slots__ = () class haxe_Exception(Exception): @@ -514,6 +529,17 @@ class haxe_ValueException(haxe_Exception): +class haxe_ds_StringMap: + _hx_class_name = "haxe.ds.StringMap" + __slots__ = ("h",) + _hx_fields = ["h"] + _hx_interfaces = [haxe_IMap] + + def __init__(self): + self.h = dict() + + + class haxe_iterators_ArrayIterator: _hx_class_name = "haxe.iterators.ArrayIterator" __slots__ = ("array", "current") @@ -567,7 +593,7 @@ class haxe_iterators_ArrayKeyValueIterator: class python_Boot: _hx_class_name = "python.Boot" __slots__ = () - _hx_statics = ["keywords", "toString1", "fields", "simpleField", "field", "getInstanceFields", "getSuperClass", "getClassFields", "prefixLength", "unhandleKeywords"] + _hx_statics = ["keywords", "toString1", "fields", "simpleField", "hasField", "field", "getInstanceFields", "getSuperClass", "getClassFields", "prefixLength", "unhandleKeywords"] @staticmethod def toString1(o,s): @@ -731,6 +757,12 @@ class python_Boot: else: return None + @staticmethod + def hasField(o,field): + if isinstance(o,_hx_AnonObject): + return o._hx_hasattr(field) + return hasattr(o,(("_hx_" + field) if ((field in python_Boot.keywords)) else (("_hx_" + field) if (((((len(field) > 2) and ((ord(field[0]) == 95))) and ((ord(field[1]) == 95))) and ((ord(field[(len(field) - 1)]) != 95)))) else field))) + @staticmethod def field(o,field): if (field is None): @@ -1507,30 +1539,16 @@ class xrfragment_Query: -class xrfragment_Value: - _hx_class_name = "xrfragment.Value" - __slots__ = ("x", "y", "z", "color", "string", "int", "float", "args") - _hx_fields = ["x", "y", "z", "color", "string", "int", "float", "args"] - - def __init__(self): - self.args = None - self.float = None - self.int = None - self.string = None - self.color = None - self.z = None - self.y = None - self.x = None - - - class xrfragment_Url: _hx_class_name = "xrfragment.Url" __slots__ = () - _hx_statics = ["parse", "guessType"] + _hx_statics = ["error", "parse", "guessType"] @staticmethod def parse(qs): + Frag = haxe_ds_StringMap() + Frag.h["pos"] = xrfragment_Type.isVector + Frag.h["prio"] = xrfragment_Type.isInt fragment = qs.split("#") _this = (fragment[1] if 1 < len(fragment) else None) splitArray = _this.split("&") @@ -1548,27 +1566,32 @@ class xrfragment_Url: if (len(splitByEqual) > 1): _this1 = regexPlus.split((splitByEqual[1] if 1 < len(splitByEqual) else None)) value = python_lib_urllib_Parse.unquote(" ".join([python_Boot.toString1(x1,'') for x1 in _this1])) - xrfragment_Url.guessType(v,value) - if (len(value.split("|")) > 1): - v.args = list() - args = value.split("|") - _g2 = 0 - _g3 = len(args) - while (_g2 < _g3): - i1 = _g2 - _g2 = (_g2 + 1) - x = xrfragment_Value() - xrfragment_Url.guessType(x,(args[i1] if i1 >= 0 and i1 < len(args) else None)) - _this2 = v.args - _this2.append(x) - setattr(resultMap,(("_hx_" + key) if ((key in python_Boot.keywords)) else (("_hx_" + key) if (((((len(key) > 2) and ((ord(key[0]) == 95))) and ((ord(key[1]) == 95))) and ((ord(key[(len(key) - 1)]) != 95)))) else key)),v) + if (key in Frag.h): + _this2 = Frag.h.get(key,None) + _this2.matchObj = python_lib_Re.search(_this2.pattern,value) + if (_this2.matchObj is not None): + xrfragment_Url.guessType(v,value) + if (len(value.split("|")) > 1): + v.args = list() + args = value.split("|") + _g2 = 0 + _g3 = len(args) + while (_g2 < _g3): + i1 = _g2 + _g2 = (_g2 + 1) + x = xrfragment_Value() + xrfragment_Url.guessType(x,(args[i1] if i1 >= 0 and i1 < len(args) else None)) + _this3 = v.args + _this3.append(x) + setattr(resultMap,(("_hx_" + key) if ((key in python_Boot.keywords)) else (("_hx_" + key) if (((((len(key) > 2) and ((ord(key[0]) == 95))) and ((ord(key[1]) == 95))) and ((ord(key[(len(key) - 1)]) != 95)))) else key)),v) + else: + print(str((((("[ i ] fragment '" + ("null" if key is None else key)) + "' has incompatible value (") + ("null" if value is None else value)) + ")"))) + else: + print(str((("[ i ] fragment '" + ("null" if key is None else key)) + "' does not exist or has no type defined (yet)"))) return resultMap @staticmethod def guessType(v,_hx_str): - isColor = EReg("^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$","") - isInt = EReg("^[0-9]+$","") - isFloat = EReg("^[0-9]+\\.[0-9]+$","") v.string = _hx_str if (len(_hx_str.split(",")) > 1): xyz = _hx_str.split(",") @@ -1577,23 +1600,54 @@ class xrfragment_Url: if (len(xyz) > 1): v.y = Std.parseFloat((xyz[1] if 1 < len(xyz) else None)) if (len(xyz) > 2): - v.z = Std.parseFloat((xyz[2] if 2 < len(xyz) else None)) - isColor.matchObj = python_lib_Re.search(isColor.pattern,_hx_str) - if (isColor.matchObj is not None): + v.y = Std.parseFloat((xyz[2] if 2 < len(xyz) else None)) + _this = xrfragment_Type.isColor + _this.matchObj = python_lib_Re.search(_this.pattern,_hx_str) + if (_this.matchObj is not None): v.color = _hx_str - isFloat.matchObj = python_lib_Re.search(isFloat.pattern,_hx_str) - if (isFloat.matchObj is not None): + _this = xrfragment_Type.isFloat + _this.matchObj = python_lib_Re.search(_this.pattern,_hx_str) + if (_this.matchObj is not None): v.float = Std.parseFloat(_hx_str) - isInt.matchObj = python_lib_Re.search(isInt.pattern,_hx_str) - if (isInt.matchObj is not None): + _this = xrfragment_Type.isInt + _this.matchObj = python_lib_Re.search(_this.pattern,_hx_str) + if (_this.matchObj is not None): v.int = Std.parseInt(_hx_str) + +class xrfragment_Value: + _hx_class_name = "xrfragment.Value" + __slots__ = ("x", "y", "color", "string", "int", "float", "args") + _hx_fields = ["x", "y", "color", "string", "int", "float", "args"] + + def __init__(self): + self.args = None + self.float = None + self.int = None + self.string = None + self.color = None + self.y = None + self.x = None + + + +class xrfragment_Type: + _hx_class_name = "xrfragment.Type" + __slots__ = () + _hx_statics = ["isColor", "isInt", "isFloat", "isVector"] + Math.NEGATIVE_INFINITY = float("-inf") Math.POSITIVE_INFINITY = float("inf") Math.NaN = float("nan") Math.PI = python_lib_Math.pi +Test.errors = 0 python_Boot.keywords = set(["and", "del", "from", "not", "with", "as", "elif", "global", "or", "yield", "assert", "else", "if", "pass", "None", "break", "except", "import", "raise", "True", "class", "exec", "in", "return", "False", "continue", "finally", "is", "try", "def", "for", "lambda", "while"]) python_Boot.prefixLength = len("_hx_") +xrfragment_Url.error = "" +xrfragment_Type.isColor = EReg("^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$","") +xrfragment_Type.isInt = EReg("^[0-9]+$","") +xrfragment_Type.isFloat = EReg("^[0-9]+\\.[0-9]+$","") +xrfragment_Type.isVector = EReg("([,]+|\\w)","") Test.main()