portugython 0.3.0__tar.gz → 0.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. {portugython-0.3.0 → portugython-0.4.0}/CHANGELOG.md +54 -1
  2. {portugython-0.3.0 → portugython-0.4.0}/PKG-INFO +105 -2
  3. {portugython-0.3.0 → portugython-0.4.0}/README.md +103 -0
  4. {portugython-0.3.0 → portugython-0.4.0}/portugython/__init__.py +18 -1
  5. {portugython-0.3.0 → portugython-0.4.0}/portugython/_importador.py +76 -12
  6. portugython-0.4.0/portugython/transpilador.py +254 -0
  7. {portugython-0.3.0 → portugython-0.4.0}/pyproject.toml +5 -2
  8. portugython-0.4.0/tests/ola_mundo.ptpy +38 -0
  9. {portugython-0.3.0 → portugython-0.4.0}/tests/test_portugython.py +1 -1
  10. portugython-0.4.0/tests/test_transpilador.py +310 -0
  11. {portugython-0.3.0 → portugython-0.4.0}/.gitignore +0 -0
  12. {portugython-0.3.0 → portugython-0.4.0}/LICENSE +0 -0
  13. {portugython-0.3.0 → portugython-0.4.0}/portugython/abstrato.py +0 -0
  14. {portugython-0.3.0 → portugython-0.4.0}/portugython/aleatorio.py +0 -0
  15. {portugython-0.3.0 → portugython-0.4.0}/portugython/argumentos.py +0 -0
  16. {portugython-0.3.0 → portugython-0.4.0}/portugython/assincronico.py +0 -0
  17. {portugython-0.3.0 → portugython-0.4.0}/portugython/bisecao.py +0 -0
  18. {portugython-0.3.0 → portugython-0.4.0}/portugython/calendario.py +0 -0
  19. {portugython-0.3.0 → portugython-0.4.0}/portugython/caminho.py +0 -0
  20. {portugython-0.3.0 → portugython-0.4.0}/portugython/classe_dados.py +0 -0
  21. {portugython-0.3.0 → portugython-0.4.0}/portugython/codificacao.py +0 -0
  22. {portugython-0.3.0 → portugython-0.4.0}/portugython/colecoes.py +0 -0
  23. {portugython-0.3.0 → portugython-0.4.0}/portugython/compressao.py +0 -0
  24. {portugython-0.3.0 → portugython-0.4.0}/portugython/configuracao.py +0 -0
  25. {portugython-0.3.0 → portugython-0.4.0}/portugython/contexto.py +0 -0
  26. {portugython-0.3.0 → portugython-0.4.0}/portugython/copia.py +0 -0
  27. {portugython-0.3.0 → portugython-0.4.0}/portugython/csv_pt.py +0 -0
  28. {portugython-0.3.0 → portugython-0.4.0}/portugython/depuracao.py +0 -0
  29. {portugython-0.3.0 → portugython-0.4.0}/portugython/enumeracao.py +0 -0
  30. {portugython-0.3.0 → portugython-0.4.0}/portugython/estatistica.py +0 -0
  31. {portugython-0.3.0 → portugython-0.4.0}/portugython/estrutura.py +0 -0
  32. {portugython-0.3.0 → portugython-0.4.0}/portugython/expressao.py +0 -0
  33. {portugython-0.3.0 → portugython-0.4.0}/portugython/formatacao.py +0 -0
  34. {portugython-0.3.0 → portugython-0.4.0}/portugython/funcional.py +0 -0
  35. {portugython-0.3.0 → portugython-0.4.0}/portugython/hash_pt.py +0 -0
  36. {portugython-0.3.0 → portugython-0.4.0}/portugython/identificador.py +0 -0
  37. {portugython-0.3.0 → portugython-0.4.0}/portugython/iteradores.py +0 -0
  38. {portugython-0.3.0 → portugython-0.4.0}/portugython/json.py +0 -0
  39. {portugython-0.3.0 → portugython-0.4.0}/portugython/matematica.py +0 -0
  40. {portugython-0.3.0 → portugython-0.4.0}/portugython/py.typed +0 -0
  41. {portugython-0.3.0 → portugython-0.4.0}/portugython/rede.py +0 -0
  42. {portugython-0.3.0 → portugython-0.4.0}/portugython/registro.py +0 -0
  43. {portugython-0.3.0 → portugython-0.4.0}/portugython/serializacao.py +0 -0
  44. {portugython-0.3.0 → portugython-0.4.0}/portugython/sistema.py +0 -0
  45. {portugython-0.3.0 → portugython-0.4.0}/portugython/soquete.py +0 -0
  46. {portugython-0.3.0 → portugython-0.4.0}/portugython/subprocesso.py +0 -0
  47. {portugython-0.3.0 → portugython-0.4.0}/portugython/tempo.py +0 -0
  48. {portugython-0.3.0 → portugython-0.4.0}/portugython/threads.py +0 -0
  49. {portugython-0.3.0 → portugython-0.4.0}/portugython/tipagem.py +0 -0
  50. {portugython-0.3.0 → portugython-0.4.0}/tests/__init__.py +0 -0
  51. {portugython-0.3.0 → portugython-0.4.0}/tests/test_importador.py +0 -0
  52. {portugython-0.3.0 → portugython-0.4.0}/tests/test_submodulos.py +0 -0
@@ -1,6 +1,59 @@
1
1
  # Changelog
2
2
 
