How to extend recoder¶
Schmitt
Danger
Recoder is planned to be removed. See PR #3120.
These notes explain the changes to the existing code of the KeY system that are necessary to add a new data type to JML. If the new data type is only used in JML specifications then only the changes described in Section 1 need to be done. If you want to use ghost variables or ghost fields, in particular assignments to ghost variables and fields of the new adt then in addition the changes described in Section 2 need to be done.
These notes do not aim to provide an understanding of the existing code they just tell the user what to do by mimicking what is already there. We take the data type Seq of finite sequences as a model.
In the following we use Basepath
as an abbreviation for the path\
gitKeY/key/key/key.core/src/de/uka/ilkd/key/
Extension Of The JML Parser¶
At the moment the following instructions do not cover the addition of new variable binder symbols.
Change file: KeYJMLLexer.g
¶
Here are some existing entries:
SEQ : '\\seq'; //KeY extension, not official JML
SEQCONCAT : '\\seq_concat'; //KeY extension, not official JML
SEQEMPTY : '\\seq_empty'; //KeY extension, not official JML
that you change to fit the purpose. It is important to note that it is here that
you establish the syntax that is to be used in the code for the data type name
and its operations. In the present case that is \seq
for the data type of
finite sequences and \seq_concat
for the concatenation operation and
\seq_empty
for the empty sequence. Note, the addition escape character \
. At
the same time you also establish a name for the corresponding nonterminal
grammar symbol.
Personal note: I am suprised that seqLen
does not occur here.
Change file: KeYJMLParser.g
¶
Change¶
Look for the existing grammar rule for sequences which looks roughly like this:
sequence returns [SLExpression ret = null] throws SLTranslationException
@init {
ImmutableList<Term> tlist = null;
KeYJavaType typ;
Term t, t2;
Token tk = null;
Pair<KeYJavaType,ImmutableList<LogicVariable>> declVars = null;
}
@after { ret = result; }
:
SEQEMPTY
{
result=new SLExpression(tb.seqEmpty());
}
|
.
.
.
| (tk2=SEQCONCAT{tk=tk2;} | tk3=SEQGET{tk=tk3;} | tk4=INDEXOF{tk=tk4;})
LPAREN e1=expression COMMA e2=expression RPAREN
{
result=translator.translate(tk.getText(),
SLExpression.class,
services,e1,e2);
}
;
Add a new rule replacing the name sequences
and adapt what follows after the
colon :
.
Change 2
In the jmlprimary
rule take the entry
| (SEQEMPTY
|
.
.
.
| SEQCONCAT
| SEQGET
| INDEXOF)
=> result = sequence
as a model. Of course you replace sequence
in this example by the rule name
you chose in the first change. Do not forget to add an equivalent for your new
data type for the line
| (LPAREN (SEQDEF | SEQ) quantifiedvardecls SEMI)
This is neacessary fo parsing quantifier variables of the new data type.
Change 3
In the builtintype
rule add a new entry for your data type mimicking
| SEQ
{
type = javaInfo.getKeYJavaType(PrimitiveType.JAVA_SEQ);
}
Change file: TermBuilder.java
¶
Look for the section starting with the comment lines
//------------------------
//sequence operators
//-----------------------
add a suitably adapted section for the new data type. You will notice
that you have to use the method names introduced in the public interface
section in OrdLDT
.
Create files¶
For each nonterminal NT
for an operator as declared in KeYJMLLexer.g
create a class file NameNT.java
in the directory\
Basepath/java/expression/operator/adt
following the examples\
Take SeqConcat.java
or SeqLength.java
as an example.\
It is a good idea to pick a name NameNT
that somehwo resembles NT
.
Constants are treated as literals, not as operators, and their corresponding files go into a different directory. See below.
Calls to the constructors of these new classes are passed on via super
to the constructors of the class it extends, Operator.java
or
BinaryOperator.java
. So here there is nothing else to do but renaming.
You should also get the getArity()
right.\
I also set the result of the getPrecedence()
method to 0. I am not
sure if that is always correct.
Work needs to be done for the last two methods visit(Visitor v)
and
prettyPrint(PrettyPrinter p)
as will be detailed in the next steps.
Change file: Visitor.java
¶
This class is an interface. All you have to do here is to add a line that adapts
the examples you see, e.g. void performActionOnSeqConcat(SeqConcat x);
Change file: CreatingASTVisitor.java
¶
The empty method specifications from Visitor.java
are overwritten here. Just
copy what you see, e.g. for performActionOnSeqConcat
and do the appropriate
renaming.
Change file: JavaASTVisitor.java
¶
This is an abstract class. You need to add a default method implementation. See
performActionOnSeqConcat(SeqConcat x)
for a model.
Always when you edit a file check the import clauses. You need to add an
adaption of import de.uka.ilkd.key.java.expression.operator.adt.SeqConcat;
in
all three files you edit in the last three steps. You replace of course
SeqConcat
by the name of the file you created in step 2.
The files for literals go into the directory
Basepath/java/expression/literal
instead.
Change file: PrettyPrinter.java
¶
You need to add a method printnewOp
for your operator newOp
in the data type
newAdt
. tylke your leads e.g. from printSeqConcat
. Of course you enter here
the string you want to see printer for your operator.
Create files¶
in directory Basepath/java/expression/literals
\
This parallels the creation of files like SeqConcat.java
for the
operation SeqConcat
, but now for the literals declared in
KeYJMLLexer.g
. Note, that these files go into a different directory.
The following changes also parallel those for operators except that for
literals JavaASTVisitor.java
is not affected.
Change file: Visitor.java
¶
Change file: JavaASTVisitor.java
¶
Change fil: PrettyPrinter.java
¶
Create file¶
in directory Basepath/ldt
\
Look at the existing file SeqLDT.java
as a model for the new file to
be created. You will note that for every literal and operation class
created in java.expression.literal
and java.expression.operator.adt
a field of type Function
is declared. To choose a name for this field
best follow the pattern you find in SeqLDT.java
. The link between the
classes in java.expression.operator.adt
and the fields are effected in
method getFunctionFor
. The link between the classes in
java.expression.literal
and the fields are effected in method
translateTerm
.
Also note that in the constructor SeqLDT(TermServices services)
e.g.
in the line
seqConcat = addFunction(services, "seqConcat");
the string, here "seqConcat"
, must match the declarations in the .key
file, here in seq.key
.
Make sure to include all the files created in the previous two create steps in the import statements.
This is a lot of work.
Change file
LDT.java
in directory Basepath/ldt/
\
In the body of the method getNewLDTInstances(Services s)
add aline for
the new data type mimicking the existing lines, e.g.\
ret.put(SeqLDT.NAME, new SeqLDT(s));
Change file
TypeConverter.java
in Basepath/java
\
Add a line for the new data type taking the following line for the
sequence data type as a model:
public SeqLDT getSeqLDT() {
return (SeqLDT) getLDT(SeqLDT.NAME);
}
and, as always, do not forget the necessary import statement.
Extension Of The Recoder To KeY Translation¶
Change file
LDT.java
in directory Basepath/ldt
\
Add a line for the new data type mimicking e.g. the existing line for
the Seq data type
ret.put(SeqLDT.NAME, new SeqLDT(s));
Change file
ProofJavaParser.jj
in Basepath/parser/proofjava/
Change 1¶
In section /* RESERVED WORDS AND LITERALS */
mimick the line\
| < SEQ: "\\seq" >
\
The string SEQ
for the nonterminal of the grammar and the name \seq
for the new data type are the same as in the grammar file
KeYJMLLexer.g
in Basepath/speclang/jml/translation
. Note, the
additional escape character \
. The notation you substitute for \seq
will be used in the annotated Java code.
Change 2¶
Below TypeReference PrimitiveType() :
add a line by adapting\
| "\\seq"
Change 3¶
Look for ADTGetter() :
and add all getter symbols of the new data type
Just mimick what you see there for operators starting with \seq_
. Here
is an example:
|
"\\seq_concat" "(" expr=Expression() "," result=Expression()")"
{
result = new SeqConcat(expr, result)
setPrefixInfo(result);
}
|
enclosed in the disjunctive separator symbol |
of the grammar syntax.
The name of the operator in place of \seq_concat
you take from the
grammar file KeYJMLLexer.g
in Basepath/speclang/jml/translation
. The
result in the above code fragment refers to a constructor,
SeqConcat(expr,result)
, for the class SeqConcat
. It is your job to
add these classes, one for each operator, getter, constructor or
literal. See below.
Change 4¶
Look for ADTConstructor() :
and add all constructor symbols for the
new data type. Here is a guiding example from the seq data type:
|
"\\seq_concat" "(" expr = Expression() "," result = Expression() ")"
{
result = new SeqConcat(expr, result)
setPrefixInfo(result);
}
|
Change 5¶
Constants are not considered as constructors. You have to add them as literals. Here is a simple example that suffices for the simplest case of just adding one literal.
EmptySeqLiteral EmptySeqLiteral() :
{
EmptySeqLiteral result;
}
{
"\\seq_empty"
{
result = EmptySeqLiteral.INSTANCE;
setPrefixInfo(result);
return result;
}
}
Things get complicated if you want families of literals as e.g. in
bigint
. I did not investigate this.
Change file
Recoder2KeYConverter.java
in directory Basepath/java
\
convert methods needs to be added. Again look for the line\
public SeqConcat convert(...adt.SeqConcat e)
\
and do the appropriate changes.
Do not forget to add the necessary import statements.
Change file
PrimitiveType.java
in directory Basepath/java/abstraction
\
Here is the entry for the data type of finite sequence that you may take
as a model:
public static final PrimitiveType JAVA_SEQ =
new PrimitiveType("\\seq", EmptySeqLiteral.INSTANCE, SeqLDT.NAME);
The second argument refers to the default element for the new data type. See below.
Create files¶
in the directory Basepath/java/recoderext/adt/
for each operator and
each literal introduced in KeYJMLLexer.g
in directory\
Basepath/speclang/jml/translation
.
Look at EmptySeqLiteral.java
, SeqLength.java
, and SeqConcat.java
as examples for literals, unary and binary operators.
In these files you find a field
private static final long serialVersionUID
which you can safely set
to 0.
Change file
RecoderModelTransformer.java
in Basepath/java/recoderext
\
In this file the default element of the new data tpye is handled. Mimic
the line
} else if ("\\seq".equals(type.getName())) {
return EmptySeqLiteral.INSTANCE;
and include the file that replaces EmptySeqLiteral
in the import
statements.
Change file
KeYCrossReferenceSourceInfo.java
in directory\
gitKeY/key/key/key.core/src/recoder/service
\
First include all the new files you created in the directory\
Basepath/java.recoderext.adt
in the import statements. Then extend the
method public Type getType(Expression expr)
appropriately. It seems
that only constructor symbols need to be included.
Change file
ProgramSVSort.java
in directory\
Basepath/logic/sort
.\
This is important for the selection of applicable taclets during proof
search. It tells the matching algorithm for schema variables to treat
adt expressions as simple expressions, e.g. when used in assignments to
ghost fields.
User Interface¶
You may also want to add a description of the functions of your new data
type to the Info tab in the user interface. This is done by adding to
the xml- file functionExplanations.xml
in the directory\
gitKeY/key/key/key.ui/resources/de/uka/ilkd/key/gui/help
.
Find the Error¶
To find the possible error when KeY fails to load I have added the following piece of code
System.out.println("reached reportError with message "+message);
System.out.println("throwable is "+ t);
java.io.StringWriter sw = new java.io.StringWriter();
java.io.PrintWriter pw = new java.io.PrintWriter(sw);
t.printStackTrace(pw);
System.out.println(sw.toString());
at the beginning of the method body for
reportError(String message, Throwable t)
in file Recoder2KeY.java
in
directory Basepath/java
.\
This prints the error trace of the thrown exception t
and might give
you a hint what could be the problem.