WIP, add autogenerated tests against upstream

master
Sven Slootweg 2 years ago
parent 4cb239c6a0
commit 52f39570a7

@ -4,7 +4,7 @@
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
"test": "tape tests/**/* | tap-difflet"
},
"repository": {
"type": "git",
@ -32,11 +32,14 @@
"map-obj": "^5.0.0",
"match-value": "^1.1.0",
"split-filter": "^1.1.3",
"tap-difflet": "^0.7.2",
"tape": "^5.5.0",
"tree-sitter-javascript": "^0.19.0",
"tree-sitter-nix": "cstrahan/tree-sitter-nix"
},
"devDependencies": {
"@joepie91/eslint-config": "^1.1.0",
"eslint": "^8.8.0"
"eslint": "^8.8.0",
"tape-catch": "^1.0.6"
}
}

@ -3,44 +3,11 @@
const fs = require("fs");
const assert = require("assert");
const transpile = require("./src/transpile");
const measureTime = require("./src/astformer/util/measure-time");
const evaluate = require("./src/evaluate");
assert(process.argv[2] != null);
const nixFilePath = process.argv[2];
const nixFile = fs.readFileSync(nixFilePath, "utf8");
let transpiled = transpile(nixFile);
const api = {
builtins: {},
$$jsNix$memoize: function (func) {
let isCalled = false;
let storedResult;
return function (arg) {
if (isCalled === false) {
storedResult = func(arg);
isCalled = true;
}
return storedResult;
};
}
};
// TODO: Switch to Node `vm` API instead, and check whether there's a polyfill for it for non-Node environments, build a custom one if not
const context = { module: {}, exports: {} };
context.module.exports = exports;
new Function("module", transpiled)(context.module);
console.log("-- EVALUATION RESULT:");
// Warm-up for hot VM performance testing
// for (let i = 0; i < 10000; i++) {
// context.module.exports(api);
// }
console.log(measureTime(() => context.module.exports(api)));
console.log(evaluate(nixFile));

@ -196,7 +196,7 @@ module.exports = function optimizeTree(ast, optimizers) {
debuggers[debuggerName](`Node of type '${typeOf(node)}' replaced by node of type '${typeOf(result)}'`);
if (iterations >= EVALUATION_LIMIT) {
throw new Error(`Exceeded evaluation limit in optimizer ${debuggerName}; aborting optimization. If you are a user of raqb, please report this as a bug. If you are writing an optimizer, make sure that your optimizer eventually stabilizes on a terminal condition (ie. NoChange)!`);
throw new Error(`Exceeded evaluation limit in optimizer ${debuggerName}; aborting optimization. If you are a user of this software, please report this as a bug. If you are a developer writing an optimizer, make sure that your optimizer eventually stabilizes on a terminal condition (ie. NoChange)!`);
} else {
return handleASTNode(result, iterations + 1, path, initialStateLog, context);
}

@ -0,0 +1,45 @@
"use strict";
const measureTime = require("./astformer/util/measure-time");
const transpile = require("./transpile");
module.exports = function evaluate(nixCode) {
let transpiled = transpile(nixCode);
const api = {
builtins: {},
$$jsNix$memoize: function (func) {
let isCalled = false;
let storedResult;
return function (arg) {
if (isCalled === false) {
storedResult = func(arg);
isCalled = true;
}
return storedResult;
};
}
};
// TODO: Switch to Node `vm` API instead, and check whether there's a polyfill for it for non-Node environments, build a custom one if not
const context = { module: {}, exports: {} };
context.module.exports = exports;
new Function("module", transpiled)(context.module);
// Warm-up for hot VM performance testing
// for (let i = 0; i < 10000; i++) {
// context.module.exports(api);
// }
let result = measureTime(() => context.module.exports(api));
if (process.env.DEBUG_NIX) {
console.log("-- EVALUATION RESULT:");
console.log(result);
}
return result;
};

@ -7,5 +7,13 @@ module.exports = {
visitors: {
NixIntegerLiteral: (node) => types.numericLiteral(parseInt(node.value)),
NixFloatLiteral: (node) => types.numericLiteral(parseFloat(node.value)),
NixStringLiteral: (node) => {
if (node.parts.length === 1 && node.parts[0].type === "NixInterpolationLiteral") {
// Fast case; just a simple, non-interpolated string literal
return types.stringLiteral(node.parts[0].value);
} else {
throw new Error(`Unimplemented: string interpolation is not supported yet`);
}
}
}
};

@ -0,0 +1,40 @@
"use strict";
const tape = require("tape-catch");
const fs = require("fs");
const path = require("path");
const evaluate = require("../src/evaluate");
const NIX_SOURCE_REPO = process.env.NIX_SOURCE_REPO;
if (NIX_SOURCE_REPO == null) {
throw new Error(`To run the upstream Nix language tests, you must specify a NIX_SOURCE_REPO environment variable, that points at the root of a local checkout of the Git repository for Nix`);
}
const testsPath = path.join(NIX_SOURCE_REPO, "tests/lang");
let tests = fs.readdirSync(testsPath)
.filter((entry) => entry.endsWith(".exp"))
.map((entry) => entry.replace(/\.exp$/, ""));
for (let test of tests) {
try {
let expression = fs.readFileSync(path.join(testsPath, `${test}.nix`), "utf8");
let expectedResult = fs.readFileSync(path.join(testsPath, `${test}.exp`), "utf8").replace(/\n$/, "");
tape(`Nix upstream language tests - ${test}`, (test) => {
test.plan(1);
let result = evaluate(expression).value.toString();
test.equals(expectedResult, result);
});
} catch (error) {
// FIXME: This would currently cause ENOENTs during evaluation (eg. reading a file from Nix itself) to be ignored
if (error.code === "ENOENT") {
// skip
} else {
throw error;
}
}
}
Loading…
Cancel
Save