1

stringstream を使用して、このような行をトークン化するにはどうすればよいでしょうか。

[ラベル] オペコード [arg1] [,arg2]

ラベルは常にあるとは限りませんが、ない場合は空白になります。オペコードは常にそこにあり、オペコードと arg1 の間にはスペースまたはタブがあります。次に、arg1 と arg2 の間に空白はありませんが、コンマで区切られています。

また、一部の空白行には空白が含まれるため、破棄する必要があります。「#」はコメントです

たとえば、次のようになります。

#Sample Input
TOP  NoP
     L   2,1
VAL  INT  0

これは、これから読み込むテキスト ファイルのほんの一例です。したがって、行 1 のラベルでは TOP になり、opcode は引数が渡​​されずに = NOP になります。

私はそれに取り組んできましたが、トークン化するためのより簡単な方法が必要です.本当に感謝しています。

私はこれを行う方法について頭を悩ませてきましたが、作業せずに尋ねるだけではないことを示すために、現在のコードは次のとおりです。

int counter = 0;
int i = 0;
int j = 0;
int p = 0;

while (getline(myFile, line, '\n'))
{


    if (line[0] == '#')
    {
        continue;
    }

    if (line.length() == 0)
    {
        continue;
    }

    if (line.empty())
    {
        continue;
    }

    // If the first letter isn't a tab or space then it's a label

    if (line[0] != '\t' && line[0] != ' ')
    {

        string delimeters = "\t ";

        int current;
        int next = -1;


        current = next + 1;
        next = line.find_first_of( delimeters, current);
        label = line.substr( current, next - current );

        Symtablelab[i] = label;
        Symtablepos[i] = counter;

        if(next>0)
        {
            current = next + 1;
            next = line.find_first_of(delimeters, current);
            opcode = line.substr(current, next - current);


            if (opcode != "WORDS" && opcode != "INT")
            {
                counter += 3;
            }

            if (opcode == "INT")
            {
                counter++;
            }

            if (next > 0)
            {
                delimeters = ", \n\t";
                current = next + 1;
                next = line.find_first_of(delimeters, current);
                arg1 = line.substr(current, next-current);

                if (opcode == "WORDS")
                {
                    counter += atoi(arg1.c_str());
                }
            }

            if (next > 0)
            {
                delimeters ="\n";
                current = next +1;
                next = line.find_first_of(delimeters,current);
                arg2 = line.substr(current, next-current);

            }
        }

        i++;

    }

    // If the first character is a tab or space then there is no label and we just need to get a counter
    if (line[0] == '\t' || line[0] == ' ')
    {
        string delimeters = "\t \n";
        int current;
        int next = -1;
        current = next + 1;
        next = line.find_first_of( delimeters, current);
        label = line.substr( current, next - current );

    if(next>=0)
        {
            current = next + 1;
            next = line.find_first_of(delimeters, current);
            opcode = line.substr(current, next - current);

            if (opcode == "\t" || opcode =="\n"|| opcode ==" ")
            {
                continue;
            }

            if (opcode != "WORDS" && opcode != "INT")
            {
                counter += 3;
            }

            if (opcode == "INT")
            {
                counter++;
            }


            if (next > 0)
            {
                delimeters = ", \n\t";
                current = next + 1;
                next = line.find_first_of(delimeters, current);
                arg1 = line.substr(current, next-current);

                if (opcode == "WORDS")
                {
                    counter += atoi(arg1.c_str());
                }

            }



            if (next > 0)
            {
                delimeters ="\n\t ";
                current = next +1;
                next = line.find_first_of(delimeters,current);
                arg2 = line.substr(current, next-current);

            }
        }

    }
}

myFile.clear();
myFile.seekg(0, ios::beg);

