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
We will need to decide which mode in which to operate. For the moment we will use the value of the LITERATE environment variable to select which mode.
setup mode flags +≡: literate-env ( -- $ ) s" LITERATE" getenv ; : literate-mode ( $ -- ) literate-env compare 0= constant ;
Running is selected by having LITERATE unset or empty. Anything else is considered an error.
setup mode flags +≡s" weave" literate-mode weaving? s" tangle" literate-mode tangling? s" " literate-mode running?
As a sanity check, we will insist we are in at least one mode.
setup mode flags +≡weaving? tangling? or running? or assert
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 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! run-filename atom-string@ included run-cleanup ;
apply literate mode +≡
: |. ( exit literate mode ) chapter-finish weaving? if weave bye then tangling? if tangle bye then running? if run then ;