Je n'ai qu'une connaissance limitée de Lisp (en essayant d'apprendre un peu pendant mon temps libre) mais pour autant que je sache, les macros Lisp permettent d'introduire de nouvelles constructions de langage et de la syntaxe en les décrivant dans Lisp lui-même. Cela signifie qu'une nouvelle construction peut être ajoutée en tant que bibliothèque, sans changer le compilateur / interpréteur Lisp.
Cette approche est très différente de celle des autres langages de programmation. Par exemple, si je voulais étendre Pascal avec un nouveau type de boucle ou un idiome particulier, je devrais étendre la syntaxe et la sémantique du langage, puis implémenter cette nouvelle fonctionnalité dans le compilateur.
Existe-t-il d'autres langages de programmation en dehors de la famille Lisp (c'est-à-dire en dehors de Common Lisp, Scheme, Clojure (?), Racket (?), Etc.) qui offrent une possibilité similaire d'étendre le langage à l'intérieur du langage lui-même?
ÉDITER
S'il vous plaît, évitez les discussions prolongées et soyez précis dans vos réponses. Au lieu d'une longue liste de langages de programmation qui peuvent être étendus d'une manière ou d'une autre, j'aimerais comprendre d'un point de vue conceptuel ce qui est spécifique aux macros Lisp en tant que mécanisme d'extension, et quels langages de programmation non Lisp offrent un concept qui leur est proche.
la source
Réponses:
Scala rend cela également possible (en fait, il a été consciemment conçu pour prendre en charge la définition de nouvelles constructions de langage et même des DSL complètes).
Outre les fonctions d'ordre supérieur, les lambdas et le curry, qui sont courants dans les langages fonctionnels, il existe ici des fonctionnalités de langage spéciales pour permettre cela *:
a.and(b)
-à- dire qu'ils sont équivalents àa and b
sous forme d'infixepour les appels de fonction à paramètre unique, vous pouvez utiliser des accolades au lieu des accolades normales - cela (avec le curry) vous permet d'écrire des choses comme
où
withPrintWriter
est une méthode simple avec deux listes de paramètres, contenant toutes deux un seul paramètremyAssert(() => x > 3)
sous une forme plus courte commemyAssert(x > 3)
La création d'un exemple DSL est discutée en détail dans le chapitre 11. Langages spécifiques au domaine dans Scala du livre gratuit Programming Scala .
* Je ne veux pas dire que ceux-ci sont uniques à Scala, mais au moins ils ne semblent pas être très courants. Je ne suis cependant pas un expert en langages fonctionnels.
la source
Perl permet le prétraitement de son langage. Bien que cela ne soit pas souvent utilisé au point de changer radicalement la syntaxe du langage, il peut être vu dans certains des modules ... bizarres:
Il existe également un module qui permet à perl d'exécuter du code qui semble avoir été écrit en Python.
Une approche plus moderne de cela dans perl serait d'utiliser Filter :: Simple (l'un des modules principaux de perl5).
Notez que tous ces exemples impliquent Damian Conway qui a été appelé le "Docteur fou de Perl". C'est toujours une capacité étonnamment puissante dans perl pour tordre le langage comme on le veut.
Plus de documentation pour cela et d'autres alternatives existe sur perlfilter .
la source
Haskell
Haskell a "Template Haskell" ainsi que "Quasiquotation":
http://www.haskell.org/haskellwiki/Template_Haskell
http://www.haskell.org/haskellwiki/Quasiquotation
Ces fonctionnalités permettent aux utilisateurs d'ajouter considérablement à la syntaxe de la langue en dehors des moyens normaux. Ceux-ci sont également résolus au moment de la compilation, ce qui je pense est un grand must (pour les langages compilés au moins) [1].
J'ai déjà utilisé la quasiquotation dans Haskell pour créer un modèle de correspondance avancé sur un langage de type C:
[1] Sinon, ce qui suit est considéré comme une extension de syntaxe:,
runFeature "some complicated grammar enclosed in a string to be evaluated at runtime"
ce qui bien sûr est une charge de merde.la source
forM
).Tcl a une longue histoire de prise en charge de la syntaxe extensible. Par exemple, voici l'implémentation d'une boucle qui itère trois variables (jusqu'à l'arrêt) sur les cardinaux, leurs carrés et leurs cubes:
Cela serait alors utilisé comme ceci:
Ce type de technique est largement utilisé dans la programmation Tcl, et la clé pour le faire sainement sont les commandes
upvar
etuplevel
(upvar
lie une variable nommée dans une autre portée à une variable locale etuplevel
exécute un script dans une autre portée: dans les deux cas, le1
indique que la portée en question est celle de l'appelant). Il est également beaucoup utilisé dans le code qui se couple aux bases de données (en exécutant du code pour chaque ligne dans un ensemble de résultats), dans Tk pour les interfaces graphiques (pour faire la liaison des rappels aux événements), etc.Cependant, ce n'est qu'une fraction de ce qui est fait. Le langage intégré n'a même pas besoin d'être Tcl; cela peut être pratiquement n'importe quoi (tant qu'il équilibre ses accolades - les choses deviennent syntaxiquement horribles si ce n'est pas vrai - ce qui est l'énorme majorité des programmes) et Tcl peut simplement envoyer à la langue étrangère intégrée si nécessaire. Des exemples de cela incluent l' intégration de C pour implémenter les commandes Tcl et l'équivalent avec Fortran. (On peut dire que toutes les commandes intégrées de Tcl sont exécutées de cette façon dans un sens, en ce sens qu'elles ne sont en réalité qu'une bibliothèque standard et non le langage lui-même.)
la source
C'est en partie une question de sémantique. L'idée de base de Lisp est que le programme est des données qui peuvent elles-mêmes être manipulées. Les langages couramment utilisés dans la famille Lisp, comme Scheme, ne vous permettent pas vraiment d'ajouter une nouvelle syntaxe au sens de l'analyseur; ce ne sont que des listes entre parenthèses délimitées par des espaces. C'est juste que puisque la syntaxe de base fait si peu, vous pouvez en faire presque n'importe quelle construction sémantique . Scala (discuté ci-dessous) est similaire: les règles de nom de variable sont si libérales que vous pouvez facilement en faire de belles DSL (tout en restant dans les mêmes règles de syntaxe de base).
Ces langages, bien qu'ils ne vous permettent pas réellement de définir une nouvelle syntaxe au sens des filtres Perl, ont un noyau suffisamment flexible pour que vous puissiez l'utiliser pour créer des DSL et ajouter des constructions de langage.
La caractéristique commune importante est qu'ils vous permettent de définir des constructions de langage qui fonctionnent aussi bien que celles intégrées, en utilisant des fonctionnalités exposées par les langues. Le degré de prise en charge de cette fonctionnalité varie:
sin()
,round()
, etc., sans aucun moyen de mettre en œuvre votre propre.static_cast<target_type>(input)
,dynamic_cast<>()
,const_cast<>()
,reinterpret_cast<>()
) peuvent être émulés en utilisant des fonctions de modèle, qui utilise Boost pourlexical_cast<>()
,polymorphic_cast<>()
,any_cast<>()
, ....for(;;){}
,while(){}
,if(){}else{}
,do{}while()
,synchronized(){}
,strictfp{}
) et ne vous permet pas de définir votre propre. Scala définit à la place une syntaxe abstraite qui vous permet d'appeler des fonctions à l'aide d'une syntaxe de type structure de contrôle pratique, et les bibliothèques l'utilisent pour définir efficacement de nouvelles structures de contrôle (par exemplereact{}
dans la bibliothèque des acteurs).Vous pouvez également consulter la fonctionnalité de syntaxe personnalisée de Mathematica dans le package de notation . (Techniquement, il fait partie de la famille Lisp, mais a des fonctionnalités d'extensibilité faites différemment, ainsi que l'extensibilité Lisp habituelle.)
la source
(defmacro ...)
. En fait, je porte actuellement cette langue sur Racket, juste pour le plaisir. Mais, je suis d'accord que c'est quelque chose de peu utile, car la syntaxe des expressions S est plus que suffisante pour la plupart des sémantiques utiles possibles.(define-macro ...)
équivalent, qui, à son tour, peut utiliser n'importe quel type d'analyse en interne.Rebol ressemble presque à ce que vous décrivez, mais un peu sur le côté.
Plutôt que de définir une syntaxe spécifique, tout dans Rebol est un appel de fonction - il n'y a pas de mots-clés. (Oui, vous pouvez redéfinir
if
etwhile
si vous le désirez vraiment). Par exemple, ceci est uneif
déclaration:if
est une fonction qui prend 2 arguments: une condition et un bloc. Si la condition est vraie, le bloc est évalué. Cela ressemble à la plupart des langues, non? Eh bien, le bloc est une structure de données, il ne se limite pas au code - c'est un bloc de blocs, par exemple, et un exemple rapide de la flexibilité de "code is data":Tant que vous pouvez vous en tenir aux règles de syntaxe, l'extension de ce langage ne sera, pour la plupart, rien de plus que la définition de nouvelles fonctions. Certains utilisateurs ont rétroporté des fonctionnalités de Rebol 3 dans Rebol 2, par exemple.
la source
Ruby a une syntaxe assez flexible, je pense que c'est une façon "d'étendre la langue à l'intérieur de la langue elle-même".
Un exemple est le râteau . C'est écrit en Ruby, c'est Ruby, mais ça ressemble à make .
Pour vérifier certaines possibilités, vous pouvez rechercher les mots clés Ruby et métaprogrammation .
la source
L'extension de la syntaxe de la façon dont vous parlez vous permet de créer des langues spécifiques au domaine. Alors, peut-être que la façon la plus utile de reformuler votre question est la suivante: quelles autres langues ont un bon support pour les langues spécifiques au domaine?
Ruby a une syntaxe très flexible, et beaucoup de DSL y ont prospéré, comme le râteau. Groovy comprend beaucoup de cette bonté. Il comprend également des transformations AST, qui sont plus directement analogues aux macros Lisp.
R, le langage du calcul statistique, permet aux fonctions de ne pas évaluer leurs arguments. Il l'utilise pour créer une DSL pour spécifier la formule de régression. Par exemple:
signifie "ajuster une ligne de la forme k0 + k1 * a + k2 * b aux valeurs en y".
signifie "ajuster une ligne de la forme k0 + k1 * a + k2 * b + k3 * a * b aux valeurs en y".
Etc.
la source
Converge est un autre langage de métaprogrammation non lispy. Et, dans une certaine mesure, C ++ est également qualifié.
Sans doute, MetaOCaml est assez loin de Lisp. Pour un style d'extensibilité de syntaxe totalement différent, mais pourtant assez puissant, jetez un œil à CamlP4 .
Nemerle est un autre langage extensible avec une métaprogrammation de style Lisp, bien qu'il soit plus proche de langages comme Scala.
Et, Scala lui - même deviendra bientôt aussi une telle langue.
Edit: j'ai oublié l'exemple le plus intéressant - JetBrains MPS . Il n'est pas seulement très éloigné de tout ce qui est lispish, c'est même un système de programmation non textuel, avec un éditeur fonctionnant directement au niveau AST.
Edit2: Pour répondre à une question mise à jour - il n'y a rien d'unique et d'exceptionnel dans les macros Lisp. En théorie, n'importe quel langage peut fournir un tel mécanisme (je l'ai même fait en C simple). Tout ce dont vous avez besoin est un accès à votre AST et une capacité à exécuter du code en temps de compilation. Une réflexion pourrait être utile (interrogation sur les types, les définitions existantes, etc.).
la source
Prolog permet de définir de nouveaux opérateurs qui sont traduits en termes composés du même nom. Par exemple, cela définit un
has_cat
opérateur et le définit comme un prédicat pour vérifier si une liste contient l'atomecat
:Le
xf
moyen quihas_cat
est un opérateur postfixé; l'utilisation enfx
ferait un opérateur préfixe et enxfx
ferait un opérateur infixe, en prenant deux arguments. Consultez ce lien pour plus de détails sur la définition des opérateurs dans Prolog.la source
TeX est totalement absent de la liste. Vous le savez tous, non? Cela ressemble à ceci:
… Sauf que vous pouvez redéfinir la syntaxe sans restrictions. Chaque (!) Jeton de la langue peut recevoir une nouvelle signification. ConTeXt est un package de macro qui a remplacé les accolades par des accolades carrées:
Le package de macros plus courant LaTeX redéfinit également le langage à ses fins, par exemple en ajoutant la
\begin{environment}…\end{environment}
syntaxe.Mais cela ne s'arrête pas là. Techniquement, vous pouvez tout aussi bien redéfinir les jetons pour analyser les éléments suivants:
Oui, absolument possible. Certains packages l'utilisent pour définir de petites langues spécifiques au domaine. Par exemple, le package TikZ définit une syntaxe concise pour les dessins techniques, qui permet ce qui suit:
De plus, TeX est Turing complet, vous pouvez donc tout faire avec. Je n'ai jamais vu cela utilisé à son plein potentiel car il serait assez inutile et très compliqué, mais il est tout à fait possible de rendre le code suivant analysable juste en redéfinissant les jetons (mais cela irait probablement aux limites physiques de l'analyseur, en raison de la façon dont il est construit):
la source
Boo vous permet de personnaliser fortement la langue au moment de la compilation grâce à des macros syntaxiques.
Boo a un "pipeline de compilateur extensible". Cela signifie que le compilateur peut appeler votre code pour effectuer des transformations AST à tout moment pendant le pipeline du compilateur. Comme vous le savez, des choses comme les génériques de Java ou Linq de C # ne sont que des transformations de syntaxe au moment de la compilation, c'est donc assez puissant.
Par rapport à Lisp, le principal avantage est que cela fonctionne avec n'importe quel type de syntaxe. Boo utilise une syntaxe inspirée de Python, mais vous pourriez probablement écrire un compilateur extensible avec une syntaxe C ou Pascal. Et puisque la macro est évaluée au moment de la compilation, il n'y a pas de pénalité de performances.
Les inconvénients, par rapport à Lisp, sont:
Par exemple, voici comment vous pouvez implémenter une nouvelle structure de contrôle:
usage:
qui est ensuite traduit, au moment de la compilation, en quelque chose comme:
(Malheureusement, la documentation en ligne de Boo est toujours désespérément dépassée et ne couvre même pas des choses avancées comme celle-ci. La meilleure documentation pour la langue que je connais est ce livre: http://www.manning.com/rahien/ )
la source
L'évaluation Mathematica est basée sur l'appariement et le remplacement de motifs. Cela vous permet de créer vos propres structures de contrôle, de modifier les structures de contrôle existantes ou de changer la façon dont les expressions sont évaluées. Par exemple, vous pouvez implémenter une "logique floue" comme ceci (un peu simplifiée):
Ceci annule l'évaluation des opérateurs logiques prédéfinis &&, || ,! et la
If
clause intégrée .Vous pouvez lire ces définitions comme des définitions de fonction, mais la vraie signification est: si une expression correspond au modèle décrit sur le côté gauche, elle est remplacée par l'expression sur le côté droit. Vous pouvez définir votre propre clause If comme ceci:
SetAttributes[..., HoldRest]
indique à l'évaluateur qu'il doit évaluer le premier argument avant la correspondance de modèle, mais maintenir l'évaluation pour le reste jusqu'à ce que le modèle ait été mis en correspondance et remplacé.Ceci est largement utilisé dans les bibliothèques standard de Mathematica pour par exemple définir une fonction
D
qui prend une expression et évalue sa dérivée symbolique.la source
Metalua est un langage et un compilateur compatible avec Lua qui fournit cela.
Différences avec Lisp:
Un exemple d'application est l'implémentation d'un filtrage de type ML.
Voir aussi: http://lua-users.org/wiki/MetaLua
la source
Si vous recherchez des langues extensibles, vous devriez jeter un œil à Smalltalk.
Dans Smalltalk, la seule façon de programmer est d'étendre réellement la langue. Il n'y a aucune différence entre l'IDE, les bibliothèques ou la langue elle-même. Ils sont tous tellement liés que Smalltalk est souvent appelé un environnement plutôt qu'un langage.
Vous n'écrivez pas d'applications autonomes dans Smalltalk, vous étendez plutôt l'environnement linguistique.
Consultez http://www.world.st/ pour une poignée de ressources et d'informations.
Je voudrais recommander Pharo comme dialecte d'entrée dans le monde de Smalltalk: http://pharo-project.org
J'espère que cela a aidé!
la source
Il existe des outils qui permettent de créer des langages personnalisés sans écrire un compilateur entier à partir de zéro. Par exemple, il y a Spoofax , qui est un outil de transformation de code: vous mettez des règles de grammaire et de transformation d'entrée (écrites de manière déclarative de très haut niveau), puis vous pouvez générer du code source Java (ou un autre langage, si vous vous souciez suffisamment) à partir d'un langage personnalisé conçu par vous.
Ainsi, il serait possible de prendre la grammaire du langage X, de définir la grammaire du langage X '(X avec vos extensions personnalisées) et la transformation X' → X, et Spoofax générera un compilateur X '→ X.
Actuellement, si je comprends bien, le meilleur support est pour Java, avec un support C # en cours de développement (ou du moins j'ai entendu). Cette technique pourrait être appliquée à n'importe quelle langue avec une grammaire statique (donc, par exemple probablement pas Perl ).
la source
Forth est une autre langue très extensible. De nombreuses implémentations de Forth consistent en un petit noyau écrit en assembleur ou en C, puis le reste du langage est écrit en Forth lui-même.
Il existe également plusieurs langages basés sur la pile qui sont inspirés de Forth et partagent cette fonctionnalité, comme Factor .
la source
Funge-98
La fonction d'empreinte digitale de Funge-98 permet de permettre une restructuration complète de la syntaxe et de la sémantique du langage. Mais seulement si l'implémenteur fournit un mécanisme d'empreinte digitale qui a permis à l'utilisateur de modifier le langage par programmation (cela est théoriquement possible à implémenter dans la syntaxe et la sémantique Funge-98 normales). Si c'est le cas, on pourrait littéralement faire en sorte que le reste du fichier (ou toutes les parties du fichier) agissent comme C ++ ou Lisp (ou ce qu'il veut).
http://quadium.net/funge/spec98.html#Fingerprints
la source
Pour obtenir ce que vous cherchez, vous avez vraiment besoin de ces parenthèses et d'un manque de syntaxe. Quelques langages basés sur la syntaxe peuvent se rapprocher, mais ce n'est pas tout à fait la même chose qu'une vraie macro.
la source