Shinnara's Blog
Talking with Shinnara :: NaraTalk.com


앞선 글에 적은 Perl의 수행 시간을 측정한 방법입니다.

CPAN에서 Time::HiRes 를 참조했는데요, microsecond 단위까지 측정이 가능합니다.

gettimeofday()를 이용하면 초단위와 microsecond 단위의 두개의 값을 배열 형태로 반환합니다. 그래서 아래와 같이 사용하시면 됩니다.

($ssec, $susec) = gettimeofday();


저의 경우는 다음과 같은 방법으로 작업 수행 시간을 측정했습니다.

use Time::HiRes qw(gettimeofday);

...
my $ssec;
my $esec;
my $susec;
my $eusec;

($ssec, $susec) = gettimeofday();

... Do Something ...

($esec, $eusec) = gettimeofday();

my $sec = ($esec - $ssec)*1000;
$sec += (($eusec - $susec)/1000);


 결과적으로 $sec에 millisecond 단위의 값이 저장되게 됩니다. Java에서 기본적으로 제공하는 시간 단위가 milisecond이기 때문에 단위를 통일하기 위해 위와 같이 처리하였습니다.

[추가]

JEEN 님게서 좋은 정보를 주셔서 CPAN에서 Benchmark 에 대해 찾아보았습니다.

CPAN에 보니 아래와 같은 예제가 있습니다.
use Benchmark;

$t0 = new Benchmark;

# ... your code here ...

$t1 = new Benchmark;

$td = timediff($t1, $t0);

print "the code took:",timestr($td),"\n";


제가 작성한 코드에 해당 부분을 추가해보았습니다.

use Benchmark ':hireswallclock';

...

($ssec, $susec) = gettimeofday();

my $t0 = new Benchmark;

... Do Something....

($esec, $eusec) = gettimeofday();

my $t1 = new Benchmark;
my $td = timediff($t1, $t0);

print "Benchmark Time: ", timestr($td), "\n";
my $sec = ($esec - $ssec)*1000;
$sec += (($eusec - $susec)/1000);

...


이에 대한 실행 결과는 다음과 같이 나옵니다.
Benchmark Time: 1.98443 wallclock secs ( 0.61 usr + 1.25 sys = 1.86 CPU)

이전의 Time:HiRes 를 이용한 결과는
Elapsed Time: 1984.425 msec

입니다. 두 결과가 거의 일치하는 것을 알 수 있습니다.

그리고 제 코드에보면 use Benchmark ':hireswallclock'; 로 선언한 것을 알 수 있는데, Time:HiRes 가 시스템에 설치되어 있으면 시간 단위를 microsecond 까지 지원해줍니다.

0 Trackback, 2 Comment

TRACKBACK :: http://naratalk.com/trackback/275 관련글 쓰기

댓글을 달아 주세요

  1. BlogIcon JEEN  댓글주소  수정/삭제  댓글쓰기

    Benchmark 모듈도 도움이 될겁니다.

    2008/12/18 11:23


 Java로 만든 C 코드 처리 프로그램을 Perl로 포팅하는 작업을 대강 끝내었습니다. 최대 관심사 중에 하나는 과연 Perl 과 Java가 동일한 결과를 나타내는지, 그리고 그 수행성능은 어느 정도인지 입니다. 일단 처리된 결과는 거의 같은 것 같습니다. ('거의' 라고 표현한 이유는 눈으로만 대충 훑어 봤기 때문입니다.)

오늘은 간단히 처리 결과만 먼저 올려봅니다.

[Java]
>>Processing Summary
Total Files: 360
Failed Files: 0
ProcessedEvents: 1572
Elapsed Time: 3137 msec

[Perl]
>>Processing Summary
Total Files: 360
Failure Files: 0
ProcessedEvents: 1572
Elapsed Time: 1976.322 msec


실행은 같은 머신에서 수행하였고, java 버전은 1.6.0_05, Perl은 Strawberry v5.10.0 을 사용하였습니다. 프로그램을 수행할 때마다 수행시간에 약간의 차이는 존재하지만 Perl이 훨씬 더 빠른 수행시간을 보여주네요. 정확하게 어디서 시간 차이가 나는지는 분석해보지 못했지만 Perl 의 수행시간은 꽤나 인상적임에는 틀림이 없습니다. 혹시 제가 계산을 잘못했나요? ^^

관련 코드는 시간을 내어 올리도록 하겠습니다.


0 Trackback, 0 Comment

TRACKBACK :: http://naratalk.com/trackback/274 관련글 쓰기

댓글을 달아 주세요


요  며칠 C 코드에 있는 특정 문자열에 대한 처리를 가지고 글을 몇개 올리고 있었습니다. Java 로 먼저 짠 프로그램을 Perl 로 변환하는 작업을 하고 있는데요, 하다 보니 Perl 에 대해 좀더 많이 알아가는 것 같습니다.

오늘 올릴 글은 한국 펄 사용자 모임에 올라온 Tip 중의 하나 입니다. Java 로 작성한 프로그램에서 입력값으로 디렉토리도 받을 수 있도록 하고 있었습니다.  입력된 값이 디렉토리인 경우, 하위 디렉토리에 있는 C 파일에 대해서도 처리를 하도록 했습니다. 그렇게 하기 위해 traverse()라는 함수를 만들었었는데요, 이를 Perl로 구현하기 위해 관련 자료를 찾던 중 알게되었습니다.

