#//USAGE:
#//	gawk.exe -f MkLotTbl.awk 入力ファイル > 出力ファイル

#//*****************************************************************************
#//エラーメッセージを表示して終了します。
function die(s) {
	print "### " s > "/dev/stderr"
	exit (_die_exit = 1)
}

#//{{2014/08/25削除:「抽選表」形式変更のために、古いコードを削除しました。古いコードは2014/08/24まで問題無く動作していたので、参考用としてコメント化したまま残しておきます。
#//	
#//	#//*****************************************************************************
#//	#//テーブルを開始します。
#//	function BeginTable() {
#//		#//確実に、テーブルを終了します。
#//		EndTable()
#//		#//テーブル名が重複定義されていないことを確認します。
#//		if($1 in aTableName) {
#//			die(FILENAME "(" FNR ")" "テーブル名が重複定義されています。")
#//		}
#//		aTableName[$1] = 1
#//		#//テーブル名を格納します。
#//		TableName = $1
#//		#//テーブルデータをクリアします。
#//		NumCols = 0
#//		NumRows = 0
#//		delete TableData
#//	#//{{2014/08/22追加:「グループヘッダ付き抽選表」対応
#//		NumGrps = 0
#//		delete GrpCols
#//	#//}}2014/08/22追加:「グループヘッダ付き抽選表」対応
#//	}
#//	
#//	#//*****************************************************************************
#//	#//テーブルを終了します。
#//	function EndTable() {
#//		#//テーブルが開始していたら…
#//		if(TableName != "") {
#//			#//少なくとも一行以上のデータが必要です。
#//			#//- 最小のテーブル構成は、{テーブル名＋抽選列idの行}です。当選値と重み値の行は、一行も無くても構いません。
#//			if(NumRows == 0) {
#//				die(FILENAME "(" FNR ")" "データの無いテーブルが有ります。少なくとも一行以上のデータが必要です。")
#//			}
#//			#//二列目以降を抽選列と見なします。各抽選列について…
#//			tmpSize = 0
#//			tmpStr = ""
#//			for(Col = 2; Col <= NumCols; Col++) {
#//				#//重み値が0でない数を数え、当選値と(重み値-1)を文字列に追加します。
#//				ColId = TableData[Col,1]	#//抽選列id
#//				ColSize = 0			#//重み値が0でない数
#//				ColStr = ""			#//当選値と(重み値-1)を追加するための文字列
#//				#//一行目は、抽選列idの行です。当選値と重み値の行は、二行目から開始します。
#//				#//- 最小のテーブル構成は、{テーブル名＋抽選列idの行}です。当選値と重み値の行は、一行も無くても構いません。
#//				#//  従って、以下のループは一回も通らない可能性も有ります。
#//				for(Row = 2; Row <= NumRows; Row++) {
#//					#//一列目を当選値と見なします。
#//					HitVal = TableData[1,Row]
#//					#//重み値を取得します。
#//					Weight = TableData[Col,Row]
#//					#//重み値が0でなければ…
#//					if(Weight != 0) {
#//						#//重み値が0でない数を増やします。
#//						ColSize++
#//						#//当選値と(重み値-1)を文字列に追加します。
#//						ColStr = ColStr "," HitVal ",(" Weight ")-1"	#//重み値が、数式,又は,シンボルで定義されていた場合、ここで(Weight-1)の値を計算できないため、文字列"-1"を付与することにしました。
#//					}
#//				}
#//				#//抽選列id、重み値が0でない数、及び、当選値と(重み値-1)の文字列を追加します。
#//				tmpSize += (2 + ColSize * 2)
#//				tmpStr = tmpStr "\n" ColId "," ColSize ColStr ","
#//			}
#//	#//{{2014/08/22変更:「グループヘッダ付き抽選表」対応
#//	#//		#//抽選表を重複無く登録します。
#//	#//		if(!(tmpStr in aTable)) {
#//	#//			aSource[length(aSource)+1] = (NumCols-1) ",//" TableIndex tmpStr	#//ソース文字列を追加します。
#//	#//			aTable[tmpStr] = TableIndex
#//	#//			TableIndex += (1 + tmpSize)
#//	#//		}
#//	#//↓2014/08/22変更:「グループヘッダ付き抽選表」対応
#//			#//抽選列数を前方に追加します。
#//			tmpSize++
#//			tmpStr = (NumCols-1) "," tmpStr
#//			#//グループ列数を前方に追加します。
#//			for(Grp = NumGrps; Grp >= 1; Grp--) {
#//				tmpSize++
#//				tmpStr = GrpCols[Grp] "," tmpStr
#//			}
#//			#//グループ数を前方に追加します。
#//			tmpSize++
#//			tmpStr = NumGrps "," tmpStr
#//			#//抽選表を重複無く登録します。
#//			if(!(tmpStr in aTable)) {
#//				aSource[length(aSource)+1] = "//" TableIndex "\n" tmpStr		#//ソース文字列を追加します。
#//				aTable[tmpStr] = TableIndex
#//				TableIndex += tmpSize
#//			}
#//	#//}}2014/08/22変更:「グループヘッダ付き抽選表」対応
#//			aHeader[length(aHeader)+1] = "#define LotTbl_" TableName " " aTable[tmpStr]	#//ヘッダ文字列を追加します。
#//		}
#//		#//テーブル名をクリアします。
#//		TableName = ""
#//		#//テーブルデータをクリアします。
#//		NumCols = 0
#//		NumRows = 0
#//		delete TableData
#//	#//{{2014/08/22追加:「グループヘッダ付き抽選表」対応
#//		NumGrps = 0
#//		delete GrpCols
#//	#//}}2014/08/22追加:「グループヘッダ付き抽選表」対応
#//	}
#//	
#//}}2014/08/25削除:「抽選表」形式変更のために、古いコードを削除しました。古いコードは2014/08/24まで問題無く動作していたので、参考用としてコメント化したまま残しておきます。

