Skip to content

Commit 1b58a0e

Browse files
committed
Merge pretty print refactor (squash): engine, helpers, instrumentation, prelude
2 parents d79be09 + 9de783c commit 1b58a0e

18 files changed

Lines changed: 2507 additions & 0 deletions

.gitattributes

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Auto detect text files and perform LF normalization
2+
* text=auto

Cargo.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,11 @@ nom = "7.0"
88
approx = "0.5.1"
99
once_cell = "1.10"
1010
indexmap = "2.2"
11+
12+
[features]
13+
# Ativar contadores de instrumentação do pretty printer
14+
pp-profile = []
15+
# Habilita cache experimental para resultados de flatten (memoization estrutural)
16+
pp-flatten-cache = []
17+
# Mede duração do render do pretty printer e expõe função com métricas
18+
pp-timing = []

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ Para uma compreensão mais profunda dos componentes do projeto, consulte nossa d
2424

2525
- **[Type Checker Module](docs/type_checker.md)** - Sistema de verificação de tipos estática que analisa expressões e declarações para garantir segurança de tipos em tempo de compilação. Implementa regras de tipagem bem definidas para todos os construtos da linguagem. *(Em desenvolvimento)*
2626

27+
2728
## 🤝 Contribuindo
2829

2930
Adoraríamos contar com sua contribuição! Por favor, leia nossos guias de contribuição:

docs/PRELUDE.md

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# PRELUDE
2+
3+
> Superfície pública curada da crate. Hoje concentra utilidades do pretty printer, mas destina-se a agregar também itens estáveis de outros subsistemas (ex.: helpers de diagnóstico, construção de erros, futuras abstrações de ambiente) conforme amadureçam.
4+
5+
## Objetivo
6+
Fornecer um ponto único e estável para importar construções de alto nível da linguagem/engine sem expor internals voláteis. Atualmente o foco é o pretty printer; novos módulos só ingressam quando houver estabilidade mínima e testes claros.
7+
8+
## Escopo Incluído (Estado Atual)
9+
- Pretty printing:
10+
- Tipos centrais: `Doc`, `ToDoc`.
11+
- Construtores: `nil`, `text`, `line`, `softline`, `hardline`, `space`, `nest`, `group`, `concat`, `pretty`.
12+
- Helpers: `punctuate`, `join_balanced`, `hardline_if_nonempty`, `fill`.
13+
- Comentários: `line_comment`, `line_comment_ln`, `block_comment`.
14+
- Instrumentação opcional (sob feature): `PrettyMetrics`, `pretty_with_metrics`.
15+
16+
No futuro poderá incluir utilidades estáveis de:
17+
- Diagnostics (formatação padronizada de mensagens de erro).
18+
- Ambiente / runtime (builders de contexto estáveis, não mutáveis).
19+
- Parser simplifications (funções auxiliares neutras em relação a internals).
20+
21+
## Explicitamente Fora do Prelude
22+
| Item | Motivo |
23+
|------|--------|
24+
| `flatten`, `best`, `fits` | Detalhes de implementação sujeitos a refactor. |
25+
| Cache (`pp-flatten-cache`) | Otimização interna; não faz parte da modelagem semântica. |
26+
| Contadores diretos (`pp-profile`) | Acesso indireto via `pretty_with_metrics` pode ser expandido no futuro. |
27+
28+
## Política de Estabilidade
29+
1. Símbolos no prelude não serão removidos sem:
30+
- Registro prévio no `REFACTORING_LOG.md` indicando deprecação.
31+
2. Mudanças de comportamento serão acompanhadas de testes atualizados e nota de racional.
32+
3. Novos símbolos entram somente após:
33+
- Terem ao menos 1–2 testes cobrindo caso simples e limite.
34+
- Não serem marcados como experimentais.
35+
36+
## Quando Usar
37+
- Em testes ou exemplos que apenas constroem Docs ou mensagens.
38+
- Em código de geração de relatórios/diagnósticos (quando forem adicionados).
39+
- Em futuras camadas de codegen.
40+
41+
## Quando Evitar
42+
- Ao modificar o próprio engine (`pretty_print.rs`) ou internals de futuros módulos.
43+
- Ao experimentar combinadores / APIs ainda instáveis (importar direto facilita evolução).
44+
- Se precisar de acesso deliberado a detalhes internos para refactor.
45+
46+
## Processo para Adicionar Algo
47+
1. Implementar função/estrutura em módulo interno.
48+
2. Adicionar testes (happy path + edge mínimo).
49+
3. Registrar racional / estabilidade em `REFACTORING_LOG.md` ou CHANGELOG futuro.
50+
4. Após *cooldown* curto (ou consenso de revisão), reexportar em commit dedicado.
51+
52+
## Exemplos
53+
```rust
54+
use r_python::prelude::*;
55+
56+
let doc = group(concat(text("foo"), concat(line(), text("bar"))));
57+
assert_eq!(pretty(80, &doc), "foo bar");
58+
assert_eq!(pretty(3, &doc), "foo\nbar");
59+
```
60+
61+
Comentários:
62+
```rust
63+
use r_python::prelude::*;
64+
let c = block_comment("/*\n multi\n line \n*/");
65+
let rendered = pretty(40, &c);
66+
assert!(rendered.starts_with("/*"));
67+
```
68+
69+
## Futuras Extensões Planejáveis
70+
- `diagnostic(doc_like)` – construção declarativa de mensagens de erro.
71+
- `env_prelude` (sub-módulo) – acesso somente leitura a abstrações estáveis do ambiente.
72+
- `parser_atoms` – tokens/atoms reutilizáveis para geração de erros ou tooling.
73+
74+
Inclusão condicionada a: baixa chance de refactor imediato + necessidade recorrente externa.
75+
76+
## Dúvidas:
77+
**Posso ignorar o prelude?** Sim, importe de `pretty_print::pretty_print` se preferir granularidade.
78+
79+
**Isso afeta performance?** Não – é só reexport; zero custo de runtime.
80+
81+
**Por que `flatten` não está aqui?** Evita dependência em detalhes que podem ganhar caching/pipeline diferente.
82+
83+
**E se eu quiser algo que não está no prelude?** Importe diretamente; se virar uso recorrente e estável, vamos propor inclusão.
84+
85+
---
86+
Este arquivo deve permanecer curto; detalhes históricos continuam no `REFACTORING_LOG.md`.