방법은 간단합니다. File::Find 를 쓰면 무척 쉽더군요.

find( \&traverse, 'target');  # File:Find 의 Search Function
sub traverse {

   my $filename =  $File::Find::name;
   if( (-f $_) && ($filename =~ /\.c$/))
      { # c 파일만 처리         
        replace( $filename);
      }  

} # end of sub traverse

find 의 인자로 subroutine 과 초기 디렉토리를 넘겨줍니다. 그러면 find는 하위 디렉토리를 검색하면서 각 항목에 대해 find 호출 시 받은 subroutine을 호출하게 됩니다. 위의 프로그램에서는 traverse 가 불리게 됩니다.  $File::Find::name 은 파일 이름을, $File::Find::dir은 해당 파일에 대한 디렉토리 명을 알려줍니다. 만약 target이라는 하위 디렉토리에 A.c B.c C.c 라는 파일이 있다면 $File::Find::name 의 값은 각각 target/A.c target/B.c tartget/C.c 가 되고 $File::Find::dir 의 값은 target 이 됩니다.

!! 주의

File::Find
CPAN에 있는 설명을 보면 아래와 같은 문구가 있습니다.

find() does a depth-first search over the given @directories in the order they are given. For each file or directory found, it calls the &wanted subroutine. (See below for details on how to use the &wanted function). Additionally, for each directory found, it will chdir() into that directory and continue the search, invoking the &wanted function on each file or subdirectory in the directory.

find() 에 의해 자동으로 디렉토리가 바뀌는 점에 주의를 기울여야 합니다. 위의 코드에서는 replace($filename)으로 호출하고 있지만 엄밀히 따지면 전체 패스를 줄 필요가 없습니다. 이미 디렉토리가 변경되어 있기 때문이죠. 이는 매우 편리할 수도, 그리고 매우 위험할 수도 있습니다. 프로그램을 작성할 때 위의 사항에 유의해야만 합니다.


[추가]

Aero 님께서 좋은 정보를 주셔서 찾아보니 CPAN의 설명에 no_chdir 이라는 옵션이 있더군요. 그래서 다음과 같이 바꾸어 보았습니다.

find( {wanted => \&traverse, no_chdir => 1 }, 'target'); 
sub traverse {

   my $filename = $_;  
  
   if( (-f $filename)  && ($File::Find::name =~ /\.c$/))
   {        
      replace( $filename); 
   }


그리고, 더불어 펄 사용자 모임에 있는 Tip 하나를 더 소개합니다. 경로에서 디렉토리와 파일 이름 분리라는 글인데요, Regular Expression을 이용한 멋진 방법이네요.

( $dir, $file ) = ( $srcfilename =~ m|^(.*/)(.*)$| );

Regular Expression의 Greedy 특성을 이용한 방법으로 정말 멋지게 동작합니다 ^^









0 Trackback, 3 Comment

TRACKBACK :: http://naratalk.com/trackback/273 관련글 쓰기

댓글을 달아 주세요

  1. BlogIcon aero  댓글주소  수정/삭제  댓글쓰기

    File::Find의 find함수에서 디렉토리를 안 바뀌게 하는 옵션도 있습니다.
    http://kldp.org/node/101006#comment-469299 를 참고하시면 될 듯.

    2008/12/17 14:04
  2. keedi  댓글주소  수정/삭제  댓글쓰기

    안녕하세요. 유용한 글 잘 읽었습니다. :-) 정규표현식을 이용한 간단한 디렉터리 및 파일 분리도 좋지만 또 한편으로는 기본 모듈로 포함하고 있는(설치가 필요없는) File::Basename 가 제공하는 fileparse(), basename(), dirname()을 사용하면 운영체제 별로 신경쓰지 않아도 된다는 면에서 괜찮은 것 같습니다. 새해 복 많이 받으세요~ :-)

    2009/01/03 18:33


요즘 관심을 가지고 있는 언어 중의 하나가 Perl입니다.  많이 다루어보지는 않았지만 Perl을 접하면서, 그리고 Perl 관련 사이트 및 블로그를 돌아다녀보면서 느낀 점은 Perl 은 단순한 스크립트 언어가 아닌 마치 종교 같다는 것입니다. 아직 제가 그 종교에 푸욱 빠지고 있지는 않지만 시간이 지나면 어찌될 지는 저도 모르는 일입니다 ^^.

그래서 종종 시간이 나면 Perl 관련 사이트나 블로그를 돌아보는데, 자주 가는 Aero 님의 블로그에서 옛글을 살펴보다가 재밌는 링크를 발견했습니다. Aero님의 원글은 이 글의 제목과 같이 "Perl 프로그래머가 반드시 알아야 할 것"입니다.

Perl 101 : Things Every Perl Programmer Should Know

아직 많은 내용을 살펴보지는 못했지만, Perl을 시작하는 사람에게 꽤나 도움이 될 것 같네요~~

추가 링크:

Perl Training Australia

How To Start Perl - Aero 님의 글
  이 글을 보니 지난 번에 제가 올린 글에서 파일 핸들을 수정해야 겠다는 생각이 간절하네요 ^^ 앞으로 많이 배워야 겠습니다.

Perl Best Practice 1.5
  바로 위의 Aero 님의 글에서 얻은 파일인데요, Perl Best Practice 를 간략히 요약한 것이라고 합니다.



