我正在尝试实现一个数学表达式解析器,它接收一个字符串作为输入,并最终将一个条件表示输出到控制台。我已经在Python中实现了一个类似的工作程序:
def term(self):
result = self.factor()
while self.current_token.type in (MUL, DIV):
token = self.current_token
if token.type == MUL:
self.eat(MUL)
result = result * self.factor()
elif token.type == DIV:
self.eat(DIV)
result = result / self.factor()
但是现在,由于我在C语言方面的经验不足,我遇到了一些问题。我附上了一个未来程序的草图,其中我对parser_term
函数很感兴趣。
AST_T* parser_term(Parser_T* parser) {
AST_T* result;
while (parser->current_token->type == TOKEN_MUL
|| parser->current_token->type == TOKEN_DIV) {
if (parser->current_token->type == TOKEN_MUL) {
parser_eat(parser, TOKEN_MUL);
} else if (parser->current_token->type == TOKEN_DIV) {
parser_eat(parser, TOKEN_DIV);
}
}
return result;
}
我应该如何创建一个新的二进制操作节点?这可能是一个有点愚蠢的问题,但我希望你能帮助我弄清楚。
如果你能指出我的代码中的其他错误,我也会很高兴。
完整代码:
#include <stdio.h>
#include <stdlib.h>
//============================ LEXICAL ANALYSIS ============================================
//---------------------------- Token -------------------------------------------------------
typedef struct TOKEN_STRUCT
{
enum
{
TOKEN_INTEGER,
TOKEN_PLUS, TOKEN_MINUS,
TOKEN_MUL, TOKEN_DIV,
TOKEN_LBRA, TOKEN_RBRA,
TOKEN_EOF
} type;
char* value;
} Token_T;
Token_T* init_token(int type, char* value)
{
Token_T* token = calloc(1, sizeof(struct TOKEN_STRUCT));
token->type = type;
token->value = value;
return token;
}
void token_debug_print(Token_T* token)
{
printf(
"Token( type: '%d', value: '%s' )\n",
token->type, token->value
);
}
//------------------------------------------------------------------------------------------
//---------------------------- Lexer -------------------------------------------------------
typedef struct LEXER_STRUCT
{
char current_char;
unsigned int position;
char* content;
} Lexer_T;
Lexer_T* init_lexer(char* content)
{
Lexer_T* lexer = calloc(1, sizeof(struct LEXER_STRUCT));
lexer->content = content;
lexer->position = 0;
lexer->current_char = lexer->content[lexer->position];
return lexer;
}
void lexer_advance(Lexer_T* lexer)
{
if (lexer->current_char != '\0')
{
lexer->position += 1;
lexer->current_char = lexer->content[lexer->position];
}
}
void lexer_skip_whitespace(Lexer_T* lexer)
{
while (lexer->current_char == ' ')
{
lexer_advance(lexer);
}
}
char* lexer_get_current_char_as_string(Lexer_T* lexer)
{
char* stringus = calloc(1, sizeof(char));
stringus[0] = lexer->current_char;
stringus[1] = '\0';
return stringus;
}
Token_T* lexer_get_digit(Lexer_T* lexer)
{
char* lexem = calloc(1, sizeof(char));
lexem[0] = '\0';
while (lexer->current_char >= '0' && lexer->current_char <= '9')
{
char* part = lexer_get_current_char_as_string(lexer);
lexem = realloc(lexem, (strlen(lexem) + strlen(part) + 1) * sizeof(char));
strcat(lexem, part);
lexer_advance(lexer);
}
return init_token(TOKEN_INTEGER, lexem);
}
Token_T* lexer_get_op(Lexer_T* lexer)
{
switch (lexer->current_char)
{
case '+':
lexer_advance(lexer);
return init_token(TOKEN_PLUS, "+");
case '-':
lexer_advance(lexer);
return init_token(TOKEN_MINUS, "-");
case '*':
lexer_advance(lexer);
return init_token(TOKEN_MUL, "*");
case '/':
lexer_advance(lexer);
return init_token(TOKEN_DIV, "/");
}
}
Token_T* lexer_get_next_token(Lexer_T* lexer)
{
while (lexer->current_char != '\0')
{
if (lexer->current_char == ' ')
lexer_skip_whitespace(lexer);
else if (lexer->current_char >= '0' && lexer->current_char <= '9')
return lexer_get_digit(lexer);
else if (lexer->current_char == '+' || lexer->current_char == '-' ||
lexer->current_char == '*' || lexer->current_char == '/')
return lexer_get_op(lexer);
else if (lexer->current_char == '(')
{
lexer_advance(lexer);
return init_token(TOKEN_LBRA, "(");
}
else if (lexer->current_char == ')')
{
lexer_advance(lexer);
return init_token(TOKEN_RBRA, ")");
}
}
return init_token(TOKEN_EOF, "\\0");
}
//-----------------------------------------------------------------------------------------
//=========================================================================================
//============================ SYNTAX ANALYSIS ============================================
//---------------------------- AST --------------------------------------------------------
typedef struct AST_STRUCT
{
enum{
AST_NUMBER,
AST_BINOP,
AST_PAREN_EXPR
} type;
char* number_value;
char* bin_operator;
struct AST_STRUCT* left;
struct AST_STRUCT* right;
struct AST_STRUCT* paren_expr;
} AST_T;
AST_T* init_AST(int type)
{
AST_T* ast = calloc(1, sizeof(struct AST_STRUCT));
ast->type = type;
return ast;
}
//-----------------------------------------------------------------------------------------
//---------------------------- Parser -----------------------------------------------------
typedef struct PARSER_STRUCT
{
Lexer_T* lexer;
Token_T* current_token;
} Parser_T;
Parser_T* init_parser(Lexer_T* lexer)
{
Parser_T* parser = calloc(1, sizeof(struct PARSER_STRUCT));
parser->lexer = lexer;
parser->current_token = lexer_get_next_token(parser->lexer);
return parser;
}
AST_T* parser_factor(Parser_T* parser);
AST_T* parser_term(Parser_T* parser);
AST_T* parser_expr(Parser_T* parser);
void parser_eat(Parser_T* parser, int type)
{
if (parser->current_token->type == type)
{
parser->current_token = lexer_get_next_token(parser->lexer);
}
else
{
printf("Unexpected token");
exit(0);
}
}
AST_T* parser_expr(Parser_T* parser)
{
}
AST_T* parser_factor(Parser_T* parser)
{
if (parser->current_token->type == TOKEN_INTEGER)
{
AST_T* node = init_AST(TOKEN_INTEGER);
node->number_value = parser->current_token->value;
parser_eat(parser, TOKEN_INTEGER);
return node;
}
}
AST_T* parser_term(Parser_T* parser)
{
AST_T* result;
while (parser->current_token->type == TOKEN_MUL || parser->current_token->type == TOKEN_DIV)
{
if (parser->current_token->type == TOKEN_MUL)
{
parser_eat(parser, TOKEN_MUL);
}
else if (parser->current_token->type == TOKEN_DIV)
{
parser_eat(parser, TOKEN_DIV);
}
}
return result;
}
//-----------------------------------------------------------------------------------------
//=========================================================================================
//============================ VISITOR ====================================================
typedef struct VISITOR_STRUCT
{
} Visitor_T;
Visitor_T* init_visitor(AST_T* ast)
{
Visitor_T* visitor = calloc(1, sizeof(struct VISITOR_STRUCT));
return visitor;
}
void visitor_visit_number(Visitor_T* visitor, AST_T* node)
{
printf("Number {\n");
printf(" %s\n", node->number_value);
printf("}\n");
}
void visitor_visit_bin_op(Visitor_T* visitor, AST_T* node)
{
printf("Binop {\n");
visitor_visit(visitor, node->left);
visitor_visit(visitor, node->right);
printf("\n}\n");
}
void visitor_visit_paren_expr(Visitor_T* visitor, AST_T* node)
{
visitor_visit(visitor, node);
}
void visitor_visit(Visitor_T* visitor, AST_T* ast)
{
if (ast->type == AST_NUMBER)
{
visitor_visit_number(visitor, ast);
}
else if (ast->type == AST_BINOP)
{
visitor_visit_bin_op(visitor, ast);
}
else if (ast->type == AST_PAREN_EXPR)
{
visitor_visit_paren_expr(visitor, ast);
}
}
//=========================================================================================
int main()
{
char* code = "77 * 12 * 9 * 2";
Lexer_T* lexer = init_lexer(code);
Parser_T* parser = init_parser(lexer);
AST_T* ast = parser_term(parser);
Visitor_T* visitor = init_visitor(ast);
visitor_visit(visitor, ast);
return 0;
}
我尝试先获取因子值并将其添加到节点中,然后继续解析表达式,但这只会让我感到困惑。我期望这个程序能够处理类似的二进制操作,并将它们转化为一个AST。
1条答案
按热度按时间h6my8fg21#
这里有一个明确的问题:
我应该如何创建一个新的二元操作节点?
你需要一个在需要时创建的对象,但它的生命周期不会自动限制在它开始执行的函数中。这种组合需要动态分配。(在Python中,你总是自动获得它,但在C中,你必须要求它。)例如:
作为最佳实践,在尝试使用分配的对象之前,您应该始终验证分配是否成功。如果没有,您应该回退到某种替代或恢复操作,或者更常见的是,失败。在 * 程序 * 中,与库相反,通过打印诊断并终止来失败是合理的。例如:
但是程序中的分配不太可能失败,除非有其他严重的错误。
假设分配成功,你需要适当地设置新对象的成员。可能沿着这样的:
最后,您需要返回指向新节点的指针(这似乎是您预期要做的事情),或者将其分配给解析器的成员。或者两者兼而有之。这些都很简单。例如,
如果你能给我指出其他的错误,我也会很高兴的
我担心这对SO的要求太宽泛了。但是一定要打开编译器的警告并注意它们。以你的经验水平,你应该假设每个警告都描述了一个会使你的程序错误运行的问题。