execution de mon petit langage

a marqué ce sujet comme résolu.

bon, voila le bilan : J’AI IMPLEMENTE LES FONCTIONS(la recursion n’est pas encore disponible):

def pow(n) {
  n*n
}

@pow(4)

et oui pour appeler une fonction il faut commencer par @

Pourquoi es tu sur l’édition 2018 ? À moins d’avoir une bonne raison, tu as autant de passer tout de suite sur 2021. Les éditions viennent principalement avec des changements de confort donc autant en profiter. Là en l’occurrence tu as notamment les captures partielles dans les closures, et l’élimination des bare trait objects qui sont pas mal.

fix :)

Tu n’as pas besoin de extern crate depuis longtemps (l’édition 2018 justement), la doc de lalrpop n’est pas à jour (ça pourrait mériter une PR).

si, j’en ai besoin parce que sans il y a cette erreur: error: cannot find macro 'lalrpop_mod' in this scope

Tu devrais corriger les warnings de cargo check. Bon là t’en as deux qui viennent de lalrpop, ça pourrait aussi mériter un ticket pour qu’ils ajoutent les bons attributs si ces warnings sont pas corrigeables facilement.

presque tout fix(a part deux ou trois que je verrai plus tard)

Tu devrais jeter un coup d’œil à cargo clippy (malheureusement aussi pollué par lalrpop, ça mérite aussi un ticket pour qu’ils ajoutent les bons attributs, probablement un #![allow(clippy::all)]). Il signale plusieurs maladresses dans ton code comme des return, des ? ou des clone inutiles. De manière générale, exploite le fait que tout est une expression en Rust

a tiens non dans le mien mais plutot celui de lalrpop

Tu devrais utiliser cargo fmt.

ok je l’utilise

T’en qu’à faire, tu devrais utiliser un outil genre rust analyzer qui pointe du doigt les problèmes au fur-et-à-mesure que tu codes et offre des corrections rapides, c’est pratique. À minima, cargo fix t’aiderait pas mal à améliorer la qualité du code sans faire d’énormes efforts. Le chemin du fichier source est en dur dans ton main, c’est pas très pratique. Tu devrais accepter un argument en ligne de commande (pour info, cargo run — args passe les args à ton programme) avec le chemin du fichier source à compiler.

pas encore fait mais bientot ;)

Ton exemple de fichier source est pour le moins surprenant, il ne fait rien et a même une branche while avec autre chose qu’un booléen en argument (et qui du coup n’est pas exécutée alors qu’elle a l’air infinie). C’est pas terrible. Tu pourrais pas donner un exemple qui sort quelque chose de plus intéressant, même si c’est un bête compteur ?

je l’ai fait vraiment vite mais j’ai des choses plus importants dans tlang a faire

T’en qu’à faire, tu devrais utiliser un outil genre rust analyzer qui pointe du doigt les problèmes au fur-et-à-mesure que tu codes et offre des corrections rapides, c’est pratique. À minima, cargo fix t’aiderait pas mal à améliorer la qualité du code sans faire d’énormes efforts.

fix :D

Le chemin du fichier source est en dur dans ton main, c’est pas très pratique. Tu devrais accepter un argument en ligne de commande (pour info, cargo run — args passe les args à ton programme) avec le chemin du fichier source à compiler.

en vrais je voulais juste voir si ca marche je ferai une command ou un repl apres

Tu pourrais avoir intérêt à faire une lib utilisée par main pour que les appels dans main soit plus simple. Il est plus usuel d’avoir un main presque vide qui appelle une lib et une lib qui essentiellement se contente de donner la structure des mod et exposer l’interface publique. Ça rend l’écriture de tests plus facile (tu devrais commencer à en écrire d’ailleurs, histoire de pas risquer de tout casser sans t’en rendre compte).

ok, je vais le faire prochainement