docs/ROBUSTEZ_PRETTY_PRINT.md

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
# Robustez do Pretty Printer
2+
3+
> Objetivo: garantir que o pretty printer seja previsível, observável e extensível antes de ampliarmos o front-end e a geração de código.
4+
5+
Este documento complementa o `REFACTORING_LOG.md` descrevendo como e quando usar os recursos opcionais e novos combinadores do sistema de pretty printing.
6+
7+
## Visão Geral
8+
O pretty printer segue abordagem algébrica (Wadler) com variantes: `Nil, Text, Line, HardLine, Nest, Concat, Group`. Otimizações e extensões introduzidas na Fase 6 visam:
9+
- Observabilidade (tempo, contadores)
10+
- Robustez estrutural (balanceamento, cache opcional)
11+
- Ergonomia (fill, comentários, prelude)
12+
13+
## Combinadores Principais
14+
- `group(doc)`: tenta layout em uma linha; se não couber, quebra nos `Line`.
15+
- `nest(indent, doc)`: aplica indentação às quebras internas.
16+
- `line() / hardline()`: quebra suave vs obrigatória.
17+
- `space()`: atalho `" "`.
18+
- `concat(a, b)`: concatenação direta.
19+
- `punctuate(sep, docs)`: intercala `sep` entre elementos; adaptativo (balanceado) para listas > 8.
20+
- `fill(docs)`: tenta manter itens sucessivos na mesma linha antes de quebrar.
21+
- `hardline_if_nonempty(docs)`: adiciona `hardline` após cada doc quando há pelo menos um.
22+
- Comentários: `line_comment`, `line_comment_ln`, `block_comment`.
23+
24+
## Comentários
25+
- `line_comment("foo")` produz `// foo` (prefixo `//` opcional na entrada é normalizado).
26+
- `line_comment_ln` adiciona quebra obrigatória após o comentário.
27+
- `block_comment`:
28+
- Entrada single-line => `/* body */`.
29+
- Multi-linha => formato padronizado com prefixo ` * ` e fechamento em linha separada.
30+
- Linhas externas vazias são removidas; espaços à esquerda por linha normalizados.
31+
32+
## Prelude
33+
Uso opcional de ergonomia. Para a lista curada de símbolos e política de estabilidade consulte `docs/PRELUDE.md`.
34+
Exemplo rápido:
35+
```
36+
use r_python::prelude::*;
37+
let doc = group(concat(text("a"), concat(line(), text("b"))));
38+
assert_eq!(pretty(4, &doc), "a b");
39+
```
40+
41+
## Features de Instrumentação
42+
| Feature | Propósito | Overhead | Quando usar |
43+
|---------|-----------|----------|-------------|
44+
| `pp-profile` | Contadores (`flatten`, `fits`, grupos) | Muito baixo | Diagnóstico de regressões / tuning rápido |
45+
| `pp-timing` | Tempo total + métricas (struct `PrettyMetrics`) | Baixo (chamada a `Instant`) | Medir impacto de novos combinadores |
46+
| `pp-flatten-cache` | Memoização de `flatten(doc)` por identidade | Médio (HashMap) | Árvores grandes reutilizando subdocs |
47+
48+
Ative combinando:
49+
```
50+
cargo test --features "pp-profile pp-timing"
51+
```
52+
53+
## Estratégias Recomendadas
54+
1. Investigação de desempenho: ligue `pp-profile` para contagem de chamadas.
55+
2. Medição comparativa: adicione `pp-timing` e compare `duration_ns` antes/depois.
56+
3. Casos sintéticos repetitivos: experimente `pp-flatten-cache` e verifique se `flatten_calls` cai proporcionalmente.
57+
4. Uso cotidiano / produção: nenhuma feature para via mais enxuta.
58+
59+
## Padrões de Uso
60+
- Listas grandes: use `punctuate` diretamente; o balanceamento interno evita profundidade excessiva.
61+
- Sequências densas heterogêneas (ex.: parâmetros de função com tamanhos variáveis): considere `fill` para melhor compactação horizontal.
62+
- Comentários antes de blocos: `line_comment_ln` seguido de `group(...)` do bloco garante isolamento visual.
63+
- Documentar módulo público: `block_comment` multi-linha antes de declarações top-level.
64+
65+
## Limitações Atuais
66+
- `fill` ainda não implementa heurística de redistribuição após primeira quebra (versão incremental).
67+
- `block_comment` normaliza indentação em vez de preservar formato ASCII-art.
68+
- Sem suporte a comentários de documentação diferenciados (planejável via futura função `doc_comment`).
69+
70+
## Possíveis Evoluções Futuras
71+
- Interning lazy da forma flatten dentro de `Group` (elimina HashMap externo).
72+
- `fill` avançado (variante com alternância espaço/quebra minimizando altura total).
73+
- Comentários doc vs regulares com marcação distinta.
74+
- Ferramenta de benchmark sintético automatizado (collect + relatório diff).
75+
76+
## Exemplo End-to-End
77+
```
78+
use r_python::prelude::*;
79+
80+
let params: Vec<_> = [
81+
text("user_id"),
82+
text("very_long_parameter_name"),
83+
text("flag"),
84+
].into_iter().collect();
85+
86+
let signature = group(concat(text("fn foo("), concat(fill(&params), text(")"))));
87+
let commented = concat(line_comment_ln("Função de exemplo"), signature);
88+
let out = pretty(40, &commented);
89+
println!("{out}");
90+
```
91+
92+
## Checklist Rápido
93+
- Resultado inesperado flatten? Ative `pp-profile` e observe contadores.
94+
- Layout muito vertical? Tente `fill` em vez de `punctuate` simples.
95+
- Performance degradou? Compare `duration_ns` com e sem alteração.
96+
97+
## Exemplos Práticos
98+
99+
### 1. Instrumentação (tempo + contadores)
100+
Arquivo: `examples/pp_timing.rs`
101+
102+
Somente tempo:
103+
```
104+
cargo run --release --features pp-timing --example pp_timing
105+
```
106+
Tempo + contadores:
107+
```
108+
cargo run --release --features "pp-timing pp-profile" --example pp_timing
109+
```
110+
Mostra variação de layout (larguras 120 / 40 / 15) e, quando `pp-profile` ativado, imprime `flatten=`, `fits=`, `groups=`.
111+
112+
### 2. Comentário seguido de bloco
113+
```
114+
let seq = concat(line_comment_ln("comentário"), group(concat(text("x = 1"), concat(line(), text("y = 2")))));
115+
```
116+
117+
### 3. Lista grande balanceada (> 8 elementos)
118+
```
119+
let xs: Vec<_> = (0..15).map(|i| text(format!("x{i}"))).collect();
120+
let doc = group(punctuate(concat(text(","), line()), &xs));
121+
```
122+
123+
### 4. Fill com itens heterogêneos
124+
```
125+
let docs = vec![text("a"), text("bbbbbbbbbbbb"), text("c"), text("dd")];
126+
let laid = fill(&docs);
127+
```
128+
Mantém densidade à esquerda até a quebra forçada pelo item longo.
129+
130+
131+
---
132+
Manter este arquivo conciso e focado em operação; detalhes cronológicos continuam em `REFACTORING_LOG.md`.

