How can I get the name of procedure in Nim? - macros

I am trying to write a macro for debug print in the Nim language.
Currently this macro adds filename andline to the output by instantiationInfo().
import macros
macro debugPrint(msg: untyped): typed =
result = quote do:
let pos = instantiationInfo()
echo pos.filename, ":", pos.line, ": ", `msg`
proc hello() =
debugPrint "foo bar"
hello()
currently output:
debug_print.nim:9: foo bar
I would like to add the name of the procedure (or iterator) of the place where the macro was called.
desired output:
debug_print.nim:9(proc hello): foo bar
How can I get the name of procedure (or iterator) in Nim, like __func__ in C?

At runtime you can do getFrame().procname, but it only works with stacktrace enabled (not in release builds).
At compile-time surprisingly I can't find a way to do it. There is callsite() in macros module, but it doesn't go far enough. It sounds like something that might fit into the macros.LineInfo object.
A hacky solution would be to also use __func__ and parse that back into the Nim proc name:
template procName: string =
var name: cstring
{.emit: "`name` = __func__;".}
($name).rsplit('_', 1)[0]

building on answer from #def- but making it more robust to handle edge cases of functions containing underscores, and hashes containing trailing _N or not
also using more unique names as otherwise macro would fail if proc defines a variable name
import strutils
proc procNameAux*(name:cstring): string =
let temp=($name).rsplit('_', 2)
#CHECKME: IMPROVE; the magic '4' chosen to be enough for most cases
# EG: bar_baz_9c8JPzPvtM9azO6OB23bjc3Q_3
if temp.len>=3 and temp[2].len < 4:
($name).rsplit('_', 2)[0]
else:
# EG: foo_9c8JPzPvtM9azO6OB23bjc3Q
($name).rsplit('_', 1)[0]
template procName*: string =
var name2: cstring
{.emit: "`name2` = __func__;".}
procNameAux(name2)
proc foo_bar()=
echo procName # prints foo_bar
foo_bar()
NOTE: this still has some issues that trigger in complex edge cases, see https://github.com/nim-lang/Nim/issues/8212

Related

Pre-compile textual replacement macro with arguments

