Benutzer-Werkzeuge

Webseiten-Werkzeuge


tinypg

Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen angezeigt.

Link zu dieser Vergleichsansicht

Beide Seiten der vorigen Revision Vorhergehende Überarbeitung
tinypg [2025/12/12 12:42]
jango
tinypg [2025/12/12 12:49] (aktuell)
jango [V4]
Zeile 856: Zeile 856:
     print y     print y
   end   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"
 +
 +switch x do
 +  case "a" do
 +    print "case a"
 +  case "b" do
 +    print "case b"
 +  else
 +    print "default"
 end end
 </code> </code>
tinypg.txt · Zuletzt geändert: 2025/12/12 12:49 von jango