This is the source for tangle. This compiles code from a .lit
file into runnable code.
We do this by going through all the codeblocks, and storing them in an associative array.
Then we apply additions and redefinitions so that the array just contains the code for
each codeblock (indexed with a string: the codeblock name). Then we find the root codeblocks,
i.e. the ones that are a filename, and recursively parse all the codeblocks, following each
link and adding in the code for it.
Here is an overview of the file:
The tangle
function will take a program in, go through all the chapters, sections
and find all the codeblocks. It will then apply the codeblock with +=
and :=
.
Another thing it must do is find the root blocks, that is, the files that need
to be generated. Starting with those, it will recursively write code to a file using
the writeCode
function.
The tangle function should find the codeblocks, apply the +=
and :=
, find the
root codeblocks, and call writeCode
from those.
We'll start with these three variables.
Block[string] rootCodeblocks; Block[string] codeblocks; getCodeblocks(p, codeblocks, rootCodeblocks);
Added to in section 3
Used in section 1
Now we check if there are any root codeblocks.
if (rootCodeblocks.length == 0) { warn(p.file, 1, "No file codeblocks, not writing any code"); }
Added to in section 3
Used in section 1
Finally we go through every root codeblock, and run writeCode on it. We open a file
(making sure it is in outDir
). We get the commentString
from the list of commands.
Then we call writeCode
, which will recursively follow the links and generate all
the code.
The writeCode function recursively follows the links inside a codeblock and writes all the code for a codeblock. It also keeps the leading whitespace to make sure indentation in the target file is correct.
void writeCode(Block[string] codeblocks, string blockName, File file, string filename, string whitespace) { Block block = codeblocks[blockName]; if (block.commentString != "") { if (!noOutput) file.writeln(whitespace ~ block.commentString.replace("%s", blockName)); } foreach (lineObj; block.lines) { string line = lineObj.text; string stripLine = strip(line); if (stripLine.startsWith("@{") && stripLine.endsWith("}")) { string newWS = leadingWS(line); auto index = stripLine.length - 1; auto newBlockName = stripLine[2..index]; if (newBlockName == blockName) { error(lineObj.file, lineObj.lineNum, "{" ~ blockName ~ "} refers to itself"); tangleErrors = true; return; } if ((newBlockName in codeblocks) !is null) { writeCode(codeblocks, newBlockName, file, filename, whitespace ~ newWS); } else { error(lineObj.file, lineObj.lineNum, "{" ~ newBlockName ~ "} does not exist"); tangleErrors = true; } } else { if (!noOutput) { if (lineDirectives) { if (lineDirectiveStr != "") { file.writeln(lineDirectiveStr, " ", lineObj.lineNum); } else { file.writeln(block.commentString.replace("%s", to!string(lineObj.lineNum))); } } file.writeln(whitespace ~ line); } } } if (!noOutput) file.writeln(); }
Used in section 1