I am trying to create some kind of a dut_error wrapper. Something that will take some arguments and construct them in a specific way to a dut_error.
I can't use a method to replace the calls to dut_error because to my understanding after check that ... then ... else can only come a dut_error (or dut_errorf). And indeed if I try to do something like:
my_dut_error(arg1: string, arg2: string) is {
dut_error("first argument is ", arg, " and second argument is ", arg2);
};
check that FALSE else my_dut_error("check1", "check2");
I get an error:
*** Error: Unrecognized exp
[Unrecognized expression 'FALSE else my_dut_error("check1", "check2")']
at line x in main.e
check that FALSE else my_dut_error("check1", "check2");
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
So I thought about defining a macro to simply do a textual replace from my wrapper to an actual dut_error:
define <my_dut_error'exp> "my_dut_error(<arg1'name>, <arg2'name>)" as {
dut_error("first argument is ", <arg1'name>, " and second argument is ", <arg2'name>)
};
But got the same error.
Then I read about the preprocessor directive #define so tried:
#define my_dut_error(arg1, arg2) dut_error("first argument is ", arg, " and second argument is ", arg2)
But that just gave a syntax error.
How can I define a pre-compiled textual replacement macro that takes arguments, similar to C?
The reason I want to do that is to achieve some sort of an "interface" to the dut_error so all errors have a consistent structure. This way, different people writing different errors will only pass the arguments necessary by that interface and internally an appropriate message will be created.
not sure i understood what you want to do in the wrapper, but perhaps you can achieve what you want by using the dut_error_struct.
it has set of api, which you can use as hooks (do something when the error is caught) and to query about the specific error.
for example:
extend dut_error_struct {
pre_error() is also {
if source_method_name() == "post_generate" and
source_struct() is a BLUE packet {
out("\nProblem in generation? ", source_location());
// do something for error during generation
};
write() is first {
if get_mesage() ~ "AHB Error..." {
ahb_monitor::increase_errors();
};
};
};
dut_error accepts one parameter, one string. but you can decide of a "separator", that will define two parts to the message.
e.g. - instruct people to write "XXX" in the message, before "first arg" and "second arg".
check that legal else dut_error("ONE thing", "XXX", "another thing");
check that x < 7 else dut_error("failure ", "XXX", "of x not 7 but is ", x);
extend dut_error_struct {
write() is first {
var message_parts := str_split(get_message(), "XXX");
if message_parts.size() == 2 {
out ("First part of message is ", message_parts[0],
"\nand second part of message is ", message_parts[1]
);
};
};
I could get pretty close to what I want using the dut_errorf method combined with a preprocessor directive defining the format string:
#define DUT_FORMAT "first argument is %s and second argument is %s"
check that FALSE else dut_errorf(DUT_FORMAT, "check1", "check2");
but I would still prefer a way that doesn't require this DUT_FORMAT directive and instead uses dut_error_struct or something similar.

How to cut a string from the end in UIPATH

I have this string: "C:\Procesos\rrhh\CorteDocumentos\Cortados\10001662-1_20060301_29_1_20190301.pdf" and im trying to get this part : "20190301". The problem is the lenght is not always the same. It would be:
"9001662-1_20060301_4_1_20190301".
I've tried this: item.ToString.Substring(66,8), but it doesn't work sometimes.
What can I do?.
This is a code example of what I said in my comment.
Sub Main()
Dim strFileName As String = ""
Dim di As New DirectoryInfo("C:\Users\Maniac\Desktop\test")
Dim aryFi As FileInfo() = di.GetFiles("*.pdf")
Dim fi As FileInfo
For Each fi In aryFi
Dim arrname() As String
arrname = Split(Path.GetFileNameWithoutExtension(fi.Name), "_")
strFileName = arrname(arrname.Count - 1)
Console.WriteLine(strFileName)
Next
End Sub
You could achieve this using a simple regular expressions, which has the added benefit of including pattern validation.
If you need to get exactly eight numbers from the end of file name (and after an underscore), you can use this pattern:
_(\d{8})\.pdf
And then this VB.NET line:
Regex.Match(fileName, "_(\d{8})\.pdf").Groups(1).Value
It's important to mention that Regex is by default case sensitive, so to prevent from being in a situations where "pdf" is matched and "PDF" is not, the patter can be adjusted like this:
(?i)_(\d{8})\.pdf
You can than use it directly in any expression window:
PS: You should also ensure that System.Text.RegularExpressions reference is in the Imports:
You can achieve it by this way as well :)
Path.GetFileNameWithoutExtension(Str1).Split("_"c).Last
Path.GetFileNameWithoutExtension
Returns the file name of the specified path string without the extension.
so with your String it will return to you - 10001662-1_20060301_29_1_20190301
then Split above String i.e. 10001662-1_20060301_29_1_20190301 based on _ and will return an array of string.
Last
It will return you the last element of an array returned by Split..
Regards..!!
AKsh

How can we trace expressions / print statements with line numbers in Scala?

If you want print statements with line numbers, how do you do it?
Check out Haoyi Li's sourcecode library, I think it gives you what you are looking for.
sourcecode is a small Scala library for that provides common "source
code" context to your program at runtime, similar to Python's
__name__, C++'s __LINE__ or Ruby's __FILE__. For example, you can
ask for the file-name and line number of the current file, either
through the () syntax or via an implicit.
See for example https://github.com/lihaoyi/sourcecode#logging
You can use sourcecode.File and sourcecode.Line to define log
functions that automatically capture their line number and file-name
def log(foo: String)(implicit line: sourcecode.Line, file: sourcecode.File) = {
println(s"${file.value}:${line.value} $foo")
}
log("Foooooo") // sourcecode/shared/src/test/scala/sourcecode/Tests.scala:86 Fooooo
It depends on what you want to do.
With the scala-trace-debug library, you can type something like this:
Debug.trace(1 + 2)
And get this:
"3" in thread main:
path.to.file(file.Scala: 22) // click-able stack trace
You can customize the number of lines of stack trace, like so:
Debug.trace(1 + 2, 3) // 3 lines of stack trace
And if you do info.collaboration_station.debug._, you can even do this:
val three = 3.trace
...
"3" in thread main:
path.to.file(file.Scala: 22)
Finally, there is support for expressions:
Debug.traceExpression{
val myVal = 4
1 + 2 + myVal
}
...
"{
val myVal = 4;
(3).+(myVal)
} -> 7" in thread main:
at main.Main$.main(Main.scala:12)
Unlike the other library, this is more geared toward debugging. If I wanted to provide a history of what was going on and I did not want the user to see a stack trace, I would not use this tool.

