Acid, le lisp-like de la communauté !

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

a marqué ce sujet comme résolu.

J'ai commencé mon parser en C++, et je commence par le lexer : j'ai créé un lexer générique qui peut s'utiliser avec un autre langage qu'Acid, mais je n'ai pas encore commencé à rédiger le code propre à Acid. Pour l'instant, je n'ai pas créé de dépôt sur Github, je le ferai dès que j'ai le temps.

En attendant, je poste le code ici si quelqu'un veut l'utiliser. A moi d'utiliser ma magie noire :diable: (non, je rigole rien de compliqué là dedans) ! Bref, trève de parlote :

Lexer.hpp :

 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
#include <map>
#include <regex>
#include <functional>

// TODO : WRITE DOC !!!

template<typename TokenTraits>
class Lexer
{
public:
    explicit Lexer(std::istream& is)
    : m_is(is)
    , associations{}
    {   
    }

    Lexer(Lexer const&) = delete;
    Lexer& operator=(Lexer const&) = delete;

    Lexer(Lexer&&) = delete;
    Lexer& operator=(Lexer&&) = delete;

    virtual ~Lexer()
    {
    }

public:
    using Tokens = TokenTraits::Tokens;

    using Token = Tokens;

public:
    Token popToken();

    Token getCurrentToken() const
    {
        return m_currentToken;
    }

public:
    // Todo : find a better name for this.
    std::map<std::regex, Token> associations;

private:
    virtual Token handleSpecialLexem()
    {
    }

private:
    std::istream& m_is;

    Token m_currentToken;
};

Lexer.inl :

 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
// TODO : WRITE DOC !!!

#include "Lexer.hpp"

#include <string>

Lexer::Token Lexer::popToken()
{
    auto popped = getCurrentToken();

    std::string word{};

    if(!(m_is >> word))
    {
        if(is.eof())
        {
            m_currentToken = Tokens::EndOfFile;
        }

        return popped;
    }

    for(auto const& element : associations)
    {
        if(std::regex_match(word, element.first)
        {
            m_currentToken = element.second;

            return popped;
        }
    }

    m_currentToken = handleSpecialLexem();

    return popped;
}

un exemple de TokenTraits :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
struct TokenTraits
{
    enum class Tokens
    {
        Lambda,
                ...,
        EndOfFile,
        Other,
        Unknown
    }
}

Si vous voulez des explications, n'hésitez pas à demander !

P.S : Avec un peu de chance, peut-être que ce message attirera des amateurs de C++ sur le sujet ;) D'ailleurs, peux-tu ajouter le tag C++ dans le sujet ?

EDIT : nohar a montré de trucs cool en python, il fallait bien que je défende de mon mieux C++ :p

EDIT 2 : J'ai renommé le paramètre template en TokenTraits, car c'est plus une classe de traits qu'une classe de politiques

+3 -0

J'ai changé l'architecture de mon parser suite aux conseils de nohar. J'ai de nouveau fait une PR mais ya un conflit donc j'ai pas les droits suffisants pour merger. Les modifications que j'ai fait prennent en compte les tiennes @the_new_sky. Tu pourrais merger ?

+1 nohar. Je me tâte moi-même pour faire un truc en Rust.

Sinon, mes enfants, je suis encore un peu trop occupé IRL pour m’en occuper dans l’immédiat, mais jeudi soir si je suis pas trop en miettes, ou sinon vendredi, je vous ponds une spec plus complète que l’actuelle, avec des explications annexes sur le pourquoi dans un message du forum.

Ah, et j’ai déjà écrit un programme complet qui suit cette spec et emploie une bonne partie des possibilités du langage. Je le publierai à ce moment-là.

+11 -0

N'ayant pas encore eu le temps de mettre les mains sur un ordi pendant assez longtemps pour implémenter quoi que ce soit, je me suis gratté la tête pendant quelques minutes sur le langage en question pour concevoir un compilateur et estimer les points de frottement principaux.

Comme on dit, "il n'y a que les cons qui ne changent pas d'avis" : le langage Acid, tout comme Lisp, a tellement peu de formes syntaxiques différentes qu'il est beaucoup plus facile de produire un AST Acid qu'un AST Python.

Voyons un peu les tokens possibles :

  • ( et ) : les délimiteurs de liste,
  • Int : une constante entière comme -1 ou 42
  • Decimal : une constante décimale comme 3.14
  • Str : une chaîne constante délimitée par ". C'est peut être le token le plus tricky à parser puisqu'il faut gérer les symboles échappés.
  • Keyword : un mot-clé comme lambda, define ou match.
  • Id : un identifieur quelconque, à savoir une suite de caractères qui ne contient pas du blanc, ni un des précédents tokens.

Par contre, lexeur doit définir au moins deux états : un état par defaut qui remplit une liste, et un état secondaire dans lequel on se situe lorsque l'on lexe les commentaires (donc entre // et \n ou entre /* et */).

Et… C'est tout !

