eguatech / egua

Linguagem de programação em português, simples e moderna
https://egua.dev/idegua/
MIT License
822 stars 39 forks source link

Erro ao passar função como parâmetro #38

Closed barbozafernando closed 3 years ago

barbozafernando commented 3 years ago

Olá, tomei liberdade de criar o método mapear, cuja função é a mesma do Array.map() do Javascript. Porém, estou com um problema.

err

A linguagem não permite que uma função receba outra função como parâmetro. Alguém poderia me guiar nesse trecho ou corrigir pra poder dar pull?

Valeu!

barbozafernando commented 3 years ago

Bom, testando algumas coisas aqui, percebi um detalhe. No exemplo acima, como função de callback, eu chamo function ao invés de funcao.

Fazendo pelo jeito correto, que é isso aqui: var array = [1, 2]; var novoArray = mapear(array, funcao(item){ return item * 2 });

Retorna os seguintes erros: [Linha: 1] Erro no 'item': Esperado ';' após expressão. [Linha: 1] Erro no final: Esperado '}' após o bloco.

lucaspompeun commented 3 years ago

Boa noite @barbozafernando . Mais uma vez eu agradeço imensamente o interesse por nos ajudar no desenvolvimento da linguagem, peço desculpas pela demora também, eu estava fazendo alguns code reviews no código atual e acabei demorando mais do que esperava.

Vamos lá. Vi que você falou sobre código duplicado no arquivo dist.js e esse arquivo é o arquivo gerado automaticamente pela build para web via Browserify, por isso os códigos sempre estarão duplicados, já que o código fonte do interpretador é via Node.JS e o navegador não aceita código escrito apenas para o node sem um tratamento prévio.

Em relação a função mapear, ela ainda não existe na linguagem, então nativamente não ée possível chamá-la. Você está desenvolvendo ela? Caso positivo, acredito que o melhor jeito é construir no modelo de função interna, pois sinceramente desconheço uma maneira de fazer função aceitar função como argumento via interpretador. Há também um erro em var novoArray = mapear(array, funcao(item){ return item * 2 });, falta o ; ao final do retorna, ficaria var novoArray = mapear(array, funcao(item){ retorna item * 2; });

barbozafernando commented 3 years ago

Boa noite, @lucaspompeun.

Em relação a demora, fique tranquilo. Eu percebi que você estava ocupado com outras coisas e etc. Sem problema!

Sim, em relação ao dist.js eu acabei percebendo depois hahahaha, my bad!

Ah ta, entendi. Cara. Na verdade, eu achei que já estava criando ela na forma de função interna. Peço desculpas.

E em relação ao erro do ; eu acabei percebendo depois hahaha

Mas sim, Lucas, estou desenvolvendo a função mapear. E novamente peço desculpas por não conhecer tanto do projeto ainda, pois as únicas coisas que sei, foram através do seu vídeo postado no youtube a um tempo atrás. Bem, tem como você me dar um pequeno auxílio me dizendo aonde posso criar a função de forma nativa da linguagem? Pois como disse, eu achei que já estava fazendo isso hahaha.

Inclusive, aqui está o código:

globals.defineVar(
    "mapear", 
    new StandardFn(1, function(array, callback){     
      if (!Array.isArray(array)) {
        throw new RuntimeError(
          this.token,
          "Parâmetro inválido. O primeiro parâmetro da função, deve ser um array."
        );
      }

      if (typeof callback !== 'function'){
        throw new RuntimeError(
          this.token,
          "Parâmetro inválido. O segundo parâmetro da função, deve ser uma função."
        );
      }

      let provisorio = [];
      for(let index = 0; index < array.length; ++index){
        provisorio.push(
          callback(
            array[index], index, array
          )
        );
      }

      return provisorio;
    })
  );
lucaspompeun commented 3 years ago

As funções internas são feitas no arquivo egua/src/lib/globalLib.js

barbozafernando commented 3 years ago

Foi o que pensei. Criei justamente lá mesmo.

lucaspompeun commented 3 years ago

E pra testar na interface IDEgua não esquece de dar o npm run build-web

barbozafernando commented 3 years ago

Cara, não to conseguindo fazer executar a função de callback. Tem alguma ideia aí?

Outra coisa, não consigo testar o método pelo terminal porque da esse erro. Screenshot_1

E pela interface do Égua, não aparece nada. Eu acho que a função de callback não tá sendo executada, por isso não loga nada.

lucaspompeun commented 3 years ago

Tentei a solução:

globals.defineVar(
    "mapear",
    new StandardFn(1, function(vetor, funcao){
      return vetor.map(function(){
        return funcao;
      });
    })
  )

Entretanto o interpretador da linguagem não permite a passagem de uma expressão segunda expressão para executar na função interna.

image

Por enquanto não consigo pensar em uma solução em termos de interpretador para isso :/

barbozafernando commented 3 years ago

Aproveitando a sua boa vontade, @leonelsanchesdasilva hahaha, teria alguma dica pra esse caso aqui?

leonelsanchesdasilva commented 3 years ago

@barbozafernando Tenho sim.