while(getline(myFile, line))
{
    if (line.empty())
    {
        continue;
    }

    if (line[0] == '#')
    {
        continue;
    }

    if (line.length() == 0)
    {
        continue;
    }



    // If the first letter isn't a tab or space then it's a label

    if (line[0] != '\t' && line[0] != ' ')
    {

        string delimeters = "\t ";

        int current;
        int next = -1;


        current = next + 1;
        next = line.find_first_of( delimeters, current);
        label = line.substr( current, next - current );


        if(next>0)
        {
            current = next + 1;
            next = line.find_first_of(delimeters, current);
            opcode = line.substr(current, next - current);



            if (next > 0)
            {
                delimeters = ", \n\t";
                current = next + 1;
                next = line.find_first_of(delimeters, current);
                arg1 = line.substr(current, next-current);

            }

            if (next > 0)
            {
                delimeters ="\n\t ";
                current = next +1;
                next = line.find_first_of(delimeters,current);
                arg2 = line.substr(current, next-current);

            }
        }

        if (opcode == "INT")
        {
            memory[p] = arg1;
            p++;
            continue;
        }

        if (opcode == "HALT" || opcode == "NOP" || opcode == "P_REGS")
        {
            memory[p] = opcode;
            p+=3;
            continue;
        }

        if(opcode == "J" || opcode =="JEQR" || opcode == "JNE" || opcode == "JNER" || opcode == "JLT" || opcode == "JLTR" || opcode == "JGT" || opcode == "JGTR" || opcode == "JLE" || opcode == "JLER" || opcode == "JGE" || opcode == "JGER" || opcode == "JR")
        {
            memory[p] = opcode;
            memory[p+1] = arg1;
            p+=3;
            continue;
        }

        if (opcode == "WORDS")
        {
            int l = atoi(arg1.c_str());
            for (int k = 0; k <= l; k++)
            {
                memory[p+k] = "0";
            }

            p+=l;
            continue;
        }

        else
        {
            memory[p] = opcode;
            memory[p+1] = arg1;
            memory[p+2] = arg2;
            p+=3;
        }

    }

    // If the first character is a tab or space then there is no label and we just need to get a counter        


    if (line[0] == '\t' || line[0] == ' ')
    {
        string delimeters = "\t ";
        int current;
        int next = -1;
        current = next + 1;
        next = line.find_first_of( delimeters, current);
        label = line.substr( current, next - current );

    if(next>=0)
        {
            current = next + 1;
            next = line.find_first_of(delimeters, current);
            opcode = line.substr(current, next - current);

            if (opcode == "\t" || opcode =="\n"|| opcode ==" "|| opcode == "")
            {
                continue;
            }



            if (next > 0)
            {
                delimeters = ", \n\t";
                current = next + 1;
                next = line.find_first_of(delimeters, current);
                arg1 = line.substr(current, next-current);

            }



            if (next > 0)
            {
                delimeters ="\n\t ";
                current = next +1;
                next = line.find_first_of(delimeters,current);
                arg2 = line.substr(current, next-current);

            }
        }

        if (opcode == "INT")
        {
            memory[p] = arg1;
            p++;
            continue;
        }

        if (opcode == "HALT" || opcode == "NOP" || opcode == "P_REGS")
        {
            memory[p] = opcode;
            p+=3;
            continue;
        }

        if(opcode == "J" || opcode =="JEQR" || opcode == "JNE" || opcode == "JNER" || opcode == "JLT" || opcode == "JLTR" || opcode == "JGT" || opcode == "JGTR" || opcode == "JLE" || opcode == "JLER" || opcode == "JGE" || opcode == "JGER" || opcode == "JR")
        {
            memory[p] = opcode;
            memory[p+1] = arg1;
            p+=3;
            continue;
        }

        if (opcode == "WORDS")
        {
            int l = atoi(arg1.c_str());
            for (int k = 0; k <= l; k++)
            {
                memory[p+k] = "0";
            }

            p+=l;

            continue;
        }

        else
        {
            memory[p] = opcode;
            memory[p+1] = arg1;
            memory[p+2] = arg2;
            p+=3;
        }
    }
}

私は明らかにこれをもっと良くしたいと思っているので、どんな助けも大歓迎です.

4

2 に答える 2

3

これらの巨大なステートメントを維持しifたり、Boost Spirit を学ぼうとしたりすることに夢中になる前に、非常に単純なパーサーを作成してみましょう。これは少し長い投稿であり、要点には直接触れませんが、ご容赦ください。

まず、非常に単純に見える文法が必要です。

    line
          label(optional)   opcode   argument-list(optional)

    argument-list
          argument
          argument, argument-list

英語で: コード行は、オプションのラベル、オペコード、およびオプションの引数リストで構成されます。引数リストは、単一の引数 (整数) か、引数の後にセパレーター (コンマ) と別の引数リストが続きます。

まず、2 つのデータ構造を定義しましょう。ラベルは一意であるはずなので (そうですか?)、一連の文字列を用意して、いつでも簡単に検索できるようにし、重複するラベルが見つかった場合はエラーを報告できるようにします。次のものは への文字列のマップでsize_t、有効なオペコードのシンボル テーブルとして機能し、各オペコードの予想される引数の数と一緒に機能します。

std::set<std::string> labels;
std::map<std::string, size_t> symbol_table = {
    { "INT", 1},
    { "NOP", 0},
    { "L",   2}
};

あなたのコードの正確な内容はわかりませんがmemory、オフセットを計算して引数をどこに置くかを計算する方法は、不必要に複雑に思えます。代わりに、コード行をエレガントに保持できるデータ構造を定義しましょう。私はこのようなことをします:

typedef std::vector<int> arg_list;

