I have the following tcl file:
proc test {} {
## proc test {} {
# puts "test"
## }
puts "Hallo"
}
Which does not produce an error.
If I now delete the closing braced in the comment I get an error
proc test {} {
## proc test {} {
# puts "test"
##
puts "Hallo"
}
Why does the interpreter behave like this? Are parts of comments evaluated in some way?
Tcl's comments are real comments, but they're parsed at the same time as other parts of the syntax, such as braces. This means that when we parse your code, we've got to take into account the brace counting as well. Here's how the parser thinks:
“Ready to parse a command.”
proc test {} {
“Still parsing the command. Now looking for one closing brace.”
## proc test {} {
“Still parsing the command. Now looking for two closing braces.”
# puts "test"
“Still parsing the command. Now looking for two closing braces.”
##
“Still parsing the command. Now looking for two closing braces.”
puts "Hallo"
“Still parsing the command. Now looking for two closing braces.”
}
“Still parsing the command. Now looking for one closing brace.”
“Oops! No more script but still looking for the end. Error time!”
The parser gets to the end and it's still looking for that closing brace. It only interprets the interior #s as the start of comments when the procedure is called and the procedure's body script is evaluated.
The upside to doing this is that you can use # for things other than comments as well, such as if you're embedding some C code inside your Tcl script, when the # starts a preprocessor directive.
If a brace - that is to say, the character { - appears in a Tcl comment inside a proc, the Tcl parser will behave in a way that you may not expect.
The # character is not a special preprocessor directive (as comments are in C/C++). In Tcl, comments act a lot like a command. If a command starts with the character #, then the rest of the line is ignored.
When you define a proc in Tcl, you are actually running the proc command, and it expects three arguments: the name of the procedure, a list of procedure arguments and a procedure body. Both the argument list and the procedure body have to be properly formed strings, and they are usually quoted with braces. You can have nested quotes within these strings, but you can't have extra open braces or unbalanced quotation marks. So as far as the proc command is concerned, this is a perfectly valid declaration.
proc foo { args } {
puts "executing procedure foo"
# comment out this code block {
foreach a $args {
puts "argument: $a"
}
}
}
Note that procedure body is a well formed Tcl string. All the nested braces balance. The proc command doesn't actually do anything with the procedure body. That happens later when you execute the procedure.
When you execute the procedure foo, Tcl recognizes the # comment command, and ignores the rest of the characters on that line including the trailing open-brace. It then processes the foreach command, which expects three arguments, one of which is a multi-line string quoted by braces. It then attempts to process the closing brace as a command, and fails:
% foo test
invalid command name "}"
You may have been trying to comment out a block of code, like this
# comment out this block {
proc foo {} {
blah ; blah ; blah
}
}
but that doesn't work because of the unmatched braces in the comment. The most thorough way to comment out that block of code is to place a # at the beginning of each line. But that is a lot of work. Instead, if your block is well-formed, parsable, Tcl code, remove the code with an if or proc command, like this:
# comment out this block using 'if'
if 0 {
proc foo {} {
blah ; blah ; blah
}
}
or
# comment out this block using 'proc'
proc donteverrunme {} {
proc foo {} {
blah ; blah ; blah
}
}
Source : http://wiki.tcl.tk/462
Related
I ran the following from a bash shell:
echo 'hello world' | ruby -ne 'puts $_ if /hello/'
I thought it was a typo at first, but it outputted hello world surprisingly.
I meant to type:
echo 'hello world' | ruby -ne 'puts $_ if /hello/ === $_'
Can anyone give an explanation, or point to documentation, to why we get this implicit comparison to $_?
I'd also like to note:
echo 'hello world' | ruby -ne 'puts $_ if /test/'
Won't output anything.
The Ruby parser has a special case for regular expression literals in conditionals. Normally (i.e. without using the e, n or p command line options) this code:
if /foo/
puts "TRUE!"
end
produces:
$ ruby regex-in-conditional1.rb
regex-in-conditional1.rb:1: warning: regex literal in condition
Assigning something that matches the regex to $_ first, like this:
$_ = 'foo'
if /foo/
puts "TRUE!"
end
produces:
$ ruby regex-in-conditional2.rb
regex-in-conditional2.rb:2: warning: regex literal in condition
TRUE!
This is a (poorly documented) exception to the normal rules for Ruby conditionals, where anything that’s not false or nil evaluates as truthy.
This only applies to regex literals, the following behaves as you might expect for a conditional:
regex = /foo/
if regex
puts "TRUE!"
end
output:
$ ruby regex-in-conditional3.rb
TRUE!
This is handled in the parser. Searching the MRI code for the text of the warning produces a single match in parse.y:
case NODE_DREGX:
case NODE_DREGX_ONCE:
warning_unless_e_option(parser, node, "regex literal in condition");
return NEW_MATCH2(node, NEW_GVAR(rb_intern("$_")));
I don’t know Bison, so I can’t explain exactly what is going on here, but there are some clues you can deduce. The warning_unless_e_option function simply suppresses the warning if the -e option has been set, as this feature is discouraged in normal code but can be useful in expressions from the command line (this explains why you don’t see the warning in your code). The next line seems to be constructing a parse subtree which is a regular expression match between the regex and the $_ global variable, which contains “[t]he last input line of string by gets or readline”. These nodes will then be compiled into the usually regular expression method call.
That shows what is happening, I’ll just finish with a quote from the Kernel#gets documentation which may explain why this is such an obscure feature
The style of programming using $_ as an implicit parameter is gradually losing favor in the Ruby community.
After digging through the Ruby source (MRI), I think I found an explanation.
The code:
pp RubyVM::InstructionSequence.compile('puts "hello world" if /hello/').to_a
produces the following output:
...
[:trace, 1],
[:putobject, /hello/],
[:getspecial, 0, 0],
[:opt_regexpmatch2],
...
The instructions seem to be calling opt_regexpmatch2 with two arguments, the first argument being the regex /hello/ and the second being a return value from getspecial
getspecial can be found in insns.def
/**
#c variable
#e Get value of special local variable ($~, $_, ..).
#j 特殊なローカル変数($~, $_, ...)の値を得る。
*/
DEFINE_INSN
getspecial
(rb_num_t key, rb_num_t type)
()
(VALUE val)
{
val = vm_getspecial(th, GET_LEP(), key, type);
}
Note that our instructions are most likely telling the VM to bring back the value of $_. $_ is automatically set for us when we run ruby with the correct options, e.g., -n
Now that we have our two arguments, we call opt_regexpmatch2
/**
#c optimize
#e optimized regexp match 2
#j 最適化された正規表現マッチ 2
*/
DEFINE_INSN
opt_regexpmatch2
(CALL_INFO ci)
(VALUE obj2, VALUE obj1)
(VALUE val)
{
if (CLASS_OF(obj2) == rb_cString &&
BASIC_OP_UNREDEFINED_P(BOP_MATCH, STRING_REDEFINED_OP_FLAG)) {
val = rb_reg_match(obj1, obj2);
}
else {
PUSH(obj2);
PUSH(obj1);
CALL_SIMPLE_METHOD(obj2);
}
}
At the end of the day
if /hello/' is equivalent to if $_ =~ /hello/ -- $_ will be nil unless we run ruby with the correct options.
From the Ruby_Newbie Guide to Symbols:
Author is trying to show a simplified implementation of the attr_writer method.
#!/usr/bin/env ruby
def make_me_a_setter(thename)
eval <<-SETTERDONE # <----- Here
def #{thename}(myarg)
##{thename} = myarg
end
SETTERDONE
end
class Example
make_me_a_setter :symboll
make_me_a_setter "stringg"
def show_symboll
puts #symboll
end
def show_stringg
puts #stringg
end
end
example = Example.new
example.symboll("ITS A SYMBOL")
example.stringg("ITS A STRING")
example.show_symboll
example.show_stringg
It's a heredoc. From the "Here Documents" documentation:
If you are writing a large block of text you may use a “here document”
or “heredoc”:
expected_result = <<HEREDOC
This would contain specially formatted text.
That might span many lines
HEREDOC
The heredoc starts on the line following <<HEREDOC and ends with the
next line that starts with HEREDOC. The result includes the ending
newline.
It's a multi line string. The code evaluates code embedded inside string. More on multi line strings:
http://blog.jayfields.com/2006/12/ruby-multiline-strings-here-doc-or.html
P.S. Using eval is not recommended, alternatives - yield, instance_eval, class_eval.
There is an example in the book:
"Seconds/day: #{24*60*60}" # => Seconds/day: 86400
"#{'Ho! '*3}Merry Christmas!" # => Ho! Ho! Ho! Merry Christmas!
"This is line #$." # => This is line 3
But when I try to implement the third line's symbol #$ it in a separate file, it prints smth strange. Here's my file str2.rb:
puts "Hello, World #$."
puts "Hello, World #$"
puts "#$"
Now I run it (in Win XP console):
C:\ruby\sbox>ruby str2.rb
Hello, World 0
Hello, World ["enumerator.so", "C:/RailsInstaller/Ruby1.9.3/lib/ruby/1.9.1/i386-mingw32/enc/encdb.so", "C:/Rai
lsInstaller/Ruby1.9.3/lib/ruby/1.9.1/i386-mingw32/enc/windows_1251.so", "C:/RailsInstaller/Ruby1.9.3/lib/ruby/
1.9.1/i386-mingw32/enc/trans/transdb.so", "C:/RailsInstaller/Ruby1.9.3/lib/ruby/site_ruby/1.9.1/rubygems/defau
lts.rb", "C:/RailsInstaller/Ruby1.9.3/lib/ruby/1.9.1/i386-mingw32/rbconfig.rb", "C:/RailsInstaller/Ruby1.9.3/l
ib/ruby/site_ruby/1.9.1/rubygems/deprecate.rb", "C:/RailsInstaller/Ruby1.9.3/lib/ruby/site_ruby/1.9.1/rubygems
/exceptions.rb", "C:/RailsInstaller/Ruby1.9.3/lib/ruby/site_ruby/1.9.1/rubygems/defaults/operating_system.rb",
"C:/RailsInstaller/Ruby1.9.3/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb", "C:/RailsInstaller/Ruby1.9
.3/lib/ruby/site_ruby/1.9.1/rubygems.rb", "C:/RailsInstaller/Ruby1.9.3/lib/ruby/1.9.1/i386-mingw32/enc/utf_16l
e.so", "C:/RailsInstaller/Ruby1.9.3/lib/ruby/1.9.1/i386-mingw32/enc/trans/utf_16_32.so", "C:/RailsInstaller/Ru
by1.9.3/lib/ruby/1.9.1/i386-mingw32/enc/trans/single_byte.so"]
puts
I've found that #$. (the period is mandatory) shows the line number only in Interactive Ruby Console. Used in file it produces 0 on any line. But why all that text is printed if I use symbols like this "#$" \n "#$"?
Also such a code in a file:
puts "Hello, World #$" ## without period at the end
produces such an error:
C:\ruby\sbox>ruby str2.rb
str2.rb:3: unterminated string meets end of file
What does #$ mean? Where and how to use it?
"#$." is shorthand for "#{$.}", or the interpolation of a global variable. Similarly there is ## for an instance variable & ### for a class variable.
The problem with what you have is that the second " in "#$" is not interpreted as the closing quote for the string, but instead as part of the global variable name being interpolated ($"). To make it more clear how your code is actually being interpreted, I’ll use string literals in place of what Ruby thinks are string delimiters:
puts %(Hello, World #$.)
puts %(Hello, World #$"
puts )#$"
As you can see, this is where the array printed comes from (it’s the contents of $") as well as the “puts” string at the end. The #$" at the end of your code is interpreted as a comment. (Note that the second string is spanning—and including—the newline between lines two and three.)
If you actually want to print #$ as a string, you have to escape part of it or use a single-quoted string:
"\#$" #=> "#$"
"#\$" #=> "#$"
'#$' #=> "#$"
Simply placing #$ in an interpolated string without escaping is not valid, as can be seen by using string literals:
%(#$) #=> #<SyntaxError: (eval):2: syntax error, unexpected $undefined
# %(#$)
# ^>
In Ruby a global variables are defined using dollar sign.
$foo = "bar"
There are some pre-defined globals such as
# last line number seen by interpreter
$.
I think you are just missing the period. You can insert a variable into a string using -
"line #{$.}"
or shorthand
"line #$."
I am trying to read arguments from a text file and the pass them all at once to a Ruby method.
The arguments in the text file are properly formatted e.g.:
"path", ["elem1","elem2"], 4,"string"
I intend to make a function call like this:
my_method("path", ["elem1","elem2"], 4,"string")
This hopefully I am trying to achieve like this:
IO.readlines("path").each do |line|
puts "#{line}"
my_method(*line.split(","))
end
The problem is that in the method all the array elements are wrapped in quotes. So my method ends up getting this:
""path"", "["elem1","elem2"]", "4",""string""
Now, this is probably because its an array of strings, but why wrap it with an additional "" when I say *arr?
If I use eval:
IO.readlines("path").each do |line|
puts "#{line}"
my_method(*eval(line))
end
I end up with syntax error, unexpected ',' after the first argument in "path", ["elem1","elem2"], 4,"string"
How do I achieve passing all the elements to the method at once reading the arguments from a text file
Also since Ruby does not care about types, why do I have to wrap my arguments with "" in the first place. If I don't wrap the argument in a quote, I get undefined variable for main:object error.
I have one solution, but instead of using "," as your delimiter use some other special character as delimiter in the input line.
# Input line in somefile.txt delimited by "||" :
# "path" || ["elem1","elem2"] || 4 || "string"
def my_method(arg1, arg2, arg3, arg4)
path = arg1
arr = arg2.gsub(/([\[\]])/, "").split(",")
number = arg3.to_i
string = arg4
puts "path : #{path} and is #{path.class}"
puts "arr : #{arr} and is #{arr.class}"
puts "number : #{number} and is #{number.class}"
puts "string : #{string} and is #{string.class}"
end
IO.readlines("somefile.txt").each do |line|
my_method(*line.gsub(/[(\\")]/, " ").split("||"))
end
I hope this helped you out. Let me know if you have any problem.
IO.readlines("path").each do |line|
params = line.split(",").each do |param|
param = eval(param)
end
my_method(*params)
end
When you read the line, all params are strings, so to get arrays and integers you might try to eval then first.
the eval tip might be enough to fix your code.
if you pass the param without quotes, the interpreter will understand it as a constant and not as a string. Thats why you get undefined variable. Again, the eval tip should solve this.
OBS: Be careful with eval since it will execute any code, a command to erase the file or even worse (like mess with your computer or server) if the person behind the source of that file knows it.
class Test
def printsomething
p "lol"
end
end
teet = Test.new
p "#{teet.printsomething}"
Output for above code is "lol"\n"lol"
why is this happening? I am running ruby 1.9.2 Archlinux x86_64
p is an inspect not really meant to be used to output text string. What it does is prints out the literal content of an object not an escaped string.
Just replace p with puts
You can see what I mean if you do this:
p "#{teet}"
=> "#<Test:0x00000100850678>"
Notice how it's inside quotes.
First thing that Ruby does when it sees a double-quoted string is replacing the #{expr} parts with the result of evaluated expr. For example, "#{2+2}" becomes "4". So, let's see what happens here. Ruby evaluates teet.printsomething. During this evaluation it executes the method and it prints "lol" in the 3-rd line. Note that although the method printsomething doesn't have a return statement, it nevertheless returns some value: it's the value returned by the last statement of that method. The return value of p object is the object itself, so the result of the method printsomething is "lol". This result now replaces the #{} part in the string, and the string becomes "lol" instead of "#{teet.printsomething}". Now the p method in the 7-th line is executed and outputs "lol" again.
What happens if you replace p with puts? The difference is that the return value of puts is nil. When the result of expr is nil, the whole expression #{} is replaced by empty string. So the 7-th line becomes puts "". As a result, the whole program outputs "lol" followed by an empty line.