Rust

Compiladores e Interpretadores — Construindo uma Linguagem com Rust Já leu

26 min de leitura

Compiladores e Interpretadores — Construindo uma Linguagem com Rust
Rust — Artigo #44 Compiladores e Interpretadores — Construindo uma Linguagem com Rust Por Prof. Dr. Marcelo Fontes | Série: Dominando Rust em 1 Ano Construir um

Rust — Artigo #44

Compiladores e Interpretadores — Construindo uma Linguagem com Rust

Por Prof. Dr. Marcelo Fontes | Série: Dominando Rust em 1 Ano


Construir um compilador ou interpretador é um dos projetos mais reveladores da computação. Você implementa todos os conceitos que usa diariamente — tokenização, parsing, análise semântica, avaliação — e entende profundamente por que linguagens funcionam da forma que funcionam.

Rust é excepcionalmente adequado para esta tarefa. Enums com dados são perfeitos para representar ASTs (Abstract Syntax Trees). O pattern matching exaustivo garante que todos os casos são tratados. O sistema de tipos captura invariantes da linguagem em tempo de compilação.

Neste artigo construímos uma linguagem completa — Mira — com variáveis, funções, closures, condicionais, laços e tipos básicos.


Arquitetura do interpretador

Código fonte (texto)
       ↓
   [Lexer]
       ↓
  Tokens (Vec<Token>)
       ↓
   [Parser]
       ↓
   AST (Expr, Stmt)
       ↓
 [Analisador Semântico]
       ↓
  AST Verificado
       ↓
  [Interpretador]
       ↓
   Valor (resultado)

A linguagem Mira — gramática

// Exemplos de código Mira:

var x = 42;
var nome = "Rust";
var lista = [1, 2, 3, 4, 5];

fn soma(a, b) {
    return a + b;
}

fn fatorial(n) {
    if n <= 1 {
        return 1;
    }
    return n * fatorial(n - 1);
}

var resultado = soma(10, 20);
print(resultado);   // 30

var i = 0;
while i < 5 {
    print(i);
    i = i + 1;
}

var dobrar = fn(x) { return x * 2; };
print(dobrar(21));  // 42

Lexer — tokenização

// src/lexer.rs

#[derive(Debug, Clone, PartialEq)]
pub enum Token {
    // Literais
    Numero(f64),
    Texto(String),
    Booleano(bool),
    Nulo,

    // Identificadores e palavras-chave
    Identificador(String),
    Var,
    Fn,
    Return,
    If,
    Else,
    While,
    For,
    In,
    Print,
    True,
    False,

    // Operadores aritméticos
    Mais,        // +
    Menos,       // -
    Asterisco,   // *
    Barra,       // /
    Percentual,  // %

    // Operadores relacionais
    Igual,       // ==
    DiferenteDe, // !=
    Menor,       // <
    MenorIgual,  // <=
    Maior,       // >
    MaiorIgual,  // >=

    // Operadores lógicos
    E,           // &&
    Ou,          // ||
    Nao,         // !

    // Atribuição
    Atribuicao,  // =

    // Pontuação
    ParenEsq,    // (
    ParenDir,    // )
    ChaveEsq,    // {
    ChaveDir,    // }
    ColcheteEsq, // [
    ColcheteDir, // ]
    Virgula,     // ,
    PontoEVirg,  // ;
    Ponto,       // .

    // Controle
    EOF,
}

#[derive(Debug, Clone)]
pub struct TokenComPosicao {
    pub token: Token,
    pub linha: usize,
    pub coluna: usize,
}

#[derive(Debug)]
pub struct ErroLexer {
    pub mensagem: String,
    pub linha: usize,
    pub coluna: usize,
}

impl std::fmt::Display for ErroLexer {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "[Linha {}:{}] {}", self.linha, self.coluna, self.mensagem)
    }
}

pub struct Lexer {
    fonte: Vec<char>,
    posicao: usize,
    linha: usize,
    coluna: usize,
}

impl Lexer {
    pub fn novo(fonte: &str) -> Self {
        Lexer {
            fonte: fonte.chars().collect(),
            posicao: 0,
            linha: 1,
            coluna: 1,
        }
    }

    fn atual(&self) -> Option<char> {
        self.fonte.get(self.posicao).copied()
    }

    fn proximo(&self) -> Option<char> {
        self.fonte.get(self.posicao + 1).copied()
    }

    fn avancar(&mut self) -> Option<char> {
        let c = self.atual();
        self.posicao += 1;
        if c == Some('
') {
            self.linha += 1;
            self.coluna = 1;
        } else {
            self.coluna += 1;
        }
        c
    }

    fn pular_espacos(&mut self) {
        while let Some(c) = self.atual() {
            if c == '/' && self.proximo() == Some('/') {
                // Comentário de linha
                while self.atual() != Some('
') && self.atual().is_some() {
                    self.avancar();
                }
            } else if c.is_whitespace() {
                self.avancar();
            } else {
                break;
            }
        }
    }

    fn ler_numero(&mut self) -> f64 {
        let mut s = String::new();
        while let Some(c) = self.atual() {
            if c.is_ascii_digit() || c == '.' {
                s.push(c);
                self.avancar();
            } else {
                break;
            }
        }
        s.parse().unwrap_or(0.0)
    }

