2007年アジア予選H問題(Bug Hunt)(2007/11/14(水) 00:05:05)
2007年アジア予選H問題を解いてみた。
以下のBNFのプログラムのインタプリタを作成し、何行目にバグがあるかを見つける問題。
<program> ::= <declaration> | <program><declaration> | <program><assignment> <declaration> ::= <array name>[<number>]<new line> <assignment> ::= <array name>[<expression>]=<expression><new line> <expression> ::= <number> | <array name>[<expression>] <number> ::= <digit> | <digit positive><digit string> <digit string> ::= <digit> | <digit><digit string> <digit positive> ::= 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 <digit> ::= 0 | <digit positive> <array name> ::= a | b | c | d | e | f | g | h | i | j | k | l | m | n | o | p | q | r | s | t | u | v | w | x | y | z | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
問題のプログラムの仕様
次のプログラムは、『大きさが10の配列aの宣言』を意味する。
(配列のインデックスは、C言語と同じでNの大きさの配列では0〜N-1となる)
a[10]
また、次のプログラムは、『変数a[1]にa[2]を代入』を意味する。
代入は、宣言された変数及び有効なインデックス内でないとエラー(バグ)となる。
参照は、正常な配列を指定していても、配列の値を参照する場合は、既に代入された配列のインデックスでないとエラー(バグ)となる。
a[1]=a[2]
解法
配列の宣言のための構造体DEFINEと代入された値を保存するための構造体VALUEを用意する。
1行を読み込んだ時に、"="が含まれる行は『代入』、含まれない行は『宣言』の行として処理する。
新しい配列宣言が来るとDEFINEに追加し、代入が来るとVALUEに追加する。
基本的に1文字ずつ解析し、配列のインデックスの参照では再帰的に呼び出して実現している。
また、2重ポインタにしているのは、再帰先で解析された続きを解析するため。
ソース
(目標40分以内)
#include <iostream> #include <vector> using namespace std; typedef struct DEFINE{ char n; int l; DEFINE(char n0, int l0){ n = n0;l = l0; } }DEFINE; typedef struct VALUE{ char n; int i; int v; VALUE(char n0, int i0, int v0){ n = n0;i = i0;v = v0; } }VALUE; vector<DEFINE> define; vector<VALUE> value; int isDefine(char n, int index){ for(int i = 0; i < define.size(); i++){ if(define[i].n == n && define[i].l > index)return 1; } return 0; } int setValue(char n, int index, int v){ if(isDefine(n, index) == 0)return 0; int i; for(i = 0; i < value.size(); i++){ if(value[i].n == n && value[i].i == index){ value[i].v = v;//更新 break; } } if(i == value.size())value.push_back(VALUE(n, index, v));//新たに代入 return 1; } int getValue(char n, int index){ for(int i = 0; i < value.size(); i++){ if(value[i].n == n && value[i].i == index)return value[i].v; } if(isDefine(n, index) == 1)return -1;//未代入 return -2;//未定義 } int getValue(VALUE v){ if(isalpha(v.n))return getValue(v.n, v.i);//変数値 if(v.i == 0)return v.v;//値 return -3;//エラー } VALUE check(char **pp){ char *p = *pp; if(isalpha(*p)){ char name = *p++; if(*p == '['){ p++; VALUE v = check(&p); int val = getValue(v); if(*p != ']' || val < 0)return VALUE(0, -1, -1);//構文エラー||未定義 *pp = p; return VALUE(name, val, -1); } return VALUE(0, -1, -1);//構文エラー } if(isdigit(*p)){ int sum = 0; for(; isdigit(*p); p++)sum = sum * 10 + *p - '0'; *pp = p; return VALUE(0, 0, sum); } return VALUE(0, -1, -1); } int main(){ char l[128]; int line = 0, error = 0, dot = 0; while(1){ line++; gets(l); if(l[0] == '.'){ if(dot)break; cout << error << endl;//行数表示 dot++; line = error = 0; define.clear(); value.clear(); } else if(error == 0){ dot = 0; if(strchr(l, '=') != NULL){ char *p1 = strtok(l, "="); char *p2 = strtok(NULL, "="); VALUE v1 = check(&p1); VALUE v2 = check(&p2); if(isDefine(v1.n, v1.i) == 0)error = line;//未定義 else if(getValue(v2) < 0)error = line;//未定義 or 未代入 else if(setValue(v1.n, v1.i, getValue(v2)) == 0)error = line;//代入エラー } else{ char *p = l; VALUE v = check(&p); if(isalpha(v.n))define.push_back(DEFINE(v.n, v.i));//宣言 else error = line;//宣言エラー } } } return 0; }