work in progress [might break]

This commit is contained in:
Leon van Kammen 2023-03-09 19:58:08 +01:00
parent 315e67ce05
commit 7d95ec66b6
14 changed files with 30196 additions and 1 deletions

1
.vimrc Normal file
View File

@ -0,0 +1 @@
noremap <silent> <F5> :!./make && ./make runtest \| most<CR>

View File

@ -1 +1,10 @@
# xrfragment
# usage
# development
```
$ ./make install
$ ./make
$ ./make runtest
```

83
build.hxml Normal file
View File

@ -0,0 +1,83 @@
-dce no
-cp src
xrfragment.Query
-D shallow-expose
--resource stub/stub.js@stub
-js dist/xrfragment.js
--next
-dce no
-cp src
Test
-D shallow-expose
-m Test
-js test/generated/test.js
--next
-dce no
-cp src
xrfragment.Query
-lua dist/xrfragment.lua
--next
-dce no
-cp src
xrfragment.Query
-python dist/xrfragment.py
--next
-dce no
-cp src
Test
-m Test
-python test/generated/test.py
#--next
#
#-dce no
#-cp src
#
#xrfragment.Query
#-php dist/xrfragment.php5
#
#--next
#
#-dce no
#-cp src
#
#xrfragment.Query
#-cpp dist/xrfragment.cpp
#
#--next
#
#-dce no
#-cp src
#
#xrfragment.Query
#-cppia dist/xrfragment.cppia
#
#--next
#
#-dce no
#-cp src
#
#xrfragment.Query
#-cs dist/xrfragment.csharp
#
#
#--next
#
#-dce no
#-cp src
#
#xrfragment.Query
#-java xrfragment.jdk

2257
dist/xrfragment.js vendored Normal file

File diff suppressed because it is too large Load Diff

10028
dist/xrfragment.lua vendored Normal file

File diff suppressed because it is too large Load Diff

7433
dist/xrfragment.py vendored Normal file

File diff suppressed because it is too large Load Diff

26
make Executable file
View File

