views:

215

answers:

6

I want to perform a batch replace operation on a project by following some rules. For e.g. I am taking notes in the code like this:

On every code piece, which is commented like this, I want to perform a replace operation, which will replace the input code piece with the output code piece in the following examples:

Input 1:

//+
a++;
//+(+SomeException$SomeMessage)

Output 1:

try
{
    a++;
}
catch (AnException)
{
    throw;
}
catch (Exception ex)
{
    throw new SomeException("SomeMessage", "15", ex);
}

Input 2:

//+
a++;
//-(+InvalidOperationException$SomeMessage)

Output 2:

try
{
    a++;
}
catch (InvalidOperationException ex)
{
    throw new AnException("SomeMessage", "16", ex);
}

Input 3:

//+
a++;
//-(SomeMessage)

Output 3:

try
{
    a++;
}
catch (Exception ex)
{
    throw new AnException("SomeMessage", "17", ex);
}

The magic numbers (15, 16, 17) will increase for each code piece commented like this. I know this is not the best practice but I am not making the decisions and I am expected to handle exceptions like this, so I thought I can ease the pain by taking notes and batch replacing in the end. What is the best way to do this? Should I write my own code to perform replaces or is there some regex replace tool or something like that exist that can automatically make this for me?

Update: This is a one time job and my magic number has to be globally unique. So if it was 25 for the last match in a file, it must be 26 for the first match in the next file.

A: 

This looks like a simple language that you're going to compile into another language that looks like Java. A compiler is the right tool for a job like this, especially because you need to keep around the state of the current magic number. It also seems likely that whoever is making the decisions would want to add new features to the language, in which case a solution glued together with regular expressions might not work properly.

If I'm right about what you really want, your question is reduced to the problem of "How do I write a Domain Specific Language?" I'm not sure what the best method would be for this, but if you know Perl you could probably put together a solution with Parse::RecDescent.

I think it's possibly to do this with scripting and regular expressions, but this is the type of problem for which compilers were invented. If you end up making something hacky, God help the person that has to maintain it after you! :)

James Thompson
Unfortunately I do not know perl and I am not trying to write a DSL. I just want to replace some strings in my code and increase the magic number for every match.
Serhat Özgel
I think that you are trying to implement a DSL that exists in the comments of your source code. It sounds like it's been written for you. :)Implementing operations like this is not just a simple search and replace problem, you need to parse input and emit symbols based on how the input has been parsed. That's the job of a compiler, and you probably need to write or use one.
James Thompson
A: 

You could write a CodeSmith template that reads that input and outputs that output. But, I'm not sure you could do it in-line. That is, you would need a file of just inputs and then your template could give you the file of outputs. I'm not sure if that acceptable tho.

JP Alioto
I watched the code smith tutorial and I think I can use the actual .cs files as templates. I hope it can transfer a state from a template file to another since the magic number has to be globally unique.
Serhat Özgel
A: 

There's a lot of ways you could do this, even though you probably shouldn't (as you seem to realize, this will just result in meaningless exceptions). Nevertheless, here's a sed/sh combo to do the first one. It doesn't handle the autonumbering or your other variants. I'll leave that as an exercise for the OP.

P1='\/\/+'; P2='\(.*\)'; P3='\/\/+(+\([^$]*\)$\(.*\))'; 
echo 'foo()\n//+\na++\n//+(+SomeException$Message)'|sed ' /'$P1'/ { N; /'$P2'/ { N; /'$P3'/ { s/'$P1'\n'$P2'\n'$P3'/try\n{\n\t\1\n}\ncatch (AnException)\n{\n\tthrow;\n}\ncatch (Exception ex)\n{\n\tthrow new \2("\3", "0", ex);\n}/ } } } '

The echo is just a test string.

Matthew Flaschen
Thanks a lot for generous regex writing time but once I figure out the correct tools or approach, I think I can form the regexes myself. The biggest problem here is the autonumbering.
Serhat Özgel
+1  A: 

What is the best way to do this? Should I write my own code to perform replaces or is there some regex replace tool or something like that exist that can automatically make this for me?

I'd write a little program in C++ or C# to do this. There are presumably other tools and script languages that can do it; but given that it's a trivial job in C++ or C# and given that I aready know how to do it in these languages, why not?

I don't know what you mean by the "best" way, but for me at least this would be one of the easiest ways.

ChrisW
Serhat Özgel
In that case, like I said, I'd do it in C++ or C#: because I'd find that an easy way that I already know how to, without research.
ChrisW
I agree, assuming Serhat knows C#, that would be the easiest way because everything else he'd have to learn before being able to use it.
mxp
A: 

As an Emacs user, for a one time job I'd do this by defining keyboard macros, then use set/increment/insert-register for the autonumbering magic. There shouldn't really be any need for writing your own elisp functions.

Though if you need to perform this on more than just a couple of files, you'll probably be better off writing a script to do the job.

Lars Haugseth
A: 

If you do not happen to use an IDE like Emacs (as answered by many) with strong regex support I would write a little script. Note that text manipulation is in general more a scripting operation, e.g. Perl, Ruby, due to regex support in the language itself. On the other hand if you are very familiar with say Java Pattern, then writing it in Java is propably the fastest solution, even if you need more overhead esp. for a one time operation.

So a litte Ruby script might look like that (beware, I did not test it):

$cnt = 1
IO.readlines(filename).collect { |line|
  if line =~ /^\s*\/\/\+\s*$/
    $cnt += 1
    ["try\n", "{\n" ]
  elsif line =~ /^\s*\/\/\+\(\+(.+)\$(.+)\)\s*/
    ["}\n", "catch (#{$1} ex)\n", "{\n", 
       "throw new AnException(\"#{$2}\", \"#{$cnt}\", ex);\n", "}\n"]
  # propably more else for all cases
  else
    line
  end
}.flatten
# save the file again
Peter Kofler