Langages de programmation avec un mécanisme d'extension de syntaxe de type Lisp [fermé]

20

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.

Giorgio
la source
6
Lisp a une autre astuce en plus des macros régulières. Les "macros de lecture" permettent de capturer et d'étendre la syntaxe de l'analyseur au moment de l'exécution, de sorte que même la structure de base des jetons du langage est sous contrôle.
ddyer
@ddyer: Merci, je n'en savais rien: un autre sujet à ajouter à ma liste de lecture.
Giorgio
Je parie que la métaprogrammation Ruby peut répondre à cela.
Rig
1
votre question est contradictoire, vous demandez d'abord une liste, puis vous demandez des informations conceptuelles, c'est quoi ???
Je demande une liste, mais pas générique (pas n'importe quel langage de programmation qui peut être étendu d'une manière ou d'une autre) car ce serait trop général. Je voudrais connaître les langues qui peuvent être étendues d'une manière similaire à Lisp (l'extension est définie en utilisant la même langue qu'elle étend, pas une sorte de méta-langue). Les réponses de Péter Török, Thomas Eding, SK-logic et Mechanical snail semblent être les plus proches de ce que j'avais en tête. Je dois quand même lire attentivement toutes les réponses.
Giorgio

Réponses:

19

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 *:

  • pas d'opérateurs - tout est une fonction, mais les noms de fonction peuvent inclure des caractères spéciaux comme '+', '-' ou ':'
  • les points et les accolades peuvent être omis pour les appels de méthode à paramètre unique, c'est a.and(b)-à- dire qu'ils sont équivalents à a and bsous forme d'infixe
  • pour 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

    val file = new File("example.txt")
    
    withPrintWriter(file) {
      writer => writer.println("this line is a function call parameter")
    }
    

    withPrintWriterest une méthode simple avec deux listes de paramètres, contenant toutes deux un seul paramètre

  • les paramètres par nom vous permettent d'omettre une liste de paramètres vide dans lambdas, vous permettant d'écrire des appels comme myAssert(() => 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.

Péter Török
la source
1
+1: Intéressant. Cela signifie-t-il que pour étendre la syntaxe, je peux définir de nouvelles classes et que les signatures de méthode fourniront la nouvelle syntaxe comme sous-produit?
Giorgio
@Giorgio, fondamentalement oui.
Péter Török
Les liens ne fonctionnent plus.
Nate Glenn
13

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:

  • Acme :: Bleach pour un code vraiment propre
  • Acme :: Morse pour écrire en code morse
  • Lingua :: Romana :: Perligata pour écrire en latin (par exemple, les noms sont des variables et le nombre, la casse et la déclinaison changent le type du nom nextum ==> $ next, nexta ==> @next)

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 .

user40980
la source
13

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:

moveSegment :: [Token] -> Maybe (SegPath, SegPath, [Token])
moveSegment [hc| HC_Move_Segment(@s, @s); | s1 s2 ts |] = Just (mkPath s1, mkPath s2, ts)
moveSegment _ = Nothing

[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.

Thomas Eding
la source
3
Il existe également d'autres fonctionnalités de Haskell qui permettent essentiellement une syntaxe personnalisée, comme la création de votre propre opérateur ou le curry automatique couplé à des fonctions lambda (pensez par exemple à l'utilisation typique de forM).
Xion
Je pense honnêtement que le curry et les opérateurs personnalisés ne sont pas admissibles. Ils permettent d'utiliser correctement la langue, mais ils ne vous permettent pas d'ajouter / nouvelle / fonctionnalité à la langue. TH et QQ le font. Au sens strict, TH et QQ non plus dans le sens où ils font exactement ce pour quoi ils sont conçus, mais ils vous permettent vraiment de "sortir du langage" au moment de la compilation.
Thomas Eding
1
"Sinon, ce qui suit est considéré comme une extension de syntaxe ...": très bon point.
Giorgio
12

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:

proc loopCard23 {cardinalVar squareVar cubeVar body} {
    upvar 1 $cardinalVar cardinal $squareVar square $cubeVar cube

    # We borrow a 'for' loop for the implementation...
    for {set cardinal 0} true {incr cardinal} {
        set square [expr {$cardinal ** 2}]
        set cube [expr {$cardinal ** 3}]

        uplevel 1 $body
    }
}

Cela serait alors utilisé comme ceci:

loopCard23 a b c {
    puts "got triplet: $a, $b, $c"
    if {$c > 400} {
        break
    }
}

Ce type de technique est largement utilisé dans la programmation Tcl, et la clé pour le faire sainement sont les commandes upvaret uplevel( upvarlie une variable nommée dans une autre portée à une variable locale et uplevelexécute un script dans une autre portée: dans les deux cas, le 1indique 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.)

Associés Donal
la source
10

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:

  • De nombreuses langues anciennes pourvu des fonctions intégrées comme sin(), round(), etc., sans aucun moyen de mettre en œuvre votre propre.
  • C ++ fournit un support limité. Par exemple , certains mots - clés dans INTÉGRÉE comme moulages ( 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 pour lexical_cast<>(), polymorphic_cast<>(), any_cast<>(), ....
  • Java a des structures de contrôle intégré ( 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 exemple react{}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.)

2 tours
la source
2
Faux. Lisp vous permet en fait d'ajouter absolument n'importe quel type de nouvelle syntaxe. Comme dans cet exemple: meta-alternative.net/pfront.pdf - ce n'est rien d'autre qu'une macro Lisp.
SK-logic
Cela semble être implémenté dans un langage spécialement conçu pour la création de DSL . Bien sûr, vous pouvez créer un langage dans la famille Lisp qui offre également de telles fonctionnalités; Je voulais dire que cette fonctionnalité n'est pas une idée de base de Lisp prise en charge par les Lisps couramment utilisés (par exemple Scheme). Modifié pour clarifier.
Escargot mécanique
Ce "langage pour construire des DSL" est une collection de macros construites sur un Lisp assez typique et minimaliste. Il peut être facilement porté sur n'importe quel autre Lisp avec (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.
SK-logic
Et, Scheme n'est pas différent de Common Lisp, à partir de R6RS officiellement et officieusement pour les âges, car il fournit un (define-macro ...)équivalent, qui, à son tour, peut utiliser n'importe quel type d'analyse en interne.
SK-logic
8

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 ifet whilesi vous le désirez vraiment). Par exemple, ceci est une ifdéclaration:

if now/time < 12:00 [print "Morning"]

ifest 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":

SomeArray: [ [foo "One"] [bar "Two"] [baz "Three"] ]
foreach action SomeArray [action/1: 'print] ; Change the data
if now/time < 12:00 SomeArray/2 ; Use the data as code - right now, if now/time < 12:00 [print "Two"]

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.

Izkata
la source
7

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 .

knut
la source
13
La "syntaxe flexible" est TRÈS différente de la "syntaxe extensible". Cela fait longtemps que je n'ai pas programmé en Ruby, mais Rake ressemble à une utilisation bien conçue de la syntaxe intégrée. En d'autres termes, ce n'est pas un exemple.
Thomas Eding,
2
N'est-ce pas une question de degré, pas de nature? Comment distingueriez-vous un langage à "syntaxe extensible" qui peut étendre certains aspects de sa syntaxe mais pas d'autres, et un langage à "syntaxe flexible"?
comingstorm
1
Si la ligne est floue, repoussons la ligne pour que C soit considéré comme prenant en charge la syntaxe extensible.
Thomas Eding,
Pour faire le lien avec votre exemple, je tracerais la ligne ici: Un langage avec une syntaxe extensible peut faire du rake, qui ressemble à make. Un langage avec une syntaxe flexible peut être étendu (ha, beau mélange linguistique là-bas) pour compiler et exécuter des makefiles. Votre point sur la question du degré est cependant bon. Peut-être que certains langages permettent de compiler Make, mais pas Python. Et d'autres permettraient les deux.
Clayton Stanley
N'importe quelle langue complète doit être capable de traiter les fichiers Make. La flexibilité de la syntaxe n'est pas un facteur au-delà de la difficulté que cela représenterait.
Rig
7

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:

y ~ a + b

signifie "ajuster une ligne de la forme k0 + k1 * a + k2 * b aux valeurs en y".

y ~ a * b

signifie "ajuster une ligne de la forme k0 + k1 * a + k2 * b + k3 * a * b aux valeurs en y".

Etc.

Martin C. Martin
la source
2
Les transformées AST de Groovy sont très verbeuses par rapport aux macros Lisp ou Clojure. Par exemple, l'exemple Groovy de 20+ lignes sur groovy.codehaus.org/Global+AST+Transformations pourrait être réécrit en une seule ligne courte dans Clojure, par exemple `(this (println ~ message)). Non seulement cela, mais vous n'auriez pas non plus à compiler le pot, à écrire les métadonnées ou tout autre élément sur cette page Groovy.
Vorg van Geir
7

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.).

SK-logic
la source
Votre lien Scala indique explicitement que les macros proposées "ne peuvent pas changer la syntaxe de Scala". (Intéressant, il répertorie cela comme une différence entre cette proposition et les macros de préprocesseur C / C ++!)
ruakh
@ruakh, oui, c'est la même approche que Converge et Template Haskell - la macro application est explicitement marquée et ne peut pas être mélangée avec une syntaxe "normale". Mais, à l'intérieur, vous pouvez avoir n'importe quelle syntaxe que vous aimez, donc c'est sans doute une syntaxe extensible. L'exigence "non lisp", malheureusement, limite vos options à des langues comme celle-ci.
SK-logic
"Je l'ai même fait avec du C simple": Comment est-ce possible?
Giorgio
@Giorgio, bien sûr, j'ai modifié un compilateur (ajout de macros et d'une compilation de modules incrémentiels, ce qui est en fait assez naturel pour C).
SK-logic
Pourquoi l'accès à l'AST est-il nécessaire?
Elliot Gorokhovsky
6

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_catopérateur et le définit comme un prédicat pour vérifier si une liste contient l'atome cat:

:- op(500, xf, has_cat).
X has_cat :- member(cat, X).

?- [apple, cat, orange] has_cat.
true ;
false.

Le xfmoyen qui has_catest un opérateur postfixé; l'utilisation en fxferait un opérateur préfixe et en xfxferait 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.

Ambroz Bizjak
la source
5

TeX est totalement absent de la liste. Vous le savez tous, non? Cela ressemble à ceci:

Some {\it ``interesting''} example.

… 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:

Some \it[``interesting''] example.

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:

Some <it>“interesting”</it> example.

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:

\foreach \angle in {0, 30, ..., 330} 
  \draw[line width=1pt] (\angle:0.82cm) -- (\angle:1cm);

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):

