Acid, le lisp-like de la communauté !

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

a marqué ce sujet comme résolu.

Je vais intervenir une deuxième fois (même si j'ai promis de ne plus intervenir) mais force est de constater que la majorité des personnes qui ont été à l'initiative de ce projet on, encore, besoin d'un n-ième conseil afin de décanter ce qui pourrait être un bonne objectif pour cette atelier.

Je pense que je peux donc pointer 2 posts qui (et je ne pense pas me tromper) réaffirme se qu'à dit Eusèbe:

  • Mon méga long post où vous devriez lire la deuxième partie et la conclusion (et la première pour votre culture): ici
  • Le post très succinct de Lz36GQfANCkOchnWu2yv qui donne, selon moi, la meilleur piste pour tout le monde dans cette atelier: ici

Alors je vais éclaircir un point. Le dernier sujet qui s'est terminé par l'initiative de the_new_sky qui s'auto-proclama1 auprès de Emeric gérant du projet a surtout montré un problème de fond que j'ai tenté d'expliquer avec les bonnes formes2 comme vient de le faire Eusèbe3 et dont il résulte un constat simple.

Vous vous trompez de question.4

Et à ceci, je réponds que la véritable question de cette atelier est:

Comment comparer des langages informatiques ?5

Ce qui m'énerve profondément, c'est que même après mon mega-post bien formé, même après la proposition de pistes concrètes (comme commencer avec LE COURS DE THIZANNE6), même après l'intervention répété de plusieurs personnes qui partagent dans les grandes lignes le même propos (ces mêmes personnes ayant l'expérience dans le domaine), même après 2 sujets qui font chacun une dizaine-quinzaine de pages, on retourne à ce point fatidique.

Je vais pas me répéter ici, ça sert rien puisque certaines personnes ne veulent tout simplement pas entendre. Mais pour ceux qui veulent vraiment aboutir à un atelier communautaire sur l'implémentation d'un langage informatique, j'interviens juste pour rappeler le passé existant sur le ZdS mais aussi sur d'autres sites comme progmod.org ou progdupeu.pl ou même le SdZ sur lesquels vous trouverez TOUTES les informations nécessaires à propos de cette atelier.

Donc je le redis, on est pas là pour vous tacler, on est là pour vous montrer la bonne direction par rapport à notre expérience sur ces sujets. Et je trouve ça véritablement dommage mais surtout obstiné et insolent7 de votre part de continuer dans cette voie — une voie qu'on sait pertinemment qu'elle ne va pas fonctionner.

PS: j'écris beaucoup donc pour pas faire la même erreur que dans mon ancien post qui était long, je décante avec des notes en bas de page


  1. à vous de considérer ce propos comme péjoratif ou pas, c'est en tout cas factuel 

  2. comme on nous reprochait d'être violent dans nos propos, notamment sur IRC 

  3. dont je ne partage pas forcément tout son comportement cependant 

  4. je le mets en gros et vous pouvez le retrouver dans mon post mais personne n'en a véritablement fait une remarque pertinente ou non — mais j'accepte l'idée que cette dernière s'est dilué dans mon large propos 

  5. propos qui ne vient pas de moi mais de bluestorm dont l'atelier ressemble étrangement au votre mais dont j'ai été le seul à pointer 

  6. on l'a dit, je ne sais combien de fois mais il y a toujours des gens trouvent le moyen de dire, blablabla ocaml blablabla c'est nul blablabla on peut mieux faire sans même regarder — mais c'est d'autant plus affligeant que pas mal de personne s'accordent sur cette même idée de commencer par ce cours mais que personne ne s'y applique 

  7. le mot est fort mais il décrit réellement mon propos pour le coup. Cela peut paraître insultant mais c'est d'autant plus insultant de suivre ce projet, d'y participer, de se rendre compte que personne ne prends en compte son point de vue8 et de se rendre compte 10 pages plus tard que vous en venez à la même opinion 

  8. qu'on ne prenne pas en compte mon point de vue ne me dérange, c'est surtout la suite qui me dérange 

Alors je vais éclaircir un point. Le dernier sujet qui s'est terminé par l'initiative de the_new_sky qui s'auto-proclama auprès de Emeric gérant du projet (à vous de considérer ce propos comme péjoratif ou pas, c'est en tout cas factuel).

Dinosaure

