Weaving, Tangling, and Running

Modes of operation

There are three actions typically taken on literate programs: weave, tangle, and running. Weaving is the generation of documentation from a literate program. Tangling is the generation of macro expanded source code from a literate program. Running is the act of tangling followed by evaluation of the tangled output. Most of the plumbing to handle these modes is executed on each run, for simplicity and to ensure failures are detected early. The pieces look like this:

primary program flow +≡
setup mode flags
weaving implementation
tangle implementation
run implementation
apply literate mode

Mode selection

We will need to decide which mode in which to operate. The properties we desire are:

One way to achieve this is to look for a mode code on the stack. Systems like gforth can then be invoked like this: gforth -e mode-codeprogram_lit.fs. Recursive use is possible bye passing the code for running mode to before importing literate_lit.fs. By using depththe default action of running can be preserved. A small drawback of this approach is that it precludes programs which need to use a similar mechanism. We'll use the following codes:

Let's setup a variable containing the current mode.

setup mode flags +≡
variable literate-mode
: literate-setup
    depth 0<= if 0 then literate-mode ! ;
literate-setup

Setup flags based on this.

setup mode flags +≡
literate-mode @ 0 = constant running?
literate-mode @ 1 = constant tangling?
literate-mode @ 2 = constant weaving?

As a sanity check, we will insist we are in at least one mode.

setup mode flags +≡
weaving? tangling? or running? or assert

Tangling

The process of tangling can generate one or more files depending on user input. At the point we are doing final tangling, all filenames will have a "meaning" associated with them that is their desired content.

tangle implementation +≡
: tangle-file ( file -- )
    file-name@ dup means swap file! ;

Each file is then iterated thru.

tangle implementation +≡
: tangle
    out-files @ begin dup while
    dup tangle-file ->next repeat drop ;

Running

Running involves tangling followed by evaluation. Ideally, evaluation could happen in memory. Unfortunately, ANSFORTH's EVALUATE word can only be used to fill in one "line" in the input buffer. This precludes the use of multi-line parsing words which are line aware (such as \). Since we would like to support Forth's full syntax, we will instead output a temporary file and use INCLUDED.

We will select a temporary filename based on the document base. This can cause problems if multiple instances are running at once from the same directory. However, pre-tangling can be used in this case.

run implementation +≡
: run-filename ( -- A )
    doc-base @ atom" _running.tmp" atom+ ;

After evaluation we will want to cleanup the temporary file.

run implementation +≡
: run-cleanup
    run-filename atom-string@ delete-file drop ;

We will override bye to attempt to make sure cleanup happens even if the evaluated program exits early.

run implementation +≡
: bye   run-cleanup bye ;

When running, as there can be many tangled output files, we adopt noweb's convention that the root for evaluation is the chunk named "*".

run implementation +≡
: run
    atom" *" means
    run-filename file!-tmp
    forth-wordlist 1 set-order
    forth-wordlist set-current
    include-file
    run-cleanup
;

Commence operation

apply literate mode +≡
: |. ( exit literate mode )
     chapter-finish
     weaving? if weave bye then
     tangling? if tangle bye then
     running? if run then ;