From 2e21dc50b861d9bcce1431379e098bba17bd9813 Mon Sep 17 00:00:00 2001 From: sulzmann Date: Tue, 28 Nov 2023 15:57:45 +0100 Subject: [PATCH] Add files via upload --- lec-cpp-advanced-syntax.html | 983 +++++++++++++++++++++++++++++++++++ lec-cpp-advanced-vm.html | 575 ++++++++++++++++++++ 2 files changed, 1558 insertions(+) create mode 100644 lec-cpp-advanced-syntax.html create mode 100644 lec-cpp-advanced-vm.html diff --git a/lec-cpp-advanced-syntax.html b/lec-cpp-advanced-syntax.html new file mode 100644 index 0000000..60ca197 --- /dev/null +++ b/lec-cpp-advanced-syntax.html @@ -0,0 +1,983 @@ + + + + + + + + + Syntax Analyse in C++ + + + + + +
+

Syntax Analyse in C++

+

+Martin Sulzmann +

+
+
+

Syntax Analyse

+

Darstellung von Syntax

+

Konkrete versus abstrakte Syntax.

+

Darstellung in C++?

+

Parsing

+

Allgemeiner Parser. Siehe Cocke–Younger–Kasami +algorithm.

+

Kubische Komplexität.

+

Wir verwenden Top-Down +Parsing.

+

Deterministisches parsen in linearer Zeit.

+

Am Beispiel von reguläre Ausdrücken.

+

Weitere Beispiele und Details siehe hier.

+
+
+

Reguläre Ausdrücke

+
R ::= x           // Alphabet symbol
+    | R + R       // Alternative
+    | R . R       // Konkatenation
+    | R*          // Kleensche Hülle
+    | (R)         // Klammerung
+

beschrieben durch eine kontext-freie Grammatik in EBNF.

+

Beispiele

+
x*
+
+(x*) . (y*)
+
+(x + y) . z
+

Beachte:

+ +

Darstellung in C++

+

Wir unterscheiden zwischen konkreter und abstrakter +Syntax Darstellung.

+

Wir verwenden

+ +

Unten-stehender Programmcode verwendet

