如何在 ANTLR 中实现 C 解析器的词法分析器 hack

Nic*_*ick 5 c parsing antlr abstract-syntax-tree antlr4

是否可以使用标准 C 语法(如官方 ANTLR4 GitHub 存储库中找到的语法)来实现经典的Yacc 词法分析器 hack,以区分 ANTLR4 生成的 C 解析器中的标识符名称和类型名称?

看来可以插入 ANTLR4 词法分析器的临时代码非常有限。在《The Definitive ANTLR4 Reference》一书中,Terrence Parr 说道:

“一种长期存在的常见做法涉及从解析器向词法分析器发送反馈,以便词法分析器可以向解析器发送精确的标记。[...]不幸的是,这对于 ANTLR 语法来说是不可能的,因为 ANTLR 生成的解析器通常在令牌流中向前看以做出解析决策。[...]”

有什么方法可以规避上述问题并实现反馈循环吗?或者在访问解析树时如果不采取疯狂的黑客手段,在 ANTLR4 中实现 C 解析器是不可能的吗?

Def*_*ked -3

不。您逐字引用了您自己问题的答案。

顺便说一句,编写解析器并不难。预警一下,这个递归下降解析器是我凭空写出来的;它尚未经过测试,但它仍然会给您基本的想法:

#include <ctype.h>     // isalnum, isalpha, isdigit
#include <stdio.h>     // printf
#include <stdlib.h>    // atoi
#include <string.h>    // strncmp


/* ASCII numbers are positive, and only between 0 and 127.
 * We can thus use negative numbers to identify special tokens. */
enum {
    TOK_SPACE = -1,
    TOK_ID = -2,
    TOK_NUM = -3,
    TOK_ELSE = -4,
    TOK_IF = -5
};


int token_length;
union {
    char *s;
    int i;
} token_value;


int get_token(char *str) {
    token_length = 0;
    if (isspace(*str)) {
        do {
            token_length++;
            str++;
        } while (isspace(*str));
        return TOK_SPACE;
    }
    if (isalpha(*str) || *str == '_') {
        token_value.s = str;
        do {
            token_length++;
            str++;
        } while (isalnum(*str) || *str == '_');
        if (!strncmp(token_value.s, "else", token_length))
            return TOK_ELSE;
        if (!strncmp(token_value.s, "if", token_length))
            return TOK_IF;
        return TOK_ID;
    }
    if (isdigit(*str)) {
        token_value.s = str;
        do {
            token_length++;
            str++;
        } while (isdigit(*str));
        token_value.i = atoi(str);
        return TOK_NUM;
    }
    token_length = 1;
    return *str;
}

int is_conditional(char *str) {
    int tok, len = 0, rule_len;
    tok = get_token(str);
    if (tok == TOK_SPACE) {      // accept space
        len += token_length;
        str += token_length;
        tok = get_token(str);
    }
    if (tok != TOK_ID && tok != TOK_NUM) // require id or number
        return 0;
    return len + token_length;   // return the length of the entire match
}

int is_if_statement(char *str) {
    int tok, len = 0, rule_len;
    tok = get_token(str);
    if (tok == TOK_SPACE) {      // accept space
        len += token_length;
        str += token_length;
        tok = get_token(str);
    }
    if (tok == TOK_IF) {         // require 'if'
        len += token_length;
        str += token_length;
        tok = get_token(str);
    } else return 0;
    if (tok == TOK_SPACE) {      // accept space
        len += token_length;
        str += token_length;
        tok = get_token(str);
    }
    if (tok == '(') {            // require l-paren
        len++;
        str++;
        tok = get_token(str);
    } else return 0;
    rule_len = is_conditional(str);
    if (rule_len != 0) {         // require conditional
        len += rule_len;
        str += rule_len;
        tok = get_token(str);
    } else return 0;
    if (tok == TOK_SPACE) {      // accept space
        len += token_length;
        str += token_length;
        tok = get_token(str);
    }
    if (tok == ')') {            // require r-paren
        len++;
        str++;
        tok = get_token(str);
    } else return 0;
    if (tok == TOK_SPACE) {      // accept space
        len += token_length;
        str += token_length;
        tok = get_token(str);
    }
    if (tok != TOK_ID)
        return 0;
    return len + token_length;   // return the length of the entire match
}

void parse(char *str) {
    int rule_len;
    if ((rule_len = is_if_statement(str)))
        printf("an if-statement was matched!\n");
    else
        printf("the input is not valid code!\n");
    str += rule_len;
}

int main(int argc, char *argv[]) {
    parse("if (x) y");     // valid!
    parse("if (1) ...");   // invalid!
    return 0;
}
Run Code Online (Sandbox Code Playgroud)