TheCodeNaked

Parte 7 - Mini Engine Declarativa Delphi (Copy)

🎯 Objetivo

Criar um sistema Delphi capaz de:

  1. Interpretar um arquivo JSON com ações e decisões
  2. Executar comandos registrados dinamicamente
  3. Manter um contexto de variáveis (como memória de trabalho)
  4. Permitir fluxos condicionais (if/else)
  5. Permitir integração de plugins (ações customizadas)

🧱 Estrutura do Projeto

1. Classes principais

Classe / UnidadeResponsabilidade
TDynaEngineGerencia execução do JSON
TActionRegistryRegistro de ações (Dictionary<string, TProc>)
TExecutionContextArmazena variáveis durante a execução
TDynaInterpreterLê e executa o JSON como script
IDynaAction (opcional)Interface padrão para ações plugináveis

📦 Exemplo de Fluxo JSON

[
  { "acao": "DefinirVariavel", "nome": "saldo", "valor": 500 },
  {
    "condicional": {
      "se": { "variavel": "saldo", "operador": ">=", "valor": 100 },
      "entao": [
        { "acao": "EnviarEmail", "para": "cliente@exemplo.com", "mensagem": "Compra aprovada" }
      ],
      "senao": [
        { "acao": "EnviarEmail", "para": "cliente@exemplo.com", "mensagem": "Saldo insuficiente" }
      ]
    }
  }
]

🧪 Ações já implementadas na engine

AçãoDescrição
DefinirVariavelDefine uma variável no contexto
EnviarEmailSimula envio de e-mail com log
MostrarMensagemMostra mensagem na tela
AtualizarStatusAtualiza uma variável de controle

🛠 Tecnologia

  • Delphi FMX (ou VCL, se preferir)
  • FireDAC para JSON (ou System.JSON)
  • TDictionary<string, Variant> para contexto
  • Código 100% reaproveitável e desacoplado

📁 Organização da Pasta do Projeto

DynaFlow/
├── Engine/
│   ├── DynaEngine.pas
│   ├── DynaInterpreter.pas
│   ├── ActionRegistry.pas
│   └── ExecutionContext.pas
├── Plugins/
│   ├── ActionEmail.pas
│   ├── ActionMensagens.pas
│   └── ...
├── Forms/
│   └── MainForm.pas
├── Sample/
│   └── fluxo_exemplo.json

🚀 Etapas de Desenvolvimento

  1. Criar estrutura base (TDynaEngineTActionRegistryTExecutionContext)
  2. Implementar leitura e execução de JSON
  3. Criar ações básicas e registrá-las
  4. Executar JSON a partir do botão na UI
  5. Mostrar variáveis e logs de execução
unit DynaEngine;

interface

uses
  System.SysUtils, System.Classes, System.Generics.Collections, System.JSON;

type
  TExecutionContext = class
  private
    FVars: TDictionary<string, Variant>;
  public
    constructor Create;
    destructor Destroy; override;
    procedure SetVar(const Name: string; const Value: Variant);
    function GetVar(const Name: string): Variant;
    function TryGetVar(const Name: string; out Value: Variant): Boolean;
    procedure Clear;
  end;

  TActionProc = reference to procedure(const Params: TJSONObject; const Context: TExecutionContext);

  TActionRegistry = class
  private
    FActions: TDictionary<string, TActionProc>;
  public
    constructor Create;
    destructor Destroy; override;
    procedure RegisterAction(const Name: string; Proc: TActionProc);
    function Execute(const Name: string; const Params: TJSONObject; const Context: TExecutionContext): Boolean;
    function Contains(const Name: string): Boolean;
  end;

implementation

{ TExecutionContext }

constructor TExecutionContext.Create;
begin
  FVars := TDictionary<string, Variant>.Create;
end;

destructor TExecutionContext.Destroy;
begin
  FVars.Free;
  inherited;
end;

procedure TExecutionContext.Clear;
begin
  FVars.Clear;
end;

function TExecutionContext.GetVar(const Name: string): Variant;
begin
  Result := FVars[Name];
end;

procedure TExecutionContext.SetVar(const Name: string; const Value: Variant);
begin
  FVars.AddOrSetValue(Name, Value);
end;

