How do I convert Antlr3 to Antlr4? [duplicate] - antlr

In ANTLR 3 you could just do the following:
andExpression
: (andnotExpression -> andnotExpression)
(AND? a=andnotExpression -> ^(AndNode $andExpression $a))*
;
Any idea how to do it in the new version?

As mentioned by Sam (280Z28), ANTLR 4 does not have rewrite operators.
When generating the parser, ANTLR 4 creates some listener classes that you can use to listen for "enter" and "exit" events of all parser rules.
Also, ANTLR 4 supports "direct left recursive rules", so your expression rules can be defined in a single rule as demonstrated below:
grammar Expr;
parse
: expression EOF
;
expression
: '(' expression ')'
| IDENTIFIER
| NOT expression
| expression AND? expression
| expression OR expression
;
LPAREN : '(';
RPAREN : ')';
NOT : 'NOT';
AND : 'AND';
OR : 'OR';
IDENTIFIER : [a-zA-Z_] [a-zA-Z_0-9]*;
SPACE : [ \t\r\n]+ -> skip;
When parsing the input "a b OR NOT c AND d", the following parse tree will be created:
(image created using ANTLRWorks2, thank you Sam! Very impressive IDE, I love it!)
Generate the parser and listener classes:
java -cp antlr-4.0-complete.jar org.antlr.v4.Tool Expr.g4
and the following class is generated that will let you help "walk" the tree:
public class ExprBaseListener implements ExprListener {
#Override public void enterExpression(ExprParser.ExpressionContext ctx) { }
#Override public void exitExpression(ExprParser.ExpressionContext ctx) { }
#Override public void enterParse(ExprParser.ParseContext ctx) { }
#Override public void exitParse(ExprParser.ParseContext ctx) { }
#Override public void enterEveryRule(ParserRuleContext<Token> ctx) { }
#Override public void exitEveryRule(ParserRuleContext<Token> ctx) { }
#Override public void visitTerminal(TerminalNode<Token> node) { }
#Override public void visitErrorNode(ErrorNode<Token> node) { }
}
Now you'll need to inspect the ExprParser.ExpressionContext to see which of the alternatives in expression is matched, which is where "tree-labels" come in handy. Change the expression rule as follows:
expression
: '(' expression ')' # EXPR
| IDENTIFIER # ID_EXPR
| 'NOT' expression # NOT_EXPR
| expression 'AND'? expression # AND_EXPR
| expression 'OR' expression # OR_EXPR
;
and regenerate the parser and listeners, and you'll see that ExprBaseListener now looks like this:
public class ExprBaseListener implements ExprListener {
#Override public void enterAND_EXPR(ExprParser.AND_EXPRContext ctx) { }
#Override public void exitAND_EXPR(ExprParser.AND_EXPRContext ctx) { }
#Override public void enterOR_EXPR(ExprParser.OR_EXPRContext ctx) { }
#Override public void exitOR_EXPR(ExprParser.OR_EXPRContext ctx) { }
#Override public void enterEXPR(ExprParser.EXPRContext ctx) { }
#Override public void exitEXPR(ExprParser.EXPRContext ctx) { }
#Override public void enterNOT_EXPR(ExprParser.NOT_EXPRContext ctx) { }
#Override public void exitNOT_EXPR(ExprParser.NOT_EXPRContext ctx) { }
#Override public void enterID_EXPR(ExprParser.ID_EXPRContext ctx) { }
#Override public void exitID_EXPR(ExprParser.ID_EXPRContext ctx) { }
#Override public void enterParse(ExprParser.ParseContext ctx) { }
#Override public void exitParse(ExprParser.ParseContext ctx) { }
#Override public void enterEveryRule(ParserRuleContext ctx) { }
#Override public void exitEveryRule(ParserRuleContext ctx) { }
#Override public void visitTerminal(TerminalNode node) { }
#Override public void visitErrorNode(ErrorNode node) { }
}
I.e., for each label in expression a separate enter- and exit-method is created.
Now, let's say you're only interested in enter-events of the AND expression. You could create a custom class that extends this ExprBaseListener and override enterAND_EXPR:
public class ExprWalker extends ExprBaseListener {
#Override
public void enterAND_EXPR(ExprParser.AND_EXPRContext ctx) {
java.util.List<ExprParser.ExpressionContext> e = ctx.expression();
System.out.println("AND -> " + e.get(0).getText() + ", " + e.get(1).getText());
}
}
To test this all, create a small driver class:
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.*;
public class Main {
public static void main(String[] args) throws Exception {
String input = "a b OR NOT c AND d";
ExprLexer lexer = new ExprLexer(new ANTLRInputStream(input));
ExprParser parser = new ExprParser(new CommonTokenStream(lexer));
ParseTree tree = parser.parse();
ParseTreeWalker walker = new ParseTreeWalker();
walker.walk(new ExprWalker(), tree);
}
}
and run it:
java -cp antlr-4.0-complete.jar org.antlr.v4.Tool Expr.g4
javac -cp antlr-4.0-complete.jar *.java
java -cp .:antlr-4.0-complete.jar Main
after which you'll see the following being printed to your console:
AND -> a, bORNOTcANDd
AND -> NOTc, d

