Dies ist eine alte Version des Dokuments!
TinyPG
In der Datei
Templates\C#\ParseTree.cs
in der Klasse ParseTree folgendes Property dazu!
public Dictionary<string, int> Variables = new Dictionary<string, int>();
//@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
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
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
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