How to convert dynamically input user expression to java code? - javassist

I read the byte buddy and javassist doc and I would like do not know if is possible to convert a string like:
get foos where name == toto
to
data.getFoos().stream()
.filter( f -> f.name.equals( "toto" ) )
.collect( Collectors.toSet() )
A regex could capture the expression as:
final Pattern query = Pattern.compile("get (\\w+) where (\\w+) ([=!]+) (\\w+)");
final Scanner scanner = new Scanner(System.in);
final Matcher matcher = query.matcher(input);
matcher.group(1) // foos -> Foo and foos -> getFoos()
matcher.group(2) // field to use as filter
matcher.group(3) // symbol == / !=
matcher.group(4) // thing to match
convert get foos to getFoos()
check from Foo class if name field exists
if fields name is not an instance of Number.class translate == to .equals
make the expression
loop and print results
I read some examples without able to found a such thing. So I come here to get your light. thanks

Both Byte Buddy and Javassist generate byte code, not Java code. Javassist does however have functionality to translate String-contained source code to byte code from your inputs. However, the source code level is at Java 4 level such that you cannot use lambdas.
I do however wonder if this is the correct approach for your problem. Rather, I would suggest you to programmatically resolve a stream from the arguments. You could amplify this by creating a custom aPI to transform your arguments into the stream in question.

Related

Kotlin // Solve the example in a line

How to solve an example in a string?
Let's say we have val example : String = "3+5"
So how do I solve this example? Or if val example : String = "3*5/3"
Two ways to achieve it:
Keval - 3rd party dependency
You can either use Keval.eval("(3+4)(2/8 * 5) % PI") or as an extension function on String, "(3+4)(2/8 * 5) % PI".keval(). This will return the calculated value as Double. For your example, "3*5/3".keval().
To use it, add implementation("com.notkamui.libs:keval:0.8.0") in dependencies in app level build.gradle file and sync gradle. Then, use it in any file as mentioned in the above para, put the cursor on the line and press Alt + Enter (or hover for suggestions) to import the necessary imports.
Look into its Readme.md on the provided link for more usage and implementation details.
Custom String parsing using BODMAS rule
You can split the string using the BODMAS rule, parse the split array as int/double, if it throws exception, it means the substring is an expression too, again split it using BODMAS, parse and perform the calculation.

Java 8 cyclic inference warning

My knowledge about list operations is from scripting languages. So in Java I stopped on something strange in case of finding cookie with particular name.
List<Cookie> cookies = Arrays.asList(request.getCookies());
String auth = cookies.stream()
.filter(c -> c.getName().equals("auth"))
.map(Cookie::getValue);
On the map method IntelliJ is showing me "Cyclic inference".
Java compiler Error:(52, 25) java: incompatible types: no instance(s) of type variable(s) R exist so that java.util.stream.Stream conforms to java.lang.String
Your current code returns a Stream<String>, so you need an extra step to return a string:
Optional<String> auth = cookies.stream()
.filter(c -> c.getName().equals("auth"))
.map(Cookie::getValue)
.findAny();
Note that it returns an Optional<String> because there may be no Cookie that matches "auth". If you want to use a default if "auth" is not found you can use:
String auth = cookies.stream()
.filter(c -> c.getName().equals("auth"))
.map(Cookie::getValue)
.findAny().orElse("");
In essence, what this rather cryptic error message is saying is "the output of the stream sequence doesn't match what you are assigning it to in the end", ex:
String s = list.stream().map(s -> s); // this doesn't result in a String...
.findFirst().get()
So to debug compilation, remove the assignment (temporarily), or, as the other answer says, add something that makes it return a String by collecting (ex: .collect(Collectors.joining(","))) or (getting like .findFirst().get()), or change the assignment, like Stream<String> stream = list.stream().map(...)

Serialization of ANTLR ParseTree

