Como implementar uma avaliação lenta de if ()


10

No momento, estou implementando um avaliador de expressão (expressões de linha única, como fórmulas) com base no seguinte:

  • a expressão inserida é tokenizada para separar booleanos literais, números inteiros, decimais, seqüências de caracteres, funções, identificadores (variáveis)
  • Eu implementei o algoritmo Shunting-yard (modificado levemente para lidar com funções com número variável de argumentos) para se livrar dos parênteses e ordenar aos operadores uma precedência decente em uma ordem pós-fixada
  • meu pátio de derivação simplesmente produz uma fila (simulada) de tokens (por meio de uma matriz, minha linguagem do Powerbuilder Classic pode definir objetos, mas só possui matrizes dinâmicas como armazenamento nativo - lista não verdadeira, sem dicionário) que avalio sequencialmente com um máquina de pilha simples

Meu avaliador está funcionando bem, mas ainda estou com uma falta if()e estou pensando em como proceder.

Com minha avaliação pós-fixada e baseada em pilha do shunting yard, se eu adicionar if()como outra função partes verdadeiras e falsas, uma única if(true, msgbox("ok"), msgbox("not ok"))exibirá as duas mensagens enquanto eu gostaria de mostrar apenas uma. Isso ocorre porque quando preciso avaliar uma função, todos os seus argumentos já foram avaliados e colocados na pilha.

Você poderia me dar uma maneira de implementar de if()forma preguiçosa?

Pensei em processá-las como uma espécie de macro, mas, no início, ainda não tenho a avaliação da condição. Talvez eu precise usar outro tipo de estrutura que não seja uma fila para manter separadamente a condição e as expressões true / false? Por enquanto, a expressão é analisada antes da avaliação, mas também planejo armazenar a representação intermediária como um tipo de expressão pré-compilada para avaliação futura.

Edit : depois de algum pensamento sobre o problema, acho que poderia criar uma representação em árvore da minha expressão (um AST em vez de um fluxo de token linear), a partir do qual eu poderia facilmente ignorar um ou outro ramo do meu if().

Respostas:


9

Existem duas opções aqui.

1) Não implemente ifcomo uma função. Torne-o um recurso de linguagem com semântica especial. Fácil de fazer, mas menos "puro" se você quiser que tudo funcione.

2) Implemente a semântica "chamada pelo nome", que é muito mais complicada, mas permite que a mágica do compilador resolva o problema de avaliação preguiçosa, mantendo-a ifcomo uma função em vez de um elemento de linguagem. É assim:

ifé uma função que aceita dois parâmetros, ambos declarados como "por nome". Quando o compilador vê que está passando algo para um parâmetro de nome, ele altera o código a ser gerado. Em vez de avaliar a expressão e transmitir o valor, ele cria um fechamento que avalia a expressão e a passa. E ao invocar um parâmetro de nome dentro da função, o compilador gera código para avaliar o fechamento.


Não tenho certeza, mas o "fechamento" deve ser "thunk"? Hmm, talvez não; Apenas olhei para a página da Wikipedia: "um thunk é um fechamento sem parâmetros".

Quando você diz "chamar pelo nome", está se referindo globalmente? Como alternativa, a chamada global por nome está apenas implementando um tipo de fechamento, então a função if aceita apenas três fechamentos e avalia dois (condição e depois ou mais), mas nem tudo precisa ser reconhecido como fechamento, como chamada completa por chamada. semântica de nomes
Jimmy Hoffa

@ Matt: O termo "thunk" pode significar várias outras coisas no contexto da programação, e "encerramento sem parâmetros" não é o primeiro em que penso quando o ouço. "Encerramento" é muito mais inequívoco.
Mason Wheeler

11
@ JimmyHoffa: Quando digo "chamar pelo nome", estou me referindo a um estilo específico de configurar um argumento de função, que deve ser opcional. Assim como muitos idiomas existentes, você pode optar por passar um parâmetro por valor ou por referência, para esse cenário, você precisa escolher passar por nome.
Mason Wheeler

Embora sua sugestão sobre a semântica "chamar pelo nome" tenha me mostrado alguns pontos interessantes, é um pouco exagerado para o meu avaliador que não seja um compilador completo, pois minhas chamadas de função são de linha única (pense nas fórmulas do MS-excel). Estou pensando em adicionar uma etapa após o enfileiramento de tokens fazendo uma pseudo-avaliação da pilha para deduzir a árvore de chamada. Deve ser mais fácil saber da árvore os galhos a serem descartados.
Seki

3

Em vez de a função ter a assinatura:

object if(bool, object, object)

Dê a assinatura:

object if(bool, object function(), object function())

Em seguida, sua iffunção chamará a função apropriada com base na condição, avaliando apenas uma delas.


1

É bem fácil, se você compilar tudo preguiçosamente. Você deve ter alguns meios para ver se um valor já está avaliado ou se precisa de mais avaliação.

Em seguida, você pode fazer o seguinte: Se for um literal ou variável (você possui esses ?, ou seja, nomes de funções?), Empurre-o na pilha. Se for uma aplicação de uma função, compile-a separadamente e empurre o ponto de entrada na pilha.

A execução de um programa é, então, meramente repetida até que o topo da pilha seja avaliado e não uma função. Se não for avaliado ou uma função, chame o código para o qual a parte superior da pilha aponta.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.