    fn ler_texto(&mut self) -> Result<String, ErroLexer> {
        self.avancar(); // pula a aspas inicial
        let mut s = String::new();
        loop {
            match self.atual() {
                None => return Err(ErroLexer {
                    mensagem: "String não fechada".to_string(),
                    linha: self.linha,
                    coluna: self.coluna,
                }),
                Some('"') => {
                    self.avancar();
                    return Ok(s);
                }
                Some('\') => {
                    self.avancar();
                    match self.atual() {
                        Some('n')  => { s.push('
'); self.avancar(); }
                        Some('t')  => { s.push('	'); self.avancar(); }
                        Some('"')  => { s.push('"');  self.avancar(); }
                        Some('\') => { s.push('\'); self.avancar(); }
                        Some(c) => return Err(ErroLexer {
                            mensagem: format!("Escape inválido: \{c}"),
                            linha: self.linha,
                            coluna: self.coluna,
                        }),
                        None => return Err(ErroLexer {
                            mensagem: "Fim inesperado após \".to_string(),
                            linha: self.linha,
                            coluna: self.coluna,
                        }),
                    }
                }
                Some(c) => {
                    s.push(c);
                    self.avancar();
                }
            }
        }
    }

    fn ler_identificador(&mut self) -> String {
        let mut s = String::new();
        while let Some(c) = self.atual() {
            if c.is_alphanumeric() || c == '_' {
                s.push(c);
                self.avancar();
            } else {
                break;
            }
        }
        s
    }

    fn palavra_chave_ou_ident(&self, s: String) -> Token {
        match s.as_str() {
            "var"    => Token::Var,
            "fn"     => Token::Fn,
            "return" => Token::Return,
            "if"     => Token::If,
            "else"   => Token::Else,
            "while"  => Token::While,
            "for"    => Token::For,
            "in"     => Token::In,
            "print"  => Token::Print,
            "true"   => Token::Booleano(true),
            "false"  => Token::Booleano(false),
            "nulo"   => Token::Nulo,
            _        => Token::Identificador(s),
        }
    }

    pub fn tokenizar(&mut self) -> Result<Vec<TokenComPosicao>, ErroLexer> {
        let mut tokens = Vec::new();

        loop {
            self.pular_espacos();

            let linha = self.linha;
            let coluna = self.coluna;

            let token = match self.atual() {
                None => {
                    tokens.push(TokenComPosicao {
                        token: Token::EOF, linha, coluna
                    });
                    break;
                }

                Some(c) if c.is_ascii_digit() => {
                    Token::Numero(self.ler_numero())
                }

                Some('"') => {
                    Token::Texto(self.ler_texto()?)
                }

                Some(c) if c.is_alphabetic() || c == '_' => {
                    let ident = self.ler_identificador();
                    self.palavra_chave_ou_ident(ident)
                }

                Some('+') => { self.avancar(); Token::Mais }
                Some('-') => { self.avancar(); Token::Menos }
                Some('*') => { self.avancar(); Token::Asterisco }
                Some('/') => { self.avancar(); Token::Barra }
                Some('%') => { self.avancar(); Token::Percentual }
                Some('(') => { self.avancar(); Token::ParenEsq }
                Some(')') => { self.avancar(); Token::ParenDir }
                Some('{') => { self.avancar(); Token::ChaveEsq }
                Some('}') => { self.avancar(); Token::ChaveDir }
                Some('[') => { self.avancar(); Token::ColcheteEsq }
                Some(']') => { self.avancar(); Token::ColcheteDir }
                Some(',') => { self.avancar(); Token::Virgula }
                Some(';') => { self.avancar(); Token::PontoEVirg }
                Some('.') => { self.avancar(); Token::Ponto }

                Some('=') => {
                    self.avancar();
                    if self.atual() == Some('=') {
                        self.avancar();
                        Token::Igual
                    } else {
                        Token::Atribuicao
                    }
                }
                Some('!') => {
                    self.avancar();
                    if self.atual() == Some('=') {
                        self.avancar();
                        Token::DiferenteDe
                    } else {
                        Token::Nao
                    }
                }
                Some('<') => {
                    self.avancar();
                    if self.atual() == Some('=') {
                        self.avancar();
                        Token::MenorIgual
                    } else {
                        Token::Menor
                    }
                }
                Some('>') => {
                    self.avancar();
                    if self.atual() == Some('=') {
                        self.avancar();
                        Token::MaiorIgual
                    } else {
                        Token::Maior
                    }
                }
                Some('&') => {
                    self.avancar();
                    if self.atual() == Some('&') {
                        self.avancar();
                        Token::E
                    } else {
                        return Err(ErroLexer {
                            mensagem: "Esperado '&&'".to_string(),
                            linha, coluna,
                        });
                    }
                }
                Some('|') => {
                    self.avancar();
                    if self.atual() == Some('|') {
                        self.avancar();
                        Token::Ou
                    } else {
                        return Err(ErroLexer {
                            mensagem: "Esperado '||'".to_string(),
                            linha, coluna,
                        });
                    }
                }

                Some(c) => {
                    return Err(ErroLexer {
                        mensagem: format!("Caractere inesperado: '{c}'"),
                        linha, coluna,
                    });
                }
            };

            tokens.push(TokenComPosicao { token, linha, coluna });
        }

        Ok(tokens)
    }
}

AST — Abstract Syntax Tree

// src/ast.rs

/// Expressões — produzem um valor
#[derive(Debug, Clone)]
pub enum Expr {
    // Literais
    Numero(f64),
    Texto(String),
    Booleano(bool),
    Nulo,
    Lista(Vec<Expr>),

    // Variáveis e funções
    Variavel(String),
    Funcao {
        parametros: Vec<String>,
        corpo: Vec<Stmt>,
    },
    Chamada {
        funcao: Box<Expr>,
        argumentos: Vec<Expr>,
    },

    // Acesso a índice: lista[i]
    Indice {
        objeto: Box<Expr>,
        indice: Box<Expr>,
    },

    // Operações binárias
    Binaria {
        esquerda: Box<Expr>,
        operador: OperadorBin,
        direita: Box<Expr>,
    },

    // Operação unária
    Unaria {
        operador: OperadorUn,
        operando: Box<Expr>,
    },
}

#[derive(Debug, Clone)]
pub enum OperadorBin {
    Soma, Sub, Mul, Div, Mod,
    Igual, Diferente,
    Menor, MenorIgual,
    Maior, MaiorIgual,
    E, Ou,
}

#[derive(Debug, Clone)]
pub enum OperadorUn {
    Negativo,
    Nao,
}

/// Statements — executam efeitos colaterais
#[derive(Debug, Clone)]
pub enum Stmt {
    Expressao(Expr),
    Var { nome: String, valor: Expr },
    Atribuir { nome: String, valor: Expr },
    AtribuirIndice {
        objeto: String,
        indice: Expr,
        valor: Expr,
    },
    Retornar(Expr),
    Print(Expr),
    Se {
        condicao: Expr,
        entao: Vec<Stmt>,
        senao: Option<Vec<Stmt>>,
    },
    Enquanto {
        condicao: Expr,
        corpo: Vec<Stmt>,
    },
    Para {
        variavel: String,
        iteravel: Expr,
        corpo: Vec<Stmt>,
    },
    FuncaoNomeada {
        nome: String,
        parametros: Vec<String>,
        corpo: Vec<Stmt>,
    },
}

Parser — da sequência de tokens à AST

// src/parser.rs
use crate::ast::*;
use crate::lexer::{Token, TokenComPosicao};

#[derive(Debug)]
pub struct ErroParser {
    pub mensagem: String,
    pub linha: usize,
    pub coluna: usize,
}

impl std::fmt::Display for ErroParser {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "[Linha {}:{}] {}", self.linha, self.coluna, self.mensagem)
    }
}

pub struct Parser {
    tokens: Vec<TokenComPosicao>,
    posicao: usize,
}

impl Parser {
    pub fn novo(tokens: Vec<TokenComPosicao>) -> Self {
        Parser { tokens, posicao: 0 }
    }

