Pascal toca Go. Implementação de métodos e interfaces em um compilador amador

Se eu pudesse exportar um recurso do Go para outros idiomas, seriam interfaces. - Russ Cox



Meu compilador Pascal extremamente simples já se tornou objeto de duas publicações sobre Habré. Desde a sua escrita, o idioma adquiriu todas as ferramentas ausentes exigidas pelo Pascal padrão e muitos dos presentes que a Borland adicionou a Pascal na sua idade de ouro. O compilador também aprendeu algumas das otimizações locais mais simples, suficientes apenas para impedir que seus olhos sangrem quando você olha para a lista de desmontadores.

No entanto, a selva da programação orientada a objetos permaneceu completamente intocada. Então, por que não servir como campo de teste para o compilador experimentar nesta área? E por que não nos inspiramos nas palavras de Russ Cox, feitas na epígrafe? Vamos tentar implementar métodos e interfaces no estilo Go no Pascal. A idéia é interessante apenas porque todos os compiladores populares do Pascal no passado (Delphi, Free Pascal) emprestaram essencialmente o modelo de objeto do C ++. É interessante ver como uma abordagem completamente diferente, emprestada da Go, se enraíza no mesmo terreno. Se você está pronto para me seguir com uma quantidade razoável de ironia, abandone a pergunta "Por quê?" E aceite o que está acontecendo como um jogo, bem-vindo ao gato.

Princípios


Por "Go style", entenderemos vários princípios com base nos quais implementaremos métodos e interfaces em Pascal:

  • Não há conceitos independentes de classe, objeto, herança.
  • O método pode ser implementado para qualquer tipo de dados específico. Você não precisa alterar a declaração do tipo em si.
  • Uma interface é compatível com qualquer tipo de dados específico para o qual todos os métodos listados na declaração da interface são implementados. Um anúncio para um tipo de dados específico não precisa indicar que implementa uma interface.

Implementação


Para declarar métodos e interfaces, as palavras-chave e a interface padrão do Pascal são usadas na nova função. Nenhuma nova palavra-chave foi inserida. A palavra for é usada para indicar o nome e o tipo do destinatário do método (na terminologia Go). Aqui está um exemplo de descrição do método para um tipo TCat declarado anteriormente com um campo Name :

 procedure Greet for c: TCat (const HumanName: string); begin WriteLn('Meow, ' + HumanName + '! I am ' + c.Name); end; 

O receptor é realmente o primeiro argumento para o método.

Uma interface é uma entrada Pascal regular, na declaração em que o record palavras é substituído pela interface palavras. Nesse registro, não é permitido declarar nenhum campo, exceto os campos de tipo processual. Além disso, um campo Self oculto é adicionado ao início da gravação. Ele armazena um ponteiro para dados desse tipo específico, que é convertido em um tipo de interface. Aqui está um exemplo de declaração de interface:

 type IPet = interface Greet: procedure (const HumanName: string); end; 

Ao converter um tipo específico em uma interface, o compilador verifica a presença de todos os métodos exigidos pela interface e a correspondência de suas assinaturas. Em seguida, ele define o Self ponteiro, preenche todos os campos procedurais da interface com ponteiros para métodos de um tipo específico.

Comparada ao Go, a implementação atual de interfaces no Pascal tem limitações: não é possível consultar dinamicamente um tipo de dados específico que foi convertido em um tipo de interface. Consequentemente, interfaces vazias não têm sentido. Talvez o próximo passo no desenvolvimento seja preencher essa lacuna. No entanto, mesmo em sua forma atual, as interfaces fornecem polimorfismo, útil em muitas tarefas não tão triviais. Vamos considerar um desses problemas.

Exemplo


Um bom exemplo do uso de interfaces é um programa para renderizar cenas tridimensionais usando o método de rastreamento de raios. A cena consiste em corpos geométricos simples: paralelepípedos, esferas, etc. Cada raio emitido pelo olho do observador precisa ser rastreado (através de todas as suas reflexões) até atingir a fonte de luz ou chegar ao infinito. Para fazer isso, um método Intersect é atribuído a cada tipo de corpo, que calcula as coordenadas do ponto em que o raio atinge a superfície do corpo e os componentes normais nesse ponto. A implementação deste método para diferentes tipos de corpos é diferente. Assim, as informações sobre os corpos são convenientemente armazenadas em uma matriz de entradas da interface Body e, para todos os elementos da matriz, o método Intersect é chamado por sua vez. A interface redireciona essa chamada para um método específico, dependendo do tipo de corpo.

Pode parecer uma cena construída da maneira descrita:



Todo o código fonte do programa, incluindo a descrição da cena, ocupa 367 linhas.

Sumário


A implementação mais simples do polimorfismo no compilador de Pascal acabou sendo fácil, produzindo rapidamente os primeiros frutos. Algumas complicações podem ser esperadas no problema de definir dinamicamente um tipo de dados específico, que foi convertido em um tipo de interface. Os esforços também exigirão a eliminação de conflitos não óbvios com os mecanismos de verificação de tipo padrão do Pascal padrão. Por fim, além de todas as preocupações com as interfaces, uma luta desigual continua com a Microsoft em relação aos alarmes falsos do Windows Defender ao lançar alguns exemplos compilados.

Source: https://habr.com/ru/post/pt478630/


All Articles