Oi, eu de novo. Juro que é a última de hoje.
"Quando a instrução é o pseudo-operador IS
o programa deve associar o valor do operando ao rótulo passado, inserindo o par correspondente na alias_table
. Se o rótulo já foi associado, você deve produzir uma mensagem de erro. Essa é a única instrução que deve ser interpretada pelo parse_test"
.
Não podemos lidar com o operador IS dentro de parse()? O código ficaria bem mais limpo
Oi Bruna.
Oi, eu de novo. Juro que é a última de hoje.
Não se preocupe.
Perguntas são sempre muito bem-vindas!
Eu só sinto falta de discussões (este é um fórum de discussões).
De várias pessoas comentando, respondendo,..., participando.
Aqueles ou aquelas que refletiram sobre o assunto ou superarm o proplema poderiam compartilhar suas ideias.
Bem, os únicos tópicos que motivam várias mensagens parecem ser os referentes as notas...
Não podemos lidar com o operador IS dentro de parse()?
Vocês estão lidando com o IS
, certo?
Os apelidos estão sendo colocados na tabela de símbolos.
Mais adiante, o montador (assembler
como macas
) que vocês farão terá dois passos:
- no primeiro passo, o programa na linguagem de montagem (como o
macal
, extensão.as
) é lido e todos o identificadores (rótulos, apelidos, ...) são colocados em tabelas de símbolos; - no segundo passo, o montador examina mais uma vez a entrada (
.as
), mas desta vez traduzindo cada operação para a sequência de bits representando a operação me linguagem de máquina (.maco
).
Ah, faz sentido. Por isso então separar o IS no código do cliente
Obrigada (por essa e todas as outras respostas de hoje, rs), professor!
Professor,
podemos mudar a API do parse? Porque já vamos ter que ler (e quebrar a linha em operador e operandos) para achar o IS no próprio cliente, então não faz muito sentido enviarmos a linha em forma de string para o parse, para ler tudo de novo.
Pensamos em
(a) fazer o trabalho de quebra em parse_test e já enviar quebrado para o parse ou
(b) fazer a função de quebra em parse, retornando um valor específico para sabermos que ela tem um operador IS, podendo chamá-la no cliente, e aí, já com os operandos, fazer a atribuição na ST.
Caso não possamos vamos ficar com muito código repetido e lendo mais de uma vez a mesma linha...
Não tinha chegado nesse problema ainda, a Bruna tem razão. Uma solução possível sem mudar a API é parse() retornar no errptr que o operador é IS e a partir daí você processar isso no cliente. Mas as funções de quebra de linha vão ficar ligeiramente repetidas no cliente e no parse, não tem jeito.
Oi Bruna,
podemos mudar a API do parse?
Não precisa.
(a) fazer o trabalho de quebra em parse_test e já enviar quebrado para o parse ou
Quebrar tudo e devolver os pedaços na lista instr
é o trabalho da função parse()
.
Vocês devem fornecer a função uma linha por vezparse()
.
No exemplo a seguir as linhas são fornecidas uma a uma atraves da linha de comando.
for (k = 1; k < argc; k++) { s = emalloc(strlen(argv[k])); strcpy(s, argv[k]); printf("Instrucao %d = '%s'\n", k-1, s); if (parse(s, apelidos, &ini, &errptr)) { [... imprime a bagaça mostrada abaixo ...] } meu_prompt > ./parse_test " JMP 8; 1a. linha" " INT #80 ; 2a linha" " JP \$1,5; 3a. linha" Instrucao 0 = ' JMP 8; 1a. linha' parse(): intrucao valida pos = 0 lineno = 0 label = '(null)' operador = { name = 'JMP' opcode = 0x48 opd_types = { opds[0] == NUM_TYPE (0x8) opds[1] == NULL opds[2] == NULL } } Instrucao 1 = ' INT #80 ; 2a linha' parse(): intrucao valida pos = 0 lineno = 0 label = '(null)' operador = { name = 'INT' opcode = 0xfe opd_types = { opds[0] == NUM_TYPE (0x80) opds[1] == NULL opds[2] == NULL } } Instrucao 2 = ' JP $1,5; 3a. linha' parse(): intrucao valida pos = 0 lineno = 0 label = '(null)' operador = { name = 'JP' opcode = 0x4e opd_types = { opds[0] == REGISTER (1) opds[1] == NUM_TYPE (0x5) opds[2] == NULL } }
(b) fazer a função de quebra em parse, retornando um valor específico para sabermos que ela tem um operador IS, podendo chamá-la no cliente, e aí, já com os operandos, fazer a atribuição na ST.
O nó inserido por parse()
na lista de instruções tem toda essa informação.
Usem as funções de asmtype
. Copiei o trecho a seguir de asmtype.h
. Essas funções são utilizadas por parse()
, em particular, vejam instr_create()
(e todas as outras também)
/* Return new operand with the given value. */ Operand *operand_create_register(unsigned char reg); Operand *operand_create_number(octa num); Operand *operand_create_label(const char *label); Operand *operand_create_string(const char *str); /* Return copy of given operand. */ Operand *operand_dup(const Operand *opd); /* Destroy operand. */ void operand_destroy(Operand *opd); /* Create a new instruction. The given operands are not duplicated, the label is. */ Instruction *instr_create(const char *label, const Operator *op, Operand *opds[3]); /* Destroy an instruction, destroying also the operands. */ void instr_destroy(Instruction *instr);
Mas, se "Quando a instrução é o pseudo-operador IS
o programa deve associar o valor do operando ao rótulo passado, inserindo o par correspondente na alias_table
. Se o rótulo já foi associado, você deve produzir uma mensagem de erro. Essa é a única instrução que deve ser interpretada pelo parse_test
.", não temos que antes de enviar ao parse, verificar se é IS e inserir na ST (e, nesse caso, nem enviar ao parse)? Foi isso que entendemos! :S
Salve,
não temos que antes de enviar ao parse, verificar se é IS e inserir na ST (e, nesse caso, nem enviar ao parse)?
parse_test
deve enviar a funçãoparse()
um string com apenas uma linha do programa em linguagem de montagem.parse()
deve *descascar* a string. Isso corresponde a chamada análise léxica, oparse()
pegar cada item léxico (token) e identifica o que são:LABEL, REGSTER, ...
.- Se a string representa uma instrução válida, como, por exemplo
IS
, a funçãoparse()
, utilizando a função/* Create a new instruction. The given operands are not duplicated, the label is. */ Instruction *instr_create(const char *label, const Operator *op, Operand *opds[3]);
cria uma estruturaIntruction
:// A parsed instruction. typedef struct instruction_s { // Instruction position. int pos; // Line number in input file. int lineno; // Label associated with the instruction. char *label; // Operator. const Operator *op; // Operands. Operand *opds[3]; // Instructions are kept in a linked list. struct instruction_s *next; } Instruction;
que é retorna através do argumento*instr
, fazendo, por exemplo:*instr = instr_create(..., ..., ...);
- Se
parse()
detecta algum erro, liberá todas as estruturas que por ventura tenha alocada, coloca em*errptr
a posiçõa da atring onde nos demos conta do problema e retorna um valor que não me lembro se é zero ou não zero.
Após a chamada de parse()
, o cliente, que agora é parse_test
e em breve será o montador (assembler), pode verificar o conteúdo de cada campo da estrutura Instruction
apontada por instr
e tomar as ações necessárias.
Por exemplo, se a instrução é IS
, o cliente pode/deve colocar o apelido na tabela de símbolos:
if (instr->op->opcode == IS)
{
InsertionResult x = stable_insert(alias_table, instr->label);
x.data->p = instr->opds[0];
}
C é uma linguagem muito simples.
Para uma função alterar o valor de um algo externo, devemos dizer para a função onde está esse algo. Por isso o protótipo de parse()
é
int parse(const char *s, SymbolTable alias_table, Instruction **instr, const char **errptr)
e a chamada parse()
é algo como:
parse(s, alias_apelidos, &intr, &errptr));
onde instr
é um ponteiro para uma estrutura Instruction
(guarda o endereço de) e errprt
é um ponteiro para string, ....
A propósito, o ponteiro errprt
é usado para colocar aquele ^
no ponto que percebemos que havia algo errado.
Quem deve colocar esse ^
é o parse_test
e não a função parse()
Aqui vai mais um exemplo de um cliente simples que pega as instruções em linguagem de montagem da linha de comando.
Esse cliente chama parse()
e coloca os apelidos definids através de IS
na tabela de apelidos (alias_table
).
Para exibir a tabela de símbolos fiz a chamada:
stable_visit(alias_table, &visite_st);
onde visite_st
é a função que ensina stable_visit()
como imprimir o conteúdo de um item de alias_table
.
Fora isso, todas as linhas abaixo foram foram impressas pelo cliente.
meu_prompt> ./parse_test "m IS \$1 ; linha 0" \ "n IS \$1; linha 1" \ "rx IS \$252 ; linha 2" \ "euclid DIVU rx,n,m ; linha 3" Instrucao 0 = 'm IS $1 ; linha 0' parse(): instrucao valida pos = 0 lineno = 0 label = 'm' operador = { name = 'IS' opcode = 0xffffffff opd_types = { opds[0] == REGISTER ($1) opds[1] == NULL opds[2] == NULL } } Instrucao 1 = 'n IS $1; linha 1' parse(): instrucao valida pos = 0 lineno = 0 label = 'n' operador = { name = 'IS' opcode = 0xffffffff opd_types = { opds[0] == REGISTER ($1) opds[1] == NULL opds[2] == NULL } } Instrucao 2 = 'rx IS $252 ; linha 2' parse(): instrucao valida pos = 0 lineno = 0 label = 'rx' operador = { name = 'IS' opcode = 0xffffffff opd_types = { opds[0] == REGISTER ($252) opds[1] == NULL opds[2] == NULL } } Instrucao 3 = 'euclid DIVU rx,n,m ; linha 3' parse(): instrucao valida pos = 0 lineno = 0 label = 'euclid' operador = { name = 'DIVU' opcode = 0x36 opd_types = { opds[0] == REGISTER ($252) opds[1] == REGISTER ($1) opds[2] == REGISTER ($1) } } Conteudo da ST alias_stable: 0: key = 'rx', (REGISTER) $252 1: key = 'n', (REGISTER) $1 2: key = 'm', (REGISTER) $1