What's wrong with Groovy multi-line String? - string

Groovy scripts raises an error:
def a = "test"
+ "test"
+ "test"
Error:
No signature of method: java.lang.String.positive() is
applicable for argument types: () values: []
While this script works fine:
def a = new String(
"test"
+ "test"
+ "test"
)
Why?

As groovy doesn't have EOL marker (such as ;) it gets confused if you put the operator on the following line
This would work instead:
def a = "test" +
"test" +
"test"
as the Groovy parser knows to expect something on the following line
Groovy sees your original def as three separate statements. The first assigns test to a, the second two try to make "test" positive (and this is where it fails)
With the new String constructor method, the Groovy parser is still in the constructor (as the brace hasn't yet closed), so it can logically join the three lines together into a single statement
For true multi-line Strings, you can also use the triple quote:
def a = """test
test
test"""
Will create a String with test on three lines
Also, you can make it neater by:
def a = """test
|test
|test""".stripMargin()
the stripMargin method will trim the left (up to and including the | char) from each line

Similar to stripMargin(), you could also use stripIndent() like
def a = """\
test
test
test""".stripIndent()
Because of
The line with the least number of leading spaces determines the number to remove.
you need to also indent the first "test" and not put it directly after the inital """ (the \ ensures the multi-line string does not start with a newline).

You can tell Groovy that the statement should evaluate past the line ending by adding a pair of parentheses ( ... )
def a = ("test"
+ "test"
+ "test")
A second option is to use a backslash, \, at the end of each line:
def a = "test" \
+ "test" \
+ "test"
FWIW, this is identical to how Python multi-line statements work.

Related

Indent multiline string with Groovy onliner

I'd like to indent a multiline string in Groovy but I can't figure out the right RegEx syntax / or Regex flags to achieve that.
Here's what I tried so far:
def s="""This
is
multiline
"""
println s.replaceAll('/(.*)/'," \1")
println s.replaceAll('/^/'," ")
println s.replaceAll('(?m)/^/'," \1")
println s.replaceAll('(?m)/(.*)/'," \1")
These didn't work as expected for some reason.
The only thing that worked so for is this block:
def indented = ""
s.eachLine {
indented = indented + " " + it + "\n"
}
println indented
Is there a shorter / more efficient way to indent all lines of a string in Groovy?
You need to put the (?m) directive inside the regular expression; and the pattern is a slashy string, not a single quoted string with slashes inside:
s.replaceAll(/(?m)^/, " ")
You could split and join - don't know if it's more efficient, but shorter
def s="""This
is
multiline
"""
def indent = " "
println indent + s.split("\\n").join("\n" + indent);
Or perhaps using just the replace function from java which is non-regex and potentially faster:
def s="""\
This
is
multiline
"""
println ' ' + s.replace('\n', '\n ')
which prints:
This
is
multiline
note: for those who are picky enough, replace does use the java regex implementation (as in Pattern), but a LITERAL regex which means that it will ignore all normal regex escapes etc. So the above is probably still faster than split for large strings, but this makes you wish they had left some function in there that just did a replace without any involvement of the potentially slow Pattern implementation.

Groovy split on period and return only first value

I have the input as
var = primarynode.domain.local
and now I Need only primarynode from it.
I was looking both split and tokenize but not able to do it in one line code. does anyone know how to do it in one line code?
Well assuming that you want to just get the first word(before . )
from the input string.
You can use the tokenize operator of the String
If you have
def var = "primarynode.domain.local"
then you can do
def firstValue = ​var.tokenize(".")[0]​
println firstValue
output
primarynode
The split method works, you just have to be aware that the argument is a regular expression and not a plain String. And since "." means "any character" in a regular expression, you'll need to escape it...
var = 'primarynode.domain.local'.split(/\./)[0]
...or use a character class (the "." is not special inside a character class)
var = 'primarynode.domain.local'.split(/[.]/)[0]

Replacing a word with $ in groovy

I have the following piece of groovy code from a gradle build file I am using for replacing a word in a php file:
def str1="My application version is $app_version"
def str2 = str1.replaceAll('$app_version','2016072601')
println str2​
I want to replace $app_version with defined number in this method but somehow it is not working. Do I need to escape or do any special action to perform this replacement?
Strings with double quotes and $ are GStrings, which triggers Groovy's string interpolation. Also, replaceAll receives a regex as first argument, and $ is a special character in regex, thus, you need to double escape that too.
You can use single quotes OR you can escape the $ character in your str1:
def str1='My application version is $app_version'
def str2 = str1.replaceAll('\\$app_version','2016072601')
assert str2 == 'My application version is 2016072601'
Update: Expanding string interpolation a bit, it replaces the $ placeholder in your string with the variable value of the same name (though not immediately, as it creates an instance of GString first). It is roughly akin to this in Java:
String str1 = "My application version is " + app_version;
So, instead of replacing the variable with replaceAll, you could have a variable in your script with the name app_version, like def app_version = "2016072601" (which is kinda like #BZ.'s answer)
Update 2: By using string interpolation (as per #BZ.'s answer) or string concatenation, you don't need to use replaceAll, but you need a variable called app_version:
def app_version = '2016072601'
def str1 = "My application version is $app_version"
assert str1 == 'My application version is 2016072601'
And with string concatenation you also need a variable called app_version:
def app_version = '2016072601'
def str1 = "My application version is " + app_version
assert str1 == 'My application version is 2016072601'
Alternately:
def versionString = "2016072601"
def string2 = "My application version is $versionString"

parsing a file with specific format in ply (python)

i have a problem with ply, i have to receive a file with a tokens list and a grammar (bnf), i wrote a grammar to recognize the input, and it is almost working (just minor issues, we are solving them), for example this is a valid input file
#tokens = NUM PLUS TIMES
exp : exp PLUS exp | exp TIMES exp
exp : NUM
(we dont care, in this case, about ambiguous grammar or whatever, this is an example for input)
parsing every line separately works fine, but i want to parse the whole file with these rules:
#tokens must be only in first line, so if we have a #tokens declaration after grammar it is not valid
you can have 0 or more blank lines after every line of "code"
you can have as many grammar rules as you want
i tried using a loop to scan and parse every line separately, but i can't control the rirst (and really important) rule, so i tried this in my .py file:
i defined t_NLINEA (new line) i had also problem using the \n character as a literal and the file is open using rU mode to avoid conflicts about \r\n or \n characters, so i added these rules:
def p_S(p):
'''S : T N U'''
print("OK")
def p_N(p):
'''N : NLINEA N'''
pass
def p_N2(p):
'''N : '''
pass
def p_U(p):
'''U : R N U'''
pass
def p_U2(p):
'''U : '''
pass
(as i told you above, i had tu use the N rule because ply didnt accept the \n literal in my grammar, i added the \n to "literals" variable)
T is the rule to parse the #tokens declaration and R is used to parse grammar rules, T and R works ok if i use them in a single line string, but when i add the productions i wrote above i get a syntax error when parsing the fisrt gramar rule, for example A : B C i get syntax error with :
any suggestion?
thanks
Ply tries to figure out a "starting rule" based on your rules. With what you have written, it will make "exp" the start rule, which says there is only one expression per string or file. If you want multiple expressions, you probably want a list of expressions:
def p_exp_list(p):
"""exp_list :
| exp_list exp
"""
if len(p) == 1:
p[0] = []
else:
p[0] = p[1] + [p[2]]
Then your starting rule will be "exp_list". This would allow multiple expressions on each line. If you want to limit to one expression per line, then how about:
def p_line_list(p):
"""line_list :
| line_list line
"""
if len(p) == 1:
p[0] == []
else:
p[0] = p[1] + [p[2]]
def p_line(p):
"""line : exp NL"""
p[0] = p[1]
I don't think you can use newline as a literal, (because it might mess up regular expressions). You probably need a more specific token rule:
t_NL = r'[\r*\n]'
Pretty sure this would work, but haven't tried it as there isn't enough to go on.
As for the "#token" line, you could just skip it, if it doesn't appear anywhere else:
def t_COMMENT(t):
r'#.*$'
pass # ignore this token

Are Groovy String expressions anything more than syntactic sugar?

I saw this piece of code in the Groovy tutorial -
import groovy.sql.Sql
sql = Sql.newInstance( 'jdbc:jtds:sqlserver://serverName/dbName-CLASS;domain=domainName', 'username', 'password', 'net.sourceforge.jtds.jdbc.Driver' )
sql.eachRow( 'select * from tableName' ) { println "$it.id -- ${it.firstName} --" }
And it is the first occurrence of Groovy expressions (anything inside a ${} gets evaluated as an expression, not a string). My question is, does this Groovy expression feature actually provide some new functionality? It seems to me like there is nothing here that can be done with a good old string concat. IE -
println it.id + " -- " + it.firstName + " --"
Yes. That's what they are. Being able to add code and variables into strings is a feature of Groovy that make Groovy groovy. String in Groovy can be just like templates.
Now, Groovy strings are enclosed in (") quotes. But a standard Java String in Groovy is enclosed in an apostrophe ('). Standard Java strings in groovy cannot contain variable references or code.
It makes code easier to read. Sometimes looking at all the '+' signs in Java as part of string concatenation is a PITA.
What would you rather code and read:
println "$it.id -- ${it.firstName} --"
or
println it.id + " -- " + it.firstName + " --"
Now, with local variables it becomes much easier to read too:
Groovy
def name = "some name"
def age = 30
def waist = 42
println "Did $name really have a size $waist waist at age $age?"
Java:
String name = "some name";
int age = 30;
int waistSize = 42;
System.out.println("Did " + name + " really have a " + waist + " waist at age " age + "?";
Another thing you can do with them is use them as slightly Lazy Templates, ie:
def x = "abc"
// Closure expansion rather than variable
def s = "x: ${->x}"
// Prints 'x: abc'
println s
// Change x
x = 400
// Prints 'x: 400'
println s
With the pure String concatenation, you'd end up writing a print function, or repeating your concatenation code
Also, don't forget the multi-line string operators -- such as """ -- and the stripMargin method which allows you to :
def (name,age,sender) = [ 'Alice', 30, 'Tim' ]
println """Hello $name
|
|$age today? Congratulations!
|
|$sender""".stripMargin()
Which prints:
Hello Alice
30 today? Congratulations!
Tim
Again, could be done with String concatenation, but ends up being loads more typing and error prone (imo)
Lots more about Groovy strings (various flavours) here: http://groovy.codehaus.org/Strings+and+GString
I believe any string of the form "a string" (i.e. double quotes) is an instance of GString in Groovy (not String). It is GString that provides these extra capabilities.
With the accepted answer here we seem to have landed on the conclusion that, yes, GString (i.e. a double-quoted string with one or more ${<expr>} expressions) is just syntax sugar. (I'm going by the first sentence of the accepted answer: "Yes. That's what they are.")
But that seems to be wrong because of lazy evaluation, as noted above by tim_yates.
Expanding a bit on the above, if one or more expressions in the string are closures, they are only evaluated when toString() is called on the GString. So, in groovyconsole:
def stime = "The time is: ${-> new Date().getTime()}."
println stime
Thread.sleep(500)
println stime
The time is: 1384180043679.
The time is: 1384180044194.
How would you do this with + without creating a new string every time? (The answer, in two Java projects I've worked on, was to invent a Template.java class, to do this sort of thing.) This suggests to me that there is more than just syntax sugar. Or perhaps that it is syntax sugar - but for the GString class, not for java.lang.String or strings generally.
Another example:
def vals = ['a':42]
def tmpl = "The answer is ${-> vals.a}."
println tmpl
vals.a = 43
println tmpl
The answer is 42.
The answer is 43.
I'm not sure I actually like this feature of GString; I'm sort of used to strings being immutable (coming from Java). Groovy's strings are not (quite). It seems like you ought to be able to assume that if two Groovy 'strings' reference the same instance, they will have the same textual value, but that is not quite true.
http://groovy.codehaus.org/Strings+and+GString (about halfway down)

Resources