Match a single senerio with ANTLR and skip everything else as noise - java

I defined a simple grammar using an ANTLR V4 Eclipse Plugin. I want to parse a file that contains Coldfusion cfscript code, and find every instance of a property definition. For example:
property name="productTypeID" ormtype="string" length="32" fieldtype="id" generator="uuid" unsavedvalue="" default="";
That is, a property keyword followed by any number of attributes, line terminated with a semicolon.
.g4 file
grammar CFProperty;
property : 'property ' (ATR'='STRING)+EOL; // match keyword property followed by an attribute definition
ATR : [a-zA-Z]+; // match lower and upper-case identifiers name
STRING: '"' .*? '"'; // match any string
WS : [ \t\r\n]+ -> skip; // skip spaces, tabs, newlines
EOL : ';'; // end of the property line
I put together a simple java project that uses the generated parser, tree-walker etc to printout the occurrences of those matches.
The input I'm testing this with is:
"property id=\"actionID\" name=\"actionName\" attr=\"actionAttr\" hbMethod=\"HBMethod\"; public function some funtion {//some text} property name=\"actionID\" name=\"actionName\" attr=\"actionAttr\" hbMethod=\"HBMethod\"; \n more noise "
My issue is that this is only matching:
property id="actionID" name="actionName" attr="actionAttr" hbMethod="HBMethod";
And because it doesn't understand everthing else to be noise, it doesn't match the second instance of the property definition.
How can I match on multiple instances of the property definition and match on everything else in-between as noise to be skipped?

You can use lexer mode to do what you want. One mode for property and stuffs and one mode for noise. The idea behind mode is to go from a mode (a state) to another following token we found during lexing operation.
To do this, you have to cut your grammar in two files, the parser in one file and the lexer in the other.
Here is the lexer part (named TestLexer.g4 in my case)
lexer grammar TestLexer;
// Normal mode
PROPERTY : 'property';
EQUALS : '=';
ATR : [a-zA-Z]+; // match lower and upper-case identifiers name
STRING: '"' .*? '"'; // match any string
WS : [ \t\r\n]+ -> skip; // skip spaces, tabs, newlines
EOL : ';' -> pushMode(NOISE); // when ';' is found, go to noise mode where everything is skip
mode NOISE;
NOISE_PROPERTY : 'property' -> type(PROPERTY), popMode; // when 'property' is found, we say it's a PROPERTY token and we go back to normal mode
ALL : .+? -> skip; // skip all other stuffs
Here is the parser part (named Test.g4 in my case)
grammar Test;
options { tokenVocab=TestLexer; }
root : property+;
property : PROPERTY (ATR EQUALS STRING)+ EOL; // match keyword property followed by an attribute definition
This should do the work :)

Related

Lexer Priority with space

