Acid, le lisp-like de la communauté !

Créons notre langage de programmation ! Pour le fun !

a marqué ce sujet comme résolu.

Ok Folaefolc m'a conseillé un truc, maintenant ça marche chez nous deux. Avec le dernier commit ça marche Carnufex ?

J'ai aussi ajouté l'option -m sur les commandes d'exemple dans le message de la PR.

Edit: fixé un bug apparu lors du bugfix :colere:

+1 -0

Salut,

J'ai implémenté les chaînes et les caractères constants. On peut maintenant faire ça:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
(define add (lambda (x y)
    (+ x y)
))

// ligne de commentaire

/* bloc de commentaire */

(print "Le résultat est:\n")
(print (add 1 3))

Edit: Cependant je suis pas vraiment satisfait de ma méthode pour dé-echapper ma chaîne de caractère:

1
string = "saut de ligne:\\n".encode('latin-1').decode('unicode_escape')

Ça fonctionne dans ma console mais je pense que c'est pas vraiment portable.

+2 -0

Je viens de faire un tour sur le github. J'ai donc mergé les pull request (sauf celle de Folaefolc car il nous faudrais des avis ici) et j'ai ajouté les membres qui le souhaitait dans l'organisation.

Je remercie tout le monde pour les contributions.

+1 -0

Je commence à écrire le système de typage sur une nouvelle branche, et je me demandais si on pouvait encore changer quelques trucs dans la spec. Je ne sais pas si mon avis est partagé, mais je trouve que la notation est un poil trop verbeuse:

  • Pour la déclaration de type, actuellement on a (hastype type name). Personnellement j'opterai pour un symbole, parce que hastype c'est un peu chiant à la longue. Comme je suis pas inspiré je propose :: comme symbole mais libre à vous d'en trouver un autre. Aussi je trouve pas ça très naturel d'écrire en premier le type puis le nom. Dans mon idéal ça serait plutôt quelque chose comme (:: name type) du coup, parce que le nom sera souvent plus court que le type, et il est un peu effacé lorsqu'il est en fin de ligne.
  • Pour les tuples, je persiste à dire que (1, 2, 3) est plus clair que (tuple 1 2 3). Mais bon, c'est mon avis, faut voir ce qui plaît au plus de monde. Si on adopte cette syntaxe pour les tuples faudrait aussi adopter par souci de cohérence la syntaxe [1, 2, 3] pour les listes, au lieu de l'actuel [1 2 3] (j'ai un doute tout à coup, c'est pas dans la spec ça).
  • Ensuite c'est pas vraiment une histoire de verbosité, mais de logique: j'aurai tendance à écrire
1
2
3
4
5
6
(define if (lambda (cond tr fl)
    (match cond
        (True tr)
        (False fl)
    )
))

… au lieu de ça:

1
2
3
4
5
6
(define if (lambda (cond tr fl) (
    match cond (
        (True tr)
        (False fl)
    )
)))

En gros là le changement c'est qu'on oublie les parenthèses après le premier argument de match. Ça encore ça va, mais en fait je viens de me rendre compte que ce qui me gêne c'est surtout le fait que les parenthèses ne sont pas collées à match. Ça pour le coup c'est surtout une histoire de convention, pas de grammaire.

Edit: Encore un dernier truc: pour le type des lambdas, je propose plutôt la syntaxe (lambda (arg1-type arg2-type) return-type) au lieu de (lambda arg1-type arg2-type return-type). Je sais ça fait des parenthèses en plus mais au moins c'est cohérent avec la syntaxe pour déclarer des fonctions lambda (à savoir (lambda (arg1-name arg2-name) context)).

+1 -0

@Folaefolc: mon-argument-optionnel vaudrait quoi si on ne le fournissait pas ? Je suis personnellement très contre l'existence d'une valeur nil/null/None dans Acid. Ça dénaturerait totalement l'intérêt des types de données algébriques (peut-être pas totalement, mais grandement en tout cas).

Je viens de publier la première version d'atom-acid, le package Atom pour coder en Acid. Pour le moment, on a la coloration de la syntaxe, ainsi qu'une autocomplétion très basique, que j'améliorerai sûrement.

La coloration et l'autocomplétion

Pour l'installer, entrez juste la commande apm install atom-acid, et (re)lancez Atom. Si vous ouvrez un fichier .acid, ça devrait être tout coloré. :)

Pour la déclaration de type, actuellement on a (hastype type name). Personnellement j'opterai pour un symbole, parce que hastype c'est un peu chiant à la longue. Comme je suis pas inspiré je propose :: comme symbole mais libre à vous d'en trouver un autre.

Le :: est déjà utilisé pour les identifiants qualifiés. Par ailleurs, je préfère qu’on garde un mot-clé signifiant : si vraiment ça t’emmerde de le taper, rien ne t’empêche de mettre en place dans ton implémentation une option qui permet de mettre du sucre syntaxique sur cet aspect.

Aussi je trouve pas ça très naturel d'écrire en premier le type puis le nom. Dans mon idéal ça serait plutôt quelque chose comme (:: name type) du coup, parce que le nom sera souvent plus court que le type, et il est un peu effacé lorsqu'il est en fin de ligne.