TAG Perl
0 Trackback, 0 Comment

TRACKBACK :: http://naratalk.com/trackback/272 관련글 쓰기

댓글을 달아 주세요


 요즘 간혹 올리는 글들이 대체로는 Regular Expression(정규 표현식)에 대한 것들이네요. C 코드 분석이나 Perl 이야기나 모두.. 몇번에 걸쳐 올리던 C 코드 분석은 현재로서는 잠정 중단입니다. 대신에 회사 업무 도중 Regexp 를 사용할 수 있는 좋은 기회가 생겼지요.

 아시다시피 제가 속한 부서는 임베디드 소프트웨어를 만들고 있습니다. C 를 이용해서 프로그램을 만들지요. 비슷한 기능들을 모아 CSC라 분류하고 각 개발자는 몇개의 CSC를 맡아서 개발 및 테스트를 진행합니다. 보통 CSC는 디렉토리별로 나뉘게 되고, 이 디렉토리 아래에 .c 파일이 위치하게 되는 형태이지요.

 프로그램을 작성하다보면 소프트웨어의 작동시 발생되는 여러 이벤트들을 외부에 알려야 하는 경우가 생깁니다. 이를 위해 event_log 라는 함수를 사용하게 되는데요, event_log 함수는 다음과 같이 사용됩니다.

event_log( EVENT_ID , "detail desc");

 개발 과정에서는 뒤의 detail description을 이용해서 보다 알기 쉽게 이벤트의 내용을 표시해주게 됩니다. 하지만 정식 릴리즈가 가까워지면 수행 시간이나 이미지 크기 등의 문제로 인해, 뒤의 "detail desc"를 삭제해 주어야 합니다. 즉 아래와 같은 형태로 바꾸어야 하지요.

event_log( EVENT_ID );

 여기서 문제가 발생합니다. event_log 함수가 전체 프로그램 내에서 무척이나 많이 사용되고 있기 때문에 "detail desc"을 삭제하는 일이 만만치 않은 작업이 되는 것이죠. 또한 정식 릴리즈 버전이 아니라 테스트를 위해서는 자세한 이벤트의 내용을 알기를 원하기 때문에 아래와 같은 형태로 변경하기로 하였습니다.

#ifdef EVENT_DEBUG
event_log( EVENT_ID, "detail desc");
#else
event_log( EVENT_ID);
#endif

즉, 컴파일 시점에서 테스트용 이미지와 정식 빌드용 이미지를 EVENT_DEBUG를 이용해서 분리해내고자 하는 것입니다. 이러한 기법은 C/C++에서는 너무도 많이 쓰이는 방법입니다.

 소스 코드 전체에 대해서 단순히 뒤의 자세한 설명만 없애는 것을 예상했을 때는 UltraEdit, AcroEdit 등 개발용 텍스트 에디터등을 이용해 정규 표현식으로 치환하려고 했습니다. 하지만 새롭게 제안된 내용은 단순한 치환으로는 안되더군요. 해당 에디터 들에서 줄바꿈을 만들어 내기가 어려웠습니다. 물론 매크로등을 이용하면 되긴 했지만, 뭔가 찜찜한 기분에 아예 변환 도구를 만들자고 생각했습니다. 

 어떤 언어로 만들까 잠시 고민을 해보았습니다. 최근에 관심을 가지고 있는 Perl을 이용해서 간단히 작업을 해보았는데 꽤나 쉽게 처리할 수 있었습니다. 이 글을 작성하는 곳이 회사가 아니라 집이다보니 해당 소스를 완벽하게 첨부를 하지는 못하지만, 대략 아래와 같은 형태입니다.

 foreach  $aLine (<fileHandle> )
 {   
   
   if( $aLine =~ /^\s*(event_log.*),.*;$/ ) {
       print "#ifdef EVENT_DEBUG\n";
       print "$aLine\n";
       print "#else\n";
       print "$1);\n";
       print "#endif\n";
    } else {
       print "$aLine";
    }
 }
 close( fileHandle );

 앞서 소개했던 Perl을 이용한 C 소스 코드 분석 에 나온 코드 형태와 유사합니다.  일단 주어진 하나의 파일에 대해 처리하는 것은 어렵지 않더군요. 하지만 코드를 작성하다보니 점점 하고 싶어지는 게 많아집니다. #ifdef 을 붙였다가 다시 떼고 싶을 땐 어떻게 하지? 이렇게 했다가 다시 이벤트 아이디만 표시하는 깔끔한 (?) 코드로 변경하고자 한다면? 디렉토리를 입력으로 주면 하위 디렉토리에 있는 모든 .c 파일에 대해서 같은 처리를 하면 편리하지 않을까? 등등등...

 그러다 보니 아직 서툰 Perl로 작성하기에는 어려움이 많더군요. 손에 익숙치 않다보니.. 그래서 일단 Java로 짜기로 했습니다. Perl이야 말로 정규 표현식을 이용한 문자열 처리의 최고봉이긴 하지만, Java도 정규 표현식을 지원하니 비슷한 형태가 가능합니다.

오늘 회사에서 관련된 툴을 작성하여 팀 내에 배포를 하였는데, 자잘한 버그들이 존재하는 바람에 두번이나 더 배포를 해야 했습니다. 아직 한번 더 배포할 것이 남아있기도 하구요. 일단 원하는 기능을 잘 동작하는 것 같습니다. 자세한 소스는 다음에 올리기로 하고, 앞으로는 오늘 작성한 소스에 대한 공개적인 리팩토링 및 이를 Perl 등의 다른 언어로 표현하는 것에 대해서 다루어볼까 합니다. Perl을 배우는 좋은 기회가 될 것 같기도 하구요.

