diff options
Diffstat (limited to 'doc/refman/RefMan-tus.tex')
-rw-r--r-- | doc/refman/RefMan-tus.tex | 2001 |
1 files changed, 0 insertions, 2001 deletions
diff --git a/doc/refman/RefMan-tus.tex b/doc/refman/RefMan-tus.tex deleted file mode 100644 index 3e298867..00000000 --- a/doc/refman/RefMan-tus.tex +++ /dev/null @@ -1,2001 +0,0 @@ -%\documentclass[11pt]{article} -%\usepackage{fullpage,euler} -%\usepackage[latin1]{inputenc} -%\begin{document} -%\title{Writing ad-hoc Tactics in Coq} -%\author{} -%\date{} -%\maketitle -%\tableofcontents -%\clearpage - -\chapter[Writing ad-hoc Tactics in Coq]{Writing ad-hoc Tactics in Coq\label{WritingTactics}} - -\section{Introduction} - -\Coq\ is an open proof environment, in the sense that the collection of -proof strategies offered by the system can be extended by the user. -This feature has two important advantages. First, the user can develop -his/her own ad-hoc proof procedures, customizing the system for a -particular domain of application. Second, the repetitive and tedious -aspects of the proofs can be abstracted away implementing new tactics -for dealing with them. For example, this may be useful when a theorem -needs several lemmas which are all proven in a similar but not exactly -the same way. Let us illustrate this with an example. - -Consider the problem of deciding the equality of two booleans. The -theorem establishing that this is always possible is state by -the following theorem: - -\begin{coq_example*} -Theorem decideBool : (x,y:bool){x=y}+{~x=y}. -\end{coq_example*} - -The proof proceeds by case analysis on both $x$ and $y$. This yields -four cases to solve. The cases $x=y=\textsl{true}$ and -$x=y=\textsl{false}$ are immediate by the reflexivity of equality. - -The other two cases follow by discrimination. The following script -describes the proof: - -\begin{coq_example*} -Destruct x. - Destruct y. - Left ; Reflexivity. - Right; Discriminate. - Destruct y. - Right; Discriminate. - Left ; Reflexivity. -\end{coq_example*} -\begin{coq_eval} -Abort. -\end{coq_eval} - -Now, consider the theorem stating the same property but for the -following enumerated type: - -\begin{coq_example*} -Inductive Set Color := Blue:Color | White:Color | Red:Color. -Theorem decideColor : (c1,c2:Color){c1=c2}+{~c1=c2}. -\end{coq_example*} - -This theorem can be proven in a very similar way, reasoning by case -analysis on $c_1$ and $c_2$. Once more, each of the (now six) cases is -solved either by reflexivity or by discrimination: - -\begin{coq_example*} -Destruct c1. - Destruct c2. - Left ; Reflexivity. - Right ; Discriminate. - Right ; Discriminate. - Destruct c2. - Right ; Discriminate. - Left ; Reflexivity. - Right ; Discriminate. - Destruct c2. - Right ; Discriminate. - Right ; Discriminate. - Left ; Reflexivity. -\end{coq_example*} -\begin{coq_eval} -Abort. -\end{coq_eval} - -If we face the same theorem for an enumerated datatype corresponding -to the days of the week, it would still follow a similar pattern. In -general, the general pattern for proving the property -$(x,y:R)\{x=y\}+\{\neg x =y\}$ for an enumerated type $R$ proceeds as -follow: -\begin{enumerate} -\item Analyze the cases for $x$. -\item For each of the sub-goals generated by the first step, analyze -the cases for $y$. -\item The remaining subgoals follow either by reflexivity or -by discrimination. -\end{enumerate} - -Let us describe how this general proof procedure can be introduced in -\Coq. - -\section{Tactic Macros} - -The simplest way to introduce it is to define it as new a -\textsl{tactic macro}, as follows: - -\begin{coq_example*} -Tactic Definition DecideEq [$a $b] := - [<:tactic:<Destruct $a; - Destruct $b; - (Left;Reflexivity) Orelse (Right;Discriminate)>>]. -\end{coq_example*} - -The general pattern of the proof is abstracted away using the -tacticals ``\texttt{;}'' and \texttt{Orelse}, and introducing two -parameters for the names of the arguments to be analyzed. - -Once defined, this tactic can be called like any other tactic, just -supplying the list of terms corresponding to its real arguments. Let us -revisit the proof of the former theorems using the new tactic -\texttt{DecideEq}: - -\begin{coq_example*} -Theorem decideBool : (x,y:bool){x=y}+{~x=y}. -DecideEq x y. -Defined. -\end{coq_example*} -\begin{coq_example*} -Theorem decideColor : (c1,c2:Color){c1=c2}+{~c1=c2}. -DecideEq c1 c2. -Defined. -\end{coq_example*} - -In general, the command \texttt{Tactic Definition} associates a name -to a parameterized tactic expression, built up from the tactics and -tacticals that are already available. The general syntax rule for this -command is the following: - -\begin{tabbing} -\texttt{Tactic Definition} \textit{tactic-name} \= -\texttt{[}\$$id_1\ldots \$id_n$\texttt{]}\\ -\> := \texttt{[<:tactic:<} \textit{tactic-expression} \verb+>>]+ -\end{tabbing} - -This command provides a quick but also very primitive mechanism for -introducing new tactics. It does not support recursive definitions, -and the arguments of a tactic macro are restricted to term -expressions. Moreover, there is no static checking of the definition -other than the syntactical one. Any error in the definition of the -tactic ---for instance, a call to an undefined tactic--- will not be -noticed until the tactic is called. - -%This command provides a very primitive mechanism for introducing new -%tactics. The arguments of a tactic macro are restricted to term -%expressions. Hence, it is not possible to define higher order tactics -%with this command. Also, there is no static checking of the definition -%other than syntactical. If the tactic contain errors in its definition -%--for instance, a call to an undefined tactic-- this will be noticed -%during the tactic call. - -Let us illustrate the weakness of this way of introducing new tactics -trying to extend our proof procedure to work on a larger class of -inductive types. Consider for example the decidability of equality -for pairs of booleans and colors: - -\begin{coq_example*} -Theorem decideBoolXColor : (p1,p2:bool*Color){p1=p2}+{~p1=p2}. -\end{coq_example*} - -The proof still proceeds by a double case analysis, but now the -constructors of the type take two arguments. Therefore, the sub-goals -that can not be solved by discrimination need further considerations -about the equality of such arguments: - -\begin{coq_example} - Destruct p1; - Destruct p2; Try (Right;Discriminate);Intros. -\end{coq_example} - -The half of the disjunction to be chosen depends on whether or not -$b=b_0$ and $c=c_0$. These equalities can be decided automatically -using the previous lemmas about booleans and colors. If both -equalities are satisfied, then it is sufficient to rewrite $b$ into -$b_0$ and $c$ into $c_0$, so that the left half of the goal follows by -reflexivity. Otherwise, the right half follows by first contraposing -the disequality, and then applying the invectiveness of the pairing -constructor. - -As the cases associated to each argument of the pair are very similar, -a tactic macro can be introduced to abstract this part of the proof: - -\begin{coq_example*} -Hints Resolve decideBool decideColor. -Tactic Definition SolveArg [$t1 $t2] := - [<:tactic:< - ElimType {$t1=$t2}+{~$t1=$t2}; - [(Intro equality;Rewrite equality;Clear equality) | - (Intro diseq; Right; Red; Intro absurd; - Apply diseq;Injection absurd;Trivial) | - Auto]>>]. -\end{coq_example*} - -This tactic is applied to each corresponding pair of arguments of the -arguments, until the goal can be solved by reflexivity: - -\begin{coq_example*} -SolveArg b b0; - SolveArg c c0; - Left; Reflexivity. -Defined. -\end{coq_example*} - -Therefore, a more general strategy for deciding the property -$(x,y:R)\{x=y\}+\{\neg x =y\}$ on $R$ can be sketched as follows: -\begin{enumerate} -\item Eliminate $x$ and then $y$. -\item Try discrimination to solve those goals where $x$ and $y$ has -been introduced by different constructors. -\item If $x$ and $y$ have been introduced by the same constructor, -then iterate the tactic \textsl{SolveArg} for each pair of -arguments. -\item Finally, solve the left half of the goal by reflexivity. -\end{enumerate} - -The implementation of this stronger proof strategy needs to perform a -term decomposition, in order to extract the list of arguments of each -constructor. It also requires the introduction of recursively defined -tactics, so that the \textsl{SolveArg} can be iterated on the lists of -arguments. These features are not supported by the \texttt{Tactic -Definition} command. One possibility could be extended this command in -order to introduce recursion, general parameter passing, -pattern-matching, etc, but this would quickly lead us to introduce the -whole \ocaml{} into \Coq\footnote{This is historically true. In fact, -\ocaml{} is a direct descendent of ML, a functional programming language -conceived language for programming the tactics of the theorem prover -LCF.}. Instead of doing this, we prefer to give to the user the -possibility of writing his/her own tactics directly in \ocaml{}, and then -to link them dynamically with \Coq's code. This requires a minimal -knowledge about \Coq's implementation. The next section provides an -overview of \Coq's architecture. - -%It is important to point out that the introduction of a new tactic -%never endangers the correction of the theorems proven in the extended -%system. In order to understand why, let us introduce briefly the system -%architecture. - -\section{An Overview of \Coq's Architecture} - -The implementation of \Coq\ is based on eight \textsl{logical -modules}. By ``module'' we mean here a logical piece of code having a -conceptual unity, that may concern several \ocaml{} files. By the sake of -organization, all the \ocaml{} files concerning a logical module are -grouped altogether into the same sub-directory. The eight modules -are: - -\begin{tabular}{lll} -1. & The logical framework & (directory \texttt{src/generic})\\ -2. & The language of constructions & (directory \texttt{src/constr})\\ -3. & The type-checker & (directory \texttt{src/typing})\\ -4. & The proof engine & (directory \texttt{src/proofs})\\ -5. & The language of basic tactics & (directory \texttt{src/tactics})\\ -6. & The vernacular interpreter & (directory \texttt{src/env})\\ -7. & The parser and the pretty-printer & (directory \texttt{src/parsing})\\ -8. & The standard library & (directory \texttt{src/lib}) -\end{tabular} - -\vspace{1em} - -The following sections briefly present each of the modules above. -This presentation is not intended to be a complete description of \Coq's -implementation, but rather a guideline to be read before taking a look -at the sources. For each of the modules, we also present some of its -most important functions, which are sufficient to implement a large -class of tactics. - - -\subsection[The Logical Framework]{The Logical Framework\label{LogicalFramework}} - -At the very heart of \Coq there is a generic untyped language for -expressing abstractions, applications and global constants. This -language is used as a meta-language for expressing the terms of the -Calculus of Inductive Constructions. General operations on terms like -collecting the free variables of an expression, substituting a term for -a free variable, etc, are expressed in this language. - -The meta-language \texttt{'op term} of terms has seven main -constructors: -\begin{itemize} -\item $(\texttt{VAR}\;id)$, a reference to a global identifier called $id$; -\item $(\texttt{Rel}\;n)$, a bound variable, whose binder is the $nth$ - binder up in the term; -\item $\texttt{DLAM}\;(x,t)$, a deBruijn's binder on the term $t$; -\item $\texttt{DLAMV}\;(x,vt)$, a deBruijn's binder on all the terms of - the vector $vt$; -\item $(\texttt{DOP0}\;op)$, a unary operator $op$; -\item $\texttt{DOP2}\;(op,t_1,t_2)$, the application of a binary -operator $op$ to the terms $t_1$ and $t_2$; -\item $\texttt{DOPN} (op,vt)$, the application of an n-ary operator $op$ to the -vector of terms $vt$. -\end{itemize} - -In this meta-language, bound variables are represented using the -so-called deBrujin's indexes. In this representation, an occurrence of -a bound variable is denoted by an integer, meaning the number of -binders that must be traversed to reach its own -binder\footnote{Actually, $(\texttt{Rel}\;n)$ means that $(n-1)$ binders -have to be traversed, since indexes are represented by strictly -positive integers.}. On the other hand, constants are referred by its -name, as usual. For example, if $A$ is a variable of the current -section, then the lambda abstraction $[x:A]x$ of the Calculus of -Constructions is represented in the meta-language by the term: - -\begin{displaymath} -(DOP2 (Lambda,(Var\;A),DLAM (x,(Rel\;1))) -\end{displaymath} - -In this term, $Lambda$ is a binary operator. Its first argument -correspond to the type $A$ of the bound variable, while the second is -a body of the abstraction, where $x$ is bound. The name $x$ is just kept -to pretty-print the occurrences of the bound variable. - -%Similarly, the product -%$(A:Prop)A$ of the Calculus of Constructions is represented by the -%term: -%\begin{displaumath} -%DOP2 (Prod, DOP0 (Sort (Prop Null)), DLAM (Name \#A, Rel 1)) -%\end{displaymath} - -The following functions perform some of the most frequent operations -on the terms of the meta-language: -\begin{description} -\fun{val Generic.subst1 : 'op term -> 'op term -> 'op term} - {$(\texttt{subst1}\;t_1\;t_2)$ substitutes $t_1$ for - $\texttt{(Rel}\;1)$ in $t_2$.} -\fun{val Generic.occur\_var : identifier -> 'op term -> bool} - {Returns true when the given identifier appears in the term, - and false otherwise.} -\fun{val Generic.eq\_term : 'op term -> 'op term -> bool} - {Implements $\alpha$-equality for terms.} -\fun{val Generic.dependent : 'op term -> 'op term -> bool} - {Returns true if the first term is a sub-term of the second.} -%\fun{val Generic.subst\_var : identifier -> 'op term -> 'op term} -% { $(\texttt{subst\_var}\;id\;t)$ substitutes the deBruijn's index -% associated to $id$ to every occurrence of the term -% $(\texttt{VAR}\;id)$ in $t$.} -\end{description} - -\subsubsection{Identifiers, names and sections paths.} - -Three different kinds of names are used in the meta-language. They are -all defined in the \ocaml{} file \texttt{Names}. - -\paragraph{Identifiers.} The simplest kind of names are -\textsl{identifiers}. An identifier is a string possibly indexed by an -integer. They are used to represent names that are not unique, like -for example the name of a variable in the scope of a section. The -following operations can be used for handling identifiers: - -\begin{description} -\fun{val Names.make\_ident : string -> int -> identifier} - {The value $(\texttt{make\_ident}\;x\;i)$ creates the - identifier $x_i$. If $i=-1$, then the identifier has - is created with no index at all.} -\fun{val Names.repr\_ident : identifier -> string * int} - {The inverse operation of \texttt{make\_ident}: - it yields the string and the index of the identifier.} -\fun{val Names.lift\_ident : identifier -> identifier} - {Increases the index of the identifier by one.} -\fun{val Names.next\_ident\_away : \\ -\qquad identifier -> identifier list -> identifier} - {\\ Generates a new identifier with the same root string than the - given one, but with a new index, different from all the indexes of - a given list of identifiers.} -\fun{val Names.id\_of\_string : string -> - identifier} - {Creates an identifier from a string.} -\fun{val Names.string\_of\_id : identifier -> string} - {The inverse operation: transforms an identifier into a string} -\end{description} - -\paragraph{Names.} A \textsl{name} is either an identifier or the -special name \texttt{Anonymous}. Names are used as arguments of -binders, in order to pretty print bound variables. -The following operations can be used for handling names: - -\begin{description} -\fun{val Names.Name: identifier -> Name} - {Constructs a name from an identifier.} -\fun{val Names.Anonymous : Name} - {Constructs a special, anonymous identifier, like the variable abstracted - in the term $[\_:A]0$.} -\fun{val - Names.next\_name\_away\_with\_default : \\ \qquad - string->name->identifier list->identifier} -{\\ If the name is not anonymous, then this function generates a new - identifier different from all the ones in a given list. Otherwise, it - generates an identifier from the given string.} -\end{description} - -\paragraph[Section paths.]{Section paths.\label{SectionPaths}} -A \textsl{section-path} is a global name to refer to an object without -ambiguity. It can be seen as a sort of filename, where open sections -play the role of directories. Each section path is formed by three -components: a \textsl{directory} (the list of open sections); a -\textsl{basename} (the identifier for the object); and a \textsl{kind} -(either CCI for the terms of the Calculus of Constructions, FW for the -the terms of $F_\omega$, or OBJ for other objects). For example, the -name of the following constant: -\begin{verbatim} - Section A. - Section B. - Section C. - Definition zero := O. -\end{verbatim} - -is internally represented by the section path: - -$$\underbrace{\mathtt{\#A\#B\#C}}_{\mbox{dirpath}} -\underbrace{\mathtt{\tt \#zero}}_{\mbox{basename}} -\underbrace{\mathtt{\tt .cci}_{\;}}_{\mbox{kind}}$$ - -When one of the sections is closed, a new constant is created with an -updated section-path,a nd the old one is no longer reachable. In our -example, after closing the section \texttt{C}, the new section-path -for the constant {\tt zero} becomes: -\begin{center} -\texttt{ \#A\#B\#zero.cci} -\end{center} - -The following operations can be used to handle section paths: - -\begin{description} -\fun{val Names.string\_of\_path : section\_path -> string} - {Transforms the section path into a string.} -\fun{val Names.path\_of\_string : string -> section\_path} - {Parses a string an returns the corresponding section path.} -\fun{val Names.basename : section\_path -> identifier} - {Provides the basename of a section path} -\fun{val Names.dirpath : section\_path -> string list} - {Provides the directory of a section path} -\fun{val Names.kind\_of\_path : section\_path -> path\_kind} - {Provides the kind of a section path} -\end{description} - -\subsubsection{Signatures} - -A \textsl{signature} is a mapping associating different informations -to identifiers (for example, its type, its definition, etc). The -following operations could be useful for working with signatures: - -\begin{description} -\fun{val Names.ids\_of\_sign : 'a signature -> identifier list} - {Gets the list of identifiers of the signature.} -\fun{val Names.vals\_of\_sign : 'a signature -> 'a list} - {Gets the list of values associated to the identifiers of the signature.} -\fun{val Names.lookup\_glob1 : \\ \qquad -identifier -> 'a signature -> (identifier * - 'a)} - {\\ Gets the value associated to a given identifier of the signature.} -\end{description} - - -\subsection{The Terms of the Calculus of Constructions} - -The language of the Calculus of Inductive Constructions described in -Chapter \ref{Cic} is implemented on the top of the logical framework, -instantiating the parameter $op$ of the meta-language with a -particular set of operators. In the implementation this language is -called \texttt{constr}, the language of constructions. - -% The only difference -%with respect to the one described in Section \ref{} is that the terms -%of \texttt{constr} may contain \textsl{existential variables}. An -%existential variable is a place holder representing a part of the term -%that is still to be constructed. Such ``open terms'' are necessary -%when building proofs interactively. - -\subsubsection{Building Constructions} - -The user does not need to know the choices made to represent -\texttt{constr} in the meta-language. They are abstracted away by the -following constructor functions: - -\begin{description} -\fun{val Term.mkRel : int -> constr} - {$(\texttt{mkRel}\;n)$ represents deBrujin's index $n$.} - -\fun{val Term.mkVar : identifier -> constr} - {$(\texttt{mkVar}\;id)$ - represents a global identifier named $id$, like a variable - inside the scope of a section, or a hypothesis in a proof}. - -\fun{val Term.mkExistential : constr} - {\texttt{mkExistential} represents an implicit sub-term, like the question - marks in the term \texttt{(pair ? ? O true)}.} - -%\fun{val Term.mkMeta : int -> constr} -% {$(\texttt{mkMeta}\;n)$ represents an existential variable, whose -% name is the integer $n$.} - -\fun{val Term.mkProp : constr} - {$\texttt{mkProp}$ represents the sort \textsl{Prop}.} - -\fun{val Term.mkSet : constr} - {$\texttt{mkSet}$ represents the sort \textsl{Set}.} - -\fun{val Term.mkType : Impuniv.universe -> constr} - {$(\texttt{mkType}\;u)$ represents the term - $\textsl{Type}(u)$. The universe $u$ is represented as a - section path indexed by an integer. } - -\fun{val Term.mkConst : section\_path -> constr array -> constr} - {$(\texttt{mkConst}\;c\;v)$ represents a constant whose name is - $c$. The body of the constant is stored in a global table, - accessible through the name of the constant. The array of terms - $v$ corresponds to the variables of the environment appearing in - the body of the constant when it was defined. For instance, a - constant defined in the section \textsl{Foo} containing the - variable $A$, and whose body is $[x:Prop\ra Prop](x\;A)$ is - represented inside the scope of the section by - $(\texttt{mkConst}\;\texttt{\#foo\#f.cci}\;[| \texttt{mkVAR}\;A - |])$. Once the section is closed, the constant is represented by - the term $(\texttt{mkConst}\;\#f.cci\;[| |])$, and its body - becomes $[A:Prop][x:Prop\ra Prop](x\;A)$}. - -\fun{val Term.mkMutInd : section\_path -> int -> constr array ->constr} - {$(\texttt{mkMutInd}\;c\;i)$ represents the $ith$ type - (starting from zero) of the block of mutually dependent - (co)inductive types, whose first type is $c$. Similarly to the - case of constants, the array of terms represents the current - environment of the (co)inductive type. The definition of the type - (its arity, its constructors, whether it is inductive or co-inductive, etc.) - is stored in a global hash table, accessible through the name of - the type.} - -\fun{val Term.mkMutConstruct : \\ \qquad section\_path -> int -> int -> constr array - ->constr} {\\ $(\texttt{mkMutConstruct}\;c\;i\;j)$ represents the - $jth$ constructor of the $ith$ type of the block of mutually - dependent (co)inductive types whose first type is $c$. The array - of terms represents the current environment of the (co)inductive - type.} - -\fun{val Term.mkCast : constr -> constr -> constr} - {$(\texttt{mkCast}\;t\;T)$ represents the annotated term $t::T$ in - \Coq's syntax.} - -\fun{val Term.mkProd : name ->constr ->constr -> constr} - {$(\texttt{mkProd}\;x\;A\;B)$ represents the product $(x:A)B$. - The free ocurrences of $x$ in $B$ are represented by deBrujin's - indexes.} - -\fun{val Term.mkNamedProd : identifier -> constr -> constr -> constr} - {$(\texttt{produit}\;x\;A\;B)$ represents the product $(x:A)B$, - but the bound occurrences of $x$ in $B$ are denoted by - the identifier $(\texttt{mkVar}\;x)$. The function automatically - changes each occurrences of this identifier into the corresponding - deBrujin's index.} - -\fun{val Term.mkArrow : constr -> constr -> constr} - {$(\texttt{arrow}\;A\;B)$ represents the type $(A\rightarrow B)$.} - -\fun{val Term.mkLambda : name -> constr -> constr -> constr} - {$(\texttt{mkLambda}\;x\;A\;b)$ represents the lambda abstraction - $[x:A]b$. The free ocurrences of $x$ in $B$ are represented by deBrujin's - indexes.} - -\fun{val Term.mkNamedLambda : identifier -> constr -> constr -> constr} - {$(\texttt{lambda}\;x\;A\;b)$ represents the lambda abstraction - $[x:A]b$, but the bound occurrences of $x$ in $B$ are denoted by - the identifier $(\texttt{mkVar}\;x)$. } - -\fun{val Term.mkAppLA : constr array -> constr} - {$(\texttt{mkAppLA}\;t\;[|t_1\ldots t_n|])$ represents the application - $(t\;t_1\;\ldots t_n)$.} - -\fun{val Term.mkMutCaseA : \\ \qquad - case\_info -> constr ->constr - ->constr array -> constr} - {\\ $(\texttt{mkMutCaseA}\;r\;P\;m\;[|f_1\ldots f_n|])$ - represents the term \Case{P}{m}{f_1\ldots f_n}. The first argument - $r$ is either \texttt{None} or $\texttt{Some}\;(c,i)$, where the - pair $(c,i)$ refers to the inductive type that $m$ belongs to.} - -\fun{val Term.mkFix : \\ \qquad -int array->int->constr array->name - list->constr array->constr} - {\\ $(\texttt{mkFix}\;[|k_1\ldots k_n |]\;i\;[|A_1\ldots - A_n|]\;[|f_1\ldots f_n|]\;[|t_1\ldots t_n|])$ represents the term - $\Fix{f_i}{f_1/k_1:A_1:=t_1 \ldots f_n/k_n:A_n:=t_n}$} - -\fun{val Term.mkCoFix : \\ \qquad - int -> constr array -> name list -> - constr array -> constr} - {\\ $(\texttt{mkCoFix}\;i\;[|A_1\ldots - A_n|]\;[|f_1\ldots f_n|]\;[|t_1\ldots t_n|])$ represents the term - $\CoFix{f_i}{f_1:A_1:=t_1 \ldots f_n:A_n:=t_n}$. There are no - decreasing indexes in this case.} -\end{description} - -\subsubsection{Decomposing Constructions} - -Each of the construction functions above has its corresponding -(partial) destruction function, whose name is obtained changing the -prefix \texttt{mk} by \texttt{dest}. In addition to these functions, a -concrete datatype \texttt{kindOfTerm} can be used to do pattern -matching on terms without dealing with their internal representation -in the meta-language. This concrete datatype is described in the \ocaml{} -file \texttt{term.mli}. The following function transforms a construction -into an element of type \texttt{kindOfTerm}: - -\begin{description} -\fun{val Term.kind\_of\_term : constr -> kindOfTerm} - {Destructs a term of the language \texttt{constr}, -yielding the direct components of the term. Hence, in order to do -pattern matching on an object $c$ of \texttt{constr}, it is sufficient -to do pattern matching on the value $(\texttt{kind\_of\_term}\;c)$.} -\end{description} - -Part of the information associated to the constants is stored in -global tables. The following functions give access to such -information: - -\begin{description} -\fun{val Termenv.constant\_value : constr -> constr} - {If the term denotes a constant, projects the body of a constant} -\fun{Termenv.constant\_type : constr -> constr} - {If the term denotes a constant, projects the type of the constant} -\fun{val mind\_arity : constr -> constr} - {If the term denotes an inductive type, projects its arity (i.e., - the type of the inductive type).} -\fun{val Termenv.mis\_is\_finite : mind\_specif -> bool} - {Determines whether a recursive type is inductive or co-inductive.} -\fun{val Termenv.mind\_nparams : constr -> int} - {If the term denotes an inductive type, projects the number of - its general parameters.} -\fun{val Termenv.mind\_is\_recursive : constr -> bool} - {If the term denotes an inductive type, - determines if the type has at least one recursive constructor. } -\fun{val Termenv.mind\_recargs : constr -> recarg list array array} - {If the term denotes an inductive type, returns an array $v$ such - that the nth element of $v.(i).(j)$ is - \texttt{Mrec} if the $nth$ argument of the $jth$ constructor of - the $ith$ type is recursive, and \texttt{Norec} if it is not.}. -\end{description} - -\subsection[The Type Checker]{The Type Checker\label{TypeChecker}} - -The third logical module is the type checker. It concentrates two main -tasks concerning the language of constructions. - -On one hand, it contains the type inference and type-checking -functions. The type inference function takes a term -$a$ and a signature $\Gamma$, and yields a term $A$ such that -$\Gamma \vdash a:A$. The type-checking function takes two terms $a$ -and $A$ and a signature $\Gamma$, and determines whether or not -$\Gamma \vdash a:A$. - -On the other hand, this module is in charge of the compilation of -\Coq's abstract syntax trees into the language \texttt{constr} of -constructions. This compilation seeks to eliminate all the ambiguities -contained in \Coq's abstract syntax, restoring the information -necessary to type-check it. It concerns at least the following steps: -\begin{enumerate} -\item Compiling the pattern-matching expressions containing -constructor patterns, wild-cards, etc, into terms that only -use the primitive \textsl{Case} described in Chapter \ref{Cic} -\item Restoring type coercions and synthesizing the implicit arguments -(the one denoted by question marks in -{\Coq} syntax: see Section~\ref{Coercions}). -\item Transforming the named bound variables into deBrujin's indexes. -\item Classifying the global names into the different classes of -constants (defined constants, constructors, inductive types, etc). -\end{enumerate} - -\subsection{The Proof Engine} - -The fourth stage of \Coq's implementation is the \textsl{proof engine}: -the interactive machine for constructing proofs. The aim of the proof -engine is to construct a top-down derivation or \textsl{proof tree}, -by the application of \textsl{tactics}. A proof tree has the following -general structure:\\ - -\begin{displaymath} -\frac{\Gamma \vdash ? = t(?_1,\ldots?_n) : G} - {\hspace{3ex}\frac{\displaystyle \Gamma_1 \vdash ?_1 = t_1(\ldots) : G_1} - {\stackrel{\vdots}{\displaystyle {\Gamma_{i_1} \vdash ?_{i_1} - : G_{i_1}}}}(tac_1) - \;\;\;\;\;\;\;\;\; - \frac{\displaystyle \Gamma_n \vdash ?_n = t_n(\ldots) : G_n} - {\displaystyle \stackrel{\vdots}{\displaystyle {\Gamma_{i_m} \vdash ?_{i_m} : - G_{i_m}}}}(tac_n)} (tac) -\end{displaymath} - - -\noindent Each node of the tree is called a \textsl{goal}. A goal -is a record type containing the following three fields: -\begin{enumerate} -\item the conclusion $G$ to be proven; -\item a typing signature $\Gamma$ for the free variables in $G$; -\item if the goal is an internal node of the proof tree, the -definition $t(?_1,\ldots?_n)$ of an \textsl{existential variable} -(i.e. a possible undefined constant) $?$ of type $G$ in terms of the -existential variables of the children sub-goals. If the node is a -leaf, the existential variable maybe still undefined. -\end{enumerate} - -Once all the existential variables have been defined the derivation is -completed, and a construction can be generated from the proof tree, -replacing each of the existential variables by its definition. This -is exactly what happens when one of the commands -\texttt{Qed}, \texttt{Save} or \texttt{Defined} is invoked -(see Section~\ref{Qed}). The saved theorem becomes a defined constant, -whose body is the proof object generated. - -\paragraph{Important:} Before being added to the -context, the proof object is type-checked, in order to verify that it is -actually an object of the expected type $G$. Hence, the correctness -of the proof actually does not depend on the tactics applied to -generate it or the machinery of the proof engine, but only on the -type-checker. In other words, extending the system with a potentially -bugged new tactic never endangers the consistency of the system. - -\subsubsection[What is a Tactic?]{What is a Tactic?\label{WhatIsATactic}} -%Let us now explain what is a tactic, and how the user can introduce -%new ones. - -From an operational point of view, the current state of the proof -engine is given by the mapping $emap$ from existential variables into -goals, plus a pointer to one of the leaf goals $g$. Such a pointer -indicates where the proof tree will be refined by the application of a -\textsl{tactic}. A tactic is a function from the current state -$(g,emap)$ of the proof engine into a pair $(l,val)$. The first -component of this pair is the list of children sub-goals $g_1,\ldots -g_n$ of $g$ to be yielded by the tactic. The second one is a -\textsl{validation function}. Once the proof trees $\pi_1,\ldots -\pi_n$ for $g_1,\ldots g_n$ have been completed, this validation -function must yield a proof tree $(val\;\pi_1,\ldots \pi_n)$ deriving -$g$. - -Tactics can be classified into \textsl{primitive} ones and -\textsl{defined} ones. Primitive tactics correspond to the five basic -operations of the proof engine: - -\begin{enumerate} -\item Introducing a universally quantified variable into the local -context of the goal. -\item Defining an undefined existential variable -\item Changing the conclusion of the goal for another ---definitionally equal-- term. -\item Changing the type of a variable in the local context for another -definitionally equal term. -\item Erasing a variable from the local context. -\end{enumerate} - -\textsl{Defined} tactics are tactics constructed by combining these -primitive operations. Defined tactics are registered in a hash table, -so that they can be introduced dynamically. In order to define such a -tactic table, it is necessary to fix what a \textsl{possible argument} -of a tactic may be. The type \texttt{tactic\_arg} of the possible -arguments for tactics is a union type including: -\begin{itemize} -\item quoted strings; -\item integers; -\item identifiers; -\item lists of identifiers; -\item plain terms, represented by its abstract syntax tree; -\item well-typed terms, represented by a construction; -\item a substitution for bound variables, like the -substitution in the tactic \\$\texttt{Apply}\;t\;\texttt{with}\;x:=t_1\ldots -x_n:=t_n$, (see Section~\ref{apply}); -\item a reduction expression, denoting the reduction strategy to be -followed. -\end{itemize} -Therefore, for each function $tac:a \rightarrow tactic$ implementing a -defined tactic, an associated dynamic tactic $tacargs\_tac: -\texttt{tactic\_arg}\;list \rightarrow tactic$ calling $tac$ must be -written. The aim of the auxiliary function $tacargs\_tac$ is to inject -the arguments of the tactic $tac$ into the type of possible arguments -for a tactic. - -The following function can be used for registering and calling a -defined tactic: - -\begin{description} -\fun{val Tacmach.add\_tactic : \\ \qquad -string -> (tactic\_arg list ->tactic) -> unit} - {\\ Registers a dynamic tactic with the given string as access index.} -\fun{val Tacinterp.vernac\_tactic : string*tactic\_arg list -> tactic} - {Interprets a defined tactic given by its entry in the - tactics table with a particular list of possible arguments.} -\fun{val Tacinterp.vernac\_interp : CoqAst.t -> tactic} - {Interprets a tactic expression formed combining \Coq's tactics and - tacticals, and described by its abstract syntax tree.} -\end{description} - -When programming a new tactic that calls an already defined tactic -$tac$, we have the choice between using the \ocaml{} function -implementing $tac$, or calling the tactic interpreter with the name -and arguments for interpreting $tac$. In the first case, a tactic call -will left the trace of the whole implementation of $tac$ in the proof -tree. In the second, the implementation of $tac$ will be hidden, and -only an invocation of $tac$ will be recalled (cf. the example of -Section \ref{ACompleteExample}. The following combinators can be used -to hide the implementation of a tactic: - -\begin{verbatim} -type 'a hiding_combinator = string -> ('a -> tactic) -> ('a -> tactic) -val Tacmach.hide_atomic_tactic : string -> tactic -> tactic -val Tacmach.hide_constr_tactic : constr hiding_combinator -val Tacmach.hide_constrl_tactic : (constr list) hiding_combinator -val Tacmach.hide_numarg_tactic : int hiding_combinator -val Tacmach.hide_ident_tactic : identifier hiding_combinator -val Tacmach.hide_identl_tactic : identifier hiding_combinator -val Tacmach.hide_string_tactic : string hiding_combinator -val Tacmach.hide_bindl_tactic : substitution hiding_combinator -val Tacmach.hide_cbindl_tactic : - (constr * substitution) hiding_combinator -\end{verbatim} - -These functions first register the tactic by a side effect, and then -yield a function calling the interpreter with the registered name and -the right injection into the type of possible arguments. - -\subsection{Tactics and Tacticals Provided by \Coq} - -The fifth logical module is the library of tacticals and basic tactics -provided by \Coq. This library is distributed into the directories -\texttt{tactics} and \texttt{src/tactics}. The former contains those -basic tactics that make use of the types contained in the basic state -of \Coq. For example, inversion or rewriting tactics are in the -directory \texttt{tactics}, since they make use of the propositional -equality type. Those tactics which are independent from the context ---like for example \texttt{Cut}, \texttt{Intros}, etc-- are defined in -the directory \texttt{src/tactics}. This latter directory also -contains some useful tools for programming new tactics, referred in -Section \ref{SomeUsefulToolsforWrittingTactics}. - -In practice, it is very unusual that the list of sub-goals and the -validation function of the tactic must be explicitly constructed by -the user. In most of the cases, the implementation of a new tactic -consists in supplying the appropriate arguments to the basic tactics -and tacticals. - -\subsubsection{Basic Tactics} - -The file \texttt{Tactics} contain the implementation of the basic -tactics provided by \Coq. The following tactics are some of the most -used ones: - -\begin{verbatim} -val Tactics.intro : tactic -val Tactics.assumption : tactic -val Tactics.clear : identifier list -> tactic -val Tactics.apply : constr -> constr substitution -> tactic -val Tactics.one_constructor : int -> constr substitution -> tactic -val Tactics.simplest_elim : constr -> tactic -val Tactics.elimType : constr -> tactic -val Tactics.simplest_case : constr -> tactic -val Tactics.caseType : constr -> tactic -val Tactics.cut : constr -> tactic -val Tactics.reduce : redexpr -> tactic -val Tactics.exact : constr -> tactic -val Auto.auto : int option -> tactic -val Auto.trivial : tactic -\end{verbatim} - -The functions hiding the implementation of these tactics are defined -in the module \texttt{Hiddentac}. Their names are prefixed by ``h\_''. - -\subsubsection[Tacticals]{Tacticals\label{OcamlTacticals}} - -The following tacticals can be used to combine already existing -tactics: - -\begin{description} -\fun{val Tacticals.tclIDTAC : tactic} - {The identity tactic: it leaves the goal as it is.} - -\fun{val Tacticals.tclORELSE : tactic -> tactic -> tactic} - {Tries the first tactic and in case of failure applies the second one.} - -\fun{val Tacticals.tclTHEN : tactic -> tactic -> tactic} - {Applies the first tactic and then the second one to each generated subgoal.} - -\fun{val Tacticals.tclTHENS : tactic -> tactic list -> tactic} - {Applies a tactic, and then applies each tactic of the tactic list to the - corresponding generated subgoal.} - -\fun{val Tacticals.tclTHENL : tactic -> tactic -> tactic} - {Applies the first tactic, and then applies the second one to the last - generated subgoal.} - -\fun{val Tacticals.tclREPEAT : tactic -> tactic} - {If the given tactic succeeds in producing a subgoal, then it - is recursively applied to each generated subgoal, - and so on until it fails. } - -\fun{val Tacticals.tclFIRST : tactic list -> tactic} - {Tries the tactics of the given list one by one, until one of them - succeeds.} - -\fun{val Tacticals.tclTRY : tactic -> tactic} - {Tries the given tactic and in case of failure applies the {\tt - tclIDTAC} tactical to the original goal.} - -\fun{val Tacticals.tclDO : int -> tactic -> tactic} - {Applies the tactic a given number of times.} - -\fun{val Tacticals.tclFAIL : tactic} - {The always failing tactic: it raises a {\tt UserError} exception.} - -\fun{val Tacticals.tclPROGRESS : tactic -> tactic} - {Applies the given tactic to the current goal and fails if the - tactic leaves the goal unchanged} - -\fun{val Tacticals.tclNTH\_HYP : int -> (constr -> tactic) -> tactic} - {Applies a tactic to the nth hypothesis of the local context. - The last hypothesis introduced correspond to the integer 1.} - -\fun{val Tacticals.tclLAST\_HYP : (constr -> tactic) -> tactic} - {Applies a tactic to the last hypothesis introduced.} - -\fun{val Tacticals.tclCOMPLETE : tactic -> tactic} - {Applies a tactic and fails if the tactic did not solve completely the - goal} - -\fun{val Tacticals.tclMAP : ('a -> tactic) -> 'a list -> tactic} - {Applied to the function \texttt{f} and the list \texttt{[x\_1; - ... ; x\_n]}, this tactical applies the tactic - \texttt{tclTHEN (f x1) (tclTHEN (f x2) ... ))))}} - -\fun{val Tacicals.tclIF : (goal sigma -> bool) -> tactic -> tactic -> tactic} - {If the condition holds, apply the first tactic; otherwise, - apply the second one} - -\end{description} - - -\subsection{The Vernacular Interpreter} - -The sixth logical module of the implementation corresponds to the -interpreter of the vernacular phrases of \Coq. These phrases may be -expressions from the \gallina{} language (definitions), general -directives (setting commands) or tactics to be applied by the proof -engine. - -\subsection[The Parser and the Pretty-Printer]{The Parser and the Pretty-Printer\label{PrettyPrinter}} - -The last logical module is the parser and pretty printer of \Coq, -which is the interface between the vernacular interpreter and the -user. They translate the chains of characters entered at the input -into abstract syntax trees, and vice versa. Abstract syntax trees are -represented by labeled n-ary trees, and its type is called -\texttt{CoqAst.t}. For instance, the abstract syntax tree associated -to the term $[x:A]x$ is: - -\begin{displaymath} -\texttt{Node} - ((0,6), "LAMBDA", - [\texttt{Nvar}~((3, 4),"A");~\texttt{Slam}~((0,6),~Some~"x",~\texttt{Nvar}~((5,6),"x"))]) -\end{displaymath} - -The numbers correspond to \textsl{locations}, used to point to some -input line and character positions in the error messages. As it was -already explained in Section \ref{TypeChecker}, this term is then -translated into a construction term in order to be typed. - -The parser of \Coq\ is implemented using \camlpppp. The lexer and the data -used by \camlpppp\ to generate the parser lay in the directory -\texttt{src/parsing}. This directory also contains \Coq's -pretty-printer. The printing rules lay in the directory -\texttt{src/syntax}. The different entries of the grammar are -described in the module \texttt{Pcoq.Entry}. Let us present here two -important functions of this logical module: - -\begin{description} -\fun{val Pcoq.parse\_string : 'a Grammar.Entry.e -> string -> 'a} - {Parses a given string, trying to recognize a phrase - corresponding to some entry in the grammar. If it succeeds, - it yields a value associated to the grammar entry. For example, - applied to the entry \texttt{Pcoq.Command.command}, this function - parses a term of \Coq's language, and yields a value of type - \texttt{CoqAst.t}. When applied to the entry - \texttt{Pcoq.Vernac.vernac}, it parses a vernacular command and - returns the corresponding Ast.} -\fun{val gentermpr : \\ \qquad -path\_kind -> constr assumptions -> constr -> std\_ppcmds} - {\\ Pretty-prints a well-typed term of certain kind (cf. Section - \ref{SectionPaths}) under its context of typing assumption.} -\fun{val gentacpr : CoqAst.t -> std\_ppcmds} - {Pretty-prints a given abstract syntax tree representing a tactic - expression.} -\end{description} - -\subsection{The General Library} - -In addition to the ones laying in the standard library of \ocaml{}, -several useful modules about lists, arrays, sets, mappings, balanced -trees, and other frequently used data structures can be found in the -directory \texttt{lib}. Before writing a new one, check if it is not -already there! - -\subsubsection{The module \texttt{Std}} -This module in the directory \texttt{src/lib/util} is opened by almost -all modules of \Coq{}. Among other things, it contains a definition of -the different kinds of errors used in \Coq{} : - -\begin{description} -\fun{exception UserError of string * std\_ppcmds} - {This is the class of ``users exceptions''. Such errors arise when - the user attempts to do something illegal, for example \texttt{Intro} - when the current goal conclusion is not a product.} - -\fun{val Std.error : string -> 'a} - {For simple error messages} -\fun{val Std.errorlabstrm : string -> std\_ppcmds -> 'a} - {See Section~\ref{PrettyPrinter} : this can be used if the user - want to display a term or build a complex error message} - -\fun{exception Anomaly of string * std\_ppcmds} - {This for reporting bugs or things that should not - happen. The tacticals \texttt{tclTRY} and - \texttt{tclTRY} described in Section~\ref{OcamlTacticals} catch the - exceptions of type \texttt{UserError}, but they don't catch the - anomalies. So, in your code, don't raise any anomaly, unless you - know what you are doing. We also recommend to avoid constructs - such as \texttt{try ... with \_ -> ...} : such constructs can trap - an anomaly and make the debugging process harder.} - -\fun{val Std.anomaly : string -> 'a}{} -\fun{val Std.anomalylabstrm : string -> std\_ppcmds -> 'a}{} -\end{description} - -\section{The tactic writer mini-HOWTO} - -\subsection{How to add a vernacular command} - -The command to register a vernacular command can be found -in module \texttt{Vernacinterp}: - -\begin{verbatim} -val vinterp_add : string * (vernac_arg list -> unit -> unit) -> unit;; -\end{verbatim} - -The first argument is the name, the second argument is a function that -parses the arguments and returns a function of type -\texttt{unit}$\rightarrow$\texttt{unit} that do the job. - -In this section we will show how to add a vernacular command -\texttt{CheckCheck} that print a type of a term and the type of its -type. - -File \texttt{dcheck.ml}: - -\begin{verbatim} -open Vernacinterp;; -open Trad;; -let _ = - vinterp_add - ("DblCheck", - function [VARG_COMMAND com] -> - (fun () -> - let evmap = Evd.mt_evd () - and sign = Termenv.initial_sign () in - let {vAL=c;tYP=t;kIND=k} = - fconstruct_with_univ evmap sign com in - Pp.mSGNL [< Printer.prterm c; 'sTR ":"; - Printer.prterm t; 'sTR ":"; - Printer.prterm k >] ) - | _ -> bad_vernac_args "DblCheck") -;; -\end{verbatim} - -Like for a new tactic, a new syntax entry must be created. - -File \texttt{DCheck.v}: - -\begin{verbatim} -Declare ML Module "dcheck.ml". - -Grammar vernac vernac := - dblcheck [ "CheckCheck" comarg($c) ] -> [(DblCheck $c)]. -\end{verbatim} - -We are now able to test our new command: - -\begin{verbatim} -Coq < Require DCheck. -Coq < CheckCheck O. -O:nat:Set -\end{verbatim} - -Most Coq vernacular commands are registered in the module - \verb+src/env/vernacentries.ml+. One can see more examples here. - -\subsection{How to keep a hashtable synchronous with the reset mechanism} - -This is far more tricky. Some vernacular commands modify some -sort of state (for example by adding something in a hashtable). One -wants that \texttt{Reset} has the expected behavior with this -commands. - -\Coq{} provides a general mechanism to do that. \Coq{} environments -contains objects of three kinds: CCI, FW and OBJ. CCI and FW are for -constants of the calculus. OBJ is a dynamically extensible datatype -that contains sections, tactic definitions, hints for auto, and so -on. - -The simplest example of use of such a mechanism is in file -\verb+src/proofs/macros.ml+ (which implements the \texttt{Tactic - Definition} command). Tactic macros are stored in the imperative -hashtable \texttt{mactab}. There are two functions freeze and unfreeze -to make a copy of the table and to restore the state of table from the -copy. Then this table is declared using \texttt{Library.declare\_summary}. - -What does \Coq{} with that ? \Coq{} defines synchronization points. -At each synchronisation point, the declared tables are frozen (that -is, a copy of this tables is stored). - -When \texttt{Reset }$i$ is called, \Coq{} goes back to the first -synchronisation point that is above $i$ and ``replays'' all objects -between that point -and $i$. It will re-declare constants, re-open section, etc. - -So we need to declare a new type of objects, TACTIC-MACRO-DATA. To -``replay'' on object of that type is to add the corresponding tactic -macro to \texttt{mactab} - -So, now, we can say that \texttt{mactab} is synchronous with the Reset -mechanism$^{\mathrm{TM}}$. - -Notice that this works for hash tables but also for a single integer -(the Undo stack size, modified by the \texttt{Set Undo} command, for -example). - -\subsection{The right way to access to Coq constants from your ML code} - -With their long names, Coq constants are stored using: - -\begin{itemize} -\item a section path -\item an identifier -\end{itemize} - -The identifier is exactly the identifier that is used in \Coq{} to -denote the constant; the section path can be known using the -\texttt{Locate} command: - -\begin{coq_example} - Locate S. - Locate nat. - Locate eq. -\end{coq_example} - -Now it is easy to get a constant by its name and section path: - - -\begin{verbatim} -let constant sp id = - Machops.global_reference (Names.gLOB (Termenv.initial_sign ())) - (Names.path_of_string sp) (Names.id_of_string id);; -\end{verbatim} - - -The only issue is that if one cannot put: - - -\begin{verbatim} -let coq_S = constant "#Datatypes#nat.cci" "S";; -\end{verbatim} - - -in his tactic's code. That is because this sentence is evaluated -\emph{before} the module \texttt{Datatypes} is loaded. The solution is -to use the lazy evaluation of \ocaml{}: - - -\begin{verbatim} -let coq_S = lazy (constant "#Datatypes#nat.cci" "S");; - -... (Lazy.force coq_S) ... -\end{verbatim} - - -Be sure to call always Lazy.force behind a closure -- i.e. inside a -function body or behind the \texttt{lazy} keyword. - -One can see examples of that technique in the source code of \Coq{}, -for example -\verb+plugins/omega/coq_omega.ml+. - -\section[Some Useful Tools for Writing Tactics]{Some Useful Tools for Writing Tactics\label{SomeUsefulToolsforWrittingTactics}} -When the implementation of a tactic is not a straightforward -combination of tactics and tacticals, the module \texttt{Tacmach} -provides several useful functions for handling goals, calling the -type-checker, parsing terms, etc. This module is intended to be -the interface of the proof engine for the user. - -\begin{description} -\fun{val Tacmach.pf\_hyps : goal sigma -> constr signature} - {Projects the local typing context $\Gamma$ from a given goal $\Gamma\vdash ?:G$.} -\fun{val pf\_concl : goal sigma -> constr} - {Projects the conclusion $G$ from a given goal $\Gamma\vdash ?:G$.} -\fun{val Tacmach.pf\_nth\_hyp : goal sigma -> int -> identifier * - constr} - {Projects the $ith$ typing constraint $x_i:A_i$ from the local - context of the given goal.} -\fun{val Tacmach.pf\_fexecute : goal sigma -> constr -> judgement} - {Given a goal whose local context is $\Gamma$ and a term $a$, this - function infers a type $A$ and a kind $K$ such that the judgement - $a:A:K$ is valid under $\Gamma$, or raises an exception if there - is no such judgement. A judgement is just a record type containing - the three terms $a$, $A$ and $K$.} -\fun{val Tacmach.pf\_infexecute : \\ - \qquad -goal sigma -> constr -> judgement * information} - {\\ In addition to the typing judgement, this function also extracts - the $F_{\omega}$ program underlying the term.} -\fun{val Tacmach.pf\_type\_of : goal sigma -> constr -> constr} - {Infers a term $A$ such that $\Gamma\vdash a:A$ for a given term - $a$, where $\Gamma$ is the local typing context of the goal.} -\fun{val Tacmach.pf\_check\_type : goal sigma -> constr -> constr -> bool} - {This function yields a type $A$ if the two given terms $a$ and $A$ verify $\Gamma\vdash - a:A$ in the local typing context $\Gamma$ of the goal. Otherwise, - it raises an exception.} -\fun{val Tacmach.pf\_constr\_of\_com : goal sigma -> CoqAst.t -> constr} - {Transforms an abstract syntax tree into a well-typed term of the - language of constructions. Raises an exception if the term cannot - be typed.} -\fun{val Tacmach.pf\_constr\_of\_com\_sort : goal sigma -> CoqAst.t -> constr} - {Transforms an abstract syntax tree representing a type into - a well-typed term of the language of constructions. Raises an - exception if the term cannot be typed.} -\fun{val Tacmach.pf\_parse\_const : goal sigma -> string -> constr} - {Constructs the constant whose name is the given string.} -\fun{val -Tacmach.pf\_reduction\_of\_redexp : \\ - \qquad goal sigma -> red\_expr -> constr -> constr} - {\\ Applies a certain kind of reduction function, specified by an - element of the type red\_expr.} -\fun{val Tacmach.pf\_conv\_x : goal sigma -> constr -> constr -> bool} - {Test whether two given terms are definitionally equal.} -\end{description} - -\subsection[Patterns]{Patterns\label{Patterns}} - -The \ocaml{} file \texttt{Pattern} provides a quick way for describing a -term pattern and performing second-order, binding-preserving, matching -on it. Patterns are described using an extension of \Coq's concrete -syntax, where the second-order meta-variables of the pattern are -denoted by indexed question marks. - -Patterns may depend on constants, and therefore only to make have -sense when certain theories have been loaded. For this reason, they -are stored with a \textsl{module-marker}, telling us which modules -have to be open in order to use the pattern. The following functions -can be used to store and retrieve patterns form the pattern table: - -\begin{description} -\fun{val Pattern.make\_module\_marker : string list -> module\_mark} - {Constructs a module marker from a list of module names.} -\fun{val Pattern.put\_pat : module\_mark -> string -> marked\_term} - {Constructs a pattern from a parseable string containing holes - and a module marker.} -\fun{val Pattern.somatches : constr -> marked\_term-> bool} - {Tests if a term matches a pattern.} -\fun{val dest\_somatch : constr -> marked\_term -> constr list} - {If the term matches the pattern, yields the list of sub-terms - matching the occurrences of the pattern variables (ordered from - left to right). Raises a \texttt{UserError} exception if the term - does not match the pattern.} -\fun{val Pattern.soinstance : marked\_term -> constr list -> constr} - {Substitutes each hole in the pattern - by the corresponding term of the given the list.} -\end{description} - -\paragraph{Warning:} Sometimes, a \Coq\ term may have invisible -sub-terms that the matching functions are nevertheless sensible to. -For example, the \Coq\ term $(?_1,?_2)$ is actually a shorthand for -the expression $(\texttt{pair}\;?\;?\;?_1\;?_2)$. -Hence, matching this term pattern -with the term $(\texttt{true},\texttt{O})$ actually yields the list -$[?;?;\texttt{true};\texttt{O}]$ as result (and \textbf{not} -$[\texttt{true};\texttt{O}]$, as could be expected). - -\subsection{Patterns on Inductive Definitions} - -The module \texttt{Pattern} also includes some functions for testing -if the definition of an inductive type satisfies certain -properties. Such functions may be used to perform pattern matching -independently from the name given to the inductive type and the -universe it inhabits. They yield the value $(\texttt{Some}\;r::l)$ if -the input term reduces into an application of an inductive type $r$ to -a list of terms $l$, and the definition of $r$ satisfies certain -conditions. Otherwise, they yield the value \texttt{None}. - -\begin{description} -\fun{val Pattern.match\_with\_non\_recursive\_type : constr list option} - {Tests if the inductive type $r$ has no recursive constructors} -\fun{val Pattern.match\_with\_disjunction : constr list option} - {Tests if the inductive type $r$ is a non-recursive type - such that all its constructors have a single argument.} -\fun{val Pattern.match\_with\_conjunction : constr list option} - {Tests if the inductive type $r$ is a non-recursive type - with a unique constructor.} -\fun{val Pattern.match\_with\_empty\_type : constr list option} - {Tests if the inductive type $r$ has no constructors at all} -\fun{val Pattern.match\_with\_equation : constr list option} - {Tests if the inductive type $r$ has a single constructor - expressing the property of reflexivity for some type. For - example, the types $a=b$, $A\mbox{==}B$ and $A\mbox{===}B$ satisfy - this predicate.} -\end{description} - -\subsection{Elimination Tacticals} - -It is frequently the case that the subgoals generated by an -elimination can all be solved in a similar way, possibly parametrized -on some information about each case, like for example: -\begin{itemize} -\item the inductive type of the object being eliminated; -\item its arguments (if it is an inductive predicate); -\item the branch number; -\item the predicate to be proven; -\item the number of assumptions to be introduced by the case -\item the signature of the branch, i.e., for each argument of -the branch whether it is recursive or not. -\end{itemize} - -The following tacticals can be useful to deal with such situations. -They - -\begin{description} -\fun{val Elim.simple\_elimination\_then : \\ \qquad -(branch\_args -> tactic) -> constr -> tactic} - {\\ Performs the default elimination on the last argument, and then - tries to solve the generated subgoals using a given parametrized - tactic. The type branch\_args is a record type containing all - information mentioned above.} -\fun{val Elim.simple\_case\_then : \\ \qquad -(branch\_args -> tactic) -> constr -> tactic} - {\\ Similarly, but it performs case analysis instead of induction.} -\end{description} - -\section[A Complete Example]{A Complete Example\label{ACompleteExample}} - -In order to illustrate the implementation of a new tactic, let us come -back to the problem of deciding the equality of two elements of an -inductive type. - -\subsection{Preliminaries} - -Let us call \texttt{newtactic} the directory that will contain the -implementation of the new tactic. In this directory will lay two -files: a file \texttt{eqdecide.ml}, containing the \ocaml{} sources that -implements the tactic, and a \Coq\ file \texttt{Eqdecide.v}, containing -its associated grammar rules and the commands to generate a module -that can be loaded dynamically from \Coq's toplevel. - -To compile our project, we will create a \texttt{Makefile} with the -command \texttt{do\_Makefile} (see Section~\ref{Makefile}) : - -\begin{quotation} - \texttt{do\_Makefile eqdecide.ml EqDecide.v > Makefile}\\ - \texttt{touch .depend}\\ - \texttt{make depend} -\end{quotation} - -We must have kept the sources of \Coq{} somewhere and to set an -environment variable \texttt{COQTOP} that points to that directory. - -\subsection{Implementing the Tactic} - -The file \texttt{eqdecide.ml} contains the implementation of the -tactic in \ocaml{}. Let us recall the main steps of the proof strategy -for deciding the proposition $(x,y:R)\{x=y\}+\{\neg x=y\}$ on the -inductive type $R$: -\begin{enumerate} -\item Eliminate $x$ and then $y$. -\item Try discrimination to solve those goals where $x$ and $y$ has -been introduced by different constructors. -\item If $x$ and $y$ have been introduced by the same constructor, - then analyze one by one the corresponding pairs of arguments. - If they are equal, rewrite one into the other. If they are - not, derive a contradiction from the invectiveness of the - constructor. -\item Once all the arguments have been rewritten, solve the left half -of the goal by reflexivity. -\end{enumerate} - -In the sequel we implement these steps one by one. We start opening -the modules necessary for the implementation of the tactic: - -\begin{verbatim} -open Names -open Term -open Tactics -open Tacticals -open Hiddentac -open Equality -open Auto -open Pattern -open Names -open Termenv -open Std -open Proof_trees -open Tacmach -\end{verbatim} - -The first step of the procedure can be straightforwardly implemented as -follows: - -\begin{verbatim} -let clear_last = (tclLAST_HYP (fun c -> (clear_one (destVar c))));; -\end{verbatim} - -\begin{verbatim} -let mkBranches = - (tclTHEN intro - (tclTHEN (tclLAST_HYP h_simplest_elim) - (tclTHEN clear_last - (tclTHEN intros - (tclTHEN (tclLAST_HYP h_simplest_case) - (tclTHEN clear_last - intros))))));; -\end{verbatim} - -Notice the use of the tactical \texttt{tclLAST\_HYP}, which avoids to -give a (potentially clashing) name to the quantified variables of the -goal when they are introduced. - -The second step of the procedure is implemented by the following -tactic: - -\begin{verbatim} -let solveRightBranch = (tclTHEN simplest_right discrConcl);; -\end{verbatim} - -In order to illustrate how the implementation of a tactic can be -hidden, let us do it with the tactic above: - -\begin{verbatim} -let h_solveRightBranch = - hide_atomic_tactic "solveRightBranch" solveRightBranch -;; -\end{verbatim} - -As it was already mentioned in Section \ref{WhatIsATactic}, the -combinator \texttt{hide\_atomic\_tactic} first registers the tactic -\texttt{solveRightBranch} in the table, and returns a tactic which -calls the interpreter with the used to register it. Hence, when the -tactical \texttt{Info} is used, our tactic will just inform that -\texttt{solveRightBranch} was applied, omitting all the details -corresponding to \texttt{simplest\_right} and \texttt{discrConcl}. - - - -The third step requires some auxiliary functions for constructing the -type $\{c_1=c_2\}+\{\neg c_1=c_2\}$ for a given inductive type $R$ and -two constructions $c_1$ and $c_2$, and for generalizing this type over -$c_1$ and $c_2$: - -\begin{verbatim} -let mmk = make_module_marker ["#Logic.obj";"#Specif.obj"];; -let eqpat = put_pat mmk "eq";; -let sumboolpat = put_pat mmk "sumbool";; -let notpat = put_pat mmk "not";; -let eq = get_pat eqpat;; -let sumbool = get_pat sumboolpat;; -let not = get_pat notpat;; - -let mkDecideEqGoal rectype c1 c2 g = - let equality = mkAppL [eq;rectype;c1;c2] in - let disequality = mkAppL [not;equality] - in mkAppL [sumbool;equality;disequality] -;; -let mkGenDecideEqGoal rectype g = - let hypnames = ids_of_sign (pf_hyps g) in - let xname = next_ident_away (id_of_string "x") hypnames - and yname = next_ident_away (id_of_string "y") hypnames - in (mkNamedProd xname rectype - (mkNamedProd yname rectype - (mkDecideEqGoal rectype (mkVar xname) (mkVar yname) g))) -;; -\end{verbatim} - -The tactic will depend on the \Coq modules \texttt{Logic} and -\texttt{Specif}, since we use the constants corresponding to -propositional equality (\texttt{eq}), computational disjunction -(\texttt{sumbool}), and logical negation (\texttt{not}), defined in -that modules. This is specified creating the module maker -\texttt{mmk} (see Section~\ref{Patterns}). - -The third step of the procedure can be divided into three sub-steps. -Assume that both $x$ and $y$ have been introduced by the same -constructor. For each corresponding pair of arguments of that -constructor, we have to consider whether they are equal or not. If -they are equal, the following tactic is applied to rewrite one into -the other: - -\begin{verbatim} -let eqCase tac = - (tclTHEN intro - (tclTHEN (tclLAST_HYP h_rewriteLR) - (tclTHEN clear_last - tac))) -;; -\end{verbatim} - - -If they are not equal, then the goal is contraposed and a -contradiction is reached form the invectiveness of the constructor: - -\begin{verbatim} -let diseqCase = - let diseq = (id_of_string "diseq") in - let absurd = (id_of_string "absurd") - in (tclTHEN (intro_using diseq) - (tclTHEN h_simplest_right - (tclTHEN red_in_concl - (tclTHEN (intro_using absurd) - (tclTHEN (h_simplest_apply (mkVar diseq)) - (tclTHEN (h_injHyp absurd) - trivial )))))) -;; -\end{verbatim} - -In the tactic above we have chosen to name the hypotheses because -they have to be applied later on. This introduces a potential risk -of name clashing if the context already contains other hypotheses -also named ``diseq'' or ``absurd''. - -We are now ready to implement the tactic \textsl{SolveArg}. Given the -two arguments $a_1$ and $a_2$ of the constructor, this tactic cuts the -goal with the proposition $\{a_1=a_2\}+\{\neg a_1=a_2\}$, and then -applies the tactics above to each of the generated cases. If the -disjunction cannot be solved automatically, it remains as a sub-goal -to be proven. - -\begin{verbatim} -let solveArg a1 a2 tac g = - let rectype = pf_type_of g a1 in - let decide = mkDecideEqGoal rectype a1 a2 g - in (tclTHENS (h_elimType decide) - [(eqCase tac);diseqCase;default_auto]) g -;; -\end{verbatim} - -The following tactic implements the third and fourth steps of the -proof procedure: - -\begin{verbatim} -let conclpatt = put_pat mmk "{<?1>?2=?3}+{?4}" -;; -let solveLeftBranch rectype g = - let (_::(lhs::(rhs::_))) = - try (dest_somatch (pf_concl g) conclpatt) - with UserError ("somatch",_)-> error "Unexpected conclusion!" in - let nparams = mind_nparams rectype in - let getargs l = snd (chop_list nparams (snd (decomp_app l))) in - let rargs = getargs rhs - and largs = getargs lhs - in List.fold_right2 - solveArg largs rargs (tclTHEN h_simplest_left h_reflexivity) g -;; -\end{verbatim} - -Notice the use of a pattern to decompose the goal and obtain the -inductive type and the left and right hand sides of the equality. A -certain number of arguments correspond to the general parameters of -the type, and must be skipped over. Once the corresponding list of -arguments \texttt{rargs} and \texttt{largs} have been obtained, the -tactic \texttt{solveArg} is iterated on them, leaving a disjunction -whose left half can be solved by reflexivity. - -The following tactic joints together the three steps of the -proof procedure: - -\begin{verbatim} -let initialpatt = put_pat mmk "(x,y:?1){<?1>x=y}+{~(<?1>x=y)}" -;; -let decideGralEquality g = - let (typ::_) = try (dest_somatch (pf_concl g) initialpatt) - with UserError ("somatch",_) -> - error "The goal does not have the expected form" in - let headtyp = hd_app (pf_compute g typ) in - let rectype = match (kind_of_term headtyp) with - IsMutInd _ -> headtyp - | _ -> error ("This decision procedure only" - " works for inductive objects") - in (tclTHEN mkBranches - (tclORELSE h_solveRightBranch (solveLeftBranch rectype))) g -;; -;; -\end{verbatim} - -The tactic above can be specialized in two different ways: either to -decide a particular instance $\{c_1=c_2\}+\{\neg c_1=c_2\}$ of the -universal quantification; or to eliminate this property and obtain two -subgoals containing the hypotheses $c_1=c_2$ and $\neg c_1=c_2$ -respectively. - -\begin{verbatim} -let decideGralEquality = - (tclTHEN mkBranches (tclORELSE h_solveRightBranch solveLeftBranch)) -;; -let decideEquality c1 c2 g = - let rectype = pf_type_of g c1 in - let decide = mkGenDecideEqGoal rectype g - in (tclTHENS (cut decide) [default_auto;decideGralEquality]) g -;; -let compare c1 c2 g = - let rectype = pf_type_of g c1 in - let decide = mkDecideEqGoal rectype c1 c2 g - in (tclTHENS (cut decide) - [(tclTHEN intro - (tclTHEN (tclLAST_HYP simplest_case) - clear_last)); - decideEquality c1 c2]) g -;; -\end{verbatim} - -Next, for each of the tactics that will have an entry in the grammar -we construct the associated dynamic one to be registered in the table -of tactics. This function can be used to overload a tactic name with -several similar tactics. For example, the tactic proving the general -decidability property and the one proving a particular instance for -two terms can be grouped together with the following convention: if -the user provides two terms as arguments, then the specialized tactic -is used; if no argument is provided then the general tactic is invoked. - -\begin{verbatim} -let dyn_decideEquality args g = - match args with - [(COMMAND com1);(COMMAND com2)] -> - let c1 = pf_constr_of_com g com1 - and c2 = pf_constr_of_com g com2 - in decideEquality c1 c2 g - | [] -> decideGralEquality g - | _ -> error "Invalid arguments for dynamic tactic" -;; -add_tactic "DecideEquality" dyn_decideEquality -;; - -let dyn_compare args g = - match args with - [(COMMAND com1);(COMMAND com2)] -> - let c1 = pf_constr_of_com g com1 - and c2 = pf_constr_of_com g com2 - in compare c1 c2 g - | _ -> error "Invalid arguments for dynamic tactic" -;; -add_tactic "Compare" tacargs_compare -;; -\end{verbatim} - -This completes the implementation of the tactic. We turn now to the -\Coq file \texttt{Eqdecide.v}. - - -\subsection{The Grammar Rules} - -Associated to the implementation of the tactic there is a \Coq\ file -containing the grammar and pretty-printing rules for the new tactic, -and the commands to generate an object module that can be then loaded -dynamically during a \Coq\ session. In order to generate an ML module, -the \Coq\ file must contain a -\texttt{Declare ML module} command for all the \ocaml{} files concerning -the implementation of the tactic --in our case there is only one file, -the file \texttt{eqdecide.ml}: - -\begin{verbatim} -Declare ML Module "eqdecide". -\end{verbatim} - -The following grammar and pretty-printing rules are -self-explanatory. We refer the reader to the Section \ref{Grammar} for -the details: - -\begin{verbatim} -Grammar tactic simple_tactic := - EqDecideRuleG1 - [ "Decide" "Equality" comarg($com1) comarg($com2)] -> - [(DecideEquality $com1 $com2)] -| EqDecideRuleG2 - [ "Decide" "Equality" ] -> - [(DecideEquality)] -| CompareRule - [ "Compare" comarg($com1) comarg($com2)] -> - [(Compare $com1 $com2)]. - -Syntax tactic level 0: - EqDecideRulePP1 - [(DecideEquality)] -> - ["Decide" "Equality"] -| EqDecideRulePP2 - [(DecideEquality $com1 $com2)] -> - ["Decide" "Equality" $com1 $com2] -| ComparePP - [(Compare $com1 $com2)] -> - ["Compare" $com1 $com2]. -\end{verbatim} - - -\paragraph{Important:} The names used to label the abstract syntax tree -in the grammar rules ---in this case ``DecideEquality'' and -``Compare''--- must be the same as the name used to register the -tactic in the tactics table. This is what makes the links between the -input entered by the user and the tactic executed by the interpreter. - -\subsection{Loading the Tactic} - -Once the module \texttt{EqDecide.v} has been compiled, the tactic can -be dynamically loaded using the \texttt{Require} command. - -\begin{coq_example} -Require EqDecide. -Goal (x,y:nat){x=y}+{~x=y}. -Decide Equality. -\end{coq_example} - -The implementation of the tactic can be accessed through the -tactical \texttt{Info}: -\begin{coq_example} -Undo. -Info Decide Equality. -\end{coq_example} -\begin{coq_eval} -Abort. -\end{coq_eval} - -Remark that the task performed by the tactic \texttt{solveRightBranch} -is not displayed, since we have chosen to hide its implementation. - -\section[Testing and Debugging your Tactic]{Testing and Debugging your Tactic\label{test-and-debug}} - -When your tactic does not behave as expected, it is possible to trace -it dynamically from \Coq. In order to do this, you have first to leave -the toplevel of \Coq, and come back to the \ocaml{} interpreter. This can -be done using the command \texttt{Drop} (see Section~\ref{Drop}). Once -in the \ocaml{} toplevel, load the file \texttt{tactics/include.ml}. -This file installs several pretty printers for proof trees, goals, -terms, abstract syntax trees, names, etc. It also contains the -function \texttt{go:unit -> unit} that enables to go back to \Coq's -toplevel. - -The modules \texttt{Tacmach} and \texttt{Pfedit} contain some basic -functions for extracting information from the state of the proof -engine. Such functions can be used to debug your tactic if -necessary. Let us mention here some of them: - -\begin{description} -\fun{val get\_pftreestate : unit -> pftreestate} - {Projects the current state of the proof engine.} -\fun{val proof\_of\_pftreestate : pftreestate -> proof} - {Projects the current state of the proof tree. A pretty-printer - displays it in a readable form. } -\fun{val top\_goal\_of\_pftreestate : pftreestate -> goal sigma} - {Projects the goal and the existential variables mapping from - the current state of the proof engine.} -\fun{val nth\_goal\_of\_pftreestate : int -> pftreestate -> goal sigma} - {Projects the goal and mapping corresponding to the $nth$ subgoal - that remains to be proven} -\fun{val traverse : int -> pftreestate -> pftreestate} - {Yields the children of the node that the current state of the - proof engine points to.} -\fun{val solve\_nth\_pftreestate : \\ \qquad -int -> tactic -> pftreestate -> pftreestate} - {\\ Provides the new state of the proof engine obtained applying - a given tactic to some unproven sub-goal.} -\end{description} - -Finally, the traditional \ocaml{} debugging tools like the directives -\texttt{trace} and \texttt{untrace} can be used to follow the -execution of your functions. Frequently, a better solution is to use -the \ocaml{} debugger, see Chapter \ref{Utilities}. - -\section[Concrete syntax for ML tactic and vernacular command]{Concrete syntax for ML tactic and vernacular command\label{Notations-for-ML-command}} - -\subsection{The general case} - -The standard way to bind an ML-written tactic or vernacular command to -a concrete {\Coq} syntax is to use the -\verb=TACTIC EXTEND= and \verb=VERNAC COMMAND EXTEND= macros. - -These macros can be used in any {\ocaml} file defining a (new) ML tactic -or vernacular command. They are expanded into pure {\ocaml} code by -the {\camlpppp} preprocessor of {\ocaml}. Concretely, files that use -these macros need to be compiled by giving to {\tt ocamlc} the option - -\verb=-pp "camlp4o -I $(COQTOP)/parsing grammar.cma pa_extend.cmo"= - -\noindent which is the default for every file compiled by means of a Makefile -generated by {\tt coq\_makefile} (see Chapter~\ref{Addoc-coqc}). So, -just do \verb=make= in this latter case. - -The syntax of the macros is given on figure -\ref{EXTEND-syntax}. They can be used at any place of an {\ocaml} -files where an ML sentence (called \verb=str_item= in the {\tt ocamlc} -parser) is expected. For each rule, the left-hand-side describes the -grammar production and the right-hand-side its interpretation which -must be an {\ocaml} expression. Each grammar production starts with -the concrete name of the tactic or command in {\Coq} and is followed -by arguments, possibly separated by terminal symbols or words. -Here is an example: - -\begin{verbatim} -TACTIC EXTEND Replace - [ "replace" constr(c1) "with" constr(c2) ] -> [ replace c1 c2 ] -END -\end{verbatim} - -\newcommand{\grule}{\textrm{\textsl{rule}}} -\newcommand{\stritem}{\textrm{\textsl{ocaml\_str\_item}}} -\newcommand{\camlexpr}{\textrm{\textsl{ocaml\_expr}}} -\newcommand{\arginfo}{\textrm{\textsl{argument\_infos}}} -\newcommand{\lident}{\textrm{\textsl{lower\_ident}}} -\newcommand{\argument}{\textrm{\textsl{argument}}} -\newcommand{\entry}{\textrm{\textsl{entry}}} -\newcommand{\argtype}{\textrm{\textsl{argtype}}} - -\begin{figure} -\begin{tabular}{|lcll|} -\hline -{\stritem} - & ::= & -\multicolumn{2}{l|}{{\tt TACTIC EXTEND} {\ident} \nelist{\grule}{$|$} {\tt END}}\\ - & $|$ & \multicolumn{2}{l|}{{\tt VERNAC COMMAND EXTEND} {\ident} \nelist{\grule}{$|$} {\tt END}}\\ -&&\multicolumn{2}{l|}{}\\ -{\grule} & ::= & -\multicolumn{2}{l|}{{\tt [} {\str} \sequence{\argument}{} {\tt ] -> [} {\camlexpr} {\tt ]}}\\ -&&\multicolumn{2}{l|}{}\\ -{\argument} & ::= & {\str} &\mbox{(terminal)}\\ - & $|$ & {\entry} {\tt (} {\lident} {\tt )} &\mbox{(non-terminal)}\\ -&&\multicolumn{2}{l|}{}\\ -{\entry} - & ::= & {\tt string} & (a string)\\ - & $|$ & {\tt preident} & (an identifier typed as a {\tt string})\\ - & $|$ & {\tt ident} & (an identifier of type {\tt identifier})\\ - & $|$ & {\tt global} & (a qualified identifier)\\ - & $|$ & {\tt constr} & (a {\Coq} term)\\ - & $|$ & {\tt openconstr} & (a {\Coq} term with holes)\\ - & $|$ & {\tt sort} & (a {\Coq} sort)\\ - & $|$ & {\tt tactic} & (an ${\cal L}_{tac}$ expression)\\ - & $|$ & {\tt constr\_with\_bindings} & (a {\Coq} term with a list of bindings\footnote{as for the tactics {\tt apply} and {\tt elim}})\\ - & $|$ & {\tt int\_or\_var} & (an integer or an identifier denoting an integer)\\ - & $|$ & {\tt quantified\_hypothesis} & (a quantified hypothesis\footnote{as for the tactics {\tt intros until}})\\ - & $|$ & {\tt {\entry}\_opt} & (an optional {\entry} )\\ - & $|$ & {\tt ne\_{\entry}\_list} & (a non empty list of {\entry})\\ - & $|$ & {\tt {\entry}\_list} & (a list of {\entry})\\ - & $|$ & {\tt bool} & (a boolean: no grammar rule, just for typing)\\ - & $|$ & {\lident} & (a user-defined entry)\\ -\hline -\end{tabular} -\caption{Syntax of the macros binding {\ocaml} tactics or commands to a {\Coq} syntax} -\label{EXTEND-syntax} -\end{figure} - -There is a set of predefined non-terminal entries which are -automatically translated into an {\ocaml} object of a given type. The -type is not the same for tactics and for vernacular commands. It is -given in the following table: - -\begin{small} -\noindent \begin{tabular}{|l|l|l|} -\hline -{\entry} & {\it type for tactics} & {\it type for commands} \\ -{\tt string} & {\tt string} & {\tt string}\\ -{\tt preident} & {\tt string} & {\tt string}\\ -{\tt ident} & {\tt identifier} & {\tt identifier}\\ -{\tt global} & {\tt global\_reference} & {\tt qualid}\\ -{\tt constr} & {\tt constr} & {\tt constr\_expr}\\ -{\tt openconstr} & {\tt open\_constr} & {\tt constr\_expr}\\ -{\tt sort} & {\tt sorts} & {\tt rawsort}\\ -{\tt tactic} & {\tt glob\_tactic\_expr * tactic} & {\tt raw\_tactic\_expr}\\ -{\tt constr\_with\_bindings} & {\tt constr with\_bindings} & {\tt constr\_expr with\_bindings}\\\\ -{\tt int\_or\_var} & {\tt int or\_var} & {\tt int or\_var}\\ -{\tt quantified\_hypothesis} & {\tt quantified\_hypothesis} & {\tt quantified\_hypothesis}\\ -{\tt {\entry}\_opt} & {\it the type of entry} {\tt option} & {\it the type of entry} {\tt option}\\ -{\tt ne\_{\entry}\_list} & {\it the type of entry} {\tt list} & {\it the type of entry} {\tt list}\\ -{\tt {\entry}\_list} & {\it the type of entry} {\tt list} & {\it the type of entry} {\tt list}\\ -{\tt bool} & {\tt bool} & {\tt bool}\\ -{\lident} & {user-provided, cf next section} & {user-provided, cf next section}\\ -\hline -\end{tabular} -\end{small} - -\bigskip - -Notice that {\entry} consists in a single identifier and that the {\tt -\_opt}, {\tt \_list}, ... modifiers are part of the identifier. -Here is now another example of a tactic which takes either a non empty -list of identifiers and executes the {\ocaml} function {\tt subst} or -takes no arguments and executes the{\ocaml} function {\tt subst\_all}. - -\begin{verbatim} -TACTIC EXTEND Subst -| [ "subst" ne_ident_list(l) ] -> [ subst l ] -| [ "subst" ] -> [ subst_all ] -END -\end{verbatim} - -\subsection{Adding grammar entries for tactic or command arguments} - -In case parsing the arguments of the tactic or the vernacular command -involves grammar entries other than the predefined entries listed -above, you have to declare a new entry using the macros -\verb=ARGUMENT EXTEND= or \verb=VERNAC ARGUMENT EXTEND=. The syntax is -given on Figure~\ref{ARGUMENT-EXTEND-syntax}. Notice that arguments -declared by \verb=ARGUMENT EXTEND= can be used for arguments of both -tactics and vernacular commands while arguments declared by -\verb=VERNAC ARGUMENT EXTEND= can only be used by vernacular commands. - -For \verb=VERNAC ARGUMENT EXTEND=, the identifier is the name of the -entry and it must be a valid {\ocaml} identifier (especially it must -be lowercase). The grammar rules works as before except that they do -not have to start by a terminal symbol or word. As an example, here -is how the {\Coq} {\tt Extraction Language {\it language}} parses its -argument: - -\begin{verbatim} -VERNAC ARGUMENT EXTEND language -| [ "Ocaml" ] -> [ Ocaml ] -| [ "Haskell" ] -> [ Haskell ] -| [ "Scheme" ] -> [ Scheme ] -END -\end{verbatim} - -For tactic arguments, and especially for \verb=ARGUMENT EXTEND=, the -procedure is more subtle because tactics are objects of the {\Coq} -environment which can be printed and interpreted. Then the syntax -requires extra information providing a printer and a type telling how -the argument behaves. Here is an example of entry parsing a pair of -optional {\Coq} terms. - -\begin{verbatim} -let pp_minus_div_arg pr_constr pr_tactic (omin,odiv) = - if omin=None && odiv=None then mt() else - spc() ++ str "with" ++ - pr_opt (fun c -> str "minus := " ++ pr_constr c) omin ++ - pr_opt (fun c -> str "div := " ++ pr_constr c) odiv - -ARGUMENT EXTEND minus_div_arg - TYPED AS constr_opt * constr_opt - PRINTED BY pp_minus_div_arg -| [ "with" minusarg(m) divarg_opt(d) ] -> [ Some m, d ] -| [ "with" divarg(d) minusarg_opt(m) ] -> [ m, Some d ] -| [ ] -> [ None, None ] -END -\end{verbatim} - -Notice that the type {\tt constr\_opt * constr\_opt} tells that the -object behaves as a pair of optional {\Coq} terms, i.e. as an object -of {\ocaml} type {\tt constr option * constr option} if in a -\verb=TACTIC EXTEND= macro and of type {\tt constr\_expr option * -constr\_expr option} if in a \verb=VERNAC COMMAND EXTEND= macro. - -As for the printer, it must be a function expecting a printer for -terms, a printer for tactics and returning a printer for the created -argument. Especially, each sub-{\term} and each sub-{\tac} in the -argument must be typed by the corresponding printers. Otherwise, the -{\ocaml} code will not be well-typed. - -\Rem The entry {\tt bool} is bound to no syntax but it can be used to -give the type of an argument as in the following example: - -\begin{verbatim} -let pr_orient _prc _prt = function - | true -> mt () - | false -> str " <-" - -ARGUMENT EXTEND orient TYPED AS bool PRINTED BY pr_orient -| [ "->" ] -> [ true ] -| [ "<-" ] -> [ false ] -| [ ] -> [ true ] -END -\end{verbatim} - -\begin{figure} -\begin{tabular}{|lcl|} -\hline -{\stritem} & ::= & - {\tt ARGUMENT EXTEND} {\ident} {\arginfo} {\nelist{\grule}{$|$}} {\tt END}\\ -& $|$ & {\tt VERNAC ARGUMENT EXTEND} {\ident} {\nelist{\grule}{$|$}} {\tt END}\\ -\\ -{\arginfo} & ::= & {\tt TYPED AS} {\argtype} \\ -&& {\tt PRINTED BY} {\lident} \\ -%&& \zeroone{{\tt INTERPRETED BY} {\lident}}\\ -%&& \zeroone{{\tt GLOBALIZED BY} {\lident}}\\ -%&& \zeroone{{\tt SUBSTITUTED BY} {\lident}}\\ -%&& \zeroone{{\tt RAW\_TYPED AS} {\lident} {\tt RAW\_PRINTED BY} {\lident}}\\ -%&& \zeroone{{\tt GLOB\_TYPED AS} {\lident} {\tt GLOB\_PRINTED BY} {\lident}}\\ -\\ -{\argtype} & ::= & {\argtype} {\tt *} {\argtype} \\ -& $|$ & {\entry} \\ -\hline -\end{tabular} -\caption{Syntax of the macros binding {\ocaml} tactics or commands to a {\Coq} syntax} -\label{ARGUMENT-EXTEND-syntax} -\end{figure} - -%\end{document} |