Tu as raison. La forme de base, ce serait <identifiant> hastype <type>, donc en notation polonaise, ça devient logiquement hastype <identifiant> <type>. Je modifie ça dès que j’ai un instant, mais vous pouvez déjà le considérer comme adopté.

Pour les tuples, je persiste à dire que (1, 2, 3) est plus clair que (tuple 1 2 3). Mais bon, c'est mon avis, faut voir ce qui plaît au plus de monde.

Là dessus, c’est non. La demande majoritaire a été d’uniformiser ces syntaxes vers quelque chose qui n’utilise que des parenthèses et des espaces, comme c’est le cas pour tout en Lisp.

  • Ensuite c'est pas vraiment une histoire de verbosité, mais de logique: j'aurai tendance à écrire
1
2
3
4
5
6
(define if (lambda (cond tr fl)
    (match cond
        (True tr)
        (False fl)
    )
))

… au lieu de ça:

1
2
3
4
5
6
(define if (lambda (cond tr fl) (
    match cond (
        (True tr)
        (False fl)
    )
)))

En gros là le changement c'est qu'on oublie les parenthèses après le premier argument de match. Ça encore ça va, mais en fait je viens de me rendre compte que ce qui me gêne c'est surtout le fait que les parenthèses ne sont pas collées à match. Ça pour le coup c'est surtout une histoire de convention, pas de grammaire.

Pour la position des parenthèses, c’est une affaire de goût, je n’épilogue pas. En revanche, pour le fait de ne pas mettre de parenthèses autour du corps du filtrage, là, je suis contre. Ça casserait la cohérence avec la définition de types, où la liste de constructeurs est elle aussi entourée de parenthèses.

Edit: Encore un dernier truc: pour le type des lambdas, je propose plutôt la syntaxe (lambda (arg1-type arg2-type) return-type) au lieu de (lambda arg1-type arg2-type return-type). Je sais ça fait des parenthèses en plus mais au moins c'est cohérent avec la syntaxe pour déclarer des fonctions lambda (à savoir (lambda (arg1-name arg2-name) context)).

AlphaZeta

Vu que le langage ne permet pas d’application partielle en tant que telle, ça me paraît en effet une bonne idée. Pareil, je l’ajoute dès que possible.

A voir aussi pour rajouter la possibilité d'avoir des arguments optionnels en les marquant comme suit : (lambda &mon-argument-optionel body)

C’est totalement anti-idiomatique des langages fonctionnels de procéder ainsi. Ce qu’on fait habituellement, c’est définir un type Option a (c’est le nom en Rust, en Haskell, c’est Maybe a), avec deux constructeurs, None (Nothing) et Some a (Just a), et de passer des arguments de ce type quand ils peuvent être indéfinis.

En le gérant ainsi plutôt que par une fonctionnalité de langage, ça limite la complexité du compilateur / interpréteur, et ça peut même permettre d’en faire une monade d’erreur, bref, c’est très puissant, et totalement idiomatique.

Et aussi des valeurs par défaut, pour faire quelque chose comme : (lambda x (i 1) (+ x i))

Folaefolc

1
2
3
4
5
6
(lambda (x i) (
    match i (
        (Option::None (+ x 1)
        ((Option::Some a) (+ x a))
    )
))

C’est comme ça qu’on fait de manière idiomatique dans un langage fonctionnel. :)

Je viens de publier la première version d'atom-acid, le package Atom pour coder en Acid.

Bat'

Cool ! Dès que j’ai un peu de temps, je fais un pack pour gedit. ^^

+2 -0

'Soir,

J'ai besoin de conseils pour les pythonnistes ici: J'ai réécrit le parser car je n'en était pas satisfait (le code était répétitif). Du coup maintenant j'ai tout refactorisé mais le code utilise la méthode type.__subclasses__. Est-elle considérée par la communauté Python comme "propre", n'est-ce pas un peu trop overkill d'avoir recours à de la métaprogrammation pour un simple parser ? En attendant je suis plutôt satisfait du code maintenant, ça marche bien et c'est joli (c'est juste le __subclasses__ qui m'embête).

AZ.

PS: le code est ici.

Et merci pour les précisions Carnufex :)

+0 -0

Non en fait j'ai une méthode consume de ma classe Parser qui prend en argument le type du nœud AST. Par exemple si je veux consommer une expression, je fais parser.consume(Expr). Ma fonction consume répertorie toutes les fonctions qui consomment un nœud du même type (ou d'un type héritant de ce dernier) et teste toutes les possibilités.

Je m'exprime mal, tu comprendras sûrement mieux si je te donne le code précis:

Ma méthode qui utilise __subclasses__ est définie ici et je l'utilise .

Edit: C'est un problème que j'ai assez souvent: je me retrouve tout le temps en train d'utiliser des trucs de métaprogramming pour faire quelque chose de simple. Le code à la fin est quand même propre (je crois) mais c'est un peu moins lisible à moins de bien connaître ces notions (non pas que ça soit compliqué, mais il faut bien maîtriser pour comprendre l'utilisation dans un code qui n'est pas le nôtre).

