Literate Programming

WEB 0 - Start

WEB 1 - Multiple sections

Jon Breuer - September 7, 2024.

Now that we have WEB0.exe, it needs to be improved. The point of literate programming is to be able to explain each section of code as it appears, but my parser can only handle one section, so I need to fix that first.

@p
////////////
// WEB1.D
//
// This is a level 1 bootstrapping Literate Programming thing.  
// It will snip the every code sample out of the document and generate the target file.
//
module web1;

private import std.algorithm;
private import std.ascii;
private import std.file;
private import std.stdio;
private import std.string;

// Utility function to allow progressive searches within file contents.
ptrdiff_t countFromPosUntil(string haystack, ptrdiff_t startIndex, string needle)
{
    ptrdiff_t offset = countUntil(haystack[startIndex..haystack.length], needle);
    if(offset < 0) {
        return offset;
    }
    // The index we've recieved is within the truncated slice.  Return the index within the whole haystack string.
    return startIndex + offset;
}

void main(string[] args)
{
    if(args.length < 3) {
        writefln("Usage: WEB1 inputFile outputCodeFile");
    }
    
    string fileContents = cast(string) std.file.read(args[1]);
    if(fileContents.length == 0) {
        writefln("Unable to read file '%s'.", args[1]);
        return;
    }
    
    // Generate these strings so they don't appear in the source.
    const string startTag = "@" ~ "p";
    const string endTag = "@" ~ ">";
    
    string outputFileContents = "";
    ptrdiff_t blockEndIndex = 0;
    for(
        ptrdiff_t nextBlockIndex = countUntil(fileContents, startTag);
        nextBlockIndex != -1;
        nextBlockIndex = countFromPosUntil(fileContents, blockEndIndex, startTag)) {
        
        if(nextBlockIndex > 0 && fileContents[nextBlockIndex - 1] == '@') {
            // Don't parse escaped at symbols or examples.
            blockEndIndex = nextBlockIndex + 1;
            continue;
        }
        blockEndIndex = countFromPosUntil(fileContents, nextBlockIndex, endTag);
        if(blockEndIndex < 0) {
            writefln("Start tag without end tag found at location %d.", nextBlockIndex);
            return;
        }
        
        outputFileContents ~= fileContents[nextBlockIndex + startTag.length .. blockEndIndex];
    }
    
    string outputFilename = args[2];
    std.file.write(outputFilename, outputFileContents);
}
@>

WEB0 and WEB1 will automatically snip the start and end commands out so I don't have to comment them now.

Unfortunately, WEB1 is still written in a single block. The only way to test the differences between WEB1 and WEB0 is with an additional non-function block.

@p
// This is a comment which a file correctly parsed by WEB1 will include.
@>
C:\literate> web0 literate_programming_1.html web1.d
C:\literate> dmd web1.d
C:\literate> web1 literate_programming_1.html web1.d
C:\literate> dmd web1.d

WEB0 will generate a D source file that looks like this:

////////////
// WEB1.D
...
void main()
...
    std.file.write(outputFilename, outputFileContents);
}

And WEB1 should generate a file which looks like this:

////////////
// WEB1.D
...
void main()
...
    std.file.write(outputFilename, outputFileContents);
}

// This is a comment which a file correctly parsed by WEB1 will include.

Prev:WEB 0 Top:WEB 0 Next:WEB 2 - Generating HTML