    fn atual(&self) -> &TokenComPosicao {
        &self.tokens[self.posicao.min(self.tokens.len() - 1)]
    }

    fn avancar(&mut self) -> &TokenComPosicao {
        let t = self.atual();
        if self.posicao < self.tokens.len() - 1 {
            self.posicao += 1;
        }
        t
    }

    fn esperar(&mut self, esperado: &Token) -> Result<(), ErroParser> {
        let atual = self.atual();
        if &atual.token == esperado {
            self.avancar();
            Ok(())
        } else {
            Err(ErroParser {
                mensagem: format!(
                    "Esperado {:?}, encontrado {:?}",
                    esperado, atual.token
                ),
                linha: atual.linha,
                coluna: atual.coluna,
            })
        }
    }

    fn erro(&self, msg: &str) -> ErroParser {
        let t = self.atual();
        ErroParser {
            mensagem: msg.to_string(),
            linha: t.linha,
            coluna: t.coluna,
        }
    }

    pub fn parsear(&mut self) -> Result<Vec<Stmt>, ErroParser> {
        let mut stmts = Vec::new();
        while self.atual().token != Token::EOF {
            stmts.push(self.parsear_stmt()?);
        }
        Ok(stmts)
    }

    fn parsear_stmt(&mut self) -> Result<Stmt, ErroParser> {
        match &self.atual().token {
            Token::Var    => self.parsear_var(),
            Token::Fn     => self.parsear_funcao_nomeada(),
            Token::Return => self.parsear_retornar(),
            Token::Print  => self.parsear_print(),
            Token::If     => self.parsear_se(),
            Token::While  => self.parsear_enquanto(),
            Token::For    => self.parsear_para(),
            _             => self.parsear_expr_stmt(),
        }
    }

    fn parsear_var(&mut self) -> Result<Stmt, ErroParser> {
        self.avancar(); // consome 'var'
        let nome = match &self.atual().token {
            Token::Identificador(n) => n.clone(),
            _ => return Err(self.erro("Esperado nome de variável")),
        };
        self.avancar(); // consome nome
        self.esperar(&Token::Atribuicao)?;
        let valor = self.parsear_expr()?;
        self.esperar(&Token::PontoEVirg)?;
        Ok(Stmt::Var { nome, valor })
    }

    fn parsear_funcao_nomeada(&mut self) -> Result<Stmt, ErroParser> {
        self.avancar(); // consome 'fn'
        let nome = match &self.atual().token {
            Token::Identificador(n) => n.clone(),
            _ => return Err(self.erro("Esperado nome de função")),
        };
        self.avancar();
        let (parametros, corpo) = self.parsear_def_funcao()?;
        Ok(Stmt::FuncaoNomeada { nome, parametros, corpo })
    }

    fn parsear_def_funcao(
        &mut self
    ) -> Result<(Vec<String>, Vec<Stmt>), ErroParser> {
        self.esperar(&Token::ParenEsq)?;
        let mut params = Vec::new();
        while self.atual().token != Token::ParenDir {
            match &self.atual().token {
                Token::Identificador(n) => params.push(n.clone()),
                _ => return Err(self.erro("Esperado parâmetro")),
            }
            self.avancar();
            if self.atual().token == Token::Virgula {
                self.avancar();
            }
        }
        self.esperar(&Token::ParenDir)?;
        let corpo = self.parsear_bloco()?;
        Ok((params, corpo))
    }

    fn parsear_bloco(&mut self) -> Result<Vec<Stmt>, ErroParser> {
        self.esperar(&Token::ChaveEsq)?;
        let mut stmts = Vec::new();
        while self.atual().token != Token::ChaveDir
            && self.atual().token != Token::EOF
        {
            stmts.push(self.parsear_stmt()?);
        }
        self.esperar(&Token::ChaveDir)?;
        Ok(stmts)
    }

    fn parsear_retornar(&mut self) -> Result<Stmt, ErroParser> {
        self.avancar(); // consome 'return'
        let valor = self.parsear_expr()?;
        self.esperar(&Token::PontoEVirg)?;
        Ok(Stmt::Retornar(valor))
    }

    fn parsear_print(&mut self) -> Result<Stmt, ErroParser> {
        self.avancar(); // consome 'print'
        self.esperar(&Token::ParenEsq)?;
        let expr = self.parsear_expr()?;
        self.esperar(&Token::ParenDir)?;
        self.esperar(&Token::PontoEVirg)?;
        Ok(Stmt::Print(expr))
    }

