Wolfram mathematica 如何动态生成代码?

Wolfram mathematica 如何动态生成代码?,wolfram-mathematica,Wolfram Mathematica,我想用mma做一个迷你编程语言。从文本文件到包中的模块。理想情况下,我应该能够通过另一个包中的函数从Mathematica中生成包和模块 问题: 这可能吗?我正在寻找一个参考或一个例子,让这开始 编辑: 例如: 假设一个内存库有n个整型寄存器 说明如下: 1 Z(n) 2 C(m,n) 3j(m,n,q) 4 S(n) 每行都有一个地址。第一行1、第二行2等。 Z(n)在寄存器n中存储0。 C(m,n)将寄存器m的值存储在寄存器n中。 J(m,n,q)若值寄存器m等于寄存器n的值,则跳转到地址q

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

问题: 这可能吗?我正在寻找一个参考或一个例子,让这开始

编辑: 例如:

假设一个内存库有n个整型寄存器

说明如下:

1 Z(n)

2 C(m,n)

3j(m,n,q)

4 S(n)

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

然后给定两个工作程序p和Q,我想生成连接程序p+Q

然后给定两个工作程序p和Q,我想在p之后生成替换Q


最后我想开始试验递归。。。这个“小项目”的目的。

你的问题有几个部分。首先,如果您想为您的语言使用一些非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[]);
它对应于此mma代码:

Module[{a},a = (1 + 2)/(3 + 4)*(5 + 6) - 7; a]
我们的代码生成器基于一个非常简单的想法——我们将重复地将本地规则应用于我们持有的代码。本地规则将从我们的功能定义中提取,如下所示:

ClearAll[expansionRules];
expansionRules[heads : {__Symbol}] := Flatten[DownValues /@ heads]
我们需要为我们的语言提供一份标题列表。我将手动执行此操作,但通过创建自定义赋值运算符,很容易实现自动化

allHeadsToExpand[] := {testIf, testVar, testPlus, testTimes, testDivide, 
      testPower, testSet, testIf,module,package, inContext}
现在,我们生成代码:

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}]]]]
我们施工的优势在于,我们还可以直接执行AST:

In[198]:= ReleaseHold[cd]

Out[198]= -(16/7)
要将其保存到包中,只需使用
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];
以下是相同的示例,但放在一个包中:

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]]]]]
我们生成并保存代码如下:

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
我们可以
导入
它来测试:

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[]
[107]中的
:=导入[文件,“文本”]
Out[107]=
BeginPackage[“myPackage`]”;
开始[“`Private`]”;
模[{a},a=((1+2)*如果[3+4==0&&1<0,Inf,(3+4)^(-1)]*(5+6)-7;
如果[ValueQ[a],a,Throw[$Failed,{“varunde”,a}]];
结束[];
EndPackage[]
编辑2


关于您的语言中的代码的外观,您可以通过使用注释包来改变输入代码的方式,并使用
格式
/
格式值
来控制前端如何呈现代码,从而使代码更漂亮,而无需创建自己的解析器

这与问题无关,但您可能会在设置
CellEvaluationFunction

中发现重要的实用程序,您不太清楚要实现什么。请澄清。创建符号的某些定义后,可以使用
save
将其保存到文件(“package”,如果愿意)。或者您的问题是如何以编程方式创建这些定义?请给出具体的例子。Leonid的答案/{AST->“抽象语法树”}@belisarius谢谢!我可能不得不编辑这个,并添加更多的解释。哎呀。。。我花了一段时间才理解你评论的深层代码生成本质:)我甚至都没有回答这个问题,因为我知道你会发布更好的东西。@Mr.Wizard谢谢。这是我真正感兴趣的话题。@Leonid:我看不见这个:
Hold[package[“myPackage””,inContext[“Private”…
ಠ_ಠ ... 如果这是故意的,你就是个天才!谢谢@Mr.Wizard。非常有趣。这是我从“工具包”中最喜欢的一个。我真的希望文档中包含类似的内容。(+1用于传播信息)
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
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[]