Working with external objects through .g4 file in Antlr - java

I'm working on an implementation of a database in Java, and need to create an Antlr parser to process query strings like this one:
(category(Chinese) || category(Italian)) && price <= 2
I have implemented a method Set<String> category(String regex) for my database, which returns a set of the names of the restaurants that match the given text, but I have no idea how to call this method from some specific rule in my parser.
I expect that I have to have a parameter in my .g4 file that takes in my Java database object, but using #parser::members{} seems like it's only useful for initialising objects.
Any help would be appreciated!
This is my first post on stackoverflow, so sorry if it's not clear enough

If you want to give the Set to the parser so that it can then be accessed inside it you can do the following:
#parser::members {private Set<String> mySet;
public MyParser(CharStream input, Set<String> theSet) {
this(input);
mySet = theSet;
}
}
You can then access the respective mySet inside any parser-action during parsing. However you need to think about using the correct constructor.
You may also need to put the respective import statements in #parser::header.

Related

How to implement for each with an iterator that accepts an argument?

I'm implementing a custom JarFile class. One of the features is that it can limit the iteration of its contents to a subset by giving the starting path.
Simplified class example:
public class JarFile Iterable<JarEntry>
{
// Wraps java.util.jar.JarFile internally
java.util.jar.JarFile jarFile;
..
#Override
public Iterator<JarEntry> iterator() {
return new JarIterator(jarFile);
}
public Iterator<JarEntry> iterator(String startPath) {
return new JarIterator(jarFile, startPath);
}
}
For example, a jar file contains some paths:
a/file1.class
b/file2.class
c/file3.class
Iterating over the jar file normally will provide all of the entries contained therein. But my JarIterator also provides a constructor with a path argument which limits iteration to only a subset and its children.
For example, this will output the same list as above:
JarFile jf = new JarFile("Test.jar")
for (JarEntry je : jf)
System.out.println(je.getName());
I can access the alternate iterator like this:
JarIterator it = jf.iterator("b/");
while (it.hasNext()) {
JarEntry je = it.next();
System.out.println(je.getName());
}
The output is instead this subset entry:
b/file2.class
Everything already works as it is but I would like to take advantage of Java's for keyword but this only works with the default no-args constructor.
Is there a way to make it work with my custom iterator without having to call hasNext() and next()?
NB: I think this is bad API design - see below.
There is no way to haev a nice API for the users of this library with a method named iterator. You have a few options, but the most obvious one:
Don't return an iterator, return an iterable.
This meshes with my advice below that your method is a bad design in any case, but if you must have it, that it has a bad name. Once you address the name, you can return iterables instead:
for (JarEntry je : jf.filterByPrefix("b/")) { ... }
The filterByPrefix method would return an Iterable<JarEntry>; this needs to be an object that has an iterator() (no-args!) method that returns an iterator that iterates only over the stated entries. It'd be trivial; imagine you had that iterator(String prefix) method:
public Iterable<JarFile> filterByPrefix(String prefix) {
return () -> iterator(prefix);
}
Some introspection on why this doesn't sound like good API design:
jf.iterator("b/");
Not sure this is great design; once you go down this path, where is the iterator method that lets me iterate only files whose uncompressed size is no higher than the stated value? While we're at it, where is the one that lets me only iterate files whose filename has a prime length? A ridiculous question, but I'm trying to get you to realize that pre-defining the filter conditions is suboptimal: You have no idea what people might want to filter on, so now you get into a style debate with your library's users: If there is a need to filter, and you provide this filtering via this iterator method, should one always use that? But that's inconsistent. Now the code to iterate over a jar file, filtering out all entries that are larger than 1mb, looks completely different from code that filters out stuff that doesn't start with "foo". Having two ways to accomplish the same thing is usually bad news. Better not to do that.
If there is a pressing performance argument, this makes sense, but there isn't. Your impl can't skip stuff that doesn't start with "b/" any faster than:
if (!je.getName().startsWith("b/")) continue;
can, and that is just one line of java code that you're trying to save.
If you must, at least call it iteratorByPrefix or something. How is one supposed to guess that iterator("b/") iterates over all entries that start with "b/"? Why isn't that 'iterate all entries that contain the text b/', or perhaps even all entries whose text content starts with b/, or that end in b/, or have an associated zip tag with b/?
If you have a pressing performance need or just cannot stand that one-liner filter if, then instead of filtering by prefix, why not filter by function?
for (JarEntry je : jf.onlyByName(n -> n.startsWith("b/")) {}
while you're at it, perhaps also make an only method that passes in not the name but the entire JarEntry object. The parameter's type would be Predicate<String> or Predicate<JarEntry>.

Switch statement or remotely invoke methods

I have a switch statement that compares a String with set of String where each match calls a different method.
switch(((Operation) expr.getData()).getValue()){
case "+":
return add(expr.getNext());
case "car":
return car(expr.getNext());
case "cdr":
return cdr(expr.getNext());
case "cons":
return cons(expr.getNext(), expr.getNext().getNext());
case "quote":
return quote(expr.getNext());
case "define":
handleDefine(expr.getNext());
break;
default:
return null;
}
However, to me this sounds like something that could be achieved far more elegantly and efficiently using a HashMap that links up to an Operation that contains a Method and the number of parameters so I could each method to a HashMap like:
nameToOperation.put("+", new Operation("+", 1, Driver.class.getMethod("add")));
nameToOperation.put("car", new Operation("car", 1, Driver.class.getMethod("car")));
So there would be N different instances of the Operation class each containing the String, Method and number of parameters
And then I could simply call the method using something similar to this (I understand this isn't how you use invoke):
Operation op = ((Operation) expr.getData())
if(op.getNumPars() == 1)
return(op.getMethod().invoke(expr.getNext()));
else
return(op.getMethod().invoke(expr.getNext(), expr.getNext().getNext()));
However, I still don't fully like this solution as I am losing type safety and it still doesn't look that great. Another example I have seen on stackoverflow that looked quite elegant but I don't fully understand is the first solution of the top answer on: How to call a method stored in a HashMap? (Java)
What does everyone on Stackoverflow think the best solution is?
Edit: Just in case anybody searches this and was wondering about my solution, I made each operation such as Add, Car, Cdr have their own class that implemented Command. I then had to make the majority of my methods static, which I suppose by nature each of them were anyway. This seems way more elegant than the original case statement.
basicaly , the answer recommends to go with Command pattern.
"The main advantage of the command design pattern is that it decouples the object that invokes the operation from the one that know how to perform it. And this advantage must be kept. There are implementations of this design pattern in which the invoker is aware of the concrete commands classes. This is wrong making the implementation more tightly coupled. The invoker should be aware only about the abstract command class"
Basicaly your map would be type safety. by declaring
Map <character,Command>
Open to Extendibility
It looks like you are trying to write a Scheme interpreter. In that case you're gonna need a map anyway since you need to store all the user defined values und functions.
When the user writes e.g. (define (add a b) (+ a b)), you store the function in the map using "add" as key.
But your functions should use lists as inputs, i.e. each function has exactly one argument which is a list. In Scheme all expressions are lists by the way. Usually a Scheme interpreter consists of a reader and an evaluator. The reader converts the code into a bunch of nested lists.
So basically "(define (add a b) (+ a b))" could be converted into a list structure similar to this.
List<Object> list = new ArrayList<Object>();
List<Object> list2 = new ArrayList<Object>();
list2.add("add"); list2.add("a"); list2.add("b");
List<Object> list3 = new ArrayList<Object>();
list3.add("+"); list3.add("a"); list3.add("b");
list.add("define"); list.add(list1); list.add(list2);
Of course your code doesn't actually look like this, instead the lists are constructed by recursive methods parsing the input code.
Those lists don't just contain strings btw., they also contain numbers and boolean values. Nested lists like this are the most simple form of an abstract syntax tree (AST). Since the syntax of Scheme is much simpler than that of most other languages, a very simple list structure is enough to store the parsed code.
The evaluator then processes those lists.
To evaluate a list you first recursively evaluate every element in the list and then apply the first element to the rest of the list. That first element must therefore be a user defined function or a build in command e.g. "define".

How to rename or create a new String dynamically?

I have a bunch of strings with weird names (like this docFileeh934fhry) that contain text like this
System.out.println(docFileeh934fhry);
`.............document: 12345.....
...................
...............`
I want to rename these strings. So that the above string will be String doc12345
How can I do this?
I know how to get this number using Pattern.compile
Let's say I have this number String docNumber = "12345";
Now how can I dynamically create a new string?
I tried
String doc+docNumber = docFileeh934fhry; // no result
You might be able to do this with reflection, but it has to be said that this is a very poor approach.
Your naming of variables should not depend on any external input. Instead, give it a name that describes its contents and go from there.
What do you gain by naming it doc1?
If you are looping trough files all you probably want is a currentDocument. Any other variables (firstDocument, nextDocument, oldDocument, etc) you name according to their function, not their contents.
If you want a way to uniquely identify the correct document, create a class instead
class Document {
int id;
string contents;
}
After storing all these Document objects into a collection (like an ArrayList), you can just retrieve the document you need on basis of that id rather than having to mess around with a bunch of generated variables.
Another point to note: how would you even use this when you have 50 documents? 100? 10.000? This would be impossible to maintain.
If you don't want to create a custom class you can go with the HashMap<Integer, String> route.
This isn't how Java works. If you want to associate an object with a String, then use a Map such as a HashMap<String, String>.
Assign the string to the variable w/ the new name. Note that the new name will have to be have been defined when writing the code, it can't be computed on the fly (if that is what you are driving at).
You can't use + in a variable name. As above, you can't make variable names on the fly.
Here maybe one possible solution,use Hashmap,key is your varname(the way as you wish) and value is your string.

Making values in to objects using Regex (Java)

I have some data that looks like this
myobject{keyone:"valueone",keytwo:"valuetwo",keythree:"valuethree"}
myobject{keyone:"valueone",keytwo:"valuetwo",keythree:"valuethree"}
myobject{keyone:"valueone",keytwo:"valuetwo",keythree:"valuethree"}
And I'm wondering what the best way to create a bunch of objects from it would be. I've written the following regex to extract all the values from a particular Key...
Pattern p_keyone = Pattern.compile("keyone:\"(.+?)\"\\,");
Matcher match_keyone = p_keyone.matcher(string);
while(match_keyone.find()) {
myobjects.add(new MyObject(match_keyone.group(1));
}
Which gives me a bunch of objects with a single argument...
myobjects.add(<valueone>);
Is there a way I can execute a single regex query and create a bunch of objects with all the values as arguments in one go. Like this...
new MyObject( <valueone>, <valuetwo> , <valuethree> );
Thanks
Your approach is not bad.
Few things you could change, though it depends on your requirements whether they make sense:
Create a "Factory" class which takes 1 line of data and creates the object.
Read the data line by line, for each line use the Factory to create it.
Depending on how fancy (and error-prone) you want it to get, you could even read the names of the objects and properties and then use reflection to create instances and set the properties.
String.split() could help:
String line = "myobject{keyone:\"valueone\",keytwo:\"valuetwo\",keythree:\"valuethree\"}"
// ^-----[0]------^ ^--[1]-^ ^--[2]-^ ^--[3]-^ ^--[4]---^ ^--[5]---^ ^[6]
String[] parts = line.split("\"");
MyObject myObject = new MyObject(parts[1], parts[3], parts[5]);

Can I query a List?

Say I have
List<SomeObject> objList = new ArrayList<SomeObject>();
If SomeObject contains a field named id. Can we find it through some query like
objList.filter('id=2');
without looping through the list? If not, then why? This can be such a useful method and can be used as an alternative to write a tedious for loop.
Libraries with functional, well, functionality such as functionaljava provide such
methods.
You'd need to use its own List implementation which is incompatible to native Javas (Array)List or convert between the two.
An Example:
import fj.data.Java
import fj.data.List
import fj.F
// Converting an ArrayList
fj.data.List<SomeObject> objList2 = Java.ArrayList_List().f(objList);
fj.data.List<SomeObject> filteredObjList = objList2.filter(new F<SomeObject, Boolean>() {
Boolean f(SomeObject c) { return c.id == 2; }
});
// Converting back to ArrayList
java.util.List<SomeObject> objList2 = Java.List_ArrayList().f(filteredObjList );
By using functionaljava's List through out of your project you would avoid the converting.
Even if this was a supported feature of the language, then it would be Java that would need to do the iterating, so you're back at square one (in terms on time complexity).
What you're looking for is associative mapping. This can be achieved with HashMap. If you want to associate by more than one type of property for example id AND name, then you could make two HashMaps, one whose key is id and one whose key is name.
This of course doesn't scale very well if you want to query for many properties, so the next step would be using an Object oriented database such as Hibernate which will allow you to query the database for objects exactly as in your example.
short: no, you can't
long: you can write own data structure and hash/index fields of object for some more efficient search. but this is not a list, more HashMap or so.
Probably this will help someone,
http://code.google.com/p/joquery/
this library supports following code structure,
Filter<Dto> query = CQ.<Dto>filter(testList)
.where()
.property("id").eq().value(1);
Collection<Dto> filtered = query.list();
If you want something like linq for java, check out quaere
Edit: At this point, quaere looks like it is unmaintained. With Java 7 being EOL, you should be on Java 8 whose Stream API should get you most of the way there when compared to Linq's dot syntax. See this page for some examples.
Here
How do you query object collections in Java (Criteria/SQL-like)?
you can see some nice option/ I've sopped on this one actually.

Categories