grammar.pegjs 9.7 KB

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