I have a generated grammar that does two things:
Check the syntax of a domain specific language
Evaluate input against that domain specific language
These two functions are separate, lets call them validate() and evaluate().
The validate() function builds the tree from a String input while ensuring it meets the requirements of the BNF for the language. The evaluate() function plugs in values to that tree to get a result (usually true or false).
What the code is currently doing is running validate() each time on the input, just to generate the tree that evaluate() uses. Some of the inputs take up to 60 seconds to be checked. What I would LIKE to do is serialize the results of validate() (assuming it meets the syntax requirements), store the serialized form in the backend database, and just load it from the database as part of evaluate().
I noticed that I can execute the method toStringTree() on the parse tree, and retrieve a LISP style tree. However, can I restore a LISP style tree to an ANTLR parse tree? If not, can anyone recommend another way to serialize and store the generated parse tree?
Thanks for any help.
Jason
ANTLR 4's ParseRuleContext data structure (the specific implementation of ParseTree used by generated parsers to represent grammar rules in the parse tree) is not serializable by default. Open issue #233 on the project issue tracker covers the feature request. However, based on my experience with many applications using ANTLR for parsing, I'm not convinced serializing the parse trees would be useful in the long run. For each problem serializing the parse tree is meant to address, a better solution already exists.
Another option is to store a hash of the last known valid file in the database. After you use the parser to create a parse tree, you could skip the validation step if the input file has the same hash as the last time it was validated. This leverages two aspects of ANTLR 4:
For the same input file, running the parser twice will produce the same parse tree.
The ANTLR 4 parser is extremely fast in almost all cases (e.g. the Java grammar can process around 20MB of source per second). The remaining cases tend to be caused by poorly structured grammar rules that the new parser interpreter feature in ANTLRWorks 2.2 can analyze and make suggestions for improvement.
If you need performance beyond what you get with this, then a parse tree isn't the data structure you should be using. StringTemplate 4's enormous performance advantage over StringTemplate 3 came primarily from the fact that the interpreter switched from using ASTs (equivalent to parse trees for this reasoning) to a linear bytecode representation/interpreter. The ASTs for ST4 would never need to be serialized for performance reasons because the bytecode would be serialized instead. In fact, the C# port of StringTemplate 4 provides exactly this feature.
If the input data to your grammar is made of several independent blocks, you could try to store the string of each block separately, and run the parsing process again for each block independently, using a ThreadPool for example.
Say for example your input data is a set of method declarations:
int add(int a, int b) {
return a+b;
}
int mul(int a, int b) {
return a*b;
}
...
and the grammar is something like:
methodList : methodDeclaration methodList
|
;
methodDeclaration : // your method declaration rules...
The first run of the parser just collects each method text and store it. The parser starts the process at the methodList rule.
void visitMethodList(MethodListContext ctx) {
if(ctx.methodDeclaration() != null) {
String methodStr = formatParseTree(ctx.methodDeclaration(), " ");
// store methodStr for later parsing
}
// visit next method list item, if any
if(ctx.methodList() != null) {
visit(ctx.methodList());
}
}
The second run launch the parsing of each method declaration (in a separate thread for example). For this, the parser starts at the methodDeclaration rule.
void visitMethodDeclaration(MethodDeclarationContext ctx) {
// parse the method block
}
The reason why the text of a methodDeclaration rule is formatted if because calling directly ctx.methodDeclaration().getText() would combine the text of all child nodes AntLR doc, possibly making it unusable for parsing again. If white space is a token separator in the grammar, then adding one space between tokens should not change the parse tree.
String formatParseTree(ParseTree tree, String separator) {
StringBuilder builder = new StringBuilder();
for(int i = 0; i < tree.getChildCount(); i ++) {
ParseTree child = tree.getChild(i);
if(child instanceof TerminalNode) {
builder.append(child.getText());
builder.append(separator);
} else if(child instanceof RuleContext) {
builder.append(formatParseTree(child, separator));
}
}
return builder.toString();
}

Stringtemplate template arguments always evaluated to a string