    fn parsear_se(&mut self) -> Result<Stmt, ErroParser> {
        self.avancar(); // consome 'if'
        let condicao = self.parsear_expr()?;
        let entao = self.parsear_bloco()?;
        let senao = if self.atual().token == Token::Else {
            self.avancar();
            Some(self.parsear_bloco()?)
        } else {
            None
        };
        Ok(Stmt::Se { condicao, entao, senao })
    }

    fn parsear_enquanto(&mut self) -> Result<Stmt, ErroParser> {
        self.avancar(); // consome 'while'
        let condicao = self.parsear_expr()?;
        let corpo = self.parsear_bloco()?;
        Ok(Stmt::Enquanto { condicao, corpo })
    }

    fn parsear_para(&mut self) -> Result<Stmt, ErroParser> {
        self.avancar(); // consome 'for'
        let variavel = match &self.atual().token {
            Token::Identificador(n) => n.clone(),
            _ => return Err(self.erro("Esperado variável no for")),
        };
        self.avancar();
        self.esperar(&Token::In)?;
        let iteravel = self.parsear_expr()?;
        let corpo = self.parsear_bloco()?;
        Ok(Stmt::Para { variavel, iteravel, corpo })
    }

    fn parsear_expr_stmt(&mut self) -> Result<Stmt, ErroParser> {
        let expr = self.parsear_expr()?;

        // Atribuição: ident = expr
        if self.atual().token == Token::Atribuicao {
            self.avancar();
            let valor = self.parsear_expr()?;
            self.esperar(&Token::PontoEVirg)?;

            return match expr {
                Expr::Variavel(nome) =>
                    Ok(Stmt::Atribuir { nome, valor }),
                Expr::Indice { objeto, indice } => {
                    if let Expr::Variavel(nome) = *objeto {
                        Ok(Stmt::AtribuirIndice {
                            objeto: nome,
                            indice: *indice,
                            valor,
                        })
                    } else {
                        Err(self.erro("Alvo de atribuição inválido"))
                    }
                }
                _ => Err(self.erro("Alvo de atribuição inválido")),
            };
        }

        self.esperar(&Token::PontoEVirg)?;
        Ok(Stmt::Expressao(expr))
    }

    // ── Parsing de expressões com precedência ──────────

    fn parsear_expr(&mut self) -> Result<Expr, ErroParser> {
        self.parsear_ou()
    }

    fn parsear_ou(&mut self) -> Result<Expr, ErroParser> {
        let mut esq = self.parsear_e()?;
        while self.atual().token == Token::Ou {
            self.avancar();
            let dir = self.parsear_e()?;
            esq = Expr::Binaria {
                esquerda: Box::new(esq),
                operador: OperadorBin::Ou,
                direita: Box::new(dir),
            };
        }
        Ok(esq)
    }

    fn parsear_e(&mut self) -> Result<Expr, ErroParser> {
        let mut esq = self.parsear_igualdade()?;
        while self.atual().token == Token::E {
            self.avancar();
            let dir = self.parsear_igualdade()?;
            esq = Expr::Binaria {
                esquerda: Box::new(esq),
                operador: OperadorBin::E,
                direita: Box::new(dir),
            };
        }
        Ok(esq)
    }

    fn parsear_igualdade(&mut self) -> Result<Expr, ErroParser> {
        let mut esq = self.parsear_comparacao()?;
        loop {
            let op = match self.atual().token {
                Token::Igual       => OperadorBin::Igual,
                Token::DiferenteDe => OperadorBin::Diferente,
                _ => break,
            };
            self.avancar();
            let dir = self.parsear_comparacao()?;
            esq = Expr::Binaria {
                esquerda: Box::new(esq),
                operador: op,
                direita: Box::new(dir),
            };
        }
        Ok(esq)
    }

    fn parsear_comparacao(&mut self) -> Result<Expr, ErroParser> {
        let mut esq = self.parsear_adicao()?;
        loop {
            let op = match self.atual().token {
                Token::Menor      => OperadorBin::Menor,
                Token::MenorIgual => OperadorBin::MenorIgual,
                Token::Maior      => OperadorBin::Maior,
                Token::MaiorIgual => OperadorBin::MaiorIgual,
                _ => break,
            };
            self.avancar();
            let dir = self.parsear_adicao()?;
            esq = Expr::Binaria {
                esquerda: Box::new(esq),
                operador: op,
                direita: Box::new(dir),
            };
        }
        Ok(esq)
    }

    fn parsear_adicao(&mut self) -> Result<Expr, ErroParser> {
        let mut esq = self.parsear_multiplicacao()?;
        loop {
            let op = match self.atual().token {
                Token::Mais  => OperadorBin::Soma,
                Token::Menos => OperadorBin::Sub,
                _ => break,
            };
            self.avancar();
            let dir = self.parsear_multiplicacao()?;
            esq = Expr::Binaria {
                esquerda: Box::new(esq),
                operador: op,
                direita: Box::new(dir),
            };
        }
        Ok(esq)
    }

    fn parsear_multiplicacao(&mut self) -> Result<Expr, ErroParser> {
        let mut esq = self.parsear_unaria()?;
        loop {
            let op = match self.atual().token {
                Token::Asterisco  => OperadorBin::Mul,
                Token::Barra      => OperadorBin::Div,
                Token::Percentual => OperadorBin::Mod,
                _ => break,
            };
            self.avancar();
            let dir = self.parsear_unaria()?;
            esq = Expr::Binaria {
                esquerda: Box::new(esq),
                operador: op,
                direita: Box::new(dir),
            };
        }
        Ok(esq)
    }

