Как динамически генерировать код mathematica?

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

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

изменить: Например:

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

инструкции:

1 Z (n)

2 C (m,n)

3 J (m,n,q)

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 ответов


Ваш вопрос состоит из нескольких частей. Во-первых, если вы хотите использовать синтаксис не-mma для своего языка, вам нужно сделать синтаксический анализатор с вашего языка на выражение mma (AST вашего кода). Я оставлю это (так как это отдельная тема) и предположу, что вы are готов использовать синтаксис 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]]]

он соответствует этому коду 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)

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

у нас можете 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

что касается того, как будет выглядеть код на вашем языке, вы можете сделать это красивее, не создавая свой собственный парсер, используя пакет обозначений, чтобы изменить способ ввода кода и Format/FormatValues для управления тем, как он отображается интерфейсом.


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