#*******************************************************************************
#//開始時の処理を行います。
BEGIN {
	#//フィールドセパレータをタブに設定します。(CSV入力)
	FS = "\t"
	#//ソース文字列を出力する時に、[0]は無効要素として明示的に出力します。有効要素は[1]から登録します。
	TableIndex = 1
}

#//{{2014/08/25削除:「抽選表」形式変更のために、古いコードを削除しました。古いコードは2014/08/24まで問題無く動作していたので、参考用としてコメント化したまま残しておきます。
#//	
#//	#*******************************************************************************
#//	#//空行以外を処理します。
#//	NF != 0 {
#//		#//各フィールドの先頭と末尾の空白を削除します。
#//		for(Col = 1; Col <= NF; Col++) {
#//			gsub(/(^[[:blank:]]+)|([[:blank:]]+$)/, "", $Col)
#//		}
#//		#//フィールド数が1ならば…
#//		if(NF == 1) {
#//	#//{{2014/08/22変更:「グループヘッダ付き抽選表」対応
#//	#//		#//テーブル名と見なし、テーブルを開始します。
#//	#//		BeginTable()
#//	#//↓2014/08/22変更:「グループヘッダ付き抽選表」対応
#//			#//テーブルが開始していれば…
#//			if(TableName == "") {
#//				#//テーブル名と見なし、テーブルを開始します。
#//				BeginTable()
#//			#//テーブルが開始していなければ…
#//			} else {
#//				#//テーブルデータが無ければ…
#//				if(NumCols == 0) {
#//					#//グループ列数です。
#//					NumGrps++
#//					GrpCols[NumGrps] = $1
#//				#//テーブルデータが有れば…
#//				} else {
#//					#//テーブル名と見なし、テーブルを開始します。
#//					BeginTable()
#//				}
#//			}
#//	#//}}2014/08/22変更:「グループヘッダ付き抽選表」対応
#//		#//フィールド数が2以上ならば…
#//		} else {
#//			#//テーブルデータ行です。テーブルが開始していなくてはなりません。
#//			if(TableName == "") {
#//				die(FILENAME "(" FNR ")" "テーブル開始前のデータ定義があります。データよりも先に、テーブル名が必要です。")
#//			}
#//			#//テーブル開始後、一行目のテーブルデータ行ならば…
#//			if(NumCols == 0) {
#//				#//列数を記憶します。
#//				NumCols = NF
#//			#//テーブル開始後、二行目以降のテーブルデータ行ならば…
#//			} else {
#//				#//列数が同じであることを確認します。
#//				if(NF != NumCols) {
#//					die(FILENAME "(" FNR ")" "列数が異なります。一つのテーブル内の全ての行の列数は、同じでなくてはなりません。")
#//				}
#//			}
#//			#//行数を増やします。
#//			NumRows++
#//			#//テーブルデータを格納します。
#//			for(Col = 1; Col <= NumCols; Col++) {
#//				TableData[Col,NumRows] = $Col
#//				#//抽選表の中の全ての要素は、空であってはなりません。
#//				if($Col == "") {
#//					die(FILENAME "(" FNR ")" Col "列目に、不正な要素の定義があります。抽選表の中の全ての要素は、空であってはなりません。")
#//				}
#//				#//一行目は、抽選列idの行です。
#//				if(NumRows == 1) {
#//					if(Col == 1) {
#//						#//一行目の一列目のセルは無視します。何を書いても構いません。
#//						#//** no job **
#//					} else {
#//						#//一行目の二列目以降は、抽選列idです。数値、数式、又は、シンボルです。
#//						#//Awkスクリプトで、数値,数式,又は,シンボルであることを確認するのは難しいため、ここでは確認せず、出力データのコンパイルエラーで判断することにしました。
#//					}
#//				#//二行目以降は、当選値と重み値の行です。
#//				} else {
#//					if(Col == 1) {
#//						#//二行目以降の一列目のセルは、当選値です。整数式、又は、シンボルです。
#//						#//Awkスクリプトで、数値,数式,又は,シンボルであることを確認するのは難しいため、ここでは確認せず、出力データのコンパイルエラーで判断することにしました。
#//					} else {
#//						#//二行目以降の二列目以降のセルは、重み値です。整数式、又は、シンボルです。
#//						#//Awkスクリプトで、数値,数式,又は,シンボルであることを確認するのは難しいため、ここでは確認せず、出力データのコンパイルエラーで判断することにしました。
#//					}
#//				}
#//			}
#//		}
#//	}
#//	
#//}}2014/08/25削除:「抽選表」形式変更のために、古いコードを削除しました。古いコードは2014/08/24まで問題無く動作していたので、参考用としてコメント化したまま残しておきます。