    fn parsear_unaria(&mut self) -> Result<Expr, ErroParser> {
        match self.atual().token {
            Token::Menos => {
                self.avancar();
                let op = self.parsear_unaria()?;
                Ok(Expr::Unaria {
                    operador: OperadorUn::Negativo,
                    operando: Box::new(op),
                })
            }
            Token::Nao => {
                self.avancar();
                let op = self.parsear_unaria()?;
                Ok(Expr::Unaria {
                    operador: OperadorUn::Nao,
                    operando: Box::new(op),
                })
            }
            _ => self.parsear_chamada(),
        }
    }

    fn parsear_chamada(&mut self) -> Result<Expr, ErroParser> {
        let mut expr = self.parsear_primaria()?;

        loop {
            match self.atual().token {
                Token::ParenEsq => {
                    self.avancar();
                    let mut args = Vec::new();
                    while self.atual().token != Token::ParenDir {
                        args.push(self.parsear_expr()?);
                        if self.atual().token == Token::Virgula {
                            self.avancar();
                        }
                    }
                    self.esperar(&Token::ParenDir)?;
                    expr = Expr::Chamada {
                        funcao: Box::new(expr),
                        argumentos: args,
                    };
                }
                Token::ColcheteEsq => {
                    self.avancar();
                    let indice = self.parsear_expr()?;
                    self.esperar(&Token::ColcheteDir)?;
                    expr = Expr::Indice {
                        objeto: Box::new(expr),
                        indice: Box::new(indice),
                    };
                }
                _ => break,
            }
        }

        Ok(expr)
    }

    fn parsear_primaria(&mut self) -> Result<Expr, ErroParser> {
        let t = self.atual().clone();
        match t.token {
            Token::Numero(n)      => { self.avancar(); Ok(Expr::Numero(n)) }
            Token::Texto(s)       => { self.avancar(); Ok(Expr::Texto(s)) }
            Token::Booleano(b)    => { self.avancar(); Ok(Expr::Booleano(b)) }
            Token::Nulo           => { self.avancar(); Ok(Expr::Nulo) }

            Token::Identificador(nome) => {
                self.avancar();
                Ok(Expr::Variavel(nome))
            }

            Token::Fn => {
                self.avancar();
                let (params, corpo) = self.parsear_def_funcao()?;
                Ok(Expr::Funcao { parametros: params, corpo })
            }

            Token::ColcheteEsq => {
                self.avancar();
                let mut elementos = Vec::new();
                while self.atual().token != Token::ColcheteDir {
                    elementos.push(self.parsear_expr()?);
                    if self.atual().token == Token::Virgula {
                        self.avancar();
                    }
                }
                self.esperar(&Token::ColcheteDir)?;
                Ok(Expr::Lista(elementos))
            }

            Token::ParenEsq => {
                self.avancar();
                let expr = self.parsear_expr()?;
                self.esperar(&Token::ParenDir)?;
                Ok(expr)
            }

            _ => Err(ErroParser {
                mensagem: format!("Token inesperado: {:?}", t.token),
                linha: t.linha,
                coluna: t.coluna,
            }),
        }
    }
}

Interpretador — avaliação da AST

// src/interpretador.rs
use crate::ast::*;
use std::collections::HashMap;
use std::cell::RefCell;
use std::rc::Rc;

// Valores em tempo de execução
#[derive(Debug, Clone)]
pub enum Valor {
    Numero(f64),
    Texto(String),
    Booleano(bool),
    Nulo,
    Lista(Rc<RefCell<Vec<Valor>>>),
    Funcao {
        parametros: Vec<String>,
        corpo: Vec<Stmt>,
        ambiente: AmbienteRef,
    },
    FuncaoNativa(String, fn(Vec<Valor>) -> Result<Valor, ErroExec>),
}

impl std::fmt::Display for Valor {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
            Valor::Numero(n)   => {
                if n.fract() == 0.0 && n.abs() < 1e15 {
                    write!(f, "{}", *n as i64)
                } else {
                    write!(f, "{n}")
                }
            }
            Valor::Texto(s)    => write!(f, "{s}"),
            Valor::Booleano(b) => write!(f, "{b}"),
            Valor::Nulo        => write!(f, "nulo"),
            Valor::Lista(l)    => {
                let items: Vec<String> = l.borrow()
                    .iter().map(|v| v.to_string()).collect();
                write!(f, "[{}]", items.join(", "))
            }
            Valor::Funcao { parametros, .. } =>
                write!(f, "<fn({})>", parametros.join(", ")),
            Valor::FuncaoNativa(nome, _) =>
                write!(f, "<nativa:{nome}>"),
        }
    }
}

#[derive(Debug)]
pub enum ErroExec {
    Retornar(Valor),   // usado para implementar return
    Erro(String),
}

impl std::fmt::Display for ErroExec {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
            ErroExec::Retornar(_) => write!(f, "retorno inesperado"),
            ErroExec::Erro(msg)   => write!(f, "{msg}"),
        }
    }
}

// Ambiente de variáveis com escopo encadeado
pub type AmbienteRef = Rc<RefCell<Ambiente>>;

pub struct Ambiente {
    variaveis: HashMap<String, Valor>,
    pai: Option<AmbienteRef>,
}

impl Ambiente {
    pub fn novo() -> AmbienteRef {
        Rc::new(RefCell::new(Ambiente {
            variaveis: HashMap::new(),
            pai: None,
        }))
    }

    pub fn filho(pai: &AmbienteRef) -> AmbienteRef {
        Rc::new(RefCell::new(Ambiente {
            variaveis: HashMap::new(),
            pai: Some(Rc::clone(pai)),
        }))
    }

    pub fn definir(&mut self, nome: &str, valor: Valor) {
        self.variaveis.insert(nome.to_string(), valor);
    }

    pub fn obter(&self, nome: &str) -> Option<Valor> {
        if let Some(v) = self.variaveis.get(nome) {
            return Some(v.clone());
        }
        self.pai.as_ref()?.borrow().obter(nome)
    }

