/py14

Python to C++ 14 transpiler

Primary LanguageC++MIT LicenseMIT

Python to C++ 14 transpiler

Wercker Status Coverage Status Scrutinizer Code Quality Code Health Dependency Status

Try it out online: http://py14.lukasmartinelli.ch/ [The website seems to be down]

This is a fork of a little experiment that shows how far you can go with the C++ 14 auto return type and templates. This for is based on Lukas Martinelli https://github.com/lukasmartinelli/py14 C++14 has such powerful type deduction that it is possible to transpile Python into C++ without worrying about the missing type annotations in python. Only a small subset of Python is working and you will encounter many bugs. The goal is to showcase the power of C++14 templates and not to create a fully functional transpiler.

This fork aims at adding the latest C++ capabilities to

Example

Original Python version.

def factorial(num):
    if num <= 1:
        return num
    return factorial(num-1) * num

Transpiled C++ template.

template <typename T1> auto factorial(T1 num) {
  if (num <= 1) {
    return num;
  }
  return factorial(num - 1) * num;
}

Some other notable transpiler projects :

https://github.com/shedskin/shedskin Shedskin is an experimental (restricted-Python)-to-C++ compiler written in C++. The project was 75 non-trivial programs and development appears to have stalled.

https://github.com/konchunas/pyrs Python to Rust transpiler. This work is derived from py14.

How it works

Consider a map implementation.

def map(values, fun):
    results = []
    for v in values:
        results.append(fun(v))
    return results

This can be transpiled into the following C++ template.

template <typename T1, typename T2>
auto map(T1 values, T2 fun) {
    std::vector<decltype(
        fun(std::declval<typename decltype(values)::value_type>()))> results{};
    for (auto v : values) {
        results.push_back(fun(v));
    }
    return results;
}

The parameters and the return types are deduced automatically In order to define the results vector we need to:

  1. Deduce the type for v returned from the values range using v_type = typename decltype(values)::value_type
  2. Deduce the return type of fun for call with parameter v decltype(fun(v))
  3. Because we dont know v at the time of definition we need to fake it std::declval<v_type>()
  4. This results in the fully specified value type of the results vector decltype(fun(std::declval<typename decltype(values)::value_type>()))

Trying it out

Requirements:

  • clang 3.5

Transpiling:

./py14.py fib.py > fib.cpp

Compiling:

clang++ -Wall -Wextra -std=c++14 -Ipy14/runtime fib.cpp

Run regression tests:

cd regtests
make

Run tests

pip install -r requirements.txt
py.test --cov=py14

More Examples

Probability Density Function (PDF)

def pdf(x, mean, std_dev):
    term1 = 1.0 / ((2 * math.pi) ** 0.5)
    term2 = (math.e ** (-1.0 * (x-mean) ** 2.0 / 2.0 * (std_dev ** 2.0)))
    return term1 * term2
template <typename T1, typename T2, typename T3>
auto pdf(T1 x, T2 mean, T3 std_dev) {
  auto term1 = 1.0 / std::pow(2 * py14::math::pi, 0.5);
  auto term2 = std::pow(py14::math::e, -1.0 * std::pow(x - mean, 2.0) / 2.0 *
                                           std::pow(std_dev, 2.0));
  return term1 * term2;
}

Fibonacci

def fib(n):
    if n == 1:
        return 1
    elif n == 0:
        return 0
    else:
        return fib(n-1) + fib(n-2)
template <typename T1> auto fib(T1 n) {
  if (n == 1) {
    return 1;
  } else {
    if (n == 0) {
      return 0;
    } else {
      return fib(n - 1) + fib(n - 2);
    }
  }
}

Bubble Sort

def sort(seq):
    L = len(seq)
    for _ in range(L):
        for n in range(1, L):
            if seq[n] < seq[n - 1]:
                seq[n - 1], seq[n] = seq[n], seq[n - 1]
    return seq
template <typename T1> auto sort(T1 seq) {
  auto L = seq.size();
  for (auto _ : rangepp::range(L)) {
    for (auto n : rangepp::range(1, L)) {
      if (seq[n] < seq[n - 1]) {
        std::tie(seq[n - 1], seq[n]) = std::make_tuple(seq[n], seq[n - 1]);
      }
    }
  }
  return seq;
}

Working Features

Only bare functions using the basic language features are supported.

  • classes
  • functions
  • lambdas
  • multiple inheritance
  • operator overloading
  • function and class decorators
  • getter/setter function decorators
  • list comprehensions (ListComp)
  • dict comprehensions (DictComp)
  • yield (generator functions)
  • function calls with *args and **kwargs

Language Keywords

  • global, nonlocal
  • while, for, continue, break
  • if, elif, else
  • try, except, raise
  • def, lambda
  • new, class
  • from, import, as
  • pass, assert
  • and, or, is, in, not
  • return
  • yield

Builtins

  • dir
  • type
  • hasattr
  • getattr
  • setattr
  • issubclass
  • isinstance
  • dict
  • list
  • tuple
  • int
  • float
  • str
  • f-strings (JoinedStr)
  • round
  • range
  • sorted
  • sum
  • len
  • map
  • filter
  • min
  • max
  • abs
  • ord
  • chr
  • open

Data Structures

  • list
  • Set
  • String
  • Dict
  • OrderedDict
  • deque

Python standard library modules

  • datetime
  • re (regex matching)
  • collections
  • random
  • os.path
  • logging
  • time
  • deque
  • queue
  • multiprocessing
  • threading
  • asyncio
  • socket

Python libraries

For developers

You can then install the package in “editable” mode by running from the same directory:

pip install -e .

To run the tests execute the following

pytest --pyargs py14 

For gentle information on how to get started with parsing Python code take a look at https://greentreesnakes.readthedocs.io/en/latest/index.html