function TExecutionContext.TryGetVar(const Name: string; out Value: Variant): Boolean;
begin
  Result := FVars.TryGetValue(Name, Value);
end;

{ TActionRegistry }

constructor TActionRegistry.Create;
begin
  FActions := TDictionary<string, TActionProc>.Create;
end;

destructor TActionRegistry.Destroy;
begin
  FActions.Free;
  inherited;
end;

function TActionRegistry.Contains(const Name: string): Boolean;
begin
  Result := FActions.ContainsKey(Name);
end;

procedure TActionRegistry.RegisterAction(const Name: string; Proc: TActionProc);
begin
  FActions.AddOrSetValue(Name.ToLower, Proc);
end;

function TActionRegistry.Execute(const Name: string; const Params: TJSONObject; const Context: TExecutionContext): Boolean;
var
  Proc: TActionProc;
begin
  if FActions.TryGetValue(Name.ToLower, Proc) then
  begin
    Proc(Params, Context);
    Exit(True);
  end;
  Result := False;
end;

end.

Pronto! A unidade DynaEngine já está criada com:

  • TExecutionContext: armazena variáveis em tempo de execução.
  • TActionRegistry: permite registrar e executar ações dinamicamente.
  • Tipagem genérica e compatível com Delphi FMX.

Agora podemos seguir com:

  1. DynaInterpreter.pas: leitura e execução do JSON (interpretador principal)
unit DynaInterpreter;

interface

uses
  System.SysUtils, System.Classes, System.JSON, System.Variants,
  DynaEngine;

type
  TDynaInterpreter = class
  private
    FRegistry: TActionRegistry;
    FContext: TExecutionContext;
  public
    constructor Create(ARegistry: TActionRegistry; AContext: TExecutionContext);
    procedure ExecuteJson(const AJson: TJSONArray);
    procedure ExecuteItem(const Item: TJSONValue);
    procedure ExecuteConditional(const CondObj: TJSONObject);
  end;

function EvaluateCondition(const Context: TExecutionContext; const Cond: TJSONObject): Boolean;

implementation

function EvaluateCondition(const Context: TExecutionContext; const Cond: TJSONObject): Boolean;
var
  VarName, Operator: string;
  ExpectedValue, ActualValue: Variant;
  CompareResult: Integer;
begin
  VarName := Cond.GetValue<string>('variavel');
  Operator := Cond.GetValue<string>('operador');
  ExpectedValue := Cond.GetValue('valor').Value;

  if not Context.TryGetVar(VarName, ActualValue) then
    Exit(False);

  CompareResult := VarCompareValue(ActualValue, ExpectedValue);

  if Operator = '=' then
    Result := CompareResult = 0
  else if Operator = '>' then
    Result := CompareResult > 0
  else if Operator = '<' then
    Result := CompareResult < 0
  else if Operator = '>=' then
    Result := CompareResult >= 0
  else if Operator = '<=' then
    Result := CompareResult <= 0
  else if Operator = '<>' then
    Result := CompareResult <> 0
  else
    Result := False;
end;

{ TDynaInterpreter }

constructor TDynaInterpreter.Create(ARegistry: TActionRegistry; AContext: TExecutionContext);
begin
  FRegistry := ARegistry;
  FContext := AContext;
end;

procedure TDynaInterpreter.ExecuteJson(const AJson: TJSONArray);
var
  Item: TJSONValue;
begin
  for Item in AJson do
    ExecuteItem(Item);
end;

procedure TDynaInterpreter.ExecuteItem(const Item: TJSONValue);
var
  Obj: TJSONObject;
  ActionName: string;
begin
  if Item.TryGetValue<TJSONObject>(Obj) then
  begin
    if Obj.TryGetValue('acao', ActionName) then
    begin
      if not FRegistry.Execute(ActionName, Obj, FContext) then
        Writeln('Ação não encontrada: ' + ActionName);
    end
    else if Obj.Contains('condicional') then
    begin
      ExecuteConditional(Obj.GetValue<TJSONObject>('condicional'));
    end;
  end;
end;

procedure TDynaInterpreter.ExecuteConditional(const CondObj: TJSONObject);
var
  Condicao: TJSONObject;
  Entao, Senao: TJSONArray;
  Resultado: Boolean;