ANTLR 4 does not have a rewrite operator or an output=AST option like ANTLR 3. Trees produced by ANTLR 4 parsers are parse trees, the shape of which is implicitly defined by the grammar rules.

Related

How to add a simple tab in Intellij Idea plugin

I am creating a simple class diagram plugin for Intellij Idea. I'm struggling now with creating a simple tab in IDE. This tab I will fill up with a prepared JPanel and nothing else.
I have already done the same in NetBeans and I would like to find something with similar behavior as TopComponent in NetBeans provides, but anything working would be cool.
So here is the answer:
create implementation of com.intellij.openapi.fileEditor.FileEditor. This is your actual tab
create implementation of com.intellij.openapi.fileEditor.FileEditorProvider
accept() defines type of files which your editor opens
create() should returns the proper instance of your editor
register your FileEditoProvider in plugin.xml
Editor:
public class YourEditor implements FileEditor {
private VirtualFile file;
public YourEditor(VirtualFile file) {
this.file = file;
}
#Override
public #NotNull JComponent getComponent() {
JPanel tabContent = new JPanel();
tabContent.add(new JButton("foo"));
return tabContent;
}
#Override
public #Nullable JComponent getPreferredFocusedComponent() {
return null;
}
#Override
public #Nls(capitalization = Nls.Capitalization.Title)
#NotNull String getName() {
return "name";
}
#Override
public void setState(#NotNull FileEditorState fileEditorState) {
}
#Override
public boolean isModified() {
return false;
}
#Override
public boolean isValid() {
return true;
}
#Override
public void addPropertyChangeListener(#NotNull PropertyChangeListener propertyChangeListener) {
}
#Override
public void removePropertyChangeListener(#NotNull PropertyChangeListener propertyChangeListener) {
}
#Override
public #Nullable FileEditorLocation getCurrentLocation() {
return null;
}
#Override
public void dispose() {
Disposer.dispose(this);
}
#Override
public <T> #Nullable T getUserData(#NotNull Key<T> key) {
return null;
}
#Override
public <T> void putUserData(#NotNull Key<T> key, #Nullable T t) {
}
#Override
public #Nullable VirtualFile getFile() {
return this.file;
}
}
Provider:
public class YourEditorProvider implements FileEditorProvider, DumbAware {
private static String EDITOR_TYPE_ID = "DiagramView";
#Override
public boolean accept(#NotNull Project project, #NotNull VirtualFile virtualFile) {
return true; //will accept all kind of files, must be specified
}
#Override
public #NotNull
FileEditor createEditor(#NotNull Project project, #NotNull VirtualFile virtualFile) {
return new YourEditor(virtualFile);
}
#Override
public #NotNull
#NonNls
String getEditorTypeId() {
return EDITOR_TYPE_ID;
}
#Override
public #NotNull
FileEditorPolicy getPolicy() {
return FileEditorPolicy.HIDE_DEFAULT_EDITOR;
}
}
and finally put FileEditorProvider extension in pluxin.xml:
<extensions defaultExtensionNs="com.intellij">
<fileEditorProvider implementation="classDiagramPainter.DiagramViewProvider"/>
</extensions>

Using TypeDefinition in ElementMatchers.takesArguments()