    pub fn atribuir(&mut self, nome: &str, valor: Valor) -> bool {
        if self.variaveis.contains_key(nome) {
            self.variaveis.insert(nome.to_string(), valor);
            return true;
        }
        if let Some(pai) = &self.pai {
            return pai.borrow_mut().atribuir(nome, valor);
        }
        false
    }
}

pub struct Interpretador {
    ambiente_global: AmbienteRef,
}

impl Interpretador {
    pub fn novo() -> Self {
        let global = Ambiente::novo();

        // Funções nativas
        global.borrow_mut().definir(
            "tamanho",
            Valor::FuncaoNativa("tamanho", |args| {
                match args.first() {
                    Some(Valor::Lista(l)) =>
                        Ok(Valor::Numero(l.borrow().len() as f64)),
                    Some(Valor::Texto(s)) =>
                        Ok(Valor::Numero(s.len() as f64)),
                    _ => Err(ErroExec::Erro("tamanho() espera lista ou texto".into())),
                }
            }),
        );

        global.borrow_mut().definir(
            "str",
            Valor::FuncaoNativa("str", |args| {
                match args.first() {
                    Some(v) => Ok(Valor::Texto(v.to_string())),
                    None    => Err(ErroExec::Erro("str() espera 1 argumento".into())),
                }
            }),
        );

        global.borrow_mut().definir(
            "num",
            Valor::FuncaoNativa("num", |args| {
                match args.first() {
                    Some(Valor::Texto(s)) => s.parse::<f64>()
                        .map(Valor::Numero)
                        .map_err(|_| ErroExec::Erro(format!("Não é número: '{s}'"))),
                    Some(Valor::Numero(n)) => Ok(Valor::Numero(*n)),
                    _ => Err(ErroExec::Erro("num() espera texto ou número".into())),
                }
            }),
        );

        Interpretador { ambiente_global: global }
    }

    pub fn executar(&mut self, stmts: &[Stmt]) -> Result<(), ErroExec> {
        let amb = Rc::clone(&self.ambiente_global);
        self.executar_bloco(stmts, &amb)
    }

    fn executar_bloco(
        &mut self,
        stmts: &[Stmt],
        amb: &AmbienteRef,
    ) -> Result<(), ErroExec> {
        for stmt in stmts {
            self.executar_stmt(stmt, amb)?;
        }
        Ok(())
    }

    fn executar_stmt(
        &mut self,
        stmt: &Stmt,
        amb: &AmbienteRef,
    ) -> Result<(), ErroExec> {
        match stmt {
            Stmt::Expressao(expr) => {
                self.avaliar(expr, amb)?;
            }

            Stmt::Var { nome, valor } => {
                let v = self.avaliar(valor, amb)?;
                amb.borrow_mut().definir(nome, v);
            }

            Stmt::Atribuir { nome, valor } => {
                let v = self.avaliar(valor, amb)?;
                if !amb.borrow_mut().atribuir(nome, v) {
                    return Err(ErroExec::Erro(
                        format!("Variável não definida: '{nome}'")
                    ));
                }
            }

            Stmt::AtribuirIndice { objeto, indice, valor } => {
                let idx = self.avaliar(indice, amb)?;
                let val = self.avaliar(valor, amb)?;
                let lista = amb.borrow().obter(objeto)
                    .ok_or_else(|| ErroExec::Erro(
                        format!("Variável não definida: '{objeto}'")
                    ))?;

                if let Valor::Lista(l) = lista {
                    if let Valor::Numero(i) = idx {
                        let i = i as usize;
                        let mut lst = l.borrow_mut();
                        if i < lst.len() {
                            lst[i] = val;
                        } else {
                            return Err(ErroExec::Erro(
                                format!("Índice fora dos limites: {i}")
                            ));
                        }
                    }
                }
            }

            Stmt::Retornar(expr) => {
                let v = self.avaliar(expr, amb)?;
                return Err(ErroExec::Retornar(v));
            }

            Stmt::Print(expr) => {
                let v = self.avaliar(expr, amb)?;
                println!("{v}");
            }

            Stmt::Se { condicao, entao, senao } => {
                let cond = self.avaliar(condicao, amb)?;
                if e_verdadeiro(&cond) {
                    let escopo = Ambiente::filho(amb);
                    self.executar_bloco(entao, &escopo)?;
                } else if let Some(else_stmts) = senao {
                    let escopo = Ambiente::filho(amb);
                    self.executar_bloco(else_stmts, &escopo)?;
                }
            }

            Stmt::Enquanto { condicao, corpo } => {
                loop {
                    let cond = self.avaliar(condicao, amb)?;
                    if !e_verdadeiro(&cond) { break; }
                    let escopo = Ambiente::filho(amb);
                    self.executar_bloco(corpo, &escopo)?;
                }
            }

            Stmt::Para { variavel, iteravel, corpo } => {
                let iter = self.avaliar(iteravel, amb)?;
                if let Valor::Lista(lista) = iter {
                    let elementos: Vec<Valor> = lista.borrow().clone();
                    for elem in elementos {
                        let escopo = Ambiente::filho(amb);
                        escopo.borrow_mut().definir(variavel, elem);
                        self.executar_bloco(corpo, &escopo)?;
                    }
                }
            }

            Stmt::FuncaoNomeada { nome, parametros, corpo } => {
                let funcao = Valor::Funcao {
                    parametros: parametros.clone(),
                    corpo: corpo.clone(),
                    ambiente: Rc::clone(amb),
                };
                amb.borrow_mut().definir(nome, funcao);
            }
        }
        Ok(())
    }

