%mathpiper,def="OMREP;OMDef;OMForm;OMRead;OMParse;OMEcho;OMEchoEscape"

/* def file definitions
OMREP
OMDef
OMForm
OMRead
OMParse
OMEcho
OMEchoEscape
*/

////////////////////////
// Written by Alberto González Palomo and Ayal Pinkus.
////////////////////////

/* The read-eval-print loop */
/* It can take one parameter, that is the evaluation count. If it is greater
   than zero, only that number of iterations will be performed before
   exiting. This is particularly useful when connecting to MathPiper via pipes.
*/
Rulebase("OMREP",{});
Rule("OMREP",0,1,True)
[
  OMREP(0);// 0 means keep repeating, as usual.
];
Rulebase("OMREP",{count});
LocalSymbols(input,stringOut,result)
Rule("OMREP",1,1,True)
[
  Local(input,stringOut,result);
  While(Not(IsExitRequested()))
  [
    Bind(errorObject, False);
    ExceptionCatch(Bind(input, PipeFromString(ConcatStrings(ReadCmdLineString("")," "))OMRead()), Bind(errorObject,OMGetCoreError()));

    If(Not(errorObject = False), errorObject); //todo:tk:check this code.

    If (Not(IsExitRequested()) And errorObject = False,
    [
      Bind(stringOut,"");
      Bind(result,False);
      ExceptionCatch(Bind(stringOut,PipeToString()[Secure(Bind(result,Eval(input)));]), Bind(errorObject,OMGetCoreError()));

      If(Not(errorObject = False), errorObject); //todo:tk:check this code.

      If(Not(stringOut = ""), WriteString(stringOut));
      SetGlobalLazyVariable(%,result);
      If(PrettyPrinterGet()="",
      [
        Apply("OMForm",{result});
      ],
      Apply(PrettyPrinterGet(),{result}));
      If(count > 0 And (count:=count-1) = 0, Exit());
    ]);
  ];
];


LocalSymbols(omindent) [
  // Function definitions
  OMIndent() := [omindent := omindent + 2;];
  OMUndent() := [omindent := omindent - 2;];
  OMClearIndent() := [omindent := 0;];
  OMIndentSpace() := Space(omindent);

  // Initialization of indentation
  OMClearIndent();
]; // LocalSymbols(omindent)

///////////////////////////////////////////////////////////////////////
// Output

10 # OMForm(_expression)
     <--
     [
     OMClearIndent();
     OMEcho("<OMOBJ>");
     OMIndent();
     If(IsAtom(expression),
        If(expression = ToAtom("%"),
           Secure(expression := Eval(expression))
           )
        );
     OMFormExpression(expression);
     OMUndent();
     OMEcho("</OMOBJ>");
     ];

10 # OMFormExpression(i_IsString)  <-- OMEcho("<OMSTR>":i:"</OMSTR>");
11 # OMFormExpression(i_IsInteger) <-- OMEcho("<OMI>":ToString(i):"</OMI>");
12 # OMFormExpression(i_IsNumber)  <-- OMEcho("<OMF dec=\"":ToString(i):"\"/>");
13 # OMFormExpression(i_IsConstant)_(OMSymbol()[ ToString(i) ] != Empty)
     <-- OMEcho("<OMS cd=\"":OMSymbol()[ ToString(i) ][1]
                :"\" name=\"":OMSymbol()[ ToString(i) ][2]:"\"/>"
                );
14 # OMFormExpression(i_IsConstant)// Should we rather evaluate it?
     <-- OMEcho("<OMV name=\"":ToString(i):"\"/>");
15 # OMFormExpression(i_IsVariable)_(OMSymbol()[ ToString(i) ] != Empty)
     <-- OMEcho("<OMS cd=\"":OMSymbol()[ ToString(i) ][1]
                :"\" name=\"":OMSymbol()[ ToString(i) ][2]:"\"/>"
                );
16 # OMFormExpression(i_IsVariable)
     <-- OMEcho("<OMV name=\"":ToString(i):"\"/>");
16 # OMFormExpression(i_IsVariable)_(i = Empty)
     <-- False; // This is useful for void expressions.