3
- ## 0.3.0 (2026-06-26)
3
+ ## 0.4.0 (2026-06-26)
4
+
5
+ Add `transpilador` module: a tokenizer-based source transpiler that
6
+ translates Python keywords to Portuguese, enabling fully Portuguese Python syntax.
7
+
8
+ New module `portugython.transpilador`:
9
+
10
+ - `traduzir(codigo)` - translates Portuguese Python source to valid Python
11
+ - `traduzir_para_portugues(codigo)` - inverse translation
12
+ - `executar_texto(codigo)` - translate and execute a Portuguese Python string
13
+ - `executar_arquivo(caminho)` - translate and execute a `.ptpy` file
14
+ - `mostrar_traducao(codigo)` - print the translated Python source
15
+ - `MAPA_PALAVRAS_CHAVE` - full keyword translation table
16
+ - `PALAVRAS_RESERVADAS` - frozenset of reserved Portuguese keywords
17
+
18
+ Keyword translations (34 keywords):
19
+
20
+ se -> if senaose -> elif senao -> else
21
+ para -> for enquanto -> while parar -> break
22
+ continuar -> continue passar -> pass
23
+ defina -> def classe -> class retorne -> return
24
+ produzca -> yield anonima -> lambda
25
+ importe -> import de -> from como -> as
26
+ tente -> try exceto -> except finalmente -> finally
27
+ levante -> raise com -> with
28
+ nao -> not e -> and ou -> or
29
+ em -> in eh -> is
30
+ excluir -> del afirmar -> assert
31
+ global -> global naolocal -> nonlocal
32
+ assincronico -> async aguardar -> await
33
+ Verdadeiro -> True Falso -> False Nenhum -> None
34
+
35
+ New CLI entry point: `ptpy` runs `.ptpy` files directly:
36
+
37
+ ptpy meu_programa.ptpy
38
+ ptpy -c 'para i em intervalo(3): escreva(i)'
39
+ ptpy --mostrar meu_programa.ptpy # show the translated Python
40
+
41
+ Extended import hook to load `.ptpy` files via the normal import system.
42
+
43
+ Example `.ptpy` program:
44
+
45
+ ```python
46
+ defina fatorial(n):
47
+ se n <= 1:
48
+ retorne 1
49
+ senao:
50
+ retorne n * fatorial(n - 1)
51
+
52
+ para i em intervalo(1, 6):
53
+ escreva(f"{i}! = {fatorial(i)}")
54
+ ```
55
+
56
+
4
57
 
5
58
  Add import hook so all Portuguese module names work as top-level imports.
6
59
 
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: portugython
3
- Version: 0.3.0
4
- Summary: A Portuguese translation of the Python functions/modules/packages
3
+ Version: 0.4.0
4
+ Summary: A Portuguese translation of Python: functions, modules and syntax
5
5
  Project-URL: Homepage, https://github.com/spacemany2k38/portugython
6
6
  Project-URL: Bug Tracker, https://github.com/spacemany2k38/portugython/issues
7
7
  Author: Victor Kolis
@@ -281,6 +281,109 @@ if email.encontrar("contato@example.com"):
281
281
  | `filtro(f, it)` | `list(filter(f, it))` |
282
282
  | `reduzido(f, it)` | `functools.reduce(f, it)` |
283
283
 
284
+ ## Transpilador de palavras-chave
285
+
286
+ Portugython inclui um transpilador completo que traduz a sintaxe do Python
287
+ (palavras-chave) para portugues. Isso permite escrever programas Python
288
+ inteiramente em portugues.
289
+
290
+ ### CLI: ptpy
291
+
292
+ ```bash
293
+ ptpy meu_programa.ptpy # executa um arquivo .ptpy
294
+ ptpy -c 'para i em intervalo(3): escreva(i)' # executa inline
295
+ ptpy --mostrar meu_programa.ptpy # imprime o Python equivalente
296
+ ```
297
+
298
+ ### API Python
299
+
300
+ ```python
301
+ from portugython import traduzir, executar_texto, executar_arquivo
302
+
303
+ # Traduzir uma string
304
+ codigo_pt = """
305
+ defina fatorial(n):
306
+ se n <= 1:
307
+ retorne 1
308
+ senao:
309
+ retorne n * fatorial(n - 1)
310
+ """
311
+ print(traduzir(codigo_pt))
312
+ # def fatorial(n):
313
+ # if n <= 1:
314
+ # return 1
315
+ # else:
316
+ # return n * fatorial(n - 1)
317
+
318
+ # Executar diretamente
319
+ executar_texto("para i em intervalo(3):\n escreva(i)")
320
+
321
+ # Executar um arquivo .ptpy
322
+ executar_arquivo("meu_programa.ptpy")
323
+ ```
324
+
325
+ ### Programa completo em portugues (.ptpy)
326
+
327
+ ```python
328
+ # fatorial.ptpy
329
+ importe portugython
330
+
331
+ defina fatorial(n):
332
+ se n <= 1:
333
+ retorne 1
334
+ senao:
335
+ retorne n * fatorial(n - 1)
336
+
337
+ defina verificar_primo(n):
338
+ se n < 2:
339
+ retorne Falso
340
+ para i em intervalo(2, n):
341
+ se n % i == 0:
342
+ retorne Falso
343
+ retorne Verdadeiro
344
+
345
+ primos = [x para x em intervalo(2, 20) se verificar_primo(x)]
346
+ escreva("Primos:", primos)
347
+
348
+ para n em intervalo(1, 8):
349
+ escreva(f"{n}! = {fatorial(n)}")
350
+
351
+ tente:
352
+ resultado = 10 / 0
353
+ exceto ZeroDivisionError como err:
354
+ escreva("Erro capturado:", err)
355
+ finalmente:
356
+ escreva("Pronto!")
357
+ ```
358
+
359
+ ### Tabela de palavras-chave
360
+
361
+ | Portugues | Python | | Portugues | Python |
362
+ |-----------|--------|-|-----------|--------|
363
+ | `se` | `if` | | `senao` | `else` |
364
+ | `senaose` | `elif` | | `para` | `for` |
365
+ | `enquanto` | `while` | | `parar` | `break` |
366
+ | `continuar` | `continue` | | `passar` | `pass` |
367
+ | `defina` | `def` | | `classe` | `class` |
368
+ | `retorne` | `return` | | `produzca` | `yield` |
369
+ | `anonima` | `lambda` | | `importe` | `import` |
370
+ | `de` | `from` | | `como` | `as` |
371
+ | `tente` | `try` | | `exceto` | `except` |
372
+ | `finalmente` | `finally` | | `levante` | `raise` |
373
+ | `com` | `with` | | `nao` | `not` |
374
+ | `e` | `and` | | `ou` | `or` |
375
+ | `em` | `in` | | `eh` | `is` |
376
+ | `excluir` | `del` | | `afirmar` | `assert` |
377
+ | `global` | `global` | | `naolocal` | `nonlocal` |
378
+ | `assincronico` | `async` | | `aguardar` | `await` |
379
+ | `Verdadeiro` | `True` | | `Falso` | `False` |
380
+ | `Nenhum` | `None` | | | |
381
+
382
+ > **Nota**: Palavras como `e`, `ou`, `de`, `com` e `em` sao palavras-chave
383
+ > reservadas no contexto de arquivos `.ptpy`. Evite usa-las como nomes de
384
+ > variaveis nesses arquivos, assim como nao se usa `if` ou `for` como
385
+ > variaveis em Python normal.
386
+
284
387
  ## Contribuir