    fn avaliar(
        &mut self,
        expr: &Expr,
        amb: &AmbienteRef,
    ) -> Result<Valor, ErroExec> {
        match expr {
            Expr::Numero(n)   => Ok(Valor::Numero(*n)),
            Expr::Texto(s)    => Ok(Valor::Texto(s.clone())),
            Expr::Booleano(b) => Ok(Valor::Booleano(*b)),
            Expr::Nulo        => Ok(Valor::Nulo),

            Expr::Lista(elementos) => {
                let vals: Result<Vec<Valor>, _> = elementos.iter()
                    .map(|e| self.avaliar(e, amb))
                    .collect();
                Ok(Valor::Lista(Rc::new(RefCell::new(vals?))))
            }

            Expr::Variavel(nome) => {
                amb.borrow().obter(nome)
                    .ok_or_else(|| ErroExec::Erro(
                        format!("Variável não definida: '{nome}'")
                    ))
            }

            Expr::Funcao { parametros, corpo } => {
                Ok(Valor::Funcao {
                    parametros: parametros.clone(),
                    corpo: corpo.clone(),
                    ambiente: Rc::clone(amb),
                })
            }

            Expr::Chamada { funcao, argumentos } => {
                let fn_val = self.avaliar(funcao, amb)?;
                let args: Result<Vec<Valor>, _> = argumentos.iter()
                    .map(|a| self.avaliar(a, amb))
                    .collect();
                let args = args?;
                self.chamar(fn_val, args)
            }

            Expr::Indice { objeto, indice } => {
                let obj = self.avaliar(objeto, amb)?;
                let idx = self.avaliar(indice, amb)?;
                match (obj, idx) {
                    (Valor::Lista(l), Valor::Numero(i)) => {
                        let lista = l.borrow();
                        let i = i as usize;
                        lista.get(i).cloned()
                            .ok_or_else(|| ErroExec::Erro(
                                format!("Índice {i} fora dos limites")
                            ))
                    }
                    (Valor::Texto(s), Valor::Numero(i)) => {
                        s.chars().nth(i as usize)
                            .map(|c| Valor::Texto(c.to_string()))
                            .ok_or_else(|| ErroExec::Erro(
                                format!("Índice {} fora dos limites", i as usize)
                            ))
                    }
                    _ => Err(ErroExec::Erro("Indexação inválida".into())),
                }
            }

            Expr::Unaria { operador, operando } => {
                let v = self.avaliar(operando, amb)?;
                match operador {
                    OperadorUn::Negativo => match v {
                        Valor::Numero(n) => Ok(Valor::Numero(-n)),
                        _ => Err(ErroExec::Erro("Negação requer número".into())),
                    },
                    OperadorUn::Nao => Ok(Valor::Booleano(!e_verdadeiro(&v))),
                }
            }

            Expr::Binaria { esquerda, operador, direita } => {
                let esq = self.avaliar(esquerda, amb)?;
                let dir = self.avaliar(direita, amb)?;
                self.avaliar_binaria(esq, operador, dir)
            }
        }
    }

    fn chamar(
        &mut self,
        funcao: Valor,
        args: Vec<Valor>,
    ) -> Result<Valor, ErroExec> {
        match funcao {
            Valor::Funcao { parametros, corpo, ambiente } => {
                let escopo = Ambiente::filho(&ambiente);

                if args.len() != parametros.len() {
                    return Err(ErroExec::Erro(format!(
                        "Esperados {} argumentos, recebidos {}",
                        parametros.len(), args.len()
                    )));
                }

                for (param, arg) in parametros.iter().zip(args) {
                    escopo.borrow_mut().definir(param, arg);
                }

                match self.executar_bloco(&corpo, &escopo) {
                    Ok(()) => Ok(Valor::Nulo),
                    Err(ErroExec::Retornar(v)) => Ok(v),
                    Err(e) => Err(e),
                }
            }

            Valor::FuncaoNativa(_, f) => f(args),

            _ => Err(ErroExec::Erro("Não é uma função".into())),
        }
    }

    fn avaliar_binaria(
        &self,
        esq: Valor,
        op: &OperadorBin,
        dir: Valor,
    ) -> Result<Valor, ErroExec> {
        match op {
            // Aritmética
            OperadorBin::Soma => match (esq, dir) {
                (Valor::Numero(a), Valor::Numero(b)) => Ok(Valor::Numero(a + b)),
                (Valor::Texto(a),  Valor::Texto(b))  => Ok(Valor::Texto(a + &b)),
                (Valor::Texto(a),  b) => Ok(Valor::Texto(format!("{a}{b}"))),
                _ => Err(ErroExec::Erro("Operandos inválidos para '+'".into())),
            },
            OperadorBin::Sub => num_op(esq, dir, |a, b| a - b),
            OperadorBin::Mul => num_op(esq, dir, |a, b| a * b),
            OperadorBin::Div => {
                if let Valor::Numero(d) = dir {
                    if d == 0.0 {
                        return Err(ErroExec::Erro("Divisão por zero".into()));
                    }
                    num_op(esq, Valor::Numero(d), |a, b| a / b)
                } else {
                    Err(ErroExec::Erro("Divisão requer números".into()))
                }
            }
            OperadorBin::Mod => num_op(esq, dir, |a, b| a % b),

            // Comparação
            OperadorBin::Igual      =>
                Ok(Valor::Booleano(valores_iguais(&esq, &dir))),
            OperadorBin::Diferente  =>
                Ok(Valor::Booleano(!valores_iguais(&esq, &dir))),
            OperadorBin::Menor      =>
                cmp_op(esq, dir, |a, b| a < b),
            OperadorBin::MenorIgual =>
                cmp_op(esq, dir, |a, b| a <= b),
            OperadorBin::Maior      =>
                cmp_op(esq, dir, |a, b| a > b),
            OperadorBin::MaiorIgual =>
                cmp_op(esq, dir, |a, b| a >= b),

            // Lógica (short-circuit não implementado — simplificação)
            OperadorBin::E =>
                Ok(Valor::Booleano(e_verdadeiro(&esq) && e_verdadeiro(&dir))),
            OperadorBin::Ou =>
                Ok(Valor::Booleano(e_verdadeiro(&esq) || e_verdadeiro(&dir))),
        }
    }
}

