added type-check + markdown-flavored literate programming using awk

This commit is contained in:
Leon van Kammen 2023-03-30 19:51:32 +02:00
parent f524513e37
commit db8397c142
9 changed files with 324 additions and 183 deletions

58
dist/xrfragment.js vendored
View File

@ -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"];

View File

@ -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)

15
make
View File

@ -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 $?; }

View File

@ -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<Dynamic>):Void {
var Query = xrfragment.Query;
var errors:Int = 0;
for( i in 0...spec.length ){
var q:Query = null;
var res:haxe.DynamicAccess<Dynamic> = 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<Dynamic>, 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 {

5
src/spec/tmp.json Normal file
View File

@ -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}}
]

View File

@ -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"}
]

View File

@ -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<Value>; // |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<Dynamic> { //
var fragment:Array<String> = qs.split("#"); // 1. fragment URI starts with `#`
var splitArray:Array<String> = fragment[1].split('&'); // 1. fragments are split by `&`
var regexPlus = ~/\+/g; // 1. fragment-values are urlencoded (` ` becomes `+` and so on)
var resultMap:haxe.DynamicAccess<Dynamic> = {};
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<Dynamic> {
// # Fragment (values)
//
// | param | type | category | example |
var Frag:Map<String, EReg> = new Map<String, EReg>(); // |---------|---------------|-------------------------|------------------|
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<String> = qs.split("#"); // 1. fragment URI starts with `#`
var splitArray:Array<String> = 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<Dynamic> = {};
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<Value>();
var args:Array<String> = 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<Value>();
var args:Array<String> = 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<String> = 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<String> = 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<Value>; // |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)
}

View File

@ -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"];

View File

@ -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()