片目猫Tools

プログラマ片目猫の送る便利ツール集

class_sceleton.pl

2008-01-21 21:18:15 | ツール
#!c:\perl\bin\perl.exe
#==============================================================================
#  class_sceleton                              Copyright 2008 katameneko
#------------------------------------------------------------------------------
#【概要】
# クラス定義からスケルトンを生成する。
#
#【使用方法】
# perl class_sceleton.pl ヘッダファイル名
#
#【出力】
# ヘッダファイルに記載されている関数の解析情報およびスケルトン
#
#【カスタマイズ】
# setting.plでの定数定義を変更することで、下記項目を変更可能。
# ・関数のキーワード(virtual,staticなど)の追加・変更。
# ・戻り値の型に対し、スケルトンで返却する値。
#
#【制約事項】
# ・ヘッダファイルのはじめに存在する1クラスしか処理できない。
# ・マクロ(#define,#include,#if,#ifdef...)には対応していない。
# ・\での行連結に対応していない。
#==============================================================================
#【改訂履歴】
#==============================================================================
require 'jcode.pl';
require 'setting.pl';	# 定数定義

#==============================================================================
# 変数定義
#==============================================================================
$header = $ARGV[0];		# ヘッダファイル名
@honbun;				# ファイルの内容
$class_name = "";		# クラス名
@func_def;				# クラス内の関数定義
$nest_num = 0;			# {}のネスト数
$state = "out";			# 状態(class外、クラス内探し中、クラス内)
@class_sceleton_list;	# 全クラスのスケルトン情報
$debug_info = "";		# デバッグ用情報。

#==============================================================================
# メインルーチン
#==============================================================================
# 引数チェック
if ( $header eq undef )
{
	&sjprint("ヘッダファイルが指定されていません。\n");
	&sjprint("perl class_sceleton.pl ヘッダファイル名\n");
	exit;
}

open (IN,"$header") || die &sjprint("$headerをオープンできません。\n");

# ファイル読み込み&整形
$tmp = "";
foreach $line (<IN>)
{
	$line =~ s/\t/ /g;		# タブ⇒スペース
	$line =~ s/ +/ /g;		# 連続スペースを一つに
	$line =~ s/^ //g;		# 先頭スペース削除

	$line =~ s/\/\/.*\n/\n/gi;	# //コメント削除 
	$line =~ s/\#.*\n/\n/g;	# #以降削除。
	$line =~ s/: /:/g;		# :後のスペース削除
	$line =~ s/\; /\;/g;	# ;後のスペース削除

	chomp($line);	# 改行文字削除
	$tmp .= $line;
}
$tmp = &del_comment($tmp);

# 処理しやすいように再度改行文字を入れる。
$tmp =~ s/\;/\;\n/g;
$tmp =~ s/\:/\:\n/g;
$tmp =~ s/\{/\n\{\n/gi;
$tmp =~ s/\}/\n\}\n/gi;
$tmp =~ s/^class /\nclass /g;

# sjprint ($tmp);	#デバッグ:成形後のヘッダファイル出力。