for word in [Some interesting example.]:
    if word == interesting:
        it(word)
    else:
        word
Konrad Rudolph
la source
5

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:

  • Travailler avec un AST n'est pas aussi élégant que travailler avec des expressions s
  • Étant donné que la macro est invoquée au moment de la compilation, elle n'a pas accès aux données d'exécution.

Par exemple, voici comment vous pouvez implémenter une nouvelle structure de contrôle:

macro repeatLines(repeatCount as int, lines as string*):
    for line in lines:
        yield [| print $line * $repeatCount |]

usage:

repeatLines 2, "foo", "bar"

qui est ensuite traduit, au moment de la compilation, en quelque chose comme:

print "foo" * 2
print "bar" * 2

(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/ )

nikie
la source
1
La documentation Web pour cette fonctionnalité est actuellement en panne, et je n'écris pas Boo moi-même, mais je pensais que ce serait dommage si elle était négligée. J'apprécie les commentaires du mod et je reconsidérerai ma façon de contribuer à la gratuité des informations pendant mon temps libre.
Dan
4

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):

fuzzy[a_ && b_]      := Min[fuzzy[a], fuzzy[b]]
fuzzy[a_ || b_]      := Max[fuzzy[a], fuzzy[b]]
fuzzy[!a_]           := 1-fuzzy[a]
If[fuzzy[a_], b_,c_] := fuzzy[a] * fuzzy[b] + fuzzy[!a] * fuzzy[c]

