Blame contrib/make_macros

Packit 0b5880
#!/usr/bin/perl
Packit 0b5880
Packit 0b5880
# Purpose: 
Packit 0b5880
#   Generates Assert/Fail macros for use with check.
Packit 0b5880
#
Packit 0b5880
# Usage:
Packit 0b5880
#   make_macros macros.in > macros.h
Packit 0b5880
# 
Packit 0b5880
# macros.in: one or more lines containing...
Packit 0b5880
#   Assert
Packit 0b5880
#   Assert_(|not)(Success|Error|Failure|NULL|True|False|Same)
Packit 0b5880
#   Fail_(if|unless)(Success|Error|Failure|NULL|True|False|Same)
Packit 0b5880
#   Assert_(|not)(0[0-7]+|0x[0-9A-Fa-f]+|0b[01]+|[1-9][0-9]+)
Packit 0b5880
#   Fail_(if|unless)(0[0-7]+|0x[0-9A-Fa-f]+|0b[01]+|[1-9][0-9]+)
Packit 0b5880
#   Assert_(|not)(LT|LE|EQ|GE|GT|NE)
Packit 0b5880
#   Fail_(if|unless)(LT|LE|EQ|GE|GT|NE)
Packit 0b5880
#   Assert_(|not){Type}(LT|LE|EQ|GE|GT|NE)
Packit 0b5880
#   Fail_(if|unless){Type}(LT|LE|EQ|GE|GT|NE)
Packit 0b5880
#   {Type}_compare
Packit 0b5880
#
Packit 0b5880
# Example:
Packit 0b5880
#   echo -e 'Assert_notNULL\nAssertStringEQ' | make_macros > macros.h
Packit 0b5880
#
Packit 0b5880
#   #include "macros.h"
Packit 0b5880
#          ...
Packit 0b5880
#   char *foo;
Packit 0b5880
#   Assert_notNULL( foo = strdup( "foo" ) , NULL );
Packit 0b5880
#   Assert_StringEQ( foo, "foo", NULL );
Packit 0b5880
#
Packit 0b5880
# author: unknown... if you know, please correct or email check-devel
Packit 0b5880
Packit 0b5880
use warnings;
Packit 0b5880
use strict;
Packit 0b5880
Packit 0b5880
use Switch 'Perl6';
Packit 0b5880
use Text::Wrap;
Packit 0b5880
Packit 0b5880
our %need;
Packit 0b5880
our %include;
Packit 0b5880
our %macros;
Packit 0b5880
Packit 0b5880
sub make_macro {
Packit 0b5880
    my ($name, $fh) = @_;
Packit 0b5880
    my ($condition) = $name;
Packit 0b5880
    
Packit 0b5880
    my ($failIf, $failUnless, $assertNot, $assert);
Packit 0b5880
    for ($condition) {
Packit 0b5880
        $failIf     = 1, next if s/^ Fail_ if     ([A-Z0-9]?) /$1/x;
Packit 0b5880
        $failUnless = 1, next if s/^ Fail_ unless ([A-Z0-9]?) /$1/x;
Packit 0b5880
        $assertNot  = 1, next if s/^ Assert_ not  ([A-Z0-9] ) /$1/x;
Packit 0b5880
        $assert     = 1, next if s/^ Assert_      (.        ) /$1/x;
Packit 0b5880
        $assert     = 1, next if s/^ Assert                  $//x;
Packit 0b5880
        print $fh "#error don't know how to make ${name}\n\n";
Packit 0b5880
        return;
Packit 0b5880
    }
Packit 0b5880
    
Packit 0b5880
    my ($invert) = 0;
Packit 0b5880
    foreach ($failIf, $assertNot) {
Packit 0b5880
    	$invert = !$invert if $_;
Packit 0b5880
    }
Packit 0b5880
    
Packit 0b5880
    my $type = "";
Packit 0b5880
    if ($condition =~ s/(LT|LE|EQ|GE|GT|NE)$//) {
Packit 0b5880
        $type = $condition;
Packit 0b5880
        $condition = $1;
Packit 0b5880
    }
Packit 0b5880
    $type = "Number" unless length $type;
Packit 0b5880
    
Packit 0b5880
    my (@args) = qw( expr );
Packit 0b5880
    my ($expr) = qw( (expr) );
Packit 0b5880
    my ($msg);
Packit 0b5880
    my ($doc);
Packit 0b5880
    my (%argdocs) = (
Packit 0b5880
        expr => "The expression to test."
Packit 0b5880
    );
Packit 0b5880
    
Packit 0b5880
    my ($not)  = sub { $invert ? "not " :     "" };
Packit 0b5880
    my ($Not)  = sub { $invert ?     "" : "not " };
Packit 0b5880
    my ($EQ)   = sub { $invert ? "!=" : "==" };
Packit 0b5880
    my ($NE)   = sub { $invert ? "==" : "!=" };
Packit 0b5880
Packit 0b5880
    given ($condition) {
Packit 0b5880
        when "" {
Packit 0b5880
            unless ( $invert ) {
Packit 0b5880
                $doc = "Asserts that \@p expr is true.";
Packit 0b5880
                $msg = "Assertion '\"#expr\"' failed.";
Packit 0b5880
            } else {
Packit 0b5880
                $doc = "Asserts that \@p expr is false.";
Packit 0b5880
                $msg = "Negative assertion '\"#expr\"' failed";
Packit 0b5880
            }
Packit 0b5880
        }
Packit 0b5880
        when [qw(Error error Failure failure Success success)] {
Packit 0b5880
            $invert = !$invert when [qw(Success success)];
Packit 0b5880
            unless ( $invert ) {
Packit 0b5880
                $doc = "Asserts that \@p expr failed.";
Packit 0b5880
                $msg = "'\"#expr\"' did not fail";
Packit 0b5880
            } else {
Packit 0b5880
                $doc = "Asserts that \@p expr did not fail.";
Packit 0b5880
                $msg = "'\"#expr\"' failed";
Packit 0b5880
            }
Packit 0b5880
            $invert = !$invert;
Packit 0b5880
        }
Packit 0b5880
        when [qw(True true False false)] {
Packit 0b5880
            $invert = !$invert when "false";
Packit 0b5880
            $doc = "Asserts that \@p expr is ".( $invert ? "false" : "true" ).".";
Packit 0b5880
            $msg = "'\"#expr\"' was " . ( $invert ? "true" : "false" );
Packit 0b5880
        }
Packit 0b5880
        when "NULL" {
Packit 0b5880
            $msg = "'\"#expr\"' was ".&$Not."NULL";
Packit 0b5880
            $doc = "Asserts that \@p expr is ".&$not."NULL";
Packit 0b5880
            $expr = "(expr) ".&$EQ." NULL";
Packit 0b5880
            $invert = 0;
Packit 0b5880
            $include{"stddef.h"} = 1;
Packit 0b5880
        }
Packit 0b5880
        when m/^(?:[1-9]\d*|0x[0-9A-Fa-f]+|0b[01]+|0[0-7]+)$/x {
Packit 0b5880
            $msg = "'\"#expr\"' was ".&$Not."equal to $condition";
Packit 0b5880
            $doc = "Asserts that \@p expr is ".&$not."equal to $condition.";
Packit 0b5880
            $condition = oct($condition) if $condition =~ /^0/;
Packit 0b5880
            $expr = "(expr) ".&$EQ." $condition";
Packit 0b5880
            $invert = 0;
Packit 0b5880
        }
Packit 0b5880
        when [qw(GT GE LT LE EQ NE Equal equal)] {
Packit 0b5880
            my $op;
Packit 0b5880
            given ( $condition ) {
Packit 0b5880
              $op = [ "less than"                 , "<"  ] when "LT";
Packit 0b5880
              $op = [ "less than that or equal to", "<=" ] when "LE";
Packit 0b5880
              $op = [ "equal to"                  , "==" ] when ["EQ", "Equal", "equal", "NE"];
Packit 0b5880
              $op = [ "greater than or equal to"  , ">=" ] when "GE";
Packit 0b5880
              $op = [ "greater than"              , ">"  ] when "GT";
Packit 0b5880
            };
Packit 0b5880
            $invert = !$invert when "NE";
Packit 0b5880
            @args = ( $type."1", $type."2" );
Packit 0b5880
            %argdocs = (
Packit 0b5880
                $type."1" => "The expression to test.",
Packit 0b5880
                $type."2" => "The test value."
Packit 0b5880
            );
Packit 0b5880
            $msg = "'\"#$args[0]\"' is ".&$Not.$op->[0]." '\"#$args[1]\"'";
Packit 0b5880
            $doc = "Asserts that \@p $args[0] is ".&$not.$op->[0]." \@p $args[1].";
Packit 0b5880
            $expr = "(${type}_compare( $args[0], $args[1] ) ".$op->[1]." 0)";
Packit 0b5880
            $need{$type."_compare"} = 1;
Packit 0b5880
        }
Packit 0b5880
        when [qw(Same same)] {
Packit 0b5880
            @args = qw( arg1 arg2 );
Packit 0b5880
            %argdocs = (
Packit 0b5880
                arg1 => "The expression to test.",
Packit 0b5880
                arg2 => "The expected value."
Packit 0b5880
            );
Packit 0b5880
            $msg = "'\"#arg1\"' and '\"#arg2\"' are ".&$Not."the same";
Packit 0b5880
            $msg = "Asserts that \@p arg1 and \@p arg2 are ".&$not."the same";
Packit 0b5880
            $expr = "(arg1) ".&$EQ." (arg2)";
Packit 0b5880
            $invert = 0;
Packit 0b5880
        }
Packit 0b5880
        default {
Packit 0b5880
            print $fh "#error don't know how to make ${name}\n\n";
Packit 0b5880
            return;
Packit 0b5880
        }
Packit 0b5880
    }
Packit 0b5880
Packit 0b5880
    if ($invert) {
Packit 0b5880
        $expr = "!$expr";
Packit 0b5880
    }
Packit 0b5880
Packit 0b5880
    if ($doc) {
Packit 0b5880
        print $fh "/**\n";
Packit 0b5880
        print $fh wrap(" * "," * ","$doc\n");
Packit 0b5880
        my ($maxlength) = 3;
Packit 0b5880
        foreach (keys %argdocs) {
Packit 0b5880
            $maxlength = length $_ if length $_ > $maxlength;
Packit 0b5880
        }
Packit 0b5880
        my ($indent) = " " x ($maxlength + 1 + 7);
Packit 0b5880
        foreach (keys %argdocs) {
Packit 0b5880
            print $fh wrap(" * ",
Packit 0b5880
                           " * $indent",
Packit 0b5880
                          "\@param $_ ".(" " x ($maxlength - length $_)).
Packit 0b5880
                          "$argdocs{$_}\n");
Packit 0b5880
        }
Packit 0b5880
        print $fh wrap(" * ",
Packit 0b5880
                       " * $indent",
Packit 0b5880
                       "\@param ... ".(" " x ($maxlength - 3)).
Packit 0b5880
                       "An optional message to indicate the assertion failed; ".
Packit 0b5880
                       "Omit for a default message.\n");
Packit 0b5880
Packit 0b5880
        print $fh " */\n";
Packit 0b5880
    }
Packit 0b5880
    print $fh "#define ${name}(",(join ", ", @args, '...'),") \\\n";
Packit 0b5880
    print $fh "    _ck_assert_msg( $expr, __FILE__, __LINE__, \\\n";
Packit 0b5880
    print $fh "                  \"$msg\", ## __VA_ARGS__, NULL)\n";
Packit 0b5880
    print $fh "\n";
Packit 0b5880
    
Packit 0b5880
    $macros{$name} = 1;
Packit 0b5880
}
Packit 0b5880
Packit 0b5880
sub make_compare {
Packit 0b5880
    my ($name, $fh) = @_;
Packit 0b5880
    my ($type) = $name;
Packit 0b5880
    $type =~ s/_compare$//;
Packit 0b5880
Packit 0b5880
    my ($expr);
Packit 0b5880
    my ($warning);
Packit 0b5880
    my ($ord);
Packit 0b5880
    my (@name) = ( "", "" );
Packit 0b5880
Packit 0b5880
    given ($type) {
Packit 0b5880
        when "Number" {
Packit 0b5880
            $expr = "( ${type}1 - ${type}2 )";
Packit 0b5880
            $ord = "is";
Packit 0b5880
        }
Packit 0b5880
        when "String" {
Packit 0b5880
            $expr = "strcmp( ${type}1, ${type}2 )";
Packit 0b5880
            $include{"string.h"} = 1;
Packit 0b5880
            $ord = "is";
Packit 0b5880
        }
Packit 0b5880
        default {
Packit 0b5880
            $expr = "memcmp( ${type}1, ${type}2, sizeof( ${type} ) )";
Packit 0b5880
            $include{"string.h"} = 1;
Packit 0b5880
            $ord = "are";
Packit 0b5880
            @name = ( "'s bytes ", "'s" );
Packit 0b5880
            $warning = "This compares \@p ${type}1 and \@p ${type}2 "
Packit 0b5880
                     . "byte-for-byte, this is probably not what you want "
Packit 0b5880
                     . "for all but the simplest of structures.";
Packit 0b5880
        }
Packit 0b5880
    }
Packit 0b5880
Packit 0b5880
    print $fh "/**\n";
Packit 0b5880
    print $fh " * Compares \@p ${type}1 and \@p ${type}2.\n";
Packit 0b5880
    print $fh wrap(" * "," * ",$warning . "\n") if $warning;
Packit 0b5880
    print $fh " * \@param ${type}1 The first ${type}.\n";
Packit 0b5880
    print $fh " * \@param ${type}2 The second ${type}.\n";
Packit 0b5880
    my (%val_name) = (
Packit 0b5880
        ">0" => "greater than",
Packit 0b5880
        "0 " => "equal to",
Packit 0b5880
        "<0" => "less than",
Packit 0b5880
    );
Packit 0b5880
    while (my ($val, $name) = each %val_name) {
Packit 0b5880
        print $fh " * \@retval $val If \@p ${type}1 $name[0]$ord $name \@p ${type}2 $name[1].\n";
Packit 0b5880
    }
Packit 0b5880
    print $fh " */\n";
Packit 0b5880
    print $fh "#define ${type}_compare(${type}1, ${type}2) \\\n";
Packit 0b5880
    print $fh "    $expr\n";
Packit 0b5880
    print $fh "\n";
Packit 0b5880
    $macros{$name} = 1;
Packit 0b5880
}
Packit 0b5880
Packit 0b5880
my $INCLUDES = '';
Packit 0b5880
open my $includes, ">", \$INCLUDES or die;
Packit 0b5880
Packit 0b5880
my $MACROS = '';
Packit 0b5880
open my $macros, ">", \$MACROS or die;
Packit 0b5880
Packit 0b5880
my $MACROS2 = '';
Packit 0b5880
open my $macros2, ">", \$MACROS2 or die;
Packit 0b5880
Packit 0b5880
while (<>) {
Packit 0b5880
    chomp;
Packit 0b5880
    make_macro( $_, $macros2 ) if /^(?:Assert|Fail)/;
Packit 0b5880
    make_compare( $_, $macros ) if /_compare$/;
Packit 0b5880
}
Packit 0b5880
Packit 0b5880
foreach my $need (keys %need) {
Packit 0b5880
    given ($need) {
Packit 0b5880
        when /^(?:String|Number)_compare$/ {
Packit 0b5880
            make_compare( $need, $macros );
Packit 0b5880
            delete $need{$need};
Packit 0b5880
        }
Packit 0b5880
    }
Packit 0b5880
}
Packit 0b5880
Packit 0b5880
foreach my $macro (keys %macros) {
Packit 0b5880
    delete $need{$macro};
Packit 0b5880
}
Packit 0b5880
Packit 0b5880
if (scalar %need) {
Packit 0b5880
    print $includes "/* needed functions/macros:\n";
Packit 0b5880
    foreach my $need (keys %need) {
Packit 0b5880
        if ($need =~ /^(.*)_compare$/) {
Packit 0b5880
            print $includes " * int $need(${1}1,${1}2);\n";
Packit 0b5880
        } else {
Packit 0b5880
            print $includes " * $need\n";
Packit 0b5880
        }
Packit 0b5880
    }
Packit 0b5880
    print $includes " */\n\n";
Packit 0b5880
}
Packit 0b5880
Packit 0b5880
foreach my $include (keys %include) {
Packit 0b5880
    print $includes "#include <$include>\n";
Packit 0b5880
}
Packit 0b5880
Packit 0b5880
print <<'END';
Packit 0b5880
#ifndef _CHECK_MACROS_H
Packit 0b5880
#define _CHECK_MACROS_H
Packit 0b5880
Packit 0b5880
END
Packit 0b5880
print $INCLUDES;
Packit 0b5880
print "\n";
Packit 0b5880
print $MACROS;
Packit 0b5880
print $MACROS2;
Packit 0b5880
print <<'END';
Packit 0b5880
Packit 0b5880
#endif /* _CHECK_MACROS_H */
Packit 0b5880
END
Packit 0b5880
Packit 0b5880