perl

pass by value to be made into pass by pointer-perl code


I have the text of function call like this below:

Send(0x39,((rLoss>>8)&0xFF),(rLoss&0xFF) );

I want to convert this text so the function passes by pointer. I have written two macros like:

BYTE0(var)  ((uint8_t *)&var)
BYTE1(var)  ((uint8_t)&var)+1)

I want the result to be:

Send(0x39,BYTE1(rLoss),BYTE0(rLoss) );

Can you please help me do this in Perl?


Solution

  • I presume that the first argument to the call is always a hex number that doesn't have to be examined or transformed. I've also presumed that args 2 and 3 are always ANDed with 0xFF. Finally, I've presumed that the function being called and the argument being shifted are simple words - i.e. match \w+. With these presumptions, the following appears to do what you want;

    use v5.12;
    
    while (<>) {
        chomp ;
        if (/ ^ (\w+) \(  .* \) \s* ; $ /x) {
            my $call = $1 ;                            # function name being called
            s/ ^  \w+  \( //x ;                        # Strip off fn call and open paren
            s/ \) \s* ; \s* $ //x ;                    # Strip off close paren and semicolon
            my ($arg1 , $arg2 , $arg3) = split ',' ;   # split into arguments of the call
    
            my $new_args = join("," , $arg1 , transform($arg2) , transform($arg3)) ;
            say "$call($new_args );" ;
        }
        else {
            say $_ ;
        }
    }
    
    
    sub transform {
        $_ = shift ;
        my $replacement ;
    
        s/ ^ \s* \( //x;                    # Strip off opening paren
        s/ \) \s* $ //x;                    # Strip off closing paren
        s/ & 0xFF $ //x ;                   # Strip off ANDing all ones
    
        if (/^ \w+ $/x) {                   # Simple var name left?
            $replacement = "BYTE0(" . $_ . ")" ;
        }
        elsif (/ ^ \( (\w+) >> (\d+) \) $ /x) {   # var name shifted some number of bits
            my $var_name = $1 ;
            my $shift_size = $2 ;
            my $byte_num = $shift_size / 8 ;
            $replacement = "BYTE" . $byte_num . "(" . $var_name . ")" ;
        }
        else {
            warn "Don't understand '$_' on line $.\n";
            $replacement = $_ ;
        }
        return $replacement
    }
    

    It's unix filter style - input on STDIN, transformed output on STDOUT. When I feed it this made up data;

    hello
    Send(0x39,((rLoss>>8)&0xFF),(rLoss&0xFF) );
    world
    Receive(0x12,(rWin&0xFF),((rWin>>16)&0xFF) );
    bye
    

    It spits out

    hello
    Send(0x39,BYTE1(rLoss),BYTE0(rLoss) );
    world
    Receive(0x12,BYTE0(rWin),BYTE2(rWin) );
    bye
    

    Hopefully, the inline comments explain the code. The decision on whether to attempt to transform the line or leave it alone is based solely on the first regex - a word (fn call) followed by something in parentheses - this may or may not be what you want. Perhaps you know that its always a call to "Send" in which case you can put that in the regex.

    The only other thing you may not be familiar with is the integer division operator '/'. This is used to translate the number of bits being shifted to the BYTE num macro call.