Как динамически генерировать математический код? - PullRequest
10 голосов
/ 02 июня 2011

Я хочу сделать мини-язык программирования в ММА. От текстового файла к модулю (ам) в пакете. В идеале я должен иметь возможность генерировать пакет и модули из Mathematica с помощью функций из другого пакета.

Вопрос: Это возможно? Я ищу ссылку или пример, чтобы начать это.

EDIT: Например:

Представьте себе банк памяти с n целочисленными регистрами.

Инструкции:

1 Z (n)

2 С (м, н)

3 Дж (м, н, кв)

4 S (n)

Каждая строка имеет адрес. Первая строка 1, вторая 2 и т. Д. Z (n) сохраняет 0 в регистре n. C (m, n) сохраняет значение регистра m в регистре n. J (m, n, q), если значение регистра m равно значению регистра n, перейти к строке с адресом q. S (n) добавляет 1 к значению в регистре n.

Затем, учитывая две рабочие программы P и Q, я хочу сгенерировать каскадную программу P + Q.

Затем, учитывая две рабочие программы P и Q, я хочу создать замену Q после P.

Наконец, я хочу начать экспериментировать с рекурсией ... цель этого «мини-проекта».

Ответы [ 2 ]

12 голосов
/ 02 июня 2011

Ваш вопрос состоит из нескольких частей. Во-первых, если вы хотите использовать некоторый синтаксис не-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[]);

Вот небольшой фрагмент кода на этом новом языке (обернутый в 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]]]

Это соответствует этому ММА-коду:

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. Также легко расширить язык любым удобным для вас способом. Конечно, то, как выглядит код на этом языке, не очень красиво, поскольку по сути это AST, выраженный в виде выражения mma. Чтобы сделать его красивее, вам нужно ввести собственный синтаксис и написать парсер из него в mma AST, но это уже другая история.

EDIT

Относительно автоматизации генерации кода и сохранения сгенерированного кода в пакет: вот пара утилит для этого.

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

Мы можем 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[]

РЕДАКТИРОВАТЬ 2

Относительно того, как будет выглядеть код на вашем языке, вы можете сделать его более красивым, не затрачивая все усилия на создание собственного синтаксического анализатора, используя пакет Notation для изменения способа ввода кода и Format / * 1058. * чтобы контролировать то, как он отображается FrontEnd.

4 голосов
/ 02 июня 2011

Это касается вопроса, но вы можете найти важную полезность в настройке CellEvaluationFunction , как описано в посте WReach .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...