S言語の字句解析器設計と実装

字句解析器の基本設計

字句解析器はソースコードをトークンに分割するコンパイラの初期段階です。主な機能は以下の通りです:

  • ソースコードの入力処理
  • トークン分類と値の記録
  • コメントや空白の除去
  • 字句エラーの検出と報告
  • 識別子表と定数表の管理

状態遷移図に基づく実装例

typedef struct {
    int type;
    char name[20];
    int line;
} Token;

bool is_alpha(char c) {
    return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
}

bool is_digit(char c) {
    return c >= '0' && c <= '9';
}

Token* tokenize(const char* src) {
    Token* tokens = malloc(MAX_TOKENS * sizeof(Token));
    int pos = 0;
    int line = 1;
    
    while(*src) {
        if(is_alpha(*src)) {
            // 識別子またはキーワードの処理
            char ident[20] = {0};
            int i = 0;
            while(is_alpha(*src) || is_digit(*src)) {
                ident[i++] = *src++;
            }
            ident[i] = '\0';
            
            // キーワードチェック
            if(is_keyword(ident)) {
                tokens[pos].type = KEYWORD;
            } else {
                tokens[pos].type = IDENTIFIER;
            }
            strcpy(tokens[pos].name, ident);
            tokens[pos++].line = line;
        }
        // 他のトークンタイプの処理...
    }
    return tokens;
}

DFAを用いた効率的な実装

決定性有限オートマトン(DFA)を使用すると、より効率的な字句解析が可能です。

typedef enum {
    STATE_START,
    STATE_IDENT,
    STATE_NUMBER,
    STATE_OPERATOR
} LexerState;

void dfa_lexer(FILE* input) {
    LexerState state = STATE_START;
    char buffer[100];
    int buf_pos = 0;
    char c;
    
    while((c = fgetc(input)) != EOF) {
        switch(state) {
            case STATE_START:
                if(is_alpha(c)) {
                    state = STATE_IDENT;
                    buffer[buf_pos++] = c;
                }
                // 他の状態遷移...
                break;
                
            case STATE_IDENT:
                if(is_alpha(c) || is_digit(c)) {
                    buffer[buf_pos++] = c;
                } else {
                    buffer[buf_pos] = '\0';
                    process_token(buffer, TOKEN_IDENT);
                    buf_pos = 0;
                    state = STATE_START;
                    ungetc(c, input);
                }
                break;
            // 他の状態処理...
        }
    }
}

エラー処理機構

字句解析段階で検出可能な主なエラー:

  1. 不正な文字(言語仕様にない文字)
  2. 終了しないコメント
void handle_error(int error_code, int line) {
    const char* messages[] = {
        "不正な文字",
        "コメントが閉じられていません"
    };
    printf("行 %d: エラー%d - %s\n", line, error_code, messages[error_code-1]);
}

最適化技法

実用的な字句解析器の性能向上策:

  • バッファリングによるI/O最適化
  • キーワード検索のハッシュ化
  • 正規表現マッチングの活用
  • 並列処理による高速化

高度な実装例

class Lexer {
    std::unordered_map<std::string, TokenType> keywords;
    std::string buffer;
    size_t position = 0;
    
public:
    Lexer(const std::string& src) : buffer(src) {
        // キーワード初期化
        keywords = {{"int", TOKEN_INT}, {"if", TOKEN_IF} /* ... */};
    }
    
    Token next_token() {
        skip_whitespace();
        if(position >= buffer.size()) return {TOKEN_EOF};
        
        char current = peek();
        if(is_alpha(current)) return read_identifier();
        if(is_digit(current)) return read_number();
        // 他のトークンタイプ...
    }
    
private:
    char peek() const { return buffer[position]; }
    
    Token read_identifier() {
        size_t start = position;
        while(is_alpha(peek()) || is_digit(peek())) {
            advance();
        }
        std::string id = buffer.substr(start, position-start);
        if(keywords.count(id)) {
            return {keywords[id], id};
        }
        return {TOKEN_IDENTIFIER, id};
    }
    // 他の読み取りメソッド...
};

タグ: 字句解析 コンパイラ DFA 状態遷移図 C言語

6月5日 19:26 投稿