Seventh, le micro langage impératif de ZDS !

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

L'auteur de ce sujet a trouvé une solution à son problème.
Auteur du sujet

Salut à tous !

Nous sommes (je pense ne pas dire de bêtise) un petit nombre (au moins !) à penser que faire un micro-langage, comme le proposait ce sujet, impératif serait plus simple pour les débutants.

Et bien voilà !

La FAQ

tout le monde l'aime la faq après tout :D

Syntaxe

On peut s'étriper dessus longtemps, et ça j'en suis certains (je me suis beaucoup pris la tête dessus, vous zinquiétez po').
C'est pourquoi j'ai concocté une syntaxe (9 en réalité, mais celle ci me plait plus, cf la suite pour comprendre) :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
test = 10 + (12 - (14 * 16))
print << "42?"
test # devrait être -202
my-var = 10
my-list = [1 2 3 4 5 6]
print << "premier élement de my-list"
my-list @ 0
print << "my-list @ 0 et my-list@0 fonctionnent de la même manière !"
print << "my list" my-list
# commentaire
print << "test" "de" "plusieurs" "chaines"
print << "affichage de my-var"
my-var
a-func = function :first :last (:first / :last)
print << "utilisation de la fonction a-func et affichage de son résultat"
a-func << my-var 12
print << "test de if"
if (my-var - 2) (my-var = my-var - 2)
print << "test de while"
while (my-var < 15) (a-func << my-var 12) (my-var = my-var + 1) (print << my-var)

Patapez ! J'explique :

  • ça ressemble au lisp, car on a des () again ! Horreur ! => et ben oui et non je dirais, si j'ai gardé ce concept c'est parce que ça facilite grandement le parsing !

  • << sert à quoi ? => ça référence un appel de fonction. IE: (function-name << args)

  • les trucs :first dans ta fonction, c'est quoi ? => ça défini les arguments. J'ai trouvé intéressant de leur donner une syntaxe différente pour bien les différencier (par conséquent, pour les utiliser on les appelle de la même manière -dans le scope de la fonction-)

  • la déclaration de fonction est très différente des variables ! => oui, ça permet (encore) de faciliter le parsing (même si sans le mot clé function ça aurait pris quoi, 3 lignes de plus je dirais), et en plus à la lecture, c'est plus clair !

  • y a pas de types O_o ? => et non, et je trouve que ce message de Dinosaure explique bien la situation

Pour les inspirations que j'ai eu : SmallTalk, R, Arc (un lisp like orienté événements), C

Une autre syntaxe très différente (et n'exprimant pas les idées de la même manière par conséquent) a été proposée cependant (par Kje) :

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

(inspiré de Pascal)

Et pour les curieux, voici la liste des différentes syntaxes que j'avais élaboré :

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
MODELE (python):

myvar = 10
def afunc(first, last):
    return first / last
afunc(myvar, 12)
if myvar - 2:
    myvar -= 2
while myvar < 15:
    afunc(myvar, 12)
    myvar += 1

******************************************************************************************** 1

| my-var a-func |
my-var := 10
a-func := new Object
    with: first, last
    body:
    return: first / last
a-func << my-var, 12
[my-var := my-var - 2] ifTrue: my-var - 2
[
    a-func << my-var, 12
    my-var := my-var + 1
] whileTrue: my-var < 15

******************************************************************************************** 2

| my-var a-func |
my-var << 10
a-func << function :first :last {return first / last}
a-func(my-var 12)
if my-var - 2 {my-var << my-var - 2}
while my-var < 15 {
    a-func(my-var 12)
    my-var << my-var + 1
}

******************************************************************************************** 3

$my-var = 10
function $a-func (first last)
    return first / last
end
$a-func($my-var 12)
if $my-var - 2
    $my-var = $my-var - 2
end
while $my-var < 15
    $a-func($my-var 12)
    $my-var = $my-var + 1
end

******************************************************************************************** 4

my-var <- 10
a-func <- function(first, last) {
    return first / last
}
a-func(my-var, 12)
if (my-var - 2) {
    my-var <- my-var - 2
}
while (my-var < 15) {
    a-func(my-var, 12)
    my-var <- my-var + 1
}

******************************************************************************************** 5

my-var := 10
a-func := function(first, last) {
    return (first / last)
}
a-func << my-var, 12
if << my-var - 2 {
    my-var := my-var - 2
}
while << my-var < 15 {
    a-func << my-var, 12
    my-var := my-var + 1
}

******************************************************************************************** 6

($my-var 10)
($a-func (lambda first last (/ first last)))
(a-func my-var 12)
(if (- my-var 2) (- $my-var 2))
(while (< my-var 15) (begin (a-func my-var 12) (+ $my-var 1)))

******************************************************************************************** 7

(my-var = 10)
(a-func = function :first :last (/ first last))
(a-func << my-var 12)
(if (- my-var 2) (my-var = - my-var 2))
(while (< my-var 15) (a-func << my-var 12) (my-var = + my-var 1))

******************************************************************************************** 8

begin(
    my-var = 10
    a-func = function
    a-func:first
    a-func:last
    a-func = return(first / last)

    if(my-var - 2):begin(
        my-var = my-var - 2
        )
    while(my-var < 15):begin(
        a-func(my-var, 12)
        my-var = my-var + 1
        )
    )

******************************************************************************************** 9

my-var = 10
def a-func(first last)
    return first / last
end
if my-var - 2
    my-var = my-var - 2
end
while my-var < 15
    a-func(my-var 12)
    my-var = my-var + 1
end

Comment ça s'passe ?

  • comme pour acid, je propose de procéder par étapes (pour rien griller, quand même hein ;)) :

    • on discute autour de la syntaxe et on s'étripe :pirate:, elle ne va plaire à tout le monde, mais retenez bien que j'ai choisi cette syntaxe pour sa facilité de parsing !

    • on agrandi la spec, qui est actuellement … inexistante ahah (enfin, on voit function, if, et while comme mots clés, et c'est déjà suffisant pour un début je pense !)

    • on code. Chacun dans le langage qu'il veut, je vais faire une organisation github as soon as possible pour regrouper tout ça

Qui participe ?

Tout le monde est le bienvenu ! Tous les avis sont à prendre :) Après peut être que vous ne coderez pas le module pour Seventh, mais vous y aurez participé ;)