begin
  Condicao := CondObj.GetValue<TJSONObject>('se');
  Resultado := EvaluateCondition(FContext, Condicao);

  if Resultado then
  begin
    if CondObj.TryGetValue('entao', Entao) then
      ExecuteJson(Entao);
  end
  else
  begin
    if CondObj.TryGetValue('senao', Senao) then
      ExecuteJson(Senao);
  end;
end;

end.

O módulo DynaInterpreter está pronto! Ele interpreta e executa:

  • Sequências JSON com ações ou blocos condicionais
  • Condições baseadas em variáveis do ExecutionContext
  • Comparações com operadores básicos (=><, etc.)

Agora podemos seguir com a próxima etapa:

  1. Criar ações básicas (DefinirVariavelMostrarMensagemEnviarEmail) e registrá-las.
  2. Criar uma unit DynaActions.pas para conter essas ações.
  3. Depois, construir a interface FMX para rodar e testar os fluxos.

Deseja que eu continue com a unit DynaActions.pas?

unit DynaActions;

interface

uses
  System.SysUtils, System.JSON, FMX.Dialogs, DynaEngine;

procedure RegisterDefaultActions(Registry: TActionRegistry);

implementation

procedure RegisterDefaultActions(Registry: TActionRegistry);
begin
  Registry.RegisterAction('definirvariavel',
    procedure(const Params: TJSONObject; const Context: TExecutionContext)
    var
      Nome: string;
      Valor: TJSONValue;
    begin
      if Params.TryGetValue('nome', Nome) and Params.TryGetValue('valor', Valor) then
        Context.SetVar(Nome, Valor.Value);
    end);

  Registry.RegisterAction('mostrarmensagem',
    procedure(const Params: TJSONObject; const Context: TExecutionContext)
    var
      Msg: string;
    begin
      if Params.TryGetValue('mensagem', Msg) then
        ShowMessage(Msg);
    end);

  Registry.RegisterAction('enviaremail',
    procedure(const Params: TJSONObject; const Context: TExecutionContext)
    var
      Para, Mensagem: string;
    begin
      Para := Params.GetValue<string>('para');
      if Params.TryGetValue('mensagem', Mensagem) then
        ShowMessage(Format('Simulando envio de e-mail para %s: %s', [Para, Mensagem]))
      else
        ShowMessage(Format('Simulando envio de e-mail para %s.', [Para]));
    end);
end;

end.

✅ Ações Registradas em DynaActions.pas

  1. DefinirVariavel
    Define uma variável no contexto:
{ "acao": "DefinirVariavel", "nome": "saldo", "valor": 150 }
  1. Exibe uma mensagem com ShowMessage:
{ "acao": "MostrarMensagem", "mensagem": "Operação concluída." }
  1. EnviarEmail: Simula envio de e-mail (mostra via ShowMessage):
{ "acao": "EnviarEmail", "para": "cliente@exemplo.com", "mensagem": "Sua compra foi aprovada." }

🎯 Próximos passos

Agora podemos:

  1. Criar um Form FMX simples com:
    • Um TMemo ou TEdit para inserir JSON
    • Um botão para executar o fluxo
    • Um log de saída (opcional)
  2. Incluir um arquivo .json de exemplo com um fluxo completo: definição de variável, condicional, ação.

Deseja que eu crie agora o MainForm.pas com essa interface básica para execução e testes?

unit MainForm;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.StdCtrls,
  FMX.Controls.Presentation, FMX.ScrollBox, FMX.Memo, FMX.Layouts, FMX.Objects,
  System.JSON, DynaEngine, DynaInterpreter, DynaActions;

type
  TFormMain = class(TForm)
    MemoJson: TMemo;
    BtnExecutar: TButton;
    LayoutMain: TLayout;
    procedure FormCreate(Sender: TObject);
    procedure BtnExecutarClick(Sender: TObject);
  private
    FEngine: TDynaInterpreter;
    FRegistry: TActionRegistry;
    FContext: TExecutionContext;
  public
  end;

var
  FormMain: TFormMain;

implementation

{$R *.fmx}

procedure TFormMain.FormCreate(Sender: TObject);
begin
  FRegistry := TActionRegistry.Create;
  FContext := TExecutionContext.Create;
  FEngine := TDynaInterpreter.Create(FRegistry, FContext);
  RegisterDefaultActions(FRegistry);

  MemoJson.Lines.Text :=
    '[