Here my grammar.
grammar MainGrammar;
mainFile: statement*;
statement: stackStatement NL;
stackStatement: KEYWORD WS INT;
INT: [0-9]+;
KEYWORD: 'def';
SENTENCE: (~[\r\n#:=])+;
WS: [ \t]+ -> skip;
NL: '\r'? '\n' -> skip;
I try for the main rule MainFile the code
def 123456
that match only the text as SENTENCE token and don't find KEYWORD token. Whatever order of token.
Any idea?
Your problem is that you are skipping WS and NL. Therefore the parser doesn't even see the respective tokens. That's why it can't match them (They are not part of the TokenStream that is fed into the parser).
In order to fix this you might look into lexical modes or this question

Treat invalid chars as a single token in ANTLR4 lexer

I'm using the JSON grammar from the antlr4 grammar repository to parse JSON files for an editor plugin. It works, but reports invalid chars one by one. The following snippet results in 18 lexer errors:
{
sometext-without-quotes : 42
}
I want to boil it down to 1-2 by treating consecutive, invalid single-char tokens of the same type as one bigger invalid token.
For a similar question, a custom lexer was suggested that glues "unknown" elements to larger tokens: In antlr4 lexer, How to have a rule that catches all remaining "words" as Unknown token?
I assume that this bypasses the usual lexer error reporting, which I would like to avoid, if possible. Isn't there a proper solution for that rather simple task? It seems to have worked by default in ANTLR3.
The answer is in the link you provided. I don't want to copy the original answer completely so I'll try and paraphrase a bit...
In antlr4 lexer, How to have a rule that catches all remaining "words" as Unknown token?
Add unknowns to the lexer that will match multiples of these...
unknowns : Unknown+ ;
...
Unknown : . ;
There was an edit made to this post to cater for the case where you were only using a lexer and not using a parser. If using a parser then you do not need to override the nextToken method because the error can be handled in the parser in a much cleaner way ie unknowns are just another token type as far as the lexer is concerned. The lexer passes these to the parser which can then handle the errors. If using a parser I'd normally recognize all tokens as individual tokens and then in the parser emit the errors ie group them or not. The reason for doing this is all error handling is done in one place ie it's not in the lexer and in the parser. It also makes the lexer simpler to write and test ie it must recognize all text and never fail on any utf8 input. Some people would likely do it differently but this has worked for me with hand written lexers in C. The parser is in charge of determining what's actually valid and how to error on it. One other benefit is that the lexer is fairly generic and can be reused.
For lexer only solution...
Check the answer at the link and look for this comment in the answer...
... but I only have a lexer, no parsers ...
The answer states that you override the nextToken method and goes into some detail on how to do that
#Override
public Token nextToken() {
and the important part in the code is this...
Token next = super.nextToken();
if(next.getType() != Unknown) {
return next;
}
The code that comes after this handles the case where you can match the bad tokens.
What you could do is use lexer modes. For this you'd had to split grammar to parser and lexer grammar. Let's start with lexer grammar:
JSONLexer.g4
/** Taken from "The Definitive ANTLR 4 Reference" by Terence Parr */
// Derived from http://json.org
lexer grammar JSONLexer;
STRING
: '"' (ESC | ~ ["\\])* '"'
;
fragment ESC
: '\\' (["\\/bfnrt] | UNICODE)
;
fragment UNICODE
: 'u' HEX HEX HEX HEX
;
fragment HEX
: [0-9a-fA-F]
;
NUMBER
: '-'? INT '.' [0-9] + EXP? | '-'? INT EXP | '-'? INT
;
fragment INT
: '0' | [1-9] [0-9]*
;
// no leading zeros
fragment EXP
: [Ee] [+\-]? INT
;
// \- since - means "range" inside [...]
TRUE : 'true';
FALSE : 'false';
NULL : 'null';
LCURL : '{';
RCURL : '}';
COL : ':';
COMA : ',';
LBRACK : '[';
RBRACK : ']';
WS
: [ \t\n\r] + -> skip
;
NON_VALID_STRING : . ->pushMode(MODE_ERR);
mode MODE_ERR;
WS1
: [ \t\n\r] + -> skip
;
COL1 : ':' ->popMode;
MY_ERR_TOKEN : ~[':']* ->type(NON_VALID_STRING);
Basically I have added some tokens used in the parser part (like LCURL, COL, COMA etc) and introduced NON_VALID_STRING token, which is basically the first character that's nothing that already is (should be) matched. Once this token is detected, I switch the lexer to MODE_ERR mode. In this mode I go back to default mode once : is detected (this can be changed and maybe refined, but server the purpose here :) ) or I say that everything else is MY_ERR_TOKEN to which I assign NON_VALID_STRING token type. Here is what ATNLRWorks says to this when I run interpret lexer option with your input:
So s is NON_VALID_STRING type and so is everything else until :. So, same type but two different tokens. If you want them not to be of the same type, simply omit the type call in the lexer grammar.
Here is the parser grammar now
JSONParser.g4
/** Taken from "The Definitive ANTLR 4 Reference" by Terence Parr */
// Derived from http://json.org
parser grammar JSONParser;
options {
tokenVocab=JSONLexer;
}
json
: object
| array
;
object
: LCURL pair (COMA pair)* RCURL
| LCURL RCURL
;
pair
: STRING COL value
;
array
: LBRACK value (COMA value)* RBRACK
| LBRACK RBRACK
;
value
: STRING
| NUMBER
| object
| array
| TRUE
| FALSE
| NULL
;
and if you run the test rig (I do it with ANTLRworks) you'll get a single error (see screenshot)
Also you could accumulate lexer errors by overriding the generated lexer class, but I understood in the question that this is not desired or I didn't understand that part :)

ANTLR4: context-sensitive spaces?

In a grammar I would like to implement texts without string delimiting xxx.
The idea is to define things like
a = xxx;
instead of
a ="xxx";
to simplify typewriting. Otherwise there should be variable definitions
and other kind of stuff as well.
As a first approach I experimented with this grammar:
grammar SpaceNoSpace;
prog: stat+;
stat:
'somethingelse' ';'
| typed description* content
;
typed:
'something' '-'
| 'anotherthing' '-'
;
description:
'someSortOfDetails' COLON ID HASH
| 'otherSortOfDetails' COLON ID HASH
;
content:
contenttext ';'
;
contenttext:
(~';')*
;
COLON: ':' ;
HASH: '#';
SEMI: ';';
SPACE: ' ';
ID: [a-zA-Z][a-zA-z0-9]+;
WS : [ \t\n\r]+ -> channel(HIDDEN);
ANY_CHAR : . ;
This works fine for input files like this:
something-someSortOfDetails: aVariableName#
this is the content of this;
anotherthing-someSortOfDetails: aVariableName#
here spaces are accepted as much as you like;
somethingelse;
But modifying the last line to
somethingelse ;
leads to a syntax error:
line 7:15 extraneous input ' ' expecting ';'
This probably reveals that the lexer rule
WS : [ \t\n\r]+ -> channel(HIDDEN);
is not applied, (but the SPACE rule???).
Otherwise, if I delete the SPACE lexer-rule, the space
in "somethingelse ;" is ignored (by lexer-rule WS), so that the parser rule
stat : somethingelse as a consequence is detected correctly.
But as a consequence of the deleted SPACE-rule the content text will be reduced to single in-between-spaces,
so "this here" will be reduced to "this here".
This is not a big problem, but nevertheless it is an
interesting question:
is it possible to implement context-sensitive WS or SPACE
lexer rules:
within the content parser-rule any space should be preserved,
in any other rule spaces should be ignored.
Is this possible to define such a context-sensitive lexer-rule behavior in ANTLR4?
Have you considered Lexer Modes? The section with mode(), pushMode(), popMode is probably interesting for you.
Yet I think that lexer modes are more a problem than a solution. Their purpose is to use (parser) context in the lexer. Consequently one should discard the paradigm of separating lexer and parser - and use a PEG-Parser instead.
Since the SPACE rule is before the WS rule, the lexer is returning a space token to the parser. The ' ' is not being being placed on the hidden channel.

Modification of java regex

I was using the below regex to substitute file names
Regex -> .*\/([A-Z0-9_]{1,9})_(O).*.cmd
Substitution -> $1
The file names were like below:
File Name | Substituted Name
---------------------------------- ------------------
/V3/OGM_REC_Offline_Level0_4D.cmd OGM_REC
/V2/PIE_PROD_Online_Level1_6D.cmd PIE_PROD
/V3/BR2_OnDemand.cmd BR2
/opt/STING_Online_Inc0_1W.cmd STING
Then the files changed and I modified the regex
Regex -> .*\/([A-Z0-9_]{1,9})(_O|Full).*.cmd
Substitution -> $1
Additional new file names
File Name | Substituted Name
---------------------- ------------------
/opt/RSU10Full.cmd RSU10
/V4/REZ40_1Full.cmd REZ40_1
Now, it seems there are new files are getting updated with below name formats
/app/OMGIT_FullOnDemand_4W.cmd
/admin/FOC_STG_Full_6D.cmd
I've modified the regex again, but it's not getting successful
Regex -> .*\/([A-Z0-9_]{1,9})(_O|Full|_Full).*.cmd
Substitution -> $1
I suggest using a version with a lazy limiting quantifier {1,9}? and optional _:
.*/([A-Z0-9_]{1,9}?)(_O|_?Full).*[.]cmd
This way, we match as few characters with [A-Z0-9_]{1,9}? as possible to return a valid captured subtext, and _?Full part can hold the optional underscore.
See the regex demo
I've noticed that unnecessary tail is allways started with: (optional) _, letter in uppercase, letter in lowercase.
So, universal solution is:
.*\/([^a-z]*?)[_]?[A-Z][a-z].*

How to get everything till the end of file with ANTLR?

I'm trying to create ANTLR grammar that will parse the following input:
#code 123 some arbitrary text
I would like to split it onto three tokens: #code, 123 and any text after the space.. It should be something very simple, but I can't understand how to make it working..
It doesn't sound like a good problem for antlr.
You can define tokens like AT : #[a-z+], NUMBER : [0-9]+ WORD : [a-z]+ and SIGNIFICANT_SPACE : [ ]+ WS : [\n] {skip();}
Then a grammar like ,
AT NUMBER [SIGNIFICANT_SPACE | WORD] +
and reconstruct the word and spaces, but it seems wrong.
You may also look at the filter option in antlr. You can use it to parse part of the input, then examine the character ranges of the tokens to get the parts of the line that were filtered out.
This is very simple.
program : start ID end;
start : '#' ID;
end : ANYTHING;
ID : ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'_'|'0'..'1')*;
ANYTHING : .*;
WS : (' '|'\n'|'\r'|'\t')+ {$channel = HIDDEN;};
Other than that, you just need to expand on the rules to suit your purposes.
This should work with ANTLR3, but I don't know about ANTLR2.

Categories