La philosophie

continuons de nous étriper

Impératif ! (note pour le futur: agrandir cette partie pour mieux en expliquer les raisons, mais actuellement, le lien dit tout :D)

Liens

Pour l'implémentation que j'ai fait en python, je me suis aidé de ceci :

Le groupe github : https://github.com/ZLang-ZdS

L'implémentation python : https://github.com/ZLang-ZdS/PySeventh

Comment parser vers l'ast python : https://zestedesavoir.com/forums/sujet/6129/acid-le-lisp-like-de-la-communaute/?page=4#p111735

Merci à vous d'avoir lu !

Édité par Cithoran

Ma chaine YouTube ! | Seventh, un micro langage communautaire ! | Mon projet : Unamed (en pleine reprogrammation en C++11/SFML2.4) | Mon tuto sur Pygame !

+13 -0

J'avoue que l'impératif, pour débuter, ça me botte plus que le fonctionnel ! Là je suis sur mon portable, mais je commenterai ta spec sans doute demain.

Plus on apprend, et, euh… Plus on apprend. | Coliru, parfait pour tester ses codes sources !

+2 -0

Même à long terme, envisager l'OO ne me paraît pas être une bonne idée (sauf si on s'achète tous un livre sur l'architecture d'un compilateur). :)

Plus on apprend, et, euh… Plus on apprend. | Coliru, parfait pour tester ses codes sources !

+1 -0

Cette réponse a aidé l'auteur du sujet

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.

+3 -0
Auteur du sujet

