Benutzer-Werkzeuge

Webseiten-Werkzeuge


tinypg

Dies ist eine alte Version des Dokuments!


Inhaltsverzeichnis

TinyPG

In der Datei

Templates\C#\ParseTree.cs

in der Klasse ParseTree folgendes Property dazu!

public Dictionary<string, int> Variables  = new Dictionary<string, int>();

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 <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; 
};
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 <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; 
};
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 <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; 
};
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<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;
};
let x = 0
let y = "Check"
while x < 5 do
  print x
  let x = x + 1

  if x > 3 do
    print y
  end
end
tinypg.1765539776.txt.gz · Zuletzt geändert: 2025/12/12 12:42 von jango