10 # OMFormExpression(function_IsFunction)_(Type(function) = "OMError")
     <--
     [
     Local(cd, name);
     If(IsList(function[1]),
        [ cd := function[1][1]; name := function[1][2]; ],
        [ cd := "error";        name := function[1];    ]);
     OMEcho("<OME>");
     OMIndent();
     OMEcho("<OMS cd=\"":cd:"\" name=\"":name:"\"/>");
     ForEach(i, Rest(function)) OMFormExpression(i);
     OMUndent();
     OMEcho("</OME>");
     ];
10 # OMFormExpression(function_IsFunction)_(Type(function) = "OME")
     <--
     [
     OMEcho("<OME>");
     OMIndent();
     ForEach(i, function) OMFormExpression(i);
     OMUndent();
     OMEcho("</OME>");
     ];
10 # OMFormExpression(function_IsFunction)_(Type(function) = "OMS")
     <-- OMEcho("<OMS cd=\"":function[1]:"\" name=\"":function[2]:"\"/>");
10 # OMFormExpression(function_IsFunction)_(Type(function) = "OMBIND")
     <--
     [
     OMEcho("<OMBIND>");
     OMIndent();
     ForEach(i, function) OMFormExpression(i);
     OMUndent();
     OMEcho("</OMBIND>");
     ];
10 # OMFormExpression(function_IsFunction)_(Type(function) = "OMBVAR")
     <--
     [
     OMEcho("<OMBVAR>");
     OMIndent();
     ForEach(i, function) OMFormExpression(i);
     OMUndent();
     OMEcho("</OMBVAR>");
     ];
10 # OMFormExpression(function_IsFunction)_(Type(function) = "OMA")
     <--
     [
     // This is not the same as the next rule: this is OMA(a,b,c,...),
     // which is used for building OMA constructs in the mapping to OM.
     OMEcho("<OMA>");
     OMIndent();
     ForEach(i, function) OMFormExpression(i);
     OMUndent();
     OMEcho("</OMA>");
     ];
11 # OMFormExpression(function_IsFunction)
     <--
     [
     OMEcho("<OMA>");
     OMIndent();
     OMFormFunction(function);
     OMUndent();
     OMEcho("</OMA>");
     ];

11 # OMFormFunction(function_IsFunction)
     <--
     [
     Local(arity);
     arity := Length(function);
     OMEcho("<OMS cd=\"piper\" name=\"":Type(function):"\"/>");
     If(arity > 0, ForEach(arg, function) OMFormExpression(arg));
     ];
10 # OMFormFunction(function_IsFunction)_(OMSymbol()[ Type(function) ] != Empty)
     <--
     [
     Local(symbolDef);
     // [20051016 AGP] The "signature" feature is an old attempt at pattern
     // matching, but now that we have real predicates in the mappings it's
     // probably obsolete. I'll think about removing it.
     symbolDef := OMSymbol()[ OMSignature(function) ];
     If(symbolDef = Empty, symbolDef := OMSymbol()[ Type(function) ] );
     If(symbolDef = Empty Or Length(symbolDef) < 3 Or symbolDef[3] = {},
        [
        OMEcho("<OMS cd=\"":symbolDef[1]:"\" name=\"":symbolDef[2]:"\"/>");
        ForEach(arg, function) OMFormExpression(arg);
        ],
        [
        Local(result);
        result := OMApplyMapping(function, symbolDef[3]);
        //Check(IsList(result), "Return Type", PipeToString()Echo("Mapping result is not a list: ", result));
        If(IsList(result),
           [
           result := ListToFunction(Subst($, function[0]) result);
           OMFormExpression(result[0]);
           ForEach(i, result) OMFormExpression(i);
           ],
           If(result = Empty,
              Echo("No rule matched ", function, symbolDef[3]),
              Echo("Unexpected result value from OMApplyMapping(): ", result)
             )
          );
        ]
       );
     ];


OMWrite(_expression) <--
[
  Write(expression);
];

OMEcho(_expression) <--
[
  OMIndentSpace();
  Write(expression);
  NewLine();
];
OMEcho(expression_IsString) <--
[
  OMIndentSpace();
  WriteString(expression);
  NewLine();
];
OMEcho(expression_IsList) <--
[
  ForEach(arg, expression)
  [
    If (IsString(arg), WriteString(arg), Write(arg));
  ];
  NewLine();
];