' +
    '  { "acao": "DefinirVariavel", "nome": "saldo", "valor": 150 },
' +
    '  {
' +
    '    "condicional": {
' +
    '      "se": { "variavel": "saldo", "operador": ">=", "valor": 100 },
' +
    '      "entao": [
' +
    '        { "acao": "MostrarMensagem", "mensagem": "Compra aprovada" },
' +
    '        { "acao": "EnviarEmail", "para": "cliente@exemplo.com", "mensagem": "Sua compra foi aprovada." }
' +
    '      ],
' +
    '      "senao": [
' +
    '        { "acao": "MostrarMensagem", "mensagem": "Saldo insuficiente" },
' +
    '        { "acao": "EnviarEmail", "para": "cliente@exemplo.com", "mensagem": "Saldo insuficiente para compra." }
' +
    '      ]
' +
    '    }
' +
    '  }
' +
    ']';
end;

procedure TFormMain.BtnExecutarClick(Sender: TObject);
var
  Json: TJSONArray;
begin
  try
    Json := TJSONObject.ParseJSONValue(MemoJson.Text) as TJSONArray;
    if Assigned(Json) then
    begin
      FContext.Clear;
      FEngine.ExecuteJson(Json);
      Json.Free;
    end
    else
      ShowMessage('JSON inválido.');
  except
    on E: Exception do
      ShowMessage('Erro ao executar: ' + E.Message);
  end;
end;

end.

MainForm.pas está pronto! Com ele, você já pode:

  • Editar um fluxo JSON direto no TMemo
  • Executar o fluxo clicando em “Executar”
  • Ver os efeitos das ações como mensagens na tela (e-mails simulados, mensagens, etc.)

🔧 O que falta para rodar:

  1. Criar o arquivo visual .fmx com:
    • Um TMemo chamado MemoJson
    • Um TButton chamado BtnExecutar
    • Um TLayout (LayoutMain) como contêiner
  2. Adicionar as units DynaEngineDynaInterpreterDynaActions ao projeto.

Se quiser, posso descrever o layout visual que você pode montar rapidamente no Delphi FMX Designer. Deseja isso?

Perfeito! Aqui está a descrição detalhada para você montar o layout visual do formulário principal no Delphi FMX Designer, de forma simples e funcional:


🎨 Layout Visual do MainForm.fmx

🧱 Componentes e propriedades

🔹 TLayout – LayoutMain

  • Name: LayoutMain
  • Align: Client
  • Padding: 20,20,20,20

🔹 TMemo – MemoJson

  • Parent: LayoutMain
  • Name: MemoJson
  • Align: Client
  • Margins: 0,0,0,10
  • WordWrap: True
  • Lines.Text: (vazio ou será preenchido no FormCreate)

🔹 TButton – BtnExecutar

  • Parent: LayoutMain
  • Name: BtnExecutar
  • Text: Executar
  • Align: Bottom
  • Height: 50

🧭 Ordem de Criação

  1. Adicione um TLayout ao form e defina Align := Client.
  2. Dentro do TLayout, adicione:
    • Um TMemo com Align := Client e Margins.Bottom := 10
    • Um TButton com Align := BottomHeight := 50, e Text := Executar

✅ Resultado esperado

Você terá uma janela onde o usuário pode:

  • Colar ou editar o JSON no memo
  • Clicar em Executar e ver o resultado aparecer em caixas de mensagem (ShowMessage)

Se quiser, posso preparar também um arquivo .json de exemplo externo ou um botão “Carregar JSON” a partir de um arquivo. Deseja isso como próximo passo?

unit JsonLoader;

interface

uses
  System.SysUtils, System.Classes, System.JSON, System.IOUtils;

function LoadJsonFromFile(const AFileName: string): TJSONArray;
function LoadJsonTextFromFile(const AFileName: string): string;

implementation

function LoadJsonFromFile(const AFileName: string): TJSONArray;
var
  JsonText: string;
begin
  Result := nil;
  if not FileExists(AFileName) then
    Exit;

  JsonText := TFile.ReadAllText(AFileName, TEncoding.UTF8);
  Result := TJSONObject.ParseJSONValue(JsonText) as TJSONArray;