모두 좋은 밤, 그리고 즐거운 주말 되세요.

p.s. 펄 관련 유용한 사이트 링크 하나..
http://gypark.pe.kr/wiki/Perl/%EC%A0%95%EA%B7%9C%ED%91%9C%ED%98%84%EC%8B%9D

1 Trackback, 0 Comment

TRACKBACK :: http://naratalk.com/trackback/269 관련글 쓰기

  1. Subject: Regular Expression 을 실제 작업에 이용하기 (2)

    Tracked from Talking with Shinnara :: NaraTalk.com  삭제

    Regular Expression을 실제 작업에 이용하기에 이은 두번째 글입니다. 이번 글에서는 지난 글에서 소개하지 못했던 자바 소스에 대해서 간략히 설명을 할까 합니다. 그리고는 고수님들의 조언을 받아서 리팩토링도 하고, 또 다른 언어로 바꾸어볼까도 생각합니다. 지난 글에서 밝혔듯이 처음에 작성하고 섣불리 팀내에 배포했다가 몇가지 패턴을 인식못하고 또 잘못 바꾸어주는 덕에 테스트의 중요성을 실감한 사건이기도 했습니다. 정말이지 테스트는 무지 중요..

    2008/12/15 10:03

댓글을 달아 주세요


 앞서 몇개의 포스팅을 통해 C 파일을 분석하기 위한 작업을 하고 있다고 말씀드렸는데,  aero 님께서 유용한 정보를 주셔서 해당 모듈을 설치하고 테스트 해보기로 했습니다. 유용한 정보는 이전 글에서 확인하실 수 있습니다.

 알려주신 정보가 무척이나 도움이 될 것임을 알겠는데, 문제는 CPAN 모듈을 한번도 설치해본 적이 없다는 것입니다. Perl을 제대로 사용해본적도 없거니와, CPAN은 이번에 알게되었으니 말이죠. Windows에 Perl을 설치하는 과정도 시행착오(?)를 여러번 거듭했죠. ActivePerl을 깔았다가 camelBox를 깔고, 그러다가 다시 Strawberry를 깔고하는 식의 삽질(?)을 했답니다. 지금은 딸기가 깔려있고 딸기에 있는 CPAN Client를 이용해서 해당 모듈을 설치하려고 합니다.

CPAN Client를 실행하니 CPAN shell이 뜨더군요. help 를 쳐보니 어쩌구 저쩌구.. 하지만 뭘 알겠습니까.. 초보가..그래서 구글링을 해서 얻은 자료입니다.

펄(Perl)의 CPAN 모듈 사용하기

간단히 요약하자면,

i 명령을 통해 해당 모듈의 정보를 검색해 볼 수 있고,
install 명령을 통해 설치할 수 있다.


입니다.

aero 님이 알려주신 Parse::RecDescent 모듈을 검색하려면

cpan> i Parse::RecDescent


해당 모듈을 설치하려면

cpan> install Parse::RecDescent


하면 된답니다.

install 명령을 실행하면 다음과 같은 메시지가 표시됩니다.


cpan> install Parse::RecDescent
Running install for module 'Parse::RecDescent'
Running make for D/DC/DCONWAY/Parse-RecDescent-1.94.tar.gz
Fetching with LWP:
  http://cpan.strawberryperl.com/authors/id/D/DC/DCONWAY/Parse-RecDescent-1.94.t
ar.gz
Checksum for C:\strawberry\cpan\sources\authors\id\D\DC\DCONWAY\Parse-RecDescent
-1.94.tar.gz ok
Scanning cache C:\strawberry\cpan\build for sizes
DONE

  CPAN.pm: Going to build D/DC/DCONWAY/Parse-RecDescent-1.94.tar.gz

Checking if your kit is complete...
Looks good
Writing Makefile for Parse::RecDescent
cp lib/Parse/RecDescent.pm blib\lib\Parse\RecDescent.pm
cp lib/Parse/RecDescent.pod blib\lib\Parse\RecDescent.pod
  DCONWAY/Parse-RecDescent-1.94.tar.gz
  C:\strawberry\c\bin\dmake.EXE -- OK
Running make test
C:\strawberry\perl\bin\perl.exe "-Iblib\lib" "-Iblib\arch" test.pl
1..18
ok 1
ok 2
ok 3
ok 4
ok 5
ok 6
ok 7
ok 8
ok 9
ok 10
ok 11
ok 12
ok 13
ok 14
ok 15
ok 16
ok 17
ok 18
  DCONWAY/Parse-RecDescent-1.94.tar.gz
  C:\strawberry\c\bin\dmake.EXE test -- OK
Running make install
Prepending C:\strawberry\cpan\build\Parse-RecDescent-1.94-1Qhpm6/blib/arch C:\st
rawberry\cpan\build\Parse-RecDescent-1.94-1Qhpm6/blib/lib to PERL5LIB for 'insta
ll'
Installing C:\strawberry\perl\site\lib\Parse\RecDescent.pm
Installing C:\strawberry\perl\site\lib\Parse\RecDescent.pod
Writing C:\strawberry\perl\site\lib\auto\Parse\RecDescent\.packlist
Appending installation info to C:\strawberry\perl\lib/perllocal.pod
  DCONWAY/Parse-RecDescent-1.94.tar.gz
  C:\strawberry\c\bin\dmake.EXE install UNINST=1 -- OK


