su4sml/add-ons/jtestdataaccessor/src/main/antlr/lp.g

359 lines
8.7 KiB
Plaintext

header {
package ch.ethz.infsec.jtestdataaccessor.parser;
import ch.ethz.infsec.jtestdataaccessor.*;
import ch.ethz.infsec.jtestdataaccessor.nodes.*;
import java.util.*;
}
/**
* Parser for test data.
*/
class TestdataParser extends Parser;
{
/* Root of tree with tests */
private TestData testdata;
/* Name of the function whose entry is currently parsed */
private String currentFunctionName;
/* Function which is currently parsed */
private FunctionUnderTest cfut;
/* Current testcase */
private TestCase ctest;
/* Parser currently in the scope of a function? */
boolean fscope = true;
/**
* Set the root of the tree where the testdata shall be written to
*/
public void setTestdata(TestData td){
testdata = td;
}
/**
* Identifiers which are used in the file with testdata.
*/
private enum ident {
RESULTTYPE, INPUTTYPES, SETUP, TEARDOWN, INPUT, RESULT, CHECKER, COMMENT
};
}
/* Root of the parser */
startRule throws TestDataParseException:
( sectionlist )
;
/* Section head with a name (which is a function) */
section returns [String value=""]:
(LBRAK id:IDENTIFIER RBRAK)
{
value = id.getText();
}
;
/* A list of sections which contain definitions or testcase specifications */
sectionlist throws TestDataParseException
{
String fname = null;
}
:
( fname=section
{
/* Setup target of parsed data */
currentFunctionName = fname;
cfut = new FunctionUnderTest(currentFunctionName);
testdata.addTest(cfut);
}
( definition | test )* ( sectionlist )?)
;
/* A definition, such as setup = ...; */
definition throws TestDataParseException
{
List<Argument> vl = null;
ident cident = null;
}
:
( (cident=ident EQUALS vl=valuelist) )
{
if(cident != null && vl != null && vl.size() > 0){
if(fscope){
/* Current scope is a function */
switch(cident){
case RESULTTYPE:
if(vl.size() == 1 && vl.get(0) instanceof Type){
cfut.setResultType((Type)vl.get(0));
}else{
throw new TestDataParseException("resulttype only takes one type argument.", LT(0));
}
break;
case INPUTTYPES:
for(Argument arg: vl){
if(arg instanceof Type){
Type type = (Type) arg;
cfut.addInputType(type);
}else{
throw new TestDataParseException("inputtypes only takes type arguments.",LT(0));
}
}
break;
case SETUP:
if(vl.size() == 1 && vl.get(0) instanceof Function){
cfut.setSetup((Function) vl.get(0));
}else{
throw new TestDataParseException("setup requires a function argument.",LT(0));
}
break;
case TEARDOWN:
if(vl.size() == 1 && vl.get(0) instanceof Function){
cfut.setTeardown((Function) vl.get(0));
}else{
throw new TestDataParseException("teardown requires a function argument.",LT(0));
}
break;
default:
throw new TestDataParseException("Identifier "+cident+" not allowed in function scope.", LT(0));
}
}else{
/* Current scope is a testcase */
switch(cident){
case INPUT:
for(Argument arg: vl){
if(arg instanceof Value){
ctest.addInput((Value)arg);
}else{
throw new TestDataParseException("Inputs must be Values!", LT(0));
}
}
break;
case RESULT:
if(vl.size() == 1 && vl.get(0) instanceof Value){
ctest.setResult((Value)vl.get(0));
}else if(vl.size() == 1 && vl.get(0) instanceof Type){
ctest.setResult((Type)vl.get(0));
}else{
throw new TestDataParseException("Result must be a Value!",LT(0));
}
break;
case CHECKER:
if(vl.size() == 1){
if(vl.get(0) instanceof Function){
ctest.setChecker(new ResultChecker((Function)vl.get(0),testdata));
}else if(vl.get(0) instanceof ResultChecker){
ctest.setChecker((ResultChecker)vl.get(0));
}
}
break;
case COMMENT:
if(vl.size() == 1 && vl.get(0) instanceof SimpleValue){
ctest.setComment(((SimpleValue)vl.get(0)).toString());
}else{
throw new TestDataParseException("comment requires a string argument.",LT(0));
}
break;
default:
throw new TestDataParseException("Identifier "+cident+" not allowed in test scope.", LT(0));
}
}
}
}
;
/* An identifier/keyword such as setup */
ident returns [ident value=null]
:
(
RESULTTYPE { value=ident.RESULTTYPE; }
| INPUTTYPE { value = ident.INPUTTYPES; }
| SETUP { value = ident.SETUP; }
| TEARDOWN { value = ident.TEARDOWN; }
| INPUT { value = ident.INPUT; }
| RESULT { value = ident.RESULT; }
| CHECKER { value = ident.CHECKER; }
| COMMENT { value = ident.COMMENT; }
)
;
/* A testcase which is enclosed in {...} */
test throws TestDataParseException
:
( STEST {
ctest = new TestCase();
cfut.addTest(ctest);
/* Current scope is test */
fscope = false;
} (definition)+ { fscope = true; } ETEST )
;
/* A list of values, eg. "foo",10,true,"bar" */
valuelist returns [Vector<Argument> value=null]
{
ResultChecker.DefaultChecker c = null;
}
:
( (id:IDENTIFIER ((l:LPAREN r:RPAREN) | (lb:LBRAK rb:RBRAK))? | s:STRING | (neg:DASH)? (i:INT | f:FLOAT) | tr:TRUE | fa:FALSE | ch:CHAR | c=checker) ( ( COMMA value=valuelist ) | SEMICOLON ) )
{
if(value == null){
value = new Vector<Argument>();
}
/* Numbers should be negated */
String negate = "";
if(neg != null){
negate = "-";
}
Argument a = null;
/* Create an object which represents the value */
if(id != null){
if(l != null && r != null){
a = new Function(id.getText());
}else if(lb != null && rb != null){
a = new Type(id.getText());
((Type) a).setArray(true);
}else{
a = new Type(id.getText());
}
}else if(s != null){
a = new SimpleValue(s.getText().replaceAll("\"",""));
}else if(i != null){
a = new SimpleValue(new Integer(negate+i.getText()));
}else if(f != null){
a = new SimpleValue(new Double(negate+f.getText()));
}else if(tr != null){
a = new SimpleValue(new Boolean(tr.getText()));
}else if(fa != null){
a = new SimpleValue(new Boolean(fa.getText()));
}else if(ch != null){
a = new SimpleValue(new Character(ch.getText().charAt(1)));
}else if(c != null){
a = new ResultChecker(c);
}
if(a != null){
value.insertElementAt(a,0);
}
}
;
/* Checker which is used to check the result */
checker returns [ResultChecker.DefaultChecker value = null]
: (e:EQUALS | nt:FALSEC | t:TRUEC | nn:NOTNULL | n:NULL | ns:NOTSAME | s:SAME | f:FAIL | ne:NOTEQUAL)
{
if(e != null){
value = ResultChecker.DefaultChecker.EQUALS;
}else if(nt != null){
value = ResultChecker.DefaultChecker.FALSE;
}else if(t != null){
value = ResultChecker.DefaultChecker.TRUE;
}else if(nn != null){
value = ResultChecker.DefaultChecker.NOTNULL;
}else if(n != null){
value = ResultChecker.DefaultChecker.NULL;
}else if(ns != null){
value = ResultChecker.DefaultChecker.NOTSAME;
}else if(s != null){
value = ResultChecker.DefaultChecker.SAME;
}else if(f != null){
value = ResultChecker.DefaultChecker.FAIL;
}else if(ne != null){
value = ResultChecker.DefaultChecker.NOTEQUAL;
}
}
;
/**
* The Lexer for testdata
*/
class TestdataLexer extends Lexer;
/* Tokens which are keywords. The must not be used elsewhere in the file with testdata! */
tokens {
RESULTTYPE = "resulttype";
INPUTTYPE = "inputtypes";
SETUP = "setup";
TEARDOWN = "teardown";
INPUT = "input";
RESULT = "result";
CHECKER = "checker";
COMMENT = "comment";
TRUE = "true";
FALSE = "false";
EQUALS = "EQUALS";
FALSEC = "FALSE";
TRUEC = "TRUE";
NOTNULL = "NOTNULL";
NULL = "NULL";
NOTSAME = "NOTSAME";
NOTEQUAL = "NOTEQUAL";
SAME = "SAME";
FAIL = "FAIL";
}
/* Alphanumeric identifiers */
IDENTIFIER: ( ( 'a'..'z'|'A'..'Z' ) ('a'..'z'|'A'..'Z'|'0'..'9'|'-'|'_'|'.')* );
/* Strings */
STRING: ('"' (~('"'))* '"' );
/* Single characters */
CHAR: ('\''.'\'');
/* Left ( */
LPAREN: ( '(' );
/* Right ) */
RPAREN: ( ')' );
/* Left [ */
LBRAK: ( '[' );
/* Right ] */
RBRAK: ( ']' );
/* Integers */
protected
INT : ('0'..'9')+;
/* Floats */
protected
FLOAT: INT '.' INT;
/* FLOAT or INT? */
RANGE_OR_INT
: ( INT '.' ) => FLOAT { $setType(FLOAT); }
| INT { $setType(INT); }
;
/* Start of a test */
STEST: ( '{' );
/* End of a test */
ETEST: ( '}' );
/* The equals sign */
EQUALS: '=';
/* A dash */
DASH: '-';
/* A comma */
COMMA: ',';
/* A semicolon */
SEMICOLON: ';';
/* Ignore newlines, whitespace and comments */
NEWLINE
: ('\r' '\n' { newline(); } // DOS
| '\n' { newline(); } // UNIX
| ' '
| '\t'
| '#' (~('\n' | '\r'))*
)
{ $setType(Token.SKIP); } //ignore these tokens
;