end;

function LoadJsonTextFromFile(const AFileName: string): string;
begin
  Result := '';
  if FileExists(AFileName) then
    Result := TFile.ReadAllText(AFileName, TEncoding.UTF8);
end;

end.

Pronto! Criamos a unit JsonLoader.pas, com duas funções:

  • LoadJsonFromFile: retorna um TJSONArray carregado a partir de um arquivo .json
  • LoadJsonTextFromFile: retorna o conteúdo puro do JSON como string (para preencher o TMemo)

🔄 Próximo passo: integrar no MainForm

Você pode adicionar um botão chamado BtnCarregar com:

  • Text: Carregar JSON
  • Align: Bottom
  • Position.Y: acima do botão Executar

Depois, implemente algo assim:

uses JsonLoader;

procedure TFormMain.BtnCarregarClick(Sender: TObject);
var
  FileName: string;
begin
  FileName := 'fluxo_exemplo.json'; // ou um caminho escolhido via diálogo
  MemoJson.Lines.Text := LoadJsonTextFromFile(FileName);
end;

Se quiser, posso gerar esse botão e incluir o trecho completo no MainForm com evento configurado. Deseja isso agora?

unit MainForm;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.StdCtrls,
  FMX.Controls.Presentation, FMX.ScrollBox, FMX.Memo, FMX.Layouts, FMX.Objects,
  System.JSON, DynaEngine, DynaInterpreter, DynaActions, JsonLoader;

type
  TFormMain = class(TForm)
    MemoJson: TMemo;
    BtnExecutar: TButton;
    LayoutMain: TLayout;
    BtnCarregar: TButton;
    procedure FormCreate(Sender: TObject);
    procedure BtnExecutarClick(Sender: TObject);
    procedure BtnCarregarClick(Sender: TObject);
  private
    FEngine: TDynaInterpreter;
    FRegistry: TActionRegistry;
    FContext: TExecutionContext;
  public
  end;

var
  FormMain: TFormMain;

implementation

{$R *.fmx}

procedure TFormMain.FormCreate(Sender: TObject);
begin
  FRegistry := TActionRegistry.Create;
  FContext := TExecutionContext.Create;
  FEngine := TDynaInterpreter.Create(FRegistry, FContext);
  RegisterDefaultActions(FRegistry);

  MemoJson.Lines.Text :=
    '[
' +
    '  { "acao": "DefinirVariavel", "nome": "saldo", "valor": 150 },
' +
    '  {
' +
    '    "condicional": {
' +
    '      "se": { "variavel": "saldo", "operador": ">=", "valor": 100 },
' +
    '      "entao": [
' +
    '        { "acao": "MostrarMensagem", "mensagem": "Compra aprovada" },
' +
    '        { "acao": "EnviarEmail", "para": "cliente@exemplo.com", "mensagem": "Sua compra foi aprovada." }
' +
    '      ],
' +
    '      "senao": [
' +
    '        { "acao": "MostrarMensagem", "mensagem": "Saldo insuficiente" },
' +
    '        { "acao": "EnviarEmail", "para": "cliente@exemplo.com", "mensagem": "Saldo insuficiente para compra." }
' +
    '      ]
' +
    '    }
' +
    '  }
' +
    ']';
end;

procedure TFormMain.BtnExecutarClick(Sender: TObject);
var
  Json: TJSONArray;
begin
  try
    Json := TJSONObject.ParseJSONValue(MemoJson.Text) as TJSONArray;
    if Assigned(Json) then
    begin
      FContext.Clear;
      FEngine.ExecuteJson(Json);
      Json.Free;
    end
    else
      ShowMessage('JSON inválido.');
  except
    on E: Exception do
      ShowMessage('Erro ao executar: ' + E.Message);
  end;
end;

procedure TFormMain.BtnCarregarClick(Sender: TObject);
var
  FileName: string;
begin
  FileName := 'fluxo_exemplo.json';
  MemoJson.Lines.Text := LoadJsonTextFromFile(FileName);
end;

end.

Agora o MainForm conta com:

  • Botão BtnCarregar: ao clicar, ele carrega o conteúdo do arquivo fluxo_exemplo.json para o TMemo.
  • Evento BtnCarregarClick: já implementado com chamada ao LoadJsonTextFromFile.

