Implementando O Parser
Um parser é em essência um componente que analisa e transforma dados em uma representação mais estruturada de tais dados. No nosso caso: vamos converter texto em uma pilha e executar operações que aparecerem. Nosso parser vai ler a entrada, token por token. Um token para nós, será ou um número fracionário, ou uma operação. Vamos definir a interface do nosso parser. Para representar o token, vamos definir uma união rotulada (ou união disjunta, ou tipo soma).
Arquivo parser.h
#ifndef PARSER_H
#define PARSER_H
#include "ops.h"
struct parser {
char const *cursor;
};
enum token_kind {
token_num,
token_op,
token_end,
token_error
};
union token_data {
enum operation op;
double num;
};
struct token {
union token_data data;
enum token_kind kind;
};
struct token parse_token(struct parser *parser);
#endif
Vamos agora implementar. Uma função que pula espaços em branco, uma função que analisa o caso de um número, uma que analisa o caso de um operador, uma que atualiza o ponteiro se for o token for operador. Todas elas privadas. E, claro, a função que junta todas.
Arquivo parser.c
:
#include "parser.h"
#include <string.h>
#include <stdlib.h>
static int is_whitespace(char ch)
{
switch (ch) {
case ' ':
case '\n':
case '\r':
case '\t':
case '\f':
case '\v':
return 1;
default:
return 0;
}
}
static void skip_whitespace(struct parser *parser)
{
while (is_whitespace(*parser->cursor)) {
parser->cursor++;
}
}
static int advance_op(struct parser *parser, char const *test, size_t test_n)
{
int success = 0;
int ch;
size_t size = test_n - 1;
if (strncmp(parser->cursor, test, size) == 0) {
ch = parser->cursor[size];
if (ch == 0 || is_whitespace(ch)) {
success = 1;
parser->cursor += size;
}
}
return success;
}
static int parse_num(struct parser *parser, double *output)
{
char *end;
int success = 0;
*output = strtod(parser->cursor, &end);
if (end > parser->cursor) {
success = 1;
parser->cursor = end;
}
return success;
}
static int parse_op(struct parser *parser, enum operation *output)
{
char const *end= parser->cursor;
int success = 1;
if (advance_op(parser, OP_ADD_SYM, sizeof(OP_ADD_SYM))) {
*output = op_add;
} else if (advance_op(parser, OP_SUB_SYM, sizeof(OP_SUB_SYM))) {
*output = op_sub;
} else if (advance_op(parser, OP_MUL_SYM, sizeof(OP_MUL_SYM))) {
*output = op_mul;
} else if (advance_op(parser, OP_DIV_SYM, sizeof(OP_DIV_SYM))) {
*output = op_div;
} else {
success = 0;
}
return success;
}
struct token parse_token(struct parser *parser)
{
struct token token;
skip_whitespace(parser);
if (*parser->cursor == 0) {
token.kind = token_end;
} else if (parse_num(parser, &token.data.num)) {
token.kind = token_num;
} else if (parse_op(parser, &token.data.op)) {
token.kind = token_op;
} else {
token.kind = token_error;
}
return token;
}
Vamos ver se não ocorre algum erro de compilação (no Linux):
gcc -o parser.o -c parser.c
Perfeito, não ocorre.
Agora, vamos ao commit:
git add .
git status
git commit -m 'implementado o parser para operações básicas'
git push