java - tomassetti - grun antlr
¿Existe un ejemplo simple de uso de antlr4 para crear un AST a partir del código fuente de Java y extraer métodos, variables y comentarios? (2)
Aquí está.
Primero, vas a comprar el libro ANTLR4 ;-)
Segundo, descargará el tarro antlr4 y la gramática java ( pragprog.com/book/tpantlr2/the-definitive-antlr-4-reference )
Luego, puedes cambiar la gramática un poco, agregando esto al encabezado
(...)
grammar Java;
options
{
language = Java;
}
// starting point for parsing a java file
compilationUnit
(...)
Voy a cambiar una cosita en la gramática solo para ilustrar algo.
/*
methodDeclaration
: (type|''void'') Identifier formalParameters (''['' '']'')*
(''throws'' qualifiedNameList)?
( methodBody
| '';''
)
;
*/
methodDeclaration
: (type|''void'') myMethodName formalParameters (''['' '']'')*
(''throws'' qualifiedNameList)?
( methodBody
| '';''
)
;
myMethodName
: Identifier
;
Verá, la gramática original no le permite identificar el identificador del método de ningún otro identificador, por lo que he comentado el bloque original y agregado uno nuevo solo para mostrarle cómo obtener lo que desea.
Tendrá que hacer lo mismo con otros elementos que desee recuperar, como los comentarios, que actualmente se están omitiendo. Esto es para ti :-)
Ahora, crea una clase como esta para generar todos los apéndices
package mypackage;
public class Gen {
public static void main(String[] args) {
String[] arg0 = { "-visitor", "/home/leoks/EclipseIndigo/workspace2/SO/src/mypackage/Java.g4", "-package", "mypackage" };
org.antlr.v4.Tool.main(arg0);
}
}
Ejecute Gen y obtendrá un código Java creado para usted en mypackage.
Ahora crea un visitante. En realidad, el visitante se analizará en este ejemplo.
package mypackage;
import java.io.FileInputStream;
import java.io.IOException;
import mypackage.JavaParser.MyMethodNameContext;
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.ParseTreeWalker;
/**
* @author Leonardo Kenji Feb 4, 2014
*/
public class MyVisitor extends JavaBaseVisitor<Void> {
/**
* Main Method
*
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
ANTLRInputStream input = new ANTLRInputStream(new FileInputStream("/home/leoks/EclipseIndigo/workspace2/SO/src/mypackage/MyVisitor.java")); // we''ll
// parse
// this
// file
JavaLexer lexer = new JavaLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
JavaParser parser = new JavaParser(tokens);
ParseTree tree = parser.compilationUnit(); // see the grammar ->
// starting point for
// parsing a java file
MyVisitor visitor = new MyVisitor(); // extends JavaBaseVisitor<Void>
// and overrides the methods
// you''re interested
visitor.visit(tree);
}
/**
* some attribute comment
*/
private String someAttribute;
@Override
public Void visitMyMethodName(MyMethodNameContext ctx) {
System.out.println("Method name:" + ctx.getText());
return super.visitMyMethodName(ctx);
}
}
y eso es.
Obtendrás algo como
Method name:main
Method name:visitMyMethodName
PD. una cosa más. Mientras escribía este código en eclipse, tengo una extraña excepción. Esto se debe a Java 7 y se puede arreglar simplemente agregando estos parámetros a su compilador (gracias a este enlace http://java.dzone.com/articles/javalangverifyerror-expecting )
¿Alguien puede proporcionar un ejemplo detallado de cómo puedo hacer esto usando antlr4? Las instrucciones desde la instalación de antlr4 y sus dependencias serían altamente apreciadas.
grammar Criteria;
@parser::header {
import java.util.regex.Pattern;
}
options
{
superClass = ReferenceResolvingParser;
}
@parser::members {
public CriteriaParser(TokenStream input, Object object) {
this(input);
setObject(object);
}
}
/* Grammar rules */
reference returns [String value]
: ''$.'' IDENTIFIER { $value = resolveReferenceValue($IDENTIFIER.text); }
;
operand returns [String value]
: TRUE { $value = $TRUE.text; }
| FALSE { $value = $FALSE.text; }
| DECIMAL { $value = $DECIMAL.text; }
| QUOTED_LITERAL { $value = $QUOTED_LITERAL.text.substring(1, $QUOTED_LITERAL.text.length() - 1); }
| reference { $value = $reference.value; }
;
operand_list returns [List value]
@init{ $value = new ArrayList(); }
: LBPAREN o=operand { $value.add($o.value); } ('','' o=operand { $value.add($o.value); })* RBPAREN
;
comparison_expression returns [boolean value]
: lhs=operand NEQ rhs=operand { $value = !$lhs.value.equals($rhs.value); }
| lhs=operand EQ rhs=operand { $value = $lhs.value.equals($rhs.value); }
| lhs=operand GT rhs=operand { $value = $lhs.value.compareTo($rhs.value) > 0; }
| lhs=operand GE rhs=operand { $value = $lhs.value.compareTo($rhs.value) >= 0; }
| lhs=operand LT rhs=operand { $value = $lhs.value.compareTo($rhs.value) < 0; }
| lhs=operand LE rhs=operand { $value = $lhs.value.compareTo($rhs.value) <= 0; }
;
in_expression returns [boolean value]
: lhs=operand IN rhs=operand_list { $value = $rhs.value.contains($lhs.value); };
rlike_expression returns [boolean value]
: lhs=operand RLIKE rhs=QUOTED_LITERAL { $value = Pattern.compile($rhs.text.substring(1, $rhs.text.length() - 1)).matcher($lhs.value).matches(); }
;
logical_expression returns [boolean value]
: c=comparison_expression { $value = $c.value; }
| i=in_expression { $value = $i.value; }
| l=rlike_expression { $value = $l.value; }
;
chained_expression returns [boolean value]
: e=logical_expression { $value = $e.value; } (OR c=chained_expression { $value |= $c.value; })?
| e=logical_expression { $value = $e.value; } (AND c=chained_expression { $value &= $c.value; })?
;
grouped_expression returns [boolean value]
: LCPAREN c=chained_expression { $value = $c.value; } RCPAREN ;
expression returns [boolean value]
: c=chained_expression { $value = $c.value; } (OR e=expression { $value |= $e.value; })?
| c=chained_expression { $value = $c.value; } (AND e=expression { $value &= $e.value; })?
| g=grouped_expression { $value = $g.value; } (OR e=expression { $value |= $e.value; })?
| g=grouped_expression { $value = $g.value; } (AND e=expression { $value &= $e.value; })?
;
criteria returns [boolean value]
: e=expression { $value = $e.value; }
;
/* Lexical rules */
AND : ''and'' ;
OR : ''or'' ;
TRUE : ''true'' ;
FALSE : ''false'' ;
EQ : ''='' ;
NEQ : ''<>'' ;
GT : ''>'' ;
GE : ''>='' ;
LT : ''<'' ;
LE : ''<='' ;
IN : ''in'' ;
RLIKE : ''rlike'' ;
LCPAREN : ''('' ;
RCPAREN : '')'' ;
LBPAREN : ''['' ;
RBPAREN : '']'' ;
DECIMAL : ''-''?[0-9]+(''.''[0-9]+)? ;
IDENTIFIER : [a-zA-Z_][a-zA-Z_.0-9]* ;
QUOTED_LITERAL :
( ''/'''
( (''//' ''//') | (''/''' ''/''') | (''//' ''/''') | ~(''/''') )*
''/''' )
;
WS : [ /r/t/u000C/n]+ -> skip ;
public class CriteriaEvaluator extends CriteriaBaseListener
{
static class CriteriaEvaluatorErrorListener extends BaseErrorListener
{
Optional<String> error = Optional.empty();
@Override
public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) {
error = Optional.of(String.format("Failed to parse at line %d:%d due to %s", line, charPositionInLine + 1, msg));
}
}
public static boolean evaluate(String input, Object argument)
{
CriteriaLexer lexer = new CriteriaLexer(new ANTLRInputStream(input));
CriteriaParser parser = new CriteriaParser(new CommonTokenStream(lexer), argument);
parser.removeErrorListeners();
CriteriaEvaluatorErrorListener errorListener = new CriteriaEvaluatorErrorListener();
lexer.removeErrorListeners();
lexer.addErrorListener(errorListener);
parser.removeErrorListeners();
parser.addErrorListener(errorListener);
CriteriaParser.CriteriaContext criteriaCtx = parser.criteria();
if(errorListener.error.isPresent())
{
throw new IllegalArgumentException(errorListener.error.get());
}
else
{
return criteriaCtx.value;
}
}
}