[PYTHON] J'ai créé un outil pour compiler nativement Hy

introduction

Dans Article précédent, j'ai introduit un outil pour exécuter et archiver le code source à la fois. Présentation de cette fois est un outil qui compile nativement Hy pour créer des fichiers exécutables et des bibliothèques partagées. Vous pouvez également le convertir en langage Python ou C. Il est enregistré dans PyPI sous le nom HyCC. Cependant, il existe encore une possibilité de ** bogue inattendu **, veuillez donc l'utiliser en ** auto-responsabilité **. À propos, ** tous sont implémentés dans Hy **.

Comment utiliser

Veuillez installer à partir de pip. ** Cependant, le fonctionnement dans l'environnement Windows n'a pas été confirmé. ** **

$ pip install hycc

~~ De plus, si la dernière version de PyPI est Hy (v0.12.1), une erreur due à un bogue dans Hy peut se produire dans certains codes, veuillez donc installer la dernière version de Github. ~~ ** PostScript 01/07/2017: Ce travail n'est plus nécessaire en raison de la mise à jour de Hy et HyCC. **

Maintenant que la préparation est terminée, prenons le code suivant comme exemple.

hello.hy


(defn hello []
  (print "hello!"))

(defmain [&rest args]
  (hello))

Créer un fichier exécutable

$ hycc hello.hy

Cela créera un exécutable binaire appelé «bonjour» dans le répertoire courant.

$ ./hello
hello!

Créer une bibliothèque partagée

Construisez avec l'option --shared.

$ hycc hello.hy --shared

Cela créera un objet partagé appelé «hello.so» dans le répertoire courant. Python a la capacité d'importer des objets partagés en tant que modules, mais Hy est bien sûr le même. Par conséquent, ce "hello.so" peut être utilisé comme suit.

Hy pour bonjour.importer donc


(import hello)
(hello.hello)
; >hello!

Bonjour de Python.importer donc


import hello
hello.hello()
# >hello!

Les modules et exécutables compilés en mode natif sont ** environ 2 à 8 fois plus rapides **. Je pense donc que c'est plus utile que d'utiliser hyc pour compiler l'octet dans un fichier .pyc. Et bien sûr ** plus rapide que Python **. De plus, Cython est utilisé en interne, mais l'image de la vitesse est la suivante.

** C <Cython (avec spécification de type) << Cython (sans spécification de type) <HyCC <Python <Hy **

Il est généralement ** compatible vers le haut avec hyc **, mais il y a une mise en garde **. Puisqu'il est lié au mécanisme interne, je vais l'expliquer tout en expliquant le mécanisme de ce qui suit.

Comment ça fonctionne

En gros, le code Hy est converti en Python, converti en C via Cython et compilé. Cependant, comme mentionné dans Dernier message, le code Python généré en utilisant le hy2py fourni en standard avec Hy. ** Ne fonctionne pas tel quel **. Par conséquent, il n'est pas possible de simplement «hy2py» et «cythoniser».

Convertir Hy en Python

En premier lieu ** Pourquoi le code généré par «hy2py» ne fonctionne-t-il pas **? Parce que Python utilise un identifiant invalide. Les identifiants non valides en Python sont:

    • Identifiant commençant par le chiffre 0-9 *
  1. Identifiants contenant des caractères autres que * _, a-z, A-Z, 0-9 *

Prenez le code suivant comme exemple.

sample.hy


(reduce + [1 2 3])
; > 6

La conversion avec «hy2py» donne ce qui suit.

Code que hy2py crache


from hy.core.language import reduce
from hy.core.shadow import +
# +Est un identifiant invalide
reduce(+, [1, 2, 3])

Ici, «+» est le 2 ci-dessus. Parce que cela correspond à, c'est inutile. Le simple fait de le remplacer par un autre nom valide, par exemple «add», ne fonctionnera pas.

Du code que hy2py crache+Remplacer par ajouter


from hy.core.language import reduce
from hy.core.shadow import add
# ImportError
reduce(add, [1, 2, 3])

De toute évidence, le module hy.core.shadow n'a pas le nom ʻadd défini, donc ce sera ʻImportError. HyCC génère d'abord le code Python suivant à partir de la source Hy pour résoudre ce problème.

