
1. Introdução
Sejam bem-vindos, interessados em ler os desenvolvedores, não importando quais idiomas eu oriento esses artigos e cujo apoio e opiniões eu valorizo.
Para iniciantes, de acordo com tradições estabelecidas, fornecerei links para artigos anteriores:
Parte 1: escrevendo uma VM de idiomaParte 2: apresentação intermediária de programasPara formar em sua mente uma compreensão completa do que escrevemos nesses artigos, você deve se familiarizar com as partes anteriores com antecedência.
Além disso, eu deveria postar imediatamente um link para um artigo sobre um projeto que escrevi anteriormente e com base no qual toda essa discussão acontece:
Clack syudy . Talvez valha a pena se familiarizar com isso logo de início.
E um pouco sobre o projeto:
→
Site de projeto pequeno→
Repositório do GitHubBem, também direi imediatamente que tudo está escrito no Object Pascal, ou seja, no FPC.
Então, vamos começar.
O princípio de operação da maioria dos tradutores
Antes de tudo, vale a pena entender que eu não poderia escrever nada que valesse a pena sem antes me familiarizar com um monte de material teórico e várias estátuas. Descreverei o principal em algumas palavras.
A tarefa do tradutor é, antes de tudo, preparar o código para análise (por exemplo, remover comentários dele) e dividir o código em tokens (um token é o conjunto mínimo de caracteres significativos para o idioma).
Em seguida, analisando e transformando, precisamos analisar o código em uma certa representação intermediária e, em seguida, montar o aplicativo pronto para execução ou ... O que ele precisa coletar em geral.
Sim, eu realmente não disse nada com esse monte de texto - agora a tarefa está dividida em várias subtarefas.
Vamos pular como o código é preparado para execução, porque é muito chato descrever um processo. Suponha que tenhamos um conjunto de tokens prontos para análise.
Análise de código
Você pode ter ouvido falar sobre a construção de uma árvore de código e sua análise, ou coisas ainda mais obscuras. Como sempre - isso não passa de bagunçar os termos terríveis e simples. Por análise de código, quero dizer um conjunto de ações muito mais simples. A tarefa é revisar a lista de tokens e analisar o código, cada um de sua construção.
Como regra, em linguagens imperativas, o código já é apresentado na forma de um tipo de árvore a partir de estruturas.
Você deve admitir que não é aceitável iniciar o ciclo "A" no corpo do ciclo "B" e finalizá-lo fora do corpo do ciclo "B". Um código é uma estrutura que consiste em um conjunto de construções.
E o que cada design tem? Está certo - o começo e o fim (e talvez algo mais no meio, mas não o ponto).
Assim, a análise de código pode ser feita com uma única passagem, realmente sem a construção de uma árvore.
Para fazer isso, você precisa de um loop que percorrerá o código e de um grande caso de switch que executará a análise e análise do código principal.
I.e. percorremos os tokens, temos um token (por exemplo, que seja ...) "if" - eu realmente duvido que esse token possa estar no código exatamente assim -> este é o começo da construção se ... então [.. else] .. terminar!
Analisamos todos os tokens subsequentes, respectivamente, para a construção de condições em nosso idioma.
Um pouco sobre erros de código
Na fase de análise de estruturas e execução ao longo do código, é melhor não pontuar erros no processamento. Essa é uma funcionalidade útil do tradutor. Se ocorrer um erro durante a análise da estrutura, é lógico - a estrutura não foi criada corretamente e você deve notificar o desenvolvedor.
Agora sobre Mash. Como o idioma do idioma é analisado?
Acima, descrevi um conceito generalizado de um dispositivo tradutor. Agora é a hora de falar sobre o meu trabalho.
De fato, o tradutor mostrou-se muito semelhante ao descrito acima. Mas, para mim, não divide o código em um monte de tokens para análise posterior.
Antes de analisar, o código é trazido para uma forma mais bonita. Os comentários são excluídos e todas as construções são combinadas em linhas longas, se descritas em várias linhas.
Assim, em cada linha tomada separadamente, há uma construção da linguagem ou sua parte. Isso é legal, agora podemos analisar todas as linhas em nossa caixa de comutação grande, em vez de procurar essas construções no conjunto de tokens. Além disso, a vantagem aqui é que a linha tem um fim e é mais fácil determinar erros na construção com essa abordagem.
Consequentemente, a análise de estruturas individuais ocorre por métodos separados, que retornam uma representação intermediária do código de estruturas ou de suas partes.
P.S. Em um artigo anterior, descrevi a construção de um tradutor de um idioma intermediário para o bytecode de uma VM. Na verdade - essa linguagem intermediária é uma representação intermediária.
Deve-se entender que as estruturas podem consistir em várias estruturas mais simples. Porque Como analisamos cada estrutura por métodos separados, podemos chamá-los facilmente quando analisamos cada estrutura.