Pour te répondre je me considère pas m'être auto-proclamé gérant de quoique ce soit. Le sujet d'Emeric (qui n'intervenait plus depuis quelques temps dessus) semblait, à mes yeux, tourner en rond avec des personnes qui voulaient des choses différentes, j'étais au départ, contre cette idée de séparation, mais forcer est de constater que c'était une - bonne ou mauvaise - solution pour relancer le sujet. Préférant l'idée d'un Lisp-like fonctionnel, comme beaucoup d'entre nous (et je n'étais pas le premier), je fondait ce premier groupe. Et c'est ici que je veux en venir, je n'ai jamais interdit personne de fonder un autre groupe. Je considérais le sujet d'Emeric comme le sujet principal.

Et si ma démarche déplaisait à Emeric, il m'aurait contacté puis nous aurions convenu ensemble d'une solution qui irait dans son sens. (Si c'est le cas, je l'invite à le faire)

Si prendre l'initiative de passer à l'action c'est s'auto-proclamer gérant je me dit que tout le monde ici l'est…

Alors, soit je pense qu'il y a eu une incompréhension sur ma démarche, soit je me retrouve obligé de claquer la porte de ce sujet car je n'y suis pas le bienvenu.

Donc je le redis, on est pas là pour vous tacler, on est là pour vous montrer la bonne direction par rapport à notre expérience sur ces sujets.

Personnellement, j'apprécie ce genre de message quand ils restent agréable à lire. La critique constructive peut l'être, des messages comme celui dont je réponds précédemment, pas du tout.

Donc j'apprécie globalement ton message. Et je remercie aussi Eusèbe pour son avis dans son dernier message que je partage particulièrement.

+2 -1

Bon, je rentre de week-end, je vois que ça a bien discuté…

les sucres syntaxique optionnels risques de faire un sacré bordel. Chaque implémentation va en avoir une partie mais pas forcément toutes. Du coup ça sera compliqué de savoir ce qui peut être utilisé dans les différentes implémentations et de se partager les codes de tests pour comparaison. Ne serait-il pas du coup plus pertinent d'imposer deux versions possible : acid avec tout ce qui est obligatoire et (nom temporaire) acid++ qui contient en plus tout ce qui est dans les sucres syntaxique. En gros deux versions différentes mais pas de choses optionnels.

C’est pas idiot. Ça impliquera de réécrire la spec pour enlever ces morceaux-là, et le surtout le code d’exemple qui en utilise, mais vous pouvez déjà partir sur une implémentation du cœur.

il faudrait probablement définir clairement les opérations supporté nativement et leurs caractères. En gros à part l'addition qui est utilisé dans la spec on ne sait pas si on doit implémenté la soustraction, la puissance, les opérations sur les bits, etc. Et on ne sais pas non plus leur identifiant (la puissance par exemple est ^ dans certains langages et ** dans d'autres).

Kje

Mon idée générale, c’est que les opérations supportées nativement sont celles qui ne peuvent pas être raisonnablement implémentées en Acid. Ça regrouperait les quatre opérations mathématiques de base, quelques opérations sur les flottants (sin, cos, ln, etc.) et les opérations bit-à-bit. A priori, même la puissance n’a pas vraiment besoin d’être gérée nativement.

Mais bon, je verrai pour l’inclure dans la spec.

d'ailleurs, @carnufex, t'as pas pensé à implémenter call/cc :p ?

Folaefolc

Wut ? o_O

+0 -0

@the_new_sky: Tu entends quoi par "le tout" ?

AlphaZeta

J'entend tenter une implémentation incomplète et sale pour voir ce que je peut faire et surtout comment mieux le faire. C'est vrai que je n'était pas clair ;)

the_new_sky

Ce n'est pas directement lié au sujet, mais tu t'embarques dans quelque chose de probablement difficile que tu crois : notamment, une implémentation "incomplète et sale" du système de types que vous avez décrit (qui ressemble à celui de ML, décrit avec les mains) va te demander un peu de boulot (intéressant, mais ne t'attends pas à pouvoir faire ça à l'arrache en une heure de python). C'est aussi pour ça que revenir à une cible plus raisonnable serait une bonne idée.

Si jamais quelqu'un aurait besoin de vraiment écrire du Acid un jour, je me suis lancé dans la création d'un petit package Atom pour colorer le code, et peut-être plus tard faire des suggestions. J'en suis au tout début, on a de la couleur que pour le mot-clé lambda et pour les nombres entiers … :-°

Coloration du Acid dans Atom

Je mettrai ça sur Github quand j'aurai un peu plus avancé.

@carnufex : l'usage de call with current continuation, j'ai découvert ça récemment, et le concept est quand même vraiment puissant.

sinon je vous met un petit interpréteur codé en 30 minutes (très inspiré du travail de Peter Norvig bizarrement :-° même si pour une fois j'ai tout sorti de tête) :

  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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
TOKENS = {
    "left": "(",
    "right": ")",
    "comment": ";",
    "left_comment": "/*",
    "right_comment": "*/"
}


class Procedure(object):
    def __init__(self, params, body, envi):
        for elem in params:
            require(isinstance(elem, list), SyntaxError, "Missing brace arround argument '%s'" % elem)
        self.params, self.body, self.env = params, body, envi

    def __call__(self, *args):
        return eval_code(self.body, Env(self.params, args, self.env))


class Env(dict):
    def __init__(self, parms=(), args=(), outer=None):
        super().__init__(self)
        self.outer = outer
        try:
            self.update(dict(parms))
            parms_keys = tuple(dict(parms).keys())
        except ValueError:
            parms_keys = tuple([i[0] for i in parms])
        self.update(zip(parms_keys, args))

    def __xor__(self, other):
        new = Env()
        for key in other:
            if key not in self:
                new[key] = other[key]
        return new

    def __and__(self, other):
        new = Env()
        for key in other:
            if key in self and key in other:
                new[key] = self[key]
        return new

    def __getitem__(self, var):
        return dict.__getitem__(self, var) if (var in self) else None

    def find(self, var):
        require(var in self or self.outer is not None, KeyError, "'%s' does not exist (with type : %s)" % (var, type(var).__name__))
        if var in self:
            return self[var]
        elif self.outer is not None:
            return self.outer.find(var)


def op(_op, *x):
    require(bool(x), ArithmeticError, "Missing arguments")
    require(_op in ("div", "mul", "xor", "or", "and", "mod"), ValueError, "Operation is not listed")
    tot = x[0]
    for i in x[1:]:
        if _op == "div":
            tot /= i
        elif _op == "mul":
            tot *= i
        elif _op == "xor":
            tot ^= i
        elif _op == "or":
            tot |= i
        elif _op == "and":
            tot &= i
        elif _op == "mod":
            tot %= i
    return tot


def create_env():
    _env = Env()
    _env.update({
        "+": lambda *x: sum(x), "-": lambda *x: sum(-i for i in x),
        "/": lambda *x: op("div", *x), "*": lambda *x: op("mul", *x),
        "%": lambda *x: op("mod", *x), "pow": lambda x, p: x ** p,
        "^": lambda *x: op("xor", *x), "|": lambda *x: op("or", *x),
        "&": lambda *x: op("and", *x), "~": lambda x: ~x,
        ">>": lambda x, dc: x >> dc, "<<": lambda x, dc: x << dc,
        "symbol": lambda *x: " ".join(x)
    })
    return _env


def read_from_tokens(tokens):
    require(bool(tokens), SyntaxError, "Unexpected EOF while reading")
    token = tokens.pop(0)
    require(token != TOKENS["right"], SyntaxError, "Unexpected '%s'" % token)

    if token == TOKENS["left_comment"]:
        while tokens[0] != TOKENS["right_comment"]:
            tokens.pop(0)
        tokens.pop(0)
        token = tokens.pop(0)

    if token == TOKENS["left"]:
        ast = []
        while tokens[0] != end_token:
            ast.append(read_from_tokens(tokens))
        tokens.pop(0)
        return ast
    elif token == TOKENS["comment"]:
        pass
    else:
        return atom(token)


def atom(token):
    if token == '#t':
        return True
    elif token == '#f':
        return False
    try:
        return int(token)
    except ValueError:
        try:
            return float(token)
        except ValueError:
            try:
                return complex(token.replace('i', 'j', 1))
            except ValueError:
                return str(token)


def to_string(x):
    if x is True:
        return "#t"
    elif x is False:
        return "#f"
    elif isinstance(x, str):
        return x
    elif isinstance(x, str):
        return '"%s"' % x.replace('"', r'\"')
    elif isinstance(x, list):
        return '(' + ' '.join(map(to_string, x)) + ')'
    elif isinstance(x, complex):
        return str(x).replace('j', 'i')
    else:
        return str(x)


def print_schemestr(expr):
    if expr:
        print(to_string(expr))


def require(expr, err_kind, err_msg):
    if not expr:
        raise err_kind(err_msg)


def tokenize(code):
    for tok in TOKENS.values():
        code = code.replace(tok, " %s " % tok)
    return code.split(" ")


def parse(code):
    tokens = tokenize(code)
    parsed = None
    if '(' in tokens and ')' in tokens:
        parsed = read_from_tokens(tokens)
    require(parsed is not None, SyntaxError, "Missing brackets")
    return parsed


def evaluate(source, environment):
    while True:
        if isinstance(x, str):
            return env.find(x)
        elif not isinstance(x, list):
            return x
        elif x[0] == "quote":
            require(len(x) >= 2, ValueError, "Missing arguments")
            (_, *exp) = x
            return ' '.join(x)
        elif x[0] == "match":
            require(len(x) >= 3, ValueError, "Missing arguments")
            (_, cond, *patterns) = x
            val = evaluate(cond, environment)
            for (pattern, new_code) in patterns:
                if val == evaluate(pattern, environment):
                    return evaluate(new_code, environment)
            return None
        elif x[0] == "lambda":
            require(len(x) == 3, ValueError, "Missing arguments")
            (_, params, body) = x
            return Procedure(params, body, environment)
        elif x[0] == "if":
            require(3 <= len(x) <= 4, ValueError, "Missing arguments")
            if len(x) == 4:
                (_, test, conseq, alt) = x
                x = conseq if evaluate(test, environment) else alt
            elif len(x) == 3:
                (_, test, conseq) = x
                if evaluate(test, environment):
                    x = conseq
        elif x[0] == "define":
            require(len(x) == 3, ValueError, "Missing arguments")
            (_, var, *exp) = x
            require(var not in environment.keys(), RuntimeError, "Can not overwrite existing variable, use set! instead")
            tmp = evaluate(exp, environment)
            require(tmp, RuntimeError, "Impossible to create the value")
            environment[var] = tmp
            return None
        elif x[0] == "set!":
            require(len(x) == 3, ValueError, "Missing arguments")
            (_, var, *exp) = x
            require(var in environment.keys(), RuntimeError, "Can not overwrite non existing variable, use define instead")
            tmp = evaluate(exp, environment)
            require(tmp, RuntimeError, "Impossible to create the value")
            environment[var] = tmp
            return None
        elif x[0] == "begin":
            for exp in x[1:]:
                evaluate(exp, environment)
            x = x[-1]
        else:
            if not isinstance(environment.find(x[0]), str):
                exps = [evaluate(exp, environment) for exp in x]
                proc = exps.pop(0)
                if isinstance(proc, Procedure):
                    x = proc.body
                    env = Env(proc.params, exps, proc.env)
                else:
                    return proc(*exps)


def loop(prompt="ZLang"):
    p2_entire_line = "> "
    p2_unfinished_line = "' "
    code = ""
    env = create_env()
    while True:
        if code.count(TOKENS["left"]) == code.count(TOKENS["right"]):
            if not code:
                code = input(prompt + p2_entire_line)
            try:
                print_schemestr(evaluate(parse(code)))
            except Exception as exc:
                print(type(exc).__name__, ":", exc)
        else:
            code += "\n" + input(prompt + p2_unfinished_line)


if __name__ == '__main__':
    loop()

Folaefolc

Sa va beaucoup m'aider, merci bien !

Eusèbe : normalement sa ne sera pas bien complexe, mais je préfère vous dire ce que j'ai fait à la fin sinon on va me rediriger vers quelque chose. Mais je lance bien dans quelques chose d'intéressant, c'est sûr.

+2 -0

Salut,

J'ai implémenté un petit compilateur (pas vers un code machine bien entendu, mais vers un bytecode Python). Vous pouvez voir ça ici.

J'ai aussi donné la possibilité de dumper le code object obtenu dans un fichier pour faire vrai compilateur mais bon c'est pas super utile :P

AZ.

Edit: C'est encore super basique, j'ai pas encore implémenté de typage, y'a pas d'ADT ni d'alias de type et il me manque pas mal d'éléments de la spec. Je pense bosser dessus prochainement.

+0 -0

C’est peut-être moi qui suis complètement teubé, mais je n’arrive pas à le faire fonctionner. Quand je fais…

1
python3.4 acid --help

… comme suggéré dans le README, j’obtiens l’erreur suivante.

1
2
3
4
5
6
7
8
Traceback (most recent call last):
  File "/usr/lib/python3.4/runpy.py", line 170, in _run_module_as_main
    "__main__", mod_spec)
  File "/usr/lib/python3.4/runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "acid/__main__.py", line 13, in <module>
    from acid import *
ImportError: No module named 'acid'
+1 -0

Étrange… Je n'arrive pas à reproduire le bug o_O

Tu est bien dans le dossier racine de PyAcid ?

Edit: Aussi j'ai changé la commande dans le message de la PR, mais ça devrait pas changer le problème.

Edit 2: Folaefolc m'a aussi signalé un autre bug, il a testé sous Windows. Visiblement y'a quelques soucis. Je vais essayer de voir à quoi c'est dû…

Pour info je suis sous Linux Mint, si quelqu'un est aussi sous Mint il peut me dire s'il arrive à reproduire le bug ? Ubuntu ou Debian feront aussi l'affaire je pense.

+0 -0

J'ai changé les imports dans mon __main__.py, ça t'embête de réessayer ?

J'utilise pas souvent les __main__, c'est possible que j'ai mal compris d'où exactement le programme était exécuté. Par contre je m'explique pas pourquoi ça marche chez moi et pas chez vous…

Edit: Folaefolc a réussi à fixer le bug en ajoutant l'option -m à la commande python. C'est vrai que normalement on doit faire ça pour exécuter un package mais pour moi ça marche avec ou sans :-°

Edit 2: En fait non il était pas sous la bonne version du repo…

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