HPUNIX Сайт о ОС и не только!

Berp Восемь тыщ двести двенадцать достаточно увлекательный компилятор Python

15 августа 2008 - unix
Berp Восемь тысяч двести двенадцать довольно интересный компилятор Python

Не так давно я натолкнулся на один любознательный проект. Он именуется Berp и представляет собой транслятор скриптов на языке Python в программки на Haskell. Со стороны юзера Berp смотрится как интерпретатор и компилятор Python, так как вся трансляция происходит «в бэкенде».

***

Проект довольно молод. Чтоб испытать Berp, необходимо без помощи других собрать его из исходников. Сами исходники лежат на гитхабе. Сборка происходит приблизительно таким макаром:

cabal update
cabal install haskell-src-exts language-python parseargs
cabal install MonadCatchIO-mtl

cd path/to/unzipped/bjpop-berp-63eb4a0/libs
cabal configure
cabal install

cd ../compiler
cabal configure
cabal build
cd dist/build/berp

Не могу не отметить, что компилировать программки на Haskell — одно наслаждение. Никакой возни с настройкой программки под свою систему, поиском библиотек либо разными версиями компиляторов. Кто пробовал собирать в Visual Studio программки, написанные под GCC, тот соображает, о чем речь.

Итак, в итоге обрисованных выше шагов, мы получили компилятор berp.exe (я тестировал Berp под Windows). Сейчас попробуем скомпилировать с его помощью простой скрипт:

#!/usr/bin/env python

print("Hello!")

Говорим:

berp test.py
test.exe

Должны узреть:

Hello!

Размер приобретенного исполняемого файла составил Три Мб. Много, естественно, но жить можно. В экзешнике был увиден обычный для Haskell «мусор», который можно вычистить при помощи утилиты strip. (Подробнее о тюнинге программ на Haskell можно прочесть в заметке, посвященной wxHaskell.) В таблице импорта никаких излишних библиотек не обнаружилось.

Кроме самой программки, Berp также сделал два файла последующего содержания. Файл Main.hs:

module Main where
import Prelude ()
import Berp.Base (run)
import Berp_test (init)
main = run init

Файл Berp_test.hs:

module Berp_test (init) where
import Prelude ()
import Berp.Base
init globals
  = do _t_0 <- readGlobal globals (177724922, "_s_print")
       _t_0 @@ [string "Hello!"]

Как я могу судить, использовать этот код в собственных программках на Haskell будет проблемно.

***

Здорово, естественно, что таковой простой пример удачно собрался, но ведь нас с вами обычно заинтересовывают программки посложнее, не так ли? Поначалу я желал написать скрипт, выводящий квадратные корешки чисел от Один до 100, но выяснилось, что питоновский модуль math написан на Си.

Таким макаром, не ясно, как его использовать в Berp. Мы даже не можем пользоваться модулями PyPy, хотя, как я понимаю, они все написаны без использования Си. Дело в том, что PyPy — это реализация Python 2.7, а Berp соображает только Python 3. В неких отношениях различия меж этими языками достаточно существенны.

Тогда я сваял последующий пример. Файл mymodule.py:

#!/usr/bin/env python

def getHello():
  return("Hello from mymodule!")

Файл test.py:

#!/usr/bin/env python

from mymodule import getHello

print(getHello())

Этот пример удачно компилируется, но при запуске выдает ошибку:

test.exe: Berp_mymodule.hs:6:19-61: Non-exhaustive patterns in lambda

Если кому любопытно, файл Berp_mymodule.hs:

module Berp_mymodule (init) where
import Prelude ()
import Berp.Base
init globals
  = do _t_0 <- def Нуль none
                 (\ [] -> ret (string "Hello from mymodule!"))
       writeGlobal globals (9072933, "_s_getHello") _t_0

Файл Berp_test.hs:

module Berp_test (init) where
import Prelude ()
import Berp.Base
import qualified Berp_mymodule (init)
init globals
  = do _t_0 <- importModule "mymodule" Berp_mymodule.init
       _t_1 <- _t_0 . (9072933, "_s_getHello")
       writeGlobal globals (9072933, "_s_getHello") _t_1
       _t_2 <- readGlobal globals (177724922, "_s_print")
       _t_3 <- readGlobal globals (9072933, "_s_getHello")
Berp Восемь тысяч двести двенадцать довольно интересный компилятор Python
       _t_4 <- _t_3 @@ []
       _t_2 @@ [_t_4]

Main.hs остался таким же, как и в прошлом примере.

***

Ок, с импортом вышел маленький косяк. Но как Berp работает с остальными конструкциями языка? Чтоб узнать это, необходимо испытать собрать какую-нибудь реальную и при всем этом достаточно сложную программку.

На разум ничего неплохого не пришло, так что я глупо переписал (в который раз) программку, решающую задачку о кодировке цифр. Раздельно пришлось повозиться с допиливанием скрипта под Python 3. В конечном итоге вышло последующее:

#!/usr/bin/env python

# (c) Alexandr A Alexeev Две тыщи одиннадцать | http://eax.me/

from functools import reduce

def nub(lst):
  seen = set()
  rslt = list()
  for itm in lst:
    if itm not in seen:
      seen.add(itm)
      rslt.append(itm)
  return rslt
 
def nub2(lst): # lists are not hashable, but tuples are
  tmp = nub( list(map(lambda x: tuple(x), lst)) )
  return list(map(lambda x: list(tmp), tmp))

def signalsNumber(sigSet):
  return len(sigSet)

def bitsNumber(sigSet):
  return len(sigSet[0])
 
def allSignals(bits):
  if bits == 0:
    return [[]]
  rest = allSignals(bits-1)
  return list(map(lambda x: [False] + x, rest)) + \
    list(map(lambda x: [True] + x, rest))

def allDefects(bits):
  return allSignals(bits)

def isCriticalDefect(defect, sigSet):
  defectedSigSet = nub2( list(map(lambda s: \
    list(map(lambda x: x[0] & x[1], zip(s,defect))), sigSet)) )
  return signalsNumber(sigSet) != signalsNumber(defectedSigSet)

def allNoncriticalDefects(sigSet):
  defects = allDefects(bitsNumber(sigSet))
  return list(filter(lambda x: not(isCriticalDefect(x, sigSet)),
                     defects))
 
def solveFirstTask(signSet):
  defects = allNoncriticalDefects(signSet)
  temp = list(map(lambda t: \
    (reduce(lambda x, b: x if b else x + 1, t, 0), [t]), defects))
  return reduce(lambda a, b: \
    ( (a[0], a[1] + b[1]) if b[0] == a[0] else b ) if b[0] >= a[0] \
    else a, temp)
   
def solveSecondTask(signSets):
  solutions = zip( list(map(lambda x: solveFirstTask(x), signSets)), \
    list(map(lambda x: [x], signSets)) )
  return reduce(lambda x, y: \
    ( (x[0], x[1] + y[1]) if x[0][0] == y[0][0] else x) \
    if x[0][0] >= y[0][0] else y, solutions)
 
def intArrToSignalSet(mtx):
  return list(map(lambda lst: list(map(lambda itm: itm != 0, lst)),
                  mtx))
 
def sevenPosSignalSet():
  return intArrToSignalSet([
    [1, 1, 1, 0, 1, 1, 1], # 0
    [0, 0, 1, 0, 0, 1, 0], # 1
    [1, 0, 1, 1, 1, 0, 1], # 2
Berp Восемь тысяч двести двенадцать довольно интересный компилятор Python
    [1, 0, 1, 1, 0, 1, 1], # 3
    [0, 1, 1, 1, 0, 1, 0], # 4
    [1, 1, 0, 1, 0, 1, 1], # 5
    [1, 1, 0, 1, 1, 1, 1], # 6
    [1, 0, 1, 0, 0, 1, 0], # 7
    [1, 1, 1, 1, 1, 1, 1], # 8
    [1, 1, 1, 1, 0, 1, 1], # 9
    ])
   
   
def ninePosSignalSet():
  return intArrToSignalSet([
    [1, 1, 0, 1, 0, 1, 0, 1, 1], # 0
    [0, 0, 1, 1, 0, 0, 0, 1, 0], # 1
    [1, 0, 0, 1, 0, 0, 1, 0, 1], # 2
    [1, 0, 1, 0, 1, 0, 1, 0, 0], # 3
    [0, 1, 0, 1, 1, 0, 0, 1, 0], # 4
    [1, 1, 0, 0, 1, 0, 0, 1, 1], # 5
    [0, 0, 1, 0, 1, 1, 0, 1, 1], # 6
    [1, 0, 1, 0, 0, 1, 0, 0, 0], # 7
    [1, 1, 0, 1, 1, 1, 0, 1, 1], # 8
    [1, 1, 0, 1, 1, 0, 1, 0, 0], # 9
    ])

def unpackIntArrays(mtx):
  return reduce( lambda x, y: \
    [a + [b] for a in x for b in y ], mtx, [[]])
   
def multipleNinePosSignalSets():
  unpacked = unpackIntArrays([
    [[1,1,0,1,0,1,0,1,1]],
    [[0,0,1,1,0,0,0,1,0],[0,0,0,1,0,0,0,1,0],[0,1,0,0,0,1,0,0,0]],
    [[1,0,0,1,0,0,1,0,1],[1,0,0,1,1,1,0,0,1]],
    [[1,0,1,0,1,0,1,0,0],[1,0,0,1,1,0,0,1,1],[1,0,1,0,1,0,0,1,1]],
    [[0,1,0,1,1,0,0,1,0],[0,0,1,1,1,0,0,1,0]],
    [[1,1,0,0,1,0,0,1,1],[1,1,0,0,1,0,1,0,0]],
    [[0,0,1,0,1,1,0,1,1],[1,1,0,0,1,1,0,1,1]],
    [[1,0,1,0,0,1,0,0,0],[1,0,0,1,0,0,0,1,0],[1,0,0,1,0,0,1,0,0]],
    [[1,1,0,1,1,1,0,1,1]],
    [[1,1,0,1,1,0,1,0,0],[1,1,0,1,1,0,0,1,1]]
    ])
  return list(map(lambda x: intArrToSignalSet(x), unpacked))

Необходимо подчеркнуть, что писать в многофункциональном стиле на Python оказалось намного проще и приятнее, чем на Perl. Не считая того, приведенный скрипт достаточно экономично употребляет память («палка» на графике, как и в случае с Haskell) и работает полностью стремительно (хотя и во много раз медлительнее аналогичной программки на Haskell).

К огорчению, мне не удалось отыскать в стандартной библиотеке аналогов функций nub и zipWith. Также для меня остается загадкой, за каким хреном в Python Три перенесли функцию reduce в отдельный пакет. А еще в 3-ем питоне стало труднее использовать функции map и filter, так как заместо списков они сейчас возвращают объекты.

Но вернемся к Berp. Собрать приведенную выше программку он не в состоянии. Вопреки моим надеждам, интегрированной функции reduce в нем не оказалось, а на попытку ее импорта из functools мы получаем:

berp: Python source file not found: functools.py

В конечном итоге reduce пришлось дописать:

def reduce(f, lst, x = None):
  if x is None:
    x = lst[0]
    lst = lst[1:]
   
  for i in lst:
    x = f(x, i)
  return x

Дальше вылез таковой косяк:

berp: berp unsupported. x if b else x + Один

Пытаемся пофиксить и его:

def if_else(c, a, b):
  if c:
    return a
  else:
    return b

И здесь наступает epic fail:

berp: berp unsupported. lst[1:]

Не считая того, выяснилось, что Berp не поддерживает конструкцию «not in»:

berp: berp unsupported. opExp: NotIn {op_annot = SpanCoLinear
{span_filename = "lamp.py", span_row = 9, span_start_column
= 12, span_end_column = 17}}

В общем, кошмар!

***

На момент написания этих строк воспользоваться Berp было нереально. Поддержка синтаксиса Python Три реализована в нем только отчасти. Также не совершенно понятно, откуда следует брать стандартные библиотеки.

Как я уже отметил, библиотеки PyPy в этом случае не годятся.

Все же, проект достаточно увлекателен. Я от всей души надеюсь, что создатель (Bernie Pope) его не забросит. В особенности мне приглянулась мысль трансляции чего бы то ни было конкретно в Haskell, а не классические Си и C++.

Ведь в данном случае мы получаем не только лишь более неплохую переносимость, да и огромное количество фирменных фишек Хаскеля.

Нужно будет поглядеть на Berp еще разок где-нибудь через год.

Похожие статьи

Теги:
Рейтинг: +14 Голосов: 85 1173 просмотра
Комментарии (0)

Нет комментариев. Ваш будет первым!

Найти на сайте: параметры поиска

Windows 7

Среда Windows 7 на первых порах кажется весьма непривычной для многих.

Windows 8

Если резюмировать все выступления Microsoft на конференции Build 2013.

Windows XP

Если Windows не может корректно завершить работу, в большинстве случаев это

Windows Vista

Если к вашему компьютеру подключено сразу несколько мониторов, и вы регулярно...