+ +
// Polymorphie am Beispiel  regulärer Ausdrücke.
+//
+// R ::= x | R + R | R . R | R* | (R)
+//
+// 1. Templates zwecks Emulation von parametrischer Polymorphie (aka "generics")
+// 2. Overloading
+// 3. Subtyping
+
+
+
+#include <iostream>
+#include <string>
+#include <memory>
+using namespace std;
+
+
+class RE_ {
+public:
+  virtual string show() = 0;     // Quasi eine pretty-print Methode.
+  virtual bool nullable() = 0;   // Ueberprueft, ob der regulaere Ausdruck,
+                                 // das leere Wort enthaelt.
+
+};
+
+// Zwecks virtueller Methodenauswahl, müssen Objekte via Referenz erreichbar sein.
+// Wir verwenden "smart pointers".
+typedef shared_ptr<RE_> RE;                            // TEMPLATE
+
+
+// Epsilon, the empty word.
+class Eps : public RE_ {
+public:
+  string show() { return "eps"; }
+  bool nullable() { return true; }
+};
+
+// Alphabet symbols = letters.
+class Letter : public RE_ {
+  char x;
+public:
+  Letter(char y) { x = y; }
+  string show() { return string(1,x); }
+  bool nullable() { return false; }
+};
+
+// Alternatives.
+class Alt : public RE_ {
+  RE left;
+  RE right;
+public:
+  Alt(RE l, RE r) { left = l; right = r; }
+  string show() {
+    string s("(");
+    s.append(left->show());                          // OVERLOADING
+    s.append("+");
+    s.append(right->show());
+    s.append(")");
+    return s;
+  }
+  bool nullable() { return (left->nullable() || right->nullable()); }
+};
+
+
+// Concatenation.
+class Conc : public RE_ {
+  RE left;
+  RE right;
+public:
+  Conc(RE l, RE r) { left = l; right = r; }
+  string show() {
+    string s("(");
+    s.append(left->show());
+    s.append(".");
+    s.append(right->show());
+    s.append(")");
+    return s;
+  }
+  bool nullable() { return (left->nullable() && right->nullable()); }
+};
+
+// Kleene star.
+class Star : public RE_ {
+  RE r;
+public:
+  Star(RE s) { r = s; }
+  string show() {
+    string s("(");
+    s.append(r->show());
+    s.append("*)");
+    return s;
+  }
+  bool nullable() { return true; }
+};
+
+// Helpers
+
+// Point to note.
+// Co-variant subtyping here!
+// shared_ptr<Eps> <= shared_part<RE_>.
+RE mkEps() { return make_shared<Eps>(Eps()); }                     // SUBTYPING
+
+RE mkLetter(char x) { return make_shared<Letter>(Letter(x)); }
+
+RE mkAlt(RE l, RE r) { return make_shared<Alt>(Alt(l,r)); }
+
+RE mkConc(RE l, RE r) { return make_shared<Conc>(Conc(l,r)); }
+
+RE mkStar(RE r) { return make_shared<Star>(Star(r)); }
+
+
+// Beispiele
+
+void test() {
+
+  // (x + eps) . (y*)
+  RE r = mkConc(mkAlt(mkLetter('x'), mkEps()), mkStar(mkLetter('y')));
+
+  cout << "\n" << r->show();
+
+  cout << "\n" << r->nullable();
+
+}
+
+
+
+int main() {
+
+  test();
+
+}
+
+
+/*
+
+Beachte.
+
+TEMPLATE (siehe oben).
+
+     shared_ptr sind "generisch" in dem zu verwaltenden Datentyp.
+
+OVERLOADING (siehe oben).
+
+      left->show() entspricht   (*left).show()
+
+      left ist vom Typ RE, RE ist gleich shared_ptr<RE_>
+
+      Der Dereferenzierungsoperator "*" ist überladen.
+
+SUBTYPING (siehe oben).
+
+      make_shared<Eps>(Eps())  ist vom Typ   shared_ptr<Eps>
+
+      Rückgabetyp ist shared_ptr<RE_>
+
+      Beachte,  shared_ptr<RE_> ist verschieden von shared_ptr<Eps>!
+      Wieso ist der Programmtext Typkorrekt!?
+
+     Es gilt hier Ko-variantes Subtyping.
+
+      1. Subtyping zwischen Basistype
+
+      Aus der Klassendeklaration leiten wir ab  Eps <= RE_
+      D.h ein Objekt der Klasse Eps ist auch
+      vom Typ RE_
+
+
+     2. Subtyping zwischen Typen(konstruktoren) mit Typargument, z.B. shared_ptr<....>
+
+       shared_ptr<Eps> <= shared_ptr<RE_>  weil Eps <= RE_
+
+       D.h. Subtyping Relation zwischen shared_ptr Instanzen richtet sich nach
+       der Subtyping Relation zwischen den zugrunde liegenden Basistypen.
+
+       Die Richtung von "<=" bleibt erhalten. Die Subtype Relation verhaelt sich hier ko-variant.
+
+       Gibt es auch kontra-variant?
+       Ja, betrachte Coercive Subtyping Beispiel.
+
+int func(float x) {
+    if (x < 1.0) return 1;
+    return 0;
+}
+
+
+In mathematischer Schreibweise, func hat den Typ float -> int.
+Es gilt aber auch
+
+  float -> int  <=   int -> float
+
+ weil int <= float
+
+  In diesem Fall verhaelt sich Subtyping Kontra-variant im Argument und Ko-variant im Resultat.
+
+
+
+ */
+
+
+

Pretty printing

+

Im Fall von + eliminiere Klammern.

+

Idee:

