Skip to content

Commit

Permalink
Fix for function parameter parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
gwaredd committed Feb 2, 2022
1 parent 1f13464 commit 3fd5dd2
Show file tree
Hide file tree
Showing 4 changed files with 230 additions and 51 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Assets/AssetStoreTools*
# Visual Studio 2015 cache directory
.vs/
.vscode/
.vsconfig

# Autogenerated VS/MD/Consulo solution and project files
ExportedObj/
Expand Down
81 changes: 81 additions & 0 deletions Assets/Editor/Unit Tests/TestPath.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Copyright (c) 2017 Gwaredd Mountain, https://opensource.org/licenses/MIT
#if !UNIUM_DISABLE && ( DEVELOPMENT_BUILD || UNITY_EDITOR || UNIUM_ENABLE )

using NUnit.Framework;
using gw.gql;

////////////////////////////////////////////////////////////////////////////////////////////////////

public class TestPath
{
//----------------------------------------------------------------------------------------------------

[Test]
public void Basic()
{
Path p;

// quick tests
p = new Path( "/root" );
Assert.AreEqual( 1, p.Length );
Assert.AreEqual( "root", p[ 0 ].Select );
Assert.AreEqual( Query.Action.Get, p.Action );

p = new Path( "/root/obj[3].attr" );
Assert.AreEqual( 3, p.Length );

p = new Path( "/root/obj.attr" );
Assert.AreEqual( 3, p.Length );

Assert.AreEqual( "root", p[ 0 ].Select );
Assert.AreEqual( "obj", p[ 1 ].Select );
Assert.AreEqual( "attr", p[ 2 ].Select );

Assert.IsTrue( p[ 0 ].NodeType == Path.Segment.Type.Children );
Assert.IsTrue( p[ 1 ].NodeType == Path.Segment.Type.Children );
Assert.IsTrue( p[ 2 ].NodeType == Path.Segment.Type.Attribute );

// set value
p = new Path( "/root/obj.attr.val=abc,def" );
Assert.AreEqual( 3, p.Length );
Assert.AreEqual( Query.Action.Set, p.Action );
Assert.AreEqual( "val", p.Target );
Assert.AreEqual( 1, p.Arguments.Length );
Assert.AreEqual( "abc,def", p.Arguments[ 0 ] );

// function call
p = new Path( "/root/obj.attr.func(123)" );
Assert.AreEqual( 3, p.Length );
Assert.AreEqual( Query.Action.Invoke, p.Action );
Assert.AreEqual( "func", p.Target );
Assert.AreEqual( 1, p.Arguments.Length );
Assert.AreEqual( "123", p.Arguments[ 0 ] );
}

//----------------------------------------------------------------------------------------------------

[Test]
public void Args()
{
Assert.AreEqual( new string[] { }, Path.SplitArgs( "" ) );
Assert.AreEqual( new string[] { }, Path.SplitArgs( " " ) );

Assert.AreEqual( new string[] { "a" }, Path.SplitArgs( "a" ) );
Assert.AreEqual( new string[] { "a" }, Path.SplitArgs( " a " ) );
Assert.AreEqual( new string[] { "a", "b", "c" }, Path.SplitArgs( "a,b,c" ) );
Assert.AreEqual( new string[] { "a", "b", "c" }, Path.SplitArgs( " a , b , c " ) );
Assert.AreEqual( new string[] { "12.3", "45", "abc" }, Path.SplitArgs( "12.3,45,abc" ) );

Assert.AreEqual( new string[] { "a", "b", "c" }, Path.SplitArgs( " \"a\" , \"b\", \"c\" " ) );
Assert.AreEqual( new string[] { "ab,c, 7", "0" }, Path.SplitArgs( "\"ab,c, 7\", 0" ) );
Assert.AreEqual( new string[] { " ab,c\", 7 ", "0" }, Path.SplitArgs( "\" ab,c\\\", 7 \", 0" ) );

Assert.AreEqual( new string[] { "{}" }, Path.SplitArgs( " {} " ) );
Assert.AreEqual( new string[] { "{\"name\":\"g\",\"v\":{x:1,y:1,z:1}}" }, Path.SplitArgs( " {\"name\":\"g\",\"v\":{x:1,y:1,z:1}} " ) );
Assert.AreEqual( new string[] { "{\"name\":\"g\",\"v\":{x:1,y:1,z:1}}", "123" }, Path.SplitArgs( " {\"name\":\"g\",\"v\":{x:1,y:1,z:1}} , 123 " ) );

Assert.AreEqual( new string[] { "[{x:1},{a:1,b:1},{c:[1,2,3]}]" }, Path.SplitArgs( "[{x:1},{a:1,b:1},{c:[1,2,3]}]" ) );
}
}