OMEscape(_expression) <--
[
  "<![CDATA[":ToString(expression):"]]>";
];
OMEscapeString(_expression_IsString) <--
[
  "<![CDATA[":expression:"]]>";
];
OMWriteEscape(_expression) <--
[
  WriteString(OMEscape(expression));
];
OMWriteStringEscape(expression_IsString) <--
[
  WriteString(OMEscapeString(expression));
];
OMEchoEscape(_expression) <--
[
  OMWriteEscape(expression);
  NewLine();
];
OMEchoEscape(expression_IsString) <--
[
  OMWriteStringEscape(expression);
  NewLine();
];
OMEchoEscape(expression_IsList) <--
[
  WriteString("<![CDATA[");
  ForEach(arg, expression)
  [
    If (IsString(arg), WriteString(arg), Write(arg));
  ];
  WriteString("]]>");
  NewLine();
];


HoldArgumentNumber("OMForm",1,1);
//HoldArgumentNumber("OMFormExpression",1,1);
//HoldArgumentNumber("OMFormFunction",1,1);


OMSignature(_function) <-- "";
OMSignature(function_IsFunction) <--
[
  Local(makeSig);
  makeSig := {ConcatStrings, Type(function), "_"};
  Local(type);
  type := "";// If "function" doesn't have parameters, the signature is "f_".
  ForEach(arg, function)
  [
    If(Type(arg) = "List",
       type := "L",
       If(IsFunction(arg),
          type := "F",
          If(IsInteger(arg),
             type := "I",
             type := "V"
             )
          )
       );
    DestructiveAppend(makeSig, type);
  ];
  Secure(Eval(ListToFunction(makeSig)));
];
HoldArgumentNumber("OMSignature", 1, 1);



///////////////////////////////////////////////////////////////////////
// Input

// Troubleshooting guide:
// "encodingError:unexpected closing brace": this happens in the ReadOMOBJ
//      rules. It means that you forgot to call OMNextToken() from your rule.

LocalSymbols(omtoken) [
  OMNextToken() :=
  [
    omtoken := XmlExplodeTag(ToString(ReadToken()));
  ];
  OMToken() := omtoken;
]; // LocalSymbols(omtoken)

OMRead():=
[
  Local(result);
  ExceptionCatch(
  [
    XmlTokenizer();
    OMNextToken();
    result := MatchOMOBJ(OMToken());
    DefaultTokenizer();
  ],
  [
    result := OMGetCoreError();
    DefaultTokenizer();
  ]);
  result;
];


OMDump(str):=
PipeFromString(str:" EndOfFile")
[
  Local(result);
  XmlTokenizer();
  OMNextToken();
  While(OMToken() != "EndOfFile")
  [
    Echo("Exploded ",OMToken());
    OMNextToken();
  ];
  DefaultTokenizer();
  True;
];



10 # MatchClose(_x)_(x = OMToken()) <-- [OMNextToken();True;];
20 # MatchClose(_x) <-- Check(False, "Syntax", PipeToString()Echo("encodingError:unexpected closing brace")); //@@@ TODO better error reporting

10 # MatchOMOBJ(XmlTag("OMOBJ",_attributes,"Open")) <--
[
  // Any attributes are ignored.
  Local(result);
  OMNextToken();
  result := ReadOMOBJ(OMToken());
  MatchClose(XmlTag("OMOBJ",{},"Close"));
  result;
];
10 # MatchOMOBJ(XmlTag("OMOBJ",_attributes,"OpenClose")) <--
[
  OMNextToken();
  // Any attributes are ignored.
  // This is a void expression, of the form "<OMOBJ/>".
  Empty;
];
20 # MatchOMOBJ(_rest) <-- Check(False, "Type", PipeToString()Echo("encodingError:not an OMOBJ :",rest));

10 # ReadOMOBJ(XmlTag("OMOBJ",_attributes,"Close")) <--
[
  // This is a void expression, of the form "<OMOBJ></OMOBJ>".
  Empty;
];