+ +
// --std=c++17
+// Beispiel  regulärer Ausdrücke.
+//
+// R ::= x | R + R | R . R | R* | (R)
+//
+// Pretty printing
+
+
+
+#include <iostream>
+#include <string>
+#include <memory>
+#include <vector>
+#include <optional>
+using namespace std;
+
+
+// Forward Referenzen + typedefs
+class RE_;
+typedef shared_ptr<RE_> RE;
+RE mkEps();
+RE mkLetter(char);
+RE mkConc(RE,RE);
+RE mkStar(RE);
+RE mkAlts(vector<RE> rs);
+
+
+class RE_ {
+public:
+  virtual string show() = 0;     // Quasi eine pretty-print Methode.
+
+  virtual bool isAlts() { return false; }
+
+  virtual RE convert() = 0;
+
+  virtual optional<vector<RE>> getOperands() { return nullopt; }
+};
+
+
+// Epsilon, the empty word.
+class Eps : public RE_ {
+public:
+  string show() { return "eps"; }
+  RE convert() {
+    return mkEps();
+  }
+};
+
+// Alphabet symbols = letters.
+class Letter : public RE_ {
+  char x;
+public:
+  Letter(char y) { x = y; }
+  string show() { return string(1,x); }
+  RE convert() {
+    return mkLetter(x);
+  }
+};
+
+// Alternatives.
+class Alt : public RE_ {
+  RE left;
+  RE right;
+public:
+  Alt(RE l, RE r) { left = l; right = r; }
+  string show() {
+    string s("(");
+    s.append(left->show());
+    s.append("+");
+    s.append(right->show());
+    s.append(")");
+    return s;
+  }
+
+  RE convert() {
+    auto r1 = left->convert();
+    auto r2 = right->convert();
+
+    if(r1->isAlts() && r2->isAlts()) {
+      auto rs1 = (r1->getOperands()).value();
+      auto rs2 = (r2->getOperands()).value();
+
+      rs1.insert(rs1.end(), rs2.begin(), rs2.end());
+
+      return mkAlts(rs1);
+    } else if(r1->isAlts()) {
+
+      auto rs1 = (r1->getOperands()).value();
+
+      rs1.push_back(r2);
+
+      return mkAlts(rs1);
+
+    } else if(r2->isAlts()) {
+
+      auto rs2 = (r2->getOperands()).value();
+
+      rs2.insert(rs2.begin(),r1);
+
+      return mkAlts(rs2);
+    }
+    // else
+    vector<RE> ts;
+
+    ts.push_back(r1);
+    ts.push_back(r2);
+
+    return mkAlts(ts);
+  }
+};
+
+
+class Alts : public RE_ {
+public:
+  vector<RE> rs;
+  Alts(vector<RE> ts) { rs = ts; }
+  string show() {
+    string s;
+    if(rs.size() == 0)
+      return s;
+
+    s.append("(");
+    for(int i=0; i < rs.size(); i++) {
+      s.append(rs[i]->show());
+      if(i < rs.size() - 1) {
+    s.append("+");
+      }
+    }
+    s.append(")");
+    return s;
+  }
+
+  bool isAlts() {
+    return true;
+  }
+
+  optional<vector<RE>> getOperands() {
+    return make_optional<vector<RE>>(rs);
+  }
+
+  RE convert() {
+    return mkAlts(rs);
+  }
+
+};
+
+
+
+// Concatenation.
+class Conc : public RE_ {
+  RE left;
+  RE right;
+public:
+  Conc(RE l, RE r) { left = l; right = r; }
+  string show() {
+    string s("(");
+    s.append(left->show());
+    s.append(".");
+    s.append(right->show());
+    s.append(")");
+    return s;
+  }
+  RE convert() {
+    return mkConc(left->convert(),
+          right->convert());
+  }
+};
+
+// Kleene star.
+class Star : public RE_ {
+  RE r;
+public:
+  Star(RE s) { r = s; }
+  string show() {
+    string s("(");
+    s.append(r->show());
+    s.append("*)");
+    return s;
+  }
+  RE convert() {
+    return mkStar(r->convert());
+  }
+};
+
+// Helpers
+
+RE mkEps() { return make_shared<Eps>(Eps()); }
+
+RE mkLetter(char x) { return make_shared<Letter>(Letter(x)); }
+
+RE mkAlt(RE l, RE r) { return make_shared<Alt>(Alt(l,r)); }
+
+RE mkConc(RE l, RE r) { return make_shared<Conc>(Conc(l,r)); }
+
+RE mkStar(RE r) { return make_shared<Star>(Star(r)); }
+
+RE mkAlts(vector<RE> rs) { return make_shared<Alts>(Alts(rs)); }
+
+void run(RE r) {
+  cout << "\nTest\n" << r->show();
+  cout << "\n" << r->convert()->show();
+}
+
+void test() {
+
+
+
+  RE r1 = mkAlt(mkLetter('x'), mkLetter('y'));
+  RE r2 = mkAlt(r1,r1);
+
+  run(r2);
+
+  RE r3 = mkConc(r1,mkLetter('y'));
+
+  run(r3);
+
+  RE r4 = mkAlt(r3,r2);
+
+  run(r4);
+}
+
+
+
+
+int main() {
+
+  test();
+}
+
+
+

