Pergunta

Suponha que eu precise criar meu próprio pequeno DSL que usaria o Python para descrever uma determinada estrutura de dados. Por exemplo, eu gostaria de poder escrever algo como

f(x) = some_stuff(a,b,c)

e tenha python, em vez de reclamar de identificadores não declarados ou tentar invocar a função de alguma forma, convertê -la em uma expressão literal para minha conveniência adicional.

É possível obter uma aproximação razoável a isso criando uma classe com redefinido adequadamente __getattr__ e __setattr__ Métodos e use -o da seguinte forma:

e = Expression()
e.f[e.x] = e.some_stuff(e.a, e.b, e.c)

Seria legal, porém, se fosse possível se livrar do irritante "E". prefixos e talvez até evite o uso de []. Então, eu queria saber: é possível de alguma forma "redefinir temporariamente" as pesquisas e tarefas globais de nome? Em uma nota relacionada, talvez haja bons pacotes para alcançar facilmente essa funcionalidade "citando" para expressões de Python?

Foi útil?

Solução

Não tenho certeza se é uma boa ideia, mas pensei em De uma chance. Para resumir:

class PermissiveDict(dict):
    default = None

    def __getitem__(self, item):
        try:
            return dict.__getitem__(self, item)
        except KeyError:
            return self.default

def exec_with_default(code, default=None):
    ns = PermissiveDict()
    ns.default = default
    exec code in ns
    return ns

Outras dicas

Você pode querer dar uma olhada no ast ou parser Os módulos incluídos no python para analisar, acessar e transformar a árvore de sintaxe abstrata (ou a árvore de análise, respectivamente) do código de entrada. Tanto quanto eu sei, o Sistema matemático sábio, escrito em Python, tem um tipo semelhante de pré -compilador.

Em resposta ao comentário de Wai, aqui está uma solução divertida que encontrei. Primeiro de tudo, para explicar mais uma vez o que faz, suponha que você tenha o seguinte código:

definitions = Structure()
definitions.add_definition('f[x]', 'x*2')
definitions.add_definition('f[z]', 'some_function(z)')
definitions.add_definition('g.i', 'some_object[i].method(param=value)')

onde adicionar definições implica analisar os lados da mão esquerda e os lados da mão direita e fazer outras coisas feias. Agora, uma abordagem (não necessariamente boa, mas certamente divertida) aqui permitiria escrever o código acima da seguinte maneira:

@my_dsl
def definitions():
    f[x] = x*2
    f[z] = some_function(z)
    g.i  = some_object[i].method(param=value)

e peça a Python a maior parte da análise sob o capô. A ideia é baseada no simples exec <code> in <environment> Declaração, mencionada por Ian, com uma adição de hackish. Ou seja, o bytecode da função deve ser ligeiramente ajustado e todas as operações de acesso variável local (load_fast) alteradas para acesso variável do ambiente (load_name).

É mais fácil mostrado do que explicado: http://fouryears.eu/wp-content/uploads/pydsl/

Existem vários truques que você pode querer fazer para torná -lo prático. Por exemplo, no código apresentado no link acima, você não pode usar funções e construções de idiomas incorporadas como loops e instruções se dentro de uma função @MY_DSL. Você pode fazer isso funcionar, no entanto, adicionando mais comportamento à classe Env.

Atualizar. Aqui é uma explicação um pouco mais detalhada da mesma coisa.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top