examples/pp_bench.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
//! Benchmark sintético simples para o pretty printer.
2+
//! Execute com: cargo run --release --features pp-profile --example pp_bench
3+
4+
use r_python::pretty_print::*;
5+
use std::rc::Rc;
6+
7+
fn linear_chain(n: usize) -> Rc<Doc> {
8+
let mut parts = Vec::with_capacity(n);
9+
for i in 0..n {
10+
parts.push(text(format!("item{i}")));
11+
}
12+
// Implementação linear (left-deep) via fold
13+
let sep = concat(text(","), line());
14+
let doc = parts
15+
.into_iter()
16+
.reduce(|acc, d| concat(acc, concat(sep.clone(), d)))
17+
.unwrap();
18+
group(doc)
19+
}
20+
21+
fn balanced_chain(n: usize) -> Rc<Doc> {
22+
let mut parts = Vec::with_capacity(n);
23+
for i in 0..n {
24+
parts.push(text(format!("item{i}")));
25+
}
26+
let sep = concat(text(","), line());
27+
group(join_balanced(sep, &parts))
28+
}
29+
30+
fn main() {
31+
let sizes = [50, 100, 200, 400, 800];
32+
for &n in &sizes {
33+
for variant in ["linear", "balanced"] {
34+
let doc = match variant {
35+
"linear" => linear_chain(n),
36+
_ => balanced_chain(n),
37+
};
38+
let rendered = pretty(80, &doc);
39+
// Consumir output para evitar otimização
40+
assert!(rendered.len() > 0);
41+
#[cfg(feature = "pp-profile")]
42+
println!("n={n} variant={variant} {}", pp_profile_report());
43+
}
44+
}
45+
}

