Benutzer-Werkzeuge

Webseiten-Werkzeuge


tinypg

Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen angezeigt.

Link zu dieser Vergleichsansicht

Nächste Überarbeitung
Vorhergehende Überarbeitung
tinypg [2025/12/11 14:09]
jango angelegt
tinypg [2025/12/12 12:49] (aktuell)
jango [V4]
Zeile 1: Zeile 1:
 TinyPG TinyPG
  
 +In der Datei <code>Templates\C#\ParseTree.cs</code> in der Klasse ParseTree folgendes Property dazu!
 <code> <code>
-<% @TinyPG Language="c#" OutputPath="C:\chroot\Parser\" %>+public Dictionary<string, int> Variables  new Dictionary<string, int>(); 
 +</code>
  
-NUMBER -> @"[0-9]+"; +=====V1=====
-PLUSMINUS -> @"(\+|-)"; +
-MULTDIV -> @"\*|/"; +
-BROPEN ->  @"\("; +
-BRCLOSE ->  @"\)"; +
-EOF         -> @"^$";+
  
-[Skip] WHITESPACE  -> @"\s+";+<code> 
 +//@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 <expr> do <stmts> 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;  
 +}; 
 +</code> 
 + 
 +<code> 
 +let x = 13 
 + 
 +while x do 
 +    print x 
 +    let x = x - 1 
 +end 
 + 
 +print x 
 +</code> 
 + 
 + 
 +=====V2===== 
 + 
 +if/else dazu 
 + 
 +<code> 
 +//@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 <expr> do <stmts> 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 <expr> do <stmts> (else <stmts>)? 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;  
 +}; 
 +</code> 
 + 
 +<code> 
 +let x = 13 
 +let y = 2 
 + 
 +while x do 
 +    print x 
 +    let x = x - y 
 + 
 +    if x < 1do 
 + print x 
 +    end 
 +end 
 +print x 
 +</code> 
 + 
 + 
 +=====V3===== 
 + 
 +Mit Vergleichsoperatoren 
 + 
 +<code> 
 +//@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 <expr> do <stmts> 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 <expr> do <stmts> (else <stmts>)? 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;  
 +}; 
 +</code> 
 + 
 +<code> 
 +let x = 0 
 +while x < 5 do 
 +  print x 
 +  let x = x + 1 
 +end 
 + 
 +if x == 5 do 
 +  print 999 
 +else 
 +  print 111 
 +end 
 +</code> 
 + 
 + 
 +=====V4===== 
 + 
 +Mit Strings 
 + 
 +<code> 
 +//@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<string, object> 
 +    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; 
 +}; 
 +</code> 
 + 
 +<code> 
 +let x = 0 
 +let y = "Check" 
 +while x < 5 do 
 +  print x 
 +  let x = x + 1 
 + 
 +  if x > 3 do 
 +    print y 
 +  end 
 +end 
 +</code> 
 + 
 + 
 +=====V5===== 
 + 
 +Switch/Case 
 + 
 +<code> 
 +//@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 <expr> do (case <expr> do <stmts>)* (else <stmts>)? 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<string, object> 
 +    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; 
 +}; 
 +</code> 
 + 
 +<code> 
 +let x = "b"
  
-Start -> (AddExpr)? EOF; +switch x do 
-AddExpr -> MultExpr (PLUSMINUS MultExpr)*; +  case "a" do 
-MultExpr -> Atom (MULTDIV Atom)*; +    print "case a" 
-Atom -> NUMBER | BROPEN AddExpr BRCLOSE;+  case "b" do 
 +    print "case b" 
 +  else 
 +    print "default" 
 +end
 </code> </code>
tinypg.1765458574.txt.gz · Zuletzt geändert: 2025/12/11 14:09 von jango