+3
-2
@@ -6,5 +6,6 @@ var parser = require('./lib/parser'); | ||
| parse: function(input) { | ||
| var nodes = parser.parse(input.toString()); | ||
| return compiler.compile(nodes); | ||
| var str = input.toString(); | ||
| var nodes = parser.parse(str); | ||
| return compiler.compile(nodes, str); | ||
| } | ||
| }; |
+56
-77
| "use strict"; | ||
| function compile(nodes) { | ||
| var assignedPaths = []; | ||
| var valueAssignments = []; | ||
| var explicitTablePaths = []; | ||
| var tableArrayPaths = []; | ||
| function compile(nodes, inputText) { | ||
| var assignedPaths = new Set(); | ||
| var valueAssignments = new Set(); | ||
| var explicitTablePaths = new Set(); | ||
| var currentPath = ""; | ||
| var data = Object.create(null); | ||
| var context = data; | ||
| var arrayMode = false; | ||
@@ -34,6 +32,16 @@ return reduce(nodes); | ||
| function genError(err, line, col) { | ||
| function resolveLineCol(off) { | ||
| var line = 1, col = 1; | ||
| for (var i = 0; i < off; i++) { | ||
| if (inputText.charCodeAt(i) === 10) { line++; col = 1; } | ||
| else { col++; } | ||
| } | ||
| return { line: line, column: col }; | ||
| } | ||
| function genError(err, off) { | ||
| var pos = resolveLineCol(off); | ||
| var ex = new Error(err); | ||
| ex.line = line; | ||
| ex.column = col; | ||
| ex.line = pos.line; | ||
| ex.column = pos.column; | ||
| throw ex; | ||
@@ -45,6 +53,4 @@ } | ||
| var value = node.value; | ||
| var line = node.line; | ||
| var column = node.column; | ||
| var off = node.offset; | ||
| // Support both legacy single-string keys and new array-of-keys format | ||
| if (!Array.isArray(keys)) keys = [keys]; | ||
@@ -54,3 +60,2 @@ | ||
| // Navigate to the right context for dotted keys, creating intermediate tables | ||
| var target = context; | ||
@@ -63,11 +68,9 @@ for (var i = 0; i < keys.length - 1; i++) { | ||
| target[k] = Object.create(null); | ||
| if (!pathAssigned(intermediatePath)) { | ||
| assignedPaths.push(intermediatePath); | ||
| } | ||
| assignedPaths.add(intermediatePath); | ||
| } else if (typeof target[k] !== "object" || target[k] === null || Array.isArray(target[k])) { | ||
| genError("Cannot redefine existing key '" + intermediatePath + "'.", line, column); | ||
| } else if (valueAssignments.indexOf(intermediatePath) > -1) { | ||
| genError("Cannot redefine existing key '" + intermediatePath + "'.", line, column); | ||
| } else if (explicitTablePaths.indexOf(intermediatePath) > -1 && intermediatePath !== (Array.isArray(currentPath) ? currentPath.join(".") : currentPath)) { | ||
| genError("Cannot use dotted keys to extend table '" + intermediatePath + "' defined elsewhere.", line, column); | ||
| genError("Cannot redefine existing key '" + intermediatePath + "'.", off); | ||
| } else if (valueAssignments.has(intermediatePath)) { | ||
| genError("Cannot redefine existing key '" + intermediatePath + "'.", off); | ||
| } else if (explicitTablePaths.has(intermediatePath) && intermediatePath !== (Array.isArray(currentPath) ? currentPath.join(".") : currentPath)) { | ||
| genError("Cannot use dotted keys to extend table '" + intermediatePath + "' defined elsewhere.", off); | ||
| } | ||
@@ -81,3 +84,3 @@ target = target[k]; | ||
| if (typeof target[lastKey] !== "undefined") { | ||
| genError("Cannot redefine existing key '" + fullPath + "'.", line, column); | ||
| genError("Cannot redefine existing key '" + fullPath + "'.", off); | ||
| } | ||
@@ -87,13 +90,7 @@ | ||
| if (!pathAssigned(fullPath)) { | ||
| assignedPaths.push(fullPath); | ||
| valueAssignments.push(fullPath); | ||
| } | ||
| assignedPaths.add(fullPath); | ||
| valueAssignments.add(fullPath); | ||
| } | ||
| function pathAssigned(path) { | ||
| return assignedPaths.indexOf(path) !== -1; | ||
| } | ||
| function reduceValueNode(node) { | ||
@@ -111,6 +108,3 @@ if (node.type === "Array") { | ||
| var obj = Object.create(null); | ||
| // Track paths that were explicitly defined (either as values or as inline | ||
| // table results). Dotted keys can create implicit intermediate tables but | ||
| // cannot modify explicitly defined ones. | ||
| var definedKeys = []; | ||
| var definedKeys = new Set(); | ||
@@ -125,6 +119,5 @@ for (var i = 0; i < values.length; i++) { | ||
| var reduced = reduceValueNode(val.value); | ||
| setNestedKey(obj, keys, reduced, val.line, val.column, definedKeys); | ||
| setNestedKey(obj, keys, reduced, val.offset, definedKeys); | ||
| // Track the full path as a defined key | ||
| definedKeys.push(keys.join(".")); | ||
| definedKeys.add(keys.join(".")); | ||
| } | ||
@@ -135,3 +128,3 @@ | ||
| function setNestedKey(obj, keys, value, line, column, definedKeys) { | ||
| function setNestedKey(obj, keys, value, off, definedKeys) { | ||
| for (var i = 0; i < keys.length - 1; i++) { | ||
@@ -143,6 +136,5 @@ var k = keys[i]; | ||
| } else if (typeof obj[k] !== "object" || obj[k] === null || Array.isArray(obj[k])) { | ||
| genError("Cannot redefine existing key '" + intermediatePath + "'.", line, column); | ||
| } else if (definedKeys && definedKeys.indexOf(intermediatePath) > -1) { | ||
| // Cannot extend an explicitly-defined inline table | ||
| genError("Cannot extend inline table '" + intermediatePath + "'.", line, column); | ||
| genError("Cannot redefine existing key '" + intermediatePath + "'.", off); | ||
| } else if (definedKeys && definedKeys.has(intermediatePath)) { | ||
| genError("Cannot extend inline table '" + intermediatePath + "'.", off); | ||
| } | ||
@@ -153,3 +145,3 @@ obj = obj[k]; | ||
| if (typeof obj[lastKey] !== "undefined") { | ||
| genError("Cannot redefine existing key '" + keys.join(".") + "'.", line, column); | ||
| genError("Cannot redefine existing key '" + keys.join(".") + "'.", off); | ||
| } | ||
@@ -162,11 +154,10 @@ obj[lastKey] = value; | ||
| var quotedPath = path.map(quoteDottedString).join("."); | ||
| var line = node.line; | ||
| var column = node.column; | ||
| var off = node.offset; | ||
| if (pathAssigned(quotedPath)) { | ||
| genError("Cannot redefine existing key '" + path + "'.", line, column); | ||
| if (assignedPaths.has(quotedPath)) { | ||
| genError("Cannot redefine existing key '" + path + "'.", off); | ||
| } | ||
| assignedPaths.push(quotedPath); | ||
| explicitTablePaths.push(quotedPath); | ||
| context = deepRef(data, path, Object.create(null), line, column); | ||
| assignedPaths.add(quotedPath); | ||
| explicitTablePaths.add(quotedPath); | ||
| context = deepRef(data, path, Object.create(null), off); | ||
| currentPath = path; | ||
@@ -178,21 +169,17 @@ } | ||
| var quotedPath = path.map(quoteDottedString).join("."); | ||
| var line = node.line; | ||
| var column = node.column; | ||
| var off = node.offset; | ||
| // Check before filtering: cannot append to a statically-defined array | ||
| if (valueAssignments.indexOf(quotedPath) > -1) { | ||
| genError("Cannot append to statically defined array '" + quotedPath + "'.", line, column); | ||
| if (valueAssignments.has(quotedPath)) { | ||
| genError("Cannot append to statically defined array '" + quotedPath + "'.", off); | ||
| } | ||
| if (!pathAssigned(quotedPath)) { | ||
| assignedPaths.push(quotedPath); | ||
| } | ||
| assignedPaths = assignedPaths.filter(function(p) { | ||
| return p.indexOf(quotedPath) !== 0; | ||
| // Clear paths that start with this table array path | ||
| assignedPaths.forEach(function(p) { | ||
| if (p.indexOf(quotedPath) === 0) assignedPaths.delete(p); | ||
| }); | ||
| valueAssignments = valueAssignments.filter(function(p) { | ||
| return p.indexOf(quotedPath) !== 0; | ||
| valueAssignments.forEach(function(p) { | ||
| if (p.indexOf(quotedPath) === 0) valueAssignments.delete(p); | ||
| }); | ||
| assignedPaths.push(quotedPath); | ||
| context = deepRef(data, path, [], line, column); | ||
| assignedPaths.add(quotedPath); | ||
| context = deepRef(data, path, [], off); | ||
| currentPath = quotedPath; | ||
@@ -205,14 +192,8 @@ | ||
| } else { | ||
| genError("Cannot redefine existing key '" + path + "'.", line, column); | ||
| genError("Cannot redefine existing key '" + path + "'.", off); | ||
| } | ||
| } | ||
| // 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 `a` or `b` are arrays and have items in them, the last item in the | ||
| // array is used as the context for the next sub-path. | ||
| function deepRef(start, keys, value, line, column) { | ||
| var traversed = []; | ||
| function deepRef(start, keys, value, off) { | ||
| var traversedPath = ""; | ||
| var path = keys.join("."); | ||
| var ctx = start; | ||
@@ -222,4 +203,3 @@ | ||
| var key = keys[i]; | ||
| traversed.push(key); | ||
| traversedPath = traversed.join("."); | ||
| traversedPath = traversedPath ? traversedPath + "." + key : key; | ||
| if (typeof ctx[key] === "undefined") { | ||
@@ -231,5 +211,4 @@ if (i === keys.length - 1) { | ||
| } | ||
| } else if (i !== keys.length - 1 && valueAssignments.indexOf(traversedPath) > -1) { | ||
| // already a non-object value at key, can't be used as part of a new path | ||
| genError("Cannot redefine existing key '" + traversedPath + "'.", line, column); | ||
| } else if (i !== keys.length - 1 && valueAssignments.has(traversedPath)) { | ||
| genError("Cannot redefine existing key '" + traversedPath + "'.", off); | ||
| } | ||
@@ -236,0 +215,0 @@ |
+2
-2
| { | ||
| "name": "toml", | ||
| "version": "4.1.0", | ||
| "version": "4.1.1", | ||
| "description": "TOML parser for Node.js (TOML v1.1.0 compliant)", | ||
@@ -16,3 +16,3 @@ "main": "index.js", | ||
| "scripts": { | ||
| "build": "peggy --cache -o lib/parser.js src/toml.pegjs", | ||
| "build": "peggy -o lib/parser.js src/toml.pegjs", | ||
| "test": "node --test test/test_toml.js", | ||
@@ -19,0 +19,0 @@ "test:spec": "node test/spec-test.js", |
Sorry, the diff of this file is too big to display
123966
-19.91%4321
-17.76%