6

私はシェル、小さな bash のようなシェルに取り組んでおり、スクリプトを作成する必要はありません (if while ...) lexer/parser (LL) を手動で作成する必要があります。

したがって、レクサーはコマンド ( char *cmd ) をリンクされたリスト ( t_list *list ) に変換します。そして、LL パーサーは、リンクされたリスト ( t_list *list ) を文法を使用して AST (バイナリ ツリーt_btree *root ) に変換します。

したがって、LL パーサーの作成方法は知っていますが、コマンドをトークン化する方法はわかりません。

例えば:ps | grep ls >> file ; make && ./a.out

=>'ps' '|' 'grep' 'ls' '>>' 'file' ';' ''make '&&' './a.out'

ありがとう。

(発電機は使いたくない)

4

1 に答える 1

7

(これはSpudd86によって示唆されたアイデアを説明しています)。

有限状態マシンを実装する必要があります。次の状態があります。

  • 一般的な状態
  • ファイル名の中
  • &&トークンの内部
  • ||トークンの内部

状態と次の入力文字ごとに、次の状態と、トークンを出力するかどうかを決定する必要があります。例えば:

  • 現在の状態:一般; 文字: x => 次の状態:内部ファイル名
  • 現在の状態:内部ファイル名; 文字:スペース=> 次の状態:一般; トークンを出力する
  • 現在の状態:内部ファイル名; 文字: & => 次の状態: inside-&& ; トークンを出力する
  • 現在の状態: inside-&& ; 文字: & => 次の状態:一般; トークンを出力する
  • 現在の状態: inside-&& ; 文字: x => 次の状態:一般。構文エラー
  • ...(うんざり)

すべてのルールを解決するのは非常に退屈な作業です (結果のコードをデバッグしなければならないときに楽しみが始まります)。


編集:いくつかのコード(構文がめちゃくちゃになっている場合は申し訳ありません;私は通常C ++でプログラムします)

enum state {
    STATE_GENERAL,
    STATE_IN_FILENAME,
    ...
};

// Many characters are treated the same (e.g. 'x' and 'y') - so use categories
enum character_category
{
    CHAR_GENERAL, // can appear in filenames
    CHAR_WHITESPACE = ' ',
    CHAR_AMPERSAND = '&',
    CHAR_PIPE = '|',
    CHAR_EOF = EOF,
    ...
};

character_category translate(int c)
{
    switch (c) {
    case '&': return CHAR_AMPERSAND;
    case ' ': case '\t': case '\n': return CHAR_WHITESPACE;
    ...
    default: return CHAR_GENERAL;
    }
}

void do_stuff()
{
    character_category cat;
    state current_state = STATE_GENERAL;
    state next_state;
    char token[100];
    char token_length = 0;
    do {
        int c = getchar();
        cat = translate(c);
        // The following implements a switch on 2 variables
        int selector = 1000 * current_state + cat;

        switch (selector)
        {
        case 1000 * STATE_GENERAL + CHAR_GENERAL:
            next_state = STATE_IN_FILENAME;
            token[token_length++] = c; // append a character to a filename token
            break;

        case 1000 * STATE_GENERAL + CHAR_WHITESPACE:
            next_state = STATE_GENERAL; // do nothing
            break;

        case 1000 * STATE_GENERAL + CHAR_PIPE:
            next_state = STATE_IN_OR_TOKEN; // the first char in '||' or just '|'
            break;

        // Much repetitive code already; define a macro for the case constants?
        // Have to cover all states and all character categories; good luck...

        case 1000 * STATE_IN_FILENAME + EOF:
        case 1000 * STATE_IN_FILENAME + CHAR_WHITESPACE:
            next_state = STATE_GENERAL;
            printf("Filename token: %s\n", token);
            break;

        default:
            printf("Bug\n"); // forgot one of the cases?
        }

        current_state = next_state;

    } while (cat != CHAR_EOF);
}
于 2011-03-30T22:24:54.810 に答える