10 # ReadOMOBJ(XmlTag("OMI",{},"Open")) <--
[
  Local(result);
  OMNextToken();
  result := ToAtom(OMToken());
  OMNextToken();
  MatchClose(XmlTag("OMI",{},"Close"));
  result;
];

10 # ReadOMOBJ(XmlTag("OMV",{{"NAME",_name}},"OpenClose")) <--
[
  OMNextToken();
  ToAtom(name);
];

10 # ReadOMOBJ(XmlTag("OMF",{{"DEC",_dec}},"OpenClose")) <--
[
  OMNextToken();
  ToAtom(dec);
];

10 # ReadOMOBJ(XmlTag("OMSTR",{},"Open")) <--
[
  Local(result);
  OMNextToken();
  If(IsString(OMToken()), [result := OMToken(); OMNextToken();], result := "");
  MatchClose(XmlTag("OMSTR",{},"Close"));
  result;
];
10 # ReadOMOBJ(XmlTag("OMSTR",{},"OpenClose")) <--
[
  OMNextToken();
  "";
];

10 # ReadOMOBJ(XmlTag("OMA",{},"Open")) <--
[
  Local(result, new);
  result:={};
  OMNextToken();
  While (OMToken() != XmlTag("OMA",{},"Close"))
  [
    new:=ReadOMOBJ(OMToken());
    DestructiveAppend(result,new);
  ];
  MatchClose(XmlTag("OMA",{},"Close"));
  OMApplyReverseMapping(ListToFunction(result));
];

10 # ReadOMOBJ(XmlTag("OMBIND",{},"Open")) <--
[
  Local(result, new);
  result:={};
  OMNextToken();
  While (OMToken() != XmlTag("OMBIND",{},"Close"))
  [
    new:=ReadOMOBJ(OMToken());
    DestructiveAppend(result,new);
  ];
  MatchClose(XmlTag("OMBIND",{},"Close"));
  result;
];
10 # ReadOMOBJ(XmlTag("OMBVAR",{},"Open")) <--
[
  Local(result, new);
  result:={};
  OMNextToken();
  While (OMToken() != XmlTag("OMBVAR",{},"Close"))
  [
    new:=ReadOMOBJ(OMToken());
    DestructiveAppend(result,new);
  ];
  MatchClose(XmlTag("OMBVAR",{},"Close"));
  result;
];

10 # OMApplyReverseMapping(piperExp_IsFunction) <-- piperExp;
10 # OMApplyReverseMapping(piperExp_IsFunction)_(OMSymbol()[ Type(piperExp) ] != Empty)
     <--
     [
     Local(symbolDef, result);
     symbolDef := OMSymbol()[ Type(piperExp) ];
     If(symbolDef[4] = {},
        result := piperExp,
        [
          result := OMApplyMapping(piperExp, symbolDef[4]);
          result := Subst($, piperExp[0]) result;
          If(IsList(result), result := ListToFunction(result));
        ]
       );
     result;
     ];

10 # OMApplyMapping(_function, _mapping) <--
[
  Local(expandRules, result);
  expandRules := { _(_path) <- OMPathSelect(path, function) };
  expandRules[1][2][2] := function;// the "function" variable is not expanded above.

  mapping := (mapping /: expandRules);// "/:" has lower precedence than ":=".

  Local(ruleMatched);
  ruleMatched := False;
  If(Type(mapping) = "|",
     [
     mapping := Flatten(mapping, "|");
     ForEach(rule, mapping)
       If(Not ruleMatched,
          [
          If(Type(rule) = "_",
             If( Eval(rule[2]), [ result := rule[1]; ruleMatched := True; ] ),
             [ result := rule; ruleMatched := True; ]
            );
          ]
         );
     ],
     [
     If(Type(mapping) = "_",
        If(Eval(mapping[2]),
           result := mapping[1],
           result := FunctionToList(function)
          ),
	result := mapping
       );
     ruleMatched := True;
     ]
    );

  If(ruleMatched,
     If(Type(result) = ":",
        If(Length(result) = 2,
           result[1]:result[2],
           result),// Perhaps we should give a warning here.
        result),
     Empty);
];