Top-down parser für reguläre Ausdrücke

+
// g++ --std=c++11
+
+// Beispiel  regulärer Ausdrücke.
+//
+// R ::= x | R + R | R . R | R* | (R)
+//
+//   wobei x ein KLEIN Buchstaben ist.
+
+
+
+#include <iostream>
+#include <string>
+#include <memory>
+#include <vector>
+using namespace std;
+
+
+// AST Darstellung.
+
+class RE_ {
+public:
+  virtual string show() = 0;     // Quasi eine pretty-print Methode.
+};
+
+// Zwecks virtueller Methodenauswahl, müssen Objekte via Referenz erreichbar sein.
+// Wir verwenden "smart pointers".
+typedef shared_ptr<RE_> RE;                            // TEMPLATE
+
+
+// Epsilon, the empty word.
+class Eps : public RE_ {
+public:
+  string show() { return "eps"; }
+};
+
+// Alphabet symbols = letters.
+class Letter : public RE_ {
+  char x;
+public:
+  Letter(char y) { x = y; }
+  string show() { return string(1,x); }
+};
+
+// Alternatives.
+class Alt : public RE_ {
+  RE left;
+  RE right;
+public:
+  Alt(RE l, RE r) { left = l; right = r; }
+  string show() {
+    string s("(");
+    s.append(left->show());                          // OVERLOADING
+    s.append("+");
+    s.append(right->show());
+    s.append(")");
+    return s;
+  }
+};
+
+
+// Concatenation.
+class Conc : public RE_ {
+  RE left;
+  RE right;
+public:
+  Conc(RE l, RE r) { left = l; right = r; }
+  string show() {
+    string s("(");
+    s.append(left->show());
+    s.append(".");
+    s.append(right->show());
+    s.append(")");
+    return s;
+  }
+};
+
+// Kleene star.
+class Star : public RE_ {
+  RE r;
+public:
+  Star(RE s) { r = s; }
+  string show() {
+    string s("(");
+    s.append(r->show());
+    s.append("*)");
+    return s;
+  }
+};
+
+// Helpers
+
+// Point to note.
+// Co-variant subtyping here!
+// shared_ptr<Eps> <= shared_part<RE_>.
+RE mkEps() { return make_shared<Eps>(Eps()); }                     // SUBTYPING
+
+RE mkLetter(char x) { return make_shared<Letter>(Letter(x)); }
+
+RE mkAlt(RE l, RE r) { return make_shared<Alt>(Alt(l,r)); }
+
+RE mkConc(RE l, RE r) { return make_shared<Conc>(Conc(l,r)); }
+
+RE mkStar(RE r) { return make_shared<Star>(Star(r)); }
+
+
+/////////////////////////////////////
+// Tokenizer
+
+typedef enum {
+  EOS,           // End of string
+  LETTER,
+  OPEN,
+  CLOSE,
+  CONC,
+  ALT,
+  KLEENE,
+} Token_t;
+
+
+class Token {
+public:
+  Token() {}
+  Token(Token_t tt) {
+    kind = tt;
+  }
+  Token(Token_t tt, char v) {
+    kind = tt; val = v;
+  }
+  bool eos() {
+    return kind == EOS;
+  }
+  Token_t kind;
+  char val;
+} ;
+
+
+
+class Tokenize {
+  string s;
+  int pos;
+public:
+  Tokenize(string s) {
+         this->s = s;
+     pos = 0;
+  }
+
+  Token next() {
+    if(s.length() <= pos)
+      return Token(EOS);
+
+    while(1) {
+
+       if(s.length() <= pos)
+         return Token(EOS);
+
+       switch(s[pos]) {
+       case '(': pos++;
+     return Token(OPEN);
+       case ')': pos++;
+     return Token(CLOSE);
+       case '+': pos++;
+     return Token(ALT);
+       case '.': pos++;
+     return Token(CONC);
+       case '*': pos++;
+     return Token(KLEENE);
+       case ' ':
+     pos++;
+     break;
+       default:  char ch = s[pos];
+             if(ch >= 'a' && ch <= 'z') {
+                     pos++;
+             return Token(LETTER, ch);
+         } else {
+           cout << "Unerlaubtes Symbol";
+           exit(1);
+         }
+       }
+    }
+  } // next
+
+  vector<Token> scan() {
+    vector<Token> v;
+    Token t;
+
+    do {
+      t = next();
+      v.push_back(t);
+    }
+    while(! t.eos());
+
+    return v;
+  } // scan
+};
+
+// Wrapper class, provide the (current) token.
+class Tokenizer : Tokenize {
+public:
+  Token token;
+  Tokenizer(string s) : Tokenize(s) { token = next(); }
+  void nextToken() {
+    token = next();
+  }
+};
+
+
+/////////////////////////////////////
+// Top-Down Parser fuer
+//
+//  R ::= x | R + R | R . R | R* | (R)
+
+//  R ::= T R2
+//  R2 ::= . T R2 |
+//  T ::= F T2
+//  T2 ::= + F T2 |
+//  F ::= x | x* | (R) | (R)*
+
+// Beachte.
+// "+" bindet staerker als "."
+// "Kleene star" wird betrachtet im Fall von "letter" und geklammerten Ausdruecken.
+// Deshalb bindet "*" staerker als die anderen Operatoren.
+
+
+// Newer C++ versions come with "optional".
+// We simply build our own and follow the original idea as found in Haskell's Maybe
+template<typename T>
+class Optional {
+  bool b;
+  T val;
+public:
+  Optional() : b(false) {}
+  Optional(T v) : val(v), b(true) {}
+  bool isJust() { return b; }
+  bool isNothing() { return !b; }
+  T fromJust() { return val; }
+};
+
+template<typename T>
+Optional<T> nothing() {
+  return Optional<T>();
+}
+
+template<typename T>
+Optional<T> just(T v) {
+  return Optional<T>(v);
+}
+
+
+// Functional style top-down parser.
+
+Optional<RE> parseR(Tokenizer &t);
+Optional<RE> parseR2(Tokenizer &t, RE left);
+Optional<RE> parseT(Tokenizer &t);
+Optional<RE> parseT2(Tokenizer &t, RE left);
+Optional<RE> parseF(Tokenizer &t);
+
+
+//  R ::= T R2
+Optional<RE> parseR(Tokenizer &t) {
+  Optional<RE> left = parseT(t);
+  if(left.isNothing())
+    return left;
+
+  return parseR2(t,left.fromJust());
+}
+
+//  R2 ::= . T R2 |
+Optional<RE> parseR2(Tokenizer &t, RE left) {
+
+    if(t.token.kind == CONC) {
+        t.nextToken();
+
+    Optional<RE> right = parseT(t);
+    if(right.isNothing())
+      return right;
+
+    return parseR2(t, mkConc(left, right.fromJust()));
+    }
+
+    return just<RE>(left);
+}
+
+//  T ::= F T2
+Optional<RE> parseT(Tokenizer &t) {
+  Optional<RE> left = parseF(t);
+  if(left.isNothing())
+    return left;
+
+  return parseT2(t,left.fromJust());
+}
+
+// T2 ::= + F T2 |
+Optional<RE> parseT2(Tokenizer &t, RE left) {
+
+    if(t.token.kind == ALT) {
+        t.nextToken();
+
+    Optional<RE> right = parseF(t);
+    if(right.isNothing())
+      return right;
+
+    return parseT2(t, mkAlt(left, right.fromJust()));
+    }
+
+    return just<RE>(left);
+}
+
+//  F ::= x | x* | (R) | (R)*
+Optional<RE> parseF(Tokenizer &t) {
+  switch(t.token.kind) {
+  case LETTER: {
+    char ch = t.token.val;
+    t.nextToken();
+
+    if(t.token.kind == KLEENE) {
+      t.nextToken();
+      return just<RE>(mkStar(mkLetter(ch)));
+    }
+    return just<RE>(mkLetter(ch));
+  }
+  case OPEN: {
+    t.nextToken();
+    Optional<RE> re = parseR(t);
+    if (re.isNothing())
+      return re;
+
+    if (t.token.kind != CLOSE)
+      return nothing<RE>();
+
+    t.nextToken();
+
+    if(t.token.kind == KLEENE) {
+      t.nextToken();
+      re = mkStar(re.fromJust());
+    }
+    return re;
+  }
+
+  default:
+    return nothing<RE>();
+  } // switch
+}
+
+
+
+// Beispiele
+
+
+void display(Optional<RE> r) {
+  if(r.isNothing()) {
+    cout << "nothing \n";
+  } else {
+    cout << (r.fromJust())->show() << "\n";
+  }
+  return;
+}
+
+
+void parse(string s) {
+  Tokenizer t = Tokenizer(s);
+
+  auto r = parseR(t);
+
+  // Whole input shall be consumed.
+  if (t.token.kind == EOS) {
+    display(r);
+    }
+  else {
+    display(nothing<RE>());
+  }
+
+}
+
+
+void test() {
+
+  // (x + eps) . (y*)
+  RE r = mkConc(mkAlt(mkLetter('x'), mkEps()), mkStar(mkLetter('y')));
+
+  cout << "\n" << r->show();
+
+}
+
+
+
+int main() {
+
+  parse("x.y+z");
+  parse("x* + y*");
+  parse("x + y *");
+  parse("(x+y)*");
+
+  parse("x x");
+
+}
+
+ + diff --git a/lec-cpp-advanced-vm.html b/lec-cpp-advanced-vm.html new file mode 100644 index 0000000..c6add02 --- /dev/null +++ b/lec-cpp-advanced-vm.html @@ -0,0 +1,575 @@ + + + + + + + + + Stack-basierte virtuelle Maschinen in C++ + + + + + +
+

