Um compilador e interpretador totalmente funcional para as linguagens de programação AdvPL e TLPP, construído em Go.
curl -fsSL https://raw.githubusercontent.com/peder1981/AdvPP/master/install.sh | shDetecta SO/arquitetura automaticamente (Linux amd64/arm64, macOS Apple Silicon) e instala o binário advplc mais recente em ~/.local/bin (ou /usr/local/bin como root). Sem Go, sem dependências — o binário é estático.
Alternativas:
- Debian/Ubuntu: baixe o
.debem Releases esudo apt install ./advpp_*.deb - Windows: baixe o
.zipem Releases - Extensão VS Code (syntax highlighting, build/run/debug, debugger real,
advplc serveattach): baixe o.vsixem Releases e instale comcode --install-extension advpl-tlpp-*.vsix— já vem com o compilador embutido (linux-x64, linux-arm64, win32-x64, darwin-arm64), nada mais pra instalar. Fonte em tools/vscode-advpl
Requer Go 1.24+:
git clone https://github.com/peder1981/AdvPP && cd AdvPP
make build # gera advplc, adveditor, advpp-ide na raiz do repo- Lexer: Tokenizador completo para sintaxe AdvPL/TLPP incluindo palavras-chave, operadores, blocos de código e diretivas de pré-processador
- Pré-processador: Trata
#include,#define(inclusive multi-linha),#ifdef/#ifndef/#else/#endif, e#xCommand/#command/#xTranslate/#translatecom pattern-matching real (marcadores<nome>, cláusulas opcionais[...], flags[<nome:LITERAL>], resultado com<{nome}>/<.nome.>) - Parser: Parser recursivo descendente completo gerando uma AST
- Compilador: Gera bytecode otimizado com 88 opcodes
- Serialização de Bytecode: Salva bytecode compilado em disco para execução posterior
- Executáveis Standalone: Constrói executáveis autossuficientes com bytecode embutido usando go:embed
- Máquina Virtual: VM completa com todos os opcodes implementados
- Runtime: Funções nativas (ConOut, MsgInfo, AllTrim, Str, Val, aAdd, aScan, Len, etc.)
- I/O de disco, arquivo e sistema:
MemoRead/MemoWrite/FErase, API de handle para streaming (FOpen/FCreate/FReadStr/FWrite/FSeek/FClose/FError), console interativoConIne chamada de sistemaWaitRun— ver seção Funções de I/O, arquivo e sistema - BLAS ternária + IA em AdvPL puro: kernel multiply-free
MatVecTern(produto matriz-vetor ternário estilo BitNet) e três modelos escritos inteiramente em AdvPL — Markov (pt_llm), respondedor por recuperação (pt_chat) e híbrido Markov+rede neural ternária (pt_nn); ver Exemplos de IA em AdvPL puro - IDE Gráfica: Ambiente de Desenvolvimento Gráfico usando Fyne com editor de código, navegador de arquivos e compilador integrado
- Framework UI: Aplicações gráficas usando Fyne (diálogos, formulários, grids, botões, menus)
- Banco de Dados: Operações de banco de dados baseadas em Workarea (DbSelectArea, DbSeek, DbSkip, RecLock, etc.)
- Classes: Sistema de classes completo com Data/Method/Constructor, herança via
from - Blocos de Código: Blocos de código executáveis
{|| ... } - MVC: Suporte FWFormModel, FWFormView, FWFormBrowse com validação de campos e tratamento de eventos
- Multi-thread:
StartJob()(execução em VM isolado, semântica de work process) eFWGridProcess(pool de threads comSetThreadGrid,CallExecute,StopExecute,IsFinished, meters e log);advplc check arq1 arq2 ...verifica N arquivos em paralelo - Renderer web (PO-UI):
advplc serve programa.prwexecuta o programa no servidor e renderiza a interface no browser com PO-UI (embutido no binário): console e diálogos em tempo real,FWMBrowse→po-tablecom dicionário SX3, formuláriospo-dynamic-form, MSDIALOG legado (@ SAY/GET/BUTTON) como modal por heurística de grade e hot reload com--watch - Motor de inferência LLM (
pkg/llm+ classeLLM): carrega modelos GGUF quantizados em I2_S (BitNet/Falcon3-1.58bit) e gera texto direto do AdvPL/TLPP — 100% Go, sem CGO, com kernel SIMD AVX2 em amd64 e fallback escalar em qualquer outra arquitetura - Servidor MCP nativo (
pkg/mcp+ classeMCPServer): expõe funções AdvPL/TLPP como "tools" de um servidor MCP real (JSON-RPC 2.0 sobre stdio) — funciona de verdade, validado com o SDK oficial do MCP - Servidor REST nativo (
pkg/rest+ classeWSRestServer): sobe um servidor HTTP real (net/httppuro) e expõeUser Functionanotadas com@Get/@Post/@Put/@Patch/@Deletecomo rotas, com path params (/clientes/{id}), corpo JSON e dispatch real para a função AdvPL — o DSL clássicoWSRESTFUL/WSMETHODcontinua só reconhecido na sintaxe (ver Servidor REST) - Núcleo de Tensor (float32): classe
Tensoracelerada em Go (pkg/tensor) —MatMul, elementwise com broadcast, reduções, ativações,Softmax,Argmax,IndexRows— para construir e rodar modelos float com o AdvPL orquestrando; ver Núcleo de Tensor - Autodiff + treino (float32): motor de diferenciação reversa (
pkg/autograd) com a classeVariable(tape +Backward), ops diferenciáveis (MatMul, Add, Mul, Relu, Sum, Mean, MSE) e otimizadorSGD— treina modelos float com o AdvPL orquestrando; ver Autodiff e treino - Cliente HTTP nativo:
FWHttpGet/FWHttpPost/FWHttpPut/FWHttpPatch/FWHttpDelete+FWHttpBody/FWHttpStatus/FWHttpError— requisições HTTP com suporte a certificados PKCS#12 (.pfx/.p12), timeout 30s e TLS com verificação; ver Cliente HTTP Nativo
User Function McpDemo()
Local oMCP := MCPServer():New("meu-servidor", "1.0.0")
oMCP:AddTool("soma", "Soma dois números", ;
'{"type":"object","properties":{"a":{"type":"number"},"b":{"type":"number"}},"required":["a","b"]}', ;
"ToolSoma")
oMCP:Serve() // bloqueia lendo/escrevendo em stdin/stdout
Return
User Function ToolSoma(oArgs)
Return cValToChar(oArgs:A + oArgs:B)Roda com advplc run meu_programa.prw normalmente — não precisa de
comando novo. O MCPServer implementa o protocolo MCP (Model Context
Protocol) via JSON-RPC 2.0 sobre stdio — initialize, tools/list,
tools/call — em Go puro, sem CGO, sem dependências externas. Cada tool
chamada roda a função AdvPL correspondente numa VM isolada (mesmo
mecanismo do StartJob).
| Método | Descrição |
|---|---|
New(cNome, cVersao) |
Cria o servidor |
AddTool(cNome, cDescricao, cSchemaJSON, cNomeFuncao) |
Registra uma tool; a função recebe um objeto com os argumentos (oArgs:CAMPO) e retorna o texto do resultado |
Serve() |
Sobe o loop stdio (bloqueia) |
Validado com o SDK oficial em Python do MCP (cmd/advplc/mcp_integration_test.go).
@Get("/clientes/{id}")
User Function GetCliente(oParam)
Local jRet := JsonObject():New()
jRet["id"] := oParam:ID // path param populado automaticamente
jRet["nome"] := "Cliente " + oParam:ID
Return jRet
@Post("/clientes")
User Function NovoCliente(oParam)
Local jRet := JsonObject():New()
jRet["criado"] := .T.
jRet["nome"] := oParam:NOME // campo do corpo JSON da requisição
Return jRet
User Function RestDemo()
Local oRest := WSRestServer():New("meu-servidor-rest", "1.0.0")
// @Get/@Post/@Put/@Patch/@Delete acima já viram rota automaticamente;
// AddRoute cobre o caso de registrar manualmente:
oRest:AddRoute("GET", "/status", "GetStatus")
oRest:Serve(8080) // bloqueia servindo HTTP na porta 8080
ReturnRoda com advplc run meu_programa.prw normalmente. O WSRestServer sobe
um net/http.Server real (sem CGO, sem dependências externas) e, ao ser
criado, varre todas as User Function do programa procurando anotações
@Get/@Post/@Put/@Patch/@Delete("/path") para registrar como
rotas automaticamente — path params ({id}) via roteador nativo do Go
1.22+, query string e corpo JSON mesclados num único objeto de argumento
(oParam:CAMPO, maiúsculo) passado para a função. Cada requisição roda a
função numa VM isolada (mesmo mecanismo do MCPServer/StartJob),
banco e bytecode compartilhados. O retorno da função vira o corpo JSON
da resposta (200); erro vira 500; path não registrado vira 404; verbo
não registrado num path existente vira 405.
| Método | Descrição |
|---|---|
New(cNome, cVersao) |
Cria o servidor e auto-registra rotas de funções anotadas |
AddRoute(cVerbo, cPath, cNomeFuncao) |
Registra uma rota manualmente (cPath aceita {param}) |
Serve(nPorta) |
Sobe o servidor HTTP na porta indicada (bloqueia) |
Limitação conhecida: o DSL clássico WSRESTFUL <nome> ... WSMETHOD <verbo> PATH "..." ... ENDWSRESTFUL é reconhecido pelo parser mas não
executado — o verbo e o PATH são descartados ao virar AST, e a
implementação do método é ligada a uma instância de classe (não a uma
função top-level, que é o que o dispatch HTTP sabe chamar). Para expor
esse serviço via HTTP hoje, reescreva no estilo anotações acima ou
registre a rota manualmente com AddRoute. Detalhes em
COMPONENT_STATUS.md.
Testado de ponta a ponta com requisições HTTP reais em
cmd/advplc/rest_integration_test.go.
User Function LlmDemo()
Local oLLM := LLM():New("/caminho/Falcon3-3B-Instruct-1.58bit/ggml-model-i2_s.gguf")
ConOut(oLLM:Generate("The capital of France is", 6, 0)) // prompt, nMaxTokens, nTemperatura (0=greedy)
oLLM:Close()
ReturnMotor de inferência para modelos GGUF quantizados em I2_S (pesos
ternários -1/0/+1, formato usado pelo BitNet e por conversões como o
Falcon3-3B-Instruct-1.58bit) — escrito inteiramente em Go
(pkg/llm), sem llama.cpp, sem CGO e sem dependências externas.
Compila e roda de forma idêntica em Linux, Windows e macOS
(amd64/arm64); em amd64 usa um kernel SIMD (AVX2) com detecção de CPU
em runtime, caindo automaticamente para um caminho escalar puro em
qualquer CPU/arquitetura sem esse suporte.
Métodos da classe LLM:
| Método | Descrição |
|---|---|
New(cCaminhoGGUF) |
Carrega o modelo e o tokenizer |
Generate(cPrompt, nMaxTokens, nTemperatura) |
Gera texto (bloqueia até terminar; nTemperatura<=0 = greedy) |
Tokenize(cTexto) |
Retorna um array de token ids |
Decode(aTokens) |
Converte token ids de volta em texto |
Close() |
Libera o modelo |
Validado token a token contra o llama.cpp de referência (ver
pkg/llm/validate_test.go). Limitações: só arquitetura GGUF "llama"
com pesos I2_S; sem streaming (ver CHANGELOG para a lista completa).
advplc serve tests/mvc_browse_test.prw # http://localhost:8080
advplc serve programa.prw --port 9000 --watch # porta própria + hot reloadO programa AdvPL/TLPP roda na VM do servidor (mesmo banco ADVPP.db de
todas as ferramentas) e o browser é o terminal de interface — mesmo
modelo do SmartClient HTML do Protheus. O frontend PO-UI/Angular vai
embutido no binário (embed.FS): nenhuma dependência extra em produção.
ConOut→ console em tempo real (SSE)MsgYesNo/MsgInfo/... → diálogos PO-UI que bloqueiam a VM até a respostaFWMBrowsesobre um alias →po-tablecom colunas/títulos do SX3 e CRUD completo (po-dynamic-formgerado do dicionário, soft-delete padrão)DEFINE MSDIALOG+@ linha,coluna SAY/GET/BUTTON→ modal PO-UI; os valores digitados voltam para as variáveis do programa--watch: salvar o fonte recompila e recarrega o browser
Para alterar o frontend: make web (requer Node 20+; o resultado
compilado é versionado, então go build funciona sem Node).
O compilador AdvPP inclui um framework MVC (Model-View-Controller) completo para construir aplicações estruturadas:
FWFormModel - Modelo de dados com definições de campos e validação:
oModel := FWFormModel("CustomerModel")FWFormView - View de formulário com componentes e tratamento de eventos:
oView := FWFormView("CustomerView", oModel)FWFormBrowse - Componente grid/browse para exibição de dados:
oBrowse := FWFormBrowse("CustomerBrowse", oModel)- Validação de campos (obrigatório, tamanho, intervalo, personalizado)
- Tratamento de eventos (onChange, onClick, onGotFocus, onLostFocus)
- Renderização completa de widgets Fyne (TButton, TGet, TComboBox, TCheckBox, TLabel)
- Estruturas de dados de componentes com renderização visual
- Suporte a diálogos (diálogos, menus, barras de ferramentas, barras de status)
- Eventos de browse (onLineChange, onDbClick, onHeaderClick)
Nota: Componentes MVC renderizam visualmente usando Fyne. Manipuladores de eventos são parseados e armazenados no bytecode mas não conectados à VM — eventos de clique/foco/seleção do usuário não disparam os callbacks por enquanto. Usar polling ou callbacks manuais para lógica de validação/ação.
User Function MVCTest()
Local oModel := FWFormModel("CustomerModel")
Local oView := FWFormView("CustomerView", oModel)
Local oBrowse := FWFormBrowse("CustomerBrowse", oModel)
// Usar componentes MVC...
Return .T.# Compilar todas as ferramentas (advplc, adveditor, advpp-ide)
make build
# Rodar os testes (build + verificação de todos os fixtures em tests/)
make test
# Cross-compilar o CLI para Linux/Windows/macOS (amd64 e arm64) em dist/
make cross
# Gerar pacotes versionados (.tar.gz/.zip) em dist/
make package VERSION=1.1.0make release VERSION=1.1.0Isso cria e publica a tag v1.1.0. O GitHub Actions então compila
nativamente em Linux, Windows e macOS (incluindo as GUIs Fyne), gera os
pacotes (.tar.gz, .zip, .deb) e anexa tudo à Release automaticamente.
Todas as ferramentas (advplc, adveditor, advpp-ide) enxergam o mesmo banco SQLite, resolvido nesta ordem:
- Flag explícita (
advplc run prog.prw --db-path /caminho/banco.db) - Variável de ambiente
ADVPP_DB - Banco configurado em
~/.advpp/advpp_config.json(só se esse arquivo já existir — configurar isso é o que torna o banco "global") - Padrão:
./advpp.dbno diretório de trabalho atual — criado automaticamente (RetSqlName/DbSelectArea/etc. funcionam mesmo sem nenhuma tabela ainda; use o AdvEditor no mesmo diretório para criar tabelas, campos e índices nesse banco)
O driver SQLite é 100% Go (modernc.org/sqlite) — sem CGO, sem dependências externas, idêntico em Linux, Windows e macOS.
# Executar arquivo fonte AdvPL/TLPP (compila em memória e executa)
./advplc run program.prw
# Compilar fonte para arquivo de bytecode
./advplc compile program.prw -o program.bytecode
# Executar arquivo de bytecode compilado
./advplc exec program.bytecode
# Construir executável standalone (embute bytecode e runtime)
./advplc build program.prw -o program
# Verificar apenas sintaxe
./advplc check program.prw
# Imprimir estrutura AST
./advplc ast program.prw
# Imprimir bytecode
./advplc bytecode program.prw# Iniciar ambiente de desenvolvimento gráfico
./advpp-ideA IDE gráfica fornece:
- Editor de Código: Editor de texto multi-linha com suporte para arquivos .prw, .tlpp e .prg
- Operações de Arquivo: Funcionalidades Novo, Abrir, Salvar, Salvar Como
- Explorador de Projeto: Navegador de arquivos mostrando diretório atual com destaque de arquivos fonte
- Integração de Build: Comandos Compilar, Executar e Compilar & Executar
- Console de Saída: Mostra resultados de compilação e saída do programa
- Suporte a Diálogos: Funções MsgInfo, MsgStop, MsgAlert e MsgYesNo exibem diálogos Fyne
- Renderização MVC: Componentes FWFormModel, FWFormView, FWFormBrowse renderizam visualmente via Fyne (event handling parcial)
- User Function, Static Function, Function declarations
- Escopos de variável Local, Private, Public, Static
- If/ElseIf/Else/EndIf, For/Next (inclusive
Stepnegativo/descendente), While/EndDo, Do Case/EndCase Loop(continue) eExit(break) em loops, com aninhamento correto- Tratamento de erro Begin Sequence/Recover/End Sequence
- Blocos de código
{|| expr } - Class/EndClass com Data, Method, Constructor
- Implementação de método fora do bloco de classe
- Acesso a campo de alias
SA1->A1_NOME - Auto-referência
::property - Todos os tipos de dados AdvPL: Character, Numeric, Logical, Date, Array, Code Block, Nil, Object
If()/IIF()com 3 argumentos fazem curto-circuito (avaliam só o ramo escolhido)Private/Publiccom escopo dinâmico (visíveis às funções chamadas)- Closures aninhadas: codeblocks capturam Locais N níveis acima por referência
- Tipagem estática com palavra-chave
as - Tratamento de erro Try/Catch/EndTry
- Declarações de namespace
- Modificadores de acesso (Public, Private, Protected)
- Anotações REST (@Get, @Post, @Put, @Patch, @Delete) - executadas de verdade via
WSRestServer, ver Servidor REST - Suporte JSON inline com métodos JsonObject
- Identificadores longos (com namespace)
- Tipos Integer, Double, Decimal, Variant, Variadic
- Parsing de sintaxe WSRESTFUL/WSSERVICE (DSL clássico — reconhecido, execução ainda não suportada; ver limitação em Servidor REST)
Nota: o DSL clássico WSRESTFUL/WSMETHOD/ENDWSRESTFUL é parseado mas não executado (o verbo/PATH são descartados no parser e o dispatch exigiria chamar método de instância). As anotações @Get/@Post/@Put/@Patch/@Delete sobre User Function, por outro lado, sobem um servidor HTTP real via WSRestServer.
O AdvPP fornece 8 funções nativas para requisições HTTP com suporte a certificados digitais PKCS#12 (.pfx/.p12). Útil para integração com REST APIs que exigem autenticação TLS mútua (ex.: Emissor Nacional NFS-e, bancos, SEFAZ).
| Função | Descrição |
|---|---|
FWHttpGet(cUrl, cCert, cSenha) |
Requisição GET |
FWHttpPost(cUrl, cBody, cType, cCert, cSenha) |
Requisição POST |
FWHttpPut(cUrl, cBody, cType, cCert, cSenha) |
Requisição PUT |
FWHttpPatch(cUrl, cBody, cType, cCert, cSenha) |
Requisição PATCH |
FWHttpDelete(cUrl, cCert, cSenha) |
Requisição DELETE |
FWHttpBody() |
Corpo da última resposta |
FWHttpStatus() |
Código HTTP da última resposta |
FWHttpError() |
Mensagem de erro da última requisição |
Timeout de 30s por requisição. TLS com verificação de certificado habilitada.
Teste de integração: cmd/advplc/http_native_test.go (validado com requisições HTTP reais).
O runtime expõe I/O de disco, uma API de handle de arquivo para streaming e uma chamada de sistema — todas com semântica AdvPL nativa, em Go puro (sem CGO).
| Função | Descrição |
|---|---|
MemoRead(cArq) |
Lê o arquivo inteiro e retorna como string ("" se não existir) |
MemoWrite(cArq, cTexto) |
Grava a string no arquivo; retorna .T. em sucesso (alias: MemoWrit) |
FErase(cArq) |
Apaga o arquivo; 0 em sucesso, -1 em erro |
| Função | Descrição |
|---|---|
ConIn([cPrompt]) |
Lê uma linha do stdin (sem o \n); "" no EOF. Contraparte de ConOut para programas de console interativos (REPL, chat) |
| Função | Descrição |
|---|---|
FCreate(cArq[, nAttr]) |
Cria/trunca o arquivo; retorna handle (>=1) ou -1 |
FOpen(cArq[, nMode]) |
Abre existente; bit 0 de nMode = escrita (0 = leitura). Handle ou -1 |
FReadStr(nH, nBytes) |
Lê até nBytes e retorna string ("" no fim do arquivo) |
FWrite(nH, cBuffer[, nBytes]) |
Grava; retorna nº de bytes escritos |
FSeek(nH, nOffset[, nOrigin]) |
0=início, 1=atual, 2=fim; retorna a nova posição |
FClose(nH) |
Fecha o handle; .T./.F. |
FError() |
Código do último erro de I/O (0 = sem erro) |
A leitura usa
FReadStr(retorna a string lida) em vez doFReadcom buffer por referência — os natives da VM recebem valores, não lvalues, então byref em umaLocalstring não propagaria.FReadStré a forma AdvPL genuína para isso.
// Streaming de um arquivo grande em blocos de 4 KB
Local nH := FOpen("dados.txt", 0)
Local cBloco := FReadStr(nH, 4096)
While Len(cBloco) > 0
// ... processa cBloco ...
cBloco := FReadStr(nH, 4096)
End
FClose(nH)| Função | Descrição |
|---|---|
WaitRun(cCmd) |
Executa cCmd no shell do SO (cross-platform sh -c / cmd /c), herda stdio, espera e retorna o exit code (0 = sucesso) |
Para capturar a saída de um comando, use o padrão AdvPL de redirecionar para arquivo e ler — com a API de handle isso funciona para saída arbitrariamente grande, em streaming:
WaitRun("gerar_relatorio.sh > saida.txt")
Local nH := FOpen("saida.txt", 0)
Local cSaida := FReadStr(nH, 65536) // ou em blocos, para arquivos enormes
FClose(nH)| Função | Descrição |
|---|---|
MatVecTern(aMat, aVecTern) |
Produto matriz-vetor multiply-free onde o vetor é ternário (-1/0/+1): result[i] = Σ_j sign(vec[j])·mat[i][j] — só soma/subtração, o kernel do BitNet. aMat é um array de M linhas (cada uma um array de N números); aVecTern tem N entradas |
Base para redes neurais ternárias em AdvPL: peso/ativação em {-1,0,+1}
eliminam a multiplicação, viabilizando treino e inferência sem BLAS de ponto
flutuante nem GPU (ver tests/llm/pt_nn.prw).
Honram um bloco de código {|...| ... } de verdade (avaliado pela VM):
| Função | Descrição |
|---|---|
ASort(aArr, [nIni], [nQtd], [bOrder]) |
Ordena in-place; bOrder(x,y) retorna .T. se x vem antes de y (sem bloco: ascendente) |
AEval(aArr, bBloco, [nIni], [nQtd]) |
Aplica bBloco(elem, i) a cada elemento |
AScan(aArr, uVal|bBloco, [nIni], [nQtd]) |
Posição do 1º elemento igual a uVal ou onde bBloco(elem) é .T.; 0 se não achar |
File(cArq) |
.T. se o arquivo existe (não-diretório) |
GetNames(oJson) |
Array com as chaves de um JsonObject, na ordem de inserção |
Os blocos são closures de verdade: capturam Locais do escopo envolvente por
referência — leitura e escrita — inclusive quando o bloco escapa da função que o
criou (estado persistente). Ex.: AEval(a, {|x| nSoma := nSoma + x}) acumula no
nSoma externo; {|| nN := nN + 1} retornado por uma função vira um contador com
estado próprio. Captura em profundidade funciona completamente — bloco-dentro-de-bloco captura Locais N níveis acima por referência.
A classe Tensor (float32) guarda os dados como []float32 plano em Go — fora da
representação boxed de Value — e roda kernels de forward em Go puro. O AdvPL
orquestra; o Go faz a conta.
Local oX := Tensor():FromArray({1,2}, {1,2})
Local oW := Tensor():Rand({2,3}, 0.1)
Local oH := oX:MatMul(oW):Relu() // [1,3]
Local oY := oH:Softmax(2) // softmax por linha
Local nId := oY:Argmax() // classe prevista (1-based)Construtores: New(aForma), FromArray(aDados, aForma), Rand(aForma, nEscala).
Métodos: Shape, Size, Get/Set, ToArray; Add/Sub/Mul/Div (com
broadcast de escalar e linha/coluna), AddScalar/MulScalar; MatMul,
Transpose, Reshape; Sum/Mean/Max/Argmax (sem eixo → número; com eixo →
Tensor); Exp/Log/Sqrt/Relu/Tanh/Sigmoid/Gelu; Softmax; IndexRows
(lookup de embedding). Erros de forma são capturáveis por Try/Catch.
O dtype é escolhível por tensor: float32 é o default (rápido, usado pelo ML) e
float64 entra sob demanda para cálculo que exige exatidão (base do kernel de álgebra
linear/geometria). A precisão escalar do AdvPL já é float64; isto leva a dupla precisão
ao kernel de Tensor.
Local oA := Tensor():New({2,2}, "float64") // dtype float64
Local oB := Tensor():FromArray({1,2,3,4}, {2,2}, "float64")
? oB:DType() // "float64"
Local oC := oA:ToFloat64() // converte f32 -> f64
? oB:Dot(oB) // produto interno
? Tensor():FromArray({3,4},{2},"float64"):Norm() // norma L2 = 5Métodos de dtype: DType() ("float32"/"float64"), ToFloat32()/ToFloat64(),
Dot(oOutro) (produto interno) e Norm() (L2). As ops (Add/MatMul/… ) respeitam
o dtype e promovem a float64 se qualquer operando for f64; o caminho float32
permanece idêntico (o ML não é afetado). Propagação de f64 pelo autodiff fica para um
ciclo futuro (álgebra/geometria não usam gradiente).
Este ciclo entrega o forward (inferência) + precisão dupla. Autodiff/treino veio em ciclos seguintes.
Sobre o Tensor float64, operações de álgebra linear em Go puro (não-diferenciáveis — cálculo, não treino):
Local oA := Tensor():FromArray({4,7,2,6}, {2,2}, "float64")
? oA:Det() // determinante
Local oX := oA:Solve(oB) // resolve A·x = b (b vetor [n] ou [n,k])
Local oInv := oA:Inv() // inversa (A·Inv ≈ I)
Local aQR := oA:QR() // {Q, R} — Householder (Q·R ≈ A)
Local aEig := oS:EigSym() // {valores[n], vetores[n,n]} de matriz simétrica (Jacobi)Det()determinante via LU (pivô parcial); singular → 0.Solve(oB)resolveA·x = bpor substituição direta/reversa sobre a LU.Inv()inversa resolvendoA·X = I; singular → erro capturável.QR()→{Q, R}por refletores de Householder (Qortogonal,Rtriangular sup.).EigSym()→{valores, vetores}de matriz simétrica por rotações de Jacobi (autovalores decrescentes; colunas devetores= autovetores). Não-simétrica → erro.SVD()→{U, S, V}(decomposição em valores singulares, Jacobi de um lado;A ≈ U·diag(S)·Vᵀ,Sdecrescente, suporta retangular m×n).Eig()→{reais, imag}— todos os autovalores de matriz não-simétrica (real), incluindo pares complexos conjugados, via redução a Hessenberg + QR de duplo shift (Francis/hqr). Para autovalor complexo,imagtraz o par ±.
Erros (não-quadrada, singular, não-simétrica em EigSym, dims incompatíveis) são
ErrorValue capturáveis.
Funções nativas sobre vetores/pontos como arrays ({x,y}/{x,y,z}), em float64:
? VecCross({1,0,0}, {0,1,0}) // produto vetorial 3D -> {0,0,1}
? VecDot({1,2,3}, {4,5,6}) // produto escalar
? VecNorm({3,4}) // magnitude -> 5
? VecDist({0,0}, {3,4}) // distância euclidiana -> 5
? VecAngle({1,0}, {0,1}) // ângulo (rad) -> π/2
Local aU := VecNormalize({3,4}) // vetor unitário
Local aR := RotateVec2({1,0}, nTheta) // rotação 2D
Local aP := RotateVec3({1,0,0}, "z", nTheta) // rotação 3D em torno de x/y/zTambém VecAdd, VecSub, VecScale. Erros (dims incompatíveis, cross fora de 3D) são capturáveis.
Funções escalares adicionais: Atan2(y,x), Log10(x), Pow(b,e), Ceil(x),
Sign(x), Sinh/Cosh/Tanh(x), Gcd(a,b), Lcm(a,b), Fact(n).
Estatística sobre arrays: Mean(a), Variance(a), StdDev(a) (amostrais), Median(a),
LinReg(aX, aY) → {a, b} de y = a + b·x (mínimos quadrados), Interp(aX, aY, x)
(interpolação linear).
Sobre o núcleo de Tensor, a classe Variable grava um tape de operações e
Backward() propaga gradientes (reverse-mode autodiff). Otimizadores SGD e Adam
permitem treinar modelos float — o AdvPL orquestra o laço; o Go faz forward e backward.
Local oW := Variable():FromArray(aPesos, {nIn, nOut})
Local oB := Variable():FromArray(aBias, {nOut})
Local oOpt := Adam():New({oW, oB}, 0.001) // Adam optimizer (v1.9.0+)
// laço de treino:
Local oPred := oX:MatMul(oW):Add(oB):Relu()
Local oLoss := oPred:MSE(oY)
oOpt:ZeroGrad()
oLoss:Backward() // preenche oW:Grad(), oB:Grad()
oOpt:Step() // oW := oW - lr*gradOps diferenciáveis: MatMul, Add (com broadcast), Mul, Relu, Sum, Mean,
MSE, Tanh, Sigmoid, Gelu, IndexRows (embedding). oV:Value()/oV:Grad() devolvem o Tensor de valor/gradiente.
Corretude validada por verificação numérica de gradiente (diferenças finitas) no go test.
Loss de classificação: oLoss := oLogits:SoftmaxCE(aAlvo) (softmax + cross-entropy, alvo por índices de classe — adicionado v1.9.0+).
Módulos: Linear():New(nIn, nOut) e Embedding():New(nVocab, nDim) encapsulam parâmetros + Forward; oMod:Params() devolve os pesos para o otimizador; Fit(bPasso, nEpocas) roda o laço de treino avaliando um codeblock por época (adicionado v1.9.0+).
Planejado para v2.1. Modelos escritos inteiramente em AdvPL (Markov chains, retrieval-based Q&A, ternary neural networks, gradient-trained LMs) com exemplos em tests/llm/. Este ciclo entrega o motor (LLM, Tensor, autodiff) e os frameworks de treino; exemplos de código AdvPL rodam prototipados em Go tests (cmd/advplc/*_test.go) e migrarão para AdvPL exemplos em v2.1.
Planejado para v2.1: Exemplo AdvPL completo de um LM neural char-level treinado de verdade por descida de gradiente, montado 100% sobre o stack de ML do AdvPP (Tensor + autodiff/treino). Arquitetura NPLM (Bengio 2003):
oEmb := Embedding():New(V, D) // tabela de embeddings [V, D]
oL1 := Linear():New(k*D, H)
oL2 := Linear():New(H, V)
// forward: contexto de k chars -> próximo char
oLog := oL2:Forward( oL1:Forward( oEmb:Forward(aX):Reshape({N, k*D}) ):Tanh() )
oLoss := oLog:SoftmaxCE(aAlvo) // perda
// treino: Adam sobre Params() dos 3 módulos, via Fit(bPasso, nEpocas)Demonstra ponta a ponta: tokenizar → treinar → gerar, provando que o stack float treina um LM de verdade.
Planejado para v2.1: Exemplo AdvPL de um LM treinado em token-level (não char-level) sobre código AdvPL do repositório. Demonstra como o stack de ML AdvPP pode ser usado para gerar/completar código a partir de um prefixo, com REPL de autocomplete.
Status: Anotações @Get/@Post/@Put/@Patch/@Delete sobre User Function são totalmente suportadas com dispatch HTTP real. O DSL clássico WSRESTFUL <nome> ... WSMETHOD <verbo> PATH "..." ... ENDWSRESTFUL é apenas parseado — reconhecido sintaticamente, mas não executado.
Motivo: O DSL clássico liga a implementação do método a uma instância de classe (WSSERVICE), enquanto o dispatch HTTP via WSRestServer:Serve() só pode chamar funções top-level. Reescrever para ambos exigiria cirurgia de parser + mecanismo de dispatch novo sem caso de uso nos corpora validados.
Solução: Use anotações ou registre manualmente com AddRoute:
@Get("/clientes/{id}")
User Function GetCliente(oParam)
// ...
Return oResult
User Function RestDemo()
Local oRest := WSRestServer():New("api", "1.0")
// Anotação acima auto-registra; ou registre manualmente:
oRest:AddRoute("GET", "/status", "GetStatus")
oRest:Serve(8080)
ReturnStatus: RecLock() e MsUnlock() implementam semáforos por tabela desde a v2.0.3 — não são mais no-ops. Cada alias tem um semáforo exclusivo que garante acesso serial ao mesmo registro em WaitRecno().
Limitação: Semáforos são intra-VM — protegem threads dentro de um único processo advplc. Aplicações multi-processo (ex.: múltiplas instâncias de advplc num servidor de apps) não veem os locks um do outro — o banco SQLite usa busy_timeout e WAL mode para evitar contenção, mas sem consenso distribuído, a concorrência inter-processo é arriscada para operações ACID fortes.
Recomendação: Para produção multi-processo, implemente locks no banco (ex.: uma tabela de semáforos) ou use GetMV para coordenar via variáveis de sistema Protheus (se disponível).
Status: advplc build programa.prw sobe um executável com interface interativa de verdade se detectar operações de terminal (FWGetText, FWMenuSelect, ConIn, formulários MSDIALOG, FWMBrowse).
Detecção: Varre o bytecode em busca de OP_CALL_NATIVE a UI natives e OP_NEW_INSTANCE de componentes (FWMBrowse, MSDIALOG). Ambientes headless/sem TTY usam dialogs PO-UI em modo silencioso (nenhuma interação real).
Forçar GUI: ADVPP_FORCE_GUI=1 ./programa força renderização Fyne mesmo em terminal — útil para apps que preferem GUI como padrão (e-Gov, GesCon).
Exemplo:
advplc build meu_app.prw -o meu_app
./meu_app # Interativo se stdin for TTY
ADVPP_FORCE_GUI=1 ./meu_app # Força janela FyneStatus: Classe LLM carrega modelos GGUF quantizados apenas em I2_S (ternário: -1/0/+1, estilo BitNet/Falcon3-1.58bit).
Limitações:
- Quantização: Só I2_S suportado; F16/F32 não funcionam
- Streaming: Não há suporte a streaming de token;
Generate()bloqueia até terminar - Tokenizer: Pré-built na .gguf; não há suporte a tokenizers dinâmicos
- Modelos: Arquitetura deve ser
llamacom pesos I2_S — outras arquiteturas (Qwen, Mistral, etc.) e quantizações (Q4_K, Q6_K) causam erro
Alternativa: Para F16/F32 ou streaming, use uma API externa (ex.: Ollama local com FWHttpPost).
Status: Tensor suporta float32 (default) e float64 (optativo) por instância.
Padrão: Float32 (rápido, para ML) é o tipo padrão. Float64 entra sob demanda para cálculo exato (álgebra linear, geometria). As operações respeitam dtype e promovem para float64 se qualquer operando for f64.
Exemplo:
Local oA := Tensor():New({2,2}, "float64")
Local oB := oA:Add(Tensor():New({2,2})) // promocão: resultado é f64Status: Classe Variable implementa forward pass + reverse-mode autodiff + otimizador SGD.
Limitações:
- Softmax + Cross-Entropy: Classe
Variablenão implementa ainda; useTensor:SoftmaxCE()para perda de classificação (sem backward diferenciável neste ciclo) - Otimizadores: Apenas SGD fornecido; Adam foi adicionado posteriormente (v1.9.0+)
- Módulos: Suporte básico a
LineareEmbedding; não há Convolução, RNN, Transformer - Batch: Forward/backward por exemplo; batches são do usuário (loop manual)
Roadmap: Softmax/CE diferenciável, Adam, módulos complexos vêm em ciclos futuros.
Status: LM neural char-level treinado 100% com Autodiff/SGD em AdvPL puro (pt_neural.prw).
Limitações:
- Tamanho: Modelo pequeno (embedding dim 50, hidden 200); em corpus pequeno pode overfitar
- Coerência: Em corpus complexo (e.g., Dom Casmurro em prosa literária), gera morfologia correta mas coerência limitada — n-grama local não captura dependências longas
- Velocidade: VM interpretada + float em AdvPL = lento; treino de 1 época em corpus 72k tokens leva ~30–90s
- Sem generalização: Modelo pequeno em corpus finito tende a decorar + aprender padrão; não generaliza a problemas novos
Ressalva: Prova de conceito que treino real funciona; não é um LLM produção.
Status: FWFormModel, FWFormView, FWFormBrowse definem manipuladores de eventos (onChange, onClick, onGotFocus, onLostFocus, onLineChange, onDbClick, onHeaderClick).
Limitação: Manipuladores são parseados e armazenados no bytecode, mas não conectados à VM — eventos de clique/foco/seleção do usuário não disparam os callbacks.
Impacto: Aplicações MVC renderizam visualmente (Fyne) e aceitam input de usuário (formulários, grids), mas lógica de validação/ação de evento deve ficar no StartJob que roda a UI (polling ou callback manual).
Status:
Status: Classe FWHttp* e cliente HTTP nativo implementam:
- Timeout: 30 segundos por requisição (bloqueante)
- Redirecionamentos: Máximo 5 redirects (evita loops)
- TLS: Verificação de certificado habilitada (erro em cert inválido, self-signed não é aceito sem config extra)
Implicação: URLs com redirect chain >5 falham; serviços com cert self-signed requerem workaround (desabilitar verificação explicitamente via variável de ambiente ou config).
Status: Binários standalone (advplc build) detectam automaticamente se devem usar console (TTY) ou GUI (Fyne).
Heurística: Varre bytecode em busca de UI natives (FWGetText, FWMenuSelect, diálogos). Se achados, sobe GUI Fyne; caso contrário, console puro.
Caso de borda: Apps que usam only ConOut e ConIn (sem diálogos) rodamem console mesmo com stdin desacoplado (ex.: em cron ou daemon) — nenhuma interação ocorre, só logs silent. Use ADVPP_FORCE_GUI=1 se quiser GUI mesmo sem UI natives detectadas.
Claim: README diz "~200+" funções nativas; Real: 243 funções implementadas (medido em pkg/vm/natives.go).
Nota: Muitas são stubs/no-ops (ex.: FWCLEARHLP, HELP, MSDOCUMENT). A contagem ~200+ é aproximada por design; detalhes em GUIA_DO_DESENVOLVEDOR_PARA_ADVPP.md.
Status: 88 opcodes implementados na VM (0–87).
Documentação: GUIA_DO_DESENVOLVEDOR_PARA_ADVPP.md seção 4.3 documenta opcodes com mapeamento 0–88 completo, nomes, descrições e efeito de stack.
Consulte ./GUIA_DO_DESENVOLVEDOR_PARA_ADVPP.md para tabela detalhada e exemplos de cada opcode.
Status: AdvPP v2.0.3 impõe limites duros de recurso (CWE-400 Uncontrolled Resource Consumption):
| Limite | Valor | Controle |
|---|---|---|
| Profundidade de recursão (parser) | 1000 | recursionDepth++ |
| Tamanho de string | 10 MB | len(s) > 10MB → erro |
| Tamanho de stack da VM | 10000 frames | len(stack) |
| Call frames | 5000 | len(callStack) |
Goroutines via StartJob |
1000 concurrent | activeJobsCount |
| Tamanho de array | 1M elementos | len(a) > 1M → erro |
Timeout LLM Generate() |
5 minutos | context.WithTimeout |
| Timeout I/O (arquivos) | 30 segundos | context.WithTimeout |
| Timeout HTTP | 30 segundos | http.Client.Timeout |
Ultrapassar esses limites retorna erro capturável via Try/Catch ou falha gracefully (sem crash).
Status: Extensão AdvPL/TLPP (marketplace + .vsix em Releases) é v2.0.4 a partir de 2026-07-29.
Incluída: Compilador AdvPP embutido (linux-x64, linux-arm64, win32-x64, darwin-arm64); F5 e F9 para run/debug.