Dans ton match expr dans Vm::evalexpr, tu as une branche pour capturer ce que tu n’as pas encore implémenté. C’est une mauvaise idée parce que ça empêche le compilo de te dire que tu as oublié une branche si tu ajoutes une Expr. Si tu as des Expr dont l’évaluation n’est pas implémentée (comme Function), fais une branche avec todo!(). C’est la façon standard de marquer un truc que tu vas implémenter, et tu auras une erreur au runtime si tu va dans cette branche. Un outil comme cargo fix ou rust analyzer fera ça pour toi. Comme son type est !, ça se coerce au type de ton match si c’est une expression. D’ailleurs, un truc qui aurait du te mettre la puce à l’oreille est que le message d’erreur est absurde : tu peux pas avoir une "unknown expression" alors que le système de type te garantie que expr est l’une des variantes que tu t’es donné.

cargo fix n’a rien modifie mais ok, je le ferai plus tard (dans pas longtemps mais pour l’instant j’ai impl tout les cas )

La fonction Vm::eval_many_expr est surprenante : si ton programme était wrappé dans un Block tu pourrais le traiter comme n’importe quelle autre Expr.

fix :)

executer n’est pas un mot anglais.

zut ! bon pour l’instant je le garde telle qu’elle

+0 -0

bon, voila le bilan : J’AI IMPLEMENTE LES FONCTIONS(la recursion n’est pas encore disponible):

def pow(n) {
  n*n
}

@pow(4)

et oui pour appeler une fonction il faut commencer par @

C’est pas pushé, si ? De manière générale en regardant ton historique Git, tu devrais faire des commits beaucoup plus atomiques. Git scale très bien même avec des centaines de milliers de commits donc faut pas hésiter à les garder simple, et aussi à pusher souvent.

Tu n’as pas besoin de extern crate depuis longtemps (l’édition 2018 justement), la doc de lalrpop n’est pas à jour (ça pourrait mériter une PR).

si, j’en ai besoin parce que sans il y a cette erreur: error: cannot find macro 'lalrpop_mod' in this scope

Ben faut l’importer dans le scope quand même. Le patch suivant fonctionne sans problème.

diff --git a/build.rs b/build.rs
index 3dcbed9..67422ff 100644
--- a/build.rs
+++ b/build.rs
@@ -1,5 +1,3 @@
-extern crate lalrpop;
-
 fn main() {
     lalrpop::process_root().unwrap();
 }
diff --git a/src/main.rs b/src/main.rs
index 2d80123..1247203 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,7 +1,7 @@
 mod tree;
 mod executer;
 use std::fs;
-#[macro_use] extern crate lalrpop_util;
+use lalrpop_util::lalrpop_mod;
 
 lalrpop_mod!(pub tlang); // synthesized by LALRPOP

