Hier werden die Unterschiede zwischen zwei Versionen angezeigt.
| Beide Seiten der vorigen Revision Vorhergehende Überarbeitung Nächste Überarbeitung | Vorhergehende Überarbeitung | ||
|
tinypg [2025/12/12 11:35] jango |
tinypg [2025/12/12 12:49] (aktuell) jango [V4] |
||
|---|---|---|---|
| Zeile 544: | Zeile 544: | ||
| else | else | ||
| print 111 | print 111 | ||
| + | end | ||
| + | </ | ||
| + | |||
| + | |||
| + | =====V4===== | ||
| + | |||
| + | Mit Strings | ||
| + | |||
| + | < | ||
| + | //@TinyPG - a Tiny Parser Generator v1.2 | ||
| + | // Mini-Skriptsprache: | ||
| + | // int + string, ' | ||
| + | |||
| + | <% @TinyPG Language=" | ||
| + | |||
| + | // ---------------- Tokens ---------------- | ||
| + | |||
| + | EOF -> @" | ||
| + | |||
| + | [Color(255, 0, 0)] | ||
| + | [Color(255, 128, 0)] STRING | ||
| + | |||
| + | [Color(0, 0, 255)] | ||
| + | [Color(0, 0, 255)] | ||
| + | [Color(0, 0, 255)] | ||
| + | [Color(0, 0, 255)] | ||
| + | [Color(0, 0, 255)] | ||
| + | [Color(0, 0, 255)] | ||
| + | |||
| + | // Keywords – MÜSSEN VOR IDENT kommen! | ||
| + | [Color(0, 128, 0)] | ||
| + | [Color(0, 128, 0)] | ||
| + | [Color(0, 128, 0)] | ||
| + | [Color(0, 128, 0)] | ||
| + | [Color(0, 128, 0)] | ||
| + | [Color(0, 128, 0)] | ||
| + | [Color(0, 128, 0)] | ||
| + | |||
| + | [Color(128, 0, 128)] IDENT -> @" | ||
| + | |||
| + | [Skip] WHITESPACE | ||
| + | |||
| + | |||
| + | // ---------------- 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 ? " | ||
| + | return null; | ||
| + | }; | ||
| + | |||
| + | LetStmt -> LET IDENT ASSIGN Expr | ||
| + | { | ||
| + | string name = $IDENT.ToString(); | ||
| + | object value = $Expr; | ||
| + | |||
| + | // tree.Variables: | ||
| + | if (!tree.Variables.ContainsKey(name)) | ||
| + | tree.Variables.Add(name, | ||
| + | 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 == "<" | ||
| + | (op == "< | ||
| + | (op == ">" | ||
| + | (op == "> | ||
| + | (op == " | ||
| + | (op == " | ||
| + | |||
| + | return res ? 1 : 0; | ||
| + | } | ||
| + | |||
| + | // string vs string (ordinal) | ||
| + | if (left is string && right is string) | ||
| + | { | ||
| + | int cmp = string.CompareOrdinal((string)left, | ||
| + | |||
| + | bool res = | ||
| + | (op == "<" | ||
| + | (op == "< | ||
| + | (op == ">" | ||
| + | (op == "> | ||
| + | (op == " | ||
| + | (op == " | ||
| + | |||
| + | return res ? 1 : 0; | ||
| + | } | ||
| + | |||
| + | // gemischte Typen: == / != ok, Ordnung nicht | ||
| + | if (op == " | ||
| + | if (op == " | ||
| + | |||
| + | throw new Exception( | ||
| + | " | ||
| + | (left == null ? " | ||
| + | "' | ||
| + | (right == null ? " | ||
| + | "' | ||
| + | ); | ||
| + | }; | ||
| + | |||
| + | |||
| + | // ---------------- 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) ? "" | ||
| + | string rs = (rhs == null) ? "" | ||
| + | value = ls + rs; | ||
| + | } | ||
| + | else | ||
| + | { | ||
| + | // beide müssen int sein | ||
| + | if (!(value is int) || !(rhs is int)) | ||
| + | throw new Exception(" | ||
| + | value = (int)value + (int)rhs; | ||
| + | } | ||
| + | } | ||
| + | else | ||
| + | { | ||
| + | // ' | ||
| + | if (!(value is int) || !(rhs is int)) | ||
| + | throw new Exception(" | ||
| + | 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(" | ||
| + | |||
| + | 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(" | ||
| + | |||
| + | 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, | ||
| + | |||
| + | System.Text.StringBuilder sb = new System.Text.StringBuilder(); | ||
| + | for (int j = 0; j < raw.Length; j++) | ||
| + | { | ||
| + | char c = raw[j]; | ||
| + | if (c == ' | ||
| + | { | ||
| + | char n = raw[++j]; | ||
| + | if (n == ' | ||
| + | else if (n == ' | ||
| + | else if (n == ' | ||
| + | else if (n == ' | ||
| + | else if (n == '"' | ||
| + | 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(" | ||
| + | } | ||
| + | |||
| + | return $Expr; | ||
| + | }; | ||
| + | </ | ||
| + | |||
| + | < | ||
| + | let x = 0 | ||
| + | let y = " | ||
| + | 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: | ||
| + | // int + string, ' | ||
| + | |||
| + | <% @TinyPG Language=" | ||
| + | |||
| + | // ---------------- Tokens ---------------- | ||
| + | |||
| + | EOF -> @" | ||
| + | |||
| + | [Color(255, 0, 0)] | ||
| + | [Color(255, 128, 0)] STRING | ||
| + | |||
| + | [Color(0, 0, 255)] | ||
| + | [Color(0, 0, 255)] | ||
| + | [Color(0, 0, 255)] | ||
| + | [Color(0, 0, 255)] | ||
| + | [Color(0, 0, 255)] | ||
| + | [Color(0, 0, 255)] | ||
| + | |||
| + | // Keywords – MÜSSEN VOR IDENT kommen! | ||
| + | [Color(0, 128, 0)] | ||
| + | [Color(0, 128, 0)] | ||
| + | [Color(0, 128, 0)] | ||
| + | [Color(0, 128, 0)] | ||
| + | [Color(0, 128, 0)] | ||
| + | [Color(0, 128, 0)] | ||
| + | [Color(0, 128, 0)] | ||
| + | [Color(0, 128, 0)] | ||
| + | [Color(0, 128, 0)] | ||
| + | |||
| + | [Color(128, 0, 128)] IDENT -> @" | ||
| + | |||
| + | [Skip] WHITESPACE | ||
| + | |||
| + | |||
| + | // ---------------- 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 < | ||
| + | SwitchStmt -> SWITCH Expr DO (CASE Expr DO StmtList)* (ELSE StmtList)? END | ||
| + | { | ||
| + | object key = $Expr[0]; | ||
| + | int i = 1; // Expr[1..] sind case-werte | ||
| + | int caseBodyIndex = 0; // StmtList[0..] sind case-bodies, | ||
| + | |||
| + | 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, | ||
| + | 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 ? " | ||
| + | return null; | ||
| + | }; | ||
| + | |||
| + | LetStmt -> LET IDENT ASSIGN Expr | ||
| + | { | ||
| + | string name = $IDENT.ToString(); | ||
| + | object value = $Expr; | ||
| + | |||
| + | // tree.Variables: | ||
| + | if (!tree.Variables.ContainsKey(name)) | ||
| + | tree.Variables.Add(name, | ||
| + | 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 == "<" | ||
| + | (op == "< | ||
| + | (op == ">" | ||
| + | (op == "> | ||
| + | (op == " | ||
| + | (op == " | ||
| + | |||
| + | return res ? 1 : 0; | ||
| + | } | ||
| + | |||
| + | // string vs string (ordinal) | ||
| + | if (left is string && right is string) | ||
| + | { | ||
| + | int cmp = string.CompareOrdinal((string)left, | ||
| + | |||
| + | bool res = | ||
| + | (op == "<" | ||
| + | (op == "< | ||
| + | (op == ">" | ||
| + | (op == "> | ||
| + | (op == " | ||
| + | (op == " | ||
| + | |||
| + | return res ? 1 : 0; | ||
| + | } | ||
| + | |||
| + | // gemischte Typen: == / != ok, Ordnung nicht | ||
| + | if (op == " | ||
| + | if (op == " | ||
| + | |||
| + | throw new Exception( | ||
| + | " | ||
| + | (left == null ? " | ||
| + | "' | ||
| + | (right == null ? " | ||
| + | "' | ||
| + | ); | ||
| + | }; | ||
| + | |||
| + | |||
| + | // ---------------- 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) ? "" | ||
| + | string rs = (rhs == null) ? "" | ||
| + | value = ls + rs; | ||
| + | } | ||
| + | else | ||
| + | { | ||
| + | // beide müssen int sein | ||
| + | if (!(value is int) || !(rhs is int)) | ||
| + | throw new Exception(" | ||
| + | value = (int)value + (int)rhs; | ||
| + | } | ||
| + | } | ||
| + | else | ||
| + | { | ||
| + | // ' | ||
| + | if (!(value is int) || !(rhs is int)) | ||
| + | throw new Exception(" | ||
| + | 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(" | ||
| + | |||
| + | 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(" | ||
| + | |||
| + | 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, | ||
| + | |||
| + | System.Text.StringBuilder sb = new System.Text.StringBuilder(); | ||
| + | for (int j = 0; j < raw.Length; j++) | ||
| + | { | ||
| + | char c = raw[j]; | ||
| + | if (c == ' | ||
| + | { | ||
| + | char n = raw[++j]; | ||
| + | if (n == ' | ||
| + | else if (n == ' | ||
| + | else if (n == ' | ||
| + | else if (n == ' | ||
| + | else if (n == '"' | ||
| + | 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(" | ||
| + | } | ||
| + | |||
| + | return $Expr; | ||
| + | }; | ||
| + | </ | ||
| + | |||
| + | < | ||
| + | let x = " | ||
| + | |||
| + | switch x do | ||
| + | case " | ||
| + | print "case a" | ||
| + | case " | ||
| + | print "case b" | ||
| + | else | ||
| + | print " | ||
| end | end | ||
| </ | </ | ||