11 # OMPathSelect(path_IsNumber, _expression) <--
[
  If(path >= 0 And path <= Length(expression),
     expression[path],
     Undefined);
];
11 # OMPathSelect(path_IsList, _expression) <--
[
  ForEach(i, path)
    If(IsFunction(expression) And i >= 0 And i <= Length(expression),
       expression := expression[i],
       Undefined);
  expression;
];
HoldArgumentNumber("OMPathSelect", 2, 2);

// Previously, any unknown symbols where reported as errors.
// Now, we just store them as OMS(cd, name) since MathPiper is perfectly happy
// with such unknown symbols, and will handle them right: When
// producing an OpenMath result from them, they will be output back
// unmodified, forming a valid OpenMath expression.
// This way we don't have to bother defining bogus symbols for concepts that
// MathPiper does not handle.
100 # ReadOMOBJ(XmlTag("OMS", _attributes, "OpenClose")) <--
[
  OMNextToken();
  Local(omcd, omname);
  omcd   := attributes["CD"];
  omname := attributes["NAME"];
  If(omcd = Empty Or omname = Empty,
     OMCheck(False, "Argument", OMError({"moreerrors", "encodingError"}, PipeToString()Echo("missing \"cd\" or \"name\" attribute: ",attributes))),
     [
     Local(cdTable, piperform);
     cdTable := OMSymbolReverse()[ omcd ];
     If(cdTable != Empty, piperform := cdTable[ omname ]);
     // We can not optimize here by checking first whether the CD is mathpiper
     // and avoiding the table lookup then, because for some symbols the
     // OM name have to be different from the MathPiper name (e.g. "/@").
     If(piperform = Empty,
        If(cd = mathpiper, ToAtom(omname), OMS(omcd, omname)),
        If(IsString(piperform), ToAtom(piperform), piperform));
     ]
    );
];

101 # ReadOMOBJ(_rest) <-- OMCheck(False, "Unimplemented", OMError({"moreerrors", "encodingError"}, PipeToString()Echo("unhandled tag: ",rest)));



///////////////////////////////////////////////////////////////////////
// Error reporting

Macro(OMCheck,{predicate,error})
[
  If(Not(@predicate),
  [
    Assert("omErrorObject", @error) False;
    Check(False, "Undefined", "omErrorObject");
  ]
  ,
  True);
];



OMGetCoreError():=
[
  Local(result);
  result := ExceptionGet(); //todo:tk:verify that ExceptionCheck works properly with the soft error handling functions.
  If(result != False,
     If( IsError("omErrorObject"),
        [result := GetError("omErrorObject");                     ],
        [result := OMError({"moreerrors", "unexpected"}, result); ])
    );
  result;
];



///////////////////////////////////////////////////////////////////////
// Symbol mapping tables

LocalSymbols(omsymbol, omsymbolreverse) [
  // Initialization of the openmath symbol dictionaries
  omsymbol := {};
  omsymbolreverse := {};

  // Access to the dictionaries
  OMSymbol() := omsymbol;
  OMSymbolReverse() := omsymbolreverse;

]; // LocalSymbols(omsymbol, omsymbolreverse)

OMDef(_piperform, omcd_IsString, omname_IsString, _directMapping, _reverseMapping) <--
[
  Local(cdTable);
  If(IsString(piperform),
     OMSymbol()[ piperform ] := {omcd, omname, directMapping, reverseMapping}
     );
  cdTable := OMSymbolReverse()[ omcd ];
  If(cdTable = Empty,
     OMSymbolReverse()[ omcd ] := {{omname, piperform}},
     [
       Local(oldMathPiperform);
       oldMathPiperform := cdTable[ omname ];
       If(oldMathPiperform = Empty,
          cdTable[ omname ] := piperform,
          [
          If(oldMathPiperform != piperform,
             [
             cdTable[ omname ] := piperform;
             Echo("Warning: the mapping for ", omcd, ":", omname,
                  " was already defined as ", oldMathPiperform,
                  ", but is redefined now as ", piperform
                  );
             ]
            );
          ]
         );
      ]
     );
  True;
];

OMDef(_piperform, omcd_IsString, omname_IsString)
<-- OMDef(piperform, omcd, omname, {}, {});