#//*****************************************************************************
#//終了時の処理を行います。
END {
	#//die()経由ならば、ENDのアクションを実行せずに終了させます。
	#//- Awkの仕様: 引数付きのexit文を最初に実行し、その後で引数無しのexit文を実行すると、その前に使った引数が終了ステータスとして使われる。
	if(_die_exit) { exit }
	#//確実に、テーブルを終了します。
	EndTable()
	#//ヘッダ文字列を出力します。
	print  "//{{HEADER"
	for(i = 1; i <= length(aHeader); i++) { print aHeader[i] }
	print  "#define LotTbl_SIZE " TableIndex
	print "extern const unsigned short TBL_LotTbl[LotTbl_SIZE];"
	print  "//}}HEADER"
	#//ソース文字列を出力します。
	print  "//{{SOURCE"
	print  "#include \"app.h\""
	print  "const unsigned short TBL_LotTbl[]={0,"
	for(i = 1; i <= length(aSource); i++) { print aSource[i] }
	print  "};"
	print  "//}}SOURCE"
}

#//{{2014/08/25追加:これ以降は、2014/08/25「抽選表」形式変更後に追加した、新しいコードです。}}

#//*****************************************************************************
#//テーブルを開始します。
function BeginTable() {
	#//確実に、テーブルを終了します。
	EndTable()
	#//テーブル名が重複定義されていないことを確認します。
	if($1 in aTableName) {
		die(FILENAME "(" FNR ")" "テーブル名が重複定義されています。")
	}
	aTableName[$1] = 1
	#//テーブル名を格納します。
	TableName = $1
	#//テーブルデータをクリアします。
	NumBlks = 0
	delete IdSize		#//   IdSize[NumBlks]
	delete IdDupChk		#// IdDupChk[Id[*,*]]
	delete NumCols		#//  NumCols[NumBlks]
	delete NumRows		#//  NumRows[NumBlks]
	delete IdData		#//   IdData[NumBlks,NumCols,IdSize]
	delete Id		#//       Id[NumBlks,NumCols]
	delete TableData	#//TableData[NumBlks,NumCols,NumRows]
}