285
388
 
286
389
  1. Crie um fork deste repositorio
@@ -254,6 +254,109 @@ if email.encontrar("contato@example.com"):
254
254
  | `filtro(f, it)` | `list(filter(f, it))` |
255
255
  | `reduzido(f, it)` | `functools.reduce(f, it)` |
256
256
 
257
+ ## Transpilador de palavras-chave
258
+
259
+ Portugython inclui um transpilador completo que traduz a sintaxe do Python
260
+ (palavras-chave) para portugues. Isso permite escrever programas Python
261
+ inteiramente em portugues.
262
+
263
+ ### CLI: ptpy
264
+
265
+ ```bash
266
+ ptpy meu_programa.ptpy # executa um arquivo .ptpy
267
+ ptpy -c 'para i em intervalo(3): escreva(i)' # executa inline
268
+ ptpy --mostrar meu_programa.ptpy # imprime o Python equivalente
269
+ ```
270
+
271
+ ### API Python
272
+
273
+ ```python
274
+ from portugython import traduzir, executar_texto, executar_arquivo
275
+
276
+ # Traduzir uma string
277
+ codigo_pt = """
278
+ defina fatorial(n):
279
+ se n <= 1:
280
+ retorne 1
281
+ senao:
282
+ retorne n * fatorial(n - 1)
283
+ """
284
+ print(traduzir(codigo_pt))
285
+ # def fatorial(n):
286
+ # if n <= 1:
287
+ # return 1
288
+ # else:
289
+ # return n * fatorial(n - 1)
290
+
291
+ # Executar diretamente
292
+ executar_texto("para i em intervalo(3):\n escreva(i)")
293
+
294
+ # Executar um arquivo .ptpy
295
+ executar_arquivo("meu_programa.ptpy")
296
+ ```
297
+
298
+ ### Programa completo em portugues (.ptpy)
299
+
300
+ ```python
301
+ # fatorial.ptpy
302
+ importe portugython
303
+
304
+ defina fatorial(n):
305
+ se n <= 1:
306
+ retorne 1
307
+ senao:
308
+ retorne n * fatorial(n - 1)
309
+
310
+ defina verificar_primo(n):
311
+ se n < 2:
312
+ retorne Falso
313
+ para i em intervalo(2, n):
314
+ se n % i == 0:
315
+ retorne Falso
316
+ retorne Verdadeiro
317
+
318
+ primos = [x para x em intervalo(2, 20) se verificar_primo(x)]
319
+ escreva("Primos:", primos)
320
+
321
+ para n em intervalo(1, 8):
322
+ escreva(f"{n}! = {fatorial(n)}")
323
+
324
+ tente:
325
+ resultado = 10 / 0
326
+ exceto ZeroDivisionError como err:
327
+ escreva("Erro capturado:", err)
328
+ finalmente:
329
+ escreva("Pronto!")
330
+ ```
331
+
332
+ ### Tabela de palavras-chave
333
+
334
+ | Portugues | Python | | Portugues | Python |
335
+ |-----------|--------|-|-----------|--------|
336
+ | `se` | `if` | | `senao` | `else` |
337
+ | `senaose` | `elif` | | `para` | `for` |
338
+ | `enquanto` | `while` | | `parar` | `break` |
339
+ | `continuar` | `continue` | | `passar` | `pass` |
340
+ | `defina` | `def` | | `classe` | `class` |
341
+ | `retorne` | `return` | | `produzca` | `yield` |
342
+ | `anonima` | `lambda` | | `importe` | `import` |
343
+ | `de` | `from` | | `como` | `as` |
344
+ | `tente` | `try` | | `exceto` | `except` |
345
+ | `finalmente` | `finally` | | `levante` | `raise` |
346
+ | `com` | `with` | | `nao` | `not` |
347
+ | `e` | `and` | | `ou` | `or` |
348
+ | `em` | `in` | | `eh` | `is` |
349
+ | `excluir` | `del` | | `afirmar` | `assert` |
350
+ | `global` | `global` | | `naolocal` | `nonlocal` |
351
+ | `assincronico` | `async` | | `aguardar` | `await` |
352
+ | `Verdadeiro` | `True` | | `Falso` | `False` |
353
+ | `Nenhum` | `None` | | | |
354
+
355
+ > **Nota**: Palavras como `e`, `ou`, `de`, `com` e `em` sao palavras-chave
356
+ > reservadas no contexto de arquivos `.ptpy`. Evite usa-las como nomes de
357
+ > variaveis nesses arquivos, assim como nao se usa `if` ou `for` como
358
+ > variaveis em Python normal.
359
+
257
360
  ## Contribuir
258
361
 
259
362
  1. Crie um fork deste repositorio
@@ -9,8 +9,17 @@ from portugython._importador import (
9
9
  instalar,
10
10
  listar_modulos,
11
11
  )
