Ruby (ruby 2.0.0p195 (2013-05-14) [x64-mingw32]) is parsing a sequence "ident-space-slash-number" as something starting a regular epxression:
irb(main):030:0> x = 10
=> 10
irb(main):031:0> x /2
irb(main):032:0/ /
SyntaxError: (irb):32: unterminated regexp meets end of file
(Line #32 is just there to make irb finish the parsing. At the end of line #31, the first / is already treated as a regexp delimiter.)
With different spacing or different operators it works as expected:
irb(main):033:0> x / 2
=> 5
irb(main):034:0> x/ 2
=> 5
irb(main):035:0> x/2
=> 5
irb(main):036:0> x *2
=> 20
Is this a bug? Based on what assumption would the parser see a reqular expression in that case?
First of all, run irb with warnings enabled to have a better understanding of what is going on (unrelevant warnings are omitted):
$ irb -w
irb:001> x = 0
=> 0
irb:002> x /2
irb:003/ /
(irb):2: warning: `/' after local variable or literal is interpreted as binary operator
(irb):2: warning: even though it seems like regexp literal
SyntaxError: (irb):3: unterminated regexp meets end of file
On line 2 the Ruby lexer detects that x is a local variable so it assumes that the following / is a binary operator not the beginning of a regexp. On line 3 raises an error because a / by itself is an incomplete regexp.
This happens because IRB uses a lexer to know if the expression you entered is complete, and therefore can be sent to Ruby for execution, or if you need to provide more input to complete the expression. The IRB's lexer can't detect what x is, so it assumes that it is a method and tries to interpret the rest of the line (/2) as the argument to x, since it is an unterminated regexp IRB ask you to complete it on line 3, thus the code sent by IRB to the Ruby parser is invalid as explained above.
For comparison consider what happens when x is actually a method:
$ irb -w
irb:001> def x; end
=> :x
irb:002> x /2
irb:003/ /
(irb):2: warning: ambiguous first argument; put parentheses or even spaces
ArgumentError: wrong number of arguments (1 for 0)
from (irb):1:in `x'
from (irb):2
In this case both Ruby and IRB agree on the way the expression have to be parsed and you got an error because you are trying to pass an argument (namely /2\n/) to the x method which expects none.
To the point: it is a bug or not? Maybe it is a bug or maybe it is just a compromise to keep the IRB's lexer simple, I can't really tell.
As you defined x = 10. It is sure to Ruby parser that, x is a local variable. But now when you write x /2, Ruby hopes, there is a method called x also and x /2 treated as x(/2). But it is /2 syntactically incorrect, so Ruby seems you are going to give the method x a regexp object. But in the last line when you wrote /, Still the same confusion Ruby parser is having. Thus it complains, your regexp literals is unterminated.
This is partly to compliment Arup's answer
Take a look at the Regexp class documentation. The opening paragraph says:
... Regexps are created using the /.../ and %r{...} literals, and by the Regexp::new constructor...
So as Arup said, the ruby parser interprets the /2 portion of the line as the beginning of the creation of a Regexp literal, very much in the same way as "2 would be interpreted as the beginning of the creation of a String literal.
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.
Let's say I have a literal Fixnum 1420028751000 and I want to convert using this:
Time.at(1420028751000 / 1000) # => 2014-12-31 20:25:51 +0800
I can put spaces also, let's say I am beginner and my coding is bad:
Time.at(1420028751000 / 1000)
Time.at(1420028751000 /1000)
All these work fine and give me correct result.
However, once I introduced variable:
a = 1420028751000
Time.at(a/1000) # => Works!
Time.at(a / 1000) # => Works!
Time.at(a /1000) # => Strange thing happen
My question is, what is so unique about the /1000 that make it not working when introducing an variable in Ruby?
Ruby's parser is interpreting /1000 as the beginning of a literal regexp, since a is a token which may be a method. That is, imagine that a is a method:
def a(arg); end
Time.at(a /1000)
Ruby will interpret this as "a invoked with an incomplete regexp as the argument". To "complete" this call, Ruby is expecting you might want to do something like:
Time.at( a(/1000/) )
I am writing a 6502 assembler in Ruby. I am looking for a way to validate hexadecimal operands in string form. I understand that the String object provides a "hex" method to return a number, but here's a problem I run into:
"0A".hex #=> 10 - a valid hexadecimal value
"0Z".hex #=> 0 - invalid, produces a zero
"asfd".hex #=> 10 - Why 10? I guess it reads 'a' first and stops at 's'?
You will get some odd results by typing in a bunch of gibberish. What I need is a way to first verify that the value is a legit hex string.
I was playing around with regular expressions, and realized I can do this:
true if "0A" =~ /[A-Fa-f0-9]/
#=> true
true if "0Z" =~ /[A-Fa-f0-9]/
#=> true <-- PROBLEM
I'm not sure how to address this issue. I need to be able to verify that letters are only A-F and that if it is just numbers that is ok too.
I'm hoping to avoid spaghetti code, riddled with "if" statements. I am hoping that someone could provide a "one-liner" or some form of elegent code.
Thanks!
!str[/\H/] will look for invalid hex values.
String#hex does not interpret the whole string as hex, it extracts from the beginning of the string up to as far as it can be interpreted as hex. With "0Z", the "0" is valid hex, so it interpreted that part. With "asfd", the "a" is valid hex, so it interpreted that part.
One method:
str.to_i(16).to_s(16) == str.downcase
Another:
str =~ /\A[a-f0-9]+\Z/i # or simply /\A\h+\Z/ (see hirolau's answer)
About your regex, you have to use anchors (\A for begin of string and \Z for end of string) to say that you want the full string to match. Also, the + repeats the match for one or more characters.
Note that you could use ^ (begin of line) and $ (end of line), but this would allow strings like "something\n0A" to pass.
This is an old question, but I just had the issue myself. I opted for this in my code:
str =~ /^\h+$/
It has the added benefit of returning nil if str is nil.
Since Ruby has literal hex built-in, you can eval the string and rescue the SyntaxError
eval "0xA" => 10
eval "0xZ" => SyntaxError
You can use this on a method like
def is_hex?(str)
begin
eval("0x#{str}")
true
rescue SyntaxError
false
end
end
is_hex?('0A') => true
is_hex?('0Z') => false
Of course since you are using eval, make sure you are sending only safe values to the methods
I have a string that is one character long and can be any possible character value:
irb(main):001:0> "\x0"
=> "\u0000"
I thought this might work:
irb(main):002:0> "\x0" += 1
SyntaxError: (irb):2: syntax error, unexpected tOP_ASGN, expecting $end
"\x0" += 1
^ from /opt/rh/ruby193/root/usr/bin/irb:12:in `<main>'
But, as you can see, it didn't. How can I increment/decrement my character?
Edit:
Ruby doesn't seem to be set up to do this. Maybe I'm approaching this the wrong way. I want to manipulate raw data in terms of 8-bit chunks. How can I best accomplish that sort of operation?
Depending on what the possible values are, you can use String#next:
"\x0".next
# => "\u0001"
Or, to update an existing value:
c = "\x0"
c.next!
This may well not be what you want:
"z".next
# => "aa"
The simplest way I can think of to increment a character's underlying codepoint is this:
c = 'z'
c = c.ord.next.chr
# => "{"
Decrementing is slightly more complicated:
c = (c.ord - 1).chr
# => "z"
In both cases there's the assumption that you won't step outside of 0..255; you may need to add checks for that.
You cannot do:
"\x0" += 1
Because, in Ruby, that is short for:
"\x0" = "\x0" + 1
and it is a syntax error to assign a value to a string literal.
However, given an integer n, you can convert it to a character by using pack. For example,
[97].pack 'U' # => "a"
Similarly, you can convert a character into an integer by using ord. For example:
[300].pack('U').ord # => 300
With these methods, you can easily write your own increment function, as follows:
def step(c, delta=1)
[c.ord + delta].pack 'U'
end
def increment(c)
step c, 1
end
def decrement(c)
step c, -1
end
If you just want to manipulate bytes, you can use String#bytes, which will give you an array of integers to play with. You can use Array#pack to convert those bytes back to a String. (Refer to documentation for encoding options.)
You could use the String#next method.
I think the most elegant method (for alphanumeric chars) would be:
"a".tr('0-9a-z','1-9a-z0')
which would loop the a through to z and through the numbers and back to a.
I reread the question and see, that my answer has nothing to do with the question. I have no answer for manipulationg 8-bit values directly.
I am trying to see if the string s contains any of the symbols in a regex. The regex below works fine on rubular.
s = "asd#d"
s =~ /[~!##$%^&*()]+/
But in Ruby 1.9.2, it gives this error message:
syntax error, unexpected ']', expecting tCOLON2 or '[' or '.'
s = "asd#d"; s =~ /[~!##$%^&*()]/
What is wrong?
This is actually a special case of string interpolation with global and instance variables that most seem not to know about. Since string interpolation also occurs within regex in Ruby, I'll illustrate below with strings (since they provide for an easier example):
#foo = "instancefoo"
$foo = "globalfoo"
"##foo" # => "instancefoo"
"#$foo" # => "globalfoo"
Thus you need to escape the # to prevent it from being interpolated:
/[~!#\#$%^&*()]+/
The only way that I know of to create a non-interpolated regex in Ruby is from a string (note single quotes):
Regexp.new('[~!##$%^&*()]+')
I was able to replicate this behavior in 1.9.3p0. Apparently there is a problem with the '#$' combination. If you escape either it works. If you reverse them it works:
s =~ /[~!#$#%^&*()]+/
Edit: in Ruby 1.9 #$ invokes variable interpolation, even when followed by a % which is not a valid variable name.
I disagree, you need to escape the $, its the end of string character.
s =~ /[~!##\$%^&*()]/ => 3
That is correct.