How to allow lexer to parse specific code parts from java?

Viewed 74

I am currently creating a compiler with antlr4 which should allow java code to be parsed.

How do i allow:

public void =(Integer value) => java { this.value = value; }

that the code between java { } is not being parsed by antlr, but should have a visitor in my parser.

Currently i have

javaStatementBody: KWJAVA LCURLY .*? RCURLY

but this obviously does not work and .*? parses the whole file.

Please do not answer with "use quotes", thats not gonna be my solution, because i want to allow java code highlighting.

1 Answers

You could create separate lexer and parser grammars so that you can use lexical modes. Whenever the lexer "sees" the input java {, it moves to the JAVA_MODE. And when in the Java mode, you tokenise comments, string- and char literals. Also when in this mode, you encounter a {, you push the same JAVA_MODE so that the lexer knows it's nested once. And when you encounter a }, you pop a mode from the stack (resulting in either going back to the default mode, or staying in the Java mode but one level less deep).

A quick demo:

IslandLexer.g4

lexer grammar IslandLexer;

JAVA_START
 : 'java' SPACES '{' -> pushMode(JAVA_MODE)
 ;

OTHER
 : .
 ;

fragment SPACES : [ \t\r\n]+;

mode JAVA_MODE;

  JAVA_CHAR          : '\'' ( ~[\\'\r\n] | '\\' [tbnrf'\\] ) '\'';
  JAVA_STRING        : '"' ( ~[\\"\r\n] | '\\' [tbnrf"\\] )* '"';
  JAVA_LINE_COMMENT  : '//' ~[\r\n]*;
  JAVA_BLOCK_COMMENT : '/*' .*? '*/';
  JAVA_OPEN_BRACE    : '{' -> pushMode(JAVA_MODE);
  JAVA_CLOSE_BRACE   : '}' -> popMode;
  JAVA_OTHER         : ~[{}];

IslandParser.g4

parser grammar IslandParser;

options { tokenVocab=IslandLexer; }

parse
 : unit* EOF
 ;

unit
 : base_language
 | java_janguage
 ;

base_language
 : OTHER+
 ;

java_janguage
 : JAVA_START java_atom+
 ;

java_atom
 : JAVA_CHAR
 | JAVA_STRING
 | JAVA_LINE_COMMENT
 | JAVA_BLOCK_COMMENT
 | JAVA_OPEN_BRACE
 | JAVA_CLOSE_BRACE
 | JAVA_OTHER
 ;

Test it with the following code:

String source = "foo \n" +
        "\n" +
        "java { \n" +
        "  char foo() { \n" +
        "    /* a quote in a comment \\\" */ \n" +
        "    String s = \"java {...}\"; \n" +
        "    return '}'; \n" +
        "  }\n" +
        "}\n" +
        "\n" +
        "bar";

IslandLexer lexer = new IslandLexer(CharStreams.fromString(source));
IslandParser parser = new IslandParser(new CommonTokenStream(lexer));
System.out.println(parser.parse().toStringTree(parser));

which is the following parse tree:

enter image description here

Related