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.
- {portugython-0.3.0 → portugython-0.4.0}/CHANGELOG.md +54 -1
- {portugython-0.3.0 → portugython-0.4.0}/PKG-INFO +105 -2
- {portugython-0.3.0 → portugython-0.4.0}/README.md +103 -0
- {portugython-0.3.0 → portugython-0.4.0}/portugython/__init__.py +18 -1
- {portugython-0.3.0 → portugython-0.4.0}/portugython/_importador.py +76 -12
- portugython-0.4.0/portugython/transpilador.py +254 -0
- {portugython-0.3.0 → portugython-0.4.0}/pyproject.toml +5 -2
- portugython-0.4.0/tests/ola_mundo.ptpy +38 -0
- {portugython-0.3.0 → portugython-0.4.0}/tests/test_portugython.py +1 -1
- portugython-0.4.0/tests/test_transpilador.py +310 -0
- {portugython-0.3.0 → portugython-0.4.0}/.gitignore +0 -0
- {portugython-0.3.0 → portugython-0.4.0}/LICENSE +0 -0
- {portugython-0.3.0 → portugython-0.4.0}/portugython/abstrato.py +0 -0
- {portugython-0.3.0 → portugython-0.4.0}/portugython/aleatorio.py +0 -0
- {portugython-0.3.0 → portugython-0.4.0}/portugython/argumentos.py +0 -0
- {portugython-0.3.0 → portugython-0.4.0}/portugython/assincronico.py +0 -0
- {portugython-0.3.0 → portugython-0.4.0}/portugython/bisecao.py +0 -0
- {portugython-0.3.0 → portugython-0.4.0}/portugython/calendario.py +0 -0
- {portugython-0.3.0 → portugython-0.4.0}/portugython/caminho.py +0 -0
- {portugython-0.3.0 → portugython-0.4.0}/portugython/classe_dados.py +0 -0
- {portugython-0.3.0 → portugython-0.4.0}/portugython/codificacao.py +0 -0
- {portugython-0.3.0 → portugython-0.4.0}/portugython/colecoes.py +0 -0
- {portugython-0.3.0 → portugython-0.4.0}/portugython/compressao.py +0 -0
- {portugython-0.3.0 → portugython-0.4.0}/portugython/configuracao.py +0 -0
- {portugython-0.3.0 → portugython-0.4.0}/portugython/contexto.py +0 -0
- {portugython-0.3.0 → portugython-0.4.0}/portugython/copia.py +0 -0
- {portugython-0.3.0 → portugython-0.4.0}/portugython/csv_pt.py +0 -0
- {portugython-0.3.0 → portugython-0.4.0}/portugython/depuracao.py +0 -0
- {portugython-0.3.0 → portugython-0.4.0}/portugython/enumeracao.py +0 -0
- {portugython-0.3.0 → portugython-0.4.0}/portugython/estatistica.py +0 -0
- {portugython-0.3.0 → portugython-0.4.0}/portugython/estrutura.py +0 -0
- {portugython-0.3.0 → portugython-0.4.0}/portugython/expressao.py +0 -0
- {portugython-0.3.0 → portugython-0.4.0}/portugython/formatacao.py +0 -0
- {portugython-0.3.0 → portugython-0.4.0}/portugython/funcional.py +0 -0
- {portugython-0.3.0 → portugython-0.4.0}/portugython/hash_pt.py +0 -0
- {portugython-0.3.0 → portugython-0.4.0}/portugython/identificador.py +0 -0
- {portugython-0.3.0 → portugython-0.4.0}/portugython/iteradores.py +0 -0
- {portugython-0.3.0 → portugython-0.4.0}/portugython/json.py +0 -0
- {portugython-0.3.0 → portugython-0.4.0}/portugython/matematica.py +0 -0
- {portugython-0.3.0 → portugython-0.4.0}/portugython/py.typed +0 -0
- {portugython-0.3.0 → portugython-0.4.0}/portugython/rede.py +0 -0
- {portugython-0.3.0 → portugython-0.4.0}/portugython/registro.py +0 -0
- {portugython-0.3.0 → portugython-0.4.0}/portugython/serializacao.py +0 -0
- {portugython-0.3.0 → portugython-0.4.0}/portugython/sistema.py +0 -0
- {portugython-0.3.0 → portugython-0.4.0}/portugython/soquete.py +0 -0
- {portugython-0.3.0 → portugython-0.4.0}/portugython/subprocesso.py +0 -0
- {portugython-0.3.0 → portugython-0.4.0}/portugython/tempo.py +0 -0
- {portugython-0.3.0 → portugython-0.4.0}/portugython/threads.py +0 -0
- {portugython-0.3.0 → portugython-0.4.0}/portugython/tipagem.py +0 -0
- {portugython-0.3.0 → portugython-0.4.0}/tests/__init__.py +0 -0
- {portugython-0.3.0 → portugython-0.4.0}/tests/test_importador.py +0 -0
- {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.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.
|
|
4
|
-
Summary: A Portuguese translation of
|
|
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.
|
|
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
|
|
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
|
|
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.
|
|
8
|
-
description = "A Portuguese translation of
|
|
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
|
|
@@ -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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|