2011-06-02 14 views
10

Quiero hacer un mini lenguaje de programación en mma. Desde un archivo de texto a Módulo (s) en un Paquete. Idealmente, debería ser capaz de generar el paquete y los módulos desde dentro de Mathematica por funciones en otro paquete.¿Cómo generar dinámicamente el código matemático?

Pregunta: ¿Esto es posible? Estoy buscando una referencia o un ejemplo para comenzar esto.

EDIT: Por ejemplo:

imaginar un banco de memoria con n número entero tecleó registros.

Las instrucciones son:

1 Z (n)

2 C (m, n)

3 J (m, n, q)

4 S (n)

Cada línea tiene una dirección. Primera línea 1, segunda 2, etc. Z (n) almacene 0 en el registro n. C (m, n) almacenar el valor del registro m en el registro n. J (m, n, q) si el registro de valor m es igual al valor del registro n, luego salta a la línea con la dirección q. S (n) agregue 1 al valor en el registro n.

Luego, dados dos programas de trabajo P y Q, deseo generar el programa concatenado P + Q.

Entonces dieron dos programas de trabajo P y Q que desea generar la sustitución Q después de P.

Por último, quiero empezar a experimentar con la recursividad ... el propósito de este 'mini-proyecto'.

+1

No está muy claro lo que quiere lograr. Por favor aclara Una vez que haya creado algunas definiciones para un símbolo, puede guardarlas en un archivo ('paquete' si lo desea) usando 'Guardar'. ¿O es su pregunta cómo crear esas definiciones programáticamente? Por favor da ejemplos específicos. – Szabolcs

Respuesta

12

Tu pregunta tiene varias partes. En primer lugar, si desea usar alguna sintaxis que no sea mma para su idioma, debe hacer un analizador sintáctico de su lenguaje a la expresión mma (AST de su código). Voy a dejar esto fuera (ya que este es un tema separado) y supongo que son dispuestos a usar la sintaxis de mma o tienen medios para transferir su programa a alguna expresión mma.

En cuanto a la generación del código mma, Mathematica es muy adecuado para este, ya que abarca el paradigma del código-es-datos. La parte más difícil es el control de la evaluación: queremos asegurarnos de que ninguno de los fragmentos de código generados se evalúe durante el proceso de generación de código. Las técnicas estándar de control de la evaluación se pueden usar con éxito para eso, pero esto generalmente hará las cosas bastante complicadas. Ilustraré una técnica de generación de código mma, que no es la mejor/más poderosa, pero la más fácil.

Considere un lenguaje de juguete creado por estas definiciones:

SetAttributes[testSet, HoldFirst]; 
SetAttributes[testIf, HoldRest]; 
SetAttributes[testVar, HoldAll]; 
SetAttributes[module, HoldAll]; 
SetAttributes[{package, inContext}, HoldRest]; 
testPlus[x_, y_] := Plus[x, y]; 
testTimes[x_, y_] := Times[x, y]; 
testDivide[x_, y_] := If[y == 0, Inf, Times[x, Power[y, -1]]]; 
testPower[x_, y_] := If[x == 0 && y < 0, Inf, Power[x, y]]; 
testSet[HoldPattern[testVar[x_]], expr_] := Set[x, expr]; 
testVar[x_] := If[ValueQ[x], x, Throw[$Failed, {"varundef", x}]]; 
testIf[cond_, expr_] := If[cond, expr]; 
testIf[cond_, expr_, else_] := If[cond, expr, else]; 
module[{vars__}, body_] := Module[{vars}, body]; 
package[name_, code_] := (BeginPackage[name]; code; EndPackage[]); 
inContext[name_, code_] := (Begin[name]; code; End[]); 

Aquí es un pequeño fragmento de código en este nuevo idioma (envuelto en Hold):

cd = 
Hold[module[{a}, testSet[testVar[a], 
    testPlus[testTimes[testTimes[testPlus[1, 2], 
    testPower[testPlus[3, 4], -1]], testPlus[5, 6]], -7]]; testVar[a]]] 

Se corresponde con el código de MMA:

Module[{a},a = (1 + 2)/(3 + 4)*(5 + 6) - 7; a] 

Nuestro generador de código se basa en un muy idea simple: repetidamente aplicaremos las reglas locales a nuestro código retenido.Las reglas locales serán extraídos de las definiciones de las funciones, así:

ClearAll[expansionRules]; 
expansionRules[heads : {__Symbol}] := Flatten[DownValues /@ heads] 