mapear([5, 3], a * 2) não vai funcionar porque o interpretador vai avaliar a * 2 na resolução do argumento. Neste caso, você precisa declarar uma funcao primeiro.

egua> var f = funcao(a) { retorna a * 2; };

Aí implementei sua versão de "mapear" e tive o seguinte:

image

Não resolve a tipagem como "function". Resolve como "object". Mais precisamente, uma EguaFunction.

Primeiro sua função tem que aceitar uma EguaFunction e aí usar essa EguaFunction durante a execução. Acredito que a mistura entre Egua e JavaScript esteja atrapalhando um pouco aqui.

lucaspompeun commented 3 years ago

@leonelsanchesdasilva o que você sugere para que uma EguaFunction seja capaz de atender esse tipo de função interna?

barbozafernando commented 3 years ago

@leonelsanchesdasilva o que você sugere para que uma EguaFunction seja capaz de atender esse tipo de função interna?

No momento, esse é o único empecilho. Tentei de várias formas, porém sem sucesso.

leonelsanchesdasilva commented 3 years ago

@lucaspompeun @barbozafernando Bom, callback é uma classe, então tem um constructor:

image

Então implementei assim:

  globals.defineVar(
    "mapear",
    new StandardFn(1, function (array, callback) {
      if (!Array.isArray(array)) {
        throw new RuntimeError(
          this.token,
          "Parâmetro inválido. O primeiro parâmetro da função, deve ser um array."
        );
      }

      if (callback.constructor.name !== 'EguaFunction') {
        throw new RuntimeError(
          this.token,
          "Parâmetro inválido. O segundo parâmetro da função, deve ser uma função."
        );
      }

      let provisorio = [];
      for (let index = 0; index < array.length; ++index) {
        provisorio.push(
          callback(
            array[index], index, array
          )
        );
      }

      return provisorio;
    })
  );

Só que tem outro problema: callback é chamado como função normal do JavaScript. callback não é uma função do JavaScript. De novo, é uma EguaFunction. Chamando do jeito em que está, temos este erro:

egua> mapear([5, 3], f);
Waiting for the debugger to disconnect...
readline.js:1170
            throw err;
            ^

TypeError: Cannot read property 'line' of undefined
    at Egua.runtimeError (D:\GitHub\egua\src\egua.js:90:32)
    at Interpreter.interpret (D:\GitHub\egua\src\interpreter.js:779:23)
    at Egua.run (D:\GitHub\egua\src\egua.js:65:21)
    at Interface.<anonymous> (D:\GitHub\egua\src\egua.js:33:18)
    at Interface.emit (events.js:311:20)
    at Interface._onLine (readline.js:322:10)
    at Interface._line (readline.js:699:8)
    at Interface._ttyWrite (readline.js:1025:14)
    at ReadStream.onkeypress (readline.js:198:10)
    at ReadStream.emit (events.js:311:20)

Ainda que eu simplesmente troque a chamada de callback por callback.call(), eu teria outro problema. O primeiro argumento de callback.call() precisa ser uma instância de interpreter para executar corretamente. interpreter não existe dentro de globalLib por este motivo (src\interpreter.js):

module.exports = class Interpreter {
    constructor(Egua, baseDir) {
        this.Egua = Egua;
        this.baseDir = baseDir;

        this.globals = new Environment();
        this.environment = this.globals;
        this.locals = new Map();

        this.globals = loadGlobalLib(this.globals);
    }

É uma construção bastante esquisita de objeto, devo dizer. globals e Environment são a mesma coisa, então deveria prevalecer apenas o nome environment, evitando certa confusão e problemas de legibilidade.

Enfim, o interpretador inicializa o ambiente (Environment, ou globals), mas a referência do interpretador, que deveria ser passada para a função globalLib não está sendo passada.

Então o que posso fazer é modificar o interpretador para inicializar as funções globais assim:

module.exports = class Interpreter {
    constructor(Egua, baseDir) {
        this.Egua = Egua;
        this.baseDir = baseDir;

        this.globals = new Environment();
        this.environment = this.globals;
        this.locals = new Map();

        this.globals = loadGlobalLib(this, this.globals); // Passo o próprio interpretador para dentro da função que 
                                                                                   // inicializa as funções globais.
    }

E aí mudo a função que inicializa as funções globais para:

module.exports = function (interpreter, globals) { ...

Agora tenho o interpretador onde preciso, no caso, na chamada do callback. A forma correta de chamar callback fica assim:

      let provisorio = [];
      for (let index = 0; index < array.length; ++index) {
        interpreter.locals.set(callback.declaration.params[0].name.lexeme, array[index]);
        provisorio.push(
          callback.call(
            interpreter, [array[index]]
          )
        );
      }

Por fim, temos:

Console da Linguagem Egua v1.1.0

egua> var f = funcao(a) { retorna a * 2; };

egua> escreva(mapear([5, 3], f));
[ 10, 6 ]

egua>

Eu posso abrir uma PR, se for o caso.

lucaspompeun commented 3 years ago

@leonelsanchesdasilva eu ficaria extremamente feliz com um PR seu, que solução genial.