Changeset 29c9b23


Ignore:
Timestamp:
Feb 4, 2021, 9:56:11 AM (3 years ago)
Author:
Andrew Beach <ajbeach@…>
Branches:
ADT, arm-eh, ast-experimental, enum, forall-pointer-decay, jacob/cs343-translation, master, new-ast-unique-expr, pthread-emulation, qualifiedEnum
Children:
9af0fe2d
Parents:
6a99803
Message:

Andrew MMath: Supposed to be focused on features but it ended up leaking out.

Location:
doc/theses/andrew_beach_MMath
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • doc/theses/andrew_beach_MMath/existing.tex

    r6a99803 r29c9b23  
    1 \chapter{\texorpdfstring{\CFA Existing Features}{Cforall Existing Features}}
     1\chapter{\CFA Existing Features}
    22
    33\CFA (C-for-all)~\cite{Cforall} is an open-source project extending ISO C with
     
    4343
    4444\section{Reference Type}
    45 \CFA adds a rebindable reference type to C, but more expressive than the \CC
     45\CFA adds a rebindable reference type to C, but more expressive than the \Cpp
    4646reference.  Multi-level references are allowed and act like auto-dereferenced
    4747pointers using the ampersand (@&@) instead of the pointer asterisk (@*@). \CFA
     
    6060
    6161Both constructors and destructors are operators, which means they are just
    62 functions with special operator names rather than type names in \CC. The
     62functions with special operator names rather than type names in \Cpp. The
    6363special operator names may be used to call the functions explicitly (not
    64 allowed in \CC for constructors).
     64allowed in \Cpp for constructors).
    6565
    6666In general, operator names in \CFA are constructed by bracketing an operator
     
    8989matching overloaded destructor @void ^?{}(T &);@ is called.  Without explicit
    9090definition, \CFA creates a default and copy constructor, destructor and
    91 assignment (like \CC). It is possible to define constructors/destructors for
     91assignment (like \Cpp). It is possible to define constructors/destructors for
    9292basic and existing types.
    9393
     
    9595\CFA uses parametric polymorphism to create functions and types that are
    9696defined over multiple types. \CFA polymorphic declarations serve the same role
    97 as \CC templates or Java generics. The ``parametric'' means the polymorphism is
     97as \Cpp templates or Java generics. The ``parametric'' means the polymorphism is
    9898accomplished by passing argument operations to associate \emph{parameters} at
    9999the call site, and these parameters are used in the function to differentiate
     
    135135
    136136Note, a function named @do_once@ is not required in the scope of @do_twice@ to
    137 compile it, unlike \CC template expansion. Furthermore, call-site inferencing
     137compile it, unlike \Cpp template expansion. Furthermore, call-site inferencing
    138138allows local replacement of the most specific parametric functions needs for a
    139139call.
     
    179179}
    180180\end{cfa}
    181 The generic type @node(T)@ is an example of a polymorphic-type usage.  Like \CC
     181The generic type @node(T)@ is an example of a polymorphic-type usage.  Like \Cpp
    182182templates usage, a polymorphic-type usage must specify a type parameter.
    183183
  • doc/theses/andrew_beach_MMath/features.tex

    r6a99803 r29c9b23  
    55
    66\section{Virtuals}
     7Virtual types and casts are not part of the exception system nor are they
     8required for an exception system. But an object-oriented style hierarchy is a
     9great way of organizing exceptions so a minimal virtual system has been added
     10to \CFA.
     11
     12The pattern of a simple hierarchy was borrowed from object-oriented
     13programming was chosen for several reasons.
     14The first is that it allows new exceptions to be added in user code
     15and in libraries independently of each other. Another is it allows for
     16different levels of exception grouping (all exceptions, all IO exceptions or
     17a particular IO exception). Also it also provides a simple way of passing
     18data back and forth across the throw.
     19
    720Virtual types and casts are not required for a basic exception-system but are
    821useful for advanced exception features. However, \CFA is not object-oriented so
    922there is no obvious concept of virtuals. Hence, to create advanced exception
    10 features for this work, I needed to designed and implemented a virtual-like
     23features for this work, I needed to design and implement a virtual-like
    1124system for \CFA.
    1225
     26% NOTE: Maybe we should but less of the rational here.
    1327Object-oriented languages often organized exceptions into a simple hierarchy,
    1428\eg Java.
     
    6175While much of the virtual infrastructure is created, it is currently only used
    6276internally for exception handling. The only user-level feature is the virtual
    63 cast, which is the same as the \CC \lstinline[language=C++]|dynamic_cast|.
     77cast, which is the same as the \Cpp \lstinline[language=C++]|dynamic_cast|.
    6478\label{p:VirtualCast}
    6579\begin{cfa}
    6680(virtual TYPE)EXPRESSION
    6781\end{cfa}
    68 Note, the syntax and semantics matches a C-cast, rather than the unusual \CC
    69 syntax for special casts. Both the type of @EXPRESSION@ and @TYPE@ must be a
    70 pointer to a virtual type. The cast dynamically checks if the @EXPRESSION@ type
    71 is the same or a subtype of @TYPE@, and if true, returns a pointer to the
     82Note, the syntax and semantics matches a C-cast, rather than the function-like
     83\Cpp syntax for special casts. Both the type of @EXPRESSION@ and @TYPE@ must be
     84a pointer to a virtual type.
     85The cast dynamically checks if the @EXPRESSION@ type is the same or a subtype
     86of @TYPE@, and if true, returns a pointer to the
    7287@EXPRESSION@ object, otherwise it returns @0p@ (null pointer).
    7388
     
    8297\begin{cfa}
    8398trait is_exception(exceptT &, virtualT &) {
    84         virtualT const & @get_exception_vtable@(exceptT *);
     99        virtualT const & get_exception_vtable(exceptT *);
    85100};
    86101\end{cfa}
    87 The function takes any pointer, including the null pointer, and returns a
    88 reference to the virtual-table object. Defining this function also establishes
    89 the virtual type and a virtual-table pair to the \CFA type-resolver and
    90 promises @exceptT@ is a virtual type and a child of the base exception-type.
    91 
    92 \PAB{I do not understand this paragraph.}
    93 One odd thing about @get_exception_vtable@ is that it should always be a
    94 constant function, returning the same value regardless of its argument. A
    95 pointer or reference to the virtual table instance could be used instead,
    96 however using a function has some ease of implementation advantages and allows
    97 for easier disambiguation because the virtual type name (or the address of an
    98 instance that is in scope) can be used instead of the mangled virtual table
    99 name. Also note the use of the word ``promise'' in the trait
    100 description. Currently, \CFA cannot check to see if either @exceptT@ or
    101 @virtualT@ match the layout requirements. This is considered part of
    102 @get_exception_vtable@'s correct implementation.
     102The trait is defined over two types, the exception type and the virtual table
     103type. This should be one-to-one, each exception type has only one virtual
     104table type and vice versa. The only assertion in the trait is
     105@get_exception_vtable@, which takes a pointer of the exception type and
     106returns a reference to the virtual table type instance.
     107
     108The function @get_exception_vtable@ is actually a constant function.
     109Recardless of the value passed in (including the null pointer) it should
     110return a reference to the virtual table instance for that type.
     111The reason it is a function instead of a constant is that it make type
     112annotations easier to write as you can use the exception type instead of the
     113virtual table type; which usually has a mangled name.
     114% Also \CFA's trait system handles functions better than constants and doing
     115% it this way
     116
     117% I did have a note about how it is the programmer's responsibility to make
     118% sure the function is implemented correctly. But this is true of every
     119% similar system I know of (except Agda's I guess) so I took it out.
    103120
    104121\section{Raise}
     
    109126trait is_termination_exception(
    110127                exceptT &, virtualT & | is_exception(exceptT, virtualT)) {
    111         void @defaultTerminationHandler@(exceptT &);
     128        void defaultTerminationHandler(exceptT &);
    112129};
    113130\end{cfa}
     
    119136trait is_resumption_exception(
    120137                exceptT &, virtualT & | is_exception(exceptT, virtualT)) {
    121         void @defaultResumptionHandler@(exceptT &);
     138        void defaultResumptionHandler(exceptT &);
    122139};
    123140\end{cfa}
     
    126143
    127144Finally there are three convenience macros for referring to the these traits:
    128 @IS_EXCEPTION@, @IS_TERMINATION_EXCEPTION@ and @IS_RESUMPTION_EXCEPTION@. Each
    129 takes the virtual type's name, and for polymorphic types only, the
    130 parenthesized list of polymorphic arguments. These macros do the name mangling
    131 to get the virtual-table name and provide the arguments to both sides
    132 \PAB{What's a ``side''?}
     145@IS_EXCEPTION@, @IS_TERMINATION_EXCEPTION@ and @IS_RESUMPTION_EXCEPTION@.
     146All three traits are hard to use while naming the virtual table as it has an
     147internal mangled name. These macros take the exception name as their first
     148argument and do the mangling. They all take a second argument for polymorphic
     149types which is the parenthesized list of polymorphic arguments. These
     150arguments are passed to both the exception type and the virtual table type as
     151the arguments do have to match.
     152
     153For example consider a function that is polymorphic over types that have a
     154defined arithmetic exception:
     155\begin{cfa}
     156forall(Num | IS_EXCEPTION(Arithmetic, (Num)))
     157void some_math_function(Num & left, Num & right);
     158\end{cfa}
    133159
    134160\subsection{Termination}
     
    147173throw EXPRESSION;
    148174\end{cfa}
    149 The expression must return a termination-exception reference, where the
    150 termination exception has a type with a @void defaultTerminationHandler(T &)@
    151 (default handler) defined. The handler is found at the call site using \CFA's
    152 trait system and passed into the exception system along with the exception
    153 itself.
    154 
    155 At runtime, a representation of the exception type and an instance of the
    156 exception type is copied into managed memory (heap) to ensure it remains in
     175The expression must return a reference to a termination exception, where the
     176termination exception is any type that satifies @is_termination_exception@
     177at the call site.
     178Through \CFA's trait system the functions in the traits are passed into the
     179throw code. A new @defaultTerminationHandler@ can be defined in any scope to
     180change the throw's behavior (see below).
     181
     182At runtime, the exception returned by the expression
     183is copied into managed memory (heap) to ensure it remains in
    157184scope during unwinding. It is the user's responsibility to ensure the original
    158185exception object at the throw is freed when it goes out of scope. Being
     
    166193try {
    167194        GUARDED_BLOCK
    168 } @catch (EXCEPTION_TYPE$\(_1\)$ * NAME)@ { // termination handler 1
     195} catch (EXCEPTION_TYPE$\(_1\)$ * NAME$\(_1\)$) { // termination handler 1
    169196        HANDLER_BLOCK$\(_1\)$
    170 } @catch (EXCEPTION_TYPE$\(_2\)$ * NAME)@ { // termination handler 2
     197} catch (EXCEPTION_TYPE$\(_2\)$ * NAME$\(_2\)$) { // termination handler 2
    171198        HANDLER_BLOCK$\(_2\)$
    172199}
     
    179206Exception matching checks the representation of the thrown exception-type is
    180207the same or a descendant type of the exception types in the handler clauses. If
    181 there is a match, a pointer to the exception object created at the throw is
    182 bound to @NAME@ and the statements in the associated @HANDLER_BLOCK@ are
    183 executed. If control reaches the end of the handler, the exception is freed,
    184 and control continues after the try statement.
     208it is the same of a descendent of @EXCEPTION_TYPE@$_i$ then @NAME@$_i$ is
     209bound to a pointer to the exception and the statements in @HANDLER_BLOCK@$_i$
     210are executed. If control reaches the end of the handler, the exception is
     211freed and control continues after the try statement.
    185212
    186213The default handler visible at the throw statement is used if no matching
    187214termination handler is found after the entire stack is searched. At that point,
    188215the default handler is called with a reference to the exception object
    189 generated at the throw. If the default handler returns, the system default
    190 action is executed, which often terminates the program. This feature allows
     216generated at the throw. If the default handler returns, control continues
     217from after the throw statement. This feature allows
    191218each exception type to define its own action, such as printing an informative
    192219error message, when an exception is not handled in the program.
     220However the default handler for all exception types triggers a cancellation
     221using the exception.
    193222
    194223\subsection{Resumption}
     
    197226Resumption raise, called ``resume'', is as old as termination
    198227raise~\cite{Goodenough75} but is less popular. In many ways, resumption is
    199 simpler and easier to understand, as it is simply a dynamic call (as in
    200 Lisp). The semantics of resumption is: search the stack for a matching handler,
     228simpler and easier to understand, as it is simply a dynamic call.
     229The semantics of resumption is: search the stack for a matching handler,
    201230execute the handler, and continue execution after the resume. Notice, the stack
    202231cannot be unwound because execution returns to the raise point. Resumption is
     
    210239\end{cfa}
    211240The semantics of the @throwResume@ statement are like the @throw@, but the
    212 expression has a type with a @void defaultResumptionHandler(T &)@ (default
    213 handler) defined, where the handler is found at the call site by the type
    214 system. At runtime, a representation of the exception type and an instance of
    215 the exception type is \emph{not} copied because the stack is maintained during
    216 the handler search.
     241expression has return a reference a type that satifies the trait
     242@is_resumption_exception@. Like with termination the exception system can
     243use these assertions while (throwing/raising/handling) the exception.
     244
     245At runtime, no copies are made. As the stack is not unwound the exception and
     246any values on the stack will remain in scope while the resumption is handled.
    217247
    218248Then the exception system searches the stack starting from the resume and
    219 proceeding towards the base of the stack, from callee to caller. At each stack
     249proceeding to the base of the stack, from callee to caller. At each stack
    220250frame, a check is made for resumption handlers defined by the @catchResume@
    221251clauses of a @try@ statement.
     
    223253try {
    224254        GUARDED_BLOCK
    225 } @catchResume (EXCEPTION_TYPE$\(_1\)$ * NAME)@ { // resumption handler 1
     255} catchResume (EXCEPTION_TYPE$\(_1\)$ * NAME$\(_1\)$) {
    226256        HANDLER_BLOCK$\(_1\)$
    227 } @catchResume (EXCEPTION_TYPE$\(_2\)$ * NAME)@ { // resumption handler 2
     257} catchResume (EXCEPTION_TYPE$\(_2\)$ * NAME$\(_2\)$) {
    228258        HANDLER_BLOCK$\(_2\)$
    229259}
     
    254284current point on the stack because new try statements may have been pushed by
    255285the handler or functions called from the handler. If there is no match back to
    256 the point of the current handler, the search skips\label{p:searchskip} the stack frames already
    257 searched by the first resume and continues after the try statement. The default
    258 handler always continues from default handler associated with the point where
    259 the exception is created.
     286the point of the current handler, the search skips\label{p:searchskip} the
     287stack frames already searched by the first resume and continues after
     288the try statement. The default handler always continues from default
     289handler associated with the point where the exception is created.
    260290
    261291% This might need a diagram. But it is an important part of the justification
     
    276306\end{verbatim}
    277307
    278 This resumption search-pattern reflect the one for termination, which matches
    279 with programmer expectations. However, it avoids the \emph{recursive
    280 resumption} problem. If parts of the stack are searched multiple times, loops
     308This resumption search pattern reflects the one for termination, and so
     309should come naturally to most programmers.
     310However, it avoids the \emph{recursive resumption} problem.
     311If parts of the stack are searched multiple times, loops
    281312can easily form resulting in infinite recursion.
    282313
     
    284315\begin{cfa}
    285316try {
    286         throwResume$\(_1\)$ (E &){};
    287 } catch( E * ) {
    288         throwResume;
    289 }
    290 \end{cfa}
    291 Based on termination semantics, programmer expectation is for the re-resume to
    292 continue searching the stack frames after the try statement. However, the
    293 current try statement is still on the stack below the handler issuing the
    294 reresume \see{\VRef{s:Reraise}}. Hence, the try statement catches the re-raise
    295 again and does another re-raise \emph{ad infinitum}, which is confusing and
    296 difficult to debug. The \CFA resumption search-pattern skips the try statement
    297 so the reresume search continues after the try, mathcing programmer
    298 expectation.
     317        throwResume (E &){}; // first
     318} catchResume(E *) {
     319        throwResume (E &){}; // second
     320}
     321\end{cfa}
     322If this handler is ever used it will be placed on top of the stack above the
     323try statement. If the stack was not masked than the @throwResume@ in the
     324handler would always be caught by the handler, leading to an infinite loop.
     325Masking avoids this problem and other more complex versions of it involving
     326multiple handlers and exception types.
     327
     328Other masking stratagies could be used; such as masking the handlers that
     329have caught an exception. This one was choosen because it creates a symmetry
     330with termination (masked sections of the stack would be unwound with
     331termination) and having only one pattern to learn is easier.
    299332
    300333\section{Conditional Catch}
    301 Both termination and resumption handler-clauses may perform conditional matching:
     334Both termination and resumption handler clauses can be given an additional
     335condition to further control which exceptions they handle:
    302336\begin{cfa}
    303337catch (EXCEPTION_TYPE * NAME ; @CONDITION@)
     
    323357
    324358\section{Reraise}
     359\color{red}{From Andrew: I recomend we talk about why the language doesn't
     360have rethrows/reraises instead.}
     361
    325362\label{s:Reraise}
    326363Within the handler block or functions called from the handler block, it is
     
    328365@throwResume@, respective.
    329366\begin{cfa}
    330 catch( ... ) {
     367try {
     368        ...
     369} catch( ... ) {
    331370        ... throw; // rethrow
    332371} catchResume( ... ) {
     
    341380handler is generated that does a program-level abort.
    342381
    343 
    344382\section{Finally Clauses}
    345383A @finally@ clause may be placed at the end of a @try@ statement.
     
    347385try {
    348386        GUARDED_BLOCK
    349 } ...   // any number or kind of handler clauses
    350 } finally {
     387} ... // any number or kind of handler clauses
     388... finally {
    351389        FINALLY_BLOCK
    352390}
    353391\end{cfa}
    354 The @FINALLY_BLOCK@ is executed when the try statement is unwound from the
    355 stack, \ie when the @GUARDED_BLOCK@ or any handler clause finishes. Hence, the
    356 finally block is always executed.
     392The @FINALLY_BLOCK@ is executed when the try statement is removed from the
     393stack, including when the @GUARDED_BLOCK@ or any handler clause finishes or
     394during an unwind.
     395The only time the block is not executed is if the program is exited before
     396that happens.
    357397
    358398Execution of the finally block should always finish, meaning control runs off
     
    370410possible forwards the cancellation exception to a different stack.
    371411
     412Cancellation is not an exception operation like termination or resumption.
    372413There is no special statement for starting a cancellation; instead the standard
    373414library function @cancel_stack@ is called passing an exception. Unlike a
     
    379420\item[Main Stack:]
    380421The main stack is the one used by the program main at the start of execution,
    381 and is the only stack in a sequential program. Hence, when cancellation is
    382 forwarded to the main stack, there is no other forwarding stack, so after the
    383 stack is unwound, there is a program-level abort.
     422and is the only stack in a sequential program. Even in a concurrent program
     423the main stack is only dependent on the environment that started the program.
     424Hence, when the main stack is cancelled there is nowhere else in the program
     425to notify. After the stack is unwound, there is a program-level abort.
    384426
    385427\item[Thread Stack:]
     
    387429@is_thread@ trait. A thread only has two points of communication that must
    388430happen: start and join. As the thread must be running to perform a
    389 cancellation, it must occur after start and before join, so join is a
    390 cancellation point. After the stack is unwound, the thread halts and waits for
    391 another thread to join with it. The joining thread, checks for a cancellation,
     431cancellation, it must occur after start and before join, so join is used
     432for communication here.
     433After the stack is unwound, the thread halts and waits for
     434another thread to join with it. The joining thread checks for a cancellation,
    392435and if present, resumes exception @ThreadCancelled@.
    393436
     
    397440the exception is not caught. The implicit join does a program abort instead.
    398441
    399 This semantics is for safety. One difficult problem for any exception system is
    400 defining semantics when an exception is raised during an exception search:
    401 which exception has priority, the original or new exception? No matter which
    402 exception is selected, it is possible for the selected one to disrupt or
    403 destroy the context required for the other. \PAB{I do not understand the
    404 following sentences.} This loss of information can happen with join but as the
    405 thread destructor is always run when the stack is being unwound and one
    406 termination/cancellation is already active. Also since they are implicit they
    407 are easier to forget about.
     442This semantics is for safety. If an unwind is triggered while another unwind
     443is underway only one of them can proceed as they both want to ``consume'' the
     444stack. Letting both try to proceed leads to very undefined behaviour.
     445Both termination and cancellation involve unwinding and, since the default
     446@defaultResumptionHandler@ preforms a termination that could more easily
     447happen in an implicate join inside a destructor. So there is an error message
     448and an abort instead.
     449
     450The recommended way to avoid the abort is to handle the intial resumption
     451from the implicate join. If required you may put an explicate join inside a
     452finally clause to disable the check and use the local
     453@defaultResumptionHandler@ instead.
    408454
    409455\item[Coroutine Stack:] A coroutine stack is created for a @coroutine@ object
  • doc/theses/andrew_beach_MMath/future.tex

    r6a99803 r29c9b23  
    1010\item
    1111The implementation of termination is not portable because it includes
    12 hand-crafted assembly statements. These sections must be generalized to support
    13 more hardware architectures, \eg ARM processor.
     12hand-crafted assembly statements. These sections must be ported by hand to
     13support more hardware architectures, such as the ARM processor.
    1414\item
    1515Due to a type-system problem, the catch clause cannot bind the exception to a
     
    2424scope of the @try@ statement, where the local control-flow transfers are
    2525meaningful.
     26\item
     27There is no detection of colliding unwinds. It is possible for clean-up code
     28run during an unwind to trigger another unwind that escapes the clean-up code
     29itself; such as a termination exception caught further down the stack or a
     30cancellation. There do exist ways to handle this but currently they are not
     31even detected and the first unwind will simply be forgotten, often leaving
     32it in a bad state.
     33\item
     34Also the exception system did not have a lot of time to be tried and tested.
     35So just letting people use the exception system more will reveal new
     36quality of life upgrades that can be made with time.
    2637\end{itemize}
    2738
  • doc/theses/andrew_beach_MMath/uw-ethesis-frontpgs.tex

    r6a99803 r29c9b23  
    1313        \vspace*{1.0cm}
    1414
    15         \Huge
    16         {\bf Exception Handling in \CFA}
     15        {\Huge\bf Exception Handling in \CFA}
    1716
    1817        \vspace*{1.0cm}
    1918
    20         \normalsize
    2119        by \\
    2220
    2321        \vspace*{1.0cm}
    2422
    25         \Large
    26         Andrew James Beach \\
     23        {\Large Andrew James Beach} \\
    2724
    2825        \vspace*{3.0cm}
    2926
    30         \normalsize
    3127        A thesis \\
    3228        presented to the University of Waterloo \\
     
    4339        \vspace*{1.0cm}
    4440
    45         \copyright\ Andrew James Beach \the\year \\
     41        \copyright{} Andrew James Beach \the\year \\
    4642        \end{center}
    4743\end{titlepage}
  • doc/theses/andrew_beach_MMath/uw-ethesis.tex

    r6a99803 r29c9b23  
    7070%    un-comment the second \documentclass line.
    7171% 2) change the value assigned below to the boolean variable "PrintVersion"
    72 %    from " false" to "true".
     72%    from "false" to "true".
    7373
    7474% ======================================================================
     
    8282% one above:
    8383%\documentclass[letterpaper,12pt,titlepage,openright,twoside,final]{book}
     84
     85\usepackage{etoolbox}
    8486
    8587% Some LaTeX commands I define for my own nomenclature.
     
    105107% For including graphics N.B. pdftex graphics driver
    106108\usepackage[pdftex]{graphicx}
     109% Removes large sections of the document.
     110\usepackage{comment}
    107111
    108112% Hyperlinks make it very easy to navigate an electronic document.
     
    201205\makeglossaries
    202206
    203 \usepackage{comment}
    204207% cfa macros used in the document
    205208%\usepackage{cfalab}
     209% I'm going to bring back eventually.
     210\makeatletter
     211% Combines all \CC* commands:
     212\newrobustcmd*\Cpp[1][\xspace]{\cfalab@Cpp#1}
     213\newcommand\cfalab@Cpp{C\kern-.1em\hbox{+\kern-.25em+}}
     214% Optional arguments do not work with pdf string. (Some fix-up required.)
     215\pdfstringdefDisableCommands{\def\Cpp{C++}}
     216\makeatother
     217
    206218\input{common}
    207 \CFAStyle                                               % CFA code-style for all languages
    208 \lstset{language=CFA,basicstyle=\linespread{0.9}\tt}    % CFA default lnaguage
     219% CFA code-style for all languages
     220\CFAStyle
     221% CFA default lnaguage
     222\lstset{language=CFA,basicstyle=\linespread{0.9}\tt}
     223% Annotations from Peter:
    209224\newcommand{\PAB}[1]{{\color{blue}PAB: #1}}
     225% Change the style of abbreviations:
     226\renewcommand{\abbrevFont}{}
    210227
    211228%======================================================================
Note: See TracChangeset for help on using the changeset viewer.