Tu devrais jeter un coup d’œil à cargo clippy (malheureusement aussi pollué par lalrpop, ça mérite aussi un ticket pour qu’ils ajoutent les bons attributs, probablement un #![allow(clippy::all)]). Il signale plusieurs maladresses dans ton code comme des return, des ? ou des clone inutiles. De manière générale, exploite le fait que tout est une expression en Rust

a tiens non dans le mien mais plutot celui de lalrpop

C’est noyé dans la sortie de lalrpop mais tu as en plein dans executer.rs. Tu peux piper la sortie dans Vim (ou ce que tu veux) pour la lire plus facilement cargo clippy 2>&1 | vim -.

Dans ton match expr dans Vm::evalexpr, tu as une branche pour capturer ce que tu n’as pas encore implémenté. C’est une mauvaise idée parce que ça empêche le compilo de te dire que tu as oublié une branche si tu ajoutes une Expr. Si tu as des Expr dont l’évaluation n’est pas implémentée (comme Function), fais une branche avec todo!(). C’est la façon standard de marquer un truc que tu vas implémenter, et tu auras une erreur au runtime si tu va dans cette branche. Un outil comme cargo fix ou rust analyzer fera ça pour toi. Comme son type est !, ça se coerce au type de ton match si c’est une expression. D’ailleurs, un truc qui aurait du te mettre la puce à l’oreille est que le message d’erreur est absurde : tu peux pas avoir une "unknown expression" alors que le système de type te garantie que expr est l’une des variantes que tu t’es donné.

cargo fix n’a rien modifie mais ok, je le ferai plus tard (dans pas longtemps mais pour l’instant j’ai impl tout les cas )

cargo fix sera capable d’ajouter les branches manquantes si tu vires la branche qui capture tout _ => puis que tu lances cargo fix avec des branches manquantes.

C’est pas pushé, si ? De manière générale en regardant ton historique Git, tu devrais faire des commits beaucoup plus atomiques. Git scale très bien même avec des centaines de milliers de commits donc faut pas hésiter à les garder simple, et aussi à pusher souvent.

c’est push

C’est noyé dans la sortie de lalrpop mais tu as en plein dans executer.rs. Tu peux piper la sortie dans Vim (ou ce que tu veux) pour la lire plus facilement cargo clippy 2>&1 | vim -.

ok

cargo fix sera capable d’ajouter les branches manquantes si tu vires la branche qui capture tout _ => puis que tu lances cargo fix avec des branches manquantes.

non, toujours pas :( (EDIT: en faite j’ai deja remplie toute les conditions)

Ben faut l’importer dans le scope quand même. Le patch suivant fonctionne sans problème

fait :)

+0 -0

Au sujet du code des fonctions:



            Expr::FunDef {
                ref name,
                ref args,
                ref body,
            } => {
                let mut args_vec = Vec::new();
                for arg in args {
                    let arg_name = match arg {
                        Expr::Ident { ref name } => name.clone(),
                        _ => return Err("arg is not identifier".to_string()),
                    };
                    args_vec.push(Ident(arg_name));
                }
                [...]

Pourquoi parses-tu tes fonctions avec des expr pour les arguments formels, avec un test dynamique derrière pour vérifier que ce sont bien des identifiants, au lieu de demander directement des identifiants dans ta grammaire ?

En fait c’est un problème avec la règle Identifier de ta grammaire qui renvoie une Expr alors qu’elle devrait renvoyer un type ne contenant que des identifiants. Cette erreur de conception affecte sans doute d’autres endroits de ton arbre syntaxique.



            Expr::Call {
                ref name,
                ref args,
            } => {
                [...]
                let function = match self.get_ident(Ident(name.clone())) {
                    Value::Function(f) => f,
                    _ => return Err("function not found".to_string()),
                };
                new_vm.set_ident(Ident(function.name.clone()), Value::Function(function.clone()));

Je ne comprends pas l’intérêt de ce set_ident, tu en fais déjà un dans le cas FunDef non ?


Le code n’a pas l’air correct dans le cas de fonctions qui font référence à des variables au lieu de leur définition (des "fermetures" qui "capturent" une partie de l’environnement).

{
  let x = 1
  def f() { x }
  {
    let x = 2
    f()
  }
}

ce code devrait renvoyer 1, est-ce qu’il ne renverrait pas 2 par hasard ?

Pourquoi parses-tu tes fonctions avec des expr pour les arguments formels, avec un test dynamique derrière pour vérifier que ce sont bien des identifiants, au lieu de demander directement des identifiants dans ta grammaire ?

En fait c’est un problème avec la règle Identifier de ta grammaire qui renvoie une Expr alors qu’elle devrait renvoyer un type ne contenant que des identifiants. Cette erreur de conception affecte sans doute d’autres endroits de ton arbre syntaxique.

en effet , j’ai juste voulu recuperer la valeur de Expr::Ident

Je ne comprends pas l’intérêt de ce set_ident, tu en fais déjà un dans le cas FunDef non ?

oui , mais ici ce sera pour la recursion , example:

def f(a) {
  @f(1) // ERREUR f n'existe pas dans ce block si on ne fait pas set_ident dans la nouvelle vm
}

Le code n’a pas l’air correct dans le cas de fonctions qui font référence à des variables au lieu de leur définition (des "fermetures" qui "capturent" une partie de l’environnement).

faux !

let x = 5
def f(a) { x }
{
  let x = 6 
  @f(1) // renvoie une erreur : x n'existe pas dans le block de la fonction
}
+0 -0

Je ne comprends pas, la ligne


                let mut new_vm = Vm::from(self.0.clone());

semble initialiser la "nouvelle vm" (drôle de nom/concept) en copiant l’environnement courant de l’interpréteur (self.0), donc en particulier avec toutes les variables définies à cet endroit là. Qu’est-ce que je rate ?

Je ne comprends pas, la ligne


                let mut new_vm = Vm::from(self.0.clone());

semble initialiser la "nouvelle vm" (drôle de nom/concept) en copiant l’environnement courant de l’interpréteur (self.0), donc en particulier avec toutes les variables définies à cet endroit là. Qu’est-ce que je rate ?

gasche

zut j’ai pas pushe en faite je l’avais remplace par Vm::new()

+0 -0

Dans les deux cas ton code ne gère pas bien la portée lexicale dans les définitions locales de fonctions (les fonctions qui veulent parler de variables qui sont dans le "scope"). Dans mon exemple on s’attend à avoir 1, pas une erreur.

bon , le bilan de T LANG :

  • ajout des list
  • ajout des index
  • ajout des range
  • correction de cargo clippy et cargo fmt fait
  • ajout des builtin print et println

tache a faire:

  • le scope de la fonction
  • le repl
  • les quelques remarquent des fonctions
  • les todo

merci pour vos remarque pour le nouveau code : tlang

un example d’utilisation de list :

let p = [267, 484, 230, 271, 394, 172, 139]

p.1:4 // 484, 230.0, 271.0
p.3 // 271

for n in p {
  @println(n)
}

for i in 3:9 {
    @print(i) // 3,4,5,..,8
}
+0 -0

Le code de gestion des listes (Index) en particulier est très verbeux et pas très joli. Par exemple:

  • tu fais une copie de la VM sans raison claire
  • beaucoup de redondance dans la gestion des erreurs (clairement les vérifiaction de bornes d’accès pourraient être factorisées par exemple)

Je me demande pourquoi tu exposes une primitive "extraction de sous-liste" dont l’implémentation est très inefficace (complexité linéaire). Qu’est-ce que tu penses qu’un utilisateur imaginaire écrirait comme code, avec cette construction, et quelles seraient ses performances ?

tu fais une copie de la VM sans raison clair

sans, il me donne une erreur que j’ai cherche mais que je n’ai pas trouve

Je me demande pourquoi tu exposes une primitive "extraction de sous-liste" dont l’implémentation est très inefficace (complexité linéaire). Qu’est-ce que tu penses qu’un utilisateur imaginaire écrirait comme code, avec cette construction, et quelles seraient ses performances ?

tu propose quoi

Le code de gestion des listes (Index) en particulier est très verbeux et pas très joli.

je suis d’accord avec toi , je vais essayer de faire mieux

merci pour tes commentaires

+0 -0

Je trouve que c’est une erreur de conception de Python (ou PHP sous une autre forme) d’avoir un type de donnée à tout faire, la "liste" en Python, qui propose plein d’opérations sans qu’on puisse savoir clairement quel est le coût de chacune — au moins en terme de complexité algorithmique. Ça facilite le fait d’écrire du code vraiment lent sans s’en rendre compte, et ça ne donne pas de bonnes pratiques de programmation. Il me semble que ton implémentation des listes continue cette tradition.

J’aurais tendance à t’encourager à réfléchir à quelle(s) structure(s) de données tu veux exposer, et restreindre les primitives offertes à ce qu’elles permettent d’implémenter efficacement. Par exemple si tu veux offrir un "tableau avec accès arbitraire", implémenté par un Vec sous-jacent, restreindre les primitives à celles qui peuvent être implémentées efficacement sur Vec: accès arbitraire, push/pop en fin de tableau.

Si tu veux exposer une fonction de "slice" qui renvoie une sous-liste ou un sous-tableau, pourquoi pas, mais le faire avec une représentation qui le rend efficace, par exemple un triplet "tableau, début d’intervalle, taille de l’intervalle" — et alors on n’expose pas push/pop qui ne peuvent plus être implémentées efficacement.

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