OMDef(piperalias_IsString, pipername_IsString) <--
[
  OMSymbol()[ piperalias ] := OMSymbol()[ pipername ];
];
HoldArgumentNumber("OMDef", 5, 4);
HoldArgumentNumber("OMDef", 5, 5);

// Many objects, such as matrices and sets, do not have a specific
// encoding in MathPiper, but are represented as lists.
OMDef( {},     "set1","emptyset" );
OMDef( "List", "set1","set"      );
OMDef( "List", "linalg2","matrix"    );
OMDef( "List", "linalg2","matrixrow" );
OMDef( "List", "linalg2","vector"    );
OMDef( "List", "list1","list" );

// [20010916 AGP] I couldn't find these symbols in the def files:
//     "E"        ,  "nums1", "e"
//     "Gamma"    ,  "nums1", "gamma"
OMDef( "Infinity" ,  "nums1", "infinity" );
OMDef( "Undefined",  "nums1", "NaN"      );
// [20010916 AGP] From initialization.rep/stdopers.mpi:
OMDef( "And"   ,  "logic1", "and"        );
OMDef( "=="    ,  "logic1", "equivalent" );
OMDef( "!=="   ,  "logic1", "not",
                { "<OMA><OMS cd=\"logic1\" name=\"equivalent\"/>",
                  1,
                  2,
                  "</OMA>"
                }
      );
OMDef( "False",  "logic1", "false" );
OMDef( "Or"   ,  "logic1", "or"    );
OMDef( "True" ,  "logic1", "true"  );
//[20010916 AGP ] Xor is not available in MathPiper.
//     "Xor"  ,  "logic1", "xor"   );
OMDef( "&" ,  mathpiper, "bitwise_and" );
OMDef( "|" ,  mathpiper, "bitwise_or"  );
OMDef( "%" ,  mathpiper, "bitwise_xor" );
OMDef( "/" , "arith1", "divide");// This definition is for OM arith1:divide to MathPiper. In all other cases, the next one will be used.
OMDef( "/" , "nums1", "rational", {$, _1, _2}_(IsRational(_1/_2)) | {OMS("arith1", "divide"), _1, _2}, {/, _1, _2});
OMDef( "-" ,  "arith1", "unary_minus");
OMDef( "-" ,  "arith1", "minus"  );// We need a way of testing the arity.
OMDef( "+" ,  "arith1", "plus"   );
OMDef( "^" ,  "arith1", "power"  );
OMDef( "*" ,  "arith1", "times"  );


LoadScriptOnce("constants.rep/om.mpi");
LoadScriptOnce("stdfuncs.rep/om.mpi");
LoadScriptOnce("stubs.rep/om.mpi");
LoadScriptOnce("logic.rep/om.mpi");
LoadScriptOnce("complex.rep/om.mpi");
LoadScriptOnce("integrate.rep/om.mpi");
LoadScriptOnce("sums.rep/om.mpi");
LoadScriptOnce("limit.rep/om.mpi");
//LoadScriptOnce("numbers.rep/om.mpi");// Sqrt is loaded before (stubs.rep) than IntNthRoot.
LoadScriptOnce("functional.rep/om.mpi");


%/mathpiper



%mathpiper_docs,name="OMForm;OMRead",categories="User Functions;Input/Output"
*CMD OMForm --- convert MathPiper expression to OpenMath
*CMD OMRead --- convert expression from OpenMath to MathPiper expression
*STD
*CALL
	OMForm(expression)
	OMRead()

*PARMS

{expression} -- expression to convert

*DESC

{OMForm} prints an OpenMath representation of the input parameter {expression}
to standard output. {OMRead} reads an OpenMath expression from standard
input and returns a normal MathPiper expression that matches the input OpenMath
expression.

If a MathPiper symbol does not have a mapping defined by {OMDef}, it is translated
to and from OpenMath as the OpenMath symbol in the CD "mathpiper" with the same
name as it has in MathPiper.

*E.G. notest

In> str:=PipeToString()OMForm(2+Sin(a*3))
Result: "<OMOBJ>
	  <OMA>
	    <OMS cd="arith1" name="plus"/>
	    <OMI>2</OMI>
	    <OMA>
	      <OMS cd="transc1" name="sin"/>
	      <OMA>
	        <OMS cd="arith1" name="times"/>
	        <OMV name="a"/>
	        <OMI>3</OMI>
	      </OMA>
	    </OMA>
	  </OMA>
	</OMOBJ>
	";
