// Fragile, hacky script that finds exercises in chapters, extracts
// their starting code, and collects it into a big JSON object
// together with the solution code.
const PJSON = require("./pseudo_json")
var fs = require("fs");
var output = [], failed = false;
var allSolutions = fs.readdirSync("code/solutions/").filter(function(file) { return !/^2[012]/.test(file); });
var dir = fs.readdirSync(".");
dir.sort();
dir.forEach(function(file) {
var match = /^((\d+).*).md$/.exec(file), chapNum = match && match[2];
if (!match) return;
var text = fs.readFileSync(file, "utf8");
let meta = (/{{meta (.*)}}/.exec(text) || {1: "{}"})[1]
var includes = /\bload_files: (\[.*?\])/.exec(meta)
if (includes) includes = JSON.parse(includes[1]);
var chapter = {number: +chapNum,
id: match[1],
title: text.match(/(?:^|\n)# (.*?)\n/)[1],
start_code: getStartCode(text, includes),
exercises: [],
include: includes};
var zip = chapterZipFile(text, chapter);
var extraLinks = meta.match(/\bcode_links: (\[.*?\])/);
if (extraLinks) extraLinks = JSON.parse(extraLinks[1]);
if (extraLinks || zip)
chapter.links = (zip ? [zip] : []).concat(extraLinks || []);
var exerciseSection = text.indexOf("\n## Exercises\n");
var exerciseBlock = exerciseSection >= 0 ? text.slice(exerciseSection) : "";
var header = /\n### (.*?)\n/g, nextHeader = /\n##+ \w/g;
var num = 1;
while (match = header.exec(exerciseBlock)) {
nextHeader.lastIndex = header.lastIndex
let foundNext = nextHeader.exec(exerciseBlock)
var nextsection = foundNext ? foundNext.index : -1
for (var pos = header.lastIndex;;) {
var ifdef = exerciseBlock.indexOf("{{if interactive", pos);
if (ifdef == -1 || nextsection > 0 && nextsection < ifdef) break;
var indef = exerciseBlock.slice(pos = ifdef + 15, exerciseBlock.indexOf("if}}", ifdef));
var sourceBlock = indef.match(/```(.*)\n([^]+?)\n```/);
if (!sourceBlock || sourceBlock[1].indexOf("null") > -1) continue;
var type = sourceBlock[1].indexOf("html") > -1 ? "html" : "js";
var file = chapNum + "_" + num + "_" + match[1].toLowerCase().replace(/[^\-\s\w]/g, "").replace(/\s/g, "_") + "." + type;
try {
var solution = fs.readFileSync("code/solutions/" + file, "utf8");
var extra = /^\s*\s*(\n\n\n",
include: ["code/draw_graph.js", "code/chapter/22_fast.js"],
exercises: [
{name: "Pathfinding",
file: "code/solutions/22_1_pathfinding.js",
number: 1,
type: "js",
code: "function findPath(a, b) {\n // Your code here...\n}\n\nvar graph = treeGraph(4, 4);\nvar root = graph[0], leaf = graph[graph.length - 1];\nconsole.log(findPath(root, leaf).length);\n// → 4\n\nleaf.connect(root);\nconsole.log(findPath(root, leaf).length);\n// → 2\n",
solution: fs.readFileSync("code/solutions/22_1_pathfinding.js", "utf8")
},
{name: "Timing",
file: "code/solutions/22_2_timing.js",
number: 2,
type: "js",
code: "",
solution: fs.readFileSync("code/solutions/22_2_timing.js", "utf8")
},
{name: "Optimizing",
file: "code/solutions/22_3_optimizing.js",
number: 3,
type: "js",
code: "",
solution: fs.readFileSync("code/solutions/22_3_optimizing.js", "utf8")
}
]
});
if (allSolutions.length) {
console.error("Solution files " + allSolutions + " were not used.");
failed = true;
}
if (!failed)
console.log("var chapterData = " + JSON.stringify(output, null, 2) + ";");
else
process.exit(1);
function prepareHTML(code, include) {
return "\n" + (include || []).map(function(s) {
return "\n";
}).join("") + "\n" + code;
}
function guessType(code) {
return /^[\s\w\n:]*