Criando um compilador em csharp: Parte 7

Parte 7! Chegamos!

Habemus for!

E mais uma vez implementamos uma funcionalidade ao vivo! Gostei desse formato.

Agora é possível escrever códigos como esse:

for int i = 1 to 10 step 2
print(1)
end

Porém, se você não assistiu, não tem pr…


This content originally appeared on DEV Community and was authored by Angelo Belchior

Parte 7! Chegamos!

Habemus for!

E mais uma vez implementamos uma funcionalidade ao vivo! Gostei desse formato.

Agora é possível escrever códigos como esse:

for int i = 1 to 10 step 2
 print(1)
end

Porém, se você não assistiu, não tem problema: vou repassar o código de ponta a ponta para que você não perca nada.

Então, vamos ao que viemos.

Se você nos acompanha, já sabe que começamos sempre fazendo o mapeamento dos tokens:

Pasta CodeAnalysis:

public enum TokenType  
{  
  .. 
  For,  
  To,  
  Step,
  ...
}

public class Token  
{  
  ...
  public const string FOR = "for";  
  public const string TO = "to";  
  public const string STEP = "step";  
  ...
  public static Token For(int position)  
      => new(TokenType.For, FOR, position);  
  public static Token To(int position)  
      => new(TokenType.To, TO, position);  
  public static Token Step(int position)  
      => new(TokenType.Step, STEP, position);  
}

Basicamente, adicionamos novos itens de enum que mapeiam os novos tokens: for, to e step.

Agora é extrair do código:

public class Lexer  
{
    private Token ExtractKeyword()  
    {  
      ...

      if (identifier.Value == Token.FOR)  
          return Token.For(position);  
      if (identifier.Value == Token.TO)  
          return Token.To(position);  
      if (identifier.Value == Token.STEP)  
          return Token.Step(position);  

      return Token.Identifier(position, identifier.Value);  
    }
}

Sem segredo e sem suar…

Agora precisamos alterar nossa classe SyntaxParser. O mais interessante é que, após muitas implementações, temos basicamente um padrão a ser seguido para adicionar uma funcionalidade nova.

Na live fiz questão de mostrar novamente como penso no momento de implementação de uma nova feature.

Analise o código abaixo:

for int i = 1 to 10 step 2
 print(1)
end

Para que eu “visualize” o fluxo de implementação, gosto de escrever o código dessa forma:

$for [int i =] {1} to {10} $step {2} < print(1) > $end
.......................................^............^
                                       |<-<-<-<-<-<-|

Explicando:

  • O que começa com $ são tokens que preciso validar a sua existência (caso seja obrigatório) e sua posição no código.
  • O que está entre colchetes é um evaluate interno, isto é, eu valido os tokens de declaração de variável e jogo na memória global (OK, não é a forma ideal, mas isso vai mudar num futuro) .
  • O que está entre { } é uma expressão e, sendo assim, vamos aplicar o EvaluateExpression. Dessa forma, podemos colocar qualquer expressão válida para calcularmos o valor.
  • O que está entre < > é um processo que vai ser executado inúmeras vezes, e para isso vamos invocar o método ParseBlock que por sua vez efetua um EvaluateToken caso o token atual não seja um token delimitador de bloco.

Basicamente é isso. E caso você não tenha entendido o que é um token delimitador de bloco, explico: um bloco de código é aquele que começa com um determinado token e termina com end ou - no caso do if - else.

if -> end
if -> else -> end
while -> end
for -> end

Sendo, assim, precisamos saber exatamente quando existiu uma abertura e um fechamento de bloco. Falei mais sobre isso no post anterior.

Arquivo CodeAnalysis/SyntaxParser.cs:

public class SyntaxParser(Dictionary<string, Identifier> variables, List<Token> tokens)  
{
    ...
    private Identifier EvaluateToken()  
        => CurrentToken.Type switch  
        {  
            ...
            TokenType.For => EvaluateFor()
            ...
        };
    ...