#endif
11 changes: 11 additions & 0 deletions Assets/Editor/Unit Tests/TestPath.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

188 changes: 137 additions & 51 deletions Assets/Unium/GQL/Query/Path.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;

namespace gw.gql
Expand Down Expand Up @@ -42,6 +43,134 @@ public enum Type { Children, Attribute };
}


//----------------------------------------------------------------------------------------------------
// GQL function parameters are JSON objects, separated by commas
// SplitArgs - split a given parameters string into then separate argument

private static readonly HashSet<char> sPunctuation = new HashSet<char>( "\":,{}[] " );

static public string[] SplitArgs( string str )
{
var args = new List<string>();
var value = new StringBuilder();
var depth = 0;
var begin = 0;

for( int i = 0; i < str.Length; i++ )
{
// get next token

var token = str[i];

// ignore whitespace (and control characters)

if( token <= ' ' )
{
continue;
}

// depth == -1 means we have just created an argument
// therefore the only valid next token is a comma

if( depth == -1 )
{
if( token != ',' )
{
throw new FormatException( "GQL::Query - bad function parameters" );
}

depth = 0;
continue;
}

switch( token )
{
// ignore data structure tokens - we are not a validating parser

case ':':
case ',':
break;

// keep track of data structure 'nesting' so we can figure out the end.
// NB: there is no validation here, we just keep track of the brackets

case '{':
case '[':
{
if( ++depth == 1 )
{
begin = i; // the start of a JSON data structure
}
}
break;

case '}':
case ']':
{
if( --depth < 0 )
{
throw new FormatException( "GQL::Query - bad function parameters" );
}
}
break;

// get string

case '\"':
{
value.Clear();

var escape = false;

while( ++i < str.Length && ( escape || str[i] != token ) )
{
escape = !escape && str[i] == '\\';

if( !escape )
{
value.Append( str[i] );
}
}
}
break;

// get value

default:
{
token = 'V';

var start = i;
while( ++i < str.Length && !sPunctuation.Contains( str[i] ) );

value.Clear();
value.Append( str, start, i - start );
--i;
}
break;
}


// process token

if( depth == 0 )
{
if( token == 'V' || token == '\"' )
{
args.Add( value.ToString() );
depth = -1; // want comma
}
else if( token == '}' || token == ']' )
{
args.Add( str.Substring( begin, i - begin + 1 ) );
depth = -1; // want comma
}
}
}

return args.ToArray();
}

//----------------------------------------------------------------------------------------------------
// q := /some/node.attr[x>3]/child.value
// q := /some/node.attr[x>3]/child.value=value
Expand All @@ -61,6 +190,8 @@ public enum Type { Children, Attribute };
#endif
);



public void Parse( string query )
{
// parse path
Expand Down Expand Up @@ -93,7 +224,7 @@ public void Parse( string query )

else
{
// being recrusive find ...
// being recursive find ...


// only available on child nodes because we don't list attr's
Expand Down Expand Up @@ -152,53 +283,8 @@ public void Parse( string query )
throw new FormatException( "GQL::Query - failed to parse invoke, end of arguments not found" );
}

Action = Query.Action.Invoke;

var argsStr = p.Substring( 0, p.Length - 1 );

var args = new List<string>();

for( int pos = 0; pos < argsStr.Length; pos++ )
{
var ch = argsStr[ pos ];

if( char.IsWhiteSpace( ch ) )
{
continue;
}

var start = pos;

if( ch == '\'' || ch == '{' || ch == '"' )
{
var end = ch == '{' ? '}' : ch;

while( ++pos < argsStr.Length )
{
if( argsStr[ pos ] == end )
{
args.Add( argsStr.Substring( start, pos - start + 1 ) );
break;
}
}
}
else
{
do
{
if( pos == argsStr.Length || argsStr[ pos ] == ',' || char.IsWhiteSpace( argsStr[ pos ] ) )
{
args.Add( argsStr.Substring( start, pos - start ) );
break;
}

pos++;
}
while( true );
}
}

Arguments = args.ToArray();
Action = Query.Action.Invoke;
Arguments = SplitArgs( p.Substring( 0, p.Length - 1 ) );
}

// an action can only occur on the last segment
Expand All @@ -214,11 +300,11 @@ public void Parse( string query )

// wildcard name match?

#if NET_2_0
#if NET_2_0
Regex name_match = name.Contains( "*" ) ? new Regex( name.Replace( "*", ".*?" ), RegexOptions.Compiled ) : null;
#else
#else
Regex name_match = name.Contains( "*" ) ? new Regex( name.Replace( "*", ".*?" ) ) : null;
#endif
#endif


// add section to path
Expand Down

0 comments on commit 3fd5dd2

Please sign in to comment.