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