In> PipeFromString(str)OMRead()
Result: 2+Sin(a*3);

In> OMForm(NotDefinedInOpenMath(2+3))
	<OMOBJ>
	  <OMA>
	    <OMS cd="mathpiper" name="NotDefinedInOpenMath"/>
	    <OMA>
	      <OMS cd="arith1" name="plus"/>
	      <OMI>2</OMI>
	      <OMI>3</OMI>
	    </OMA>
	  </OMA>
	</OMOBJ>
Result: True

*SEE XmlTokenizer, XmlExplodeTag, OMDef
%/mathpiper_docs



%mathpiper_docs,name="OMDef",categories="User Functions;Input/Output"
*CMD OMDef --- define translations from MathPiper to OpenMath and vice-versa.
*STD
*CALL
	OMDef(mathpiperForm, cd, name)
	OMDef(mathpiperForm, cd, name, mathpiperToOM)
	OMDef(mathpiperForm, cd, name, mathpiperToOM, omToMathPiper)

*PARMS

{mathpiperForm} -- string with the name of a MathPiper symbol, or a MathPiper expression

{cd} -- OpenMath Content Dictionary for the symbol

{name} -- OpenMath name for the symbol

{mathpiperToOM} -- rule for translating an application of that symbol in MathPiper into an OpenMath expression

{omToMathPiper} -- rule for translating an OpenMath expression into an application of this symbol in MathPiper

*DESC

{OMDef} defines the translation rules for symbols between the MathPiper
representation and {OpenMath}.
The first parameter, {mathpiperForm}, can be a string or an expression. The
difference is that when giving an expression only the {omToMathPiper} translation
is defined, and it uses the exact expression given. This is used for {OpenMath}
symbols that must be translated into a whole subexpression in MathPiper, such
as {set1:emptyset} which gets translated to an empty list as follows:
In> OMDef( {}, "set1","emptyset" )
Result: True
In> PipeFromString("<OMOBJ><OMS cd=\"set1\" name=\"emptyset\"/></OMOBJ> ")OMRead()
Result: {}
In> IsList(%)
Result: True
Otherwise, a symbol that is not inside an application (OMA) gets translated to
the MathPiper atom with the given name:
In> OMDef( "EmptySet", "set1","emptyset" )
	Warning: the mapping for set1:emptyset was already defined as {} , but is redefined now as EmptySet
Result: True
In> PipeFromString("<OMOBJ><OMS cd=\"set1\" name=\"emptyset\"/></OMOBJ> ")OMRead()
Result: EmptySet

The definitions for the symbols in the MathPiper
library are in the {*.rep} script subdirectories. In those modules for which
the mappings are defined, there is a file called {om.ys} that contains the
{OMDef} calls. Those files are loaded in {openmath.rep/om.ys}, so any new
file must be added to the list there, at the end of the file.

A rule is represented as a list of expressions. Since both OM and
MathPiper expressions are actually lists, the syntax is the same in both
directions. There are two template forms that are expanded before the
translation:

*	{$}: this symbol stands for the translation of the symbol applied
in the original expression.

*	{_path}: a path into the original expression (list) to extract an
element, written as an underscore applied to an integer or a list of integers.
  Those integers are indexes into expressions, and integers in a list are
  applied recursively starting at the original expression.
  For example, {_2} means the second parameter of the expression, while
  {_{3,2,1}} means the first parameter of the second parameter of the third
  parameter of the original expression.

They can appear anywhere in the rule as expressions or subexpressions.

Finally, several alternative rules can be specified by joining them with
the {|} symbol, and each of them can be annotated with a post-predicate
applied with the underscore {_} symbol, in the style of MathPiper' simplification
rules. Only the first alternative rule that matches is applied, so the more
specific rules must be written first.

There are special symbols recognized by {OMForm} to output {OpenMath}
constructs that have no specific parallel in MathPiper, such as an OpenMath
symbol having a {CD} and {name}: MathPiper symbols have only a name.
Those special symbols are:

