Seventh, le micro langage impératif de ZDS !

Parce que les lisp-like, c'est cool, mais on aime aussi l'impératif nous !

a marqué ce sujet comme résolu.

Je ne suis pas sûr qu'une syntaxe comme ça aide tellement pour le parsing : dans un langage fonctionnel comme Lisp, elle permet d'exprimer simplement et naturellement des applications de fonctions (et on n'a besoin de rien d'autre), mais ici ce n'est pas ce dont vous avez besoin. Vous avez besoin d'exprimer des affectations de variable, des séquences d'instruction et des structures de contrôle (conditions et boucles), et la syntaxe avec des parenthèses partout n'est pas spécialement plus adaptée qu'une syntaxe classique à la C avec des accolades (la preuve : vous avez quand même des affectations x=5, et tu es obligé d'avoir inventé une syntaxe ad hoc un peu foireuse pour les paramètres - que tu vas devoir parser aussi.)

Entendons-nous bien : la syntaxe n'est pas le point principal du langage, ce qui est important, c'est les idées qu'elle permet d'exprimer. Mais en l'occurrence, la syntaxe que vous avez choisie n'est pas vraiment adaptée à l'expression des concepts impératifs, c'est donc une bonne idée de réfléchir un peu à ces concepts pour trouver une façon plus naturelle de les écrire (et pas forcément plus compliquée - en plus vous pourriez en profiter pour apprendre PLY, tant qu'à faire). Par contre, clairement, un débat sur "est ce qu'il vaut mieux des accolades ou begin/end, et ; ou , pour les arguments" n'a strictement aucun intérêt. Un conseil seulement : évitez la syntaxe avec indentation comme en python, c'est un peu pénible à parser.

Banni

Je ne vois pas en quoi la syntaxe de lisp n'est pas adaptée. Par contre, l'intérêt de cette syntaxe est qu'elle est uniforme, et là ce que tu présentes ne l'est pas du tout.

Peut-être que vous pouvez faire un parser de "s-expressions" commun avec l'autre langage ?

Banni

Par exemple (my-var = + my-var 1) : pourquoi pas (= my-var (+ my_var 1)) ? On prend l'arbre syntaxique et on le met sous forme de liste, et c'est tout, aucun cas spécial.

edit : ça devient encore plus lourd, je sais pas si c'est bien non plus.

+1 -0

Le problème, c'est que vous mélanger syntaxe infix et prefix dans un dialecte (le lisp) qui a été pensé pour être prefix avant tout notamment avec (x = a)1. Cela peut notamment amener à des misconceptions sur ce qu'on peut imaginer du langage (comme se poser légitimement la question si il est possible d'écrire (5 + 5)).

Ensuite, je vais reprendre un vieux message2 qui vient de PdP qui explique succinctement l'idée que vous devriez vous faire de la syntaxe. Le post est très long mais je vous invite vraiment à le lire car c'est un bon début de piste en ce qui concerne le travail en rapport avec la syntaxe.

Je vais te dire pourquoi je n'ai pas regardé la syntaxe du langage - mais ne t'inquiète pas, j'ai fait aussi l'erreur.2

Dans le développement d'un langage informatique, quand on est débutant et qu'on lit les ressources tel que le Modern Compiler Implementation in ML3 ou des livres plus anciens tels que le Dragon Book4, le premier chapitre parle de lexique et de syntaxe. Grosso-modo, on commence déjà par décrire la syntaxe d'un langage informatique comme vous le faites ici.

Cette technique d'apprentissage est très bien quand on est supervisé. Elle a l'avantage de produire quelque chose de concret assez rapidement ce qui permet au lecteur d'avoir un résultat de son travail assez rapidement. Aussi, la première phase d'un compilateur/interpréteur est l'analyse lexical et syntaxique (ce qu'on nomme communément le front-end). Pourtant, cela n'en fait pas5 la partie la plus intéressant. Je dirais même que c'est un piège de commencer par cette partie dans le développement d'un nouveau langage.

Un front-end à la mano ?

J'ai un peu regardé le code disponible et cette remarque convient parfaitement dans le sens où vous vous amusez à faire à la main le front-end. Je juge pas de la qualité de votre code mais surtout de la direction prise dans le développement.

Alors l'analyse lexical et syntaxique, théoriquement c'est ce qu'on nomme un automate à état fini et un automate à pile respectivement. Si vous avez la curiosité de voir les liens, vous verrez que c'est pas super beau à voir dans le sens où ce sont des modèles dont l'explication est, je trouve, complexe pour un néophyte de l'informatique et dont l'implémentation n'est guère intéressant. Ce sont pourtant des modèles basiques que tout informaticien digne doit connaître (dans les grandes lignes). Donc vous devez les apprendre.

Vous pouvez voir une implémentation d'un automate à état fini ici par Russ Cox pour vous faire une idée de ce que c'est. En plus en cadeau, vous avez un tout petit automate à pile dans le lot ici (c'est la fonction re2post) — mais je vous invite à lire le cours de @nohar sur le sujet qui vous en apprendra toutes les subtilités. Enfin, si vous regardez bien, c'est une implémentation d'un moteur d'expression régulière — en réalité, c'est une application du théorème de Kleene qui dit que pour tout automate à état fini, on peut y associer une expression régulière. Alors, montrer comme ça, ça à l'air chiant. Et ça l'est comme le dénote ce commentaire dans les sources du compilation OCaml sur l'analyse lexical.

En réalité, dans tout langage digne de ce nom, on ne refait plus d'automate à état fini ou d'automate à pile pour créer le lexique et la syntaxe du langage parce que c'est chiant. On utilise des générateurs d'automate à état fini et automate à pile qui sont respectivement pour le C Flex et GNU Bison — tu as les équivalents en OCaml avec ocamllex et menhir ou ocamlyacc (@nohar à la rescousse pour Python). Et ces outils, on les utilise partout comme PHP par exemple ou OCaml avec son propre outil. Il faut savoir que ces outils ont une portée historique, en faite il existe encore aujourd'hui les outils lex et yacc qui ont servit à implémenter le premier compilateur C (donc un vieux compilateur), ce sont des outils qui ne viennent pas de nul part. Conventionnellement, lors d'un bootstrap du langage, on lui associe ce genre d'outil portant quasiment le même nom. C'est pour cette raison que vous retrouverez pour la quasi-totalité des langages un équivalent à lex et yacc en plus ou moins évolué comme c'est le cas en OCaml6.

Ce que je veux dire par là, c'est qu'il faut utiliser ces outils. Non pas parce que c'est moi qui le dit mais parce qu'ils sont fait exactement pour ça, pour créer des langages informatiques. Faire le front-end à la main n'a qu'un intérêt didactique qui reste très limité car vous n'arriverez certainement pas à faire une implémentation équivalente à ce que produit Flex et GNU Bison autant sur la performance que sur la fiabilité, la productivité et surtout la modularité (et si vous voulez rajouter une nouvelle construction au langage ?). C'est factuel. BLABLABLA COURS DE THIZANNE BLABLABLA7.

Prendre la bonne direction

Même si la plupart des cours (je dirais même tous) commence par l'implémentation d'un front-end, lors de l'implémentation d'un nouveau langage informatique, ce n'est pas dans cette direction qu'il faut partir. Ce qui fait un langage informatique, ce n'est pas la syntaxe et le lexique, c'est la sémantique.

Pour reprendre un exemple simple, ce qui fait que vous comprenez le mot patate, ce n'est pas la juxtaposition de ses lettres, non plus la liaison entre celle-ci mais la définition du mot patate. C'est cette définition qui est importante, qui délivre l'idée. Tu peux apprendre à un gamin de 2 ans que pour décrire une carotte, il faut dire patate et quand il demandera des carottes, il demandera des patates (en tout cas, le concept lié à ce que nous considérons comme des patates) — ça ne reste qu'un mot, et c'est bien pour cette raison qu'il est différent de la langue française et anglaise quand bien même le concept reste le même.

Ce que je veux dire, c'est qu'importe qu'on est le mot-clé define ou =, l'idée derrière reste la même, à savoir l'assignation d'une valeur à une variable.

Ce qui m’amène au centre de l'explication. C'est que ce n'est pas la syntaxe qui dirige ton langage mais la sémantique. Définir d'abord les concepts de ton langage (comme le paradigme impératif) va vous permettre de vous diriger vers une syntaxe spontané (une syntaxe pensé pour vos concepts). Et plus haut, j'ai dit avoir fait la même erreur — et c'est se que vous faites en vérité. Comme vous, j'ai voulu créer mon langage mais dans l'exercice8 que l'on m'avait proposé, on m'avait imposé une syntaxe proche du lispcomme vous. Ensuite, au fur et à mesure, je me suis mis à implémenter le concept de programmation fonctionnel. Non pas parce que j'aime la programmation fonctionnelle (je ne la connaissais pas avant) mais parce que la syntaxe du lisp dérive d'une sémantique de langage fonctionnel. Ce n'est pas moi qui est choisi d'en faire un langage fonctionnel, c'est la syntaxe.

Ce que je veux dire au final, c'est que partir sur la syntaxe directement va obligatoirement vous fermer des portes sur des concepts. par exemple, partir sur une syntaxe comme le C exclut la programmation fonctionnelle — ou tout du moins, essayer d'intégrer un tel concept ne donnerais pas une syntaxe spontané — et c'est le cas par exemple avec Python et ses closures9. Et de cette expérience, je juge un langage d'abord par les concepts qu'il implémente et ensuite par sa syntaxe (car elle est tout de même importante10) et c'est pour cette raison que je n'ai pas regardé la syntaxe de votre langage.

Conclusion

Si vous deviez donc faire un nouveau langage, vous devriez partir sur les concepts qu'il propose avant — et pour le coup, vous commencer à avoir une idée claire. Ensuite, vous éluderez une syntaxe de manière spontané (qui serait proche d'un langage ou non comme le C). C'est pourtant un travail difficile et c'est pour cette raison que les langages syntaxiquement sont proches - on a toujours besoin d'un pied à terre. Mais puisque que syntaxiquement ils sont proches, ils sont aussi sémantiquement proches.

Enfin, j'aimerais que vous portiez votre attention sur un débat sur ce que peut être un langage informatique français (autre lien). Cela présente surtout une extension du débat syntaxe/sémantique visant particulièrement l'idée que l'on peut avoir de la simplicité (toute subjective) d'un langage — et ma conclusion est qu'un langage informatique français n'est pas forcément synonyme d'un langage informatique simple, cette simplicité doit se retrouver dans la sémantique.

EDIT: il doit y avoir des fautes, mais là, je suis très fatigué :(


  1. d'ailleurs, je comprends pas l'intérêt de = puisque (define x a) peut être tout autant équivalent sémantiquement mais avec une syntaxe à la lisp

  2. j'ai déjà mentionner ces sources comme étant ce que vous devriez lire avant tout mais puisque le site est inaccessible, je me permets de mettre le contenu ici. 

  3. il y a aussi la version Java et C 

  4. le livre passe pas mal de temps sur la syntaxe d'ailleurs mais c'est un vieux livre que je ne conseille plus de lire 

  5. certains ont considéré que ça pouvait être intéressant mais je le pense pas du tout. C'est très théorique (PEG, LL, LR, LALR, LALR(k)), très chiant et ça peut être très compliqué 

  6. en effet, menhir est un très bon générateur de parser que je vous conseille vivement d'utiliser 

  7. on le redira jamais assez 

  8. là aussi je fais encore référence à cet exercice 

  9. j'adore ce lien 

  10. en vérité, pour peu que le créateur du langage reste raisonnable (ce qui n'est pas le cas en C++ par exemple), pour moi, la syntaxe n'a pas de grande d'intérêt. Je ne pourrais dire que la syntaxe d'OCaml et meilleur que celle d'Haskell parce qu'il y intervient souvient une notion de subjectivité. Par contre, je pourrais parler du polymorphisme en OCaml et de l'évaluation paresseuse en Haskell. Ce sont des points intéressants et c'est un débat sur la sémantique. 

ah, donc c'est en lien avec le fonctionnement des opérateurs. pour ça on peut s'arranger dans ce cas ^^ et arriver à une syntaxe comme (my-var = my-var + 1) assez facilement. je test et je te redis ça

edit: ca fonctionne ;)

Folaefolc

Et on perd toute la facilité de parsing du Lisp, parce qu'on a des trucs comme (my-var = my-var + 2 * 3). Non à la rigueur vous pouvez effectivement écrire l'AST directement avec des parenthèses, et ce sera effectivement très facile à parser (mais ça demande de le faire vraiment, donc (= my-var (+ 1 (* 2 3))) pour l'exemple précédent. Je pense aussi qu'il faut un opérateur explicite pour les séquences d'instructions, mais il est tard et j'ai la flemme de regarder si c'est vraiment ambigüe sans. Je répète ce que j'ai déjà dit dans l'autre sujet : que la syntaxe soit pénible à écrire, on s'en fiche, elle n'est pas là pour ça ; donc dans le fond pourquoi pas. Mais ne faites pas un truc intermédiaire, ça va juste vous compliquer la vie pour rien :-)

Pourquoi faire un truc si compliqué/peu commun ? J'ai pas trop le temps de regarder les détails de ta syntaxe mais je trouve ça lourd pour rien. Si j'étais toi, déjà, je partirai sur un pseudo basic avec des délimiteurs clairs et ne travaillant que sur des entiers dans un premier temps.

Par exemple un truc comme ça (fortement inspiré du Pascal mais sans typage explicite puisque toutes les variables sont des entiers) :

1
2
3
4
5
6
7
8
9
function factorielle(n)
begin
  if n <= 1 then
    factorielle = 1
  else
    factorielle = n*factorielle(n - 1);
end;

print(factorielle(10))

Si déjà tu parse ça, tu aura une bonne base de langage impératif simple et clair et tu pourra l'étendre. Et fondamentalement c'est pas très compliqué à parsé puisques les blocs sont délimités par des begin/end et les instructions par des points virgules.

Pour le résoudre : 2 possibilités : soit on interdit plus d'un statement par ligne, soit on dit qu'un if va toujours avec un else quand on en trouve un

En fait en pascal ça se résoud facilement parce qu'un bloc de plus d'une instruction est dans un begin/end. La notation est la suivante : if <cond> then <instruction> [else <instruction>]. Comme toute instruction (sauf la dernière ligne) ça doit se terminer avec un ; (dernier caractère de la ligne 6).

Bon maintenant pour les <instruction> tu as deux possibilités : soit c'est juste une instruction et tu peux la mettre direct (comme dans mon exemple), soit il fait plusieurs lignes et tu met un bloc:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
function foo(n)
begin
  if n <= 1 then
    foo = 1
  else
  begin
    foo = n*foo(n - 1);
    foo = foo*2
  end;
end;

A partir du moment que tu comprend qu'une instruction de plus d'une ligne est forcément entre begin/end et que ça forme une instruction, c'est facile.

Je pense que le pascal est un bon langage pour débuter. La syntaxe reste facile a parser et les codes clairs.

Tu fais comme tu veux mais je pense que tu devrais vraiment pas te prendre la tête a court terme. Selon moi pour commencer tu devrais juste définir :

  • Comme définir une fonction,
  • L'affectation,
  • Les opérations arithmétiques de bases,
  • une fonction print,
  • Eventuellement un if/else

Pour le reste, tu verra plus tard, ça se rajoutera.

Le pascal que je te propose a l'avantage de rester simple a parser (car il y a pas mal de délimiteurs). Il y a effectivement une difficulté, c'est que les opérateurs ne sont plus infix mais vu que tu nous a dis déjà avoir fais un parseur de lisp, ça fait un petit challenge! Je pense que c'est largement faisable.

Je suis tout à fait d'accord avec Dinosaure, oublions la syntaxe pour l'instant. Réfléchissons plutôt à ce que notre langage peut faire, d'abord dans les grandes lignes (par exemple : paradigme impératif…), puis plus précisément (par exemple définition de fonctions, variables, écrire à l'écran, if/else, pourquoi pas structure de données…). On pourra revenir sur la syntaxe plus tard.

Sinon, je vais surement suivre son conseil et utiliser Flex et Bison (y a-t-il des portages plus adaptés pour le C++ ?).

EDIT : Est-il raisonnable de parler de l'orienté événement que Gabbro avait proposé ? Ça avait l'air rigolo. :)

EDIT 2 :

Sinon, je vais surement suivre son conseil et utiliser Flex et Bison

Cela dit, il faudra que je regarde cette histoire d'automate à états finis et à pile, un de ces jours.

EDIT 3 : Je viens de penser à un truc: que pensez-vous de boost.spirit ? Devrais-je l'utiliser comme alternative à Flex + Bison ?

+3 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte