2011-06-02 93 views
10

我想在mma中製作一個迷你編程語言。從文本文件到包中的模塊。理想情況下,我應該能夠通過另一個包中的函數從Mathematica中生成包和模塊。如何動態生成mathematica代碼?

問題: 這可能嗎?我正在尋找一個參考或例子來開始。

編輯: 例如:

想象的存儲體用正整數類型寄存器。

所述指令:

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中。 (m,n,q)如果值寄存器m等於寄存器n的值,則跳轉到地址爲q的行。 S(n)加1到寄存器n中的值。

然後給出兩個工作程序P和Q我想生成連接程序P + Q。

然後給出了兩個工作程序P和Q我想產生的替代Q後P.

最後,我要開始與遞歸......這個「小項目」的宗旨試驗。

+1

這是不是很清楚你想要完成什麼。請澄清。一旦你爲一個符號創建了一些定義,你可以使用Save來將它們保存到一個文件中(如果你喜歡,可以使用'package')。或者是你的問題如何以編程方式創建這些定義?請給出具體的例子。 – Szabolcs 2011-06-02 13:28:15

回答

12

您的問題有幾個部分。首先,如果你想爲你的語言使用一些非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如何呈現它。

+2

Leonid的答案/.{AST->"Atstract Syntax Tree「} – 2011-06-02 13:55:04

+0

@belisarius謝謝!我可能不得不編輯這個並添加更多解釋。糟糕...我花了一段時間才瞭解你的評論的深層代碼生成性質:) – 2011-06-02 13:56:49

+2

我甚至都沒有回答這個問題,因爲我知道你會發布更好的東西。 – 2011-06-02 15:48:00

4

這是問題的切線,但您可能會發現在設置CellEvaluationFunctionas described in a post by WReach重要實用程序。

+0

謝謝@ Mr.Wizard。很有意思。 – 2011-06-04 18:09:26

+0

這是我最喜歡的'工具包'之一。我真的希望在文檔中介紹類似的內容。 (傳播信息+1) – telefunkenvf14 2011-06-30 18:33:34