diff --git a/dist/xrfragment.js b/dist/xrfragment.js index 79c1bd7..ff11a21 100644 --- a/dist/xrfragment.js +++ b/dist/xrfragment.js @@ -212,6 +212,7 @@ xrfragment_Parser.parse = function(key,value,resultMap) { Frag_h["prio"] = xrfragment_Type.isInt; Frag_h["pos"] = xrfragment_Type.isVector; Frag_h["q"] = xrfragment_Type.isString; + var vec = "1,2,3"; if(Object.prototype.hasOwnProperty.call(Frag_h,key)) { if(Frag_h[key].match(value)) { var v = new xrfragment_Value(); @@ -230,11 +231,11 @@ xrfragment_Parser.parse = function(key,value,resultMap) { } resultMap[key] = v; } else { - console.log("src/xrfragment/Parser.hx:34:","[ i ] fragment '" + key + "' has incompatible value (" + value + ")"); + console.log("src/xrfragment/Parser.hx:47:","[ i ] fragment '" + key + "' has incompatible value (" + value + ")"); return false; } } else { - console.log("src/xrfragment/Parser.hx:35:","[ i ] fragment '" + key + "' does not exist or has no type defined (yet)"); + console.log("src/xrfragment/Parser.hx:48:","[ i ] fragment '" + key + "' does not exist or has no type defined (yet)"); return false; } return true; diff --git a/doc/RFC.md b/doc/RFC.md index 6223e00..4267ac5 100644 --- a/doc/RFC.md +++ b/doc/RFC.md @@ -1,51 +1,56 @@ + > version 1.0.0 - -date: 2023-03-31T19:59:16+0200 (generated by `./make doc`) - +date: 2023-04-02T21:18:46+0200 [![Actions Status](https://github.com/coderofsalvation/xrfragment/workflows/test/badge.svg)](https://github.com/coderofsalvation/xrfragment/actions) - - # `://foo.com/my3d.asset#pos=1,0,0&prio=-5` # URI parser - > icanhazcode? yes, see [URI.hx](https://github.com/coderofsalvation/xrfragment/blob/main/src/xrfragment/URI.hx) 1. fragment URI starts with `#` 1. fragments are split by `&` -1. `=` is used to split fragment key/values +1. store key/values into a associative array or dynamic object +1. loop thru each fragment +1. for each fragment split on `=` to separate key/values 1. fragment-values are urlencoded (space becomes `+` using `encodeUriComponent` e.g.) 1. every recognized fragment key/value-pair is added to a central map/associative array/object # XR Fragments (key/value params) - + > ⛁ = define in 3D asset-file (as custom property or default projection)
> ☇ = mutable, using navigator URI (`document.location.href` e.g.)
| param | type | scope(s) | category | notes | |---------|---------------|-------|--------------------|---------------------------------| -| prio | int (-10..1) | ⛁ | Asset loading / linking | \#static allow client to ignore lower-prio objects in the renderloop, to compensate frame-drop/cpu/gpu-overload scenario’soc/notes/prio.md | +| prio | int (-10..1) | ⛁ | Asset loading / linking | \#static allow client to ignore lower-prio objects in the renderloop, to compensate frame-drop/cpu/gpu-overload scenario’s | | pos | 3D vector | ⛁ ☇ |HREF navigation/portals | | | q | string | ⛁ |Query Selector | | # XR Fragments parser + note: community parsers will prolly outperform this initial parser :) > icanhazcode? yes, see [Parser.hx](https://github.com/coderofsalvation/xrfragment/blob/main/src/xrfragment/Parser.hx) - the gist of it: + +1. check if param exist 1. each key has a regex to validate its value-type (see regexes) -1. `|` is used to split multiple/fallback values +1. extract the type +1. use `|` on stringvalues, to split multiple/fallback values +1. for each multiple/fallback value, guess the type 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.) # Parser Value types @@ -59,7 +64,9 @@ the gist of it: |float | | [-]x[.xxxx] (ieee)| #prio=-20 | |array | mixed| \|-separated | #pos=0,0,0\|90,0,0 | + > rule for thumb: type-limitations will piggyback JSON limitations (IEEE floatsize e.g.) +Regexes: 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]+$/` @@ -67,6 +74,3 @@ the gist of it: 1. vectors are detected using regex `/[,]/` (but can also be an string referring to an entity-ID in the asset) 1. anything else is string `/.*/` -# Tests - -the spec is tested with [JSON unittests](./../src/spec) consumed by [Test.hx](./../src/Test.hx) to cross-test all languages. diff --git a/doc/generate.awk b/doc/generate.awk new file mode 100644 index 0000000..cdf936a --- /dev/null +++ b/doc/generate.awk @@ -0,0 +1,30 @@ +# a no-nonsense source-to-markdown generator which scans for: +# +# /** +# * # foo +# * +# * this is markdown $(cat bar.md) +# */ +# +# var foo; // comment with 2 leading spaces is markdown too $(date) +# +/\$\(/ { cmd=$0; + gsub(/^.*\$\(/,"",cmd); + gsub(/\).*/,"",cmd); + cmd | getline stdout; close(cmd); + sub(/\$\(.*\)/,stdout); + } +/\/\*\*/ { doc=1; sub(/^.*\/\*/,""); } +doc && /\*\// { doc=0; + sub(/[[:space:]]*\*\/.*/,""); + sub(/^[[:space:]]*\*[[:space:]]?/,""); + print + } +doc && /^[[:space:]]*\*/ { sub(/^[[:space:]]*\*[[:space:]]?/,""); + print + } +!doc && /\/\/ / { sub(".*// ",""); + sub("# ","\n# "); + sub("> ","\n> "); + print + } diff --git a/make b/make index 7687ffe..a799c86 100755 --- a/make +++ b/make @@ -1,6 +1,5 @@ #!/bin/sh set -e -VERSION=1.0.0 try(){ set +e; "$@" 2>/dev/null; set -e; } @@ -32,33 +31,8 @@ tests(){ } doc(){ - { - echo '' - echo '' - echo "> version $VERSION" - echo "\ndate: $(date +"%Y-%m-%dT%H:%M:%S%z") (generated by \`./make doc\`)\n" - echo "[![Actions Status](https://github.com/coderofsalvation/xrfragment/workflows/test/badge.svg)](https://github.com/coderofsalvation/xrfragment/actions)\n" - { - cat src/xrfragment/URI.hx - cat src/xrfragment/Parser.hx - } | awk ' - - /\/\/ / { - gsub(".*// ","",$0); - gsub("# ","\n# ",$0); - if( match($0,/^#code /) ){ print "```\n"; system("cat "$2); print "```\n"; next; } - if( match($0,/^#sh /) ){ $1=""; system($0); next; } - if( match($0,/#include /) ) { - o=$0; gsub(/.*#include/,"#include",$0); f=$2; $0=o; - cmd="cat "f - cmd | getline text; close(cmd) - gsub(/#include \w/, text) - } - print $0; - } - - ' - } > doc/RFC.md + awk -f doc/generate.awk src/xrfragment/URI.hx \ + src/xrfragment/Parser.hx > doc/RFC.md } test -z $1 && { try rm dist/* ; haxe build.hxml; sed -i 's|.*nonlocal .*||g' dist/xrfragment.py; exit $?; } diff --git a/src/xrfragment/Parser.hx b/src/xrfragment/Parser.hx index cc5484a..19ad59b 100644 --- a/src/xrfragment/Parser.hx +++ b/src/xrfragment/Parser.hx @@ -1,32 +1,45 @@ package xrfragment; +/** + * # XR Fragments (key/value params) + * + * > ⛁ = define in 3D asset-file (as custom property or default projection)
+ * > ☇ = mutable, using navigator URI (`document.location.href` e.g.)
+ */ + @:expose // <- makes the class reachable from plain JavaScript @:keep // <- avoids accidental removal by dead code elimination -class Parser { // # XR Fragments (key/value params) - public static var error:String = ""; // - // > ⛁ = define in 3D asset-file (as custom property or default projection)
- // > ☇ = mutable, using navigator URI (`document.location.href` e.g.)
- @:keep // +class Parser { + public static var error:String = ""; + + @:keep public static function parse(key:String,value:String,resultMap:haxe.DynamicAccess):Bool { var Frag:Map = new Map(); // | param | type | scope(s) | category | notes | // |---------|---------------|-------|--------------------|---------------------------------| - Frag.set("prio", Type.isInt); // | prio | int (-10..1) | ⛁ | Asset loading / linking | #include doc/notes/prio.md | + Frag.set("prio", Type.isInt); // | prio | int (-10..1) | ⛁ | Asset loading / linking | $(cat doc/notes/prio.md) | Frag.set("pos", Type.isVector); // | pos | 3D vector | ⛁ ☇ |HREF navigation/portals | | Frag.set("q", Type.isString); // | q | string | ⛁ |Query Selector | | - // - // # XR Fragments parser - if( Frag.exists(key) ){ // note: community parsers will prolly outperform this initial parser :) - if( Frag.get(key).match(value) ){ // > icanhazcode? yes, see [Parser.hx](https://github.com/coderofsalvation/xrfragment/blob/main/src/xrfragment/Parser.hx) - var v:Value = new Value(); // - guessType(v, value); // the gist of it: - // process multiple/fallback values // 1. each key has a regex to validate its value-type (see regexes) - if( value.split("|").length > 1 ){ // 1. `|` is used to split multiple/fallback values + var vec:String = "1,2,3"; // + //if( Type.isVector(vec) ) trace("ja"); + /** + * # XR Fragments parser + * + * note: community parsers will prolly outperform this initial parser :) + * > icanhazcode? yes, see [Parser.hx](https://github.com/coderofsalvation/xrfragment/blob/main/src/xrfragment/Parser.hx) + * the gist of it: + */ + if( Frag.exists(key) ){ // 1. check if param exist + if( Frag.get(key).match(value) ){ // 1. each key has a regex to validate its value-type (see regexes) + var v:Value = new Value(); + guessType(v, value); // 1. extract the type + // process multiple/fallback values + if( value.split("|").length > 1 ){ // 1. use `|` on stringvalues, 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]); + guessType(x, args[i]); // 1. for each multiple/fallback value, guess the type v.args.push( x ); } } @@ -40,14 +53,14 @@ class Parser { // # @:keep public static function guessType(v:Value, str:String):Void { v.string = 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( 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); } @@ -58,8 +71,8 @@ class Parser { // # // | 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 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 | @@ -68,7 +81,7 @@ class Value { // | public function new(){} // // > rule for thumb: type-limitations will piggyback JSON limitations (IEEE floatsize e.g.) } - // Regexes: + // 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]+$/` @@ -77,6 +90,6 @@ class Type { // static public var isString:EReg = ~/.*/; // 1. anything else is string `/.*/` } -// # Tests -// -// the spec is tested with [JSON unittests](./../src/spec) consumed by [Test.hx](./../src/Test.hx) to cross-test all languages. +/// # Tests +/// +/// the spec is tested with [JSON unittests](./../src/spec) consumed by [Test.hx](./../src/Test.hx) to cross-test all languages. diff --git a/src/xrfragment/URI.hx b/src/xrfragment/URI.hx index 150fbcf..3a441c8 100644 --- a/src/xrfragment/URI.hx +++ b/src/xrfragment/URI.hx @@ -2,19 +2,30 @@ package xrfragment; import xrfragment.Parser; +/** + * + * + * > version 1.0.0 + * date: $(date +"%Y-%m-%dT%H:%M:%S%z") (generated by \`./make doc\`) + * [![Actions Status](https://github.com/coderofsalvation/xrfragment/workflows/test/badge.svg)](https://github.com/coderofsalvation/xrfragment/actions) + * + * # `://foo.com/my3d.asset#pos=1,0,0&prio=-5` + * + * # URI parser + * > icanhazcode? yes, see [URI.hx](https://github.com/coderofsalvation/xrfragment/blob/main/src/xrfragment/URI.hx) + */ + @:expose // <- makes the class reachable from plain JavaScript @:keep // <- avoids accidental removal by dead code elimination - // - // # `://foo.com/my3d.asset#pos=1,0,0&prio=-5` class URI { - @:keep // # URI parser - public static function parse(qs:String):haxe.DynamicAccess { // - var fragment:Array = qs.split("#"); // > icanhazcode? yes, see [URI.hx](https://github.com/coderofsalvation/xrfragment/blob/main/src/xrfragment/URI.hx) - var splitArray:Array = fragment[1].split('&'); // - var resultMap:haxe.DynamicAccess = {}; // 1. fragment URI starts with `#` - for (i in 0...splitArray.length) { // 1. fragments are split by `&` + @:keep + 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 resultMap:haxe.DynamicAccess = {}; // 1. store key/values into a associative array or dynamic object + for (i in 0...splitArray.length) { // 1. loop thru each fragment - var splitByEqual = splitArray[i].split('='); // 1. `=` is used to split fragment key/values + var splitByEqual = splitArray[i].split('='); // 1. for each fragment split on `=` to separate key/values var regexPlus = ~/\+/g; // 1. fragment-values are urlencoded (space becomes `+` using `encodeUriComponent` e.g.) var key:String = splitByEqual[0]; diff --git a/test/generated/test.js b/test/generated/test.js index 9b5574a..b0ec805 100644 --- a/test/generated/test.js +++ b/test/generated/test.js @@ -285,6 +285,7 @@ xrfragment_Parser.parse = function(key,value,resultMap) { Frag_h["prio"] = xrfragment_Type.isInt; Frag_h["pos"] = xrfragment_Type.isVector; Frag_h["q"] = xrfragment_Type.isString; + var vec = "1,2,3"; if(Object.prototype.hasOwnProperty.call(Frag_h,key)) { if(Frag_h[key].match(value)) { var v = new xrfragment_Value(); @@ -303,11 +304,11 @@ xrfragment_Parser.parse = function(key,value,resultMap) { } resultMap[key] = v; } else { - console.log("src/xrfragment/Parser.hx:34:","[ i ] fragment '" + key + "' has incompatible value (" + value + ")"); + console.log("src/xrfragment/Parser.hx:47:","[ i ] fragment '" + key + "' has incompatible value (" + value + ")"); return false; } } else { - console.log("src/xrfragment/Parser.hx:35:","[ i ] fragment '" + key + "' does not exist or has no type defined (yet)"); + console.log("src/xrfragment/Parser.hx:48:","[ i ] fragment '" + key + "' does not exist or has no type defined (yet)"); return false; } return true; diff --git a/test/generated/test.py b/test/generated/test.py index c1480e3..a6a83e8 100644 --- a/test/generated/test.py +++ b/test/generated/test.py @@ -1371,6 +1371,7 @@ class xrfragment_Parser: Frag.h["prio"] = xrfragment_Type.isInt Frag.h["pos"] = xrfragment_Type.isVector Frag.h["q"] = xrfragment_Type.isString + vec = "1,2,3" if (key in Frag.h): _this = Frag.h.get(key,None) _this.matchObj = python_lib_Re.search(_this.pattern,value) @@ -1580,8 +1581,8 @@ class xrfragment_Query: fails = 0 qualify = 0 def _hx_local_2(expr): - nonlocal conds nonlocal fails + nonlocal conds conds = (conds + 1) fails = (fails + (0 if expr else 1)) return expr