*	{OMS(cd, name)}: {<OMS cd="cd" name="name">}
*	{OMA(f x y ...)}: {<OMA>f x y ...</OMA>}
*	{OMBIND(binderSymbol, bvars, expression)}: {<OMBIND>binderSymbol bvars expression</OMBIND>}, where {bvars} must be produced by using {OMBVAR(...)}.
*	{OMBVAR(x y ...)}: {<OMBVAR>x y ...</OMBVAR>}
*	{OME(...)}: {<OME>...</OME>}

When translating from OpenMath to MathPiper, we just store unknown symbols as
{OMS("cd", "name")}. This way we don't have to bother defining bogus symbols
for concepts that MathPiper does not handle, and we can evaluate expressions that
contain them.

*E.G. notest

In> OMDef( "Sqrt" ,  "arith1", "root", { $, _1, 2 }, $(_1)_(_2=2) | (_1^(1/_2)) );
Result: True
In> OMForm(Sqrt(3))
	<OMOBJ>
	  <OMA>
	    <OMS cd="arith1" name="root"/>
	    <OMI>3</OMI>
	    <OMI>2</OMI>
	  </OMA>
	</OMOBJ>
Result: True
In> PipeFromString("<OMOBJ><OMA><OMS cd=\"arith1\" name=\"root\"/><OMI>16</OMI><OMI>2</OMI></OMA></OMOBJ> ")OMRead()
Result: Sqrt(16)
In> PipeFromString("<OMOBJ><OMA><OMS cd=\"arith1\" name=\"root\"/><OMI>16</OMI><OMI>3</OMI></OMA></OMOBJ> ")OMRead()
Result: 16^(1/3)

In> OMDef("Limit", "limit1", "limit", \
	      {  $, _2, OMS("limit1", "under"), OMBIND(OMS("fns1", "lambda"), OMBVAR(_1), _4) }_(_3=Left)  \
	      |{ $, _2, OMS("limit1", "above"), OMBIND(OMS("fns1", "lambda"), OMBVAR(_1), _4) }_(_3=Right) \
	      |{ $, _2, OMS("limit1", "both_sides"), OMBIND(OMS("fns1", "lambda"), OMBVAR(_1), _3) },      \
	      { $, _{3,2,1}, _1, Left,  _{3,3}}_(_2=OMS("limit1", "below")) \
	      |{$, _{3,2,1}, _1, Right, _{3,3}}_(_2=OMS("limit1", "above")) \
	      |{$, _{3,2,1}, _1, _{3,3}}                                    \
	     );
In> OMForm(Limit(x,0) Sin(x)/x)
	<OMOBJ>
	  <OMA>
	    <OMS cd="limit1" name="limit"/>
	    <OMI>0</OMI>
	    <OMS cd="limit1" name="both_sides"/>
	    <OMBIND>
	      <OMS cd="fns1" name="lambda"/>
	      <OMBVAR>
	        <OMV name="x"/>
	      </OMBVAR>
	      <OMA>
	        <OMS cd="arith1" name="divide"/>
	        <OMA>
	          <OMS cd="transc1" name="sin"/>
	          <OMV name="x"/>
	        </OMA>
	        <OMV name="x"/>
	      </OMA>
	    </OMBIND>
	  </OMA>
	</OMOBJ>
Result: True
In> OMForm(Limit(x,0,Right) 1/x)
	<OMOBJ>
	  <OMA>
	    <OMS cd="limit1" name="limit"/>
	    <OMI>0</OMI>
	    <OMS cd="limit1" name="above"/>
	    <OMBIND>
	      <OMS cd="fns1" name="lambda"/>
	      <OMBVAR>
	        <OMV name="x"/>
	      </OMBVAR>
	      <OMA>
	        <OMS cd="arith1" name="divide"/>
	        <OMI>1</OMI>
	        <OMV name="x"/>
	      </OMA>
	    </OMBIND>
	  </OMA>
	</OMOBJ>
Result: True
In> PipeFromString(PipeToString()OMForm(Limit(x,0,Right) 1/x))OMRead()
Result: Limit(x,0,Right)1/x
In> %
Result: Infinity

*SEE OMForm, OMRead
%/mathpiper_docs