@honbun = split(/\n/,$tmp);
#-----------------------------------------------------------------------------
# ファイル情報を状態によって処理する。
#-----------------------------------------------------------------------------
# out	:クラス外
# search:classキーワード後、{に到達するまで
# in	:クラス内
#-----------------------------------------------------------------------------
foreach $str (@honbun)
{
	if ($state eq "out")
	{
		# クラス外の場合
		# クラス名を取得する。
		if ( ( $str =~ /class/) & ($str !~ /\;/ ) )
		{
			@list = split(/ /,$str);
			$cnt = @list;
			for($i=0;$i<$cnt;$i++)
			{
				if ($list[$i] eq "class")
				{
					$class_name = $list[$i+1];
					$debug_info .= "$class_name";
					last;
				}
			}
			$state = "search";
		}
	}
	elsif ($state eq "search")
	{
		# クラス内探し中の場合
		# '{'が見つかったらクラス内と判定。
		if ($str =~ /{/)
		{
			$nest_num++;
			$state = "in";
		}
	}
	elsif ($state eq "in")
	{
		# クラス内の場合
		# '{'が見つかったらネストを加算。
		if ($str =~ //)
		{
			$nest_num--;
			if ($nest_num == 0)
			{
				$state = "out";
				push( @class_sceleton_list,&make_sceleton(($class_name,@func_def)) );
				
				# 変数初期化
				undef(@func_def);
				next;
			}
		}
		# ネストが1の場合のみ、関数を探す。
		if ($nest_num == 1)
		{
			# ()が存在すれば、関数と判定。
			if( ($str =~ /\(/) & ($str =~ /\)/) )
			{
				push(@func_def,$str);
			}
		}
	}
}
close (IN);

#-------------------------
# 結果表示   
#-------------------------

if ($class_name ne "")
{
	print "\#include \"$header\"\n\n";
	foreach(@class_sceleton_list)
	{
		sjprint ($_);
		print "\n\n";
	}
}
else
{
	&sjprint("クラスは見つかりませんでした。\n");
}


# デバッグ情報表示
#&sjprint ($debug_info);

exit;	# メインルーチン終了
#==============================================================================
# 関数定義
#==============================================================================
#------------------------------------------------------------------------------
# Shift-JISで標準出力   
#------------------------------------------------------------------------------
sub sjprint
{
	print jcode::sjis("@_[0]");
}
#------------------------------------------------------------------------------
# c/c++コメント削除
#------------------------------------------------------------------------------
# 引数:ソースコード全体を連結した文字列。行末には改行文字(\n)が入っていること
#------------------------------------------------------------------------------
sub del_comment
{
	$str = @_[0];

	#--------------------------------------
	# //コメント削除 
	$str =~ s/\/\/.*\n/\n/gi;

	#--------------------------------------
	# /**/コメント削除 
	# 改行文字変換 \n -> !@@!
	$str =~ s/@@/@@@@/gi;
	$str =~ s/\n/!@@!/gi;

	# 改行文字追加 /**/が1行に1つになるように。
	$str =~ s/\*\//\*\/\n/g;
	
	# /**/コメント削除の実行
	$str =~ s/\/\*.*\*\/\n//gim; 

	# 改行文字変換戻し !@@! -> \n
	$str =~ s/!@@!/\n/gi;
	$str =~ s/@@@@/@@/gi;

	return $str;
}
#------------------------------------------------------------------------------
# スケルトン生成
#------------------------------------------------------------------------------
# 引数		:$クラス名,@関数定義リスト
# Global変数:%keyword_hash キーワードのハッシュ
#			  %ret_type_hash 戻り値の型のハッシュ
#------------------------------------------------------------------------------
sub make_sceleton()
{
	#引数取得
	my($class_name,@func_def) = @_;

	# 変数定義
	# クラススケルトン情報
	my($class_sceleton);
	
	# デバッグ用情報。
	my($debug_info);
	
	foreach(@func_def)
	{
		# 変数定義
		my(@keyword);
		my($ret_type);
		my($func_name);
		my($arg_list);
		
		# *,&の整形処理。型にくっつけ、関数or引数との間を空ける。
		$_ =~ s/\*/\* /gi;			# *の後ろにスペースを入れる。
		$_ =~ s/\&/\& /gi;			# &の後ろにスペースを入れる。
		$_ =~ s/ +/ /gi;			# 連続スペースを1つに。
		$_ =~ s/ \*/\*/gi;			# *の前のスペースは消す。
		$_ =~ s/ \&/\&/gi;			# &の前のスペースは消す。
		
		$_ =~ /\(/;
		$pre  = $`;
		$post = $';

		$post =~ s/\).*//;	# ()のネストを考慮していない。問題ない?

		# (より前を処理する。
		@pre_list = split(/ /,$pre);
		$num = @pre_list;
		
		# キーワードチェック
		while ( $keyword_hash{ $pre_list[0] } ne undef )
		{
			$key = shift( @pre_list );
			if ( $keyword_hash{ $key } == 1 )
			{
				push( @keyword, "$key " );
			}
			$num--;
		}
		
		#コンストラクタ/デストラクタチェック
		if ($num == 1)
		{
			if ( $pre_list[0] eq $class_name )
			{
				$debug_info .= "コンストラクタ\n";
				$func_name = "$class_name";
			}
			elsif( $pre_list[0] eq "~$class_name" )
			{
				$debug_info .= "デストラクタ\n";
				$func_name = "~$class_name";
			}
			else
			{
				die &sjprint("!エラー:不正な関数名が指定されました。: $pre_list[0]\n");
			}
		}
		elsif ($num == 2)
		{
			$debug_info .= "戻り値の型:$pre_list[0]\n";
			$debug_info .= "関数名:$pre_list[1]\n";
			$ret_type = "$pre_list[0] ";
			$func_name = $pre_list[1];
		}
		else
		{
			die &sjprint("!エラー:関数定義が不正です。: 。@pre_list\n");
		}

		# (より後を処理する。
		@func_args = split(/\,/,$post);

		$func_arg_num = 0;
		foreach $func_arg(@func_args)
		{
			$func_arg_num++;
			$debug_info .= "引数$func_arg_num:$func_arg\n";
		}

		#--------------------------------------------------------------------------
		# スケルトン生成
		#--------------------------------------------------------------------------
		$arg_list = "";
		foreach $func_arg(@func_args)
		{
			$func_arg =~ s/=.+//gi;
			$arg_list .= "$func_arg,";
		}
		chop($arg_list);

		$class_sceleton .= "@keyword$ret_type$class_name\:\:$func_name\($arg_list\)\n\{\n\t//## begin $class_name\:\:$func_name\n";

		$ret_type =~ s/ //gi;

		#--------------------------------------------------------------------------
		# 戻り値の出力。
		#--------------------------------------------------------------------------
		# コンストラクタ、デストラクタ、void型 ⇒ 戻り値なし。
		# ポインタ ⇒ return NULL;
		# 登録されている型 ⇒ 対応する値
		# 参照戻り値 ⇒ ポインタ(dummy)宣言、return *dummy。※Warnningとなる。
		# その他 ⇒ 戻り値の型の変数(dummy)宣言、return dummy;
		#--------------------------------------------------------------------------
		if( ( $func_name ne "$class_name" ) & 
			( $func_name ne "~$class_name" ) & 
			( $ret_type ne "void" ) )
		{
			if($ret_type =~ /\*/ )
			{
				$class_sceleton .=  "\n\treturn NULL\;";
			}
			else
			{
				if( $ret_type_hash{ $ret_type } ne undef)
				{
					$class_sceleton .= "\n\treturn $ret_type_hash{ $ret_type }\;";
				}
				else
				{
					if ( $ret_type =~ /\&/ )
					{
						$ret_type_ = $ret_type =~ s/\&//gi;
						$class_sceleton .=  "\n\t$ret_type* dummy\;\n\n\treturn *dummy\; "
					}
					else
					{
						$class_sceleton .=  "\n\t$ret_type dummy\;\n\n\treturn dummy\; "
					}
				}
			}
		}
		$class_sceleton .=  "\n\n\t//## end $class_name\:\:$func_name\n}\n";
	}
	# &sjprint($debug_info);		# デバッグ情報表示。
	return $class_sceleton;
}
__END__
#==============================================================================
# 以降コメント。
#==============================================================================