[![Build Status](https://travis-ci.org/rocky/FoxySheep2.svg?branch=master)](https://travis-ci.org/rocky/FoxySheep2)

<!-- markdown-toc start - Don't edit this section. Run M-x markdown-toc-refresh-toc -->
**Table of Contents**

- [FoxySheep2](#foxysheep2)
- [Examples](#examples)
- [Conversion to Python](#conversion-to-python)
- [Regenerating the lexer/parser](#regenerating-the-lexerparser)
    - [InputForm and FullForm must Subclass Lexer](#inputform-and-fullform-must-subclass-lexer)

<!-- markdown-toc end -->
# FoxySheep2

This is the Robert Jacobson's Python implementation of the FoxySheep parser and lexer for Mathematica.
It has been stripped down and reorganized a bit.

In particular, the Java code has been removed. For that see the original [FoxySheep](https://github.com/rljacobson/FoxySheep) code base.

You need Python 3.6 or later to run this.

There are various `Makefile` targets for:

* setting up to run from the source tree
* running a canned set of Mathematics translations
* running an interactive translation session
* (re)-generating Python files from the grammar

To see a list of the target names for each of the above run `remake --tasks`.

# Examples

When installed, the command-line translator is called `foxy-sheep`
which can convert [Mathematica InputForm](https://reference.wolfram.com/language/ref/InputForm.html) to [Mathematica FullForm](https://reference.wolfram.com/language/ref/FullForm.html)
having Mathematica installed: To run the code interactively:

```
$ foxy-sheep
Enter a Mathematica expression. Enter either an empty line, Ctrl-C, or Ctrl-D to exit.
in:= 1+2
Plus[1,2]
in:=D[4 x^2]
D[Times[4,Power[x,2]]]
in:=
$
```

The first few examples from ["Fast Introduction for Math Students"](https://www.wolfram.com/language/fast-introduction-for-math-students/en/entering-input/)

```
foxy-sheep -o python
Enter a Mathematica expression. Enter either an empty line, Ctrl-C, or Ctrl-D to exit.
in:= 2 + 2
(2 + 2)

Out[0]=4
in:= 1 + 2 + 3
(1 + 2 + 3)

Out[1]=6
in:= % + 4
(Out() + 4)

Out[2]=10
in:= 5 + 2 * 3 - 7.5
(5 + 2 * 3 -
decimal.Decimal(7.5))

in:= ((5 - 3) ^ (1 + 2))/ 4
((5 - 3) ** (1 + 2) / 4)

Out[4]=2.0
in:= GCD[12,15]
math.gcd(12, 15)

Out[5]=3
in:= {1, 2, 3}
[1, 2, 3]

Out[6]=[1, 2, 3]
```

To call from Python:

```python

from FoxySheep import if2ff, if2python
print(if2ff("x^2-3x+4")
# Prints: Plus[Power[x,2],Times[-1,3,x],4]
print(if2ff("x^2-3x+4")
# Prints (x ** 2 + -1 * 3)
```


For help on `foxy-sheep`, run `foxy-sheep --help`.

To see a demo run `python demo.py` in this directory.

# Conversion to Python

A very crude translator to Python has been started. While there are still a lot of details to be filled out, some of the basics are there.

Conversion to Python is done via transforming "InputForm" input to an "OutputForm" parse tree, and then using that to convert to a Python AST. Finally a module is used to dump this to text.

Why do we go the more complicated route of going from a an OutForm parse tree to a Python AST?

By keeping the structure as an AST we can contemplate more powerful transformations and make use of routines that exist for working with Python AST's.

For example, translating `{1,2,3} + 2` into Python, while not handled now, can be done by looking at the types of the operands of plus, and noticing one is a scalar while the other is a list.

# Regenerating the lexer/parser

To change the grammar you'll need the ANTLR Parser Generator (`antlr4)`, version 4.7.x  installed. If you don't want to change the grammar but just run the code, the distribution and git repository contain the antlr-derived Python files.

To (re)generate the lexer/parser:

```bash
$ make
```

The resulting files are placed in `FoxySheep/generated`.

Files generated by ANTLR4 are assumed to be in a subdirectory called `generated` containing an empty `__init__.py` file. See the Makefile for details.

## InputForm and FullForm must Subclass Lexer

In order for the generated antlr4 lexer to work we need to patch the generated Python lexers. The patch files do this.
The Makefile target for `FoxySheepParser.py` contains the `patch` command.

If patching is not done you'll get an `AttributeError` exception in the lexer when you try to run it such as through `foxy-sheep`.

An example error message looks like:

```
AttributeError: 'FoxySheepLexer' object has no attribute 'checkAdditiveOp'
```