Sed and awk application

I've read a little about sed and awk, and understand that both are text manipulators.
I plan to use one of these to edit groups of files (code in some programming language, js, python etc.) to make similar changes to large sets of files.
Primarily editing function definitions (parameters passed) and variable names for now, but the more I can do the better.
I'd like to know if someone's attempted something similar, and those who have, are there any obvious pitfalls that one should look out for? And which of sed and awk would be preferable/more suitable for such an application. (Or maybe something entirely else? )
Input
function(paramOne){
//Some code here
var variableOne = new ObjectType;
array[1] = "Some String";
instanceObj = new Something.something;
}
Output
function(ParamterOne){
//Some code here
var PartOfSomething.variableOne = new ObjectType;
sArray[1] = "Some String";
var instanceObj = new Something.something
}
Here's a GNU awk (for "gensub()" function) script that will transform your sample input file into your desired output file:
$ cat tst.awk
BEGIN{ sym = "[[:alnum:]_]+" }
{
$0 = gensub("^(" sym ")[(](" sym ")[)](.*)","\\1(ParameterOne)\\3","")
$0 = gensub("^(var )(" sym ")(.*)","\\1PartOfSomething.\\2\\3","")
$0 = gensub("^a(rray.*)","sA\\1","")
$0 = gensub("^(" sym " =.*)","var \\1","")
print
}
$ cat file
function(paramOne){
//Some code here
var variableOne = new ObjectType;
array[1] = "Some String";
instanceObj = new Something.something;
}
$ gawk -f tst.awk file
function(ParameterOne){
//Some code here
var PartOfSomething.variableOne = new ObjectType;
sArray[1] = "Some String";
var instanceObj = new Something.something;
}
BUT think about how your real input could vary from that - you could have more/less/different spacing between symbols. You could have assignments starting on one line and finishing on the next. You could have comments that contain similar-looking lines to the code that you don't want changed. You could have multiple statements on one line. etc., etc.
You can address every issue one at a time but it could take you a lot longer than just updating your files and chances are you still will not be able to get it completely right.
If your code is EXCEEDINGLY well structured and RIGOROUSLY follows a specific, highly restrictive coding format then you might be able to do what you want with a scripting language but your best bets are either:
change the files by hand if there's less than, say, 10,000 of them or
get a hold of a parser (e.g. the compiler) for the language your files are written in and modify that to spit out your updated code.
As soon as it starts to get slightly more complicated you will switch to a script language anyway. So why not start with python in the first place?
Walking directories:
walking along and processing files in directory in python
Replacing text in a file:
replacing text in a file with Python
Python regex howto:
http://docs.python.org/dev/howto/regex.html
I also recommend to install Eclipse + PyDev as this will make debugging a lot easier.
Here is an example of a simple automatic replacer
import os;
import sys;
import re;
import itertools;
folder = r"C:\Workspaces\Test\";
skip_extensions = ['.gif', '.png', '.jpg', '.mp4', ''];
substitutions = [("Test.Alpha.", "test.alpha."),
("Test.Beta.", "test.beta."),
("Test.Gamma.", "test.gamma.")];
for root, dirs, files in os.walk(folder):
for name in files:
(base, ext) = os.path.splitext(name);
file_path = os.path.join(root, name);
if ext in skip_extensions:
print "skipping", file_path;
else:
print "processing", file_path;
with open(file_path) as f:
s = f.read();
before = [[s[found.start()-5:found.end()+5] for found in re.finditer(old, s)] for old, new in substitutions];
for old, new in substitutions:
s = s.replace(old, new);
after = [[s[found.start()-5:found.end()+5] for found in re.finditer(new, s)] for old, new in substitutions];
for b, a in zip(itertools.chain(*before), itertools.chain(*after)):
print b, "-->", a;
with open(file_path, "w") as f:
f.write(s);

