refining the query language + docs...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2018-12-10 21:37:14 +03:00
parent 76d0b50b0d
commit ec42cf7899

View File

@ -771,42 +771,95 @@ var TagsPrototype = {
// Query API... // Query API...
// //
// The language (JS): //
// The language (String):
// <query> ::= <tag> // <query> ::= <tag>
// | <call> // | <call>
// | <list> // | <list>
// <query> ::= <query> ..
// //
// <tag> ::= string // <tag> ::= string
// //
// <call> ::= [ <function-name> ] // <call> ::= (<function-name>)
// | [ <function-name>, <query>, .. ] // | (<function-name> <query> .. )
// //
// <list> ::= [] // <list> ::= ()
// | [ <query>, .. ] // | (<query> .. )
// //
// //
// Values resolution: //
//
// Execution model:
// Pre-processor stage
// Executes before the main stage and produces code to be run
// on the main stage.
// Main stage
// Executes after the pre-processor and produces a list of values.
//
//
//
// The language supports three types of "functions":
// Pre-processor function
// this is processed prior to query execution and generates valid
// code to be executed on the next stage
// Function (normal)
// gets a list of resolved arguments and resolves to a list
// Special form
// gets a list of unresolved arguments (as-is) and resolves to a list
//
//
//
// Value resolution:
// tag -> resolves to list of matching values as returned by .values(tag) // tag -> resolves to list of matching values as returned by .values(tag)
// list -> resolved to list of resolved items // list -> resolved to list of resolved items
// call -> resolves to list of values returned by the called function // call -> resolves to list of values returned by the called function
// //
// //
// Quoting:
// `a
// will resolve to a literal 'a'
// NOTE: this is not the same as (values a) because (values ..)
// returns a list while quoting will place the value as-is
//
//
//
// Pre-processor function:
// (search ..)
// resolves to an (or ..) call passed a list of tags matching
// the arguments.
//
// Shorthand:
// `a` -> (search a)
//
//
//
// Functions: // Functions:
// (and ..) // (and ..)
// resolves to the list of values present in each of the arguments // resolves to the list of values present in each of the arguments
//
// (or ..) // (or ..)
// resolves to the list of all the values of all the arguments // resolves to the list of all the values of all the arguments
//
// (not a ..) // (not a ..)
// resolves to list of values in a not present in any of the // resolves to list of values in a not present in any of the
// other arguments // other arguments
// //
// //
//
// Special forms: // Special forms:
// (values ..) // (values ..)
// resolves to the list of values as-is. // resolves to the list of values as-is.
// this makes it possible to pass in a set of values as-is // this makes it possible to pass in a set of values as-is
// without resolving them as tags. // without resolving them as tags.
// //
// Shorthand:
// (`a `b `c)
// -> (values a b c)
//
// NOTE: the braces in the shorthand, (values ..) always produces
// a list while quoting a value is always a single value.
//
//
// //
// Testing queries: // Testing queries:
// (values ..) adds the ability to test queries independently of // (values ..) adds the ability to test queries independently of
@ -821,7 +874,37 @@ var TagsPrototype = {
// -> ['b', 'c'] // -> ['b', 'c']
// //
// //
//
// The language AST:
// <query> ::= <tag>
// | <call>
// | <list>
// <query> ::= [ <query>, .. ]
//
// <tag> ::= string
//
// <call> ::= [ <function-name> ]
// | [ <function-name>, <query>, .. ]
//
// <list> ::= []
// | [ <query>, .. ]
//
//
// NOTE: the AST can be used as a query format as-is, this will
// avoid the parse stage...
//
//
// XXX do we need quoting???
// XXX not sure about the .flat(1) calls... // XXX not sure about the .flat(1) calls...
__query_ns_pre: {
search: function(...args){
return ['or',
...args
.map(function(t){
return this.search(t) }.bind(this))
.flat()
.unique() ] },
},
__query_ns: { __query_ns: {
and: function(...args){ and: function(...args){
// NOTE: we are sorting the lists here to start with the // NOTE: we are sorting the lists here to start with the
@ -830,10 +913,16 @@ var TagsPrototype = {
// up... // up...
args = args args = args
.sort(function(a, b){ return a.length - b.length }) .sort(function(a, b){ return a.length - b.length })
var t = args.pop()
t = t instanceof Array ? t : [t]
return [...args return [...args
.reduce(function(res, l){ .reduce(function(res, l){
return res.intersect(l.flat(1)) }, //return res.intersect(l.flat(1)) },
new Set(args.pop()))] }, return res
.intersect(l instanceof Array ?
l.flat(1)
: [l]) },
new Set(t))] },
or: function(...args){ or: function(...args){
return [...new Set(args.flat(1))] }, return [...new Set(args.flat(1))] },
not: function(...args){ not: function(...args){
@ -856,9 +945,26 @@ var TagsPrototype = {
// //
query: function(query, raw){ query: function(query, raw){
var that = this var that = this
var pre = this.__query_ns_pre
var ns = this.__query_ns var ns = this.__query_ns
var sns = this.__query_ns_special var sns = this.__query_ns_special
// Query Language pre-processor...
var PreQL = function(args){
return (
// function -> query args and call...
args[0] in pre ?
pre[args[0]].call(that, ...PreQL(args.slice(1)))
// list of tags -> query each arg...
: args
.map(function(arg){
return arg instanceof Array ?
PreQL(arg)
// search shorthand...
: arg.startsWith('`') && arg.endsWith('`') ?
PreQL(['search', arg.slice(1, -1)])
: arg }) ) }
// Query Language Executor... // Query Language Executor...
var QL = function(args){ var QL = function(args){
return ( return (
@ -873,11 +979,15 @@ var TagsPrototype = {
.map(function(arg){ .map(function(arg){
return arg instanceof Array ? return arg instanceof Array ?
QL(arg) QL(arg)
// quoting...
// XXX this should be a lexer directive (???)
: arg.startsWith('`') ?
arg.slice(1)
: that.values(arg) }) ) } : that.values(arg) }) ) }
return QL(query instanceof Array ? return QL(PreQL(query instanceof Array ?
query query
: this.parseQuery(query) ) : this.parseQuery(query) ))
.run(function(){ .run(function(){
return raw ? return raw ?
this this