diff --git a/js/httpql/src/index.spec.ts b/js/httpql/src/index.spec.ts index bca1aa0..09a0595 100644 --- a/js/httpql/src/index.spec.ts +++ b/js/httpql/src/index.spec.ts @@ -26,8 +26,8 @@ const readFile = (path: string) => { describe("httpql", () => { describe("ast", () => { const cases = [ - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, - 21, 22, 23, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, ]; for (const c of cases) { it(`Case ${c}`, () => { diff --git a/js/httpql/src/parser/parser.terms.ts b/js/httpql/src/parser/parser.terms.ts index 6391968..0fb36b2 100644 --- a/js/httpql/src/parser/parser.terms.ts +++ b/js/httpql/src/parser/parser.terms.ts @@ -1,6 +1,5 @@ // This file was generated by lezer-generator. You probably shouldn't edit it. -export const - BlockComment = 1, +export const BlockComment = 1, LineComment = 2, HTTPQL = 3, Query = 4, @@ -57,4 +56,4 @@ export const GroupQuery = 55, LogicalQuery = 56, And = 57, - Or = 58 + Or = 58; diff --git a/js/httpql/src/parser/parser.ts b/js/httpql/src/parser/parser.ts index 8b9dc86..2d14e56 100644 --- a/js/httpql/src/parser/parser.ts +++ b/js/httpql/src/parser/parser.ts @@ -1,22 +1,39 @@ // This file was generated by lezer-generator. You probably shouldn't edit it. -import {LRParser, LocalTokenGroup} from "@lezer/lr" -import {highlight} from "./props.js" +import { LocalTokenGroup, LRParser } from "@lezer/lr"; + +import { highlight } from "./props.js"; export const parser = LRParser.deserialize({ version: 14, - states: "-zQ]QPOOPtOPOOOyOQO'#CcOOQO'#Cb'#CbO!ROPO'#CeO!WOPO'#CmO!]OPO'#DTO!bOPO'#DZO!gOPO'#D`OOQO'#Ca'#CaO!lQPO'#DeQ!sQPOOOOQO'#C`'#C`P!{O!bO'#C]POOO)C?c)C?cOOOO'#Dj'#DjO#WOQO,58}OOQO,58},58}O#`OPO,59PO#eOPO,59XO#vOSO,59oO$ROWO,59uOoOPO,59zOOQO,5:P,5:PO$ZQPO,5:PO]QPO,5:QO]QPO,5:QPOOO'#Di'#DiP$fO!bO,58wPOOO,58w,58wOOOO-E7h-E7hOOQO1G.i1G.iO$qOPO1G.kO$vOPO1G.sO${OPO1G.sO%QOPO1G.sO%VOPO1G.sO%[OPO1G.sO%dOPO1G/ZO%iOPO1G/ZO%nOPO1G/ZOOQO'#D]'#D]OOQO'#D^'#D^OOQO1G/a1G/aOOQO'#Db'#DbOOQO1G/f1G/fOOQO1G/k1G/kOOQO1G/l1G/lO%vQPO1G/lPOOO-E7g-E7gPOOO1G.c1G.cO&UOPO7+$VO&UOPO7+$_O&ZOSO7+$_O&cOSO7+$_O&hO`O7+$_O&mOPO7+$_OoOPO7+$_O&UOPO7+$uO&ZOSO7+$uO&rOSO7+$uOoOPO7+$uO&wOPO'#CiOOQO<O#X#o&g!U>TU!Rp}!O&g!Q![&g#R#S&g#T#X&g#X#Y>g#Y#o&g!U>lU!Rp}!O&g!Q![&g#R#S&g#T#f&g#f#g?O#g#o&g!U?XSsS{`!Rp}!O&g!Q![&g#R#S&g#T#o>?jU!Rp}!O&g!Q![&g#R#S&g#T#g&g#g#h7i#h#o&g|@TUWW!Rp}!O&g!Q![&g#R#S&g#T#W&g#W#X@g#X#o>@nS[S!Rp}!O&g!Q![&g#R#S&g#T#o&g!^ARYWW!Rp}!O&g!Q![&g#R#S&g#T#X&g#X#YAq#Y#]&g#]#^Bo#^#h&g#h#i;b#i#o&g!UAvU!Rp}!O&g!Q![&g#R#S&g#T#b&g#b#cBY#c#o&g!UBcScSy`!Rp}!O&g!Q![&g#R#S&g#T#o&g!QBtU!Rp}!O&g!Q![&g#R#S&g#T#_&g#_#`CW#`#o&g!QC]U!Rp}!O&g!Q![&g#R#S&g#T#X&g#X#Y1u#Y#o&g|CvUWW!Rp}!O&g!Q![&g#R#S&g#T#X&g#X#YDY#Y#o>D_U!Rp}!O&g!Q![&g#R#S&g#T#h&g#h#iDq#i#o>DvU!Rp}!O&g!Q![&g#R#S&g#T#[&g#[#]EY#]#o>E_U!Rp}!O&g!Q![&g#R#S&g#T#c&g#c#dEq#d#o>EvU!Rp}!O&g!Q![&g#R#S&g#T#W&g#W#X8Q#X#o&g#pFa]WW!Rp}!O&g!Q![&g#R#S&g#T#UGY#U#V&g#V#WHo#W#X&g#X#Y7Q#Y#`&g#`#aIo#a#f&g#f#gJW#g#o&g!UG_U!Rp}!O&g!Q![&g#R#S&g#T#a&g#a#bGq#b#o&g!UGvU!Rp}!O&g!Q![&g#R#S&g#T#X&g#X#YHY#Y#o&g!UHcStS|`!Rp}!O&g!Q![&g#R#S&g#T#o&g!QHtU!Rp}!O&g!Q![&g#R#S&g#T#c&g#c#dIW#d#o&g!QI]U!Rp}!O&g!Q![&g#R#S&g#T#b&g#b#c1^#c#o&g!QItU!Rp}!O&g!Q![&g#R#S&g#T#]&g#]#^Bo#^#o&g!QJ]U!Rp}!O&g!Q![&g#R#S&g#T#X&g#X#YJo#Y#o&g!QJtU!Rp}!O&g!Q![&g#R#S&g#T#Z&g#Z#[KW#[#o&g!QK]U!Rp}!O&g!Q![&g#R#S&g#T#X&g#X#YKo#Y#o&g!QKtU!Rp}!O&g!Q![&g#R#S&g#T#l&g#l#mLW#m#o&g!QL_Sg`!Rp}!O&g!Q![&g#R#S&g#T#o&g|LrUWW!Rp}!O&g!Q![&g#R#S&g#T#f&g#f#gMU#g#o>M]S![S!Rp}!O&g!Q![&g#R#S&g#T#o&g|MpXWW!Rp}!O&g!Q![&g#R#S&g#T#UN]#U#c&g#c#d! ]#d#f&g#f#g!!p#g#o>NbU!Rp}!O&g!Q![&g#R#S&g#T#h&g#h#iNt#i#o>NyU!Rp}!O&g!Q![&g#R#S&g#T#[&g#[#]8Q#]#o>! bU!Rp}!O&g!Q![&g#R#S&g#T#f&g#f#g! t#g#o>! yU!Rp}!O&g!Q![&g#R#S&g#T#h&g#h#i!!]#i#o>!!dScS!Rp}!O&g!Q![&g#R#S&g#T#o>!!uU!Rp}!O&g!Q![&g#R#S&g#T#X&g#X#Y!#X#Y#o>!#^U!Rp}!O&g!Q![&g#R#S&g#T#g&g#g#h!#p#h#o>!#uU!Rp}!O&g!Q![&g#R#S&g#T#X&g#X#Y!$X#Y#o>!$^U!Rp}!O&g!Q![&g#R#S&g#T#h&g#h#i!$p#i#o>!$wS!OS!Rp}!O&g!Q![&g#R#S&g#T#o&g|!%[UWW!Rp}!O&g!Q![&g#R#S&g#T#i&g#i#j!%n#j#o>!%sU!Rp}!O&g!Q![&g#R#S&g#T#X&g#X#Y!&V#Y#o>!&[U!Rp}!O&g!Q![&g#R#S&g#T#f&g#f#g!&n#g#o>!&sU!Rp}!O&g!Q![&g#R#S&g#T#m&g#m#n8Q#n#o&g!^!'^XWW!Rp}!O&g!Q![&g#R#S&g#T#U!'y#U#X&g#X#Y!(b#Y#c&g#c#d!*f#d#o&g!U!(OU!Rp}!O&g!Q![&g#R#S&g#T#k&g#k#l.m#l#o&g!U!(gY!Rp}!O&g!Q![&g#R#S&g#T#Z&g#Z#[KW#[#e&g#e#f!)V#f#g&g#g#h!)j#h#o>!)^SbS!Rp}!O&g!Q![&g#R#S&g#T#o>!)oU!Rp}!O&g!Q![&g#R#S&g#T#d&g#d#e!*R#e#o>!*YSxS!Rp}!O&g!Q![&g#R#S&g#T#o&g!U!*kW!Rp}!O&g!Q![&g#R#S&g#T#i&g#i#j!+T#j#k&g#k#l!.T#l#o&g!Q!+YU!Rp}!O&g!Q![&g#R#S&g#T#b&g#b#c!+l#c#o&g!Q!+qU!Rp}!O&g!Q![&g#R#S&g#T#W&g#W#X!,T#X#o&g!Q!,YU!Rp}!O&g!Q![&g#R#S&g#T#h&g#h#i!,l#i#o&g!Q!,qU!Rp}!O&g!Q![&g#R#S&g#T#f&g#f#g!-T#g#o&g!Q!-YU!Rp}!O&g!Q![&g#R#S&g#T#]&g#]#^!-l#^#o&g!Q!-qU!Rp}!O&g!Q![&g#R#S&g#T#d&g#d#e0y#e#o>!.[SYS!Rp}!O&g!Q![&g#R#S&g#T#o&g|!.oUWW!Rp}!O&g!Q![&g#R#S&g#T#c&g#c#d!/R#d#o>!/WU!Rp}!O&g!Q![&g#R#S&g#T#i&g#i#j!/j#j#o>!/oU!Rp}!O&g!Q![&g#R#S&g#T#f&g#f#g!0R#g#o>!0WU!Rp}!O&g!Q![&g#R#S&g#T#V&g#V#W!0j#W#o>!0oU!Rp}!O&g!Q![&g#R#S&g#T#X&g#X#Y!1R#Y#o>!1YS!TS!Rp}!O&g!Q![&g#R#S&g#T#o&g|!1mWWW!Rp}!O&g!Q![&g#R#S&g#T#`&g#`#a!2V#a#f&g#f#g!3R#g#o>!2[U!Rp}!O&g!Q![&g#R#S&g#T#g&g#g#h!2n#h#o>!2uSoS!Rp}!O&g!Q![&g#R#S&g#T#o>!3WU!Rp}!O&g!Q![&g#R#S&g#T#i&g#i#j9{#j#o&g!^!3qTWW!Rp}!O&g!Q![&g#R#S&g#T#U!4Q#U#o&g!U!4VU!Rp}!O&g!Q![&g#R#S&g#T#`&g#`#a!4i#a#o&g!U!4nU!Rp}!O&g!Q![&g#R#S&g#T#i&g#i#jGq#j#o&gW!5TP;=`<%l%j", - tokenizers: [2, 3, 4, 5, 6, new LocalTokenGroup("x~RQ!P!QX#O#P^~^Ok~~aRO;'Sj;'S;=`o;=`Oj~oO!i~~tP!i~;=`<%lj~", 39, 70), new LocalTokenGroup("u~RRYZ[]^az{i~aO!e~~fP!e~YZ[~lP!P!Qo~tO!f~~", 36, 66)], - topRules: {"HTTPQL":[0,3]}, - tokenPrec: 0 -}) + tokenData: + "!5W~R!cX^%^pq%cqr%jrs%osx%jxy%tyz%{z}%j}!O&S!O!P&x!P!Q'P!Q!['z![!](t!]!c%j!c!d({!d!q%j!q!r)`!r!}%j!}#O)m#O#P)t#P#Q+f#Q#R%j#R#S&S#S#T%j#T#U+m#U#V-S#V#W/S#W#X&S#X#Y6a#Y#Z8e#Z#[:w#[#]O#X#o&g!U>TU!Rp}!O&g!Q![&g#R#S&g#T#X&g#X#Y>g#Y#o&g!U>lU!Rp}!O&g!Q![&g#R#S&g#T#f&g#f#g?O#g#o&g!U?XSsS{`!Rp}!O&g!Q![&g#R#S&g#T#o>?jU!Rp}!O&g!Q![&g#R#S&g#T#g&g#g#h7i#h#o&g|@TUWW!Rp}!O&g!Q![&g#R#S&g#T#W&g#W#X@g#X#o>@nS[S!Rp}!O&g!Q![&g#R#S&g#T#o&g!^ARYWW!Rp}!O&g!Q![&g#R#S&g#T#X&g#X#YAq#Y#]&g#]#^Bo#^#h&g#h#i;b#i#o&g!UAvU!Rp}!O&g!Q![&g#R#S&g#T#b&g#b#cBY#c#o&g!UBcScSy`!Rp}!O&g!Q![&g#R#S&g#T#o&g!QBtU!Rp}!O&g!Q![&g#R#S&g#T#_&g#_#`CW#`#o&g!QC]U!Rp}!O&g!Q![&g#R#S&g#T#X&g#X#Y1u#Y#o&g|CvUWW!Rp}!O&g!Q![&g#R#S&g#T#X&g#X#YDY#Y#o>D_U!Rp}!O&g!Q![&g#R#S&g#T#h&g#h#iDq#i#o>DvU!Rp}!O&g!Q![&g#R#S&g#T#[&g#[#]EY#]#o>E_U!Rp}!O&g!Q![&g#R#S&g#T#c&g#c#dEq#d#o>EvU!Rp}!O&g!Q![&g#R#S&g#T#W&g#W#X8Q#X#o&g#pFa]WW!Rp}!O&g!Q![&g#R#S&g#T#UGY#U#V&g#V#WHo#W#X&g#X#Y7Q#Y#`&g#`#aIo#a#f&g#f#gJW#g#o&g!UG_U!Rp}!O&g!Q![&g#R#S&g#T#a&g#a#bGq#b#o&g!UGvU!Rp}!O&g!Q![&g#R#S&g#T#X&g#X#YHY#Y#o&g!UHcStS|`!Rp}!O&g!Q![&g#R#S&g#T#o&g!QHtU!Rp}!O&g!Q![&g#R#S&g#T#c&g#c#dIW#d#o&g!QI]U!Rp}!O&g!Q![&g#R#S&g#T#b&g#b#c1^#c#o&g!QItU!Rp}!O&g!Q![&g#R#S&g#T#]&g#]#^Bo#^#o&g!QJ]U!Rp}!O&g!Q![&g#R#S&g#T#X&g#X#YJo#Y#o&g!QJtU!Rp}!O&g!Q![&g#R#S&g#T#Z&g#Z#[KW#[#o&g!QK]U!Rp}!O&g!Q![&g#R#S&g#T#X&g#X#YKo#Y#o&g!QKtU!Rp}!O&g!Q![&g#R#S&g#T#l&g#l#mLW#m#o&g!QL_Sg`!Rp}!O&g!Q![&g#R#S&g#T#o&g|LrUWW!Rp}!O&g!Q![&g#R#S&g#T#f&g#f#gMU#g#o>M]S![S!Rp}!O&g!Q![&g#R#S&g#T#o&g|MpXWW!Rp}!O&g!Q![&g#R#S&g#T#UN]#U#c&g#c#d! ]#d#f&g#f#g!!p#g#o>NbU!Rp}!O&g!Q![&g#R#S&g#T#h&g#h#iNt#i#o>NyU!Rp}!O&g!Q![&g#R#S&g#T#[&g#[#]8Q#]#o>! bU!Rp}!O&g!Q![&g#R#S&g#T#f&g#f#g! t#g#o>! yU!Rp}!O&g!Q![&g#R#S&g#T#h&g#h#i!!]#i#o>!!dScS!Rp}!O&g!Q![&g#R#S&g#T#o>!!uU!Rp}!O&g!Q![&g#R#S&g#T#X&g#X#Y!#X#Y#o>!#^U!Rp}!O&g!Q![&g#R#S&g#T#g&g#g#h!#p#h#o>!#uU!Rp}!O&g!Q![&g#R#S&g#T#X&g#X#Y!$X#Y#o>!$^U!Rp}!O&g!Q![&g#R#S&g#T#h&g#h#i!$p#i#o>!$wS!OS!Rp}!O&g!Q![&g#R#S&g#T#o&g|!%[UWW!Rp}!O&g!Q![&g#R#S&g#T#i&g#i#j!%n#j#o>!%sU!Rp}!O&g!Q![&g#R#S&g#T#X&g#X#Y!&V#Y#o>!&[U!Rp}!O&g!Q![&g#R#S&g#T#f&g#f#g!&n#g#o>!&sU!Rp}!O&g!Q![&g#R#S&g#T#m&g#m#n8Q#n#o&g!^!'^XWW!Rp}!O&g!Q![&g#R#S&g#T#U!'y#U#X&g#X#Y!(b#Y#c&g#c#d!*f#d#o&g!U!(OU!Rp}!O&g!Q![&g#R#S&g#T#k&g#k#l.m#l#o&g!U!(gY!Rp}!O&g!Q![&g#R#S&g#T#Z&g#Z#[KW#[#e&g#e#f!)V#f#g&g#g#h!)j#h#o>!)^SbS!Rp}!O&g!Q![&g#R#S&g#T#o>!)oU!Rp}!O&g!Q![&g#R#S&g#T#d&g#d#e!*R#e#o>!*YSxS!Rp}!O&g!Q![&g#R#S&g#T#o&g!U!*kW!Rp}!O&g!Q![&g#R#S&g#T#i&g#i#j!+T#j#k&g#k#l!.T#l#o&g!Q!+YU!Rp}!O&g!Q![&g#R#S&g#T#b&g#b#c!+l#c#o&g!Q!+qU!Rp}!O&g!Q![&g#R#S&g#T#W&g#W#X!,T#X#o&g!Q!,YU!Rp}!O&g!Q![&g#R#S&g#T#h&g#h#i!,l#i#o&g!Q!,qU!Rp}!O&g!Q![&g#R#S&g#T#f&g#f#g!-T#g#o&g!Q!-YU!Rp}!O&g!Q![&g#R#S&g#T#]&g#]#^!-l#^#o&g!Q!-qU!Rp}!O&g!Q![&g#R#S&g#T#d&g#d#e0y#e#o>!.[SYS!Rp}!O&g!Q![&g#R#S&g#T#o&g|!.oUWW!Rp}!O&g!Q![&g#R#S&g#T#c&g#c#d!/R#d#o>!/WU!Rp}!O&g!Q![&g#R#S&g#T#i&g#i#j!/j#j#o>!/oU!Rp}!O&g!Q![&g#R#S&g#T#f&g#f#g!0R#g#o>!0WU!Rp}!O&g!Q![&g#R#S&g#T#V&g#V#W!0j#W#o>!0oU!Rp}!O&g!Q![&g#R#S&g#T#X&g#X#Y!1R#Y#o>!1YS!TS!Rp}!O&g!Q![&g#R#S&g#T#o&g|!1mWWW!Rp}!O&g!Q![&g#R#S&g#T#`&g#`#a!2V#a#f&g#f#g!3R#g#o>!2[U!Rp}!O&g!Q![&g#R#S&g#T#g&g#g#h!2n#h#o>!2uSoS!Rp}!O&g!Q![&g#R#S&g#T#o>!3WU!Rp}!O&g!Q![&g#R#S&g#T#i&g#i#j9{#j#o&g!^!3qTWW!Rp}!O&g!Q![&g#R#S&g#T#U!4Q#U#o&g!U!4VU!Rp}!O&g!Q![&g#R#S&g#T#`&g#`#a!4i#a#o&g!U!4nU!Rp}!O&g!Q![&g#R#S&g#T#i&g#i#jGq#j#o&gW!5TP;=`<%l%j", + tokenizers: [ + 2, + 3, + 4, + 5, + 6, + new LocalTokenGroup( + "x~RQ!P!QX#O#P^~^Ok~~aRO;'Sj;'S;=`o;=`Oj~oO!i~~tP!i~;=`<%lj~", + 39, + 70, + ), + new LocalTokenGroup("u~RRYZ[]^az{i~aO!e~~fP!e~YZ[~lP!P!Qo~tO!f~~", 36, 66), + ], + topRules: { HTTPQL: [0, 3] }, + tokenPrec: 0, +}); diff --git a/js/httpql/src/serialize.ts b/js/httpql/src/serialize.ts index d2b3c6a..0abb8b1 100644 --- a/js/httpql/src/serialize.ts +++ b/js/httpql/src/serialize.ts @@ -2,7 +2,6 @@ import { err, ok, Result } from "neverthrow"; import { type HTTPQLError, InvalidQuery } from "./errors.js"; import { - OperatorString, type ClauseHeader, type ClauseRequest, type ClauseResponse, @@ -13,6 +12,7 @@ import { type ExprPreset, type ExprSource, type ExprString, + OperatorString, type Query, } from "./primitives.js"; import { isPresent } from "./utils.js"; diff --git a/js/streamql/package.json b/js/streamql/package.json index 3a738a9..e99baaf 100644 --- a/js/streamql/package.json +++ b/js/streamql/package.json @@ -1,6 +1,6 @@ { "name": "@caido/streamql", - "version": "0.3.0", + "version": "0.3.1", "description": "Parser for the StreamQL language", "author": "Caido Labs Inc. ", "repository": "https://github.com/caido/httpql", diff --git a/js/streamql/src/deserialize/clause.websocket.ts b/js/streamql/src/deserialize/clause.websocket.ts index 6dfa671..5658843 100644 --- a/js/streamql/src/deserialize/clause.websocket.ts +++ b/js/streamql/src/deserialize/clause.websocket.ts @@ -8,6 +8,7 @@ import type { ClauseWs } from "../primitives.js"; import { getChildString, isPresent } from "../utils.js"; import { deserializeDateExpr } from "./expr.date.js"; +import { deserializeIntExpr } from "./expr.int.js"; import { deserializeStringExpr } from "./expr.string.js"; export const deserializeWsClause = ( @@ -40,6 +41,17 @@ export const deserializeWsClause = ( } })(); + const intField = (() => { + const child = getChildString(node, terms.WsIntFieldName, doc); + + if (isPresent(child)) { + switch (child) { + case "len": + return "len"; + } + } + })(); + const stringFilter = (() => { const child = node.getChild(terms.StringExpression); if (isPresent(child)) return deserializeStringExpr(child, doc); @@ -50,6 +62,11 @@ export const deserializeWsClause = ( if (isPresent(child)) return deserializeDateExpr(child, doc); })(); + const intFilter = (() => { + const child = node.getChild(terms.IntExpression); + if (isPresent(child)) return deserializeIntExpr(child, doc); + })(); + if (isPresent(stringField) && isPresent(stringFilter)) { return stringFilter.map((filter) => { return { @@ -66,5 +83,13 @@ export const deserializeWsClause = ( }); } + if (isPresent(intField) && isPresent(intFilter)) { + return intFilter.map((filter) => { + return { + [intField]: filter, + }; + }); + } + return err(new InvalidQuery()); }; diff --git a/js/streamql/src/deserialize/index.spec.ts b/js/streamql/src/deserialize/index.spec.ts index 50d15f9..be3b361 100644 --- a/js/streamql/src/deserialize/index.spec.ts +++ b/js/streamql/src/deserialize/index.spec.ts @@ -103,6 +103,29 @@ describe("deserialize", () => { }); }); + it("should parse StreamQL ws len expression", () => { + const query = "ws.len.gte:1212"; + + const filter = deserialize(query)._unsafeUnwrap(); + + expect(filter).to.deep.equal({ + websocket: { + len: { + operator: OperatorInt.Gte, + value: 1212, + }, + }, + }); + }); + + it("should fail given a quoted ws len value", () => { + const query = 'ws.len.lt:"12"'; + + const result = deserialize(query); + + expect(result.isErr()).to.be.true; + }); + it("should parse StreamQL AND expression", () => { const query = 'stream.host.cont:"test.com" AND ws.direction.eq:"client"'; diff --git a/js/streamql/src/index.spec.ts b/js/streamql/src/index.spec.ts index 78e30bb..2693ef8 100644 --- a/js/streamql/src/index.spec.ts +++ b/js/streamql/src/index.spec.ts @@ -26,7 +26,7 @@ const readFile = (path: string) => { describe("streamql", () => { describe("ast", () => { - const cases = [1, 2, 3, 4]; + const cases = [1, 2, 3, 4, 5]; for (const c of cases) { it(`Case ${c}`, () => { const input = readFile(`ast/${c}/input.streamql`); diff --git a/js/streamql/src/primitives.ts b/js/streamql/src/primitives.ts index f94f913..a5fd978 100644 --- a/js/streamql/src/primitives.ts +++ b/js/streamql/src/primitives.ts @@ -14,6 +14,7 @@ export type ClauseWs = { raw?: Maybe; direction?: Maybe; format?: Maybe; + len?: Maybe; }; export type ClauseStream = { diff --git a/js/streamql/src/serialize.ts b/js/streamql/src/serialize.ts index 0282fd0..277b964 100644 --- a/js/streamql/src/serialize.ts +++ b/js/streamql/src/serialize.ts @@ -65,6 +65,9 @@ const serializeClauseWs = (value: ClauseWs): Result => { if (isPresent(value.format)) { return serializeExprString(value.format).map((str) => `format.${str}`); } + if (isPresent(value.len)) { + return serializeExprInt(value.len).map((str) => `len.${str}`); + } return err(new InvalidQuery()); }; diff --git a/rust/streamql/src/lib.rs b/rust/streamql/src/lib.rs index bdc707f..ef6b9bd 100644 --- a/rust/streamql/src/lib.rs +++ b/rust/streamql/src/lib.rs @@ -33,6 +33,7 @@ mod tests { #[case(2)] #[case(3)] #[case(4)] + #[case(5)] fn test_ast(#[case] case: u32) { let input = std::fs::read_to_string(format!("../../tests/streamql/ast/{case}/input.streamql")) diff --git a/tests/streamql/ast/5/input.streamql b/tests/streamql/ast/5/input.streamql new file mode 100644 index 0000000..014c4a4 --- /dev/null +++ b/tests/streamql/ast/5/input.streamql @@ -0,0 +1 @@ +ws.len.gte:1212 AND stream.port.lt:8080 \ No newline at end of file diff --git a/tests/streamql/ast/5/output.ast b/tests/streamql/ast/5/output.ast new file mode 100644 index 0000000..9892a31 --- /dev/null +++ b/tests/streamql/ast/5/output.ast @@ -0,0 +1 @@ +(ws.len.gte:1212 and stream.port.lt:8080) \ No newline at end of file