Stack-basierte virtuelle Maschinen in C++

+

+Martin Sulzmann +

+
+
+

Übersicht

+

Betrachte Kompilierung von Programmen

+
Quellcode  ==> Syntaxbaum ==> .... ==> Assembler/Maschinencode
+

Assembler/Maschinencode: Intel? ARM? …

+

Ziel:

+ +

Hier: Stack-basierte virtuelle Maschine (VM) (auch Stack machine +genannt).

+ +
+
+

Stack-basierte VM

+

Instruktionen

+

Folgende Instruktionen sind vorhanden.

+

Push i

+ +

Plus

+ +

Mult

+ +

Beispiel 1

+
Push 1; Push 2; Plus;
+

Ausführung ist wie folgt.

+

Den Stack schreiben wir von links nach rechts (d.h. links steht das +oberste Element).

+
Initial:
+
+Push 1; Push 2, Plus
+
+Stack = []
+
+
+1. Schritt
+
+Push 2, Plus
+
+Stack = [1]
+
+2. Schritt
+
+Plus
+
+Stack = [2, 1]
+
+3. Schritt
+
+
+Stack = [3]
+

Beispiel 2

+
Push 1; Push 2; Push 3; Plus; Mult;
+

Ausführung ist wie folgt.

+
Initial:
+
+Push 1; Push 2; Push 3; Plus; Mult;
+
+Stack = []
+
+1. Schritt
+
+Push 2; Push 3; Plus; Mult;
+
+Stack = [1]
+
+2. Schritt
+
+Push 3; Plus; Mult;
+
+Stack = [2, 1]
+
+3. Schritt
+
+Plus; Mult;
+
+Stack = [3, 2, 1]
+
+4. Schritt
+
+Mult;
+
+Stack = [5, 1]
+
+5. Schritt
+
+Stack = [5]
+
+
+