I'm using Antlr 4 RC1 (the complete jar) to parse a grammar, build a custom ast, and generate code from that AST with stringtemplate4 (I use the stringtemplate classes in the antlr jar).
Inside a template I call another template with a list of beans e.g.
<subtemplate(myListArg=parm.listOfBeans)>
then inside subtemplate I get a list of Strings (each string is one of the beans evaluated to a String). But what I need is the list of java beans (e.g. simple java object with properties) because I want to process the properties of the beans and not the value of the beans, e.g.
<subtemplate(myListArg)> ::= <<
<myListArg: {x | {... <x.someProperty> ...}>
>>
Looks to me like parameters of a template are always evaluated to strings. Is that the intended behaviour? If yes what should else should I write?
StringTemplate 4 does not render the members of an array or List to strings while calling a subtemplate, as verified by the following. Edit: Despite claims to the contrary in the comments on this post, I reiterate that ST4 does not render the members of an array or List to strings while calling a subtemplate. The element types stored in the array or list make no difference.
start(class) ::= <<
<fields(class.fields)>
>>
fields(fieldsArray) ::= <<
<fieldsArray:{field | <field.name>}; separator="\n">
>>
If you create an instance of the start template and pass Integer.class for the class parameter, you get the following output:
MIN_VALUE
MAX_VALUE
TYPE
SIZE
One of the following must be occurring in your application:
myListArg is not actually a list of strings (i.e. you are getting unexpected output, but not for the reason listed here)
You have a ModelAdaptor registered for the type of parm which is returning a list of strings for the property listOfBeans
The type of parm has a getListOfBeans method which returns a list of strings
Item (3) does not hold, and the type of parm has a listOfBeans field which is a list of strings
Edit: Regarding the question of lists vs. arrays, I performed the unit test above with passing new Clazz(Integer.class) instead of just Integer.class to the start template:
private static class Clazz {
private final Class<?> clazz;
public Clazz(Class<?> clazz) {
this.clazz = clazz;
}
public List<Field> getFields() {
return Arrays.asList(clazz.getFields());
}
}

How to convert string into integer in the Velocity template?

I have a Velocity template file which has the data from XML. I want to convert the string into integer type.
How can I do that?
Aha! Been there.
#set($intString = "9")
#set($Integer = 0)
$Integer.parseInt($intString)
Doing this uses the java underlying velocity. The $Integer variable is nothing more that a java Integer object which you can then use to access .parseInt
Edit: The above code is for demonstration. Of course there are ways to optimize it.
If you have some control over the velocity context, here's an alternative that alleviates the need to set a variable in the Velocity template.
Context velocityContext = new Context();
velocityContext.put(Integer.class.getSimpleName(), Integer.class);
This allows you to call the static methods of java.lang.Integer in your template using $Integer.parseInt($value) and doesn't rely upon the #set having been called prior to performing the type conversion in the template.
The problem with parseInt is that it throws an exception in case the string is not parseable.
In case you have the NumberTool loaded into your context a better solution than parseInt is the following:
#set($intString = "009")
#set($Integer=$numberTool.toNumber($intString).intValue())
#if($Integer)
## ok
#else
## nok
#end
Sometimes the NumberTool is also loaded as $number.
However, a little drawback is, that the NumberTool simply parses the first number it finds and ignores the rest, so "123a" => 123.
Nice and easy:
#set( $stringToCast = "0" )
$number.toNumber($stringToCast)
$number is the default key name for the NumberTool, but it can be override by specifying a different name in the configuration (for example $numberTool). You have to check what name for NumberTool is used in your Velocity environment.
toNumber method returns:
the object as a Number or null if no conversion is possible
If you want to have explicite an int variable, not a Number object, you can use the intValue method on the result. So the above code will looks like this:
#set( $stringToCast = "0" )
$number.toNumber($stringToCast).intValue()
Of course, you can assign the result to another variable (for example $intVal).
So the full code can look like this:
#set( $stringToCast = "0" )
#set( $intVal = $number.toNumber($stringToCast).intValue() )