Ceci annule l'évaluation des opérateurs logiques prédéfinis &&, || ,! et la Ifclause 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:

myIf[True, then_, else_] := then
myIf[False, then_, else_] := else
SetAttributes[myIf, HoldRest]

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 Dqui prend une expression et évalue sa dérivée symbolique.

nikie
la source
3

Metalua est un langage et un compilateur compatible avec Lua qui fournit cela.

  • Compatibilité totale avec les sources et le bytecode Lua 5.1: sémantique et syntaxe propres et élégantes, puissance expressive incroyable, bonnes performances, portabilité quasi universelle. - Un système de macro complet, similaire en puissance à ce qui est offert par les dialectes Lisp ou Template Haskell; les programmes manipulés peuvent être considérés
    comme du code source, comme des arbres de syntaxe abstraite ou comme un mélange arbitraire de
    ceux-ci, selon ce qui convient le mieux à votre tâche.
  • Un analyseur dynamique extensible, qui vous permet de prendre en charge vos macros avec une syntaxe qui se marie bien avec le reste de la langue.

  • Un ensemble d'extensions de langage, toutes implémentées en tant que macros Metalua régulières.

Différences avec Lisp:

  • Ne dérangez pas les développeurs avec des macros lorsqu'ils n'en écrivent pas: la syntaxe et la sémantique du langage devraient être mieux adaptées aux 95% du temps où nous n'écrivons pas de macros.
  • Encouragez les développeurs à suivre les conventions du langage: non seulement avec les "bonnes pratiques" que personne n'écoute, mais en proposant une API qui facilite l'écriture des choses à la manière de Metalua. La lisibilité par les autres développeurs est plus importante et plus difficile à atteindre que la lisibilité par les compilateurs, et pour cela, avoir un ensemble commun de conventions respectées aide beaucoup.
  • Pourtant, fournissez tout le pouvoir que vous êtes prêt à gérer. Ni Lua ni Metalua ne sont dans l'esclavage et la discipline obligatoires, donc si vous savez ce que vous faites, la langue ne vous gênera pas.
  • Rendez-le évident quand quelque chose d'intéressant se produit: toutes les méta-opérations se produisent entre + {...} et - {...}, et sortent visuellement du code normal.

Un exemple d'application est l'implémentation d'un filtrage de type ML.

Voir aussi: http://lua-users.org/wiki/MetaLua

Clement J.
la source
Bien que Lua ne soit pas un Lisp en effet, votre liste de "différences" est assez suspecte (le seul élément pertinent est le dernier). Et, bien sûr, la communauté Lisp ne serait pas d'accord avec une diatribe selon laquelle on ne devrait pas écrire / utiliser des macros 95% du temps - La manière Lisp penche vers quelque chose comme 95% du temps en utilisant et en écrivant des DSL, en plus des macros, de cours.
SK-logic
2

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é!

Bernat Romagosa
la source
1
Ça semble intéressant.
Thomas Eding
1

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 ).

liori
la source
1

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 .

Dave Kirby
la source
0

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

Thomas Eding
la source
pourquoi avez-vous publié ceci séparément au lieu de l'ajouter à votre réponse précédente ?
gnat
1
Parce que Funge n'a rien à voir avec Haskell.
Thomas Eding,
-1

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.

mike30
la source