Execução de aquecimento no código
Para começar, o tradutor deve se familiarizar rapidamente com o código, examiná-lo e prestar atenção em alguns projetos.
Neste ponto, você pode lidar com variáveis globais, usa construções, bem como importações, procedimentos e funções e construções OOP.
É melhor gerar uma visão intermediária em vários objetos para armazenamento, para que
cole o código para variáveis globais após a inicialização, mas antes do início de main ().
O código para construções OOP pode ser inserido no final.
Designs sofisticados
Ok, descobrimos os desenhos simples. Agora é hora do complicado. Eu não acho que você conseguiu esquecer o exemplo com dois ciclos. Como sabemos, as estruturas geralmente vêm na forma de uma espécie de árvore. Isso significa que podemos analisar estruturas complexas usando a pilha.
O que a pilha tem a ver com isso? Além disso.
Primeiro, descrevemos a classe que colocaremos na pilha. Ao analisar construções complexas, podemos gerar uma representação intermediária para o início e o fim desse bloco, por exemplo, analisamos o loop for, while, till, se construções, métodos e, de fato, tudo na linguagem Mash.
Essa classe precisa de campos para armazenar representações intermediárias, meta informações (para algumas construções variáveis) e, é claro, para armazenar um tipo de bloco.
Vou apenas dar o código inteiro, porque não há muito:
unit u_prep_codeblock; {$mode objfpc}{$H+} interface uses Classes, SysUtils; type TBlockEntryType = (btProc, btFunc, btIf, btFor, btWhile, btUntil, btTry, btClass, btSwitch, btCase); TCodeBlock = class(TObject) public bType: TBlockEntryType; mName, bMeta, bMCode, bEndCode: string; constructor Create(bt: TBlockEntryType; MT, MC, EC: string); end; implementation constructor TCodeBlock.Create(bt: TBlockEntryType; MT, MC, EC: string); begin inherited Create; bType := bt; bMeta := MT; bMCode := MC; bEndCode := EC; end; end.
Bem, a pilha é uma TList simples, reinventar a roda aqui é simplesmente estúpido.
Assim, analisando a construção, digamos o mesmo loop while, assim:
function ParseWhile(s: string; varmgr: TVarManager): string; var WhileNum, ExprCode: string; begin Delete(s, 1, 5);
Sobre expressões matemáticas
Você pode não ter percebido isso, mas expressões matemáticas / lógicas também são código estruturado.
Eu implementei a análise deles de maneira empilhada. Primeiro, todos os elementos individuais da expressão são empurrados para a pilha e, em várias passagens, o código para a representação intermediária é gerado.
Várias vezes - porque existem operações matemáticas prioritárias, como multiplicação.
Eu não vejo o ponto aqui, porque é muito disso e é chato.
P.S. /lang/u_prep_expressions.pas - aqui está completamente e totalmente exposto para sua análise.
Sumário
Então, implementamos um tradutor que pode converter ... Por exemplo, este é o código:
proc PrintArr(arr): for(i ?= 0; i < len(arr); i++): PrintLn("arr[", i, "] = ", arr[i]) end end proc main(): var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] PrintArr(arr) InputLn() end
O que falta em nosso idioma? Certo, apoie OOP. Vamos falar sobre isso no meu próximo artigo.
Obrigado por ler até o fim, se você fez.
Se algo não estiver claro para você, estou aguardando seus comentários. Ou perguntas no
fórum , sim ... Sim, às vezes eu verifico.
E agora uma pequena enquete (para que eu veja e aprecie o significado dos meus artigos):