@@ -15,7 +15,8 @@ import {
1515 isDateTime ,
1616 isType ,
1717 isNumber ,
18- isContext
18+ isContext ,
19+ isBoolean
1920} from './types.js' ;
2021
2122import {
@@ -402,15 +403,15 @@ function evalNode(node: Node, args: any[], interpreterContext: InterpreterContex
402403 case '!=' : return ( b ) => ( a ) => ! equals ( a , b ) ;
403404 }
404405
405- } , Test ( 'boolean' ) ) ;
406+ } , 'test' ) ;
406407
407408 case 'BacktickIdentifier' : return node . input . replace ( / ` / g, '' ) ;
408409
409410 case 'Wildcard' : return ( _context ) => true ;
410411
411- case 'null' : return ( _context ) => {
412+ case 'null' : return tag ( ( _context ) => {
412413 return null ;
413- } ;
414+ } , 'nil' ) ;
414415
415416 case 'Disjunction' : return tag ( ( context ) => {
416417
@@ -433,7 +434,7 @@ function evalNode(node: Node, args: any[], interpreterContext: InterpreterContex
433434 const b = typeof right === 'boolean' ? right : null ;
434435
435436 return matrix . find ( el => el [ 0 ] === a && el [ 1 ] === b ) [ 2 ] ;
436- } , Test ( 'boolean' ) ) ;
437+ } , 'test' ) ;
437438
438439 case 'Conjunction' : return tag ( ( context ) => {
439440 const left = args [ 0 ] ( context ) ;
@@ -455,7 +456,7 @@ function evalNode(node: Node, args: any[], interpreterContext: InterpreterContex
455456 const b = typeof right === 'boolean' ? right : null ;
456457
457458 return matrix . find ( el => el [ 0 ] === a && el [ 1 ] === b ) [ 2 ] ;
458- } , Test ( 'boolean' ) ) ;
459+ } , 'test' ) ;
459460
460461 case 'Context' : return ( context ) => {
461462
@@ -518,7 +519,7 @@ function evalNode(node: Node, args: any[], interpreterContext: InterpreterContex
518519 // spaces into one (token)
519520 case 'Name' : return node . input . replace ( / \s { 2 , } / g, ' ' ) ;
520521
521- case 'VariableName' : return ( context , local = false ) => {
522+ case 'VariableName' : return tag ( ( context , local = false ) => {
522523 const name = args . join ( ' ' ) ;
523524
524525 const contextValue = getFromContext ( name , context ) ;
@@ -558,7 +559,7 @@ function evalNode(node: Node, args: any[], interpreterContext: InterpreterContex
558559 }
559560
560561 return null ;
561- } ;
562+ } , 'any' ) ;
562563
563564 case 'QualifiedName' : return ( context ) => {
564565 return args . reduce ( ( context , arg ) => arg ( context ) , context ) ;
@@ -668,7 +669,7 @@ function evalNode(node: Node, args: any[], interpreterContext: InterpreterContex
668669 const b = args [ 3 ] ( context ) ;
669670
670671 return a instanceof b ;
671- } , Test ( 'boolean' ) ) ;
672+ } , 'test' ) ;
672673
673674 case 'every' : return tag ( ( context ) => {
674675 return ( _contexts , _condition ) => {
@@ -681,7 +682,7 @@ function evalNode(node: Node, args: any[], interpreterContext: InterpreterContex
681682 return contexts . every ( ctx => isTruthy ( _condition ( ctx ) ) ) ;
682683 } ;
683684
684- } , Test ( 'boolean' ) ) ;
685+ } , 'test' ) ;
685686
686687 case 'some' : return tag ( ( context ) => {
687688 return ( _contexts , _condition ) => {
@@ -693,7 +694,7 @@ function evalNode(node: Node, args: any[], interpreterContext: InterpreterContex
693694
694695 return contexts . some ( ctx => isTruthy ( _condition ( ctx ) ) ) ;
695696 } ;
696- } , Test ( 'boolean' ) ) ;
697+ } , 'test' ) ;
697698
698699 case 'NumericLiteral' : return tag ( ( _context ) => node . input . includes ( '.' ) ? parseFloat ( node . input ) : parseInt ( node . input ) , 'number' ) ;
699700
@@ -723,7 +724,7 @@ function evalNode(node: Node, args: any[], interpreterContext: InterpreterContex
723724 return getBuiltin ( node . input , context ) ;
724725 } ;
725726
726- case 'DateTimeLiteral' : return ( context ) => {
727+ case 'DateTimeLiteral' : return tag ( ( context ) => {
727728
728729 // AtLiteral
729730 if ( args . length === 1 ) {
@@ -765,9 +766,9 @@ function evalNode(node: Node, args: any[], interpreterContext: InterpreterContex
765766 return result ;
766767 }
767768
768- } ;
769+ } , 'date' ) ;
769770
770- case 'AtLiteral' : return ( context ) => {
771+ case 'AtLiteral' : return tag ( ( context ) => {
771772
772773 const wrappedFn = wrapFunction ( getBuiltin ( '@' , context ) ) ;
773774
@@ -781,9 +782,9 @@ function evalNode(node: Node, args: any[], interpreterContext: InterpreterContex
781782 }
782783
783784 return wrappedFn . invoke ( [ args [ 0 ] ( context ) ] ) ;
784- } ;
785+ } , 'date' ) ;
785786
786- case 'FunctionInvocation' : return ( context ) => {
787+ case 'FunctionInvocation' : return tag ( ( context ) => {
787788
788789 const target = args [ 0 ] ( context ) ;
789790
@@ -817,7 +818,7 @@ function evalNode(node: Node, args: any[], interpreterContext: InterpreterContex
817818 }
818819
819820 return result ;
820- } ;
821+ } , 'any' ) ;
821822
822823 case 'IfExpression' : return ( function ( ) {
823824
@@ -841,7 +842,7 @@ function evalNode(node: Node, args: any[], interpreterContext: InterpreterContex
841842
842843 case 'Parameters' : return args . length === 3 ? args [ 1 ] : ( _context ) => [ ] ;
843844
844- case 'Comparison' : return ( context ) => {
845+ case 'Comparison' : return tag ( ( context ) => {
845846
846847 const operator = args [ 1 ] ;
847848
@@ -872,7 +873,7 @@ function evalNode(node: Node, args: any[], interpreterContext: InterpreterContex
872873 const test = operator ( ) ( right ) ;
873874
874875 return compareValue ( test , left ) ;
875- } ;
876+ } , 'test' ) ;
876877
877878 case 'QuantifiedExpression' : return ( context ) => {
878879
@@ -944,7 +945,7 @@ function evalNode(node: Node, args: any[], interpreterContext: InterpreterContex
944945
945946 case 'ParenthesizedExpression' : return args [ 1 ] ;
946947
947- case 'PathExpression' : return ( context ) => {
948+ case 'PathExpression' : return tag ( ( context ) => {
948949
949950 const pathTarget = args [ 0 ] ( context ) ;
950951 const pathProp = args [ 1 ] ;
@@ -954,10 +955,10 @@ function evalNode(node: Node, args: any[], interpreterContext: InterpreterContex
954955 } else {
955956 return pathProp ( pathTarget , true ) ;
956957 }
957- } ;
958+ } , 'any' ) ;
958959
959960 // expression !filter "[" expression "]"
960- case 'FilterExpression' : return ( context ) => {
961+ case 'FilterExpression' : return tag ( ( context ) => {
961962
962963 const target = args [ 0 ] ( context ) ;
963964
@@ -970,23 +971,27 @@ function evalNode(node: Node, args: any[], interpreterContext: InterpreterContex
970971 return null ;
971972 }
972973
973- // a[variable=number]
974- if ( typeof filterFn . type === 'undefined' ) {
975- try {
976- const value = filterFn ( context ) ;
974+ const type = filterFn . type ;
977975
978- if ( isNumber ( value ) ) {
979- filterFn . type = 'number' ;
980- }
981- } catch ( _err ) {
976+ // a[1]
977+ // a[true]
978+ // a[b]
979+ // a[b()]
980+ // a[1 + 3]
981+ if ( [ 'number' , 'boolean' , 'any' ] . includes ( type ) ) {
982+ const idx = filterFn ( context ) ;
982983
983- // ignore
984+ if ( isBoolean ( idx ) ) {
985+ if ( idx === true ) {
986+ return target ;
987+ } else {
988+ return [ ] ;
989+ }
984990 }
985- }
986991
987- // a[1]
988- if ( filterFn . type === 'number' ) {
989- const idx = filterFn ( context ) ;
992+ if ( ! isNumber ( idx ) ) {
993+ return [ ] ;
994+ }
990995
991996 const value = filterTarget [ idx < 0 ? filterTarget . length + idx : idx - 1 ] ;
992997
@@ -997,49 +1002,50 @@ function evalNode(node: Node, args: any[], interpreterContext: InterpreterContex
9971002 }
9981003 }
9991004
1000- // a[true]
1001- if ( filterFn . type === 'boolean' ) {
1002- if ( filterFn ( context ) ) {
1003- return filterTarget ;
1004- } else {
1005- return [ ] ;
1006- }
1007- }
1008-
1009- if ( filterFn . type === 'string' ) {
1005+ // TODO(nikku): not covered by spec
1006+ // a["attr"]
1007+ if ( type === 'string' ) {
10101008
10111009 const value = filterFn ( context ) ;
10121010
10131011 return filterTarget . filter ( el => el === value ) ;
10141012 }
10151013
1016- // a[test]
1017- return filterTarget . map ( el => {
1014+ // a[b=c]
1015+ // a[>10]
1016+ if ( type === 'test' ) {
10181017
1019- const iterationContext = {
1020- ...context ,
1021- item : el ,
1022- ...el
1023- } ;
1018+ return filterTarget . map ( el => {
10241019
1025- let result = filterFn ( iterationContext ) ;
1020+ const iterationContext = {
1021+ ...context ,
1022+ item : el ,
1023+ ...el
1024+ } ;
10261025
1027- // test is fn(val) => boolean SimpleUnaryTest
1028- if ( typeof result === 'function' ) {
1029- result = result ( el ) ;
1030- }
1026+ let result = filterFn ( iterationContext ) ;
10311027
1032- if ( result instanceof Range ) {
1033- result = result . includes ( el ) ;
1034- }
1028+ // test is fn(val) => boolean SimpleUnaryTest
1029+ if ( typeof result === 'function' ) {
1030+ result = result ( el ) ;
1031+ }
10351032
1036- if ( result === true ) {
1037- return el ;
1038- }
1033+ if ( result instanceof Range ) {
1034+ result = result . includes ( el ) ;
1035+ }
10391036
1040- return result ;
1041- } ) . filter ( isTruthy ) ;
1042- } ;
1037+ if ( result === true ) {
1038+ return el ;
1039+ }
1040+
1041+ return result ;
1042+ } ) . filter ( isTruthy ) ;
1043+ }
1044+
1045+ // a[null]
1046+ // a[@"2026-12-12"]
1047+ return null ;
1048+ } , 'any' ) ;
10431049
10441050 case 'SimplePositiveUnaryTest' : return tag ( ( context ) => {
10451051
@@ -1065,7 +1071,7 @@ function evalNode(node: Node, args: any[], interpreterContext: InterpreterContex
10651071 const endIncluded = right !== null && args [ 3 ] === ']' ;
10661072
10671073 return createRange ( left , right , startIncluded , endIncluded ) ;
1068- } , Test ( 'boolean' ) ) ;
1074+ } , 'test' ) ;
10691075
10701076 case 'PositiveUnaryTests' :
10711077 case 'Expressions' : return ( context ) => {
@@ -1432,10 +1438,6 @@ function isTruthy(obj) {
14321438 return obj !== false && obj !== null ;
14331439}
14341440
1435- function Test ( type : string ) : string {
1436- return `Test<${ type } >` ;
1437- }
1438-
14391441/**
14401442 * @param {Function } fn
14411443 * @param {string[] } [parameterNames]
0 commit comments