I've the following island grammar that works fine (and I think as expected):
lexer grammar FastTestLexer;
// Default mode rules (the SEA)
OPEN1 : '#' -> mode(ISLAND) ; // switch to ISLAND mode
OPEN2 : '##' -> mode(ISLAND);
OPEN3 : '###' -> mode(ISLAND);
OPEN4 : '####' -> mode(ISLAND);
LISTING_OPEN : '~~~~~' -> mode(LISTING);
NL : [\r\n]+;
TEXT : ~('#'|'~')+; // ~('#'|'~')+ ; // clump all text together
mode ISLAND;
CLOSE1 : '#' -> mode(DEFAULT_MODE) ; // back to SEA mode
CLOSE2 : '##' -> mode(DEFAULT_MODE) ; // back to SEA mode
CLOSE3 : '###' -> mode(DEFAULT_MODE) ; // back to SEA mode
CLOSE4 : '####' -> mode(DEFAULT_MODE) ; // back to SEA mode
INLINE : ~'#'+ ; // clump all text together
mode LISTING;
LISTING_CLOSE : '~~~~~' -> mode(DEFAULT_MODE);
INLINE_LISTING : ~'~'+; //~('~'|'#')+;
And the parser grammar:
parser grammar FastTextParser;
options { tokenVocab=FastTestLexer; } // use tokens from ModeTagsLexer.g4
dnpMD
: subheadline NL headline NL lead (subheading | listing | text | NL)*
;
headline
: OPEN1 INLINE CLOSE1
;
subheadline
: OPEN2 INLINE CLOSE2
;
lead
: OPEN3 INLINE CLOSE3
;
subheading
: OPEN4 INLINE CLOSE4
;
listing
: LISTING_OPEN INLINE_LISTING LISTING_CLOSE
;
text
: TEXT
;
Input text like this ones working fine:
## Heading2 ##
# Heading1 #
### Heading3 ###
fffff
#### Heading4 ####
I'm a line.
~~~~~
ffffff
~~~~~
I'm a line, too.
#### Heading4a ####
The TEXT lexer token is matching all the text. Of course except '#' and '~' so the parser knows when there are headings and listings are coming.
My problem is that within the text both characters '#' and '~' should be allowed. The single '#' is only needed for the heading and this parser rule is not active within the body (just one heading at the beginning of the document).
Is there a way to allow '#' and '~' within the text without escaping? My first thought was to disallow '##' within the text:
TEXT : ~('##'|'~')+;
But multiple characters are not allowed there. :(
Maybe someone can give me a hint. But I think this isn't solvable at all. Not solvable with ANTLR4 I mean. Maybe there's another technology.
You could try to do more work in the parser and less in the lexer. Allow # and ~ inside text and not inside TEXT, something similar to:
text
: TEXT
: OPEN1
: TEXT text
: OPEN1 text
;
Adjust the rules for the headlines etc. accordingly.
That way, not the lexer has to decide what a # (or ~) means, what can be relatively hard, because the lexer does not really know the context, but it only decides that it has seen a hash sign. Instead, the parser decides on the meaning of it, and it knows the context in which it appears.
Related
I am trying to parse message using antlr4
:12B:DOCUMENT:some nice text
DOCUMENT2:some nice text
this is the expected output from the parser
heading -> 12B
subheading -> DOCUMENT
subheading -> DOCUMENT2
TEXT -> some nice text
TEXT -> some nice text
but on trying to extract the heading with the following grammar
grammar Simple;
para : heading* EOF;
header : heading text ;
heading : COLEN HEAD COLEN;
text : TEXT;
/* tokens */
TEXT : ~[\t]+ ;
HEAD : [0-9A-Z]+ ;
COLEN : ':';
one supplying the input I am getting the following error
line 1:0 mismatched input ':12:nithin\n' expecting ':'
Could someone please tell me the possible cause and solution to parse the same? If I've missed anything, over- or under-emphasized a specific point, please let me know in the comments. Thank you so much in advance for your time.
I several projects I have run into a similar effect in my grammars.
I have the need to parse something like Key="Value"
So I create a grammar (simplest I could make to show the effect):
grammar test;
KEY : [a-zA-Z0-9]+ ;
VALUE : DOUBLEQUOTE [ _a-zA-Z0-9.-]+ DOUBLEQUOTE ;
DOUBLEQUOTE : '"' ;
EQUALS : '=' ;
entry : key=KEY EQUALS value=VALUE;
I can now parse thing="One Two Three" and in my code I receive
key = thing
value = "One Two Three"
In all of my projects I end up with an extra step to strip those " from the value.
Usually something like this (I use Java)
String value = ctx.value.getText();
value = value.substring(1, value.length()-1);
In my real grammars I find it very hard to move the check of the surrounding " into the parser.
Is there a clean way to already drop the " by doing something in the lexer/parser?
Essentially I want ctx.value.getText() to return One Two Three instead of "One Two Three".
Update:
I have been playing with the excellent answer provided by Bart Kiers and found this variation which does exactly what I was looking for.
By putting the DOUBLEQUOTE on a hidden channel they are used by the lexer and hidden from the parser.
TestLexer.g4
lexer grammar TestLexer;
KEY : [a-zA-Z0-9]+;
DOUBLEQUOTE : '"' -> channel(HIDDEN), pushMode(STRING_MODE);
EQUALS : '=';
mode STRING_MODE;
STRING_DOUBLEQUOTE
: '"' -> channel(HIDDEN), type(DOUBLEQUOTE), popMode
;
STRING
: [ _a-zA-Z0-9.-]+
;
and
TestParser.g4
parser grammar TestParser;
options { tokenVocab=TestLexer; }
entry : key=KEY EQUALS value=STRING ;
Try this:
VALUE
: DOUBLEQUOTE [ _a-zA-Z0-9.-]+ DOUBLEQUOTE
{setText(getText().substring(1, getText().length()-1));}
;
Needless to say: this ties your grammar to Java, and (depending how many embedded Java code you have) your grammar will be hard to port to some other target language.
EDIT
Once a token is created, there is no built-in way to separate it (other than doing so in embedded actions, as I demonstrated). What you're looking for can be done, but that means rewriting your grammar so that a string literal is not constructed as a single token. This can be done by using lexical modes so that the string can be constructed in the parser.
A quick demo:
TestLexer.g4
lexer grammar TestLexer;
KEY : [a-zA-Z0-9]+;
DOUBLEQUOTE : '"' -> pushMode(STRING_MODE);
EQUALS : '=';
mode STRING_MODE;
STRING_DOUBLEQUOTE
: '"' -> type(DOUBLEQUOTE), popMode
;
STRING_ATOM
: [ _a-zA-Z0-9.-]
;
TestParser.g4
parser grammar TestParser;
options { tokenVocab=TestLexer; }
entry : key=KEY EQUALS value;
value : DOUBLEQUOTE string_atoms DOUBLEQUOTE;
string_atoms : STRING_ATOM*;
If you now run the Java code:
Lexer lexer = new TestLexer(CharStreams.fromString("Key=\"One Two Three\""));
TestParser parser = new TestParser(new CommonTokenStream(lexer));
TestParser.EntryContext entry = parser.entry();
System.out.println(entry.value().string_atoms().getText());
this will be printed:
One Two Three
I am parsing a language which has some difficult syntax that I need some help or suggestions to tackle it.
Heres an a typical line =>
IF CLCI((ZNTEM+CHRCNT),1,1H())) EQ 0
The difficult bit is nH(....any character within the character set.....) where n is 1 in this example and the single char in question is a ')' . My lexer has:
fragment Lp: '(';
fragment Rp: ')';
LP: Lp;
RP: Rp; etc....
My current non-working solution is to switch modes in the lexer because then I can then define all the special chars to consume
// Default mode rules
STRING_SUB: INT 'H' LP -> pushMode(ISLAND) ; // switch to ISLAND mode
and then to switch back
// Special mode of INT H ( ID )
// ID is the string substitute which can includes, spaces, backslash, etc, special chars
mode ISLAND;
ISLAND_CLOSE : RP -> popMode ; // back to nomal mode
ID : SpecialChars+; // match/send ID in tag to parser
fragment SpecialChars: '\u0020'..'\u0027' | '\u002A'..'\u0060' | '\u0061'..'\u007E' | '¦';
But obviously the pop mode trigger is the ')' which fails in the particular example case, because the payload is a RP. Any suggestions?
I'm driving crazy trying to generate a parser Grammar with ANTLR.
I've got plain text file like:
Diagram : VW 503 FSX 09/02/2015 12/02/2015 STP
Fleet : AAAA
OFF :
AAA 05+44 5R06
KKK 05+55 06.04 1R06 5530
ZZZ 06.24 06.30 1R06 5530
YYY 07.53 REVRSE
YYY 08.23 9G98 5070
WORKS :
MILES :(LD) 1288.35 (ETY) 3.18 (TOT) 1291.53
Each "Diagram" entity is contained beetween "Diagram :" and the "(TOT) before EOF.
In the same plain txt file multiple "Diagram" entity can be present.
I've done some test with ANTRL
`grammar Hello2;
xxxt : diagram+;
diagram : DIAGRAM_ini txt fleet LEGS+ DIAGRAM_end;
txt : TEXT;
fleet : FLEET_INI txt;
num : NUMBER;
// Lexer Rules
DIAGRAM_ini : 'Diagram :';
DIAGRAM_end : '(TOT)' ;
LEGS : ('AAA' | 'KKK' | 'ZZZ' | 'YYY') ;
FLEET_INI : 'Fleet :';
TEXT : ('a'..'z')+ ;
NUMBER: ('0'..'9') ;
WHITESPACE : ( '\t' | ' ' | '\r' | '\n'| '\u000C' )+ -> skip ;`
My Goal is to be able to parse Diagrams recursively, and gather all LEGS text/number.
Any help/tips is much more than appreciated!
Many Thanks
Regs
S.
I suggest not parsing the file like you did. This file does not define a language with words and grammar, but rather a formatted text of chars:
The formatting conventions are rather weak
The labels before the colon cannot serve as tokens since they may reappear in the body (AAA (=label) vs AAAA (=body)
The tokens must be very primitive to fit this requirements
Solution with ANTLR
You need a weaker grammar to solve this problem, e.g.
grammar diagrams;
diagrams : diagram+ ;
diagram : section+ ;
section : WORD ':' body? ;
body : textline+;
textline : (WORD | NUMBER | SIGNS)* ('\r' | '\n')+;
WORD : LETTER+ ;
NUMBER : DIGIT+ ;
SIGNS : SIGN+ ;
WHITESPACE : ( '\t' | ' ' )+ -> skip ;
fragment LETTER : ('a'..'z' | 'A'..'Z') ;
fragment SIGN : ('.'|'+'|'('|')'|'/') ;
fragment DIGIT : ('0'..'9') ;
Run a visitor on the Parsing result
to build up the normalized text of body
to filter out the LEGS lines out of the body
to parse a LEGS line with another parser (a regexp-parser would be sufficient here, but you could also define another ANTLR-Parser)
Another alternative:
Try out Packrat parsing (e.g. parboiled)
- it is (especially for people with low experience in compiler construction) more comprehensible
it matches better to your grammar design
parboiled is pure java (grammar specified in java)
Disadvantages:
Whitespace handling must be done in Parser Rules
Debugging/Error Messages are a problem (with all packrat parsers)
I'm trying to build a grammar for a recognizer of a spice-like language using Antlr-3.1.3 (I use this version because of the Python target). I don't have experience with parsers. I've found a master thesis where the student has done the syntactic analysis of the SPICE 2G6 language and built a parser using the LEX and YACC compiler writing tools. (http://digitool.library.mcgill.ca/R/?func=dbin-jump-full&object_id=60667&local_base=GEN01-MCG02) In chapter 4, he describes a grammar in Backus-Naur form for the SPICE 2G6 language, and appends to the work the LEX and YACC code files of the parser.
I'm basing myself in this work to create a simpler grammar for a recognizer of a more restrictive spice language.
I read the Antlr manual, but could not figure out how to solve two problems, that the code snippet below illustrates.
grammar Najm_teste;
resistor
: RES NODE NODE VALUE 'G2'? COMMENT? NEWLINE
;
// START:tokens
RES : ('R'|'r') DIG+;
NODE : DIG+; // non-negative integer
VALUE : REAL; // non-negative real
fragment
SIG : '+'|'-';
fragment
DIG : '0'..'9';
fragment
EXP : ('E'|'e') SIG? DIG+;
fragment
FLT : (DIG+ '.' DIG+)|('.' DIG+)|(DIG+ '.');
fragment
REAL : (DIG+ EXP?)|(FLT EXP?);
COMMENT : '%' ( options {greedy=false;} : . )* NEWLINE;
NEWLINE : '\r'? '\n';
WS : (' '|'\t')+ {$channel=HIDDEN;};
// END:tokens
In the grammar above, the token NODE is a subset of the set represented by the VALUE token. The grammar correctly interprets an input like "R1 5 0 1.1/n", but cannot interpret an input like "R1 5 0 1/n", because it maps "1" to the token NODE, instead of mapping it to the token VALUE, as NODE comes before VALUE in the tokens section. Given such inputs, does anyone has an idea of how can I map the "1" to the correct token VALUE, or a suggestion of how can I alter the grammar so that I can correctly interpret the input?
The second problem is the presence of a comment at the end of a line. Because the NEWLINE token delimits: (1) the end of a comment; and (2) the end of a line of code. When I include a comment at the end of a line of code, two newline characters are necessary to the parser correctly recognize the line of code, otherwise, just one newline character is necessary. How could I improve this?
Thanks!
Problem 1
The lexer does not "listen" to the parser. The lexer simply creates tokens that contain as much characters as possible. In case two tokens match the same amount of characters, the token defined first will "win". In other words, "1" will always be tokenized as a NODE, even though the parser is trying to match a VALUE.
You can do something like this instead:
resistor
: RES NODE NODE value 'G2'? COMMENT? NEWLINE
;
value : NODE | REAL;
// START:tokens
RES : ('R'|'r') DIG+;
NODE : DIG+;
REAL : (DIG+ EXP?) | (FLT EXP?);
...
E.g., I removed VALUE, added value and removed fragment from REAL
Problem 2
Do not let the comment match the line break:
COMMENT : '%' ~('\r' | '\n')*;
where ~('\r' | '\n')* matches zero or more chars other than line break characters.