#//*****************************************************************************
#//テーブルを終了します。
function EndTable() {
	#//テーブルが開始していたら…
	if(TableName != "") {
		#//少なくとも一個以上のブロックが必要です。
		if(NumBlks == 0) { die(FILENAME "(" FNR ")" "ブロックの無いテーブルが有ります。少なくとも一個以上のブロックが必要です。") }
		sumCols = 0
		tmpSize = 0
		tmpStr = ""
		#//各ブロックについて…
		for(Blk = 1; Blk <= NumBlks; Blk++) {
			#//少なくとも一行以上のデータが必要です。
			if(NumRows[Blk] == 0) { die(FILENAME "(" FNR ")" "データの無いブロックが有ります。少なくとも一行以上のデータが必要です。") }
			#//抽選列数の合計を増やします。
			sumCols += (NumCols[Blk] - 1)	#//抽選列は二列目から
			#//二列目以降を抽選列と見なします。各抽選列について…
			for(Col = 2; Col <= NumCols[Blk]; Col++) {
				#//重み値が0でない数を数え、当選値と(重み値-1)を文字列に追加します。
				ColId = Id[Blk,Col]	#//抽選列のid。"id1,id2,...,idN,"という文字列です。(IdSize[]=0)ならば、""です。
				ColSize = 0		#//重み値が0でない数
				ColStr = ""		#//当選値と(重み値-1)を追加するための文字列
				#//各データについて…
				for(Row = 1; Row <= NumRows[Blk]; Row++) {
					#//一列目を当選値と見なします。
					HitVal = TableData[Blk,1,Row]
					#//重み値を取得します。
					Weight = TableData[Blk,Col,Row]
					#//重み値が0でなければ…
					if(Weight != 0) {
						#//重み値が0でない数を増やします。
						ColSize++
						#//当選値と(重み値-1)を文字列に追加します。
						ColStr = ColStr "," HitVal "," (Weight - 1)
					}
				}
				#//抽選列のid,重み値が0でない数,及び,当選値と(重み値-1)の文字列を、後方に追加します。
				tmpSize += (IdSize[1] + 1 + ColSize * 2)	#//IdSize[]は全て同じ値なので、[1]を参照することにした。
				tmpStr = tmpStr "\n" ColId ColSize ColStr ","
			}
		}
		#//抽選列数の合計と抽選列のid行数を、前方に追加します。
		tmpStr = sumCols "," IdSize[1] "," tmpStr			#//IdSize[]は全て同じ値なので、[1]を参照することにした。
		tmpSize += 2
		#//抽選表を重複無く登録します。
		if(!(tmpStr in aTable)) {
			aSource[length(aSource)+1] = "//" TableIndex "\n" tmpStr		#//ソース文字列を追加します。
			aTable[tmpStr] = TableIndex
			TableIndex += tmpSize
		}
		aHeader[length(aHeader)+1] = "#define LotTbl_" TableName " " aTable[tmpStr]	#//ヘッダ文字列を追加します。
	}
	#//テーブル名をクリアします。
	TableName = ""
	#//テーブルデータをクリアします。
	NumBlks = 0
	delete IdSize		#//   IdSize[NumBlks]
	delete IdDupChk		#// IdDupChk[Id[*,*]]
	delete NumCols		#//  NumCols[NumBlks]
	delete NumRows		#//  NumRows[NumBlks]
	delete IdData		#//   IdData[NumBlks,NumCols,IdSize]
	delete Id		#//       Id[NumBlks,NumCols]
	delete TableData	#//TableData[NumBlks,NumCols,NumRows]
}

