Skip to content

Commit

Permalink
WIP: Array of Tables
Browse files Browse the repository at this point in the history
  • Loading branch information
Brandon Tilley committed Feb 23, 2014
1 parent a065537 commit 6b4dd00
Show file tree
Hide file tree
Showing 6 changed files with 310 additions and 126 deletions.
188 changes: 98 additions & 90 deletions lib/compiler.js
Original file line number Diff line number Diff line change
@@ -1,99 +1,113 @@
function compile(nodes) {
var data = {};
var currentPath = null;
var topLevelKeys = {};
var tableKeys = {};

return expand(nodes);

// Collect array of nodes into `topLevelKeys` and `tableKeys` data
function collect(nodes) {
for(i in nodes) {
var node = nodes[i];
switch(node.type) {
case 'Set':
var value = node.value.value;
if (node.value.type == 'Array') value = processArray(value);
collectValue(node.line, node.column, node.key, value);
break;
case 'Table':
collectTable(node.line, node.column, node.value);
break;
var assignedPaths = [];
var currentPath = '';
var data = {};
var context = data;
var arrayMode = false;

return reduce(nodes);

function reduce(nodes) {
var node;
for (i in nodes) {
node = nodes[i];
switch (node.type) {
case 'Assign':
assign(node);
break;
case 'ObjectPath':
setPath(node);
break;
case 'ArrayPath':
addTableArray(node);
break;
}
}
};

function collectValue(line, col, key, value) {
var obj = currentPath ? tableKeys[currentPath] : topLevelKeys;
var existing = obj[key];
if (existing !== undefined) {
var path = (currentPath ? currentPath + '.' + key : key);
genError(line, col, "Cannot replace existing key " + path + ".");
} else {
obj[key] = value;
}
};

function collectTable(line, col, table) {
var existing = tableKeys[table];
checkPrefixPath(line, col, table);
if (existing !== undefined) {
genError(line, col, "Cannot replace existing table " + table + ".");
} else {
tableKeys[table] = {};
currentPath = table;

return data;
}

function genError(err, line, col) {
var ex = new Error(err);
ex.line = line;
ex.column = col;
throw ex;
}

function assign(node) {
var key = node.key;
var value = node.value;
var line = node.line;
var column = node.column;

var fullPath = currentPath + '.' + key;
if (typeof context[key] !== 'undefined') {
genError("Cannot redefine existing key '" + fullPath + "'.", line, column);
}
};

function checkPrefixPath(line, col, table) {
var parts = table.split('.');
var lastPart = parts.pop();
var prefix = parts.join('.');

var existing = tableKeys[prefix];
if (existing !== undefined) {
var subValue = existing[lastPart];
if (subValue !== undefined)
genError(line, col, "Cannot replace existing key " + table + " with table.");

if (value.type == 'Array')
context[key] = reduceArrayWithTypeChecking(value.value)
else
context[key] = value.value;

if (assignedPaths.indexOf(fullPath) === -1) assignedPaths.push(fullPath);
}

function setPath(node) {
var path = node.value;
var line = node.line;
var column = node.column;

if (assignedPaths.indexOf(path) > -1) {
genError("Cannot redefine existing table '" + path + "'.", line, column);
}
};
assignedPaths.push(path);
createContext(data, path, {}, true);
}

// Expand collected values in `topLevelKeys` and `tableKeys` into `data`
function expand(nodes) {
collect(nodes);
function addTableArray(node) {
var path = node.value;
var line = node.line;
var column = node.column;

var keys = Object.keys(topLevelKeys);
for (i in keys) {
var key = keys[i];
data[key] = topLevelKeys[key];
if (assignedPaths.indexOf(path) === -1) assignedPaths.push(path);
createContext(data, path, [], true);

if (context instanceof Array) {
var newObj = {};
context.push(newObj);
context = newObj;
}
}

// Given a path 'a.b.c', create (as necessary) `start.a`,
// `start.a.b`, and `start.a.b.c`, assigning `value` to `start.a.b.c`
// If `setPath` is true, sets `context` to `value`.
function createContext(start, path, value, setContext) {
var key;
var keys = path.split('.');
var ctx = start;

var keys = Object.keys(tableKeys);
for (i in keys) {
var key = keys[i];
var obj = tableKeys[key];
var subkeys = Object.keys(obj);
for (j in subkeys) {
var subkey = subkeys[j];
deepValue(key)[subkey] = obj[subkey];
};
key = keys[i];
if (typeof ctx[key] === 'undefined') {
if (i == keys.length - 1) {
ctx[key] = value;
} else {
ctx[key] = {};
}
}

ctx = ctx[key];
}

return data;
};

function deepValue(path) {
var obj = data;
var parts = path.split('.');
for(i in parts) {
var part = parts[i];
obj[part] = obj[part] || {};
obj = obj[part];
if (setContext) {
context = ctx;
currentPath = path;
}
return obj;
}

function processArray(array) {
function reduceArrayWithTypeChecking(array) {
// Ensure that all items in the array are of the same type
var firstType = null;
for(i in array) {
Expand All @@ -102,27 +116,21 @@ function compile(nodes) {
firstType = node.type;
} else {
if (node.type != firstType) {
genError(node.line, node.column, "Expected type " + firstType + " but " + node.type + " found.");
genError("Cannot add value of type " + node.type + " to array of type " +
node.type + ".", node.line, node.column);
}
}
}

// Recursively reduce array of nodes into array of the nodes' values
return array.map(function(elem) {
if (elem.type == 'Array') {
return processArray(elem.value);
return reduceArrayWithTypeChecking(elem.value);
} else {
return elem.value;
}
});
}

function genError(line, col, err) {
var ex = new Error(err);
ex.line = line;
ex.column = col;
throw ex;
}
};

module.exports = {
Expand Down
Loading

0 comments on commit 6b4dd00

Please sign in to comment.