Retrieving variable name in order to print them in specman

I wish to do the following in Specman:
my_task() is {
var my_var : int;
my_var = 5;
message(LOW,appendf("%s=[%d]",my_var.to_name(),my_var));
};
Currently, I'm in search of the internal task to_name(). I do not want to create a struct for this. I wish to only use Specman internals.
I'm not sure how you'd do this unless you somehow have the collection of all the fields, which would give you the name of all the fields.
So, I'm going to branch predict and assume that you want all the fields of a given struct/unit:
extend sys {
A : list of uint;
B : int;
cee : string;
run() is also {
var rf_sys: rf_struct = rf_manager.get_exact_subtype_of_instance(sys);
for each (field) in rf_sys.get_declared_fields() {
print field;
print field.get_long_name(); // <-- Here's your "get_name()" function
};
};
};
On version 8.2, this yields:
Usage: . env.sh [-32bit|-64bit] [-v] [[VAR=value]...]
Welcome to Specman Elite(64) (09.20.482-d) - Linked on Wed Mar 2 13:32:19
2011
Protected by U.S. Patents 6,141,630 ;6,182,258; 6,219,809; 6,347,388;
6,487,704; 6,499,132; 6,502,232; 6,519,727; 6,530,054; 6,675,138; 6,684,359;
6,687,662; 6,907,599; 6,918,076; 6,920,583; Other Patents Pending.
1 notification was modified by command 'set notify -severity=WARNING
DEPR_START_TCM_ARG_BY_REF'
Checking license ... OK
Loading /nfs/pdx/home/rbroger1/tmp.e ...
read...parse...update...patch...h code...code...clean...GC(sys)...
Doing setup ...
Generating the test using seed 1...
Starting the test ...
Running the test ...
field = rf_field 'time', Specman's private modules
field.get_long_name() = "time"
field = rf_field 'logger', Specman's private modules
field.get_long_name() = "logger"
field = rf_field 'A', line 5 in #tmp
field.get_long_name() = "A"
field = rf_field 'B', line 6 in #tmp
field.get_long_name() = "B"
field = rf_field 'cee', line 7 in #tmp
field.get_long_name() = "cee"
No actual running requested.
Checking the test ...
Checking is complete - 0 DUT errors, 0 DUT warnings.
If that doesn't quite answer your question, look more into Specman's introspection or reflection interface in the documentation for your version of Specman. Warning, details are a bit scarce from Cadence. Also, see my answer to "Specman: how to retrieve values of var which is stored in another var".. Finally, in specview you can use the data browser to browse the rf_manger itself ( introspection at its best). Then you can find all the functions that Cadence doesn't tell you about in their documentation.
You can't get the string name of a variable. Although you can get the name of a field using get_name().
This makes some sense, because the variable is only known at the location it is declared. You can't even access the variable in a later extension of my_task(). So if you are already editing the code at the location where the variable was declared, just say "my_var" rather than my_var.to_name(). (Yes, typos and cut and paste errors can happen.)
I think this is what you are looking for:
define <dump'action> "dump <exp>" as {
out("<exp>","=[",<exp>,"]");
};
you can use this macro in the specman command line or inside functions, for example:
foo() is {
var a : int = 5;
dump a;
};
will give:
a=[5]