🛠️ Ajustes no Designer FMX:

Adicione mais um botão:

🔹 TButton – BtnCarregar

  • Name: BtnCarregar
  • Text: Carregar JSON
  • Align: Bottom
  • Position.Y: acima do BtnExecutar
  • Height: 50

Se quiser, posso te passar o conteúdo para o arquivo fluxo_exemplo.json como exemplo inicial. Deseja isso também?

[
  { "acao": "DefinirVariavel", "nome": "saldo", "valor": 150 },

  {
    "condicional": {
      "se": { "variavel": "saldo", "operador": ">=", "valor": 100 },
      "entao": [
        { "acao": "MostrarMensagem", "mensagem": "Compra aprovada" },
        { "acao": "EnviarEmail", "para": "cliente@exemplo.com", "mensagem": "Sua compra foi aprovada." }
      ],
      "senao": [
        { "acao": "MostrarMensagem", "mensagem": "Saldo insuficiente" },
        { "acao": "EnviarEmail", "para": "cliente@exemplo.com", "mensagem": "Saldo insuficiente para compra." }
      ]
    }
  }
]

📌 Onde salvar?

Coloque esse arquivo na mesma pasta do .exe (ou do projeto durante o desenvolvimento), com o nome: fluxo_exemplo.json

Perfeito. Um texto de fechamento é essencial para amarrar todas as ideias, valorizar o que foi construído e abrir caminho para evoluções. E sim: esse tipo de mini engine é extremamente útil em prototipagem, automação leve, engines de workflow e até sistemas configuráveis por analistas.

Segue uma sugestão de texto de encerramento para o projeto e a série até aqui:


🔚 Fechamento – Um motor leve com potência real

O que começamos como um experimento de desacoplamento evoluiu para algo muito mais poderoso: uma mini engine declarativa, capaz de interpretar comandos, tomar decisões e executar fluxos — tudo a partir de um arquivo JSON.

Com poucos arquivos e componentes, criamos:

  • Uma estrutura de execução com variáveis em tempo real
  • Registro e execução dinâmica de ações
  • Condicionais com fallback
  • Um interpretador simples, mas expressivo
  • Uma interface funcional em Delphi FMX

🚀 E agora?

Esse projeto já está funcional, mas é só o começo.

Com pequenas extensões, ele pode evoluir para:

  • 🔁 Loops declarativos: repetir ações até que uma condição seja satisfeita
  • 🌿 Ramificações múltiplas: decisões complexas com múltiplos caminhos
  • 🔌 Plugins externos: carregamento de DLLs ou units dinamicamente
  • 🧩 Modularização: inclusão de arquivos JSON menores por referência
  • 💡 Sistema de regras de negócio: editável por analistas sem recompilar
  • 🧪 Testes automatizados de fluxos com dados simulados

💡 Por que isso importa?

Porque nem todo sistema precisa de um framework gigante para ser flexível.

Esse motor é:

  • Leve
  • Intuitivo
  • Reaproveitável
  • Ideal para protótipos, testes de conceito, automações internas
  • E o melhor: você tem controle total sobre ele

Se você chegou até aqui, já tem tudo para começar a criar seus próprios interpretadores, DSLs leves e engines internas sob medida.

E quando o cliente pedir mudanças no comportamento do sistema... talvez você só precise editar um JSON. 😉

Se quiser, posso transformar esse texto final em artigo ou expandir isso para documentação ou README do projeto. Deseja isso também?

Sobre o autor

TheCodeNaked

No TheCodeNaked, programar é consequência, não ponto de partida. Antes do código, vem a dúvida, a análise, o contexto. Não seguimos fórmulas — questionamos. Criar software é pensar com clareza. O resto é só digitação.

TheCodeNaked

Criar com clareza. Codificar com intenção.

TheCodeNaked

Ótimo! Você se inscreveu com sucesso.

Bem-vindo de volta! Você acessou com sucesso.

Você se inscreveu com sucesso o TheCodeNaked.

Sucesso! Verifique seu e-mail para acessar com o link mágico.

As suas informações de faturamento foram atualizadas.

Seu pagamento não foi atualizado