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.
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