cpan>

로그를 보니 설치가 잘 된것 같군요. 이제 이 모듈을 이용해서 C 코드를 파싱해보는 일만 남았네요.

잘 되면 후기(?)를 올려보겠습니다.

좋은 하루 되세요.~



0 Trackback, 0 Comment

TRACKBACK :: http://naratalk.com/trackback/268 관련글 쓰기

댓글을 달아 주세요


다들 월요일을 잘 시작하셨는지요? 저는 주말을 너무도 재밌게 보내고나서인지 오늘 하루는 조금 피곤했네요. 모든 가족들이 잠든 조용한 밤에 몇글자 끄적여볼까 합니다.

본론부터 이야기하자면, 오늘 오후를 기점으로 소스 코드 분석을 위해 Perl을 이용하려던 계획은 잠정 보류하기로 하였습니다. 이전의 두개의 포스팅을 통해 Function과 Literal을 분리해내는 Perl 코드를 작성했다고 말씀드렸는데요, 이제 글로벌 변수에 대한 추출을 하려고 하니, 이게 막막하더군요.  처음의 생각은 이러했습니다.

  1. 전체 변수를 추출
  2. 로컬 변수를 제거 : 모듈 내의 선언문을 통해 알 수 있겠죠?
  3. 대입문이 있는 경우 왼쪽에 쓰이면 Output [각주:1], 오른쪽에 쓰이면 Input. 대입문이 아니면 Input

위와 같은 단계를 거쳐 추출을 해내려고 했습니다. 그런데 위와 같은 방법으로 만들어내는 변수의 목록은 그 정확성을 100% 장담하기 어렵다는 것이지요. 즉, 뭔가 찜찜한 구석이 남게 되는 문제가 생깁니다. 그래서 생각한 것이 Syntax 에 따라 파싱을 하게되면 정확히 Input/Output을 가릴 수 있지 않을까 하는 것입니다. 그러기 위해서는 제대로된 파싱을 할 필요가 있겠지요.

그래서 일단 "Perl을 이용한 C 소스 코드 분석"은 잠시 보류하려고 합니다. 오늘 오후부터 Lex & Yacc 에 대해 살펴보고 있는 중이랍니다. 며칠 동안 서베이와 테스트 코드를 돌려보고 진행 여부를 결정해볼까 합니다.

생각보다 작업이 어려워지네요..^^

모두 즐거운 밤 되세요.

  1. 팀의 코딩 및 주석 규칙에 외부의 변수를 단순 참조할 경우 Input, 해당 값이 모듈 내에서 변경될 경우 Output으로 정하고 있습니다. [본문으로]
0 Trackback, 2 Comment

TRACKBACK :: http://naratalk.com/trackback/267 관련글 쓰기

댓글을 달아 주세요

  1. BlogIcon aero  댓글주소  수정/삭제  댓글쓰기

    Perl에서도 고급기능을 가진 Parser들이 많습니다.
    Parse::RecDescent 와 Parse::Yapp 모듈등...
    http://search.cpan.org/dist/Parse-RecDescent/ 을 보시면
    http://search.cpan.org/perldoc?csourceparser.pl 처럼 Parse::RecDescent 모듈로 C소스 파싱하는 샘플이 이미 있네요..

    2008/11/25 13:18
  2. BlogIcon Shinnara  댓글주소  수정/삭제  댓글쓰기

    오호~ 고맙습니다. 아직 Perl을 접한지 얼마 안되다보니 부족한게 많네요~ ^^

    2008/11/25 23:21


 오후에 같은 제목의 글을 올린 후, 생일 파티가 있어서 맛나게 먹고 와서 조금 더 코드를 수정하였습니다.

 이번에 추가한 부분은 리터럴을 분리해 내는 것입니다. 프로그램을 작성하는 분은 다들 아시겠지만, 주요 값들은 상수로 지정하여 사용하는 것이 일반적입니다. 프로그램을 이루는 각 모듈의 명세를 작성할 때 이러한 리터럴 상수의 종류를 구별해 놓는 것도 좋은 방법이지요. 실제로 저희 팀에서는 코드의 앞부분에 주석으로 모듈에 사용된 변수와 리터럴 상수를 모두 적어주어야 합니다. 코드가 수정되면서 갱신되지 못한 정보가 포함되는 문제가 있지만요. 그래서 제 생각에는 시간이 지남에 따라 점차 퇴색되어가는 의미없는 주석보다는 Tool을 이용해서 후처리를 해주는 것이 더 낫지 않을까 생각해보기도 합니다.

 오후에 올린 글에 이어 같은 맥락에서 코드를 추가해보겠습니다.

 open( fileHandle, $fileName) || die "Cannot open $fileName.\n";
 
 print "\n>> LITERALS \n";
 foreach  $aLine (<fileHandle> )
 {
   # delete comments
   $aLine =~ s/(\/\*).*(\*\/)//g;
   $aLine =~ s/(\/\*).*//g;  #for not ended commont with "*/"
  
   @tokens = split( /[\s\(\)\+\*\/-=;]/ , $aLine );
   foreach $token (@tokens)
   {
       $token =~ /^[A-Z][_A-Z0-9]+[A-Z0-9]$/ && ( $token =~ /^(UINT|INT)/  || print $token, "\n");
           
   }
  
 }
 close( fileHandle );

 아직은 초기 단계라 조금은 구식의 접근법이 사용되고 있는데요, 특히나 파일을 다시 여는 부분은 나중에 꼭 고쳐져야 할 부분입니다. 허나 오늘 처음 시작한 초보라는 점을 생각해주시고..^^

 앞부분의 코드는 이전 코드와 마찬가지로 주석을 제거하는 부분입니다. 그 이후의 코드에 대해 살펴보면 제일 먼저 토큰을 분리해내는 과정을 볼 수 있습니다. 컴파일러 등의 전산과 과목을 들으신 분들은 다들 아시겠지만 토큰의 분리는 파싱에 있어 가장 먼저 이루어지는 작업입니다.

