一个直接分析C语言源码的思路

有的时候我们需要分析C语言的源代码,不仅仅是做静态检查(已经有很多工具软件),也可能是做一些逻辑的验证。

我需要通过简单的查询语言能够访问C源文件中的标识符,类似于下面的语法:

[note|var|dec|fun].$name.$attr

一个可行的办法是通过正则表达式来匹配C源文件中的不同部分,然后提供给查询引擎:

1. 注释

2. 变量

3. (外部)符号声明

4. 函数实现

...

下面是简单的代码框架,由于时间关系就没有继续做下去,有兴趣的可以考虑一下。

#!/user/bin/perl
# Query grammer:
# [var|fun].$name.$attr
use strict;
use warnings;
my $iDrive = 'O:\\';
my $iFile = 'a.c';
my @IncludedHeaders = ();
my @ExternDeclarations = ();
my @FunctionImplementations = ();
my @FunctionCalls = ();
my @Variables = ();
my $VAR_QUERY = 0;
my $FUN_QUERY = 1;
&main();
sub main
{
 &PraseFile($iDrive.$iFile);
 while(1)
 {
  my $command = <STDIN>;
  my @segs = ();
  my $res = "";
  chomp($command);
  if($command =~ /var/)
  {
   @segs = split(/\./, $command);
   #print "$#segs\n";
   if($#segs eq 0)  #return last element in the array
   {
    foreach my $var (@Variables)
    {
     print "$var\n";
    }
   }
   else
   {
    $res = &Queryer($segs[1], $VAR_QUERY);
    print "Query variable: $command = $res\n";
   }
  }
  elsif($command =~ /fun/i)
  {
   @segs = split(/\./, $command);
   #print "$#segs\n";
   if($#segs eq 0)  #return last element in the array
   {
    foreach my $var (@FunctionImplementations)
    {
     print "$var\n";
    }
   }
   else
   {
    $res = &Queryer($segs[1], $FUN_QUERY);
    print "Query variable: $command = $res\n";
   }
  }
  elsif($command =~ /exit/i)
  {
   exit;
  }
 }
}
sub Queryer
{
 my $queryStr = $_[0];
 #print "$queryStr\n";
 my $ret = "unknown";
 if($_[1] eq $VAR_QUERY)
 {
  foreach my $str (@Variables)
  {
   if($str =~ /$queryStr/i)
   {
    $ret = $str;
    last;
   }
  }
 }
 elsif($_[1] eq $FUN_QUERY)
 {
  foreach my $str (@FunctionImplementations)
  {
   if($str =~ /$queryStr/i)
   {
    $ret = $str;
    last;
   }
  }
 }
 return $ret;
}
sub PraseFile
{
 my $file = $_[0];
 #print "$file\n";
 my $codeStringWithoutComments = "";
 my $SubReplaceRegularExpression = "";
 open(SOURCE_CODE, "<$file") || die "Could not read file, program halting.";
 my @lines = <SOURCE_CODE>;
 close(SOURCE_CODE);
    foreach my $line (@lines)
    {
  #chomp($line);
  $codeStringWithoutComments = $codeStringWithoutComments.$line;
 }
 #Remove comments from the string
 $SubReplaceRegularExpression = '\/\*(.|[\r\n])*?\*\/';
 $codeStringWithoutComments =~ s/$SubReplaceRegularExpression//ig; #remove c-style comment line
 
 #$SubReplaceRegularExpression = '\s*';
 #$codeStringWithoutComments =~ s/$SubReplaceRegularExpression//ig; #remove all whitespace
 #print "$codeStringWithoutComments\n";
 &LexicalAnalyser($codeStringWithoutComments);
}
sub LexicalAnalyser
{
 my $code = $_[0];
 my $storeVal = "";
 while(1)
 {
  if($code =~ s/(#ifndef.*?#endif)//s) #match as a single line ?. matches \n
  {
   #print "$1\n";
   push(@IncludedHeaders, $1);
  }
  elsif($code =~ s/(extern\s+\w+\s+\w+\(.*\)\s*;)//) # this matching must locate at the ahead of function call matching
  {
   #print "$1\n";
   push(@ExternDeclarations, $1); 
  }
  elsif($code =~ s/((\w+\s+)\**\s*(\w+)(\(.*?\))\s*\{.*?\})//s) # This function implementation matcher is not enough
  {
   #print "$1\n";
   $storeVal = $1;
   $storeVal =~ s/[\n\r]*//ig; #remove \r\n
   push(@FunctionImplementations, $storeVal); 
  }
  elsif($code =~ s/(\w+\(.*\)\s*;)//)
  {
   # This regular match all function call, including function call in others function
   # and function-style macro. 
   #print "$1\n";
   push(@FunctionCalls, $1); 
  }
  elsif($code =~ s/(((const|static|extern)?\s+)(\w+?\s+\**)(\w+?\[?.*?\]?\s*)=\s*.*?;)//s) # Variables
  {
   #print "$1\n";
   $storeVal = $1;
   $storeVal =~ s/[\n\r]*//ig; #remove \r\n
   push(@Variables, $storeVal); 
  }
  else
  {
   # condition that exit the while loop
   last;
  }
 }
 $code =~ s/\s*//ig;
 print "$code\n";
}

你可能感兴趣的:(一个直接分析C语言源码的思路)