    private Identifier EvaluateFor()
    {
        // Valido se o token é o for
        NextIfTokenIs(TokenType.For);

        /// Extraio a declaração de variável
        NextIfTokenIs(TokenType.DataType);
        var variableToken = NextIfTokenIs(TokenType.Identifier);
        var variableName = variableToken.Value;

        // Inicializo o valor da variável que dá início ao for como 1
        var from = new Identifier(DataTypes.Int, 1);
        // Caso exista uma atribuição na declaração de variável, o evaluate expression é invocado.
        if (CurrentToken.Type == TokenType.Assign)
        {
            NextIfTokenIs(TokenType.Assign);
            from = EvaluateExpression();
        }
        // Seto o valor do from na "memória"
        variables[variableName] = from;

        // Validamos e aavançamos até o to
        NextIfTokenIs(TokenType.To);
        // Obtemos o valor informado ao to a partir de uma expressão. 
        var to = EvaluateExpression();
        var end = to.ToInt();

        // O step é opcional sendo seu valor inicial 1 
        var stepValue = 1;
        // Caso o step seja informado, o seu valor é obtido a partir de uma expressão. 
        if (CurrentToken.Type == TokenType.Step)
        {
            NextIfTokenIs(TokenType.Step);
            var step = EvaluateExpression();
            stepValue = step.ToInt();
        }

        // Após obter todos os dados referente a declaração do for, armazeno a posição atual do token. Isso serve para que o looping volte sempre para o lugar correto.
        var forPosition = _currentPosition;
        // efetuamos o looping (tanto crescente, quanto decrescente)
        while (
            (stepValue > 0 && variables[variableName].ToInt() <= end) ||
            (stepValue < 0 && variables[variableName].ToInt() >= end)
        )
        {
            // Fazemos o parse do corpo do for
            ParseBlock();
            variables[variableName] = new Identifier(DataTypes.Int, variables[variableName].ToInt() + stepValue);
            _currentPosition = forPosition;
        }

       // Avançamos até sair do for
        SkipBlockUntil();

        // Validamos se o for termina com o end
        NextIfTokenIs(TokenType.End);
        return Identifier.None;
    }
}

Fiz questão de explicar trecho por trecho no próprio código e acredito que não tem segredo.

O segredo é “visualizar” o trecho de código que será analisado, demarcando cada ponto. Dessa forma, fica muito fácil implementar cada parte já que temos praticamente tudo feito pelo fato de termos implementado if/else e while.

É isso. Simples assim.

Curtiu?

Código-fonte está em https://github.com/angelobelchior/Pug.Compiler/tree/parte7

Muito obrigado e até a próxima!


This content originally appeared on DEV Community and was authored by Angelo Belchior


Print Share Comment Cite Upload Translate Updates
APA

Angelo Belchior | Sciencx (2025-08-22T23:45:00+00:00) Criando um compilador em csharp: Parte 7. Retrieved from https://www.scien.cx/2025/08/22/criando-um-compilador-em-csharp-parte-7/

MLA
" » Criando um compilador em csharp: Parte 7." Angelo Belchior | Sciencx - Friday August 22, 2025, https://www.scien.cx/2025/08/22/criando-um-compilador-em-csharp-parte-7/
HARVARD
Angelo Belchior | Sciencx Friday August 22, 2025 » Criando um compilador em csharp: Parte 7., viewed ,<https://www.scien.cx/2025/08/22/criando-um-compilador-em-csharp-parte-7/>
VANCOUVER
Angelo Belchior | Sciencx - » Criando um compilador em csharp: Parte 7. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2025/08/22/criando-um-compilador-em-csharp-parte-7/
CHICAGO
" » Criando um compilador em csharp: Parte 7." Angelo Belchior | Sciencx - Accessed . https://www.scien.cx/2025/08/22/criando-um-compilador-em-csharp-parte-7/
IEEE
" » Criando um compilador em csharp: Parte 7." Angelo Belchior | Sciencx [Online]. Available: https://www.scien.cx/2025/08/22/criando-um-compilador-em-csharp-parte-7/. [Accessed: ]
rf:citation
» Criando um compilador em csharp: Parte 7 | Angelo Belchior | Sciencx | https://www.scien.cx/2025/08/22/criando-um-compilador-em-csharp-parte-7/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.