@tokens = split( /[\s\(\)\+\*\/-=;]/ , $aLine );

 split 함수를 이용해서 각 줄에 있는 토큰을 분리해서 배열에 담는 과정입니다. 토큰을 분리하는데 사용하는 구분자(Deliminator)는 코드에 있는 것처럼 Whitespace, 괄호, 수학 연산자, 등호 입니다. 글을 쓰면서 생각해보니 그외에도 많은 연산자와 구분자들이 있네요.^^ 나중에 업데이트하기로 하구요..

 위의 코드에 의해 각 문장이 토큰으로 나뉘어 집니다. 그러면 이중에서 리터럴을 찾으면 되는데, 찾는 방법은 모두 대문자로 되어 있는지를 검사하는 것입니다. 저희 팀의 코딩 규칙상 리터럴 상수만 모두 대문자로 표기하도록 되어있습니다. 이를 찾는 코드는 다음과 같습니다.

$token =~ /^[A-Z][_A-Z0-9]+[A-Z0-9]$/  && ( $token =~ /^(UINT|INT)/  || print $token, "\n");

 코드를 보면 && 와 || 를 사용했는데, Perl 이야기 의 예에서 본 재밌는 표현이라 한번 써봤습니다. 해석해보면 모두 대문자와 언더바(_)로 이루어진 문자열이 있는지 확인하고, 해당 토큰이 UINT나 INT가 아니면 프린트 하도록 되어 있습니다. UINT 와 INT 는 형 변환에 쓰이는 데 리터럴처럼 인식이 되는 문제때문에 또다시 꼼수(?)를 부렸습니다.

 주먹 구구식의 코드이긴 하지만, 생각보다 흥미로운 결과를 보여주어서 재밌게 가지고 놀 수 있었습니다. 이제 퇴근 시간이 가까워 집니다. 금요일의 오후 5시 55분은 너무도 설레이는 시간이지요. 모두들 좋은 주말 되세요.~ 다음 주에 다시 오겠습니다.








0 Trackback, 2 Comment

TRACKBACK :: http://naratalk.com/trackback/264 관련글 쓰기

댓글을 달아 주세요

  1. BlogIcon Shinnara  댓글주소  수정/삭제  댓글쓰기

    split 하는 부분에 deliminator를 조금 더 추가하였습니다.

    @tokens = split( /[\s\(\)\[\]\+\*\/-=;,&]/ , $aLine );

    여러 코드에 대해 테스트를 해보니 아직 많이 수정 보완해야겠네요^^

    모두들 즐거운 주말 되시길..

    2008/11/22 10:14
  2. BlogIcon Shinnara  댓글주소  수정/삭제  댓글쓰기

    Perl 과 관련하여 재밌는 행사가 있었네요. 조금만 빨리 Perl을 알았어도 좋았을 것을.. 관련 후기를 보고 알았네요. http://iklo.egloos.com/4570027

    2008/11/22 10:22


 아침에 Perl의 마수에 걸려들었다는 글을 썼는데요, 오후에도 그 마수에 빠져 열심히 허우적거리고 있습니다. 본래 Perl을 알아보게 된 계기가 팀에서 만들고 있는 프로그램 소스 코드를 분석해야 해서 문자열 처리가 쉬운 언어를 찾는 것이었습니다. 그래서 Perl의 기초를 읽어가면서 테스트 프로그램을 짜보았습니다. Perl 이야기를 무척이나 재미있게 읽었는데, 처음 Perl을 접하시는 분들에게는 많은 도움이 될 것입니다.

 먼저 하고자 하는 것을 간략히 소개하자면
  • 소스 코드 내에 존재하는 글로벌 변수와 리터럴 상수를 추출
  • 소스 코드에 사용된 함수 이름 (Function Call)
입니다. 위의 정보를 이용해서 해당 모듈( 저희 팀의 코딩 규칙은 하나의 c 파일에 하나의 함수만을 포함합니다)에 대한 명세를 만들어 내는 것이 소스 코드 분석의 목적입니다.

 소스 코드에 대한 분석 툴이 다양한 것으로 알고있는데, 특히 Doxygen 같은 툴을 쓰면 Call Graph까지 그려준다고 들은 바 있습니다. 아직 제대로 사용해본 적은 없습니다. 지난 번에 잠깐 써 봤는데, 생각보다 쉽지 않더군요.

 또한, 소스 코드를 분석하는 방법은 크게 두가지
  • lex, yacc 같은 parsing 툴을 이용하는 방법
  • 직접 parsing 로직을 구현하는 방법
