How can I determine which alternative node was chosen in ANTLR - java

Suppose I have the following:
variableDeclaration: Identifier COLON Type SEMICOLON;
Type: T_INTEGER | T_CHAR | T_STRING | T_DOUBLE | T_BOOLEAN;
where those T_ names are just defined as "integer", "char" etc.
Now suppose I'm in the exitVariableDeclaration method of a test program called LittleLanguage. I can refer to LittleLanguageLexer.T_INTEGER (etc.) but I can't see how determine which of these types was found through the context.
I had thought it would be context.Type().getSymbol().getType() but that doesn't return the right value. I know that I COULD use context.Type().getText() but I really don't want to be doing string comparisons.
What am I missing?
Thanks

You are loosing information in the lexer by combining the tokens prematurely. Better to combine in a parser rule:
variableDeclaration: Identifier COLON type SEMICOLON;
type: T_INTEGER | T_CHAR | T_STRING | T_DOUBLE | T_BOOLEAN;
Now, type is a TerminalNode whose underlying token instance has a unique type:
variableDeclarationContext ctx = .... ;
TerminalNode typeNode = (TerminalNode) ctx.type().getChild(0);
switch(typeNode.getSymbol().getType()) {
case YourLexer.T_INTEGER:
...

Related

how to remove space in when using cucumber example placeholder?

I have following test case where i want pass '00:00:00.0' (date_suffix) for one example and one for not.
however using this approach it also append space in first example with no date_suffix
so it results something like this:
// I need to get rid of last space (after /17) for example 1.
example1. "1996/06/17 "
example2. "1996/06/17 00:00:00.0"
--
Then Some case:
| birthdate |
| 1996/06/17 <date_suffix> |
| 1987-11-08 <date_suffix> |
| 1998-07-20 <date_suffix> |
#example1
Examples:
| date_suffix |
| |
#example2
Examples:
| date_suffix |
| 00:00:00.0 |
What you want to do is not possible in Gherkin.
However it seems like you are testing a date parser or validation tool through some other component.
By adding the time stamp to the date, you're adding incidental details to your scenario. It is not immediately apparent what these test and maybe overlooked in the future.
Consider instead testing the parser/validator separately and directly.
Once you have confidence in the date parser works correctly, use for your current scenario a list of mixed dates, some with and some without suffix.
Use a trim function to eliminate all the spacese.
exemple = urString.trim();

ANTLR4 error recovery issues for class bodies

I've found a strange issue regarding error recovery in ANTLR4. If I take the grammar example from the ANTLR book
grammar simple;
prog: classDef+ ; // match one or more class definitions
classDef
: 'class' ID '{' member+ '}' // a class has one or more members
;
member
: 'int' ID ';' // field definition
| 'int' f=ID '(' ID ')' '{' stat '}' // method definition
;
stat: expr ';'
| ID '=' expr ';'
;
expr: INT
| ID '(' INT ')'
;
INT : [0-9]+ ;
ID : [a-zA-Z]+ ;
WS : [ \t\r\n]+ -> skip ;
and use the input
class T {
y;
int x;
}
it will see the first member as an error (as it expects 'int' before 'y').
classDef
| "class"
| ID 'T'
| "{"
|- member
| | ID "y" -> error
| | ";" -> error
|- member
| | "int"
| | ID "x"
| | ";"
In this case ANTLR4 recovers from the error in the first member subrule and parses the second member correct.
But if the member classDef is changed from mandatory member+ to optional member*
classDef
: 'class' ID '{' member* '}' // a class has zero or more members
;
then the parsed tree will look like
classDef
| "class" -> error
| ID "T" -> error
| "{" -> error
| ID "y" -> error
| ";" -> error
| "int" -> error
| ID "x" -> error
| ";" -> error
| "}" -> error
It seems that the error recovery cannot solve the issue inside the member subrule anymore.
Obviously using member+ is the way forward as it provides the correct error recovery result. But how do I allow empty class bodies? Am I missing something in the grammar?
The DefaultErrorStrategy class is quite complex with token deletions and insertions and the book explains the theory of this class in a very good way. But what I'm missing here is how to implement custom error recovery for specific rules?
In my case I would add something like "if { is already consumed, try to find int or }" to optimize the error recovery for this rule.
Is this possible with ANTLR4 error recovery in a reasonable way at all? Or do I have to implement manual parser by hand to really gain control over error recovery for those use cases?
It is worth noting that the parser never enters the sub rule for the given input. The classDef rule fails before trying to match a member.
Before trying to parse the sub-rule, the sync method on DefaultErrorStrategy is called. This sync recognizes there is a problem and tries to recover by deleting a single token to see if that fixes things up.
In this case it doesn't, so an exception is thrown and then tokens are consumed until a 'class' token is found. This makes sense because that is what can follow a classDef and it is the classDef rule, not the member rule that is failing at this point.
It doesn't look simple to do correctly, but if you install a custom subclass of DefaultErrorStrategy and override the sync() method, you can get any recovery strategy you like.
Something like the following could be a starting point:
#Override
public void sync(Parser recognizer) throws RecognitionException {
if (recognizer.getContext() instanceof simpleParser.ClassDefContext) {
return;
}
super.sync(recognizer);
}
The result being that the sync doesn't fail, and the member rule is executed. Parsing the first member fails, and the default recovery method handles moving on to the next member in the class.

Citrus-Framework change variable during scenario execution

I want to initialize some variables in order create a new item in my database, then in the same scenario change one variable, but keep the other variables the same and do an update. For example:
Update path: path/${resourceId}/${orderId}
#DEV #API
Scenario: order not found in container
Given variables
| resourceId | citrus:randomUUID() |
| orderId | citrus:randomString(10,false) |
| errorCode | 404 |
| reasonPhrase | NOT_FOUND |
When <ApiClient> sends message <containerPostRequest>
And <ApiClient> should receive Json message <containerPostResponse>
Given variable orderId is "newString"
And <ApiClient> sends message <addOrderRequest>
Then <ApiClient> should receive Json message <errorResponse>
I am attempting to throw an order not found exception, but using this method sets orderId to newString from the very beginning so the order is being created and located with newString.
Is this approach possible?
I can hard code scenarios and backgrounds to achieve my goal, but the method above I'm trying to use would reduce the amount of code I would need to write.
The expression Given variable ... is "..." overwrites the variables in test case initialization. This is not what we need in your case. You need to use a test action that overwrites the variable as part of the test execution. You can do this with following custom step definition:
Then set variable orderId to "newString"
You need to add a step definition that implements this step.
public class SetVariableStep {
#CitrusResource
private TestDesigner designer;
#Then("^set variable ([^\\s]+) to \"([^\"]*)\"$")
public void setVariable(String name, String value) {
designer.createVariable(name, value);
}
}
The Citrus team should add this to the default step definition implementations.

ANTLR 4: Parsing grammar

I want to parse some data from AppleSoft Basic script.
I choose ANTLR and download this grammar: jvmBasic
I'm trying to extract function name without parameters:
return parser.prog().line(0).amprstmt(0).statement().getText();
but it returns PRINT"HELLO" e.g full expression except the line number
Here is string i want to parse:
10 PRINT "Hello!"
I think this question really depends on your ANTLR program implementation but if you are using a treewalker/listener you probably want to be targeting the rule for the specific tokens not the entire "statement" rule which is circular and encompasses many types of statement :
//each line can have one to many amprstmt's
line
: (linenumber ((amprstmt (COLON amprstmt?)*) | (COMMENT | REM)))
;
amprstmt
: (amperoper? statement) //encounters a statement here
| (COMMENT | REM)
;
//statements can be made of 1 to many sub statements
statement
: (CLS | LOAD | SAVE | TRACE | NOTRACE | FLASH | INVERSE | GR | NORMAL | SHLOAD | CLEAR | RUN | STOP | TEXT | HOME | HGR | HGR2)
| prstmt
| printstmt1 //the print rule
//MANY MANY OTHER RULES HERE TOO LONG TO PASTE........
;
//the example rule that occurs when the token's "print" is encountered
printstmt1
: (PRINT | QUESTION) printlist?
;
printlist
: expression (COMMA | SEMICOLON)? printlist*
;
As you can see from the BNF type grammar here the statement rule in this grammar includes the rules for a print statement as well as every other type of statement so it will encompass 10, PRINT and hello and subsequently return the text with the getText() method when any of these are encountered in your case, everything but linenumber which is a rule outside of the statement rule.
If you want to target these specific rules to handle what happens when they are encountered you most likely want to add functionality to each of the methods ANTLR generates for each rule by extending the jvmBasiListener class as shown here
example:
-jvmBasicListener.java
-extended to jvmBasicCustomListener.java
void enterPrintstmt1(jvmBasicParser.Printstmt1Context ctx){
System.out.println(ctx.getText());
}
However if all this is setup and you are just wanting to return a string value etc using the single line you have then trying to access the methods at a lower level by addressing the child nodes of statement may work amprstmt->statement->printstmt1->value :
return parser.prog().line().amprstmt(0).statement().printstmt1().getText();
Just to maybe narrow my answer slightly, the rules specifically that address your input "10 PRINT "HELLO" " would be :
linenumber (contains Number) , statement->printstmt1 and statement->datastmt->datum (contains STRINGLITERAL)
So as shown above the linenumber rule exists on its own and the other 2 rules that defined your text are children of statement, which explains outputting everything except the line number when getting the statement rules text.
Addressing each of these and using getText() rather than an encompassing rule such as statement may give you the result you are looking for.
I will update to address your question since the answer may be slightly longer, the easiest way in my opinion to handle specific rules rather than generating a listener or visitor would be to implement actions within your grammar file rules like this :
printstmt1
: (PRINT | QUESTION) printlist? {System.out.println("Print"); //your java code }
;
This would simply allow you to address each rule and perform whichever java action you would wish to carry out. You can then simply compile your code with something like :
java -jar antlr-4.5.3-complete.jar jvmBasic.g4 -visitor
After this you can simply run your code however you wish, here is an example:
import JVM1.jvmBasicLexer;
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTree;
public class Jvm extends jvmBasicBaseVisitor<Object> {
public static void main(String[] args) {
jvmBasicLexer lexer = new jvmBasicLexer(new ANTLRInputStream("10 PRINT \"Hello!\""));
jvmBasicParser parser = new jvmBasicParser(new CommonTokenStream(lexer));
ParseTree tree = parser.prog();
}
}
The output for this example would then be just :
Print
You could also incorporate whatever Java methods you like within the grammar to address each rule encountered and either develop your own classes and methods to handle it or directly print it out a result.
Update
Just to address the latest question now :
parser.line().linenumber().getText() - For line Number, as line is not part of a statement
parser.prog().line(0).amprstmt(0).statement().printstmt1().PR‌​INT().getText() - For PRINT as it is isolated in printstmt1, however does not include CLR in the rule
parser.prog().line(0).amprstmt(0).statement().printstmt1().pr‌intlist().expression().getText() - To get the value "hello" as it is part of an expression contained within the printstmt1 rule.
:) Good luck

Creating empty string for 'rep:glob'

To restrict node access to user(using principalbased.ACL), tried with following snippet, but turns ineffective:
Map<String, Value> restrictions = new HashMap<String, Value>();
//Apply privilege to user to have read only access to root folder
restrictions.put("rep:nodePath", valueFactory.createValue(ROOT, PropertyType.PATH));
restrictions.put("rep:glob", valueFactory.createValue(""));
accessControlList.addEntry(userPrincipal, privileges, true , restrictions);
accessControlManager.setPolicy(accessControlList.getPath(), accessControlList);
Is there any other way to set rep:glob property to ""?
When I provide access to root('/'), it should access only that. Instead all the nodes under root are accessible to user.
When you don't provide the rep:glob property while restricting principal access, the default ACL effects the whole subtree of the target node.
Thus the optional rep:glob propety will save you time of creating restrictions on every path you want to limit the policy and it accepts JCR Values and the passed String instance while creating this Value will have below effect on node/subnodes restriction when appliedon the ndoe with path "/foo" :
-----------------------------------------------------------------------------------
rep:glob value | Effect |
-----------------------------------------------------------------------------------
null | matches /foo and all its children |
-----------------------------------------------------------------------------------
"" (Empty String)| matches "/foo" only |
* (wildcard) | all descendants |
/*bar | all childrens which path ends with "bar" |
/*/bar | all non-direct descendants of "/foo" named "bar" |
/bar* | all childrens which paths begin with "bar" |
*bar | all siblings and descendants of "/foo" that begin with "bar" |
-----------------------------------------------------------------------------------
If the the restriction is not applied only for your target node, then there should be some mistake somewhere, and you may need to post the whole code snippet for further help.
Even this(http://jackrabbit.apache.org/api/2.2/org/apache/jackrabbit/core/security/authorization/GlobPattern.html) pattern didn't work for root. Anyways I have used other approach to skip root access check, while getting user. Need to provide SimpleWorkspaceAccessManager in SecurityManager tag.

Categories