Voie HyCC


import hy.core.language as _
reduce = _.getattr("reduce")
import hy.core.shadow as _
+ = _.getattr("+")
reduce(+, [1, 2, 3])

Ce code est équivalent au code que «hy2py» crache, mais vous pouvez remplacer «+» par n'importe quel nom. HyCC évite les erreurs en combinant correctement l'accès au niveau AST et l'accès au niveau du code source. De même, l'accès membre de l'objet est réécrit avec getattr et setattr.

Précautions lors de l'utilisation de HyCC (** important **)

C'est la précaution lors de l'utilisation de HyCC que j'ai mentionnée plus tôt. Comme expliqué jusqu'à présent, HyCC remplace les identifiants invalides par des identifiants valides avec une certaine ingéniosité. Pour le moment, il n'y a qu'un seul effet secondaire ou problème.

Mauvais gars HyCC


(def hoge/fuga 0)
(print (get (globals) "hoge/fuga"))
; > 0

Le code ci-dessus est converti par HyCC en code Python suivant.

Code généré par HyCC


from __future__ import print_function
import hy
hogex2Ffuga = 0L
print(globals()[u'hoge/fuga'])

Vous pouvez ignorer l'ajout de certaines importations. Dans le code Hy original, hoge / fuga était un nom invalide en Python. Par conséquent, il est remplacé par «hogex2Ffuga» dans le code généré par HyCC.

L'exécution de ce code entraînera l'erreur suivante.

  File "test.py", line 4, in <module>
    print(globals()[u'hoge/fuga'])
KeyError: u'hoge/fuga'

