#!/usr/mesh/bin/perl5 # # Parameter file parser # # $Id: loadparaex.pl,v 1.2 2001/02/10 15:52:50 shige Exp $ # # Update 28th Jan 2001 # defined式判定の問題 # `#'とdirective名の間のspace文字を許す # multiple lineの処理の問題 # # Update 30th Jul 1998 # OPTIMIZE # directive処理を連想配列で呼び出しするように変更 # ifディレクティブ構文ステート遷移を連想配列化 # Update 7th Jul 1998 # defineの展開において未定義だった場合の処理が変 # Update 5th Jul 1998 # includeディレクティブをサポート # pre-compile処理を追加,GetDataの実行速度改善 # その他,細々と処理の見直し # Update 4th Jun 1998 # コメント行を早めにスキップ # Update 27th Mar 1998 # データ中にスタートトークン'{'があるとそこまで識別子になってしまう # (マッチ式の定義ができない) # # Update 8th Mar 1998 # sub IfDirectiveの後ろの'()'を削除 # 型名とエイリアス名に使用できる文字を空白文字以外に拡張 # Special thank's for Sir.Nir # エイリアス名とスタートクンの間にスペースが入るとマッチしない # Update 5th Mar 1998 # GetDataでデータメンバが一つも無い場合マッチしない # dellimiterが可変でない # Update 23th Feb 1998 # GetDataで取得したデータの最後にトークン';'が付いてくる # データが一つの時もスプリットしなくてはならない # Update 6th Jan 1998 # 指定した型以外のデータのエイリアスが格納される # # 0:型チェック # 1:エイリアス取得 # 2:スタートトークンチェック # 3:デリミッター/終了トークンチェック # Update 22th Jan 1999 # 処理の最適化 # ####################################### # 拡張版LoadParameter仕様 ####################################### #ねらい # ・パラメータファイルのアクセス数削減 # 現状呼び出しの度にファイルを開き直している # ・同じ内容の定義を減らせるようにする # 特にマルチコンフィグで繰り返し同じ内容を定義している場合がある # ・コメントを行末に追加できるようにする package Params; #>>>DEBUG_CODE use strict; no strict 'refs'; #>>>DEBUG_CODE $Params'start_token='\{'; $Params'stop_token='\};'; $Params'dellmitter=';'; ####################################### # static variable #@Params'tempbuffer=(); %Params'DirectiveControl =( 'if', 'FlowControlIf', 'elseif', 'FlowControlElseIf', 'else', 'FlowControlElse', 'endif', 'FlowControlEndIf', 'include', 'ExecPreProcess', ); %Params'DefineDirective = ( 'define', 'Define', 'undef', 'UnDefine', ); ####################################### sub LoadParameter { local($Params'file,$Params'type,*ailias,*def_value)=@_; local(@Params'buffer)=(); &PreProcess($Params'file,*buffer); &GetData(*buffer,$Params'type,*ailias,*def_value); return 1; } ####################################### # pre-processが終了したものを解析する # 改行コードは残っている sub GetData { local(*buffer,$Params'type,*ailias,*def_value)=@_; local($Params'temp_line); local(@Params'filted_data); local($Params'line_buf)=""; @Params'filted_data = grep( /^($Params'type)(\s+)(.*)/,@Params'buffer); foreach(@Params'filted_data) { if( /(\S+)\s+([^\s$Params'start_token]+)\s*$Params'start_token\s*(.*)\s*$Params'dellmitter\s*$Params'stop_token/ || /(\S+)\s+([^\s$Params'start_token]+)\s*$Params'start_token\s*(.*)\s*$Params'stop_token/ ) { if( $1 eq $Params'type ) {#型チェック #エイリアス取得 push(@Params'ailias,$2); # データ列の取得 $Params'def_value{$Params'ailias[$#Params'ailias]}=$3; } else { warn "Unexcept type $Params'type!=$1\n"; } } } return 1; } ####################################### # pre-processが終了したものをPreCompileする # 改行コードは残っている sub PreCompile { local(*buffer)=@_; local(@Params'pre_buffer); local($Params'line_buf)=""; foreach(@Params'buffer) {#parameter define listを展開する chop($_); $Params'line_buf.=$_; if( $Params'line_buf =~ /(\S+)\s+([^\s$Params'start_token]+)\s*$Params'start_token\s*(.*)\s*$Params'dellmitter\s*$Params'stop_token/ || $Params'line_buf =~ /(\S+)\s+([^\s$Params'start_token]+)\s*$Params'start_token\s*(.*)\s*$Params'stop_token/ ) { push(@Params'pre_buffer,$Params'line_buf); $Params'line_buf=""; } } return(@Params'pre_buffer); } ####################################### # pre-processを実行する #ディレクティブ # #define # #undef # #if,#elseif,#else,#endif # #include # sub PreProcess { local($Params'file,*buffer)=@_; local(%Params'defined)=(); @Params'state=(1); $Params'handle="a"; # normal=0 # search endif=1 # (enter if block or return from if nest) # search else=2 &ExecPreProcess($Params'file,*buffer,*defined); @Params'buffer = &PreCompile(*buffer); return @Params'buffer; } sub ExecPreProcess { local($Params'file,*buffer,*defined) = @_; local(@Params'temp_buf,@Params'work_buf); ++$Params'handle; open($Params'handle,$Params'file)||die "Can not open $Params'file: $!\n"; # @Params'temp_buf = <$Params'handle>; #マルチプルラインの処理 # foreach( @Params'temp_buf ) while( <$Params'handle> ) { if(@Params'temp_buf > 1 && $Params'temp_buf[-1] =~ s/\\\s+$// ) { $Params'temp_buf[-1] .= $_; } else { push(@Params'temp_buf,$_); } } close($Params'handle); #コメント行削除 # @Params'work_buf = grep( !/^\s*#\s*[^deis].*/, @Params'temp_buf); @Params'work_buf = grep( /^\s*([^#]|#\s*[deis])/, @Params'temp_buf); #空行を削除 @Params'temp_buf = grep( !/^\s*$/, @Params'work_buf); foreach( @Params'temp_buf ) { #前後のスペースを削除する(改行コードもスペース文字である) s/^\s*(.*)\s*$/$1\n/; $_ = &ExtractDefine($_); if( /^#\s*(if|else|elseif|endif|include)\s+(.*)/ ) {#制御構文 # &{$Params'DirectiveControl{$1}}($2); $Params'work = $Params'DirectiveControl{$1}; do $Params'work($2,*buffer,*defined); next; } elsif( $Params'state[$#Params'state] <= 1 ) { if( /^#\s*(define|undef)\s+(.*)/ ) { # &{ $Params'DefineDirective{$1} }($2); $Params'work = $Params'DefineDirective{$1}; do $Params'work($2); next; } elsif(/^#/) {# comment line next; } else { push(@Params'buffer,$_); } } } --$Params'handle; } sub ExtractDefine { ($_)=@_; s/(%\w+%)/&GetDefineString($1)/eg; # print $_; return $_; } sub GetDefineString { local($_) = @_; m/%(\w+)%/; local($Params'key_string) = $1; return($Params'defined{$Params'key_string}) if( defined($Params'defined{$Params'key_string}) ); return($ENV{$Params'key_string}) if( defined($ENV{$Params'key_string})); return($_); } # 0: normal # 1:Internal True block if EXPR # 2:Search Else block(causes if EXPR is False) # 3:Search #endif (already end True block) # search endif=1: Internal True block if EXPR # and search block end # (enter if block or return from if nest) # search else=2: if EXPR is False and search block end # # for #else and #elseif %Params'NextState = ( 0, "Unexepect #else/elseif\n", 1, 3, 2, 1, 3, 3, ); sub FlowControlIf { local($Params'expr)=@_; if( $Params'state[$#Params'state] <= 1 ) { if( &IfDirective($Params'expr) ) { push(@Params'state,1); } else { push(@Params'state,2) } } else { push(@Params'state,3); } } sub FlowControlElseIf { local($Params'expr)=@_; if( defined($Params'NextState{$Params'state[$#Params'state]} ) ) { if( $Params'state[$#Params'state] == 2 ) { if( &IfDirective($Params'expr) ) { $Params'state[$#Params'state]=1; } } else { $Params'state[$#Params'state] = $Params'NextState{$Params'state[$#Params'state]}; die $Params'state[$#Params'state] unless ( $Params'state[$#Params'state] =~ /[0-9]+/ ); } } else { die "Unknown state $Params'state[$#Params'state]\n"; } } sub FlowControlElse { local($Params'expr)=@_; if( defined($Params'NextState{$Params'state[$#Params'state]} ) ) { $Params'state[$#Params'state] = $Params'NextState{$Params'state[$#Params'state]}; die $Params'state[$#Params'state] unless ( $Params'state[$#Params'state] =~ /[0-9]+/ ); } else { die "Unknown state $Params'state[$#Params'state]\n"; } } sub FlowControlEndIf { local($Params'expr)=@_; if( $Params'state[$#Params'state]==0 ) { die "Unexepect #endif\n"; } pop(@Params'state); } ####################################### # Define/Undefine directive sub Define {# マクロ定義 local($_)=@_; m/^(\w+)\s*(.*)/; $Params'defined{$1}=$2; } sub UnDefine { local($_)=@_; /^(\w+)/; delete $Params'defined{$1}; } ####################################### # Flow contorol directive sub IfDirective { local($_)=@_; local($Params'bool); local($Params'templine); s/defined\(\s*(\w+)\s*\)/(defined(\$Params'defined{"$1"}) || defined(\$ENV{"$1"}))/g; eval '$Params\'bool='."$_";warn $@ if $@; if( $Params'bool ) { $Params'state=1; return 1; } else { $Params'state=2; return 0; } } 1; #return true