From b0c47947a6c18cecf3dc7fe08611d32528191f0e Mon Sep 17 00:00:00 2001 From: Suzanne Soy Date: Wed, 19 Aug 2020 18:36:41 +0000 Subject: [PATCH] Custom exception classes, HashCode, started Parser --- .replit | 2 +- Exceptions.cs | 17 +++++++++++++++++ Lexer.cs | 6 +++--- Parser.cs | 23 +++++++++++++++++++++-- ParserGenerator.cs | 44 ++++++++++++++++++++++++++++++++++++++++++++ T4/Generator.cs | 43 ++++++++++++++++++++++++++----------------- Utils/Equality.cs | 16 ++++++++++++++++ main.cs | 46 ++++++++++++++++++++++++++-------------------- 8 files changed, 154 insertions(+), 43 deletions(-) create mode 100644 Exceptions.cs create mode 100644 ParserGenerator.cs diff --git a/.replit b/.replit index bf8522c..bd648a2 100644 --- a/.replit +++ b/.replit @@ -1 +1 @@ -run='make' +run='reset; make' diff --git a/Exceptions.cs b/Exceptions.cs new file mode 100644 index 0000000..6b2f463 --- /dev/null +++ b/Exceptions.cs @@ -0,0 +1,17 @@ +using System; + +public abstract class UserErrorException : Exception { + public UserErrorException(string e) : base(e) {} +} + +public class ParserErrorException : UserErrorException { + public ParserErrorException(string e) : base("Parser error: " + e) {} +} + +public class LexerErrorException : UserErrorException { + public LexerErrorException(string e) : base("Lexer error: " + e) {} +} + +public class TestFailedException : UserErrorException { + public TestFailedException(string e) : base("Test failed: " + e) {} +} \ No newline at end of file diff --git a/Lexer.cs b/Lexer.cs index 6402812..ed5c4d5 100644 --- a/Lexer.cs +++ b/Lexer.cs @@ -122,7 +122,7 @@ public static partial class Lexer { return result; } - public static Exception ParseError(StringBuilder context, IEnumerator stream, S state, List possibleNext, GraphemeCluster gc) { + public static ParserErrorException ParserError(StringBuilder context, IEnumerator stream, S state, List possibleNext, GraphemeCluster gc) { var rest = stream .SingleUseEnumerable() @@ -136,7 +136,7 @@ public static partial class Lexer { .First() .Match(some: (x => x.UnicodeCategory(0).ToString()), none: "None (empty string)"); - return new Exception( + return new ParserErrorException( $"Unexpected {actual} (Unicode category {cat}) while the lexer was in state {state}: expected one of {expected}{Environment.NewLine}{context} <--HERE {rest}" ); } @@ -158,7 +158,7 @@ public static partial class Lexer { possibleNext .First(r => r.test(c)) .IfSome(rule => Transition(ref state, ref lexeme, c, rule)) - .ElseThrow(() => ParseError(context, e, state, possibleNext, c)); + .ElseThrow(() => ParserError(context, e, state, possibleNext, c)); } } diff --git a/Parser.cs b/Parser.cs index 6ad8bee..7f6c6c9 100644 --- a/Parser.cs +++ b/Parser.cs @@ -4,9 +4,27 @@ using System.Collections.Generic; using System.Linq; using Immutable; using S = Lexer.S; +using Lexeme = Lexer.Lexeme; using static Global; -public static class Parser { +public static partial class Parser { + public class PrecedenceDAG: Dictionary {}; + + public static PrecedenceDAG DefaultPrecedenceDAG = new PrecedenceDAG(); + + public static PrecedenceDAG With(PrecedenceDAG precedenceDAG, Operator @operator) { + + throw new NotImplementedException(); + } + + public static void DagToGrammar(DAGNode precedenceDAG) { + + } + + public static void RecursiveDescent(IEnumerable e) { + + } + public static Ast.Expr Parse(string source) { return Lexer.Lex(source) .SelectMany(lexeme => @@ -21,7 +39,8 @@ public static class Parser { ) ) .Single() - .ElseThrow(() => new Exception("empty file or more than one expression in file.")); + .ElseThrow(() => new ParserErrorException( + "empty file or more than one expression in file.")); } } diff --git a/ParserGenerator.cs b/ParserGenerator.cs new file mode 100644 index 0000000..0536f9d --- /dev/null +++ b/ParserGenerator.cs @@ -0,0 +1,44 @@ +using static Generator; + +public static class ParserGenerator { + public static void Main() { + Generate( + "ParserGenerated.cs", + "using System.Collections.Generic;\n" + + "using S = Lexer.S;", + "public static partial class Parser {", + "}", + "Parser.", + Types( + Variant("Grammar", + Case("List", "Or"), + Case("List", "Sequence")), + + Variant("Fixity", + Case("Closed"), + Case("InfixLeftAssociative"), + Case("InfixRightAssociative"), + Case("InfixNonAssociative"), + Case("Prefix"), + Case("Postfix"), + Case("Terminal")), + + Record("Operator", + Field("List", "Parts"), + Field("List", "Holes")), + + Record("Closed", + Field("S", "openSymbol"), + Field("S", "closedSymbol")), + + Record("DAGNode", + Field("List", "infixLeftAssociative"), + Field("List", "infixRightAssociative"), + Field("List", "infixNonAssociative"), + Field("List", "prefix"), + Field("List", "postfix"), + Field("List", "closed"), + Field("List", "terminal"), + Field("List", "successors")))); + } +} \ No newline at end of file diff --git a/T4/Generator.cs b/T4/Generator.cs index 288fdd1..871edfc 100644 --- a/T4/Generator.cs +++ b/T4/Generator.cs @@ -11,7 +11,7 @@ public enum Kind { public static class Generator { public static void WriteVariant(this System.IO.StreamWriter o, string header, string footer, string qualifier, string name, Dictionary variant) { o.WriteLine($"{header}"); - o.WriteLine(""); + o.WriteLine($""); o.WriteLine($" /* To match against an instance of {name}, write:"); o.WriteLine($" x.Match("); @@ -21,17 +21,29 @@ public static class Generator { o.WriteLine($" */"); o.WriteLine($" public abstract class {name} : IEquatable<{name}> {{"); + + o.WriteLine($" public class Visitor {{"); + foreach (var @case in variant) { + var C = @case.Key; + var Ty = @case.Value; + + o.WriteLine($" public Func<{Ty == null ? "" : $"{Ty}, "}T> {C} {{ get; set; }} "); + } + o.WriteLine($" }}"); + o.WriteLine($" public abstract T Match_(Visitor c);"); foreach (var @case in variant) { var C = @case.Key; var Ty = @case.Value; - o.WriteLine($" public static {name} {C}{Ty == null ? $" = new {C}()" : $"({Ty} value) => new {C}(value)"};"); + o.WriteLine($" public static {name} {C}{Ty == null + ? $" = new Constructors.{C}()" + : $"({Ty} value) => new Constructors.{C}(value)"};"); } foreach (var @case in variant) { var C = @case.Key; var Ty = @case.Value; - o.WriteLine($" public virtual Immutable.Option<{C}> As{C}() => Immutable.Option.None<{C}>();"); + o.WriteLine($" public virtual Immutable.Option As{C}() => Immutable.Option.None();"); } o.WriteLine($" private string GetTag() {{"); @@ -40,23 +52,17 @@ public static class Generator { $" {@case.Key}: {@case.Value == null ? "()" : "value"} => \"{@case.Key}\""))); o.WriteLine($" );"); o.WriteLine($" }}"); - o.WriteLine(""); - o.WriteLine($" public abstract bool Equals(Object other);"); + o.WriteLine($""); + o.WriteLine($" public override abstract bool Equals(Object other);"); o.WriteLine($" public abstract bool Equals({name} other);"); - o.WriteLine(""); - o.WriteLine($" }}"); - o.WriteLine(""); + o.WriteLine($""); + o.WriteLine($" public override abstract int GetHashCode();"); + o.WriteLine($" public static class Constructors {{"); foreach (var @case in variant) { var C = @case.Key; var Ty = @case.Value; - o.WriteLine( - $" public partial class Visitor {{" - + $" public Func<{Ty == null ? "" : $"{Ty}, "}T> {C} {{ get; set; }} " - + @"}"); - o.WriteLine(""); - o.WriteLine($" public sealed class {C} : {name} {{"); if (Ty != null) { o.WriteLine($" public readonly {Ty} value;"); @@ -88,6 +94,9 @@ public static class Generator { o.WriteLine(""); } + o.WriteLine($" }}"); + o.WriteLine($" }}"); + o.WriteLine(""); o.WriteLine($"}}"); o.WriteLine($"public static class {name}ExtensionMethods {{"); @@ -96,7 +105,7 @@ public static class Generator { o.WriteLine(String.Join(",\n", variant.Select(c => $" Func<{c.Value == null ? "" : $"{c.Value}, "}T> {c.Key}"))); o.WriteLine($" ) {{"); - o.WriteLine($" return e.Match_(new {qualifier}Visitor {{"); + o.WriteLine($" return e.Match_(new {qualifier}{name}.Visitor {{"); o.WriteLine(String.Join(",\n", variant.Select(c => $" {c.Key} = {c.Key}"))); o.WriteLine($" }});"); @@ -107,7 +116,7 @@ public static class Generator { public static void WriteRecord(this System.IO.StreamWriter o, string header, string footer, string qualifier, string name, Dictionary record) { o.WriteLine($"{header}"); o.WriteLine(""); - o.WriteLine($" public class {name} : IEquatable<{name}> {{"); + o.WriteLine($" public sealed class {name} : IEquatable<{name}> {{"); foreach (var @field in record) { var F = @field.Key; var Ty = @field.Value; @@ -136,7 +145,7 @@ public static class Generator { o.WriteLine($" public bool Equals({name} other)"); o.WriteLine($" => Equality.Equatable<{name}>(this, other);"); o.WriteLine($" public override int GetHashCode()"); - o.WriteLine($" => HashCode.Combine(\"{name}\","); + o.WriteLine($" => Equality.HashCode(\"{name}\","); o.WriteLine(String.Join(",\n", record.Select(@field => $" this.{@field.Key}"))); o.WriteLine($" );"); diff --git a/Utils/Equality.cs b/Utils/Equality.cs index 1c8697f..2958242 100644 --- a/Utils/Equality.cs +++ b/Utils/Equality.cs @@ -56,4 +56,20 @@ public static class Equality { return ((Object)a).Equals(b); } } + + public static int HashCode(Object o1) => System.HashCode.Combine(o1); + public static int HashCode(Object o1, Object o2) => System.HashCode.Combine(o1, o2); + public static int HashCode(Object o1, Object o2, Object o3) => System.HashCode.Combine(o1, o2, o3); + public static int HashCode(Object o1, Object o2, Object o3, Object o4) => System.HashCode.Combine(o1, o2, o3, o4); + public static int HashCode(Object o1, Object o2, Object o3, Object o4, Object o5) => System.HashCode.Combine(o1, o2, o3, o4, o5); + public static int HashCode(Object o1, Object o2, Object o3, Object o4, Object o5, Object o6) => System.HashCode.Combine(o1, o2, o3, o4, o5, o6); + public static int HashCode(Object o1, Object o2, Object o3, Object o4, Object o5, Object o6, Object o7) => System.HashCode.Combine(o1, o2, o3, o4, o5, o6, o7); + public static int HashCode(Object o1, Object o2, Object o3, Object o4, Object o5, Object o6, Object o7, Object o8) => System.HashCode.Combine(o1, o2, o3, o4, o5, o6, o7, o8); + + public static int HashCode(Object o1, Object o2, Object o3, Object o4, Object o5, Object o6, Object o7, Object o8, params Object[] objects) { + var hash = System.HashCode.Combine(o1, o2, o3, o4, o5, o6, o7, o8);foreach (var o in objects) { + hash = System.HashCode.Combine(hash, o); + } + return hash; + } } \ No newline at end of file diff --git a/main.cs b/main.cs index 6622d1e..7fd9b56 100644 --- a/main.cs +++ b/main.cs @@ -32,7 +32,7 @@ public static class MainClass { var expectedStr = expected.Read(); if (actualStr != expectedStr) { Console.WriteLine("\x1b[1;31mFail\x1b[m"); - throw new Exception($"Test failed {source}: expected {expectedStr} but got {actualStr}."); + throw new TestFailedException($"{source}: expected {expectedStr} but got {actualStr}."); } else { Console.Write("\x1b[1;32mOK\x1b[m\r"); } @@ -57,26 +57,32 @@ public static class MainClass { } public static void Main (string[] args) { - if (args.Length != 1) { - Console.WriteLine("Usage: mono main.exe path/to/file.e"); + try { + if (args.Length != 1) { + Console.WriteLine("Usage: mono main.exe path/to/file.e"); + Console.WriteLine(""); + Console.WriteLine("Language syntax:"); + Console.WriteLine(""); + Console.WriteLine(" Expression ="); + Console.WriteLine(" Int"); + Console.WriteLine(" | String"); + Console.WriteLine(" | Variable"); + Console.WriteLine(" | Pattern \"->\" Expression"); + Console.WriteLine(""); + Console.WriteLine("I'll run the tests for you in the meanwhile."); + Console.WriteLine(""); + RunTests(); + } else { + var source = args[0].File(); + var destPrefix = source.DropExtension(); + CompileToFile(Compilers.JS.Compile, source, destPrefix.Combine(Ext(".js"))); + CompileToFile(Evaluator.Evaluate, source, destPrefix.Combine(Ext(".txt"))); + Console.Write(destPrefix.Combine(Ext(".txt")).Read()); + } + } catch (UserErrorException e) { Console.WriteLine(""); - Console.WriteLine("Language syntax:"); - Console.WriteLine(""); - Console.WriteLine(" Expression ="); - Console.WriteLine(" Int"); - Console.WriteLine(" | String"); - Console.WriteLine(" | Variable"); - Console.WriteLine(" | Pattern \"->\" Expression"); - Console.WriteLine(""); - Console.WriteLine("I'll run the tests for you in the meanwhile."); - Console.WriteLine(""); - RunTests(); - } else { - var source = args[0].File(); - var destPrefix = source.DropExtension(); - CompileToFile(Compilers.JS.Compile, source, destPrefix.Combine(Ext(".js"))); - CompileToFile(Evaluator.Evaluate, source, destPrefix.Combine(Ext(".txt"))); - Console.Write(destPrefix.Combine(Ext(".txt")).Read()); + Console.WriteLine(e.Message); + Environment.Exit(1); } } } \ No newline at end of file