"";;010B8CEF872E4CAE653DCBA950DA8437DE154147ADBAE6B1F9D5B2BB0806
""{ Logiweb, a system for electronic distribution of mathematics
Copyright (C) 2004-2009 Klaus Grue
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed IN the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 021111307 USA
Contact: Klaus Grue, DIKU, Universitetsparken 1, DK2100 Copenhagen,
Denmark, grue@diku.dk, http://logiweb.eu/, http://www.diku.dk/~grue/
Logiweb is a system for distribution of mathematical definitions,
lemmas, and proofs. For more on Logiweb, consult http://logiweb.eu/.
""}
""P lgc
""R base
""D 0
late define " as " end define
lgc-get-events ( " )
lgc-set-events ( " , " )
lgc-clr-events ( " )
lgc-push-event ( " , " )
lgc-do-events ( " )
lgc-exec ( " )
lgc-exec-events ( " , " )
lgc-main
lgc-config-1 ( " )
lgc-config-2 ( " , " )
lgc-config-3 ( " , " )
lgc-config-4 ( " )
lgc-config-5 ( " , " )
lgc-config-6 ( " , " )
lgc-tilde-expand ( " , " )
lgc-default-leap
lgc-default-options
lgc-process-argv ( " , " )
lgc-argv-short-to-long
lgc-process-short-argv ( " , " , " , " )
lgc-split-long-arg ( " )
lgc-argv-downcase ( " )
lgc-argv-downcase1 ( " )
lgc-argv-downcase2 ( " )
lgc-process-long-argv ( " , " )
lgc-process-assignment ( " , " , " , " )
lgc-process-env ( " , " )
lgc-process-lines ( " , " )
lgc-file2lines ( " )
lgc-file2lines1 ( " , " , " )
lgc-add-line-to-lines ( " , " )
lgc-process-file ( " , " )
lgc-process-parameters ( " )
lgc-process-query ( " )
lgc-help
lgc-help2
lgc-help3
lgc-version
lgc-license
lgc-options ( " )
lgc-options1 ( " , " )
lgc-options2 ( " , " )
lgc-option ( " )
lgc-seconds-per-minute
lgc-seconds-per-day
lgc-minutes-per-hour
lgc-hours-per-day
lgc-days-per-month
lgc-days-per-dimester
lgc-days-per-quimester
lgc-days-per-year
lgc-days-per-olympiad
lgc-days-per-century
lgc-days-per-Gregorian
lgc-months-per-dimester
lgc-months-per-quimester
lgc-months-per-year
lgc-month-of-march
lgc-years-per-olympiad
lgc-years-per-century
lgc-years-per-Gregorian
lgc-grd-of-mjd0
lgc-grd2day ( " )
lgc-day-of-mjd0
lgc-grd2mjd ( " )
lgc-limited-floor ( " , " , " )
lgc-mjd-of-grd-0-03-01
lgc-mjd2grd ( " )
lgc-parse-prefix ( " , " )
lgc-prefix-grd
lgc-prefix-hyphen
lgc-parse-leap ( " )
lgc-convert-leap ( " )
lgc-check-leap ( " )
lgc-initial-leap
lgc-add-leap ( " )
lgc-add-leap1 ( " , " , " )
lgc-process-leap ( " )
lgc-ref2lgt ( " )
lgc-lgt2vt ( " )
lgc-lgt2mjdtai ( " )
lgc-lgt2mjdtai2vt ( " )
lgc-lgt2utc ( " , " , " )
lgc-lgt2grdutc ( " , " )
lgc-lgt2grdutc2vt ( " , " )
lgc-lgt2grdutc2v ( " , " )
lgc-unix2lgt ( " , " )
lgc-lgt-of-unix
lgc-unix2lgt1 ( " , " , " , " , " )
test-leaps
test-leapstate
test-leapstate2
test-leapstate3
lgc-lex-1 ( " )
lgc-lex-2 ( " , " )
lgc-lex-3 ( " , " )
lgc-lex-4 ( " , " )
lgc-lex-source ( " , " )
lgc-lex-position ( " , " )
lgc-lex-newline ( " , " )
lgc-lex-comment1 ( " )
lgc-lex-comment2 ( " , " , " )
lgc-lex-comment3 ( " )
lgc-lex-comment4 ( " , " , " , " )
lgc-lex-escape1 ( " )
lgc-lex-escape2 ( " , " )
lgc-lex-escape3 ( " , " )
lgc-lex-collect ( " )
lgc-lex-collect1 ( " , " , " )
lgc-lex-left-trim ( " )
lgc-lex-right-trim ( " )
lgc-lex-trim ( " )
lgc-lex-reverse-contract ( " )
lgc-lex-reverse-contract1 ( " , " )
lgc-lex-contract ( " )
lgc-lex-reverse-contract* ( " )
lgc-lex-contract* ( " )
lgc-lex-reverse-trim-contract ( " )
lgc-lex-trim-contract ( " )
lgc-lex-space ( " )
lgc-lex-page ( " , " , " , " )
lgc-lex-page1 ( " , " , " , " )
lgc-lex-ref ( " , " , " , " )
lgc-lex-ref1 ( " , " , " , " )
lgc-lex-def ( " )
lgc-lex-def0 ( " , " , " , " , " )
lgc-lex-def1 ( " , " , " )
lgc-lex-def2 ( " , " , " , " , " )
lgc-lex-string ( " , " , " , " )
lgc-lex-hex ( " , " , " , " , " )
lgc-lex-hex1 ( " , " , " , " , " , " )
lgc-lex-extract-body ( " )
lgc-lex-extract-body1 ( " , " )
lgc-lex-extract-other ( " , " )
lgc-lex-extract-page ( " , " )
lgc-lex-extract-ref ( " , " )
lgc-lex-extract-def ( " , " )
lgc-lex-extract-include ( " , " )
lgc-include-1 ( " )
lgc-include-2 ( " , " )
lgc-load-1 ( " )
lgc-load-fetch0 ( " , " )
lgc-load-setpath ( " )
lgc-progress-fetch ( " , " , " )
lgc-heads ( " )
lgc-load-fetch ( " )
lgc-load-fetch-name ( " )
lgc-load-fetch-ref ( " , " )
lgc-load-fetch-ref1 ( " , " )
lgc-file-prefix
lgc-http-prefix
lgc-//-prefix
lgc-lgw-prefix
lgc-name-prefix
lgw-suffix
lgr-suffix
lgu-suffix
lgc-load-fetch-path ( " , " )
lgc-load-fetch-file ( " , " )
lgc-tilde-expand1 ( " , " )
lgc-cwd-expand ( " , " )
lgc-load-fetch-http ( " , " )
lgc-prefix ( " , " )
lgc-char-split ( " , " )
lgc-char-split1 ( " , " , " )
lgc-replace-colon ( " , " )
lgc-replace-colon1 ( " , " )
lgc-file-suffix ( " )
lgc-load-fetch-ref-failed ( " , " )
lgc-load-no-colon ( " )
lgc-wrong-suffix ( " , " )
lgc-wrong-lgu-suffix ( " , " )
lgc-load-malformed-url ( " , " )
lgc-load-malformed-lgu ( " , " )
lgc-load-malformed-page ( " )
lgc-load-wrong-page ( " )
lgc-load-receive ( " , " )
lgc-load-received-nothing ( " )
lgc-load-receive-lgw ( " , " )
lgc-load-receive-lgw1 ( " , " )
lgc-logiweb-version
lgc-wrong-ref ( " , " , " )
lgc-load-receive-lgr ( " , " )
lgc-load-receive-lgu ( " , " )
lgc-trim-newline ( " )
lgc-load-codify ( " )
lgc-load-codify1 ( " , " )
lgc-load-codify-lgw ( " , " )
lgc-load-codify-lgr ( " , " )
lgc-load-render ( " , " )
lgc-load-render1 ( " , " )
lgc-load-codify-closure ( " , " )
lgc-parse-int ( " )
lgc-parse-int1 ( " , " )
lgc-atoi ( " , " )
lgc-itoa ( " )
lgc-itoa1 ( " , " )
lgc-ctoa ( " , " )
lgc-ordinal-suffix ( " )
lgc-ordinal ( " )
lgc-max-messages
lgc-add-message ( " , " , " , " )
lgc-throw-message ( " , " , " )
lgc-die ( " )
lgc-report-messages ( " )
lgc-report-messages1 ( " , " , " )
lgc-report-messages2 ( " , " , " )
lgc-position ( " , " , " , " )
lgc-position-1 ( " , " , " , " , " )
lgc-max-lines
lgc-max-chars
lgc-report-message3 ( " , " )
lgc-split ( " , " , " )
lgc-split1 ( " , " )
lgc-char-start1
lgc-char-start2
lgc-char-start ( " )
lgc-size-limit ( " , " , " , " )
lgc-error ( " , " , " )
lgc-simple-error ( " , " )
lgc-progress ( " , " , " )
lgc-print ( " , " )
lgc-EOF
lgc-esc-.
lgc-esc--
lgc-esc-!
lgc-esc-#
lgc-esc-$
lgc-esc-left
lgc-esc-right
lgc-esc-brace
lgc-esc-B
lgc-esc-C
lgc-esc-D
lgc-esc-N
lgc-esc-P
lgc-esc-R
lgc-esc-S
lgc-esc-f
lgc-esc-n
lgc-esc-r
lgc-esc-t
lgc-esc-x
lgc-left-trim ( " )
lgc-right-trim ( " )
lgc-trim ( " )
lgc-reverse-contract ( " )
lgc-reverse-contract1 ( " , " )
lgc-contract ( " )
lgc-contract* ( " )
lgc-trim-contract ( " )
lgc-panic ( " )
lgc-splice ( " , " )
lgw-parse-string ( " )
lgw-parse-string1 ( " , " , " )
lgw-parse-bibliography ( " )
lgw-parse-bibliography1 ( " , " )
lgw-parse-dictionary ( " )
lgw-parse-dictionary1 ( " , " )
lgw-trisect ( " )
lgw-codify ( " , " , " )
lgw-codify1 ( " , " , " , " )
lgw-codify-unpack ( " , " )
lgw-codify-unpacker ( " , " )
lgw-codify-unpack1 ( " , " )
lgw-make-dict ( " , " )
lgw-make-dict1 ( " , " , " , " , " )
lgw-make-dict2 ( " , " , " , " , " )
lgw-parse-tree ( " , " , " )
lgw-parse-tree* ( " , " , " , " , " , " )
lgw-parse-tree-string ( " , " )
lgw-codify-initialize ( " , " )
lgw-codify-expand ( " , " )
lgw-codify-expand1 ( " , " )
lgw-codify-harvest ( " , " , " , " )
lgw-codify-harvest* ( " , " , " , " )
lgw-proclaim-array
lgw-codify-proclaim ( " , " , " )
lgw-tree2aspect ( " , " )
lgw-codify-define ( " , " , " , " )
lgc-node2ref ( " )
lgc-node2idx ( " )
lgc-node2charge ( " )
lgc-node2open ( " )
lgc-node2closed ( " )
lgc-node2name ( " )
lgc-node2binary ( " )
lgc-node2relref ( " )
lgc-grammar ( " )
lgc-grammar1 ( " , " )
lgc-grammar-init
lgc-grammar-init1 ( " , " )
lgc-grammar-add-bib ( " , " , " , " )
lgc-grammar-add-ref ( " , " , " , " )
lgc-grammar-add-dictionary ( " , " , " , " , " )
lgc-grammar-add-construct ( " , " , " , " , " )
lgc-grammar-add-construct1 ( " , " , " , " , " , " )
lgc-card2septet* ( " )
lgc-aritysymbol2vt ( " , " , " , " )
lgc-aritysymbol2vt1 ( " , " , " , " )
lgc-grammar-valid-construct ( " , " )
lgc-def2charge ( " )
lgc-grammar-get-charge ( " , " , " )
lgc-grammar-default-construct ( " , " , " )
lgc-grammar-default-arglist ( " )
lgc-grammar-default-arglist1 ( " )
lgc-grammar-add-def ( " )
lgc-grammar-def-construct ( " , " , " , " )
lgc-arity ( " )
lgc-grammar-add-defs* ( " , " , " )
lgc-grammar-add-defs ( " , " , " )
lgc-grammar-def-construct* ( " , " , " , " )
lgc-parse-charge ( " , " )
lgc-parse-charge1 ( " , " )
lgc-parse-charge2 ( " )
lgc-charge2vector* ( " )
lgc-charge2vector*1 ( " )
lgc-grammar-get-tuple ( " , " )
lgc-charge ( " , " )
lgc-charge1 ( " , " , " )
lgc-charge2 ( " , " , " , " )
lgc-charge-right ( " , " , " , " , " )
lgc-charge-right1 ( " , " )
lgc-charge-brace ( " , " , " , " , " )
lgc-charge-brace0 ( " )
lgc-charge-brace1 ( " , " , " , " )
lgc-less-charge ( " , " )
lgc-charge-left ( " )
lgc-charge-left1 ( " , " , " )
lgc-charge-string ( " , " )
lgc-charge-string1 ( " , " , " )
lgc-string2bytes ( " , " )
lgc-charge-bytes ( " , " )
lgc-charge-binary ( " , " , " )
lgc-charge-text ( " , " , " )
lgc-charge-text1 ( " , " , " )
lgc-host-newline ( " )
lgc-get-newline ( " , " )
lgc-add-newline ( " , " )
lgc-add-headline ( " , " , " )
lgc-charge-source ( " , " )
lgc-headline
lgc-parse-headline ( " )
lgc-parse-headline1 ( " , " )
lgc-charge-name ( " , " , " )
lgc-charge-charge ( " , " , " )
lgc-charge-auto ( " , " , " , " )
lgc-charge-name-find ( " , " , " , " )
lgc-charge-name2binary ( " , " )
lgc-charge-best-node ( " )
lgc-charge-auto1 ( " , " , " , " , " , " )
lgc-vectorize ( " , " )
lgc-add-dict ( " , " )
lgc-add-bib ( " , " )
lgc-ref2vector* ( " )
lgc-ref-version
lgc-hex2card ( " )
lgc-mixed2vector* ( " )
lgc-add-ref ( " , " )
lgc-add-ref1 ( " , " )
lgc-parse1 ( " , " )
lgc-parse2 ( " )
lgc-parse3 ( " )
lgc-parse4 ( " , " )
lgc-parse5 ( " )
lgc-parse6 ( " , " )
lgc-parse ( " , " )
lgc-parse-any ( " , " , " , " , " , " )
lgc-parse-left ( " , " , " , " , " , " )
lgc-parse-right ( " , " , " , " , " , " )
lgc-parse-brace ( " , " , " , " , " , " )
lgc-parse-token ( " , " , " , " , " )
lgc-parse-best ( " , " , " )
lgc-parse-no-interpretations ( " , " )
lgc-parse-no-inter1 ( " )
lgc-shortest ( " )
lgc-shortest1 ( " )
lgc-shortest2 ( " )
lgc-parse-ambiguous ( " , " )
lgc-parse-ambiguous1 ( " , " , " )
lgc-parse-ambiguous2 ( " )
lgc-parse-ambiguous3 ( " )
lgc-parse-ambiguous4 ( " , " )
lgc-parse-ambiguous-construct ( " , " )
lgc-parse-cannot-trisect ( " )
lgc-proclaim-error ( " , " )
lgc-parse-codify-lgw ( " , " )
lgc-rendering-no-colon ( " )
lgc-tree2vector* ( " , " )
lgc-string2vt ( " )
lgc-string2vt1 ( " )
lgc-symbol2vt ( " , " , " )
lgc-symbol2vt1 ( " , " , " )
lgc-tree2vt ( " , " )
lgc-tree2vt0 ( " , " , " , " )
lgc-open
lgc-close
lgc-tree*2vt ( " , " , " , " )
lgc-tree2vt1 ( " , " )
lgc-ref2vt ( " , " )
tree2vt ( " , " )
tree*2vt ( " , " )
lgc-vector*2html ( " )
lgc-html-open ( " )
lgc-html-close ( " )
lgc-tree2html ( " , " )
lgc-html-begin ( " )
lgc-html-end ( " )
lgc-html-tag ( " )
lgc-html-br
lgc-html-wrap ( " , " )
lgc-html-title ( " )
lgc-html-h2 ( " )
lgc-html-h3 ( " )
lgc-html-h4 ( " )
lgc-html-p ( " )
lgc-html-it ( " )
lgc-html-tt ( " )
lgc-html-ptt ( " )
lgc-html-string ( " )
lgc-html-arg ( " , " )
lgc-html-favicon ( " )
lgc-html-utf8
lgc-html-head ( " )
lgc-html-icon
lgc-html-href ( " , " )
lgc-html-name ( " , " )
lgc-html-named-h3 ( " , " )
lgc-html-address ( " )
lgc-html-body ( " , " , " )
lgc-html-page ( " , " , " )
lgc-html-help
lgc-render ( " , " )
lgc-render-verify ( " , " )
lgc-render-add-slash ( " )
lgc-render-dirname ( " , " )
lgc-render-dir ( " , " )
lgc-lgs-suffix
lgc-page-name ( " )
lgc-page-name1 ( " , " )
lgc-render-link ( " , " )
lgc-render-link1 ( " , " , " , " )
lgc-render-vector ( " , " )
lgc-render-ref ( " , " )
lgc-render-source ( " , " )
lgc-render-source1 ( " )
lgc-render-tdiagnose ( " , " , " )
lgc-render-pdiagnose ( " , " )
lgc-render-icons ( " , " )
lgc-logiweb.png
lgc-logiweb.ico
lgc-logiweb.eps
lgc-render-index ( " , " , " )
lgc-render-extract ( " , " , " , " )
lgc-render-extract-toc
lgc-render-extract-date ( " , " )
lgc-render-bib ( " , " )
lgc-render-ref-link ( " , " )
lgc-render-bib1 ( " , " , " , " )
lgc-render-def ( " , " )
lgc-render-def1 ( " , " , " , " )
lgc-render-def2 ( " , " , " , " )
lgc-render-def-sym* ( " , " , " , " )
lgc-render-def-sym ( " , " , " , " )
lgc-render-def-string ( " , " , " )
lgc-render-def-sym1 ( " , " , " )
lgc-render-def-sym2 ( " , " , " )
lgc-render-def-sym3 ( " , " )
lgc-render-charge ( " , " )
lgc-collect-charge ( " , " , " )
lgc-collect-charge1 ( " , " , " )
lgc-collect-charge2 ( " , " , " , " )
lgc-collect-charge3 ( " , " , " , " )
lgc-collect-charge4 ( " , " , " )
lgc-render-charge0 ( " , " , " , " )
lgc-render-charge1 ( " , " , " , " , " )
lgc-render-charge2 ( " , " , " , " )
lgc-render-charge-bib ( " , " , " )
lgc-render-charge-self ( " , " , " )
lgc-render-hdiagnose ( " , " , " , " , " )
lgc-render-correct
lgc-render-hdiagnose1 ( " )
lgc-render-dump ( " , " )
lgc-render-user ( " , " )
lgc-render-user0 ( " )
lgc-render-noevent
lgc-render-user1 ( " , " , " )
lgc-render-user2 ( " , " )
lgc-render-response ( " , " )
lgc-goodbye ( " )
lgc-render-file ( " , " , " , " )
lgc-render-file-exec ( " , " , " , " )
lgc-render-text ( " , " , " , " )
lgc-render-text-exec ( " , " , " , " )
lgc-render-add-lf ( " )
lgc-render-lgwam ( " , " , " , " )
lgc-render-invoke ( " , " , " , " )
lgc-render-path ( " )
lgc-render-path1 ( " )
lgc-render-path-dot ( " )
lgc-render-path-slash ( " )
lgc-render-refuse ( " , " )
lgc-render-events ( " , " , " )
lgc-render-event0 ( " )
lgc-render-event ( " , " , " )
lgc-render-expand ( " , " )
lgc-render-expand1 ( " , " , " )
lgc-render-default ( " , " , " )
lgc-render-body ( " , " , " )
lgc-render-diagnose ( " , " , " )
lgc-render-diagnose1 ( " , " , " , " )
lgc-render-pdftitle ( " )
lgc-render-pdftitle1 ( " )
lgc-render-exec ( " , " , " )
lgc-render-exec1 ( " , " , " , " )
lgc-render-exec2 ( " , " , " , " , " )
lgc-render-exec-arg ( " , " )
lgc-gdef ( " , " )
lgc-only-letters ( " )
lgc-gdef-ref ( " , " )
lgc-gdef-bib ( " , " )
lgc-break ( " )
lgc-break1 ( " )
lgc-render-lgwinclude ( " , " , " )
lgc-index ( " , " , " )
lgc-const2val ( " , " )
lgc-render-compile ( " , " , " )
lgc-render-compile1 ( " , " , " )
lgc-render-compile* ( " , " , " )
lgc-render-eval ( " , " , " , " )
lgc-render-eval* ( " , " , " , " )
lgc-render-use
lgc-render-use1 ( " )
lgc-render-compile-use ( " , " , " , " )
lgc-render-compile-use1 ( " , " , " , " )
lgc-understood ( " )
lgc-render-show
lgc-render-show1 ( " )
lgc-render-compile-show ( " , " , " , " )
lgc-render-compile-show1 ( " , " , " )
lgc-render-name ( " , " , " )
lgc-render-name1 ( " , " )
lgc-render-name2 ( " , " )
lgc-render-string ( " )
lgc-render-string0 ( " )
lgc-render-string1 ( " , " , " )
lgc-render-string2 ( " , " )
lgc-render-array
selfref
baseref
lgw-trisect-self
lgw-trisect-base
lgw-codify-self
lgw-codify-base
lgc-test ( " )
""B
page ( ""N , ""C )
title "Compiler"
bib "
@techreport{appendix,
author = {Klaus Grue},
year = {2008},
title = {Compiler - appendix},
institution={Logiweb},
note =
{\href{\lgwBlockRelay \lgwBlockThis /page/appendix.pdf}{%
\lgwBreakRelay \lgwBreakThis /page/appendix.pdf}}}
"
main text "
\title{Compiler}
\author{Klaus Grue}
\maketitle
\tableofcontents
\section{The lgc compiler}\nocite{appendix}"[ "
\subsection{Frontend language}
The Logiweb compiler (lgc) comprises a \emph{frontend}, a \emph{codifier}, and a \emph{renderer}.
The frontend takes a Logiweb \emph{source} file as input. Source files typically have extension .lgs; they are human readable and human editable files expressed in the Logiweb \emph{frontend language}.
The frontend produces a Logiweb \intro{vector} file as output. Vector files typically have extension .lgw; they are compact, binary files suited for transmission over the Internet. We shall refer to the contents of a vector file as a Logiweb \emph{vector}, regardless of whether the vector is retrieved from a file, retrieved over a network, or is passed in memory from the frontend.
The codifier takes a vector as input and produces a Logiweb \emph{rack} file as output. Rack files typically have extension .lgr; they are big, binary files suited for storing in the local file system. A file or memory structure which allows to look up racks given their references is termed a Logiweb \emph{cache}. We shall refer to the contents of a rack file as a Logiweb \emph{rack}, regardless of how it is retrieved.
The renderer takes a rack as input and produces a Logiweb \emph{rendering} as output. A rendering is a directory structure of interlinked html, pdf, and other files suited for viewing in a browser.
Any Logiweb compiler must contain a frontend, a codifier, and a renderer. Furthermore, the codifier of any Logiweb compiler must do exactly as the codifier defined in the following. The frontend and renderer, however, are replacable.
As an example, one may design a language different from the Logiweb frontend language and write a frontend for that language. One could even imagine a wysiwyg Logiweb frontend. The only requirement to a frontend is that it must produce a Logiweb vector as output.
Any backend should take a Logiweb rack as input and produce some sort of rendering as output.
\subsection{File extensions}
The Logiweb compiler uses the following file extensions:
\begin{description}
\item[.lgw] Logiweb vector (lgw for LoGiWeb).
\item[.lgs] Logiweb source text (s for source).
\item[.lgr] Logiweb rack (r for rack).
\item[.lgu] Indirection (u for URL).
\item[.lgp] Reserved for Logiweb reference (p for pointer).
\item[.lgo] Reserved for link to Logiweb rendering (o for output).
\end{description}
\subsection{Files}
\begin{description}
\item[/etc/logiweb/lgc.conf] Default location for site configuration file.
\item[\$HOME/.logiweb/lgc.conf] Default location for a user configuration file.
\item[\$HOME/.logiweb/logiweb/] Default location for output and racks.
\item[/usr/bin/lgwam] Default location of Logiweb abstract machine.
\item[/usr/bin/lgc] Default location of Logiweb compiler.
\item[/usr/man] Default location of Logiweb man pages.
\end{description}
\subsection{General functions}
\begin{statements}
\item "[[ macro define late define u as v end define as eager define u as v end define end define ]]"
\end{statements}
\subsection{Event handling functions}
\begin{statements}
\item "[[ late define lgc-get-events ( s ) as s [[ !"events" ]] end define ]]"
Return the list of events to be executed. The list is in reverse order.
\item "[[ late define lgc-set-events ( s , E ) as s [[ !"events" -> E ]] end define ]]"
Set the list of events to the list "[[ E ]]".
\item "[[ late define lgc-clr-events ( s ) as lgc-set-events ( s , true ) end define ]]"
Clear the list of events.
\item "[[ late define lgc-push-event ( s , e ) as push ( s , !"events" , e ) end define ]]"
Add an event "[[ e ]]" to list "[[ E ]]" of events. When executed, "[[ e ]]" will be executed after the events in "[[ E ]]".
\item "[[ late define lgc-do-events ( s ) as newline reverse ( lgc-get-events ( s ) ) end define ]]"
Push the event "[[ e ]]" onto the list of events, then reverse the list.
\item "[[ macro define lgc-exec ( f ) as exec request ( true , map ( \ s . \ x . f ) apply lgc-clr-events ( s ) maptag ) end define ]]"
Forge an execute event for invoking "[[ f ]]".
\item "[[ macro define lgc-exec-events ( s , f ) as newline
reverse ( lgc-exec ( f ) :: lgc-get-events ( s ) ) end define ]]"
Push an execute event onto the list of events, then reverse the list.
\end{statements}
\subsection{State machine}
At top level, lgc is a state machine with the following states:
\begin{verbatim}
lgc-config-1 ( x )
lgc-config-2 ( x , s )
lgc-config-3 ( x , s )
lgc-config-5 ( x , s )
lgc-config-6 ( x , s )
lgc-lex-2 ( x , s )
lgc-lex-3 ( x , s )
lgc-include-2 ( x , s )
lgc-load-receive ( x , s )
lgc-load-codify1 ( x , s )
lgc-grammar1 ( x , s )
lgc-parse1 ( x , s )
lgc-parse-codify-lgw ( x , s )
lgc-parse6 ( x , s )
lgc-render ( x , s )
lgc-render-verify ( x , s )
lgc-render-dump ( x , s )
lgc-render-user ( x , s )
lgc-render-user2 ( x , s )
\end{verbatim}
\subsection{Machine}
The following statements define the main program of the Logiweb compiler.
\begin{statements}
\item "[[ define "execute" of "lgc" as newline
<< lgc-main ,, !"siteconfig=/etc/logiweb/lgc.conf" >> end define ]]"
Dump the lgc compiler "[[ lgc-main ]]" under the name of lgc and with the given compiled in default.
\item "[[ late define lgc-main as map ( \ x . lgc-config-1 ( x ) ) end define ]]"
As the first activity, read configuration files.
\end{statements}
\subsection{State entries}
During parameter processing, the compiler builds up a state "[[ s ]]" with the following entries:
\begin{itemize}
\item "[[ s [[ !"init" ]] ]]" The initial event containing command line arguments, environment variables, and compiled in defaults.
\item "[[ s [[ !"nsiteconfig" ]] ]]" Tilde expanded name of site configuration file.
\item "[[ s [[ !"siteconfig" ]] ]]" Contents of site configuration file (if any).
\item "[[ s [[ !"nuserconfig" ]] ]]" Tilde expanded name of user configuration file.
\item "[[ s [[ !"userconfig" ]] ]]" Contents of user configuration file (if any).
\item "[[ s [[ !"parameters" ]] ]]" Array of all parameters.
\item "[[ s [[ !"verbose" ]] ]]" Verbosity level as a cardinal.
\end{itemize}
The array "[[ t ]]" of parameters has the following entries:
\begin{itemize}
\item "[[ t [[ !"source" ]] ]]" The name of the source file.
\item "[[ t [[ !"leap" ]] ]]" List of leap second definitions like "[[ !"GRD-1972-06-30+1" ]]" which indicates that Gregorian Date 1972-06-30 ended with one, positive leap second.
\item "[[ t [[ !"siteconfig" ]] ]]" The location of the site configuration file. Not tilde-expanded.
\item "[[ t [[ !"userconfig" ]] ]]" The location of the user configuration file. Not tilde-expanded.
\item "[[ t [[ !"path" ]] ]]" List of locations to look up pages given their reference. As an example, an item of value "[[ !"~/.logiweb/logiweb/:/rack.lgr" ]]" makes the compiler look in the given location under the users home catalog where the rightmost colon is replaced by the reference of the page in mixed endian hexadecimal.
\item "[[ t [[ !"namepath" ]] ]]" List of locations to look up pages given their name. The rightmost colon is replaced by the name of the referenced page when translating "[[ !""-""!""!R" ]]" directives.
\item "[[ t [[ !"rendering" ]] ]]" Location of rendering output and racks. The rightmost colon is replaced by the reference of the page.
\item "[[ t [[ !"link" ]] ]]" List of locations for storing links to the rendering output. The rightmost colon is replaced by the name of the source file of the page (with the suffix removed, if any).
\item "[[ t [[ !"newline" ]] ]]" Host newline (CR, LF, CRLF, or LFCR).
\item "[[ t [[ !"script" ]] ]]" Script headline.
\item "[[ t [[ !"verbose" ]] ]]" Verbosity, c.f.\ Section \ref{sec:VerbosityLevels}.
\item "[[ t [[ !"options" ]] ]]" When unequal to "[[ !"no" ]]": print the values of all options to standard output and exit.
\item "[[ t [[ !"help" ]] ]]" When unequal to "[[ !"no" ]]": print help message one to standard output and exit.
\item "[[ t [[ !"help2" ]] ]]" When unequal to "[[ !"no" ]]": print help message two to standard output and exit.
\item "[[ t [[ !"help3" ]] ]]" When unequal to "[[ !"no" ]]": print help message three to standard output and exit.
\item "[[ t [[ !"version" ]] ]]" When unequal to "[[ !"no" ]]": print version to standard output and exit.
\item "[[ t [[ !"license" ]] ]]" When unequal to "[[ !"no" ]]": print license information to standard output and exit.
\end{itemize}
\subsection{Reading of configuration files}
Parameters are collected from the following sources:
\begin{itemize}
\item Static defaults which are defined in the code.
\item Dynamic defaults like siteconfig=/etc/logiweb/lgc.conf which are in the code but are easy to modify after compilation.
\item Parameters defined in a site configuration file.
\item Parameters defined in a user configuration file.
\item Environment variables.
\item Command line parameters.
\end{itemize}
The sources are shown in order of precedence in the sense that command line parameters override environment variables and so on.
All parameters can be defined in any of the locations. As an exception, however, the location of the site configuration file cannot be defined in the site or user configuration file and the location of the user configuration file cannot be defined in the user configuration file.
Each parameter is equal to a list of values. Environment variables merely allow to define one-element lists. All other sources allow to set a parameter to a one-element list or to add an element at the beginning or end of the current list.
Configuration files may contain lines like the following:
\begin{verbatim}
# This is a comment
uuu=xxx
vvv+yyy
www++zzz
\end{verbatim}
The lines above set parameter uuu to xxx, adds yyy in front of vvv, and adds zzz at the end of www.
Command lines may contain arguments like the following:
\begin{verbatim}
--uuu=xxx --vvv+yyy --www++zzz
\end{verbatim}
The command line arguments above have the same effect as the configuration file above.
Command line parameters typically have short forms. If u, v, and w are the short forms of uuu, vvv, and www, then one may write:
\begin{verbatim}
-u xxx +v yyy ++w zzz
\end{verbatim}
The two given command lines have the same effect.
Configuration files and command line parametes are processed in the reading direction. As an example, \verb/--vvv+aaa --vvv+bbb/ adds first aaa and then bbb in front of vvv so that bbb ends up occurring in front of aaa.
Dynamic defaults, environment variables, and command line arguments are embedded in the parameter "[[ x ]]" of "[[ lgc-config-1 ( x ) ]]" below. The functions in the following read in the site and user configuration files.
\begin{statements}
\item "[[ late define lgc-config-1 ( x ) as newline
let s = true [[ !"init" -> x ]] in newline
let t = lgc-process-parameters ( s ) in newline
let n = lgc-tilde-expand ( t [[ !"siteconfig" ]] head , t ) in newline
let s = s [[ !"nsiteconfig" -> n ]] in << tight newline
extend request ( lgcio ( true ) , lgcio-interface ) ,, tight newline
unixTime ,, tight newline
fileGetCwd ,, tight newline
fileType ( n ) ,, tight newline
lgc-exec ( lgc-config-2 ( x , s ) ) >> end define ]]"
Ask if the site configuration file exists.
\item "[[ late define lgc-config-2 ( x , s ) as newline
let << true ,, << true ,, f >> ,, << true ,, d >> ,, u >> = x in newline
let s = s [[ !"time" -> parse-unixTime ( u ) ]] in newline
let s = s [[ !"cwd" -> d ]] in newline
if f head != 258 then lgc-config-4 ( s ) else << tight newline
fileRead ( s [[ !"nsiteconfig" ]] ) ,, tight newline
lgc-exec ( lgc-config-3 ( x , s ) ) >> end define ]]"
If the site configuration file exists, read it. Otherwise, pretend that the site configuration file is empty and go on to the user configuration file.
\item "[[ late define lgc-config-3 ( x , s ) as newline
let << true ,, << true ,, f >> >> = x in newline
let s = s [[ !"siteconfig" -> f ]] in lgc-config-4 ( s ) end define ]]"
Store the site configuration file and go on to the user configuration file.
\item "[[ late define lgc-config-4 ( s ) as newline
let t = lgc-process-parameters ( s ) in newline
let n = lgc-tilde-expand ( t [[ !"userconfig" ]] head , t ) in newline
let s = s [[ !"nuserconfig" -> n ]] in << tight newline
fileType ( n ) ,, tight newline
lgc-exec ( lgc-config-5 ( x , s ) ) >> end define ]]"
Ask if the user configuration file exists.
\item "[[ late define lgc-config-5 ( x , s ) as newline
let << true ,, << true ,, f >> >> = x in newline
if f head != 258 then lgc-process-query ( s ) else << tight newline
fileRead ( s [[ !"nuserconfig" ]] ) ,, tight newline
lgc-exec ( lgc-config-6 ( x , s ) ) >> end define ]]"
If the user configuration file exists, read it. Otherwise, pretend that the user configuration file is empty and go on to query processing.
\item "[[ late define lgc-config-6 ( x , s ) as newline
let << true ,, << true ,, f >> >> = x in newline
let s = s [[ !"userconfig" -> f ]] in lgc-process-query ( s ) end define ]]"
Store the site configuration file and go on to query processing.
\item "[[ late define lgc-tilde-expand ( n , t ) as newline
if n then true else newline
if vector-head ( n ) != "~" - NULL then n else newline
t [[ "home" ]] :: vector-tail ( n ) end define ]]"
\end{statements}
\subsection{Parameter processing}
This section defines "[[ lgc-process-parameters ( s ) ]]" which converts a state "[[ s ]]" into an array which maps parameter names to parameter value lists. The state "[[ s ]]" must contain an initial event and may contain a site and a user configuration file.
\begin{statements}
\item "[[ late define lgc-default-leap as << tight newline
!"GRD-2008-12-31+1" ,, tight newline
!"GRD-2005-12-31+1" ,, tight newline
!"GRD-1998-12-31+1" ,, tight newline
!"GRD-1997-06-30+1" ,, tight newline
!"GRD-1995-12-31+1" ,, tight newline
!"GRD-1994-06-30+1" ,, tight newline
!"GRD-1993-06-30+1" ,, tight newline
!"GRD-1992-06-30+1" ,, tight newline
!"GRD-1990-12-31+1" ,, tight newline
!"GRD-1989-12-31+1" ,, tight newline
!"GRD-1987-12-31+1" ,, tight newline
!"GRD-1985-06-30+1" ,, tight newline
!"GRD-1983-06-30+1" ,, tight newline
!"GRD-1982-06-30+1" ,, tight newline
!"GRD-1981-06-30+1" ,, tight newline
!"GRD-1979-12-31+1" ,, tight newline
!"GRD-1978-12-31+1" ,, tight newline
!"GRD-1977-12-31+1" ,, tight newline
!"GRD-1976-12-31+1" ,, tight newline
!"GRD-1975-12-31+1" ,, tight newline
!"GRD-1974-12-31+1" ,, tight newline
!"GRD-1973-12-31+1" ,, tight newline
!"GRD-1972-12-31+1" ,, tight newline
!"GRD-1972-06-30+1" >> end define ]]"
Define the static default for the leap parameter.
\item "[[ late define lgc-default-options as true [[ newline
!"leap" -> lgc-default-leap ]] [[ newline
!"siteconfig" -> << !"/etc/logiweb/lgc.conf" >> ]] [[ newline
!"userconfig" -> << !"~/.logiweb/lgc.conf" >> ]] [[ newline
!"path" -> << !"~/.logiweb/logiweb/:/rack.lgr" >> ]] [[ newline
!"rendering" -> << !"~/.logiweb/logiweb/:/" >> ]] [[ newline
!"link" -> << !":" ,, !"~/.logiweb/name/:" >> ]] [[ newline
!"namepath" -> << !"~/.logiweb/name/:/rack.lgr" >> ]] [[ newline
!"newline" -> << !"LF" >> ]] [[ newline
!"script" -> << !"#!/usr/bin/lgwam script" >> ]] [[ newline
!"verbose" -> << !"3" >> ]] [[ newline
!"options" -> << !"no" >> ]] [[ newline
!"help" -> << !"no" >> ]] [[ newline
!"help2" -> << !"no" >> ]] [[ newline
!"help3" -> << !"no" >> ]] [[ newline
!"version" -> << !"no" >> ]] [[ newline
!"license" -> << !"no" >> ]] end define ]]"
Define static defaults for all parameters.
\item "[[ late define lgc-process-argv ( v , t ) as newline
if v atom then t else newline
let a :: v = v in newline
if vector-prefix ( a , 2 ) = !"--" then newline
let l = lgc-process-long-argv ( vector-suffix ( a , 2 ) , t ) in newline
lgc-process-argv ( v , l ) else newline
if vector-prefix ( a , 1 ) = !"-" then newline
lgc-process-short-argv ( vector-suffix ( a , 1 ) , !"-" , v , t ) else newline
if vector-prefix ( a , 2 ) = !"++" then newline
lgc-process-short-argv ( vector-suffix ( a , 2 ) , !"++" , v , t ) else newline
if vector-prefix ( a , 1 ) = !"+" then newline
lgc-process-short-argv ( vector-suffix ( a , 1 ) , !"+" , v , t ) else newline
lgc-process-argv ( v , t [[ !"source" -> << a >> ]] ) end define ]]"
Process the command line arguments given in "[[ v ]]" and add them to the array "[[ t ]]". It is assumed that "[[ v ]]" is the list of genuine arguments, i.e.\ that argv[0] has been removed.
\item "[[ late define lgc-argv-short-to-long as true [[ newline
!"s" -> !"siteconfig" ]] [[ newline
!"u" -> !"userconfig" ]] [[ newline
!"p" -> !"path" ]] [[ newline
!"n" -> !"namepath" ]] [[ newline
!"r" -> !"rendering" ]] [[ newline
!"l" -> !"link" ]] [[ newline
!"o" -> !"option" ]] [[ newline
!"h" -> !"help" ]] [[ newline
!"H" -> !"help2" ]] [[ newline
!"v" -> !"verbose" ]] end define ]]"
Define the mapping from short to long parameter names. Internally in the compiler, parameters are always stored under their long name.
\item "[[ late define lgc-process-short-argv ( k , a , v , t ) as newline
let k prime = lgc-argv-short-to-long [[ k ]] in newline
let k = if k prime != true then k prime else k in newline
lgc-process-argv ( v tail , lgc-process-assignment ( k , a , v head , t ) ) end define ]]"
Process the short option with keyword "[[ k ]]" and assignment operator "[[ a ]]". The assignment operator may be \verb/-/ or \verb/+/ or \verb/++/.
\item "[[ late define lgc-split-long-arg ( a ) as newline
if a = !"". then 0 else newline
if vector-prefix ( a , 1 ) = !"=" then 0 else newline
if vector-prefix ( a , 1 ) = !"+" then 0 else newline
1 + lgc-split-long-arg ( vector-suffix ( a , 1 ) ) end define ]]"
Return the index of the end of the keyword. This is not very efficient, but who cares about efficiency in command line handling?
\item "[[ late define lgc-argv-downcase ( a ) as newline
bt2vector ( lgc-argv-downcase1 ( vector2byte* ( a ) ) ) end define ]]"
Change A..Z to a..z in the vector "[[ a ]]".
\item "[[ late define lgc-argv-downcase1 ( a ) as newline
if a atom then true else
let c :: a = a in newline
lgc-argv-downcase2 ( c ) :: lgc-argv-downcase1 ( a ) end define ]]"
Change A..Z to a..z in the list "[[ a ]]" of bytes.
\item "[[ late define lgc-argv-downcase2 ( c ) as newline
if c < !"A" - NULL then c else newline
if c > !"Z" - NULL then c else newline
c - !"A" + !"a" end define ]]"
Change A..Z to a..z.
\item "[[ late define lgc-process-long-argv ( a , t ) as newline
let p = lgc-split-long-arg ( a ) in newline
let k = lgc-argv-downcase ( vector-prefix ( a , p ) ) in newline
let a = vector-suffix ( a , p ) in newline
if a = !"". then t [[ k -> true ]] else newline
if vector-prefix ( a , 1 ) = !"=" then newline
lgc-process-assignment ( k , !"-" , vector-suffix ( a , 1 ) , t ) else newline
if vector-prefix ( a , 2 ) = !"++" then newline
lgc-process-assignment ( k , !"++" , vector-suffix ( a , 2 ) , t ) else newline
lgc-process-assignment ( k , !"+" , vector-suffix ( a , 1 ) , t ) end define ]]"
Split the long option a into a keyword, an assignment operator, and a value and execute the associated assignement.
Process the long option "[[ a ]]".
\item "[[ late define lgc-process-assignment ( k , a , v , t ) as newline
if a = !"-" then t [[ k -> << v >> ]] else newline
if a = !"+" then t [[ k -> v :: t [[ k ]] ]] else newline
t [[ k -> append ( t [[ k ]] , << v >> ) ]] end define ]]"
Assign the value "[[ v ]]" to the keyword "[[ k ]]" in the array "[[ t ]]" using the assignment operator "[[ a ]]".
\item "[[ late define lgc-process-env ( v , t ) as newline
if v atom then t else newline
let a :: v = v in newline
if lgc-argv-downcase ( vector-prefix ( a , 4 ) ) = !"lgc_" then newline
let l = lgc-process-long-argv ( vector-suffix ( a , 4 ) , t ) in newline
lgc-process-env ( v , l ) else newline
if lgc-argv-downcase ( vector-prefix ( a , 5 ) ) = !"home=" then newline
lgc-process-env ( v , lgc-process-long-argv ( a , t ) ) else newline
lgc-process-env ( v , t ) end define ]]"
Process the environment arguments given in "[[ v ]]" and add them to the array "[[ t ]]".
\item "[[ late define lgc-process-lines ( l , t ) as newline
if l atom then t else
let a :: l = l in newline
let t = lgc-process-long-argv ( a , t ) in newline
lgc-process-lines ( l , t ) end define ]]"
Process the configuration file lines given in "[[ v ]]" and add them to the array "[[ t ]]". It is assumed that "[[ v ]]" is the list of genuine lines, i.e.\ that empty lines and comment lines have been removed.
\item "[[ late define lgc-file2lines ( f ) as lgc-file2lines1 ( f , true , true ) end define ]]"
Convert the list "[[ f ]]" of singleton strings into a list of lines, removing empty lines and comment lines.
\item "[[ late define lgc-file2lines1 ( f , l , b ) as newline
if f atom then reverse ( lgc-add-line-to-lines ( b , l ) ) else newline
let c :: f = f in newline
if c = LF .or. c = CR then newline
lgc-file2lines1 ( f , lgc-add-line-to-lines ( b , l ) , true ) else newline
lgc-file2lines1 ( f , l , c :: b ) end define ]]"
Convert the list "[[ f ]]" of singleton strings into a list of lines. Lines are accumulated in "[[ l ]]" in reverse order. Characters of each line are buffered in "[[ b ]]" in reverse order.
\item "[[ late define lgc-add-line-to-lines ( b , l ) as newline
let b = reverse ( b ) in newline
if b atom then l else newline
if b head = !"#" then l else newline
vt2vector ( b ) :: l end define ]]"
Add the line "[[ b ]]" to the list "[[ l ]]" of lines unless "[[ b ]]" is empty or starts with a hash-mark.
\item "[[ late define lgc-process-file ( f , t ) as newline
let l = lgc-file2lines ( f ) in newline
lgc-process-lines ( l , t ) end define ]]"
Process the configuration file "[[ f ]]" and add parameter values to the array "[[ t ]]".
\item "[[ late define lgc-process-parameters ( s ) as newline
let << << true ,, true :: a ,, e ,, c ,, d >> >> = s [[ !"init" ]] in newline
let t = lgc-default-options in newline
let t = lgc-process-lines ( d , t ) in newline
let u = t [[ !"siteconfig" ]] in newline
let t = lgc-process-file ( s [[ !"siteconfig" ]] , t ) in newline
let v = t [[ !"userconfig" ]] in newline
let t = lgc-process-file ( s [[ !"userconfig" ]] , t ) in newline
let t = t [[ !"siteconfig" -> u ]] [[ !"userconfig" -> v ]] in newline
let t = lgc-process-env ( e , t ) in newline
let t = lgc-process-argv ( a , t ) in newline
t end define ]]"
Process parameters from all sources in "[[ s ]]".
\end{statements}
\subsection{Query processing}
Some invokations of the lgc compiler queries information rather than asking for compilation. The query options are \verb/help/, \verb/help2/, \verb/help3/, \verb/version/, \verb/license/, \verb/options/, and \verb/option/. The three first give preset responses, the fourth lists all options, and the last lists the value of a particular option.
\begin{statements}
\item "[[ late define lgc-help as newline
!"
Logiweb compiler (lgc)
Usage: lgc [option]... [source]
--siteconfig or -s Location of site configuration file
--userconfig or -u Location of user configuration file
--path or -p Path for searching pages by reference
--namepath or -n Path for searching pages by name
--rendering or -r Location of rendering output
--link or -l Location of links to rendering output
--verbose or -v Set verbosity
--option or -o Print given option and exit
--help or -h Print present message and exit
--help2 or -H Print help on further options
Examples:
--path=x or -p x Set path to singleton x
--path+x or +p x Add x in front of path
--path++x or ++p x Add x at end of path
--path Set path to the empty list
" end define ]]"
\item "[[ late define lgc-help2 as newline
!"
Logiweb compiler (lgc)
Usage: lgc [option]... [source]
--help or -h Print help on frequently used options
--help2 or -H Print present message
--help3 Print help on config file options
--version Print version
--license Print license
--options Print all options
--source E.g. 'lgc --source=X' is like 'lgc X'
" end define ]]"
\item "[[ late define lgc-help3 as newline
!"
Logiweb compiler (lgc)
Usage: lgc [option]... [source]
--help or -h Print help on frequently used options
--help2 or -H Print help on further options
--help3 Print present message and exit
The following options typically occur (without hyphens) in
configuration files or at the end of scripts.
--leap Location of leap seconds
--newline Host newline (CR, LF, CRLF, or LFCR)
--script Script headline (e.g. #!/usr/bin/lgwam script)
" end define ]]"
\item "[[ late define lgc-version as newline
!"Logiweb compiler (lgc) version " :: !""$version" end define ]]"
\item "[[ late define lgc-license as newline
!"Logiweb, a system for electronic distribution of mathematics
Copyright (C) 2004-2009 Klaus Grue
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed IN the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 021111307 USA
Contact: Klaus Grue, DIKU, Universitetsparken 1, DK2100 Copenhagen,
Denmark, grue@diku.dk, http://logiweb.eu/, http://www.diku.dk/~grue/
Logiweb is a system for distribution of mathematical definitions,
lemmas, and proofs. For more on Logiweb, consult http://logiweb.eu/.
" end define ]]"
\item "[[ late define lgc-options ( t ) as newline
<< write request ( reverse ( lgc-options1 ( t , true ) ) ) >> end define ]]"
Print all options in the array "[[ t ]]" (in peculiar order).
\item "[[ late define lgc-options1 ( t , r ) as newline
if t atom then r else newline
if t head intp then lgc-options2 ( t tail , LF :: t head :: r ) else newline
lgc-options1 ( t head , lgc-options1 ( t tail , r ) ) end define ]]"
Add options in "[[ t ]]" to the list "[[ r ]]".
\item "[[ late define lgc-options2 ( t , r ) as newline
if t atom then r else newline
lgc-options2 ( t tail , LF :: t head :: !" " :: r ) end define ]]"
Add list "[[ t ]]" of values to "[[ r ]]".
\item "[[ late define lgc-option ( s ) as newline
if s atom then true else newline
writeln request ( s head ) :: lgc-option ( s tail ) end define ]]"
\item "[[ late define lgc-process-query ( s ) as newline
let t = lgc-process-parameters ( s ) in newline
let s = s [[ !"parameters" -> t ]] in newline
if t [[ !"help" ]] head != !"no" then << write request ( lgc-help ) >> else newline
if t [[ !"help2" ]] head != !"no" then << write request ( lgc-help2 ) >> else newline
if t [[ !"help3" ]] head != !"no" then << write request ( lgc-help3 ) >> else newline
if t [[ !"version" ]] head != !"no" then << write request ( lgc-version ) >> else newline
if t [[ !"license" ]] head != !"no" then << write request ( lgc-license ) >> else newline
if t [[ !"options" ]] head != !"no" then lgc-options ( t ) else newline
let o = t [[ !"option" ]] head in newline
if o != true .and. o != !"". then lgc-option ( t [[ o ]] ) else newline
let v = t [[ !"verbose" ]] head in newline
let e :: v = lgc-atoi ( vector2vector* ( v ) , true ) catch in newline
if e then << writeln request ( !"Invalid verbosity" ) >> else newline
let s = s [[ !"verbose" -> v ]] in newline
let e :: s = lgc-process-leap ( s ) catch in newline
if e then << writeln request ( s ) >> else newline
let s = s [[ !"time" -> lgc-unix2lgt ( s [[ !"time" ]] , s ) ]] in newline
lgc-lex-1 ( s ) end define ]]"
\end{statements}
" ]"\section{Time functions}"[ "
\subsection{Time schemes}
Logiweb uses the following time schemes
\begin{verbatim}
TAI International atomic time
UTC Universal coordinated time
MJD Modified Julian day
GRD Gregorian date
LGT Logiweb time
\end{verbatim}
\subsection{TAI}
TAI (International atomic time) is a 'paper' clock in the sense that it is a computed average of lots of real, atomic clocks located all over the world.
TAI counts seconds, minutes, and hours as regularly as possible. Each TAI day has 24 TAI hours, each TAI hour has 60 TAI minutes, and each TAI minute has 60 TAI seconds. Each TAI second is, as closely as possible, one SI second. An SI second is the duration of 9 192 631 770 periods of the radiation corresponding to the transition between the two hyperfine levels of the ground state of the caesium 133 atom.
TAI time is independent of the rotation of planet Earth.
In Logiweb, TAI hour hh, minute mm, and second ss is written TAI:hh:mm:ss. We have "[[ 00 <= h,h <= 23 ]]", "[[ 00 <= m,m < 59 ]]", and "[[ 00 <= s,s <= 59 ]]". Occasionally, we shall use TAI:24:00:00 to denote TAI:00:00:00 on the following day. Decimal fractions of a second are written after a dot as in TAI:12:23:34.456 which denotes 0.456 seconds past TAI:12:23:34. The notation is compatible with ISO 8601 except that we prepend ``TAI:'' to emphasize the use of International Atomic Time.
\subsection{UTC}
UTC is a combination of TAI and yet another time scale named UT1.
UT1 is a measure of the rotation angle of planet Earth relative to the direction from the Earth to the Sun. Each UT1 day has 24 UT1 hours, each UT1 hour has 60 UT1 minutes, and each UT1 minute has 60 UT1 seconds. It is noon in UT1 when Greenwich is under the Sun. At the time of writing (February 2009), UT1 is around 34 seconds behind TAI. In 1972, UT1 was 10 seconds behind TAI.
As mentioned, UTC is a combination of TAI and UT1. UTC equals TAI plus a politically decided offset. This UTC offset indicates how much UTC lacks behind TAI. At the time of writing, the UTC offset is 34 seconds indicating that UTC is 34 seconds behind TAI.
At any time, the UTC offset is an integral number of seconds, but the UTC offset may be incremented or decremented by decree from the International Earth Rotation Service (IERS). Hence, UTC depends on TAI and IERS, but IERS has the intension to keep the difference between UTC and UT1 below 0.9 seconds, so UTC indirectly depends on UT1.
UTC makes a leap whenever IERS increments or decrements the UTC offset. Such leaps are implemented by irregular UTC minutes.
A regular UTC minute has 60 UTC seconds. An irregular one either has 59 or 61. Apart from that UTC counts like TAI and UT1: days have 24 hours and hours have 60 minutes.
Whenever IERS increments (decrements) the UTC offset, the last minute of the last hour of a particular UTC day has 61 (59) seconds. IERS intends to place irregular seconds at the end of June 30 and December 31 when necessary and intends to announce the leaps in advance.
In Logiweb, UTC hour hh, minute mm, and second ss is written UTC:\-hh:\-mm:\-ss. We have "[[ 00 <= h,h <= 23 ]]", "[[ 00 <= m,m < 59 ]]", and "[[ 00 <= s,s <= 60 ]]". Occasionally, we shall use UTC:24:00:00 to denote UTC:00:00:00 on the following day. Decimal fractions of a second are written after a dot as in UTC:12:23:34.456 which denotes 0.456 seconds past UTC:12:23:34. The notation is compatible with ISO 8601 except that we prepend ``UTC:'' to emphasize the use of Universal Coordinated Time.
At the time of writing, IERS has never decremented the UTC offset, but has incremented the UTC offset at the end of the following days:
\begin{verbatim}
GRD-1972-06-30
GRD-1972-12-31
GRD-1973-12-31
GRD-1974-12-31
GRD-1975-12-31
GRD-1976-12-31
GRD-1977-12-31
GRD-1978-12-31
GRD-1979-12-31
GRD-1981-06-30
GRD-1982-06-30
GRD-1983-06-30
GRD-1985-06-30
GRD-1987-12-31
GRD-1989-12-31
GRD-1990-12-31
GRD-1992-06-30
GRD-1993-06-30
GRD-1994-06-30
GRD-1995-12-31
GRD-1997-06-30
GRD-1998-12-31
GRD-2005-12-31
GRD-2008-12-31
\end{verbatim}
Before GRD-1972-06-03, the UTC offset was 10 seconds.
\subsection{MJD}
MJD (Modified Julian Day) is a scheme for counting days in a completely regular fasion. Each day is simply expressed by the number of days since a particular day.
MJD is a regular and reliable day count used by astronomers. Furthermore, it is politically correct in the sense that, even though Julius Caeser was quite controversial in his own time, few people today are offended by a time scale named after him.
MJD is based on yet another time scale named JD (Julian Day). JD expresses the number of days since noon, January 1, year -4712 (year 4713 BC), in the Julian calender.
In ancient times, a day was measured from noon to noon, so people actually counted nights instead of days (as a reminiscence, a period of 14 days is still called a fortnight in the English tongue). Today, we prefer to step our day counters when the sun is on the other side of the planet, which is of course difficult to observe, but which possesses little problem for modern technology.
To get a day count based on JD which steps at midnight, the Modified Julian Day (MJD) is offset from JD by 2400000.5 days. In consequence, MJD counts the number of days since GRD-1858-11-17.
When we combine MJD with UTC, then MJD steps at UTC:00:00:00. When we combine MJD with TAI, then MJD steps at TAI:00:00:00. Hence, MJD/UTC and MJD/TAI are two different day counts, but at the time of writing they merely differ by 34 seconds.
In Logiweb, MJD day d is written MJD-d. As an example, GRD-1858-11-17 equals MJD-0 and MJD-51544 equals GRD-2000-01-01. The day before MJD-0 is named MJD--1 (i.e. Modified Julian Day hyphen minus one). The notation follows ISO 8601 in using a hyphen in connection with day counting, but is otherwise completely unrelated.
Combinations of day and second counting schemes are glued together with a dot. As an example, 0.456 seconds past TAI:12:23:34 on MJD-51544 is written MJD-51544.TAI:12:23:34.456. This follows ISO-8601 in putting the day before the second but does not follow the suggestion of ISO-8601 to separate day and second by a capital ``T''.
\subsection{GRD}
GRD (Gregorian Date) is a scheme for counting days in a fasion so complicated that it has taken millennia to screw it up. Furthermore, GRD is ``politically incorrect'' in that it counts days, not after ``Jesus'', but after ``The Lord'' (Anno Domini), which is not completely neutral.
But GRD is widespread, and hence we use it in the Human-Computer-Interfaces of Logiweb. In Logiweb itself, GRD has no place.
In GRD, day 0 of a year is named ``January 1'', and day 100 is named April 11 (except if the year is divisible by 4, in which case it is named April 10 (except if the year is divisible by 100, in which case it is named April 11 (except if the year is divisible by 400, in which case it is named April 10))).
In Logiweb, Gregorian year Y, month MM, and day DD is written GRD-Y-MM-DD. We have "[[ 01 <= M,M <= 12 ]]" and "[[ 01 <= D,D <= 31 ]]". The notation is compatible with ISO 8601 except for the following: (1) We prepend ``GRD-'' to emphasize that we label days like the Gregorian calender does (GRD for GRegorian Date). (2) We allow the year to have more than four digits after year 9999 and to have less then four digits before year 1000. (3) We allow the year to be zero and negative. As examples, GRD-0-01-01 and GRD--5-01-01 are January 1 on year 1 BC and 6 BC, respectively.
When we combine GRD with UTC, then GRD steps at UTC:00:00:00. When we combine GRD with TAI, then GRD steps at TAI:00:00:00. Hence, GRD/UTC and GRD/TAI are two different day counts, but at the time of writing they merely differ by 34 seconds.
Combinations of day and second counting schemes are glued together with a dot. As an example, 0.456 seconds past UTC:12:23:34 on GRD-2000-01-01 is written GRD-2000-01-01.UTC:12:23:34.456. This follows ISO-8601 in putting the day before the second but does not follow the suggestion of ISO-8601 to separate day and second by a capital ``T''.
\subsection{LGT}
LGT (Logiweb time) is the number of TAI seconds since MJD-0.TAI:00:00:00.
Logiweb time is expressed on the form $ M \cdot 10 ^ {-E} $ where M is an integer and E is a cardinal (i.e. a non-negative integer). In Logiweb, M is always non-negative, so one could as well say that M is a cardinal.
Logiweb time $ M \cdot 10 ^ {-E} $ is written LGW-Me-E. As an example,
\begin{quote}
LGW-1083564821686603e-6
\end{quote}
\noindent equals
\begin{quote}
GRD.2004-05-03.UTC:06:13:41.686603.
\end{quote}
\noindent The e-E may be replaced by the following decadic suffixes:
\addvspace{\abovedisplayskip}
\begin{tabular}{|r|c|l|}
\hline
Exponent & Suffix & Name \\
\hline
e-0 & U & unit \\
e-3 & m & milli \\
e-6 & u & micro \\
e-6 & $\mu$ & micro \\
e-9 & n & nano \\
e-12 & p & pico \\
e-15 & f & femto \\
e-18 & a & atto \\
e-21 & z & zepto \\
e-24 & y & yocto \\
\hline
\end{tabular}
\addvspace{\belowdisplayskip}
In a Logiweb time like LGW-1083564821686603e-6 one should not replace the small e by a capital one as that may cause confusion with the decadic suffix E (Exa) which stands for $10^{15}$.
By the way note the following: Logiweb is a computational system intended for mathematics. In physics, one uses decadic prefixes that glue in front of physical units. In computing systems it is better to use decadic suffixes that glue behind numbers. When needed, Logiweb uses the SI units meter, kilogram, second, etc., and derived units. As an example, font sizes are measured in meters. A font size of twelve typographic points is 4218u, and a printer with a resolution of 600 dots per 0.0254 meters has a distance between pixels of 42.333u. An area of 1m by 1m (one milli meter by one milli meter) is $1m^2$ (one square milli) or 1u (one micro) or 1e-6 measured in the derived SI unit of square meters. A weight of 1m is one gram (one milli kilogram). A weight of 1um is one microgram (one micro milli kilogram). This is almost but not completely different from the use of decadic prefixes in the SI system.
\subsection{GUTC}
When presenting Logiweb time to a user, we use GRD/UTC which we shall refer to as GUTC. This section describes GUTC in more detail than the individual sections on GRD and UTC.
GUTC is irregular compared to Logiweb time in that it occasionally includes leap seconds and, furthermore, it counts days in a rather complicated (Gregorian) manner, which includes leap days.
GUTC is built up from the following cycles:
\textbf{GUTC second.} The length of a GUTC second is one SI second (which equals one TAI and one UTC second). Each GUTC second starts at the 'tick' of the TAI 'paper' clock. As an example, the duration from UTC:00:00:00 to UTC:00:00:01 on GRD-2000-03-01 (March 1, year 2000) is a GUTC second.
\textbf{GUTC minute.} Regular GUTC minutes consist of 60 GUTC seconds. Irregular GUTC minutes consist of 61 or 59 GUTC seconds. As an example, the duration from UTC:00:00:00 to UTC:00.01.00 on GRD-2000-03-01 is a regular GUTC minute.
\textbf{GUTC hour.} Regular GUTC hours consist of 60 regular GUTC minutes. Irregular GUTC hours consist of 59 regular GUTC minutes followed by one irregular GUTC minute. As an example, the duration from UTC:00:00:00 to UTC:01.00.00 on GRD-2000-03-01 is a regular GUTC hour.
\textbf{GUTC day.} Regular GUTC days consist of 24 regular GUTC hours. Irregular GUTC days consist of 23 regular GUTC hours followed by one irregular GUTC hour. As an example, the duration from GRD-2000-03-01.UTC:00:00:00 to GRD-2000-03-02.UTC:00.00.00 is a regular GUTC day.
\textbf{Long GUTC month.} A long GUTC month consists of 31 GUTC days. As an example, the duration from GRD-2000-03-01.UTC:00:00:00 to GRD-2000-04-01.UTC:00.00.00 (i.e. March) is a long GUTC month.
\textbf{Short GUTC month.} A short GUTC month consists of 30 GUTC days. As an example, the duration from GRD-2000-04-01.UTC:00:00:00 to GRD-2000-05-01.UTC:00.00.00 (i.e. April) is a short GUTC month.
\textbf{GUTC dimester.} A GUTC dimester (dimester = two months, compare trimester = tres menses = three months and semester = sex menses = six months) consists of a long GUTC month followed by a short one. As an example, the duration from March to April (inclusive) is a GUTC dimester.
\textbf{GUTC quimester.} A GUTC quimester (quimester = five months) consists of a long, a short, a long, a short, and a long GUTC month. In other words, a quimester consists of two regular dimesters followed by an irregular one that ends abruptly at the end of the quimester. As an example, the duration from March to July (inclusive) is a GUTC quimester. The duration from August to December is another quimester.
\textbf{GUTC Roman year.} A GUTC Roman year is the duration from March 1, inclusive, to the following March 1, exclusive. A regular Roman year has 365 GUTC days; an irregular one has one more. As an example, the period from GRD-2000-03-01.UTC:00:00:00 to GRD-2001-02-28.UTC:24:00:00 is GUTC Roman year 2000, which is regular. In contrast, the Gregorian year 2000 is a leap year and, hence, irregular. The difference arises because the Gregorian and Roman years have newyear before and after the leap day, respectively.
A GUTC Roman year consists of three quimesters, the third of which ends abruptly at the end of the year. As a consequence of the conventions mentioned until now, the last month of a regular GUTC Roman year (February) gets 28 GUTC days, and all the other months gets 30 and 31 GUTC days in the pattern prescribed by the Gregorian calender.
\textbf{GUTC olympiad.} A regular GUTC olympiad consists of three regular GUTC years followed by an irregular one. An irregular GUTC olympiad consists of four regular GUTC years. As an example, the period from March 1, 2000 to February 29, 2004 is a regular GUTC olympiad.
\textbf{GUTC century.} A regular GUTC century consists of 24 regular GUTC olympiads followed by an irregular one. An irregular GUTC olympiad consists of 25 regular GUTC olympiads. As an example, March 1, 2000 to February 28, 2100 is a regular GUTC century.
\textbf{GUTC Gregorian cycle.} A GUTC Gregorian cycle consists of three regular GUTC centuries followed by an irregular one.
The rules above allow to convert from LGT to GUTC and back provided one knows the location of leap seconds. A historical 'Roman year' just had 10 months, starting around vernal equinox. 10 month after vernal equinox, the romans stopped counting days and just waited for the next vernal equinox. The dimester/quimester structure described above is accidental. For the sake of Roman political correctness, the month of August (named after Augustus) was extended to 31 days to make it as long as July (named af Julius).
\subsection{Unix time}
Unix time counts seconds since GRD-1970-01-01.UTC:00:00:00. Each Unix day has 24 Unix hours, each Unix hour has 60 Unix minutes, and each Unix minute has 60 Unix seconds. Most Unix seconds are approximately equal to one SI second.
Each Unix computer maintains its own Unix time and each Unix computer tries to keep its Unix time in sync with UTC. When Unix time on a computer lacks behind UTC then the Unix system on that computer makes the Unix clock on that computer run faster until Unix time on that computer catches up with UTC. Likewise, the Unix system slows down the Unix clock when needed.
Whenever UTC makes a leap, Unix time makes a stumble. As an example, Unix time may stumble as follows: Whenever UTC makes a positive leap, Unix time counts at half speed for two seconds. In that way Unix time keeps in sync with UTC in the long run.
As can be seen, a Unix second is not always an SI second. Exactly how Unix time stumbles depends on the version of Unix used.
One can get a poor mans TAI from Unix time by stumbling backwards. That is what the Logiweb compiler does. When converting Unix time to poor mans TAI we assume that each Unix second corresponds to one TAI second except when a leap second occurs. Whenever UTC makes a positive leap, we assume that Unix time counts at half speed from UTC:23:59:59 to UTC:23:59:61 (UTC:23:59:61 equals UTC:00:00:00 on the next day). Whenever UTC makes a negative leap, we assume that Unix time counts at double speed from UTC:23:59:58 to UTC:23:59:59 (UTC:23:59:59 equals UTC:00:00:00 on the next day).
\subsection{The purpose of time stamps}
One purpose of using time stamps in Logiweb is as follows: A Logiweb reference includes a RIPEMD code. If the reference contained nothing else, there would be a theoretical limit of $ 2 ^ {160} $ Logiweb pages. Since Logiweb is supposed, among other, to support mathematical logic, it is convenient to have no upper limit on the number of pages, not even a theoretical one. Otherwise, one could fear pathological metatheorems stating that certain theorems fail because their proof would require more then $ 2 ^ {160} $ Logiweb pages to prove. As a countermeasure, Logiweb references include a timestamp. To ensure that there is not even an upper limit on the number of pages that can be published per second, the timestamp comprises an exponent and a mantissa, allowing time stamps to have arbitrary resolution.
Another reason for including a time stamp in Logiweb references is thus: RIPEMD ensures that the probability of generating two Logiweb pages with the same reference is negligible. However, adding a timestamp makes it possible to recover even in the theoretical situation where two pages get the same RIPEMD code anyway: One may simply resubmit both pages so that they get new time stamps.
A third reason for including a time stamp is thus: Logiweb pages are allowed to reference each other, but the pages and references are required to form a directed, acyclic graph. That can be enforced by a rule stating that a page is only allowed to reference pages which are older according to their time stamp. That is not used in the present version of Logiweb because RIPEMD is trusted to ensure acyclicity: If one tries to produce two Logiweb pages which reference each other, then the RIPEMD codes of each page will depend on the RIPEMD code of the other, making it infeasible to construct those codes.
Accidentally, the time stamps turned out to be quite convenient when rendering pages using \LaTeX: Some {\TeX} and {\LaTeX} styles need the date and time of submission to generate a front page, and that date and time may conveniently be taken from the time stamp of the page.
\subsection{Parameter names}
In the time conversion functions we use variable names thus:
\begin{tabular}{ll}
"[[ S ]]" & state \\
"[[ g ]]" & Gregorian cycle (400 year period) \\
"[[ c ]]" & Century (100 year period) \\
"[[ o ]]" & Olympiad (4 year period) \\
"[[ Y ]]" & Year \\
"[[ q ]]" & Quimester (5 month period) \\
"[[ d ]]" & Dimester (2 month period) \\
"[[ M ]]" & Month \\
"[[ D ]]" & Day \\
"[[ h ]]" & Hour \\
"[[ m ]]" & minute \\
"[[ s ]]" & second \\
"[[ f ]]" & fraction \\
"[[ e ]]" & exponent \\
"[[ G ]]" & Gregorian date "[[ << Y ,, M ,, D >> ]]" \\
\end{tabular}
\subsection{Constants}
\begin{statements}
\item "[[ late define lgc-seconds-per-minute as 60 end define ]]"
\item "[[ late define lgc-seconds-per-day as 24 * 60 * 60 end define ]]"
\item "[[ late define lgc-minutes-per-hour as 60 end define ]]"
\item "[[ late define lgc-hours-per-day as 24 end define ]]"
\item "[[ late define lgc-days-per-month as 31 end define ]]"
\item "[[ late define lgc-days-per-dimester as 31 + 30 end define ]]"
\item "[[ late define lgc-days-per-quimester as 31 + 30 + 31 + 30 + 31 end define ]]"
\item "[[ late define lgc-days-per-year as 365 end define ]]"
\item "[[ late define lgc-days-per-olympiad as 4 * lgc-days-per-year + 1 end define ]]"
\item "[[ late define lgc-days-per-century as 25 * lgc-days-per-olympiad - 1 end define ]]"
\item "[[ late define lgc-days-per-Gregorian as 4 * lgc-days-per-century + 1 end define ]]"
\item "[[ late define lgc-months-per-dimester as 2 end define ]]"
\item "[[ late define lgc-months-per-quimester as 5 end define ]]"
\item "[[ late define lgc-months-per-year as 12 end define ]]"
\item "[[ late define lgc-month-of-march as 3 end define ]]"
March is month number three in the Gregorian calender.
\item "[[ late define lgc-years-per-olympiad as 4 end define ]]"
\item "[[ late define lgc-years-per-century as 100 end define ]]"
\item "[[ late define lgc-years-per-Gregorian as 400 end define ]]"
This is the length of the Gregorian cycle.
\item "[[ late define lgc-grd-of-mjd0 as << 1858 ,, 11 ,, 17 >> end define ]]"
\end{statements}
\subsection{Conversion from GRD to MJD}
\begin{statements}
\item "[[ late define lgc-grd2day ( G ) as newline
let << Y ,, M ,, D >> = G in newline
let M = lgc-months-per-year * Y + M - lgc-month-of-march in newline
let Y :: M = floor ( M , lgc-months-per-year ) in newline
let g = Y div lgc-years-per-Gregorian in newline
let c = Y div lgc-years-per-century in newline
let o = Y div lgc-years-per-olympiad in newline
let q :: M prime = floor ( M , lgc-months-per-quimester ) in newline
let d = M prime div lgc-months-per-dimester in newline
D - 1 + M * lgc-days-per-month + Y * lgc-days-per-year - d - q * 2 + o - c + g end define ]]"
Compute the number of days since GRD-0-03-01 as follows:
Destruct the Gregorian date "[[ G ]]" into year "[[ Y ]]", month "[[ M ]]", and day "[[ D ]]". Then compute the month count "[[ M ]]" since March, Gregorian year zero (recall that year zero is the one before year one, i.e.\ year one BC).
Then compute the Roman year "[[ Y ]]" and month "[[ M ]]". We count from zero so that March is month zero. The Romans counted March as month one. So October is month eight according to the Romans (Octo=8) but we count it as month seven.
After that, compute the day count "[[ D ]]" since GRD-0-03-01 as a linear combination of day, month, and year, and adjust for variations in the length of months and years.
\item "[[ late define lgc-day-of-mjd0 as lgc-grd2day ( lgc-grd-of-mjd0 ) end define ]]"
\item "[[ late define lgc-grd2mjd ( G ) as lgc-grd2day ( G ) - lgc-day-of-mjd0 end define ]]"
\end{statements}
\subsection{Conversion from MJD to GRD}
\begin{statements}
\item "[[ late define lgc-limited-floor ( x , y , l ) as newline
let R = floor ( x , y ) in newline
if R head < l then R else l :: x - l * y end define ]]"
Compute "[[ q :: r ]]" such that "[[ x = q * y + r ]]". The quotient "[[ q ]]" is the one computed by "[[ x div y ]]" except that it cannot exceed "[[ l ]]".
\item "[[ late define lgc-mjd-of-grd-0-03-01 as lgc-grd2mjd ( << 0 ,, 3 ,, 1 >> ) end define ]]"
\item "[[ late define lgc-mjd2grd ( D ) as newline
let D = D - lgc-mjd-of-grd-0-03-01 in newline
let g :: D = floor ( D , lgc-days-per-Gregorian ) in newline
let c :: D = lgc-limited-floor ( D , lgc-days-per-century , 3 ) in newline
let o :: D = floor ( D , lgc-days-per-olympiad ) in newline
let Y :: D = lgc-limited-floor ( D , lgc-days-per-year , 3 ) in newline
let q :: D = floor ( D , lgc-days-per-quimester ) in newline
let d :: D = floor ( D , lgc-days-per-dimester ) in newline
let M :: D = floor ( D , lgc-days-per-month ) in newline
let M = M + d * lgc-months-per-dimester in newline
let M = M + q * lgc-months-per-quimester in newline
let Y = Y + o * lgc-years-per-olympiad in newline
let Y = Y + c * lgc-years-per-century in newline
let Y = Y + g * lgc-years-per-Gregorian in newline
let M = M + Y * lgc-months-per-year in newline
let Y :: M = floor ( M + lgc-month-of-march - 1 , lgc-months-per-year ) in newline
<< Y ,, M + 1 ,, D + 1 >> end define ]]"
Convert the Modified Julian Day "[[ D ]]" to a Gregorian date "[[ << Y ,, M ,, D >> ]]" as follows:
First compute the number of days "[[ D ]]" since GRD-0-03-01 (March 1, Gregorian year zero). Then convert "[[ D ]]" into a number of Gregorian cycles "[[ g ]]", centuries "[[ c ]]", olympiads "[[ o ]]", years "[[ Y ]]", quimesters "[[ q ]]", dimesters "[[ d ]]", months "[[ M ]]", and days "[[ D ]]". Then combine "[[ g ]]", "[[ c ]]", "[[ o ]]", and "[[ Y ]]" into the number of years "[[ Y ]]" since GRD-0-03-01, and convert "[[ q ]]", "[[ d ]]", and "[[ M ]]" into the number of months that have elapsed on top of those years. Then combine "[[ M ]]" and "[[ Y ]]" into the number of months "[[ M ]]" since GRD-0-03-01. Then move newyear to GRD-0-01-01 and convert back to year "[[ Y ]]" and month "[[ M ]]". Finally construct the Gregorian date, taking into acount that month and day count from one.
\end{statements}
\subsection{GRD Parsing functions}
\begin{statements}
\item "[[ late define lgc-parse-prefix ( p , a ) as newline
if p atom then a else newline
if p head != a head then exception else newline
lgc-parse-prefix ( p tail , a tail ) end define ]]"
\item "[[ late define lgc-prefix-grd as vt2vector* ( !"GRD-" ) end define ]]"
\item "[[ late define lgc-prefix-hyphen as vt2vector* ( !"-" ) end define ]]"
\item "[[ late define lgc-parse-leap ( a ) as newline
let a = lgc-parse-prefix ( lgc-prefix-grd , a ) in newline
let Y :: a = lgc-parse-int ( a ) in newline
let a = lgc-parse-prefix ( lgc-prefix-hyphen , a ) in newline
let M :: a = lgc-parse-int ( a ) in newline
let a = lgc-parse-prefix ( lgc-prefix-hyphen , a ) in newline
let D :: a = lgc-parse-int ( a ) in newline
let D = lgc-grd2mjd ( << Y ,, M ,, D >> ) in newline
let c :: a = a in newline
let l :: a = lgc-parse-int ( a ) in newline
if a pairp then exception else newline
if c = !"+" then D :: l else newline
if c = !"-" then D :: - l else exception end define ]]"
\item "[[ late define lgc-convert-leap ( L ) as newline
if L atom then true else newline
let a :: L = L in newline
let e :: v = lgc-parse-leap ( vt2vector* ( a ) ) catch in newline
if e then ( !"Invalid leap: " :: a ) raise else newline
v :: lgc-convert-leap ( L ) end define ]]"
Convert the list L of leap seconds on external form to a list of leap seconds on form "[[ << D :: U prime ,, ... >> ]]" where "[[ D ]]" is the day in MJD and "[[ U prime ]]" is the change in UTC lack (the amount UTC lacks behind TAI). The change "[[ U prime ]]" in UTC lack is +1 for a positive leap, -1 for a negative one.
\item "[[ late define lgc-check-leap ( L ) as newline
if L tail atom then true else newline
if L head head <= L tail head head then newline
!"Leap seconds must be stated in descending order" raise else newline
lgc-check-leap ( L tail ) end define ]]"
Check that the dates of leap seconds are stated in descending order.
\item "[[ late define lgc-initial-leap as 10 end define ]]"
The UTC lack before the first announced leap second.
\item "[[ late define lgc-add-leap ( L ) as newline
lgc-add-leap1 ( reverse ( L ) , lgc-initial-leap , true ) end define ]]"
Convert the list "[[ L ]]" on form "[[ << D :: U prime ,, ... >> ]]" into a list of form "[[ U :: << s :: U prime ,, ... >> ]]". The value of "[[ U prime ]]" is unchanged. The value of "[[ U ]]" is the UTC lack after all the leap seconds have occurred. The value of "[[ s ]]" is the end of the day "[[ D ]]" expressed in TAI. The ``end of the day'' equals the beginning of the next day, i.e.\ the point in time where the leap ends. Note that negative leaps are instantaneous so that they begin and end on the same moment whereas positive leaps have a duration of one second.
\item "[[ late define lgc-add-leap1 ( L , U , r ) as newline
if L atom then U :: r else newline
let ( D :: U prime ) :: L = L in newline
let U = U + U prime in newline
let s = ( D + 1 ) * lgc-seconds-per-day + U in newline
lgc-add-leap1 ( L , U , ( s :: U prime ) :: r ) end define ]]"
\item "[[ late define lgc-process-leap ( s ) as newline
let L = s [[ !"parameters" ]] [[ !"leap" ]] in newline
let L = lgc-convert-leap ( L ) in newline
lgc-check-leap ( L ) .then. newline
let L = lgc-add-leap ( L ) in newline
s [[ !"leap" -> L ]] end define ]]"
Extract the leap parameter from the state "[[ s ]]", convert it, and store it back into "[[ s [[ !"leap" ]] ]]".
\end{statements}
\subsection{Conversion from reference to Logiweb time}
The "[[ lgc-ref2lgt ( r ) ]]" function returns the time stamp of the Logiweb reference "[[ r ]]" expressed in Logiweb time.
\begin{statements}
\item "[[ late define lgc-ref2lgt ( r ) as newline
let r = vt2vector* ( r ) in newline
let v :: r = r in newline
if v != lgc-ref-version then lgc-panic ( !"Internal error: Wrong version" ) else newline
let r = list-suffix ( r , 20 ) in newline
let f :: r = parse-card ( r ) in newline
let e :: r = parse-card ( r ) in newline
<< f ,, e >> end define ]]"
\end{statements}
\subsection{Conversion from Logiweb time to printed representation}
"[[ lgc-lgt2vt ( s ) ]]" converts the Logiweb time "[[ s = << f ,, e >> ]]" to a human readable Logiweb time. The return value is a vector tree.
\begin{statements}
\item "[[ late define lgc-lgt2vt ( s ) as newline
let << f ,, e >> = s in newline
let f = lgc-itoa ( f ) in newline
let e = lgc-itoa ( e ) in newline
<< !"LGT-" ,, f ,, !"e-" ,, e >> end define ]]"
\end{statements}
\subsection{Conversion from Logiweb time to MJD/TAI}
"[[ lgc-lgt2mjdtai ( s ) ]]" converts the Logiweb time "[[ s = << f ,, e >> ]]" to MJD and TAI on form "[[ << D ,, h ,, m ,, s ,, f ,, e >> ]]"
"[[ lgc-lgt2mjdtai2vt ( s ) ]]" converts the Logiweb time "[[ s ]]" to human readable MJD and TAI. The return value is a vector tree.
The conversion is trivial because LGT expresses the number of TAI seconds since MJD-0.TAI:0:0:0 and since MJD, TAI, and LGT are all completely regular time counting schemes.
\begin{statements}
\item "[[ late define lgc-lgt2mjdtai ( s ) as newline
let << f ,, e >> = s in newline
let s :: f = floor ( f , exp10 ( e ) ) in newline
let m :: s = floor ( s , lgc-seconds-per-minute ) in newline
let h :: m = floor ( m , lgc-minutes-per-hour ) in newline
let D :: h = floor ( h , lgc-hours-per-day ) in newline
<< D ,, h ,, m ,, s ,, f ,, e >> end define ]]"
\item "[[ late define lgc-lgt2mjdtai2vt ( s ) as newline
let << D ,, h ,, m ,, s ,, f ,, e >> = lgc-lgt2mjdtai ( s ) in newline
let D = lgc-itoa ( D ) in newline
let h = lgc-ctoa ( h , 2 ) in newline
let m = lgc-ctoa ( m , 2 ) in newline
let s = lgc-ctoa ( s , 2 ) in newline
let f = if e = 0 then true else !"." :: lgc-ctoa ( f , e ) in newline
<< !"MJD-" ,, D ,, !".TAI:" ,, h ,, !":" ,, m ,, !":" ,, s ,, f >> end define ]]"
\end{statements}
\subsection{Conversion from Logiweb time to GRD/UTC}
At any time, UTC is a UTC lack "[[ U ]]" behind TAI. In 1972, the UTC lack "[[ U ]]" was 10 seconds.
We represent the location of leap seconds by a list "[[ L ]]" of form "[[ << s prime :: U prime ,, ... >> ]]" where "[[ s prime ]]" indicates the end time of a UTC leap expressed in LGT and "[[ U prime ]]" is "[[ + 1 ]]" for a positive leap and "[[ - 1 ]]" for a negative leap. At any time "[[ T ]]", the UTC lack equals 10 seconds plus the values of "[[ U prime ]]" for all leap seconds before "[[ T ]]".
The "[[ lgc-lgt2utc ( s , U , L ) ]]" function takes Logiweb second "[[ s ]]", a leap second list "[[ L ]]", and a UTC lack "[[ U ]]" as input. The UTC lack "[[ U ]]" must be the lack that applies after the last leap second in "[[ L ]]".
The "[[ lgc-lgt2utc ( s , U , L ) ]]" returns a pair "[[ u :: l ]]". The value of "[[ l ]]" is zero in case a positive UTC leap is in progress. At UTC time "[[ << h ,, m ,, s >> ]]" of MJD "[[ D ]]", the value of "[[ u ]]" is "[[ ( ( D * 24 + h ) * 60 + m ) * 60 + s - l ]]". We shall refer to "[[ u ]]" and "[[ l ]]" as the UTC second and leap, respectively.
As time progresses, the UTC second "[[ u ]]" is incremented once per second except when a leap second occurs. When a negative leap occurs, "[[ u ]]" is incremented twice instead of once. When a positive leap occurs, "[[ u ]]" has the same value of two consecutive seconds. During the second of those two seconds, the UTC leap equals "[[ 1 ]]".
"[[ lgc-lgt2grdutc2vt ( s , S ) ]]" expresses the Logiweb second "[[ s ]]" in human readable form using GRD and UTC. The return value is a vector tree. The "[[ lgc-lgt2grdutc2vt ( s , S ) ]]" function gets the UTC lack "[[ U ]]" and leap second list "[[ L ]]" from the state "[[ S ]]" and uses "[[ lgc-lgt2utc ( s , U , L ) ]]" to convert to UTC second "[[ u ]]" and leap "[[ l ]]". Then it converts "[[ u ]]" into year "[[ Y ]]", month "[[ M ]]", day "[[ D ]]", hour "[[ h ]]", minute "[[ m ]]", and second "[[ s ]]", and finally adds "[[ l ]]" to "[[ s ]]".
"[[ lgc-lgt2grdutc2v ( s , S ) ]]" is like "[[ lgc-lgt2grdutc2vt ( s , S ) ]]" except that it returns a vector.
\begin{statements}
\item "[[ late define lgc-lgt2utc ( s , U , L ) as newline
if L atom then s - U :: 0 else newline
let ( s prime :: U prime ) :: L = L in newline
if s prime <= s then s - U :: 0 else newline
if s prime <= s + U prime then s - U :: U prime else newline
lgc-lgt2utc ( s , U - U prime , L ) end define ]]"
\item "[[ late define lgc-lgt2grdutc ( s , S ) as newline
let U :: L = S [[ !"leap" ]] in newline
let << f ,, e >> = s in newline
let s :: f = floor ( f , exp10 ( e ) ) in newline
let s :: l = lgc-lgt2utc ( s , U , L ) in newline
let m :: s = floor ( s , lgc-seconds-per-minute ) in newline
let h :: m = floor ( m , lgc-minutes-per-hour ) in newline
let D :: h = floor ( h , lgc-hours-per-day ) in newline
let << Y ,, M ,, D >> = lgc-mjd2grd ( D ) in newline
<< Y ,, M ,, D ,, h ,, m ,, s + l ,, f ,, e >> end define ]]"
\item "[[ late define lgc-lgt2grdutc2vt ( s , S ) as newline
let << Y ,, M ,, D ,, h ,, m ,, s ,, f ,, e >> = lgc-lgt2grdutc ( s , S ) in newline
let Y = lgc-itoa ( Y ) in newline
let M = lgc-ctoa ( M , 2 ) in newline
let D = lgc-ctoa ( D , 2 ) in newline
let h = lgc-ctoa ( h , 2 ) in newline
let m = lgc-ctoa ( m , 2 ) in newline
let s = lgc-ctoa ( s , 2 ) in newline
let f = if e = 0 then true else !"." :: lgc-ctoa ( f , e ) in newline
<< !"GRD-" ,, Y ,, !"-" ,, M ,, !"-" ,, D ,, !".UTC:" ,, h ,, !":" ,, m ,, !":" ,, s ,, f >> end define ]]"
\item "[[ late define lgc-lgt2grdutc2v ( s , S ) as newline
vt2vector ( lgc-lgt2grdutc2vt ( s , S ) ) end define ]]"
\end{statements}
\subsection{Conversion from Unix time to Logiweb time}
As mentioned, the Logiweb compiler generates a poor mans TAI from Unix time. More specificially, the function below converts from Unix time to Logiweb time:
\begin{statements}
\item "[[ late define lgc-unix2lgt ( s , S ) as newline
let U :: L = S [[ !"leap" ]] in newline
let << f ,, e >> = s in newline
let E = exp10 ( e ) in newline
let s :: f = floor ( f , E ) in newline
let s = s + lgc-lgt-of-unix + U in newline
lgc-unix2lgt1 ( s , f , E , e , L ) end define ]]"
\item "[[ late define lgc-lgt-of-unix as lgc-grd2mjd ( << 1970 ,, 1 ,, 1 >> ) * lgc-seconds-per-day end define ]]"
"[[ lgc-lgt-of-unix ]]" indicates the begining of the TAI day containing the Unix epoch. More precisely, "[[ lgc-lgt-of-unix ]]" indicates 10 seconds before the Unix epoch since the UTC lack was 10 seconds in 1970.
\item "[[ late define lgc-unix2lgt1 ( s , f , E , e , L ) as newline
if L atom then << s * E + f ,, e >> else newline
let ( s prime :: U prime ) :: L = L in newline
let d = s - s prime in newline
if 0 <= d then << s * E + f ,, e >> else newline
if 0 <= d - 2 * U prime then << ( 10 * s prime + 5 * d ) * E + 5 * f ,, e + 1 >> else newline
if 0 <= d + U prime then << ( s prime + 2 * d ) * E + 2 * f ,, e >> else newline
lgc-unix2lgt1 ( s - U prime , f , E , e , L ) end define ]]"
\end{statements}
" ]"\section{Message generation}"[ "
This section defines functions for generation of error messages, warnings, and progress messages.
\subsection{State entries}
Message generation uses the following entries of the state:
\begin{itemize}
\item "[[ s [[ !"continue" ]] ]]" Set to false when error detected.
\item "[[ s [[ !"msg" ]] ]]" Stack of "[[ p :: m ]]" pairs where "[[ p ]]" is a position in the source file and "[[ m ]]" is a message to be printed.
\end{itemize}
\subsection{Integer input/output}
The "[[ lgc-atoi ( a , m ) ]]" function converts the sequence "[[ a ]]" of singleton strings to an integer. The function throws "[[ m ]]" in case of error.
The "[[ lgc-itoa ( i ) ]]" function converts the integer "[[ i ]]" to a list of singleton strings.
The "[[ lgc-ctoa ( c , w ) ]]" function converts the cardinal "[[ c ]]" to a vector tree and pads with zeros in front to make the string at least "[[ w ]]" characters wide.
The "[[ lgc-parse-int ( a ) ]]" function parses the integer at the beginning of the list "[[ a ]]" of singlton strings and returns "[[ i :: a prime ]]" where "[[ i ]]" is the integer and "[[ a prime ]]" is the unparsed part of "[[ a ]]". "[[ lgc-parse-int ( a ) ]]" throws an exception if no integer is found.
\begin{statements}
\item "[[ late define lgc-parse-int ( a ) as newline
if a atom then exception else newline
if a head != !"-" then lgc-parse-int1 ( a , 0 ) else newline
if a tail atom then exception else newline
let n :: a = lgc-parse-int1 ( a tail , 0 ) in - n :: a end define ]]"
Parse integer at beginning of the string "[[ a ]]".
\item "[[ late define lgc-parse-int1 ( a , r ) as newline
if a atom then r :: true else newline
let c = a head in newline
if c < !"0" .or. c > !"9" then r :: a else newline
lgc-parse-int1 ( a tail , c - !"0" + Base * r ) end define ]]"
Parse integer at beginning of the string "[[ a ]]", accumulating the result in "[[ r ]]".
\item "[[ late define lgc-atoi ( a , m ) as newline
let e :: n :: a = lgc-parse-int ( a ) catch in newline
if e .or. a != true then m raise else n end define ]]"
Convert the singleton list "[[ a ]]" to an integer.
\item "[[ late define lgc-itoa ( i ) as newline
if i = 0 then << "0" >> else newline
if i > 0 then lgc-itoa1 ( i , true ) else "-" :: lgc-itoa1 ( - i , true ) end define ]]"
Convert the integer "[[ i ]]" to a singleton list.
\item "[[ late define lgc-itoa1 ( i , r ) as newline
if i = 0 then r else newline
let i :: m = floor ( i , Base ) in newline
lgc-itoa1 ( i , m + !"0" :: r ) end define ]]"
Convert the cardinal "[[ i ]]" to a singleton list, accumulating the result in "[[ r ]]".
\item "[[ late define lgc-ctoa ( c , w ) as newline
let c = lgc-itoa1 ( c , true ) in newline
repeat ( w - length ( c ) , !"0" ) :: c end define ]]"
Convert the cardinal "[[ c ]]" to an integer of width at least "[[ w ]]", prepending with zeros as needed.
\item "[[ late define lgc-ordinal-suffix ( n ) as newline
if ( n div Base ) mod Base = 1 then "th" else newline
let n = n mod Base in newline
if n = 1 then "st" else newline
if n = 2 then "nd" else newline
if n = 3 then "rd" else "th" end define ]]"
Return the suffix to append to a cardinal to form an ordinal (where `cardinal' and `ordinal' are from linguistics, not from mathematics).
\item "[[ late define lgc-ordinal ( n ) as newline
lgc-itoa ( n ) :: lgc-ordinal-suffix ( n ) end define ]]"
Convert the cardinal "[[ n ]]" to an ordinal expressed as a vector tree.
\end{statements}
\subsection{Verbosity levels}\label{sec:VerbosityLevels}
When invoking the Logiwab compiler, the user can set the verbosity level. The default verbosity level is 3. The levels are:
\begin{description}
\item[1] Print error messages
\item[2] Print warning messages
\item[3] Print progress information
\item[4] Print more progress information
\item[5] Print debugging information
\end{description}
Each message has a severity level "[[ l ]]" where small values of "[[ l ]]" indicate a high severity level. Messages are only sent to the user when "[[ l <= v ]]" where "[[ v ]]" is the verbosity.
Verbosity level zero is reserved for silent operation. The present compiler, however, does not allow suppression of error messages.
\subsection{Message stacking}
Messages are stored in "[[ s [[ !"msg" ]] ]]".
The function "[[ lgc-add-message ( s , l , p , m ) ]]" adds the message "[[ m ]]" with severity level "[[ l ]]" in the state "[[ s ]]" provided that "[[ l <= v ]]" where "[[ v ]]" is the verbosity.
The value of "[[ p ]]" in "[[ lgc-add-message ( s , l , p , m ) ]]" must indicate the position in the source file which gave rise to the message. The value of "[[ p ]]" must be the position given as a byte offset. As an example, "[[ p = 5 ]]" indicates the position between the fifth and sixth byte of the file. Note that "[[ p ]]" is a byte offset and not a character offset. Characters are supposed to be encoded in Logiweb Unicode UTF-8, so one character can take up several bytes. A position "[[ p ]]" can indicate a position inside a character. A negative value of "[[ p ]]" represents the end of the source file.
Once a message "[[ m ]]" is added to the state "[[ s ]]", then "[[ s ]]" should be raised as an exception in case the message is fatal, and "[[ s ]]" should be used as the new state otherwise. The "[[ lgc-add-message ( s , l , p , m ) ]]" adds at most ten messages to the state. When "[[ lgc-add-message ( s , l , p , m ) ]]" adds the tenth message, it raises the resulting state as an exception.
The function "[[ lgc-throw-message ( s , p , m ) ]]" is like "[[ lgc-add-message ( s , l , p , m ) ]]" but always throws and always sets the severity to one.
\begin{statements}
\item "[[ late define lgc-max-messages as 10 end define ]]"
Maximum number of messages allowed from one run of the compiler.
\item "[[ late define lgc-add-message ( s , l , p , m ) as newline
let s = if l > 1 then s else s [[ !"continue" -> false ]] in newline
if s [[ !"verbose" ]] < l then s else newline
let M = s [[ !"msg" ]] in newline
let M = ( p :: m ) :: M in newline
let s = s [[ !"msg" -> M ]] in newline
if length ( M ) >= lgc-max-messages then s raise else s end define ]]"
Add message "[[ m ]]" relating to position "[[ p ]]" in the source file to the state "[[ s ]]" provided the severity level "[[ l ]]" is less than or equal to the verbosity.
\item "[[ late define lgc-throw-message ( s , p , m ) as newline
lgc-add-message ( s , 1 , p , m ) raise end define ]]"
Like Add message but always throws and always sets the severity to one.
\end{statements}
\subsection{Message reporting}
The "[[ lgc-report-messages ( s ) ]]" function formats messages stacked in "[[ s ]]" and convert them to a list of output events.
\begin{statements}
\item "[[ late define lgc-die ( m ) as newline
<< writeln request ( m ) ,, quit request ( 1 ) >> end define ]]"
Die with last word "[[ m ]]".
\item "[[ late define lgc-report-messages ( s ) as newline
let M = s [[ !"msg" ]] in newline
if M then lgc-die ( !"Unhandled exception, goodbye." ) else newline
lgc-die ( lgc-report-messages1 ( s , M , !"Goodbye." ) ) end define ]]"
Extract messages from the state and convert them into a write request.
\item "[[ late define lgc-report-messages1 ( s , M , r ) as newline
if M atom then r else newline
let ( p :: m ) :: M = M in newline
if .not. p intp then lgc-panic ( !"Internal error: Error position is no int" ) else newline
lgc-report-messages1 ( s , M , lgc-report-messages2 ( s , p , m ) :: r ) end define ]]"
Format the list "[[ M ]]" of messages.
\item "[[ late define lgc-report-messages2 ( s , p , m ) as newline
let f = s [[ !"source" ]] in newline
let p = if p < 0 then length ( f ) else p in newline
let l :: c = lgc-position ( f , p , 1 , 1 ) in newline
let m _ { 1 } = !"Line " :: lgc-itoa ( l ) :: !" character " :: lgc-itoa ( c ) :: !":" in newline
let m _ { 2 } = lgc-report-message3 ( f , p ) in newline
!"---" :: LF :: m _ { 1 } :: LF :: m :: LF :: m _ { 2 } :: LF end define ]]"
Format the message "[[ m ]]" which relates to position "[[ p ]]" in the source file. Note that "[[ LF ]]" marks the end of a line regardless of the end-of-line convention of the underlying operating system.
\item "[[ late define lgc-position ( f , p , L , C ) as newline
if f atom .or. p = 0 then L :: C else newline
let c :: f = f in newline
if c = LF then lgc-position-1 ( CR , f , p - 1 , L + 1 , 1 ) else newline
if c = CR then lgc-position-1 ( LF , f , p - 1 , L + 1 , 1 ) else newline
if c = FF then lgc-position ( f , p - 1 , L + 1 , 1 ) else newline
if c = TAB then lgc-position ( f , p - 1 , L , C + 1 ) else newline
if c < SP then lgc-position ( f , p - 1 , L , C ) else newline
if lgc-char-start ( c ) then lgc-position ( f , p - 1 , L , C + 1 ) else newline
lgc-position ( f , p - 1 , L , C ) end define ]]"
Convert position "[[ p ]]" given as a byte offset to a pair "[[ L :: C ]]" of line number "[[ L ]]" and character number "[[ C ]]" assuming Logiweb Unicode UTF-8 encoding.
\item "[[ late define lgc-position-1 ( c , f , p , L , C ) as newline
if f head = c then lgc-position ( f tail , p , L , C ) else newline
lgc-position ( f , p , L , C ) end define ]]"
Same as above except that character "[[ c ]]" is ignored if it occurs at the beginning of "[[ f ]]".
\item "[[ late define lgc-max-lines as 3 end define ]]"
Maximum number of printed lines before and after error location.
\item "[[ late define lgc-max-chars as 80 end define ]]"
Maximum number of characters per line before and after error location.
\item "[[ late define lgc-report-message3 ( f , p ) as newline
let h :: t = lgc-split ( f , p , true ) in newline
let h = lgc-size-limit ( h , lgc-max-lines , lgc-max-chars , true ) in newline
let h = if h head = LF then h tail else h in newline
let t = lgc-size-limit ( t , lgc-max-lines , lgc-max-chars , true ) in newline
h :: !"|" :: LF :: !"---" :: LF :: reverse ( t tail ) end define ]]"
Split the file "[[ f ]]" into head "[[ h ]]" and tail "[[ t ]]" at position "[[ p ]]". Then limit head and tail to at most "[[ lgc-max-lines ]]" lines of at most "[[ lgc-max-chars ]]" characters each. Then glue the head and tail together.
\item "[[ late define lgc-split ( f , p , r ) as newline
if p = 0 .or. f atom then lgc-split1 ( r , f ) else lgc-split ( f tail , p - 1 , f head :: r ) end define ]]"
Pass "[[ p ]]" bytes from "[[ f ]]" to "[[ r ]]". Then call "[[ lgc-split1 ( r , f ) ]]" to move back to last character boundary.
\item "[[ late define lgc-split1 ( h , t ) as newline
if h atom .or. t atom .or. lgc-char-start ( t head ) then h :: t else newline
lgc-split1 ( h tail , h head :: t ) end define ]]"
Move backwards until the tail "[[ t ]]" starts at a character boundary.
\item "[[ late define lgc-char-start1 as NULL + 128 end define ]]"
The value of the smallest UTF-8 byte which does not start a character.
\item "[[ late define lgc-char-start2 as NULL + 128 + 64 end define ]]"
One plus the value of the largest UTF-8 byte which does not start a character.
\item "[[ late define lgc-char-start ( c ) as c < lgc-char-start1 .or. lgc-char-start2 <= c end define ]]"
True if the character "[[ c ]]" marks the start of a UTF-8 character (also true if "[[ c ]]" is illegal in UTF-8).
\item "[[ late define lgc-size-limit ( f , L , C , r ) as newline
if f atom .or. L = 0 then r else newline
if C = 0 then lgc-size-limit ( f , L - 1 , lgc-max-chars , r ) else newline
let c :: f = f in newline
let r = c :: r in newline
if c = LF then lgc-size-limit ( f , L - 1 , lgc-max-chars , r ) else newline
if lgc-char-start ( c ) then lgc-size-limit ( f , L , C - 1 , r ) else newline
lgc-size-limit ( f , L , C , r ) end define ]]"
\end{statements}
\subsection{Unconditional errors}
The "[[ lgc-error ( s , p , a ) ]]" function formats the message "[[ a ]]" and converts it to a list of output events. The "[[ lgc-simple-error ( a , s ) ]]" function does the same except that it simply outputs "[[ a ]]" and exits.
\begin{statements}
\item "[[ late define lgc-error ( s , p , a ) as newline
let e :: s = lgc-add-message ( s , 1 , p , a ) catch in newline
lgc-report-messages ( s ) end define ]]"
\item "[[ late define lgc-simple-error ( a , s ) as newline
let s = lgc-progress ( a , 1 , s ) in newline
lgc-do-events ( s ) end define ]]"
\end{statements}
\subsection{Progress messages}
The "[[ lgc-progress ( a , l , s ) ]]" function prints the message "[[ a ]]" unless the verbosity is less than the level "[[ l ]]".
The "[[ lgc-print ( p , a ) ]]" function prints "[[ a ]]" if "[[ p ]]" is true. The "[[ lgc-print ( p , a ) ]]" function uses "[[ print ( a ) ]]" rather than constructing an event.
\begin{statements}
\item "[[ late define lgc-progress ( a , l , s ) as newline
if s [[ !"verbose" ]] < l then s else newline
lgc-push-event ( s , writeln request ( a ) ) end define ]]"
\item "[[ late define lgc-print ( p , a ) as newline
if p then print ( a :: LF ) else true end define ]]"
\end{statements}
" ]"\section{Lexical analysis}"[ "
The Logiweb frontend language has a static and a dynamic syntax. The static and dynamic syntax are treated during lexical analysis and parsing, respectively. We define the static syntax in the following.
We shall refer to a sequence of two or more double quote characters as a \emph{multiquote}. Multiquotes mark the start of an escape sequence in Logiweb source files.
We shall refer to a multiquote followed by a character or the end of the file as an \emph{escape sequence}. Hence, an escape sequence covers both the multiquote and the first character (if any) after the multiquote.
Escape sequences that may occur in the body of a page are:
\begin{verbatim}
""! Start of string (until double quote or ""!""!.)
""!""!- Start of string (until double quote or ""!""!.)
""!""!; Start of short comment (until end of line)
""!""!{ Start of long comment (until ""!""!})
""!""!# Binary include as string (until double quote or ""!""!.)
""!""!$ Text include as string (until double quote or ""!""!.)
""!""!P Page name (until end of line or ""!""!n)
""!""!R Reference (until end of line or ""!""!n)
""!""!D Definition (until ""!""!P, ""!""!R, ""!""!D or ""!""!B)
""!""!. The empty string (self-terminated)
""!""!S Page source as a string (self-terminated)
""!""!N Name definitions (self-terminated)
""!""!C Charge defintions (self-terminated)
\end{verbatim}
The number of double quote characters in the escape sequence closing a long comment must equal the number of double quote characters in the escape sequence opening the long comment. Apart from this, the number of double quote characters in an escape sequence is of no importance.
Some examples read:
\begin{verbatim}
""! this is a string ""!
""!""!- this is another string ""!
""! this is a third string ""!""!.
""!""!- this is a fourth string ""!""!.
""!""!; this is a short comment
""!""!{ this is a long comment ""!""!}
""!""!""!{ this is another long comment ""!""!""!}
""!""!""!{ this is a strange ""!""!}""!""!;""!""!{ long comment ""!""!""!}
""!""!#/this/is/a/binary/include""!
""!""!$/this/is/a/text/include""!
""!""!P qualifier 1 ""! qualifier 2 ""! this is a page name
""!""!R qualifier 3 ""! qualifier 4 ""! /this/is/a/reference
""!""!D 1.2.3 ""!""!; charge
""! + ""! ""!""!; production
""! - ""! ""!""!; another production
""!""!B ""!""!; body (end of definition)
""!""!. ""!""!; the empty string
""!""!S ""!""!; Page source as a string
""!""!N ""!""!; Name definitions
""!""!C ""!""!; Charge defintions
\end{verbatim}
Escapes that may occur inside a string:
\begin{verbatim}
""!""!- No character
""!""!! Double quote
""!""!f Form feed
""!""!n Line feed
""!""!r Carriage return
""!""!t Horizontal tab
""!""!x Characters given in hexadecimal (until period)
\end{verbatim}
Example:
\begin{verbatim}
""!AB""!""!-""!""!!""!""!x4344.""!""!!EF""!""!.
\end{verbatim}
The string above comprises the following eight characters:
\begin{verbatim}
AB""!CD""!DE
\end{verbatim}
Escapes that may occur inside the path name part of a reference:
\begin{verbatim}
""!""!! Double quote
\end{verbatim}
Having an escape in a reference is useful only in the unlikely event that a file name contains a double quote character.
Escapes that may occur inside a definition:
\begin{verbatim}
""!""!n Line feed
\end{verbatim}
The escape sequence above allows to define more than one production on one line.
\subsection{State entries}
During lexical analysis, the compiler builds up a state "[[ s ]]" with the following contents:
\begin{itemize}
\item "[[ s [[ !"source" ]] ]]" The raw source text.
\item "[[ s [[ !"sourcename" ]] ]]" The path of the source text (after tilde-expansion and optionally appending of .lgs).
\item "[[ s [[ !"body" ]] ]]" The body of a page after lexical analysis. The body is a list of items of form "[[ c :: p :: S ]]" where "[[ c ]]" is a character, "[[ p ]]" is the position of that character in the source file, and "[[ S ]]" is the structure (if any) initiated by that structure. As an example, if "[[ c ]]" is "[[ lgc-esc-# ]]" then "[[ S ]]" is the name of the file to be included.
\item "[[ s [[ !"page" ]] ]]" The page name after lexical analysis.
\item "[[ s [[ !"def" ]] ]]" The definitions after lexical analysis.
\item "[[ s [[ !"bib" ]] ]]" The bibliography after lexical analysis.
\item "[[ s [[ !"nincludes" ]] ]]" Stack of pairs "[[ p :: n ]]" where the values of "[[ n ]]" are names of include files and "[[ p ]]" are the positions in the source file where they occur.
\item "[[ s [[ !"includes" ]] ]]" Array mapping names of include files to their contents.
\end{itemize}
\subsection{Lexical analysis main functions}
The "[[ lgc-lex-1 ( s ) ]]" function requests the source file and "[[ lgc-lex-2 ( x , s ) ]]" processes it.
\begin{statements}
\item "[[ late define lgc-lex-1 ( s ) as newline
let n = s [[ !"parameters" ]] [[ !"source" ]] head in newline
if n then << writeln request ( !"No source file specified" ) >> else newline
let n = lgc-tilde-expand ( n , s [[ !"parameters" ]] ) in newline
let s = s [[ !"sourcename" -> n ]] in newline
let s = lgc-progress ( lgc-lgt2grdutc2vt ( s [[ !"time" ]] , s ) , 3 , s ) in newline
let s = lgc-progress ( !"Reading file:" :: n , 3 , s ) in newline
let s = lgc-push-event ( s , fileTypeRead ( n ) ) in newline
lgc-exec-events ( s , lgc-lex-2 ( x , s ) ) end define ]]"
Process query, if any. Else go on to compilation.
\item "[[ late define lgc-lex-2 ( x , s ) as newline
let << true ,, << true ,, t :: f >> >> = x in newline
if t = FileTypeRegular then lgc-lex-4 ( f , s ) else newline
let n = s [[ !"sourcename" ]] :: !".lgs" in newline
let s = s [[ !"sourcename" -> n ]] in newline
let s = lgc-progress ( !"Reading file:" :: n , 3 , s ) in newline
let s = lgc-push-event ( s , fileTypeRead ( n ) ) in newline
lgc-exec-events ( s , lgc-lex-3 ( x , s ) ) end define ]]"
\item "[[ late define lgc-lex-3 ( x , s ) as newline
let << true ,, << true ,, t :: f >> >> = x in newline
if t = FileTypeRegular then lgc-lex-4 ( f , s ) else newline
<< writeln request ( !"Source file not found" ) >> end define ]]"
\item "[[ late define lgc-lex-4 ( f , s ) as newline
let s = s [[ !"source" -> f ]] in newline
let e :: S = lgc-lex-source ( f , s ) catch in newline
if e then lgc-error ( s , S head , S tail ) else newline
lgc-include-1 ( S ) end define ]]"
Extract the source from the input event "[[ x ]]". Then prepare a state "[[ s ]]" for compilation.
\end{statements}
\subsection{Escape sequences}
We now define quantities like "[[ lgc-esc-# ]]" for representing escape sequences. We also define a value, "[[ lgc-EOF ]]", for representing the End Of File.
\begin{statements}
\item "[[ late define lgc-EOF as 0 end define ]]"
\item "[[ late define lgc-esc-. as !"." - NULL end define ]]"
\item "[[ late define lgc-esc-- as !"-" - NULL end define ]]"
\item "[[ late define lgc-esc-! as !"!" - NULL end define ]]"
\item "[[ late define lgc-esc-# as !"#" - NULL end define ]]"
\item "[[ late define lgc-esc-$ as !"$" - NULL end define ]]"
\item "[[ late define lgc-esc-left as !"[" - NULL end define ]]"
\item "[[ late define lgc-esc-right as !"]" - NULL end define ]]"
\item "[[ late define lgc-esc-brace as !"}" - NULL end define ]]"
\item "[[ late define lgc-esc-B as !"B" - NULL end define ]]"
\item "[[ late define lgc-esc-C as !"C" - NULL end define ]]"
\item "[[ late define lgc-esc-D as !"D" - NULL end define ]]"
\item "[[ late define lgc-esc-N as !"N" - NULL end define ]]"
\item "[[ late define lgc-esc-P as !"P" - NULL end define ]]"
\item "[[ late define lgc-esc-R as !"R" - NULL end define ]]"
\item "[[ late define lgc-esc-S as !"S" - NULL end define ]]"
\item "[[ late define lgc-esc-f as !"f" - NULL end define ]]"
\item "[[ late define lgc-esc-n as !"n" - NULL end define ]]"
\item "[[ late define lgc-esc-r as !"r" - NULL end define ]]"
\item "[[ late define lgc-esc-t as !"t" - NULL end define ]]"
\item "[[ late define lgc-esc-x as !"x" - NULL end define ]]"
\end{statements}
\subsection{Space trimming}
The following functions remove spaces at the beginning or end of a string. They can remove at most one space character at each end. The input and output are lists of singleton strings.
\begin{statements}
\item "[[ late define lgc-left-trim ( a ) as newline
if a head = SP then a tail else a end define ]]"
Remove leading space, if any.
\item "[[ late define lgc-right-trim ( a ) as newline
reverse ( lgc-left-trim ( reverse ( a ) ) ) end define ]]"
Remove trailing space, if any.
\item "[[ late define lgc-trim ( a ) as newline
lgc-right-trim ( lgc-left-trim ( a ) ) end define ]]"
Remove leading space, if any, and trailing space, if any.
\end{statements}
\subsection{Space contraction}
The following functions contracts multiple spaces into single space characters and possibly trims or reverses the strings. The input and output are lists of singleton strings.
\begin{statements}
\item "[[ late define lgc-reverse-contract ( a ) as newline
lgc-reverse-contract1 ( a , true ) end define ]]"
Contract multiple spaces in "[[ a ]]" to single spaces and reverse "[[ a ]]".
\item "[[ late define lgc-reverse-contract1 ( a , r ) as newline
if a atom then r else newline
let c :: a = a in newline
if c = SP .and. r head = SP then newline
lgc-reverse-contract1 ( a , r ) else newline
lgc-reverse-contract1 ( a , c :: r ) end define ]]"
Accumulate "[[ lgc-reverse-contract ( a ) ]]" in "[[ r ]]".
\item "[[ late define lgc-contract ( a ) as newline
reverse ( lgc-reverse-contract ( a ) ) end define ]]"
Contract multiple spaces in "[[ a ]]" to single spaces.
\item "[[ late define lgc-contract* ( a ) as newline
if a atom then true else newline
lgc-contract ( a head ) :: lgc-contract* ( a tail ) end define ]]"
Apply "[[ lgc-contract ( e ) ]]" in each element of the list "[[ a ]]".
\item "[[ late define lgc-trim-contract ( a ) as newline
lgc-left-trim ( reverse ( lgc-left-trim ( lgc-reverse-contract ( a ) ) ) ) end define ]]"
Contract multiple spaces in "[[ a ]]" to single spaces and remove leading and trailing spaces, if any.
\end{statements}
\subsection{Space trimming of text containing character positions}
The following functions remove spaces at the beginning or end of a string. They can remove at most one space character at each end. The input and output are lists of singleton strings.
\begin{statements}
\item "[[ late define lgc-lex-left-trim ( a ) as newline
if a head head = SP then lgc-lex-left-trim ( a tail ) else a end define ]]"
Remove leading spaces, if any.
\item "[[ late define lgc-lex-right-trim ( a ) as newline
reverse ( lgc-lex-left-trim ( reverse ( a ) ) ) end define ]]"
Remove trailing space, if any.
\item "[[ late define lgc-lex-trim ( a ) as newline
lgc-lex-right-trim ( lgc-lex-left-trim ( a ) ) end define ]]"
Remove leading space, if any, and trailing space, if any.
\end{statements}
\subsection{Space contraction of text containing character positions}
The following functions contracts multiple spaces into single space characters and possibly trims or reverses the strings. The input and output are lists of singleton strings.
\begin{statements}
\item "[[ late define lgc-lex-reverse-contract ( a ) as newline
lgc-lex-reverse-contract1 ( a , true ) end define ]]"
Contract multiple spaces in "[[ a ]]" to single spaces and reverse "[[ a ]]".
\item "[[ late define lgc-lex-reverse-contract1 ( a , r ) as newline
if a atom then r else newline
let c :: a = a in newline
if c head = SP .and. r head head = SP then newline
lgc-lex-reverse-contract1 ( a , r ) else newline
lgc-lex-reverse-contract1 ( a , c :: r ) end define ]]"
Accumulate "[[ lgc-lex-reverse-contract ( a ) ]]" in "[[ r ]]".
\item "[[ late define lgc-lex-contract ( a ) as newline
reverse ( lgc-lex-reverse-contract ( a ) ) end define ]]"
Contract multiple spaces in "[[ a ]]" to single spaces.
\item "[[ late define lgc-lex-reverse-contract* ( a ) as newline
if a atom then true else newline
lgc-lex-reverse-contract ( a head ) :: lgc-lex-reverse-contract* ( a tail ) end define ]]"
Apply "[[ lgc-lex-contract ( e ) ]]" in each element of the list "[[ a ]]".
\item "[[ late define lgc-lex-contract* ( a ) as newline
if a atom then true else newline
lgc-lex-contract ( a head ) :: lgc-lex-contract* ( a tail ) end define ]]"
Apply "[[ lgc-lex-contract ( e ) ]]" in each element of the list "[[ a ]]".
\item "[[ late define lgc-lex-reverse-trim-contract ( a ) as newline
lgc-lex-left-trim ( lgc-lex-reverse-contract ( lgc-lex-left-trim ( a ) ) ) end define ]]"
Contract multiple spaces in "[[ a ]]" to single spaces and remove leading and trailing spaces, if any. Reverse the list during the process.
\item "[[ late define lgc-lex-trim-contract ( a ) as newline
reverse ( lgc-lex-reverse-trim-contract ( a ) ) end define ]]"
Contract multiple spaces in "[[ a ]]" to single spaces and remove leading and trailing spaces, if any.
\end{statements}
\subsection{Lexical analysis}
Split source into constituents and store them in "[[ s [[ !"page" ]] ]]", "[[ s [[ !"bib" ]] ]]", "[[ s [[ !"def" ]] ]]", "[[ s [[ !"body" ]] ]]".
\begin{statements}
\item "[[ late define lgc-lex-source ( f , s ) as newline
let f = newline
let f = newline
let f = newline
let f = newline
let f = newline
lgc-lex-position ( f , 0 ) in newline
lgc-lex-newline ( f , true ) in newline
lgc-lex-comment1 ( f ) in newline
lgc-lex-escape1 ( f ) in newline
lgc-lex-collect ( f ) in newline
let g = lgc-lex-left-trim ( lgc-lex-extract-body ( f ) ) in newline
let s = s [[ !"body" -> g ]] in newline
let s = lgc-lex-extract-other ( f , s ) in newline
s end define ]]"
Apply lexical analysis to the source by adding positions, normalizing newline sequences, removing comments, replacing escape sequences by escape codes, and collecting structures (like strings) into tokens. The funny way of stating let-statements ensures that the rather large intermediate results can be re-claimed by the garbage collector.
\end{statements}
\subsection{Add positions to text}
\begin{statements}
\item "[[ late define lgc-lex-position ( f , p ) as newline
if f atom then true else newline
<< f head ,, p >> :: lgc-lex-position ( f tail , p + 1 ) end define ]]"
Add position numbers to all characters.
\end{statements}
\subsection{Replace newline sequences with newline characters}
Input files are assumed to be encoded in Logiweb Unicode UTF-8. This is identical to the Unicode UTF-8 encoding with the following, additional conventions concerning ``special characters'', i.e.\ characters with codes between Code 0 and Code 31, inclusive:
\begin{description}
\item[Code 9 horizontal tab (TAB)] A TAB is treated as a Code 32 space.
\item[Code 10 line feed (LF)] An LF marks the end of a line.
\item[Code 12 form feed (FF)] An FF is treated as an LF.
\item[Code 13 carriage return (CR)] A CR is treated as an LF. However, an LF which follows a non-ignored CR is ignored and a CR which follows a non-ignored LF is ignored. As an example, the sequence "[[ << LF ,, CR ,, CR ,, LF >> ]]" is interpreted as a non-ignored "[[ LF ]]", an ignored "[[ CR ]]", a non-ignored "[[ CR ]]", and an ignored "[[ LF ]]". The non-ignored "[[ LF ]]" and "[[ CR ]]" both mark the end of a line. Hence, "[[ << LF ,, CR ,, CR ,, LF >> ]]" is interpreted as two "[[ LF ]]" characters.
\item[Other] All characters in the range 0--31 other than TAB, LF, FF, and CR are considered illegal.
\end{description}
\begin{statements}
\item "[[ late define lgc-lex-newline ( f , C ) as newline
if f atom then true else newline
let << c ,, p >> :: f = f in newline
if c >= !" " then << c ,, p >> :: lgc-lex-newline ( f , true ) else newline
if c = C then lgc-lex-newline ( f , true ) else newline
if c = LF then << c ,, p >> :: lgc-lex-newline ( f , CR ) else newline
if c = CR then << LF ,, p >> :: lgc-lex-newline ( f , LF ) else newline
if c = FF then << LF ,, p >> :: lgc-lex-newline ( f , true ) else newline
if c = TAB then << SP ,, p >> :: lgc-lex-newline ( f , true ) else
( p :: !"Illegal character: 0x" :: lgc-string2mixed ( c ) ) raise end define ]]"
Normalize newline sequences in the text "[[ f ]]" into newline characters.
\end{statements}
\subsection{Remove comments}
Logiweb sources may contain short and long comments.
A short comment starts with a multiquote followed by a semicolon and ends by a newline or the end of the file. Form feed (FF) and carriage return (CR) characters are converted to newlines, so a short comment may effectively be ended by an FF or CR.
A long comment starts with a multiquote followed by a left brace and ends by a multiquote followed by a right brace. The number of double quote characters of the closing escape sequence must match the number of double quote characters of the opening escape sequence.
All escape sequences inside short and long comments are ignored, except that a right brace escape sequence of the right size ends a long comment. In normal usage, the programmer is supposed to use escape sequences starting with two double quote characters. But a programmer may occasionally want to comment out a piece of source text which contains a mix of comments, code, and strings, in which case a long comment of starting with three double quote characters may be used.
\begin{statements}
\item "[[ late define lgc-lex-comment1 ( f ) as newline
if f atom then true else newline
if f head head != QQ then f head :: lgc-lex-comment1 ( f tail ) else newline
lgc-lex-comment2 ( f head first , 1 , f tail ) end define ]]"
Remove comments from the text "[[ f ]]".
\item "[[ late define lgc-lex-comment2 ( P , n , f ) as newline
if f atom then repeat ( n , << QQ ,, P >> ) else newline
let << c ,, p >> :: f = f in newline
if c = QQ then lgc-lex-comment2 ( P , n + 1 , f ) else newline
if n > 1 .and. c = !";" then lgc-lex-comment3 ( f ) else newline
if n > 1 .and. c = !"{" then lgc-lex-comment4 ( P , n , 0 , f ) else newline
append ( repeat ( n , << QQ ,, P >> ) , << c ,, p >> :: lgc-lex-comment1 ( f ) ) end define ]]"
Parse the start of a comment. Invoke "[[ lgc-lex-comment3 ( f ) ]]" for \verb+""!""!;+ comments and "[[ lgc-lex-comment4 ( P , n , 0 , f ) ]]" for \verb+""!""!{+ comments.
\item "[[ late define lgc-lex-comment3 ( f ) as newline
if f atom then true else newline
if f head head = LF then lgc-lex-comment1 ( f tail ) else newline
lgc-lex-comment3 ( f tail ) end define ]]"
Remove all characters up to next newline.
\item "[[ late define lgc-lex-comment4 ( P , n , m , f ) as newline
if f atom then ( P :: !"End of file in long comment" ) raise else newline
if f head head = QQ then lgc-lex-comment4 ( P , n , m + 1 , f tail ) else newline
if m = n .and. f head head = !"}" then lgc-lex-comment1 ( f tail ) else newline
lgc-lex-comment4 ( P , n , 0 , f tail ) end define ]]"
\end{statements}
\subsection{Parse escape sequences}
\begin{statements}
\item "[[ late define lgc-lex-escape1 ( f ) as newline
if f atom then true else newline
if f head head = QQ then lgc-lex-escape2 ( f head first , f tail ) else newline
f head :: lgc-lex-escape1 ( f tail ) end define ]]"
Convert escape sequences in the text "[[ f ]]".
\item "[[ late define lgc-lex-escape2 ( P , f ) as newline
if f atom then << QQ ,, P >> :: true else newline
if f head head = QQ then lgc-lex-escape3 ( P , f tail ) else newline
<< QQ ,, P >> :: lgc-lex-escape1 ( f ) end define ]]"
Convert the text "[[ f ]]" which follows one double quote.
\item "[[ late define lgc-lex-escape3 ( P , f ) as newline
if f atom then ( P :: !"End of file in escape sequence" ) raise else newline
let << c >> :: f = f in newline
if c = QQ then lgc-lex-escape3 ( P , f ) else newline
<< c - NULL ,, P >> :: lgc-lex-escape1 ( f ) end define ]]"
Convert the text "[[ f ]]" which follows a multiquote.
\end{statements}
\subsection{Collect structures}
\begin{statements}
\item "[[ late define lgc-lex-collect ( f ) as newline
if f atom then true else newline
let << c ,, P >> :: f = f in newline
if c < NULL then lgc-lex-collect1 ( c , P , f ) else newline
if c = SP then << SP ,, P >> :: lgc-lex-space ( f ) else newline
if c = LF then << SP ,, P >> :: lgc-lex-space ( f ) else newline
if c = QQ then lgc-lex-string ( lgc-esc-- , P , true , f ) else newline
<< c ,, P >> :: lgc-lex-collect ( f ) end define ]]"
Collect tokens in the text "[[ f ]]".
\item "[[ late define lgc-lex-collect1 ( c , P , f ) as newline
if c = lgc-esc-- then lgc-lex-string ( lgc-esc-- , P , true , f ) else newline
if c = lgc-esc-# then lgc-lex-string ( lgc-esc-# , P , true , f ) else newline
if c = lgc-esc-$ then lgc-lex-string ( lgc-esc-$ , P , true , f ) else newline
if c = lgc-esc-P then lgc-lex-page ( P , true , true , f ) else newline
if c = lgc-esc-R then lgc-lex-ref ( P , true , true , f ) else newline
if c = lgc-esc-D then lgc-lex-def ( f ) else newline
if c = lgc-esc-B then lgc-lex-collect ( f ) else newline
if c = lgc-esc-. then ( lgc-esc-- :: P :: '' ) :: lgc-lex-collect ( f ) else newline
if c = lgc-esc-S then << lgc-esc-S ,, P >> :: lgc-lex-collect ( f ) else newline
if c = lgc-esc-N then << lgc-esc-N ,, P >> :: lgc-lex-collect ( f ) else newline
if c = lgc-esc-C then << lgc-esc-C ,, P >> :: lgc-lex-collect ( f ) else newline
( P :: !"Unknown escape in body: 0x" :: lgc-string2mixed ( c + NULL ) ) raise end define ]]"
Collect tokens that start with escape sequences in the text "[[ f ]]".
\end{statements}
\subsection{Space parsing}
\begin{statements}
\item "[[ late define lgc-lex-space ( f ) as newline
let c = f head head in newline
if c = SP .or. c = LF then lgc-lex-space ( f tail ) else newline
lgc-lex-collect ( f ) end define ]]"
Skip spaces.
\end{statements}
\subsection{Page parsing}
\begin{statements}
\item "[[ late define lgc-lex-page ( P , r , R , f ) as newline
if f atom then lgc-lex-page1 ( P , r , R , true ) else newline
let << c ,, p >> :: f = f in newline
if c = LF .or. c = lgc-esc-n then lgc-lex-page1 ( P , r , R , f ) else newline
if c = QQ then lgc-lex-page ( P , true , r :: R , f ) else newline
if c >= NULL then lgc-lex-page ( P , c :: r , R , f ) else newline
( p :: !"Unknown escape in page: 0x" :: lgc-string2mixed ( c + NULL ) ) raise end define ]]"
Parse page directive until the end of the line. Chop at quotes. Accumulate characters in "[[ r ]]" and accumulate interquote strings in "[[ R ]]". Finally call "[[ lgc-lex-page1 ( P , r , R , f ) ]]" where "[[ r ]]" is the page name and "[[ R ]]" is the list of prefixes.
\item "[[ late define lgc-lex-page1 ( P , r , R , f ) as newline
let r = lgc-lex-reverse-trim-contract ( r ) in newline
let R = lgc-lex-reverse-contract* ( R ) in newline
if r then ( P :: !"Empty page name" ) raise else newline
( lgc-esc-P :: P :: r :: R ) :: lgc-lex-collect ( f ) end define ]]"
Normalize the page name "[[ r ]]" and the prefixes "[[ R ]]". Then continue lexical analysis.
\end{statements}
\subsection{Reference parsing}
\begin{statements}
\item "[[ late define lgc-lex-ref ( P , r , R , f ) as newline
if f atom then lgc-lex-ref1 ( P , r , R , true ) else newline
let << c ,, p >> :: f = f in newline
if c = LF .or. c = lgc-esc-n then lgc-lex-ref1 ( P , r , R , f ) else newline
if c = QQ then lgc-lex-ref ( P , true , r :: R , f ) else newline
if c >= NULL then lgc-lex-ref ( P , c :: r , R , f ) else newline
( p :: !"Unknown escape in reference: 0x" :: lgc-string2mixed ( c + NULL ) ) raise end define ]]"
Parse reference until the end of the line. Chop at quotes. Accumulate characters in "[[ r ]]" and accumulate interquote strings in "[[ R ]]". Finally call "[[ lgc-lex-page1 ( P , r , R , f ) ]]" where "[[ r ]]" is the reference and "[[ R ]]" is the list of prefixes.
\item "[[ late define lgc-lex-ref1 ( P , r , R , f ) as newline
let r = lgc-lex-reverse-trim-contract ( r ) in newline
let R = lgc-lex-reverse-contract* ( R ) in newline
if r then ( P :: !"Empty reference" ) raise else newline
( lgc-esc-R :: P :: r :: R ) :: lgc-lex-collect ( f ) end define ]]"
Normalize the reference "[[ r ]]" and the prefixes "[[ R ]]". Then continue lexical analysis.
\end{statements}
\subsection{Definition parsing}
\begin{statements}
\item "[[ late define lgc-lex-def ( f ) as newline
if f atom then true else lgc-lex-def0 ( f head first , f head first , true , true , f ) end define ]]"
\item "[[ late define lgc-lex-def0 ( P , Q , r , R , f ) as newline
if f atom then lgc-lex-def2 ( P , Q , r , R , f ) else newline
let << c ,, p >> :: f = f in newline
if c = LF .or. c = lgc-esc-n then newline
lgc-lex-def0 ( P , f head first , true , lgc-lex-def1 ( Q , r , R ) , f ) else newline
if c >= NULL then lgc-lex-def0 ( P , Q , c :: r , R , f ) else newline
if c = lgc-esc-P .or. c = lgc-esc-R .or. c = lgc-esc-D .or. c = lgc-esc-B then newline
lgc-lex-def2 ( P , Q , r , R , << c ,, p >> :: f ) else
( p :: !"Unknown escape in definition: 0x" :: lgc-string2mixed ( c + NULL ) ) raise end define ]]"
Parse lines until the end of the definition section. Chop at newlines. Accumulate characters in "[[ r ]]" and accumulate lines in "[[ R ]]". Finally call "[[ lgc-lex-def2 ( P , Q , r , R , f ) ]]" where "[[ r ]]" is the reference and "[[ R ]]" is the list of prefixes. "[[ P ]]" is the start of the definition directive and "[[ Q ]]" is the start of the line.
\item "[[ late define lgc-lex-def1 ( Q , r , R ) as newline
let r = reverse ( r ) in newline
if R then << r >> else newline
let p = r head first in newline
let r = lgc-lex-trim-contract ( r ) in newline
if r = true then R else newline
if r tail != true .or. r head head != QQ then r :: R else newline
( Q :: !"Construct must contain at least one proper character" ) raise end define ]]"
\item "[[ late define lgc-lex-def2 ( P , Q , r , R , f ) as newline
let R = lgc-lex-def1 ( Q , r , R ) in newline
let R = reverse ( R ) in newline
( lgc-esc-D :: P :: R ) :: lgc-lex-collect ( f ) end define ]]"
\end{statements}
\subsection{String parsing}
\begin{statements}
\item "[[ late define lgc-lex-string ( C , P , r , f ) as newline
if f atom then ( P :: !"End of file in string" ) raise else newline
let << c ,, p >> :: f = f in newline
if c = QQ .or. c = lgc-esc-. then ( C :: P :: vt2vector ( reverse ( r ) ) ) :: lgc-lex-collect ( f ) else newline
if c >= NULL then lgc-lex-string ( C , P , c :: r , f ) else newline
if c = lgc-esc-- then lgc-lex-string ( C , P , r , f ) else newline
if c = lgc-esc-! then lgc-lex-string ( C , P , QQ :: r , f ) else newline
if c = lgc-esc-f then lgc-lex-string ( C , P , FF :: r , f ) else newline
if c = lgc-esc-n then lgc-lex-string ( C , P , LF :: r , f ) else newline
if c = lgc-esc-r then lgc-lex-string ( C , P , CR :: r , f ) else newline
if c = lgc-esc-t then lgc-lex-string ( C , P , TAB :: r , f ) else newline
if c = lgc-esc-x then lgc-lex-hex ( C , P , p , r , f ) else newline
( p :: !"Unknown escape in string: 0x" :: lgc-string2mixed ( c + NULL ) ) raise end define ]]"
\item "[[ late define lgc-lex-hex ( C , P , Q , r , f ) as newline
if f atom then ( P :: !"End of file in string" ) raise else
let << c ,, p >> :: f = f in newline
if c = QQ then ( Q :: !"End of string in hex code" ) raise else newline
if c = !"." then lgc-lex-string ( C , P , r , f ) else newline
if c = SP .or. c = LF then lgc-lex-hex ( C , P , Q , r , f ) else newline
if !"0" <= c .and. c <= !"9" then lgc-lex-hex1 ( C , P , Q , c - !"0" , r , f ) else newline
if !"A" <= c .and. c <= !"F" then lgc-lex-hex1 ( C , P , Q , c - !"A" + Base , r , f ) else newline
if c >= NULL then ( p :: !"Invalid character in hex constant" ) raise else
( P :: !"Unknown escape in string: 0x" :: lgc-string2mixed ( c + NULL ) ) raise end define ]]"
\item "[[ late define lgc-lex-hex1 ( C , P , Q , d , r , f ) as newline
if f atom then ( P :: !"End of file in string" ) raise else newline
let << c ,, p >> :: f = f in newline
if c = QQ then ( Q :: !"End of string in hex code" ) raise else newline
if c = !"." then ( Q :: !"Odd number of digits in hex code" ) raise else newline
if c = SP .or. c = LF then lgc-lex-hex1 ( C , P , Q , d , r , f ) else newline
if !"0" <= c .and. c <= !"9" then lgc-lex-hex ( C , P , Q , NULL + d * 16 + c - !"0" :: r , f ) else newline
if !"A" <= c .and. c <= !"F" then lgc-lex-hex ( C , P , Q , NULL + d * 16 + c - !"A" + Base :: r , f ) else newline
if c >= NULL then ( p :: !"Invalid character in hex constant" ) raise else
( P :: !"Unknown escape in string: 0x" :: lgc-string2mixed ( c + NULL ) ) raise end define ]]"
\end{statements}
\subsection{Extract result of lexical analysis}
\begin{statements}
\item "[[ late define lgc-lex-extract-body ( f ) as newline
if f atom then true else newline
let c = f head head in newline
if c = lgc-esc-P .or. c = lgc-esc-R .or. c = lgc-esc-D then lgc-lex-extract-body ( f tail ) else newline
if c = SP then lgc-lex-extract-body1 ( f head , f tail ) else newline
"";if f tail atom .and. c = SP then true else newline
f head :: lgc-lex-extract-body ( f tail ) end define ]]"
Extract the body from the text "[[ f ]]" by disregarding page, reference, and definition directives.
\item "[[ late define lgc-lex-extract-body1 ( x , f ) as newline
if f atom then true else newline
let c = f head head in newline
if c = lgc-esc-P .or. c = lgc-esc-R .or. c = lgc-esc-D then lgc-lex-extract-body1 ( x , f tail ) else newline
if c = SP then x :: lgc-lex-extract-body ( f tail ) else newline
x :: lgc-lex-extract-body ( f ) end define ]]"
Extract the body from the text "[[ f ]]" knowing that it follows the space character "[[ x ]]".
\item "[[ late define lgc-lex-extract-other ( f , s ) as newline
if f atom then s else newline
let c = f head head in newline
if c = lgc-esc-P then lgc-lex-extract-page ( f , s ) else newline
if c = lgc-esc-R then lgc-lex-extract-ref ( f , s ) else newline
if c = lgc-esc-D then lgc-lex-extract-def ( f , s ) else newline
if c = lgc-esc-$ then lgc-lex-extract-include ( f , s ) else newline
if c = lgc-esc-# then lgc-lex-extract-include ( f , s ) else newline
lgc-lex-extract-other ( f tail , s ) end define ]]"
\item "[[ late define lgc-lex-extract-page ( f , s ) as newline
if s [[ !"page" ]] != true then ( f head first :: !"More than one page name found" ) raise else
let s = s [[ !"page" -> f head tail tail ]] in newline
lgc-lex-extract-other ( f tail , s ) end define ]]"
\item "[[ late define lgc-lex-extract-ref ( f , s ) as newline
let s = push ( s , !"bib" , f head tail tail ) in newline
lgc-lex-extract-other ( f tail , s ) end define ]]"
\item "[[ late define lgc-lex-extract-def ( f , s ) as newline
let ( true :: p :: r :: R ) :: f = f in newline
let s = push ( s , !"def" , ( p :: r ) :: R ) in newline
lgc-lex-extract-other ( f , s ) end define ]]"
\item "[[ late define lgc-lex-extract-include ( f , s ) as newline
let ( true :: p :: n ) :: f = f in newline
let s = push ( s , !"nincludes" , p :: n ) in newline
lgc-lex-extract-other ( f , s ) end define ]]"
\end{statements}
\subsection{Include processing}
The "[[ lgc-include-1 ( s ) ]]" function requests the include files and "[[ lgc-include-2 ( x , s ) ]]" processes them.
\begin{statements}
\item "[[ late define lgc-include-1 ( s ) as newline
let l = s [[ !"nincludes" ]] in newline
if l atom then lgc-load-1 ( s ) else newline
let ( true :: n ) :: l = l in newline
if .not. s [[ !"includes" ]] [[ n ]] then lgc-include-1 ( s [[ !"nincludes" -> l ]] ) else newline
let n = lgc-tilde-expand ( n , s [[ !"parameters" ]] ) in newline
let s = lgc-progress ( !"Reading file:" :: n , 3 , s ) in newline
let s = lgc-push-event ( s , fileTypeRead ( n ) ) in newline
lgc-exec-events ( s , lgc-include-2 ( x , s ) ) end define ]]"
\item "[[ late define lgc-include-2 ( x , s ) as newline
let << true ,, << true ,, t :: f >> >> = x in newline
let l = s [[ !"nincludes" ]] in newline
let ( p :: n ) :: l = l in newline
if t = FileTypeNonexistent then newline
lgc-error ( s , p , !"Could not find include file " :: n ) else newline
let s = s [[ !"nincludes" -> l ]] in newline
let s = s [[ << !"includes" ,, n >> => f ]] in newline
lgc-include-1 ( s ) end define ]]"
\end{statements}
" ]"\section{Loading}"[ "
\subsection{Load processing}
The "[[ lgc-load-1 ( s ) ]]" function requests all Logiweb pages transitively referenced by the page being translated.
The source text references other pages using \verb+""!""!R+ escape sequences. The reference following the \verb+""!""!R+ escape sequence may be:
\begin{description}
\item[A url] such as \url{http://logiweb.eu/logiweb/page/base/fixed/vector/vector.lgw} or \url{file:~/.logiweb/latest/base/rack.lgr}
\item[A page name] such as \url{name:base}
\end{description}
References which do not contain a colon character are taken to be page names, so \url{name:base} can be abbreviated as \url{base}. Names are looked up using the \verb+namepath+ as described later.
A url must have type \verb+http:+ or \verb+file:+ or \verb+lgw:+. An http url is retrieved using an http GET operation. A file url is looked up in the local file system. Tilde expansion is performed on file urls. An lgw url is looked up using the \verb+path+ as described later.
An http or file url must have extension \verb+.lgw+ or \verb+.lgr+ or \verb+.lgu+. The extension determines the format of the referenced page. A url with extension \verb+.lgw+, \verb+.lgr+, and \verb+.lgu+ contains a Logiweb vector, rack, and url, respectively. A Logiweb vector is a compact format suited for transmitting over the Internet which takes some time to translate to internal form. A Logiweb rack is a more extensive format suited for storing in the local file system which is faster to translate to internal format. A Logiweb url is a like a symbolic link in that it just contains the http url of the page to be fetched.
As mentioned, names are translated to pages using the \verb+namepath+. This is done by considering the elements of \verb+namepath+ one at a time and replacing the rightmost colon character of the element by the given name. The result must be a url which is then looked up. If successful, the page found is used. Otherwise, the next element of the \verb+namepath+ is tried.
Similarly, urls of type \verb+lgw:+ are looked up using the \verb+path+ variable by replacing the rightmost colon character of each element by the characters following \verb+lgw:+. In this case, however, the characters following \verb+lgw:+ must be a Logiweb reference expressed in mixed endian hexadecimal. In mixed endian hexadecimal, bytes are written in network byte order and each byte is written as two hexadecimal digits with the most significant digit first.
Once a page is retrieved, it will be in either \verb+.lgw+ or \verb+.lgr+ format. If it is in \verb+.lgw+ format, then its reference is extracted and an attempt is made to look it up in \verb+.lgr+ format using the \verb+path+. If that is successful, loading is based on the \verb+.lgr+ version.
Note that one should only retrieve pages in \verb+.lgr+ format from trusted locations and only over high bandwidth channels. In practice, most users will be happy just retrieving \verb+.lgr+ pages from their own cache. Whenever the Logiweb compiler has translated an \verb+.lgw+ page, it stores the \verb+.lgr+ equivalent in the users cache so that the time consuming translation is done once only.
Loading a page involves the following major steps:
\begin{description}
\item[Fetch] Tranlate the reference to a page in \verb+.lgw+ or \verb+.lgr+ format.
\item[Trisect] Do a first round of processing of the page.
\item[Traverse] Transitively load all pages referenced by the page.
\item[Codify] Do a second round of processing of the page.
\end{description}
\subsection{State entries}
A number of hooks of the state are used for keeping track of the loading process:
\begin{description}
\item "[[ s [[ !"cluster" ]] [[ r ]] ]]" The cache of the page with reference "[[ r ]]". This is the main result of loading.
\item "[[ s [[ !"stack" ]] ]]" List of list of not yet loaded pages. The element at the bottom of the stack is a list of urls/names. The other elements are lists of references. Set to "[[ true ]]" when all pages are loaded.
\item "[[ s [[ !"events" ]] ]]" List of output events in reverse order.
\item "[[ s [[ !"path" ]] ]]" Search path (from path or namepath option).
\item "[[ s [[ !"fetching" ]] ]]" The url being fetched.
\item "[[ s [[ !"suffix" ]] ]]" The suffix of the url being fetched (lgw or lgr or lgu)
\item "[[ s [[ !"type" ]] ]]" The type of the url being fetched (http or file)
\item "[[ s [[ !"vector" ]] ]]" The vector of the page that has been fetched. This is set only when a page has been found in .lgw format but a search for the same page in .lgr format is in progress.
\item "[[ s [[ !"refbib" ]] ]]" The bibliography as a list of references.
\end{description}
\subsection{Loading in more detail}
Loading of a Logiweb page is a process which transforms a Logiweb page into an in-memory \emph{rack} structure suited for computer processing. A \emph{rack} is an inhomogeneous array, i.e.\ an array "[[ c ]]" for which "[[ c [[ i ]] ]]" contains different kinds of information for different values of "[[ i ]]". Values of indices "[[ i ]]" are named \emph{hooks}, and we shall refer to "[[ c [[ i ]] ]]" as the value that hangs on the the hook "[[ i ]]". Racks correspond to record structures in e.g.\ C. The input to the load process may be:
\begin{itemize}
\item The Logiweb reference of the page. The reference of a page is a sequence of about 30 bytes which provides a world-wide unique identification of the page.
\item The page itself in lgw or lgr format.
\item The name of a file which contain the page in lgw or lgr format.
\item An http url which points to a file which contains the page in lgw format.
\item The name of the page which, combinted with the namepath argument of the compiler allows to locate a file which contains the page in lgw or lgr format.
\end{itemize}
When given a Logiweb reference as input, loading comprises the following steps:
\begin{description}
\item[Fetch] Translate the reference of a page into the vector of the page. The vector of the page is a sequence of bytes which encodes the page in a compact way suited for transmission over a network. Fetching a page comprises two activities:
\begin{description}
\item[Locate] Translate the reference of a page into an http URL suited for getting the page.
\item[Get] Receive the vector using an http GET operation.
\end{description}
\item[Trisect] Parse the vector of the page into a bibliography, a dictionary, and a flat body. The bibliography is a sequence of references. Reference zero (the first reference) of the bibliography is the reference of the page itself. The proper references (all but the first one) are pointers to other Logiweb pages. The dictionary is an association list which maps indexes to arities. Indexes as well as arities are natural numbers. A flat body is a sequence of unparsed bytes.
\item[Traverse] Recursively load the proper references of tbe bibliography.
\item[Codify] Parse the flat body, macro expand the body and harvest \emph{revelations} (i.e.\ \emph{definitions}, \emph{introductions}, and \emph{proclamations}) from the expanded tree, then verify the correctness of the page. We shall refer to the outcome of macro expansion and harvesting as the \emph{expansion} and \emph{codex}, respectively, of the page. The expansion resembles an S-expression like the body does. The codex is an associative structure containing all revelations in the expansion organized in a way that makes the revelations easy to access. We shall refer to the outcome of verification as a \emph{diagnose} which, if empty, indicates that the page is correct. Codification comprises the following activities:
\begin{description}
\item[Unpack] Parse the flat body and return a parse tree. We shall refer to the parse tree as the \emph{body} of the page. The body resembles a Lisp S-expression.
\item[Initialize] Construct an initial codex. The initial codex is empty if the bibliography contains more than one reference (i.e. contains at least one proper reference). Otherwise, the codex contains exactly one entry which proclaims a \emph{proclamation} symbol. The user can use the proclamation symbol to assign semantics to other symbols and thus bootstrap the Logiweb system. This is for experienced, determined users only. Other users just reference at least one other page and thus leaves the bootstrap to somebody else.
\item[Compile] Compile all value definitions in the codex and lazily verify the page. During the first iteration of the codification there are no value definitions in the codex so the compilation done in the first iteration is always trivial.
\item[Macro expand] Macro expand the page. First look for a macro expander in the codex of the page. If one is found, apply it. Otherwise, look for a macro expander in the first reference of the page. If one is found, apply it. Otherwise, use the identity function as macro expander, i.e.\ use the body unchanged as expansion.
\item[Harvest] Scan the expansion for revelation symbols, i.e.\ for symbols which are proclaimed to denote definition, introduction, or proclamation. Each time a revelation symbol is found, add one revelation to the codex. When encountering a symbol which is proclaimed to be a hiding symbol, do not scan the subtrees of the symbol. If harvesting changes the codex, reiterate from \emph{Compile} above.
\end{description}
\end{description}
After loading a page, one may do the following:
\begin{itemize}
\item Render the page. Rendering may result in a human readable version of the page as well as executables.
\item Verify the page. Verification is done lazily during codification, so one just needs to force evaluation of the diagnose to do verification.
\item Dump the page to cache. Before a page can be cached, verification has to be forced.
\end{itemize}
\subsection{Top level function}
The "[[ lgc-load-1 ( s ) ]]" function implements traversing in that it arranges that all transitively referenced pages are loaded. When invoked, lexical analysis has placed a structure of form "[[ << n :: p ,, *** >> ]]" in "[[ s [[ !"bib" ]] ]]" where "[[ n ]]" is the name of a directly referenced page and "[[ p ]]" is a list of prefixes for grammar construction. The "[[ s [[ !"bib" ]] ]]" list is in reverse order compared to the source file, so first task is to reverse it. Then "[[ lgc-heads ( b ) ]]" is used to extract the names, and the list of names is stacked. During loading, this list of names appear at the bottom of the stack. All other elements of the stack are lists of references. The actual stacking of "[[ lgc-heads ( b ) ]]" is done by "[[ lgc-load-fetch0 ( << lgc-heads ( b ) >> , s ) ]]".
The "[[ lgc-load-setpath ( s ) ]]" function sets "[[ s [[ !"path" ]] ]]" to the path relevant for the first element of the top element of the stack. When there is more than one element in the stack, the top element is a list of references and, hence, the relevant path is "[[ s [[ !"parameters" ]] [[ !"path" ]] ]]". If the stack has one element then, usually, the relevant path is "[[ s [[ !"parameters" ]] [[ !"namepath" ]] ]]". If the first element of the top element is an explicit Logiweb reference (starting with "[[ !"lgw:" ]]") then the relevant path is "[[ s [[ !"parameters" ]] [[ !"path" ]] ]]". The "[[ lgc-load-setpath ( s ) ]]" function is defined separately because it is needed more than one place.
\begin{statements}
\item "[[ late define lgc-load-1 ( s ) as newline
let b = reverse ( s [[ !"bib" ]] ) in newline
let s = s [[ !"bib" -> b ]] in newline
lgc-load-fetch0 ( << lgc-heads ( b ) >> , s ) end define ]]"
Reverse the bibliography "[[ b ]]" from lexical analysis, extract page names, and call "[[ lgc-load-fetch0 ( S , s ) ]]" to stack the list of page names and invoke fetching.
\item "[[ late define lgc-load-fetch0 ( S , s ) as newline
let s = s [[ !"stack" -> S ]] in newline
let s = lgc-load-setpath ( s ) in newline
lgc-load-fetch ( s ) end define ]]"
Initialize the stack and leave further work to "[[ lgc-load-fetch ( s ) ]]".
\item "[[ late define lgc-load-setpath ( s ) as newline
let r = s [[ !"stack" ]] in newline
let p = r head head in newline
if p then s else newline
let s = lgc-progress-fetch ( p , r , s ) in newline
if r tail pairp .or. lgc-prefix ( lgc-lgw-prefix , p ) then newline
s [[ !"path" -> s [[ !"parameters" ]] [[ !"path" ]] ]] else newline
s [[ !"path" -> s [[ !"parameters" ]] [[ !"namepath" ]] ]] end define ]]"
Set "[[ s [[ !"path" ]] ]]" to path or namepath argument as appropriate.
\item "[[ late define lgc-progress-fetch ( p , r , s ) as newline
if r tail then lgc-progress ( !"Fetching " :: p , 3 , s ) else newline
let P = !"lgw:" :: lgc-string2mixed ( p ) in newline
if s [[ !"cluster" ]] [[ p ]] = true then newline
lgc-progress ( !"Fetching " :: P , 3 , s ) else newline
lgc-progress ( !"Fetching " :: P , 4 , s ) end define ]]"
\item "[[ late define lgc-heads ( b ) as newline
if b atom then true else b head head :: lgc-heads ( b tail ) end define ]]"
Extract the references from the bibliography "[[ b ]]".
\end{statements}
\subsection{Constants}
\begin{statements}
\item "[[ late define lgc-file-prefix as vt2vector* ( !"file:" ) end define ]]"
\item "[[ late define lgc-http-prefix as vt2vector* ( !"http:" ) end define ]]"
\item "[[ late define lgc-//-prefix as vt2vector* ( !"//" ) end define ]]"
\item "[[ late define lgc-lgw-prefix as vt2vector* ( !"lgw:" ) end define ]]"
\item "[[ late define lgc-name-prefix as vt2vector* ( !"name:" ) end define ]]"
\item "[[ late define lgw-suffix as vt2vector* ( !"lgw" ) end define ]]"
\item "[[ late define lgr-suffix as vt2vector* ( !"lgr" ) end define ]]"
\item "[[ late define lgu-suffix as vt2vector* ( !"lgu" ) end define ]]"
\end{statements}
\subsection{Auxiliary functions}
\begin{statements}
\item "[[ late define lgc-tilde-expand1 ( n , s ) as newline
if n then true else newline
if n head != !"~" then n else newline
s [[ !"parameters" ]] [[ "home" ]] :: n tail end define ]]"
Tilde expansion when "[[ n ]]" is a list of singleton strings.
\item "[[ late define lgc-cwd-expand ( n , s ) as newline
let n = vt2vector* ( n ) in newline
if n head = !"/" then n else s [[ !"cwd" ]] :: !"/" :: n end define ]]"
Expand relative pathname into absolute pathname. If combined with tilde expansion, do tilde expansion first.
\item "[[ late define lgc-prefix ( x , y ) as newline
if x atom then true else newline
if y atom then false else newline
x head = y head .and. lgc-prefix ( x tail , y tail ) end define ]]"
Return "[[ true ]]" if the list "[[ x ]]" is a prefix of the list "[[ y ]]".
\item "[[ late define lgc-char-split ( c , a ) as lgc-char-split1 ( c , a , true ) end define ]]"
Split the list "[[ a ]]" at the element with value "[[ c ]]" and return a pair "[[ u :: v ]]" of the list preceeding and succeeding "[[ c ]]". If "[[ c ]]" does not occur in "[[ a ]]" then "[[ u ]]" is set to "[[ a ]]" and "[[ v ]]" to "[[ true ]]".
\item "[[ late define lgc-char-split1 ( c , a , r ) as newline
if a atom then reverse ( r ) :: true else newline
let C :: a = a in newline
if c = C then reverse ( r ) :: a else newline
lgc-char-split1 ( c , a , C :: r ) end define ]]"
Compute "[[ u :: v = lgc-char-split ( c , a ) ]]", accumulating "[[ u ]]" in "[[ r ]]" in reverse order.
\item "[[ late define lgc-replace-colon ( n , r ) as newline
vt2vector* ( lgc-replace-colon1 ( reverse ( n ) , r ) ) end define ]]"
Replace the rightmost colon character in "[[ n ]]" by "[[ r ]]".
\item "[[ late define lgc-replace-colon1 ( n , r ) as newline
if n then exception else newline
let c :: n = n in newline
if c = !":" then reverse ( n ) :: r else newline
lgc-replace-colon1 ( n , r ) :: c end define ]]"
Auxiliary function for computing "[[ lgc-replace-colon ( n , r ) ]]".
\item "[[ late define lgc-file-suffix ( n ) as newline
let n = reverse ( n ) in newline
let u :: v = lgc-char-split ( !"." , n ) in newline
reverse ( u ) end define ]]"
\end{statements}
\subsection{Message generators}
\begin{statements}
\item "[[ late define lgc-load-fetch-ref-failed ( r , s ) as newline
lgc-simple-error ( !"Could not load reference " :: r , s ) end define ]]"
Complain about a reference which could not be loaded. "[[ r ]]" is supposed to be the name of the reference as given in the source file or the mixed endian hexadecimal representation of an indirectly referenced page.
\item "[[ late define lgc-load-no-colon ( s ) as newline
let n = s [[ !"path" ]] head in newline
lgc-simple-error ( !"Missing colon in path or namepath element " :: n , s ) end define ]]"
All elements of the path and namepath parameters must contain at least one colon character.
\item "[[ late define lgc-wrong-suffix ( n , s ) as newline
let s = lgc-progress ( !"Illegal suffix in path or namepath element " :: n , 1 , s ) in newline
lgc-simple-error ( !"Only .lgw, .lgr, and .lgu are legal suffixes" , s ) end define ]]"
\item "[[ late define lgc-wrong-lgu-suffix ( n , s ) as newline
let s = lgc-progress ( !"Illegal suffix in lgu link: " :: n , 1 , s ) in newline
let s = lgc-progress ( !"Only .lgw and .lgu are legal suffixes" , 1 , s ) in newline
lgc-simple-error ( !"Source of link: " :: s [[ !"type" ]] :: !":" :: s [[ !"fetching" ]] , s ) end define ]]"
All elements of the path and namepath parameters must contain at least one colon character.
\item "[[ late define lgc-load-malformed-url ( n , s ) as newline
lgc-simple-error ( !"Malformed url: http:" :: n , s ) end define ]]"
Complain about a malformed http url. Some valid http urls read:
\begin{itemize}
\item \verb+//my.domain:8080/my/path.lgw+
\item \verb+//my.domain/my/path.lgr+
\end{itemize}
\item "[[ late define lgc-load-malformed-lgu ( n , s ) as newline
let s = lgc-progress ( !"Malformed lgu link: " :: n , 1 , s ) in newline
let s = lgc-progress ( !"Such links must start with 'http:'" , 1 , s ) in newline
lgc-simple-error ( !"Source of link: " :: s [[ !"type" ]] :: !":" :: s [[ !"fetching" ]] , s ) end define ]]"
Complain about a malformed lgu link.
\item "[[ late define lgc-load-malformed-page ( s ) as newline
lgc-simple-error ( !"Malformed page found at " :: s [[ !"type" ]] :: !":" :: s [[ !"fetching" ]] , s ) end define ]]"
Complain about a malformed page.
\item "[[ late define lgc-load-wrong-page ( s ) as newline
lgc-simple-error ( !"Wrong page found at " :: s [[ !"type" ]] :: !":" :: s [[ !"fetching" ]] , s ) end define ]]"
Complain about a malformed page.
\end{statements}
\subsection{Requesting pages}
The "[[ lgc-load-fetch ( s ) ]]" function initiates loading of the first element of the top element of the stack (if any). When done, the function invokes "[[ lgc-grammar ( s ) ]]".
When the stack has more than one element, the top element of the stack is a list of references. In that case, the first element of the top element is loaded using a call to "[[ lgc-load-fetch-ref ( r , s ) ]]". Otherwise, "[[ lgc-load-fetch-name ( s ) ]]" is used. That function dispatches on the url type of the reference which may be "[[ !"file:" ]]", "[[ !"http:" ]]", "[[ !"lgw:" ]]", or "[[ !"name:" ]]", defaulting to "[[ !"name:" ]]".
If the reference is given as an url of type "[[ !"file:" ]]" or "[[ "http:" ]]" then "[[ lgc-load-fetch-file ( r , s ) ]]" or "[[ lgc-load-fetch-http ( r , s ) ]]" is called. Otherwise, the reference is looked up using the path. If the path is empty, it has been exhausted. Otherwise, the next element of the path is tried. When the next element of the path is tried, the rightmost colon character of the path element is replaced by the name or reference to be looked up, and "[[ lgc-load-fetch-path ( n , s ) ]]" is called which dispatches on the url type of the resulting url.
\begin{statements}
\item "[[ late define lgc-load-fetch ( s ) as newline
let r = s [[ !"stack" ]] in newline
if r head .and. r tail then lgc-grammar ( s [[ !"stack" -> true ]] ) else newline
let v = s [[ !"vector" ]] in newline
if .not. v then lgc-load-fetch-ref ( lgw-parse-string ( v ) head , s ) else newline
if r head then lgc-load-codify ( s ) else newline
if r tail then lgc-load-fetch-name ( s ) else newline
if .not. s [[ !"cluster" ]] [[ r head head ]] then lgc-load-fetch0 ( r head tail :: r tail , s ) else newline
lgc-load-fetch-ref ( r head head , s ) end define ]]"
Find out whether or not we are done with loading. If we are done, invoke "[[ lgc-grammar ( s ) ]]". Otherwise, if we have already found the page in .lgw format, look for the page in .lgr format. Otherwise, if we are at the bottom of the stack, fetch a url/name, else fetch a reference.
\item "[[ late define lgc-load-fetch-name ( s ) as newline
let n = s [[ !"stack" ]] head head in newline
if lgc-prefix ( lgc-file-prefix , n ) then newline
lgc-load-fetch-file ( list-suffix ( n , 5 ) , s ) else newline
if lgc-prefix ( lgc-http-prefix , n ) then newline
lgc-load-fetch-http ( list-suffix ( n , 5 ) , s ) else newline
if lgc-prefix ( lgc-lgw-prefix , n ) then newline
lgc-load-fetch-ref1 ( list-suffix ( n , 4 ) , s ) else newline
if lgc-prefix ( lgc-name-prefix , n ) then newline
lgc-load-fetch-ref1 ( list-suffix ( n , 5 ) , s ) else newline
lgc-load-fetch-ref1 ( n , s ) end define ]]"
Dispatch on url type.
\item "[[ late define lgc-load-fetch-ref ( r , s ) as newline
lgc-load-fetch-ref1 ( lgc-string2mixed ( r ) , s ) end define ]]"
Fetch an indirectly referenced page. Indirectly referenced pages are always looked up using the mixed endian hexadecimal representation of the Logiweb reference of the page.
\item "[[ late define lgc-load-fetch-ref1 ( r , s ) as newline
let p = s [[ !"path" ]] in newline
let v = s [[ !"vector" ]] in newline
if p .and. v then lgc-load-fetch-ref-failed ( r , s ) else newline
if p then lgc-load-receive-lgw1 ( v , s [[ !"vector" -> true ]] ) else newline
let n prime :: p = p in newline
let e :: n = lgc-replace-colon ( vt2vector* ( n prime ) , r ) catch in newline
if e then lgc-load-no-colon ( s ) else newline
let t = lgc-file-suffix ( n ) in newline
if t != lgw-suffix .and. t != lgr-suffix .and. t != lgu-suffix then newline
lgc-wrong-suffix ( n prime , s ) else newline
let s = s [[ !"suffix" -> t ]] in newline
let s = s [[ !"path" -> p ]] in newline
if .not. v .and. t != lgr-suffix then lgc-load-fetch-ref1 ( r , s ) else newline
lgc-load-fetch-path ( n , s ) end define ]]"
Locate page using "[[ s [[ !"path" ]] ]]" where "[[ s [[ !"path" ]] ]]" is the namepath parameter if we are at the bottom of the stack and the path parameter otherwise. In the latter case, "[[ r ]]" is always a mixed endian hexadecimal logiweb reference. The function tries the next element of the path, if any. When the path is exhausted, the function checks if we have the page in .lgw format. If we have, then the function translates the .lgw format. If not, then the function issues an error message. When the path is not exhausted, the function tries the next path element but only accepts elements with suffix .lgr if we already have the page in .lgw format.
\item "[[ late define lgc-load-fetch-path ( n , s ) as newline
if lgc-prefix ( lgc-file-prefix , n ) then newline
lgc-load-fetch-file ( list-suffix ( n , 5 ) , s ) else newline
if lgc-prefix ( lgc-http-prefix , n ) then newline
lgc-load-fetch-http ( list-suffix ( n , 5 ) , s ) else newline
lgc-load-fetch-file ( n , s ) end define ]]"
After replacing the rightmost colon of the next path element by the name being looked up, dispatch on url type (\verb+file:+ of \verb+http:+, defaulting to \verb+file:+).
\item "[[ late define lgc-load-fetch-file ( n , s ) as newline
let s = s [[ !"fetching" -> n ]] in newline
let s = s [[ !"type" -> "file" ]] in newline
let n = lgc-tilde-expand1 ( n , s ) in newline
let s = lgc-progress ( !"Reading file:" :: n , 4 , s ) in newline
let s = lgc-push-event ( s , fileTypeRead ( n ) ) in newline
lgc-exec-events ( s , lgc-load-receive ( x , s ) ) end define ]]"
Request given file.
\item "[[ late define lgc-load-fetch-http ( n , s ) as newline
let s = s [[ !"fetching" -> n ]] in newline
let s = s [[ !"type" -> "http" ]] in newline
let s = lgc-progress ( !"Reading http:" :: n , 4 , s ) in newline
if .not. lgc-prefix ( lgc-//-prefix , n ) then newline
lgc-load-malformed-url ( n , s ) else newline
let d :: q = lgc-char-split ( !"/" , list-suffix ( n , 2 ) ) in newline
let d :: p = lgc-char-split ( !":" , d ) in newline
let p = if p then !"80" else p in newline
let x :: p = lgc-atoi ( p , true ) catch in newline
if x .or. p < 0 .or. p > 65535 then newline
lgc-load-malformed-url ( n , s ) else newline
let s = lgc-push-event ( s , tcpQuery ( d , p , 3 , 0 , "GET /" :: q ) ) in newline
lgc-exec-events ( s , lgc-load-receive ( x , s ) ) end define ]]"
Request given http url with a patience of $ 3 \cdot 10 ^ 0 $ seconds.
\end{statements}
\subsection{Receiving pages}
The "[[ lgc-load-receive ( x , s ) ]]" may or may not receive a page. If it receives nothing, it calls "[[ lgc-load-received-nothing ( s ) ]]". Otherwise, it dispatched on the type of the received data.
\begin{statements}
\item "[[ late define lgc-load-receive ( x , s ) as
let << true ,, << true ,, x >> >> = x in newline
let t = s [[ !"type" ]] in newline
if t = !"http" .and. x .or. t = !"file" .and. x head = FileTypeNonexistent then newline
lgc-load-received-nothing ( s ) else newline
let x = if t = !"file" then x tail else x in newline
let t = s [[ !"suffix" ]] in newline
if t = lgw-suffix then lgc-load-receive-lgw ( x , s ) else newline
if t = lgr-suffix then lgc-load-receive-lgr ( x , s ) else newline
if t = lgu-suffix then lgc-load-receive-lgu ( x , s ) else newline
lgc-panic ( !"Internal error in lgc-load-receive" ) end define ]]"
\item "[[ late define lgc-load-received-nothing ( s ) as newline
let n = s [[ !"stack" ]] head head in newline
if lgc-prefix ( lgc-file-prefix , n ) .or. lgc-prefix ( lgc-http-prefix , n ) then newline
lgc-load-fetch-ref-failed ( n , s ) else newline
lgc-load-fetch ( s ) end define ]]"
\item "[[ late define lgc-load-receive-lgw ( x , s ) as newline
let e :: r :: true = lgw-parse-string ( x ) catch in newline
if e .or. r = !"". .or. vector-index ( r , 0 ) != lgc-logiweb-version then lgc-load-received-nothing ( s ) else newline
let ( R :: B ) :: S = s [[ !"stack" ]] in newline
if lgc-wrong-ref ( r , R , S ) then lgc-load-wrong-page ( s ) else newline
if S != true .or. lgc-prefix ( lgc-lgw-prefix , R ) then lgc-load-receive-lgw1 ( x , s ) else newline
if s [[ !"cluster" ]] [[ r ]] != true then lgc-load-fetch0 ( B :: S , push ( s , !"refbib" , r ) ) else newline
let s = s [[ !"vector" -> x ]] in newline
let s = s [[ !"path" -> s [[ !"parameters" ]] [[ !"path" ]] ]] in newline
lgc-load-fetch ( s ) end define ]]"
Receive the fetched page in .lgw format. Parse the reference of the received page. If the version number is wrong, consider the page non-existent. Otherwise check that the page is the one requested and complain if not. If the page is requested by reference, go on load it. Otherwise, it is requested by name in which case it might already be in the cluster or may be available in lgr format. First check if it is in the cluster and go on if it is. Otherwise, search for the page in lgr format.
\item "[[ late define lgc-load-receive-lgw1 ( x , s ) as newline
let e :: c = lgw-trisect ( x ) catch in newline
if e then lgc-load-malformed-page ( s ) else newline
let r = c [[ 0 ]] in newline
let true :: b = c [[ r ]] [[ !"bibliography" ]] in newline
let ( R :: B ) :: S = s [[ !"stack" ]] in newline
let s = if S then push ( s , !"refbib" , r ) else s in newline
let s = s [[ << !"cluster" ,, r >> => c ]] in newline
let n = if S then R else !"lgw:" :: lgc-string2mixed ( r ) in newline
lgc-load-fetch0 ( b :: ( ( r :: !".lgw" :: n ) :: B ) :: S , s ) end define ]]"
Receive a page in lgw format, knowing that the reference is the one requested and that there is no easy way to process the page. In this case, trisect the page and fetch the pages in the bibliography.
\item "[[ late define lgc-logiweb-version as 1 end define ]]"
The current version of Logiweb.
\item "[[ late define lgc-wrong-ref ( r , R , S ) as newline
if .not. S then r != R else newline
if .not. lgc-prefix ( lgc-lgw-prefix , R ) then false else newline
list-suffix ( R , 4 ) != lgc-string2mixed ( r ) end define ]]"
The "[[ lgc-wrong-ref ( r , R , S ) ]]" function returns "[[ true ]]" if the references "[[ r ]]" and "[[ R ]]" differ. If we are at the top of the stack (i.e.\ if "[[ S ]]" is empty) then "[[ R ]]" is a bibliographic reference which can only be compared to "[[ r ]]" if it is of type "[[ !"lgw:" ]]".
\item "[[ late define lgc-load-receive-lgr ( x , s ) as newline
let s = s [[ !"vector" -> true ]] in newline
let e :: c = sl2rack ( x ) catch in newline
if e then lgc-load-malformed-page ( s ) else newline
let r :: b = c [[ !"bibliography" ]] in newline
let c = true [[ 0 -> r ]] [[ r -> c ]] in newline
let ( R :: B ) :: S = s [[ !"stack" ]] in newline
if lgc-wrong-ref ( r , R , S ) then lgc-load-wrong-page ( s ) else newline
let s = if S then push ( s , !"refbib" , r ) else s in newline
if .not. s [[ !"cluster" ]] [[ r ]] then lgc-load-fetch0 ( B :: S , s ) else newline
let s = s [[ << !"cluster" ,, r >> => c ]] in newline
let n = if S then R else !"lgw:" :: lgc-string2mixed ( r ) in newline
lgc-load-fetch0 ( b :: ( ( r :: !".lgr" :: n ) :: B ) :: S , s ) end define ]]"
Receive the fetched page in .lgr format. Clear "[[ s [[ !"vector" ]] ]]" to indicate that we no longer try to find the page in .lgr format. Unpack the rack of the fetched page, convert it to a cache, and store it in the cluster. Then unstack the page that has been found, stack the type of the page (.lgw) and stack the bibliography. Then go on fetching pages.
\item "[[ late define lgc-load-receive-lgu ( n , s ) as newline
let n = reverse ( lgc-trim-newline ( reverse ( n ) ) ) in newline
let t = lgc-file-suffix ( n ) in newline
if t != lgw-suffix .and. t != lgu-suffix then newline
lgc-wrong-lgu-suffix ( n , s ) else newline
let s = s [[ !"suffix" -> t ]] in newline
if lgc-prefix ( lgc-http-prefix , n ) then newline
lgc-load-fetch-http ( list-suffix ( n , 5 ) , s ) else newline
lgc-load-malformed-lgu ( n , s ) end define ]]"
\item "[[ late define lgc-trim-newline ( n ) as newline
let c = n head in newline
if c = LF .or. c = CR then lgc-trim-newline ( n tail ) else n end define ]]"
\end{statements}
\subsection{Codifying loaded pages}\label{sec:CodifyingLoadedPages}
Loading a page involves a first round of processing, loading transitively referenced pages, and a second round of processing. The functions in the following perform the second round of processing.
\begin{statements}
\item "[[ late define lgc-load-codify ( s ) as newline
let true :: ( ( r :: t :: n ) :: true ) :: true = s [[ !"stack" ]] in newline
let s = lgc-progress ( !"Codifying " :: n , 3 , s ) in newline
lgc-exec-events ( s , lgc-load-codify1 ( x , s ) ) end define ]]"
Extract the reference "[[ r ]]" and type "[[ t ]]" of the page to be processed, add a progress message, flush the event queue, and invoke "[[ lgc-load-codify1 ( x , s ) ]]".
\item "[[ late define lgc-load-codify1 ( x , s ) as newline
let true :: ( ( r :: t :: n ) :: s prime ) :: s prime prime = s [[ !"stack" ]] in newline
let s = s [[ !"stack" -> s prime :: s prime prime ]] in newline
if t = !".lgw" then lgc-load-codify-lgw ( r , s ) else newline
if t = !".lgr" then lgc-load-codify-lgr ( r , s ) else newline
lgc-panic ( !"Internal error in lgc-load-codify1" ) end define ]]"
Unstack the reference "[[ r ]]" and type "[[ t ]]" of the page to be processed. Dispatch on "[[ t ]]".
\item "[[ late define lgc-load-codify-lgw ( r , s ) as newline
let s = lgc-load-codify-closure ( r , s ) in newline
let c = s [[ !"cluster" ]] [[ r ]] in newline
let e :: c = lgw-codify ( r , c , s [[ !"verbose" ]] ) catch in newline
if e then lgc-proclaim-error ( c , s ) else newline
let s = s [[ << !"cluster" ,, r >> => c ]] in newline
let s = lgc-load-setpath ( s ) in newline
lgc-load-render ( r , s ) end define ]]"
Call "[[ lgc-load-codify-closure ( r , s ) ]]" to add racks of transitively referenced pages to "[[ s [[ !"cluster" ]] [[ r ]] ]]" and caches of transitively referenced pages to "[[ s [[ !"cluster" ]] [[ r ]] [[ r ]] [[ !"cluster" ]] ]]". Then use "[[ lgw-codify ( r , s , v ) ]]" to codify the page.
\item "[[ late define lgc-load-codify-lgr ( r , s ) as newline
let s = lgc-load-codify-closure ( r , s ) in newline
let c = s [[ !"cluster" ]] [[ r ]] in newline
let c = lgr-cache-restore ( c ) in newline
let s = s [[ << !"cluster" ,, r >> => c ]] in newline
let s = lgc-load-setpath ( s ) in newline
lgc-load-render ( r , s ) end define ]]"
Same as "[[ lgc-load-codify-lgw ( r , s ) ]]" but faster since we have all information except compiled code.
\item "[[ late define lgc-load-render ( r , s ) as newline
let s = s [[ !"reference" -> r ]] in newline
let e :: p = lgc-render-dirname ( r , s ) catch in newline
if e then lgc-rendering-no-colon ( s ) else newline
let p = p :: !"index.html" in newline
let s = lgc-progress ( !"Probing " :: p , 4 , s ) in newline
let s = lgc-push-event ( s , fileType ( p ) ) in newline
lgc-exec-events ( s , lgc-load-render1 ( x , s ) ) end define ]]"
Probe "[[ !"index.html" ]]" to see if referenced page has to be re-rendered.
\item "[[ late define lgc-load-render1 ( x , s ) as newline
let << true ,, << true ,, f >> >> = x in newline
let s = lgc-progress ( if f head = NULL then !"Not found" else !"Found" , 4 , s ) in newline
if f head != NULL then lgc-load-fetch ( s ) else newline
let r = s [[ !"reference" ]] in newline
let n = lgc-ref2vt ( r , s ) in newline
let s = lgc-progress ( !"Rendering " :: n , 3 , s ) in newline
lgc-exec-events ( s , lgc-render ( x , s ) ) end define ]]"
Re-render referenced page if needed. Else go on fetching.
\item "[[ late define lgc-load-codify-closure ( r , s ) as newline
let c = s [[ !"cluster" ]] in newline
let c = lgr-cluster-closure ( r , c ) in newline
s [[ !"cluster" -> c ]] end define ]]"
Supplement "[[ s [[ !"cluster" ]] [[ r ]] ]]" with racks and "[[ s [[ !"cluster" ]] [[ r ]] [[ r ]] [[ !"cluster" ]] ]]" with caches of transitively referenced pages.
\end{statements}
\subsection{Trisecting}
The "[[ lgw-trisect ( v ) ]]" function splits the list "[[ v ]]" of singleton strings in lgw format into bibliography, dictionary, and flat body and returns a cache "[[ c ]]" with the following properties:
\begin{itemize}
\item "[[ c [[ 0 ]] ]]" is the reference "[[ r ]]" of the page.
\item "[[ c [[ r ]] [[ !"vector" ]] ]]" is the vector "[[ v ]]" of the page.
\item "[[ c [[ r ]] [[ !"bibliography" ]] ]]" is the bibliography of the page.
\item "[[ c [[ r ]] [[ !"dictionary" ]] ]]" is the dictionary of the page.
\item "[[ c [[ r ]] [[ !"body" ]] ]]" is the flat (unparsed) body of the page.
\end{itemize}
\begin{statements}
\item "[[ late define lgw-parse-string ( v ) as newline
let n :: v = parse-card ( v ) in newline
lgw-parse-string1 ( n , v , true ) end define ]]"
Parse the string at the beginning of "[[ v ]]" and return "[[ c :: v prime ]]" where "[[ c ]]" is the string and "[[ v prime ]]" is the unparsed part of "[[ v ]]". A string comprises a cardinal "[[ n ]]" followed by "[[ n ]]" bytes.
\item "[[ late define lgw-parse-string1 ( n , v , r ) as newline
if n = 0 then vt2vector ( r ) :: v else newline
if v atom then exception else newline
lgw-parse-string1 ( n - 1 , v tail , r :: v head ) end define ]]"
Accumulate the result of "[[ lgw-parse-string ( v ) ]]" in "[[ r ]]".
\item "[[ late define lgw-parse-bibliography ( v ) as lgw-parse-bibliography1 ( true , v ) end define ]]"
Parse a bibliography where a bibliography is a list of strings terminated by an empty string.
\item "[[ late define lgw-parse-bibliography1 ( r , v ) as newline
let b :: v = lgw-parse-string ( v ) in newline
if b = !"". then reverse ( r ) :: v else newline
lgw-parse-bibliography1 ( b + 0 :: r , v ) end define ]]"
Accumulate the result of "[[ lgw-parse-bibliography ( v ) ]]" in "[[ r ]]".
Using "[[ b + 0 ]]" instead of "[[ b ]]" above forces bibliographic entries to be stored as integers rather than vectors internally. That is done to circumvent a bug in the logiweb-0.1.x compiler.
\item "[[ late define lgw-parse-dictionary ( v ) as lgw-parse-dictionary1 ( true [[ 0 -> 0 ]] , v ) end define ]]"
Parse a dictionary where a dictionary is a list "[[ << i_1 ,, a_1 ,, ... >> ]]" of pairs of cardinals ended by a zero cardinal. Return the dictionary as an array which maps indices "[[ i_n ]]" to arities "[[ a_n ]]". It is understood that index 0 maps to arity 0.
\item "[[ late define lgw-parse-dictionary1 ( r , v ) as newline
let i :: v = parse-card ( v ) in newline
if i = 0 then r :: v else newline
let a :: v = parse-card ( v ) in newline
lgw-parse-dictionary1 ( r [[ i -> a ]] , v ) end define ]]"
Accumulate the result of "[[ lgw-parse-dictionary ( v ) ]]" in "[[ r ]]".
\item "[[ late define lgw-trisect ( V ) as newline
let b :: v = lgw-parse-bibliography ( V ) in newline
let r = b head in newline
let c = true [[ 0 -> r ]] in newline
let c = c [[ << r ,, !"vector" >> => vt2vector ( V ) ]] in newline
let c = c [[ << r ,, !"bibliography" >> => b ]] in newline
let d :: v = lgw-parse-dictionary ( v ) in newline
c [[ << r ,, !"dictionary" >> => d ]] [[ << r ,, !"body" >> => v ]] end define ]]"
Trisect the list "[[ v ]]" of singleton strings in lgw format into bibliography, dictionary, and flat body, and produce a cache "[[ c ]]" containing them.
\end{statements}
\subsection{Codifying}
The "[[ lgw-codify ( r , c , v ) ]]" function takes a reference "[[ r ]]" and a cache "[[ c ]]" as input and codifies the flat body found in it. The "[[ v ]]" parameter defines the verbosity (2- means silent, 3 means some output, 4+ means all output). The cache "[[ c ]]" is expected to contain the following:
\begin{itemize}
\item "[[ c [[ 0 ]] ]]": The reference of the page, i.e.\ a copy of "[[ r ]]"
\item "[[ c [[ r ]] [[ !"bibliography" ]] ]]": The bibliography of the page.
\item "[[ c [[ r ]] [[ !"dictionary" ]] ]]": The dictionary of the page.
\item "[[ c [[ r ]] [[ !"body" ]] ]]": The flat body of the page.
\item "[[ c [[ r ]] [[ !"cluster" ]] [[ r prime ]] ]]": The cache of page "[[ r prime ]]" provided "[[ r prime ]]" is in the transitive irreflexive bibliography of page "[[ r ]]".
\item "[[ c [[ r prime ]] ]]": The rack of page "[[ r prime ]]" provided "[[ r prime ]]" is in the transitive irreflexive bibliography of page "[[ r ]]".
\end{itemize}
The "[[ lgw-codify ( r , c , v ) ]]" function returns a cache "[[ c prime ]]" in which the following branches are changed:
\begin{itemize}
\item "[[ c [[ r ]] [[ !"body" ]] ]]": The body of the page.
\item "[[ c [[ r ]] [[ !"expansion" ]] ]]": The macro expanded body of the page.
\item "[[ c [[ r ]] [[ !"codex" ]] ]]": The codex of the page.
\item "[[ c [[ r ]] [[ !"code" ]] ]]": Compiled versions of value definitions in the codex.
\item "[[ c [[ r ]] [[ !"diagnose" ]] ]]": The diagnose of the page. The diagnose is maptagged and is computed lazily. In other words, verification is not done by "[[ lgw-codify ( r , c , v ) ]]". Rather, verification occurs when evaluation of the diagnose is forced by untagging it. The page is considered correct if the untagged diagnose is "[[ true ]]".
\end{itemize}
\begin{statements}
\item "[[ late define lgw-codify ( r , c , v ) as newline
lgc-print ( 4 <= v , " Unpack" ) .then. newline
let c = lgw-codify-unpack ( r , c ) in newline
lgc-print ( 4 <= v , " Initialize" ) .then. newline
let c = lgw-codify-initialize ( r , c ) in newline
lgw-codify1 ( r , c , v , 1 ) end define ]]"
\item "[[ late define lgw-codify1 ( r , c , v , n ) as newline
lgc-print ( 3 <= v , lgc-ordinal ( n ) :: " reading" ) .then. newline
lgc-print ( 4 <= v , " Compile" ) .then. newline
let c = compile ( c ) in newline
lgc-print ( 4 <= v , " Expand" ) .then. newline
let t = lgw-codify-expand ( r , c ) in newline
let C = c [[ << r ,, !"expansion" >> => t ]] in newline
let C = C [[ << r ,, !"codex" >> => true ]] in newline
lgc-print ( 4 <= v , " Harvest" ) .then. newline
let C = lgw-codify-harvest ( r , t , c , C ) in newline
lgc-print ( 4 <= v , " Compare" ) .then. newline
if c [[ r ]] [[ !"codex" ]] = C [[ r ]] [[ !"codex" ]] then compile ( C ) else newline
lgw-codify1 ( r , C , v , n + 1 ) end define ]]"
\end{statements}
\subsection{Unpacking}
The "[[ lgw-codify-unpack ( r , c ) ]]" function parses the flat body "[[ c [[ !"body" ]] ]]", stores the result back in "[[ c [[ !"body" ]] ]]", and returns the modified "[[ c ]]".
\begin{statements}
\item "[[ late define lgw-codify-unpack ( r , c ) as newline
let u = lgw-codify-unpacker ( r , c ) in newline
if u then lgw-codify-unpack1 ( r , c ) else newline
let v = c [[ r ]] [[ !"body" ]] in newline
let c = c [[ << r ,, !"body" >> => true ]] in newline
let t = prune ( ( eval ( u third , true , c ) apply c maptag apply v maptag ) untag , c ) in newline
c [[ << r ,, !"body" >> => t ]] end define ]]"
\item "[[ late define lgw-codify-unpacker ( r , c ) as newline
let b = c [[ r ]] [[ !"bibliography" ]] first in newline
if b then true else c [[ b ]] [[ !"codex" ]] [[ b ]] [[ 0 ]] [[ 0 ]] [[ !"unpack" ]] end define ]]"
\item "[[ late define lgw-codify-unpack1 ( r , c ) as newline
let v = c [[ r ]] [[ !"body" ]] in newline
let a = lgw-make-dict ( r , c ) in newline
let t = lgw-parse-tree ( a , << r >> , v ) in newline
c [[ << r ,, !"body" >> => t head ]] end define ]]"
Extract the flat body of page "[[ r ]]" from the cache "[[ c ]]". Parse it into a tree and store it back as the body of the tree. For the sake of efficiency, the bibliography "[[ b ]]" and the length "[[ l ]]" of the bibliography are computed once. Debugging information is accumulated in "[[ d ]]".
\item "[[ late define lgw-make-dict ( r , c ) as newline
let b = c [[ r ]] [[ !"bibliography" ]] in newline
let l = length ( b ) in lgw-make-dict1 ( 0 , b , l , c , true ) end define ]]"
Convert the bibliography "[[ b ]]" which has length "[[ l ]]" into an array "[[ a ]]" for which "[[ a [[ R + l * i ]] = << r ,, i ,, n >> ]]" where "[[ R ]]" is the relative reference of a symbol (the position of the reference in the bibliography), "[[ i ]]" is the index of a symbol, "[[ r ]]" is the absolute references of the symbol, and "[[ n ]]" is the arity of the symbol.
\item "[[ late define lgw-make-dict1 ( R , b , l , c , a ) as newline
if b atom then a else newline
let a = lgw-make-dict2 ( R , b head , l , c [[ b head ]] [[ !"dictionary" ]] , a ) in newline
lgw-make-dict1 ( R + 1 , b tail , l , c , a ) end define ]]"
\item "[[ late define lgw-make-dict2 ( R , r , l , d , a ) as newline
if d atom then a else newline
if .not. d head intp then lgw-make-dict2 ( R , r , l , d head , lgw-make-dict2 ( R , r , l , d tail , a ) ) else newline
let i = d head in newline
a [[ 1 + R + l * i -> << r ,, i ,, d tail >> ]] end define ]]"
Add the symbols in the dictionary "[[ d ]]" to the array "[[ a ]]" described under "[[ lgw-make-dict ( r , c ) ]]". "[[ R ]]" and "[[ r ]]" are the relative and absolute reference, respectively, of the page being processed and "[[ l ]]" is the length of the bibliography.
\item "[[ late define lgw-parse-tree ( a , d , v ) as newline
let c :: v = parse-card ( v ) in newline
if c = 0 then lgw-parse-tree-string ( d , v ) else newline
let << r ,, i ,, n >> = a [[ c ]] in newline
let t = lgw-parse-tree* ( a , n , 0 , d , v , true ) in newline
( << r ,, i ,, d >> :: t head ) :: t tail end define ]]"
Convert the flat tree "[[ v ]]" into a tree. "[[ d ]]" must be debugging information to be installed in the tree. "[[ a ]]" must be the value of "[[ lgw-make-dict ( r , c ) ]]" where "[[ c ]]" is the cache of the page.
\item "[[ late define lgw-parse-tree* ( a , n , i , d , v , r ) as newline
if i >= n then reverse ( r ) :: v else newline
let t :: v = lgw-parse-tree ( a , i :: d , v ) in newline
lgw-parse-tree* ( a , n , i + 1 , d , v , t :: r ) end define ]]"
Parse "[[ n ]]" trees in the vector "[[ v ]]". "[[ d ]]" and "[[ a ]]" are described under the previous function. "[[ i ]]" counts from "[[ 0 ]]" to "[[ n - 1 ]]". The list of trees is accumulated in "[[ t ]]" in reverse order.
\item "[[ late define lgw-parse-tree-string ( d , v ) as newline
let s :: v = lgw-parse-string ( v ) in newline
<< << 0 ,, s ,, d >> >> :: v end define ]]"
\end{statements}
\subsection{Initializing}
The "[[ lgw-codify-initialize ( r , c ) ]]" function stores an initial codex in "[[ c [[ r ]] [[ !"codex" ]] ]]" and returns the modified cache "[[ c ]]".
\begin{statements}
\item "[[ late define lgw-codify-initialize ( r , c ) as newline
if c [[ r ]] [[ !"bibliography" ]] tail pairp then c else newline
c [[ << r ,, !"codex" ,, r ,, 1 ,, 0 ,, !"definition" >> => << << 0 ,, !"proclaim" >> >> ]] end define ]]"
\end{statements}
\subsection{Macro expanding}
The "[[ lgw-codify-expand ( r , c ) ]]" function macro expands "[[ c [[ r ]] [[ !"body" ]] ]]" and returns the expansion.
\begin{statements}
\item "[[ late define lgw-codify-expand ( r , c ) as newline
let d = c [[ r ]] [[ !"codex" ]] [[ r ]] [[ 0 ]] [[ 0 ]] [[ !"macro" ]] in newline
if d pairp then lgw-codify-expand1 ( d , c ) else newline
let b = c [[ r ]] [[ !"bibliography" ]] first in
if b then c [[ r ]] [[ !"body" ]] else newline
let d = c [[ b ]] [[ !"codex" ]] [[ b ]] [[ 0 ]] [[ 0 ]] [[ !"macro" ]] in newline
if d pairp then lgw-codify-expand1 ( d , c ) else c [[ r ]] [[ !"body" ]] end define ]]"
Macro expand page "[[ r ]]" in cache "[[ c ]]". First look up the macro expander of the page itself and call it "[[ d ]]". If found, apply it. Else look up the macro expander of the bed page "[[ b ]]" and call it "[[ d ]]". If found, apply it. Else, default to the identity macro expander and return the body of the page.
\item "[[ late define lgw-codify-expand1 ( d , c ) as newline
let f = eval ( d third , true , c ) in newline
let t = ( f apply c maptag ) untag in newline
prune ( t , c ) end define ]]"
Evaluate right hand side of the macro expander definition "[[ d ]]". Then apply the result to the cache "[[ c ]]" and prune the outcome.
\end{statements}
\subsection{Harvesting}
The "[[ lgw-codify-harvest ( r , t , c , C ) ]]" function harvests the term "[[ t ]]" from page "[[ r ]]" in cache "[[ c ]]" and accumulate the result in the new cache "[[ C ]]" which is eventually returned.
\begin{statements}
\item "[[ late define lgw-codify-harvest ( r , t , c , C ) as newline
let d = c [[ t ref ]] [[ !"codex" ]] [[ t ref ]] [[ t idx ]] [[ 0 ]] [[ !"definition" ]] in newline
if d then lgw-codify-harvest* ( r , t tail , c , C ) else newline
if d idx = !"hide" then C else newline
if d idx = !"proclaim" then lgw-codify-proclaim ( r , t , C ) else newline
if ( d idx = !"define" .or. d idx = !"introduce" ) then lgw-codify-define ( r , t , c , C ) else C end define ]]"
Harvest the term "[[ t ]]" from page "[[ r ]]" in cache "[[ c ]]". Accumulate the result in the new cache "[[ C ]]". In the first line, store the definition aspect of the root of the tree "[[ t ]]" in "[[ d ]]". If "[[ d ]]" is empty then recurse.
\item "[[ late define lgw-codify-harvest* ( r , t , c , C ) as newline
if t atom then C else lgw-codify-harvest* ( r , t tail , c , lgw-codify-harvest ( r , t head , c , C ) ) end define ]]"
\item "[[ late define lgw-proclaim-array as true
[[ newline !"apply" -> 2 :: !"value" ]]
[[ newline !"lambda" -> 2 :: !"value" ]]
[[ newline !"true" -> 0 :: !"value" ]]
[[ newline !"if" -> 3 :: !"value" ]]
[[ newline !"quote" -> 1 :: !"value" ]]
[[ newline !"proclaim" -> 2 :: !"definition" ]]
[[ newline !"define" -> 3 :: !"definition" ]]
[[ newline !"introduce" -> 3 :: !"definition" ]]
[[ newline !"hide" -> true :: !"definition" ]] end define ]]"
\item "[[ late define lgw-codify-proclaim ( r , t , C ) as newline
if t second ref != 0 then C else newline
if t first ref != r then t raise else newline
let d = lgw-proclaim-array [[ t second idx ]] in newline
if d .or. .not. d head .and. d head != length ( t first tail ) then newline
t raise else newline
if t second ref != 0 then t raise else newline
C [[ << r ,, !"codex" ,, r ,, t first idx ,, 0 ,, d tail >> => << << 0 ,, t second idx >> >> ]] end define ]]"
Add the proclamation "[[ t ]]" from page "[[ r ]]" to the cache "[[ C ]]".
\item "[[ late define lgw-tree2aspect ( t , c ) as newline
if t ref = 0 then t else c [[ t ref ]] [[ !"codex" ]] [[ t ref ]] [[ t idx ]] [[ 0 ]] [[ !"message" ]] third end define ]]"
Convert the term "[[ t ]]" into the aspect represented by the term according to the cache "[[ c ]]". If "[[ t ]]" is a string, then "[[ t ]]" is itself that aspect. Otherwise, the aspect represented by "[[ t ]]" is looked up in "[[ c ]]". If "[[ t ]]" does not represent any aspect the "[[ true ]]" is returned.
\item "[[ late define lgw-codify-define ( r , t , c , C ) as newline
let a = lgw-tree2aspect ( t first , c ) in newline
if a then C else newline
C [[ << r ,, !"codex" ,, t second ref ,, t second idx ,, a ref ,, a idx >> => t ]] end define ]]"
Find the aspect of the definition "[[ t ]]" from page "[[ r ]]" in the cache "[[ c ]]" and then accumulate the definition in "[[ C ]]".
\end{statements}
" ]"\section{Grammars}\label{sec:Grammars}"[ "
The Logiweb parser "[[ lgc-parse ( g , a ) ]]" converts a list "[[ a ]]" of tokens to a parse tree as specified by the grammar "[[ g ]]". We describe tokens and grammars in more detail in the following sections.
\subsection{Tokens}
A Logiweb token can be one of the following:
\begin{itemize}
\item A byte
\item A string
\item A binary include
\item A text include
\item An \verb+""!""!S+ escape sequence
\item An \verb+""!""!C+ escape sequence
\item An \verb+""!""!N+ escape sequence
\end{itemize}
Bytes from the source text are represented as singleton strings which in turn are represented by integers. As an example, a capital A in the input is represented by the singleton string "[[ !"A" ]]" which in turn is represented by the integer "[[ 65 + 256 = 321 ]]" where 65 is the Unicode UTF-8 representation of a capital A. Hence, a capital A in the input is represented by one token and that token has value 321.
As another example, a capital \AE\ (Unicode \verb+C6+) is represented by a \verb+C3+ (195) byte followed by a \verb+86+ (134) byte in UTF-8. Thus, a capital \AE\ is represented by two tokens, namely a "[[ 195 + 256 = 451 ]]" token followed by a "[[ 134 + 256 = 390 ]]" token.
Note that in the Logiweb programming language, a single character is represented by one or more tokens. This is opposite to languages like C where a token represents one or more characters.
A string is represented as a pair "[[ lgc-esc-- :: S ]]" where "[[ S ]]" is the string. As an example, \verb+""!ABC""!+ in the source text is represented by a token with value "[[ lgc-esc-- :: !"ABC" ]]". Recall that the value of "[[ lgc-esc-- ]]" is 45 where 45 is the Unicode of a hyphen.
A binary include is represented as the pair "[[ lgc-esc-# :: S ]]" where "[[ S ]]" is the name of the file to be included. Such a binary include represents a string whose bytes are taken verbatim from the included file. As an example, \verb+""!""!#ABC""!+ in the source text is represented by a token with value "[[ lgc-esc-# :: !"ABC" ]]". That token represents a string whose bytes are taken from the file named \verb+ABC+.
A text include is represented as the pair "[[ lgc-esc-$ :: S ]]" where "[[ S ]]" is the name of the file to be included. Such a text include represents a string whose bytes are taken from the included file except that carriage return and line feed characters are parsed as in Section \ref{sec:ByteParsing}. Note that in order to ensure cross platform interoperability, Logiweb marks the end of a line by a line feed character regardless of what the underlying operating system does.
The escape sequences \verb+""!""!S+ is represented by a token with value "[[ lgc-esc-S ]]" which in turn equals the Unicode of the character S. An "[[ lgc-esc-S ]]" token represents the entire source text represented as a string. This corresponds to a binary include of the source file.
The escape sequences \verb+""!""!C+ is represented by a token with value "[[ lgc-esc-C ]]" which in turn equals the Unicode of the character C. An "[[ lgc-esc-C ]]" token represents a list of charge definitions.
The escape sequences \verb+""!""!N+ is represented by a token with value "[[ lgc-esc-N ]]" which in turn equals the Unicode of the character N. An "[[ lgc-esc-N ]]" token represents a list of name definitions.
\subsection{Source grammars}
In the Logiweb programming language, grammars are defined in the source text. During lexical analysis, the grammar is extracted from the source file and converted into a \emph{trie} structure. A trie structure is essentially defined recursively as an array which maps integers to trie structures.
Hence, grammars exist in two forms: externally in the source text and internally as trie structures. We shall refer to these two forms as source and trie grammars, respectively.
A source grammar may look thus:
\begin{verbatim}
""!""!D 0
x
y
z
""!""!D 4
""! + ""!
\end{verbatim}
The source grammar above defines four constructs, "[[ x ]]", "[[ y ]]", "[[ z ]]", and "[[ *** + *** ]]".
Furthermore, a source grammar assigns an \emph{charge} (c.f.\ Section \ref{sec:Associativities}) to each construct. The source grammar above assigns charge 0 to "[[ x ]]", "[[ y ]]", and "[[ z ]]" and charge 4 to "[[ *** + *** ]]".
\subsection{Associativities}\label{sec:Associativities}
A charge consists of a sequence of integers separated by periods. As an example, 7.9.13 is a charge.
Trailing zeros are considered insignificant so 1.2, 1.2.0, and 1.2.0.0.0 represent the same charge. On the other hand, 1.1 and 1.10 are considered different since the 0 in 1.10 is part of the integer 10.
Associativities are ordered lexically. As an example, we have
\begin{verbatim}
1 < 1.5 < 2.-10 < 2.-9 < 2.-1 < 2 < 2.1 < 2.9 < 2.10 < 3
\end{verbatim}
Constructs with low charge bind tighter than constructs with high charge (so constructs with low charge has high priority and vice versa).
A charge is said to be \emph{even} (\emph{odd}) if its last, non-zero number is even (odd). As examples, 1.2.-10 and 1.2.-10.0 are even and 1.2.-11 and 1.2.-11.0 are odd. As a special case, charge 0 is considered to be even.
Constructs with even charge are \emph{preassociative}. A preassociative construct is left associative in text that runs left to right, right associative in text that runs right to left, top associative in text that runs from top to bottom, counter-clock-wise-associative in text written in clock-wise spirals, and so on. Constructs with odd charge are \emph{postassociative}.
\subsection{Constructs}\label{sec:Constructs}
Each construct has four properties:
\begin{itemize}
\item Name
\item Charge
\item Reference
\item Index
\end{itemize}
A name is a string of characters with the following restrictions:
\begin{itemize}
\item A name cannot contain characters with codes below 32 (space).
\item A name cannot start or end with a space
\item A name cannot have two or more spaces in a row anywhere in the name.
\item A name cannot have two or more double quote characters in a row anywhere in the name.
\item A name must contain at least one character which is not a double quote character.
\end{itemize}
In names, double quote characters serve as placeholders. As an example, consider the construct
\begin{verbatim}
if ""! then ""! else ""!
\end{verbatim}
When using that construct, one must replace the three double quote characters with expressions.
As mentioned in Section \ref{sec:Associativities}, a charge is a sequence of integers separated by periods.
The \emph{reference} of a construct is a cardinal (i.e.\ a non-negative integer). The reference of a construct is equal to the reference of the \emph{home page} of the construct. The home page of a construct is the page on which the construct is defined.
The \emph{index} of a construct is also a cardinal. Constructs on a page a numbered consequtively, starting with 1. In addition, every page has a \emph{page construct} whose name is the name of the page. The page construct has index 0. The page construct is defined by a \verb+""!""!P+ escape sequence.
Each construct also has a number of derived properties:
\begin{description}
\item[arity] The arity of a construct equals the total number of double quote characters in the name of the construct.
\item[Pre-openness] A construct is pre-open if its name begins with a double quote character and pre-closed otherwise.
\item[Post-closedness] A construct is post-open if its name ends with a double quote character and post-closed otherwise.
\item[Fixity] We shall say that a construct is open if it is pre- and post-open and that it is closed if it is pre- and post-closed. We shall say that a construct is prefix if it is pre-closed and post-open and suffix if it is pre-open and post-closed.
\end{description}
Some examples read:
\begin{tabular}{|l|l|l|l|}
\hline
Construct & Pre-openness & Post-openness & Fixity \\
\hline
\verb/""! + ""!/ & Pre-open & Post-open & Open \\
\verb+( ""! )+ & Pre-closed & Post-closed & Closed \\
\verb+if ""! then ""! else ""!+ & Pre-closed & Post-open & Prefix \\
\verb+""! factorial+ & Pre-open & Post-closed & Suffix \\
\hline
\end{tabular}
Internally, we represent constructs by tuples "[[ << r ,, i ,, a ,, p ,, n ,, b ,, R >> ]]" where
\begin{description}
\item "[[ r ]]" is the reference of the construct (a cardinal)
\item "[[ i ]]" is the index of the construct (a cardinal)
\item "[[ a ]]" is the charge of the construct expressed as a list of integers. Charge 0 is represented by the empty list.
\item "[[ p ]]" is the post-openness of the construct expressed as a Boolean ("[[ p prime = true ]]" means that the construct is post-open.
\item "[[ n ]]" is the name of the construct expressed as a list of singleton strings.
\item "[[ b ]]" is the binary encoding of the construct used in Logiweb vectors.
\item "[[ R ]]" is the relative reference of the construct (the position of the construct in the bibliography).
\end{description}
In Logiweb vectors, constructs are represented by byte sequences. Suppose a page references "[[ N ]]" pages (including reference zero). The construct with index "[[ i ]]" from relative reference "[[ R ]]" is represented by the number "[[ 1 + i + N * R ]]" expressed as a list of septets. A cardinal is expressed as a list of septets by expressing the cardinal little endian base 128 and then adding 128 to all bytes except the last.
\subsection{Qualified constructs}
When importing constructs from different pages, the problem may arise that distinct constructs accidentally have the same name. To cope with that, the programmer may decide to \emph{qualify} constructs.
A construct is qualified by splicing a qualifier (a string) into its name. A qualifier must satisfy the following:
\begin{itemize}
\item A qualifier cannot contain characters with codes below 32 (space).
\item A qualifier cannot contain double quote characters.
\end{itemize}
A qualifier is spliced into the following location:
\begin{itemize}
\item If the construct does not start with a double quote then the qualifier is spliced in in front of the first character.
\item If the construct starts with a quote followed by a non-space then the qualifier is spliced in between the first and second character.
\item If the construct starts with a quote followed by a space followed by a non-quote then the qualifier is spliced in between the second and third character.
\item If the construct starts with a quote followed by a space followed by a quote then the space is doubled and the qualifier is spliced in between the two spaces.
\end{itemize}
Once the qualifier is spliced in, duplicate spaces in the result are reduced to single spaces and leading and trailing spaces are removed. Because of this, leading spaces in qualifiers have effect only on constructs which start with a quote followed by a non-space. In contrast, trailing spaces in qualifiers do have effect on all constructs except those which start with a double quote followed by a space followed by a double quote. Some examples are given in the following table.
\addvspace{\abovedisplayskip}
\begin{tabular}{|l|l|l|}
\hline
Qualifier & Name & Qualified name \\
\hline
\verb*/ base / & \verb*/if ""! then ""! else ""!/ & \verb*/base if ""! then ""! else ""!/ \\
\verb*/base / & \verb*/if ""! then ""! else ""!/ & \verb*/base if ""! then ""! else ""!/ \\
\verb*/ base/ & \verb*/if ""! then ""! else ""!/ & \verb*/baseif ""! then ""! else ""!/ \\
\verb*/base/ & \verb*/if ""! then ""! else ""!/ & \verb*/baseif ""! then ""! else ""!/ \\
\hline
\verb*/ base / & \verb*/""! + ""!/ & \verb*/""! base + ""!/ \\
\verb*/base / & \verb*/""! + ""!/ & \verb*/""! base + ""!/ \\
\verb*/ base/ & \verb*/""! + ""!/ & \verb*/""! base+ ""!/ \\
\verb*/base/ & \verb*/""! + ""!/ & \verb*/""! base+ ""!/ \\
\hline
\verb*/ base / & \verb*/""!,""!/ & \verb*/""! base ,""!/ \\
\verb*/base / & \verb*/""!,""!/ & \verb*/""!base ,""!/ \\
\verb*/ base/ & \verb*/""!,""!/ & \verb*/""! base,""!/ \\
\verb*/base/ & \verb*/""!,""!/ & \verb*/""!base,""!/ \\
\hline
\verb*/ base / & \verb*/""! ""!/ & \verb*/""! base ""!/ \\
\verb*/base / & \verb*/""! ""!/ & \verb*/""! base ""!/ \\
\verb*/ base/ & \verb*/""! ""!/ & \verb*/""! base ""!/ \\
\verb*/base/ & \verb*/""! ""!/ & \verb*/""! base ""!/ \\
\hline
\end{tabular}
\addvspace{\belowdisplayskip}
The "[[ lgc-splice ( q , c ) ]]" splices the qualifier "[[ q ]]" into the construct "[[ c ]]":
\begin{statements}
\item "[[ late define lgc-panic ( x ) as trace ( x ) .then. bottom end define ]]"
\item "[[ late define lgc-splice ( q , c ) as newline
if c atom then lgc-panic ( !"Internal error 1 in lgc-splice" ) else newline
if c head != QQ then append ( lgc-left-trim ( q ) , c ) else newline
let c = c tail in newline
if c atom then lgc-panic ( !"Internal error 2 in lgc-splice" ) else newline
if c head != SP then QQ :: append ( q , c ) else newline
let c = c tail in newline
if c head != QQ then QQ :: SP :: append ( lgc-left-trim ( q ) , c ) else newline
QQ :: SP :: append ( lgc-trim ( q ) , SP :: c ) end define ]]"
\end{statements}
\subsection{Specifying qualification in source files}
When referencing a page, all constructs of that page are \emph{imported} and made available for use. As an example, the following line references a page named page1 and makes all constructs of that page available:
\begin{verbatim}
""!""!R page1
\end{verbatim}
As another example, the following line references a page named page2 and qualifies all its constructs by \verb*+ qual2 +:
\begin{verbatim}
""!""!R qual2 ""! page2
\end{verbatim}
One may specify more than one qualifier:
\begin{verbatim}
""!""!R qual3 ""! Qual3 ""! page3
\end{verbatim}
If page3 defines a construct named \verb*/""! + ""!/ then the reference above allows to reference that construct as \verb*/""! qual3 + ""!/ or \verb*/""! Qual3 + ""!/ but not as \verb*/""! + ""!/.
One may specify an empty qualifier:
\begin{verbatim}
""!""!R""! qual4 ""! Qual4 ""! page4
\end{verbatim}
If page4 defines a construct named \verb*/""! + ""!/ then the reference above allows to reference that construct as \verb*/""! qual4 + ""!/ or \verb*/""! Qual4 + ""!/ or \verb*/""! + ""!/.
When specifying an empty qualifier, the empty qualifier must be first in the list: if one tries to put it later then one is forced to put two double quote characters next to each other, yeilding an escape sequence.
The page construct also allows qualification:
\begin{verbatim}
""!""!P""! myqual ""! Myqual ""! mypage
\end{verbatim}
The line above defines a page construct named \verb+mypage+ and qualifies all locally defined constructs by the empty string and \verb+myqual+ and \verb+Myqual+. That allows to define a construct named e.g.\ \verb*/""! + ""!/ and to refer to it is \verb*/""! + ""!/, \verb*/""! myqual + ""!/, and \verb*/""! Myqual + ""!/.
In case \verb+mypage+ is later on referenced from some other page, then only \verb*/""! + ""!/ will be imported from \verb+mypage+ to the referencing page. The referencing will not import constructs named \verb*/""! myqual + ""!/ and \verb*/""! Myqual + ""!/. But the referencing page may specify its own qualifications.
\subsection{Grammar ambiguity}\label{sec:GrammarAmbiguity}
The Logiweb language allows the user to define ambiguous grammars. Actually, the compiler makes no attempt to decide whether or not grammars are ambiguous. Rather, the compiler parses the source text according to the grammar and complains if it cannot parse the source or if it can parse the source in more than one way. In the latter case, the source is said to be ambiguous.
A grammar can be ambiguous in a particularly visible way: it can contain multiple constructs sharing the same name. As an example, suppose Page1 and Page2 both define a construct named \verb*/""! + ""!/ and that they are referenced thus:
\begin{verbatim}
""!""!R""! Qual1""! Page1
""!""!R""! Qual2""! Page2
\end{verbatim}
The resulting grammar will know constructs named \verb*/""! + ""!/, \verb*/""! Qual1+ ""!/, and \verb*/""! Qual2+ ""!/. However, \verb*/""! + ""!/ will be the name of two, distinct constructs: one from Page1 and one from Page2. If the programmer writes e.g.\ \verb*/2 Qual1+ 3 Qual2+ 4/ then the first plus will be from Page1 and the second from Page2. But if the programmer writes e.g.\ \verb*/2 + 3/ then the programmer will get an error message stating that the source text can be parsed in more than one way.
\subsection{Trie grammars}
Consider the following source grammar:
\begin{verbatim}
""!""!D 0
abc
abd
""!""!D 4
""! + ""!
\end{verbatim}
When the Logiweb compiler reads a grammar like the one above, it constructs a trie grammar "[[ g ]]" with the following properties:
"[[[ array ( left,left,left )
g [[ !"a" ]] [[ !"b" ]] [[ !"c" ]] [[ 0 ]] & %% = %% & << *** >> \\
g [[ !"a" ]] [[ !"b" ]] [[ !"d" ]] [[ 0 ]] & %% = %% & << *** >> \\
g [[ !""-""!" ]] [[ !" " ]] [[ !"+" ]] [[ !" " ]] [[ !""-""!" ]] [[ 0 ]] & %% = %% & << *** >>
end array ]]]"
The value of "[[ g [[ !"a" ]] [[ !"b" ]] [[ !"c" ]] [[ 0 ]] ]]" is a list of \emph{grammar nodes}. The list has one node for each construct named \verb+abc+. In a typical situation, only one construct is named \verb+abc+ and the list will have only one node. However, as mentioned in Section \ref{sec:GrammarAmbiguity}, more than one construct can have the same name, in which case the list will have more than one node.
\subsection{Grammar nodes}
Each grammar node has form "[[ << r ,, i ,, C ,, p ,, n ,, b >> ]]" which specifies the reference, index, charge, post-openness, name, and byte representation of the construct, c.f.\ Section \ref{sec:Constructs}.
When the Logiweb compiler translates a source text, it loads all referenced pages and assembles a grammar comprising all constructs of all referenced pages plus all constructs defined by the page itself. All constructs are qualified as specified in \verb+""!""!P+ and verb+""!""!R+ statements before they enter the grammar.
The following functions may be used for accessing individual fields of grammar nodes.
\begin{statements}
\item "[[ late define lgc-node2ref ( n ) as n zeroth end define ]]"
\item "[[ late define lgc-node2idx ( n ) as n first end define ]]"
\item "[[ late define lgc-node2charge ( n ) as n second end define ]]"
\item "[[ late define lgc-node2open ( n ) as n third end define ]]"
\item "[[ late define lgc-node2closed ( n ) as .not. n third end define ]]"
\item "[[ late define lgc-node2name ( n ) as n fourth end define ]]"
\item "[[ late define lgc-node2binary ( n ) as n fifth end define ]]"
\item "[[ late define lgc-node2relref ( n ) as n sixth end define ]]"
\end{statements}
\subsection{Grammar construction}
The "[[ lgc-grammar1 ( x , s ) ]]" function adds the following to the state:
\begin{description}
\item "[[ s [[ !"grammar" ]] ]]" The grammar containing constructs from the page being translated and its directly referenced pages.
\item "[[ s [[ !"dictionary" ]] [[ i ]] ]]" Association list whose entires have form "[[ i :: a ]]" where "[[ i ]]" is the index of a construct of the current page and "[[ a ]]" is its associated arity. Sorted in descending "[[ i ]]".
\item "[[ s [[ !"binary" ]] [[ i ]] ]]" The binary representation of the construct with index "[[ i ]]" from the page being translated.
\item "[[ s [[ !"name" ]] [[ i ]] ]]" The name of the construct with index "[[ i ]]" from the page being translated.
\item "[[ s [[ !"charge" ]] [[ i ]] ]]" The charge of the construct with index "[[ i ]]" from the page being translated.
\item "[[ s [[ !"index" ]] ]]" The next unused index.
\end{description}
The "[[ x ]]" parameter allows "[[ lgc-grammar1 ( x , s ) ]]" to be invoked using by an exec event, but "[[ x ]]" is ignored as it is not supposed to contain meaningfull events.
\begin{statements}
\item "[[ late define lgc-grammar ( s ) as newline
lgc-exec-events ( s , lgc-grammar1 ( x , s ) ) end define ]]"
\item "[[ late define lgc-grammar1 ( x , s ) as newline
let b = reverse ( s [[ !"refbib" ]] ) in newline
let s = s [[ !"refbib" -> b ]] in newline
let s = s [[ !"grammar" -> lgc-grammar-init ]] in newline
let s = lgc-grammar-add-bib ( s [[ !"bib" ]] , 1 , b , s ) in newline
let e :: s = lgc-grammar-add-def ( s ) catch in newline
if e then lgc-report-messages ( s ) else newline
let s = lgc-progress ( !"Parsing" , 3 , s ) in newline
lgc-exec-events ( s , lgc-parse1 ( x , s ) ) end define ]]"
\item "[[ late define lgc-grammar-init as newline
let g = true in newline
let g = lgc-grammar-init1 ( lgc-esc-- , g ) in newline
let g = lgc-grammar-init1 ( lgc-esc-# , g ) in newline
let g = lgc-grammar-init1 ( lgc-esc-$ , g ) in newline
let g = lgc-grammar-init1 ( lgc-esc-C , g ) in newline
let g = lgc-grammar-init1 ( lgc-esc-N , g ) in newline
let g = lgc-grammar-init1 ( lgc-esc-S , g ) in g end define ]]"
\item "[[ late define lgc-grammar-init1 ( x , g ) as newline
g [[ << x ,, 0 >> => << << true ,, true ,, true ,, false >> >> ]] end define ]]"
\end{statements}
\subsection{Constructs from referenced pages}
\begin{statements}
\item "[[ late define lgc-grammar-add-bib ( B , R , b , s ) as newline
if b atom then s else newline
let s = lgc-grammar-add-ref ( B head , R , b head , s ) in newline
lgc-grammar-add-bib ( B tail , R + 1 , b tail , s ) end define ]]"
Add all constructs in referenced pages to the grammar in the state "[[ s ]]". "[[ b ]]" is a list of references of pages which have not yet been added. "[[ B ]]" is the list of the same references but taken from the source text. "[[ B ]]" contains prefixes to be added to constructs. "[[ R ]]" is the reletive reference of the first element of "[[ b ]]".
\item "[[ late define lgc-grammar-add-ref ( P , R , r , s ) as newline
let P = P tail in newline
let P = if P then << true >> else P in newline
let d = s [[ !"cluster" ]] [[ r ]] [[ r ]] [[ !"dictionary" ]] in newline
lgc-grammar-add-dictionary ( P , R , r , d , s ) end define ]]"
Add all constructs from the page with reference "[[ r ]]" and relative reference "[[ R ]]" to the grammar. "[[ P ]]" has form "[[ << n ,, p _ { 1 } ,, ... ,, p _ { n } >> ]]" where "[[ n ]]" is the way the page was referenced in the source text and the "[[ p ]]"'s are prefixes to be added to grammatical constructs.
\item "[[ late define lgc-grammar-add-dictionary ( P , R , r , d , s ) as newline
if d then s else newline
if d head intp then lgc-grammar-add-construct ( P , R , r , d , s ) else newline
lgc-grammar-add-dictionary ( P , R , r , d tail , lgc-grammar-add-dictionary ( P , R , r , d head , s ) ) end define ]]"
Add all constructs in the dictionary "[[ d ]]" to the grammar. "[[ P ]]" is the list of prefixes to be added to constructs and "[[ r ]]" and "[[ R ]]" are the reference and relative reference, respectively, of the page being added.
\item "[[ late define lgc-grammar-add-construct ( P , R , r , d , s ) as newline
let i :: a = d in newline
let n = lgc-aritysymbol2vt ( r , i , a , s ) in newline
let c = lgc-grammar-get-charge ( r , i , s ) in newline
lgc-grammar-add-construct1 ( P , R , i , c , n , s ) end define ]]"
Add the construct in the subdirectory "[[ d ]]" to the grammar. "[[ d ]]" has form "[[ i :: a ]]" where "[[ i ]]" and "[[ a ]]" are the index and arity, respectively, of the construct. The function looks up the name "[[ n ]]" and charge "[[ c ]]" of the construct and adds them to the grammar.
\item "[[ late define lgc-grammar-add-construct1 ( P , R , i , c , n , s ) as newline
if P then s else newline
let q :: P = P in newline
let N = lgc-splice ( q , n ) in newline
let p = ( N last = QQ ) in newline
let b = lgc-card2septet* ( 1 + R + i * ( 1 + length ( s [[ !"bib" ]] ) ) ) in newline
let t = << R ,, i ,, c ,, p ,, N ,, b ,, R >> in newline
let s = push* ( s , !"grammar" :: append ( N , << 0 >> ) , t ) in newline
lgc-grammar-add-construct1 ( P , R , i , c , n , s ) end define ]]".
Splice the qualifiers in the list "[[ P ]]" into the name "[[ n ]]", construct the tuple "[[ t ]]" which represents the construct, and add it to the grammar.
\item "[[ late define lgc-card2septet* ( c ) as newline
if c < septet-base then bt2vector* ( c ) else newline
let c :: r = floor ( c , septet-base ) in newline
bt2vector* ( r + septet-base ) :: lgc-card2septet* ( c ) end define ]]"
Express the cardinal "[[ c ]]" as a list of septets.
\item "[[ late define lgc-aritysymbol2vt ( r , i , a , s ) as newline
lgc-aritysymbol2vt1 ( r , i , a , s [[ !"cluster" ]] [[ r ]] ) end define ]]"
Find or construct a name for the symbol with reference "[[ r ]]", index "[[ i ]]", and arity "[[ a ]]" using the state "[[ s ]]".
\item "[[ late define lgc-aritysymbol2vt1 ( r , i , a , c ) as newline
let N = c [[ r ]] [[ !"codex" ]] [[ r ]] [[ i ]] [[ 0 ]] [[ !"name" ]] in newline
if N then lgc-grammar-default-construct ( r , i , a ) else newline
let << true ,, true ,, true ,, n >> = N in newline
if n ref != 0 then lgc-grammar-default-construct ( r , i , a ) else newline
let n = lgc-trim-contract ( vt2vector* ( n idx ) ) in newline
if n = true .or. n = << QQ >> then lgc-grammar-default-construct ( r , i , a ) else newline
if lgc-grammar-valid-construct ( a , n ) then n else newline
lgc-grammar-default-construct ( r , i , a ) end define ]]"
Find or construct a name for the symbol with reference "[[ r ]]", index "[[ i ]]", and arity "[[ a ]]" using the cache "[[ c ]]".
Look up the name definition "[[ N ]]". If no definition is found, use the default name. If a name definition is found, extract the right hand side "[[ n ]]" of the definition. If "[[ n ]]" is no string, use the default name. Else normalize the string. If the result is one of the two forbidden constructs, use the default name. Finally, if "[[ n ]]" has wrong arity or has two double quotes in row, use the default name. Else use the name "[[ n ]]".
\item "[[ late define lgc-grammar-valid-construct ( a , n ) as newline
if n then a = 0 else newline
let c :: n = n in newline
if c != QQ then lgc-grammar-valid-construct ( a , n ) else newline
n head != QQ .and. lgc-grammar-valid-construct ( a - 1 , n ) end define ]]"
Check that the name "[[ n ]]" has the right arity and does not have two double quotes in a row.
\item "[[ late define lgc-def2charge ( C ) as newline
if C then true else newline
let << true ,, true ,, true ,, c >> = C in newline
if c ref != 0 then true else newline
let c = vt2vector* ( c idx ) in newline
let e :: c = lgc-parse-charge1 ( c , true ) catch in newline
if e then true else c end define ]]"
Convert the charge definition "[[ C ]]" to a charge. Return the default charge "[[ true ]]" if the definition is missing or malformed.
\item "[[ late define lgc-grammar-get-charge ( r , i , s ) as newline
lgc-def2charge ( s [[ !"cluster" ]] [[ r ]] [[ r ]] [[ !"codex" ]] [[ r ]] [[ i ]] [[ 0 ]] [[ !"charge" ]] ) end define ]]"
Look up the charge definition "[[ C ]]" and convert it to a charge (i.e.\ a list of integers).
\end{statements}
\subsection{Default names}
The "[[ lgc-grammar-default-construct ( r , i , a ) ]]" construct returns a default name for the construct with reference "[[ r ]]", index "[[ i ]]", and arity "[[ a ]]".
\begin{statements}
\item "[[ late define lgc-grammar-default-construct ( r , i , a ) as newline
let r = lgc-string2mixed ( vector-subseq ( r , 1 , 3 ) ) in newline
let i = lgc-itoa ( i ) in newline
let a = lgc-grammar-default-arglist ( a ) in newline
vt2vector* ( << !"#[" ,, r ,, !":" ,, i ,, !"]" ,, a >> ) end define ]]"
Construct a default name for the construct with reference "[[ r ]]" and index "[[ i ]]".
\item "[[ late define lgc-grammar-default-arglist ( a ) as newline
if a = 0 then true else newline
<< !" (" ,, lgc-grammar-default-arglist1 ( a - 1 ) ,, !" ""! )" >> end define ]]"
Construct the arglist of the default name of a construct with arity "[[ a ]]".
\item "[[ late define lgc-grammar-default-arglist1 ( a ) as newline
if a = 0 then true else newline
!" ""! ," :: lgc-grammar-default-arglist1 ( a - 1 ) end define ]]"
Auxiliary function for the previous function.
\end{statements}
\subsection{Constructs from source text}
\begin{statements}
\item "[[ late define lgc-grammar-add-def ( s ) as newline
let p = s [[ !"page" ]] in newline
if p then lgc-throw-message ( s , 0 , !"No page name found" ) else newline
let p :: P = p in newline
let P = if P then << true >> else P in newline
let s = s [[ !"index" -> 0 ]] in newline
let s = lgc-grammar-def-construct ( P , true , p , s ) in newline
let d = reverse ( s [[ !"def" ]] ) in newline
lgc-grammar-add-defs* ( P , d , s ) end define ]]"
Find the page name "[[ p ]]", the list "[[ P ]]" of prefixes, and the list "[[ d ]]" of charge sections. Add "[[ p ]]" as the construct with index zero and then add all constructs in "[[ d ]]".
\item "[[ late define lgc-grammar-def-construct ( P , c , n , s ) as newline
let i = s [[ !"index" ]] in newline
let s = s [[ !"index" -> i + 1 ]] in newline
let a = lgc-arity ( n ) in newline
let s = push ( s , !"dictionary" , i :: a ) in newline
let b = lgc-card2septet* ( 1 + i * ( 1 + length ( s [[ !"bib" ]] ) ) ) in newline
let s = s [[ << !"binary" ,, i >> => b ]] in newline
let s = s [[ << !"name" ,, i >> => n ]] in newline
let s = s [[ << !"charge" ,, i >> => lgc-charge2vector* ( c ) ]] in newline
lgc-grammar-add-construct1 ( P , 0 , i , c , n , s ) end define ]]"
Add the construct "[[ n ]]" with charge "[[ c ]]" and prefixes "[[ P ]]" to the state "[[ s ]]". The arity of the construct is found by counting double quote characters in "[[ n ]]" and the index "[[ i ]]" is taken from the state.
\item "[[ late define lgc-arity ( n ) as newline
if n atom then 0 else newline
if n head != QQ then lgc-arity ( n tail ) else newline
1 + lgc-arity ( n tail ) end define ]]"
Count the number of double quote characters in "[[ n ]]".
\item "[[ late define lgc-grammar-add-defs* ( P , d , s ) as newline
if d atom then s else newline
let s = lgc-grammar-add-defs ( P , d head , s ) in newline
lgc-grammar-add-defs* ( P , d tail , s ) end define ]]"
Add the list "[[ d ]]" of charge sections to the grammar.
\item "[[ late define lgc-grammar-add-defs ( P , d , s ) as newline
let p :: d = d in newline
let p = lgc-parse-charge ( p , s ) in newline
lgc-grammar-def-construct* ( P , p , d , s ) end define ]]"
Add the charge section "[[ d ]]" to the grammar.
\item "[[ late define lgc-grammar-def-construct* ( P , p , d , s ) as newline
if d atom then s else newline
let s = lgc-grammar-def-construct ( P , p , d head , s ) in newline
lgc-grammar-def-construct* ( P , p , d tail , s ) end define ]]"
Add all constructs in the list "[[ d ]]" of constructs to the grammar.
\item "[[ late define lgc-parse-charge ( a , s ) as newline
let p :: a = a in newline
let a = lgc-trim-contract ( a ) in newline
let e :: c = lgc-parse-charge1 ( a , true ) catch in newline
if .not. e then c else newline
let t = lgc-grammar-get-tuple ( s , a ) in newline
if t != true .and. t tail = true then t head second else newline
if t = true then newline
lgc-throw-message ( s , p , !"Malformed charge" ) else newline
lgc-throw-message ( s , p , !"Charge refers to ambiguous construct" ) end define ]]"
Remove the position "[[ p ]]" from "[[ a ]]". Then parse the singleton string "[[ a ]]" into a charge. If that fails, assume "[[ a ]]" is a construct and look up the charge of that construct. If no charge is found that way, complain.
\item "[[ late define lgc-parse-charge1 ( a , r ) as newline
let i :: a = lgc-parse-int ( a ) in newline
if a atom then reverse ( lgc-parse-charge2 ( i :: r ) ) else newline
if a head != !"." then exception else newline
lgc-parse-charge1 ( a tail , i :: r ) end define ]]"
Parse the charge "[[ a ]]" and accumulate it in reverse order in "[[ r ]]".
\item "[[ late define lgc-parse-charge2 ( r ) as newline
if r head != 0 then r else lgc-parse-charge2 ( r tail ) end define ]]"
Remove leading zeros. Since this is used on a reversed list, this function is effectively used for removing trailing zeros.
\item "[[ late define lgc-charge2vector* ( c ) as newline
if c atom then << !"0" >> else newline
append ( lgc-itoa ( c head ) , lgc-charge2vector*1 ( c tail ) ) end define ]]"
Convert the charge "[[ c ]]" to a singleton list.
\item "[[ late define lgc-charge2vector*1 ( c ) as newline
if c atom then true else newline
!"." :: append ( lgc-itoa ( c head ) , lgc-charge2vector*1 ( c tail ) ) end define ]]"
Convert the charge tail "[[ c ]]" to a singleton list.
\item "[[ late define lgc-grammar-get-tuple ( s , n ) as newline
get* ( s [[ !"grammar" ]] , n ) [[ 0 ]] end define ]]"
Get the tuple associated to the construct "[[ n ]]" in the grammar.
\end{statements}
" ]"\section{Parser}"[ "
As mentioned in the beginning of Section \ref{sec:Grammars}, the Logiweb parser "[[ lgc-parse ( g , a ) ]]" converts a list "[[ a ]]" of tokens to a parse tree as specified by the grammar "[[ g ]]". We now proceed to define "[[ lgc-parse ( g , a ) ]]" in detail.
\subsection{Token lists}\label{sec:TokenLists}
The list "[[ a ]]" of tokens is installed in "[[ s [[ !"body" ]] ]]" by lexical analysis. Each token in "[[ a ]]" has form "[[ c :: p :: S ]]". A token may have one of the following forms:
\begin{itemize}
\item "[[ c :: p :: true ]]" where "[[ c ]]" is a character represented as a singleton string from the source file and "[[ p ]]" is the position of that character.
\item "[[ lgc-esc-- :: p :: S ]]" where "[[ S ]]" is a string from the source file and "[[ p ]]" is the position of the beginning of the string.
\item "[[ lgc-esc-# :: p :: S ]]" where "[[ S ]]" is the name of a file to be binary included and "[[ p ]]" is the position of the beginning of the include directive.
\item "[[ lgc-esc-$ :: p :: S ]]" where "[[ S ]]" is the name of a file to be text included and "[[ p ]]" is the position of the beginning of the include directive.
\item "[[ lgc-esc-S :: p :: true ]]" where "[[ p ]]" is the position of the \verb+""!""!S+ escape sequence which gave rise to this token.
\item "[[ lgc-esc-C :: p :: true ]]" where "[[ p ]]" is the position of the \verb+""!""!S+ escape sequence which gave rise to this token.
\item "[[ lgc-esc-N :: p :: true ]]" where "[[ p ]]" is the position of the \verb+""!""!S+ escape sequence which gave rise to this token.
\end{itemize}
\subsection{Linear parse trees}
By a \emph{linear parse tree} we shall mean a text in which understood parentheses have been added.
As an example, consider a grammar with the following productions:
\begin{verbatim}
x
y
z
" * "
" + "
\end{verbatim}
If we use brackets to represent understood parentheses, then the linear parse tree of
\begin{verbatim}
x + y + z
\end{verbatim}
may read
\begin{verbatim}
[[[x] + [y]] + [z]]
\end{verbatim}
or
\begin{verbatim}
[[x] + [[y] + [z]]]
\end{verbatim}
\subsection{Left parse trees}
We now introduce the notion of a \emph{left parse tree}. A linear parse tree can be converted into a left parse tree in three steps:
\begin{enumerate}
\item Replace all left brackets that follow a left brack by a left brace.
\item Replace all right brackets which match a left brace by a right brace.
\item Delete all left braces.
\end{enumerate}
As an example, \verb/[[[x] + [y]] + [z]]/ is converted thus:
\begin{verbatim}
[[[x] + [y]] + [z]]
-> [{{x] + [y]] + [z]]
-> [{{x} + [y]} + [z]]
-> [x} + [y]} + [z]]
\end{verbatim}
The conversion from linear to left parse tree is reversible: The two first steps are obviously reversible, and it is not too hard to see how to reverse the third one.
The goal of "[[ lgc-parse ( g , a ) ]]" is find a left parse tree "[[ t ]]" of the list "[[ a ]]" of tokens according to the grammar "[[ g ]]".
\subsection{Representation of left parse trees}
A left parse tree is represented by a list "[[ t ]]" of tokens. Each token has form "[[ c :: p :: S ]]" and can have one of the following forms:
\begin{itemize}
\item An input token. See Section \ref{sec:TokenLists} for the seven kinds of input tokens.
\item "[[ lgc-esc-left :: p :: true ]]" a left bracket.
\item "[[ lgc-esc-right :: p :: v ]]" a right bracket where "[[ v ]]" is the value which represents the construct ended by the bracket.
\item "[[ lgc-esc-brace :: p :: v ]]" a right brace where "[[ v ]]" is the value which represents the construct ended by the brace.
\end{itemize}
\subsection{Return value}
If "[[ lgc-parse ( g , a ) ]]" is unable to parse "[[ a ]]" according to the grammar "[[ g ]]", then it returns "[[ << p ,, s >> ]]" where "[[ p ]]" is the farthest position into "[[ a ]]" that the parser was able to go before it had to give up. "[[ s ]]" is a value useful for constructing an error message.
If "[[ lgc-parse ( g , a ) ]]" can interpret "[[ a ]]" in exactly one way then it returns "[[ << true ,, t >> ]]" where "[[ t ]]" is the interpretation expressed as a left parse tree.
If "[[ lgc-parse ( g , a ) ]]" can interpret "[[ a ]]" in more than one way then it returns "[[ << t ,, t prime >> raise ]]" where "[[ t ]]" and "[[ t prime ]]" are two, distinct interpretations of "[[ a ]]" expressed as two, distinct left parse trees.
\subsection{Restrictions on left parse trees}
The parser parses "[[ a ]]" as if all constructs are right associative and have the same charge. Hence, \verb/x + y + z/ should be interpreted as
\begin{verbatim}
[[x] + [[y] + [z]]]
\end{verbatim}
rather than
\begin{verbatim}
[[[x] + [y]] + [z]]
\end{verbatim}
Or, expressed using left parse trees, it should be interpreted as
\begin{verbatim}
[x} + [y} + [z]]]
\end{verbatim}
rather than
\begin{verbatim}
[x} + [y]} + [z]]
\end{verbatim}
This right-associative-same-charge parsing is implemented by adding a rule saying that a right brace is not allowed to follow a right bracket.
Furthermore, since we do not permit the empty construct, a right brace or bracket cannot follow a left bracket.
Furthermore, since we do not permit constructs with two double quotes in a row, a left bracket cannot follow a right brace or bracket.
Furthermore, since we do not permit the construct which contains one double quote and nothing else, a right bracket or brace cannot follow a right brace.
Finally, a left bracket cannot follow a left bracket due to the definition of left parse trees.
Hence, left parse trees can have sequences of right brackets, but apart from that, brackets and braces are separated by characters of input.
One consequence of the restrictions on grammars are that every construct contains at least one non-double-quote character. Hence, the parse tree of a page cannot have more nodes than the number of characters in the body of the source text. In principle, this ensures that "[[ lgc-parse ( g , a ) ]]" terminates in finite time. In practice, however, "[[ lgc-parse ( g , a ) ]]" can take very long time if the user defines a silly grammar. We consider making sensible grammars to be the responsibility of the user. We make absolutely no attempt to help users who define silly grammars.
\subsection{Functions for invoking charge rules}
Charge invokation functions vectorize a left parse tree into its binary representation respecting charge rules. The functions also take care of vectorizing of strings and includes and of autogeneration of name and charge definitions.
In the charge invokation functions, we use parameter names as follows:
\begin{tabular}{ll}
"[[ s ]]" & state \\
"[[ L = t :: L ]]" & left parse tree, i.e.\ a list of tokens \\
"[[ t = << c ,, p ,, v >> ]]" & token \\
"[[ c ]]" & character \\
"[[ p ]]" & position in source text \\
"[[ v ]]" & value in token. The type of "[[ v ]]" depends on "[[ c ]]": \\
\quad "[[ c = lgc-esc-- ]]" & "[[ v ]]" is a string \\
\quad "[[ c = lgc-esc-# ]]" & "[[ v ]]" is the name of a binary include file \\
\quad "[[ c = lgc-esc-$ ]]" & "[[ v ]]" is the name of a text include file \\
\quad "[[ c = lgc-esc-right ]]" & "[[ v ]]" is a list "[[ N ]]" of nodes \\
\quad "[[ c = lgc-esc-brace ]]" & "[[ v ]]" is a list "[[ N ]]" of nodes \\
\quad otherwise & "[[ v ]]" is "[[ true ]]" \\
"[[ C ]]" & charge \\
"[[ r = M :: r ]]" & list of marked trees \\
"[[ M = m :: P ]]" & marked tree \\
"[[ m ]]" & mark; normally "[[ true ]]", occasionally "[[ false ]]" \\
"[[ P = n :: A ]]" & partial tree \\
"[[ N = n :: N ]]" & list of grammar nodes \\
"[[ n = << R ,, i ,, C ,, p prime ,, N prime ,, b >> ]]" & grammar node \\
"[[ R ]]" & relative reference \\
"[[ i ]]" & index \\
"[[ p prime ]]" & post-openness \\
"[[ N prime ]]" & name of construct \\
"[[ b ]]" & byte representation of construct \\
"[[ A = a :: A ]]" & list of arguments \\
"[[ a ]]" & argument as vector tree \\
\end{tabular}
\subsection{Main charge functions}
\begin{statements}
\item "[[ late define lgc-charge ( L , s ) as newline
lgc-charge1 ( L , s , true ) end define ]]"
Convert the left parse tree "[[ L ]]" into a vector tree "[[ a ]]" which, when flattened, becomes the body of the produced Logiweb vector.
\item "[[ late define lgc-charge1 ( L , s , r ) as newline
if L then r else newline
let t :: L = L in newline
let r = lgc-charge2 ( t , L , s , r ) in newline
lgc-charge1 ( L , s , r ) end define ]]"
Process the left parse tree "[[ L ]]" one token at a time, accumulating the result in "[[ r ]]" as a list of marked, partial trees. As an exception, however, "[[ r ]]" is the final result when "[[ t ]]" is empty. That is due to the way "[[ lgc-charge2 ( t , L , s , r ) ]]" works.
\item "[[ late define lgc-charge2 ( t , L , s , r ) as newline
let << c ,, p ,, v >> = t in newline
if c >= NULL then r else newline
if c = lgc-esc-right then lgc-charge-right ( p , v , L , s , r ) else newline
if c = lgc-esc-brace then lgc-charge-brace ( p , v , L , s , r ) else newline
if c = lgc-esc-left then lgc-charge-left ( r ) else newline
if c = lgc-esc-- then lgc-charge-string ( v , r ) else newline
if c = lgc-esc-# then lgc-charge-binary ( v , s , r ) else newline
if c = lgc-esc-$ then lgc-charge-text ( v , s , r ) else newline
if c = lgc-esc-S then lgc-charge-source ( s , r ) else newline
if c = lgc-esc-N then lgc-charge-name ( p , s , r ) else newline
if c = lgc-esc-C then lgc-charge-charge ( p , s , r ) else newline
lgc-panic ( !"Internal error in lgc-charge2" ) end define ]]"
Destruct the token "[[ t ]]" into character "[[ c ]]", position "[[ p ]]", and additional data "[[ v ]]". Then dispatch on "[[ v ]]". All the functions return a list "[[ r ]]" of marked, partial trees except "[[ lgc-charge-left ( r ) ]]" which returns the final result of charge invokation when it processes the leftmost left bracket.
\end{statements}
\subsection{Charge handling of brackets and braces}
\begin{statements}
\item "[[ late define lgc-charge-right ( p , N , L , s , r ) as newline
if N tail then << true ,, N head >> :: r else newline
let a = lgc-charge-right1 ( L , 0 ) :: N in newline
lgc-parse-ambiguous-construct ( a , s ) end define ]]"
Process a right bracket. If the construct is ambiguous, throw the position "[[ p ]]" and the list "[[ N ]]" of possible interpretations. Otherwise, push a marked tree "[[ M = m :: P ]]" onto "[[ r ]]". The mark "[[ m ]]" is set to "[[ true ]]". The partial tree "[[ P = n :: A ]]" has node "[[ n = N head ]]" and an empty argument list "[[ A ]]". Later on, arguments are pushed onto "[[ A ]]". If the construct is pre-open then the mark "[[ m ]]" changes to "[[ false ]]" when processing the first argument (which is processed last since arguments are processed in reverse order). For pre-closed constructs, the mark never changes.
\item "[[ late define lgc-charge-right1 ( L , l ) as newline
if L atom then 0 else newline
let << c ,, p ,, v >> :: L = L in newline
if c = lgc-esc-right then lgc-charge-right1 ( L , l + 1 ) else newline
if c = lgc-esc-brace then lgc-charge-right1 ( L , l ) else newline
if c = lgc-esc-left then lgc-charge-right1 ( L , l - 1 ) else newline
if l > 0 then lgc-charge-right1 ( L , l ) else p end define ]]"
Skip "[[ l ]]" left brackets, then return the position of the first, proper character.
\item "[[ late define lgc-charge-brace ( p , N , L , s , r ) as newline
let r = lgc-charge-brace0 ( r ) in newline
lgc-charge-right ( p , N , L , s , r ) end define ]]"
Process a right brace. First reorganize the stack according to charge. Then, since a right brace represents a right bracket, call "[[ lgc-charge-right ( p , N , L , s , r ) ]]".
\item "[[ late define lgc-charge-brace0 ( r ) as newline
let ( true :: P ) :: r = r in newline
let n = P head in newline
let C = lgc-node2charge ( n ) in newline
let p = lgc-node2closed ( n ) in newline
let r prime = << false :: P >> in newline
lgc-charge-brace1 ( C , p , r prime , r ) end define ]]"
A right brace indicates that the construct at the top of the stack "[[ r ]]" is pre-open and that we are about to parse its first argument, so we change the mark of the top element of "[[ r ]]" to "[[ false ]]". Then we let the construct bubble up the parse tree to its proper location according to its charge. That is done by "[[ lgc-charge-brace1 ( C , p , r prime , r ) ]]" which moves the list "[[ r prime ]]" of marked trees down the stack "[[ r ]]" to its proper location according to the charge "[[ C ]]".
\item "[[ late define lgc-charge-brace1 ( C , p , r prime , r ) as newline
if r then revappend ( r prime , r ) else newline
let m :: n :: A = r head in newline
if p .and. .not. m then lgc-charge-brace1 ( C , p , r head :: r prime , r tail ) else newline
if A != true .or. lgc-node2closed ( n ) then revappend ( r prime , r ) else newline
if lgc-less-charge ( C , lgc-node2charge ( n ) ) then revappend ( r prime , r ) else newline
r head :: lgc-charge-brace1 ( C , p , r prime , r tail ) end define ]]"
Bubble the list "[[ r prime ]]" of marked trees to its proper location in the "[[ r ]]". "[[ C ]]" and "[[ p ]]" are the charge and post-closedness, respectively, of the last element of "[[ r prime ]]". If "[[ r ]]" is empty, "[[ r prime ]]" is already at the root and cannot bubble further. To bubble, the construct at the top of the stack must be post-open and must be in the state where its last argument is being processed. Since arguments are processed in reverse order, the last argument is being processed iff the argument list "[[ A ]]" is empty. Finally, the charge "[[ C ]]" must be larger than the charge of the top element for "[[ r prime ]]" to bubble. When "[[ r prime ]]" bubbles, it pushes the node above in front of it if the last element of "[[ r prime ]]" is post-closed.
\item "[[ late define lgc-less-charge ( x , y ) as newline
let a :: x = x in newline
let b :: y = y in newline
let a = if a then 0 else a in newline
let b = if b then 0 else b in newline
if a < b then true else newline
if a > b then false else newline
if x .and. y then oddp ( a ) else newline
lgc-less-charge ( x , y ) end define ]]"
Compare the charges "[[ x ]]" and "[[ y ]]" and return "[[ true ]]" if "[[ x ]]" is less than "[[ y ]]". The ordering is lexicographic with the following modifications:
\begin{enumerate}
\item If one of the two is shorter than the other, then the shorter one is padded with zeros to match the length of the longer.
\item If "[[ x ]]" and "[[ y ]]" are equal after padding, then return "[[ true ]]" if "[[ x ]]" and "[[ y ]]" end with an odd number. Constructs whose charge end with an odd number are right associative, so they should not bubble when they meet their equal.
\end{enumerate}
\item "[[ late define lgc-charge-left ( r ) as newline
let ( true :: n :: A ) :: r = r in newline
lgc-charge-left1 ( n , A , r ) end define ]]"
Process a left bracket. When processing in reverse order, a left bracket closes one right bracket and zero, one or more right braces. The right braces to be closed need not be the ones that match the left bracket since the stack "[[ r ]]" has been reorganized according to charge. Rather, we use the mark of the marked trees in "[[ r ]]" to find out how many constructs to close.
The construct at the top of the stack always has mark "[[ true ]]" so we unstack the top element, discards the mark, and destructs the partial tree into a node "[[ n ]]" and an argument list "[[ A ]]". Then we use "[[ lgc-charge-left1 ( n , A , r ) ]]" to process further constructs from "[[ r ]]" until "[[ r ]]" is empty or the top element has mark "[[ true ]]". In other words, we process the top element of "[[ r ]]" which is known to have mark "[[ true ]]" and then process all constructs at the top of "[[ r ]]" which have mark "[[ false ]]".
\item "[[ late define lgc-charge-left1 ( n , A , r ) as newline
let a = lgc-node2binary ( n ) :: A in newline
if r atom then a else newline
let ( m :: n :: A ) :: r = r in newline
if m then ( true :: n :: a :: A ) :: r else newline
lgc-charge-left1 ( n , a :: A , r ) end define ]]"
When "[[ lgc-charge-left1 ( n , A , r ) ]]" is called, "[[ n ]]" and "[[ A ]]" have just been popped from the top of "[[ r ]]". That top element is a partial tree which has just been completed and, thus, has become a complete tree representing a subterm of the body of the page. We convert that just completed tree to its representation as a vector tree "[[ a ]]". When "[[ a ]]" is flattened later on, that becomes the sequence of bytes representing the just completed tree. If "[[ r ]]" has become empty we return "[[ a ]]" with no further warning. Normally, we return a stack "[[ r ]]", so this could baffle the caller. However, the stack "[[ r ]]" becomes empty exactly when the left parse tree "[[ L ]]" becomes empty, so "[[ lgc-charge1 ( L , s , r ) ]]" will know that "[[ r ]]" is not a stack but, rather, the final result of charge invokation. If "[[ r ]]" is not empty we look at the mark "[[ m ]]" of the new top element. If "[[ m = true ]]" then we can close no more constructs and add "[[ a ]]" to the argument list of the top element. Otherwise we also add "[[ a ]]" to the argument list but then we recurse.
\end{statements}
\subsection{Charge handling of strings}
\begin{statements}
\item "[[ late define lgc-charge-string ( v , r ) as newline
lgc-charge-string1 ( vector-length ( v ) , v , r ) end define ]]"
Replace the top element of "[[ r ]]" by the string "[[ v ]]". "[[ v ]]" must be a vector.
\item "[[ late define lgc-charge-string1 ( l , v , r ) as newline
lgc-charge-bytes ( lgc-string2bytes ( l , v ) , r ) end define ]]"
Replace the top element of "[[ r ]]" by the string "[[ v ]]" of length "[[ l ]]". "[[ v ]]" may be an arbitrary vector tree.
\item "[[ late define lgc-string2bytes ( l , v ) as newline
NULL :: lgc-card2septet* ( l ) :: v end define ]]"
Convert the vector tree "[[ v ]]" of length "[[ l ]]" into the binary representation of a string.
\item "[[ late define lgc-charge-bytes ( b , r ) as newline
let n = << true ,, true ,, true ,, true ,, true ,, b >> in newline
<< true ,, n >> :: r tail end define ]]"
Replace the top element of "[[ r ]]" by the bytes "[[ b ]]". "[[ b ]]" may be an arbitrary vector tree.
\item "[[ late define lgc-charge-binary ( v , s , r ) as newline
let v = s [[ !"includes" ]] [[ v ]] in newline
lgc-charge-string1 ( length ( v ) , v , r ) end define ]]"
Replace the top element of "[[ r ]]" by the include file named "[[ v ]]".
\item "[[ late define lgc-charge-text ( v , s , r ) as newline
let v = s [[ !"includes" ]] [[ v ]] in newline
let v = lgc-charge-text1 ( v , true , true ) in newline
lgc-charge-string1 ( length ( v ) , v , r ) end define ]]"
Replace the top element of "[[ r ]]" by the include file named "[[ v ]]" after processing of newlines.
\item "[[ late define lgc-charge-text1 ( a , i , r ) as newline
if a atom then reverse ( r ) else newline
let c :: a = a in newline
if c = i then lgc-charge-text1 ( a , true , r ) else newline
if c = CR then lgc-charge-text1 ( a , LF , LF :: r ) else newline
if c = LF then lgc-charge-text1 ( a , CR , LF :: r ) else newline
lgc-charge-text1 ( a , true , c :: r ) end define ]]"
Perform newline processing on the list "[[ a ]]" of singleton strings and accumulate the result in "[[ r ]]".
\item "[[ late define lgc-host-newline ( s ) as newline
let n = s [[ !"parameters" ]] [[ !"newline" ]] head in newline
let n = lgc-argv-downcase ( n ) in newline
if n = "crlf" then CRLF else newline
if n = "cr" then CR else newline
if n = "lfcf" then LFCR else LF end define ]]"
Return the host newline sequence as a vector tree.
\item "[[ late define lgc-get-newline ( s , v ) as newline
if v atom then lgc-host-newline ( s ) else newline
let c :: v = v in newline
if c = LF then if v head = CR then LFCR else LF else newline
if c = CR then if v head = LF then CRLF else CR else newline
lgc-get-newline ( s , v ) end define ]]"
Return the first newline sequence of the singleton list "[[ v ]]", defaulting to the host newline sequence.
\item "[[ late define lgc-add-newline ( s , v ) as newline
lgc-get-newline ( s , v ) :: v end define ]]"
Add a newline in front of the singleton list "[[ v ]]".
\item "[[ late define lgc-add-headline ( s , R , v ) as newline
let h :: true :: v = lgc-parse-headline ( v ) in newline
if h != true then append ( h , append ( R , v ) ) else newline
append ( lgc-headline , append ( R , lgc-add-newline ( s , v ) ) ) end define ]]"
Add a headline with reference "[[ R ]]".
\item "[[ late define lgc-charge-source ( s , r ) as newline
let v = s [[ !"source" ]] in newline
let v = lgc-add-headline ( s , true , v ) in newline
lgc-charge-string1 ( length ( v ) , v , r ) end define ]]"
Replace the top element of "[[ r ]]" by the source text after removal of the explicit reference "[[ R ]]" (if any).
\item "[[ late define lgc-headline as << QQ ,, QQ ,, !";" ,, !";" >> end define ]]"
\item "[[ late define lgc-parse-headline ( v ) as newline
let e :: V = lgc-parse-prefix ( lgc-headline , v ) catch in newline
if e then true :: true :: v else newline
lgc-headline :: lgc-parse-headline1 ( V , true ) end define ]]"
\item "[[ late define lgc-parse-headline1 ( v , r ) as newline
if v atom then r :: v else newline
let c :: V = v in newline
if !"0" <= c .and. c <= !"9" then lgc-parse-headline1 ( V , c :: r ) else newline
if !"A" <= c .and. c <= !"F" then lgc-parse-headline1 ( V , c :: r ) else r :: v end define ]]"
Remove all hex digits at the begining of "[[ v ]]".
\end{statements}
\subsection{Autogeneration of name and charge definitions}
\begin{statements}
\item "[[ late define lgc-charge-name ( p , s , r ) as newline
lgc-charge-auto ( p , "name" , s , r ) end define ]]"
Add a name definition for each construct in the dictionary.
\item "[[ late define lgc-charge-charge ( p , s , r ) as newline
lgc-charge-auto ( p , "charge" , s , r ) end define ]]"
Add a charge definition for each construct in the dictionary.
\item "[[ late define lgc-charge-auto ( p , n , s , r ) as newline
let b = lgc-charge-name-find ( p , !""-""! then ""!" , true , s ) in newline
let b = b :: lgc-charge-name-find ( p , !"def ""! of ""! as ""! enddef" , true , s ) in newline
let b = b :: lgc-charge-name-find ( p , n , n , s ) in newline
let v = lgc-charge-name-find ( p , !"var" , true , s ) in newline
let e = lgc-charge-name-find ( p , !"end" , true , s ) in newline
let d = s [[ !"dictionary" ]] in newline
let b = lgc-charge-auto1 ( d , b , v , s [[ n ]] , s [[ !"binary" ]] , e ) in newline
lgc-charge-bytes ( b , r ) end define ]]"
Find the constructs (lgcdef, lgcvar, lgcthen, lgcend, and either lgcname or lgccharge) needed for auto-construction of name or charge definitions. Then generate the definitions and replace the head of "[[ r ]]" with the result.
\item "[[ late define lgc-charge-name-find ( p , n , d , s ) as newline
let q = vt2vector* ( !"lgc" ) in newline
let n = lgc-splice ( q , vt2vector* ( n ) ) in newline
let b = lgc-charge-name2binary ( lgc-splice ( q , n ) , s ) in newline
if b != true then b else newline
let b = lgc-charge-name2binary ( n , s ) in newline
if b != true then b else newline
if d != true then lgc-string2bytes ( vector-length ( d ) , d ) else
let m = !"Cannot generate name and charge definitions (""!""!N and ""!""!C)" in newline
let m = m :: LF :: !"Can find neither " :: n :: !" nor " :: lgc-splice ( q , n ) in newline
lgc-throw-message ( s , p , m ) end define ]]"
Convert construct "[[ n ]]" prefixed with lgclgc or lgc to binary, defaulting to the string "[[ d ]]", if provided.
\item "[[ late define lgc-charge-name2binary ( n , s ) as newline
let N = lgc-grammar-get-tuple ( s , n ) in newline
let n = lgc-charge-best-node ( N ) in newline
lgc-node2binary ( n ) end define ]]"
Convert the name "[[ n ]]" given as a vector tree to a vector tree of bytes. Return "[[ true ]]" if "[[ n ]]" is not found.
\item "[[ late define lgc-charge-best-node ( N ) as newline
if N atom then true else newline
let n :: N = N in newline
let n prime = lgc-charge-best-node ( N ) in newline
if n prime then n else newline
let R = lgc-node2relref ( n ) in newline
let R prime = lgc-node2relref ( n prime ) in newline
if R < R prime then n else newline
if R prime < R then n prime else newline
let i = lgc-node2idx ( n ) in newline
let i prime = lgc-node2idx ( n prime ) in newline
if i < i prime then n else n prime end define ]]"
Return the node with lowest relative reference and index from the list "[[ N ]]" of nodes. If "[[ N ]]" is empty return "[[ true ]]".
\item "[[ late define lgc-charge-auto1 ( d , b , v , A , B , r ) as newline
if d atom then r else newline
let ( i :: a ) :: d = d in newline
let S = A [[ i ]] in newline
let S = lgc-string2bytes ( length ( S ) , S ) in newline
let r = b :: B [[ i ]] :: repeat ( a , v ) :: S :: r in newline
lgc-charge-auto1 ( d , b , v , A , B , r ) end define ]]"
Generate name or charge definitions for all constructs in the dictionary "[[ d ]]". "[[ b ]]" must contain the binary representations of lgcthen, lgcdef, and lgcname/lgccharge in that order. "[[ v ]]" must contain the binary representation of lgcvar. "[[ A ]]" must be an array from indexes to right hand sides (i.e.\ names or charges) expressed as singleton lists."[[ B ]]" must be an array from indexes to binary representations of constructs.
\end{statements}
\subsection{Vectorizing}
\begin{statements}
\item "[[ late define lgc-vectorize ( L , s ) as newline
let v = lgc-charge ( L , s ) in newline
let v = lgc-add-dict ( s [[ !"dictionary" ]] , v ) in newline
let v = lgc-add-bib ( s [[ !"refbib" ]] , v ) in newline
let v = lgc-add-ref ( s , v ) in newline
v end define ]]"
Reorganize the left parse tree "[[ L ]]" according to charge and add dictionary and bibliography to form the vector of the page being translated.
\item "[[ late define lgc-add-dict ( d , v ) as newline
if d atom then NULL :: v else newline
let ( i :: a ) :: d = d in newline
let v = lgc-add-dict ( d , v ) in newline
if i = 0 then v else newline
let i = lgc-card2septet* ( i ) in newline
let a = lgc-card2septet* ( a ) in i :: a :: v end define ]]"
Add the dictionary "[[ d ]]" to the vector tree "[[ v ]]". The dictionary "[[ d ]]" is supposed to have form "[[ << i :: a ,, *** >> ]]". The dictionary is supposed to be sorted in descending "[[ i ]]" but we do not depend on that. The last element (the page symbol) is supposed to have form "[[ 0 :: 0 ]]" but we do not depend on that either.
\item "[[ late define lgc-add-bib ( b , v ) as newline
if b atom then NULL :: v else newline
let r :: b = b in newline
let r = lgc-ref2vector* ( r ) in newline
let v = lgc-add-bib ( b , v ) in r :: v end define ]]"
Add the bibliography "[[ b ]]" to the vector tree "[[ v ]]".
\item "[[ late define lgc-ref2vector* ( r ) as newline
let l = vector-length ( r ) in newline
let l = lgc-card2septet* ( l ) in newline
let r = vt2vector* ( r ) in newline l :: r end define ]]"
Convert the reference "[[ r ]]" to a vector tree suited for inclusion in the vector of the page being translated.
\item "[[ late define lgc-ref-version as bt2vector ( 1 ) end define ]]"
\item "[[ late define lgc-hex2card ( c ) as newline
if !"0" <= c .and. c <= !"9" then c - "0" else newline
if !"A" <= c .and. c <= !"F" then c - "A" + Base else true end define ]]"
Convert the singleton string "[[ c ]]" to its hex value (or "[[ true ]]" if "[[ c ]]" is not a hex digit).
\item "[[ late define lgc-mixed2vector* ( R ) as newline
if R tail atom then true else newline
let c :: d :: R = R in newline
let D = bt2vector ( lgc-hex2card ( c ) * 16 + lgc-hex2card ( d ) ) in newline
D :: lgc-mixed2vector* ( R ) end define ]]"
\item "[[ late define lgc-add-ref ( s , v ) as newline
let h :: H :: true = lgc-parse-headline ( s [[ !"source" ]] ) in newline
if h then lgc-add-ref1 ( s , v ) else newline
let H = lgc-mixed2vector* ( reverse ( H ) ) in newline
let t = list-suffix ( H , 21 ) in newline
let R = ripemd ( t :: v ) in newline
let r = vt2vector* ( lgc-ref-version :: R :: t ) in newline
if H != r then lgc-add-ref1 ( s , v ) else newline
let r = append ( lgc-card2septet* ( length ( r ) ) , r ) in newline
r :: v end define ]]"
\item "[[ late define lgc-add-ref1 ( s , v ) as newline
let << m ,, e >> = s [[ !"time" ]] in newline
let m = lgc-card2septet* ( m ) in newline
let e = lgc-card2septet* ( e ) in newline
let R = ripemd ( m :: e :: v ) in newline
let r = vt2vector* ( lgc-ref-version :: R :: m :: e ) in newline
let r = append ( lgc-card2septet* ( length ( r ) ) , r ) in newline
r :: v end define ]]"
\end{statements}
\subsection{Invokation of the parser}
\begin{statements}
\item "[[ late define lgc-parse1 ( x , s ) as newline
let g = s [[ !"grammar" ]] in newline
let a = s [[ !"body" ]] in newline
let e :: v = lgc-parse ( g , a ) catch in newline
if e then lgc-parse-ambiguous ( v , s ) else newline
if v head != true then lgc-parse-no-interpretations ( v , s ) else newline
let e :: b = vt2vector* ( lgc-vectorize ( v tail , s ) ) catch in newline
if e then lgc-report-messages ( b ) else newline
let s = s [[ !"vector" -> b ]] in newline
lgc-parse2 ( s ) end define ]]"
Parse body according to given grammar. Store the resulting vector in "[[ s [[ !"vector" ]] ]]". Then pass control to "[[ lgc-parse2 ( s ) ]]" to handle header.
\item "[[ late define lgc-parse2 ( s ) as newline
let r :: true = lgw-parse-string ( s [[ !"vector" ]] ) in newline
let s = s [[ !"reference" -> r ]] in newline
let h :: R :: S = lgc-parse-headline ( s [[ !"source" ]] ) in newline
if h then lgc-parse3 ( s ) else newline
let r prime = lgc-string2mixed ( r ) in newline
if r prime = reverse ( R ) then newline lgc-parse4 ( r prime , s ) else newline
let S = h :: r prime :: S in newline
let s = lgc-progress ( !"Writing header back to source" , 3 , s ) in newline
let s = lgc-push-event ( s , fileWrite ( s [[ !"sourcename" ]] , S ) ) in newline
lgc-parse3 ( s ) end define ]]"
If source has no header, invoke codifier by a call to "[[ lgc-parse3 ( s ) ]]". If source has correct header, see if the page is already codified by a call to "[[ lgc-parse4 ( r prime , s ) ]]". If source has incorrect header, write correct header back to source and invoke codifier.
\item "[[ late define lgc-parse3 ( s ) as newline
let s = lgc-progress ( !"Codifying" , 3 , s ) in newline
lgc-exec-events ( s , lgc-parse-codify-lgw ( x , s ) ) end define ]]"
Invoke codifier.
\item "[[ late define lgc-parse4 ( r , s ) as newline
let s = s [[ !"path" -> s [[ !"parameters" ]] [[ !"path" ]] ]] in newline
lgc-parse5 ( s [[ !"mixed" -> r ]] ) end define ]]"
Set "[[ s [[ !"path" ]] ]]" to the search path and "[[ s [[ !"mixed" ]] ]]" to the mixed endian hexadecimal representation of the reference. Then see if the page is already codified.
\item "[[ late define lgc-parse5 ( s ) as newline
let P = s [[ !"path" ]] in newline
if P atom then lgc-parse3 ( s ) else newline
let p :: P = P in newline
let s = s [[ !"path" -> P ]] in newline
let e :: p = lgc-replace-colon ( vt2vector* ( p ) , s [[ !"mixed" ]] ) catch in newline
if e then lgc-load-no-colon ( s ) else newline
if lgc-file-suffix ( p ) != lgr-suffix then lgc-parse5 ( s ) else newline
if lgc-prefix ( lgc-http-prefix , p ) then lgc-parse5 ( s ) else newline
if lgc-prefix ( lgc-lgw-prefix , p ) then lgc-parse5 ( s ) else newline
if lgc-prefix ( lgc-name-prefix , p ) then lgc-parse5 ( s ) else newline
let p = if lgc-prefix ( lgc-file-prefix , p ) then list-suffix ( p , 5 ) else p in newline
let p = lgc-tilde-expand1 ( p , s ) in newline
let s = lgc-progress ( !"Reading file:" :: p , 4 , s ) in newline
let s = lgc-push-event ( s , fileTypeRead ( p ) ) in newline
lgc-exec-events ( s , lgc-parse6 ( x , s ) ) end define ]]"
See if the page is already codified.
\item "[[ late define lgc-parse6 ( x , s ) as newline
let << true ,, << true ,, true :: x >> >> = x in newline
if x then lgc-parse5 ( s ) else newline
let e :: c = sl2rack ( x ) catch in newline
if e then lgc-load-malformed-page ( s ) else newline
let r :: b = c [[ !"bibliography" ]] in newline
let c = true [[ 0 -> r ]] [[ r -> c ]] in newline
let s = s [[ << !"cluster" ,, r >> => c ]] in newline
let s = lgc-load-codify-closure ( r , s ) in newline
let c = s [[ !"cluster" ]] [[ r ]] in newline
let c = lgr-cache-restore ( c ) in newline
let s = s [[ << !"cluster" ,, r >> => c ]] in newline
let s = lgc-progress ( !"Rendering" , 3 , s ) in newline
lgc-exec-events ( s , lgc-render ( x , s ) ) end define ]]"
\end{statements}
\subsection{Definition of the parser}
\begin{statements}
\item "[[ late define lgc-parse ( g , a ) as newline
let t = << << lgc-esc-left ,, 0 ,, true >> >> in newline
let s = << g >> in newline
let r = << 0 ,, s >> in newline
lgc-parse-token ( a , t , s , r , g ) end define ]]"
Push a left bracket onto "[[ t ]]", set the top of the stack to the entire grammar, and set "[[ r ]]" to the best result so far (which is that we have moved zero characters into "[[ a ]]"). Then try to parse a token since a token is the only thing that can follow a left bracket in a left parse tree.
\item "[[ late define lgc-parse-any ( p , a , t , s , r , g ) as newline
let r = lgc-parse-left ( p , a , t , s , r , g ) in newline
let r = lgc-parse-right ( p , a , t , s , r , g ) in newline
let r = lgc-parse-brace ( p , a , t , s , r , g ) in newline
lgc-parse-token ( a , t , s , r , g ) end define ]]"
Parse the list "[[ a ]]" of tokens, accumulating the associated left parse tree in reverse order in "[[ t ]]". "[[ p ]]" is the position in the source file of the last parsed character. "[[ s ]]" is a stack of positions in the grammar which indicates where the parser is in the grammar. All elements of "[[ s ]]" below the top element are positioned right after a double quote. The value of "[[ r ]]" is the best result so far. "[[ g ]]" is the entire grammar. During parsing, one may regard "[[ g ]]" as a constant.
The `any' function above in turn tries to add a left bracket, a right bracket, a right brace, and a token to "[[ t ]]". This covers all possibilities which is what `any' refers to.
\item "[[ late define lgc-parse-left ( p , a , t , s , r , g ) as newline
if t head head = lgc-esc-left then r else newline
let G = s head [[ QQ ]] in newline
if G then r else newline
let t = << lgc-esc-left ,, p ,, true >> :: t in newline
lgc-parse-token ( a , t , g :: G :: s tail , r , g ) end define ]]"
If "[[ t ]]" starts with a left bracket we cannot add one more so we return the best result "[[ r ]]" found so far. Else, advance the top of the stack "[[ s ]]" by a double quote and store the result in "[[ G ]]". If "[[ G ]]" is empty, adding a left bracket to "[[ t ]]" is no option and we return the best result "[[ r ]]" found so far. Else add a left bracket to "[[ t ]]", replace the top of "[[ s ]]" by "[[ G ]]", and then push a fresh copy of the entire grammar "[[ g ]]" onto "[[ s ]]".
The function tries to add to "[[ t ]]" a \emph{left} bracket which is what `left' in the function name refers to. Only tokens can follow a left bracket, so the function calls the `token' function in case it succeeds to add a left bracket.
\item "[[ late define lgc-parse-right ( p , a , t , s , r , g ) as newline
let G = s head [[ 0 ]] in newline
if G then r else newline
let t = << lgc-esc-right ,, p ,, G >> :: t in newline
let s = s tail in newline
if .not. s then lgc-parse-any ( p , a , t , s , r , g ) else
if .not. a then r else newline
if r head then << r tail ,, t >> raise else true :: t end define ]]"
Advance the top of the stack "[[ s ]]" by an end of construct marker and store the result in "[[ G ]]". If "[[ G ]]" is empty, adding a right bracket to "[[ t ]]" is no option and we return the best result "[[ r ]]" found so far. Else add a right bracket to "[[ t ]]" and pop "[[ s ]]". If "[[ a ]]" or "[[ s ]]" are non-empty, continue parsing. Else, we have found an interpretation "[[ t ]]". If "[[ t ]]" is the second interpretation found, throw an exception containing both interpretations. Else return the interpretation as the best result found so far.
The function tries to add to "[[ t ]]" a \emph{right} bracket which is what `right' in the function name refers to.
\item "[[ late define lgc-parse-brace ( p , a , t , s , r , g ) as newline
let G = s head [[ 0 ]] in newline
if G then r else newline
if t head head = lgc-esc-right then r else newline
let t = << lgc-esc-brace ,, p ,, G >> :: t in newline
let G = g [[ QQ ]] in newline
if G then r else newline
let s = s tail in newline
lgc-parse-token ( a , t , G :: s , r , g ) end define ]]"
Same as the previous function with the following exceptions: (1) The stack "[[ s ]]" is handled differently. (2) To enforce all constructs to be right associative and to have the same charge, we check if "[[ a head head = lgc-esc-right ]]". If it is, we cannot add a brace since a right brace is not allowed to follow a right bracket.
The function tries to add to "[[ t ]]" a right \emph{brace} which is what `brace' in the function name refers to. Recall that "[[ t ]]" may contain right but not left braces. Only tokens can follow a left brace, so the function calls the `token' function in case it succeeds to add a left brace.
\item "[[ late define lgc-parse-token ( a , t , s , r , g ) as newline
if a then r else newline
let T :: a = a in newline
let G = s head [[ T head ]] in newline
if G then r else newline
let << true ,, p >> :: true = a in newline
let p = default ( - 1 , p ) in newline
let t = T :: t in newline
let s = G :: s tail in newline
let r = lgc-parse-best ( r , p , s ) in newline
lgc-parse-any ( p , a , t , s , r , g ) end define ]]"
If "[[ a ]]" is empty there are no more tokens to parse and we return the best result "[[ r ]]" found so far. Else, we advance the top of "[[ s ]]" by the next token and store the result in "[[ G ]]". If "[[ G ]]" is empty, moving a token to "[[ t ]]" is no option and we return "[[ r ]]". Else, we move the token to "[[ t ]]". The value of the best result found so far is updated if moving the token make us move farther into "[[ a ]]" than has been done before.
The function tries to add to "[[ t ]]" a \emph{token} which is what `token' in the function name refers to. All four kinds of items (left bracket, right bracket, right brace, or token) can follow a left brace, so the function calls the `any' function in case it succeeds to add a token.
\item "[[ late define lgc-parse-best ( r , p , s ) as newline
if r head then r else newline
if r head = - 1 then r else newline
if p = -1 then p :: s else newline
if r head > p then r else p :: s end define ]]"
Return the best result of "[[ r ]]" and "[[ p :: s ]]". If "[[ r head = true ]]" then "[[ r ]]" contains an interpretation which is better than the partial result "[[ p :: s ]]". Otherwise, "[[ r ]]" has form "[[ p prime :: s prime ]]" in which case one prefers the larger of "[[ p ]]" and "[[ p prime ]]" except that a value of "[[ - 1 ]]" denotes the end of the file which is larger than any position inside the file.
\end{statements}
\subsection{Message generators}
\begin{statements}
\item "[[ late define lgc-parse-no-interpretations ( v , s ) as newline
let p :: S = v in newline
let m = !"Syntax error" in newline
let m = m :: LF :: !"Cannot parse beyond this point" in newline
let a = lgc-parse-no-inter1 ( S ) in newline
let a = vt2vector* ( a ) in newline
if a then lgc-error ( s , p , m :: LF :: !"Expected e.g. end of file" :: LF :: !"---" ) else newline
let m = m :: LF :: !"Expected e.g. |" in newline
let m = m :: list-prefix ( a , 50 ) in newline
let m = m :: LF :: !"---" in newline
lgc-error ( s , p , m ) end define ]]"
Complain about syntactically invalid page. Suggest possible continuation.
\item "[[ late define lgc-parse-no-inter1 ( S ) as newline
if S atom then true else newline
lgc-shortest ( S head ) tail :: lgc-parse-no-inter1 ( S tail ) end define ]]"
Convert list "[[ S ]]" of grammars into possible continuation.
\item "[[ late define lgc-shortest ( g ) as newline
if g then 1 :: true else newline
if g head intp then lgc-shortest1 ( g ) else newline
lgc-shortest2 ( g ) end define ]]"
Return "[[ l :: r ]]" where "[[ r ]]" is the shortest production in the grammar "[[ g ]]" and "[[ l ]]" is the lenght of "[[ r ]]".
\item "[[ late define lgc-shortest1 ( g ) as newline
let c :: g = g in newline
if c = 0 then 0 :: true else newline
let l :: r = lgc-shortest ( g ) in l + 1 :: c :: r end define ]]"
Same as "[[ lgc-shortest ( g ) ]]" except that the head of "[[ g ]]" is known to be an integer.
\item "[[ late define lgc-shortest2 ( g ) as newline
let r = lgc-shortest ( g head ) in newline
let R = lgc-shortest ( g tail ) in newline
if r then R else newline
if R then r else newline
if r head < R head then r else R end define ]]"
Same as "[[ lgc-shortest ( g ) ]]" except that the head of "[[ g ]]" is known not to be an integer.
\item "[[ late define lgc-parse-ambiguous ( v , s ) as newline
lgc-parse-ambiguous1 ( reverse ( v zeroth ) , reverse ( v first ) , s ) end define ]]"
Complain about ambiguous page.
\item "[[ late define lgc-parse-ambiguous1 ( a , b , s ) as newline
if a head = b head then lgc-parse-ambiguous1 ( a tail , b tail , s ) else newline
let p = default ( a head tail head , b head tail head ) in newline
let m = !"Ambiguous source text" in newline
let m = m :: LF :: lgc-parse-ambiguous2 ( a ) in newline
let m = m :: LF :: lgc-parse-ambiguous2 ( b ) in newline
lgc-error ( s , p , m ) end define ]]"
Find first difference between the interpretations "[[ a ]]" and "[[ b ]]" and construct an error message based on that.
\item "[[ late define lgc-parse-ambiguous2 ( a ) as newline
if a then !"Could be at the end of the text" else newline
let c = a head head in newline
let p = lgc-parse-ambiguous3 ( a ) in newline
let c = lgc-parse-ambiguous4 ( a , 0 ) in newline
<< !"Could be " ,, p ,, !" of " ,, c >> end define ]]"
Translate the list "[[ a ]]" of tokens into a possible interpretation.
"[[ late define lgc-parse-ambiguous3 ( a ) as newline
let c = a head head in newline
if c = lgc-esc-right .or. c = lgc-esc-brace then !"at the end" else newline
if c = lgc-esc-left then !"at the start" else !"in the middle" end define ]]"
Find location inside construct (start, middle, or end).
"[[ late define lgc-parse-ambiguous4 ( a , l ) as newline
if a atom then !"no construct" else newline
let << c ,, p ,, v >> :: a = a in newline
if c = lgc-esc-left then lgc-parse-ambiguous4 ( a , l + 1 ) else newline
if c != lgc-esc-right .and. c != lgc-esc-brace then lgc-parse-ambiguous4 ( a , l ) else newline
if l > 0 then lgc-parse-ambiguous4 ( a , if c = lgc-esc-brace then l else l - 1 ) else newline
let << << r ,, i ,, C ,, p ,, n ,, b >> >> = v in newline
if r then !"special construct" else << !"construct " ,, n >> end define ]]"
Skip "[[ l ]]" right brackets, then return the name of the next bracket or brace.
\item "[[ late define lgc-parse-ambiguous-construct ( a , s ) as newline
let << p ,, << R ,, i >> ,, << R prime ,, i prime ,, true ,, true ,, n prime >> >> = a in newline
let m = !"Use of ambiguous construct " :: n prime in newline
let m = m :: LF :: !"Could be construct " :: lgc-itoa ( i ) :: !" of reference " :: lgc-itoa ( R ) in newline
let m = m :: LF :: !"Could be construct " :: lgc-itoa ( i prime ) :: !" of reference " :: lgc-itoa ( R prime ) in newline
lgc-throw-message ( s , p , m ) end define ]]"
\item "[[ late define lgc-parse-cannot-trisect ( s ) as newline
lgc-simple-error ( !"Could not trisect generated page" , s ) end define ]]"
\item "[[ late define lgc-proclaim-error ( t , s ) as newline
let s = lgc-push-event ( s , writeln request ( !"Invalid proclamation:" ) ) in newline
let s = lgc-push-event ( s , writeln request ( lgc-tree2vt ( t , s ) ) ) in newline
lgc-do-events ( s ) end define ]]"
\end{statements}
\subsection{Codification of parsed page}
The function defined in this section codifies the page being translated as opposed to the function in Section \ref{sec:CodifyingLoadedPages} which codifies transitively referenced pages.
\begin{statements}
\item "[[ late define lgc-parse-codify-lgw ( x , s ) as newline
let v = s [[ !"vector" ]] in newline
let e :: c = lgw-trisect ( v ) catch in newline
if e then lgc-parse-cannot-trisect ( s ) else newline
let r = c [[ 0 ]] in newline
let s = s [[ << !"cluster" ,, r >> => c ]] in newline
let s = lgc-load-codify-closure ( r , s ) in newline
let c = s [[ !"cluster" ]] [[ r ]] in newline
let e :: c = lgw-codify ( r , c , s [[ !"verbose" ]] ) catch in newline
if e then lgc-proclaim-error ( c , s ) else newline
let s = s [[ << !"cluster" ,, r >> => c ]] in newline
let s = lgc-progress ( !"Rendering" , 3 , s ) in newline
lgc-exec-events ( s , lgc-render ( x , s ) ) end define ]]"
Trisect and codify the Logiweb vector "[[ v ]]". Then pass control to "[[ lgc-render ( x , s ) ]]" to render the page.
\end{statements}
" ]"\section{Rendering}\label{sec:Rendering}"[ "
\subsection{Layout of rendering}
Pages are rendered in a \emph{rendering directory}. The name of the rendering directory is obtained by replacing the rightmost colon character of the ``rendering'' option by the reference of the page in mixed endian hexadecimal. The default value of the rendering option is
\begin{verbatim}
~/.logiweb/logiweb/:/
\end{verbatim}
The rack of a page is stored as ``rack.lgr'' in the rendering directory.
Rendering of a page results in the following entries in the rendering directory:
\begin{description}
\item[index.html] Overview with html pointers.
\item[vector.lgw] The vector of the page (`lgw' for `LoGiWeb', acknowledging that this format is the main format for exchanges of Logiweb pages over the Internet).
\item[rack.lgr] The rack of the page.
\item[ref.lgp] The reference of the page (`p' in `lgp' stands for `pointer'; the obvious choice `r' for reference was taken by `rack').
\item[source.lgs] The source text (if known).
\item[diagnose.txt] Diagnose as text, i.e.\ as Logiweb source text.
\item[diagnose.html] Diagnose in html.
\item[extract.html] Extract (time stamp, bibliography, dictionary, codex, and priority table) in html.
\item[logiweb.png] Logiweb icon in png.
\item[logiweb.ico] Logiweb favicon (small icon for browser titlebar).
\item[page/] Directory containing the user defined rendering of the page.
\item[page/logiweb.eps] Logiweb icon in eps.
\item[page/lgwinclude.tex] Include file with useful \TeX\ definitions.
\item[page/index.html] Typical location of document overview.
\item[page/page.pdf] Typical location of main document.
\item[page/diagnose.pdf] Typical location of diagnose in pdf.
\item[page/bin/] Typical location of generated binaries.
\end{description}
Contents of extract:
\begin{itemize}
\item Name and reference of page
\item Bibliography containing relref, name, and ref.
\item Codex. One codex section per symbol containing: name, index, arity, refname, relref, ref, definitions.
\item Priority table.
\end{itemize}
The following have been abandonned in favour of leaving such representations to user defined rendering: Rack in lisp. Rack in xml. Source translated to html.
\subsection{State entries}
Rendering uses the following entries of the state:
\begin{itemize}
\item "[[ s [[ !"reference" ]] ]]" The reference as a vector.
\item "[[ s [[ !"vector" ]] ]]" The page vector as a list of singletons.
\end{itemize}
\subsection{Messages}
\begin{statements}
\item "[[ late define lgc-rendering-no-colon ( s ) as newline
lgc-simple-error ( !"Missing colon in rendering option" , s ) end define ]]"
\end{statements}
\subsection{Translation of terms to Logiweb source}
The construct "[[ lgc-tree2vt ( t , s ) ]]" converts the tree "[[ t ]]" to a vector tree based on the state "[[ s ]]".
\begin{statements}
\item "[[ late define lgc-tree2vector* ( t , s ) as newline
if t then true else vt2vector* ( lgc-tree2vt ( t , s ) ) end define ]]"
Convert the tree "[[ t ]]" into a source text representation of "[[ t ]]". As a special case, if "[[ t ]]" is "[[ true ]]" then "[[ true ]]" is returned.
\item "[[ late define lgc-string2vt ( t ) as newline
if t = '' then QQ :: QQ :: !"." else newline
let t = vector2vector* ( t ) in newline
( if t head = QQ then QQ :: QQ :: !"-" else QQ ) :: lgc-string2vt1 ( t ) :: QQ end define ]]"
Convert the string "[[ t ]]" given as a vector to a source representation of that string.
\item "[[ late define lgc-string2vt1 ( t ) as newline
if t atom then true else newline
let c :: t = t in newline
( if c = QQ then QQ :: QQ :: !"!" else c ) :: lgc-string2vt1 ( t ) end define ]]"
Convert the string "[[ t ]]" given as a singleton list to a source representation in which quotes are escaped.
\item "[[ late define lgc-symbol2vt ( r , i , s ) as newline
lgc-symbol2vt1 ( r , i , s [[ !"cluster" ]] [[ r ]] ) end define ]]"
Convert reference "[[ r ]]" and index "[[ i ]]" into a symbol name using the state "[[ s ]]".
\item "[[ late define lgc-symbol2vt1 ( r , i , c ) as newline
let a = c [[ r ]] [[ !"dictionary" ]] [[ i ]] in newline
lgc-aritysymbol2vt1 ( r , i , a , c ) end define ]]"
Convert reference "[[ r ]]" and index "[[ i ]]" into a symbol name using the cache "[[ c ]]".
\item "[[ late define lgc-tree2vt ( t , s ) as newline
lgc-tree2vt0 ( false , false , t , s ) end define ]]"
Convert the tree "[[ t ]]" into a source text representation of "[[ t ]]".
\item "[[ late define lgc-tree2vt0 ( p , q , t , s ) as newline
let << r ,, i >> :: t = t in newline
if r = 0 then lgc-string2vt ( i ) else newline
let n = lgc-symbol2vt ( r , i , s ) in newline
let P = ( n head = QQ ) in newline
let Q = ( n last = QQ ) in newline
let t = lgc-tree*2vt ( P , Q , t , s ) in newline
let t = lgc-tree2vt1 ( n , t ) in newline
if q .and. P .or. p .and. Q then newline
<< QQ ,, QQ ,, !"[" ,, t ,, QQ ,, QQ ,, !"]" >> else t end define ]]"
Convert the tree "[[ t ]]" into a source text representation of "[[ t ]]". "[[ p ]]" is true if "[[ t ]]" is the first argument of a pre-open construct and "[[ q ]]" is true if "[[ t ]]" is the last argument of a post-open construct.
\item "[[ late define lgc-open as << QQ ,, QQ ,, !"[" >> end define ]]"
\item "[[ late define lgc-close as << QQ ,, QQ ,, !"]" >> end define ]]"
\item "[[ late define lgc-tree*2vt ( P , Q , t , s ) as newline
if t atom then true else newline
lgc-tree2vt0 ( P , Q .and. t tail , t head , s ) :: lgc-tree*2vt ( false , Q , t tail , s ) end define ]]"
Apply "[[ lgc-tree2vt ( t , s ) ]]" to each element of "[[ t ]]".
\item "[[ late define lgc-tree2vt1 ( n , t ) as newline
if n atom then true else newline
let c :: n = n in newline
if c != QQ then c :: lgc-tree2vt1 ( n , t ) else newline
t head :: lgc-tree2vt1 ( n , t tail ) end define ]]"
Merge production "[[ n ]]" with treelist "[[ t ]]".
\item "[[ late define lgc-ref2vt ( r , s ) as newline
lgc-tree2vt ( << << r ,, 0 >> >> , s ) end define ]]"
Convert the reference "[[ r ]]" to the name of the associated page.
\end{statements}
\subsection{Translation of terms to Logiweb source}
The construct "[[ tree2vt ( t , c ) ]]" is not used in the lgc compiler but is included here because of its general applicability. It uses the cache "[[ c ]]" instead of the state "[[ s ]]".
\begin{statements}
\item "[[ eager define tree2vt ( t , c ) as newline
let << r ,, i >> :: t = t in newline
if r = 0 then lgc-string2vt ( i ) else newline
let n = lgc-symbol2vt1 ( r , i , c ) in newline
let t = tree*2vt ( t , c ) in newline
lgc-tree2vt1 ( n , t ) end define ]]"
Convert the tree "[[ t ]]" into a source text representation of "[[ t ]]".
\item "[[ eager define tree*2vt ( t , c ) as newline
if t atom then true else newline
tree2vt ( t head , c ) :: tree*2vt ( t tail , c ) end define ]]"
Apply "[[ tree2vt ( t , c ) ]]" to each element of "[[ t ]]".
\end{statements}
\subsection{General html constructors}
\begin{statements}
\item "[[ late define lgc-vector*2html ( w ) as newline
if w atom then true else newline
let c :: v = w in newline
if c = LF then CRLF :: lgc-vector*2html ( v ) else newline
if c < SP then lgc-vector*2html ( v ) else newline
if c = !"<" then !"<" :: lgc-vector*2html ( v ) else newline
if c = !">" then !">" :: lgc-vector*2html ( v ) else newline
if c = !"&" then !"&" :: lgc-vector*2html ( v ) else newline
if c != QQ then c :: lgc-vector*2html ( v ) else newline
if lgc-prefix ( lgc-open , w ) then lgc-html-open ( w ) else newline
if lgc-prefix ( lgc-close , w ) then lgc-html-close ( w ) else newline
c :: lgc-vector*2html ( v ) end define ]]"
Escape special html characters in "[[ v ]]". Translate understood opening and closing brackets by blue brackets.
\item "[[ late define lgc-html-open ( w ) as newline
let w = lgc-vector*2html ( list-suffix ( w , 3 ) ) in newline
!"[" :: w end define ]]"
Produce blue opening bracket.
\item "[[ late define lgc-html-close ( w ) as newline
let w = lgc-vector*2html ( list-suffix ( w , 3 ) ) in newline
!"]" :: w end define ]]"
Produce blue closing bracket.
\item "[[ late define lgc-tree2html ( t , s ) as newline
lgc-vector*2html ( vt2vector* ( lgc-tree2vt ( t , s ) ) ) end define ]]"
Translate the tree "[[ t ]]" to Logiweb source and escape special html characters.
\item "[[ late define lgc-html-begin ( t ) as !"<" :: t :: !">" end define ]]"
Enclose "[[ t ]]" in angle brackets, forming an opening tag.
\item "[[ late define lgc-html-end ( t ) as lgc-html-begin ( !"/" :: t ) end define ]]"
Prepend "[[ t ]]" by a slash and enclose in angle brackets, forming a closing tag.
\item "[[ late define lgc-html-tag ( t ) as lgc-html-begin ( t :: !"/" ) end define ]]"
Suffix "[[ t ]]" by a slash and enclose in angle brackets, forming a self contained tag.
\item "[[ late define lgc-html-br as lgc-html-tag ( !"br" ) :: CRLF end define ]]"
\item "[[ late define lgc-html-wrap ( t , b ) as newline
lgc-html-begin ( t ) :: b :: lgc-html-end ( t ) end define ]]"
Sandwich the body "[[ b ]]" between an opening and closing tag.
\item "[[ late define lgc-html-title ( t ) as lgc-html-wrap ( !"title" , t ) end define ]]"
Turn the string "[[ t ]]" into a title.
\item "[[ late define lgc-html-h2 ( t ) as CRLF :: lgc-html-wrap ( !"h2" , t ) end define ]]"
Turn the string "[[ t ]]" into headline.
\item "[[ late define lgc-html-h3 ( t ) as CRLF :: lgc-html-wrap ( !"h3" , t ) end define ]]"
Turn the string "[[ t ]]" into a second level headline.
\item "[[ late define lgc-html-h4 ( t ) as CRLF :: lgc-html-wrap ( !"h4" , t ) end define ]]"
Turn the string "[[ t ]]" into a third level headline.
\item "[[ late define lgc-html-p ( t ) as lgc-html-wrap ( !"p" , t ) end define ]]"
Turn the string "[[ t ]]" into a paragraph.
\item "[[ late define lgc-html-it ( t ) as lgc-html-wrap ( !"i" , t ) end define ]]"
Display the string "[[ t ]]" in italics.
\item "[[ late define lgc-html-tt ( t ) as lgc-html-wrap ( !"tt" , t ) end define ]]"
Display the string "[[ t ]]" in fixed width font.
\item "[[ late define lgc-html-ptt ( t ) as lgc-html-p ( lgc-html-tt ( t ) ) end define ]]"
Turn the string "[[ t ]]" into a paragraph in fixed width font.
\item "[[ late define lgc-html-string ( t ) as QQ :: t :: QQ end define ]]"
Enclose "[[ t ]]" in double quote characters.
\item "[[ late define lgc-html-arg ( k , v ) as newline
!" " :: k :: !"=" :: lgc-html-string ( v ) end define ]]"
Construct a keyword/value pair for inclusion in a tag.
\item "[[ late define lgc-html-favicon ( r ) as newline
let a = lgc-html-arg ( !"rel" , r ) in newline
let a = a :: lgc-html-arg ( !"href" , !"logiweb.ico" ) in newline
let a = a :: lgc-html-arg ( !"type" , !"image/x-icon" ) in newline
lgc-html-tag ( !"link" :: a ) end define ]]"
Construct a Logiweb favicon for inclusion in the head of an html page. The given ``rel'' type "[[ r ]]" may be "[[ !"icon" ]]" or "[[ !"shortcut icon" ]]".
\item "[[ late define lgc-html-utf8 as newline
let a = lgc-html-arg ( !"http-equiv" , !"Content-Type" ) in newline
let a = a :: lgc-html-arg ( !"content" , !"text/html; charset=UTF-8" ) in newline
lgc-html-tag ( !"meta" :: a ) end define ]]"
\item "[[ late define lgc-html-head ( t ) as newline
let a = lgc-html-utf8 in newline
let a = a :: CRLF :: lgc-html-title ( t ) in newline
let a = a :: CRLF :: lgc-html-favicon ( !"icon" ) in newline
let a = a :: CRLF :: lgc-html-favicon ( !"shortcut icon" ) in newline
lgc-html-wrap ( !"head" , a ) end define ]]"
Construct a head with the given title.
\item "[[ late define lgc-html-icon as newline
let a = lgc-html-arg ( !"alt" , !"Logiweb(TM)" ) in newline
let a = a :: CRLF :: lgc-html-arg ( !"align" , !"right" ) in newline
let a = a :: CRLF :: lgc-html-arg ( !"src" , !"logiweb.png" ) in newline
let a = a :: CRLF :: lgc-html-arg ( !"height" , !"62" ) in newline
let a = a :: CRLF :: lgc-html-arg ( !"width" , !"46" ) in newline
let a = a :: CRLF :: lgc-html-arg ( !"hspace" , !"30" ) in newline
lgc-html-tag ( !"img" :: a ) end define ]]"
Construct a Logiweb icon for inclusion in the body of an html page.
\item "[[ late define lgc-html-href ( r , t ) as newline
let a = lgc-html-begin ( !"a" :: lgc-html-arg ( !"href" , r ) ) in newline
a :: t :: lgc-html-end ( !"a" ) end define ]]"
Construct an html reference.
\item "[[ late define lgc-html-name ( r , t ) as newline
let a = lgc-html-begin ( !"a" :: lgc-html-arg ( !"name" , r ) ) in newline
a :: t :: lgc-html-end ( !"a" ) end define ]]"
Construct an html anchor.
\item "[[ late define lgc-html-named-h3 ( r , t ) as newline
lgc-html-h3 ( lgc-html-name ( r , t ) ) end define ]]"
Construct a second level headline which can serve as an anchor.
\item "[[ late define lgc-html-address ( s ) as newline
let r = s [[ !"reference" ]] in newline
let T = lgc-lgt2grdutc2vt ( lgc-ref2lgt ( r ) , s ) in newline
let h = !"http://logiweb.eu/logiweb/doc/compiler/index.html" in newline
let a = lgc-html-href ( h , !"The Logiweb compiler (lgc)" ) in newline
let h = !"http://logiweb.eu/logiweb/doc/misc/time.html" in newline
let a = a :: CRLF :: lgc-html-href ( h , T ) in newline
let a = lgc-html-wrap ( !"address" , a ) in newline
lgc-html-p ( a ) end define ]]"
Footer of auto-generated Logiweb pages.
\item "[[ late define lgc-html-body ( t , b , s ) as newline
let a = lgc-html-icon in newline
let a = a :: CRLF :: lgc-html-h2 ( t ) in newline
let a = a :: CRLF :: b :: CRLF :: CRLF :: lgc-html-address ( s ) in newline
lgc-html-wrap ( !"body" , a ) end define ]]"
Body of auto-generated Logiweb pages.
\item "[[ late define lgc-html-page ( t , b , s ) as newline
lgc-html-head ( t ) :: CRLF :: lgc-html-body ( t , b , s ) end define ]]"
Autogenerated Logiweb page with title "[[ t ]]" and body "[[ b ]]".
\item "[[ late define lgc-html-help as newline
let h = !"http://logiweb.eu/logiweb/doc/index.html" in newline
lgc-html-href ( h , !"Help" ) end define ]]"
\end{statements}
\subsection{Rendering}
\begin{statements}
\item "[[ late define lgc-render ( x , s ) as newline
let r = s [[ !"reference" ]] in newline
let t = lgc-ref2lgt ( r ) in newline
let u = lgc-lgt2grdutc2vt ( t , s ) in newline
let e :: p = lgc-render-dirname ( r , s ) catch in newline
if e then lgc-rendering-no-colon ( s ) else newline
let n = lgc-tree2html ( << << r ,, 0 >> >> , s ) in newline
let s = lgc-render-dir ( p , s ) in newline
let s = lgc-render-link ( p , s ) in newline
let s = lgc-render-icons ( p , s ) in newline
let s = lgc-render-index ( p , n , s ) in newline
let s = lgc-render-extract ( r , p , n , s ) in newline
let s = lgc-render-vector ( p , s ) in newline
let s = lgc-render-ref ( p , s ) in newline
let s = lgc-render-source ( p , s ) in newline
let s = lgc-progress ( !"Verifying" , 3 , s ) in newline
lgc-exec-events ( s , lgc-render-verify ( x , s ) ) end define ]]"
Render standard contents of root directory of page except diagnose. Then pass control to verification.
\end{statements}
\subsection{Rendering directory}
\begin{statements}
\item "[[ late define lgc-render-add-slash ( p ) as newline
if p then true else newline
let c :: p = p in newline
if p != true then c :: lgc-render-add-slash ( p ) else newline
if c = !"/" then << c >> else << c ,, !"/" >> end define ]]"
Add a slash to the end of the singleton list "[[ p ]]" unless "[[ p ]]" already has such a slash.
\item "[[ late define lgc-render-dirname ( r , s ) as newline
let r = lgc-string2mixed ( r ) in newline
let R :: true = s [[ !"parameters" ]] [[ !"rendering" ]] in newline
let R = lgc-render-add-slash ( vt2vector* ( R ) ) in newline
let p = lgc-replace-colon ( R , r ) in newline
lgc-tilde-expand1 ( p , s ) end define ]]"
Return the name of the rendering directory.
\item "[[ late define lgc-render-dir ( p , s ) as newline
lgc-push-event ( s , fileMkdir ( p :: !"page/" ) ) end define ]]"
Create the directory "[[ p ]]" and its ancestors. Also create a subdirectory named `page' under "[[ p ]]". (Actually, the function creates p/page and all its ancestors, including p itself).
\end{statements}
\subsection{Rendering of links}
\begin{statements}
\item "[[ late define lgc-lgs-suffix as reverse ( vt2vector* ( ".lgs" ) ) end define ]]"
\item "[[ late define lgc-page-name ( s ) as newline
let n = s [[ !"parameters" ]] [[ !"source" ]] head in newline
let n = vt2vector* ( n ) in newline
let n = reverse ( n ) in newline
let e :: n prime = lgc-parse-prefix ( lgc-lgs-suffix , n ) catch in
let n = if e then n else n prime in newline
lgc-page-name1 ( n , true ) end define ]]"
Return the name of the source file with directory names and suffix "[[ !".lgs" ]]" removed if present. As an example, if the source file is named "[[ !"../foo.lgs" ]]" then "[[ << !"f" ,, !"o" ,, !"o" >> ]]" is returned.
\item "[[ late define lgc-page-name1 ( n , r ) as newline
if n atom then r else newline
let c :: n = n in newline
if c = !"/" then r else newline
lgc-page-name1 ( n , c :: r ) end define ]]"
Remove directory names from the reverse path name "[[ n ]]" and accumulate the result in "[[ r ]]".
\item "[[ late define lgc-render-link ( p , s ) as newline
if s [[ !"stack" ]] != true then s else newline
let p = lgc-cwd-expand ( p , s ) in newline
let L = s [[ !"parameters" ]] [[ !"link" ]] in newline
let N = lgc-page-name ( s ) in newline
lgc-render-link1 ( p , N , L , s ) end define ]]"
Generate all links to the page root directory.
\item "[[ late define lgc-render-link1 ( p , N , L , s ) as newline
if L atom then s else newline
let l :: L = L in newline
let e :: n = lgc-replace-colon ( vt2vector* ( l ) , N ) catch in newline
let n = if e then l else n in newline
let n = lgc-tilde-expand1 ( n , s ) in newline
let s = lgc-push-event ( s , fileMkdir ( n ) ) in newline
let s = lgc-push-event ( s , fileRm ( n ) ) in newline
let s = lgc-push-event ( s , fileSymlink ( p , n ) ) in newline
lgc-render-link1 ( p , N , L , s ) end define ]]"
Replace the rightmost colon of each element of the link list "[[ L ]]" by the name "[[ N ]]" and create a link which points to "[[ p ]]". Also create ancestor directories as needed and overwrite the link if it exists already.
\end{statements}
\subsection{Rendering of non-html}
\begin{statements}
\item "[[ late define lgc-render-vector ( p , s ) as newline
let E = fileWrite ( p :: !"vector.lgw" , s [[ !"vector" ]] ) in newline
lgc-push-event ( s , E ) end define ]]"
Generate the vector of the page.
\item "[[ late define lgc-render-ref ( p , s ) as newline
let r = s [[ !"reference" ]] in newline
let r = lgc-string2mixed ( r ) in newline
let E = fileWrite ( p :: !"ref.lgp" , r ) in newline
lgc-push-event ( s , E ) end define ]]"
Generate the vector of the page.
\item "[[ late define lgc-render-source ( p , s ) as newline
let S = lgc-render-source1 ( s ) in newline
let E = fileWrite ( p :: !"source.lgs" , S ) in newline
lgc-push-event ( s , E ) end define ]]"
Write the source text to the rendering directory. The function is prepared for a situation where the source may be unknown.
\item "[[ late define lgc-render-source1 ( s ) as newline
let v = s [[ !"source" ]] in newline
if v then !"Source not known, sorry." else newline
let R = lgc-string2mixed ( s [[ !"reference" ]] ) in newline
lgc-add-headline ( s , R , v ) end define ]]"
Add reference to headline. Act sensibly if the source is unknown.
\item "[[ late define lgc-render-icons ( p , s ) as newline
let s = lgc-push-event ( s , fileWrite ( p :: !"logiweb.png" , lgc-logiweb.png ) ) in newline
let s = lgc-push-event ( s , fileWrite ( p :: !"logiweb.ico" , lgc-logiweb.ico ) ) in newline
lgc-push-event ( s , fileWrite ( p :: !"page/logiweb.eps" , lgc-logiweb.eps ) ) end define ]]"
Write icons to the rendering directory.
\end{statements}
\subsection{Rendering of index}
\begin{statements}
\item "[[ late define lgc-render-index ( p , n , s ) as newline
let t = !"Logiweb main menu of " :: n in newline
let a = lgc-html-h3 ( !"Rendering" ) in newline
let a = a :: CRLF :: lgc-html-href ( !"page/page.pdf" , !"Main text" ) in newline
let a = a :: CRLF :: lgc-html-href ( !"page/index.html" , !"Index" ) in newline
let a = a :: CRLF :: lgc-html-href ( !"page/diagnose.pdf" , !"Diagnose" ) in newline
let a = a :: CRLF :: lgc-html-h3 ( !"Debugging aids" ) in newline
let a = a :: CRLF :: lgc-html-href ( !"extract.html" , !"Extract" ) in newline
let a = a :: CRLF :: lgc-html-href ( !"source.lgs" , !"Source" ) in newline
let a = a :: CRLF :: lgc-html-href ( !"diagnose.html" , !"Diagnose" ) in newline
let a = a :: CRLF :: lgc-html-h3 ( !"Documentation" ) in newline
let a = a :: CRLF :: lgc-html-help in newline
let a = lgc-html-page ( t , a , s ) in newline
let E = fileWrite ( p :: !"index.html" , a ) in newline
lgc-push-event ( s , E ) end define ]]"
Write an html index to the rendering directory.
\subsection{Rendering of extract}
\begin{statements}
\item "[[ late define lgc-render-extract ( r , p , n , s ) as newline
let t = !"Logiweb extract of " :: n in newline
let a = lgc-html-href ( !"index.html" , !"Up" ) in newline
let a = a :: CRLF :: lgc-html-help in newline
let a = a :: CRLF :: lgc-render-extract-toc in newline
let a = a :: CRLF :: lgc-render-extract-date ( r , s ) in newline
let a = a :: CRLF :: lgc-render-bib ( r , s ) in newline
let a = a :: CRLF :: lgc-render-def ( r , s ) in newline
let a = a :: CRLF :: lgc-render-charge ( r , s ) in newline
let a = lgc-html-page ( t , a , s ) in newline
let E = fileWrite ( p :: !"extract.html" , a ) in newline
lgc-push-event ( s , E ) end define ]]"
Render the extract of the page.
\item "[[ late define lgc-render-extract-toc as newline
let a = lgc-html-href ( !"#timestamp" , !"Date of publication" ) :: lgc-html-br in newline
let a = a :: lgc-html-href ( !"#bibliography" , !"Bibliography" ) :: lgc-html-br in newline
let a = a :: lgc-html-href ( !"#codex" , !"Definitions" ) :: lgc-html-br in newline
let a = a :: lgc-html-href ( !"#charge" , !"Charges" ) in newline
let a = lgc-html-p ( a ) in newline
lgc-html-h3 ( !"Table of contents" ) :: CRLF :: a end define ]]"
Render a table of contents for the extract.
\item "[[ late define lgc-render-extract-date ( r , s ) as newline
let t = lgc-ref2lgt ( r ) in newline
let a = lgc-lgt2grdutc2vt ( t , s ) in newline
let a = a :: !" (Gregorian Date / Universal Coordinated Time)" in newline
let a = a :: lgc-html-br in newline
let a = a :: lgc-lgt2mjdtai2vt ( t ) in newline
let a = a :: !" (Modified Julian Day / International Atomic Time)" in newline
let a = a :: lgc-html-br in newline
let a = a :: lgc-lgt2vt ( t ) in newline
let a = a :: !" (Logiweb Time)" in newline
let a = lgc-html-p ( a ) in newline
lgc-html-named-h3 ( !"timestamp" , !"Date of publication" ) :: CRLF :: a end define ]]"
Render date of publication in three formats.
\end{statements}
\subsection{Rendering of bibliography}
\begin{statements}
\item "[[ late define lgc-render-bib ( r , s ) as newline
let b = s [[ !"cluster" ]] [[ r ]] [[ r ]] [[ !"bibliography" ]] in newline
let w = length ( lgc-itoa ( length ( b ) - 1 ) ) in newline
let a = lgc-html-named-h3 ( !"bibliography" , !"Bibliography" ) in newline
a :: CRLF :: lgc-html-ptt ( lgc-render-bib1 ( 0 , w , b , s ) ) end define ]]"
Render bibliography.
\item "[[ late define lgc-render-ref-link ( r , a ) as newline
let h = lgc-string2mixed ( r ) in newline
lgc-html-href ( !"../../logiweb/" :: h :: "/index.html" , a ) end define ]]"
Add link to page with reference r.
\item "[[ late define lgc-render-bib1 ( i , w , b , s ) as newline
let r :: b = b in newline
let h = lgc-string2mixed ( r ) in newline
let a = !"[" :: lgc-ctoa ( i , w ) :: !"] " in newline
let a = a :: lgc-ref2vt ( r , s ) in newline
let a = a :: !" (" :: h :: !")" in newline
let a = lgc-render-ref-link ( r , a ) in newline
if b then a else newline
a :: lgc-html-br :: lgc-render-bib1 ( i + 1 , w , b , s ) end define ]]"
Render each bibliography entry.
\end{statements}
\subsection{Rendering of definitions}
\begin{statements}
\item "[[ late define lgc-render-def ( r , s ) as newline
let a = lgc-html-named-h3 ( !"codex" , !"Definitions" ) in newline
let R = s [[ !"cluster" ]] [[ r ]] [[ r ]] in newline
let b = R [[ !"bibliography" ]] in newline
let c = R [[ !"codex" ]] in newline
let d = R [[ !"dictionary" ]] in newline
let I = array-domain ( d ) in newline
let a = a :: CRLF :: lgc-render-def-sym* ( r , r , I , s ) in newline
let c = c [[ r -> true ]] in newline
if c then a else newline
a :: CRLF :: lgc-render-def1 ( r , b , c , s ) end define ]]"
Render all definitions: Extract the rack "[[ R ]]" from the state "[[ s ]]". Then extract bibliography "[[ b ]]", codex "[[ c ]]", and dictionary "[[ d ]]".
Then render domestic definitions (i.e.\ definitions on page "[[ r ]]" of symbols from page "[[ r ]]"). The rendering of domestic definitions is special because a symbol gets a section even if it has no definitions. This allows the user to see that the symbol exists and to deduce its arity. In that way it is ensured that the information of the dictionary is present.
When a page has been rendered, it is removed from the codex "[[ c ]]" so that it is not rendered more than once.
\item "[[ late define lgc-render-def1 ( p , b , c , s ) as newline
if b atom then lgc-render-def2 ( p , array-domain ( c ) , c , s ) else newline
let r :: b = b in newline
let a = lgc-render-def-sym* ( p , r , array-domain ( c [[ r ]] ) , s ) in newline
let c = c [[ r -> true ]] in newline
if c then a else newline
a :: CRLF :: lgc-render-def1 ( p , b , c , s ) end define ]]"
Render definitions of symbols from directly referenced pages. Definitions are sorted according to the order in which pages are referenced. If (for some silly reason) the bibliography references some page more than once then the definitions of that page are only stated once.
\item "[[ late define lgc-render-def2 ( p , R , c , s ) as newline
if R atom then true else newline
let r :: R = R in newline
let a = lgc-render-def-sym* ( p , r , array-domain ( c [[ r ]] ) , s ) in newline
if R then a else newline
a :: CRLF :: lgc-render-def2 ( p , R , c , s ) end define ]]"
Render definitions of symbols from transitively referenced pages which have not yet been rendered.
\item "[[ late define lgc-render-def-sym* ( p , r , I , s ) as newline
if I atom then true else newline
let i :: I = I in newline
let a = lgc-render-def-sym ( p , r , i , s ) in newline
if I atom then a else newline
a :: CRLF :: lgc-render-def-sym* ( p , r , I , s ) end define ]]"
Render the definitions of the symbols with reference "[[ r ]]" and index in the list "[[ I ]]" of indices
\item "[[ late define lgc-render-def-sym ( p , r , i , s ) as newline
if r = 0 then lgc-render-def-string ( p , i , s ) else newline
let a = lgc-symbol2vt ( r , i , s ) in newline
let a = lgc-vector*2html ( a ) in newline
let a = lgc-html-h4 ( a ) in newline
let b = lgc-render-ref-link ( r , lgc-ref2vt ( r , s ) ) in newline
let b = !"Index " :: lgc-itoa ( i ) :: " of page " :: b in newline
let a = a :: CRLF :: lgc-html-p ( b ) in newline
let d = s [[ !"cluster" ]] [[ p ]] [[ p ]] [[ !"codex" ]] [[ r ]] [[ i ]] in newline
a :: CRLF :: lgc-render-def-sym1 ( d , array-domain ( d ) , s ) end define ]]"
Render the definitions of the symbol with reference "[[ r ]]" and index "[[ i ]]".
\item "[[ late define lgc-render-def-string ( p , i , s ) as newline
let a = lgc-vector*2html ( vt2vector* ( i ) ) in newline
let a = lgc-html-h4 ( lgc-html-it ( a ) ) in newline
let b = !"Index " :: lgc-itoa ( i ) :: " of the string page (page zero)" in newline
let a = a :: CRLF :: lgc-html-p ( b ) in newline
let d = s [[ !"cluster" ]] [[ p ]] [[ p ]] [[ !"codex" ]] [[ 0 ]] [[ i ]] in newline
a :: CRLF :: lgc-render-def-sym1 ( d , array-domain ( d ) , s ) end define ]]"
Render the definitions of the string "[[ i ]]".
\item "[[ late define lgc-render-def-sym1 ( D , R , s ) as newline
if R atom then true else newline
let r :: R = R in newline
let d = D [[ r ]] in newline
let a = lgc-render-def-sym2 ( d , array-domain ( d ) , s ) in newline
if R atom then a else newline
a :: CRLF :: lgc-render-def-sym1 ( D , R , s ) end define ]]"
Render definitions in "[[ D ]]" for the list "[[ R ]]" of aspect references.
\item "[[ late define lgc-render-def-sym2 ( D , I , s ) as newline
if I atom then true else newline
let i :: I = I in newline
let a = lgc-html-p ( lgc-render-def-sym3 ( D [[ i ]] , s ) ) in newline
if I atom then a else newline
a :: CRLF :: lgc-render-def-sym2 ( D , I , s ) end define ]]"
Render definitions in "[[ D ]]" for the list "[[ I ]]" of aspect indices.
\item "[[ late define lgc-render-def-sym3 ( t , s ) as newline
if t ref != 0 then lgc-tree2html ( t , s ) else newline
lgc-html-tt ( !"Proclamed meaning: " :: lgc-html-string ( t idx ) ) end define ]]"
\end{statements}
\subsection{Rendering of charges}
\begin{statements}
\item "[[ late define lgc-render-charge ( r , s ) as newline
let a = lgc-html-named-h3 ( !"charge" , !"Charges" ) in newline
let b = s [[ !"cluster" ]] [[ r ]] [[ r ]] [[ !"bibliography" ]] in newline
let C = lgc-collect-charge ( b , s , true ) in newline
a :: CRLF :: lgc-render-charge0 ( b , C , s , true ) end define ]]"
Construct "[[ C ]]" such that "[[ get* ( C , c ) tail ]]" equals "[[ false ]]" if some construct has charge "[[ c ]]" and such that "[[ get* ( C , c ) head [[ r ]] [[ i ]] ]]" equals "[[ false ]]" if the symbol with reference "[[ r ]]" and index "[[ i ]]" has charge "[[ c ]]".
\item "[[ late define lgc-collect-charge ( b , s , C ) as newline
if b atom then C else newline
let r :: b = b in newline
let c = s [[ !"cluster" ]] [[ r ]] [[ r ]] [[ !"codex" ]] [[ r ]] in newline
let C = lgc-collect-charge1 ( r , c , C ) in newline
lgc-collect-charge ( b , s , C ) end define ]]"
Collect all charges used on pages listed the bibliography "[[ b ]]" and add them to "[[ C ]]".
\item "[[ late define lgc-collect-charge1 ( r , c , C ) as newline
if c atom then C else newline
if c head intp then lgc-collect-charge2 ( r , c head , c tail , C ) else newline
let C = lgc-collect-charge1 ( r , c head , C ) in newline
lgc-collect-charge1 ( r , c tail , C ) end define ]]"
Collect all charges used by the subcodex "[[ c ]]".
\item "[[ late define lgc-collect-charge2 ( r , i , c , C ) as newline
let c = lgc-def2charge ( c [[ 0 ]] [[ !"charge" ]] ) in newline
lgc-collect-charge3 ( r , i , c , C ) end define ]]"
Add the symbol with reference "[[ r ]]", index "[[ i ]]", and charge "[[ c ]]" to "[[ C ]]".
\item "[[ late define lgc-collect-charge3 ( r , i , c , C ) as newline
if c atom then lgc-collect-charge4 ( r , i , C ) else newline
let C = if C tail != false then C else true [[ 0 -> C ]] in newline
C [[ c head -> lgc-collect-charge3 ( r , i , c tail , C [[ c head ]] ) ]] end define ]]"
Add the symbol with reference "[[ r ]]", index "[[ i ]]", and charge "[[ c ]]" to "[[ C ]]" by recursion in "[[ c ]]".
\item "[[ late define lgc-collect-charge4 ( r , i , C ) as newline
if C = true .or. C tail = false then C head [[ << r ,, i >> => false ]] :: false else newline
C [[ 0 -> lgc-collect-charge4 ( r , i , C [[ 0 ]] ) ]] end define ]]"
Add the symbol with reference "[[ r ]]" and index "[[ i ]]" to "[[ C ]]". Extend the charge by trailing zeros until we have "[[ C tail = false ]]".
\item "[[ late define lgc-render-charge0 ( b , C , s , c ) as newline
if C tail = false then lgc-render-charge2 ( b , C , s , c ) else newline
let D = array-domain ( C ) in lgc-render-charge1 ( b , C , D , s , c ) end define ]]"
\item "[[ late define lgc-render-charge1 ( b , C , D , s , c ) as newline
if D atom then true else newline
let d :: D = D in newline
let a = lgc-render-charge0 ( b , C [[ d ]] , s , d :: c ) in newline
if D atom then a else newline
a :: CRLF :: lgc-render-charge1 ( b , C , D , s , c ) end define ]]"
Render all charges in "[[ C ]]" in increasing order. "[[ D ]]" is the list of not yet processed part of the domain of "[[ C ]]". "[[ c ]]" accumulates the name of the charge. "[[ b ]]" is the bibliography of the page and "[[ s ]]" is the state.
\item "[[ late define lgc-render-charge2 ( b , C , s , c ) as newline
let C = C head in newline
let r :: b = b in newline
let a = lgc-render-charge-bib ( b , C , s ) in newline
let D = array-domain ( C [[ r ]] ) in newline
let a = a :: CRLF :: lgc-render-charge-self ( r , D , s ) in newline
let c = lgc-charge2vector* ( reverse ( lgc-parse-charge2 ( c ) ) ) in newline
lgc-html-h4 ( default ( !"0" , c ) ) :: CRLF :: lgc-html-ptt ( a ) end define ]]"
Render charge section for charge "[[ c ]]".
\item "[[ late define lgc-render-charge-bib ( b , C , s ) as newline
if b atom then true else newline
let r :: b = b in newline
let a = lgc-render-charge-bib ( b , C , s ) in newline
let D = array-domain ( C [[ r ]] ) in newline
if D then a else newline
lgc-symbol2vt ( r , D head , s ) :: lgc-html-br :: a end define ]]"
For each page listed in "[[ b ]]" render the first construct (if any) which occurs in "[[ C ]]".
\item "[[ late define lgc-render-charge-self ( r , D , s ) as newline
if D atom then true else newline
let i :: D = D in newline
let a = lgc-render-charge-self ( r , D , s ) in newline
lgc-symbol2vt ( r , i , s ) :: lgc-html-br :: a end define ]]"
Render all constructs in the list "[[ D ]]" of indices.
\end{statements}
\subsection{Verification}
\begin{statements}
\item "[[ late define lgc-render-verify ( x , s ) as newline
let r = s [[ !"reference" ]] in newline
let p = lgc-render-dirname ( r , s ) in newline
let n = lgc-tree2html ( << << r ,, 0 >> >> , s ) in newline
let d = s [[ !"cluster" ]] [[ r ]] [[ r ]] [[ !"diagnose" ]] untag in newline
let D = lgc-tree2vector* ( d , s ) in newline
let s = lgc-render-tdiagnose ( p , D , s ) in newline
let s = lgc-render-hdiagnose ( p , n , d , D , s ) in newline
let s = lgc-render-pdiagnose ( D , s ) in newline
let s = lgc-progress ( !"Dumping to cache" , 3 , s ) in newline
lgc-exec-events ( s , lgc-render-dump ( x , s ) ) end define ]]"
Render diagnose. Then pass control to cache dumping.
\item "[[ late define lgc-render-tdiagnose ( p , D , s ) as newline
let E = fileWrite ( p :: !"diagnose.txt" , D ) in newline
lgc-push-event ( s , E ) end define ]]"
Write the diagnose to the rendering directory.
\item "[[ late define lgc-render-pdiagnose ( D , s ) as newline
if D then lgc-progress ( LF :: !"The page is correct" :: LF , 2 , s ) else newline
let s = lgc-progress ( LF :: !"Claim failed" :: LF , 2 , s ) in newline
lgc-progress ( D , 3 , s ) end define ]]"
Write the diagnose to standard output (``p'' for ``progress'' or ``print'').
\end{statements}
\subsection{HTML rendering of diagnose}
\item "[[ late define lgc-render-hdiagnose ( p , n , d , D , s ) as newline
let t = !"Logiweb diagnose of " :: n in newline
let a = lgc-html-href ( !"index.html" , !"Up" ) in newline
let a = a :: CRLF :: lgc-html-help in newline
let d = if d then lgc-render-correct else lgc-render-hdiagnose1 ( D ) in newline
let a = lgc-html-page ( t , a :: CRLF :: d , s ) in newline
let E = fileWrite ( p :: !"diagnose.html" , a ) in newline
lgc-push-event ( s , E ) end define ]]"
Write an html version of the diagnose to the rendering directory.
\item "[[ late define lgc-render-correct as newline
lgc-html-h3 ( !"The page is correct" ) end define ]]"
Diagnose for correct pages.
\item "[[ late define lgc-render-hdiagnose1 ( D ) as newline
lgc-html-p ( lgc-vector*2html ( vt2vector* ( D ) ) ) end define ]]"
Diagnose for incorrect pages.
\end{statements}
\subsection{Dumping to cache}
\begin{statements}
\item "[[ late define lgc-render-dump ( x , s ) as newline
let r = s [[ !"reference" ]] in newline
let p = lgc-render-dirname ( r , s ) :: "rack.lgr" in newline
let R = s [[ !"cluster" ]] [[ r ]] [[ r ]] in newline
let R = lgr-rack-clean ( R ) in newline
let R = rack2sl ( R ) in newline
let s = lgc-progress ( !"Dumping to " :: p , 4 , s ) in newline
let s = lgc-push-event ( s , fileMkdir ( p ) ) in newline
let s = lgc-push-event ( s , fileWrite ( p , R ) ) in newline
let s = lgc-progress ( !"User rendering" , 3 , s ) in newline
lgc-exec-events ( s , lgc-render-user ( x , s ) ) end define ]]"
Dump the rack "[[ R ]]" and pass control to user rendering at end of rack dumping.
\end{statements}
" ]"\section{User rendering}"[ "
\subsection{Overview}
Rendering of a page comprises fixed rendering in the rendering directory as defined in Section \ref{sec:Rendering} plus user (i.e.\ author) defined rendering in a subdirectory named `page' of the rendering directory. By convension, the page directory is supposed to contain a file named `index.html' which is supposed to give a user (i.e.\ reader) friendly overview of the contents of the page directory. Furthermore, by convension, the page directory is supposed to contain a `bin' directory which is supposed to contain binaries defined on the page.
The author of a page may define his or her own rendering function or may rely on the default provided by the lgc-compiler. In any case, rendering is done in two stages: First, the page is converted into a vector tree "[[ R ]]" which contains \emph{rendering events}. Second, the tree "[[ R ]]" is converted into output events which are then executed by the Logiweb machine, ultimately resulting in files in the page directory.
Conversion of the tree "[[ R ]]" of rendering events to a list of output events is done by "[[ lgc-render-user1 ( R , true , s ) ]]" as defined in Section \ref{sec:MainUserRenderingFunctions}. This conversion allows the user to produce text files and binary files and to invoke latex, bibtex, makeindex, and dvipdfm.
The difference between text and binary files is in the handling of newlines. When producing a binary file, bytes are writen directly to the file. When producing a text file, newline sequences are converted to host newline sequences.
From the point of view of the implementer of Logiweb, the ability to call latex, bibtex, makeindex, and dvipdfm provides a cheap but not completely satisfactoy way to produce high quality documents. A more satisfactory solution would be to port those four programs to Logiweb and call them from the renderer inside the system. In that way Logiweb would not rely on external programs and could guarantee that pages would look the same regardless of e.g.\ which sty files are available at each site.
It should be noted that, once upon a time, Logiweb also allowed to call Mizar and also had facilities for generating MathML, XML, and several other formats. Today, Logiweb just allows the user to define his or her own renderer, so it is up to each author which formats he or she wants to support.
As mentioned, the author of a page may define his or her own rendering function or may rely on the default provided by the lgc-compiler. The conversion is done by "[[ lgc-render-expand ( r , s ) ]]" defined in Section \ref{sec:InvokationOfUserRendering}. In case the author has defined a renderer, it is invoked by "[[ lgc-render-expand1 ( d , V , c ) ]]", and otherwise default renderer "[[ lgc-render-default ( d , V , c ) ]]" is invoked.
The default renderer renders the body of the page in the page directory and the executables defined on the page in the bin directory under the page directory. The function for rendering of executables, "[[ lgc-render-exec ( r , V , c ) ]]" is quite simple. The function for rendering the body, "[[ lgc-render-body ( r , V , c ) ]]" is more complicated and resembles the function used for macro expansion on the base page.
However, to speed up rendering, the default renderer is split in two parts: a compiler and an evaluator. The compiler translates rendering definitions (use, show, and name definitions) to \emph{rendering code}. The evaluator evaluates the rendering code. In contrast, the macro expansion engine is an evaluator which directly interprets macro definitions. The reason for splitting rendering into compilation and evaluation is that compilation takes quite some time but only needs to be done once for each construct. Hence, each construct used is compiled and the resulting rendering code is memorized.
The "[[ lgc-render-body ( r , V , c ) ]]" function uses "[[ stateexpand ( t , s , c ) ]]" to render the body where "[[ t ]]" is the body expressed as a term, "[[ s ]]" is a \emph{rendering state} and "[[ c ]]" is the cache of the page.
A rendering state "[[ s ]]" has form "[[ f :: C :: V ]]" where "[[ f ]]" is a tagged function which is used by default for rendering subexpressions, "[[ C ]]" is used for memorizing rendering code, and "[[ V ]]" is a value passed down from calling to called renderers. In particular, "[[ V [[ !"parameters" ]] ]]" is supposed to contain the parameters used when invoking lgc. This should be used sparingly but may be used, e.g.\ when there is a need to know the newline convention of the underlying host system or when there is a need to know the location of leap seconds. In addition, "[[ V [[ !"cache" ]] ]]" contains the cache of the page.
\subsection{Variable names}
Default rendering converts a tree "[[ t ]]" into a vector tree "[[ R ]]" which we refer to as the rendering of "[[ t ]]".
In the default rendering functions, we use parameter names as follows:
\begin{tabular}{ll}
"[[ t ]]" & term to be rendered \\
"[[ R ]]" & rendering \\
"[[ T ]]" & right hand side of use or show definition \\
"[[ p = T :: p ]]" & parameter list \\
"[[ a = T :: a ]]" & argument list \\
"[[ b = ( T :: t ) :: b ]]" & association list from parameters to arguments \\
"[[ f ]]" & tagged function \\
"[[ v ]]" & arbitrary value \\
"[[ s = f :: v ]]" & rendering state \\
"[[ c ]]" & cache \\
\end{tabular}
\subsection{Main user rendering functions}\label{sec:MainUserRenderingFunctions}
\begin{statements}
\item "[[ late define lgc-render-user ( x , s ) as newline
let r = s [[ !"reference" ]] in newline
let E :: R = lgc-render-expand ( r , s ) catch in newline
if E then newline
lgc-simple-error ( "Exception raised during user rendering, goodbye." , s ) else newline
lgc-render-user1 ( R , true , s ) end define ]]"
Perform user rendering, then pass control to "[[ lgc-render-user1 ( R , S , s ) ]]" to get the rendering executed.
\item "[[ late define lgc-render-user0 ( R ) as newline
if R atom then true :: true else newline
if R head intp then R else lgc-render-user0 ( R head ) end define ]]"
Move down in "[[ R ]]" until right above an integer.
\item "[[ late define lgc-render-noevent as true [[ newline
!"file" -> false ]] [[ newline
!"exec" -> false ]] [[ newline
!"text" -> false ]] [[ newline
!"script" -> false ]] [[ newline
!"lgwam" -> false ]] [[ newline
!"latex" -> false ]] [[ newline
!"bibtex" -> false ]] [[ newline
!"makeindex" -> false ]] [[ newline
!"dvipdfm" -> false ]] end define ]]"
We have "[[ lgc-render-noevent [[ e ]] = false ]]" iff "[[ e ]]" names a rendering event.
\item "[[ late define lgc-render-user1 ( R , S , s ) as newline
if R atom then newline
if S then lgc-goodbye ( s ) else lgc-render-user1 ( S head , S tail , s ) else newline
let e :: R = R in newline
if .not. e intp .or. lgc-render-noevent [[ e ]] then lgc-render-user1 ( e , R :: S , s ) else
let s = s [[ !"renderstack" -> S ]] in newline
let r = s [[ !"reference" ]] in newline
let p = lgc-render-dirname ( r , s ) :: !"page/" in newline
let x :: R = lgc-render-user0 ( R ) in newline
if e = !"file" then lgc-render-file ( p , x , R , s ) else newline
if e = !"exec" then lgc-render-file-exec ( p , x , R , s ) else newline
if e = !"text" then lgc-render-text ( p , x , R , s ) else newline
if e = !"script" then lgc-render-text-exec ( p , x , R , s ) else newline
if e = !"lgwam" then lgc-render-lgwam ( p , x , R , s ) else newline
if e = !"latex" then lgc-render-invoke ( e , p , x , s ) else newline
if e = !"bibtex" then lgc-render-invoke ( e , p , x , s ) else newline
if e = !"makeindex" then lgc-render-invoke ( e , p , x , s ) else newline
if e = !"dvipdfm" then lgc-render-invoke ( e , p , x , s ) else
let s = lgc-progress ( !"Unknown rendering event: " :: e , 2 , s ) in newline
lgc-goodbye ( s ) end define ]]"
Translate the event "[[ R ]]" to an output event followed by passing control to "[[ lgc-render-user2 ( x , s ) ]]".
\item "[[ late define lgc-render-user2 ( x , s ) as newline
let s = lgc-render-response ( x , s ) in newline
let S = s [[ !"renderstack" ]] in newline
if S then lgc-goodbye ( s ) else newline
lgc-render-user1 ( S head , S tail , s ) end define ]]"
Receive the response from the previous rendering event, then search for the next rendering event.
\item "[[ late define lgc-render-response ( x , s ) as newline
let << true ,, << true ,, r >> >> = x in newline
if r = <<>> .or. r = << NULL >> then s else newline
let << p ,, e ,, n >> = s [[ !"invoked" ]] in newline
lgc-progress ( !"Error running " :: e :: SP :: n :: !" in " :: p , 2 , s ) end define ]]"
Complain if the last rendering event returned a non-zero exit code.
\item "[[ late define lgc-goodbye ( s ) as newline
if s [[ !"stack" ]] != true then lgc-load-fetch ( s ) else newline
let s = lgc-progress ( !"Goodbye" , 3 , s ) in newline
lgc-do-events ( s ) end define ]]"
Terminate execution.
\item "[[ late define lgc-render-refuse ( n , s ) as newline
let s = lgc-progress ( !"Improper filename: " :: n , 2 , s ) in newline
let s = lgc-progress ( lgc-render-path ( n ) , 2 , s ) in newline
lgc-goodbye ( s ) end define ]]"
Complain about filename, then quit.
\end{statements}
\subsection{Functions for executing individual rendering events}
\begin{statements}
\item "[[ late define lgc-render-file ( p , n , c , s ) as newline
if lgc-render-path ( n ) != true then lgc-render-refuse ( n , s ) else newline
let p = p :: n in newline
let s = lgc-progress ( !"Writing file:" :: p , 4 , s ) in newline
let E = fileMkdir ( p ) in newline
let s = lgc-push-event ( s , E ) in newline
let E = fileWrite ( p , c ) in newline
let s = lgc-push-event ( s , E ) in newline
lgc-exec-events ( s , lgc-render-user2 ( x , s ) ) end define ]]"
Write contents "[[ c ]]" to file named "[[ n ]]" relative to path "[[ p ]]".
\item "[[ late define lgc-render-file-exec ( p , n , c , s ) as newline
if lgc-render-path ( n ) != true then lgc-render-refuse ( n , s ) else newline
let p = p :: n in newline
let s = lgc-progress ( !"Writing file:" :: p , 4 , s ) in newline
let E = fileMkdir ( p ) in newline
let s = lgc-push-event ( s , E ) in newline
let E = fileWriteExec ( p , c ) in newline
let s = lgc-push-event ( s , E ) in newline
lgc-exec-events ( s , lgc-render-user2 ( x , s ) ) end define ]]"
Write contents "[[ c ]]" to executable file named "[[ n ]]" relative to path "[[ p ]]".
\item "[[ late define lgc-render-text ( p , n , c , s ) as newline
if lgc-render-path ( n ) != true then lgc-render-refuse ( n , s ) else newline
let p = p :: n in newline
let N = lgc-host-newline ( s ) in newline
let s = lgc-progress ( !"Writing file:" :: p , 4 , s ) in newline
let E = fileMkdir ( p ) in newline
let s = lgc-push-event ( s , E ) in newline
let E = textWrite ( p , N , c ) in newline
let s = lgc-push-event ( s , E ) in newline
lgc-exec-events ( s , lgc-render-user2 ( x , s ) ) end define ]]"
Write contents "[[ c ]]" with newline translation to file named "[[ n ]]" relative to path "[[ p ]]".
\item "[[ late define lgc-render-text-exec ( p , n , c , s ) as newline
if lgc-render-path ( n ) != true then lgc-render-refuse ( n , s ) else newline
let p = p :: n in newline
let N = lgc-host-newline ( s ) in newline
let s = lgc-progress ( !"Writing file:" :: p , 4 , s ) in newline
let E = fileMkdir ( p ) in newline
let s = lgc-push-event ( s , E ) in newline
let E = textWriteExec ( p , N , c ) in newline
let s = lgc-push-event ( s , E ) in newline
lgc-exec-events ( s , lgc-render-user2 ( x , s ) ) end define ]]"
Write contents "[[ c ]]" with newline translation to executable file named "[[ n ]]" relative to path "[[ p ]]".
\item "[[ late define lgc-render-add-lf ( a ) as newline
if a atom then true else newline
a head :: LF :: lgc-render-add-lf ( a tail ) end define ]]"
Add line feed after each element of "[[ a ]]".
\item "[[ late define lgc-render-lgwam ( p , n , c , s ) as newline
if lgc-render-path ( n ) != true then lgc-render-refuse ( n , s ) else newline
let p = p :: n in newline
let N = lgc-host-newline ( s ) in newline
let a = lgc-render-add-lf ( s [[ !"parameters" ]] [[ !"script" ]] ) in newline
let s = lgc-progress ( !"Writing file:" :: p , 4 , s ) in newline
let E = fileMkdir ( p ) in newline
let s = lgc-push-event ( s , E ) in newline
let E = textWriteExec ( p , N , a :: c ) in newline
let s = lgc-push-event ( s , E ) in newline
lgc-exec-events ( s , lgc-render-user2 ( x , s ) ) end define ]]"
Add script headline "[[ a ]]" to contents "[[ c ]]". Then write contents "[[ c ]]" with newline translation to executable file named "[[ n ]]" relative to path "[[ p ]]". This is supposed to work under Unix but, in the future, "[[ lgc-render-lgwam ( p , n , c , s ) ]]" is supposed to be enhanced to work under other operating systems as well. The intension is that an lgwam rendering event should work regardless of operating system.
\item "[[ late define lgc-render-invoke ( e , p , n , s ) as newline
if lgc-render-path ( n ) != true then lgc-render-refuse ( n , s ) else newline
let s = s [[ !"invoked" -> << p ,, e ,, n >> ]] in newline
let s = lgc-progress ( !"Invoking " :: e :: SP :: n , 4 , s ) in newline
let E = execlp1 ( p , e , n ) in newline
let s = lgc-push-event ( s , E ) in newline
lgc-exec-events ( s , lgc-render-user2 ( x , s ) ) end define ]]"
Write contents "[[ c ]]" with newline translation to executable file named "[[ n ]]" relative to path "[[ p ]]".
\end{statements}
\subsection{Safety check filename}
At present, we put restrictions on file names that occur in rendering events. Among other, we do not allow file names like "[[ !"../../foo" ]]". These restrictions prevent users from corrupting their file structure accidentally, but they do not provide any protection against malicious code.
Protection against malicious code requires one to establish a chroot or schroot jail or to abandon use of latex, bibtex, makeindex, and dvipdfm. The latter approach is best, but requires those four programs to be ported to the Logiweb programming language and included in the present compiler.
\begin{statements}
\item "[[ late define lgc-render-path ( n ) as newline
let n = vt2vector* ( n ) in newline
if n then !"Empty filename" else newline
if n head = !"/" then !"Relative filename starts with slash" else newline
if n = << !"." >> then !"Filename must be more than a dot" else newline
lgc-render-path-slash ( n ) end define ]]"
Return "[[ true ]]" if "[[ n ]]" is a proper path. Else return error message.
\item "[[ late define lgc-render-path-slash ( n ) as newline
if n atom then "Filename ends with a slash" else newline
if n = << !"." >> then "Filename ends with slash-dot" else newline
if n head = !"/" then "Filename contains two slashes in a row" else newline
lgc-render-path1 ( n ) end define ]]"
Return "[[ true ]]" if a slash followed by "[[ n ]]" is a proper path. Else return error message.
\item "[[ late define lgc-render-path1 ( n ) as newline
if n atom then true else newline
let c :: n = n in newline
if !"a" <= c .and. c <= !"z" then lgc-render-path1 ( n ) else newline
if !"A" <= c .and. c <= !"Z" then lgc-render-path1 ( n ) else newline
if !"0" <= c .and. c <= !"9" then lgc-render-path1 ( n ) else newline
if c = !"-" .or. c = !"_" then lgc-render-path1 ( n ) else newline
if c = !"." then lgc-render-path-dot ( n ) else newline
if c = !"/" then lgc-render-path-slash ( n ) else newline
!"Filename must be constructed from letters (a to z and A to Z)" :: newline
LF :: !"digits (0 to 9), hyphen (-), underscore (_), dot (.), and slash (/)" end define ]]"
Return "[[ true ]]" if non-slash followed by "[[ n ]]" is a proper path. Else return error message.
\item "[[ late define lgc-render-path-dot ( n ) as newline
if n head = !"." then !"Filename contains two dots in a row" else newline
lgc-render-path1 ( n ) end define ]]"
Return "[[ true ]]" if a dot followed by "[[ n ]]" is a proper path. Else return error message.
\end{statements}
\subsection{Invokation of user rendering}\label{sec:InvokationOfUserRendering}
The "[[ lgc-render-expand ( r , s ) ]]" function renders the page with reference "[[ r ]]" for state "[[ s ]]" and returns a list of rendering output events.
\begin{statements}
\item "[[ late define lgc-render-expand ( r , s ) as newline
let c = s [[ !"cluster" ]] [[ r ]] in newline
let V = true [[ !"cache" -> c ]] in newline
let V = V [[ !"parameters" -> s [[ !"parameters" ]] ]] in newline
let V = V [[ !"leap" -> s [[ !"leap" ]] ]] in newline
let d = c [[ r ]] [[ !"codex" ]] [[ r ]] [[ 0 ]] [[ 0 ]] [[ !"render" ]] in newline
if d pairp then lgc-render-expand1 ( d , V , c ) else newline
let b = c [[ r ]] [[ !"bibliography" ]] first in newline
if b then lgc-render-default ( r , V , c ) else newline
let d = c [[ b ]] [[ !"codex" ]] [[ b ]] [[ 0 ]] [[ 0 ]] [[ !"render" ]] in newline
if d pairp then lgc-render-expand1 ( d , V , c ) else newline
lgc-render-default ( r , V , c ) end define ]]"
Macro expand page "[[ r ]]" in cache "[[ c ]]". First look up the macro expander of the page itself and call it "[[ d ]]". If found, apply it. Else look up the macro expander of the bed page "[[ b ]]" and call it "[[ d ]]". If found, apply it. Else, default to the identity macro expander and return the body of the page.
\item "[[ late define lgc-render-expand1 ( d , V , c ) as newline
let f = eval ( d third , true , c ) in newline
( f apply V maptag ) untag end define ]]"
Evaluate right hand side of the rendering definition "[[ d ]]". Then apply the result to the cache "[[ c ]]".
\item "[[ late define lgc-render-default ( r , V , c ) as newline
let R = lgc-render-exec ( r , V , c ) in newline
let R = R :: lgc-render-lgwinclude ( r , V , c ) in newline
let R = R :: lgc-render-diagnose ( r , V , c ) in newline
R :: lgc-render-body ( r , V , c ) end define ]]"
Default rendering of a cache "[[ c ]]" involves rendering of the body and rendering of executables.
\item "[[ late define lgc-render-body ( r , V , c ) as newline
let t = c [[ r ]] [[ "body" ]] in newline
stateexpand ( t , lgc-render-use :: true :: V , c ) head end define ]]"
Default rendering of a body is done by passing the body expressed as a term "[[ t ]]" and a rendering state "[[ f :: C :: V ]]" to the stateexpand function.
The real work is done by the function "[[ f ]]" which defines how to render the root of the body. "[[ f ]]" typically renders subterms of the root and then combine renderings of subterms into a rendering of the whole term.
The rendering cache "[[ C ]]" of accumulated information is set to "[[ true ]]". During rendering, information is accumulated in "[[ C ]]". The call to stateexpand results in a pair "[[ R :: C ]]" where "[[ R ]]" is the rendering and "[[ C ]]" is the final value of "[[ C ]]". The "[[ lgc-render-body ( r , V , c ) ]]" function discards this final "[[ C ]]" and returns the rendering "[[ R ]]".
The rendering state value "[[ V ]]" contains information that may by useful for the renderer. The rendering state value is passed down from calling to called renderer, so "[[ V ]]" can only be used for passing information from the root towards the leafs while rendering the body "[[ t ]]".
\item "[[ late define lgc-render-diagnose ( r , V , c ) as newline
let t = c [[ r ]] [[ "diagnose" ]] untag in newline
if t then lgc-render-diagnose1 ( !"No errors found" , r , V , c ) else newline
let R = stateexpand ( t , lgc-render-use :: true :: V , c ) head in newline
if t tail .or. .not. t tail tail then R else newline
let d = c [[ t ref ]] [[ !"codex" ]] [[ t ref ]] [[ t idx ]] [[ 0 ]] [[ !"use" ]] in newline
if d .or. .not. d second first t= d third then R else newline
lgc-render-diagnose1 ( R , r , V , c ) end define ]]"
\item "[[ late define lgc-render-diagnose1 ( m , r , V , c ) as newline
let N = lgc-render-pdftitle ( vt2vector* ( lgc-symbol2vt1 ( r , 0 , c ) ) ) in newline
let n = stateexpand ( << << r ,, 0 >> >> , lgc-render-show :: true :: V , c ) head in newline
let t = lgc-lgt2grdutc2vt ( lgc-ref2lgt ( r ) , V ) in newline
let R = newline
!"\documentclass[fleqn]{article}
\setlength{\overfullrule}{1mm}
\usepackage{latexsym}
\setlength{\parindent}{0em}
\setlength{\parskip}{1ex}
\usepackage{graphicx}
\usepackage[dvipdfm=true]{hyperref}
\hypersetup{pdfpagemode=UseNone}
\hypersetup{pdfstartpage=1}
\hypersetup{pdfstartview=FitBH}
\hypersetup{pdfpagescrop={120 80 490 730}}
\hypersetup{pdftitle=Logiweb diagnose of " :: N :: !"}
\hypersetup{colorlinks=true}
\everymath{\rm}
\begin{document}\raggedright
\newlength{\lgwlinewidth}
\setlength{\lgwlinewidth}{\linewidth}
\begin{list}{}{\setlength{\leftmargin}{0mm}
\setlength{\rightmargin}{20mm}
\setlength{\topsep}{0mm}
\setlength{\partopsep}{0mm}
\setlength{\itemsep}{0mm}
\setlength{\parsep}{0mm}
}\item \makebox[0mm][l]{\makebox[\lgwlinewidth][r]{\includegraphics [0mm,18.5mm][16.5mm,19mm]{logiweb.eps}}}%
{\bf{\Large Logiweb diagnose of $ " :: n :: !" $}}
\end {list}\vspace{12.5mm}
" :: m :: !"
\vspace{2ex}
{\em\href{http://logiweb.eu/index.html}{The Logiweb compiler (lgc)},
\href {http://logiweb.eu/logiweb/doc/misc/time.html}{" :: t :: !"}}
\end{document}
" in newline
let R = !"text" :: !"diagnose.tex" :: R in newline
let R = << R ,, !"latex" ,, !"diagnose" >> in newline
<< R ,, !"dvipdfm" ,, !"diagnose" >> end define ]]"
\item "[[ late define lgc-render-pdftitle ( a ) as newline
if a atom then true else newline
let c :: a = a in newline
let a = lgc-render-pdftitle ( a ) in newline
if lgc-render-pdftitle1 ( c ) then c :: a else newline
!"(" :: lgc-string2mixed ( c ) :: !")" :: a end define ]]"
Convert the singleton list "[[ a ]]" into a vt which is safe to use as a pdftitle.
\item "[[ late define lgc-render-pdftitle1 ( c ) as newline
!"a" <= c .and. c <= !"z" .or. newline
!"A" <= c .and. c <= !"Z" .or. newline
!"0" <= c .and. c <= !"9" .or. newline
c = !" " .or. c = !":" .or. c = !"-" end define ]]"
Return "[[ true ]]" if "[[ c ]]" is a singleton which is safe to include in a pdftitle. This function is over-conservative.
\end{statements}
\subsection{Default rendering of executables}
\begin{statements}
\item "[[ late define lgc-render-exec ( r , V , c ) as newline
lgc-render-exec1 ( r , V , c [[ r ]] [[ !"codex" ]] [[ 0 ]] , true ) end define ]]"
Render all executables defined by page "[[ r ]]".
\item "[[ late define lgc-render-exec1 ( r , V , c , R ) as newline
if c atom then R else newline
if c head intp then lgc-render-exec2 ( r , V , c head , c tail , R ) else newline
lgc-render-exec1 ( r , V , c head , lgc-render-exec1 ( r , V , c tail , R ) ) end define ]]"
Render all executables defined by page "[[ r ]]" in subcodex "[[ c ]]" and accumulate the result in "[[ R ]]".
\item "[[ late define lgc-render-exec2 ( r , V , i , c , R ) as newline
let t = c [[ 0 ]] [[ !"execute" ]] third second in newline
if t then R else newline
let v = eval ( t , true , V [[ !"cache" ]] ) untag in newline
let v = lgc-render-exec-arg ( v , V ) in newline
let a = !"string" :: LF :: lgc-string2mixed ( r ) in newline
let a = a :: LF :: i :: LF :: !"execute" :: v in newline
let p = vt2vector ( "bin/" :: i ) in newline
<< !"lgwam" ,, p ,, a >> :: R end define ]]"
Render executable if defined.
\item "[[ late define lgc-render-exec-arg ( v , V ) as newline
if v atom then LF else newline
let a :: v = v in newline
let w = lgc-render-exec-arg ( v , V ) in newline
if a intp then LF :: a :: w else newline
if .not. a mapp then w else newline
let a = ( a apply V maptag ) untag in newline
if a intp then LF :: a :: w else w end define ]]"
Insert newlines in front of each string in the list "[[ v ]]" of strings. Apply maptagged elements to "[[ V ]]" first.
\end{statements}
\subsection{Rendering of lgwinclude file}
Default rendering includes generation of a file named lgwinclude.tex. That file contains \TeX\ macros which can be useful to include. Using lgwinclude.tex is not a must, however. One can just as well use advanced rendering functions e.g.\ for setting \verb+\today+ to the timestamp of the page.
\begin{statements}
\item "[[ late define lgc-gdef ( x , y ) as << !"\gdef\" ,, x ,, !"{" ,, y ,, !"}" :: LF >> end define ]]"
Construct a \TeX\ global definition.
\item "[[ late define lgc-only-letters ( x ) as newline
if x pairp then lgc-only-letters ( x head ) :: lgc-only-letters ( x tail ) else newline
if .not. x intp then true else newline
if !"A" <= x .and. x <= !"Z" .or. !"a" <= x .and. x <= !"z" then x else true end define ]]"
Remove all characters which are not Latin letters from the vector tree "[[ x ]]".
\item "[[ late define lgc-gdef-ref ( r , c ) as newline
let n = lgc-symbol2vt1 ( r , 0 , c ) in newline
let n = vt2vector* ( n ) in newline
let n = lgc-only-letters ( n ) in newline
let X = lgc-string2mixed ( r ) in newline
let R = lgc-gdef ( !"lgwBlock" :: n , X ) in newline
R :: lgc-gdef ( !"lgwBreak" :: n , lgc-break ( X ) ) end define ]]"
Convert the reference "[[ r ]]" (given as a vector) into two definitions useful for referencing the page with reference "[[ r ]]". "[[ c ]]" must be the cache of the referencing page.
\item "[[ late define lgc-gdef-bib ( b , c ) as newline
if b atom then true else newline
lgc-gdef-ref ( b head , c ) :: lgc-gdef-bib ( b tail , c ) end define ]]"
Convert the bibliography "[[ b ]]" into two \TeX\ definitions for each reference. "[[ c ]]" must be the cache of the referencing page.
\item "[[ late define lgc-break ( v ) as lgc-break1 ( vt2vector* ( v ) ) end define ]]"
\item "[[ late define lgc-break1 ( v ) as newline
if v tail atom then v else newline
v head :: !"\lgwbreak" :: LF :: lgc-break1 ( v tail ) end define ]]"
\item "[[ late define lgc-render-lgwinclude ( r , V , c ) as newline
let t = lgc-ref2lgt ( r ) in newline
let R = lgc-gdef ( !"today" , lgc-lgt2grdutc2vt ( t , V ) ) in newline
let R = R :: lgc-gdef ( !"lgwlgt" , lgc-lgt2vt ( t ) ) in newline
let R = R :: lgc-gdef ( !"lgwmjdtai" , lgc-lgt2mjdtai2vt ( t ) ) in newline
let R = R :: lgc-gdef ( !"lgwgrdutc" , lgc-lgt2grdutc2vt ( t , V ) ) in newline
let << f ,, e >> = t in newline
let R = R :: lgc-gdef ( !"lgwmantissa" , lgc-itoa ( f ) ) in newline
let R = R :: lgc-gdef ( !"lgwexponent" , lgc-itoa ( e ) ) in newline
let << Y ,, M ,, D ,, h ,, m ,, s ,, f ,, e >> = lgc-lgt2grdutc ( t , V ) in newline
let R = R :: lgc-gdef ( !"lgwfraction" , lgc-ctoa ( f , e ) ) in newline
let R = R :: lgc-gdef ( !"lgwsecond" , lgc-ctoa ( s , 2 ) ) in newline
let R = R :: lgc-gdef ( !"lgwminute" , lgc-ctoa ( m , 2 ) ) in newline
let R = R :: lgc-gdef ( !"lgwhour" , lgc-ctoa ( h , 2 ) ) in newline
let R = R :: lgc-gdef ( !"lgwday" , lgc-ctoa ( D , 2 ) ) in newline
let R = R :: lgc-gdef ( !"lgwmonth" , lgc-ctoa ( M , 2 ) ) in newline
let R = R :: lgc-gdef ( !"lgwyear" , lgc-itoa ( Y ) ) in newline
let R = R :: lgc-gdef ( !"lgwbreak" , !"\linebreak[0]\hskip0em plus0.5em{}" ) in newline
let R = R :: lgc-gdef ( !"lgwhyphen" , !"-" ) in newline
let R = R :: lgc-gdef ( !"lgwunderscore" , !"\_" ) in newline
let X = default ( !"../../../logiweb/" , V [[ !"parameters" ]] [[ !"relay" ]] ) in newline
let R = R :: lgc-gdef ( !"lgwBlockRelay" , X ) in newline
let R = R :: lgc-gdef ( !"lgwBreakRelay" , lgc-break ( X ) ) in newline
let X = lgc-string2mixed ( r ) in newline
let R = R :: lgc-gdef ( !"lgwBlockThis" , X ) in newline
let R = R :: lgc-gdef ( !"lgwBreakThis" , lgc-break ( X ) ) in newline
let R = R :: lgc-gdef-bib ( c [[ r ]] [[ !"bibliography" ]] , c ) in newline
"text" :: "lgwinclude.tex" :: R end define ]]"
\end{statements}
\subsection{Rendering compiler}
The rendering compiler translates a right hand side "[[ T ]]" and a parameter list "[[ p ]]" of a rendering definition into rendering code.
The constituents of "[[ T ]]" are represented thus: A string "[[ << << 0 ,, i >> >> ]]" is represented by itself. A parameter is represented by the index of the parameter in the parameter list "[[ p ]]". A construct which has no value definition is represented as "[[ true ]]" followed by the translations of subterms. A construct which does have a value definition is represented as the code of that value definition followed by the translations of subterms.
In the latter case above, the code is assumed to be a function which does not depend on its parameters. For that reason, the code is applied to "[[ true ]]" as many times as its arity indicates to get rid of the parameters. Once that is done, the code is assumed to be a map-tagged function of one parameter.
The parameter is supposed to be of form "[[ a :: s :: c :: T ]]" where "[[ s ]]" in turn is of form "[[ f :: C :: V ]]". "[[ a ]]" is the list of subtrees of the construct being rendered. T is the list of subtrees of the construct in the rendering definition which is being evaluated. "[[ c ]]" is the cache of the page being rendered. "[[ f ]]" is a function suggested for rendering of subtrees (i.e.\ of the elements of "[[ a ]]"). "[[ C ]]" is used for memorizing information which only needs to be computed once (like compiled versions of definitions). "[[ V ]]" is passed down from root to leaf in the term being rendered.
\begin{statements}
\item "[[ late define lgc-index ( T , p , r ) as newline
if p atom then true else newline
if T t= p head then r else newline
lgc-index ( T , p tail , r + 1 ) end define ]]"
Find the position of the term "[[ T ]]" in the parameter list "[[ p ]]" and return that position. Return "[[ true ]]" if "[[ T ]]" is not found.
\item "[[ late define lgc-const2val ( f , x ) as newline
if x atom then f untag else newline
lgc-const2val ( f apply true maptag , x tail ) end define ]]"
Convert the code "[[ f ]]" of a constant construct to the constant value of the construct. The length of the list "[[ x ]]" indicates the arity of the construct.
\item "[[ late define lgc-render-compile ( T , p , c ) as newline
let << << r ,, i >> >> = T in newline
if r = 0 then T else newline
let f = c [[ r ]] [[ !"code" ]] [[ i ]] in newline
if .not. f mapp then lgc-render-compile1 ( T , p , c ) else newline
let f = lgc-const2val ( f , T tail ) in newline
if .not. f mapp then lgc-render-compile1 ( T , p , c ) else newline
f :: lgc-render-compile* ( T tail , p , c ) end define ]]"
Convert the right hand side "[[ T ]]" and parameter list "[[ p ]]" into a compiled right hand side. In a compiled right hand side, symbols are replaced by their codes, parameters are replaced by their indices, and strings are kept as they are.
\item "[[ late define lgc-render-compile1 ( T , p , c ) as newline
let t = lgc-index ( T , p , 0 ) in newline
if t intp then t else newline
true :: lgc-render-compile* ( T tail , p , c ) end define ]]"
Convert the right hand side "[[ T ]]" and parameter list "[[ p ]]" into a compiled right hand side when "[[ T ]]" is known not to have a valid definition.
\item "[[ late define lgc-render-compile* ( T , p , c ) as newline
if T atom then true else newline
lgc-render-compile ( T head , p , c ) :: lgc-render-compile* ( T tail , p , c ) end define ]]"
Coordinatewise application of "[[ lgc-render-compile ( T , p , c ) ]]".
\end{statements}
\subsection{Rendering evaluator}
The rendering evaluator evaluates rendering code "[[ T ]]" for a rendering state "[[ s ]]" and cache "[[ c ]]". Furthermore, the evaluator takes a list "[[ a ]]" of subtrees of the construct being rendered.
\begin{statements}
\item "[[ late define lgc-render-eval ( T , a , s , c ) as newline
if T intp then stateexpand ( nth ( T , a ) , s , c ) else newline
let f :: T = T in newline
let true :: C :: V = s in newline
if f pairp then f first :: C else newline
if f mapp then ( f apply ( ( a :: s :: c :: T ) maptag ) ) untag else newline
if T then true :: C else newline
if T tail then lgc-render-eval ( T head , a , lgc-render-show :: C :: V , c ) else newline
lgc-render-eval* ( T , a , s , c ) end define ]]"
Apply the compiled right hand side "[[ T ]]" to the argument list "[[ a ]]", rendering state "[[ s ]]", and cache "[[ c ]]".
\item "[[ late define lgc-render-eval* ( T , a , s , c ) as newline
let f :: C :: V = s in newline
if T atom then true :: C else newline
let A :: C = lgc-render-eval ( T head , a , s , c ) in newline
let B :: C = lgc-render-eval* ( T tail , a , f :: C :: V , c ) in newline
( A :: B ) :: C end define ]]"
Coordinatewise application of "[[ lgc-render-eval ( T , a , s , c ) ]]".
If "[[ T ]]" is a string then the function returns the string.
Else, if the root of "[[ T ]]" has a value definition then we assume it to be a renderer. A renderer is supposed to be a function whose right hand side has form "[[ map ( \ u . *** ) ]]". Such a function ignores its arguments, so we use "[[ lgc-const2val ( f , x ) ]]" to convert the function to its constant value and then apply it to all available information.
Else, we look up "[[ T ]]" in the list "[[ b ]]" of bindings and render the subtree "[[ t ]]" if one is found.
Else, we concatenate the renderings of all subterms of "[[ T ]]".
\end{statements}
\subsection{Use rendering}
\begin{statements}
\item "[[ late define lgc-render-use as map ( \ x . lgc-render-use1 ( x ) ) end define ]]"
Rendering state for use rendering.
\item "[[ late define lgc-render-use1 ( x ) as newline
let << t ,, s ,, c >> = x in newline
let f :: C :: V = s in newline
if t then !"\mbox{\tt\char34}" :: C else newline
if t head then lgc-understood ( lgc-render-use1 ( << t first ,, s ,, c >> ) ) :: C else newline
let << << r ,, i >> >> = t in newline
if r = 0 then i :: C else newline
let T = C [[ !"use" ]] [[ r ]] [[ i ]] in newline
if .not. T then lgc-render-eval ( T , t tail , s , c ) else newline
let T :: C = lgc-render-compile-use ( r , i , C , c ) in newline
lgc-render-eval ( T , t tail , f :: C :: V , c ) end define ]]"
Produce the rendering of the tree "[[ t ]]" for the rendering state "[[ s ]]" and the cache "[[ c ]]". The function has to treat certain malformed trees in certain ways: A value of "[[ true ]]" has to be rendered as a double quote because the Logiweb compiler uses "[[ true ]]" as a placeholder when rendering partial trees. The Logiweb compiler uses that e.g.\ when rendering the Logiweb symbols in the dictionary of a page. Furthermore, a tree whose root is "[[ true ]]" should be enclosed in blue brackets because the Logiweb compiler represents an understood parenthesis as the root "[[ true ]]" with one subtree. The Logiweb compiler uses that e.g.\ when rendering the diagnose of a faulty page.
\item "[[ late define lgc-render-compile-use ( r , i , C , c ) as newline
let T :: C = lgc-render-compile-use1 ( r , i , C , c ) in newline
let C = C [[ << !"use" ,, r ,, i >> => T ]] in T :: C end define ]]"
Compile the use definition of the construct with reference "[[ r ]]" and index "[[ i ]]" into rendering code and memorize the result in "[[ C ]]".
\item "[[ late define lgc-render-compile-use1 ( r , i , C , c ) as newline
let d = c [[ r ]] [[ "codex" ]] [[ r ]] [[ i ]] [[ 0 ]] [[ !"use" ]] in newline
if d pairp then lgc-render-compile ( d third , d second tail , c ) :: C else newline
lgc-render-compile-show ( r , i , C , c ) end define ]]"
Compile the use definition of the construct with reference "[[ r ]]" and index "[[ i ]]" into rendering code.
\item "[[ late define lgc-understood ( t ) as newline
LF :: !"\ \linebreak[0]\textcolor{blue}{[}\linebreak[0]\ " :: t :: newline
LF :: !"\ \linebreak[0]\textcolor{blue}{]}\linebreak[0]\ " end define ]]"
Function for enclosing "[[ t ]]" in blue brackets.
\end{statements}
\subsection{Show rendering}
\begin{statements}
\item "[[ late define lgc-render-show as map ( \ x . lgc-render-show1 ( x ) ) end define ]]"
Rendering state for show rendering.
\item "[[ late define lgc-render-show1 ( x ) as newline
let << t ,, s ,, c >> = x in newline
let f :: C :: V = s in newline
if t then !"\mbox{\tt\char34}" :: C else newline
if t head then lgc-understood ( lgc-render-show1 ( << t first ,, s ,, c >> ) ) :: C else newline
let << << r ,, i >> >> = t in newline
if r = 0 then lgc-render-string ( i ) :: C else newline
let T = C [[ !"show" ]] [[ r ]] [[ i ]] in newline
if .not. T then lgc-render-eval ( T , t tail , s , c ) else newline
let T :: C = lgc-render-compile-show ( r , i , C , c ) in newline
lgc-render-eval ( T , t tail , f :: C :: V , c ) end define ]]"
Produce the rendering of the tree "[[ t ]]" for the rendering state "[[ s ]]" and the cache "[[ c ]]". The function has to treat certain malformed trees in certain ways: A value of "[[ true ]]" has to be rendered as a double quote because the Logiweb compiler uses "[[ true ]]" as a placeholder when rendering partial trees. The Logiweb compiler uses that e.g.\ when rendering the Logiweb symbols in the dictionary of a page. Furthermore, a tree whose root is "[[ true ]]" should be enclosed in blue brackets because the Logiweb compiler represents an understood parenthesis as the root "[[ true ]]" with one subtree. The Logiweb compiler uses that e.g.\ when rendering the diagnose of a faulty page.
\item "[[ late define lgc-render-compile-show ( r , i , C , c ) as newline
let T = lgc-render-compile-show1 ( r , i , c ) in newline
let C = C [[ << !"show" ,, r ,, i >> => T ]] in T :: C end define ]]"
\item "[[ late define lgc-render-compile-show1 ( r , i , c ) as newline
let d = c [[ r ]] [[ "codex" ]] [[ r ]] [[ i ]] [[ 0 ]] [[ !"show" ]] in newline
if d pairp then lgc-render-compile ( d third , d second tail , c ) else newline
lgc-render-name ( r , i , c ) end define ]]"
Compile the show definition of the construct with reference "[[ r ]]" and index "[[ i ]]" into rendering code.
\end{statements}
\subsection{Rendering based on name}
\begin{statements}
\item "[[ late define lgc-render-name ( r , i , c ) as newline
let n = lgc-symbol2vt1 ( r , i , c ) in newline
let n = vt2vector* ( n ) in newline
let n = lgc-render-name1 ( n , true ) in newline
true :: << << 0 ,, n head >> >> :: lgc-render-name2 ( n tail , 0 ) end define ]]"
Function for compiling constructs that do not have a tex aspect.
\item "[[ late define lgc-render-name1 ( n , r ) as newline
if n atom then << lgc-render-string0 ( reverse ( r ) ) >> else newline
let c :: n = n in newline
if c != QQ then lgc-render-name1 ( n , c :: r ) else newline
lgc-render-string0 ( reverse ( r ) ) :: lgc-render-name1 ( n , true ) end define ]]"
Convert the name "[[ n ]]" into a list of inter-quote strings.
\item "[[ late define lgc-render-name2 ( n , i ) as newline
if n atom then true else newline
i :: << << 0 ,, n head >> >> :: lgc-render-name2 ( n tail , i + 1 ) end define ]]"
Merge the list "[[ n ]]" of inter-quote strings with parameter indexes.
\end{statements}
\subsection{Show rendering of strings}
\begin{statements}
\item "[[ late define lgc-render-string ( i ) as newline
let i = vt2vector* ( i ) in newline
let i = lgc-render-string0 ( i ) in newline
!"\mbox{`}" :: i :: !"\mbox{'}" end define ]]"
Render the string "[[ i ]]".
\item "[[ late define lgc-render-string0 ( i ) as newline
lgc-render-string1 ( i , lgc-render-array , true ) end define ]]"
\item "[[ late define lgc-render-string1 ( i , a , b ) as newline
if i atom then lgc-render-string2 ( b , true ) else newline
let c :: i = i in newline
let d = a [[ c ]] in newline
if d then lgc-render-string2 ( c :: b , lgc-render-string0 ( i ) ) else newline
if d intp then d :: lgc-render-string0 ( i ) else newline
lgc-render-string1 ( i , d , c :: b ) end define ]]"
Look up the byte sequence at the beginning of "[[ i ]]" in the trie "[[ a ]]" and, simultaneously, accumulate the looked up bytes in "[[ b ]]". If the array provides a translation (i.e.\ if "[[ a [[ c ]] ]]" is a string) then include that translation in the result and discard "[[ b ]]". If the array provides no translation (i.e.\ if "[[ a [[ c ]] = true ]]") then give up and render "[[ b ]]" as a sequence of bytes. Otherwise, look at one more byte.
\item "[[ late define lgc-render-string2 ( b , i ) as newline
if b atom then i else newline
let c :: b = b in newline
let m :: n = floor ( c - NULL , 16 ) in newline
let m = lgc-hexdigit ( m ) in newline
let n = lgc-hexdigit ( n ) in newline
lgc-render-string2 ( b , !"(" :: m :: n :: !")" :: i ) end define ]]"
Render "[[ b ]]" as a sequence of bytes and revappend them to "[[ i ]]".
\item "[[ late define lgc-render-array as newline
true tight endline
[[ LF -> vt2vector ( LF :: !"\newline " ) ]] tight endline
[[ !" " -> vt2vector ( !"\linebreak [0]\ " ) ]] tight endline
[[ !"!" -> !"!" ]] tight endline
[[ !""-""!" -> !"\mbox{\tt\char34}" ]] tight endline
[[ !"#" -> !"\#" ]] tight endline
[[ !"$" -> !"\$" ]] tight endline
[[ !"%" -> !"\%" ]] tight endline
[[ !"&" -> !"\&" ]] tight endline
[[ !"'" -> !"\mbox{'}" ]] tight endline
[[ !"(" -> !"(" ]] tight endline
[[ !")" -> !")" ]] tight endline
[[ !"*" -> !"{*}" ]] tight endline
[[ !"+" -> !"{+}" ]] tight endline
[[ !"," -> !"," ]] tight endline
[[ !"-" -> !"\mbox{-}" ]] tight endline
[[ !"." -> !"." ]] tight endline
[[ !"/" -> !"/" ]] tight endline
[[ !"0" -> !"0" ]] tight endline
[[ !"1" -> !"1" ]] tight endline
[[ !"2" -> !"2" ]] tight endline
[[ !"3" -> !"3" ]] tight endline
[[ !"4" -> !"4" ]] tight endline
[[ !"5" -> !"5" ]] tight endline
[[ !"6" -> !"6" ]] tight endline
[[ !"7" -> !"7" ]] tight endline
[[ !"8" -> !"8" ]] tight endline
[[ !"9" -> !"9" ]] tight endline
[[ !":" -> !"{:}" ]] tight endline
[[ !";" -> !";" ]] tight endline
[[ !"<" -> !"<" ]] tight endline
[[ !"=" -> !"{=}" ]] tight endline
[[ !">" -> !">" ]] tight endline
[[ !"?" -> !"?" ]] tight endline
[[ !"@" -> !"@" ]] tight endline
[[ !"A" -> !"A" ]] tight endline
[[ !"B" -> !"B" ]] tight endline
[[ !"C" -> !"C" ]] tight endline
[[ !"D" -> !"D" ]] tight endline
[[ !"E" -> !"E" ]] tight endline
[[ !"F" -> !"F" ]] tight endline
[[ !"G" -> !"G" ]] tight endline
[[ !"H" -> !"H" ]] tight endline
[[ !"I" -> !"I" ]] tight endline
[[ !"J" -> !"J" ]] tight endline
[[ !"K" -> !"K" ]] tight endline
[[ !"L" -> !"L" ]] tight endline
[[ !"M" -> !"M" ]] tight endline
[[ !"N" -> !"N" ]] tight endline
[[ !"O" -> !"O" ]] tight endline
[[ !"P" -> !"P" ]] tight endline
[[ !"Q" -> !"Q" ]] tight endline
[[ !"R" -> !"R" ]] tight endline
[[ !"S" -> !"S" ]] tight endline
[[ !"T" -> !"T" ]] tight endline
[[ !"U" -> !"U" ]] tight endline
[[ !"V" -> !"V" ]] tight endline
[[ !"W" -> !"W" ]] tight endline
[[ !"X" -> !"X" ]] tight endline
[[ !"Y" -> !"Y" ]] tight endline
[[ !"Z" -> !"Z" ]] tight endline
[[ !"[" -> !"[" ]] tight endline
[[ !"\" -> !"\mbox{$\backslash$}" ]] tight endline
[[ !"]" -> !"]" ]] tight endline
[[ !"^" -> !"{\char94}" ]] tight endline
[[ !"_" -> !"\_" ]] tight endline
[[ !"`" -> !"\mbox{`}" ]] tight endline
[[ !"a" -> !"a" ]] tight endline
[[ !"b" -> !"b" ]] tight endline
[[ !"c" -> !"c" ]] tight endline
[[ !"d" -> !"d" ]] tight endline
[[ !"e" -> !"e" ]] tight endline
[[ !"f" -> !"f" ]] tight endline
[[ !"g" -> !"g" ]] tight endline
[[ !"h" -> !"h" ]] tight endline
[[ !"i" -> !"i" ]] tight endline
[[ !"j" -> !"j" ]] tight endline
[[ !"k" -> !"k" ]] tight endline
[[ !"l" -> !"l" ]] tight endline
[[ !"m" -> !"m" ]] tight endline
[[ !"n" -> !"n" ]] tight endline
[[ !"o" -> !"o" ]] tight endline
[[ !"p" -> !"p" ]] tight endline
[[ !"q" -> !"q" ]] tight endline
[[ !"r" -> !"r" ]] tight endline
[[ !"s" -> !"s" ]] tight endline
[[ !"t" -> !"t" ]] tight endline
[[ !"u" -> !"u" ]] tight endline
[[ !"v" -> !"v" ]] tight endline
[[ !"w" -> !"w" ]] tight endline
[[ !"x" -> !"x" ]] tight endline
[[ !"y" -> !"y" ]] tight endline
[[ !"z" -> !"z" ]] tight endline
[[ !"{" -> !"\{" ]] tight endline
[[ !"|" -> !"|" ]] tight endline
[[ !"}" -> !"\}" ]] tight endline
[[ !"~" -> !"{\char126}" ]] tight endline
[[ << NULL + 195 ,, NULL + 198 >> => !"\mbox{\AE}" ]] tight endline
[[ << NULL + 195 ,, NULL + 216 >> => !"\mbox{\O}" ]] tight endline
[[ << NULL + 195 ,, NULL + 197 >> => !"\mbox{\AA}" ]] tight endline
[[ << NULL + 195 ,, NULL + 230 >> => !"\mbox{\ae}" ]] tight endline
[[ << NULL + 195 ,, NULL + 248 >> => !"\mbox{\o}" ]] tight endline
[[ << NULL + 195 ,, NULL + 229 >> => !"\mbox{\aa}" ]] tight endline
end define ]]"
Typesetting of ASCII and Danish characters. The Danish characters are included to show how multibyte characters are treated. Once the present source is expressed in UTF-8 one can replace e.g.\ "[[ << NULL + 195 ,, NULL + 198 >> ]]" with "[[ vt2vector* ( !"x" ) ]]" where x should be replaced by \AE.
\end{statements}
" ]"\section{Test cases}
\subsection{Lexical Analysis}
\begin{statements}
\item "[[ late define lgc-test ( f ) as newline
let f = vt2vector* ( f ) in newline
let s = true [[ !"source" -> f ]] in newline
let s = s [[ !"verbose" -> 3 ]] in newline
let e :: S = lgc-lex-source ( f , s ) catch in newline
let s = if e then s [[ !"msg" -> S ]] else S in newline
s [[ !"body" -> reverse ( s [[ !"body" ]] ) ]] end define ]]"
The function above invokes lexical analysis on the source text "[[ f ]]".
At some time in the past, "[[ s [[ !"body" ]] ]]" was reversed so that it appears in natural order. But the test cases below still compare "[[ s [[ !"body" ]] ]]" to reversed lists, so the function above reverses "[[ s [[ !"body" ]] ]]" to make it work with the old test cases below.
\item "[[ etst lgc-test ( !"abc" ) [[ !"body" ]] ; << << !"c" ,, 2 >> ,, << !"b" ,, 1 >> ,, << !"a" ,, 0 >> >> end test ]]"
\item "[[ etst lgc-test ( << !"a" ,, SP ,, !"b" >> ) [[ !"body" ]] ; << << !"b" ,, 2 >> ,, << SP ,, 1 >> ,, << !"a" ,, 0 >> >> end test ]]"
\item "[[ etst lgc-test ( << !"a" ,, SP ,, SP ,, !"b" >> ) [[ !"body" ]] ; << << !"b" ,, 3 >> ,, << SP ,, 1 >> ,, << !"a" ,, 0 >> >> end test ]]"
\item "[[ etst lgc-test ( << SP ,, SP ,, !"a" ,, SP ,, SP ,, !"b" >> ) [[ !"body" ]] ; << << !"b" ,, 5 >> ,, << SP ,, 3 >> ,, << !"a" ,, 2 >> >> end test ]]"
\item "[[ etst lgc-test ( << !"a" ,, TAB ,, !"b" >> ) [[ !"body" ]] ; << << !"b" ,, 2 >> ,, << SP ,, 1 >> ,, << !"a" ,, 0 >> >> end test ]]"
\item "[[ etst lgc-test ( << !"a" ,, FF ,, !"b" >> ) [[ !"body" ]] ; << << !"b" ,, 2 >> ,, << SP ,, 1 >> ,, << !"a" ,, 0 >> >> end test ]]"
\item "[[ etst lgc-test ( << !"a" ,, CR ,, LF ,, !"b" >> ) [[ !"body" ]] ; << << !"b" ,, 3 >> ,, << SP ,, 1 >> ,, << !"a" ,, 0 >> >> end test ]]"
\item "[[ etst lgc-test ( << !"a" ,, LF ,, CR ,, !"b" >> ) [[ !"body" ]] ; << << !"b" ,, 3 >> ,, << SP ,, 1 >> ,, << !"a" ,, 0 >> >> end test ]]"
\item "[[ etst lgc-test ( << !"a" ,, CR ,, LF ,, CR ,, LF ,, !"b" >> ) [[ !"body" ]] ; << << !"b" ,, 5 >> ,, << SP ,, 1 >> ,, << !"a" ,, 0 >> >> end test ]]"
\item "[[ etst lgc-test ( << !"a" ,, CR ,, LF ,, LF ,, CR ,, !"b" >> ) [[ !"body" ]] ; << << !"b" ,, 5 >> ,, << SP ,, 1 >> ,, << !"a" ,, 0 >> >> end test ]]"
\item "[[ etst lgc-test ( << !"a" ,, LF ,, CR ,, CR ,, LF ,, !"b" >> ) [[ !"body" ]] ; << << !"b" ,, 5 >> ,, << SP ,, 1 >> ,, << !"a" ,, 0 >> >> end test ]]"
\item "[[ etst lgc-test ( << !"a" ,, LF ,, CR ,, LF ,, CR ,, !"b" >> ) [[ !"body" ]] ; << << !"b" ,, 5 >> ,, << SP ,, 1 >> ,, << !"a" ,, 0 >> >> end test ]]"
\item "[[ etst lgc-test ( << !"a" ,, LF ,, FF ,, CR ,, !"b" >> ) [[ !"body" ]] ; << << !"b" ,, 4 >> ,, << SP ,, 1 >> ,, << !"a" ,, 0 >> >> end test ]]"
\item "[[ etst lgc-test ( ""-""!""!C""!""!N""!""!S" ) [[ !"body" ]] ; << << lgc-esc-S ,, 6 >> ,, << lgc-esc-N ,, 3 >> ,, << lgc-esc-C ,, 0 >> >> end test ]]"
\item "[[ etst lgc-test ( << !"a""!""!;b" ,, LF ,, !"c" >> ) [[ !"body" ]] ; << << !"c" ,, 6 >> ,, << !"a" ,, 0 >> >> end test ]]"
\item "[[ etst lgc-test ( !"a""!""!{b""!""!}c" ) [[ !"body" ]] ; << << !"c" ,, 8 >> ,, << !"a" ,, 0 >> >> end test ]]"
\item "[[ etst lgc-test ( !"a""!""!{b""!""!""!}c""!""!}d" ) [[ !"body" ]] ; << << !"d" ,, 13 >> ,, << !"a" ,, 0 >> >> end test ]]"
\item "[[ etst lgc-test ( !"a""!bc""!d" ) [[ !"body" ]] ; << << !"d" ,, 5 >> ,, lgc-esc-- :: 1 :: !"bc" ,, << !"a" ,, 0 >> >> end test ]]"
\item "[[ etst lgc-test ( !"a""!""!-bc""!d" ) [[ !"body" ]] ; << << !"d" ,, 7 >> ,, lgc-esc-- :: 1 :: !"bc" ,, << !"a" ,, 0 >> >> end test ]]"
\item "[[ etst lgc-test ( !"a""!""!$bc""!d" ) [[ !"body" ]] ; << << !"d" ,, 7 >> ,, lgc-esc-$ :: 1 :: !"bc" ,, << !"a" ,, 0 >> >> end test ]]"
\item "[[ etst lgc-test ( !"a""!""!#bc""!d" ) [[ !"body" ]] ; << << !"d" ,, 7 >> ,, lgc-esc-# :: 1 :: !"bc" ,, << !"a" ,, 0 >> >> end test ]]"
\item "[[ etst lgc-test ( !"a""!""!#bc""!""!.d" ) [[ !"body" ]] ; << << !"d" ,, 9 >> ,, lgc-esc-# :: 1 :: !"bc" ,, << !"a" ,, 0 >> >> end test ]]"
\item "[[ etst lgc-test ( !"a""!b""!""!nc""!d" ) [[ !"body" ]] ; << << !"d" ,, 8 >> ,, lgc-esc-- :: 1 :: vt2vector ( !"b" :: LF :: !"c" ) ,, << !"a" ,, 0 >> >> end test ]]"
\item "[[ etst lgc-test ( !"a""!b""!""!!c""!d" ) [[ !"body" ]] ; << << !"d" ,, 8 >> ,, lgc-esc-- :: 1 :: vt2vector ( !"b" :: QQ :: !"c" ) ,, << !"a" ,, 0 >> >> end test ]]"
\item "[[ etst lgc-test ( !"a""!b""!""!-c""!d" ) [[ !"body" ]] ; << << !"d" ,, 8 >> ,, lgc-esc-- :: 1 :: vt2vector ( !"b" :: !"c" ) ,, << !"a" ,, 0 >> >> end test ]]"
\item "[[ etst lgc-test ( !"a""!b""!""!rc""!d" ) [[ !"body" ]] ; << << !"d" ,, 8 >> ,, lgc-esc-- :: 1 :: vt2vector ( !"b" :: CR :: !"c" ) ,, << !"a" ,, 0 >> >> end test ]]"
\item "[[ etst lgc-test ( !"a""!b""!""!fc""!d" ) [[ !"body" ]] ; << << !"d" ,, 8 >> ,, lgc-esc-- :: 1 :: vt2vector ( !"b" :: FF :: !"c" ) ,, << !"a" ,, 0 >> >> end test ]]"
\item "[[ etst lgc-test ( !"a""!b""!""!tc""!d" ) [[ !"body" ]] ; << << !"d" ,, 8 >> ,, lgc-esc-- :: 1 :: vt2vector ( !"b" :: TAB :: !"c" ) ,, << !"a" ,, 0 >> >> end test ]]"
\item "[[ etst lgc-test ( !"a""!b""!""!x41.c""!d" ) [[ !"body" ]] ; << << !"d" ,, 11 >> ,, lgc-esc-- :: 1 :: !"bAc" ,, << !"a" ,, 0 >> >> end test ]]"
\item "[[ etst lgc-test ( !"a""!b""!""!x4142.c""!d" ) [[ !"body" ]] ; << << !"d" ,, 13 >> ,, lgc-esc-- :: 1 :: !"bABc" ,, << !"a" ,, 0 >> >> end test ]]"
\item "[[ etst lgc-test ( !"a""!""!-""!""!x4142.""!d" ) [[ !"body" ]] ; << << !"d" ,, 13 >> ,, lgc-esc-- :: 1 :: !"AB" ,, << !"a" ,, 0 >> >> end test ]]"
\item "[[ etst lgc-test ( << QQ ,, SP ,, QQ >> ) [[ !"body" ]] ; << lgc-esc-- :: 0 :: SP >> end test ]]"
\item "[[ etst lgc-test ( << QQ ,, SP ,, SP ,, QQ >> ) [[ !"body" ]] ; << lgc-esc-- :: 0 :: !" " >> end test ]]"
\item "[[ etst lgc-test ( << QQ ,, TAB ,, QQ >> ) [[ !"body" ]] ; << lgc-esc-- :: 0 :: SP >> end test ]]"
\item "[[ etst lgc-test ( << QQ ,, FF ,, QQ >> ) [[ !"body" ]] ; << lgc-esc-- :: 0 :: vt2vector ( LF ) >> end test ]]"
\item "[[ etst lgc-test ( << QQ ,, CR ,, LF ,, QQ >> ) [[ !"body" ]] ; << lgc-esc-- :: 0 :: vt2vector ( LF ) >> end test ]]"
\item "[[ etst lgc-test ( << QQ ,, LF ,, CR ,, QQ >> ) [[ !"body" ]] ; << lgc-esc-- :: 0 :: vt2vector ( LF ) >> end test ]]"
\item "[[ etst lgc-test ( << QQ ,, CR ,, LF ,, CR ,, LF ,, QQ >> ) [[ !"body" ]] ; << lgc-esc-- :: 0 :: vt2vector ( LF :: LF ) >> end test ]]"
\item "[[ etst lgc-test ( << QQ ,, CR ,, LF ,, LF ,, CR ,, QQ >> ) [[ !"body" ]] ; << lgc-esc-- :: 0 :: vt2vector ( LF :: LF ) >> end test ]]"
\item "[[ etst lgc-test ( << QQ ,, LF ,, CR ,, CR ,, LF ,, QQ >> ) [[ !"body" ]] ; << lgc-esc-- :: 0 :: vt2vector ( LF :: LF ) >> end test ]]"
\item "[[ etst lgc-test ( << QQ ,, LF ,, CR ,, LF ,, CR ,, QQ >> ) [[ !"body" ]] ; << lgc-esc-- :: 0 :: vt2vector ( LF :: LF ) >> end test ]]"
\item "[[ etst lgc-test ( << QQ ,, LF ,, FF ,, CR ,, QQ >> ) [[ !"body" ]] ; << lgc-esc-- :: 0 :: vt2vector ( LF :: LF :: LF ) >> end test ]]"
\item "[[ etst lgc-test ( !"a""!""!.b" ) [[ !"body" ]] ; << << !"b" ,, 4 >> ,, lgc-esc-- :: 1 :: !"". ,, << !"a" ,, 0 >> >> end test ]]"
\item "[[ etst lgc-test ( !"ab""!""!Pcd""nef" ) [[ !"body" ]] ; << << !"f" ,, 9 >> ,, << !"e" ,, 8 >> ,, << !"b" ,, 1 >> ,, << !"a" ,, 0 >> >> end test ]]"
\item "[[ etst lgc-test ( !"ab""!""!Pcd""nef" ) [[ !"page" ]] ; << << !"c" ,, !"d" >> >> end test ]]"
\item "[[ etst lgc-test ( !"ab""!""!P c d ""nef" ) [[ !"page" ]] ; << << !"c" ,, SP ,, !"d" >> >> end test ]]"
\item "[[ etst lgc-test ( !"ab""!""!Pab""!cd""!ef""nef" ) [[ !"page" ]] ; << << !"e" ,, !"f" >> ,, << !"c" ,, !"d" >> ,, << !"a" ,, !"b" >> >> end test ]]"
\item "[[ etst lgc-test ( !"ab""!""!P a b ""! c d ""! e f ""nef" ) [[ !"page" ]] ; << << !"e" ,, SP ,, !"f" >> ,, << SP ,, !"c" ,, SP ,, !"d" ,, SP >> ,, << SP ,, !"a" ,, SP ,, !"b" ,, SP >> >> end test ]]"
\item "[[ etst lgc-test ( !"abyz" ) [[ !"bib" ]] ; <<>> end test ]]"
\item "[[ etst lgc-test ( !"ab""!""!Rcd""nyz" ) [[ !"bib" ]] ; << << << !"c" ,, !"d" >> >> >> end test ]]"
\item "[[ etst lgc-test ( !"ab""!""!Rcd""n""!""!Ref""nyz" ) [[ !"bib" ]] ; << << << !"e" ,, !"f" >> >> ,, << << !"c" ,, !"d" >> >> >> end test ]]"
\item "[[ etst lgc-test ( !"ab""!""!Rcd""!ef""!gh""n""!""!Rij""nyz" ) [[ !"bib" ]] ; << << << !"i" ,, !"j" >> >> ,, << << !"g" ,, !"h" >> ,, << !"e" ,, !"f" >> ,, << !"c" ,, !"d" >> >> >> end test ]]"
\item "[[ etst lgc-test ( !"ab""!""!R c d ""! e f ""! g h ""n""!""!Rij""nyz" ) [[ !"bib" ]] ; << << << !"i" ,, !"j" >> >> ,, << << !"g" ,, SP ,, !"h" >> ,, << SP ,, !"e" ,, SP ,, !"f" ,, SP >> ,, << SP ,, !"c" ,, SP ,, !"d" ,, SP >> >> >> end test ]]"
\item "[[ etst lgc-test ( !"ab""!""!D1""n c d ""! e f ""! g h ""n""!ij""!kl""!""n""!""!D2""nmn""!""!Byz" ) [[ !"def" ]] ; << << << 45 ,, "2" >> ,, << "m" ,, "n" >> >> ,, << << 5 ,, "1" >> ,, << "c" ,, SP ,, "d" ,, SP ,, QQ ,, SP ,, "e" ,, SP ,, "f" ,, SP ,, QQ ,, SP ,, "g" ,, SP ,, "h" >> ,, << QQ ,, "i" ,, "j" ,, QQ ,, "k" ,, "l" ,, QQ >> >> >> end test ]]"
\item "[[ etst lgc-test ( !"ab""!""!D c d ""! e f ""! g h ""n""!ij""!kl""!""n""!""!Dmn""!""!Byz" ) [[ !"body" ]] ; << << !"z" ,, 49 >> ,, << !"y" ,, 48 >> ,, << !"b" ,, 1 >> ,, << !"a" ,, 0 >> >> end test ]]"
\end{statements}
\subsection{Splicing}
\begin{statements}
\item "[[ etst lgc-splice ( vt2vector* ( !" base " ) , vt2vector* ( !"if ""! then ""! else ""!" ) ) ; vt2vector* ( !"base if ""! then ""! else ""!" ) end test ]]"
\item "[[ etst lgc-splice ( vt2vector* ( !"base " ) , vt2vector* ( !"if ""! then ""! else ""!" ) ) ; vt2vector* ( !"base if ""! then ""! else ""!" ) end test ]]"
\item "[[ etst lgc-splice ( vt2vector* ( !" base" ) , vt2vector* ( !"if ""! then ""! else ""!" ) ) ; vt2vector* ( !"baseif ""! then ""! else ""!" ) end test ]]"
\item "[[ etst lgc-splice ( vt2vector* ( !"base" ) , vt2vector* ( !"if ""! then ""! else ""!" ) ) ; vt2vector* ( !"baseif ""! then ""! else ""!" ) end test ]]"
\item "[[ etst lgc-splice ( vt2vector* ( !" base " ) , vt2vector* ( !""-""! + ""!" ) ) ; vt2vector* ( !""-""! base + ""!" ) end test ]]"
\item "[[ etst lgc-splice ( vt2vector* ( !"base " ) , vt2vector* ( !""-""! + ""!" ) ) ; vt2vector* ( !""-""! base + ""!" ) end test ]]"
\item "[[ etst lgc-splice ( vt2vector* ( !" base" ) , vt2vector* ( !""-""! + ""!" ) ) ; vt2vector* ( !""-""! base+ ""!" ) end test ]]"
\item "[[ etst lgc-splice ( vt2vector* ( !"base" ) , vt2vector* ( !""-""! + ""!" ) ) ; vt2vector* ( !""-""! base+ ""!" ) end test ]]"
\item "[[ etst lgc-splice ( vt2vector* ( !" base " ) , vt2vector* ( !""-""!,""!" ) ) ; vt2vector* ( !""-""! base ,""!" ) end test ]]"
\item "[[ etst lgc-splice ( vt2vector* ( !"base " ) , vt2vector* ( !""-""!,""!" ) ) ; vt2vector* ( !""-""!base ,""!" ) end test ]]"
\item "[[ etst lgc-splice ( vt2vector* ( !" base" ) , vt2vector* ( !""-""!,""!" ) ) ; vt2vector* ( !""-""! base,""!" ) end test ]]"
\item "[[ etst lgc-splice ( vt2vector* ( !"base" ) , vt2vector* ( !""-""!,""!" ) ) ; vt2vector* ( !""-""!base,""!" ) end test ]]"
\item "[[ etst lgc-splice ( vt2vector* ( !" base " ) , vt2vector* ( !""-""! ""!" ) ) ; vt2vector* ( !""-""! base ""!" ) end test ]]"
\item "[[ etst lgc-splice ( vt2vector* ( !"base " ) , vt2vector* ( !""-""! ""!" ) ) ; vt2vector* ( !""-""! base ""!" ) end test ]]"
\item "[[ etst lgc-splice ( vt2vector* ( !" base" ) , vt2vector* ( !""-""! ""!" ) ) ; vt2vector* ( !""-""! base ""!" ) end test ]]"
\item "[[ etst lgc-splice ( vt2vector* ( !"base" ) , vt2vector* ( !""-""! ""!" ) ) ; vt2vector* ( !""-""! base ""!" ) end test ]]"
\end{statements}
\subsection{Auxiliary definitions}
\begin{statements}
\item "[[ late define selfref as self [[ 0 ]] end define ]]"
\item "[[ late define baseref as base [[ 0 ]] end define ]]"
\item "[[ late define lgw-trisect-self as newline
let v = self [[ selfref ]] [[ !"vector" ]] in newline
let c = lgw-trisect ( vt2vector* ( v ) ) in newline
c [[ baseref -> base [[ baseref ]] ]] end define ]]"
\item "[[ late define lgw-trisect-base as newline
lgw-trisect ( vt2vector* ( base [[ baseref ]] [[ !"vector" ]] ) ) end define ]]"
\item "[[ late define lgw-codify-self as lgw-codify ( selfref , lgw-trisect-self , 4 ) end define ]]"
\item "[[ late define lgw-codify-base as lgw-codify ( baseref , lgw-trisect-base , 4 ) end define ]]"
\end{statements}
\subsection{Trisecting of lgw files}
\begin{statements}
\item "[[ etst lgw-parse-string ( bt2vector* ( << 2 ,, 65 ,, 66 ,, 67 >> ) ) ; << !"AB" ,, "C" >> end test ]]"
\item "[[ etst lgw-parse-bibliography ( bt2vector* ( << 1 ,, 65 ,, 1 ,, 66 ,, 0 ,, 67 >> ) ) ; << << !"A" ,, !"B" >> ,, !"C" >> end test ]]"
\item "[[ etst lgw-parse-dictionary ( bt2vector* ( << 1 ,, 0 ,, 2 ,, 1 ,, 0 ,, 67 >> ) ) ; << true [[ 0 -> 0 ]] [[ 1 -> 0 ]] [[ 2 -> 1 ]] ,, "C" >> end test ]]"
\item "[[ etst lgw-trisect ( bt2vector* ( << 1 ,, 65 ,, 1 ,, 66 ,, 0 ,, 1 ,, 0 ,, 2 ,, 1 ,, 0 ,, 67 >> ) ) ; true [[ 0 -> !"A" ]] [[ << !"A" ,, !"vector" >> => bt2vector ( << 1 ,, 65 ,, 1 ,, 66 ,, 0 ,, 1 ,, 0 ,, 2 ,, 1 ,, 0 ,, 67 >> ) ]] [[ << !"A" ,, !"bibliography" >> => << !"A" ,, !"B" >> ]] [[ << !"A" ,, !"dictionary" ,, 0 >> => 0 ]] [[ << !"A" ,, !"dictionary" ,, 1 >> => 0 ]] [[ << !"A" ,, !"dictionary" ,, 2 >> => 1 ]] [[ << !"A" ,, !"body" >> => << !"C" >> ]] end test ]]"
\end{statements}
""{
THIS IS TIME CONSUMING.
The "codify self" test covers the "unpack self", "expand self" and "harvest self" test cases so the latter are only useful when locating a bug.
\subsection{Codification of self}
\begin{statements}
\item "[[ etst measure ( "trisect self" , lgw-trisect-self [[ selfref ]] [[ !"bibliography" ]] ) ; self [[ selfref ]] [[ !"bibliography" ]] end test ]]"
\item "[[ etst lgw-trisect-self [[ selfref ]] [[ !"dictionary" ]] ; self [[ selfref ]] [[ !"dictionary" ]] end test ]]"
\item "[[ etst measure ( "unpack self" , lgw-codify-unpack ( selfref , lgw-trisect-self ) ) [[ selfref ]] [[ !"body" ]] ; self [[ selfref ]] [[ !"body" ]] end test ]]"
\item "[[ etst measure ( "expand self" , lgw-codify-expand ( selfref , self ) ) ; self [[ selfref ]] [[ !"expansion" ]] end test ]]"
\item "[[ etst measure ( "harvest self" , lgw-codify-harvest ( selfref , self [[ selfref ]] [[ !"expansion" ]] , self , self [[ << selfref ,, !"codex" >> => true ]] ) [[ selfref ]] [[ !"codex" ]] ) ; self [[ selfref ]] [[ !"codex" ]] end test ]]"
\item "[[ etst measure ( "codify self" , lgw-codify-self [[ selfref ]] [[ !"body" ]] ) ; self [[ selfref ]] [[ !"body" ]] end test ]]"
\item "[[ etst lgw-codify-self [[ selfref ]] [[ !"expansion" ]] ; self [[ selfref ]] [[ !"expansion" ]] end test ]]"
\item "[[ etst lgw-codify-self [[ selfref ]] [[ !"codex" ]] ; self [[ selfref ]] [[ !"codex" ]] end test ]]"
\end{statements}
""}
""{
THIS IS TIME CONSUMING.
See above for dependency between tests.
\subsection{Codification of base}
\begin{statements}
\item "[[ etst measure ( "trisect base" , lgw-trisect-base [[ baseref ]] [[ !"bibliography" ]] ) ; base [[ baseref ]] [[ !"bibliography" ]] end test ]]"
\item "[[ etst lgw-trisect-base [[ baseref ]] [[ !"dictionary" ]] ; base [[ baseref ]] [[ !"dictionary" ]] end test ]]"
\item "[[ etst measure ( "unpack base" , lgw-codify-unpack ( baseref , lgw-trisect-base ) ) [[ baseref ]] [[ !"body" ]] ; base [[ baseref ]] [[ !"body" ]] end test ]]"
\item "[[ etst measure ( "expand base" , lgw-codify-expand ( baseref , base ) ) ; base [[ baseref ]] [[ !"expansion" ]] end test ]]"
\item "[[ etst measure ( "harvest base" , lgw-codify-harvest ( baseref , base [[ baseref ]] [[ !"expansion" ]] , base , base [[ << baseref ,, !"codex" >> => true ]] ) [[ baseref ]] [[ !"codex" ]] ) ; base [[ baseref ]] [[ !"codex" ]] end test ]]"
\item "[[ etst measure ( "codify base" , lgw-codify-base [[ baseref ]] [[ !"body" ]] ) ; base [[ baseref ]] [[ !"body" ]] end test ]]"
\item "[[ etst lgw-codify-base [[ baseref ]] [[ !"expansion" ]] ; base [[ baseref ]] [[ !"expansion" ]] end test ]]"
\item "[[ etst lgw-codify-base [[ baseref ]] [[ !"codex" ]] ; base [[ baseref ]] [[ !"codex" ]] end test ]]"
\end{statements}
""}
\subsection{Test of conversion from GRD to MJD}
\begin{statements}
\item "[[ etst lgc-grd2mjd ( << 1858 ,, 11 ,, 17 >> ) ; 0 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 1858 ,, 11 ,, 18 >> ) ; 1 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 1858 ,, 11 ,, 20 >> ) ; 3 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 1858 ,, 11 ,, 30 >> ) ; 13 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 1858 ,, 12 ,, 01 >> ) ; 14 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 1858 ,, 12 ,, 31 >> ) ; 44 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 1859 ,, 01 ,, 01 >> ) ; 45 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 1859 ,, 02 ,, 01 >> ) ; 45 + 31 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 1859 ,, 03 ,, 01 >> ) ; 45 + 31 + 28 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 1859 ,, 04 ,, 01 >> ) ; 45 + 31 + 28 + 31 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 1859 ,, 05 ,, 01 >> ) ; 45 + 31 + 28 + 31 + 30 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 1859 ,, 06 ,, 01 >> ) ; 45 + 31 + 28 + 31 + 30 + 31 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 1859 ,, 07 ,, 01 >> ) ; 45 + 31 + 28 + 31 + 30 + 31 + 30 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 1859 ,, 08 ,, 01 >> ) ; 45 + 31 + 28 + 31 + 30 + 31 + 30 + 31 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 1859 ,, 09 ,, 01 >> ) ; 45 + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 1859 ,, 10 ,, 01 >> ) ; 45 + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 1859 ,, 11 ,, 01 >> ) ; 45 + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 1859 ,, 12 ,, 01 >> ) ; 45 + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 1860 ,, 01 ,, 01 >> ) ; 45 + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 1860 ,, 01 ,, 01 >> ) ; 45 + 365 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 1860 ,, 02 ,, 01 >> ) ; 45 + 365 + 31 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 1860 ,, 03 ,, 01 >> ) ; 45 + 365 + 31 + 29 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 1860 ,, 04 ,, 01 >> ) ; 45 + 365 + 31 + 29 + 31 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 1860 ,, 05 ,, 01 >> ) ; 45 + 365 + 31 + 29 + 31 + 30 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 1860 ,, 06 ,, 01 >> ) ; 45 + 365 + 31 + 29 + 31 + 30 + 31 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 1860 ,, 07 ,, 01 >> ) ; 45 + 365 + 31 + 29 + 31 + 30 + 31 + 30 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 1860 ,, 08 ,, 01 >> ) ; 45 + 365 + 31 + 29 + 31 + 30 + 31 + 30 + 31 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 1860 ,, 09 ,, 01 >> ) ; 45 + 365 + 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 1860 ,, 10 ,, 01 >> ) ; 45 + 365 + 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 1860 ,, 11 ,, 01 >> ) ; 45 + 365 + 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 1860 ,, 12 ,, 01 >> ) ; 45 + 365 + 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 1861 ,, 01 ,, 01 >> ) ; 45 + 365 + 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 1861 ,, 01 ,, 01 >> ) ; 45 + 365 + 366 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 1862 ,, 01 ,, 01 >> ) ; 45 + 365 + 366 + 365 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 1863 ,, 01 ,, 01 >> ) ; 45 + 365 + 366 + 365 + 365 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 1864 ,, 01 ,, 01 >> ) ; 45 + 365 + 366 + 365 + 365 + 365 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 1865 ,, 01 ,, 01 >> ) ; 45 + 365 + 366 + 365 + 365 + 365 + 366 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 1900 ,, 01 ,, 01 >> ) ; 15020 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 1900 ,, 02 ,, 28 >> ) ; 15078 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 1900 ,, 03 ,, 01 >> ) ; 15079 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 2000 ,, 01 ,, 01 >> ) ; 51544 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 2000 ,, 02 ,, 28 >> ) ; 51602 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 2000 ,, 03 ,, 01 >> ) ; 51604 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 2100 ,, 02 ,, 28 >> ) ; 88127 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 2100 ,, 03 ,, 01 >> ) ; 88128 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 2200 ,, 02 ,, 28 >> ) ; 124651 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 2200 ,, 03 ,, 01 >> ) ; 124652 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 2300 ,, 02 ,, 28 >> ) ; 161175 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 2300 ,, 03 ,, 01 >> ) ; 161176 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 2400 ,, 02 ,, 28 >> ) ; 197699 end test ]]"
\item "[[ etst lgc-grd2mjd ( << 2400 ,, 03 ,, 01 >> ) ; 197701 end test ]]"
\end{statements}
\subsection{Test of conversion from MJD to GRD}
\begin{statements}
\item "[[ etst << 1858 ,, 11 ,, 17 >> ; lgc-mjd2grd ( 0 ) end test ]]"
\item "[[ etst << 1858 ,, 11 ,, 18 >> ; lgc-mjd2grd ( 1 ) end test ]]"
\item "[[ etst << 1858 ,, 11 ,, 20 >> ; lgc-mjd2grd ( 3 ) end test ]]"
\item "[[ etst << 1858 ,, 11 ,, 30 >> ; lgc-mjd2grd ( 13 ) end test ]]"
\item "[[ etst << 1858 ,, 12 ,, 01 >> ; lgc-mjd2grd ( 14 ) end test ]]"
\item "[[ etst << 1858 ,, 12 ,, 31 >> ; lgc-mjd2grd ( 44 ) end test ]]"
\item "[[ etst << 1859 ,, 01 ,, 01 >> ; lgc-mjd2grd ( 45 ) end test ]]"
\item "[[ etst << 1859 ,, 02 ,, 01 >> ; lgc-mjd2grd ( 45 + 31 ) end test ]]"
\item "[[ etst << 1859 ,, 03 ,, 01 >> ; lgc-mjd2grd ( 45 + 31 + 28 ) end test ]]"
\item "[[ etst << 1859 ,, 04 ,, 01 >> ; lgc-mjd2grd ( 45 + 31 + 28 + 31 ) end test ]]"
\item "[[ etst << 1859 ,, 05 ,, 01 >> ; lgc-mjd2grd ( 45 + 31 + 28 + 31 + 30 ) end test ]]"
\item "[[ etst << 1859 ,, 06 ,, 01 >> ; lgc-mjd2grd ( 45 + 31 + 28 + 31 + 30 + 31 ) end test ]]"
\item "[[ etst << 1859 ,, 07 ,, 01 >> ; lgc-mjd2grd ( 45 + 31 + 28 + 31 + 30 + 31 + 30 ) end test ]]"
\item "[[ etst << 1859 ,, 08 ,, 01 >> ; lgc-mjd2grd ( 45 + 31 + 28 + 31 + 30 + 31 + 30 + 31 ) end test ]]"
\item "[[ etst << 1859 ,, 09 ,, 01 >> ; lgc-mjd2grd ( 45 + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 ) end test ]]"
\item "[[ etst << 1859 ,, 10 ,, 01 >> ; lgc-mjd2grd ( 45 + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 ) end test ]]"
\item "[[ etst << 1859 ,, 11 ,, 01 >> ; lgc-mjd2grd ( 45 + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 ) end test ]]"
\item "[[ etst << 1859 ,, 12 ,, 01 >> ; lgc-mjd2grd ( 45 + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 ) end test ]]"
\item "[[ etst << 1860 ,, 01 ,, 01 >> ; lgc-mjd2grd ( 45 + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31 ) end test ]]"
\item "[[ etst << 1860 ,, 01 ,, 01 >> ; lgc-mjd2grd ( 45 + 365 ) end test ]]"
\item "[[ etst << 1860 ,, 02 ,, 01 >> ; lgc-mjd2grd ( 45 + 365 + 31 ) end test ]]"
\item "[[ etst << 1860 ,, 03 ,, 01 >> ; lgc-mjd2grd ( 45 + 365 + 31 + 29 ) end test ]]"
\item "[[ etst << 1860 ,, 04 ,, 01 >> ; lgc-mjd2grd ( 45 + 365 + 31 + 29 + 31 ) end test ]]"
\item "[[ etst << 1860 ,, 05 ,, 01 >> ; lgc-mjd2grd ( 45 + 365 + 31 + 29 + 31 + 30 ) end test ]]"
\item "[[ etst << 1860 ,, 06 ,, 01 >> ; lgc-mjd2grd ( 45 + 365 + 31 + 29 + 31 + 30 + 31 ) end test ]]"
\item "[[ etst << 1860 ,, 07 ,, 01 >> ; lgc-mjd2grd ( 45 + 365 + 31 + 29 + 31 + 30 + 31 + 30 ) end test ]]"
\item "[[ etst << 1860 ,, 08 ,, 01 >> ; lgc-mjd2grd ( 45 + 365 + 31 + 29 + 31 + 30 + 31 + 30 + 31 ) end test ]]"
\item "[[ etst << 1860 ,, 09 ,, 01 >> ; lgc-mjd2grd ( 45 + 365 + 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 ) end test ]]"
\item "[[ etst << 1860 ,, 10 ,, 01 >> ; lgc-mjd2grd ( 45 + 365 + 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 ) end test ]]"
\item "[[ etst << 1860 ,, 11 ,, 01 >> ; lgc-mjd2grd ( 45 + 365 + 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 ) end test ]]"
\item "[[ etst << 1860 ,, 12 ,, 01 >> ; lgc-mjd2grd ( 45 + 365 + 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 ) end test ]]"
\item "[[ etst << 1861 ,, 01 ,, 01 >> ; lgc-mjd2grd ( 45 + 365 + 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31 ) end test ]]"
\item "[[ etst << 1861 ,, 01 ,, 01 >> ; lgc-mjd2grd ( 45 + 365 + 366 ) end test ]]"
\item "[[ etst << 1862 ,, 01 ,, 01 >> ; lgc-mjd2grd ( 45 + 365 + 366 + 365 ) end test ]]"
\item "[[ etst << 1863 ,, 01 ,, 01 >> ; lgc-mjd2grd ( 45 + 365 + 366 + 365 + 365 ) end test ]]"
\item "[[ etst << 1864 ,, 01 ,, 01 >> ; lgc-mjd2grd ( 45 + 365 + 366 + 365 + 365 + 365 ) end test ]]"
\item "[[ etst << 1865 ,, 01 ,, 01 >> ; lgc-mjd2grd ( 45 + 365 + 366 + 365 + 365 + 365 + 366 ) end test ]]"
\item "[[ etst << 1900 ,, 01 ,, 01 >> ; lgc-mjd2grd ( 15020 ) end test ]]"
\item "[[ etst << 1900 ,, 02 ,, 28 >> ; lgc-mjd2grd ( 15078 ) end test ]]"
\item "[[ etst << 1900 ,, 03 ,, 01 >> ; lgc-mjd2grd ( 15079 ) end test ]]"
\item "[[ etst << 2000 ,, 01 ,, 01 >> ; lgc-mjd2grd ( 51544 ) end test ]]"
\item "[[ etst << 2000 ,, 02 ,, 28 >> ; lgc-mjd2grd ( 51602 ) end test ]]"
\item "[[ etst << 2000 ,, 03 ,, 01 >> ; lgc-mjd2grd ( 51604 ) end test ]]"
\item "[[ etst << 2100 ,, 02 ,, 28 >> ; lgc-mjd2grd ( 88127 ) end test ]]"
\item "[[ etst << 2100 ,, 03 ,, 01 >> ; lgc-mjd2grd ( 88128 ) end test ]]"
\item "[[ etst << 2200 ,, 02 ,, 28 >> ; lgc-mjd2grd ( 124651 ) end test ]]"
\item "[[ etst << 2200 ,, 03 ,, 01 >> ; lgc-mjd2grd ( 124652 ) end test ]]"
\item "[[ etst << 2300 ,, 02 ,, 28 >> ; lgc-mjd2grd ( 161175 ) end test ]]"
\item "[[ etst << 2300 ,, 03 ,, 01 >> ; lgc-mjd2grd ( 161176 ) end test ]]"
\item "[[ etst << 2400 ,, 02 ,, 28 >> ; lgc-mjd2grd ( 197699 ) end test ]]"
\item "[[ etst << 2400 ,, 03 ,, 01 >> ; lgc-mjd2grd ( 197701 ) end test ]]"
\end{statements}
\subsection{Test of GRD parsing functions}
\begin{statements}
\item "[[ etst lgc-convert-leap ( << !"GRD-2000-01-02+1" >> ) ; << lgc-grd2mjd ( << 2000 ,, 01 ,, 02 >> ) :: +1 >> end test ]]"
\item "[[ etst lgc-convert-leap ( << !"GRD-2000-01-02-1" >> ) ; << lgc-grd2mjd ( << 2000 ,, 01 ,, 02 >> ) :: -1 >> end test ]]"
\item "[[ etst lgc-convert-leap ( << !"GRD-2000-01-02+1" ,, !"GRD-2000-01-01-1" >> ) ; << lgc-grd2mjd ( << 2000 ,, 01 ,, 02 >> ) :: +1 ,, lgc-grd2mjd ( << 2000 ,, 01 ,, 01 >> ) :: -1 >> end test ]]"
\item "[[ etst lgc-convert-leap ( << !"GRD-2000-01-02+1" ,, !"ERD-2000-01-01-1" >> ) ; ( !"Invalid leap: " :: !"ERD-2000-01-01-1" ) raise end test ]]"
\item "[[ etst lgc-convert-leap ( << !"GRD-2000-01-02+1" ,, !"GRD:2000-01-01-1" >> ) ; ( !"Invalid leap: " :: !"GRD:2000-01-01-1" ) raise end test ]]"
\item "[[ etst lgc-convert-leap ( << !"GRD-2000-01-02+1" ,, !"GRD-20A0-01-01-1" >> ) ; ( !"Invalid leap: " :: !"GRD-20A0-01-01-1" ) raise end test ]]"
\item "[[ etst lgc-convert-leap ( << !"GRD-2000-01-02+1" ,, !"GRD-2000:01-01-1" >> ) ; ( !"Invalid leap: " :: !"GRD-2000:01-01-1" ) raise end test ]]"
\item "[[ etst lgc-convert-leap ( << !"GRD-2000-01-02+1" ,, !"GRD-2000-01:01-1" >> ) ; ( !"Invalid leap: " :: !"GRD-2000-01:01-1" ) raise end test ]]"
\item "[[ etst lgc-convert-leap ( << !"GRD-2000-01-02+1" ,, !"GRD-2000-01-01:1" >> ) ; ( !"Invalid leap: " :: !"GRD-2000-01-01:1" ) raise end test ]]"
\item "[[ etst lgc-convert-leap ( << !"ARD-2000-01-02+1" ,, !"GRD-2000-01-01:1" >> ) ; ( !"Invalid leap: " :: !"ARD-2000-01-02+1" ) raise end test ]]"
\item "[[ etst lgc-process-leap ( true [[ << !"parameters" ,, !"leap" >> => << !"GRD-2000-01-02+1" ,, !"GRD-2000-01-01-1" >> ]] ) [[ !"leap" ]] ; << 10 ,, lgc-grd2mjd ( << 2000 ,, 01 ,, 03 >> ) * 24 * 60 * 60 + 10 :: +1 ,, lgc-grd2mjd ( << 2000 ,, 01 ,, 02 >> ) * 24 * 60 * 60 + 9 :: -1 >> end test ]]"
\item "[[ etst lgc-process-leap ( true [[ << !"parameters" ,, !"leap" >> => << !"GRD-2000-01-02+1" ,, !"GRD-2000-01-01+1" >> ]] ) [[ !"leap" ]] ; << 12 ,, lgc-grd2mjd ( << 2000 ,, 01 ,, 03 >> ) * 24 * 60 * 60 + 12 :: +1 ,, lgc-grd2mjd ( << 2000 ,, 01 ,, 02 >> ) * 24 * 60 * 60 + 11 :: +1 >> end test ]]"
\item "[[ etst lgc-process-leap ( true [[ << !"parameters" ,, !"leap" >> => << !"GRD-2000-01-02+1" ,, !"ARD-2000-01-01-1" >> ]] ) [[ !"leap" ]] ; ( !"Invalid leap: " :: !"ARD-2000-01-01-1" ) raise end test ]]"
\item "[[ etst lgc-process-leap ( true [[ << !"parameters" ,, !"leap" >> => << !"GRD-2000-01-01-1" ,, !"GRD-2000-01-02+1" >> ]] ) [[ !"leap" ]] ; !"Leap seconds must be stated in descending order" raise end test ]]"
\end{statements}
\subsection{Test of conversion from Logiweb time to MJD/TAI}
\begin{statements}
\item "[[ etst lgc-lgt2mjdtai ( << 0 ,, 6 >> ) ;
<< 0 ,, 0 ,, 0 ,, 0 ,, 0 ,, 6 >> end test ]]"
\item "[[ etst lgc-lgt2mjdtai ( << 0 ,, 2 >> ) ;
<< 0 ,, 0 ,, 0 ,, 0 ,, 0 ,, 2 >> end test ]]"
\item "[[ etst lgc-lgt2mjdtai ( << 0 ,, 0 >> ) ;
<< 0 ,, 0 ,, 0 ,, 0 ,, 0 ,, 0 >> end test ]]"
\item "[[ etst lgc-lgt2mjdtai ( << 1 ,, 6 >> ) ;
<< 0 ,, 0 ,, 0 ,, 0 ,, 1 ,, 6 >> end test ]]"
\item "[[ etst lgc-lgt2mjdtai ( << 1 ,, 2 >> ) ;
<< 0 ,, 0 ,, 0 ,, 0 ,, 1 ,, 2 >> end test ]]"
\item "[[ etst lgc-lgt2mjdtai ( << 10 ,, 2 >> ) ;
<< 0 ,, 0 ,, 0 ,, 0 ,, 10 ,, 2 >> end test ]]"
\item "[[ etst lgc-lgt2mjdtai ( << 100 ,, 2 >> ) ;
<< 0 ,, 0 ,, 0 ,, 1 ,, 0 ,, 2 >> end test ]]"
\item "[[ etst lgc-lgt2mjdtai ( << 100 * 10 ,, 2 >> ) ;
<< 0 ,, 0 ,, 0 ,, 10 ,, 0 ,, 2 >> end test ]]"
\item "[[ etst lgc-lgt2mjdtai ( << 100 * 60 ,, 2 >> ) ;
<< 0 ,, 0 ,, 1 ,, 0 ,, 0 ,, 2 >> end test ]]"
\item "[[ etst lgc-lgt2mjdtai ( << 100 * 60 * 10 ,, 2 >> ) ;
<< 0 ,, 0 ,, 10 ,, 0 ,, 0 ,, 2 >> end test ]]"
\item "[[ etst lgc-lgt2mjdtai ( << 100 * 60 * 60 ,, 2 >> ) ;
<< 0 ,, 1 ,, 0 ,, 0 ,, 0 ,, 2 >> end test ]]"
\item "[[ etst lgc-lgt2mjdtai ( << 100 * 60 * 60 * 10 ,, 2 >> ) ;
<< 0 ,, 10 ,, 0 ,, 0 ,, 0 ,, 2 >> end test ]]"
\item "[[ etst lgc-lgt2mjdtai ( << 100 * 60 * 60 * 24 ,, 2 >> ) ;
<< 1 ,, 0 ,, 0 ,, 0 ,, 0 ,, 2 >> end test ]]"
\item "[[ etst lgc-lgt2mjdtai ( << 100 * 60 * 60 * 24 * 10 ,, 2 >> ) ;
<< 10 ,, 0 ,, 0 ,, 0 ,, 0 ,, 2 >> end test ]]"
\item "[[ etst lgc-lgt2mjdtai ( << - 100 * 60 * 60 * 24 * 10 ,, 2 >> ) ;
<< - 10 ,, 0 ,, 0 ,, 0 ,, 0 ,, 2 >> end test ]]"
\item "[[ etst vt2vector ( lgc-lgt2mjdtai2vt ( << 0 ,, 6 >> ) ) ; !"MJD-0.TAI:00:00:00.000000" end test ]]"
\item "[[ etst vt2vector ( lgc-lgt2mjdtai2vt ( << 0 ,, 2 >> ) ) ; !"MJD-0.TAI:00:00:00.00" end test ]]"
\item "[[ etst vt2vector ( lgc-lgt2mjdtai2vt ( << 0 ,, 0 >> ) ) ; !"MJD-0.TAI:00:00:00" end test ]]"
\item "[[ etst vt2vector ( lgc-lgt2mjdtai2vt ( << 1 ,, 6 >> ) ) ; !"MJD-0.TAI:00:00:00.000001" end test ]]"
\item "[[ etst vt2vector ( lgc-lgt2mjdtai2vt ( << 1 ,, 2 >> ) ) ; !"MJD-0.TAI:00:00:00.01" end test ]]"
\item "[[ etst vt2vector ( lgc-lgt2mjdtai2vt ( << 10 ,, 2 >> ) ) ; !"MJD-0.TAI:00:00:00.10" end test ]]"
\item "[[ etst vt2vector ( lgc-lgt2mjdtai2vt ( << 100 ,, 2 >> ) ) ; !"MJD-0.TAI:00:00:01.00" end test ]]"
\item "[[ etst vt2vector ( lgc-lgt2mjdtai2vt ( << 100 * 10 ,, 2 >> ) ) ; !"MJD-0.TAI:00:00:10.00" end test ]]"
\item "[[ etst vt2vector ( lgc-lgt2mjdtai2vt ( << 100 * 60 ,, 2 >> ) ) ; !"MJD-0.TAI:00:01:00.00" end test ]]"
\item "[[ etst vt2vector ( lgc-lgt2mjdtai2vt ( << 100 * 60 * 10 ,, 2 >> ) ) ; !"MJD-0.TAI:00:10:00.00" end test ]]"
\item "[[ etst vt2vector ( lgc-lgt2mjdtai2vt ( << 100 * 60 * 60 ,, 2 >> ) ) ; !"MJD-0.TAI:01:00:00.00" end test ]]"
\item "[[ etst vt2vector ( lgc-lgt2mjdtai2vt ( << 100 * 60 * 60 * 10 ,, 2 >> ) ) ; !"MJD-0.TAI:10:00:00.00" end test ]]"
\item "[[ etst vt2vector ( lgc-lgt2mjdtai2vt ( << 100 * 60 * 60 * 24 ,, 2 >> ) ) ; !"MJD-1.TAI:00:00:00.00" end test ]]"
\item "[[ etst vt2vector ( lgc-lgt2mjdtai2vt ( << 100 * 60 * 60 * 24 * 10 ,, 2 >> ) ) ; !"MJD-10.TAI:00:00:00.00" end test ]]"
\item "[[ etst vt2vector ( lgc-lgt2mjdtai2vt ( << - 100 * 60 * 60 * 24 * 10 ,, 2 >> ) ) ; !"MJD--10.TAI:00:00:00.00" end test ]]"
\end{statements}
\subsection{Test of conversion from Logiweb time to GRD/UTC}
\begin{statements}
\item "[[ late define test-leaps as newline
<< 11 ,, 6 * 60 + 11 :: + 1 ,, 4 * 60 + 10 :: + 1 ,, 2 * 60 + 9 :: - 1 >> end define ]]"
A situation where a negative leap occured two minutes after epoch and two positive ones occured four and six minutes after epoch.
\item "[[ etst 0 :: 0 ; lgc-lgt2utc ( 0 , 0 , <<>> ) end test ]]"
\item "[[ etst - 10 :: 0 ; lgc-lgt2utc ( 0 , 10 , <<>> ) end test ]]"
\item "[[ etst 0 :: 0 ; lgc-lgt2utc ( 10 , 10 , <<>> ) end test ]]"
\item "[[ etst 362 :: 0 ; lgc-lgt2utc ( 373 , test-leaps head , test-leaps tail ) end test ]]"
\item "[[ etst 361 :: 0 ; lgc-lgt2utc ( 372 , test-leaps head , test-leaps tail ) end test ]]"
\item "[[ etst 360 :: 0 ; lgc-lgt2utc ( 371 , test-leaps head , test-leaps tail ) end test ]]"
\item "[[ etst 359 :: 1 ; lgc-lgt2utc ( 370 , test-leaps head , test-leaps tail ) end test ]]"
\item "[[ etst 359 :: 0 ; lgc-lgt2utc ( 369 , test-leaps head , test-leaps tail ) end test ]]"
\item "[[ etst 358 :: 0 ; lgc-lgt2utc ( 368 , test-leaps head , test-leaps tail ) end test ]]"
\item "[[ etst 302 :: 0 ; lgc-lgt2utc ( 312 , test-leaps head , test-leaps tail ) end test ]]"
\item "[[ etst 301 :: 0 ; lgc-lgt2utc ( 311 , test-leaps head , test-leaps tail ) end test ]]"
\item "[[ etst 300 :: 0 ; lgc-lgt2utc ( 310 , test-leaps head , test-leaps tail ) end test ]]"
\item "[[ etst 299 :: 0 ; lgc-lgt2utc ( 309 , test-leaps head , test-leaps tail ) end test ]]"
\item "[[ etst 298 :: 0 ; lgc-lgt2utc ( 308 , test-leaps head , test-leaps tail ) end test ]]"
\item "[[ etst 297 :: 0 ; lgc-lgt2utc ( 307 , test-leaps head , test-leaps tail ) end test ]]"
\item "[[ etst 242 :: 0 ; lgc-lgt2utc ( 252 , test-leaps head , test-leaps tail ) end test ]]"
\item "[[ etst 241 :: 0 ; lgc-lgt2utc ( 251 , test-leaps head , test-leaps tail ) end test ]]"
\item "[[ etst 240 :: 0 ; lgc-lgt2utc ( 250 , test-leaps head , test-leaps tail ) end test ]]"
\item "[[ etst 239 :: 1 ; lgc-lgt2utc ( 249 , test-leaps head , test-leaps tail ) end test ]]"
\item "[[ etst 239 :: 0 ; lgc-lgt2utc ( 248 , test-leaps head , test-leaps tail ) end test ]]"
\item "[[ etst 238 :: 0 ; lgc-lgt2utc ( 247 , test-leaps head , test-leaps tail ) end test ]]"
\item "[[ etst 182 :: 0 ; lgc-lgt2utc ( 191 , test-leaps head , test-leaps tail ) end test ]]"
\item "[[ etst 181 :: 0 ; lgc-lgt2utc ( 190 , test-leaps head , test-leaps tail ) end test ]]"
\item "[[ etst 180 :: 0 ; lgc-lgt2utc ( 189 , test-leaps head , test-leaps tail ) end test ]]"
\item "[[ etst 179 :: 0 ; lgc-lgt2utc ( 188 , test-leaps head , test-leaps tail ) end test ]]"
\item "[[ etst 178 :: 0 ; lgc-lgt2utc ( 187 , test-leaps head , test-leaps tail ) end test ]]"
\item "[[ etst 177 :: 0 ; lgc-lgt2utc ( 186 , test-leaps head , test-leaps tail ) end test ]]"
\item "[[ etst 122 :: 0 ; lgc-lgt2utc ( 131 , test-leaps head , test-leaps tail ) end test ]]"
\item "[[ etst 121 :: 0 ; lgc-lgt2utc ( 130 , test-leaps head , test-leaps tail ) end test ]]"
\item "[[ etst 120 :: 0 ; lgc-lgt2utc ( 129 , test-leaps head , test-leaps tail ) end test ]]"
\item "[[ etst 118 :: 0 ; lgc-lgt2utc ( 128 , test-leaps head , test-leaps tail ) end test ]]"
\item "[[ etst 117 :: 0 ; lgc-lgt2utc ( 127 , test-leaps head , test-leaps tail ) end test ]]"
\item "[[ etst 116 :: 0 ; lgc-lgt2utc ( 126 , test-leaps head , test-leaps tail ) end test ]]"
\item "[[ etst 62 :: 0 ; lgc-lgt2utc ( 72 , test-leaps head , test-leaps tail ) end test ]]"
\item "[[ etst 61 :: 0 ; lgc-lgt2utc ( 71 , test-leaps head , test-leaps tail ) end test ]]"
\item "[[ etst 60 :: 0 ; lgc-lgt2utc ( 70 , test-leaps head , test-leaps tail ) end test ]]"
\item "[[ etst 59 :: 0 ; lgc-lgt2utc ( 69 , test-leaps head , test-leaps tail ) end test ]]"
\item "[[ etst 58 :: 0 ; lgc-lgt2utc ( 68 , test-leaps head , test-leaps tail ) end test ]]"
\item "[[ etst 57 :: 0 ; lgc-lgt2utc ( 67 , test-leaps head , test-leaps tail ) end test ]]"
\item "[[ etst 2 :: 0 ; lgc-lgt2utc ( 12 , test-leaps head , test-leaps tail ) end test ]]"
\item "[[ etst 1 :: 0 ; lgc-lgt2utc ( 11 , test-leaps head , test-leaps tail ) end test ]]"
\item "[[ etst 0 :: 0 ; lgc-lgt2utc ( 10 , test-leaps head , test-leaps tail ) end test ]]"
\item "[[ late define test-leapstate as true [[ !"leap" -> test-leaps ]] end define ]]"
\item "[[ etst << 1858 ,, 11 ,, 17 ,, 0 ,, 0 ,, 0 ,, 0 ,, 6 >> ;
lgc-lgt2grdutc ( << 0 ,, 6 >> , true [[ !"leap" -> << 0 >> ]] ) end test ]]"
\item "[[ etst << 1858 ,, 11 ,, 17 ,, 0 ,, 0 ,, 10 ,, 0 ,, 6 >> ;
lgc-lgt2grdutc ( << 0 ,, 6 >> , true [[ !"leap" -> << - 10 >> ]] ) end test ]]"
\item "[[ etst << 1858 ,, 11 ,, 17 ,, 0 ,, 0 ,, 11 ,, 23 ,, 2 >> ;
lgc-lgt2grdutc ( << 123 ,, 2 >> , true [[ !"leap" -> << - 10 >> ]] ) end test ]]"
\item "[[ etst << 1858 ,, 11 ,, 17 ,, 0 ,, 0 ,, 11 ,, 0 ,, 0 >> ;
lgc-lgt2grdutc ( << 1 ,, 0 >> , true [[ !"leap" -> << - 10 >> ]] ) end test ]]"
\item "[[ etst << 1858 ,, 11 ,, 17 ,, 0 ,, 0 ,, 00 ,, 0 ,, 0 >> ;
lgc-lgt2grdutc ( << 10 ,, 0 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst << 1858 ,, 11 ,, 17 ,, 0 ,, 0 ,, 01 ,, 0 ,, 0 >> ;
lgc-lgt2grdutc ( << 11 ,, 0 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst << 1858 ,, 11 ,, 17 ,, 0 ,, 0 ,, 02 ,, 0 ,, 0 >> ;
lgc-lgt2grdutc ( << 12 ,, 0 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst << 1858 ,, 11 ,, 17 ,, 0 ,, 0 ,, 57 ,, 0 ,, 0 >> ;
lgc-lgt2grdutc ( << 67 ,, 0 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst << 1858 ,, 11 ,, 17 ,, 0 ,, 0 ,, 58 ,, 0 ,, 0 >> ;
lgc-lgt2grdutc ( << 68 ,, 0 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst << 1858 ,, 11 ,, 17 ,, 0 ,, 0 ,, 59 ,, 0 ,, 0 >> ;
lgc-lgt2grdutc ( << 69 ,, 0 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst << 1858 ,, 11 ,, 17 ,, 0 ,, 1 ,, 00 ,, 0 ,, 0 >> ;
lgc-lgt2grdutc ( << 70 ,, 0 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst << 1858 ,, 11 ,, 17 ,, 0 ,, 1 ,, 01 ,, 0 ,, 0 >> ;
lgc-lgt2grdutc ( << 71 ,, 0 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst << 1858 ,, 11 ,, 17 ,, 0 ,, 1 ,, 02 ,, 0 ,, 0 >> ;
lgc-lgt2grdutc ( << 72 ,, 0 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst << 1858 ,, 11 ,, 17 ,, 0 ,, 1 ,, 56 ,, 0 ,, 0 >> ;
lgc-lgt2grdutc ( << 126 ,, 0 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst << 1858 ,, 11 ,, 17 ,, 0 ,, 1 ,, 57 ,, 0 ,, 0 >> ;
lgc-lgt2grdutc ( << 127 ,, 0 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst << 1858 ,, 11 ,, 17 ,, 0 ,, 1 ,, 58 ,, 0 ,, 0 >> ;
lgc-lgt2grdutc ( << 128 ,, 0 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst << 1858 ,, 11 ,, 17 ,, 0 ,, 2 ,, 00 ,, 0 ,, 0 >> ;
lgc-lgt2grdutc ( << 129 ,, 0 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst << 1858 ,, 11 ,, 17 ,, 0 ,, 2 ,, 01 ,, 0 ,, 0 >> ;
lgc-lgt2grdutc ( << 130 ,, 0 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst << 1858 ,, 11 ,, 17 ,, 0 ,, 2 ,, 02 ,, 0 ,, 0 >> ;
lgc-lgt2grdutc ( << 131 ,, 0 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst << 1858 ,, 11 ,, 17 ,, 0 ,, 2 ,, 57 ,, 0 ,, 0 >> ;
lgc-lgt2grdutc ( << 186 ,, 0 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst << 1858 ,, 11 ,, 17 ,, 0 ,, 2 ,, 58 ,, 0 ,, 0 >> ;
lgc-lgt2grdutc ( << 187 ,, 0 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst << 1858 ,, 11 ,, 17 ,, 0 ,, 2 ,, 59 ,, 0 ,, 0 >> ;
lgc-lgt2grdutc ( << 188 ,, 0 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst << 1858 ,, 11 ,, 17 ,, 0 ,, 3 ,, 00 ,, 0 ,, 0 >> ;
lgc-lgt2grdutc ( << 189 ,, 0 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst << 1858 ,, 11 ,, 17 ,, 0 ,, 3 ,, 01 ,, 0 ,, 0 >> ;
lgc-lgt2grdutc ( << 190 ,, 0 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst << 1858 ,, 11 ,, 17 ,, 0 ,, 3 ,, 02 ,, 0 ,, 0 >> ;
lgc-lgt2grdutc ( << 191 ,, 0 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst << 1858 ,, 11 ,, 17 ,, 0 ,, 3 ,, 58 ,, 0 ,, 0 >> ;
lgc-lgt2grdutc ( << 247 ,, 0 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst << 1858 ,, 11 ,, 17 ,, 0 ,, 3 ,, 59 ,, 0 ,, 0 >> ;
lgc-lgt2grdutc ( << 248 ,, 0 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst << 1858 ,, 11 ,, 17 ,, 0 ,, 3 ,, 60 ,, 0 ,, 0 >> ;
lgc-lgt2grdutc ( << 249 ,, 0 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst << 1858 ,, 11 ,, 17 ,, 0 ,, 4 ,, 00 ,, 0 ,, 0 >> ;
lgc-lgt2grdutc ( << 250 ,, 0 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst << 1858 ,, 11 ,, 17 ,, 0 ,, 4 ,, 01 ,, 0 ,, 0 >> ;
lgc-lgt2grdutc ( << 251 ,, 0 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst << 1858 ,, 11 ,, 17 ,, 0 ,, 4 ,, 02 ,, 0 ,, 0 >> ;
lgc-lgt2grdutc ( << 252 ,, 0 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst << 1858 ,, 11 ,, 17 ,, 0 ,, 4 ,, 57 ,, 0 ,, 0 >> ;
lgc-lgt2grdutc ( << 307 ,, 0 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst << 1858 ,, 11 ,, 17 ,, 0 ,, 4 ,, 58 ,, 0 ,, 0 >> ;
lgc-lgt2grdutc ( << 308 ,, 0 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst << 1858 ,, 11 ,, 17 ,, 0 ,, 4 ,, 59 ,, 0 ,, 0 >> ;
lgc-lgt2grdutc ( << 309 ,, 0 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst << 1858 ,, 11 ,, 17 ,, 0 ,, 5 ,, 00 ,, 0 ,, 0 >> ;
lgc-lgt2grdutc ( << 310 ,, 0 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst << 1858 ,, 11 ,, 17 ,, 0 ,, 5 ,, 01 ,, 0 ,, 0 >> ;
lgc-lgt2grdutc ( << 311 ,, 0 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst << 1858 ,, 11 ,, 17 ,, 0 ,, 5 ,, 02 ,, 0 ,, 0 >> ;
lgc-lgt2grdutc ( << 312 ,, 0 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst << 1858 ,, 11 ,, 17 ,, 0 ,, 5 ,, 58 ,, 0 ,, 0 >> ;
lgc-lgt2grdutc ( << 368 ,, 0 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst << 1858 ,, 11 ,, 17 ,, 0 ,, 5 ,, 59 ,, 0 ,, 0 >> ;
lgc-lgt2grdutc ( << 369 ,, 0 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst << 1858 ,, 11 ,, 17 ,, 0 ,, 5 ,, 60 ,, 0 ,, 0 >> ;
lgc-lgt2grdutc ( << 370 ,, 0 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst << 1858 ,, 11 ,, 17 ,, 0 ,, 6 ,, 00 ,, 0 ,, 0 >> ;
lgc-lgt2grdutc ( << 371 ,, 0 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst << 1858 ,, 11 ,, 17 ,, 0 ,, 6 ,, 01 ,, 0 ,, 0 >> ;
lgc-lgt2grdutc ( << 372 ,, 0 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst << 1858 ,, 11 ,, 17 ,, 0 ,, 6 ,, 02 ,, 0 ,, 0 >> ;
lgc-lgt2grdutc ( << 373 ,, 0 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst !"GRD-1858-11-17.UTC:00:00:00.000000" ; newline
lgc-lgt2grdutc2v ( << 0 ,, 6 >> , true [[ !"leap" -> << 0 >> ]] ) end test ]]"
\item "[[ etst !"GRD-1858-11-17.UTC:00:00:10.000000" ; newline
lgc-lgt2grdutc2v ( << 0 ,, 6 >> , true [[ !"leap" -> << - 10 >> ]] ) end test ]]"
\item "[[ etst !"GRD-1858-11-17.UTC:00:00:11.23" ; newline
lgc-lgt2grdutc2v ( << 123 ,, 2 >> , true [[ !"leap" -> << - 10 >> ]] ) end test ]]"
\item "[[ etst !"GRD-1858-11-17.UTC:00:00:11" ; newline
lgc-lgt2grdutc2v ( << 1 ,, 0 >> , true [[ !"leap" -> << - 10 >> ]] ) end test ]]"
\item "[[ etst !"GRD-1858-11-17.UTC:00:00:00" ; newline
lgc-lgt2grdutc2v ( << 10 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst !"GRD-1858-11-17.UTC:00:00:01" ; newline
lgc-lgt2grdutc2v ( << 11 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst !"GRD-1858-11-17.UTC:00:00:02" ; newline
lgc-lgt2grdutc2v ( << 12 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst !"GRD-1858-11-17.UTC:00:00:57" ; newline
lgc-lgt2grdutc2v ( << 67 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst !"GRD-1858-11-17.UTC:00:00:58" ; newline
lgc-lgt2grdutc2v ( << 68 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst !"GRD-1858-11-17.UTC:00:00:59" ; newline
lgc-lgt2grdutc2v ( << 69 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst !"GRD-1858-11-17.UTC:00:01:00" ; newline
lgc-lgt2grdutc2v ( << 70 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst !"GRD-1858-11-17.UTC:00:01:01" ; newline
lgc-lgt2grdutc2v ( << 71 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst !"GRD-1858-11-17.UTC:00:01:02" ; newline
lgc-lgt2grdutc2v ( << 72 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst !"GRD-1858-11-17.UTC:00:01:56" ; newline
lgc-lgt2grdutc2v ( << 126 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst !"GRD-1858-11-17.UTC:00:01:57" ; newline
lgc-lgt2grdutc2v ( << 127 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst !"GRD-1858-11-17.UTC:00:01:58" ; newline
lgc-lgt2grdutc2v ( << 128 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst !"GRD-1858-11-17.UTC:00:02:00" ; newline
lgc-lgt2grdutc2v ( << 129 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst !"GRD-1858-11-17.UTC:00:02:01" ; newline
lgc-lgt2grdutc2v ( << 130 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst !"GRD-1858-11-17.UTC:00:02:02" ; newline
lgc-lgt2grdutc2v ( << 131 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst !"GRD-1858-11-17.UTC:00:02:57" ; newline
lgc-lgt2grdutc2v ( << 186 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst !"GRD-1858-11-17.UTC:00:02:58" ; newline
lgc-lgt2grdutc2v ( << 187 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst !"GRD-1858-11-17.UTC:00:02:59" ; newline
lgc-lgt2grdutc2v ( << 188 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst !"GRD-1858-11-17.UTC:00:03:00" ; newline
lgc-lgt2grdutc2v ( << 189 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst !"GRD-1858-11-17.UTC:00:03:01" ; newline
lgc-lgt2grdutc2v ( << 190 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst !"GRD-1858-11-17.UTC:00:03:02" ; newline
lgc-lgt2grdutc2v ( << 191 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst !"GRD-1858-11-17.UTC:00:03:58" ; newline
lgc-lgt2grdutc2v ( << 247 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst !"GRD-1858-11-17.UTC:00:03:59" ; newline
lgc-lgt2grdutc2v ( << 248 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst !"GRD-1858-11-17.UTC:00:03:60" ; newline
lgc-lgt2grdutc2v ( << 249 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst !"GRD-1858-11-17.UTC:00:04:00" ; newline
lgc-lgt2grdutc2v ( << 250 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst !"GRD-1858-11-17.UTC:00:04:01" ; newline
lgc-lgt2grdutc2v ( << 251 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst !"GRD-1858-11-17.UTC:00:04:02" ; newline
lgc-lgt2grdutc2v ( << 252 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst !"GRD-1858-11-17.UTC:00:04:57" ; newline
lgc-lgt2grdutc2v ( << 307 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst !"GRD-1858-11-17.UTC:00:04:58" ; newline
lgc-lgt2grdutc2v ( << 308 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst !"GRD-1858-11-17.UTC:00:04:59" ; newline
lgc-lgt2grdutc2v ( << 309 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst !"GRD-1858-11-17.UTC:00:05:00" ; newline
lgc-lgt2grdutc2v ( << 310 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst !"GRD-1858-11-17.UTC:00:05:01" ; newline
lgc-lgt2grdutc2v ( << 311 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst !"GRD-1858-11-17.UTC:00:05:02" ; newline
lgc-lgt2grdutc2v ( << 312 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst !"GRD-1858-11-17.UTC:00:05:58" ; newline
lgc-lgt2grdutc2v ( << 368 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst !"GRD-1858-11-17.UTC:00:05:59" ; newline
lgc-lgt2grdutc2v ( << 369 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst !"GRD-1858-11-17.UTC:00:05:60" ; newline
lgc-lgt2grdutc2v ( << 370 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst !"GRD-1858-11-17.UTC:00:06:00" ; newline
lgc-lgt2grdutc2v ( << 371 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst !"GRD-1858-11-17.UTC:00:06:01" ; newline
lgc-lgt2grdutc2v ( << 372 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ etst !"GRD-1858-11-17.UTC:00:06:02" ; newline
lgc-lgt2grdutc2v ( << 373 ,, 0 >> , test-leapstate ) end test ]]"
\item "[[ late define test-leapstate2 as newline
lgc-process-leap ( true [[ << !"parameters" ,, !"leap" >> => lgc-default-leap ]] ) end define ]]"
\item "[[ etst !"GRD-2009-02-18.UTC:08:56:04" ; newline
lgc-lgt2grdutc2v ( << 4741664198 ,, 0 >> , test-leapstate2 ) end test ]]"
GRD-2009-02-18.UTC:08:56:04 = MJD-54880.TAI:08:56:38.
\item "[[ etst !"GRD-1858-11-17.UTC:00:00:00" ; newline
lgc-lgt2grdutc2v ( << 10 ,, 0 >> , test-leapstate2 ) end test ]]"
\item "[[ etst !"GRD-1972-07-01.UTC:00:00:01" ; newline
lgc-lgt2grdutc2v ( << lgc-grd2mjd ( << 1972 ,, 7 ,, 1 >> ) * 24 * 60 * 60 + 12 ,, 0 >> , test-leapstate2 ) end test ]]"
\item "[[ etst !"GRD-1972-07-01.UTC:00:00:00" ; newline
lgc-lgt2grdutc2v ( << lgc-grd2mjd ( << 1972 ,, 7 ,, 1 >> ) * 24 * 60 * 60 + 11 ,, 0 >> , test-leapstate2 ) end test ]]"
\item "[[ etst !"GRD-1972-06-30.UTC:23:59:60" ; newline
lgc-lgt2grdutc2v ( << lgc-grd2mjd ( << 1972 ,, 7 ,, 1 >> ) * 24 * 60 * 60 + 10 ,, 0 >> , test-leapstate2 ) end test ]]"
\item "[[ etst !"GRD-1972-06-30.UTC:23:59:59" ; newline
lgc-lgt2grdutc2v ( << lgc-grd2mjd ( << 1972 ,, 7 ,, 1 >> ) * 24 * 60 * 60 + 9 ,, 0 >> , test-leapstate2 ) end test ]]"
\item "[[ etst !"GRD-1972-06-30.UTC:23:59:58" ; newline
lgc-lgt2grdutc2v ( << lgc-grd2mjd ( << 1972 ,, 7 ,, 1 >> ) * 24 * 60 * 60 + 8 ,, 0 >> , test-leapstate2 ) end test ]]"
\end{statements}
\subsection{Test of conversion from Unix time to Logiweb time}
\begin{statements}
\item "[[ etst !"GRD-1970-01-01.UTC:00:00:00" ; newline
lgc-lgt2grdutc2v ( lgc-unix2lgt ( << 0 ,, 0 >> , test-leapstate2 ) , test-leapstate2 ) end test ]]"
\item "[[ etst !"GRD-1970-01-01.UTC:00:00:00.000000" ; newline
lgc-lgt2grdutc2v ( lgc-unix2lgt ( << 0 ,, 6 >> , test-leapstate2 ) , test-leapstate2 ) end test ]]"
\item "[[ etst !"GRD-1998-12-31.UTC:23:59:58.0" ; newline
lgc-lgt2grdutc2v ( lgc-unix2lgt ( << 9151487980 ,, 1 >> , test-leapstate2 ) , test-leapstate2 ) end test ]]"
\item "[[ etst !"GRD-1998-12-31.UTC:23:59:58.9" ; newline
lgc-lgt2grdutc2v ( lgc-unix2lgt ( << 9151487989 ,, 1 >> , test-leapstate2 ) , test-leapstate2 ) end test ]]"
\item "[[ etst !"GRD-1998-12-31.UTC:23:59:59.0" ; newline
lgc-lgt2grdutc2v ( lgc-unix2lgt ( << 9151487990 ,, 1 >> , test-leapstate2 ) , test-leapstate2 ) end test ]]"
\item "[[ etst !"GRD-1998-12-31.UTC:23:59:59.2" ; newline
lgc-lgt2grdutc2v ( lgc-unix2lgt ( << 9151487991 ,, 1 >> , test-leapstate2 ) , test-leapstate2 ) end test ]]"
\item "[[ etst !"GRD-1998-12-31.UTC:23:59:59.4" ; newline
lgc-lgt2grdutc2v ( lgc-unix2lgt ( << 9151487992 ,, 1 >> , test-leapstate2 ) , test-leapstate2 ) end test ]]"
\item "[[ etst !"GRD-1998-12-31.UTC:23:59:59.6" ; newline
lgc-lgt2grdutc2v ( lgc-unix2lgt ( << 9151487993 ,, 1 >> , test-leapstate2 ) , test-leapstate2 ) end test ]]"
\item "[[ etst !"GRD-1998-12-31.UTC:23:59:59.8" ; newline
lgc-lgt2grdutc2v ( lgc-unix2lgt ( << 9151487994 ,, 1 >> , test-leapstate2 ) , test-leapstate2 ) end test ]]"
\item "[[ etst !"GRD-1998-12-31.UTC:23:59:60.0" ; newline
lgc-lgt2grdutc2v ( lgc-unix2lgt ( << 9151487995 ,, 1 >> , test-leapstate2 ) , test-leapstate2 ) end test ]]"
\item "[[ etst !"GRD-1998-12-31.UTC:23:59:60.2" ; newline
lgc-lgt2grdutc2v ( lgc-unix2lgt ( << 9151487996 ,, 1 >> , test-leapstate2 ) , test-leapstate2 ) end test ]]"
\item "[[ etst !"GRD-1998-12-31.UTC:23:59:60.4" ; newline
lgc-lgt2grdutc2v ( lgc-unix2lgt ( << 9151487997 ,, 1 >> , test-leapstate2 ) , test-leapstate2 ) end test ]]"
\item "[[ etst !"GRD-1998-12-31.UTC:23:59:60.6" ; newline
lgc-lgt2grdutc2v ( lgc-unix2lgt ( << 9151487998 ,, 1 >> , test-leapstate2 ) , test-leapstate2 ) end test ]]"
\item "[[ etst !"GRD-1998-12-31.UTC:23:59:60.8" ; newline
lgc-lgt2grdutc2v ( lgc-unix2lgt ( << 9151487999 ,, 1 >> , test-leapstate2 ) , test-leapstate2 ) end test ]]"
\item "[[ etst !"GRD-1999-01-01.UTC:00:00:00.0" ; newline
lgc-lgt2grdutc2v ( lgc-unix2lgt ( << 9151488000 ,, 1 >> , test-leapstate2 ) , test-leapstate2 ) end test ]]"
\item "[[ late define test-leapstate3 as newline
lgc-process-leap ( true [[ << !"parameters" ,, !"leap" >> => << !"GRD-1970-01-01-1" >> ]] ) end define ]]"
\item "[[ etst !"GRD-1970-01-01.UTC:00:00:00" ; newline
lgc-lgt2grdutc2v ( lgc-unix2lgt ( << 0 ,, 0 >> , test-leapstate3 ) , test-leapstate2 ) end test ]]"
\item "[[ etst !"GRD-1970-01-01.UTC:00:00:00.000000" ; newline
lgc-lgt2grdutc2v ( lgc-unix2lgt ( << 0 ,, 6 >> , test-leapstate3 ) , test-leapstate2 ) end test ]]"
\item "[[ etst !"GRD-1970-01-01.UTC:23:59:57.0" ; newline
lgc-lgt2grdutc2v ( lgc-unix2lgt ( << 863970 ,, 1 >> , test-leapstate3 ) , test-leapstate3 ) end test ]]"
\item "[[ etst !"GRD-1970-01-01.UTC:23:59:57.9" ; newline
lgc-lgt2grdutc2v ( lgc-unix2lgt ( << 863979 ,, 1 >> , test-leapstate3 ) , test-leapstate3 ) end test ]]"
\item "[[ etst !"GRD-1970-01-01.UTC:23:59:58.00" ; newline
lgc-lgt2grdutc2v ( lgc-unix2lgt ( << 863980 ,, 1 >> , test-leapstate3 ) , test-leapstate3 ) end test ]]"
\item "[[ etst !"GRD-1970-01-01.UTC:23:59:58.05" ; newline
lgc-lgt2grdutc2v ( lgc-unix2lgt ( << 863981 ,, 1 >> , test-leapstate3 ) , test-leapstate3 ) end test ]]"
\item "[[ etst !"GRD-1970-01-01.UTC:23:59:58.45" ; newline
lgc-lgt2grdutc2v ( lgc-unix2lgt ( << 863989 ,, 1 >> , test-leapstate3 ) , test-leapstate3 ) end test ]]"
\item "[[ etst !"GRD-1970-01-01.UTC:23:59:58.50" ; newline
lgc-lgt2grdutc2v ( lgc-unix2lgt ( << 863990 ,, 1 >> , test-leapstate3 ) , test-leapstate3 ) end test ]]"
\item "[[ etst !"GRD-1970-01-01.UTC:23:59:58.95" ; newline
lgc-lgt2grdutc2v ( lgc-unix2lgt ( << 863999 ,, 1 >> , test-leapstate3 ) , test-leapstate3 ) end test ]]"
\item "[[ etst !"GRD-1970-01-02.UTC:00:00:00.0" ; newline
lgc-lgt2grdutc2v ( lgc-unix2lgt ( << 864000 ,, 1 >> , test-leapstate3 ) , test-leapstate3 ) end test ]]"
\end{statements}
\bibliography{./page}
"
appendix "
\title{Compiler - appendix}
\author{Klaus Grue}
\maketitle
\tableofcontents
\section{\TeX\ definitions}
\begin{statements}
\item "[[ tex show define late define x as y end define as "
[ "[ texshow x end texshow ]"
\stackrel{\bullet\bullet}{=} "[ y ]"
]" end define ]]"
\end{statements}
\section{Icon definitions}
\begin{statements}
\item "[[ late define lgc-logiweb.png as include (
""#logiweb.png" ) end define ]]"
\item "[[ late define lgc-logiweb.ico as include (
""#logiweb.ico" ) end define ]]"
\item "[[ late define lgc-logiweb.eps as include (
""#logiweb.eps" ) end define ]]"
\end{statements}
"
end page