, necesitamos proveer una lista de cabezas de nuestro idioma. Haré eso manualmente, pero es fácil de automatizar, creando operadores de asignación personalizados.

allHeadsToExpand[] := {testIf, testVar, testPlus, testTimes, testDivide, 
     testPower, testSet, testIf,module,package, inContext} 

Ahora, generamos nuestro código:

In[195]:= expanded = cd//.expansionRules[allHeadsToExpand[]] 

Out[195]= 
Hold[Module[{a}, 
    a = ((1 + 2) If[3 + 4 == 0 && -1 < 0, Inf, 1/(3 + 4)]) (5 + 6) - 7; 
    If[ValueQ[a], a, Throw[$Failed, {"varundef", a}]]]] 

Para ejecutarlo, sólo tiene que utilizar ReleaseHold:

In[197]:= ReleaseHold[expanded] 

Out[197]= -(16/7) 

La ventaja de nuestra construcción es que también podemos ejecutar nuestra AST directamente:

In[198]:= ReleaseHold[cd] 

Out[198]= -(16/7) 

Para guardar esto en un paquete, simplemente puede usar el comando Put. También es fácil extender el idioma de la forma que desee. Por supuesto, la forma en que se ve el código en este lenguaje no es bonita, ya que es esencialmente el AST expresado como expresión mma. Para hacerlo más bonito, necesitarías introducir tu propia sintaxis y escribir un analizador para mma AST, pero esa es otra historia.

EDITAR

En cuanto a la automatización de la generación de código y guardar el código generado en un paquete: aquí hay un par de utilidades para hacer eso.

Clear[generateCode]; 
generateCode[code_Hold] := 
    code //. expansionRules[allHeadsToExpand[]] //. 
    HoldPattern[ 
     CompoundExpression[left___, CompoundExpression[middle___], right___]] :> 
     (left; middle; right); 

Clear[formatCode]; 
formatCode[code_Hold] := 
    StringReplace[Function[Null, ToString[Unevaluated[#], InputForm], HoldAll] @@ 
    code, ";" :> ";\n"]; 

Clear[saveCode]; 
saveCode[file_, generatedCode_] := 
With[{result = BinaryWrite[file, [email protected]]}, 
    Close[file]; 
    result]; 

aquí es el mismo ejemplo, pero coloca en un envase:

cdp = Hold[ 
    package["myPackage`", 
    inContext["`Private`", 
     module[{a}, 
     testSet[testVar[a], 
      testPlus[testTimes[testTimes[testPlus[1, 2], 
      testPower[testPlus[3, 4], -1]], testPlus[5, 6]], -7]]; 
     testVar[a]]]]] 

Generamos y guardar el código de la siguiente manera:

In[101]:= file = FileNameJoin[{"C:","Temp","myPackage.m"}] 
Out[101]= C:\Temp\myPackage.m 

In[106]:= saved =saveCode[file,generateCode[cdp]] 
Out[106]= C:\Temp\myPackage.m 

Podemos Import a prueba:

In[107]:= Import[file,"Text"] 

Out[107]= 
BeginPackage["myPackage`"]; 
Begin["`Private`"]; 
Module[{a}, a = ((1 + 2)*If[3 + 4 == 0 && -1 < 0, Inf, (3 + 4)^(-1)])*(5 + 6) - 7; 
    If[ValueQ[a], a, Throw[$Failed, {"varundef", a}]]]; 
End[]; 
EndPackage[] 

EDIT 2

En cuanto a la forma en que el código en su idioma se verá, usted puede hacer esto más bonito sin tener que ir todo el camino para crear su propio programa de análisis, utilizando el paquete de notación para alterar la forma en que puede código de entrada y Format/FormatValues para controlar cómo lo renderiza FrontEnd.

+2

respuesta de Leonid /.{AST->"Abstract Syntax Tree "} –

+0

@belisarius Gracias! Probablemente tendré que editar esto y agregar más explicaciones. Oops ... Me tomó un tiempo entender la naturaleza profunda de generación de código de tu comentario :) –

+2

Ni siquiera me molesté en responder esta pregunta, ya que sabía que estarías por publicar algo mucho mejor. –

4

Es tangencial a la pregunta, pero puede encontrar una utilidad importante en la configuración CellEvaluationFunctionas described in a post by WReach.

+0

Gracias @ Mr.Wizard. Muy interesante. –

+0

Esa es una de mis favoritas de la 'bolsa de herramientas'. Realmente deseo que algo así se cubriera en la documentación. (+1 para difundir la información) – telefunkenvf14

Cuestiones relacionadas