Util

1. Introduction

This file contains some utilities for the rest of the literate program.

It has functions for reading the entire source of a file, and functions for reporting errors and warnings.

{util.d 1}
import globals;
import std.stdio;
import std.conv;
import parser;
import std.string;
import std.path;

{readall function, 2}
{error function, 3}
{warning function, 3}
{leadingWS function, 4}
{getCodeblocks function, 5}
{getChapterHtmlFile function, 6}

2. Readall

The readall function reads an entire text file, or reads from stdin until control-d is pressed, and returns the string.

{readall function 2}
// Read from a file
string readall(File file) {
    string src = "";
    while (!file.eof) {
        src ~= file.readln();
    }
    file.close();
    return src;
}

// Read from stdin
string readall() {
    string src = "";
    string line;
    while ((line = readln()) !is null) {
        src ~= line;
    }
    return src;
}

Used in section 1

3. Error and Warning

These functions simply write errors or warnings to stdout.

{error function 3}
void error(string file, int line, string message) {
    writeln(file, ":", line, ":error: ", message);
}

Used in section 1

{warning function 3}
void warn(string file, int line, string message) {
    writeln(file, ":", line, ":warning: ", message);
}

Used in section 1

4. Leading Whitespace function

This function returns the leading whitespace of the input string.

{leadingWS function 4}
string leadingWS(string str) {
    auto firstChar = str.indexOf(strip(str)[0]);
    return str[0..firstChar];
}

Used in section 1

5. getCodeblocks function

tempCodeblocks is an array that contains only codeblocks that have += or :=. rootCodeblocks and codeblocks are both associative arrays which will hold more important information. codeblocks will contain every codeblock after the += and := transformations have been applied.

Here we go through every single block in the program, and add it to the tempCodeblocks array if it has a += or :=. Otherwise, we add it to the codeblocks array, and if it matches the filename regex .*\.\w+, we add it to the rootCodeblocks array.

{getCodeblocks function 5}
void getCodeblocks(Program p, 
                   out Block[string] codeblocks,
                   out Block[string] rootCodeblocks) {
    Block[] tempCodeblocks;

    foreach (c; p.chapters) {
        foreach (s; c.sections) {
            foreach (b; s.blocks) {
                bool isRootBlock = false;
                if (b.isCodeblock) {
                    Block copy = b.dup();
                    auto fileMatch = matchAll(copy.name, regex(".*\\.\\w+"));
                    auto quoteMatch = matchAll(copy.name, regex("^\".*\"$"));
                    if (fileMatch || quoteMatch) {
                        copy.isRootBlock = true;
                        if (quoteMatch) {
                            copy.name = copy.name[1..$-1];
                        }
                    }
                    if ((!copy.modifiers.canFind(Modifier.additive)) && (!copy.modifiers.canFind(Modifier.redef))) {
                        codeblocks[copy.name] = copy;
                        if (copy.isRootBlock) {
                            rootCodeblocks[copy.name] = copy;
                        }
                    } else {
                        tempCodeblocks ~= copy;
                    }
                }
            }
        }
    }

    // Now we go through every codeblock in tempCodeblocks and apply the += and :=
    foreach (b; tempCodeblocks) {
        if (b.modifiers.canFind(Modifier.additive)) {
            auto index = b.name.length;
            string name = strip(b.name[0..index]);
            if ((name in codeblocks) is null) {
                error(p.file, b.startLine, "Trying to add to {" ~ name ~ "} which does not exist");
            } else {
                codeblocks[name].lines ~= b.lines;
            }
        } else if (b.modifiers.canFind(Modifier.redef)) {
            auto index = b.name.length;
            string name = strip(b.name[0..index]);
            if ((name in codeblocks) is null) {
                error(p.file, b.startLine, "Trying to redefine {" ~ name ~ "} which does not exist");
            } else {
                codeblocks[name].lines = b.lines;
            }
        }
    }
}

Used in section 1

6. getChapterHtmlFile function

This function returns the html file for a chapter given the major and minor numbers for it. The minor and major nums are passed in as a string formatted as: major.minor.

{getChapterHtmlFile function 6}
string getChapterHtmlFile(Chapter[] chapters, string num) {
    string[] nums = num.split(".");
    int majorNum = to!int(nums[0]);
    int minorNum = 0;
    if (nums.length > 1) {
        minorNum = to!int(nums[1]);
    }
    foreach (Chapter c; chapters) {
        if (c.majorNum == majorNum && c.minorNum == minorNum) {
            return stripExtension(baseName(c.file)) ~ ".html";
        }
    }
    return "";
}

Used in section 1