으로 생각해보았습니다. 첫번째 방법의 경우, 관련 분야의 전문적인 Tool이라 분명 멋진 결과를 얻어 낼 수 있을 테지만, 문제는 제가 아직 그 툴에 대해 잘 모른다는 것입니다. 제대로 쓰는 데까지 꽤 많은 시간이 걸릴 것 같아 일단은 선택에서 제외하였습니다.

 남은 방법은 직접 로직을 구현하는 것인데, 이 때부터 어떤 방식으로 구현할 지를 고민하게 되었습니다. 제게 있어 가장 손쉽게 쓸수 있는 언어는 Java입니다. 좋아하기도 하고 많이 쓰기도 했죠. 하지만 파싱할 생각을 하니 그리 만만치는 않더군요. Java에서도 Regular Expression을 쓸 수 있지만, 지난 몇번의 시도에서 RE를 제대로 쓰지 못한 기억 때문인지 선뜻 Java를 선택하기 어려웠습니다. 그래서 아침에 Google 의 도움을 받았는데, 앞의 글에서 처럼 Perl의 마수에 제대로 걸려든 것이지요.

 그래서 먼저 Perl을 배워볼 겸 해서 관련 문서를 뒤적이고 인터넷 강좌를 열심히 읽었습니다. 그래봐야 1~2시간이지만요. 새로운 언어를 배우는 것이라 시간이 걸리는 것은 lex,yacc 같은 툴을 배우는 것과 별반 차이가 없을 것 같지만 그래도 툴이 아닌 새로운 언어라는게 더 매력적이라고 혼자서 위로한답니다. ^^

 하여간, 제일 먼저 작성해본 것은 function의 이름을 찾는 것입니다. 오늘 작성한 코드는 가장 기본적인 단계인 call 하는 function의 이름을 순차적으로 console에 찍는 것입니다. 좀더 나아간다면 중복되는 함수를 제거하고 깔끔하게 출력을 해주어야 겠지만 이 정도만 되어도 오늘은 충분히 만족합니다.^^

 #!/usr/bin/perl
# mycat2.pl