Arithmetische Ausdrücke

+

Betrachte

+
1 + 2
+

Obiger Ausdruck kann wie folgt dargestellt werde

+
Push 1; Push 2; Plus;
+

Für arithmetische Ausdrücke verwenden wir “infix” Notation:

+ +

Im Fall von VM Instrukionen verwenden wir “postfix” Notation:

+ +

Diese Darstellung von arithmetischen Ausdrücken ist auch bekannt als +Reverse +Polish Notation (RPN).

+

Was ist der Vorteil von RPN?

+ +

Betrachte

+
1 * (2 + 3)
+

dargestellt als

+
Push 1; Push 2; Push 3; Plus; Mult;
+
+
+

Von arithmetischen Ausdrücken zu VM Instruktionen

+

Wir zeigen wie wir systematisch Ausdrücke als VM Instruktionen +darstellen können.

+

Wr nehmen einen vereinfachten Befehlssatz an.

+ +

Z.B.

+
1 * (2 + 1)
+

entspricht dann

+
ONE; TWO; ONE; PLUS; MULT
+

Schritt 1: Darstellung als +Syntaxbaum

+
1 * (2 + 1)
+

entspricht dann

+
Mult(Int(1), Plus(Int(2), Int(1)))
+

Schritt +2: Konvertiere Syntaxbaum in eine Sequenz von VM Instrukionen

