第3章: 構文解析の理論と抽象構文木

3.1 構文解析の理論的基礎

文脈自由文法(Context-Free Grammar)

Noam Chomskyは1956年の研究において、形式言語を4つの階層に分類した。構文解析が対象とするのはType 2 文脈自由文法(CFG: Context-Free Grammar)である。

文脈自由文法の形式的定義

G = (V, Σ, R, S)

V  : 非終端記号(変数)の有限集合
Σ  : 終端記号(トークン)の有限集合
R  : 生成規則の有限集合(A → α の形式、A ∈ V, α ∈ (V ∪ Σ)*)
S  : 開始記号(S ∈ V)

文脈自由文法は、プログラミング言語の構文を記述するのに十分な表現力を持つ。

Backus-Naur Form(BNF)

John BackusPeter Naurは、ALGOL 60の仕様記述のためにBNF(Backus-Naur Form)を開発した(1960年)。これは文脈自由文法を表記するための標準的な記法となった。

BNFの表記規則:

  • <非終端記号> : 非終端記号
  • 終端記号 : 終端記号(リテラル)
  • ::= : 定義
  • | : 選択(または)

ALGOL 60 Reportからの例

<expression> ::= <term> | <expression> + <term> | <expression> - <term>
<term> ::= <factor> | <term> * <factor> | <term> / <factor>
<factor> ::= <primary> | <factor> ^ <primary>

Peter Naurは後に謙遜して「Backus Normal Form」と呼ぶべきだと述べたが、Donald Knuthが「Backus-Naur Form」という名称を提案し、これが定着した。

拡張BNF(EBNF)

Niklaus Wirthは、BNFを拡張したEBNF(Extended BNF)を提案した(1977年)。

EBNF追加記法:

  • { } : 0回以上の繰り返し(Kleene閉包)
  • [ ] : オプション(0回または1回)
  • ( ) : グループ化
  • pipeline = command { "|" command }
    command = word { word } { redirection }
    redirection = ( "<" | ">" | ">>" | "<<" ) word
    

    ISO/IEC 14977:1996として標準化されている。

    3.2 構文解析アルゴリズムの歴史

    初期の構文解析(1960年代)

    構文解析アルゴリズムの研究は、ALGOL 60コンパイラの開発とともに始まった。

    主要なマイルストーン

  • Floyd(1961年): 演算子順位構文解析
  • Irons(1961年): 最初の構文主導翻訳
  • Earley(1968年): 任意の文脈自由文法を O(n³) で解析
  • Knuth(1965年): LR構文解析の理論

トップダウン構文解析

トップダウン構文解析は、開始記号から入力文字列を導出する試みである。

主な手法:

  • 再帰下降構文解析(Recursive Descent): 各非終端記号に対応する関数を再帰呼び出し
  • LL構文解析: 左から読み(Left-to-right)、最左導出(Leftmost derivation)
  • 予測的構文解析: 先読みにより決定的に解析

LL(k)文法:k個のトークンを先読みすることで、適用すべき生成規則を一意に決定できる文法。

ボトムアップ構文解析

ボトムアップ構文解析は、入力文字列から開始記号への還元を試みる。

主な手法:

  • LR構文解析: Donald Knuth(1965年)が発明
  • LALR構文解析: LRの実用的な近似
  • SLR構文解析: 単純化されたLR

LR(k)文法:左から読み(Left-to-right)、最右導出の逆(Rightmost derivation in reverse)、k個の先読み。

YACC(1975年)

Stephen C. JohnsonはBell LabsでYACC(Yet Another Compiler-Compiler)を開発した(1975年)。YACCはLALR(1)構文解析器を自動生成するツールである。

%token WORD PIPE REDIRECT_IN REDIRECT_OUT

%%
pipeline : command
         | pipeline PIPE command
         ;

command  : word_list
         | word_list redirections
         ;

word_list: WORD
         | word_list WORD
         ;
%%

GNU BisonはYACCの互換実装である。

3.3 再帰下降構文解析

再帰下降法の原理

再帰下降構文解析(Recursive Descent Parsing)は、各非終端記号に対して1つの関数を定義し、文法規則に従って再帰的に呼び出す手法である。

Niklaus Wirthは"Algorithms + Data Structures = Programs"(1976年)において、再帰下降パーサの実装技法を体系的に解説した。

再帰下降法の特徴

  • 文法規則とコードが1対1に対応
  • 手書きで実装しやすい
  • エラーメッセージの制御が容易
  • LL(1)文法に適する

左再帰の除去

再帰下降パーサは左再帰(Left Recursion)を処理できない。

左再帰の例:

<expr> ::= <expr> + <term> | <term>

この文法を直接実装すると、parse_expr()parse_expr() を呼び出し、無限ループになる。

左再帰の除去

/* 左再帰 */
A ::= A α | β

/* 除去後 */
A  ::= β A'
A' ::= α A' | ε

シェル文法への適用:

/* 元の文法(左再帰あり) */
<pipeline> ::= <pipeline> '|' <command> | <command>

/* 除去後 */
<pipeline> ::= <command> <pipeline'>
<pipeline'> ::= '|' <command> <pipeline'> | ε

実装では、これをwhileループとして表現:

t_cmd *parse_pipeline(t_parser *parser)
{
    t_cmd *left = parse_command(parser);

    while (current_token_is(parser, TOKEN_PIPE))
    {
        consume(parser);  /* '|' を消費 */
        t_cmd *right = parse_command(parser);
        left = create_pipe_node(left, right);
    }

    return left;
}

