const local = classer('builder', function(h_input) { let { node: k_node, } = h_input; // destruct nodes under target namespaces let { volt: k_node_volt, } = k_node.$; // create new state for this property let h_context = context(); // pretty print: indentation let s_indent_chr = '\t'; let s_indent = s_indent_chr.repeat(1); // let s_head = ''; let s_body = ''; let s_tail = ''; // each instruction is evaluated in series k_node_volt.instructions((k_instruction) => { // forward instruction to instruction builder duties.instruction(k_instruction, h_context, { head(s_add) { s_head += s_indent_chr+s_add+'\n'; }, body(s_add, n_indent_add=0) { // decrease indent before committing this string if(n_indent_add < 0) s_indent = s_indent.slice(0, s_indent_chr.length*n_indent_add); // commit string s_body += s_indent+s_add+'\n'; // increase indent after adding this string if(n_indent_add > 0) s_indent += s_indent_chr.repeat(n_indent_add); }, async(s_open, s_close='});') { s_body += s_indent+s_open+'\n'; s_tail = `${s_indent}${s_close}\n${s_tail}`; s_indent += s_indent_chr; }, }); }); // final async failure s_body += `${s_indent}return yield_(false, self);\n`; // let s_contents = s_head+s_body+s_tail; // let h_args = { util: evaluator({ $$: { // proxy spaz select call by choosing graphs too select(...a_args) { return $$.select(...a_args).from(['graph:source', 'graph:reference', 'graph:output']); }, }, }), }; // let a_arg_names = Object.keys(h_args); let a_arg_destructs = a_arg_names.map((s_arg_name) => { // ref arg struct let h_arg_struct = h_args[s_arg_name]; // build destruct assignment code return `let {${ Object.keys(h_arg_struct).join(', ') }} = ${s_arg_name};`; }); let s_whole = `(function(${a_arg_names.join(',')}) {\n` +`${a_arg_destructs.map(s => s_indent_chr+s).join('\n')}\n${s_contents}\n})`; local.info(s_whole); ({ code: s_whole } = require('babel-core').transform(s_whole, { presets: ['es2015'], })); let f_property = eval(s_whole); // return f_property; });
const local = classer('plugins', (h_plugin_modules, h_util) => { // let h_router = {}; // add all plugins from hash for(let p_namespace in h_plugin_modules) { h_router[p_namespace] = {}; } // return function(p_function, a_args, f_okay) { // prep namespace uri let p_namespace; // prep user config hash let h_user_config = {}; // parse function uri let [, p_prefix, s_querystring, s_function_name] = R_FUNCTION_URI.exec(p_function); // querystring-style uri if(s_function_name) { // no plugin exists in namespace if(!h_router.hasOwnProperty(p_prefix)) { debugger; throw 'no plugin is registered that matches namespace'; } // set namespace p_namespace = p_prefix; } // no querystring, search for namespace else { // set default querystring s_querystring = ''; // find which namespaces match let a_namespace_matches = Object.keys(h_plugin_modules).filter(p_ns => p_prefix.startsWith(p_ns)); // no matching namespace if(!a_namespace_matches.length) { debugger; throw 'no plugin is registered that matches namespace'; } // multiple namespaces else if(a_namespace_matches.length > 1) { debugger; throw 'multiple namespaces occupy same prefix'; } // single namespace else { // set namespace p_namespace = a_namespace_matches[0]; // extract function name s_function_name = p_function.substr(p_namespace.length); } } // prep plugin instance let k_plugin_instance; // ref plugin let h_plugin = h_router[p_namespace]; // plugin not yet reserved if(!h_plugin) { // claim the namespace h_plugin = h_router[p_namespace] = {}; } // user config instance does not yet exist if(!h_plugin.hasOwnProperty(s_querystring)) { // parse querystring using default options from qs module h_user_config =s_querystring.length? qs.parse(s_querystring): {}; // lazily create instance with user config k_plugin_instance = h_plugin[s_querystring] = h_plugin_modules[p_namespace](h_user_config); // plugin did not return anything useful let s_instance_type = typeof k_plugin_instance; if('object' !== s_instance_type && 'function' !== s_instance_type) { debugger; throw 'plugin did not produce a routable datatype'; } } // instance exists else { // ref instance k_plugin_instance = h_plugin[s_querystring]; } // function does not exist if(!k_plugin_instance.hasOwnProperty(s_function_name)) { debugger; throw 'function not exists in plugin'; } // function exists else { // apply function to input args k_plugin_instance[s_function_name](f_okay, ...a_args); } }; });
const local = classer('compiler', (h_config) => { // destruct config let { code: s_program, } = h_config; // prep program hash let h_program; // parse program try { h_program = parser.parse(s_program); } catch(e_parse) { return { error: e_parse, }; } // build model ttl let s_model = rapunzel({ builder: new model_builder(h_program), config: { closing_delimiter: ' .', }, }); return { model: s_model, }; });
const local = classer('router', (s_name, z_router, h_locals) => { // shuffle args if(!h_locals) { h_locals = z_router; z_router = undefined; } // return (...a_args) => { // prep route name & default return value let s_route; let w_return; // ref router type let s_router_type = typeof z_router; // router is string if('string' === s_router_type) { // split by decimal let a_properties = z_router.split(/\./g); // no need for recursion if(1 === a_properties.length) { // use that property name of 0th arg s_route = a_args[0][a_properties[0]]; } // recurse else { // start with 0th arg let w_node = a_args[0]; while(a_properties.length) { // shift property from each word w_node = w_node[a_properties.shift()]; } // end at terminus s_route = w_node; } } // router is function else if('function' === s_router_type) { // prep route callback; set route value let f_set_route = (_s_route) => s_route = _s_route; // find which route to use w_return = z_router.apply(h_locals, [f_set_route, ...a_args]); } // router is void else if('undefined' === s_router_type) { // stringify arg value s_route = a_args[0]+''; } // route was set if(s_route) { // ref handler let k_route_handler = h_locals[s_route]; // route does not exist if(!k_route_handler) { local.fail(`${s_name} cannot route "${s_route}"`); } // forward call to route handler return k_route_handler(...a_args); } // route not set; explicit return value else { return w_return; } }; }, { /** * public static: **/ // define how to route from an input to the local handlers group(s_group_name, w_router, h_handlers) { let h_output = {}; // each handler for(let s_handler_name in h_handlers) { let h_handler = h_handlers[s_handler_name]; // create handler interface h_output[s_handler_name] = local(`${s_group_name}.${s_handler_name}`, w_router, h_handler); } // render callee as output return h_output; }, });
const local = classer('QueryPatterns', function(h_init) { /** * private: **/ let h_prefixes = h_init.prefixes; let a_where = h_init.where; // let h_struct = rmprop({}); let h_graph = {}; let as_top_nodes = new Set(); // each triple a_where.forEach((h_pattern) => { // switch(h_pattern.type) { // basic graph pattern case 'bgp': // each triple in basic graph pattern h_pattern.triples.forEach((h_triple) => { // ref subject let s_subject = h_triple.subject; // first triple using this subject if(!h_graph[s_subject]) { // create list to store triples sharing this subject h_graph[s_subject] = []; } // group triple into buckets by same subject h_graph[s_subject].push(h_triple); // only add non-blanknodes to top-level if(!s_subject.startsWith('_:')) { as_top_nodes.add(s_subject); } }); break; // subquery case 'query': // break; } }); // ------------------------------ // const groups = function*(h_group, a_types, s_graph='') { let s_latent_graph = ''; switch(h_group.type) { case 'graph': s_latent_graph = h_group.graph; case 'group': case 'union': case 'exists': case 'notexists': for(let i_group in h_group.patterns) { yield *groups(h_group.patterns[i_group], a_types, s_latent_graph); } case 'bgp': case 'query': case 'values': if(!a_types || -1 !== a_types.indexOf(h_group.type)) { yield [s_graph, h_group]; } break; default: local.fail('group not yet caught: '+h_group.type); break; } }; // const depth_first = function*(h_container) { // each triple in this node for(let h_triple of h_container) { // destructure triple let {subject: s_subject, predicate: s_predicate, object: s_object} = h_triple; // triple points to a blanknode if(s_object.startsWith('_:')) { // yield depth first triples down that path yield *depth_first(h_graph[s_object]); } // then yield this container triple yield [h_triple, s_subject, s_predicate, s_object]; } }; // const closure = function(s_target, h_context) { // ref visits let as_visits = h_context.visits; // traverse in a depth-first manner for(let [h_triple, s_subject, s_object, s_predicate] of operator.depth_first()) { // triple has been visited already if(as_visits.has(h_triple)) continue; // local.warn('testing for <'+s_target+'> in: '+arginfo(h_triple)); // otherwise; mark this triple as visited as_visits.add(h_triple); // prep to remember this triple had a match let b_matched = false; // subject matches target if(s_subject === s_target) { b_matched = true; // close predicate closure(s_predicate, h_context); // close object closure(s_subject, h_context); } // predicate matches target else if(s_predicate === s_target) { b_matched = true; // close subject closure(s_subject, h_context); // close object closure(s_subject, h_context); } // object matches target else if(s_object === s_target) { b_matched = true; // close subject closure(s_subject, h_context); // close predicate closure(s_predicate, h_context); } // push triple to list if(b_matched) { local.good('+match'); h_context.triples.push(h_triple); } } local.info('final triples of closure, '+arginfo(h_context.triples)); // return triples list return h_context.triples; }; // const bgp_closure = function(h_bgp, as_targets) { // let as_triples = new Set(); // h_bgp.triples.forEach((h_triple) => { // if(as_targets.has(h_triple.subject)) { as_triples.add(h_triple); } if(as_targets.has(h_triple.predicate)) { as_triples.add(h_triple); } if(as_targets.has(h_triple.object)) { as_triples.add(h_triple); } }); // return { type: 'bgp', triples: Array.from(as_triples), }; }; // const node_tree = function(s_node_id, h_forest) { // find this tree let h_tree = h_forest[s_node_id]; // no tree! if(!h_tree) return s_node_id; // each branch in tree for(let s_predicate in h_tree) { // transform each leaf by pointing it to another tree h_tree[s_predicate] = h_tree[s_predicate].map((s_object) => { // only recurse on blanknodes; TODO: what about variables? if(s_object.startsWith('_:')) { return node_tree(s_object, h_forest); } else { return s_object; } }); } // return tree; return h_tree; }; // let operator = { // groups in a depth-first manner groups: function*(a_types) { // each top-level group for(let i_group in a_where) { let h_group = a_where[i_group]; // yield *groups(h_group, a_types); } }, // subqueries subqueries: function*() { // yield groups of subquery type yield *operator.groups(['query']); }, // depth-first iteration depth_first: function*() { // each top level node for(let s_node_id of as_top_nodes) { // yield depth first triples yield *depth_first(h_graph[s_node_id]); } }, // // depth-last iteration // depth_last: function*() { // // each top level node // for(let s_node_id of as_top_nodes) { // // yield depth first triples // yield *depth_last(h_graph[s_node_id]); // } // }, // top-level iteration top_level: function*() { // each top level node for(let s_node_id of as_top_nodes) { // each triple pointed to by this subject for(let h_triple of h_graph[s_node_id]) { // destructure triple let {subject: s_subject, predicate: s_predicate, object: s_object} = h_triple; // yield top-level triples yield [h_triple, s_subject, s_predicate, s_object]; } } }, // obtains all triples that constrain any of the subject, predicate or object in the given triple constrained_by: function(h_triple) { // create list of targets to constrain from triple let as_targets = new Set([h_triple.subject, h_triple.predicate, h_triple.object]); // prep to store all patterns necessary for constraint let a_patterns = []; // each group in where block for(let [s_graph, h_group] of operator.groups()) { // graphs not yet supported if(s_graph) { local.fail('cannot constrain groups within GRAPH blocks'); } // what is the group type? switch(h_group.type) { // basic graph pattern case 'bgp': // do bgp closure keeping only triples that constrain targets a_patterns.push( bgp_closure(h_group, as_targets) ); break; // values block case 'values': // ref variables let a_vars = Object.keys(h_group.values[0]); debugger; // at least one of the variables is in targets if(a_vars.some((s_variable) => as_targets.has(s_variable))) { // keep entire values block a_patterns.push(h_group); // constrain all other variables if(a_vars.length > 1) { local.fail('cannot constrain multiple variable in VALUES block'); } } break; // something else default: local.fail('cannot constrain '+h_group.type.toUpperCase()+' block'); break; } debugger; } // return a_patterns; }, // filter only triples that are closed by the given thing closure: function(s_thing) { // return closure(s_thing, { visits: new Set(), triples: [], }); }, // create a tree to describe all properties of a subject tree: function(s_node_id) { // get statement forest let h_forest = operator.bgp_forest(); // build node tree return node_tree(s_node_id, h_forest); }, // create a tree of all statements in a bgp bgp_forest: function() { // prep hash for each node by id let h_forest = {}; // for(let s_subject in h_graph) { // ref triples list let a_triples = h_graph[s_subject]; // each triple a_triples.forEach((h_triple) => { // ref node root let h_root = h_forest[s_subject]; // node root does not yet exist if(!h_root) { // create node root h_root = h_forest[s_subject] = {}; } // ref predicate let s_predicate = h_triple.predicate; // ref node branch let h_branch = h_root[s_predicate]; // node branch does not yet exit if(!h_branch) { // create node branch h_branch = h_root[s_predicate] = []; } // push node leaf to node branch h_branch.push(h_triple.object); }); } // return forest return h_forest; }, }; // return operator; }, { /** * public static: **/ });
const local = classer('engine', (h_config={}) => { // destruct config arg let { endpoint: s_endpoint, plugins: h_plugins, } = h_config; // open connection to SPARQL endpoint let $$ = spaz({ engine: { endpoint: s_endpoint, http_methods: 'post', }, prefixes: Object.assign({}, H_DEFAULT_PREFIXES, h_config.prefixes || {}, H_REQUIRED_PREFIXES), }); // create plugin router let k_plugin_router = plugins(h_config.plugins || {}, $$); /** * operator: **/ return classer.operator((s_sparql, f_okay) => { // new query id let s_query_id = i_query.toString(N_ID_RADIX); // parse sparql query let q_input; try { q_input = $$(s_sparql); } catch(e) { return f_okay({ error: e+'', }); } // extract graph patterns as a structured graph let k_patterns = q_input.patterns(); // create graphs for this query instance; copy defaults let h_graphs = {}; for(let s_key in H_GRAPH_GROUPS) { h_graphs[s_key] = H_GRAPH_GROUPS[s_key].slice(0); } // set unique graphs for this query A_UNIQUE_GRAPHS.forEach((s_graph_type) => { h_graphs[s_graph_type] = ['graph:'+s_graph_type+'_'+s_query_id]; }); // prep transfer hash let h_transfer = { $$, graphs: h_graphs, patterns: k_patterns, plugin_router: k_plugin_router, }; // step through the query phases in series async.series([ // relations (f_next) => { relations(h_transfer, () => { local.good('all done with relations'); f_next(); }); }, ], () => { // all done evaluating phases of query local.good('=== all done evaluating phases of input query ==='); // set the default `from` graphs q_input // content graphs .from(h_graphs.content) // input graph .from(h_graphs.input); // ref query's `order by` conditions let a_order = q_input.order(); // no order clause! if(!a_order.length) { // execute input query and return results to callback return q_input.exec(f_okay); } // at least one order clause else { throw 'no order by clause handler yet'; debugger; // // examine each order clause // a_order.forEach((h_order) => { // // clause contains function call // if(h_order.expression && 'functionCall' === h_order.expression.type) { // // set resolve to results filter function // h_transfer.resolve = f_filter_results; // // set expression // h_transfer.expression = h_order.expression; // // set extra args for function call // h_transfer.extra_args = a_extra_args; // // evaluate the function call // solve_query_function_call(h_transfer); // } // }); } }); }, { }); }, { /** * public static: **/ // lazily load compiler compile(...a_args) { return require('../compiler/compiler.js').default(...a_args); }, });