+

Idee: Rekursiver Ansatz (von “innen” nach “aussen”).

+
  Mult(Int(1), Plus(Int(2), Int(1)))
+
+==convert=>
+
+   Zwischenschritte:
+
+      Int(1) ==convert=>  ONE
+
+
+      Plus(Int(2), Int(1)) ==convert==>  ONE; TWO; PLUS
+
+
+   Fasse Zwischenergebnisse zusammen:
+
+
+    ONE; TWO; ONE; PLUS; MULT
+

Genauer betrachtet, wir verwenden eine virtuelle Methode +convert. In Pseudo Code, die Konvertierung ist wie +folgt.

+
    Mult(Int(1), Plus(Int(2), Int(1))).convert()
+
+=  Int(1).convert() ++ Plus(Int(2), Int(1)).convert() ++ [MULT]
+
+= [ONE] ++ Int(2).convert ++ Int(1).convert() ++ [PLUS] ++ [MULT]
+
+= [ONE] ++ [TWO] ++ [ONE] ++ [PLUS] ++ [MULT]
+
+= [ONE,TWO,ONE,PLUS,MULT]
+

Wir schreiben [...] für Listen und ++ für +Konkatenation.

+
+
+

Einfache Stack Maschine in C++

+

Wir

+ +

+// Simple stack-based VM.
+
+#include <vector>
+#include <stack>
+#include <iostream>
+#include <string>
+#include <memory>
+using namespace std;
+
+
+///////////////////////////////////////
+// VM code
+typedef enum {
+  PLUS,
+  MULT,
+  ONE,
+  TWO
+} Code_t;
+
+string show(Code_t c) {
+  switch(c) {
+  case ONE: return "1";
+  case TWO: return "2";
+  case PLUS: return "+";
+  case MULT: return "*";
+  }
+}
+
+string show(vector<Code_t> cs) {
+  string s;
+  for(int i=0; i < cs.size(); i++) {
+    s = s + show(cs[i]);
+    if(i < cs.size() - 1) {
+      s = s + " ";
+    }
+  }
+  return s;
+}
+
+///////////////////////////////////////
+// Expressions
+//  - eval (interpreter)
+//  - convert to "reverse polish notation"
+//        Operands come first
+//        Computations require a single stack
+class Exp {
+public:
+  virtual int eval() = 0;
+  virtual vector<Code_t>convert() = 0;
+};
+class IntExp : public Exp {
+public:
+  int x;
+  IntExp(int x) {
+    if(x == 1 || x == 2) {
+      this->x = x;
+    }
+    else {
+      this->x = 1;
+      cout << "\nmust be 1 or 2";
+    }
+  }
+  int eval() { return this->x; }
+  vector<Code_t>convert() {
+    vector<Code_t> v;
+    auto n = x == 1 ? ONE : TWO;
+    v.push_back(n);
+
+    return v;
+  }
+};
+class PlusExp : public Exp {
+public:
+  std::shared_ptr<Exp> left, right;
+  PlusExp(std::shared_ptr<Exp> left, std::shared_ptr<Exp> right) {
+    this->left = left; this->right = right;
+  }
+  int eval() { return left->eval() + right->eval(); }
+  vector<Code_t>convert() {
+    auto v1 = left->convert();
+    auto v2 = right->convert();
+    v1.insert(v1.end(),v2.begin(),v2.end()); // append v2 to v1
+    v1.push_back(PLUS);
+
+    return v1;
+  }
+};
+
+class MultExp : public Exp {
+public:
+  std::shared_ptr<Exp> left, right;
+  MultExp(std::shared_ptr<Exp> left, std::shared_ptr<Exp>right) {
+    this->left = left; this->right = right;
+  }
+  int eval() { return left->eval() * right->eval(); }
+  vector<Code_t>convert() {
+    auto v1 = left->convert();
+    auto v2 = right->convert();
+    v1.insert(v1.end(),v2.begin(),v2.end()); // append v2 to v1
+    v1.push_back(MULT);
+
+    return v1;
+  }
+};
+
+// Helper functions
+
+std::shared_ptr<Exp> newInt(int i) {
+  return std::make_shared<IntExp>(i);
+}
+
+std::shared_ptr<Exp> newPlus(std::shared_ptr<Exp> left, std::shared_ptr<Exp> right) {
+  return std::make_shared<PlusExp>(PlusExp(left,right));
+}
+
+std::shared_ptr<Exp> newMult(std::shared_ptr<Exp> left, std::shared_ptr<Exp> right) {
+  return std::make_shared<MultExp>(MultExp(left,right));
+}
+
+
+///////////////////////////////////////
+// VM run-time
+class VM {
+    vector<Code_t> code;
+  public:
+    VM(vector<Code_t> c) : code(c) {}
+
+  void showRunConvert() {
+    cout << "\n VM code: " << show(code);
+    cout << "\n => " << run();
+    cout << "\n Exp: " << convert()->eval();
+  }
+
+  int run() {
+    stack<int> s;
+
+    for(int i = 0; i < code.size(); i++) {
+    switch(code[i]) {
+    case ONE:
+      s.push(1);
+        break;
+    case TWO:
+      s.push(2);
+        break;
+    case MULT: {
+      auto right = s.top();
+      s.pop();
+      auto left = s.top();
+      s.pop();
+      s.push(left * right);
+      break;
+    }
+    case PLUS: {
+      auto right = s.top();
+      s.pop();
+      auto left = s.top();
+      s.pop();
+      s.push(left + right);
+      break;
+    }
+    } // switch
+      } // for
+
+      return s.top();
+  } // run
+
+  std::shared_ptr<Exp> convert() {
+    stack<std::shared_ptr<Exp>> s;
+
+      for(int i = 0; i < code.size(); i++) {
+    switch(code[i]) {
+    case ONE:
+      s.push(newInt(1));
+        break;
+    case TWO:
+      s.push(newInt(2));
+        break;
+    case MULT: {
+      auto right = s.top();
+      s.pop();
+      auto left = s.top();
+      s.pop();
+      s.push(newMult(left,right));
+      break;
+    }
+    case PLUS: {
+      auto right = s.top();
+      s.pop();
+      auto left = s.top();
+      s.pop();
+      s.push(newPlus(left,right));
+      break;
+    }
+    } // switch
+      } // for
+
+      return s.top();
+  } // convert
+
+
+};
+
+
+
+///////////////////////////////////////
+// Examples
+
+void testVM() {
+
+  {
+    vector<Code_t> cs{
+      ONE, TWO, TWO, PLUS, MULT
+    };
+
+
+    VM(cs).showRunConvert();
+  }
+
+  {
+    vector<Code_t> cs{
+      ONE, TWO, PLUS, TWO, MULT
+    };
+
+
+    VM(cs).showRunConvert();
+  }
+
+}
+
+
+void testExp() {
+
+  auto e = newPlus(newMult(newInt(1),newInt(2)), newInt(1));
+
+  auto run = [](std::shared_ptr<Exp> e) {
+        cout << "\n Exp yields " << e->eval();
+        auto vm = VM(e->convert());
+        vm.showRunConvert();
+  };
+
+  run(e);
+
+}
+
+
+int main() {
+
+  testVM();
+
+  // testExp();
+}
+
+
+
+/*
+
+
+
+Wie koennten wir das ganze testen?
+
+z.B.
+
+Property 1:
+
+ Exp => convert => VMCode => convert Exp2
+
+ teste ob Exp.eval == Exp2.eval ist
+
+Property 2:
+
+ VMCode => convert => Exp => convert => VMCode2
+
+ teste ob VMCode.run == VMCod2.run
+
+
+Generierung von Testeingaben!
+Z.B. generiere beliebige Exp Objekte!
+
+
+
+ */
+
+ +