B u s L o g i c:- prolog, code, chatter.

A Static Website Generator

For those that have looked at this site in any detail will notice that each page is pretty much the same with only the middle different. The reason for this is because I have written a very simple program to place the content for each page into a template so that there is consistent look without having to use a complex content management system.

The code for the generator is so simple that it fits into a page itself!

Note that while this site no longer uses the generator, the original format has been retained!

Pages are defined using the page/2 predicate, for example:

page(
    source('index.html'),
    dest('index.html')
).
page(
    source('hex_codes\\hex_codes.html'),
    dest('hex_codes.html')
).
...

The source/1 term refers to the where the content for the page is located, and the dest/1 is what the page + template will be called. One restriction is that all generated files are created in the same directory, which isn't a concern for me at the moment.

Some constants are used to determine the base path for the source, and the base path for the output.

site_base_input_dir('.\\').
site_output_dir('blog\\').

A predicate is used to load the template, and split it into a header and footer. There is a keyword in the template to determine where to do the split, you can find out what this is by querying atom_codes(X, [123, 99, 111, 110, 116, 101, 110, 116, 125])

Note that there is a cut here, this is really for convienience because append leaves a choice point that will always fail, which means the that program doesn't finish cleanly.

load_template(Header, Footer) :- 
    read_file_to_codes('template.html', Template, []),
    append(Header, [123, 99, 111, 110, 116, 101, 110, 116, 125 | Footer], Template), !.

Most of the work is done in the convert_page/3 predicate.

    convert_page(Header, Footer, [SourcePath, DestPath]) :-

Create the path to the source file and read in the content.

    site_base_input_dir(InDir),
    atom_concat(InDir, SourcePath, In),
    read_file_to_codes(In, Content, []).

Need to create the path to the output file as well, based on the path destination.

    site_output_dir(OutDir),
    atom_concat(OutDir,DestPath, Out),

Finally open the output file and write the header + content + footer.

    open(Out, write, OutStream),
    maplist(write_codes(OutStream), [Header, Content, Footer]),
    close(OutStream).

There is one helper predicate, which writes out a list of codes. There might be a library predicate for this but I couldn't find one.

write_codes(St, Codes) :- 
    atom_codes(A, Codes), 
    write(St, A).

Ok, to put it all together, load the header and footer. Call convert page for all pages.

generate :-
    load_template(Header, Footer),
    forall(
        page(path(Source), path_to_use(Dest)),
        convert_page(Header,Footer, [Source,Dest])).

Now just call generate. and voila! one website.