// This function takes a 2d array and converts it to JSON
// where the JSON is a table of objects. The first row of the
// array determines the property names.
function TableToJson(table) {
let returnArray = [];
let props = table.splice(0,1).flat(); // Remove first row from the table and convert to 1d array.
for(let row of table) {
let rowObj = {};
for(let c in row) {
let colName = props[c]; // select property name from the removed row
let colData = row[c]; // select the matching data from the current row
rowObj[colName] = colData;
}
returnArray.push(rowObj); // Add row to the final array
}
return JSON.stringify(returnArray);
}
// This function takes a JSON string and optional
// array of property names and returns a 2d array.
// Json should be structured as an array of objects.
// All objects should have the same properties.
function JsonToTable(json, header = []) {
let returnArray = [];
json = JSON.parse(json);
// Unless specific properties were provided, get all available properties from the first object.
if(header.length == 0) {
for(let prop in json[0]) {
header.push(prop);
}
}
// Go through every object, set the selected data as an array, push the row to the 2d array
for(let obj of json) {
let newRow = [];
for(let colName of header) {
let data = obj[colName] || ""; // If the property doesn't exist, place an empty string.
newRow.push(data);
}
returnArray.push(newRow);
}
// Add the header row to the front of the array so it appears at the top of the table
returnArray.unshift(header);
return returnArray;
}
// I took inspiration for this function from Lua.
// Not only is it easier than typing console.log all the time,
// but it also can take multiple arguments and print them all
// at once, wrapped in an array.
function print(input) {
if(arguments.length <= 1) {console.log(input); return;}
var args = [];
for(a in arguments) {
args.push(arguments[a]);
}
console.log(args);
}
// This function is from gSQL.
// It takes two strings, one as a Column ID and one as a table name.
// It detects if the column id is in the format "Col" followed by a number,
// "Col" followed by a letter, or if it is some other identifier.
// It then converts the letter or identifier to a number, which is returned.
function GetColIndex_(colId, tableName) {
if(colId.match(/^Col\d+$/)) { // if Col1
colId = colId.replace(/^Col/, "") - 1;
}
else if(colId.match(/^Col[A-Z]+$/)) { // if ColA
colId = ColLetterToNumber_(colId.replace(/^Col/, "")) - 1;
}
else { // if reference by column header
colId = CLUSTER.getActiveDatabase().getTable(tableName).getHeaderPos(colId); // convert index to number.
}
return colId;
}
This function is from gSQL.
It is not something
that I designed, but I included it here to show how I documented most
of the gSQL functions.
This interface is (I believe), one of the design patterns from the book Design Patterns, although that is not where I found it. It allows a structure that is built in an OOP language to be used in a functional style (useful for this application).
//-----------------------------------------------------------------------------------------------------
// Generic Visit Handler
//-----------------------------------------------------------------------------------------------------
/*
* This function enables a Visitor to pass itself to a Node, which passes itself
* back into that Visitor's method for handling that Node type.
* This allows any Visitor to operate on any Node with only one call ( someNode.visit(this) ).
*
* Each Node should have a 'this.visit = vInterface_(this, "");' property.
* Any Visitor should have a 'visit' method
*
* @param {Node} instance A Node object of some type.
* @param {string} name The name of the Node, identical to the function name.
* @return {function} A closure that holds the node instance and name of the Visitor method to call ( visit ).
*/
function vInterface_(instance, name) { // This might not even be the most trippy thing about an AST.
return (v => {/*print(`visit${name}`);*/ v[`visit${name}`](instance)});
}
This function is from gSQL.
It is one of the nodes used for the AST built by the parser.
//-----------------------------------------------------------------------------------------------------
/*
* Node class that represents a select statement.
* Its children are the from clause and the where clause, and it contains a list of Columns.
*
* @param {array} columns An array of ColumnNodes.
* @param {Node} from A From node.
* @param {Node} where A WhereNode.
* @return {SelectNode} This.
*/
function SelectNode_(columns, from, where) {
this.value = SELECT;
this.columns = columns;
this.from = from;
this.where = where;
this.visit = vInterface_(this, "SelectNode");
return this;
}
//-----------------------------------------------------------------------------------------------------
This function is from gSQL.
It is one of the Parser's functions, and helps to build an Abstract Syntax Tree (AST) out of the available node classes.
This method of calling other parser functions is called recursive descent and automatically sorts abstract syntax into an order of operations that you define.
//-----------------------------------------------------------------------------------------------------
Parser_.prototype.selectStatement = function() { // ::= SELECT columns() fromStmt() whereStmt()
var columns, from, where;
this.consume(SELECT);
columns = this.columns();
from = this.fromStatement();
where = this.whereStatement();
return new SelectNode_(columns, from, where);
}
//-----------------------------------------------------------------------------------------------------