grammar.pegjs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. {
  2. const {TokenConverter, TermOperator, FilterType, config} = options;
  3. const tc = new TokenConverter({text, location, config});
  4. const opDefault = TermOperator.Default;
  5. }
  6. search
  7. = space:spaces terms:term* {
  8. return [space, ...terms.flat()];
  9. }
  10. term
  11. = (boolean_operator / paren_group / filter / free_text) spaces
  12. boolean_operator
  13. = (or_operator / and_operator) {
  14. return tc.tokenLogicBoolean(text().toUpperCase());
  15. }
  16. paren_group
  17. = open_paren spaces:spaces inner:term+ closed_paren {
  18. return tc.tokenLogicGroup([spaces, ...inner].flat());
  19. }
  20. free_text
  21. = free_text_quoted / free_text_unquoted
  22. free_text_unquoted
  23. = (!filter !boolean_operator (free_parens / [^()\n ]+) spaces)+ {
  24. return tc.tokenFreeText(text(), false);
  25. }
  26. free_text_quoted
  27. = value:quoted_value {
  28. return tc.tokenFreeText(value.value, true);
  29. }
  30. free_parens
  31. = open_paren free_text? closed_paren
  32. // All key:value filter types
  33. filter
  34. = date_filter
  35. / specific_date_filter
  36. / rel_date_filter
  37. / duration_filter
  38. / size_filter
  39. / boolean_filter
  40. / numeric_in_filter
  41. / numeric_filter
  42. / aggregate_duration_filter
  43. / aggregate_size_filter
  44. / aggregate_numeric_filter
  45. / aggregate_percentage_filter
  46. / aggregate_date_filter
  47. / aggregate_rel_date_filter
  48. / has_filter
  49. / is_filter
  50. / text_in_filter
  51. / text_filter
  52. // filter for dates
  53. date_filter
  54. = key:search_key sep op:operator value:iso_8601_date_format &{
  55. return tc.predicateFilter(FilterType.Date, key, value, op)
  56. } {
  57. return tc.tokenFilter(FilterType.Date, key, value, op, false);
  58. }
  59. // filter for exact dates
  60. specific_date_filter
  61. = key:search_key sep value:iso_8601_date_format &{
  62. return tc.predicateFilter(FilterType.SpecificDate, key)
  63. } {
  64. return tc.tokenFilter(FilterType.SpecificDate, key, value, opDefault, false);
  65. }
  66. // filter for relative dates
  67. rel_date_filter
  68. = key:search_key sep value:rel_date_format &{
  69. return tc.predicateFilter(FilterType.RelativeDate, key)
  70. } {
  71. return tc.tokenFilter(FilterType.RelativeDate, key, value, opDefault, false);
  72. }
  73. // filter for durations
  74. duration_filter
  75. = negation:negation? key:search_key sep op:operator? value:duration_format &{
  76. return tc.predicateFilter(FilterType.Duration, key)
  77. } {
  78. return tc.tokenFilter(FilterType.Duration, key, value, op, !!negation);
  79. }
  80. // filter for file size
  81. size_filter
  82. = negation:negation? key:search_key sep op:operator? value:size_format &{
  83. return tc.predicateFilter(FilterType.Size, key)
  84. } {
  85. return tc.tokenFilter(FilterType.Size, key, value, op, !!negation);
  86. }
  87. // boolean comparison filter
  88. boolean_filter
  89. = negation:negation? key:search_key sep value:boolean_value &{
  90. return tc.predicateFilter(FilterType.Boolean, key)
  91. } {
  92. return tc.tokenFilter(FilterType.Boolean, key, value, opDefault, !!negation);
  93. }
  94. // numeric in filter
  95. numeric_in_filter
  96. = negation:negation? key:search_key sep value:numeric_in_list &{
  97. return tc.predicateFilter(FilterType.NumericIn, key)
  98. } {
  99. return tc.tokenFilter(FilterType.NumericIn, key, value, opDefault, !!negation);
  100. }
  101. // numeric comparison filter
  102. numeric_filter
  103. = negation:negation? key:search_key sep op:operator? value:numeric_value &{
  104. return tc.predicateFilter(FilterType.Numeric, key)
  105. } {
  106. return tc.tokenFilter(FilterType.Numeric, key, value, op, !!negation);
  107. }
  108. // aggregate duration filter
  109. aggregate_duration_filter
  110. = negation:negation? key:aggregate_key sep op:operator? value:duration_format &{
  111. return tc.predicateFilter(FilterType.AggregateDuration, key)
  112. } {
  113. return tc.tokenFilter(FilterType.AggregateDuration, key, value, op, !!negation);
  114. }
  115. // aggregate file size filter
  116. aggregate_size_filter
  117. = negation:negation? key:aggregate_key sep op:operator? value:size_format &{
  118. return tc.predicateFilter(FilterType.AggregateSize, key)
  119. } {
  120. return tc.tokenFilter(FilterType.AggregateSize, key, value, op, !!negation);
  121. }
  122. // aggregate percentage filter
  123. aggregate_percentage_filter
  124. = negation:negation? key:aggregate_key sep op:operator? value:percentage_format &{
  125. return tc.predicateFilter(FilterType.AggregatePercentage, key)
  126. } {
  127. return tc.tokenFilter(FilterType.AggregatePercentage, key, value, op, !!negation);
  128. }
  129. // aggregate numeric filter
  130. aggregate_numeric_filter
  131. = negation:negation? key:aggregate_key sep op:operator? value:numeric_value &{
  132. return tc.predicateFilter(FilterType.AggregateNumeric, key)
  133. } {
  134. return tc.tokenFilter(FilterType.AggregateNumeric, key, value, op, !!negation);
  135. }
  136. // aggregate date filter
  137. aggregate_date_filter
  138. = negation:negation? key:aggregate_key sep op:operator? value:iso_8601_date_format &{
  139. return tc.predicateFilter(FilterType.AggregateDate, key)
  140. } {
  141. return tc.tokenFilter(FilterType.AggregateDate, key, value, op, !!negation);
  142. }
  143. // filter for relative dates
  144. aggregate_rel_date_filter
  145. = negation:negation? key:aggregate_key sep op:operator? value:rel_date_format &{
  146. return tc.predicateFilter(FilterType.AggregateRelativeDate, key)
  147. } {
  148. return tc.tokenFilter(FilterType.AggregateRelativeDate, key, value, op, !!negation);
  149. }
  150. // has filter for not null type checks
  151. has_filter
  152. = negation:negation? &"has:" key:search_key sep value:(search_key / search_value) &{
  153. return tc.predicateFilter(FilterType.Has, key)
  154. } {
  155. return tc.tokenFilter(FilterType.Has, key, value, opDefault, !!negation);
  156. }
  157. // is filter. Specific to issue search
  158. is_filter
  159. = negation:negation? &"is:" key:search_key sep value:search_value &{
  160. return tc.predicateFilter(FilterType.Is, key)
  161. } {
  162. return tc.tokenFilter(FilterType.Is, key, value, opDefault, !!negation);
  163. }
  164. // in filter key:[val1, val2]
  165. text_in_filter
  166. = negation:negation? key:text_key sep value:text_in_list &{
  167. return tc.predicateFilter(FilterType.TextIn, key)
  168. } {
  169. return tc.tokenFilter(FilterType.TextIn, key, value, opDefault, !!negation);
  170. }
  171. // standard key:val filter
  172. //
  173. // The text_filter is a little special since it may not have an operator
  174. // depending on the configuration of the search parser, thus we have a
  175. // predicate for the operator.
  176. text_filter
  177. = negation:negation?
  178. key:text_key
  179. sep
  180. op:(operator &{ return tc.predicateTextOperator(key); })?
  181. value:search_value &{
  182. return tc.predicateFilter(FilterType.Text, key)
  183. } {
  184. return tc.tokenFilter(FilterType.Text, key, value, op ? op[0] : opDefault, !!negation);
  185. }
  186. // Filter keys
  187. key
  188. = value:[a-zA-Z0-9_.-]+ {
  189. return tc.tokenKeySimple(value.join(''), false);
  190. }
  191. quoted_key
  192. = '"' key:[a-zA-Z0-9_.:-]+ '"' {
  193. return tc.tokenKeySimple(key.join(''), true);
  194. }
  195. explicit_tag_key
  196. = prefix:"tags" open_bracket key:search_key closed_bracket {
  197. return tc.tokenKeyExplicitTag(prefix, key);
  198. }
  199. aggregate_key
  200. = name:key open_paren s1:spaces args:function_args? s2:spaces closed_paren {
  201. return tc.tokenKeyAggregate(name, args, s1, s2);
  202. }
  203. function_args
  204. = arg1:aggregate_param
  205. args:(spaces comma spaces !comma aggregate_param?)* {
  206. return tc.tokenKeyAggregateArgs(arg1, args);
  207. }
  208. aggregate_param
  209. = quoted_aggregate_param / raw_aggregate_param
  210. raw_aggregate_param
  211. = param:[^()\t\n, \"]+ {
  212. return tc.tokenKeyAggregateParam(param.join(''), false);
  213. }
  214. quoted_aggregate_param
  215. = '"' param:('\\"' / [^\t\n\"])* '"' {
  216. return tc.tokenKeyAggregateParam(`"${param.join('')}"`, true);
  217. }
  218. search_key
  219. = key / quoted_key
  220. text_key
  221. = explicit_tag_key / search_key
  222. // Filter values
  223. value
  224. = value:[^()\t\n ]* {
  225. return tc.tokenValueText(value.join(''), false);
  226. }
  227. quoted_value
  228. = '"' value:('\\"' / [^"])* '"' {
  229. return tc.tokenValueText(value.join(''), true);
  230. }
  231. in_value
  232. = (&in_value_termination in_value_char)+ {
  233. return tc.tokenValueText(text(), false);
  234. }
  235. text_in_value
  236. = quoted_value / in_value
  237. search_value
  238. = quoted_value / value
  239. numeric_value
  240. = value:("-"? numeric) unit:[kmb]? &(end_value / comma / closed_bracket) {
  241. return tc.tokenValueNumber(value.join(''), unit);
  242. }
  243. boolean_value
  244. = value:("true"i / "1" / "false"i / "0") &end_value {
  245. return tc.tokenValueBoolean(value);
  246. }
  247. text_in_list
  248. = open_bracket
  249. item1:text_in_value
  250. items:(spaces comma spaces !comma text_in_value?)*
  251. closed_bracket
  252. &end_value {
  253. return tc.tokenValueTextList(item1, items);
  254. }
  255. numeric_in_list
  256. = open_bracket
  257. item1:numeric_value
  258. items:(spaces comma spaces !comma numeric_value?)*
  259. closed_bracket
  260. &end_value {
  261. return tc.tokenValueNumberList(item1, items);
  262. }
  263. // See: https://stackoverflow.com/a/39617181/790169
  264. in_value_termination
  265. = in_value_char (!in_value_end in_value_char)* in_value_end
  266. in_value_char
  267. = [^(), ]
  268. in_value_end
  269. = closed_bracket / (spaces comma)
  270. // Format values
  271. // XXX: Since pegjs does not support regex there is no easy way to repeat
  272. // groups n times. So we have some dumb tokens here to handle that. We don't do
  273. // this in the backend grammar since we just use regex there.
  274. num2 = [0-9] [0-9]
  275. num4 = [0-9] [0-9] [0-9] [0-9]
  276. date_format = num4 "-" num2 "-" num2
  277. time_format = "T" num2 ":" num2 ":" num2 ("." ms_format)?
  278. ms_format = [0-9] [0-9]? [0-9]? [0-9]? [0-9]? [0-9]?
  279. tz_format = [+-] num2 ":" num2
  280. iso_8601_date_format
  281. = date_format time_format? ("Z" / tz_format)? &end_value {
  282. return tc.tokenValueIso8601Date(text());
  283. }
  284. rel_date_format
  285. = sign:[+-] value:[0-9]+ unit:[wdhm] &end_value {
  286. return tc.tokenValueRelativeDate(value.join(''), sign, unit);
  287. }
  288. duration_format
  289. = value:numeric
  290. unit:("ms"/"s"/"min"/"m"/"hr"/"h"/"day"/"d"/"wk"/"w")
  291. &end_value {
  292. return tc.tokenValueDuration(value, unit);
  293. }
  294. size_format
  295. = value:numeric
  296. unit:("bit"/"nb"/"bytes"/"kb"/"mb"/"gb"/"tb"/"pb"/"eb"/"zb"/"yb"/"kib"/"mib"/"gib"/"tib"/"pib"/"eib"/"zib"/"yib")
  297. &end_value {
  298. return tc.tokenValueSize(value, unit);
  299. }
  300. percentage_format
  301. = value:numeric "%" {
  302. return tc.tokenValuePercentage(value);
  303. }
  304. // NOTE: the order in which these operators are listed matters because for
  305. // example, if < comes before <= it will match that even if the operator is <=
  306. operator = ">=" / "<=" / ">" / "<" / "=" / "!="
  307. or_operator = "OR"i &end_value
  308. and_operator = "AND"i &end_value
  309. numeric = [0-9]+ ("." [0-9]*)? { return text(); }
  310. open_paren = "("
  311. closed_paren = ")"
  312. open_bracket = "["
  313. closed_bracket = "]"
  314. sep = ":"
  315. negation = "!"
  316. comma = ","
  317. spaces = " "* { return tc.tokenSpaces(text()) }
  318. end_value = [\t\n )] / !.