en fait, je trouve que l'usage de () est sympa car ca permet de délimiter des blocs, et donc le parsing en est hautement simplifié. bon après, c'est peut être une question de point de vu, mais (perso) c'est pas gênant (lourd à la longue, mais c'est juste pour apprendre que l'on fait ça)

sinon j'ai oublié de le dire … mais en réalité ça fait 2 jours que je bosse sur Seventh, et … c'est fonctionnel (ça marche quoi, sinon c'est impératif xD) !

au programme pour le moment :

  • nombres, string, bool, array

  • if, while, function (paramètres aussi)

  • opérations de base (+, -, /, , *, ^, &, ~, |, rshift, lshift, fichiers, qqs fonctions de la lib comme zip, min, max …)

  • appel de fonctions maison / de base

  • création de variables

  • commentaires (les multilignes bug encore un peu)

bug connu : les lignes sont mal comptées (c'est comme si tout était sur la ligne 1)

disponible pour tester ici : https://github.com/ZLang-ZdS/PySeventh

Ma chaine YouTube ! | Seventh, un micro langage communautaire ! | Mon projet : Unamed (en pleine reprogrammation en C++11/SFML2.4) | Mon tuto sur Pygame !

+0 -0

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 ?

+2 -0

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.

Édité par blo yhg

+1 -0
Auteur du sujet

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

Édité par Cithoran

Ma chaine YouTube ! | Seventh, un micro langage communautaire ! | Mon projet : Unamed (en pleine reprogrammation en C++11/SFML2.4) | Mon tuto sur Pygame !

+0 -0

Cette réponse a aidé l'auteur du sujet

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. 

Édité par Dinosaure

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

+4 -0
Auteur du sujet

Salut Dinosaure, la syntaxe change beaucoup en ce moment, actuellement (mais pas pushé) on doit faire 5+5 et + 5 5. sauf que … ça court-circuite en beauté la possibilité de faire 5+5+6+7+8

à la base je suis parti bille en tête en me disant : si ça ressemble pas au C, c'est pas de l'impératif (grosse erreur !)

je vais tenter de simplifier ça :

(my-var = 4) => my-var = 4

édit monoligne : Eusèbe, penses tu que si j'adopte cette syntaxe, ce soit un bon choix de placer les tokens () lors du tokenizing ? de sorte à pas d'embêter trop. (si c'est non, je chercherai une autre façon de parserr, peut être en utilisant l'attribut Line de mes tokens (c'est un peu crade je trouve ça))

(mais on peut envisager (define my-var 4) si ça plaît pas)

je pense tout de même garder les () pour les blocs, c'est vachement intéressant comme délimiteur (mais comme vous le dîtes tous les 2, on perd la facilité de parsing de lisp avec la notation des opérateurs actuelle (je rappelle : 5+5 et plus + 5 5)

sinon je vais poser des concepts sûrement cet aprem, ou demain (après le bac de français écrit) parce que oui, c'est très très le brouillon la syntaxe

enfin, Dinosaure va sûrement me taper, mais ma raison pour écrire tokenizer et parseur moi même est la même que la création de ce langage : se planter et apprendre ^^ premier gros problème que j'ai déjà pu voir (mais réglé "facilement") : le parseur doit TOUJOURS avoir une liste de tokens en argument. et une liste de listes de tokens fera tout planter. alors que dans ykar, un Lisp like fait par mes soins, liste de listes de listes de listes … ne fera pas du tout planter le bousin ! bref, faut revoir tout ça (à voir si je propose pas 2 syntaxes (et là on choisira plutôt personnellement : laquelle on préfère, mais les deux voudront dire la même chose au fond))

Édité par Cithoran

Ma chaine YouTube ! | Seventh, un micro langage communautaire ! | Mon projet : Unamed (en pleine reprogrammation en C++11/SFML2.4) | Mon tuto sur Pygame !

+0 -0
Staff

Cette réponse a aidé l'auteur du sujet

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.

+6 -0
Auteur du sujet

Effectivement, c'est bien plus simple x)

Sauf qu'ici tu introduits un premier problème : le dangling else

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

donc :

1
2
3
4
5
if a then if b then print "ok" else print "bad"
# deviendrait au parsing :
if a then (if b then print "ok" else print "bad")
# et non
if a then (if b then print "ok") else print "bad"

pour la délimitation des blocs, je suis entièrement pour !

et les ; ça va aider un max je pense ^^

logiquement, je peux facilement tokenizer ça. pour ce qui est du parsing, ça prendra plus de temps pour trouver une "logique" (dans le sens où, par exemple, on a toujours un mot-clé en premier token (je considère dans cet exemple qu'un identifiant de variable est un mot-clé, même s'il sera traité dans un if à part lors de l'évaluation))

Ma chaine YouTube ! | Seventh, un micro langage communautaire ! | Mon projet : Unamed (en pleine reprogrammation en C++11/SFML2.4) | Mon tuto sur Pygame !

+0 -0
Staff

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.

+2 -0
Auteur du sujet

@com

Pour en finir avec les parenthèses, j'ai plusieurs solutions :

  • Seventh-C (parce que oui, y a eu un Seventh-A et un Seventh-B :P) :
1
2
3
4
5
6
my-var = 10 # commentaire
print << my-var "hello world"
a-func = function :param1 :param2 (:param1 / :param2)
a-func << my-var 12
if (my-var < 10) (print << "oh non !")
while (true) (print << "boucle " my-var " boucle") (print << "autre ligne de code dans le while")

(c'est toujours très très la fantaisie, je sais)

  • Eleventh (je l'ai nommé comme ça car y a eu un Tenth, mais ressemblant énormément à Acid (usage massif de parenthèses dans un esprit très lispique), j'ai pensé que c'était pas intéressant / innovant, dans le sens où on reprendrait le code (ou on serait tenté de le faire) d'Acid) : la syntaxe proposée par Kje

Qu'on se fixe donc rapidement (parce que les 2 syntaxes expriment les mêmes idées, et que même si une syntaxe c'est important à définir, on va passer 5 ans dessus : on sera jamais d'accord de toute façon) : votez +1 pour seventh-c, -1 pour Eleventh (sur ce message)

ps: perso je vote largement -1, ça va être très intéressant d'implémenter ce pascal like

ps2: @Kje : pour les opérations, on aura tout le temps quelque chose comme ça : operande1 operateur operande2 ? (avec la possibilité que operande1 et/ou operande2 soit une "liste" dans ce style : (operande1bis operateurBis operande2bis) où les mêmes contraintes s'appliquent à operande1bis et operande2bis, ce qui peut donner : ((42 % 7.8) * ((12 / 5) + 4)) + 112.124) * (0 - 789.123654)

je vais me renseigner sur pascal ^^

note ps2: pour les nombres négatifs pour le moment je préfère avoir 0 - nb. c'est peut être peu clair, mais au parsing on aura pas de problèmes au moins

Édité par Cithoran

Ma chaine YouTube ! | Seventh, un micro langage communautaire ! | Mon projet : Unamed (en pleine reprogrammation en C++11/SFML2.4) | Mon tuto sur Pygame !

+1 -0
Staff

Cette réponse a aidé l'auteur du sujet

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.

+2 -0

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 ?

Édité par mehdidou99

Plus on apprend, et, euh… Plus on apprend. | Coliru, parfait pour tester ses codes sources !

+3 -0
Auteur du sujet

Je continue de porter Seventh (version n°C - #laPrivateJoke), et les opérations du style (10 + (14 - ((147.852 % 79.654) / 42))) * (my-var - (142 + 47)) fonctionnent :)

Sinon j'ai mon tokenizer et un début de parser pour Eleventh (pas totalement fonctionnel le parser, ok)

Ma chaine YouTube ! | Seventh, un micro langage communautaire ! | Mon projet : Unamed (en pleine reprogrammation en C++11/SFML2.4) | Mon tuto sur Pygame !

+1 -0
Vous devez être connecté pour pouvoir poster un message.
Connexion

Pas encore inscrit ?

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