Avez-vous compris? L'effet négatif du remplacement d'un identifiant invalide par un identifiant valide est que les modules globals, locals et ʻinspect` peuvent ne pas fonctionner correctement dans certains cas.

Nous envisageons quelques contre-mesures pour ce problème, mais ** Hy lui-même a déjà un problème similaire ** en premier lieu. Dans Hy, hoge! Est remplacé par hoge_bang et hoge? ʻEst remplacé par ʻis_hoge au stade de l'analyse syntaxique. Par conséquent, le code suivant ne fonctionne pas correctement.

Hy et méchant


(def hoge! 0)
(print (get (globals) "hoge!"))
; > KeyError!

Par conséquent, l'utilisation de «globals», etc. peut être considérée comme ** des précautions lors de l'utilisation de HyCC plutôt que des précautions lors de l'utilisation de HyCC **.

*** PostScript 04/06/2017 *** ** Pris en charge par la mise à jour! ** Plus précisément, l'erreur de clé est évitée en enveloppant globals et locaux dans une classe mystère comme dict. Comme d'habitude, le module ʻinspect ne fonctionne pas correctement, mais ** Cython lui-même ne prend pas en charge ʻinspect **, donc je ne peux rien y faire. Pour plus de détails, reportez-vous à This Commit.

Bonus (convertir en Python qui fonctionne correctement)

HyCC a également une fonction pour convertir de Hy en Python comme hy2py.

$ hycc hello.hy --python

Cela affichera hello.py dans le répertoire courant. Si vous faites attention aux notes ci-dessus, cela fonctionnera correctement contrairement au code produit par ** hy2py **. Étonnamment, cette fonctionnalité peut être plus demandée. De même, il peut être converti en langage C avec l'option --clang, mais il est délicat de savoir s'il peut être utilisé.

Bug connu (ajouté le 13/06/2017)

La bibliothèque partagée de l'importation échoue

Comme je l'ai écrit dans problèmes github, est-ce une spécification Python? Lors de l'obtention d'un sous-module avec gettatr pour un objet module Cela semble être une «erreur d'attribut» si le sous-module contient une bibliothèque partagée. Je réfléchis à comment y faire face.

en conclusion

Introduction de Outil de compilation native de Hy. Tout le développement se fait sur github, alors n'hésitez pas à Pururiku Vous pouvez lancer des pulls) ou issues. Bien entendu, les commentaires ici sont également les bienvenus. Hy est encore un putain de langage mineur en développement, mais j'espère qu'il deviendra plus sophistiqué à mesure que le nombre d'utilisateurs augmentera et que les discussions deviendront plus actives. Merci d'avoir lu jusqu'ici.

Recommended Posts

J'ai créé un outil pour compiler nativement Hy
J'ai créé un outil pour obtenir de nouveaux articles
J'ai créé un outil pour créer un nuage de mots à partir de wikipedia
[Titan Craft] J'ai créé un outil pour invoquer un géant sur Minecraft
J'ai fait un script pour afficher des pictogrammes
J'ai créé un outil d'estampage automatique du navigateur.
〇✕ J'ai fait un jeu
J'ai créé un outil utile pour Digital Ocean
J'ai créé un outil pour parcourir automatiquement plusieurs sites avec Selenium (Python)
J'ai créé un outil CLI pour convertir les images de chaque répertoire en PDF
J'ai créé un outil de collecte de configuration de routeur Config Collecor
J'ai créé un outil pour convertir Jupyter py en ipynb avec VS Code
J'ai fait un outil pour estimer le temps d'exécution de cron (+ débuts de PyPI)
Je viens de créer un outil pour afficher facilement les données sous forme de graphique par opération GUI
J'ai créé un outil pour générer du Markdown à partir du fichier JSON Scrapbox exporté
J'ai créé un outil pour sauvegarder automatiquement les métadonnées de l'organisation Salesforce
J'ai créé un package pour créer un fichier exécutable à partir du code source Hy
J'ai fait une bibliothèque pour bien séparer les phrases japonaises
J'ai créé un outil de nettoyage pour Google Container Registry
J'ai fait un script pour mettre un extrait dans README.md
J'ai créé un module Python pour traduire les commentaires
J'ai créé un code pour convertir illustration2vec en modèle Keras
J'ai fait une commande pour marquer le clip de la table
J'ai créé une bibliothèque python qui fait rouler le rang
J'ai créé un outil de génération de données texte répétitif "rpttxt"
J'ai fait un texte Python
J'ai fait un robot discord
J'ai créé un package pour filtrer les séries chronologiques avec python
J'ai fait une boîte pour me reposer avant que Pepper ne se fatigue
J'ai fait une commande pour générer un commentaire pour une table dans Django
J'ai fait une fonction pour vérifier le modèle de DCGAN
J'ai essayé de commencer avec Hy ・ Définir une classe
Je vous ai fait exécuter des commandes depuis un navigateur WEB
J'ai fait un script pour dire bonjour à mon Koshien
Création d'un toolver qui crache le système d'exploitation, Python, les modules et les versions d'outils à Markdown
J'ai créé un outil qui facilite un peu la création et l'installation d'une clé publique.
J'ai créé un outil pour obtenir les liens de réponse d'OpenAI Gym en même temps
J'ai créé un outil pour générer automatiquement un simple diagramme ER à partir de l'instruction CREATE TABLE
Création d'un outil qui facilite la définition des paramètres des modèles d'apprentissage automatique
J'ai créé un site d'apprentissage C ++
J'ai fait un Line-bot avec Python!
J'ai fait un wikipedia gacha bot
J'ai fait une loterie avec Python.
J'ai créé un script de traduction basé sur CUI
Outil pour convertir la configuration Juniper
J'ai créé un démon avec Python
[5e] J'ai essayé de créer un certain outil de type Authenticator avec python
J'ai essayé de faire un programme pour résoudre (indice) la recherche d'erreur de Saiseriya
J'ai créé une bibliothèque qui lit facilement les fichiers de configuration avec Python
[2nd] J'ai essayé de créer un certain outil de type Authenticator avec python
J'ai créé un serveur Web avec Razpai pour regarder des anime
[3ème] J'ai essayé de créer un certain outil de type Authenticator avec python
Création d'un outil CLI client / serveur WebSocket (comme WebSocket version netcat)
[4th] J'ai essayé de créer un certain outil de type Authenticator avec python
J'ai essayé de créer un outil d'échafaudage pour le framework Web Python Bottle
[1er] J'ai essayé de créer un certain outil de type Authenticator avec python
J'ai fait une commande pour afficher un calendrier coloré dans le terminal
J'ai essayé de créer un linebot (implémentation)
J'ai créé un conteneur Docker pour utiliser JUMAN ++, KNP, python (pour pyKNP).
J'ai créé un plugin pour générer une table Markdown à partir de csv avec Vim