3.4 抽象構文木(AST)

構文木の理論

構文木(Parse Tree / Derivation Tree)は、文法による導出過程を木構造で表現したものである。

入力: "ls | grep test"

構文木:
        <pipeline>
        /    |    \
   <command> '|' <command>
       |            |
     <word>       <word_list>
       |          /       \
      "ls"    <word>    <word>
               |          |
             "grep"    "test"

抽象構文木(AST)

抽象構文木(Abstract Syntax Tree / AST)は、構文木から冗長な情報を除去し、プログラムの本質的な構造のみを保持したものである。

入力: "ls | grep test"

AST:
      PIPE
     /    \
   CMD    CMD
    |    /   \
  "ls" "grep" "test"

ASTは以下の点で構文木より優れている:

  • 簡潔性: 文法規則の詳細を隠蔽
  • 効率性: 不要なノードを省略
  • 意味的妥当性: プログラムの意味構造を反映
  • シェルのAST設計

    シェルコマンドのASTノード種別:

    /**
     * ASTノードの種類
     */
    typedef enum e_node_type
    {
        NODE_COMMAND,   /* 単純コマンド: argv[] + redirects */
        NODE_PIPE       /* パイプ: left | right */
    }   t_node_type;
    
    /**
     * リダイレクトの種類
     */
    typedef enum e_redir_type
    {
        REDIR_INPUT,    /* < file */
        REDIR_OUTPUT,   /* > file */
        REDIR_APPEND,   /* >> file */
        REDIR_HEREDOC   /* << delimiter */
    }   t_redir_type;
    

    3.5 シェル文法の形式的定義

    MinishellのEBNF文法

    42のMinishellプロジェクトのためのEBNF文法定義:

    (* 最上位規則 *)
    command_line = pipeline ;
    
    (* パイプライン: 1つ以上のコマンドをパイプで接続 *)
    pipeline = command { "|" command } ;
    
    (* 単純コマンド: 引数とリダイレクトの混合 *)
    command = { command_element }+ ;
    
    (* コマンド要素: 単語またはリダイレクト *)
    command_element = word | redirection ;
    
    (* リダイレクト *)
    redirection = redirect_op word ;
    
    redirect_op = "<" | ">" | ">>" | "<<" ;
    
    (* 単語: クォートあり/なし *)
    word = WORD | SQUOTED_WORD | DQUOTED_WORD ;
    

    文法の曖昧性

    シェル文法には曖昧性(Ambiguity)がある場合がある。例えば、リダイレクトの位置:

    # これらは等価
    cat < file
    < file cat
    cat file < file2
    

    この曖昧性は、構文解析時ではなく意味解析時に解決される。

    3.6 構文解析器のデータ構造

    パーサ状態構造体

    /**
     * 構文解析器の状態
     */
    typedef struct s_parser
    {
        t_token *tokens;    /* トークンリストの先頭 */
        t_token *current;   /* 現在のトークン */
        int     error;      /* エラーフラグ */
        char    *error_msg; /* エラーメッセージ */
    }   t_parser;
    

    リダイレクト構造体

    /**
     * リダイレクト情報
     */
    typedef struct s_redirect
    {
        t_redir_type        type;   /* リダイレクトの種類 */
        char                *file;  /* ファイル名またはdelimiter */
        struct s_redirect   *next;  /* 次のリダイレクト */
    }   t_redirect;
    

    ASTノード構造体

    /**
     * AST(抽象構文木)ノード
     */
    typedef struct s_ast
    {
        t_node_type     type;       /* ノードの種類 */
        char            **argv;     /* コマンドと引数(NULL終端) */
        t_redirect      *redirects; /* リダイレクトリスト */
        struct s_ast    *left;      /* パイプの左側 */
        struct s_ast    *right;     /* パイプの右側 */
    }   t_ast;
    

    3.7 パーサのユーティリティ関数

    トークン操作

    /**
     * 現在のトークンを参照(消費しない)
     */
    t_token *parser_peek(t_parser *parser)
    {
        return (parser->current);
    }
    
    /**
     * 現在のトークンが指定された種類かどうかを判定
     */
    int parser_check(t_parser *parser, t_token_type type)
    {
        if (!parser->current)
            return (0);
        return (parser->current->type == type);
    }
    
    /**
     * 現在のトークンを消費し、次に進む
     */
    t_token *parser_advance(t_parser *parser)
    {
        t_token *token;
    
        token = parser->current;
        if (parser->current)
            parser->current = parser->current->next;
        return (token);
    }
    
    /**
     * 指定された種類のトークンを期待し、消費する
     * 一致しない場合はエラー
     */
    int parser_expect(t_parser *parser, t_token_type type)
    {
        if (!parser_check(parser, type))
        {
            parser->error = 1;
            return (0);
        }
        parser_advance(parser);
        return (1);
    }
    
    /**
     * 入力の終端かどうかを判定
     */
    int parser_at_end(t_parser *parser)
    {
        return (parser->current == NULL);
    }
    

    判定関数

    /**
     * リダイレクトトークンかどうかを判定
     */
    int is_redirect_token(t_token_type type)
    {
        return (type == TOKEN_REDIRECT_IN ||
                type == TOKEN_REDIRECT_OUT ||
                type == TOKEN_REDIRECT_APPEND ||
                type == TOKEN_HEREDOC);
    }
    
    /**
     * 単語トークンかどうかを判定
     */
    int is_word_token(t_token_type type)
    {
        return (type == TOKEN_WORD);
    }
    
    /**
     * コマンド要素の開始トークンかどうかを判定
     */
    int is_command_element(t_token_type type)
    {
        return (is_word_token(type) || is_redirect_token(type));
    }
    

    3.8 再帰下降パーサの実装

    エントリポイント

    /**
     * 構文解析のエントリポイント
     * @param tokens トークンリスト
     * @return AST、エラー時はNULL
     */
    t_ast   *parser_parse(t_token *tokens)
    {
        t_parser    parser;
        t_ast       *ast;
    
        if (!tokens)
            return (NULL);
    
        parser.tokens = tokens;
        parser.current = tokens;
        parser.error = 0;
        parser.error_msg = NULL;
    
        ast = parse_pipeline(&parser);
    
        if (parser.error)
        {
            ast_free(ast);
            return (NULL);
        }
    
        /* 残りのトークンがないか確認 */
        if (!parser_at_end(&parser))
        {
            ft_putendl_fd("minishell: syntax error: unexpected token", 2);
            ast_free(ast);
            return (NULL);
        }
    
        return (ast);
    }
    

    パイプラインの解析

    /**
     * パイプラインを解析する
     * pipeline = command { "|" command }
     */
    t_ast   *parse_pipeline(t_parser *parser)
    {
        t_ast   *left;
        t_ast   *right;
        t_ast   *pipe_node;
    
        left = parse_command(parser);
        if (!left || parser->error)
            return (left);
    
        /* パイプが続く限り解析 */
        while (parser_check(parser, TOKEN_PIPE))
        {
            parser_advance(parser);  /* '|' を消費 */
    
            /* パイプの右側が必要 */
            if (parser_at_end(parser) ||
                !is_command_element(parser->current->type))
            {
                ft_putendl_fd(
                    "minishell: syntax error near unexpected token `|'", 2);
                parser->error = 1;
                ast_free(left);
                return (NULL);
            }
    
            right = parse_command(parser);
            if (!right || parser->error)
            {
                ast_free(left);
                return (NULL);
            }
    
            /* パイプノードを作成 */
            pipe_node = ast_create_pipe(left, right);
            if (!pipe_node)
            {
                ast_free(left);
                ast_free(right);
                return (NULL);
            }
            left = pipe_node;
        }
    
        return (left);
    }
    

    コマンドの解析

    /**
     * 単純コマンドを解析する
     * command = { command_element }+
     */
    t_ast   *parse_command(t_parser *parser)
    {
        t_ast       *cmd;
        t_list      *argv_list;
        t_redirect  *redirects;
    
        argv_list = NULL;
        redirects = NULL;
    
        /* コマンド要素を収集 */
        while (!parser_at_end(parser) &&
               is_command_element(parser->current->type))
        {
            if (is_redirect_token(parser->current->type))
            {
                if (!parse_redirection(parser, &redirects))
                    return (free_and_null(&argv_list, &redirects));
            }
            else if (is_word_token(parser->current->type))
            {
                if (!list_add_back(&argv_list,
                                   ft_strdup(parser->current->value)))
                    return (free_and_null(&argv_list, &redirects));
                parser_advance(parser);
            }
            else
            {
                break;
            }
        }
    
        /* 空のコマンドはエラー(リダイレクトのみは許可) */
        if (!argv_list && !redirects)
        {
            ft_putendl_fd("minishell: syntax error: empty command", 2);
            parser->error = 1;
            return (NULL);
        }
    
        /* ASTノードを作成 */
        cmd = ast_create_command(list_to_argv(argv_list), redirects);
        list_free(&argv_list);
    
        return (cmd);
    }
    

    リダイレクトの解析

    /**
     * リダイレクトを解析する
     * redirection = redirect_op word
     */
    int parse_redirection(t_parser *parser, t_redirect **redirects)
    {
        t_token         *op_token;
        t_token         *file_token;
        t_redirect      *redir;
        t_redir_type    type;
    
        op_token = parser_advance(parser);
    
        /* リダイレクト種類を決定 */
        if (op_token->type == TOKEN_REDIRECT_IN)
            type = REDIR_INPUT;
        else if (op_token->type == TOKEN_REDIRECT_OUT)
            type = REDIR_OUTPUT;
        else if (op_token->type == TOKEN_REDIRECT_APPEND)
            type = REDIR_APPEND;
        else if (op_token->type == TOKEN_HEREDOC)
            type = REDIR_HEREDOC;
        else
            return (0);
    
        /* ファイル名を取得 */
        if (parser_at_end(parser) ||
            !is_word_token(parser->current->type))
        {
            ft_putendl_fd(
                "minishell: syntax error near unexpected token `newline'", 2);
            parser->error = 1;
            return (0);
        }
    
        file_token = parser_advance(parser);
    
        /* リダイレクト構造体を作成 */
        redir = redirect_create(type, file_token->value);
        if (!redir)
            return (0);
    
        redirect_add(redirects, redir);
        return (1);
    }
    

    3.9 ASTノードの生成

    コマンドノード

    /**
     * コマンドノードを作成する
     */
    t_ast   *ast_create_command(char **argv, t_redirect *redirects)
    {
        t_ast   *node;
    
        node = malloc(sizeof(t_ast));
        if (!node)
            return (NULL);
    
        node->type = NODE_COMMAND;
        node->argv = argv;
        node->redirects = redirects;
        node->left = NULL;
        node->right = NULL;
    
        return (node);
    }
    

    パイプノード

    /**
     * パイプノードを作成する
     */
    t_ast   *ast_create_pipe(t_ast *left, t_ast *right)
    {
        t_ast   *node;
    
        node = malloc(sizeof(t_ast));
        if (!node)
            return (NULL);
    
        node->type = NODE_PIPE;
        node->argv = NULL;
        node->redirects = NULL;
        node->left = left;
        node->right = right;
    
        return (node);
    }
    

    リダイレクト操作

    /**
     * リダイレクト構造体を作成する
     */
    t_redirect  *redirect_create(t_redir_type type, const char *file)
    {
        t_redirect  *redir;
    
        redir = malloc(sizeof(t_redirect));
        if (!redir)
            return (NULL);
    
        redir->type = type;
        redir->file = ft_strdup(file);
        if (!redir->file)
        {
            free(redir);
            return (NULL);
        }
        redir->next = NULL;
    
        return (redir);
    }
    
    /**
     * リダイレクトをリストに追加する
     */
    void    redirect_add(t_redirect **list, t_redirect *new_redir)
    {
        t_redirect  *current;
    
        if (!*list)
        {
            *list = new_redir;
            return;
        }
    
        current = *list;
        while (current->next)
            current = current->next;
        current->next = new_redir;
    }
    

    3.10 変数展開

    展開フェーズの位置づけ

    POSIX規格では、シェルの処理順序は以下の通り:

  • トークン化(字句解析)
  • 構文解析
  • 展開(Expansion)
  • リダイレクトの設定
  • コマンド実行
  • 変数展開は構文解析の、実行のに行われる。

    展開の種類

    POSIX準拠シェルでの展開順序:

  • ブレース展開: {a,b,c}a b c(Minishellでは不要)
  • チルダ展開: ~ → ホームディレクトリ(Minishellでは不要)
  • パラメータ展開: $VAR, ${VAR} → 変数の値
  • コマンド置換: $(cmd), ` cmd → コマンドの出力(Minishellでは不要)
  • 算術展開: $((expr))` → 計算結果(Minishellでは不要)
  • 単語分割: IFSによる分割
  • パス名展開: ワイルドカード(Minishellでは不要)
  • クォート除去
  • Minishellでは、主にパラメータ展開クォート除去を実装する。

    変数展開の実装

    /**
     * AST全体の変数を展開する
     */
    void    expand_ast(t_ast *ast, t_shell *shell)
    {
        int i;
    
        if (!ast)
            return;
    
        if (ast->type == NODE_COMMAND)
        {
            /* argvの展開 */
            i = 0;
            while (ast->argv && ast->argv[i])
            {
                ast->argv[i] = expand_string(ast->argv[i], shell);
                i++;
            }
    
            /* リダイレクトファイル名の展開 */
            expand_redirects(ast->redirects, shell);
        }
        else if (ast->type == NODE_PIPE)
        {
            expand_ast(ast->left, shell);
            expand_ast(ast->right, shell);
        }
    }
    
    /**
     * 文字列中の変数を展開する
     */
    char    *expand_string(char *str, t_shell *shell)
    {
        t_expander  exp;
        char        *result;
    
        if (!str || !ft_strchr(str, '

    展開器の実装

    /**
     * 展開器構造体
     */
    typedef struct s_expander
    {
        const char  *input;
        size_t      pos;
        t_shell     *shell;
        char        *result;
    }   t_expander;
    
    /**
     * 展開処理のメインループ
     */
    char    *expander_process(t_expander *exp)
    {
        while (exp->input[exp->pos])
        {
            if (exp->input[exp->pos] == '

    3.11 構文エラー処理

    エラー検出

    /**
     * 構文エラーを報告する
     */
    void    parser_error(t_parser *parser, const char *msg)
    {
        ft_putstr_fd("minishell: ", STDERR_FILENO);
        ft_putendl_fd(msg, STDERR_FILENO);
        parser->error = 1;
    }
    
    /**
     * 予期しないトークンのエラー
     */
    void    parser_unexpected_token(t_parser *parser, t_token *token)
    {
        ft_putstr_fd("minishell: syntax error near unexpected token `",
                     STDERR_FILENO);
        if (token)
            ft_putstr_fd(token->value, STDERR_FILENO);
        else
            ft_putstr_fd("newline", STDERR_FILENO);
        ft_putendl_fd("'", STDERR_FILENO);
        parser->error = 1;
    }
    

    事前構文チェック

    /**
     * トークン列の構文を事前チェックする
     */
    int syntax_check(t_token *tokens)
    {
        t_token *current;
    
        if (!tokens)
            return (1);  /* 空入力はOK */
    
        /* パイプで始まる */
        if (tokens->type == TOKEN_PIPE)
        {
            ft_putendl_fd(
                "minishell: syntax error near unexpected token `|'", 2);
            return (0);
        }
    
        current = tokens;
        while (current)
        {
            /* 連続するパイプ */
            if (current->type == TOKEN_PIPE &&
                current->next &&
                current->next->type == TOKEN_PIPE)
            {
                ft_putendl_fd(
                    "minishell: syntax error near unexpected token `|'", 2);
                return (0);
            }
    
            /* パイプで終わる */
            if (current->type == TOKEN_PIPE && !current->next)
            {
                ft_putendl_fd(
                    "minishell: syntax error near unexpected token `|'", 2);
                return (0);
            }
    
            /* リダイレクトの後にファイル名がない */
            if (is_redirect_token(current->type))
            {
                if (!current->next ||
                    !is_word_token(current->next->type))
                {
                    ft_putendl_fd(
                        "minishell: syntax error near unexpected token `newline'", 2);
                    return (0);
                }
            }
    
            current = current->next;
        }
    
        return (1);
    }
    

    3.12 メモリ管理

    ASTの解放

    /**
     * ASTを再帰的に解放する
     */
    void    ast_free(t_ast *ast)
    {
        int i;
    
        if (!ast)
            return;
    
        if (ast->type == NODE_PIPE)
        {
            ast_free(ast->left);
            ast_free(ast->right);
        }
        else if (ast->type == NODE_COMMAND)
        {
            /* argvの解放 */
            if (ast->argv)
            {
                i = 0;
                while (ast->argv[i])
                    free(ast->argv[i++]);
                free(ast->argv);
            }
    
            /* リダイレクトの解放 */
            redirect_free_list(ast->redirects);
        }
    
        free(ast);
    }
    
    /**
     * リダイレクトリストを解放する
     */
    void    redirect_free_list(t_redirect *list)
    {
        t_redirect  *next;
    
        while (list)
        {
            next = list->next;
            free(list->file);
            free(list);
            list = next;
        }
    }
    

    3.13 デバッグとテスト

    AST可視化

    /**
     * ASTを可視化する(デバッグ用)
     */
    void    ast_print(t_ast *ast, int depth)
    {
        int i;
    
        if (!ast)
            return;
    
        /* インデント */
        for (i = 0; i < depth; i++)
            printf("  ");
    
        if (ast->type == NODE_PIPE)
        {
            printf("PIPE\n");
            ast_print(ast->left, depth + 1);
            ast_print(ast->right, depth + 1);
        }
        else if (ast->type == NODE_COMMAND)
        {
            printf("CMD:");
            if (ast->argv)
            {
                i = 0;
                while (ast->argv[i])
                    printf(" %s", ast->argv[i++]);
            }
            printf("\n");
    
            /* リダイレクト表示 */
            redirect_print(ast->redirects, depth + 1);
        }
    }
    
    /**
     * リダイレクトを表示する
     */
    void    redirect_print(t_redirect *redir, int depth)
    {
        int         i;
        const char  *type_str;
    
        while (redir)
        {
            for (i = 0; i < depth; i++)
                printf("  ");
    
            if (redir->type == REDIR_INPUT)
                type_str = "<";
            else if (redir->type == REDIR_OUTPUT)
                type_str = ">";
            else if (redir->type == REDIR_APPEND)
                type_str = ">>";
            else
                type_str = "<<";
    
            printf("REDIR: %s %s\n", type_str, redir->file);
            redir = redir->next;
        }
    }
    

    テストケース

    /**
     * パーサのテスト
     */
    void    parser_test(void)
    {
        t_token *tokens;
        t_ast   *ast;
    
        printf("=== Parser Tests ===\n\n");
    
        /* テスト1: 単純コマンド */
        printf("Test 1: ls -la\n");
        tokens = lexer_tokenize("ls -la");
        ast = parser_parse(tokens);
        ast_print(ast, 0);
        ast_free(ast);
        lexer_free_tokens(tokens);
    
        /* テスト2: パイプ */
        printf("\nTest 2: cat file | grep test\n");
        tokens = lexer_tokenize("cat file | grep test");
        ast = parser_parse(tokens);
        ast_print(ast, 0);
        ast_free(ast);
        lexer_free_tokens(tokens);
    
        /* テスト3: リダイレクト */
        printf("\nTest 3: cat < in.txt > out.txt\n");
        tokens = lexer_tokenize("cat < in.txt > out.txt");
        ast = parser_parse(tokens);
        ast_print(ast, 0);
        ast_free(ast);
        lexer_free_tokens(tokens);
    
        /* テスト4: 複合 */
        printf("\nTest 4: cat file | grep test | wc -l > result.txt\n");
        tokens = lexer_tokenize("cat file | grep test | wc -l > result.txt");
        ast = parser_parse(tokens);
        ast_print(ast, 0);
        ast_free(ast);
        lexer_free_tokens(tokens);
    }
    

    3.14 まとめ

    本章では、構文解析の理論と実装を学んだ:

  • 文脈自由文法: Chomsky Type 2、BNF/EBNF
  • 構文解析アルゴリズムの歴史: LR、LALR、YACC
  • 再帰下降構文解析: Wirthの手法、左再帰の除去
  • 抽象構文木(AST): 構文木との違い、設計
  • シェル文法の定義: Minishell用EBNF
  • パーサ実装: パイプライン、コマンド、リダイレクト
  • 変数展開: POSIX展開順序
  • エラー処理: 構文エラーの検出と報告
  • 次章では、コマンド実行について学ぶ。ASTを解釈してプロセスを生成し、パイプとリダイレクトを設定する方法を解説する。

    ---

    参考文献

  • Chomsky, N. (1956). "Three Models for the Description of Language", IRE Transactions on Information Theory, 2(3)
  • Backus, J. W. et al. (1960). "Report on the Algorithmic Language ALGOL 60", Communications of the ACM, 3(5)
  • Knuth, D. E. (1965). "On the Translation of Languages from Left to Right", Information and Control, 8(6)
  • Wirth, N. (1976). "Algorithms + Data Structures = Programs", Prentice Hall
  • Aho, A. V., Sethi, R., & Ullman, J. D. (1986). "Compilers: Principles, Techniques, and Tools", Addison-Wesley
  • Johnson, S. C. (1975). "YACC: Yet Another Compiler-Compiler", Bell Laboratories Computing Science Technical Report No. 32
  • ISO/IEC 14977:1996 "Syntactic metalanguage — Extended BNF"
  • IEEE (1992). "IEEE Std 1003.2-1992: Shell and Utilities", IEEE Computer Society

)) return (str); expander_init(&exp, str, shell); result = expander_process(&exp); free(str); return (result); }

展開器の実装

___CODE_BLOCK_26___

3.11 構文エラー処理

エラー検出

___CODE_BLOCK_27___

事前構文チェック

___CODE_BLOCK_28___

3.12 メモリ管理

ASTの解放

___CODE_BLOCK_29___

3.13 デバッグとテスト

AST可視化

___CODE_BLOCK_30___

テストケース

___CODE_BLOCK_31___

3.14 まとめ

本章では、構文解析の理論と実装を学んだ:

  • 文脈自由文法: Chomsky Type 2、BNF/EBNF
  • 構文解析アルゴリズムの歴史: LR、LALR、YACC
  • 再帰下降構文解析: Wirthの手法、左再帰の除去
  • 抽象構文木(AST): 構文木との違い、設計
  • シェル文法の定義: Minishell用EBNF
  • パーサ実装: パイプライン、コマンド、リダイレクト
  • 変数展開: POSIX展開順序
  • エラー処理: 構文エラーの検出と報告
  • 次章では、コマンド実行について学ぶ。ASTを解釈してプロセスを生成し、パイプとリダイレクトを設定する方法を解説する。

    ---

    参考文献

  • Chomsky, N. (1956). "Three Models for the Description of Language", IRE Transactions on Information Theory, 2(3)
  • Backus, J. W. et al. (1960). "Report on the Algorithmic Language ALGOL 60", Communications of the ACM, 3(5)
  • Knuth, D. E. (1965). "On the Translation of Languages from Left to Right", Information and Control, 8(6)
  • Wirth, N. (1976). "Algorithms + Data Structures = Programs", Prentice Hall
  • Aho, A. V., Sethi, R., & Ullman, J. D. (1986). "Compilers: Principles, Techniques, and Tools", Addison-Wesley
  • Johnson, S. C. (1975). "YACC: Yet Another Compiler-Compiler", Bell Laboratories Computing Science Technical Report No. 32
  • ISO/IEC 14977:1996 "Syntactic metalanguage — Extended BNF"
  • IEEE (1992). "IEEE Std 1003.2-1992: Shell and Utilities", IEEE Computer Society
  • ) expand_variable(exp); else append_char(exp, exp->input[exp->pos++]); } return (exp->result); } /** * 変数を展開する */ void expand_variable(t_expander *exp) { char *var_name; char *var_value; exp->pos++; /* '

    3.11 構文エラー処理

    エラー検出

    ___CODE_BLOCK_27___

    事前構文チェック

    ___CODE_BLOCK_28___

    3.12 メモリ管理

    ASTの解放

    ___CODE_BLOCK_29___

    3.13 デバッグとテスト

    AST可視化

    ___CODE_BLOCK_30___

    テストケース

    ___CODE_BLOCK_31___

    3.14 まとめ

    本章では、構文解析の理論と実装を学んだ:

  • 文脈自由文法: Chomsky Type 2、BNF/EBNF
  • 構文解析アルゴリズムの歴史: LR、LALR、YACC
  • 再帰下降構文解析: Wirthの手法、左再帰の除去
  • 抽象構文木(AST): 構文木との違い、設計
  • シェル文法の定義: Minishell用EBNF
  • パーサ実装: パイプライン、コマンド、リダイレクト
  • 変数展開: POSIX展開順序
  • エラー処理: 構文エラーの検出と報告
  • 次章では、コマンド実行について学ぶ。ASTを解釈してプロセスを生成し、パイプとリダイレクトを設定する方法を解説する。

    ---

    参考文献

  • Chomsky, N. (1956). "Three Models for the Description of Language", IRE Transactions on Information Theory, 2(3)
  • Backus, J. W. et al. (1960). "Report on the Algorithmic Language ALGOL 60", Communications of the ACM, 3(5)
  • Knuth, D. E. (1965). "On the Translation of Languages from Left to Right", Information and Control, 8(6)
  • Wirth, N. (1976). "Algorithms + Data Structures = Programs", Prentice Hall
  • Aho, A. V., Sethi, R., & Ullman, J. D. (1986). "Compilers: Principles, Techniques, and Tools", Addison-Wesley
  • Johnson, S. C. (1975). "YACC: Yet Another Compiler-Compiler", Bell Laboratories Computing Science Technical Report No. 32
  • ISO/IEC 14977:1996 "Syntactic metalanguage — Extended BNF"
  • IEEE (1992). "IEEE Std 1003.2-1992: Shell and Utilities", IEEE Computer Society
  • )) return (str); expander_init(&exp, str, shell); result = expander_process(&exp); free(str); return (result); }

    展開器の実装

    ___CODE_BLOCK_26___

    3.11 構文エラー処理

    エラー検出

    ___CODE_BLOCK_27___

    事前構文チェック

    ___CODE_BLOCK_28___

    3.12 メモリ管理

    ASTの解放

    ___CODE_BLOCK_29___

    3.13 デバッグとテスト

    AST可視化

    ___CODE_BLOCK_30___

    テストケース

    ___CODE_BLOCK_31___

    3.14 まとめ

    本章では、構文解析の理論と実装を学んだ:

  • 文脈自由文法: Chomsky Type 2、BNF/EBNF
  • 構文解析アルゴリズムの歴史: LR、LALR、YACC
  • 再帰下降構文解析: Wirthの手法、左再帰の除去
  • 抽象構文木(AST): 構文木との違い、設計
  • シェル文法の定義: Minishell用EBNF
  • パーサ実装: パイプライン、コマンド、リダイレクト
  • 変数展開: POSIX展開順序
  • エラー処理: 構文エラーの検出と報告
  • 次章では、コマンド実行について学ぶ。ASTを解釈してプロセスを生成し、パイプとリダイレクトを設定する方法を解説する。

    ---

    参考文献

  • Chomsky, N. (1956). "Three Models for the Description of Language", IRE Transactions on Information Theory, 2(3)
  • Backus, J. W. et al. (1960). "Report on the Algorithmic Language ALGOL 60", Communications of the ACM, 3(5)
  • Knuth, D. E. (1965). "On the Translation of Languages from Left to Right", Information and Control, 8(6)
  • Wirth, N. (1976). "Algorithms + Data Structures = Programs", Prentice Hall
  • Aho, A. V., Sethi, R., & Ullman, J. D. (1986). "Compilers: Principles, Techniques, and Tools", Addison-Wesley
  • Johnson, S. C. (1975). "YACC: Yet Another Compiler-Compiler", Bell Laboratories Computing Science Technical Report No. 32
  • ISO/IEC 14977:1996 "Syntactic metalanguage — Extended BNF"
  • IEEE (1992). "IEEE Std 1003.2-1992: Shell and Utilities", IEEE Computer Society
  • をスキップ */ /* $? の処理 */ if (exp->input[exp->pos] == '?') { var_value = ft_itoa(exp->shell->last_status); append_str(exp, var_value); free(var_value); exp->pos++; return; } /* 変数名を抽出 */ var_name = extract_var_name(exp); if (!var_name || !*var_name) { append_char(exp, '

    3.11 構文エラー処理

    エラー検出

    ___CODE_BLOCK_27___

    事前構文チェック

    ___CODE_BLOCK_28___

    3.12 メモリ管理

    ASTの解放

    ___CODE_BLOCK_29___

    3.13 デバッグとテスト

    AST可視化

    ___CODE_BLOCK_30___

    テストケース

    ___CODE_BLOCK_31___

    3.14 まとめ

    本章では、構文解析の理論と実装を学んだ:

  • 文脈自由文法: Chomsky Type 2、BNF/EBNF
  • 構文解析アルゴリズムの歴史: LR、LALR、YACC
  • 再帰下降構文解析: Wirthの手法、左再帰の除去
  • 抽象構文木(AST): 構文木との違い、設計
  • シェル文法の定義: Minishell用EBNF
  • パーサ実装: パイプライン、コマンド、リダイレクト
  • 変数展開: POSIX展開順序
  • エラー処理: 構文エラーの検出と報告
  • 次章では、コマンド実行について学ぶ。ASTを解釈してプロセスを生成し、パイプとリダイレクトを設定する方法を解説する。

    ---

    参考文献

  • Chomsky, N. (1956). "Three Models for the Description of Language", IRE Transactions on Information Theory, 2(3)
  • Backus, J. W. et al. (1960). "Report on the Algorithmic Language ALGOL 60", Communications of the ACM, 3(5)
  • Knuth, D. E. (1965). "On the Translation of Languages from Left to Right", Information and Control, 8(6)
  • Wirth, N. (1976). "Algorithms + Data Structures = Programs", Prentice Hall
  • Aho, A. V., Sethi, R., & Ullman, J. D. (1986). "Compilers: Principles, Techniques, and Tools", Addison-Wesley
  • Johnson, S. C. (1975). "YACC: Yet Another Compiler-Compiler", Bell Laboratories Computing Science Technical Report No. 32
  • ISO/IEC 14977:1996 "Syntactic metalanguage — Extended BNF"
  • IEEE (1992). "IEEE Std 1003.2-1992: Shell and Utilities", IEEE Computer Society
  • )) return (str); expander_init(&exp, str, shell); result = expander_process(&exp); free(str); return (result); }

    展開器の実装

    ___CODE_BLOCK_26___

    3.11 構文エラー処理

    エラー検出

    ___CODE_BLOCK_27___

    事前構文チェック

    ___CODE_BLOCK_28___

    3.12 メモリ管理

    ASTの解放

    ___CODE_BLOCK_29___

    3.13 デバッグとテスト

    AST可視化

    ___CODE_BLOCK_30___

    テストケース

    ___CODE_BLOCK_31___

    3.14 まとめ

    本章では、構文解析の理論と実装を学んだ:

  • 文脈自由文法: Chomsky Type 2、BNF/EBNF
  • 構文解析アルゴリズムの歴史: LR、LALR、YACC
  • 再帰下降構文解析: Wirthの手法、左再帰の除去
  • 抽象構文木(AST): 構文木との違い、設計
  • シェル文法の定義: Minishell用EBNF
  • パーサ実装: パイプライン、コマンド、リダイレクト
  • 変数展開: POSIX展開順序
  • エラー処理: 構文エラーの検出と報告
  • 次章では、コマンド実行について学ぶ。ASTを解釈してプロセスを生成し、パイプとリダイレクトを設定する方法を解説する。

    ---

    参考文献

  • Chomsky, N. (1956). "Three Models for the Description of Language", IRE Transactions on Information Theory, 2(3)
  • Backus, J. W. et al. (1960). "Report on the Algorithmic Language ALGOL 60", Communications of the ACM, 3(5)
  • Knuth, D. E. (1965). "On the Translation of Languages from Left to Right", Information and Control, 8(6)
  • Wirth, N. (1976). "Algorithms + Data Structures = Programs", Prentice Hall
  • Aho, A. V., Sethi, R., & Ullman, J. D. (1986). "Compilers: Principles, Techniques, and Tools", Addison-Wesley
  • Johnson, S. C. (1975). "YACC: Yet Another Compiler-Compiler", Bell Laboratories Computing Science Technical Report No. 32
  • ISO/IEC 14977:1996 "Syntactic metalanguage — Extended BNF"
  • IEEE (1992). "IEEE Std 1003.2-1992: Shell and Utilities", IEEE Computer Society
  • ); free(var_name); return; } /* 変数値を取得して追加 */ var_value = env_get(exp->shell->env, var_name); if (var_value) append_str(exp, var_value); free(var_name); } /** * 変数名を抽出する */ char *extract_var_name(t_expander *exp) { size_t start; size_t len; start = exp->pos; /* 最初の文字: 英字または '_' */ if (!ft_isalpha(exp->input[exp->pos]) && exp->input[exp->pos] != '_') return (ft_strdup("")); /* 続く文字: 英数字または '_' */ while (ft_isalnum(exp->input[exp->pos]) || exp->input[exp->pos] == '_') exp->pos++; len = exp->pos - start; return (ft_substr(exp->input, start, len)); }

    3.11 構文エラー処理

    エラー検出

    ___CODE_BLOCK_27___

    事前構文チェック

    ___CODE_BLOCK_28___

    3.12 メモリ管理

    ASTの解放

    ___CODE_BLOCK_29___

    3.13 デバッグとテスト

    AST可視化

    ___CODE_BLOCK_30___

    テストケース

    ___CODE_BLOCK_31___

    3.14 まとめ

    本章では、構文解析の理論と実装を学んだ:

  • 文脈自由文法: Chomsky Type 2、BNF/EBNF
  • 構文解析アルゴリズムの歴史: LR、LALR、YACC
  • 再帰下降構文解析: Wirthの手法、左再帰の除去
  • 抽象構文木(AST): 構文木との違い、設計
  • シェル文法の定義: Minishell用EBNF
  • パーサ実装: パイプライン、コマンド、リダイレクト
  • 変数展開: POSIX展開順序
  • エラー処理: 構文エラーの検出と報告
  • 次章では、コマンド実行について学ぶ。ASTを解釈してプロセスを生成し、パイプとリダイレクトを設定する方法を解説する。

    ---

    参考文献

  • Chomsky, N. (1956). "Three Models for the Description of Language", IRE Transactions on Information Theory, 2(3)
  • Backus, J. W. et al. (1960). "Report on the Algorithmic Language ALGOL 60", Communications of the ACM, 3(5)
  • Knuth, D. E. (1965). "On the Translation of Languages from Left to Right", Information and Control, 8(6)
  • Wirth, N. (1976). "Algorithms + Data Structures = Programs", Prentice Hall
  • Aho, A. V., Sethi, R., & Ullman, J. D. (1986). "Compilers: Principles, Techniques, and Tools", Addison-Wesley
  • Johnson, S. C. (1975). "YACC: Yet Another Compiler-Compiler", Bell Laboratories Computing Science Technical Report No. 32
  • ISO/IEC 14977:1996 "Syntactic metalanguage — Extended BNF"
  • IEEE (1992). "IEEE Std 1003.2-1992: Shell and Utilities", IEEE Computer Society
  • )) return (str); expander_init(&exp, str, shell); result = expander_process(&exp); free(str); return (result); }

    展開器の実装

    ___CODE_BLOCK_26___

    3.11 構文エラー処理

    エラー検出

    ___CODE_BLOCK_27___

    事前構文チェック

    ___CODE_BLOCK_28___

    3.12 メモリ管理

    ASTの解放

    ___CODE_BLOCK_29___

    3.13 デバッグとテスト

    AST可視化

    ___CODE_BLOCK_30___

    テストケース

    ___CODE_BLOCK_31___

    3.14 まとめ

    本章では、構文解析の理論と実装を学んだ:

  • 文脈自由文法: Chomsky Type 2、BNF/EBNF
  • 構文解析アルゴリズムの歴史: LR、LALR、YACC
  • 再帰下降構文解析: Wirthの手法、左再帰の除去
  • 抽象構文木(AST): 構文木との違い、設計
  • シェル文法の定義: Minishell用EBNF
  • パーサ実装: パイプライン、コマンド、リダイレクト
  • 変数展開: POSIX展開順序
  • エラー処理: 構文エラーの検出と報告
  • 次章では、コマンド実行について学ぶ。ASTを解釈してプロセスを生成し、パイプとリダイレクトを設定する方法を解説する。

    ---

    参考文献

  • Chomsky, N. (1956). "Three Models for the Description of Language", IRE Transactions on Information Theory, 2(3)
  • Backus, J. W. et al. (1960). "Report on the Algorithmic Language ALGOL 60", Communications of the ACM, 3(5)
  • Knuth, D. E. (1965). "On the Translation of Languages from Left to Right", Information and Control, 8(6)
  • Wirth, N. (1976). "Algorithms + Data Structures = Programs", Prentice Hall
  • Aho, A. V., Sethi, R., & Ullman, J. D. (1986). "Compilers: Principles, Techniques, and Tools", Addison-Wesley
  • Johnson, S. C. (1975). "YACC: Yet Another Compiler-Compiler", Bell Laboratories Computing Science Technical Report No. 32
  • ISO/IEC 14977:1996 "Syntactic metalanguage — Extended BNF"
  • IEEE (1992). "IEEE Std 1003.2-1992: Shell and Utilities", IEEE Computer Society