@@ -736,106 +736,121 @@ func parseArrayPath(path string) (r arrayPathResult) {
736
736
r .alogkey = path [2 :]
737
737
r .path = path [:1 ]
738
738
} else if path [1 ] == '[' || path [1 ] == '(' {
739
- var end byte
740
- if path [1 ] == '[' {
741
- end = ']'
742
- } else {
743
- end = ')'
744
- }
745
- r .query .on = true
746
739
// query
747
- i += 2
748
- // whitespace
749
- for ; i < len (path ); i ++ {
750
- if path [i ] > ' ' {
740
+ r .query .on = true
741
+ if true {
742
+ qpath , op , value , _ , fi , ok := parseQuery (path [i :])
743
+ if ! ok {
744
+ // bad query, end now
751
745
break
752
746
}
753
- }
754
- s := i
755
- for ; i < len (path ); i ++ {
756
- if path [i ] <= ' ' ||
757
- path [i ] == '!' ||
758
- path [i ] == '=' ||
759
- path [i ] == '<' ||
760
- path [i ] == '>' ||
761
- path [i ] == '%' ||
762
- path [i ] == end {
763
- break
747
+ r .query .path = qpath
748
+ r .query .op = op
749
+ r .query .value = value
750
+ i = fi - 1
751
+ if i + 1 < len (path ) && path [i + 1 ] == '#' {
752
+ r .query .all = true
764
753
}
765
- }
766
- r . query . path = path [ s : i ]
767
- // whitespace
768
- for ; i < len ( path ); i ++ {
769
- if path [ i ] > ' ' {
770
- break
754
+ } else {
755
+ var end byte
756
+ if path [ 1 ] == '[' {
757
+ end = ']'
758
+ } else {
759
+ end = ')'
771
760
}
772
- }
773
- if i < len (path ) {
774
- s = i
775
- if path [i ] == '!' {
776
- if i < len (path )- 1 && (path [i + 1 ] == '=' ||
777
- path [i + 1 ] == '%' ) {
778
- i ++
779
- }
780
- } else if path [i ] == '<' || path [i ] == '>' {
781
- if i < len (path )- 1 && path [i + 1 ] == '=' {
782
- i ++
761
+ i += 2
762
+ // whitespace
763
+ for ; i < len (path ); i ++ {
764
+ if path [i ] > ' ' {
765
+ break
783
766
}
784
- } else if path [i ] == '=' {
785
- if i < len (path )- 1 && path [i + 1 ] == '=' {
786
- s ++
787
- i ++
767
+ }
768
+ s := i
769
+ for ; i < len (path ); i ++ {
770
+ if path [i ] <= ' ' ||
771
+ path [i ] == '!' ||
772
+ path [i ] == '=' ||
773
+ path [i ] == '<' ||
774
+ path [i ] == '>' ||
775
+ path [i ] == '%' ||
776
+ path [i ] == end {
777
+ break
788
778
}
789
779
}
790
- i ++
791
- r .query .op = path [s :i ]
780
+ r .query .path = path [s :i ]
792
781
// whitespace
793
782
for ; i < len (path ); i ++ {
794
783
if path [i ] > ' ' {
795
784
break
796
785
}
797
786
}
798
- s = i
799
- for ; i < len (path ); i ++ {
800
- if path [i ] == '"' {
801
- i ++
802
- s2 := i
803
- for ; i < len (path ); i ++ {
804
- if path [i ] > '\\' {
805
- continue
806
- }
807
- if path [i ] == '"' {
808
- // look for an escaped slash
809
- if path [i - 1 ] == '\\' {
810
- n := 0
811
- for j := i - 2 ; j > s2 - 1 ; j -- {
812
- if path [j ] != '\\' {
813
- break
787
+ if i < len (path ) {
788
+ s = i
789
+ if path [i ] == '!' {
790
+ if i < len (path )- 1 && (path [i + 1 ] == '=' ||
791
+ path [i + 1 ] == '%' ) {
792
+ i ++
793
+ }
794
+ } else if path [i ] == '<' || path [i ] == '>' {
795
+ if i < len (path )- 1 && path [i + 1 ] == '=' {
796
+ i ++
797
+ }
798
+ } else if path [i ] == '=' {
799
+ if i < len (path )- 1 && path [i + 1 ] == '=' {
800
+ s ++
801
+ i ++
802
+ }
803
+ }
804
+ i ++
805
+ r .query .op = path [s :i ]
806
+ // whitespace
807
+ for ; i < len (path ); i ++ {
808
+ if path [i ] > ' ' {
809
+ break
810
+ }
811
+ }
812
+ s = i
813
+ for ; i < len (path ); i ++ {
814
+ if path [i ] == '"' {
815
+ i ++
816
+ s2 := i
817
+ for ; i < len (path ); i ++ {
818
+ if path [i ] > '\\' {
819
+ continue
820
+ }
821
+ if path [i ] == '"' {
822
+ // look for an escaped slash
823
+ if path [i - 1 ] == '\\' {
824
+ n := 0
825
+ for j := i - 2 ; j > s2 - 1 ; j -- {
826
+ if path [j ] != '\\' {
827
+ break
828
+ }
829
+ n ++
830
+ }
831
+ if n % 2 == 0 {
832
+ continue
814
833
}
815
- n ++
816
- }
817
- if n % 2 == 0 {
818
- continue
819
834
}
835
+ break
820
836
}
821
- break
822
837
}
838
+ } else if path [i ] == end {
839
+ if i + 1 < len (path ) && path [i + 1 ] == '#' {
840
+ r .query .all = true
841
+ }
842
+ break
823
843
}
824
- } else if path [i ] == end {
825
- if i + 1 < len (path ) && path [i + 1 ] == '#' {
826
- r .query .all = true
827
- }
828
- break
829
844
}
845
+ if i > len (path ) {
846
+ i = len (path )
847
+ }
848
+ v := path [s :i ]
849
+ for len (v ) > 0 && v [len (v )- 1 ] <= ' ' {
850
+ v = v [:len (v )- 1 ]
851
+ }
852
+ r .query .value = v
830
853
}
831
- if i > len (path ) {
832
- i = len (path )
833
- }
834
- v := path [s :i ]
835
- for len (v ) > 0 && v [len (v )- 1 ] <= ' ' {
836
- v = v [:len (v )- 1 ]
837
- }
838
- r .query .value = v
839
854
}
840
855
}
841
856
}
@@ -847,6 +862,115 @@ func parseArrayPath(path string) (r arrayPathResult) {
847
862
return
848
863
}
849
864
865
+ // splitQuery takes a query and splits it into three parts:
866
+ // path, op, middle, and right.
867
+ // So for this query:
868
+ // #(first_name=="Murphy").last
869
+ // Becomes
870
+ // first_name # path
871
+ // =="Murphy" # middle
872
+ // .last # right
873
+ // Or,
874
+ // #(service_roles.#(=="one")).cap
875
+ // Becomes
876
+ // service_roles.#(=="one") # path
877
+ // # middle
878
+ // .cap # right
879
+ func parseQuery (query string ) (
880
+ path , op , value , remain string , i int , ok bool ,
881
+ ) {
882
+ if len (query ) < 2 || query [0 ] != '#' ||
883
+ (query [1 ] != '(' && query [1 ] != '[' ) {
884
+ return "" , "" , "" , "" , i , false
885
+ }
886
+ i = 2
887
+ j := 0 // start of value part
888
+ depth := 1
889
+ for ; i < len (query ); i ++ {
890
+ if depth == 1 && j == 0 {
891
+ switch query [i ] {
892
+ case '!' , '=' , '<' , '>' , '%' :
893
+ // start of the value part
894
+ j = i
895
+ continue
896
+ }
897
+ }
898
+ if query [i ] == '\\' {
899
+ i ++
900
+ } else if query [i ] == '[' || query [i ] == '(' {
901
+ depth ++
902
+ } else if query [i ] == ']' || query [i ] == ')' {
903
+ depth --
904
+ if depth == 0 {
905
+ break
906
+ }
907
+ } else if query [i ] == '"' {
908
+ // inside selector string, balance quotes
909
+ i ++
910
+ for ; i < len (query ); i ++ {
911
+ if query [i ] == '\\' {
912
+ i ++
913
+ } else if query [i ] == '"' {
914
+ break
915
+ }
916
+ }
917
+ }
918
+ }
919
+ if depth > 0 {
920
+ return "" , "" , "" , "" , i , false
921
+ }
922
+ if j > 0 {
923
+ path = trim (query [2 :j ])
924
+ value = trim (query [j :i ])
925
+ remain = query [i + 1 :]
926
+ // parse the compare op from the value
927
+ var opsz int
928
+ switch {
929
+ case len (value ) == 1 :
930
+ opsz = 1
931
+ case value [0 ] == '!' && value [1 ] == '=' :
932
+ opsz = 2
933
+ case value [0 ] == '!' && value [1 ] == '%' :
934
+ opsz = 2
935
+ case value [0 ] == '<' && value [1 ] == '=' :
936
+ opsz = 2
937
+ case value [0 ] == '>' && value [1 ] == '=' :
938
+ opsz = 2
939
+ case value [0 ] == '=' && value [1 ] == '=' :
940
+ value = value [1 :]
941
+ opsz = 1
942
+ case value [0 ] == '<' :
943
+ opsz = 1
944
+ case value [0 ] == '>' :
945
+ opsz = 1
946
+ case value [0 ] == '=' :
947
+ opsz = 1
948
+ case value [0 ] == '%' :
949
+ opsz = 1
950
+ }
951
+ op = value [:opsz ]
952
+ value = trim (value [opsz :])
953
+ } else {
954
+ path = trim (query [2 :i ])
955
+ remain = query [i + 1 :]
956
+ }
957
+ return path , op , value , remain , i + 1 , true
958
+ }
959
+
960
+ func trim (s string ) string {
961
+ left:
962
+ if len (s ) > 0 && s [0 ] <= ' ' {
963
+ s = s [1 :]
964
+ goto left
965
+ }
966
+ right:
967
+ if len (s ) > 0 && s [len (s )- 1 ] <= ' ' {
968
+ s = s [:len (s )- 1 ]
969
+ goto right
970
+ }
971
+ return s
972
+ }
973
+
850
974
type objectPathResult struct {
851
975
part string
852
976
path string
@@ -1135,6 +1259,16 @@ func queryMatches(rp *arrayPathResult, value Result) bool {
1135
1259
if len (rpv ) > 2 && rpv [0 ] == '"' && rpv [len (rpv )- 1 ] == '"' {
1136
1260
rpv = rpv [1 : len (rpv )- 1 ]
1137
1261
}
1262
+ if ! value .Exists () {
1263
+ return false
1264
+ }
1265
+ if rp .query .op == "" {
1266
+ // the query is only looking for existence, such as:
1267
+ // friends.#(name)
1268
+ // which makes sure that the array "friends" has an element of
1269
+ // "name" that exists
1270
+ return true
1271
+ }
1138
1272
switch value .Type {
1139
1273
case String :
1140
1274
switch rp .query .op {
0 commit comments