@ -0,0 +1,26 @@
#!/bin/sh
set -e
try(){ set +e; "$@" 2>/dev/null; set -e; }
install(){
which haxe || {
echo " 1. install haxe from haxe.org"
echo "[2.] download neko for cpp output"
echo "[3.] install mono openjdk14 for csharp + java output"
}
haxelib setup
haxelib install hxcpp
haxelib install hxjava
haxelib install hxcs
haxelib install hscript
}
runtest(){
set -x
which python3 && python3 test/generated/test.py | awk '{ print "py: "$0 } END{ print "\n"}'
which node && node test/generated/test.js | awk '{ print "js: "$0 } END{ print "\n"}'
}
test -z $1 && { try rm dist/* ; haxe build.hxml; exit $?; }
test -z $1 || "$@"

118
src/Test.hx Normal file
View File

@ -0,0 +1,118 @@
import xrfragment.Query;
class Test {
static public function main():Void {
trace("starting tests");
var Query = xrfragment.Query;
trace( (new Query("foo or bar")).toObject() );
trace( (new Query("class:fopoer or bar foo:bar")).toObject().or[0] );
trace( (new Query("-skybox class:foo")).toObject().or[0] );
trace( (new Query("foo/flop moo or bar")).toObject().or[0] );
trace( (new Query("-foo/flop moo or bar")).toObject().or[0] );
trace( (new Query("price:>4 moo or bar")).toObject().or[0] );
trace( (new Query("price:>=4 moo or bar")).toObject().or[0] );
trace( (new Query("price:<=4 moo or bar")).toObject().or[0] );
trace( (new Query("price:!=4 moo or bar")).toObject().or[0] );
var q:Dynamic = new Query("price:!=4 moo or bar");
var obj:Dynamic = q.toObject();
q.test( "price", 4);
var ok = !q.qualify("slkklskdf");
if( !ok ) throw 'node should not be allowed';
q = new Query("price:!=3 moo or bar");
var obj:Dynamic = q.toObject();
q.test( "price", 4);
var ok = q.qualify("slkklskdf");
if( !ok ) throw 'non-mentioned node should be allowed';
q = new Query("moo or bar");
var obj:Dynamic = q.toObject();
var ok = !q.qualify("slkklskdf");
if( !ok ) throw 'node should not be allowed';
obj = q.toObject();
var ok = q.qualify("moo");
if( !ok ) throw 'moo should be allowed';
var ok = q.qualify("bar");
if( !ok ) throw 'bar should be allowed';
q = new Query("price:>3 moo or bar");
var obj:Dynamic = q.toObject();
q.test( "price", 4);
var ok = q.qualify("foo");
if( !ok ) throw 'node should be allowed';
var ok = q.qualify("bar");
if( !ok ) throw 'node should be allowed';
var ok = q.qualify("moo");
if( !ok ) throw 'node should be allowed';
q = new Query("price:>3 price:<10 -bar");
var obj:Dynamic = q.toObject();
q.test( "price", 4);
var ok = q.qualify("foo");
if( !ok ) throw 'node should be allowed';
var ok = !q.qualify("bar");
if( !ok ) throw 'bar should not be allowed';
q.test("price", 20);
var ok = !q.qualify("foo");
if( !ok ) throw 'price 20 should not be allowed';
q = new Query("-bar");
var obj:Dynamic = q.toObject();
var ok = q.qualify("foo");
if( !ok ) throw 'node should be allowed';
var ok = !q.qualify("bar");
if( !ok ) throw 'bar should not be allowed';
q = new Query("title:*");
var obj:Dynamic = q.toObject();
var ok = !q.qualify("foo");
if( !ok ) throw 'node should not be allowed';
q.test("foo","bar");
var ok = !q.qualify("foo");
if( !ok ) throw 'node should not be allowed';
q.test("title","bar");
var ok = q.qualify("foo");
if( !ok ) throw 'node should be allowed';
q = new Query("-bar +bar");
var obj:Dynamic = q.toObject();
var ok = q.qualify("foo");
if( !ok ) throw 'node should be allowed';
var ok = q.qualify("bar");
if( !ok ) throw 'bar should be allowed';
q = new Query("?discount");
var obj:Dynamic = q.toObject();
q.test("?discount","-foo");
var ok = !q.qualify("foo");
if( !ok ) throw 'foo should not be allowed';
q = new Query("?");
q.test("?","-foo");
var ok = !q.qualify("foo");
if( !ok ) throw 'foo should not be allowed';
q = new Query("?");
var ok = q.qualify("foo");
if( !ok ) throw 'foo should not be allowed';
q = new Query("?discount");
q.test("?discount","-foo");
var ok = !q.qualify("foo");
if( !ok ) throw 'foo should not be allowed';
q = new Query("?discount +foo");
var obj:Dynamic = q.toObject();
q.test("?discount","-foo");
var ok = !q.qualify("foo");
if( !ok ) throw 'foo should not be allowed';
var ok = !q.qualify("foo");
if( !ok ) throw 'foo should not be allowed';
trace("all tests passed");
}
}

175
src/xrfragment/Query.hx Normal file
View File

@ -0,0 +1,175 @@
package xrfragment;
@:expose // <- makes the class reachable from plain JavaScript
@:keep // <- avoids accidental removal by dead code elimination
#if js
var ok:Bool = js.Syntax.code('
// haxe workarounds
Array.prototype.contains = Array.prototype.includes
if (typeof Array.prototype.remove !== "function") {
Array.prototype.remove = function (item) {
const oldLength = this.length
let newLength = 0
for (let i = 0; i < oldLength; i++) {
const entry = this[i]
if (entry === item) {
let newLength = i++
while (i !== this.length) {
const entry = this[i]
if (entry !== item) this[newLength++] = entry
i++
}
this.length = newLength
for (let i = newLength; i < oldLength; i++) delete this[i]
return true
}
}
return false
}
}
');
#end
class Query {
private var str:String = "";
private var q:Dynamic = {};
private var include:Array<String> = new Array();
private var exclude:Array<String> = new Array();
private var accept:Bool = false;
private var preset:String = "";
public function new(str:String){
if( str != null ) this.parse(str);
}
public function toObject() : Dynamic {
return this.q;
}
public function qualify( nodename:String ): Bool {
if( this.q.copy_all ) this.accept = true;
if( this.include.contains(nodename) ) this.accept = true;
if( this.exclude.contains(nodename) ) this.accept = false;
return this.accept;
}
public function parse(str:String,recurse:Bool = false) : Dynamic {
var copyAll:Bool = recurse ? this.q.copy_all : str.substr(0,1) == "-" || str.substr(0,1) == "?" || str == "";
var isOr:EReg = ~/^or$/;
var isProp:EReg = ~/.*:[><=!]?/;
var isName:EReg = ~/[^:\/]/;
var isExclude:EReg = ~/^-/;
var isInclude:EReg = ~/^\+/;
var isPreset:EReg = ~/^\?/;
var token = str.split(" ");
var ors = new Array();
var q:haxe.DynamicAccess<Dynamic> = {};
function composeQuery() : Dynamic {
q = {};
q.set("object", new Array() );
q.set("-object", new Array() );
ors.push(q);
return q;
}
composeQuery();
function match(str,prefix = ""){
if( isPreset.match(str) && !recurse ){
this.preset = str;
return;
}
if( isExclude.match(str) || isInclude.match(str) ){
var t = str.substr(1);
match(t, str.substr(0,1) );
return;
}
if( isProp.match(str) ){
var skip = 0;
var type = "=";
if( str.indexOf("*") != -1 ) type = "*";
if( str.indexOf(">") != -1 ) type = ">";
if( str.indexOf("<") != -1 ) type = "<";
if( str.indexOf("!=") != -1 ) type = "!=";
if( str.indexOf(">=") != -1 ) type = ">=";
if( str.indexOf("<=") != -1 ) type = "<=";
if( type != "=" ) skip += type.length;
var property = str.split(":")[0];
var value:haxe.DynamicAccess<Dynamic>;
if( q.get(prefix+property) ) value = q.get(prefix+property);
else value = {};
value[ type ] = str.split(":")[1].substr(skip);
q.set(prefix+property,value);
return;
}
if( isName.match(str) ){
if( prefix == '-' ){
q["-object"].push(str);
while( q["object"].contains(str) == true) {
q["object"].remove(str);
}
}else{
q["object"].push(str);
while( q["-object"].contains(str) == true ){
q["-object"].remove(str);
}
}
return;
}
}
for( i in 0...token.length ) {
if( isOr.match(token[i]) ){
composeQuery();
}else match(token[i]);
}
for ( i in 0...ors.length ) {
var or:Dynamic = ors[i];
if( Reflect.field(or,'object') != null ) this.include = this.include.concat( Reflect.field(or,'object') );
if( Reflect.field(or,'-object') != null ) this.exclude = this.exclude.concat( Reflect.field(or,'-object') );
}
this.q = { or: ors, copy_all: copyAll };
return this.q;
}
public function test( property:String, ?value:Dynamic ):Void{
if( this.preset == property ){
this.parse( value, true );
}
for ( i in 0...this.q.or.length ) {
var or:Dynamic = this.q.or[i];
var conds:Int = 0;
var fails:Int = 0;
var pass:Int = 0;
var when = function(expr:Bool) : Bool {
conds+=1;
fails+= expr ? 0 : 1;
return expr;
}
for ( k in Reflect.fields(or) ){
var orval:Dynamic = Reflect.field(or,k);
if( k != property ) continue;
if( Reflect.field(orval,'=') != null && when( value == Reflect.field(orval,'=') ) ) pass += 1;
if( Reflect.field(orval,'*') != null && when( value != null ) ) pass += 1;
if( Reflect.field(orval,'>') != null && when( value > Std.parseInt(Reflect.field(orval,'>' )) ) ) pass += 1;
if( Reflect.field(orval,'<') != null && when( value < Std.parseInt(Reflect.field(orval,'<' )) ) ) pass += 1;
if( Reflect.field(orval,'>=') != null && when( value >= Std.parseInt(Reflect.field(orval,'>=')) ) ) pass += 1;
if( Reflect.field(orval,'<=') != null && when( value >= Std.parseInt(Reflect.field(orval,'<=')) ) ) pass += 1;
if( Reflect.field(orval,'!=') != null && when( value != Std.parseInt(Reflect.field(orval,'!=')) ) ) pass += 1;
}
if( this.accept && conds > 0 && fails > 0 ) this.accept = false;
if( conds > 0 && pass > 0 && fails == 0 ) this.accept = true;
}
}
}

2432
test/generated/test.js Normal file

File diff suppressed because it is too large Load Diff

7590
test/generated/test.py Normal file

File diff suppressed because it is too large Load Diff

28
test/test.js Normal file
View File

@ -0,0 +1,28 @@
// in the browser use this instead of require():
//
// <script src="Query.js"></script>
// <script>
// var q = new hgltf.Query()
// </script>
var hgltf = require('../Query').hgltf
var q = new hgltf.Query()
var res = q.parse("")
if( !res.copy_all ) throw 'empty string should always set copy_all to true'
console.log( q.parse("") )
console.log( JSON.stringify(q.parse("foo:*"), null, 2) )
return
console.log( q.parse("-skybox -plane") )
console.log( q.parse("foo or bar") );
console.log( q.parse("class:fopoer or bar foo:bar").or[0] );
console.log( q.parse("-skybox class:foo").or[0] );
console.log( q.parse("foo/flop moo or bar").or[0] );
console.log( q.parse("-foo/flop moo or bar").or[0] );
console.log( q.parse("price:>4 moo or bar").or[0] );
console.log( q.parse("price:>=4 moo or bar").or[0] );
console.log( q.parse("price:<=4 moo or bar").or[0] );
console.log( q.parse("price:!=4 moo or bar").or[0] );

1
test/test.lua Normal file
View File

@ -0,0 +1 @@
require "../Query"

14
test/test.py Normal file
View File

@ -0,0 +1,14 @@
from Query import hgltf_Query
q = hgltf_Query()
print( q.parse("foo or bar") )
print( q.parse("foo or bar") )
print( q.parse("class:fopoer or bar foo:bar") )
print( q.parse("-skybox class:foo") )
print( q.parse("foo/flop moo or bar") )
print( q.parse("-foo/flop moo or bar") )
print( q.parse("price:>4 moo or bar") )
print( q.parse("price:>=4 moo or bar") )
print( q.parse("price:<=4 moo or bar") )
print( q.parse("price:!=4 moo or bar") )