Why do I get this?
p {a:3}
# => syntax error, unexpected tINTEGER, expecting tSTRING_CONTENT or tSTRING_DBEG or tSTRING_DVAR or tSTRING_END
# => p {a:3}
^
Ruby has a few oddities in its parsing engine. One is that certain things require parentheses around them.
For instance, this should work.
p({a:3})
Or this
hash = { a: 3 }
p hash
As the other answer pointed out. The reason for this is that the interpreter processes as below.
# Input
p { a: 3 }
# What the interpreter sees
p do
a: 3
end
The Kernel#p doesn't support blocks, so you must use the parentheses.
Related
This question already has answers here:
Why does white-space affect ruby function calls?
(2 answers)
Closed 6 years ago.
How can I remove the "warning: `*' interpreted as argument prefix" from the following code?
hash = {"a" => 1,
"b" => 2,
"s" => 3,}
if "string".start_with? *hash.keys then
puts "ok"
else
puts "ng"
end
When I run the code above, I get:
$ ruby -w /tmp/a.rb
/tmp/a.rb:5: warning: `*' interpreted as argument prefix
ok
What is the best way to fix this warning?
I've tried to put parenthesis around hash like this:
hash = {"a" => 1,
"b" => 2,
"s" => 3,}
if "string".start_with? (*hash.keys) then
puts "ok"
else
puts "ng"
end
then you get:
$ ruby -w /tmp/a.rb
/tmp/a.rb:5: syntax error, unexpected *
if "string".start_with? (*hash.keys) then
^
/tmp/a.rb:5: syntax error, unexpected ')', expecting '='
if "string".start_with? (*hash.keys) then
^
/tmp/a.rb:7: syntax error, unexpected keyword_else, expecting end-of-input
And this is the problem described in Why does white-space affect ruby function calls?, and clearly not the way to fix the warning I'm trying to fix.
My ruby version is:
$ ruby --version
ruby 2.3.3p222 (2016-11-21) [x86_64-linux-gnu]
If you're going to use method-calling-parentheses then you must avoid putting a space between the method name and the opening parentheses:
if "string".start_with?(*hash.keys)
puts "ok"
else
puts "ng"
end
Also, then is rather archaic so we'll pretend that was never there. If there is a space between the method name and the opening parentheses then your parentheses are interpreted as expression-grouping-parentheses and that's where your syntax error comes from.
Once you add the method-calling-parentheses you remove any possible hint of ambiguity as to what your * is supposed to mean and the warning should go away.
BTW, the warning you're getting in this case is rather, um, silly. On second thought, the warning isn't so silly because Ruby can be whitespace sensitive in surprising ways. This:
o.m *x
can be interpreted as:
o.m(*x)
or as:
o.m() * x
but these:
o.m * x
o.m*x
o.m* x
can be interpreted in the same ways. Of course, all three of those are interpreted as o.m() * x and only o.m *x is seen as o.m(*x). Sane whitespace usage would suggest that o.m *x is obviously a splat whereas o.m * x is obviously a multiplication but a couple days on SO should convince you that whitespace usage is hardly sane or consistent.
That said, -w's output in the Real World tends to be so voluminous and noisy that -w is nearly useless.
Why is it not possible to print a hash directly.
p {:First=>1, :Second=>2}
syntax error, unexpected =>, expecting '}'
But its possible to save it to a variable and then print that variable?
my_hash = {:First=>1, :Second=>2}
p my_hash
{:First=>1, :Second=>2}
It appears that it's because Ruby is confused and thinks you're passing a block to the p method.
p {:First=>1, :Second=>2}
SyntaxError: unexpected =>, expecting '}'
As you can see from the error, it was expecting a } where your hash rocket for the first key is. As you've already found out, you can set this as a variable and it works. But you can also pass the hash to the p method without setting it as a variable if you use parenthesis, because p, puts and print are just methods too. This works, because passing a block to a method in Ruby requires it to be outside of the parenthesis which then cuts down on the ambiguity of what you're trying to do.
p({:First=>1, :Second=>2})
{:First=>1, :Second=>2}
#=> {:First=>1, :Second=>2}
As noted by tadman in the comments, you can also omit the parenthesis and curly braces as Ruby knows that key/value pairs passed as arguments to a method are the equivalent to passing a hash and wouldn't be misinterpreted as a block.
p :First => 1, :Second => 2
{:First=>1, :Second=>2}
#=> {:First=>1, :Second=>2}
def foo
1,2
end
causes syntax error "unexpected ',', expecting keyword_end"
I'd think this is valid Ruby. What's wrong?
You're not returning an array.
You should have this:
def foo
[1, 2]
end
Ruby isn't expecting a comma (,) because it isn't valid syntax. Integers in a simple array should be surrounded by brackets as well as delineated by a comma.
If you used explicit return it will work.
def foo
return 1,2
end
But that wouldn't work with implicit return. To make it work with implicit return you need to give it [1, 2].
When I used respond_with and passed a literal hash, it gave me the error:
syntax error, unexpected tASSOC, expecting '}'
`respond_with {:status => "Not found"}`
However, when I enclosed the literal hash in parentheses like so:
respond_with({:status => "Not found"})
the function runs without a hitch. Why do the parentheses make a difference? Isn't a hash an enclosed call?
When calling a method, the opening curly bracket directly after the method name is interpreted as the start of a block. This has precedence over the interpretation as a hash. One way to circumvent the issue is to use parenthesis to enforce the interpretation as a method argument. As an example, please note the difference in meaning of these two method calls:
# interpreted as a block
[:a, :b, :c].each { |x| puts x }
# interpreted as a hash
{:a => :b}.merge({:c => :d})
Another way is to just get rid of the curly brackets as you can always skip the brackets on the last argument of a method. Ruby is "clever" enough to interpret everything which looks like an association list at the end of an argument list as a single hash. Please have a look at this example:
def foo(a, b)
puts a.inspect
puts b.inspect
end
foo "hello", :this => "is", :a => "hash"
# prints this:
# "hello"
# {:this=>"is", :a=>"hash"}
Why does this error occur?
Regexp.new("[#$]")
# => SyntaxError: (irb):1: syntax error, unexpected $undefined
# => Regexp.new("[#$]")
# ^
# (irb):1: unterminated string meets end of file
# from ~/.rvm/rubies/ruby-1.9.3-p194/bin/irb:1:in `<main>'
This should describe the subset of strings consisting of either a single $ or #, literally. And, AFAIU Ruby's Regexp engine, # and $ don't need to be escaped inside a character class even though they're usually metacharacters.
I would guess from the error message that Ruby is trying to interpolate $ when it's hitting # within double-quotes, but...why? Ordering is important. The $ and # characters have multiple overloaded behaviors, so I'm at a loss about what's triggering this.
PS, FYI:
/[#$]/
# => SyntaxError: (irb):1: syntax error, unexpected $undefined
/[$#]/
# => /[$#]/
Regexp.new '[$#]'
# => /[$#]/
Regexp.new '[#$]'
# => /[#$]/
Regexp.new "[#$]"
# => SyntaxError: (irb):1: syntax error, unexpected $undefined
The problem is not $, but #, as #... is usually used for variable expansion in double quoted strings. Like "#{x}".
But the thing is you can also expand global variables directly using #$global, and that explains your problem:
$global = "hello"
"#$global"
=> "hello"
So the solution is to escape either # or $, as this will break the string interpolation state machine out of it's effort to interpret the construct as an interpolation:
puts "\#$global"
=> #$global
puts "#\$global"
=> #$global
EDIT
And just to make it really clear :) The problem is not the Regexp, but you are trying to expand a global variable named $] when you type "#$]":
puts "#$]"
SyntaxError: (irb):22: syntax error, unexpected $undefined
To fix it you need to escape something:
puts "\#$]"
=> #$]