PR04 - Operador IS

PR04 - Operador IS

por Bruna Thalenberg -
Número de respostas: 9

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 sorriso

Em resposta à Bruna Thalenberg

Re: PR04 - Operador IS

por José Coelho de Pina -

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). sorriso
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... triste

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).
Em resposta à José Coelho de Pina

Re: PR04 - Operador IS

por Bruna Thalenberg -

Ah, faz sentido. Por isso então separar o IS no código do cliente sorriso

Obrigada (por essa e todas as outras respostas de hoje, rs), professor!

Em resposta à José Coelho de Pina

Re: PR04 - Operador IS

por Bruna Thalenberg -

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...

Em resposta à Bruna Thalenberg

Re: PR04 - Operador IS

por Bruno Carneiro da Cunha -

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. 

Em resposta à Bruna Thalenberg

Re: PR04 - Operador IS

por José Coelho de Pina -

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
       }
   }
Em resposta à Bruna Thalenberg

Re: PR04 - Operador IS

por José Coelho de Pina -

(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);
Em resposta à José Coelho de Pina

Re: PR04 - Operador IS

por Bruna Thalenberg -

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

Em resposta à Bruna Thalenberg

Re: PR04 - Operador IS

por José Coelho de Pina -

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ção parse() 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, o parse() 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ção parse(), 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 estrutura Intruction:
    // 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()

 

Em resposta à José Coelho de Pina

Re: PR04 - Operador IS

por José Coelho de Pina -

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