{"id":3131,"date":"2022-06-24T22:09:44","date_gmt":"2022-06-24T19:09:44","guid":{"rendered":"https:\/\/demensdeum.com\/blog\/?p=3131"},"modified":"2024-12-16T22:32:20","modified_gmt":"2024-12-16T19:32:20","slug":"pattern-interpreter","status":"publish","type":"post","link":"https:\/\/demensdeum.com\/blog\/fr\/2022\/06\/24\/pattern-interpreter\/","title":{"rendered":"Interpr\u00e8te de mod\u00e8le"},"content":{"rendered":"<h3>Ce qui est inclus<\/h3>\n<p>Le mod\u00e8le Interpreter fait r\u00e9f\u00e9rence aux mod\u00e8les de conception comportementale. Ce mod\u00e8le vous permet d&#8217;impl\u00e9menter votre propre langage de programmation en travaillant avec un arbre AST dont les sommets sont des expressions terminales et non terminales qui impl\u00e9mentent la m\u00e9thode Interpret, qui fournit les fonctionnalit\u00e9s du langage.<\/p>\n<ul>\n<li>Expression de terminal\u00a0: par exemple, constante de cha\u00eene\u00a0&#8211; &#8220;Bonjour tout le monde&#8221;<\/li>\n<li>Expression non terminale\u00a0: par exemple Print(&#8220;Hello World&#8221;), contient Print et un argument de l&#8217;expression terminale &#8220;Hello World&#8221;<\/li>\n<\/ul>\n<p>Quelle est la diff\u00e9rence\u00a0? La diff\u00e9rence est que l&#8217;interpr\u00e9tation se termine sur les expressions terminales, mais pour les expressions non terminales, elle se poursuit en profondeur sur tous les sommets\/arguments entrants. Si l&#8217;arborescence AST \u00e9tait constitu\u00e9e uniquement d&#8217;expressions non terminales, l&#8217;application ne se terminerait jamais, car une certaine finitude de tout processus est requise, cette finitude est ce que sont les expressions terminales, elles contiennent g\u00e9n\u00e9ralement des donn\u00e9es, par exemple des cha\u00eenes.<\/p>\n<p>Un exemple d&#8217;arborescence AST est ci-dessous\u00a0:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-3143\" src=\"https:\/\/demensdeum.com\/blog\/wp-content\/uploads\/2022\/06\/ast.png\" alt=\"\" width=\"512\" height=\"578\" srcset=\"https:\/\/demensdeum.com\/blog\/wp-content\/uploads\/2022\/06\/ast.png 512w, https:\/\/demensdeum.com\/blog\/wp-content\/uploads\/2022\/06\/ast-266x300.png 266w\" sizes=\"auto, (max-width: 512px) 100vw, 512px\" \/><br \/>\nDcoetzee, CC0, via Wikimedia Commons<\/p>\n<p>Comme vous pouvez le voir, les expressions terminales sont constantes et variables, les expressions non terminales sont le reste.<\/p>\n<h3>Ce qui n&#8217;est pas inclus<\/h3>\n<p>L&#8217;impl\u00e9mentation de l&#8217;Interpreter n&#8217;inclut pas l&#8217;analyse de la cha\u00eene de langue entr\u00e9e dans l&#8217;arborescence AST. Il suffit d&#8217;impl\u00e9menter des classes d&#8217;expressions terminales et non terminales, des m\u00e9thodes Interpret avec l&#8217;argument Context en entr\u00e9e, de cr\u00e9er un arbre d&#8217;expressions AST et d&#8217;ex\u00e9cuter la m\u00e9thode Interpret au niveau de l&#8217;expression racine. Un contexte peut \u00eatre utilis\u00e9 pour stocker l&#8217;\u00e9tat de l&#8217;application au moment de l&#8217;ex\u00e9cution.<\/p>\n<h3>Mise en \u0153uvre<\/h3>\n<p>Le mod\u00e8le implique\u00a0:<\/p>\n<ul>\n<li>Client \u2013 \u200b\u200b\u200b\u200brenvoie l&#8217;arborescence AST et ex\u00e9cute Interpret(context) pour le n\u0153ud racine (Client)<\/li>\n<li>Contexte\u00a0: contient l&#8217;\u00e9tat de l&#8217;application, transmis aux expressions lorsqu&#8217;elles sont interpr\u00e9t\u00e9es (Contexte)<\/li>\n<li>Expression abstraite\u00a0: une classe abstraite contenant la m\u00e9thode Interpret(context) (Expression)<\/li>\n<li>L&#8217;expression terminale est une expression finale, descendante d&#8217;une expression abstraite (TerminalExpression)<\/li>\n<li>Une expression non terminale n&#8217;est pas une expression finie\u00a0; elle contient des pointeurs vers des sommets profonds dans l&#8217;arborescence AST\u00a0; les sommets subordonn\u00e9s affectent g\u00e9n\u00e9ralement le r\u00e9sultat de l&#8217;interpr\u00e9tation de l&#8217;expression non terminale (NonTerminalExpression)<\/li>\n<\/ul>\n<p>Exemple client en C#<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-unknown\" data-lang=\"unknown\"><code>        static void Main(string[] args)\n        {\n            var context = new Context();\n            var initialProgram = new PerformExpression(\n                new IExpression[] {\n                    new SetExpression(\"alpha\", \"1\"),\n                    new GetExpression(\"alpha\"),\n                    new PrintExpression(\n                        new IExpression[] {\n                            new ConstantExpression(\"Hello Interpreter Pattern\")\n                        }\n                    )\n                }\n            );\n            System.Console.WriteLine(initialProgram.interpret(context));\n        }\n}\n<\/code><\/pre>\n<\/div>\n<p>Exemple d&#8217;expression abstraite en C#<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-unknown\" data-lang=\"unknown\"><code>{\n    String interpret(Context context);\n}\n<\/code><\/pre>\n<\/div>\n<p>Exemple d&#8217;expression de terminal en C# (constante de cha\u00eene)<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-unknown\" data-lang=\"unknown\"><code>{\n    private String constant;\n\n    public ConstantExpression(String constant) {\n        this.constant = constant;\n    }\n\n    override public String interpret(Context context) {\n        return constant;\n    }\n}\n<\/code><\/pre>\n<\/div>\n<p>Exemple d&#8217;expression non terminale en C# (d\u00e9marrage et concat\u00e9nation des r\u00e9sultats des sommets subordonn\u00e9s, en utilisant le d\u00e9limiteur \u00ab\u00a0;\u00a0\u00bb<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-unknown\" data-lang=\"unknown\"><code>{\n    public PerformExpression(IExpression[] leafs) : base(leafs) {\n        this.leafs = leafs;\n    }\n    \n    override public String interpret(Context context) {\n        var output = \"\";\n        foreach (var leaf in leafs) {\n            output += leaf.interpret(context) + \";\";\n        }\n        return output;\n    }\n}\n<\/code><\/pre>\n<\/div>\n<h3>Pouvez-vous le faire fonctionnellement\u00a0?<\/h3>\n<p>Comme on le sait, tous les langages Turing-complets sont \u00e9quivalents. Est-il possible de transf\u00e9rer le mod\u00e8le orient\u00e9 objet vers le langage de programmation fonctionnel\u00a0?<\/p>\n<p>Pour une exp\u00e9rience, prenons un langage FP pour le Web appel\u00e9 Elm. Il n&#8217;y a pas de classes dans Elm, mais il y a des enregistrements et des types, donc les enregistrements et types suivants sont impliqu\u00e9s dans l&#8217;impl\u00e9mentation\u00a0:<\/p>\n<ul>\n<li>Expression\u00a0: liste de toutes les expressions linguistiques possibles (Expression)<\/li>\n<li>Expression subordonn\u00e9e\u00a0: expression subordonn\u00e9e \u00e0 l&#8217;expression non terminale (ExpressionLeaf)<\/li>\n<li>Contexte\u00a0: un enregistrement stockant l&#8217;\u00e9tat de l&#8217;application (Contexte)<\/li>\n<li>Fonctions qui impl\u00e9mentent les m\u00e9thodes Interpret(context)\u00a0:\u00a0toutes les fonctions n\u00e9cessaires qui impl\u00e9mentent les fonctionnalit\u00e9s des expressions terminales et non terminales<\/li>\n<li>Enregistrements auxiliaires de l&#8217;\u00e9tat de l&#8217;interpr\u00e8te &#8211; n\u00e9cessaires au bon fonctionnement de l&#8217;interpr\u00e8te, ils stockent l&#8217;\u00e9tat de l&#8217;interpr\u00e8te, le contexte<\/li>\n<\/ul>\n<p>Un exemple de fonction qui impl\u00e9mente l&#8217;interpr\u00e9tation pour l&#8217;ensemble des expressions possibles dans Elm\u00a0:<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-unknown\" data-lang=\"unknown\"><code>  case input.expression of\n    Constant text -&gt;\n      { \n        output = text, \n        context = input.context \n      }\n    Perform leafs -&gt;\n      let inputs = List.map (\\leaf -&gt; { expressionLeaf = leaf, context = input.context } ) leafs in\n        let startLeaf = { expressionLeaf = (Node (Constant \"\")), context = { variables = Dict.empty } } in\n          let outputExpressionInput = List.foldl mergeContextsAndRunLeafs startLeaf inputs in\n            {\n              output = (runExpressionLeaf outputExpressionInput).output,\n              context = input.context\n            }\n    Print printExpression -&gt;\n      run \n      { \n        expression = printExpression, \n        context = input.context \n      }\n    Set key value -&gt;\n      let variables = Dict.insert key value input.context.variables in\n      {\n        output = \"OK\",\n        context = { variables = variables }\n      }\n    Get key -&gt;\n      {\n        output = Maybe.withDefault (\"No value for key: \" ++ key) (Dict.get key input.context.variables),\n        context = input.context\n      }\n<\/code><\/pre>\n<\/div>\n<h3>Et l&#8217;analyse syntaxique\u00a0?<\/h3>\n<p>L&#8217;analyse du code source dans une arborescence AST n&#8217;est pas incluse dans le mod\u00e8le Interpreter\u00a0; il existe plusieurs approches pour analyser le code source, mais nous y reviendrons une autre fois.<br \/>Dans l&#8217;impl\u00e9mentation de l&#8217;Interpreter for Elm, j&#8217;ai \u00e9crit un analyseur simple dans l&#8217;arborescence AST, compos\u00e9 de deux fonctions\u00a0: analyser un sommet et analyser les sommets subordonn\u00e9s.<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-unknown\" data-lang=\"unknown\"><code>parseLeafs state =\n    let tokensQueue = state.tokensQueue in\n        let popped = pop state.tokensQueue in\n            let tokensQueueTail = tail state.tokensQueue in\n                if popped == \"Nothing\" then\n                    state\n                else if popped == \"Perform(\" then\n                    {\n                        tokensQueue = tokensQueue,\n                        result = (state.result ++ [Node (parse tokensQueue)])\n                    }\n                else if popped == \")\" then\n                    parseLeafs {\n                        tokensQueue = tokensQueueTail,\n                        result = state.result\n                    }\n                else if popped == \"Set\" then\n                    let key = pop tokensQueueTail in\n                        let value = pop (tail tokensQueueTail) in\n                            parseLeafs {\n                                tokensQueue = tail (tail tokensQueueTail),\n                                result = (state.result ++ [Node (Set key value)])\n                            }\n                else if popped == \"Get\" then\n                    let key = pop tokensQueueTail in\n                        parseLeafs {\n                            tokensQueue = tail tokensQueueTail,\n                            result = (state.result ++ [Node (Get key)])\n                        }\n                else \n                    parseLeafs {\n                        tokensQueue = tokensQueueTail,\n                        result = (state.result ++ [Node (Constant popped)])\n                    }\n\nparse tokensQueue =\n    let popped = pop tokensQueue in\n        let tokensQueueTail = tail tokensQueue in\n            if popped == \"Perform(\" then\n                Perform (\n                    parseLeafs {\n                        tokensQueue = tokensQueueTail, \n                        result = []\n                    }\n                ).result\n            else if popped == \"Set\" then\n                let key = pop tokensQueueTail in\n                    let value = pop (tail tokensQueueTail) in\n                        Set key value\n            else if popped == \"Print\" then\n                Print (parse tokensQueueTail)\n            else\n                Constant popped\n<\/code><\/pre>\n<\/div>\n<h3>Liens<\/h3>\n<p><a href=\"https:\/\/gitlab.com\/demensdeum\/patterns\/-\/tree\/master\/interpreter\/elm\" target=\"_blank\" rel=\"noopener\">https:\/\/gitlab.com\/demensdeum \/patterns\/-\/tree\/master\/interpreter\/elm<\/a><br \/><a href=\"https:\/\/gitlab.com\/demensdeum\/patterns\/-\/tree\/master\/interpreter\/csharp\" target=\"_blank\" rel=\"noopener\">https:\/\/gitlab.com\/demensdeum\/patterns\/-\/tree\/master\/interpreter\/csharp<\/a><\/p>\n<h3>Sources<\/h3>\n<p><a href=\"https:\/\/en.wikipedia.org\/wiki\/Interpreter_pattern\" target=\"_blank\" rel=\"noopener\">https:\/\/en.wikipedia.org\/wiki\/Interpreter_pattern<\/a> <br \/><a href=\"https:\/\/elm-lang.org\/\" target=\"_blank\" rel=\"noopener\">https:\/\/elm-lang.org\/<\/a><br \/>\n<a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/csharp\/\" target=\"_blank\" rel=\"noopener\">https:\/\/docs.microsoft.com\/en-us\/dotnet\/csharp\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Ce qui est inclus Le mod\u00e8le Interpreter fait r\u00e9f\u00e9rence aux mod\u00e8les de conception comportementale. Ce mod\u00e8le vous permet d&#8217;impl\u00e9menter votre propre langage de programmation en travaillant avec un arbre AST dont les sommets sont des expressions terminales et non terminales qui impl\u00e9mentent la m\u00e9thode Interpret, qui fournit les fonctionnalit\u00e9s du langage. Expression de terminal\u00a0: par<a class=\"more-link\" href=\"https:\/\/demensdeum.com\/blog\/fr\/2022\/06\/24\/pattern-interpreter\/\">Continue reading <span class=\"screen-reader-text\">&#8220;Interpr\u00e8te de mod\u00e8le&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[61,52],"tags":[95],"class_list":["post-3131","post","type-post","status-publish","format-standard","hentry","category-techie","category-tutorials","tag-patterns","entry"],"translation":{"provider":"WPGlobus","version":"3.0.2","language":"fr","enabled_languages":["en","ru","zh","de","fr","ja","pt","hi"],"languages":{"en":{"title":true,"content":true,"excerpt":false},"ru":{"title":true,"content":true,"excerpt":false},"zh":{"title":true,"content":true,"excerpt":false},"de":{"title":true,"content":true,"excerpt":false},"fr":{"title":true,"content":true,"excerpt":false},"ja":{"title":true,"content":true,"excerpt":false},"pt":{"title":true,"content":true,"excerpt":false},"hi":{"title":false,"content":false,"excerpt":false}}},"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/demensdeum.com\/blog\/fr\/wp-json\/wp\/v2\/posts\/3131","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/demensdeum.com\/blog\/fr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/demensdeum.com\/blog\/fr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/fr\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/fr\/wp-json\/wp\/v2\/comments?post=3131"}],"version-history":[{"count":15,"href":"https:\/\/demensdeum.com\/blog\/fr\/wp-json\/wp\/v2\/posts\/3131\/revisions"}],"predecessor-version":[{"id":3886,"href":"https:\/\/demensdeum.com\/blog\/fr\/wp-json\/wp\/v2\/posts\/3131\/revisions\/3886"}],"wp:attachment":[{"href":"https:\/\/demensdeum.com\/blog\/fr\/wp-json\/wp\/v2\/media?parent=3131"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/fr\/wp-json\/wp\/v2\/categories?post=3131"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/fr\/wp-json\/wp\/v2\/tags?post=3131"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}