#*******************************************************************************
#//空行以外を処理します。
NF != 0 {
	#//各フィールドの先頭と末尾の空白を削除します。
	for(Col = 1; Col <= NF; Col++) {
		gsub(/(^[[:blank:]]+)|([[:blank:]]+$)/, "", $Col)
	}
	#//フィールド数が1ならば…
	if(NF == 1) {
		#//テーブル名と見なし、テーブルを開始します。
		BeginTable()
	#//フィールド数が2以上ならば…
	} else {
		#//抽選列のid行,又は,データ行です。テーブルが開始していなくてはなりません。
		if(TableName == "") { die(FILENAME "(" FNR ")" "テーブルの開始前に、抽選列のid行,又は,データ行が有ります。先にテーブル名が必要です。") }
		#//テーブル開始直後の抽選列のid行,又は,データ行ならば、ブロック数を増やす。
		if(NumBlks == 0) { NumBlks++ }
		#//一列目が「id」,又は,「id(0文字以上の説明文字列)」ならば、抽選列のid行です。
		if($1 ~ /^id(\(.*\))?$/) {
			#//データ行出現後の抽選列のid行ならば、ブロック数を増やす。
			if(NumRows[NumBlks] != 0) { NumBlks++ }
			#//抽選列のid行の最大列数を更新します。
			if(NF > NumCols[NumBlks]) { NumCols[NumBlks] = NF }
			#//抽選列のid行数を増やします。
			IdSize[NumBlks]++
			#//抽選列のid行を格納します。
			for(Col = 2; Col <= NF; Col++) {				#//抽選列のidは二列目から
				IdData[NumBlks,Col,IdSize[NumBlks]] = $Col
				#//二列目以降のセルは、抽選列のid,又は,空です。抽選列のidは、整数値,整数式,又は,シンボルです。
				#//Awkスクリプトで、整数値,整数式,又は,シンボルであることを確認するのは難しいため、ここでは確認せず、出力データのコンパイルエラーで判断することにしました。
			}
		#//一列目が「id」,又は,「id(0文字以上の説明文字列)」でなければ、データ行です。
		} else {
			#//抽選列のid行数が0ならば、IdSize[NumBlks]が未定義のまなので、ここで0にします。
			IdSize[NumBlks] = IdSize[NumBlks] + 0
			#//ブロック開始後の最初のデータ行ならば…
			if(NumRows[NumBlks] == 0) {
				#//二個目以降のブロックの抽選列のid行数は、最初のブロックの抽選列のid行数と同じでなくてはなりません。
				if((NumBlks != 1) && (IdSize[NumBlks] != IdSize[1])) { die(FILENAME "(" FNR ")" "二個目以降のブロックの抽選列のid行数は、最初のブロックの抽選列のid行数と同じでなくてはなりません。") }
				#//抽選列のidを決定します。
				delete IdNow							#//抽選列のidの引き継ぎ用
				for(Row = 1; Row <= IdSize[NumBlks]; Row++) { IdNow[Row] = 0 }	#//抽選列のidが空ならば、前列の抽選列のidを引き継ぐ。最初の列の抽選列のidが空である場合、初期値は0とします。
				for(Col = 2; Col <= NumCols[NumBlks]; Col++) {			#//抽選列のidは二列目から
					tmpStr = ""											#//┐
					for(Row = 1; Row <= IdSize[NumBlks]; Row++) {							#//│
						if(IdData[NumBlks,Col,Row] != "") { IdNow[Row] = IdData[NumBlks,Col,Row] }		#//│抽選列のidを連結する。IdSize=0であった場合は、tmpStr=""となる。
						tmpStr = tmpStr IdNow[Row] ","								#//│
					}												#//┘
					if(tmpStr in IdDupChk) { die(FILENAME "(" FNR ")" Col "列目の抽選列のidが重複しています。") }	#//┬連結した抽選列のidが、このテーブルのブロック全体で一意でなくてはならない。
					IdDupChk[tmpStr] = 1					#//────────────────────┘
					Id[NumBlks,Col] = tmpStr				#//連結した抽選列のidを格納する
				}
			}
			#//抽選列のid行数が0ならば、抽選列が1列だけの抽選表と見なします。
			#// - 抽選列が1列だけの抽選表は、抽選列のidを付与してもしなくても構いませんが、付与しない方がアプリケーションからの使用方法が簡単になります。
			#//   具体的な使用方法は、clip/cliplot.hの、LOT_draw_table_*()の[note]を参照してください。
			if(NumCols[NumBlks] == 0) { NumCols[NumBlks] = 2 }		#//当選値と抽選列1列の合計2列
			#//データ行の列数が、抽選列id行の最大列数に一致していることを確認する。
			if(NF != NumCols[NumBlks]) { die(FILENAME "(" FNR ")" "列数が不正です。データ行の列数は、抽選列id行の最大列数に一致していなくてはなりません。") }
			#//データの行数を増やします。
			NumRows[NumBlks]++
			#//データを格納します。
			for(Col = 1; Col <= NumCols[NumBlks]; Col++) {
				TableData[NumBlks,Col,NumRows[NumBlks]] = $Col
				#//データ行の全ての要素は、空であってはなりません。
				if($Col == "") { die(FILENAME "(" FNR ")" Col "列目の要素が不正です。データ行の全ての要素は、空であってはなりません。") }
				if(Col == 1) {
					#//一列目のセルは、当選値です。当選値は、整数値,整数式,又は,シンボルです。
					#//Awkスクリプトで、整数値,整数式,又は,シンボルであることを確認するのは難しいため、ここでは確認せず、出力データのコンパイルエラーで判断することにしました。
				} else {
					#//二列目以降のセルは、重み値です。重み値は、整数値です。
					#//整数式やシンボルは不可とします。当スクリプトのEndTable()関数にて、重み値が0かを判断する必要が有るためです。
					if($Col !~ /^(0|[1-9][0-9]*)$/) { die(FILENAME "(" FNR ")" Col "列目の要素が不正です。重み値は、整数値でなくてはなりません。") }
				}
			}
		}
	}
}