struct code_line {
    code_line() : label(), opcode(), args() {}
    std::string  label;      // labels are optional, so an empty string
                             // will mean absence of label
    std::string  opcode;     // opcode, doh
    arg_list     args;       // variable number of arguments, it can be empty, too.
                             // It needs to match with opcode, we'll deal with
                             // that later
};

構文エラーは、簡単には回復できない一種の例外的な状況なので、例外をスローして対処しましょう。単純な例外クラスは次のようになります。

struct syntax_error {
    syntax_error(std::string m) : msg(m) { }
    std::string msg;
};

トークン化、字句解析、解析は、通常、別々のタスクです。しかし、この単純な例では、トークナイザーとレクサーを 1 つのクラスに組み合わせることができると思います。文法が構成されている要素は既にわかっているので、入力をテキストとして受け取り、そこから文法要素を抽出するクラスを作成しましょう。インターフェイスは次のようになります。

class token_stream {
    std::istringstream stream; // stringstream for input
    std::string buffer;        // a buffer for a token, more on this later
public:
    token_stream(std::string str) : stream(str), buffer() { }

    // these methods are self-explanatory
    std::string get_label();
    std::string get_opcode();
    arg_list get_arglist();

    // we're taking a kind of top-down approach with this,
    // so let's forget about implementations for now
};

code_lineそして、トークンを理解しようとし、すべてがうまくいけば構造体を返す関数です。

code_line parse(std::string line)
{
    code_line temp;
    token_stream stream(line);

    // Again, self-explanatory, get a label, opcode and argument list from
    // token stream.

    temp.label = stream.get_label();
    temp.opcode = stream.get_opcode();
    temp.args = stream.get_arglist();

    // Everything went fine so far, remember we said we'd be throwing exceptions
    // in case of syntax errors.

    // Now we can check if we got the correct number of arguments for the given opcode:

    if (symbol_table[temp.opcode] != temp.args.size()) {
        throw syntax_error("Wrong number of parameters.");
    }

    // The last thing, if there's a label in the line, we insert it in the table.
    // We couldn't do that inside the get_label method, because at that time
    // we didn't yet know if the rest of the line is sintactically valid and a
    // exception thrown would have left us with a "dangling" label in the table.

    if (!temp.label.empty()) labels.insert(temp.label);

    return temp;
}

そして、これらすべてを使用する方法は次のとおりです。

int main()
{
    std::string line;
    std::vector<code_line> code;

    while (std::getline(std::cin, line)) {

        // empty line or a comment, ignore it
        if (line.empty() || line[0] = '#') continue;

        try {
            code.push_back(parse(line));
        } catch (syntax_error& e) {
            std::cout << e.msg << '\n';

            // Give up, try again, log... up to you.
        }
    }
}

入力が正常に解析された場合、すべての情報 (ラベル、引数の数) を含む有効な行のベクトルを取得し、それを使ってほとんど何でも実行できます。このコードは、IMO よりも保守と拡張がはるかに簡単です。たとえば、新しいオペコードを導入する必要がある場合は、マップに別のエントリを作成します ( symbol_table)。あなたのif発言と比べてどうですか?:)

残っているのは、token_streams メソッドの実際の実装だけです。これが私がそれをした方法ですget_label

std::string token_stream::get_label()
{
    std::string temp;

    // Unless the stream is empty (and it shouldn't be, we checked that in main),
    // operator>> for std::string is unlikely to fail. It doesn't hurt to be robust
    // with error checking, though

    if (!(stream >> temp)) throw ("Fatal error, empty line, bad stream?");

    // Ok, we got something. First we should check if the string consists of valid
    // characters - you probably don't want punctuation characters and such in a label.
    // I leave this part out for simplicity.

    // Since labels are optional, we need to check if the token is an opcode.
    // If that's the case, we return an empty (no) label.

    if (symbol_table.find(temp) != symbol_table.end()) {
        buffer = temp;
        return "";
    }

    // Note that above is where that `buffer` member of token_stream class got used.
    // If the token was an opcode, we needed to save it so get_opcode method can make
    // use of it. The other option would be to put the string back in the underlying 
    // stringstream, but that's more work and more code. This way, get_opcode needs   
    // to check if there's anything in buffer and use it, or otherwise extract from
    // the stringstream normally.

    // Check if the label was used before:

    if (labels.count(temp))
        throw syntax_error("Label already used.");

    return temp;
}

以上です。残りの実装は演習として残します。それが役に立ったことを願っています。:)

于 2012-09-19T10:18:53.837 に答える
1

Boostregexなどの正規表現が絶対に必要です。または、このバージョンの質問のlex / yacc、flex / bison、boostspiritなどの字句解析および解析ツール。

文字列とストリームを維持することは、この複雑さを維持する価値はありません。

于 2012-09-18T00:23:34.370 に答える