TinyPG In der Datei Templates\C#\ParseTree.cs in der Klasse ParseTree folgendes Property dazu! public Dictionary Variables = new Dictionary(); =====V1===== //@TinyPG - a Tiny Parser Generator v1.2 // Mini-Skriptsprache: print, Variablen, while <% @TinyPG Language="C#" Namespace="MyScript" OutputPath="C:\Users\manuel.zarat\source\repos\ConsoleApp1\ConsoleApp1" %> // Tokens (Reihenfolge ist wichtig!) EOF -> @"^\s*$"; [Color(255, 0, 0)] NUMBER -> @"[0-9]+"; [Color(0, 0, 255)] PLUSMINUS -> @"(\+|-)"; [Color(0, 0, 255)] MULTDIV -> @"\*|/"; [Color(0, 0, 255)] BROPEN -> @"\("; [Color(0, 0, 255)] BRCLOSE -> @"\)"; [Color(0, 0, 255)] ASSIGN -> @"="; // Keywords – MÜSSEN VOR IDENT kommen! [Color(0, 128, 0)] PRINT -> @"print"; [Color(0, 128, 0)] LET -> @"let"; [Color(0, 128, 0)] WHILE -> @"while"; [Color(0, 128, 0)] DO -> @"do"; [Color(0, 128, 0)] END -> @"end"; // Bezeichner [Color(128, 0, 128)] IDENT -> @"[a-zA-Z_][a-zA-Z0-9_]*"; [Skip] WHITESPACE -> @"\s+"; // --------- Grammatik --------- // Ein Programm ist eine Liste von Statements Start -> StmtList EOF { return $StmtList; }; // Liste von Statements StmtList -> (Stmt)* { // WICHTIG: wir laufen direkt über die Kindknoten, // damit jedes Stmt genau einmal evaluiert wird. foreach (ParseNode node in nodes) { if (node.Token.Type == TokenType.Stmt) node.Eval(tree); } return null; }; // Ein Statement kann print, let oder while sein Stmt -> PrintStmt | LetStmt | WhileStmt ; // print-Ausgabe PrintStmt -> PRINT AddExpr { int value = Convert.ToInt32($AddExpr); Console.WriteLine(value); return null; }; // Variablenzuweisung: let x = 3+4 LetStmt -> LET IDENT ASSIGN AddExpr { string name = $IDENT.ToString(); int value = Convert.ToInt32($AddExpr); if (!tree.Variables.ContainsKey(name)) tree.Variables.Add(name, value); else tree.Variables[name] = value; return null; }; // while-Schleife: while do end // Schleife läuft, solange Ausdruck != 0 ist WhileStmt -> WHILE AddExpr DO StmtList END { while (Convert.ToInt32($AddExpr) != 0) { // Body neu ausführen $StmtList; } return null; }; // ---------- Ausdrücke ---------- AddExpr -> MultExpr (PLUSMINUS MultExpr)* { int Value = Convert.ToInt32($MultExpr); int i = 1; while ($MultExpr[i] != null) { string sign = $PLUSMINUS[i-1].ToString(); if (sign == "+") Value += Convert.ToInt32($MultExpr[i++]); else Value -= Convert.ToInt32($MultExpr[i++]); } return Value; }; MultExpr -> Atom (MULTDIV Atom)* { int Value = Convert.ToInt32($Atom); int i = 1; while ($Atom[i] != null) { string sign = $MULTDIV[i-1].ToString(); if (sign == "*") Value *= Convert.ToInt32($Atom[i++]); else Value /= Convert.ToInt32($Atom[i++]); } return Value; }; // Atom: Zahl, Variable oder (Ausdruck) Atom -> NUMBER | IDENT | BROPEN AddExpr BRCLOSE { if ($NUMBER != null) return $NUMBER; if ($IDENT != null) { string name = $IDENT.ToString(); if (tree.Variables.ContainsKey(name)) return tree.Variables[name]; else throw new Exception("Undefined variable: " + name); } // Klammer-Ausdruck return $AddExpr; }; let x = 13 while x do print x let x = x - 1 end print x =====V2===== if/else dazu //@TinyPG - a Tiny Parser Generator v1.2 // Mini-Skriptsprache: print, Variablen, while <% @TinyPG Language="C#" Namespace="MyScript" OutputPath="C:\Users\manuel.zarat\source\repos\ConsoleApp1\ConsoleApp1" %> // Tokens (Reihenfolge ist wichtig!) EOF -> @"^\s*$"; [Color(255, 0, 0)] NUMBER -> @"[0-9]+"; [Color(0, 0, 255)] PLUSMINUS -> @"(\+|-)"; [Color(0, 0, 255)] MULTDIV -> @"\*|/"; [Color(0, 0, 255)] BROPEN -> @"\("; [Color(0, 0, 255)] BRCLOSE -> @"\)"; [Color(0, 0, 255)] ASSIGN -> @"="; // Keywords – MÜSSEN VOR IDENT kommen! [Color(0, 128, 0)] PRINT -> @"print"; [Color(0, 128, 0)] LET -> @"let"; [Color(0, 128, 0)] WHILE -> @"while"; [Color(0, 128, 0)] DO -> @"do"; [Color(0, 128, 0)] END -> @"end"; [Color(0, 128, 0)] IF -> @"if"; [Color(0, 128, 0)] ELSE -> @"else"; // Bezeichner [Color(128, 0, 128)] IDENT -> @"[a-zA-Z_][a-zA-Z0-9_]*"; [Skip] WHITESPACE -> @"\s+"; // --------- Grammatik --------- // Ein Programm ist eine Liste von Statements Start -> StmtList EOF { return $StmtList; }; // Liste von Statements StmtList -> (Stmt)* { // WICHTIG: wir laufen direkt über die Kindknoten, // damit jedes Stmt genau einmal evaluiert wird. foreach (ParseNode node in nodes) { if (node.Token.Type == TokenType.Stmt) node.Eval(tree); } return null; }; // Ein Statement kann print, let oder while sein Stmt -> PrintStmt | LetStmt | WhileStmt | IfStmt ; // print-Ausgabe PrintStmt -> PRINT AddExpr { int value = Convert.ToInt32($AddExpr); Console.WriteLine(value); return null; }; // Variablenzuweisung: let x = 3+4 LetStmt -> LET IDENT ASSIGN AddExpr { string name = $IDENT.ToString(); int value = Convert.ToInt32($AddExpr); if (!tree.Variables.ContainsKey(name)) tree.Variables.Add(name, value); else tree.Variables[name] = value; return null; }; // while-Schleife: while do end // Schleife läuft, solange Ausdruck != 0 ist WhileStmt -> WHILE AddExpr DO StmtList END { while (Convert.ToInt32($AddExpr) != 0) { // Body neu ausführen $StmtList; } return null; }; // if: if do (else )? end // True, wenn Ausdruck != 0 IfStmt -> IF AddExpr DO StmtList (ELSE StmtList)? END { if (Convert.ToInt32($AddExpr) != 0) { // then-Block $StmtList[0]; } else { // else-Block (falls vorhanden) if ($StmtList[1] != null) $StmtList[1]; } return null; }; // ---------- Ausdrücke ---------- AddExpr -> MultExpr (PLUSMINUS MultExpr)* { int Value = Convert.ToInt32($MultExpr); int i = 1; while ($MultExpr[i] != null) { string sign = $PLUSMINUS[i-1].ToString(); if (sign == "+") Value += Convert.ToInt32($MultExpr[i++]); else Value -= Convert.ToInt32($MultExpr[i++]); } return Value; }; MultExpr -> Atom (MULTDIV Atom)* { int Value = Convert.ToInt32($Atom); int i = 1; while ($Atom[i] != null) { string sign = $MULTDIV[i-1].ToString(); if (sign == "*") Value *= Convert.ToInt32($Atom[i++]); else Value /= Convert.ToInt32($Atom[i++]); } return Value; }; // Atom: Zahl, Variable oder (Ausdruck) Atom -> NUMBER | IDENT | BROPEN AddExpr BRCLOSE { if ($NUMBER != null) return $NUMBER; if ($IDENT != null) { string name = $IDENT.ToString(); if (tree.Variables.ContainsKey(name)) return tree.Variables[name]; else throw new Exception("Undefined variable: " + name); } // Klammer-Ausdruck return $AddExpr; }; let x = 13 let y = 2 while x do print x let x = x - y if x < 1do print x end end print x =====V3===== Mit Vergleichsoperatoren //@TinyPG - a Tiny Parser Generator v1.2 // Mini-Skriptsprache: print, Variablen, while <% @TinyPG Language="C#" Namespace="MyScript" OutputPath="C:\Users\manuel.zarat\source\repos\ConsoleApp1\ConsoleApp1" %> // Tokens (Reihenfolge ist wichtig!) EOF -> @"^\s*$"; [Color(255, 0, 0)] NUMBER -> @"[0-9]+"; [Color(0, 0, 255)] PLUSMINUS -> @"(\+|-)"; [Color(0, 0, 255)] MULTDIV -> @"\*|/"; [Color(0, 0, 255)] BROPEN -> @"\("; [Color(0, 0, 255)] BRCLOSE -> @"\)"; [Color(0, 0, 255)] RELOP -> @"(<=|>=|==|!=|<|>)"; [Color(0, 0, 255)] ASSIGN -> @"="; // Keywords – MÜSSEN VOR IDENT kommen! [Color(0, 128, 0)] PRINT -> @"print"; [Color(0, 128, 0)] LET -> @"let"; [Color(0, 128, 0)] WHILE -> @"while"; [Color(0, 128, 0)] DO -> @"do"; [Color(0, 128, 0)] END -> @"end"; [Color(0, 128, 0)] IF -> @"if"; [Color(0, 128, 0)] ELSE -> @"else"; // Bezeichner [Color(128, 0, 128)] IDENT -> @"[a-zA-Z_][a-zA-Z0-9_]*"; [Skip] WHITESPACE -> @"\s+"; // --------- Grammatik --------- // Ein Programm ist eine Liste von Statements Start -> StmtList EOF { return $StmtList; }; // Liste von Statements StmtList -> (Stmt)* { // WICHTIG: wir laufen direkt über die Kindknoten, // damit jedes Stmt genau einmal evaluiert wird. foreach (ParseNode node in nodes) { if (node.Token.Type == TokenType.Stmt) node.Eval(tree); } return null; }; // Ein Statement kann print, let oder while sein Stmt -> PrintStmt | LetStmt | WhileStmt | IfStmt ; // print-Ausgabe PrintStmt -> PRINT CondExpr { int value = Convert.ToInt32($CondExpr); Console.WriteLine(value); return null; }; LetStmt -> LET IDENT ASSIGN CondExpr { string name = $IDENT.ToString(); int value = Convert.ToInt32($CondExpr); if (!tree.Variables.ContainsKey(name)) tree.Variables.Add(name, value); else tree.Variables[name] = value; return null; }; // while-Schleife: while do end // Schleife läuft, solange Ausdruck != 0 ist WhileStmt -> WHILE CondExpr DO StmtList END { while (Convert.ToInt32($CondExpr) != 0) { $StmtList; } return null; }; // ---------- Bedingungen / Vergleiche ---------- // Liefert int: true=1, false=0 CondExpr -> AddExpr (RELOP AddExpr)? { int left = Convert.ToInt32($AddExpr[0]); // kein Vergleich -> normaler Zahlenwert (wie bisher) if ($RELOP == null) return left; int right = Convert.ToInt32($AddExpr[1]); string op = $RELOP.ToString(); bool result = (op == "<" && left < right) || (op == "<=" && left <= right) || (op == ">" && left > right) || (op == ">=" && left >= right) || (op == "==" && left == right) || (op == "!=" && left != right); return result ? 1 : 0; }; // if: if do (else )? end // True, wenn Ausdruck != 0 IfStmt -> IF CondExpr DO StmtList (ELSE StmtList)? END { if (Convert.ToInt32($CondExpr) != 0) $StmtList[0]; else { if ($StmtList[1] != null) $StmtList[1]; } return null; }; // ---------- Ausdrücke ---------- AddExpr -> MultExpr (PLUSMINUS MultExpr)* { int Value = Convert.ToInt32($MultExpr); int i = 1; while ($MultExpr[i] != null) { string sign = $PLUSMINUS[i-1].ToString(); if (sign == "+") Value += Convert.ToInt32($MultExpr[i++]); else Value -= Convert.ToInt32($MultExpr[i++]); } return Value; }; MultExpr -> Atom (MULTDIV Atom)* { int Value = Convert.ToInt32($Atom); int i = 1; while ($Atom[i] != null) { string sign = $MULTDIV[i-1].ToString(); if (sign == "*") Value *= Convert.ToInt32($Atom[i++]); else Value /= Convert.ToInt32($Atom[i++]); } return Value; }; // Atom: Zahl, Variable oder (Ausdruck) Atom -> NUMBER | IDENT | BROPEN AddExpr BRCLOSE { if ($NUMBER != null) return $NUMBER; if ($IDENT != null) { string name = $IDENT.ToString(); if (tree.Variables.ContainsKey(name)) return tree.Variables[name]; else throw new Exception("Undefined variable: " + name); } // Klammer-Ausdruck return $AddExpr; }; let x = 0 while x < 5 do print x let x = x + 1 end if x == 5 do print 999 else print 111 end =====V4===== Mit Strings //@TinyPG - a Tiny Parser Generator v1.2 // Mini-Skriptsprache: print, let, while, if/else // int + string, '+' kann concat, RELOP auch für Strings <% @TinyPG Language="C#" Namespace="MyScript" OutputPath="C:\Users\manuel.zarat\source\repos\ConsoleApp1\ConsoleApp1" %> // ---------------- Tokens ---------------- EOF -> @"^\s*$"; [Color(255, 0, 0)] NUMBER -> @"[0-9]+"; [Color(255, 128, 0)] STRING -> @"""([^""\\]|\\.)*"""; [Color(0, 0, 255)] PLUSMINUS -> @"(\+|-)"; [Color(0, 0, 255)] MULTDIV -> @"(\*|/)"; [Color(0, 0, 255)] BROPEN -> @"\("; [Color(0, 0, 255)] BRCLOSE -> @"\)"; [Color(0, 0, 255)] RELOP -> @"(<=|>=|==|!=|<|>)"; [Color(0, 0, 255)] ASSIGN -> @"="; // Keywords – MÜSSEN VOR IDENT kommen! [Color(0, 128, 0)] PRINT -> @"print"; [Color(0, 128, 0)] LET -> @"let"; [Color(0, 128, 0)] WHILE -> @"while"; [Color(0, 128, 0)] DO -> @"do"; [Color(0, 128, 0)] END -> @"end"; [Color(0, 128, 0)] IF -> @"if"; [Color(0, 128, 0)] ELSE -> @"else"; [Color(128, 0, 128)] IDENT -> @"[a-zA-Z_][a-zA-Z0-9_]*"; [Skip] WHITESPACE -> @"\s+"; // ---------------- Grammatik ---------------- Start -> StmtList EOF { return $StmtList; }; StmtList -> (Stmt)* { foreach (ParseNode node in nodes) { if (node.Token.Type == TokenType.Stmt) node.Eval(tree); } return null; }; Stmt -> PrintStmt | LetStmt | WhileStmt | IfStmt ; // ---------------- Statements ---------------- PrintStmt -> PRINT Expr { object v = $Expr; Console.WriteLine(v == null ? "null" : v.ToString()); return null; }; LetStmt -> LET IDENT ASSIGN Expr { string name = $IDENT.ToString(); object value = $Expr; // tree.Variables: Dictionary if (!tree.Variables.ContainsKey(name)) tree.Variables.Add(name, value); else tree.Variables[name] = value; return null; }; WhileStmt -> WHILE CondExpr DO StmtList END { while (Convert.ToInt32($CondExpr) != 0) { $StmtList; } return null; }; IfStmt -> IF CondExpr DO StmtList (ELSE StmtList)? END { if (Convert.ToInt32($CondExpr) != 0) $StmtList[0]; else { if ($StmtList[1] != null) $StmtList[1]; } return null; }; // ---------------- Bedingungen / Vergleiche ---------------- // Ergebnis: int (true=1, false=0) CondExpr -> AddExpr (RELOP AddExpr)? { object left = $AddExpr[0]; // ohne Vergleich: truthy if ($RELOP == null) { if (left == null) return 0; if (left is int) return ((int)left != 0) ? 1 : 0; if (left is bool) return ((bool)left) ? 1 : 0; if (left is string) return (((string)left).Length != 0) ? 1 : 0; return 1; } object right = $AddExpr[1]; string op = $RELOP.ToString(); // int vs int if (left is int && right is int) { int l = (int)left; int r = (int)right; bool res = (op == "<" && l < r) || (op == "<=" && l <= r) || (op == ">" && l > r) || (op == ">=" && l >= r) || (op == "==" && l == r) || (op == "!=" && l != r); return res ? 1 : 0; } // string vs string (ordinal) if (left is string && right is string) { int cmp = string.CompareOrdinal((string)left, (string)right); bool res = (op == "<" && cmp < 0) || (op == "<=" && cmp <= 0) || (op == ">" && cmp > 0) || (op == ">=" && cmp >= 0) || (op == "==" && cmp == 0) || (op == "!=" && cmp != 0); return res ? 1 : 0; } // gemischte Typen: == / != ok, Ordnung nicht if (op == "==") return 0; if (op == "!=") return 1; throw new Exception( "Invalid comparison between '" + (left == null ? "null" : left.GetType().Name) + "' and '" + (right == null ? "null" : right.GetType().Name) + "' using '" + op + "'." ); }; // ---------------- Ausdrücke ---------------- Expr -> AddExpr { return $AddExpr; }; AddExpr -> MultExpr (PLUSMINUS MultExpr)* { object value = $MultExpr; int i = 1; while ($MultExpr[i] != null) { string sign = $PLUSMINUS[i-1].ToString(); object rhs = $MultExpr[i++]; if (sign == "+") { // concat wenn einer string ist if (value is string || rhs is string) { string ls = (value == null) ? "" : value.ToString(); string rs = (rhs == null) ? "" : rhs.ToString(); value = ls + rs; } else { // beide müssen int sein if (!(value is int) || !(rhs is int)) throw new Exception("Operator '+' erwartet Zahl+Zahl oder String-Konkatenation."); value = (int)value + (int)rhs; } } else { // '-' nur numerisch if (!(value is int) || !(rhs is int)) throw new Exception("Operator '-' ist nur für Zahlen erlaubt."); value = (int)value - (int)rhs; } } return value; }; MultExpr -> Atom (MULTDIV Atom)* { object accObj = $Atom; // Wenn kein * oder / folgt, dürfen wir int ODER string einfach durchreichen if ($Atom[1] == null) return accObj; // Sobald * oder / vorkommt: nur Zahlen erlaubt if (!(accObj is int)) throw new Exception("Operator '*' und '/' sind nur für Zahlen erlaubt."); int acc = (int)accObj; int i = 1; while ($Atom[i] != null) { string sign = $MULTDIV[i-1].ToString(); object rhsObj = $Atom[i++]; if (!(rhsObj is int)) throw new Exception("Operator '*' und '/' sind nur für Zahlen erlaubt."); int rhs = (int)rhsObj; if (sign == "*") acc *= rhs; else acc /= rhs; } return acc; }; Atom -> NUMBER | STRING | IDENT | BROPEN Expr BRCLOSE { if ($NUMBER != null) return Convert.ToInt32($NUMBER.ToString()); if ($STRING != null) { string raw = $STRING.ToString(); raw = raw.Substring(1, raw.Length - 2); System.Text.StringBuilder sb = new System.Text.StringBuilder(); for (int j = 0; j < raw.Length; j++) { char c = raw[j]; if (c == '\\' && j + 1 < raw.Length) { char n = raw[++j]; if (n == 'n') sb.Append('\n'); else if (n == 't') sb.Append('\t'); else if (n == 'r') sb.Append('\r'); else if (n == '\\') sb.Append('\\'); else if (n == '"') sb.Append('"'); else sb.Append(n); } else { sb.Append(c); } } return sb.ToString(); } if ($IDENT != null) { string name = $IDENT.ToString(); if (tree.Variables.ContainsKey(name)) return tree.Variables[name]; throw new Exception("Undefined variable: " + name); } return $Expr; }; let x = 0 let y = "Check" while x < 5 do print x let x = x + 1 if x > 3 do print y end end =====V5===== Switch/Case //@TinyPG - a Tiny Parser Generator v1.2 // Mini-Skriptsprache: print, let, while, if/else // int + string, '+' kann concat, RELOP auch für Strings <% @TinyPG Language="C#" Namespace="MyScript" OutputPath="C:\Users\manuel.zarat\source\repos\ConsoleApp1\ConsoleApp1" %> // ---------------- Tokens ---------------- EOF -> @"^\s*$"; [Color(255, 0, 0)] NUMBER -> @"[0-9]+"; [Color(255, 128, 0)] STRING -> @"""([^""\\]|\\.)*"""; [Color(0, 0, 255)] PLUSMINUS -> @"(\+|-)"; [Color(0, 0, 255)] MULTDIV -> @"(\*|/)"; [Color(0, 0, 255)] BROPEN -> @"\("; [Color(0, 0, 255)] BRCLOSE -> @"\)"; [Color(0, 0, 255)] RELOP -> @"(<=|>=|==|!=|<|>)"; [Color(0, 0, 255)] ASSIGN -> @"="; // Keywords – MÜSSEN VOR IDENT kommen! [Color(0, 128, 0)] PRINT -> @"print"; [Color(0, 128, 0)] LET -> @"let"; [Color(0, 128, 0)] WHILE -> @"while"; [Color(0, 128, 0)] DO -> @"do"; [Color(0, 128, 0)] END -> @"end"; [Color(0, 128, 0)] IF -> @"if"; [Color(0, 128, 0)] ELSE -> @"else"; [Color(0, 128, 0)] SWITCH -> @"switch"; [Color(0, 128, 0)] CASE -> @"case"; [Color(128, 0, 128)] IDENT -> @"[a-zA-Z_][a-zA-Z0-9_]*"; [Skip] WHITESPACE -> @"\s+"; // ---------------- Grammatik ---------------- Start -> StmtList EOF { return $StmtList; }; StmtList -> (Stmt)* { foreach (ParseNode node in nodes) { if (node.Token.Type == TokenType.Stmt) node.Eval(tree); } return null; }; Stmt -> PrintStmt | LetStmt | WhileStmt | IfStmt | SwitchStmt ; // ---------------- Statements ---------------- // switch do (case do )* (else )? end SwitchStmt -> SWITCH Expr DO (CASE Expr DO StmtList)* (ELSE StmtList)? END { object key = $Expr[0]; // switch-key int i = 1; // Expr[1..] sind case-werte int caseBodyIndex = 0; // StmtList[0..] sind case-bodies, danach optional else while ($Expr[i] != null) { object cv = $Expr[i]; bool match = false; if (key is int && cv is int) match = ((int)key == (int)cv); else if (key is string && cv is string) match = (string.CompareOrdinal((string)key, (string)cv) == 0); else match = false; // gemischte Typen: kein Match if (match) { // genau den passenden case-body ausführen $StmtList[caseBodyIndex]; return null; } i++; caseBodyIndex++; } // else-body (falls vorhanden) ist das nächste StmtList nach den case-bodies if ($StmtList[caseBodyIndex] != null) $StmtList[caseBodyIndex]; return null; }; PrintStmt -> PRINT Expr { object v = $Expr; Console.WriteLine(v == null ? "null" : v.ToString()); return null; }; LetStmt -> LET IDENT ASSIGN Expr { string name = $IDENT.ToString(); object value = $Expr; // tree.Variables: Dictionary if (!tree.Variables.ContainsKey(name)) tree.Variables.Add(name, value); else tree.Variables[name] = value; return null; }; WhileStmt -> WHILE CondExpr DO StmtList END { while (Convert.ToInt32($CondExpr) != 0) { $StmtList; } return null; }; IfStmt -> IF CondExpr DO StmtList (ELSE StmtList)? END { if (Convert.ToInt32($CondExpr) != 0) $StmtList[0]; else { if ($StmtList[1] != null) $StmtList[1]; } return null; }; // ---------------- Bedingungen / Vergleiche ---------------- // Ergebnis: int (true=1, false=0) CondExpr -> AddExpr (RELOP AddExpr)? { object left = $AddExpr[0]; // ohne Vergleich: truthy if ($RELOP == null) { if (left == null) return 0; if (left is int) return ((int)left != 0) ? 1 : 0; if (left is bool) return ((bool)left) ? 1 : 0; if (left is string) return (((string)left).Length != 0) ? 1 : 0; return 1; } object right = $AddExpr[1]; string op = $RELOP.ToString(); // int vs int if (left is int && right is int) { int l = (int)left; int r = (int)right; bool res = (op == "<" && l < r) || (op == "<=" && l <= r) || (op == ">" && l > r) || (op == ">=" && l >= r) || (op == "==" && l == r) || (op == "!=" && l != r); return res ? 1 : 0; } // string vs string (ordinal) if (left is string && right is string) { int cmp = string.CompareOrdinal((string)left, (string)right); bool res = (op == "<" && cmp < 0) || (op == "<=" && cmp <= 0) || (op == ">" && cmp > 0) || (op == ">=" && cmp >= 0) || (op == "==" && cmp == 0) || (op == "!=" && cmp != 0); return res ? 1 : 0; } // gemischte Typen: == / != ok, Ordnung nicht if (op == "==") return 0; if (op == "!=") return 1; throw new Exception( "Invalid comparison between '" + (left == null ? "null" : left.GetType().Name) + "' and '" + (right == null ? "null" : right.GetType().Name) + "' using '" + op + "'." ); }; // ---------------- Ausdrücke ---------------- Expr -> AddExpr { return $AddExpr; }; AddExpr -> MultExpr (PLUSMINUS MultExpr)* { object value = $MultExpr; int i = 1; while ($MultExpr[i] != null) { string sign = $PLUSMINUS[i-1].ToString(); object rhs = $MultExpr[i++]; if (sign == "+") { // concat wenn einer string ist if (value is string || rhs is string) { string ls = (value == null) ? "" : value.ToString(); string rs = (rhs == null) ? "" : rhs.ToString(); value = ls + rs; } else { // beide müssen int sein if (!(value is int) || !(rhs is int)) throw new Exception("Operator '+' erwartet Zahl+Zahl oder String-Konkatenation."); value = (int)value + (int)rhs; } } else { // '-' nur numerisch if (!(value is int) || !(rhs is int)) throw new Exception("Operator '-' ist nur für Zahlen erlaubt."); value = (int)value - (int)rhs; } } return value; }; MultExpr -> Atom (MULTDIV Atom)* { object accObj = $Atom; // Wenn kein * oder / folgt, dürfen wir int ODER string einfach durchreichen if ($Atom[1] == null) return accObj; // Sobald * oder / vorkommt: nur Zahlen erlaubt if (!(accObj is int)) throw new Exception("Operator '*' und '/' sind nur für Zahlen erlaubt."); int acc = (int)accObj; int i = 1; while ($Atom[i] != null) { string sign = $MULTDIV[i-1].ToString(); object rhsObj = $Atom[i++]; if (!(rhsObj is int)) throw new Exception("Operator '*' und '/' sind nur für Zahlen erlaubt."); int rhs = (int)rhsObj; if (sign == "*") acc *= rhs; else acc /= rhs; } return acc; }; Atom -> NUMBER | STRING | IDENT | BROPEN Expr BRCLOSE { if ($NUMBER != null) return Convert.ToInt32($NUMBER.ToString()); if ($STRING != null) { string raw = $STRING.ToString(); raw = raw.Substring(1, raw.Length - 2); System.Text.StringBuilder sb = new System.Text.StringBuilder(); for (int j = 0; j < raw.Length; j++) { char c = raw[j]; if (c == '\\' && j + 1 < raw.Length) { char n = raw[++j]; if (n == 'n') sb.Append('\n'); else if (n == 't') sb.Append('\t'); else if (n == 'r') sb.Append('\r'); else if (n == '\\') sb.Append('\\'); else if (n == '"') sb.Append('"'); else sb.Append(n); } else { sb.Append(c); } } return sb.ToString(); } if ($IDENT != null) { string name = $IDENT.ToString(); if (tree.Variables.ContainsKey(name)) return tree.Variables[name]; throw new Exception("Undefined variable: " + name); } return $Expr; }; let x = "b" switch x do case "a" do print "case a" case "b" do print "case b" else print "default" end