2 * Introduction to pytranslate::
3 * Functions in pytranslate::
4 * Extending pytranslate::
7 @node Introduction to pytranslate, Functions in pytranslate, , Package pytranslate
8 @section Introduction to pytranslate
10 @code{pytranslate} package provides Maxima to Python translation functionality. The package is experimental, and the specifications of the functions in this package might change. It was written as a Google Summer of Code project by Lakshya A Agrawal (Undergraduate Student, IIIT-Delhi) in 2019. A detailed project report is available as a @url{https://gist.github.com/LakshyAAAgrawal/33eee2d33c4788764087eef1fa67269e, GitHub Gist}.@*
12 The package needs to be loaded in a Maxima instance for use, by executing @code{load("pytranslate");}@*
13 The statements are converted to python3 syntax. The file pytranslate.py must be imported for all translations to run, as shown in example.
17 @c load ("pytranslate")$
18 @c /* Define an example function to calculate factorial */
19 @c pytranslate(my_factorial(x) := if (x = 1 or x = 0) then 1
20 @c else x * my_factorial(x - 1));
24 (%i1) load ("pytranslate")$
26 /* Define an example function to calculate factorial */
27 (%i2) pytranslate(my_factorial(x) := if (x = 1 or x = 0) then 1
28 else x * my_factorial(x - 1));
30 def my_factorial(x, v = v):
33 return((1 if ((v["x"] == 1) or (v["x"] == 0)) \
34 else (v["x"] * my_factorial((v["x"] + (-1))))))
35 m["my_factorial"] = my_factorial
38 (%i3) my_factorial(5);
42 >>> from pytranslate import *
43 >>> def my_factorial(x, v = v):
44 ... v = Stack(@{@}, v)
45 ... v.ins(@{"x" : x@})
46 ... return((1 if ((v["x"] == 1) or (v["x"] == 0)) \
47 ... else (v["x"] * my_factorial((v["x"] + (-1))))))
54 The Maxima to Python Translator works in two stages:@*
55 1. Conversion of the internal Maxima representation to a defined Intermediate Representation, henceforth referred as IR(mapping is present in @file{share/pytranslate/maxima-to-ir.html})@*
56 2. The conversion of IR to Python.
58 Supported Maxima forms:@*
59 1. @mref{Numbers}(including complex numbers)@*
60 2. @mref{Assignment operators}@*
61 3. @mref{Arithmetic operators}(+, -, *, ^, /, !)@*
62 4. @mref{Logical operators}(and, or, not)@*
63 5. @mref{Relational operators}(@code{>}, @code{<}, @code{>=}, @code{<=}, @code{!=}, @code{==})@*
67 9. @mref{Function} and function calls@*
68 10. @mref{if}-else converted to Python conditionals@*
69 11. @mref{for} loops@*
70 12. @mref{lambda} form
72 @subsection Tests for pytranslate
73 The tests for @code{pytranslate} are present at @file{share/pytranslate/rtest_pytranslate.mac} and can be run by executing @code{batch(rtest_pytranslate, test);}
75 @node Functions in pytranslate, Extending pytranslate, Introduction to pytranslate, Package pytranslate
76 @section Functions in pytranslate
78 @deffn {Function} pytranslate (@var{expr}, [print-ir])
79 Translates the expression @var{expr} to equivalent python3 statements. Output is printed in the stdout.
83 @c load ("pytranslate")$
84 @c pytranslate('(for i:8 step -1 unless i<3 do (print(i))));
87 (%i1) load ("pytranslate")$
89 (%i2) pytranslate('(for i:8 step -1 unless i<3 do (print(i))));
92 while not((v["i"] < 3)):
94 v["i"] = (v["i"] + -1)
99 @var{expr} is evaluated, and the return value is used for translation. Hence, for statements like assignment, it might be useful to quote the statement:
101 @c load ("pytranslate")$
102 @c pytranslate(x:20);
103 @c pytranslate('(x:20));
106 (%i1) load ("pytranslate")$
108 (%i2) pytranslate(x:20);
113 (%i3) pytranslate('(x:20));
119 Passing the optional parameter (@var{print-ir}) to @code{pytranslate} as t, will print the internal IR representation of @code{expr} and return the translated python3 code.
122 @c load("pytranslate");
123 @c pytranslate('(plot3d(lambda([x, y], x^2+y^(-1)), [x, 1, 10],
128 (%i1) load("pytranslate");
132 (%i2) pytranslate('(plot3d(lambda([x, y], x^2+y^(-1)), [x, 1, 10],
135 (funcall (element-array "m" (string "plot3d"))
137 ((symbol "x") (symbol "y")
141 (funcall (symbol "stack") (dictionary) (symbol "v"))))
143 (funcall (element-array (symbol "m") (string "pow"))
144 (symbol "x") (num 2 0))
145 (funcall (element-array (symbol "m") (string "pow"))
146 (symbol "y") (unary-op - (num 1 0)))))
147 (struct-list (string "x") (num 1 0) (num 10 0))
148 (struct-list (string "y") (num 1 0) (num 10 0))))
150 m["plot3d"](lambda x, y, v = Stack(@{@}, v): (m["pow"](x, 2) + m["\
151 pow"](y, (-1))), ["x", 1, 10], ["y", 1, 10])
156 @deffn {Function} show_form (@var{expr})
157 Displays the internal maxima form of @code{expr}
160 (%i4) show_form(a^b);
167 @node Extending pytranslate, , Functions in pytranslate, Package pytranslate
168 @section Extending pytranslate
169 Working of pytranslate:
172 The entry point for pytranslate is the function @code{$pytranslate} defined in @file{share/pytranslate/pytranslate.lisp}.
174 @code{$pytranslate} calls the function @code{maxima-to-ir} with the Maxima expression as an argument(henceforth referred as @code{expr}).
176 @code{maxima-to-ir} determines if @code{expr} is atomic or non-atomic(lisp cons form). If atomic, @code{atom-to-ir} is called with @code{expr} which returns the IR for the atomic expression.@*
177 To define/modify translation for atomic expressions, make changes to the definition of @code{atom-to-ir} in accordance with the IR.
179 If @code{expr} is non-atomic, the function @code{cons-to-ir} is called with @code{expr} as an argument.@*
182 @code{cons-to-ir} looks for @code{(caar expr)} which specifies the type of @code{expr}, in hash-table @var{*maxima-direct-ir-map*} and if the type is found, then appends the retrieved IR with the result of lisp call @code{(mapcar #'maxima-to-ir (cdr expr))}, which applies maxima-to-ir function to all the elements present in the list. Effectively, recursively generate IR for all the elements present in @code{expr} and append them to the IR map for the type.@*
186 (%i9) show_form(a+b);
190 (%i10) pytranslate(a+b, t);
191 (body (op + (element-array (symbol "v") (string "b")) \
192 (element-array (symbol "v") (string "a"))))
197 Here, operator + with internal maxima representation, @code{(mplus)} is present in @var{*maxima-direct-ir-map*} and mapped to @code{(op +)} to which the result of generating IR for all other elements of the list (a b), i.e. @code{(ELEMENT-ARRAY (SYMBOL "v") (STRING "b")) (ELEMENT-ARRAY (SYMBOL "v") (STRING "a"))} is appended.@*
200 If @code{(caar expr)} is not found in @var{*maxima-direct-ir-map*}, then @code{cons-to-ir} looks for the type in @var{*maxima-special-ir-map*} which returns the function to handle the translation of the type of @code{expr}. @code{cons-to-ir} then calls the returned function with argument @code{expr} as an argument.@*
204 (%i11) show_form(g(x) := x^2);
205 ((mdefine simp) (($g) $x) ((mexpt) $x 2))
208 (%i12) pytranslate(g(x):=x^2, t);
211 (func-def (symbol "g")
212 ((symbol "x") (op-no-bracket = (symbol "v") (symbol "v")))
214 (op-no-bracket = (symbol "v") (funcall (symbol "stack") \
215 (dictionary) (symbol "v")))
216 (obj-funcall (symbol "v") (symbol "ins") (dictionary \
217 ((string "x") (symbol "x"))))
218 (funcall (symbol "return")
219 (funcall (element-array (symbol "f") (string "pow"))
220 (element-array (symbol "v") (string "x"))
222 (op-no-bracket = (element-array (symbol "f") (string "g")) \
228 return(f["pow"](v["x"], 2))
233 Here, @code{mdefine}, which is the type of @code{expr} is present in @var{*maxima-special-ir-map*} which returns @code{func-def-to-ir} as handler function, which is then called with @code{expr} to generate the IR.@*
234 To define/modify translation for a type, add an entry to @var{*maxima-direct-ir-map*} if only a part of the IR needs to be generated and the rest can be appended, otherwise, for complete handling of @code{expr}, add an entry to @var{*maxima-special-ir-map*} and define a function with the name defined in @var{*maxima-special-ir-map*} which returns the IR for the form. The function naming convention for ir generators is (type)-to-ir, where type is the @code{(caar expr)} for expression(@code{mdefine -> func-def-to-ir}). The function must return a valid IR for the specific type.
237 After the generation of IR, the function @code{ir-to-python} is called with the generated @code{ir} as an argument, which performs the codegen in a recursive manner.
240 @code{ir-to-python} looks for lisp @code{(car ir)} in the hash-table @var{*ir-python-direct-templates*}, which maps IR type to function handlers and calls the function returned with @code{ir} as an argument.
243 To extend the IR of pytranslate, define a function with the naming convention (type)-to-python and add the name to @var{*ir-python-direct-templates*}.