examples/pp_timing.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//! Exemplo de instrumentação com `pp-timing` (+ opcional `pp-profile`).
2+
//! Execute com:
3+
//! cargo run --release --features "pp-timing" --example pp_timing
4+
//! Ou com contadores:
5+
//! cargo run --release --features "pp-timing pp-profile" --example pp_timing
6+
7+
#[cfg(feature = "pp-timing")]
8+
fn main() {
9+
use r_python::prelude::*;
10+
11+
// Construímos um Doc com mistura de grupos, nest e fill para gerar alguma atividade.
12+
let items: Vec<_> = (0..20)
13+
.map(|i| {
14+
group(concat(
15+
text(format!("item{i}")),
16+
concat(line(), text("valor")),
17+
))
18+
})
19+
.collect();
20+
let filled = fill(&items);
21+
let doc = group(nest(2, concat(text("lista:"), concat(line(), filled))));
22+
23+
// Mede em diferentes larguras para comparar decisões de quebra.
24+
for &w in &[120usize, 40, 15] {
25+
let metrics = pretty_with_metrics(w, &doc);
26+
println!("--- width={w} ---");
27+
println!("{}", metrics.output);
28+
println!(
29+
"len={} duration_ns={}{}",
30+
metrics.rendered_len,
31+
metrics.duration_ns,
32+
match (
33+
metrics.flatten_calls,
34+
metrics.fits_calls,
35+
metrics.group_decisions
36+
) {
37+
(Some(f), Some(ft), Some(g)) => format!(" flatten={f} fits={ft} groups={g}"),
38+
_ => String::new(),
39+
}
40+
);
41+
println!();
42+
}
43+
}
44+
45+
#[cfg(not(feature = "pp-timing"))]
46+
fn main() {
47+
eprintln!("Este exemplo requer a feature 'pp-timing'. Use: \n cargo run --features pp-timing --example pp_timing");
48+
}

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
pub mod ir;
22
pub mod parser;
3+
pub mod prelude;
4+
pub mod pretty_print;

src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ pub mod environment;
1414
pub mod interpreter;
1515
pub mod ir;
1616
pub mod parser;
17+
pub mod pretty_print;
1718
pub mod type_checker;
1819

1920
fn main() {

src/prelude.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//! Prelude estável de construção de documentos. Detalhes completos em `docs/PRELUDE.md`.
2+
//! Inclui apenas superfície considerada estável; internals ficam fora.
3+
4+
pub use crate::pretty_print::pretty_print::{
5+
block_comment, concat, fill, group, hardline, hardline_if_nonempty, join_balanced, line,
6+
line as softline, line_comment, line_comment_ln, nest, nil, pretty, punctuate, space, text,
7+
Doc, ToDoc,
8+
};
9+
10+
#[cfg(feature = "pp-timing")]
11+
pub use crate::pretty_print::pretty_print::{pretty_with_metrics, PrettyMetrics};
12+
13+
// Fora do prelude: flatten, best, fits, caches/profile diretos.

0 commit comments

Comments
 (0)