{"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\/de\/2022\/06\/24\/pattern-interpreter\/","title":{"rendered":"Musterinterpreter"},"content":{"rendered":"<h3>Was ist enthalten<\/h3>\n<p>Das Interpreter-Muster bezieht sich auf Verhaltensentwurfsmuster. Mit diesem Muster k\u00f6nnen Sie Ihre eigene Programmiersprache implementieren, indem Sie mit einem AST-Baum arbeiten, dessen Eckpunkte terminale und nicht-terminale Ausdr\u00fccke sind, die die Interpret-Methode implementieren, die die Funktionalit\u00e4t der Sprache bereitstellt.<\/p>\n<ul>\n<li>Terminalausdruck \u2013 zum Beispiel die Zeichenfolgenkonstante &#8211; &#8220;Hallo Welt&#8221;<\/li>\n<li>Nicht-terminaler Ausdruck \u2013 zum Beispiel Print(&#8220;Hello World&#8221;), enth\u00e4lt Print und ein Argument aus dem Terminalausdruck &#8220;Hello World&#8221;<\/li>\n<\/ul>\n<p>Was ist der Unterschied? Der Unterschied besteht darin, dass die Interpretation bei terminalen Ausdr\u00fccken endet, bei nicht-terminalen Ausdr\u00fccken jedoch ausf\u00fchrlich \u00fcber alle eingehenden Eckpunkte\/Argumente hinweg fortgesetzt wird. Wenn der AST-Baum nur aus nicht-terminalen Ausdr\u00fccken best\u00fcnde, w\u00fcrde die Anwendung niemals abgeschlossen werden, weil F\u00fcr jeden Prozess ist eine gewisse Endlichkeit erforderlich. Diese Endlichkeit ist das, was terminale Ausdr\u00fccke ausmachen. Sie enthalten normalerweise Daten, zum Beispiel Zeichenfolgen.<\/p>\n<p>Ein Beispiel f\u00fcr einen AST-Baum finden Sie unten:<\/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, \u00fcber Wikimedia Commons<\/p>\n<p>Wie Sie sehen k\u00f6nnen, sind Terminalausdr\u00fccke konstant und variabel, nichtterminale Ausdr\u00fccke sind der Rest.<\/p>\n<h3>Was nicht enthalten ist<\/h3>\n<p>Die Interpreter-Implementierung umfasst nicht das Parsen der in den AST-Baum eingegebenen Sprachzeichenfolge. Es reicht aus, Klassen von Terminal- und Nicht-Terminal-Ausdr\u00fccken zu implementieren, Methoden mit dem Kontextargument am Eingang zu interpretieren, einen AST-Ausdrucksbaum zu erstellen und die Interpret-Methode am Stammausdruck auszuf\u00fchren. Ein Kontext kann verwendet werden, um den Anwendungsstatus zur Laufzeit zu speichern.<\/p>\n<h3>Implementierung<\/h3>\n<p>Das Muster beinhaltet:<\/p>\n<ul>\n<li>Client \u2013 \u200b\u200b\u200b\u200bgibt den AST-Baum zur\u00fcck und f\u00fchrt Interpret(context) f\u00fcr den Wurzelknoten (Client) aus<\/li>\n<li>Kontext \u2013 enth\u00e4lt den Status der Anwendung, der bei der Interpretation an Ausdr\u00fccke \u00fcbergeben wird (Kontext)<\/li>\n<li>Abstrakter Ausdruck \u2013 eine abstrakte Klasse, die die Methode Interpret(context) (Expression) enth\u00e4lt<\/li>\n<li>Terminalausdruck ist ein endg\u00fcltiger Ausdruck, ein Nachkomme eines abstrakten Ausdrucks (TerminalExpression)<\/li>\n<li>Ein nicht-terminaler Ausdruck ist kein endlicher Ausdruck; er enth\u00e4lt Zeiger auf Scheitelpunkte tief im AST-Baum. Untergeordnete Scheitelpunkte wirken sich normalerweise auf das Ergebnis der Interpretation des nicht-terminalen Ausdrucks aus (NonTerminalExpression).<\/li>\n<\/ul>\n<p>Client-Beispiel in 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>Beispiel f\u00fcr einen abstrakten Ausdruck in 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>Beispiel f\u00fcr einen Terminalausdruck in C# (String-Konstante)<\/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>Beispiel eines nichtterminalen Ausdrucks in C# (Starten und Verketten der Ergebnisse untergeordneter Scheitelpunkte unter Verwendung des Trennzeichens \u201e;\u201c<\/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>K\u00f6nnen Sie es funktionell umsetzen?<\/h3>\n<p>Bekanntlich sind alle Turing-vollst\u00e4ndigen Sprachen gleichwertig. Ist es m\u00f6glich, das objektorientierte Muster auf die funktionale Programmiersprache zu \u00fcbertragen?<\/p>\n<p>F\u00fcr ein Experiment nehmen wir eine FP-Sprache f\u00fcr das Web namens Elm. In Elm gibt es keine Klassen, aber Datens\u00e4tze und Typen. Daher sind die folgenden Datens\u00e4tze und Typen an der Implementierung beteiligt:<\/p>\n<ul>\n<li>Ausdruck \u2013 Auflistung aller m\u00f6glichen Sprachausdr\u00fccke (Ausdruck)<\/li>\n<li>Untergeordneter Ausdruck \u2013 ein Ausdruck, der dem nichtterminalen Ausdruck (ExpressionLeaf) untergeordnet ist<\/li>\n<li>Kontext \u2013 ein Datensatz, der den Status der Anwendung speichert (Kontext)<\/li>\n<li>Funktionen, die Interpret(Kontext)-Methoden implementieren \u2013 alle notwendigen Funktionen, die die Funktionalit\u00e4t von Terminal- und Nicht-Terminal-Ausdr\u00fccken implementieren<\/li>\n<li>Hilfsdatens\u00e4tze des Interpreter-Status \u2013 notwendig f\u00fcr den korrekten Betrieb des Interpreters, sie speichern den Interpreter-Status und den Kontext<\/li>\n<\/ul>\n<p>Ein Beispiel f\u00fcr eine Funktion, die die Interpretation f\u00fcr den gesamten Satz m\u00f6glicher Ausdr\u00fccke in Elm implementiert:<\/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>Was ist mit dem Parsen?<\/h3>\n<p>Das Parsen von Quellcode in einen AST-Baum ist nicht im Interpreter-Muster enthalten; es gibt mehrere Ans\u00e4tze zum Parsen von Quellcode, aber dazu ein anderes Mal mehr.<br \/>Bei der Implementierung des Interpreters f\u00fcr Elm habe ich einen einfachen Parser im AST-Baum geschrieben, der aus zwei Funktionen besteht: Parsen eines Scheitelpunkts und Parsen untergeordneter Scheitelpunkte.<\/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>Links<\/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>Quellen<\/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>Was ist enthalten Das Interpreter-Muster bezieht sich auf Verhaltensentwurfsmuster. Mit diesem Muster k\u00f6nnen Sie Ihre eigene Programmiersprache implementieren, indem Sie mit einem AST-Baum arbeiten, dessen Eckpunkte terminale und nicht-terminale Ausdr\u00fccke sind, die die Interpret-Methode implementieren, die die Funktionalit\u00e4t der Sprache bereitstellt. Terminalausdruck \u2013 zum Beispiel die Zeichenfolgenkonstante &#8211; &#8220;Hallo Welt&#8221; Nicht-terminaler Ausdruck \u2013 zum Beispiel<a class=\"more-link\" href=\"https:\/\/demensdeum.com\/blog\/de\/2022\/06\/24\/pattern-interpreter\/\">Continue reading <span class=\"screen-reader-text\">&#8220;Musterinterpreter&#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":"de","enabled_languages":["en","ru","zh","de","fr","ja","pt"],"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}}},"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/demensdeum.com\/blog\/de\/wp-json\/wp\/v2\/posts\/3131","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/demensdeum.com\/blog\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/demensdeum.com\/blog\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/de\/wp-json\/wp\/v2\/comments?post=3131"}],"version-history":[{"count":15,"href":"https:\/\/demensdeum.com\/blog\/de\/wp-json\/wp\/v2\/posts\/3131\/revisions"}],"predecessor-version":[{"id":3886,"href":"https:\/\/demensdeum.com\/blog\/de\/wp-json\/wp\/v2\/posts\/3131\/revisions\/3886"}],"wp:attachment":[{"href":"https:\/\/demensdeum.com\/blog\/de\/wp-json\/wp\/v2\/media?parent=3131"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/de\/wp-json\/wp\/v2\/categories?post=3131"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/de\/wp-json\/wp\/v2\/tags?post=3131"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}