diff --git a/samples/CalcExpr/ExpressionParser.cs b/samples/CalcExpr/ExpressionParser.cs index 22334f1..12fbb68 100644 --- a/samples/CalcExpr/ExpressionParser.cs +++ b/samples/CalcExpr/ExpressionParser.cs @@ -35,12 +35,12 @@ private static Parser CreateExpressionParser() ).Do((u, _, d) => u.HasValue ? -d : d); var mul_expr = - unary_expr.Fold( + unary_expr.FoldL( OneOf("*/").ThenIgnore(S), (l, r, o) => o == '*' ? l * r : l / r); sum_expr.Parser = - mul_expr.Fold( + mul_expr.FoldL( OneOf("+-").ThenIgnore(S), (l, r, o) => o == '+' ? l + r : l - r); diff --git a/samples/CalcExpr/README.md b/samples/CalcExpr/README.md index 37b43ae..a4a0730 100644 --- a/samples/CalcExpr/README.md +++ b/samples/CalcExpr/README.md @@ -4,21 +4,21 @@ This project implements a simple mathematical expression parser. ## Simple Expression Grammar -```sh +```text start = S sum_expr EOF ; sum_expr - = mul_expr (S [+-] mul_expr)* + = mul_expr ([+-] S mul_expr)* ; mul_expr - = unary_expr (S [*/] unary_expr)* + = unary_expr ([*/] S unary_expr)* ; unary_expr - = S "-"? primary_expr + = "-"? S primary_expr ; primary_expr @@ -26,15 +26,19 @@ primary_expr ; parenthesis_expr - = S "(" Sum S ")" + = "(" S sum_expr ")" S ; number_expr - = S [0-9]+ + = number S + ; + +number + = [+-]? [0-9]+ ("." [0-9]+)? ([eE] [+-]? [0-9]+)? ; S - = [ \t\r\n]* + = [\s]* ; EOF diff --git a/samples/Json/JsonParser.cs b/samples/Json/JsonParser.cs index b7290d7..f5c9b39 100644 --- a/samples/Json/JsonParser.cs +++ b/samples/Json/JsonParser.cs @@ -55,6 +55,6 @@ public static class JsonParser @object ).ThenIgnore(S); - return S.Then(value); + return value.Between(S, Eof); } } diff --git a/samples/Json/README.md b/samples/Json/README.md index 9bf1a9f..469c3cc 100644 --- a/samples/Json/README.md +++ b/samples/Json/README.md @@ -2,11 +2,9 @@ This project implements a simple JSON parser. -## JSON Grammar - -```sh +```text start - = value $ + = S value $ ; value @@ -18,7 +16,7 @@ object ; member - = string ":" S value + = string S ":" S value ; array @@ -26,6 +24,6 @@ array ; S - = [ \t\r\n]* + = [\s]* ; ``` diff --git a/samples/TinyC/Node.cs b/samples/TinyC/Node.cs index 1e9f4dd..be92f82 100644 --- a/samples/TinyC/Node.cs +++ b/samples/TinyC/Node.cs @@ -14,6 +14,7 @@ public abstract record Node public static Node Unary(char op, Node operand) => new UnaryNode(op, operand); public static Node Binary(string op, Node left, Node right) => new BinaryNode(op, left, right); public static Node Assign(Node variable, Node value) => Binary("=", variable, value); + public static Node ExpressionStatement(Node expression) => new ExpressionStatementNode(expression); public static Node Block(IReadOnlyList statements) => new BlockNode(statements); public static Node Block(Node statement) => statement is BlockNode ? statement : new BlockNode([statement]); @@ -57,6 +58,7 @@ static void Print(Node node, IndentedStringBuilder sb) break; case TernaryNode c: + sb.Append('('); Print(c.Test, sb); sb.IncrementIndent(); sb @@ -68,6 +70,7 @@ static void Print(Node node, IndentedStringBuilder sb) .AppendLine() .Append(": "); Print(c.IfFalse, sb); + sb.Append(')'); sb.DecrementIndent(); break; @@ -95,8 +98,9 @@ static void Print(Node node, IndentedStringBuilder sb) break; case BinaryNode(Operator: "=", var variable, var expr): + sb.Append('('); Print(variable, sb); - sb.Append(" = ("); + sb.Append(" = "); Print(expr, sb); sb.Append(')'); break; @@ -109,16 +113,17 @@ static void Print(Node node, IndentedStringBuilder sb) sb.Append(')'); break; + case ExpressionStatementNode statement: + Print(statement.Expression, sb); + sb.AppendLine(";"); + break; + case BlockNode block: sb.AppendLine("{"); sb.IncrementIndent(); foreach (var stmt in block.Statements) - { Print(stmt, sb); - if (stmt is NumberNode or VariableNode or UnaryNode or BinaryNode) - sb.AppendLine(";"); - } sb.DecrementIndent(); sb.AppendLine("}"); @@ -135,5 +140,6 @@ public sealed record WhileLoopNode(Node Test, Node Body) : Node; public sealed record DoWhileLoopNode(Node Test, Node Body) : Node; public sealed record UnaryNode(char Operator, Node Operand) : Node; public sealed record BinaryNode(string Operator, Node Left, Node Right) : Node; + public sealed record ExpressionStatementNode(Node Expression) : Node; public sealed record BlockNode(IReadOnlyList Statements) : Node; } diff --git a/samples/TinyC/README.md b/samples/TinyC/README.md index 299f75c..839b1eb 100644 --- a/samples/TinyC/README.md +++ b/samples/TinyC/README.md @@ -1,22 +1,42 @@ # Tiny-C -This project implements a parser for the [Tiny-C](http://www.iro.umontreal.ca/~felipe/IFT2030-Automne2002/Complements/tinyc.c) language, a highly simplified version of `C` designed as an educational tool for learning about compilers. - -All variables are predefined, of integer type, and initialized to zero. +This project implements a parser for the [Tiny-C](http://www.iro.umontreal.ca/~felipe/IFT2030-Automne2002/Complements/tinyc.c) language, +a highly simplified version of `C` designed as an educational tool for learning about compilers. The main differences from the original `Tiny-C` are: - Variable names are not limited to single letters. - Additional operators are supported. +- Line comments (`// ...`) and block comments (`/* ... */`) are supported. ## Tiny-C Grammar -```sh +``` start = S statement EOF ; keyword - = ("while" / "do" / "if" / "else") ![\w] + = while_keyword / do_keyword / if_keyword / else_keyword + ; + +identifier_part + = [a-zA-Z_0-9] + ; + +while_keyword + = "while" !identifier_part + ; + +do_keyword + = "do" !identifier_part + ; + +if_keyword + = "if" !identifier_part + ; + +else_keyword + = "else" !identifier_part ; number @@ -24,11 +44,23 @@ number ; variable - = !keyword [a-zA-Z_][a-zA-Z0-9_]* + = !keyword [a-z] identifier_part* ; S - = [ \t\n\r]* + = (single_comment / multiline_comment / [\s])* + ; + +single_comment + = "//" (!EOL .)* EOL + ; + +multiline_comment + = "/*" (!"*/" .)* "*/" + ; + +EOL + = "\r\n" / [\r\n\u0085\u2028\u2029] / EOF ; EOF @@ -44,11 +76,11 @@ number_expr ; expr - = assigment_expr + = assignment_expr / ternary_expr ; -assigment_expr +assignment_expr = var_expr "=" S expr ; @@ -81,7 +113,7 @@ eq_expr ; relational_expr - = shift_expr (("<" / "<=" / ">" / ">=") S shift_expr)* + = shift_expr (("<=" / "<" / ">=" / ">") S shift_expr)* ; shift_expr @@ -120,15 +152,15 @@ statement ; if_statement - = "if" S "(" S expr ")" S statement ("else" S statement)? + = if_keyword S "(" S expr ")" S statement (else_keyword S statement)? ; while_statement - = "while" S "(" S expr ")" S statement + = while_keyword S "(" S expr ")" S statement ; do_while_statement - = "do" S statement "while" S "(" S expr ")" S ";" S + = do_keyword S statement while_keyword S "(" S expr ")" S ";" S ; block_statement diff --git a/samples/TinyC/TinyCParser.cs b/samples/TinyC/TinyCParser.cs index 8f53813..66b87b7 100644 --- a/samples/TinyC/TinyCParser.cs +++ b/samples/TinyC/TinyCParser.cs @@ -14,10 +14,8 @@ public static class TinyCParser [SuppressMessage("ReSharper", "InconsistentNaming")] private static Parser CreateParser() { - var keyword = - Seq( - OneOf("while", "do", "if", "else"), - Not(Set("\\w"))); + var identifier_part = + Set("a-zA-Z_0-9"); var number = Set("0-9") @@ -25,13 +23,6 @@ private static Parser CreateParser() .Map(Node (m) => Node.Number(int.Parse(m, NumberStyles.Integer, CultureInfo.InvariantCulture))) .As("number"); - var variable = - Seq( - Not(keyword), - Set("a-z"), - Set("a-zA-Z_0-9").ZeroOrMore() - ).Map(Identifier).As("variable"); - var multiline_comment = Seq( L("/*"), @@ -63,16 +54,30 @@ private static Parser CreateParser() Seq(L('='), ws).Void(); var if_keyword = - Seq(L("if"), ws).Void(); + Keyword("if").ThenIgnore(ws).Void(); var else_keyword = - Seq(L("else"), ws).Void(); + Keyword("else").ThenIgnore(ws).Void(); var while_keyword = - Seq(L("while"), ws).Void(); + Keyword("while").ThenIgnore(ws).Void(); var do_keyword = - Seq(L("do"), ws).Void(); + Keyword("do").ThenIgnore(ws).Void(); + + var keyword = + Choice( + while_keyword, + do_keyword, + if_keyword, + else_keyword); + + var variable = + Seq( + Not(keyword), + Set("a-z"), + identifier_part.ZeroOrMore() + ).Map(Identifier).As("variable"); var expr = Deferred(); @@ -101,52 +106,52 @@ private static Parser CreateParser() primary_expr ).Do(CreateUnary); - var mul_expr = unary_expr.Fold( + var mul_expr = unary_expr.FoldL( OneOf("*/%").ThenIgnore(ws), CreateBinary); var sum_expr = - mul_expr.Fold( + mul_expr.FoldL( OneOf("+-").ThenIgnore(ws), CreateBinary); var shift_expr = - sum_expr.Fold( + sum_expr.FoldL( OneOf("<<", ">>").ThenIgnore(ws), (l, r, o) => Node.Binary(o, l, r)); var relational_expr = - shift_expr.Fold( + shift_expr.FoldL( OneOf("<", "<=", ">", ">=").ThenIgnore(ws), (l, r, o) => Node.Binary(o, l, r)); var eq_expr = - relational_expr.Fold( + relational_expr.FoldL( OneOf("==", "!=").ThenIgnore(ws), (l, r, o) => Node.Binary(o, l, r)); var bitwise_and_expr = - eq_expr.Fold( + eq_expr.FoldL( L('&').ThenIgnore(ws), (l, r, _) => Node.Binary("&", l, r)); var bitwise_xor_expr = - bitwise_and_expr.Fold( + bitwise_and_expr.FoldL( L('^').ThenIgnore(ws), (l, r, _) => Node.Binary("^", l, r)); var bitwise_or_expr = - bitwise_xor_expr.Fold( + bitwise_xor_expr.FoldL( L('|').ThenIgnore(ws), (l, r, _) => Node.Binary("|", l, r)); var logical_and_expr = - bitwise_or_expr.Fold( + bitwise_or_expr.FoldL( L("&&").ThenIgnore(ws), (l, r, _) => Node.Binary("&&", l, r)); var logical_or_expr = - logical_and_expr.Fold( + logical_and_expr.FoldL( L("||").ThenIgnore(ws), (l, r, _) => Node.Binary("||", l, r)); @@ -213,7 +218,7 @@ private static Parser CreateParser() ).Do(CreateDoWhile); var expr_statement = - expr.ThenIgnore(semicolon); + expr.ThenIgnore(semicolon).Do(Node.ExpressionStatement); statement.Parser = Choice( @@ -227,6 +232,9 @@ private static Parser CreateParser() return statement .Between(ws, Eof); + Parser Keyword(string word) => + L(word).ThenIgnore(Not(identifier_part)).As($"'{word}'"); + static Node Identifier(Match m) => Node.Variable(m.ToString());