I am creating a class like this:
class A{
A(A a){
...
}
A(){
A(null);
}
}
and my code looks like this:
TypeDefinition selfTypeDefinition = TypeDescription.Generic.Builder.rawType(TargetType.class).build();
Class c = new ByteBuddy().subclass(Object.class,ConstructorStrategy.Default.NO_CONSTRUCTORS).name("A").
defineConstructor(Modifier.PUBLIC).withParameters(selfTypeDefinition)
.intercept(MethodCall.invoke(Object.class.getConstructor())
.andThen(MethodCall.run(new Runnable() {
#Override
public void run() {
System.out.println("!!!!init A(A)");
}
})))
.defineConstructor(Modifier.PUBLIC).withParameters(new Type[0])
.intercept(MethodCall.invoke(ElementMatchers.isConstructor()
.and(ElementMatchers.takesArguments(selfTypeDefinition)))
.with(new Object[1])
.andThen(MethodCall.run(new Runnable() {
#Override
public void run() {
System.out.println("!!!!init A()");
}
})))
.make().load(Test.class.getClassLoader()).getLoaded();
The problem is I cannot pass TypeDefinition to ElementMatchers.takesArguments(). I have tried passing selfTypeDefinition.asErasure() but it does not work.
Just use selfTypeDefinition.asErasure() to represent a TypeDescription. Byte Buddy also supports matching generic type descriptions, but the matcher you are using expects an erasure format.

When attaching agent to running process, bytebuddy transformer doesn't seem to take effect