fn num_op(
    esq: Valor,
    dir: Valor,
    f: impl Fn(f64, f64) -> f64,
) -> Result<Valor, ErroExec> {
    match (esq, dir) {
        (Valor::Numero(a), Valor::Numero(b)) => Ok(Valor::Numero(f(a, b))),
        _ => Err(ErroExec::Erro("Operandos devem ser números".into())),
    }
}

fn cmp_op(
    esq: Valor,
    dir: Valor,
    f: impl Fn(f64, f64) -> bool,
) -> Result<Valor, ErroExec> {
    match (esq, dir) {
        (Valor::Numero(a), Valor::Numero(b)) => Ok(Valor::Booleano(f(a, b))),
        _ => Err(ErroExec::Erro("Comparação requer números".into())),
    }
}

fn e_verdadeiro(v: &Valor) -> bool {
    match v {
        Valor::Booleano(b) => *b,
        Valor::Nulo        => false,
        Valor::Numero(n)   => *n != 0.0,
        _                  => true,
    }
}

fn valores_iguais(a: &Valor, b: &Valor) -> bool {
    match (a, b) {
        (Valor::Numero(x),  Valor::Numero(y))  => x == y,
        (Valor::Texto(x),   Valor::Texto(y))   => x == y,
        (Valor::Booleano(x),Valor::Booleano(y))=> x == y,
        (Valor::Nulo,       Valor::Nulo)       => true,
        _                                       => false,
    }
}

Juntando tudo — REPL e execução de arquivo

// src/main.rs
mod lexer;
mod ast;
mod parser;
mod interpretador;

use lexer::Lexer;
use parser::Parser;
use interpretador::Interpretador;
use std::io::{self, BufRead, Write};

fn executar_codigo(codigo: &str, interp: &mut Interpretador) {
    let mut lexer = Lexer::novo(codigo);
    let tokens = match lexer.tokenizar() {
        Ok(t)  => t,
        Err(e) => { eprintln!("Erro léxico: {e}"); return; }
    };

    let mut parser = Parser::novo(tokens);
    let ast = match parser.parsear() {
        Ok(a)  => a,
        Err(e) => { eprintln!("Erro de sintaxe: {e}"); return; }
    };

    if let Err(e) = interp.executar(&ast) {
        eprintln!("Erro de execução: {e}");
    }
}

fn repl() {
    println!("Mira 0.1.0 — REPL");
    println!("Digite 'sair' para encerrar.
");

    let mut interp = Interpretador::novo();
    let stdin = io::stdin();

    loop {
        print!("mira> ");
        io::stdout().flush().unwrap();

        let mut linha = String::new();
        if stdin.lock().read_line(&mut linha).is_err() { break; }

        let linha = linha.trim();
        if linha == "sair" || linha.is_empty() { break; }

        executar_codigo(linha, &mut interp);
    }
}

fn executar_arquivo(caminho: &str) {
    let codigo = match std::fs::read_to_string(caminho) {
        Ok(c)  => c,
        Err(e) => { eprintln!("Erro ao ler arquivo: {e}"); return; }
    };

    let mut interp = Interpretador::novo();
    executar_codigo(&codigo, &mut interp);
}

fn main() {
    let args: Vec<String> = std::env::args().collect();

    match args.len() {
        1 => repl(),
        2 => executar_arquivo(&args[1]),
        _ => eprintln!("Uso: mira [arquivo.mira]"),
    }
}

Testando a linguagem:

cat > teste.mira << 'EOF'
// Fibonacci em Mira
fn fib(n) {
    if n <= 1 {
        return n;
    }
    return fib(n - 1) + fib(n - 2);
}

var i = 0;
while i <= 10 {
    print(fib(i));
    i = i + 1;
}

// Closures
fn fazer_somador(x) {
    return fn(y) { return x + y; };
}

var soma5 = fazer_somador(5);
print(soma5(10));  // 15
print(soma5(20));  // 25

// Listas
var nums = [1, 2, 3, 4, 5];
var total = 0;
for n in nums {
    total = total + n;
}
print(total);  // 15
EOF

cargo run -- teste.mira

Fontes e leituras recomendadas

  • "Crafting Interpreters" — Robert Nystrom — o melhor livro sobre o assunto, gratuito — https://craftinginterpreters.com
  • "Writing A Compiler In Go" — Thorsten Ball — abordagem prática excelente
  • "dragon-book" — Aho, Lam, Sethi, Ullman — referência clássica de compiladores
  • logos crate — lexer por atributos em Rust — https://docs.rs/logos
  • lalrpop crate — gerador de parsers LR — https://docs.rs/lalrpop
  • pest crate — parser PEG (visto no Artigo #30) — https://docs.rs/pest
  • inkwell crate — bindings LLVM para Rust — https://docs.rs/inkwell
  • cranelift crate — backend de codegen nativo — https://github.com/bytecodealliance/wasmtime/tree/main/cranelift

Artigo #44 de 52 | Série: Dominando Rust em 1 Ano Próximo → Artigo #45: Rust e Blockchain — Smart contracts, criptografia aplicada e Web3


Comentários

Mais em Rust

Por que Rust existe e o que ele quer de você
Por que Rust existe e o que ele quer de você

Toda linguagem de programa&ccedil;&atilde;o nasce de uma frustra&ccedil;&atil...

Design Patterns em Rust — Adaptando Padrões Clássicos à Linguagem
Design Patterns em Rust — Adaptando Padrões Clássicos à Linguagem

Design patterns são soluções recorrentes para problemas recorrentes. O livro...

Sistemas Distribuídos — Consenso, Mensageria e Tolerância a Falhas
Sistemas Distribuídos — Consenso, Mensageria e Tolerância a Falhas

Rust — Artigo #43 Sistemas Distribuídos — Consenso, Mensageria e Tolerância a...