您的問題有幾個部分。首先,如果你想爲你的語言使用一些非mma語法,你需要從你的語言到mma表達式(你的代碼的AST)創建一個解析器。我會離開這一個(因爲這是一個單獨的主題),並假設你是願意使用mma語法或有手段將您的程序轉移到某些mma表達式。
關於mma代碼的生成,Mathematica非常適合它,因爲它包含了code-is-data範例。這裏最難的部分是評估控制 - 我們希望確保在代碼生成過程中我們生成的代碼片段都不會評估。評估控制的標準技術可以成功地用於此,但這通常會使事情變得相當複雜。我將說明一種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[]);
下面是在這個新的語言一小段代碼(包裹在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]]]
它對應於這個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}]]]]
要執行它,你可以簡單地使用ReleaseHold
:
In[197]:= ReleaseHold[expanded]
Out[197]= -(16/7)
我們結構的優點是,我們也能執行我們的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, [email protected]]},
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
我們可以把它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[]
EDIT 2
關於方式在你的語言的代碼看起來,你可以讓這個漂亮不將所有的方式來創建自己的解析器,通過使用符號包改變的方式,你可以輸入代碼和Format
/FormatValues
來控制FrontEnd如何呈現它。
這是不是很清楚你想要完成什麼。請澄清。一旦你爲一個符號創建了一些定義,你可以使用Save來將它們保存到一個文件中(如果你喜歡,可以使用'package')。或者是你的問題如何以編程方式創建這些定義?請給出具體的例子。 – Szabolcs 2011-06-02 13:28:15