Les AST sont également très simples : un programme en Lisp n'est rien d'autre qu'une liste dont les éléments sont des expressions, et une expression est une liste, un id ou une constante.

Pas besoin de ply ou de lex/yacc ou même de parseur à automates pour ça.

Contrairement à Dominus je pense qu'il faut gérer les chaînes de caractères nativement, parce que c'est ce que tous les langages un peu sérieux et pratiques font, et que ça va beaucoup simplifier les choses si vous pouvez manipuler nativement des chaînes unicode sans vous prendre la tête sur le détail des encodages.

Après une première phase d'analyse sémantique, vous pouvez qualifier certaines listes :

  • List(Keyword(match) Expr List [List...]) est une construction match,
  • List(Keyword(define) Id Expr) est une affectation,
  • List(Keyword(lambda) List List) est une définition de lambda,
  • List(Id Expr [Expr...]) un appel de fonction…

Autant de constructions qui vont enrichir vos AST après une première analyse.

Ce qui me fait peur, c'est le moment où vous allez vouloir introduire du typage dans le langage. Ça me semble assez bonbon à concevoir.

PS : quoique… Au moins vous n'avez pas de type abstrait. :)

+2 -0

Je ne suis pas spécialement contre un AST Acid, mais dans ce cas, il faut que quelqu'un s'occupe de coder un module pour effectuer la traduction.

Sinon, j'ai moi aussi activé mon cerveau pendant que je ne pouvais pas coder, et il y a des trucs qui ne me plaisent pas dans mon lexer. Je m'occuperai de ça aujourd'hui.

+0 -0

Je pense que l'on peut mettre les deux, car Emeric a proposé l'idée de base et j'ai ensuite proposé le Lisp-like, le nom, l'organisation, qui s'efforce de maintenir le Github…

Sinon, quelqu'un à des propositions pour la syntaxe de l'AST d'Acid ?

+1 -2

C'est bien ce qui me semblait, mais je n'étais pas certain et j'avais la flemme de vérifier. ^^

Maintenant que chaque César a récupéré ce qui lui appartenait, on peut se remettre à parler du projet. :)

EDIT : A propos, the_new_sky, peux-tu m'ajouter à l'organisation pour que je puisse créer un dépôt pour le C++ ? Je m'occuperai de le maintenir (ça devrait pas être compliqué, je suis le seul contributeur pour l'instant ^^).

EDIT 2 : Il serait temps de choisir une licence. Que dîtes-vous de MIT ? Pour un projet comme celui-ci, ça me paraît particulièrement adapté.

+0 -0

@nohar: J'ai pas trop compris pourquoi tu voulais séparer les lexèmes en listes pour ensuite identifier la nature de ces listes (appel de fonction, définition, etc…). Personnellement j'ai juste à balancer la liste de lexèmes et mon parser gère ça directement :°

Quel avantage apporte ta méthode ?

Pas de licence américaine, elles ne sont pas valables en France. Il faut prendre une des CeCIll ou bien l’EUPL.

Dominus Carnufex

Dans ce cas, la Cecill-B me paraît pas mal, qu'en penses-tu ?

@nohar: J'ai pas trop compris pourquoi tu voulais séparer les lexèmes en listes pour ensuite identifier la nature de ces listes (appel de fonction, définition, etc…). Personnellement j'ai juste à balancer la liste de lexèmes et mon parser gère ça directement :°

AlphaZeta

Ce n'est pas ce qu'il a dit. Ce qu'il montre, c'est justement les éléments d'un AST Acid, donc ce que générerais ton parser.

Je suis d'accord pour gérer les chaînes nativement, soit dit en passant.

+0 -0

@nohar: J'ai pas trop compris pourquoi tu voulais séparer les lexèmes en listes pour ensuite identifier la nature de ces listes (appel de fonction, définition, etc…). Personnellement j'ai juste à balancer la liste de lexèmes et mon parser gère ça directement :°

AlphaZeta

Ce n'est pas ce qu'il a dit. Ce qu'il montre, c'est justement les éléments d'un AST Acid, donc ce que générerais ton parser.

Je suis d'accord pour gérer les chaînes nativement, soit dit en passant.

mehdidou99

"List" dans mon post désigne un (type de) noeud de l'AST. C'est la nature même de Lisp (éthymologiquement List processor et non pas Lots of Irritating Stupid Parentheses), que de baser sa syntaxe sur des listes. Or l'AST doit traduire la syntaxe, donc modéliser exactement ce qui est écrit à quitte à être transformé plus tard pour donner du sens à cette syntaxe.

+0 -0

Sinon, il y a ça comme licence sympa, que j'ai trouvé par hasard sur un plugin Sublime Text :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
  DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE

  Version 2, December 2004

  Copyright (C) 2016 Pierre-Louis Caron-Auger [pierre-louis@outlook.com]

  Everyone is permitted to copy and distribute verbatim or modified copies of this license document, and changing it is allowed as long as the name is changed.

  DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0.You just DO WHAT THE FUCK YOU WANT TO.

^^

+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