1

This has me stumped

I have a large C source file which has a variable that must always be added to like this:

CycleCounter += SomeValue

The some value isn't important.

There are several hundred lines like this in the code in a source file with several thousand lines. I'm trying to ensure all assignments are with added and accidentally assigned or subtracted.

I want find all occurrences where CycleCounter is not followed by a '+' or some white space and a '+'

I tried this expression :

CycleCounter\s*[^+]

but in this example it also matches where a '+' does occur.

It matches case 1 as well but I only want it to match cases 2 & 3 which do not have a trailing '+'.

if (postbyte & 0x80)
{
    switch (postbyte & 0x1F)
    {
    case 0: // Post inc by 1
        ea = (*xfreg16[Register]);
        (*xfreg16[Register])++;
        CycleCounter+=NatEmuCycles21; // good. expression correctly ignored this
        break;

    case 1: // post in by 2
        ea = (*xfreg16[Register]);
        (*xfreg16[Register]) += 2;
        CycleCounter += NatEmuCycles32; // good. expression incorrectly identified 
        break;

    case 2: // pre dec by 1
        (*xfreg16[Register]) -= 1;
        ea = (*xfreg16[Register]);
        CycleCounter -= NatEmuCycles21;  // mistake, subtracted. expression correctly identified
        break;

    case 3: // pre dec by 2
        (*xfreg16[Register]) -= 2;
        ea = (*xfreg16[Register]);
        CycleCounter = NatEmuCycles32; // mistake, assigned. expression correctly identified
        break;

    case 4: // no offset
        ea = (*xfreg16[Register]);
        break;
1
  • What regex tool, specifically? if it supports PCRE, then you should be able to use negative lookahead, like CycleCounter(?!\s*[+]=) Oct 7, 2020 at 2:38

3 Answers 3

1

If using Perl Compatible Regular Expressions (PCRE), adding a + after \s* will greedily match those zero more spaces, preventing [^+] from matching the spaces. Eg. using grep with -P to use PCRE (a GNU grep option) and also -n to show line numbers:

grep -Pn 'CycleCounter\s*+[^+]' file

Another PCRE:

grep -Pn 'CycleCounter(?!\s*\+)' file

This time using negative lookahead to specify not followed a '+' or some white space and a '+'.


You could use sed to remove unwanted lines from output:

sed '/CycleCounter/!d; /CycleCounter[[:space:]]*+/d; =' file

Delete any line which doesn't contain CycleCounter, also delete any line that does contain CycleCounter followed by any space and a '+'. = to print line numbers.

1

It's all about the * quantifier. \s* - this means that there may be no matches, and the second expression [^+] will match a whitespace character immediately after the word CycleCounter.

grep 'CycleCounter\s\+[^+]'

or

grep 'CycleCounter\s*[^+]='

or

grep 'CycleCounter\s*[^+ ]'
2
  • Neither of these worked. Unfortunately my example didn't cover all possibilities and second fails with 'CycleCounter=...' and the first just fails in most cases. Oct 7, 2020 at 4:03
  • 1
    grep 'CycleCounter\s*[^+ ]'. I added to answer.
    – nezabudka
    Oct 7, 2020 at 4:39
1

If you want to match on CycleCounter provided it's not followed by optional whitespace and +, you can use perl's negative look-ahead operator:

grep -P 'CycleCounter(?!\s*\+)'

(here assuming a grep implementation that supports -P for perl-like regexps).

If you want to match on CycleCounter provided it's followed by optional whitespace and a character that is neither + nor whitespace:

grep 'CycleCounter[[:space:]]*[^+[:space:]]'

[[:space:]] is the POSIX regexp equivalent of Perl's \s. Some grep implementations also support \s in their BREs/EREs as an extension, but not inside bracket expressions where [\s] is required by POSIX to match on either \ or s.

In any case while both [[:space:]] and \s would match on a newline character, grep by default works on the contents of one line at a time (not including the newline delimiter), so both would still match on the first line of:

   CycleCounter
     += 12;

for instance.

With pcregrep (that comes with PCRE, a library that implements perl-like regexps and used by most of the grep implementations that support a -P option), you could do:

pcregrep -M '(?s)CycleCounter(?!\s*\+).*?;'

Where -M enables a multi-line mode and (?s) causes . to also match on newline, and .*?; is used to make sure the full C statement up to the next ; character is printed.

Of course, it could still be fooled with things like:

CycleCounter // blah ;
  += (c == ';')
  + 3;

Also note that they would match on MyCycleCounter2 += 3. To avoid that you could add word boundary operators around CycleCounter. In Perl, that's \bCycleCounter\b. There's no POSIX regex equivalent though some grep implementations also support \b, or support \<CycleCounter\> or [[:<:]]CycleCounter[[:>:]] as an extension.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .