Я поддержу точку зрения @EliBendersky относительно использования ast.parse вместо парсера (о котором я раньше не знал). Я также настоятельно рекомендую вам просмотреть его блог. Я использовал ast.parse для перевода Python-> JavaScript (@ https://bitbucket.org/amirouche/pythonium ). Я придумал Pythonium дизайн на несколько обзор других реализаций и пытается их самостоятельно. Я раздвоил Pythonium из https://github.com/PythonJS/PythonJS, который я также начал, это фактически полная переписывание. Общий дизайн вдохновлен PyPy и http://www.hpl.hp.com/techreports/Compaq-DEC/WRL-89-1.pdf бумагой.
Все, что я пробовал, от начала до лучшего решения, даже если это похоже на маркетинг Pythonium, на самом деле это не так (не стесняйтесь сказать мне, если что-то не кажется правильным для сетевого этикета):
Реализуйте семантику Python в обычном старом JavaScript с использованием наследования прототипов: AFAIK невозможно реализовать множественное наследование Python с использованием объектной системы прототипов JS. Позже я попытался сделать это, используя другие приемы (см. Getattribute). Насколько мне известно, в JavaScript нет реализации множественного наследования Python, лучшее, что существует, - это одиночное наследование + миксины, и я не уверен, что они обрабатывают наследование алмаза. Вроде как Skulpt, но без закрытия Google.
Я пробовал использовать Google clojure, точно так же, как Skulpt (компилятор), вместо того, чтобы читать код Skulpt #fail. Во всяком случае, из-за объектной системы на основе прототипа JS все еще невозможно. Создание привязки было очень и очень сложным, вам нужно было написать JavaScript и много шаблонного кода (см. Https://github.com/skulpt/skulpt/issues/50, где я - призрак). В то время не было четкого способа интегрировать привязку в систему сборки. Я думаю, что Skulpt - это библиотека, и вам просто нужно включить свои файлы .py в html для выполнения, разработчик не требует выполнения этапа компиляции.
Пробовал pyjaco (компилятор), но создание привязок (вызов кода Javascript из кода Python) было очень трудным, слишком много шаблонного кода для создания каждый раз. Теперь я думаю, что pyjaco - это тот, который ближе к Pythonium. pyjaco написан на Python (в том числе ast.parse), но многое написано на JavaScript и использует наследование прототипов.
На самом деле мне никогда не удавалось запустить Pyjamas #fail, и я никогда не пытался снова прочитать код #fail. Но на мой взгляд, пижама выполняла перевод API-> API (или фреймворка в фреймворк), а не перевод Python в JavaScript. Инфраструктура JavaScript использует данные, которые уже есть на странице, или данные с сервера. Код Python - это всего лишь «сантехника». После этого я обнаружил, что pajamas на самом деле был настоящим переводчиком python-> js.
Тем не менее, я думаю, что можно сделать перевод API-> API (или framework-> framework), и это в основном то, что я делаю в Pythonium, но на более низком уровне. Вероятно, Pyjamas использует тот же алгоритм, что и Pythonium ...
Затем я обнаружил, что brython полностью написан на Javascript, как Skulpt, не требует компиляции и лишен всякой ерунды ... но написан на JavaScript.
С момента написания начальной строки в ходе этого проекта я знал о PyPy, даже о бэкэнде JavaScript для PyPy. Да, вы можете, если найдете, напрямую сгенерировать интерпретатор Python в JavaScript из PyPy. Говорят, это была катастрофа. Я не читал где почему. Но я думаю, причина в том, что промежуточный язык, который они используют для реализации интерпретатора, RPython, представляет собой подмножество Python, адаптированное для перевода на C (и, возможно, asm). Ира Бакстер говорит, что вы всегда делаете предположения, когда создаете что-то, и, вероятно, вы точно настраиваете его, чтобы он был лучшим для того, что он должен делать в случае перевода PyPy: Python-> C. Эти предположения могут не иметь отношения к другому контексту, хуже того, что они могут приводить к накладным расходам, в противном случае прямой перевод, скорее всего, всегда будет лучше.
Написание интерпретатора на Python звучало как (очень) хорошая идея. Но меня больше интересовал компилятор по соображениям производительности, а также на самом деле компилировать Python в JavaScript проще, чем интерпретировать его.
Я начал PythonJS с идеей собрать подмножество Python, которое я мог бы легко перевести на JavaScript. Сначала я даже не стал внедрять объектно-ориентированную систему из-за прошлого опыта. Подмножество Python, которое мне удалось перевести на JavaScript:
- функция с полной семантикой параметров как в определении, так и при вызове. Это та часть, которой я горжусь больше всего.
- а / если / элиф / еще
- Типы Python были преобразованы в типы JavaScript (нет никаких типов Python)
- for может перебирать только массивы Javascript (для массива in)
- Прозрачный доступ к JavaScript: если вы напишете Array в коде Python, он будет переведен в Array в javascript. Это самое большое достижение с точки зрения удобства использования по сравнению с конкурентами.
- Вы можете передать функцию, определенную в исходном коде Python, в функции javascript. Аргументы по умолчанию будут приняты во внимание.
- В нем есть специальная функция new, которая переводится в JavaScript new, например: new (Python) (1, 2, spam, «egg») переводится в «new Python (1, 2, spam,« egg »).
- "var" автоматически обрабатываются переводчиком. (очень хорошая находка от Бретта (участник PythonJS).
- глобальное ключевое слово
- закрытие
- лямбды
- составить список
- импорт поддерживается через requirejs
- наследование одного класса + миксин через classyjs
Это кажется большим, но на самом деле очень узким по сравнению с полноценной семантикой Python. Это действительно JavaScript с синтаксисом Python.
Сгенерированный JS идеален, т.е. нет накладных расходов, его нельзя улучшить по производительности дальнейшим редактированием. Если вы можете улучшить сгенерированный код, вы также можете сделать это из исходного файла Python. Кроме того, компилятор не полагался на какие-либо уловки JS, которые вы можете найти в .js, написанном http://superherojs.com/. , поэтому он очень удобочитаемый.
Прямым потомком этой части PythonJS является режим Pythonium Veloce. Полную реализацию можно найти по адресу https://bitbucket.org/amirouche/pythonium/src/33898da731ee2d768ced392f1c369afd746c25d7/pythonium/veloce/veloce.py?at=master. 793 SLOC + около 100 SLOC общего кода с другим переводчиком.
Адаптированная версия pystones.py может быть переведена в режиме Veloce cf. https://bitbucket.org/amirouche/pythonium/src/33898da731ee2d768ced392f1c369afd746c25d7/pystone/?at=master
После настройки базового перевода Python-> JavaScript я выбрал другой путь для перевода полного Python в JavaScript. Способ glib создания объектно-ориентированного кода на основе классов, за исключением того, что целевой язык - это JS, поэтому у вас есть доступ к массивам, объектам, подобным карте, и многим другим трюкам, и все это было написано на Python. IIRC нет кода javascript, написанного в переводчике Pythonium. Получить единичное наследование несложно, вот те сложные моменты, которые делают Pythonium полностью совместимым с Python:
spam.egg
в Python всегда переводится на то, что getattribute(spam, "egg")
я не профилировал это в частности, но я думаю, что там, где он теряет много времени, и я не уверен, что смогу улучшить его с помощью asm.js или чего-то еще.
- порядок разрешения методов: даже с алгоритмом, написанным на Python, перевод его в код, совместимый с Python Veloce, был большой задачей.
- getattributre : фактический алгоритм разрешения getattribute довольно сложен, и он по-прежнему не поддерживает дескрипторы данных
- на основе класса метакласса: я знаю, где вставить код, но все же ...
- последнее бу не в последнюю очередь: some_callable (...) всегда переводится в "call (some_callable)". AFAIK переводчик вообще не использует логический вывод, поэтому каждый раз, когда вы выполняете вызов, вам нужно проверять, какой тип объекта должен называть его так, как он должен вызываться.
Эта часть учтена в https://bitbucket.org/amirouche/pythonium/src/33898da731ee2d768ced392f1c369afd746c25d7/pythonium/compliant/runtime.py?at=master Он написан на Python, совместимом с Python Veloce.
Фактически совместимый переводчик https://bitbucket.org/amirouche/pythonium/src/33898da731ee2d768ced392f1c369afd746c25d7/pythonium/compliant/compliant.py?at=master не генерирует код JavaScript напрямую и, самое главное, не выполняет преобразование ast-> ast . Я пробовал использовать ast-> ast, и ast, даже если он лучше, чем cst, нехорошо работать даже с ast.NodeTransformer и, что более важно, мне не нужно делать ast-> ast.
Выполнение python ast в python ast в моем случае, по крайней мере, может быть улучшением производительности, поскольку я иногда проверяю содержимое блока перед генерацией связанного с ним кода, например:
- var / global: чтобы иметь возможность что-то var, я должен знать, что мне нужно, а не var. Вместо того, чтобы генерировать блок, отслеживающий, какие переменные созданы в данном блоке, и вставляя их поверх сгенерированного функционального блока, я просто ищу удобное назначение переменных, когда я вхожу в блок, прежде чем фактически посетить дочерний узел для генерации связанного кода.
- yield, генераторы пока имеют специальный синтаксис в JS, поэтому мне нужно знать, какая функция Python является генератором, когда я хочу написать «var my_generator = function»
Поэтому я не посещаю каждый узел один раз на каждом этапе перевода.
Общий процесс можно описать как:
Python source code -> Python ast -> Python source code compatible with Veloce mode -> Python ast -> JavaScript source code
Встроенные функции Python написаны на коде Python (!), IIRC имеет несколько ограничений, связанных с типами начальной загрузки, но у вас есть доступ ко всему, что может переводить Pythonium в совместимый режим. Взгляните на https://bitbucket.org/amirouche/pythonium/src/33898da731ee2d768ced392f1c369afd746c25d7/pythonium/compliant/builtins/?at=master
Чтение JS-кода, сгенерированного с помощью pythonium-совместимого, можно понять, но карты источников очень помогут.
Ценный совет, который я могу дать вам в свете этого опыта, - это старые добрые пердуны:
- подробно изучить тему как в литературе, так и в существующих проектах с закрытым исходным кодом или бесплатно. Когда я просматривал различные существующие проекты, мне следовало уделить им больше времени и мотивации.
- задавать вопросы! Если бы я знал заранее, что бэкэнд PyPy бесполезен из-за накладных расходов из-за семантического несоответствия C / Javascript. Возможно, у меня была идея Pythonium раньше, чем 6 месяцев назад, может быть, 3 года назад.
- знайте, что вы хотите делать, имейте цель. В этом проекте у меня были другие цели: немного поработать JavaScript, узнать больше о Python и уметь писать код Python, который будет работать в браузере (подробнее об этом ниже).
- неудача - это опыт
- маленький шаг - это шаг
- начать с малого
- большая мечта
- делать демо
- повторять
Только с режимом Python Veloce, я очень счастлив! Но по пути я обнаружил, что то, что я действительно искал, - это освобождение меня и других от Javascript, но, что более важно, возможность создавать комфортным способом. Это привело меня к Scheme, DSL, Models и, в конечном итоге, к моделям, специфичным для предметной области (см. Http://dsmforum.org/ ).
О чем ответ Ира Бакстер:
Оценки совершенно бесполезны. У меня было около 6 месяцев свободного времени как для PythonJS, так и для Pythonium. Так что от 6 месяцев полной занятости я могу ожидать большего. Я думаю, все мы знаем, что 100 человеко-лет в контексте предприятия могут означать и вовсе не означать ...
Когда кто-то говорит, что что-то сложно или чаще невозможно, я отвечаю, что «требуется только время, чтобы найти решение проблемы, которая невозможна», в противном случае сказал, что нет ничего невозможного, кроме случаев, когда это доказано в данном случае математическим доказательством ...
Если не доказано, что это невозможно, остается место для воображения:
- найти доказательство, доказывающее, что это невозможно
и
- Если это невозможно, может быть «неполная» проблема, у которой есть решение.
или
- если это не невозможно, найти решение
Это не просто оптимистическое мышление. Когда я начинал Python-> Javascript, все говорили, что это невозможно. PyPy невозможно. Метаклассы слишком сложные. и т. д. Я думаю, что единственная революция, которая привносит PyPy в бумагу Scheme-> C (которой 25 лет), - это автоматическая генерация JIT (на основе подсказок, написанных в интерпретаторе RPython, я думаю).
Большинство людей, которые говорят, что что-то «сложно» или «невозможно», не приводят причин. C ++ сложно разобрать? Я знаю это, но они (бесплатные) парсеры C ++. Зло в деталях? Я знаю это. Сказать, что это невозможно в одиночку, бесполезно. Это даже хуже, чем «бесполезно», это обескураживает, и некоторые люди хотят отговорить других. Я слышал об этом вопросе через /programming/22621164/how-to-automatically-generate-a-parser-code-to-code-translator-from-a-corpus .
Что было бы для вас совершенством ? Так вы определяете следующую цель и, возможно, достигнете общей цели.
Мне больше интересно знать, какие типы шаблонов я мог бы применить к коду, чтобы упростить перевод (например, IoC, SOA?) Кода, чем то, как сделать перевод.
Я не вижу шаблонов, которые нельзя было бы перевести с одного языка на другой хотя бы неидеально. Поскольку возможен перевод с одного языка на другой, вам лучше сначала сделать это. Поскольку, я думаю, согласно http://en.wikipedia.org/wiki/Graph_isomorphism_problem , перевод между двумя компьютерными языками - это дерево или изоморфизм DAG. Даже если мы уже знаем, что они оба завершены по Тьюрингу, так что ...
Framework-> Framework, который я лучше визуализирую как перевод API-> API, может быть тем, что вы могли бы иметь в виду как способ улучшить сгенерированный код. Например: Пролог как очень специфический синтаксис, но все же вы можете выполнять Пролог как вычисление, описывая тот же граф в Python ... Если бы я должен был реализовать переводчик Пролога в Python, я бы не реализовал унификацию в Python, а в библиотеке C и пришел с "синтаксисом Python", который очень удобен для чтения Pythonist. В конце концов, синтаксис - это всего лишь «картина», которой мы придаем смысл (поэтому я и начал схему). Зло в деталях языка, и я не говорю о синтаксисе. Понятия, которые используются в языке getattributeкрючок (вы можете жить и без него), но с необходимыми функциями виртуальной машины, такими как оптимизация хвостовой рекурсии, может быть трудно справиться. Вам все равно, если исходная программа не использует хвостовую рекурсию, и даже если на целевом языке нет хвостовой рекурсии, вы можете эмулировать ее с помощью гринлетов / цикла событий.
Для целевого и исходного языков ищите:
- Большие и конкретные идеи
- Крошечные и общие идеи
Из этого выйдет:
- Вещи, которые легко перевести
- Вещи, которые сложно перевести
Вы также, вероятно, сможете узнать, что будет преобразовано в быстрый и медленный код.
Также есть вопрос о stdlib или любой другой библиотеке, но нет четкого ответа, это зависит от ваших целей.
Идиоматический код или читаемый сгенерированный код также имеют решения ...
Ориентация на такую платформу, как PHP, намного проще, чем на браузеры, поскольку вы можете обеспечить C-реализацию медленного и / или критического пути.
Учитывая, что ваш первый проект - это перевод Python на PHP, по крайней мере, для подмножества PHP3, о котором я знаю, настройка veloce.py - ваш лучший выбор. Если вы можете реализовать veloce.py для PHP, то, вероятно, вы сможете запустить соответствующий режим ... Также, если вы можете перевести PHP в подмножество PHP, которое вы можете сгенерировать с помощью php_veloce.py, это означает, что вы можете перевести PHP в подмножество Python, которое может использовать veloce.py, что означает, что вы можете перевести PHP в Javascript. Просто говорю...
Вы также можете взглянуть на эти библиотеки:
Также вас может заинтересовать это сообщение в блоге (и комментарии): https://www.rfk.id.au/blog/entry/pypy-js-poc-jit/