Lua - Interpretando script lua em aplicativo delphi

Sábado pela manhã (15/05/2010) determinado a dar jeito em como fazer para implementar as fórmulas de cálculo variáveis para montar a folha de pagamento do módulo salários e benefícios da prática IV (gestão de pessoas) da faculdade, finalmente fiz a opção por Lua.

Outras possibilidades que eu tinha:

1 - Lisp - Sugestão do Eduardo Signori. Problema é que eu nunca programei LISP.

2 - Um algoritmo montado pelo Maikel Sheid e sugerido por ele.


3 - O Mateus Chies também me enviou um interpretador de fórmulas que ele  fez em JavaScript... já era um começo para fazer em Delphi.

4 - Opção de não implementar, ano passado não saiu nem folha de pagamento, que dirá "fórmulas variáveis" para os benefícios em função de indicadores dos colaboradores.

As principais referências que me fizeram decidir por Lua:

1 - http://www.matrix44.de/lua/ - Lua 5.0 Delphi Binding, produzido pelo Rolf Meyerhoff em 2005. Lua agora está em uma versão mais nova, mas a 5.0 já era muito boa! Essa implementação é bem completa.

2 - http://www.explainth.at/en/delphi/luawrap.shtml - Delphi Wrapper for Lua - Mesma coisa que a implementação do Rolf, só que mais simples. Já daria com isso.

3 - http://www.spreendigital.de/blog/2009/09/28/lua-5-1-for-delphi-2010/ - Lua 5.1 for Delphi 2010 - Esse cara pegou a implementação do Rolf e ajustou para funcionar com o nova classe de strings do Delphi 2010, além disso fez o binding para versão 5.1 de Lua e implementou alguma coisa a mais de orientação a objeto.

4 - http://www.gamedev.com.br/forum/viewtopic.php?t=2145 - Lua e Delphi - Aqui conversa vai e conversa vem o cara deixou o link com exemplos de programas em Delphi usando a implementação do Rolf. Era o que eu precisava. Dentro do zip ainda tem textos, explicando conceitualmente como as coisas funcionam. Obrigado Jerson!


* Obs geral: Na Alemanha deve ter muito programador, e pessoal de qualidade, porque sem querer já me deparei com muito, mas muito material de excelente qualidade, em alemão ou de um autor alemão.




Bem a idea aqui é fazer a aplicação Delphi gravar em um banco de dados oracle a fóruma. Através dos controles padrão do DBNavegator vai se poder criar novas fórumulas, editar e excluir. Depois vamos colocar um botão para "rodar" a fóruma cadastrada. Sigam-me os bons!

A tabela no BD é essa (complicada não)

create table programas(
 luascript varchar(4000)
)

Agora no form da nossa aplicação, adicionamos um "SQLConnection" e configuramos a conexão.
Tudo padrão, como se fosse só uma tela para cadastrar dados. A única modificação foi que o "DBEdit" que veio  quando arrastei daquela telinha dos "field list" do "Client Data Set" eu substituí por um DBMemo, e também eu não tenho um Data Module, coloquei os componentes no form mesmo.

O Edit lá em baixo do formulário vai mostrar o resultado do script.

No evento "onClick" do botão "Salvar no BD" não se esqueça de mandar efetivamente gravar. Veja o código:

ClientDataSet1.ApplyUpdates(0);

Bom, antes de prosseguir, adicione os arquivos selecionados na figura a seguir no diretório do seu projeto.

Adicione ao projeto (project > add to project) o arquivo lua.pas (que tem declaradas as interfaces com Lua).


E também Alt + F11, adicione Lua a suas "uses" na tela em que a interpretação do código será feita.

Pronto. Agora é fazer o load do código, passar para o interpretador e pegar o resultado. Segue o código do botão "executar".

procedure TForm1.Button2Click(Sender: TObject);
  var
    pL: ^lua_State;
    resultado: real;
begin
  pL:= lua_open();
  // habilita  as bibliotecas padrão de lua
  luaopen_base(pL);
  luaopen_string(pL);
  luaopen_table(pL);
  luaopen_math(pL);
  luaopen_io(pL);

  // salva o arquivo no disco
  DBMemo1.Lines.SaveToFile('temp.lua');
  // passa ele para o interpretador
  lua_dofile(pL,'temp.lua');

  lua_settop(pL,0);// reseta a pilha
  lua_getglobal(pL,'resultado');

  resultado:= lua_tonumber(pL,1); // pegando o item 1 da pilha

  Edit1.Text:= FloatToStr(resultado); // passa para o delphi

  lua_close(pL); // fecha
end;

Uma coisa estranha que achei é que não deu certo fazer "lua_dostring"... ele sempre dava imcompatibilidade entre os tipos "string" e o PAnsiChar, que era o que ele estava esperando. Dessa forma, primeiro mandei salvar num arquivo e depois fazer o load dele. Não gostei disso, não é performático. Mas para testes está ok.

Bom, salvando, compilando e brincando com a aplicação...

Se você não sabe Lua, recomendo o manual (a versão 5.0 né, porque esse binding é baseado na versão 5.0, não na mais recente, pode ser que alguma coisa mudou). Mas é mais fácil que fritar ovo!

Veja que a nossa aplicação (Delphi) está resgatando o valor da variável Lua "resultado" então podemos fazer qualquer coisa, o valor que tiver atribuído em "resultado" vai ser trazido para a aplicação.


Passar valor entra um aplicativo compilado e o interpretador Lua, funciona através de pilhas, é pilhas, como você aprendia lá na disciplina de estrutura de dados. Você coloca na pilha a função, depois os parâmetros dela, quando estiver ok, manda executar, ele executa, limpa a pilha e devolve na pilha o resultado, aí você resgata o resultado, limpa a pilha e manda as demais instruções. Se você já mexeu com Lua + C deve estar achando tudo muito familiar.

Bom, mais expressões matemáticas...

E lembrando que da para mandar a fazer qualquer coisa dentro desse script que da para fazer com Lua. Sistemas de arquivos, BD, trabalhar com tables, strings.. nossa! Só a parte muito bem desenvolvida de Lua para trabalhar com strings já vale um binding!!!

Da para também o script lua chamar funções, e pegar valor de variáveis do programa, implementadas em Delphi/Pascal. De uma olhada naquela material do gamedev que eu indiquei o download antes.


E seguem mais testes...



Lucidity? Sim, Delain, o álbum que eu estava ouvindo.

Download do exemplo aqui. Com os fontes, as DLL's e a Lua.pas. Bom estudo!

Saudações!
2010-05-16 15:25:00
Este é o antigo Live Helton

Então, português é minha língua mãe, eu não tenho tanto a aprender quanto nos demais idiomas, assim este blog não discute aprendizado do idioma, e sim tópicos randômicos de interesse do dia a dia. Tecnologia, desenvolvimento, um pouco de reflexão crítica, enfim, uma bagunça bem como nossa mente é.
RSS Feed
"A vida é curta demais para ser pequena / The live is so short to be small / Das leben zu kurz sind für kleine sein". (Benjamin Disraeli)