diff options
Diffstat (limited to 'Master/texmf-dist/source/latex/tcldoc/dtxload.dtx')
-rw-r--r-- | Master/texmf-dist/source/latex/tcldoc/dtxload.dtx | 1256 |
1 files changed, 1256 insertions, 0 deletions
diff --git a/Master/texmf-dist/source/latex/tcldoc/dtxload.dtx b/Master/texmf-dist/source/latex/tcldoc/dtxload.dtx new file mode 100644 index 00000000000..f68a0ca3a8b --- /dev/null +++ b/Master/texmf-dist/source/latex/tcldoc/dtxload.dtx @@ -0,0 +1,1256 @@ +% +% +% \iffalse Meta-comment +% +%<*driver> +\documentclass{tcldoc} + +\DeclareRobustCommand{\package}[1]{\textsf{#1}} +\newcommand{\Alpha}{\textit{Alpha}} +\newcommand{\AlphaTk}{\textit{Alphatk}} + +\PageIndex +\setcounter{IndexColumns}{2} +\CodelineNumbered +\RecordChanges +\IndexPrologue{\section*{Index}\markboth{Index}{Index}% + Numbers written in italic refer to the page where + the corresponding entry is described; numbers underlined refer + to the definition; numbers in roman refer to the pages where + the entry is used.} +\expandafter\def \expandafter\GlossaryParms \expandafter{\GlossaryParms + \hbadness=10000\emergencystretch=2cm} + +\begin{document} +\DocInput{dtxload.dtx} +\PrintChanges +\PrintIndex +\end{document} +%</driver> +% \fi +% +% \title{\package{dtxload} --- loading \Tcllogo\ code directly from +% \texttt{.dtx} source files} +% \author{Lars Hellstr\"om^^A +% \thanks{E-mail: \texttt{Lars.Hellstrom@math.umu.se}}} +% \date{December 20, 2000} +% \maketitle +% +% \begin{abstract} +% \package{dtxload} is a feature for the text editor \Alpha. It +% implements a command for loading a piece of \Tcllogo\ code directly +% from a \texttt{.dtx} file. +% \end{abstract} +% +% \tableofcontents +% +% \section{Usage} +% +% \subsection{About \Alpha} +% +% About \Alpha\ itself, the \Alpha~v\,7.3 manual says the following: +% \begin{quotation} +% \Alpha\ is a very powerful, multi-purpose text editor. +% \Alpha\ is multi-modal, which means it switches between different +% modes depending on which type of document you are editing. Everybody +% who has used Emacs knows about this concept. The idea with different +% modes is to change \Alpha's behaviour depending on which kind of text +% you're editing. For example if you're programming in C$^{++}$ or +% editing a \LaTeX\ document you want different features. +% +% \Alpha\ is very customizable. Much of \Alpha's functionality is +% implemented in `packages' or `extensions'. [\dots] +% \Alpha\ uses Dr.~Ousterhout's \emph{Tool Command Language} (\Tcllogo) as +% an extension language. Many of the functions bound to keystrokes, +% as well as many of the functions in the menus, are written in \Tcllogo. +% All the `packages' or `extensions' just mentioned are all written +% in \Tcllogo. Anybody who wants can write new packages for \Alpha! +% +% Of course, you don't have to learn \Tcllogo\ to use \Alpha. You can +% have lots of use of \Alpha\ without knowing anything about \Tcllogo. +% But if you want to become a real power user, you should consider +% trying it out. Then the possibilities of customizing \Alpha\ are +% unlimited. +% \end{quotation} +% Originally \Alpha\ only existed on the \mbox{MacOS} platform, but +% recently the program was rewritten in Tk.\footnote{Tk is an extension of +% \Tcllogo\ which includes GUI support.} In that form (which is called +% \AlphaTk), \Alpha\ can be run on any platform for which there exists +% a port of Tk! +% +% \medskip +% +% There are also some \Alpha-related URLs which should be mentioned. +% \Alpha's official home page is~\cite{AlphaHome}, but it is currently +% rather out-of-date. More recent information can be found on +% e.g.~\cite{DocScriptingAlpha}, which also has links to many other +% \Alpha-related sites. The home page for \AlphaTk\ is~\cite{Alphatk}. +% +% +% +% \subsection{Installation} +% +% To install \package{dtxload} as an \Alpha\ feature, you first \LaTeX\ +% the file \texttt{dtxload.ins}---this will generate a file +% \texttt{dtxload.tcl}, which contains the \Tcllogo\ code for the +% package in a form that \Alpha\ understands. Then follow the normal +% procedure for installing an \Alpha\ package, i.e., open +% \texttt{dtxload.tcl}\footnote{Which should be somewhere outside +% \Alpha's directory subtree.} in \Alpha---this will trigger the +% automatic installation dialog for the package and you proceed as +% usual. +% +% +% \subsection{Loading \Tcllogo\ code} +% \label{Ssec:Loading} +% +% To load (evaluate) a piece of \Tcllogo\ code in a \texttt{.dtx} +% document, simply select that code and press \marginpar{Loading code: +% \textsf{Cmd--L}}Command--L. The commands are evaluated one by one and +% the result of each command will be shown at the current log destination +% (by default the status line, but that can be changed). In case one of +% the commands return with an error, that error will be shown as well, +% evaluation of further commands is cancelled, and the insertion point is +% moved to the beginning of the erroneous command. Furthermore the mark +% will be left at the end of the selection, so that the part of the +% original selection which has not yet been successfully evaluated can +% easily be reselected using the \textsf{Mark Hilite} command. +% +% Loading \Tcllogo\ code does not necessary mean all text in the +% selection will be evaluated; only extractable code will be passed on +% to the \Tcllogo\ interpreter. There are several factors which affect +% whether a piece of text is extractable code or not, but the foremost +% is that text on \package{docstrip} comment lines (i.e., lines which +% begin by a `|%|' that is not immediately followed by a `|<|') is +% \emph{not} extractable code. Secondly, if the selection contains a +% \package{docstrip} guard line which marks the start of a module then +% \package{dtxload} will ask you whether you want the code in that +% module to be loaded. If you answer no, it naturally won't be. +% +% Thus far the factors which may restrict which text is extractable +% code correspond exactly to what the \package{docstrip} +% program~\cite{docstrip} might do when generating a file of extracted +% code, but \package{dtxload} has two additional mechanisms which may +% categorize text as ``not extractable code''. Both are however +% controlled by preferences in the \Alpha\ \TeX\ mode, so they can be +% switched off if you don't like them. +% The first is the \describestring[var.][]{onlyLoadDtxFiles} +% \textsf{Only Load Dtx Files} preference, which when on restricts +% loading to files with suffix \texttt{.dtx}. For \TeX\ files with other +% suffixes I suspect that Command--L is pressed more often by accident +% than because there actually is some \Tcllogo\ code that should be +% loaded \textellipsis\space The second mechanism is controlled by the +% \describestring[var.][]{onlyLoadTclEnvs} \textsf{Only Load Tcl Envs} +% preference, which when on adds the restriction that extractable code +% must appear inside a \texttt{tcl} or \texttt{tcl*} environment +% (defined by the \package{tcldoc} \LaTeX\ package~\cite{tcldoc}). This +% is primarily useful when a \texttt{.dtx} file contains both \Tcllogo\ +% code in \texttt{tcl} environments and code in some other language in +% e.g.\ \texttt{macrocode} environments, since if the preference had been +% off then \package{dtxload} would stop at the \package{docstrip} guard +% that must appear at the beginning of each non-\Tcllogo\ code section +% and ask whether the code should be included, whereas if it is on then +% \package{dtxload} doesn't even have to ask whether the code in the +% \texttt{macrocode} environments should be loaded. +% +% \medskip +% +% As an example of how the load \Tcllogo\ code command works, consider +% the following code: +% \DontCheckModules\iffalse +%<*example> +%<<EXAMPLE +% \fi\begin{macrocode} +% Text text text text text +% \begin{tcl} +set a A +%<*andB> +append a B +%</andB> +append a C +%<*andD> +append a D +%</andB> +expr $a +string length $a +% \end{tcl} +% text text text. +% \end{macrocode}\iffalse +%EXAMPLE +%</example> +% \fi \CheckModules +% If lines 1--7 above are selected and Command--L is pressed, +% \package{dtxload} will display the message ``\texttt{Tcl eval OK}'' on +% the status line and the following information will have been added to +% the log.\footnote{This shows the window log format, but there are +% other formats. Furthermore the `\texttt{<}' and `\texttt{>}' will +% actually be guillemets in \Alpha\ (but not in \AlphaTk).} +% \begin{trivlist}\item\small +% |<dtxload> set a A|\\ +% |A|\\[\medskipamount] +% |<dtxload> append a B|\\ +% |AB|\\[\medskipamount] +% |<dtxload> append a C|\\ +% |ABC| +% \end{trivlist} +% The |<dtxload>| lines show the commands that were evaluated and the +% lines after these contain the respective results of these commands. +% Before the |append a B| command is evaluated, \package{dtxload} will +% stop and ask +% \begin{quote} +% \textsf{Should \textless andB\textgreater\ modules be included?} +% \end{quote} +% In the case above the answer given was `Yes'; if it had been `No' then +% the |append a B| command would neither be logged nor evaluated, and +% the result of the |append a C| command would have been |AC|. +% +% If one continues by selecting lines 9--12 and pressing Command--L +% then \package{dtxload} will display the message ``\texttt{Tcl eval +% error}'' on the status line, the insertion point will be moved to the +% beginning of line 11, and the following information will be found in +% the log: +% \begin{trivlist}\item\small +% |<dtxload> append a D|\\ +% |ABCD|\\[\medskipamount] +% |<dtxload> expr $a|\\ +% |Error: syntax error in expression "ABCD"| +% \end{trivlist} +% If then line 11 is changed to |expr {$a}| and lines 11--12 are again +% selected (e.g. using \textsf{Mark Hilite}) and loaded then +% \package{dtxload} will log: +% \begin{trivlist}\item\small +% |<dtxload> expr {$a}|\\ +% |ABCD|\\[\medskipamount] +% |<dtxload> string length $a|\\ +% |4| +% \end{trivlist} +% +% Finally, if lines 3--12 are selected and loaded then these are the +% questions \package{dtxload} will ask: +% \begin{itemize} +% \item \textsf{Should \textless andB\textgreater\ modules be included?} +% (Possible answers are `Yes' and `No'.) +% \item \textsf{Should \textless andD\textgreater\ modules be included?} +% (Possible answers are `Yes' and `No'.) +% \item \textsf{Module nesting error: \textless*andD\textgreater\ +% module ended by \textless/andB\textgreater. For which +% guards should the positions be pushed?} +% (Possible answers are `None', `Start', `End', and `Both'.) +% \end{itemize} +% In the last case, \package{dtxload} has noticed that the guard +% expressions that began (\textsf{andD}) and ended (\textsf{andB}) a +% module are not equal (which is an error). Answering `Both' here will +% push the positions of (the beginning of) both guard lines (8 and 10) onto +% the bookmark stack so that they can easily be jumped to and corrected +% later, but \package{dtxload} will continue evaluating the rest of the +% selection after you answer. +% +% +% \subsection{Selecting log destination} +% +% At startup, \package{dtxload} is logging results on the status line, +% i.e., after a command has been evaluated it either displays the message +% \begin{quote} +% \texttt{Tcl eval OK:\ }\meta{result} +% \end{quote} +% or +% \begin{quote} +% \texttt{Tcl eval error:\ }\meta{error message} +% \end{quote} +% depending on whether the command returned an error or not. The +% evaluation log can however be sent to a window instead. +% +% To set the destination for the log, one uses the procedure +% \describestring[proc][dtxload]{logdest}|dtxload::logdest|. To set the +% log destination to the window \word{win}, one gives the command +% \begin{quote} +% |dtxload::logdest window| \word{win} +% \end{quote} +% When the log destination is a window, the log shows the commands +% that were evaluated as well as their result. If there is no window +% named \word{win} then it is created. A slight +% variation on this is the command +% \begin{quote} +% |dtxload::logdest shell| \word{win} +% \end{quote} +% which makes the \word{win} window a \Alpha\ \Tcllogo\ shell window if +% it has to create it. When the log destination is a shell window, the +% commands that are executed will be saved on the history list as well +% as inserted in the window. +% +% A short form which may be convenient is +% \begin{quote} +% |dtxload::logdest here| +% \end{quote} +% which simply makes the current window the log destination. Finally, +% the log destination is reset to the status line after the command +% \begin{quote} +% |dtxload::logdest status| +% \end{quote} +% Evaluation will also be logged on the status line if the log +% destination window is closed. +% +% +% \subsection{Other features and surprises} +% +% If nothing is selected when Command--L is pressed, \package{dtxload} +% will continue as if all text was selected. +% +% Like \Alpha's \Tcllogo\ shell, \package{dtxload} can send the commands +% to evaluate to a remote \Tcllogo\ interpreter rather than \Alpha's +% built-in interpreter. In both cases the choice of target interpreter is +% controlled by the global \Alpha\ variable +% \describestring[var.][]{evaluateRemotely}|evaluateRemotely|. The +% easiest way of changing this variable is via the \Tcllogo-tk submenu +% of \Alpha's \Tcllogo\ menu. +% +% If the \textsf{Only Load Tcl Envs} preference is on and lines 4--6 in +% the code example of Subsection~\ref{Ssec:Loading} are loaded then +% \package{dtxload} will simply display the message +% \begin{quote} +% \texttt{No extractable code found.} +% \end{quote} +% The reason for this is that the first character of the selection is +% a |%|; when the first character in the interval to load is a percent +% character then \package{dtxload} will guess that this position is not +% in a \texttt{tcl} or \texttt{tcl*} environment, and therefore it won't +% try to extract any code until it has seen a comment line that contains +% |\begin{tcl}| or |\begin{tcl*}|. You can always avoid making a +% \package{docstrip} guard the first thing in the selection to load. +% +% +% +% \StopEventually{ +% \begin{thebibliography}{9} +% \bibitem{Alphatk} +% Vince Darley: \textit{Alphatk} ({\small WWW} page), +% \texttt{http}://\texttt{www.santafe.edu}\slash +% \texttt{\~{}vince}\slash \texttt{alphatk}\slash +% \texttt{about.html} +% \bibitem{DocScriptingAlpha} +% Donavan Hall: \textit{Alpha} ({\small WWW} page), +% \texttt{http}://\texttt{magnet.fsu.edu}\slash +% \texttt{\~{}hall}\slash\texttt{docscripting}\slash +% \texttt{alpha}/. +% \bibitem{tcldoc} +% Lars Hellstr\"om: +% \textit{The \package{tcldoc} package and class}, +% \texttt{ftp}:/\slash\texttt{ftp.tex.ac.uk}\slash +% \texttt{tex-archive}\slash \texttt{macros}\slash +% \texttt{latex}\slash \texttt{contrib}\slash +% \texttt{supported}\slash \texttt{tcldoc.dtx}. +% \bibitem{AlphaHome} +% Pete Keleher: \textit{Alpha Home Page} ({\small WWW} page), +% \texttt{http}://\texttt{alpha.olm.net}/. +% \bibitem{docstrip} +% Frank Mittelbach, Denys Duchier, Johannes Braams, Marcin +% Woli\'nski, and Mark Wooding: \textit{The \textsf{DocStrip} +% program}, The \LaTeX3 Project; +% \texttt{ftp}:/\slash\texttt{ftp.tex.ac.uk}\slash +% \texttt{tex-archive}\slash \texttt{macros}\slash +% \texttt{latex}\slash \texttt{base}\slash \texttt{docstrip.dtx}. +% \end{thebibliography} +% } +% +% \section{Implementation} +% +% +% \subsection{Interface to \Alpha} +% +% The package begins with an |alpha::feature| command to declare itself +% to \Alpha. As it is hardly useful outside the \texttt{TeX} mode, it +% declares itself as being specific to that mode. +% \begin{tcl} +%<*tcl> +alpha::feature dtxload 1.1 {TeX} { +% \end{tcl} +% +% \begin{variable}{onlyLoadDtxFiles} +% \begin{variable}{onlyLoadTclEnvs} +% Next comes the initialization script for the feature, which +% declares the two preference variables used by \package{dtxload}. +% Both variables are flags. |onlyLoadDtxFiles| is |1| if loading of +% code is restricted to files with suffix \texttt{.dtx} and |0| if +% there is no restriction. |onlyLoadTclEnvs| is |1| if only code +% surrounded by a \texttt{tcl} or \texttt{tcl*} environment is loaded +% and |0| if there is no such restriction. Both preferences are by +% default |1|. +% \begin{tcl} + # When set, the Load Tcl Code command will be disabled in + # TeX files whose suffix is not dtx. + newPref flag onlyLoadDtxFiles 1 TeX + # When set, the Load Tcl Code command will only extract code + # from code lines in tcl and tcl* environments. + newPref flag onlyLoadTclEnvs 1 TeX +% \end{tcl} +% \end{variable}\end{variable} +% \begin{variable}{loadTclCode} +% The initialization script also declares a keybinding preference (to +% the |dtxload::load| procedure), which is used for +% \package{dtxload}'s only command. The default value is Command--L +% and the binding is specific to the \texttt{TeX} mode. +% \begin{tcl} + # This is the key binding for dtxload's Load Tcl Code command. + newPref binding loadTclCode <O/L TeX "" dtxload::load TeX +% \end{tcl} +% \end{variable} +% +% After the initialization script comes the activation and deactivation +% scripts (which, if I understood things correctly, are called when +% switching between modes), but these need not contain anything for +% \package{dtxload}. Instead the arguments of |alpha::feature| continue +% with the uninstallation procedure +% \begin{tcl} +} "" "" uninstall {this-file} \ +% \end{tcl} +% the maintainer specification (|\x9a| is \texttt{\"{o}}) +% \begin{tcl} + maintainer {"Lars Hellstr\x9am" Lars.Hellstrom@math.umu.se} \ +% \end{tcl} +% and a short help text +% \begin{tcl} + help {The dtxload package implements a command for loading + Tcl code that is embedded in a .dtx file without docstripping + the file first. For more help, LaTeX the file dtxload.dtx.} +% \end{tcl} +% It is possible that the help should rather be |file dtxload.dtx|, but +% then this file would have to be installed somewhere that \Alpha\ +% would find it. I haven't studied the matter. +% +% Next the |dtxload| namespace is declared to \Tcllogo. +% \begin{tcl} +namespace eval dtxload {} +% \end{tcl} +% \setnamespace{dtxload} +% +% +% \subsection{Code extraction} +% +% +% The code extraction algorithm of \package{dtxload} lack a few of the +% features of proper \package{docstrip}, but I don't think any of them +% are essential for the kind of work \package{dtxload} is designed for. +% The differences to \package{docstrip} are: +% \begin{itemize} +% \item The values of guard expressions are not determined from a +% base set of options, instead the user is inquired for the value of +% each guard expression. +% \item Extraction of \package{docstrip} verbatim mode lines is not +% supported, but it isn't needed for \Tcllogo\ code either. +% \item Metacomments aren't extracted, but they hardly contain any +% code of interest to the \Tcllogo\ interpreter anyway. +% \item \package{dtxload} may scan the comment lines for certain +% known markup elements (beginning and end of \texttt{tcl} and +% \texttt{tcl*} environments) and choose to enable or disable +% extraction based on that. +% \end{itemize} +% \package{dtxload} does however act on the guard modifiers and it also +% looks for errors in the syntax of guards. +% +% \begin{variable}{module_stack} +% \begin{variable}{next_module_idx} +% The |dtxload::module_stack| variable is keeps track of how modules +% are nested around the currently considered line. The variable is a +% stack implemented as a list in which the last element corresponds to +% the innermost module. Each element in this list is itself a list +% with the structure +% \begin{quote} +% \word{expression} \word{start pos.} \word{previous}\regopt +% \end{quote} +% The \word{expression} is the guard expression, as a string. The +% \word{start pos.} is the position in the file where the module +% started. Finally, the \word{previous} is a boolean value which +% records the inclusion status (value of |dtxload::module_included|) +% that was in force before the module was entered. +% +% The |dtxload::next_module_idx| variable holds the index in the +% |dtxload:|\penalty\hyphenpenalty|:module_stack| list of the next +% module whose expression has not yet been evaluated; it is equal to +% |llength ${dtxload::module_stack}| if all module expressions have +% been evaluated. Those elements in the |dtxload::module_stack| list +% which have index $\geq$ |dtxload::next_module_idx| do not have a +% \word{previous} element. +% \end{variable}\end{variable} +% +% \begin{arrayvar}{known_expressions}[expression] +% The |dtxload::known_expressions| array records the values of all +% known expressions, i.e., those expressions about whose values the +% user has been inquired. The entries are either |0| (for ``don't +% include'') or |1| (for ``include''). +% \end{arrayvar} +% +% \begin{proc}{eval_guard} +% The |dtxload::eval_guard| procedure takes the string that is a guard +% expression as argument. It returns |1| if the expression evaluates +% to true and |0| if it evaluates to false. +% +% If the expression has an entry in the |dtxload::known_expressions| +% array then the |dtxload::eval_guard| procedure returns that value, +% and if it does not the user is inquired for the value. +% \begin{tcl} +proc dtxload::eval_guard {e} { + global dtxload::known_expressions + if {![info exists dtxload::known_expressions($e)]} then { + set dtxload::known_expressions($e)\ + [expr {"yes" ==\ + "[askyesno "Should <$e> modules be included?"]"}] + } + set dtxload::known_expressions($e) +} +% \end{tcl} +% \end{proc} +% +% \begin{proc}{push_module} +% The |dtxload::push_module| procedure pushes a block module onto the +% module stack, but it does not modify the value of +% |dtxload::module_included|. The syntax of |dtxload::push_module| is +% \begin{quote} +% |dtxload::push_module| \word{expression} \word{position} +% \end{quote} +% where \word{expression} is the expression string and +% \word{position} is the position where the guard begins. +% \begin{tcl} +proc dtxload::push_module {e p} { + global dtxload::module_stack + lappend dtxload::module_stack [list $e $p] +} +% \end{tcl} +% \end{proc} +% +% \begin{proc}{pop_module} +% The |dtxload::pop_module| procedure pops a block module off the +% module stack and checks that the modules are properly nested. Its +% syntax is +% \begin{quote} +% |dtxload::pop_module| \word{expression} \word{position} +% \end{quote} +% where \word{expression} is the expression string of the module to +% pop and \word{position} is the position where the (end of module) +% guard begins. +% +% If the module nesting is incorrect then the user is presented with +% a dialog with buttons for pushing the positions of the incorrect +% modules. It is not an error if the module stack is empty, since it +% is perfectly reasonable that the beginning of the code to evaluate +% is in the middle of a module. +% \begin{tcl} +proc dtxload::pop_module {e p} { + global dtxload::module_stack dtxload::module_included\ + dtxload::next_module_idx + set len [llength ${dtxload::module_stack}] + if {$len==0} then {return} + set L [lindex ${dtxload::module_stack} [expr {$len-1}]] + if {[lindex $L 0] != $e} then { + switch [buttonAlert "Module nesting error: <*[lindex $L 0]>\ + module ended by </$e>. For which guards should the positions\ + be pushed?" None Start End Both]\ + { + Start {dtxload::push_bookmarks [lindex $L 1]} + End {dtxload::push_bookmarks $p} + Both {dtxload::push_bookmarks [lindex $L 1] $p} + } + } + set dtxload::module_stack\ + [lreplace ${dtxload::module_stack} end end] + if {$len>=${dtxload::next_module_idx}} then { + incr dtxload::next_module_idx -1 + } + if {[llength $L]>2} then { + set dtxload::module_included [lindex $L 2] + } +} +% \end{tcl} +% \end{proc} +% +% \begin{proc}{push_bookmarks} +% The |dtxload::push_bookmarks| procedure pushes one or several +% positions in the current window onto the bookmarks stack. The +% arguments are the positions to push. +% \begin{tcl} +proc dtxload::push_bookmarks {args} { + global markStack markName + foreach pos $args { + set name mark$markName + incr markName + createTMark $name $pos + set fileName [win::Current] + set markStack [linsert $markStack 0 [list $fileName $name]] + } +} +% \end{tcl} +% \end{proc} +% +% \begin{variable}{module_included} +% \begin{variable}{in_tcl_env} +% The |dtxload::module_included| variable keeps track of whether the +% current position is in an included module. It is |1| if the current +% position should be included and |0| if it shouldn't. +% The |dtxload::in_tcl_env| variable is used for keeping track of +% what markup context surrounds the currently considered line in the +% file. It is set to |1| when a |\begin{tcl}| or |\begin{tcl*}| is +% scanned, and set to |0| when a |\end{tcl}| or |\end{tcl*}| is scanned. +% \end{variable}\end{variable} +% +% \begin{proc}{update_included} +% Simply pushing a module onto the module stack does not update the +% |dtxload:|\penalty\hyphenpenalty|:module_included| variable; that is +% instead done by the |dtxload::update_included| procedure, which +% should be called right before the value of |dtxload::module_included| +% is used. The reason for this arrangement is that it avoids evaluating +% guard expressions whose values do not matter. +% +% |dtxload::update_included| has no arguments. +% \begin{tcl} +proc dtxload::update_included {} { + global dtxload::module_included dtxload::module_stack\ + dtxload::next_module_idx + while {${dtxload::module_included} &&\ + ${dtxload::next_module_idx}<[llength ${dtxload::module_stack}]}\ + { + set L [lindex ${dtxload::module_stack} ${dtxload::next_module_idx}] + lappend L ${dtxload::module_included} + set dtxload::module_stack [lreplace ${dtxload::module_stack}\ + ${dtxload::next_module_idx} ${dtxload::next_module_idx} $L] + incr dtxload::next_module_idx + set dtxload::module_included [dtxload::eval_guard [lindex $L 0]] + } +} +% \end{tcl} +% \end{proc} +% +% +% \begin{proc}{extract_line} +% The |dtxload::extract_line| procedure steps forward in the file one +% line at a time until it has found a codeline that can be extracted or +% it reaches the end of the part of the file from which code is to be +% extracted. Its syntax is +% \begin{quote} +% |dtxload::extract_line| \word{startpos} \word{endpos} +% \end{quote} +% Here \word{startpos} is the position at which it should start to +% look for an extractable code line, and \word{endpos} is the +% position at which the search should stop if no such line is found. +% \word{startpos} is assumed to be at the beginning of a line (for +% identification of code, comment, and guard line purposes) but no +% test for this is made. +% +% The return value of |dtxload::extract_line| is a list with the +% structure +% \begin{quote} +% \word{nextpos} \word{line}\regopt\ \word{foundpos}\regopt +% \end{quote} +% \word{nextpos} is the first position in the file after the part that +% was searched. \word{line} is the extracted codeline and +% \word{foundpos} is the position at which it began, but these two items +% are missing if there was no more codeline. +% +% The overall structure of |dtxload::extract_line| is a loop +% which steps through the lines until it reaches the |endpos|. When a +% line to extract is actually found the procedure returns from within +% the loop using the |return| command. The local variable |pos| +% stores the current position, and |pos2| stores the position of the +% beginning of the next line. +% \begin{tcl} +proc dtxload::extract_line {startpos endpos} { + global dtxload::in_tcl_env onlyLoadTclEnvs dtxload::module_included + for {set pos $startpos} {[pos::compare {$pos<$endpos}]}\ + {set pos $pos2} { +% \end{tcl} +% Inside the loop, the next line is extracted and stored in the local +% variable |line|. Then |switch| is used for classifying the line and +% taking appropriate actions. +% \begin{tcl} + set pos2 [nextLineStart $pos] + if {$pos2>$endpos} then {set pos2 $endpos} + set line [getText $pos $pos2] + switch -regexp -- $line { +% \end{tcl} +% \changes{v\,1.0}{2000/10/15}{Corrected regular expressions; only +% percents on the beginning of a line are significant. (LH)} +% \changes{v\,1.1}{2000/12/03}{Guard lines will affect code extraction. +% (LH)} +% \changes{v\,1.1}{2000/12/18}{The code extraction procedures can +% now handle \package{docstrip} verbatim mode guards, provided +% they do not appear in a block of extractable code. (LH)} +% Lines that begin with |%<<| are \package{docstrip} guard lines that +% start verbatim mode. Extracting code from such lines is currently +% not supported by \package{dtxload}, so if such a guard is ever +% encountered the extraction is stopped, but \package{dtxload} can +% skip verbatim sections from which it shouldn't extract code. +% \begin{tcl} + {^%<<} { + if {!$onlyLoadTclEnvs || ${dtxload::in_tcl_env}} then { + dtxload::update_included + if {${dtxload::module_included}} then { + alertnote "docstrip verbatim mode guard line\ + encountered. Verbatim mode extraction is\ + currently not supported, so I stop here." + goto $pos + return [list $endpos] + } + } +% \end{tcl} +% The part of the guard line which comes after the |%<<| is called an +% end-tag. Verbatim mode ends with the next line which consists of a +% percent character followed by this end-tag, and therefore all lines +% up to and including that one are skipped. A technical matter here +% is that spaces at the end of the line does not count (\TeX\ cannot +% see them), but spaces inside the end-tag do count! +% \begin{tcl} + set endtag %[string trimright [string trimright\ + [string range $line 3 end] \n\r] \ ] + while {[string trimright [string trimright $line \n\r] \ ]\ + != $endtag} { + set pos $pos2 + if {[pos::compare {$pos>=$endpos}]}\ + then {return [list $pos]} + set pos2 [nextLineStart $pos] + if {$pos2>$endpos} then {set pos2 $endpos} + set line [getText $pos $pos2] + } + } +% \end{tcl} +% Lines that begin with |%<*| or |%</| are \package{docstrip} guard +% lines that delimit block modules, but they do not contain any code +% themselves. The module is pushed onto or popped off the module stack +% when the guard line is encountered, but evaluating it is deferred +% until the first extractable code line. +% \begin{tcl} + {^%<[*/]} { + if {![regexp {^%<(\*|/)([^>]+)>} $line foo modifier\ + expression]}\ + then { + if {"[askyesno "Malformed guard line \"$line\"\ + encountered. Push position?"]"=="yes"}\ +% + then {dtxload::push_bookmarks $pos} + } elseif {$modifier=="*"} then { + dtxload::push_module $expression $pos + } else { + dtxload::pop_module $expression $pos + } + } +% \end{tcl} +% +% Other lines that begin with |%<| are also \package{docstrip} +% guard lines, but in these cases the lines (i) contain some code +% and (ii) the guard only affect the code on that line. There must be +% a |>| somewhere on the line, and the part of the line after the |>| +% is code that \package{docstrip} can extract. +% \begin{tcl} + {^%<} { + if {![regexp {^%<(-|\+|)([^>]+)>(.*)$} $line\ + foo modifier expression code]}\ + then { + if {"[askyesno "Malformed guard line \"$line\"\ + encountered. Push position?"]"=="yes"}\ +% + then {dtxload::push_bookmarks $pos} + } elseif {!$onlyLoadTclEnvs || ${dtxload::in_tcl_env}} then { + dtxload::update_included + if {${dtxload::module_included}} then { + if {[dtxload::eval_guard $expression] !=\ + ($modifier=="-")}\ + then {return [list $pos2 $code $pos]} + } + } + } +% \end{tcl} +% Remaining lines that begin with |%| are considered comment lines, +% but they could technically be metacomment lines (begin with |%%|) or +% a line that \package{docstrip} would process in verbatim mode. In +% both these cases \package{docstrip} would copy the line, but as there +% is little point in using these \package{docstrip} features for lines +% that the \Tcllogo\ interpreter would miss, \package{dtxload} ignores +% those subtleties. +% \begin{tcl} + {^%} { + while {[regexp -indices {\\(begin|end) *{tcl\*?}} $line\ + match type]} { + set dtxload::in_tcl_env\ + [expr {"[eval string range \$line $type]"=="begin"}] + set line [string range $line\ + [expr {[lindex $match 1]+1}] end] + } + } +% \end{tcl} +% Finally, all lines that do not begin with a |%| are code lines. +% \begin{tcl} + default { + if {!$onlyLoadTclEnvs || ${dtxload::in_tcl_env}} then { + dtxload::update_included + if {${dtxload::module_included}}\ +% + then {return [list $pos2 $line $pos]} + } + } + } + } + list $endpos +} +% \end{tcl} +% \end{proc} +% +% +% +% \subsection{Evaluating the code} +% +% The code in this subsection is based on the \Alpha\ procedures +% |Alpha::evaluate| and |tcltk::evaluate|, but |dtxload::evaluate| +% tries to return a bit more information. +% +% \begin{proc}{evaluate} +% The |dtxload::evaluate| procedure takes a script as argument and +% evaluates it in the global variable context. Any errors that occur +% are caught. The returned value is a list with the structure +% \begin{quote} +% \word{code} \word{message} +% \begin{regexp}[\regopt]\word{errorInfo} +% \word{errorCode}\end{regexp} +% \end{quote} +% \word{code} is the \Tcllogo\ return code, as returned by |catch| +% when evaluating the script. \word{message} is the value returned by +% the script; it is an error message if an error occurred. +% \word{errorInfo} is the value that the global variable |errorInfo| +% got after the script was evaluated and \word{errorCode} is the +% corresponding value of |errorCode|. +% +% When the \Alpha\ global variable |evaluateRemotely| is |0| the +% evaluation is carried out by \Alpha\ itself, but when +% |evaluateRemotely| is |1| the script is instead sent to a separate +% \Tcllogo\ shell for evaluation. The current implementation does not +% return the values of the |errorInfo| and |errorCode| variables in +% that case, which is why they may be absent from the return value of +% |dtxload::evaluate|. +% \changes{v\,1.0}{2000/10/17}{Moved \texttt{history} interaction to +% \texttt{dtxload::shell\_log}. (LH)} +% \changes{v\,1.1}{2000/12/05}{Added code for remote evaluation. (LH)} +% \begin{tcl} +proc dtxload::evaluate {t} { + global errorInfo errorCode evaluateRemotely tcl_platform + if {!$evaluateRemotely} then { + if {[set code [catch {uplevel \#0 $t} msg]] == 1} then { +% \end{tcl} +% If there was an error, the last part of the value of |errorInfo| +% will be due to the |uplevel| command. As that command was not +% requested by the user, it would be confusing and it is therefore +% best to remove it. +% \begin{tcl} + set L [split $errorInfo \n] + set errorInfo [join [lrange $L 0 [expr {[llength $L] - 4}]] \n] + } + list $code $msg $errorInfo $errorCode + } elseif {$tcl_platform(platform) == "macintosh"} then { +% \end{tcl} +% On the \mbox{MacOS} platform, the script is sent to a remote +% \Tcllogo\ shell via AppleEvents. +% \begin{tcl} + global tclshSig + app::ensureRunning $tclshSig + list [catch { + aebuild::result -t 30000 '${tclshSig}' misc dosc ----\ + [aebuild::TEXT $t] + } res]\ + $res + } else { +% \end{tcl} +% On the other platforms I don't really know what should be done. The +% following code is based on that in |tcltk::evaluate|, but I haven't +% been able to test it (help, please?). +% \begin{tcl} + global tclshInterp + if {![info exists tclshInterp]} { + if {[catch {tcltk::findTclshInterp}]} { + return [list 1 "No shell selected"] + } + } + if {$tcl_platform(platform) == "windows"} { + if {[dde services Tk $tclshInterp] == ""} { + alertnote "The remote shell has died, please select a new one." + unset tclshInterp + return [dtxload::evaluate $t] + } + list [catch {dde execute Tk $tclshInterp $t} res] $res + } else { + list [catch {send $tclshInterp $t} res] $res + } + } +} +% \end{tcl} +% \end{proc} +% +% +% \subsection{Logging} +% +% The code in this subsection is based on the \Alpha\ procedures +% |Shel::carriageReturn| and |Alpha::evaluate|, but many details had to +% be changed because, amongst other things, the window to which text is +% written is not the frontmost one. +% +% \begin{proc}{message_log} +% The |dtxload::message_log| procedure handles logging a script +% evaluation on the status line. It takes one argument, which is the +% script to evaluate, and returns a boolean value that specifies whether +% an error occurred (|0| is no error, |1| is that there was an error). +% +% Since the status line is rather limited in size there isn't very +% much that can be logged on it. |dtxload::message_log| simply displays +% the result or error that occurred. +% \begin{tcl} +proc dtxload::message_log {script} { + set res [dtxload::evaluate $script] + if {[lindex $res 0]==1} then { + message "Tcl eval error: [lindex $res 1]" + expr 1 + } else { + if {[string length [lindex $res 1]] == 0} then { + message "Tcl eval OK." + } else { + message "Tcl eval OK: [lindex $res 1]" + } + expr 0 + } +} +% \end{tcl} +% \end{proc} +% +% +% \begin{proc}{window_log} +% \changes{v\,1.1}{2000/12/05}{Renamed procedure (old name was +% \texttt{dtxload:\discretionary{}{}{}:shell\_log}). (LH)} +% \begin{variable}{log_window_name} +% \changes{v\,1.1}{2000/12/05}{Renamed variable (old name was +% \texttt{dtxload:\discretionary{}{}{}:shell\_window\_name}). (LH)} +% The |dtxload::window_log| procedure handles logging a script +% evaluation in a window, which must be open when this procedure is +% called. It takes one argument, which is the script to evaluate, and +% returns a boolean value that specifies whether an error occurred +% (|0| is no error, |1| is that there was an error). +% +% When it is set the |dtxload::log_window_name| variable holds the +% name of the window which is used in logging, and when it isn't set +% this means that logging should be in the status line. Initially it +% is unset. +% \begin{tcl} +if {[info exists dtxload::log_window_name]} then { + unset dtxload::log_window_name +} +% \end{tcl} +% \end{variable} +% +% If the log window has mode |Shel| then the |script| is recorded in +% the history list, but since the shells have trouble handling history +% items that contain more than one line, all newlines in the recorded +% script will be replaced by semicolons. This \emph{can} change the +% meaning of the script, but hopefully that happens so rarely that one +% can live with it. +% \changes{v\,1.0}{2000/11/01}{Scripts added to the history list are +% converted to one-liners. (LH)} +% \begin{tcl} +proc dtxload::window_log {script} { + upvar #0 dtxload::log_window_name win + global win::Modes Shel::startPrompt Shel::endPrompt + if {[set win::Modes($win)] == "Shel"} then { + global Shel::histnum + regsub -all \n [string trimright $script] " ; " text + history add $text + set Shel::histnum [history nextid] + } +% \end{tcl} +% +% Next the script is inserted into (at the end of) the log window. +% The lines on which logged scripts are written are marked out using +% a special \package{dtxload} prompt, which is constructed as +% \begin{quote} +% |"${Shel::startPrompt}dtxload${Shel::endPrompt} "| +% \end{quote} +% If the log window mode is |Shel| and the last thing in the window +% is the current prompt for the window, then that prompt is replaced +% by the \package{dtxload} prompt, otherwise a newline followed by a +% \package{dtxload} prompt will be appended to the window. The result +% of a logged script is put on the line after the last line of the +% script. +% \begin{tcl} + set pos [maxPos -w $win] + select -w $win $pos $pos + set dtxprompt "${Shel::startPrompt}dtxload${Shel::endPrompt} " + if {[set win::Modes($win)] != "Shel"} then { + insertText -w $win \r$dtxprompt + } else { + set stdprompt [Alpha::Prompt] + set pos2 [pos::math $pos - [string length $stdprompt]] + if {[getText -w $win $pos2 $pos] != $stdprompt} then { + insertText -w $win \r$dtxprompt + } else { + replaceText -w $win $pos2 $pos $dtxprompt + refresh + } + } +% \end{tcl} +% There currently is a bug (it is fixed in \Alpha~8) in +% |replaceText| which causes it to always draw in the current window. +% The |refresh| restores the proper window contents. +% \changes{v\,1.0}{2000/10/16}{\texttt{refresh} inserted to overcome +% \texttt{replaceText} bug. (LH)} +% +% Inserting the actual script text is basically an |insertText|, but +% before the script is inserted all newlines in it (except the last) +% are replaced by newlines followed by a \package{dtxload} prompt. +% Since |dtxload::load| converts all newlines to linefeeds, it is +% sufficient to replace linefeeds. It seems that newlines in \Alpha\ +% are always carriage returns (regardless of the window's ``platform'' +% attribute), and I assume that the \package{dtxload} prompt doesn't +% contain any regexp metacharacter. +% \changes{v\,1.0}{2000/10/16}{Assuming newlines in the script to log +% are linefeeds. (LH)} +% \begin{tcl} + regsub -all \n [string trimright $script] \r$dtxprompt text + insertText -w $win "$text\r" +% \end{tcl} +% After the script has been logged, it is time to evaluate it and log +% the result. +% \begin{tcl} + set res [dtxload::evaluate $script] + if {[lindex $res 0]==1} then { + insertText -w $win "Error: [lindex $res 1]\r" + if {[llength $res]>2} then { + global errorInfo + set errorInfo [lindex $res 2] + } + message "Tcl eval error" + } else { + if {[string length [lindex $res 1]]>0}\ + then {insertText -w $win "[lindex $res 1]\r"} + message "Tcl eval OK" + } +% \end{tcl} +% If the window has mode |Shel| then the window's current prompt is +% inserted. It is necessary to call |Alpha::Prompt| again because +% evaluating the script might have changed the prompt. Then the return +% value is finally computed. +% \begin{tcl} + if {[set win::Modes($win)] == "Shel"}\ + then {insertText -w $win [Alpha::Prompt]} + expr {[lindex $res 0]==1} +} +% \end{tcl} +% \end{proc} +% +% +% +% +% \subsection{Putting it all together} +% +% \begin{proc}{load} +% The |dtxload::load| procedure implements the \package{dtxload} user +% command---it extracts and evaluates the code in the current +% selection (if something is selected) or the entire front window (if +% nothing is selected). The evaluation is logged a window or on the +% status line depending on the current value of +% |dtxload::log_window_name|. +% +% Code is extracted and evaluated one command (or as close as one gets) +% at a time. In case an error occurs the loading is stopped and the +% insertion point is moved to the beginning of the problematic script, +% whereas the mark is put at the end of the code to load. This way a +% simple \textsf{Mark Hilite} will select the part of the code that +% remains to be loaded. +% +% The very first thing |dtxload::load| does is to check whether it is +% allowed to load any code in this window. +% \begin{tcl} +proc dtxload::load {} { + global onlyLoadDtxFiles + if {$onlyLoadDtxFiles && ![string match *.dtx [win::CurrentTail]]}\ + then { + message "Not a .dtx file." + return + } +% \end{tcl} +% Then the interval from which code should be extracted is +% determined. +% \begin{tcl} + set startpos [getPos] + set endpos [selEnd] + if {[pos::compare {$startpos == $endpos}]} then { + set startpos [minPos] + set endpos [maxPos] + } +% \end{tcl} +% Next it is determined what logging method should be used. The name +% of the procedure gets stored in the local variable |log|. +% \begin{tcl} + global dtxload::log_window_name + if { + [info exists dtxload::log_window_name] && + [lsearch -exact [winNames] ${dtxload::log_window_name}]!=-1 + } then {set log dtxload::window_log}\ + else {set log dtxload::message_log} +% \end{tcl} +% After that a couple of extraction-related variables are initialized. +% The idea behind how the |dtxload::in_tcl_env| variable is set is that +% if one is loading a short piece of code inside a \texttt{tcl} +% environment then the first line probably doesn't start with a |%|, +% whereas if one is loading an entire file (and thus starts in +% non-\texttt{tcl}-environment) the first character quite likely is a +% |%|. +% \begin{tcl} + global dtxload::in_tcl_env dtxload::module_stack\ + dtxload::next_module_idx dtxload::module_included + set dtxload::in_tcl_env\ + [expr {[getText $startpos [pos::math {$startpos+1}]] != "%"}] + set dtxload::module_stack {} + set dtxload::next_module_idx 0 + set dtxload::module_included 1 +% \end{tcl} +% The last piece of initialization concerns the |script| local +% variable, in which the extracted lines are collected until they +% make up an entire command. The |escaped| variable is a boolean +% which is true when the newline at the end of the last line of +% |script| was escaped. The |spos| variable stores the position at +% which the command currently being collected began, but it is +% initialized to |none| here to help distinguishing the case that no +% extractable code was found. +% \begin{tcl} + set script "" + set escaped 0 + set spos none + while {[pos::compare $startpos < $endpos]} { + set extract [dtxload::extract_line $startpos $endpos] + if {[llength $extract]>1} then { +% \end{tcl} +% When a line of code has been extracted, it must be appended to +% |script|. This isn't as easy as one might imagine, because \Tcllogo\ +% code being |source|d (which is what loading must function as) is fed +% through a few more conversion steps than code being |eval|ed. In +% particular all escaped newlines are converted to spaces and all +% other newlines are converted to linefeeds, so that must be done +% explicitly. A newline is considered to be escaped if is is preceded +% by an odd number of backslashes, and if it is then the last backslash +% and the newline are replaced by a single space. If the last line of +% |script| ended by an escaped newline then the script is considered +% incomplete and more code will be collected before evaluation is +% attempted. Evaluation of |script| is also conditioned by that |info\ +% complete $script| returns |1|. +% \changes{v\,1.0}{2000/10/16}{Converting nonescaped newlines to +% linefeeds, since \Tcllogo\ doesn't consider carriage returns to +% be command separators. (LH)} +% \begin{tcl} + if {[string length $script]==0} then { + set spos [lindex $extract 2] + } + set line [lindex $extract 1] + if {$escaped} then {set line [string trimleft $line]} + set escaped [regsub -all\ + "((^|\[^\\\\\])(\\\\\\\\)*)\\\\(\r|\n|\r\n)$" $line {\1 } line] + if {!$escaped} then {regsub "\r\n?\$" $line \n line} + append script $line + if {!$escaped && [info complete $script]} then { + if {[$log $script]} then { + select $spos $endpos + markHilite + return + } else { + set script "" + } + } + } elseif {$spos=="none"} then { + message "No extractable code found." + } + set startpos [lindex $extract 0] + } +% \end{tcl} +% If the |script| variable isn't empty, then try to evaluate whatever +% is in it before returning. If not even that causes an error the +% |dtxload:|\penalty\hyphenpenalty|:known_expressions| array is unset +% so that one can use other options for the next piece of code loaded. +% \begin{tcl} + if {[string length $script]>0 && [$log $script]} then { + select $spos $endpos + markHilite + } else { + global dtxload::known_expressions + if {[info exists dtxload::known_expressions]}\ + then {unset dtxload::known_expressions} + } +} +% \end{tcl} +% \end{proc} +% +% +% \subsection{Setting the log destination} +% +% \begin{proc}{logdest} +% \changes{v\,1.0}{2000/10/16}{Procedure added. (LH)} +% \changes{v\,1.1}{2000/12/07}{Procedure renamed from +% \texttt{dtxload::redirect} and the syntax was changed. (LH)} +% The log destination is set using the |dtxload::logdest| procedure. +% The general syntax for this procedure is +% \begin{quote} +% |dtxload::logdest| \word{keyword} \word{window name}\regopt +% \end{quote} +% where the \word{keyword} (mainly) specifies the type of log +% destination and \word{window name} is the name of the target +% window (ignored for some \word{keyword}s). +% \begin{tcl} +proc dtxload::logdest {type {win "dtxload log"}} { + global dtxload::log_window_name + switch -exact -- $type { +% \end{tcl} +% The first form of |dtxload::logdest| has the syntax +% \begin{quote} +% |dtxload::logdest status| +% \end{quote} +% It makes the status line the log destination. +% \begin{tcl} + status { + if {[info exists dtxload::log_window_name]}\ + then {unset dtxload::log_window_name} + } +% \end{tcl} +% The second form has the syntax +% \begin{quote} +% |dtxload::logdest here| +% \end{quote} +% It makes the current window the log destination, regardless of what +% may already be in it or which mode it has. +% \begin{tcl} + here {set dtxload::log_window_name [win::CurrentTail]} +% \end{tcl} +% The third form has the syntax +% \begin{quote} +% |dtxload::logdest window| \word{window name} +% \end{quote} +% It sets the log destination to the window named \word{window name}, +% regardless of what may already be in it or which mode it has. If +% the window does not already exist it is created. +% \begin{tcl} + window { + if {[lsearch -exact [winNames] $win] == -1} then { + new -n $win -shell 1 -text "This is a dtxload log window.\r" + } + set dtxload::log_window_name $win + } +% \end{tcl} +% The fourth and final form has the syntax +% \begin{quote} +% |dtxload::logdest shell| \word{window name} +% \end{quote} +% It calls |Shel::start| to open a \Tcllogo\ shell window and sets the +% log destination to that window. +% \begin{tcl} + shell { + Shel::start "Alpha" $win\ + "This is a dtxload log window (and Alpha Tcl shell).\r" + set dtxload::log_window_name $win + } + } +} +%</tcl> +% \end{tcl} +% \end{proc} +% +% +% \Finale +% +\endinput |