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
logoscrate — lexer por atributos em Rust — https://docs.rs/logoslalrpopcrate — gerador de parsers LR — https://docs.rs/lalrpoppestcrate — parser PEG (visto no Artigo #30) — https://docs.rs/pestinkwellcrate — bindings LLVM para Rust — https://docs.rs/inkwellcraneliftcrate — 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