如何动态生成mathematica代码?

nil*_*ock 10 wolfram-mathematica

我想在mma中制作一个迷你编程语言.从文本文件到包中的模块.理想情况下,我应该能够通过另一个包中的函数从Mathematica中生成包和模块.

问题:这可能吗?我正在寻找一个参考或一个例子来启动它.

编辑:例如:

想象一下具有n个整数类型寄存器的存储体.

说明是:

1 Z(n)

2 C(m,n)

3 J(m,n,q)

4 S(n)

每行都有一个地址.第一行1,第二行2等.Z(n)在寄存器n中存储0.寄存器n中的寄存器m的C(m,n)存储值.J(m,n,q)如果值寄存器m等于寄存器n的值则跳转到地址q的行.S(n)将n加1到寄存器n中的值.

然后给出两个工作程序P和QI想要生成连接程序P + Q.

然后给出两个工作程序P和QI想要在P之后生成替换Q.

最后,我想开始尝试递归...这个'迷你项目'的目的.

Leo*_*rin 12

你的问题有几个部分.首先,如果你想为你的语言使用一些非mma语法,你需要从你的语言到mma表达式(你的代码的AST)创建一个解析器.我将离开这一个(因为这是一个独立的主题),并假设你愿意使用MMA的语法或有办法程序转移到一些MMA表达.

关于mma代码生成,Mathematica非常适合它,因为它包含代码是数据范例.这里最难的部分是评估控件 - 我们希望确保在代码生成过程中我们生成的代码片段都不会评估.评估控制的标准技术可以成功地用于此,但这通常会使事情变得相当复杂.我将说明一种mma代码生成技术,它不是最好/最强大的技术,但最简单.

考虑一下这些定义创建的玩具语言:

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[]);
Run Code Online (Sandbox Code Playgroud)

这是一种使用这种新语言的小代码片段(包含在内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]]]
Run Code Online (Sandbox Code Playgroud)

它对应于这个mma代码:

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

我们的代码生成器基于一个非常简单的想法 - 我们将重复将本地规则应用于我们的代码.本地规则将从我们的函数定义中提取,如下所示:

ClearAll[expansionRules];
expansionRules[heads : {__Symbol}] := Flatten[DownValues /@ heads]
Run Code Online (Sandbox Code Playgroud)

我们需要提供我们语言的头部列表.我将手动执行此操作,但通过创建自定义赋值运算符可以轻松实现自动化.

allHeadsToExpand[] := {testIf, testVar, testPlus, testTimes, testDivide, 
      testPower, testSet, testIf,module,package, inContext}
Run Code Online (Sandbox Code Playgroud)

现在,我们生成代码:

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}]]]]
Run Code Online (Sandbox Code Playgroud)

要执行它,您可以简单地使用ReleaseHold:

In[197]:= ReleaseHold[expanded]

Out[197]= -(16/7)
Run Code Online (Sandbox Code Playgroud)

我们的构造的优点是我们也可以直接执行我们的AST:

In[198]:= ReleaseHold[cd]

Out[198]= -(16/7)
Run Code Online (Sandbox Code Playgroud)

要将其保存到包中,只需使用Put命令即可.以任何您想要的方式扩展语言也很容易.当然,这种语言中的代码看起来不太漂亮,因为它本质上是表达为mma表达式的AST.为了使它更漂亮,你需要引入自己的语法并从中编写一个解析器到mma AST,但这是另一个故事.

编辑

关于代码生成的自动化以及将生成的代码保存到包中:这里有几个实用程序.

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, formatCode@generatedCode]},
   Close[file];
   result];
Run Code Online (Sandbox Code Playgroud)

这是相同的示例,但放在一个包中:

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]]]]]
Run Code Online (Sandbox Code Playgroud)

我们生成并保存代码如下:

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
Run Code Online (Sandbox Code Playgroud)

我们可以Import测试:

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[]
Run Code Online (Sandbox Code Playgroud)

编辑2

关于你的语言代码的外观,你可以通过使用Notation包改变你输入代码的方式和Format/ FormatValues来控制它的呈现方式来使这个更漂亮,而不是一直创建自己的解析器.前端.

  • @Leonid:我_ notnot unsee_ this:``Hold [package ["myPackage`",inContext ["`private`"...```ಠ_ಠ......如果那是故意的,那你就是天才! (3认同)
  • Leonid的回答/.{AST->"Abstract Syntax Tree"} (2认同)
  • 我甚至没有回答这个问题,因为我知道你会发布一些更好的东西. (2认同)