Changeset View
Changeset View
Standalone View
Standalone View
query_language/grammar.js
// Copyright (C) 2019-2021 The Software Heritage developers | // Copyright (C) 2019-2021 The Software Heritage developers | ||||
// See the AUTHORS file at the top-level directory of this distribution | // See the AUTHORS file at the top-level directory of this distribution | ||||
// License: GNU General Public License version 3, or any later version | // License: GNU General Public License version 3, or any later version | ||||
// See top-level LICENSE file for more information | // See top-level LICENSE file for more information | ||||
const PRECEDENCE = { | const PRECEDENCE = { | ||||
or: 2, | or: 2, | ||||
and: 3, | and: 3, | ||||
bracket: 4, | bracket: 4, | ||||
} | } | ||||
module.exports = grammar({ | module.exports = grammar({ | ||||
name: 'swh_search_ql', | name: 'swh_search_ql', | ||||
rules: { | rules: { | ||||
query: $ => seq($.filters, optional($.sortBy) ,optional($.limit)), | query: $ => seq( | ||||
$.filters, | |||||
optional($.and), | |||||
choice( | |||||
seq(optional($.sortBy), optional($.and), optional($.limit)), | |||||
seq(optional($.limit), optional($.and), optional($.sortBy)), | |||||
), | |||||
), | |||||
filters: $ => choice( | filters: $ => choice( | ||||
prec.left(PRECEDENCE.and, | prec.left(PRECEDENCE.and, | ||||
seq( | seq( | ||||
field('left', $.filters), | field('left', $.filters), | ||||
field('operator', $.and), | field('operator', $.and), | ||||
field('right', $.filters), | field('right', $.filters), | ||||
) | ) | ||||
Show All 10 Lines | rules: { | ||||
), | ), | ||||
$.filter | $.filter | ||||
), | ), | ||||
sortBy: $ => seq($.sortByField, $.sortByOp, $.sortByVal), | sortBy: $ => seq($.sortByField, $.sortByOp, $.sortByVal), | ||||
sortByField: $ => token('sort_by'), | sortByField: $ => token('sort_by'), | ||||
sortByOp: $ => $.equalOp, | sortByOp: $ => $.equalOp, | ||||
sortByVal: $ => createArray(optionalWrapWith($.sortByOptions, ["'", '"'])), | sortByVal: $ => createArray(optionalWrapWith($.sortByOptions, ["'", '"'])), | ||||
sortByOptions: $ => seq(optional(token.immediate('-')) ,choice( | sortByOptions: $ => seq(optional(token.immediate('-')), choice( | ||||
'visits', | 'visits', | ||||
'last_visit', | 'last_visit', | ||||
'last_eventful_visit', | 'last_eventful_visit', | ||||
'last_revision', | 'last_revision', | ||||
'last_release', | 'last_release', | ||||
'created', | 'created', | ||||
'modified', | 'modified', | ||||
'published' | 'published' | ||||
▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | rules: { | ||||
"tar" | "tar" | ||||
), // TODO: fetch this list dynamically from other swh services? | ), // TODO: fetch this list dynamically from other swh services? | ||||
sortBy: $ => seq($.sortByField, $.sortByOp, $.sortByVal), | sortBy: $ => seq($.sortByField, $.sortByOp, $.sortByVal), | ||||
sortByField: $ => token(choice('sort_by')), | sortByField: $ => token(choice('sort_by')), | ||||
sortByOp: $ => $.equalOp, | sortByOp: $ => $.equalOp, | ||||
sortByVal: $ => createArray(optionalWrapWith($.sortByOptions, ["'", '"'])), | sortByVal: $ => createArray(optionalWrapWith($.sortByOptions, ["'", '"'])), | ||||
sortByOptions: $ => seq( | sortByOptions: $ => seq( | ||||
optional(token.immediate('-')), | optional('-'), | ||||
choice( | choice( | ||||
'visits', | 'visits', | ||||
'last_visit', | 'last_visit', | ||||
'last_eventful_visit', | 'last_eventful_visit', | ||||
'last_revision', | 'last_revision', | ||||
'last_release', | 'last_release', | ||||
'created', | 'created', | ||||
'modified', | 'modified', | ||||
'published' | 'published' | ||||
)), | ) | ||||
), | |||||
unboundedListFilter: $ => seq($.listField, $.listOp, $.listVal), | unboundedListFilter: $ => seq($.listField, $.listOp, $.listVal), | ||||
listField: $ => token(choice('language', 'license', 'keyword')), | listField: $ => token(choice('language', 'license', 'keyword')), | ||||
listOp: $ => $.choiceOp, | listOp: $ => $.choiceOp, | ||||
listVal: $ => createArray($.string), | listVal: $ => createArray($.string), | ||||
dateFilter: $ => seq($.dateField, $.dateOp, $.dateVal), | dateFilter: $ => seq($.dateField, $.dateOp, $.dateVal), | ||||
Show All 11 Lines | rules: { | ||||
limit: $ => seq('limit', $.equalOp, $.number), | limit: $ => seq('limit', $.equalOp, $.number), | ||||
rangeOp: $ => token(choice('<', '<=', '=', '!=', '>=', '>')), | rangeOp: $ => token(choice('<', '<=', '=', '!=', '>=', '>')), | ||||
equalOp: $ => token('='), | equalOp: $ => token('='), | ||||
choiceOp: $ => token(choice('in', 'not in')), | choiceOp: $ => token(choice('in', 'not in')), | ||||
isoDateTime: $ => /\d{4}[-]\d{2}[-]\d{2}(\s|T)*(\d{2}:\d{2}(:\d{2})?)?(Z)?/, | isoDateTime: $ => { | ||||
const dateRegex = (/\d{4}[-]\d{2}[-]\d{2}/).source | |||||
const dateTimeSepRegex = (/(\s|T)*/).source | |||||
const timeRegex = (/(\d{2}:\d{2}(:\d{2}(\.\d{6})?)?)?/).source | |||||
const timezoneRegex = (/(\+\d{2}:\d{2}|Z)?/).source | |||||
return new RegExp(dateRegex + dateTimeSepRegex + timeRegex + timezoneRegex) | |||||
}, | |||||
string: $ => choice(wrapWith($.stringContent, ["'", '"']), $.singleWord), | string: $ => choice(wrapWith($.stringContent, ["'", '"']), $.singleWord), | ||||
number: $ => /\d+/, | number: $ => /\d+/, | ||||
booleanTrue: $ => "true", | booleanTrue: $ => "true", | ||||
booleanFalse: $ => "false", | booleanFalse: $ => "false", | ||||
or: $ => "or", | or: $ => "or", | ||||
and: $ => "and", | and: $ => "and", | ||||
stringContent: $ => repeat1(choice( | stringContent: $ => repeat1(choice( | ||||
token.immediate(/[^\\"\n]+/), | token.immediate(/[^\\'"\n]+/), | ||||
$.escape_sequence | $.escape_sequence | ||||
)), | )), | ||||
singleWord: $ => /[^\s"'\[\]\(\),]+/, | singleWord: $ => /[^\s"'\[\]\(\),]+/, | ||||
escape_sequence: $ => token.immediate(seq( | escape_sequence: $ => token.immediate(seq( | ||||
'\\', | '\\', | ||||
/(\"|\'|\\|\/|b|n|r|t|u)/ | /(\"|\'|\\|\/|b|n|r|t|u)/ | ||||
)), | )), | ||||
Show All 36 Lines |