I'm working on a tool in the context of a java project to evaluate a custom domain specific, rule-like expression like
min-5 avg datalist > Number
with the individual tokens meaning the following:
min-5 : optional minimum (or maximum, in that case max-5) occurences of the following term
avg : an optional aggregation function which operates on the following token datalist (can also be sum or anything similar)
datalist : A list of data (type: integer/ double) which will be available before the evaluation of the entire expression starts, can be reduced to a single value by the preceding aggregation function
operator: conditional operator < or > or =
Number: value for the conditional operator
Note(s):
The optional amount of occurrences and the aggregation can not happen both, that would make no sense.
There can be multiple of the above expressions, chained with and/or
These expressions are external input, not pre-defined
The evaluation of this expression should output a boolean
As I am rather new to expression evaluation / parsing I am searching for an elegant way to solve this, possibly with a java framework/tool.
What I've tried so far:
Parsing by hand which turned out not so nicely
Trying to use Janino Expression Evaluator, but I don't know how to handle this programmatically
I am searching for a solution to solve this in an elegant way, I am thankful for any suggestions
what you try to do is a DSL (domain specific language) and the elegant way to solve your issue is to create a grammar for yuor specific language that help you on parsing function.
Take a look at JavaCC or Antlr.
I'm looking for a flexible/generic way to build up conditions using metadata stored in a Database and then validate incoming requests at runtime
against the conditions and concatenate value(s) if the condition is met.
My use case looks something like this:
1) A business user selects an operation from a UI i.e. (IF condition from a dropdown), then selects an appropraite field to evaluate i.e. ("language")
then selects a value for the condition i.e. "Java" followed by some values to concatenate i.e "Java 9" and "is coming soon!"
2) This metaData will get stored in a Database (lets say as a List for the moment) i.e ["language","Java","Java 9","is coming soon"]
When my application starts I want to build the appropriate concatenation conditions:
private String concatenateString(String condition, String conditionValue, String concatValue1, String concatValue2){
StringBuilder sb = new StringBuilder();
if (condition.equals(conditionValue)){
sb.append(concatValue1);
sb.append(concatValue2);
}
return sb.toString();
}
3) so at runtime when I receieve a request, i want to compare the values on my incoming request to the various conditions that got built at start up:
if language == "Java" then the output would look like => "Java 9 is coming soon"
While the above might work for 2 String concatenations, how can achieve the same for a variable number of conditions and concatenation values.
So you want user to create a program by selecting options from a GUI which will be stored in a DB. When the options are read back from the DB you want to parse this into a compileable program and run it?
Use StringBuilder to build a string of the code from the data gotten back from the DB, something like this:
"if (language == '"Java"') { doSomething() }" (you'll need to take care to escape strings inside your string if you are storing strings in the DB.
You can then use Compiler class to compile the string to a program which yo can run (all in runtime, google dynamically compiling c# at runtime).
However, you'll probably want to question why you are thinking of going down that route... I've been there before, dynamic compilation has a very narrow use case.
You could, for instance, create a Dictionary which maps selected languages to some output string and simply use this to get your output like:
Dictionary<string, string> langaugeOutputMap = new Dictionary<string, string>();
languageOutputMap.Put("Java", "Java9 is coming soon");
private string concatString(string: userChosenString) {
if (languageOutputMap.containsKey(userChosenString) {
return languageOutputMap.getValue(userChosenString);
}
return string.Empty()
}
If you then want to manage multiple conditions, you could have multiple Dictionaries for each condition type and enumerate them in a collection, iterate over them when given a variable sized set of conditions and make sure that all the conditions evaluate through the use of containsKey().
Also, you can use params to specify variable length function arguments like so:
public string manyArgs(params string[] stringArgs) {
}
Also, look at PredicateBuilder:
http://www.albahari.com/nutshell/predicatebuilder.aspx
So I am trying to create a CalcParser class that, when given a mathematical expression, generates a string containing commands to evaluate the expression on a stack machine. For example, 1+2*3 should output:
push 1.0
push 2.0
push 3.0
multiply
add
Also, parentheses must be observed. So (1+2)*3 should output:
push 1.0
push 2.0
add
push 3.0
multiply
I am working with a pre-existing CalcLexer class that holds the string being parsed and converts it into a series of tokens using java.util.StringTokenizer. The only piece of information necessary from this class is the operators \t\n\r+-*/() and whitespace are also considered individual tokens.
Here is my question. If I were using parse trees to generate the output code, how would I take into account the parentheses since an expression inside parentheses will always be done first? Here is what I have so far for the parseRootexp() method, and I have a feeling I'm a bit off:
private double value;
private CalcLexer lexer = new CalcLexer();
private void parseRootexp(){
if (lexer.nextToken() == '('){
match('(');
do{
lexer.nextToken();
if (lexer.nextToken() == CalcLexer.NUMBER_TOKEN){
value = lexer.getNum();
System.out.println("push " + value + "\n");
}
} while (lexer.nextToken() != ')');
}
}
The match(int token) method is used simply to match the current token with an allowable terminal symbol. If not, an error is returned. I know this is far from what I want ideally, but what I need is something to push me in the right direction. Thanks in advance.
System.out.println("neon.mems.cmu.edu/people/".split("/").length); // output is 2
I was doing some url processing. To my surprise I just got the result above. I thought the number of elements could be the number of splitters plus one.
I didn't realize the last empty string(or just null) is cut off from the splitted array until now. I wonder if this is the case with every programming language.
No that's not the case for every programming language and there is no universal specification so there is no reason it should be.
Go
a := strings.Split("neon.mems.cmu.edu/people/", "/")
fmt.Println(len(a)) // prints 3
Javascript
Type this in the console of your browser :
"neon.mems.cmu.edu/people/".split('/')
The result is
["neon.mems.cmu.edu", "people", ""]
What you should do when a match is empty isn't something obvious or inherent to the split concept. A proof of that is that old Internet Explorer versions did remove those empty matches.
why it is discarded empty string?
String#split(regx) internally call String#split(regx,0) which execute Pattern.compile(regex).split(this, limit); actually - code snippet
if (limit == 0)
while (resultSize > 0 && matchList.get(resultSize-1).equals(""))
resultSize--;
where empty string has been discarded from resultSize if limit is 0.
How to get desired result?
String#split(regx,limit) use get desired result.
System.out.println("neon.mems.cmu.edu/people/".split("/",3).length);
Result :
3
And about language specification I am agree with #dystroy
Is there any built in support for array in XQuery? For example, if we want to implement
the simple java program in xquery how we would do it:
(I am not asking to translate the entire program into xquery, but just asking
how to implement the array in line number 2 of the below code to xquery? I am
using marklogic / xdmp functions also).
java.lang.String test = new String("Hello XQuery");
char[] characters = test.toCharArray();
for(int i = 0; i<characters.length; i++) {
if(character[i] == (char)13) {
character[i] = (char) 0x00;
}
}
Legend:
hex 0x00 dec 0 : null
hex 0x0d dec 13: carriage return
hex 0x0a dec 10: line feed
hex 0x20 dec 22: dquote
The problem with converting your sample code to XQuery is not the absence of support for arrays, but the fact that x00 is not a valid character in XML. If it weren't for this problem, you could express your query with the simple function call:
translate($input, '', '')
Now, you could argue that's cheating, it just happens so that there's a function that does exactly what you are trying to do by hand. But if this function didn't exist, you could program it in XQuery: there are sufficient primitives available for strings to allow you to manipulate them any way you want. If you need to (and it's rarely necessary) you can convert a string to a sequence of integers using the function string-to-codepoints(), and then take advantage of all the XQuery facilities for manipulating sequences.
The lesson is, when you use a declarative language like XQuery or XSLT, don't try to use the same low-level programming techniques you were forced to use in more primitive languages. There's usually a much more direct way of expressing the problem.
XQuery has built-in support for sequences. The function tokenize() (as suggested by #harish.ray) returns a sequence. You can also construct one yourself using braces and commas:
let $mysequence = (1, 2, 3, 4)
Sequences are ordered lists, so you can rely on that. That is slightly different from a node-set returned from an XPath, those usually are document-ordered.
On a side mark: actually, everything in XQuery is either a node-set or a sequence. Even if a function is declared to return one string or int, you can treat that returned value as if it is a sequence of one item. No explicit casting is necessary, for which there are no constructs in XQuery anyhow. Functions like fn:exists() and fn:empty() always work.
HTH!
Just for fun, here's how I would do this in XQuery if fn:translate did not exist. I think Michael Kay's suggestion would end up looking similar.
let $test := "Hello XQuery"
return codepoints-to-string(
for $c in string-to-codepoints($test)
return if ($c eq 32) then 44 else $c)
Note that I changed the transformation because of the problem he pointed: 0 is not a legal codepoint. So instead I translated spaces to commas.
With MarkLogic, another option is to use http://docs.marklogic.com/json:array and its associated functions. The json:set-item-at function would allow coding in a vaguely imperative style. Coding both variations might be a good learning exercise.
There are two ways to do this.
Firstly you can create an XmlResults object using
XmlManager.createResults(), and use XmlResults.add() to add your
strings to this. You can then use the XmlResults object to set the
value of a variable in XmlQueryContext, which can be used in your
query.
Example:
XmlResults values = XMLManager.createResults();
values.add(new XmlValue("value1"));
values.add(new XmlValue("value2"));
XmlQueryContext.setVariableValue("files", values);
The alternative is to split the string in XQuery. You
can do this using the tokenize() function, which works using a
regular expression to match the string separator.
http://www.w3.org/TR/xpath-functions/#func-tokenize
Thanks.
A little outlook: XQuery 3.1 will provide native support for arrays. See http://www.w3.org/TR/xquery-31/ for more details.
You can construct an array like this:
$myArray = tokenize('a b c d e f g', '\s')
// $myArray[3] -> c
Please note that the first index of this pseudo-array is 1 not 0!
Since the question "How to use or implement arrays in XQuery?" is being held generic (and thus shows up in search results on this topic), I would like to add a generic answer for future reference (making it a Community Wiki, so others may expand):
As Christian GrĂ¼n has already hinted at, with XQuery 3.1 XQuery got a native array datatype, which is a subtype of the function datatype.
Since an array is a 'ordered list of values' and an XPath/XQuery sequence is as well, the first question, which may arise, is: "What's the difference?" The answer is simple: a sequence can not contain another sequence. All sequences are automatically flattened. Not so an array, which can be an array of arrays. Just like sequences, arrays in XQuery can also have any mix of any other datatype.
The native XQuery array datatype can be expressed in either of two ways: As [] or via array {}. The difference being, that, when using the former constructor, a comma is being considered a 'hard' comma, meaning that the following array consists of two members:
[ ("apples", "oranges"), "plums" ]
while the following will consist of three members:
array { ("apples", "oranges"), "plums" }
which means, that the array expression within curly braces is resolved to a flat sequence first, and then memberized into an array.
Since Array is a subtype of function, an array can be thought of as an anonymous function, that takes a single parameter, the numeric index. To get the third member of an array, named $foo, we thus can write:
$foo(3)
If an array contains another array as a member you can chain the function calls together, as in:
$foo(3)(5)
Along with the array datatype, special operators have been added, which make it easy to look up the values of an array. One such operator (also used by the new Map datatype) is the question mark followed by an integer (or an expression that evaluates to zero or more integers).
$foo?(3)
would, again, return the third member within the array, while
$foo?(3, 6)
would return the members 3 and 6.
The parenthesis can be left out, when working with literal integers. However, the parens are needed, to form the lookup index from a dynamic expression, like in:
$foo?(3 to 6)
here, the expression in the parens gets evaluated to a sequence of integers and thus the expression would return a sequence of all members from index position 3 to index position 6.
The asterisk * is used as wildcard operator. The expression
$foo?*
will return a sequence of all items in the array. Again, chaining is possible:
$foo?3?5
matches the previos example of $foo(3)(5).
More in-depth information can be found in the official spec: XML Path Language (XPath) 3.1 / 3.11.2 Arrays
Also, a new set of functions, specific to arrays, has been implemented. These functions resinde in the namespace http://www.w3.org/2005/xpath-functions/array, which, conventionally, is being prefixed with array and can be found referenced in here: XPath and XQuery Functions and Operators 3.1 / 17.3 Functions that Operate on Arrays