12
+ from portugython.transpilador import (
13
+ MAPA_PALAVRAS_CHAVE,
14
+ PALAVRAS_RESERVADAS,
15
+ executar_arquivo,
16
+ executar_texto,
17
+ mostrar_traducao,
18
+ traduzir,
19
+ traduzir_para_portugues,
20
+ )
12
21
 
13
- __version__ = "0.3.0"
22
+ __version__ = "0.4.0"
14
23
 
15
24
  # Instala o gancho de importacao automaticamente
16
25
  instalar()
@@ -503,6 +512,14 @@ __all__ = [
503
512
  "instalar",
504
513
  "desinstalar",
505
514
  "listar_modulos",
515
+ # Transpilador
516
+ "traduzir",
517
+ "traduzir_para_portugues",
518
+ "executar_texto",
519
+ "executar_arquivo",
520
+ "mostrar_traducao",
521
+ "MAPA_PALAVRAS_CHAVE",
522
+ "PALAVRAS_RESERVADAS",
506
523
  # IO
507
524
  "escreva",
508
525
  "leia",
@@ -5,6 +5,10 @@ Quando ativado, permite usar nomes de modulos em Portugues diretamente:
5
5
  import aleatorio
6
6
  from matematica import raiz_quadrada
7
7
  from colecoes import Contador
8
+
9
+ Tambem permite importar arquivos .ptpy (Python em Portugues):
10
+
11
+ import meu_modulo # carrega meu_modulo.ptpy se existir
8
12
  """
9
13
 
10
14
  from __future__ import annotations
@@ -12,8 +16,10 @@ from __future__ import annotations
12
16
  import importlib
13
17
  import importlib.abc
14
18
  import importlib.machinery
19
+ import importlib.util
15
20
  import sys
16
- from importlib.machinery import ModuleSpec
21
+ from importlib.machinery import ModuleSpec, SourceFileLoader
22
+ from pathlib import Path
17
23
  from typing import Sequence
18
24
 
19
25
  # Mapeamento de nome em Portugues -> caminho completo do modulo portugython
@@ -106,11 +112,75 @@ class ImportadorPortugues(importlib.abc.MetaPathFinder):
106
112
  _gancho = ImportadorPortugues()
107
113
 
108
114
 
115
+
116
+
117
+ def listar_modulos() -> dict[str, str]:
118
+ """Retorna o mapeamento de nomes em Portugues para modulos portugython.
119
+
120
+ Returns:
121
+ Dicionario com nome_portugues -> caminho_modulo.
122
+ """
123
+ return dict(MAPA_MODULOS)
124
+
125
+
126
+ # ---------------------------------------------------------------------------
127
+ # Carregador de arquivos .ptpy
128
+ # ---------------------------------------------------------------------------
129
+
130
+
131
+ class _CarregadorPtpy(SourceFileLoader):
132
+ """Carrega arquivos .ptpy transpilando-os para Python antes da execucao."""
133
+
134
+ def get_data(self, path: str) -> bytes:
135
+ """Le o arquivo .ptpy e retorna o codigo Python traduzido como bytes."""
136
+ from portugython.transpilador import traduzir # noqa: PLC0415
137
+
138
+ codigo_pt = Path(path).read_text(encoding="utf-8")
139
+ codigo_py = traduzir(codigo_pt)
140
+ return codigo_py.encode("utf-8")
141
+
142
+
143
+ class ImportadorPtpy(importlib.abc.MetaPathFinder):
144
+ """Importa arquivos .ptpy (Python em Portugues) de qualquer diretorio
145
+ no sys.path."""
146
+
147
+ def find_spec(
148
+ self,
149
+ fullname: str,
150
+ path: Sequence[str] | None,
151
+ target=None, # noqa: ANN001
152
+ ) -> ModuleSpec | None:
153
+ """Procura um arquivo .ptpy para o modulo solicitado.
154
+
155
+ Args:
156
+ fullname: Nome completo do modulo.
157
+ path: Caminhos de busca.
158
+ target: Modulo alvo (ignorado).
159
+ """
160
+ diretorios = path if path else sys.path
161
+ for diretorio in diretorios:
162
+ candidato = Path(diretorio) / f"{fullname}.ptpy"
163
+ if candidato.exists():
164
+ carregador = _CarregadorPtpy(fullname, str(candidato))
165
+ return ModuleSpec(
166
+ fullname,
167
+ carregador,
168
+ origin=str(candidato),
169
+ )
170
+ return None
171
+
172
+
173
+ _gancho_ptpy = ImportadorPtpy()
174
+
175
+
109
176
  def instalar() -> None:
110
177
  """Instala o importador de modulos em Portugues no sys.meta_path.
111
178
 
112
- Apos chamar esta funcao, e possivel importar modulos pelo nome em Portugues:
179
+ Apos chamar esta funcao:
180
+ - Modulos portugython sao importaveis pelo nome em Portugues
181
+ - Arquivos .ptpy sao importaveis via import normal
113
182
 
183
+ Example:
114
184
  import aleatorio
115
185
  from matematica import raiz_quadrada, PI
116
186
  from colecoes import Contador, Pilha
@@ -118,19 +188,13 @@ def instalar() -> None:
118
188
  """
119
189
  if not any(isinstance(f, ImportadorPortugues) for f in sys.meta_path):
120
190
  sys.meta_path.insert(0, _gancho)
191
+ if not any(isinstance(f, ImportadorPtpy) for f in sys.meta_path):
192
+ sys.meta_path.append(_gancho_ptpy)
121
193
 
122
194
 
123
195
  def desinstalar() -> None:
124
196
  """Remove o importador de modulos em Portugues do sys.meta_path."""
125
197
  sys.meta_path[:] = [
126
- f for f in sys.meta_path if not isinstance(f, ImportadorPortugues)
198
+ f for f in sys.meta_path
199
+ if not isinstance(f, (ImportadorPortugues, ImportadorPtpy))
127
200
  ]
128
-
129
-
130
- def listar_modulos() -> dict[str, str]:
131
- """Retorna o mapeamento de nomes em Portugues para modulos portugython.
132
-
133
- Returns:
134
- Dicionario com nome_portugues -> caminho_modulo.
135
- """
136
- return dict(MAPA_MODULOS)
@@ -0,0 +1,254 @@
1
+ """transpilador - converte codigo Python escrito em Portugues para Python padrao.
2
+
3
+ Uso como modulo:
4
+
5
+ from portugython.transpilador import traduzir, executar_texto, executar_arquivo
6
+ codigo_py = traduzir("se x > 0:\\n escreva(x)")
7
+ executar_texto("para i em intervalo(3):\\n escreva(i)")
8
+
9
+ Uso como CLI:
10
+
11
+ ptpy meu_programa.ptpy
12
+ ptpy meu_programa.py # qualquer .py com sintaxe portuguesa
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import io
18
+ import sys
19
+ import tokenize
20
+ from pathlib import Path
21
+
22
+ # ---------------------------------------------------------------------------
23
+ # Mapa de palavras-chave: Portugues -> Python
24
+ # ---------------------------------------------------------------------------
25
+
26
+ MAPA_PALAVRAS_CHAVE: dict[str, str] = {
27
+ # Controle de fluxo
28
+ "se": "if",
29
+ "senao": "else",
30
+ "senaose": "elif",
31
+ "para": "for",
32
+ "enquanto": "while",
33
+ "parar": "break",
34
+ "continuar": "continue",
35
+ "passar": "pass",
36
+ # Funcoes e classes
37
+ "defina": "def",
38
+ "classe": "class",
39
+ "retorne": "return",
40
+ "produzca": "yield",
41
+ "anonima": "lambda",
42
+ # Importacoes
43
+ "importe": "import",
44
+ "de": "from",
45
+ "como": "as",
46
+ # Tratamento de erros
47
+ "tente": "try",
48
+ "exceto": "except",
49
+ "finalmente": "finally",
50
+ "levante": "raise",
51
+ # Gerenciadores de contexto
52
+ "com": "with",
53
+ # Operadores logicos (como funcoes: e/ou/nao ja existem em __init__.py,
54
+ # mas aqui traduzimos os tokens da sintaxe)
55
+ "nao": "not",
56
+ "e": "and",
57
+ "ou": "or",
58
+ # Pertencimento e identidade
59
+ "em": "in",
60
+ "eh": "is",
61
+ # Escopo de variaveis
62
+ "global": "global",
63
+ "naolocal": "nonlocal",
64
+ # Outros
65
+ "excluir": "del",
66
+ "afirmar": "assert",
67
+ # Concorrencia
68
+ "assincronico": "async",
69
+ "aguardar": "await",
70
+ # Constantes booleanas e nula
71
+ "Verdadeiro": "True",
72
+ "Falso": "False",
73
+ "Nenhum": "None",
74
+ }
75
+
76
+ # Mapa inverso (Python -> Portugues) para referencia
77
+ MAPA_INVERSO: dict[str, str] = {v: k for k, v in MAPA_PALAVRAS_CHAVE.items()}
78
+
79
+ # Palavras reservadas em portugues (nao podem ser nomes de variaveis)
80
+ PALAVRAS_RESERVADAS: frozenset[str] = frozenset(MAPA_PALAVRAS_CHAVE.keys())
81
+
82
+ # ---------------------------------------------------------------------------
83
+ # Nucleo do transpilador
84
+ # ---------------------------------------------------------------------------
85
+
86
+
87
+ def traduzir(codigo: str) -> str:
88
+ """Traduz codigo Python em Portugues para Python padrao.
89
+
90
+ Usa o tokenizador do Python para substituir apenas tokens NAME que
91
+ correspondem a palavras-chave em Portugues. Strings, comentarios e
92
+ identificadores compostos (ex: minha_se_variavel) sao intocados.
93
+
94
+ Args:
95
+ codigo: Codigo fonte em Portugues.
96
+
97
+ Returns:
98
+ Codigo fonte Python valido.
99
+
100
+ Example:
101
+ >>> traduzir("se x > 0:\\n retorne x")
102
+ 'if x > 0:\\n return x'
103
+ """
104
+ tokens_novos: list[tuple] = []
105
+ try:
106
+ leitor = io.StringIO(codigo).readline
107
+ for tok in tokenize.generate_tokens(leitor):
108
+ tipo, valor, inicio, fim, linha = tok
109
+ if tipo == tokenize.NAME and valor in MAPA_PALAVRAS_CHAVE:
110
+ tokens_novos.append(
111
+ (tipo, MAPA_PALAVRAS_CHAVE[valor], inicio, fim, linha)
112
+ )
113
+ else:
114
+ tokens_novos.append(tok)
115
+ except tokenize.TokenError:
116
+ return codigo
117
+
118
+ return tokenize.untokenize(tokens_novos)
119
+
120
+
121
+ def traduzir_para_portugues(codigo: str) -> str:
122
+ """Traduz codigo Python padrao para Python em Portugues.
123
+
124
+ Operacao inversa de traduzir().
125
+
126
+ Args:
127
+ codigo: Codigo fonte Python padrao.
128
+
129
+ Returns:
130
+ Codigo fonte em Portugues.
131
+ """
132
+ tokens_novos: list[tuple] = []
133
+ try:
134
+ leitor = io.StringIO(codigo).readline
135
+ for tok in tokenize.generate_tokens(leitor):
136
+ tipo, valor, inicio, fim, linha = tok
137
+ if tipo == tokenize.NAME and valor in MAPA_INVERSO:
138
+ tokens_novos.append((tipo, MAPA_INVERSO[valor], inicio, fim, linha))
139
+ else:
140
+ tokens_novos.append(tok)
141
+ except tokenize.TokenError:
142
+ return codigo
143
+
144
+ return tokenize.untokenize(tokens_novos)
145
+
146
+
147
+ # ---------------------------------------------------------------------------
148
+ # Execucao
149
+ # ---------------------------------------------------------------------------
150
+
151
+
152
+ def executar_texto(
153
+ codigo: str,
154
+ globais: dict | None = None,
155
+ locais: dict | None = None,
156
+ ) -> None:
157
+ """Traduz e executa uma string de codigo em Portugues.
158
+
159
+ O ambiente de execucao inclui automaticamente todos os nomes definidos
160
+ em portugython (escreva, intervalo, Verdadeiro, etc.).
161
+
162
+ Args:
163
+ codigo: Codigo em Portugues a executar.
164
+ globais: Namespace global adicional. Mesclado com portugython.
165
+ locais: Namespace local. None usa o global.
166
+
167
+ Example:
168
+ executar_texto("para i em intervalo(3):\\n escreva(i)")
169
+ """
170
+ import portugython # garante que o hook esta ativo # noqa: PLC0415
171
+
172
+ env: dict = {
173
+ k: getattr(portugython, k)
174
+ for k in dir(portugython)
175
+ if not k.startswith("_")
176
+ }
177
+ env["__builtins__"] = __builtins__
178
+ if globais:
179
+ env.update(globais)
180
+ codigo_py = traduzir(codigo)
181
+ exec(compile(codigo_py, "<portugython>", "exec"), env, locais or env) # noqa: S102
182
+
183
+
184
+ def executar_arquivo(caminho: str | Path) -> None:
185
+ """Traduz e executa um arquivo .ptpy ou .py em Portugues.
186
+
187
+ O ambiente de execucao inclui automaticamente todos os nomes definidos
188
+ em portugython.
189
+
190
+ Args:
191
+ caminho: Caminho para o arquivo a executar.
192
+
193
+ Example:
194
+ executar_arquivo("meu_programa.ptpy")
195
+ """
196
+ import portugython # garante que o hook esta ativo # noqa: PLC0415
197
+
198
+ caminho = Path(caminho)
199
+ codigo = caminho.read_text(encoding="utf-8")
200
+ codigo_py = traduzir(codigo)
201
+ env: dict = {
202
+ k: getattr(portugython, k)
203
+ for k in dir(portugython)
204
+ if not k.startswith("_")
205
+ }
206
+ env["__builtins__"] = __builtins__
207
+ env["__file__"] = str(caminho)
208
+ env["__name__"] = "__main__"
209
+ exec(compile(codigo_py, str(caminho), "exec"), env) # noqa: S102
210
+
211
+
212
+ def mostrar_traducao(codigo: str) -> None:
213
+ """Imprime o codigo traduzido para Python padrao.
214
+
215
+ Util para depuracao e aprendizado.
216
+
217
+ Args:
218
+ codigo: Codigo em Portugues.
219
+ """
220
+ print(traduzir(codigo))
221
+
222
+
223
+ # ---------------------------------------------------------------------------
224
+ # Entrada de linha de comando: ptpy
225
+ # ---------------------------------------------------------------------------
226
+
227
+
228
+ def _main() -> None:
229
+ """Ponto de entrada do CLI: ptpy <arquivo>."""
230
+ if len(sys.argv) < 2:
231
+ print("Uso: ptpy <arquivo.ptpy>")
232
+ print(" ptpy -c 'codigo em portugues'")
233
+ sys.exit(1)
234
+
235
+ if sys.argv[1] == "-c":
236
+ if len(sys.argv) < 3:
237
+ print("ptpy -c requer um argumento de codigo")
238
+ sys.exit(1)
239
+ codigo = sys.argv[2]
240
+ sys.argv = sys.argv[2:]
241
+ executar_texto(codigo)
242
+ elif sys.argv[1] == "--mostrar":
243
+ if len(sys.argv) < 3:
244
+ print("ptpy --mostrar requer um arquivo")
245
+ sys.exit(1)
246
+ codigo = Path(sys.argv[2]).read_text(encoding="utf-8")
247
+ mostrar_traducao(codigo)
248
+ else:
249
+ caminho = Path(sys.argv[1])
250
+ if not caminho.exists():
251
+ print(f"Arquivo nao encontrado: {caminho}")
252
+ sys.exit(1)
253
+ sys.argv = sys.argv[1:]
254
+ executar_arquivo(caminho)
@@ -4,8 +4,8 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "portugython"
7
- version = "0.3.0"
8
- description = "A Portuguese translation of the Python functions/modules/packages"
7
+ version = "0.4.0"
8
+ description = "A Portuguese translation of Python: functions, modules and syntax"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
11
11
  requires-python = ">=3.10"
@@ -29,6 +29,9 @@ classifiers = [
29
29
  Homepage = "https://github.com/spacemany2k38/portugython"
30
30
  "Bug Tracker" = "https://github.com/spacemany2k38/portugython/issues"
31
31
 
32
+ [project.scripts]
33
+ ptpy = "portugython.transpilador:_main"
34
+
32
35
  [project.optional-dependencies]
33
36
  dev = ["pytest", "ruff"]
34
37
 
@@ -0,0 +1,38 @@
1
+ # Programa de exemplo em portugues usando sintaxe traduzida
2
+ importe portugython
3
+
4
+ de aleatorio importe escolha, inteiro_aleatorio
5
+
6
+ defina saudacao(nome):
7
+ retorne "Ola, " + nome + "!"
8
+
9
+ defina calcular_fatorial(n):
10
+ se n <= 1:
11
+ retorne 1
12
+ senao:
13
+ retorne n * calcular_fatorial(n - 1)
14
+
15
+ nomes = ["Alice", "Bob", "Carlos", "Diana"]
16
+ nome_escolhido = escolha(nomes)
17
+ mensagem = saudacao(nome_escolhido)
18
+
19
+ para i em intervalo(1, 6):
20
+ fato = calcular_fatorial(i)
21
+ escreva(f"{i}! = {fato}")
22
+
23
+ numero = inteiro_aleatorio(1, 10)
24
+ se numero > 5:
25
+ escreva("Grande:", numero)
26
+ senaose numero == 5:
27
+ escreva("Exato cinco!")
28
+ senao:
29
+ escreva("Pequeno:", numero)
30
+
31
+ frutas = ["manga", "abacaxi", "goiaba", "caju"]
32
+ maiusculas = [fruta.upper() para fruta em frutas]
33
+ escreva(maiusculas)
34
+
35
+ contador = 0
36
+ enquanto contador < 3:
37
+ escreva("Contando:", contador)
38
+ contador = contador + 1
@@ -33,7 +33,7 @@ from portugython import (
33
33
 
34
34
  def test_version():
35
35
  """Testa a versao do pacote."""
36
- assert __version__ == "0.3.0"
36
+ assert __version__ == "0.4.0"
37
37
 
38
38
 
39
39
  def test_type_functions():
@@ -0,0 +1,310 @@
1
+ """Testes para o transpilador de palavras-chave em Portugues."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import textwrap
6
+
7
+ from portugython.transpilador import (
8
+ MAPA_PALAVRAS_CHAVE,
9
+ PALAVRAS_RESERVADAS,
10
+ executar_texto,
11
+ mostrar_traducao,
12
+ traduzir,
13
+ traduzir_para_portugues,
14
+ )
15
+
16
+ # ---------------------------------------------------------------------------
17
+ # Testes de traducao de palavras-chave individuais
18
+ # ---------------------------------------------------------------------------
19
+
20
+
21
+ def test_se_para_if():
22
+ resultado = traduzir("se x > 0: passar")
23
+ assert "if" in resultado
24
+ assert "pass" in resultado
25
+ assert "se" not in resultado.split()
26
+
27
+
28
+ def test_senao_para_else():
29
+ codigo = "se x:\n passar\nsenao:\n passar"
30
+ resultado = traduzir(codigo)
31
+ assert "else" in resultado
32
+ assert "pass" in resultado
33
+
34
+
35
+ def test_senaose_para_elif():
36
+ codigo = "se x > 0:\n passar\nsenaose x == 0:\n passar"
37
+ resultado = traduzir(codigo)
38
+ assert "elif" in resultado
39
+
40
+
41
+ def test_para_para_for():
42
+ resultado = traduzir("para i em intervalo(10): passar")
43
+ assert "for" in resultado
44
+ assert "in" in resultado
45
+ assert "pass" in resultado
46
+ # intervalo e um alias de funcao (nao palavra-chave), permanece intacto
47
+ assert "intervalo" in resultado
48
+
49
+
50
+ def test_enquanto_para_while():
51
+ resultado = traduzir("enquanto Verdadeiro: parar")
52
+ assert "while" in resultado
53
+ assert "True" in resultado
54
+ assert "break" in resultado
55
+
56
+
57
+ def test_defina_para_def():
58
+ resultado = traduzir("defina minha_funcao(): passar")
59
+ assert "def" in resultado
60
+ assert "pass" in resultado
61
+
62
+
63
+ def test_classe_para_class():
64
+ resultado = traduzir("classe MinhaClasse: passar")
65
+ assert "class" in resultado
66
+
67
+
68
+ def test_retorne_para_return():
69
+ resultado = traduzir("defina f(): retorne 42")
70
+ assert "return" in resultado
71
+
72
+
73
+ def test_importe_para_import():
74
+ resultado = traduzir("importe os")
75
+ assert "import os" in resultado
76
+
77
+
78
+ def test_de_importe_para_from_import():
79
+ resultado = traduzir("de os importe path")
80
+ assert "from os import path" in resultado
81
+
82
+
83
+ def test_como_para_as():
84
+ resultado = traduzir("importe os como sistema_op")
85
+ assert "as sistema_op" in resultado
86
+
87
+
88
+ def test_tente_exceto_finalmente():
89
+ codigo = textwrap.dedent("""
90
+ tente:
91
+ passar
92
+ exceto ValueError:
93
+ passar
94
+ finalmente:
95
+ passar
96
+ """)
97
+ resultado = traduzir(codigo)
98
+ assert "try" in resultado
99
+ assert "except" in resultado
100
+ assert "finally" in resultado
101
+
102
+
103
+ def test_levante_para_raise():
104
+ resultado = traduzir("levante ValueError('erro')")
105
+ assert "raise" in resultado
106
+
107
+
108
+ def test_com_como_para_with_as():
109
+ resultado = traduzir("com abrir('f') como arq: passar")
110
+ assert "with" in resultado
111
+ assert "as" in resultado
112
+
113
+
114
+ def test_produzca_para_yield():
115
+ resultado = traduzir("defina gen():\n produzca 1")
116
+ assert "yield" in resultado
117
+
118
+
119
+ def test_nao_ou_e():
120
+ resultado = traduzir("se nao x ou y e z: passar")
121
+ assert "not" in resultado
122
+ assert "or" in resultado
123
+ assert "and" in resultado
124
+
125
+
126
+ def test_eh_para_is():
127
+ resultado = traduzir("se x eh Nenhum: passar")
128
+ assert "is" in resultado
129
+ assert "None" in resultado
130
+
131
+
132
+ def test_excluir_para_del():
133
+ resultado = traduzir("excluir minha_var")
134
+ assert "del" in resultado
135
+
136
+
137
+ def test_afirmar_para_assert():
138
+ resultado = traduzir("afirmar x > 0")
139
+ assert "assert" in resultado
140
+
141
+
142
+ def test_global_e_naolocal():
143
+ codigo = "defina f():\n global x\n naolocal y"
144
+ resultado = traduzir(codigo)
145
+ assert "global" in resultado
146
+ assert "nonlocal" in resultado
147
+
148
+
149
+ def test_assincronico_aguardar():
150
+ codigo = "assincronico defina buscar():\n aguardar algo()"
151
+ resultado = traduzir(codigo)
152
+ assert "async def" in resultado
153
+ assert "await" in resultado
154
+
155
+
156
+ def test_verdadeiro_falso_nenhum():
157
+ resultado = traduzir("x = Verdadeiro\ny = Falso\nz = Nenhum")
158
+ assert "True" in resultado
159
+ assert "False" in resultado
160
+ assert "None" in resultado
161
+
162
+
163
+ def test_parar_continuar_passar():
164
+ codigo = textwrap.dedent("""
165
+ para i em intervalo(10):
166
+ se i == 5: parar
167
+ se i == 3: continuar
168
+ passar
169
+ """)
170
+ resultado = traduzir(codigo)
171
+ assert "break" in resultado
172
+ assert "continue" in resultado
173
+ assert "pass" in resultado
174
+
175
+
176
+ # ---------------------------------------------------------------------------
177
+ # Testes de preservacao de strings e comentarios
178
+ # ---------------------------------------------------------------------------
179
+
180
+
181
+ def test_strings_nao_sao_traduzidas():
182
+ codigo = 'x = "para se enquanto defina"'
183
+ resultado = traduzir(codigo)
184
+ assert '"para se enquanto defina"' in resultado
185
+
186
+
187
+ def test_comentarios_nao_sao_traduzidos():
188
+ codigo = "# para se enquanto\nx = 1"
189
+ resultado = traduzir(codigo)
190
+ assert "# para se enquanto" in resultado
191
+
192
+
193
+ def test_identificadores_compostos_preservados():
194
+ codigo = "minha_para_funcao = 1\nresultado_se_positivo = 2"
195
+ resultado = traduzir(codigo)
196
+ assert "minha_para_funcao" in resultado
197
+ assert "resultado_se_positivo" in resultado
198
+
199
+
200
+ # ---------------------------------------------------------------------------
201
+ # Testes de execucao
202
+ # ---------------------------------------------------------------------------
203
+
204
+
205
+ def test_executar_texto_basico(capsys):
206
+ executar_texto("escreva('ola mundo')")
207
+ saida = capsys.readouterr().out
208
+ assert "ola mundo" in saida
209
+
210
+
211
+ def test_executar_texto_com_se(capsys):
212
+ executar_texto("x = 10\nse x > 5:\n escreva('grande')")
213
+ saida = capsys.readouterr().out
214
+ assert "grande" in saida
215
+
216
+
217
+ def test_executar_texto_para_loop(capsys):
218
+ executar_texto("para i em intervalo(3):\n escreva(i)")
219
+ saida = capsys.readouterr().out
220
+ assert "0" in saida
221
+ assert "1" in saida
222
+ assert "2" in saida
223
+
224
+
225
+ def test_executar_texto_defina_funcao(capsys):
226
+ codigo = textwrap.dedent("""
227
+ defina quadrado(n):
228
+ retorne n * n
229
+ escreva(quadrado(4))
230
+ """)
231
+ executar_texto(codigo)
232
+ saida = capsys.readouterr().out
233
+ assert "16" in saida
234
+
235
+
236
+ def test_executar_texto_tente_exceto(capsys):
237
+ codigo = textwrap.dedent("""
238
+ tente:
239
+ levante ValueError("teste")
240
+ exceto ValueError como err:
241
+ escreva("capturado:", err)
242
+ """)
243
+ executar_texto(codigo)
244
+ saida = capsys.readouterr().out
245
+ assert "capturado" in saida
246
+
247
+
248
+ def test_executar_texto_enquanto_loop(capsys):
249
+ codigo = "i = 0\nenquanto i < 3:\n escreva(i)\n i = i + 1"
250
+ executar_texto(codigo)
251
+ saida = capsys.readouterr().out
252
+ assert "0" in saida
253
+ assert "2" in saida
254
+
255
+
256
+ def test_executar_texto_lista_compreensao(capsys):
257
+ codigo = "resultado = [x * 2 para x em intervalo(4)]\nescreva(resultado)"
258
+ executar_texto(codigo)
259
+ saida = capsys.readouterr().out
260
+ assert "[0, 2, 4, 6]" in saida
261
+
262
+
263
+ # ---------------------------------------------------------------------------
264
+ # Testes do mapa e palavras reservadas
265
+ # ---------------------------------------------------------------------------
266
+
267
+
268
+ def test_mapa_palavras_chave_nao_vazio():
269
+ assert len(MAPA_PALAVRAS_CHAVE) > 20
270
+
271
+
272
+ def test_palavras_reservadas_sao_frozenset():
273
+ assert isinstance(PALAVRAS_RESERVADAS, frozenset)
274
+
275
+
276
+ def test_palavras_reservadas_contem_palavras_chave():
277
+ for pt in MAPA_PALAVRAS_CHAVE:
278
+ assert pt in PALAVRAS_RESERVADAS
279
+
280
+
281
+ def test_traducao_inversa(capsys):
282
+ codigo_python = "if x > 0:\n return x"
283
+ codigo_pt = traduzir_para_portugues(codigo_python)
284
+ assert "se" in codigo_pt
285
+ assert "retorne" in codigo_pt
286
+
287
+
288
+ def test_mostrar_traducao(capsys):
289
+ mostrar_traducao("se x: passar")
290
+ saida = capsys.readouterr().out
291
+ assert "if" in saida
292
+ assert "pass" in saida
293
+
294
+
295
+ # ---------------------------------------------------------------------------
296
+ # Testes do arquivo .ptpy
297
+ # ---------------------------------------------------------------------------
298
+
299
+
300
+ def test_executar_arquivo_ptpy(tmp_path, capsys):
301
+ arquivo = tmp_path / "teste.ptpy"
302
+ arquivo.write_text(
303
+ "defina ola(nome):\n retorne 'Ola, ' + nome\nescreva(ola('mundo'))",
304
+ encoding="utf-8",
305
+ )
306
+ from portugython.transpilador import executar_arquivo
307
+
308
+ executar_arquivo(arquivo)
309
+ saida = capsys.readouterr().out
310
+ assert "Ola, mundo" in saida
File without changes
File without changes