if ( $#ARGV < 0 )
 { die "no input file name.\n"; }
if ( $#ARGV > 0 )
 { die "Too many input file.\n"; }
 

$fileName = shift(@ARGV);

if( -d $fileName )
 {die "$fileName is a directory.\n"}
 
 -e $fileName || die "$fileName is not exist.\n";
 
 -T $fileName || die "$fileName is not a text file.\n";
 
 open( fileHandle, $fileName) || die "Cannot open $fileName.\n";
 
 #@allLines = <fileHandle>;
 #close(fileHandle);
 #print @allLines;
 
 foreach  $aLine (<fileHandle> )
 {
   # delete comments
   $aLine =~ s/(\/\*).*(\*\/)//g;
   $aLine =~ s/(\/\*).*//g;  #for not ended commont with "*/"
  
   # show function call
   if( $aLine =~ /([\w\d])+\s*\(.*\)/ ) {
      unless ($& =~ /^(if|switch|sizeof|for|while)/ )
      {
        $& =~ /\s*\(/;
        print "$` \n";
      }
   }
 }
 close( fileHandle );
 코드의 앞부분에 나오는 파일 체크하는 부분은 앞에서 말한 Perl 이야기의 강좌 부분을 참고하였습니다. 위의 코드를 실행시키면 해당 모듈 내에서 호출하는 함수의 이름이 나오게 됩니다. 좀더 자세히 코드를 살펴보겠습니다.

   # delete comments
   $aLine =~ s/(\/\*).*(\*\/)//g;
   $aLine =~ s/(\/\*).*//g;  #for not ended commont with "*/"

 위의 두 라인은 문서 내에 존재하는 주석을 제거하는 부분입니다. $aLine은 foreach 문장에서 fileHandle로 부터 1줄씩 가져오게됩니다. 따라서 처리의 기준이 1 문장입니다. 첫번째 치환문은 /* 로 시작해서 */로 끝나는 부분을 공백으로 치환합니다. 이를 통해서도 제거 되지 않는 주석이 있는데, */ 로 끝나지 않은 주석은 여전히 남게 됩니다. 그래서 아래줄을 이용해서 그러한 주석도 제거합니다. 이 경우 아래와 같은 경우에서 문제가 생길 수 있습니다.

/* this is a
    comment for you */

첫째줄의 /* this is a 는 공백으로 치환되지만, 둘째 줄의 comm... */ 은 그대로 남게 되겠지요. 하지만 팀의 코딩 Standard에서는 여러줄에 걸치 Comment 는 무조건 앞부분에 /* 를 붙이도록 되어 있어 위와 같은 문제점은 발생하지 않습니다. 그래도 범용(?)적인 처리를 위해서라면 아래와 같은 코드를 추가할 수 도 있겠네요.

$aLine =~ s/.*\*\)//g;

이 문장을 뒷부분에 추가시키면 앞의 두 구문에 의해 제거되지 않은 주석도 제거가 되겠지요? (해보지 않았습니다 ^^)

if( $aLine =~ /([\w\d])+\s*\(.*\)/ ) {


 위 if 문은 함수 호출의 형태로 쓰여진 부분을 찾습니다. 즉 알파벳과 숫자로 쓰여진 부분과 괄호로 여닫힌 부분으로 이루어진 문자열을 찾습니다. 중간에 \s* 가 들어간 것은 소스 코드를 작성하는 과정에서 함수 이름과 괄호 사이에 공백이 들어가는 경우도 있기 때문에 이를 위해서 삽입했습니다. 위와 같은 검색의 경우 몇가지 문제가 발생합니다.
 
 첫째, 현재의 처리 단위가 1줄이기때문에 함수 호출이 두줄 이상에 걸쳐진 경우 위의 조건식으로 검사가 안됩니다. 닫는 괄호 부분을 삭제하면 일정 부분 문제가 줄어들겠지요.
 둘째, if(조건문) 과 같이 마치 실제로는 함수의 호출이 아닌 경우가 존재합니다.

첫번째 문제는 다음으로 미루기로 하고, 남은 문제의 해결을 위해 아래와 같은 꼼수(?)를 부렸습니다.

unless ($& =~ /^(if|switch|sizeof|for|while)/ )

if,switch 등과 같은 예약어(?)로 시작되지 않는 경우에 한해서 다음 처리를 하도록 한  것입니다. 현재는 위와 같은 정도면 대강 처리가 가능하더군요.

마지막으로 실제 함수 이름을 찾아내서 찍어주는 부분입니다.

$& =~ /\s*\(/;
print "$` \n";

원리는 간단하죠? 즉, 함수 호출에서 인자가 쓰이는 괄호 앞부분을 찾아내어 출력하게 됩니다.

아직 가야할 길이 멀겠지만, 시작이 반이라 했으니 이제 반만 더 가면 되겠지요?

앞으로 계속해서 관련 내용을 올리도록 하겠습니다.

0 Trackback, 2 Comment

TRACKBACK :: http://naratalk.com/trackback/263 관련글 쓰기

댓글을 달아 주세요

  1. Tony  댓글주소  수정/삭제  댓글쓰기

    펄 좋죠 ^^ language for human

    2008/11/21 15:37
    • BlogIcon Shinnara  댓글주소  수정/삭제

      오홋.. 이렇게나 빨리 글을 읽으시다니.. ^^ 글 쓰고 나서 다시 읽고 있는 중이었답니다~~ Perl 좋은 것 같아요. 빨리 배우고 싶답니다~~~

      2008/11/21 15:40


 정말이지 여기 저기 찔러보며 맛보려는 이놈의 습관은 어디다 가져다 버려야 할까요? 올해에 배워보기 위해 시작한 언어가 벌써 여럿입니다. 함수형 언어에 대한 막연한 동경 때문에 시작한 Lisp. 회사 서버와 개인 노트북에 설치를 반복해가며 조금씩 코드를 작성해보고 있습니다. Scheme 과 Haskell, Scala 도 맛만 조금 보았네요. 병렬처리에서 막강하다는 Erlang. 시작은 했지만 왠지 익숙해지기엔 너무도 먼 당신이더군요. 그리고 오늘 다시 제 눈에 들어온게 Perl. 문자열 처리를 할 부분이 있어서 구글에서 문자열 처리와 관련된 언어를 찾아본게 화근이었습니다. 그냥 잘 쓰던 Java로 파싱하면 될 것을.. 결국 오전에 무지 많은 PDF 자료를 찾아서 다운받아 놨습니다. 오후에는 출력해서 책으로 만드는 일이 남았네요. 얼마나 읽게 될지 모르지만 말입니다.

 호기심이 많다는 것은 참 좋은 장점이라고 생각합니다. 하지만 시작만 해놓는 호기심은 오히려 단점이 될 수 도 있을 것입니다. "아니한 만 못하다"라는 말이 있듯이 조금 더 열심히, 그리고 깊이 파고들어야 봐야겠습니다.

 그나저나 Perl을 시작해야 할까요? 고민입니다. 말은 이렇게 하지만 이 글을 쓰고 나면 PDF 출력부터 하는 제 모습을 발견하겠지요. ^^
0 Trackback, 5 Comment

TRACKBACK :: http://naratalk.com/trackback/262 관련글 쓰기

댓글을 달아 주세요

  1. BlogIcon Shinnara  댓글주소  수정/삭제  댓글쓰기

    윈도우즈용 Perl을 다운받아서 설치를 했답니다. http://www.activestate.com/Products/activeperl/index.mhtml 여기서 다운로드 받을 수 있답니다.

    2008/11/21 11:11
  2. BlogIcon Shinnara  댓글주소  수정/삭제  댓글쓰기

    http://kldp.org/node/74080 오늘 Perl의 마수에 걸리게 만든 글 타래입니다. Perl 만세~~~

    2008/11/21 11:15
  3. BlogIcon 허니  댓글주소  수정/삭제  댓글쓰기

    일단... perl 채널로 오셔야 겠군요 ㅎㅎ

    2008/11/21 19:10
  4. BlogIcon 허니  댓글주소  수정/삭제  댓글쓰기

    커뮤니티를 목적으로 한 IRC 방입니다 여기에 오셔서 닉네임을 정하고 들어오시면 됩니다.

    http://embed.mibbit.com/?server=irc.freenode.org&channel=%23perl-kr

    2008/12/01 14:08

1 
다...... (264)
Computer/Programming (106)
Links (14)
책 읽는 즐거움 (7)
끄적임 (66)
즐거운 과학 나라 (7)
일본 (5)
Study (4)