The code of my program to be attached is as below.
public class Foo {
}
public class TestEntry {
public TestEntry() {
}
public static void main(String[] args) throws Exception {
try
{
while(true)
{
System.out.println(new Foo().toString());
Thread.sleep(1000);
}
}
catch(Exception e)
{}
}
}
What I attempt to do is to make Foo.toString() returns 'test' by using the following agent.
public class InjectionAgent {
public InjectionAgent() {
}
public static void agentmain(String args, Instrumentation inst) throws Exception
{
System.out.println("agentmain Args:" + args);
new AgentBuilder.Default()
.type(ElementMatchers.named("Foo"))
.transform(new AgentBuilder.Transformer() {
#Override
public Builder<?> transform(Builder<?> arg0, TypeDescription arg1,
ClassLoader arg2, JavaModule arg3) {
return arg0.method(ElementMatchers.named("toString"))
.intercept(FixedValue.value("test"));
}
}).installOn(inst);
}
public static void premain(String args, Instrumentation inst) throws Exception
{
System.out.println("premain Args:" + args);
new AgentBuilder.Default()
.type(ElementMatchers.named("Foo"))
.transform(new AgentBuilder.Transformer() {
#Override
public Builder<?> transform(Builder<?> arg0, TypeDescription arg1,
ClassLoader arg2, JavaModule arg3) {
return arg0.method(ElementMatchers.named("toString"))
.intercept(FixedValue.value("test"));
}
}).installOn(inst);
}
}
I notice that, it was successful when I using -javaagent way, whereas attach way failed, here is code for attach.
public class Injection {
public Injection() {
}
public static void main(String[] args) throws AttachNotSupportedException, IOException, AgentLoadException, AgentInitializationException, InterruptedException {
VirtualMachine vm = null;
String agentjarpath = args[0];
vm = VirtualMachine.attach(args[1]);
vm.loadAgent(agentjarpath, "This is Args to the Agent.");
vm.detach();
}
}
I tried to add AgentBuilder.Listener.StreamWriting.toSystemOut() to the agent, after attaching, the output of TestEntry shows
[Byte Buddy] DISCOVERY Foo [sun.misc.Launcher$AppClassLoader#33909752, null, loaded=true]
[Byte Buddy] TRANSFORM Foo [sun.misc.Launcher$AppClassLoader#33909752, null, loaded=true]
[Byte Buddy] COMPLETE Foo [sun.misc.Launcher$AppClassLoader#33909752, null, loaded=true]
Foo#7f31245a
Foo#6d6f6e28
Foo#135fbaa4
Foo#45ee12a7
Foo#330bedb4
==================================Update=====================================
I defined a public method 'Bar' in Foo like this
public class Foo {
public String Bar()
{
return "Bar";
}
}
and then I was trying to make Foo.Bar() returns "modified" in the following way:
public static void agentmain(String args, Instrumentation inst) throws Exception
{
System.out.println("agentmain Args:" + args);
premain(args, inst);
new AgentBuilder.Default()
.with(RedefinitionStrategy.RETRANSFORMATION)
.disableClassFormatChanges()
.with(AgentBuilder.Listener.StreamWriting.toSystemOut())
.type(ElementMatchers.named("Foo"))
.transform(new AgentBuilder.Transformer() {
#Override
public Builder<?> transform(Builder<?> arg0, TypeDescription arg1,
ClassLoader arg2, JavaModule arg3) {
return arg0.visit(Advice.to(InjectionTemplate.class).on(ElementMatchers.named("Bar")));
}
})
.installOn(inst);
}
static class InjectionTemplate {
#Advice.OnMethodExit
static void exit(#Advice.Return String self) {
System.out.println(self.toString() + " " + self.getClass().toString());
self = new String("modified");
}
}
but I got this error:
java.lang.IllegalStateException: Cannot write to read-only parameter class java.lang.String at 1
any suggestions?
It does not seem like you are using redefinition for your agent. You can activate it using:
new AgentBuilder.Default()
.with(RedefinitionStrategy.RETRANSFORMATION)
.disableClassFormatChanges();
The last part is required on most JVMs (with the notable exception of the dynamic code evolution VM, a custom build of HotSpot). It tells Byte Buddy to not add fields or methods, what most VMs do not support.
In this case, it is no longer possible to invoke the original implementation of a method what is however not required for your FixedValue. Typically, users of Byte Buddy take advantage of Advice when creating an agent that applies dynamic transformations of classes.

how to intercept a method with specific parameters with bytebuddy

I want to intercept method named methodA with one arg which's type is String as blow, what should i do. How to use hasParameters() api?
public class Demo {
public static void main(String[] args) {
new ByteBuddy()
.subclass(A.class)
.method(named("methodA").and(hasParameters(?)))
}
static class A {
public void methodA() {
System.out.println("methodA() invoked.");
}
public void methodA(String arg) {
System.out.println("methodA(" + arg + ") invoked.");
}
}
}
For this you want the ElementMatchers.takesArguments(String.class) matcher.
So something like that:
Class<? extends A> loaded = new ByteBuddy().subclass(A.class)
.method(ElementMatchers.named("methodA").and(ElementMatchers.takesArguments(String.class)))
.intercept(MethodDelegation.to(Demo.class))
.make().load(Demo.class.getClassLoader(), ClassLoadingStrategy.Default.INJECTION).getLoaded();
A instance = loaded.getConstructor().newInstance();
instance.methodA("abc");
instance.methodA();
public class Demo {
static void intercept(String arg){
System.out.println("intercepted");
}
}
To clarify, you need to define a matcher (similar to a filter) to apply to methods. Create some constraint in the matcher so it will only match to some parameter structure you specify:
ElementMatcher<Iterable<? extends ParameterDescription>> matcher = parameterDescriptions -> {
for (ParameterDescription p : parameterDescriptions) {
if (p.getType().equals(TypeDescription.STRING.asGenericType()) && p.getIndex() == 0) return true;
}
return false;
};
ByteBuddyAgent.install();
new ByteBuddy()
.redefine(SomeType.class)
.method(ElementMatchers.hasParameters(matcher))
.intercept(FixedValue.value("Hello World"))
.make()
.load(SomeType.class.getClassLoader(),
ClassReloadingStrategy.fromInstalledAgent());

Is there a way to use Java 8 lambda style to add custom Jackson serializer?

Right now adding Jackson custom serializer via Jackson module is verbose and does not lend itself to the new Java 8 lambda pattern.
Is there a way to use Java 8 lambda style to add custom Jackson serializer?
You can make a simple Jackson8Module that would allow you to do the following:
ObjectMapper jacksonMapper = new ObjectMapper();
Jackson8Module module = new Jackson8Module();
module.addStringSerializer(LocalDate.class, (val) -> val.toString());
module.addStringSerializer(LocalDateTime.class, (val) -> val.toString());
jacksonMapper.registerModule(module);
The Jackson8Module code just extends Jackson SimpleModule to provide Java 8 friendly methods (it can be extended to support other Jackson Module methods) :
public class Jackson8Module extends SimpleModule {
public <T> void addCustomSerializer(Class<T> cls, SerializeFunction<T> serializeFunction){
JsonSerializer<T> jsonSerializer = new JsonSerializer<T>() {
#Override
public void serialize(T t, JsonGenerator jgen, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
serializeFunction.serialize(t, jgen);
}
};
addSerializer(cls,jsonSerializer);
}
public <T> void addStringSerializer(Class<T> cls, Function<T,String> serializeFunction) {
JsonSerializer<T> jsonSerializer = new JsonSerializer<T>() {
#Override
public void serialize(T t, JsonGenerator jgen, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
String val = serializeFunction.apply(t);
jgen.writeString(val);
}
};
addSerializer(cls,jsonSerializer);
}
public static interface SerializeFunction<T> {
public void serialize(T t, JsonGenerator jgen) throws IOException, JsonProcessingException;
}
}
Here is the gist of the Jackson8Module: https://gist.github.com/jeremychone/a7e06b8baffef88a8816