+0 -0

En principe il vaut mieux faire la recherche opposée : les classes parentes d'un type donné plutôt que celle sur les classes qui dérivent d'un type de base. C'est pas naturel de reposer sur la connaissance de ses filles par une classe mère, ça viole un peu le principe d'inversion de dépendances.

Je montre exactement le pattern et l'astuce qui vont bien dans mon tuto sur le Dispatcher.

+0 -0

Je voudrais bien participer au projet, mais manque de compétence je ne peux pas.

Ducoup que dois-je apprendre pour aider à réaliser le projet ?

Arckazur

Salut ! Dommage que personne n'ait pris le temps de te répondre. Globalement, je pense que tu peux commencer par apprendre un langage fonctionnel, ça peut te faire découvrir une toute autre façon de programmer et améliorer ta capacité de raisonnement (à un point que tu n'imagines pas). Personnellement je conseille le Haskell. Beaucoup diront que c'est un langage très/trop difficile pour un premier pas dans le fonctionnel, et ils n'ont pas nécessairement tort. Toutefois, si tu prends le temps de maîtriser un tant soit peu le langage, tu pourras par la suite suivre le tutoriel (proposé plus tôt) Write Yourself a Scheme in Haskell, qui pour moi devrait être une référence en terme d'apprentissage de la compilation, tellement il te permet de créer un langage puissant avec finalement pas grand chose et de manière très pédagogue.

Les true hardcore qui connaissent les bails te proposeront une lecture de Compilateurs, principes, techniques et outils, mais non, juste non. Je l'ai, c'est un bouquin très intéressant, tout ce que tu as besoin de théorique pour te lancer est dedans (l'expérience n'est pas fournie avec malheureusement), mais le temps que tu le finisses (et que tu maîtrises hein), tes arrière-petits-enfants auront de la barbe.

Dominus Carnufex, attention : tu parles beaucoup "d'idiomes fonctionnels", mais les idiomes dont tu parles sont surtout des idiomes haskell. Par exemple, OCaml a une syntaxe pour des arguments optionnels souvent utilisée (qui est un sucre vers option, certes), et les différents dialectes de Lisp sont souvent radicalement de ce que tu présentes (par exemple, tout ce que tu fais avec les types algébriques, c'est tout sauf idiomatique : les lisp typés sont assez rares, c'est souvent des extensions peu utilisées).

Tout ça pour redire qu'il ne faut pas vous prendre la tête avec des détails aussi insignifiants que ", ou pas pour les tuples" : en dehors du fait que ça ne change pas grand chose, vous semblez avancer un peu à l'aveuglette (c'est normal, on ne peut pas tout savoir), et les grandes discussions de conception aboutissent donc souvent à des conclusions un peu bizarres.

My 2 cents : si vraiment vous voulez partir sur Lisp, oubliez complètement les types algébriques à la ML. Vous aurez toujours le temps de regarder ça plus tard, c'est un gros morceau et vous feriez mieux de vous concentrer sur autre chose pour l'instant. Pareil pour les tuples : en lisp, tout est liste.

Oubliez aussi les valeurs par défaut, ça n'a aucun intérêt. Idem pour tout ce qui est sucre syntaxique : peu importe que zlang soit verbeux, vous ne cherchez pas à programmer avec !

+1 -1

Autant pour les autres fois c'est vrai qu'on se prenait trop la tête, mais aujourd'hui on a bien avancé, je trouve: on a un "compilateur" fonctionnel pour Python, et j'ai codé une bonne partie du système de typage.

oubliez complètement les types algébriques à la ML. Vous aurez toujours le temps de regarder ça plus tard, c'est un gros morceau et vous feriez mieux de vous concentrer sur autre chose pour l'instant

Comme quoi ?

Ca dépend de qui : comme dit plusieurs fois, je ne pense pas que le projet à 15 soit la bonne façon de faire. Tu as l'air d'avancer plus vite, alors tu peux effectivement en profiter pour regarder maintenant des choses plus avancées : un peu de compilation vers du bas niveau, une machine virtuelle, du typage effectivement (mais pour le coup, la syntaxe du Lisp n'est pas très pratique pour exprimer ce genre d'idée : pourquoi pas un parser un peu plus costaud pour un mini-ML ?), des macros (tant qu'à faire du Lisp !)… Il y a plein de choses à faire, choisis celles qui t'intéressent :-) dans tous les cas, n'hésite pas à poser des questions sur la direction que tu prends : je pense qu'il vaut mieux être guidé dès le début plutôt qu'avancer au hasard. Par exemple, puisque tu parles de typage, c'est probablement une bonne idée de te documenter sur les bases des systèmes de type du lambda calcul (simplement typé, un peu de système F et F omega), puis sur le système de ML et l'algorithme W.

Pour les autres, ceux qui ont leur bac ou ceux qui procrastinent tout simplement, je pense qu'ils feraient mieux de travailler à leur rythme. À plusieurs éventuellement, mais en prenant bien le temps de comprendre toutes les étapes avant de s'attaquer à celles qui sont plus compliquées. Ca n'empêche évidemment pas de poster son avancement ici !

+2 -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