From 9ce933878872361903c89c73cbfba3cfb10df950 Mon Sep 17 00:00:00 2001 From: b3b00 Date: Mon, 29 Nov 2021 10:44:02 +0100 Subject: [PATCH 1/6] wip --- Issue259/CslyNullIssue/CslyNullIssue.csproj | 13 +++ Issue259/CslyNullIssue/ExpressionParser.cs | 111 ++++++++++++++++++++ Issue259/CslyNullIssue/ExpressionToken.cs | 68 ++++++++++++ Issue259/CslyNullIssue/ParserSubclass.cs | 35 ++++++ Issue259/Tests/Issue259Tests.cs | 56 ++++++++++ Issue259/Tests/Tests.csproj | 28 +++++ sly.sln | 16 +++ sly/parser/parser/Parser.cs | 11 +- sly/parser/parser/SyntaxParseResult.cs | 4 + 9 files changed, 339 insertions(+), 3 deletions(-) create mode 100644 Issue259/CslyNullIssue/CslyNullIssue.csproj create mode 100644 Issue259/CslyNullIssue/ExpressionParser.cs create mode 100644 Issue259/CslyNullIssue/ExpressionToken.cs create mode 100644 Issue259/CslyNullIssue/ParserSubclass.cs create mode 100644 Issue259/Tests/Issue259Tests.cs create mode 100644 Issue259/Tests/Tests.csproj diff --git a/Issue259/CslyNullIssue/CslyNullIssue.csproj b/Issue259/CslyNullIssue/CslyNullIssue.csproj new file mode 100644 index 00000000..9667f59a --- /dev/null +++ b/Issue259/CslyNullIssue/CslyNullIssue.csproj @@ -0,0 +1,13 @@ + + + + Library + net5.0 + CslyNull259Issue + + + + + + + diff --git a/Issue259/CslyNullIssue/ExpressionParser.cs b/Issue259/CslyNullIssue/ExpressionParser.cs new file mode 100644 index 00000000..bc5e8482 --- /dev/null +++ b/Issue259/CslyNullIssue/ExpressionParser.cs @@ -0,0 +1,111 @@ +using sly.lexer; +using sly.parser; +using sly.parser.generator; +using System; +using System.Globalization; +using System.Linq; + +namespace CslyNullIssue +{ + public abstract class ExpressionParser + { + [Operand] + [Production("logical_literal: OFF")] + [Production("logical_literal: ON")] + public string LiteralBool(Token token) + { + return token.Value; + } + + [Operand] + [Production("primary: HEX_NUMBER")] + public string NumericExpressionFromLiteralNumber(Token offsetToken) + { + return offsetToken.Value; + } + + [Operand] + [Production("primary: DECIMAL_NUMBER")] + public string NumericExpressionFromDecimalNumber(Token offsetToken) + { + var text = offsetToken.Value; + var value = double.Parse(text, CultureInfo.InvariantCulture); + return value.ToString(CultureInfo.InvariantCulture); + } + + [Infix((int)ExpressionToken.PLUS, Associativity.Left, 14)] + [Infix((int)ExpressionToken.MINUS, Associativity.Left, 14)] + [Infix((int)ExpressionToken.TIMES, Associativity.Left, 15)] + [Infix((int)ExpressionToken.DIVIDE, Associativity.Left, 15)] + [Infix((int)ExpressionToken.BITWISE_AND, Associativity.Left, 10)] + [Infix((int)ExpressionToken.BITWISE_OR, Associativity.Left, 8)] + public string NumberExpression(string lhs, Token token, string rhs) + { + return $"({lhs} {token.Value} {rhs})"; + } + + [Infix((int)ExpressionToken.LOGICAL_AND, Associativity.Left, 7)] + [Infix((int)ExpressionToken.LOGICAL_OR, Associativity.Left, 6)] + public string LogicalExpression(string lhs, Token token, string rhs) + { + return $"({lhs} {token.Value} {rhs})"; + } + + [Prefix((int)ExpressionToken.MINUS, Associativity.Right, 17)] + public string NumericExpression(Token _, string child) + { + return $"-{child}"; + } + + // We want NOT to to bind tighter than AND/OR but looser than numeric comparison operations + [Prefix((int)ExpressionToken.NOT, Associativity.Right, 11)] + public string LogicalExpression(Token _, string child) + { + return $"(NOT {child})"; + } + + [Infix((int)ExpressionToken.COMPARISON, Associativity.Left, 12)] + public string Comparison(string lhs, Token token, string rhs) + { + return $"({lhs} {token.Value} {rhs})"; + } + + private static Parser cachedParser; + + public static string Parse(string expression) where T : ExpressionParser, new() + { + if (cachedParser == null) + { + var startingRule = $"{typeof(T).Name}_expressions"; + var parserInstance = new T(); + var builder = new ParserBuilder(); + var parser = builder.BuildParser(parserInstance, ParserType.EBNF_LL_RECURSIVE_DESCENT, startingRule); + if (parser.IsError) + { + throw new Exception($"Could not create parser. BNF is not valid. {parser.Errors[0]}"); + } + cachedParser = parser.Result; + } + + // To simplify an ambiguous lexer which would result from having both && and & as well as || and |, we'll + // simplify the incoming expression by turning && into AND and || into OR: + expression = expression.Replace("&&", " AND "); + expression = expression.Replace("||", " OR "); + + var parseResult = cachedParser.Parse(expression); + if (parseResult.IsError) + { + if (parseResult.Errors.Any()) + { + throw new Exception(parseResult.Errors[0].ErrorMessage); + } + else + { + throw new Exception("unknwon error "); + } + } + + return parseResult.Result; + } + } +} \ No newline at end of file diff --git a/Issue259/CslyNullIssue/ExpressionToken.cs b/Issue259/CslyNullIssue/ExpressionToken.cs new file mode 100644 index 00000000..8b3cff53 --- /dev/null +++ b/Issue259/CslyNullIssue/ExpressionToken.cs @@ -0,0 +1,68 @@ +using sly.lexer; + +namespace CslyNullIssue +{ + public enum ExpressionToken + { + [Lexeme("L:[A-Za-z0-9_]+")] + LVAR = 50, + + [Lexeme("A:[:A-Za-z0-9 ]+,\\s*([A-Za-z0-9 ]+)")] + SIMVAR = 52, + + // [Lexeme("\\[[A-Z_0-9 ]+:[A-Z_0-9 ]+\\]")] + [Lexeme("\\[[A-Za-z_0-9 ]+:[A-Za-z_0-9 ]+\\]")] + DCS_VAR = 53, + + [Lexeme("OFF")] + OFF = 0, + + [Lexeme("ON")] + ON = 1, + + // Hex/decimal prefix? + [Lexeme("0[xX][0-9a-fA-F]+")] + HEX_NUMBER = 2, + + [Lexeme("[0-9]+(\\.[0-9]+)?")] + DECIMAL_NUMBER = 3, + + [Lexeme("\\+")] + PLUS = 4, + + [Lexeme("-")] + MINUS = 5, + + [Lexeme("\\*")] + TIMES = 6, + + [Lexeme("/")] + DIVIDE = 7, + + [Lexeme("\\|")] + BITWISE_OR = 8, + + [Lexeme("&")] + BITWISE_AND = 9, + + [Lexeme("[ \\t]+", isSkippable: true)] + WHITESPACE = 20, + + [Lexeme("OR")] + LOGICAL_OR = 10, + [Lexeme("AND")] + LOGICAL_AND = 11, + + [Lexeme("NOT")] + NOT = 12, + + [Lexeme("(<=?)|(==)|(!=)|(>=?)")] + COMPARISON = 13, + + [Lexeme("\\(")] + LPAREN = 30, + + [Lexeme("\\)")] + RPAREN = 31, + } +} \ No newline at end of file diff --git a/Issue259/CslyNullIssue/ParserSubclass.cs b/Issue259/CslyNullIssue/ParserSubclass.cs new file mode 100644 index 00000000..2ad6fec7 --- /dev/null +++ b/Issue259/CslyNullIssue/ParserSubclass.cs @@ -0,0 +1,35 @@ +using sly.lexer; +using sly.parser.generator; + +namespace CslyNullIssue +{ + public class ParserSubclass : ExpressionParser + { + + [Operand] + [Production("numeric_literal: LVAR")] + public string Lvar(Token token) + { + return token.Value; + } + + [Operand] + [Production("numeric_literal: SIMVAR")] + public string SimVarExpression(Token simvarToken) + { + var text = simvarToken.Value[2..]; + var bits = text.Split(","); + var varName = bits[0]; + var type = bits[1].Trim(); + return $"A:{varName}, {type}"; + } + + [Operand] + [Production("group : LPAREN ParserSubclass_expressions RPAREN")] + public string Group(Token _1, string child, Token _2) + { + return child; + } + + } +} diff --git a/Issue259/Tests/Issue259Tests.cs b/Issue259/Tests/Issue259Tests.cs new file mode 100644 index 00000000..6e1ed066 --- /dev/null +++ b/Issue259/Tests/Issue259Tests.cs @@ -0,0 +1,56 @@ +using CslyNullIssue; +using System; +using Xunit; + +namespace Tests +{ + public class Issue259Tests + { + [Theory] + [InlineData("ON", "ON")] + [InlineData("ON AND OFF", "(ON AND OFF)")] + [InlineData("ON OR OFF", "(ON OR OFF)")] + [InlineData("OFF", "OFF")] + [InlineData("A:FOO, bool < 42", "(A:FOO, bool < 42)")] + [InlineData("L:BAR == 1", "(L:BAR == 1)")] + [InlineData("3 < 4", "(3 < 4)")] + [InlineData("(1 + 3) < (3 + 4)", "((1 + 3) < (3 + 4))")] + [InlineData("3 < 4 OR 4 < 5", "((3 < 4) OR (4 < 5))")] + [InlineData("(2 + 3) < (A:FOO,bool + 12)", "((2 + 3) < (A:FOO, bool + 12))")] + [InlineData("(2 + 3) <= (A:FOO,bool + 12)", "((2 + 3) <= (A:FOO, bool + 12))")] + [InlineData("(2 + 3) >= (A:FOO,bool + 12)", "((2 + 3) >= (A:FOO, bool + 12))")] + [InlineData("(2 + 3) > (A:FOO,bool + 12)", "((2 + 3) > (A:FOO, bool + 12))")] + [InlineData("3 == 4", "(3 == 4)")] + [InlineData("3 != 4", "(3 != 4)")] + [InlineData("1 + 2 < 3 + 4", "((1 + 2) < (3 + 4))")] + [InlineData("1 + 2 * 3 < 3 * 4 - 5", "((1 + (2 * 3)) < ((3 * 4) - 5))")] + [InlineData("1<2 AND 2>3", "((1 < 2) AND (2 > 3))")] + + // Comparison operators + [InlineData("1 < 2 && 1 <= 2 && 1 == 2 && 1 >= 2 && 1 > 2 && 1 != 2 && 1 <> 2", "(((((((1 < 2) AND (1 <= 2)) AND (1 == 2)) AND (1 >= 2)) AND (1 > 2)) AND (1 != 2)) AND (1 != 2))")] + + // AND/OR precedence. (AND binds tighter) + [InlineData("ON AND OFF OR ON AND OFF", "((ON AND OFF) OR (ON AND OFF))")] + [InlineData("ON AND (OFF OR ON) AND OFF", "((ON AND (OFF OR ON)) AND OFF)")] + [InlineData("1==1 || 2==2 && 3==3 || 4==4", "(((1 == 1) OR ((2 == 2) AND (3 == 3))) OR (4 == 4))")] + + // Alternate AND/OR + [InlineData("1==1 AND 2==2 && 3==3 OR 4==4 || 5==5", "(((((1 == 1) AND (2 == 2)) AND (3 == 3)) OR (4 == 4)) OR (5 == 5))")] + + // NOT checks. (NOT binds tightly to logicals and loosely to numerics + [InlineData("NOT ON", "(NOT ON)")] + [InlineData("NOT ON AND OFF", "((NOT ON) AND OFF)")] + [InlineData("NOT ON OR OFF", "((NOT ON) OR OFF)")] + [InlineData("NOT 1 == 2", "(NOT (1 == 2))")] + [InlineData("NOT ON AND NOT OFF OR NOT ON OR NOT OFF", "((((NOT ON) AND (NOT OFF)) OR (NOT ON)) OR (NOT OFF))")] + + // Unary minus checks + [InlineData("- A:FOO, bool > 3", "(-A:FOO, bool > 3)")] + [InlineData("-(A:FOO, bool) > 3", "(-A:FOO, bool > 3)")] + public void BooleanParser(string expression, string expected) + { + var result = ExpressionParser.Parse(expression); + Assert.Equal(expected, result); + } + } +} diff --git a/Issue259/Tests/Tests.csproj b/Issue259/Tests/Tests.csproj new file mode 100644 index 00000000..0172afda --- /dev/null +++ b/Issue259/Tests/Tests.csproj @@ -0,0 +1,28 @@ + + + + net5.0 + + false + + Issue259Tests + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/sly.sln b/sly.sln index 7755d7b6..eefdb0b1 100644 --- a/sly.sln +++ b/sly.sln @@ -33,6 +33,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "indented", "samples\indente EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "indentedWhile", "samples\IndentedWhile\indentedWhile.csproj", "{B1FAADF0-208D-4A0A-A24D-D7BF4E33DCB4}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Issue259", "Issue259", "{134B62C4-0138-4B17-A1C1-FC74D6DBB604}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CslyNullIssue", "Issue259\CslyNullIssue\CslyNullIssue.csproj", "{FFE5ABA4-9356-4909-BCB3-C8657FAC04EE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Issue259\Tests\Tests.csproj", "{9818D545-02DE-4157-BCEA-A904E3B3BBCD}" +EndProject Global GlobalSection(Performance) = preSolution HasPerformanceSessions = true @@ -102,6 +108,14 @@ Global {B1FAADF0-208D-4A0A-A24D-D7BF4E33DCB4}.Debug|Any CPU.Build.0 = Debug|Any CPU {B1FAADF0-208D-4A0A-A24D-D7BF4E33DCB4}.Release|Any CPU.ActiveCfg = Release|Any CPU {B1FAADF0-208D-4A0A-A24D-D7BF4E33DCB4}.Release|Any CPU.Build.0 = Release|Any CPU + {FFE5ABA4-9356-4909-BCB3-C8657FAC04EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FFE5ABA4-9356-4909-BCB3-C8657FAC04EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FFE5ABA4-9356-4909-BCB3-C8657FAC04EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FFE5ABA4-9356-4909-BCB3-C8657FAC04EE}.Release|Any CPU.Build.0 = Release|Any CPU + {9818D545-02DE-4157-BCEA-A904E3B3BBCD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9818D545-02DE-4157-BCEA-A904E3B3BBCD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9818D545-02DE-4157-BCEA-A904E3B3BBCD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9818D545-02DE-4157-BCEA-A904E3B3BBCD}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -115,6 +129,8 @@ Global {FFCA5A16-0388-4A8E-8B40-0D1FDA5D4B7B} = {36ED7F1A-2E81-4A71-81FE-A357E5840A33} {7C6C4A13-2AE5-4070-A2E1-05B6A83DC97C} = {36ED7F1A-2E81-4A71-81FE-A357E5840A33} {B1FAADF0-208D-4A0A-A24D-D7BF4E33DCB4} = {36ED7F1A-2E81-4A71-81FE-A357E5840A33} + {FFE5ABA4-9356-4909-BCB3-C8657FAC04EE} = {134B62C4-0138-4B17-A1C1-FC74D6DBB604} + {9818D545-02DE-4157-BCEA-A904E3B3BBCD} = {134B62C4-0138-4B17-A1C1-FC74D6DBB604} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {43254130-CF3E-480E-952F-E50CA5D2E417} diff --git a/sly/parser/parser/Parser.cs b/sly/parser/parser/Parser.cs index 5e6b5fa3..380774e3 100644 --- a/sly/parser/parser/Parser.cs +++ b/sly/parser/parser/Parser.cs @@ -119,9 +119,14 @@ public ParseResult ParseWithContext(IList> tokens, object par var errors = new List(); foreach (var expecting in byEnding) { - var expectingTokens = expecting.SelectMany(x => x.ExpectedTokens).Distinct(); - var expected = new UnexpectedTokenSyntaxError(expecting.First().UnexpectedToken, I18n,expectingTokens.ToArray()); - errors.Add(expected); + var expectingTokens = expecting.SelectMany(x => (x.ExpectedTokens ?? new List())).Distinct(); + var expectedTokens = (expectingTokens != null && expectingTokens.Any()) ? expectingTokens?.ToArray() : null; + if (expectedTokens != null) + { + var expected = new UnexpectedTokenSyntaxError(expecting.First().UnexpectedToken, I18n, + expectedTokens); + errors.Add(expected); + } } result.Errors.AddRange(errors); diff --git a/sly/parser/parser/SyntaxParseResult.cs b/sly/parser/parser/SyntaxParseResult.cs index 81bab0ff..64a6238e 100644 --- a/sly/parser/parser/SyntaxParseResult.cs +++ b/sly/parser/parser/SyntaxParseResult.cs @@ -30,6 +30,10 @@ public void AddExpecting(IN expected) public void AddExpectings(IEnumerable expected) { + if (expected == null) + { + return; + } if (Expecting == null) { Expecting = new List(); From df154a30e5ef612bca3521897ff199ca67fa8b6f Mon Sep 17 00:00:00 2001 From: b3b00 Date: Mon, 29 Nov 2021 16:36:01 +0100 Subject: [PATCH 2/6] bugfix #259 --- Issue259/CslyNullIssue/CslyNullIssue.csproj | 13 ----- Issue259/Tests/Issue259Tests.cs | 56 ------------------- Issue259/Tests/Tests.csproj | 28 ---------- .../Issue259/Issue259ExpressionParser.cs | 50 +++++++++-------- .../Issue259/Issue259ExpressionToken.cs | 2 +- .../Issue259/Issue259ParserSubclass.cs | 10 ++-- ParserTests/Issue259/Issue259Tests.cs | 16 ++++++ sly/parser/generator/NonTerminal.cs | 3 +- .../llparser/RecursiveDescentSyntaxParser.cs | 1 + 9 files changed, 52 insertions(+), 127 deletions(-) delete mode 100644 Issue259/CslyNullIssue/CslyNullIssue.csproj delete mode 100644 Issue259/Tests/Issue259Tests.cs delete mode 100644 Issue259/Tests/Tests.csproj rename Issue259/CslyNullIssue/ExpressionParser.cs => ParserTests/Issue259/Issue259ExpressionParser.cs (56%) rename Issue259/CslyNullIssue/ExpressionToken.cs => ParserTests/Issue259/Issue259ExpressionToken.cs (96%) rename Issue259/CslyNullIssue/ParserSubclass.cs => ParserTests/Issue259/Issue259ParserSubclass.cs (57%) create mode 100644 ParserTests/Issue259/Issue259Tests.cs diff --git a/Issue259/CslyNullIssue/CslyNullIssue.csproj b/Issue259/CslyNullIssue/CslyNullIssue.csproj deleted file mode 100644 index 9667f59a..00000000 --- a/Issue259/CslyNullIssue/CslyNullIssue.csproj +++ /dev/null @@ -1,13 +0,0 @@ - - - - Library - net5.0 - CslyNull259Issue - - - - - - - diff --git a/Issue259/Tests/Issue259Tests.cs b/Issue259/Tests/Issue259Tests.cs deleted file mode 100644 index 6e1ed066..00000000 --- a/Issue259/Tests/Issue259Tests.cs +++ /dev/null @@ -1,56 +0,0 @@ -using CslyNullIssue; -using System; -using Xunit; - -namespace Tests -{ - public class Issue259Tests - { - [Theory] - [InlineData("ON", "ON")] - [InlineData("ON AND OFF", "(ON AND OFF)")] - [InlineData("ON OR OFF", "(ON OR OFF)")] - [InlineData("OFF", "OFF")] - [InlineData("A:FOO, bool < 42", "(A:FOO, bool < 42)")] - [InlineData("L:BAR == 1", "(L:BAR == 1)")] - [InlineData("3 < 4", "(3 < 4)")] - [InlineData("(1 + 3) < (3 + 4)", "((1 + 3) < (3 + 4))")] - [InlineData("3 < 4 OR 4 < 5", "((3 < 4) OR (4 < 5))")] - [InlineData("(2 + 3) < (A:FOO,bool + 12)", "((2 + 3) < (A:FOO, bool + 12))")] - [InlineData("(2 + 3) <= (A:FOO,bool + 12)", "((2 + 3) <= (A:FOO, bool + 12))")] - [InlineData("(2 + 3) >= (A:FOO,bool + 12)", "((2 + 3) >= (A:FOO, bool + 12))")] - [InlineData("(2 + 3) > (A:FOO,bool + 12)", "((2 + 3) > (A:FOO, bool + 12))")] - [InlineData("3 == 4", "(3 == 4)")] - [InlineData("3 != 4", "(3 != 4)")] - [InlineData("1 + 2 < 3 + 4", "((1 + 2) < (3 + 4))")] - [InlineData("1 + 2 * 3 < 3 * 4 - 5", "((1 + (2 * 3)) < ((3 * 4) - 5))")] - [InlineData("1<2 AND 2>3", "((1 < 2) AND (2 > 3))")] - - // Comparison operators - [InlineData("1 < 2 && 1 <= 2 && 1 == 2 && 1 >= 2 && 1 > 2 && 1 != 2 && 1 <> 2", "(((((((1 < 2) AND (1 <= 2)) AND (1 == 2)) AND (1 >= 2)) AND (1 > 2)) AND (1 != 2)) AND (1 != 2))")] - - // AND/OR precedence. (AND binds tighter) - [InlineData("ON AND OFF OR ON AND OFF", "((ON AND OFF) OR (ON AND OFF))")] - [InlineData("ON AND (OFF OR ON) AND OFF", "((ON AND (OFF OR ON)) AND OFF)")] - [InlineData("1==1 || 2==2 && 3==3 || 4==4", "(((1 == 1) OR ((2 == 2) AND (3 == 3))) OR (4 == 4))")] - - // Alternate AND/OR - [InlineData("1==1 AND 2==2 && 3==3 OR 4==4 || 5==5", "(((((1 == 1) AND (2 == 2)) AND (3 == 3)) OR (4 == 4)) OR (5 == 5))")] - - // NOT checks. (NOT binds tightly to logicals and loosely to numerics - [InlineData("NOT ON", "(NOT ON)")] - [InlineData("NOT ON AND OFF", "((NOT ON) AND OFF)")] - [InlineData("NOT ON OR OFF", "((NOT ON) OR OFF)")] - [InlineData("NOT 1 == 2", "(NOT (1 == 2))")] - [InlineData("NOT ON AND NOT OFF OR NOT ON OR NOT OFF", "((((NOT ON) AND (NOT OFF)) OR (NOT ON)) OR (NOT OFF))")] - - // Unary minus checks - [InlineData("- A:FOO, bool > 3", "(-A:FOO, bool > 3)")] - [InlineData("-(A:FOO, bool) > 3", "(-A:FOO, bool > 3)")] - public void BooleanParser(string expression, string expected) - { - var result = ExpressionParser.Parse(expression); - Assert.Equal(expected, result); - } - } -} diff --git a/Issue259/Tests/Tests.csproj b/Issue259/Tests/Tests.csproj deleted file mode 100644 index 0172afda..00000000 --- a/Issue259/Tests/Tests.csproj +++ /dev/null @@ -1,28 +0,0 @@ - - - - net5.0 - - false - - Issue259Tests - - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - - - - diff --git a/Issue259/CslyNullIssue/ExpressionParser.cs b/ParserTests/Issue259/Issue259ExpressionParser.cs similarity index 56% rename from Issue259/CslyNullIssue/ExpressionParser.cs rename to ParserTests/Issue259/Issue259ExpressionParser.cs index bc5e8482..2f174af6 100644 --- a/Issue259/CslyNullIssue/ExpressionParser.cs +++ b/ParserTests/Issue259/Issue259ExpressionParser.cs @@ -7,83 +7,87 @@ namespace CslyNullIssue { - public abstract class ExpressionParser + public abstract class Issue259ExpressionParser { [Operand] [Production("logical_literal: OFF")] [Production("logical_literal: ON")] - public string LiteralBool(Token token) + public string LiteralBool(Token token) { return token.Value; } [Operand] [Production("primary: HEX_NUMBER")] - public string NumericExpressionFromLiteralNumber(Token offsetToken) + public string NumericExpressionFromLiteralNumber(Token offsetToken) { return offsetToken.Value; } [Operand] [Production("primary: DECIMAL_NUMBER")] - public string NumericExpressionFromDecimalNumber(Token offsetToken) + public string NumericExpressionFromDecimalNumber(Token offsetToken) { var text = offsetToken.Value; var value = double.Parse(text, CultureInfo.InvariantCulture); return value.ToString(CultureInfo.InvariantCulture); } - [Infix((int)ExpressionToken.PLUS, Associativity.Left, 14)] - [Infix((int)ExpressionToken.MINUS, Associativity.Left, 14)] - [Infix((int)ExpressionToken.TIMES, Associativity.Left, 15)] - [Infix((int)ExpressionToken.DIVIDE, Associativity.Left, 15)] - [Infix((int)ExpressionToken.BITWISE_AND, Associativity.Left, 10)] - [Infix((int)ExpressionToken.BITWISE_OR, Associativity.Left, 8)] - public string NumberExpression(string lhs, Token token, string rhs) + [Infix((int)Issue259ExpressionToken.PLUS, Associativity.Left, 14)] + [Infix((int)Issue259ExpressionToken.MINUS, Associativity.Left, 14)] + [Infix((int)Issue259ExpressionToken.TIMES, Associativity.Left, 15)] + [Infix((int)Issue259ExpressionToken.DIVIDE, Associativity.Left, 15)] + [Infix((int)Issue259ExpressionToken.BITWISE_AND, Associativity.Left, 10)] + [Infix((int)Issue259ExpressionToken.BITWISE_OR, Associativity.Left, 8)] + public string NumberExpression(string lhs, Token token, string rhs) { return $"({lhs} {token.Value} {rhs})"; } - [Infix((int)ExpressionToken.LOGICAL_AND, Associativity.Left, 7)] - [Infix((int)ExpressionToken.LOGICAL_OR, Associativity.Left, 6)] - public string LogicalExpression(string lhs, Token token, string rhs) + [Infix((int)Issue259ExpressionToken.LOGICAL_AND, Associativity.Left, 7)] + [Infix((int)Issue259ExpressionToken.LOGICAL_OR, Associativity.Left, 6)] + public string LogicalExpression(string lhs, Token token, string rhs) { return $"({lhs} {token.Value} {rhs})"; } - [Prefix((int)ExpressionToken.MINUS, Associativity.Right, 17)] - public string NumericExpression(Token _, string child) + [Prefix((int)Issue259ExpressionToken.MINUS, Associativity.Right, 17)] + public string NumericExpression(Token _, string child) { return $"-{child}"; } // We want NOT to to bind tighter than AND/OR but looser than numeric comparison operations - [Prefix((int)ExpressionToken.NOT, Associativity.Right, 11)] - public string LogicalExpression(Token _, string child) + [Prefix((int)Issue259ExpressionToken.NOT, Associativity.Right, 11)] + public string LogicalExpression(Token _, string child) { return $"(NOT {child})"; } - [Infix((int)ExpressionToken.COMPARISON, Associativity.Left, 12)] - public string Comparison(string lhs, Token token, string rhs) + [Infix((int)Issue259ExpressionToken.COMPARISON, Associativity.Left, 12)] + public string Comparison(string lhs, Token token, string rhs) { return $"({lhs} {token.Value} {rhs})"; } - private static Parser cachedParser; + private static Parser cachedParser; - public static string Parse(string expression) where T : ExpressionParser, new() + public static string Parse(string expression) where T : Issue259ExpressionParser, new() { if (cachedParser == null) { var startingRule = $"{typeof(T).Name}_expressions"; var parserInstance = new T(); - var builder = new ParserBuilder(); + var builder = new ParserBuilder(); var parser = builder.BuildParser(parserInstance, ParserType.EBNF_LL_RECURSIVE_DESCENT, startingRule); if (parser.IsError) { throw new Exception($"Could not create parser. BNF is not valid. {parser.Errors[0]}"); } + + var dump = parser.Result.Configuration.Dump(); + Console.WriteLine(dump); + cachedParser = parser.Result; } diff --git a/Issue259/CslyNullIssue/ExpressionToken.cs b/ParserTests/Issue259/Issue259ExpressionToken.cs similarity index 96% rename from Issue259/CslyNullIssue/ExpressionToken.cs rename to ParserTests/Issue259/Issue259ExpressionToken.cs index 8b3cff53..44da3030 100644 --- a/Issue259/CslyNullIssue/ExpressionToken.cs +++ b/ParserTests/Issue259/Issue259ExpressionToken.cs @@ -2,7 +2,7 @@ namespace CslyNullIssue { - public enum ExpressionToken + public enum Issue259ExpressionToken { [Lexeme("L:[A-Za-z0-9_]+")] LVAR = 50, diff --git a/Issue259/CslyNullIssue/ParserSubclass.cs b/ParserTests/Issue259/Issue259ParserSubclass.cs similarity index 57% rename from Issue259/CslyNullIssue/ParserSubclass.cs rename to ParserTests/Issue259/Issue259ParserSubclass.cs index 2ad6fec7..72456c17 100644 --- a/Issue259/CslyNullIssue/ParserSubclass.cs +++ b/ParserTests/Issue259/Issue259ParserSubclass.cs @@ -3,19 +3,19 @@ namespace CslyNullIssue { - public class ParserSubclass : ExpressionParser + public class Issue259ParserSubclass : Issue259ExpressionParser { [Operand] [Production("numeric_literal: LVAR")] - public string Lvar(Token token) + public string Lvar(Token token) { return token.Value; } [Operand] [Production("numeric_literal: SIMVAR")] - public string SimVarExpression(Token simvarToken) + public string SimVarExpression(Token simvarToken) { var text = simvarToken.Value[2..]; var bits = text.Split(","); @@ -25,8 +25,8 @@ public string SimVarExpression(Token simvarToken) } [Operand] - [Production("group : LPAREN ParserSubclass_expressions RPAREN")] - public string Group(Token _1, string child, Token _2) + [Production("group : LPAREN Issue259ParserSubclass_expressions RPAREN")] + public string Group(Token _1, string child, Token _2) { return child; } diff --git a/ParserTests/Issue259/Issue259Tests.cs b/ParserTests/Issue259/Issue259Tests.cs new file mode 100644 index 00000000..23a5f07e --- /dev/null +++ b/ParserTests/Issue259/Issue259Tests.cs @@ -0,0 +1,16 @@ +using System; +using Xunit; + +namespace CslyNullIssue +{ + public class Issue259Tests + { + [Fact] + public static void Issue259Test() + { + var expression = "1 < 2 && 1 <= 2 && 1 == 2 && 1 >= 2 && 1 > 2 && 1 != 2 && 1 <> 2"; + var exception = Assert.Throws(() => Issue259ExpressionParser.Parse(expression)); + Assert.True(exception.Message.Contains(@"COMPARISON [<] @line 1, column 79")); + } + } +} \ No newline at end of file diff --git a/sly/parser/generator/NonTerminal.cs b/sly/parser/generator/NonTerminal.cs index 119aa51c..614c6070 100644 --- a/sly/parser/generator/NonTerminal.cs +++ b/sly/parser/generator/NonTerminal.cs @@ -33,7 +33,8 @@ public string Dump() foreach (var rule in Rules) { - dump.Append(Name).Append(" : "); + + dump.Append(Name).Append(rule.IsInfixExpressionRule ? " (*) ":"").Append(" : "); foreach (IClause clause in rule.Clauses) { dump.Append(clause.Dump()).Append(" "); diff --git a/sly/parser/parser/llparser/RecursiveDescentSyntaxParser.cs b/sly/parser/parser/llparser/RecursiveDescentSyntaxParser.cs index d0a5b03f..1d79382a 100644 --- a/sly/parser/parser/llparser/RecursiveDescentSyntaxParser.cs +++ b/sly/parser/parser/llparser/RecursiveDescentSyntaxParser.cs @@ -239,6 +239,7 @@ public SyntaxParseResult ParseTerminal(IList> tokens, TerminalClau token.Discarded = terminal.Discarded; result.Root = new SyntaxLeaf(token, terminal.Discarded); result.HasByPassNodes = false; + result.Errors.Add(new UnexpectedTokenSyntaxError(token,I18n,terminal.ExpectedToken)); result.AddExpecting(terminal.ExpectedToken); return result; } From 76341edcf98fb7b1b8598b3d17835aba34312f9c Mon Sep 17 00:00:00 2001 From: b3b00 Date: Mon, 29 Nov 2021 16:41:38 +0100 Subject: [PATCH 3/6] bugfix #259 --- sly.sln | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/sly.sln b/sly.sln index eefdb0b1..7755d7b6 100644 --- a/sly.sln +++ b/sly.sln @@ -33,12 +33,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "indented", "samples\indente EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "indentedWhile", "samples\IndentedWhile\indentedWhile.csproj", "{B1FAADF0-208D-4A0A-A24D-D7BF4E33DCB4}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Issue259", "Issue259", "{134B62C4-0138-4B17-A1C1-FC74D6DBB604}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CslyNullIssue", "Issue259\CslyNullIssue\CslyNullIssue.csproj", "{FFE5ABA4-9356-4909-BCB3-C8657FAC04EE}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Issue259\Tests\Tests.csproj", "{9818D545-02DE-4157-BCEA-A904E3B3BBCD}" -EndProject Global GlobalSection(Performance) = preSolution HasPerformanceSessions = true @@ -108,14 +102,6 @@ Global {B1FAADF0-208D-4A0A-A24D-D7BF4E33DCB4}.Debug|Any CPU.Build.0 = Debug|Any CPU {B1FAADF0-208D-4A0A-A24D-D7BF4E33DCB4}.Release|Any CPU.ActiveCfg = Release|Any CPU {B1FAADF0-208D-4A0A-A24D-D7BF4E33DCB4}.Release|Any CPU.Build.0 = Release|Any CPU - {FFE5ABA4-9356-4909-BCB3-C8657FAC04EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FFE5ABA4-9356-4909-BCB3-C8657FAC04EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FFE5ABA4-9356-4909-BCB3-C8657FAC04EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FFE5ABA4-9356-4909-BCB3-C8657FAC04EE}.Release|Any CPU.Build.0 = Release|Any CPU - {9818D545-02DE-4157-BCEA-A904E3B3BBCD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9818D545-02DE-4157-BCEA-A904E3B3BBCD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9818D545-02DE-4157-BCEA-A904E3B3BBCD}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9818D545-02DE-4157-BCEA-A904E3B3BBCD}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -129,8 +115,6 @@ Global {FFCA5A16-0388-4A8E-8B40-0D1FDA5D4B7B} = {36ED7F1A-2E81-4A71-81FE-A357E5840A33} {7C6C4A13-2AE5-4070-A2E1-05B6A83DC97C} = {36ED7F1A-2E81-4A71-81FE-A357E5840A33} {B1FAADF0-208D-4A0A-A24D-D7BF4E33DCB4} = {36ED7F1A-2E81-4A71-81FE-A357E5840A33} - {FFE5ABA4-9356-4909-BCB3-C8657FAC04EE} = {134B62C4-0138-4B17-A1C1-FC74D6DBB604} - {9818D545-02DE-4157-BCEA-A904E3B3BBCD} = {134B62C4-0138-4B17-A1C1-FC74D6DBB604} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {43254130-CF3E-480E-952F-E50CA5D2E417} From a1f11c8b070a8f84aa2035afce18fd54497fcfc0 Mon Sep 17 00:00:00 2001 From: b3b00 Date: Mon, 29 Nov 2021 16:43:57 +0100 Subject: [PATCH 4/6] cleaning --- ParserTests/Issue259/Issue259ExpressionParser.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ParserTests/Issue259/Issue259ExpressionParser.cs b/ParserTests/Issue259/Issue259ExpressionParser.cs index 2f174af6..017ab4a7 100644 --- a/ParserTests/Issue259/Issue259ExpressionParser.cs +++ b/ParserTests/Issue259/Issue259ExpressionParser.cs @@ -84,10 +84,6 @@ public string Comparison(string lhs, Token token, strin { throw new Exception($"Could not create parser. BNF is not valid. {parser.Errors[0]}"); } - - var dump = parser.Result.Configuration.Dump(); - Console.WriteLine(dump); - cachedParser = parser.Result; } From 4a6ddfb12a32756d270b5cbab6bc7050d8a1948a Mon Sep 17 00:00:00 2001 From: b3b00 Date: Tue, 30 Nov 2021 17:40:15 +0100 Subject: [PATCH 5/6] bugfix #259 --- ...9ExpressionParser.cs => Issue259Parser.cs} | 66 ++++++++----------- .../Issue259/Issue259ParserSubclass.cs | 35 ---------- ParserTests/Issue259/Issue259Tests.cs | 37 ++++++++++- .../generator/ExpressionRulesGenerator.cs | 3 +- .../EBNFRecursiveDescentSyntaxParser.cs | 13 +--- .../llparser/RecursiveDescentSyntaxParser.cs | 1 + 6 files changed, 64 insertions(+), 91 deletions(-) rename ParserTests/Issue259/{Issue259ExpressionParser.cs => Issue259Parser.cs} (61%) delete mode 100644 ParserTests/Issue259/Issue259ParserSubclass.cs diff --git a/ParserTests/Issue259/Issue259ExpressionParser.cs b/ParserTests/Issue259/Issue259Parser.cs similarity index 61% rename from ParserTests/Issue259/Issue259ExpressionParser.cs rename to ParserTests/Issue259/Issue259Parser.cs index 017ab4a7..ee94909f 100644 --- a/ParserTests/Issue259/Issue259ExpressionParser.cs +++ b/ParserTests/Issue259/Issue259Parser.cs @@ -1,13 +1,10 @@ -using sly.lexer; -using sly.parser; +using System.Globalization; +using sly.lexer; using sly.parser.generator; -using System; -using System.Globalization; -using System.Linq; namespace CslyNullIssue { - public abstract class Issue259ExpressionParser + public class Issue259Parser { [Operand] [Production("logical_literal: OFF")] @@ -69,43 +66,32 @@ public string Comparison(string lhs, Token token, strin { return $"({lhs} {token.Value} {rhs})"; } + - private static Parser cachedParser; - - public static string Parse(string expression) where T : Issue259ExpressionParser, new() + [Operand] + [Production("numeric_literal: LVAR")] + public string Lvar(Token token) { - if (cachedParser == null) - { - var startingRule = $"{typeof(T).Name}_expressions"; - var parserInstance = new T(); - var builder = new ParserBuilder(); - var parser = builder.BuildParser(parserInstance, ParserType.EBNF_LL_RECURSIVE_DESCENT, startingRule); - if (parser.IsError) - { - throw new Exception($"Could not create parser. BNF is not valid. {parser.Errors[0]}"); - } - cachedParser = parser.Result; - } - - // To simplify an ambiguous lexer which would result from having both && and & as well as || and |, we'll - // simplify the incoming expression by turning && into AND and || into OR: - expression = expression.Replace("&&", " AND "); - expression = expression.Replace("||", " OR "); + return token.Value; + } - var parseResult = cachedParser.Parse(expression); - if (parseResult.IsError) - { - if (parseResult.Errors.Any()) - { - throw new Exception(parseResult.Errors[0].ErrorMessage); - } - else - { - throw new Exception("unknwon error "); - } - } + [Operand] + [Production("numeric_literal: SIMVAR")] + public string SimVarExpression(Token simvarToken) + { + var text = simvarToken.Value[2..]; + var bits = text.Split(","); + var varName = bits[0]; + var type = bits[1].Trim(); + return $"A:{varName}, {type}"; + } - return parseResult.Result; + [Operand] + [Production("group : LPAREN Issue259Parser_expressions RPAREN")] + public string Group(Token _1, string child, Token _2) + { + return child; } + } -} \ No newline at end of file +} diff --git a/ParserTests/Issue259/Issue259ParserSubclass.cs b/ParserTests/Issue259/Issue259ParserSubclass.cs deleted file mode 100644 index 72456c17..00000000 --- a/ParserTests/Issue259/Issue259ParserSubclass.cs +++ /dev/null @@ -1,35 +0,0 @@ -using sly.lexer; -using sly.parser.generator; - -namespace CslyNullIssue -{ - public class Issue259ParserSubclass : Issue259ExpressionParser - { - - [Operand] - [Production("numeric_literal: LVAR")] - public string Lvar(Token token) - { - return token.Value; - } - - [Operand] - [Production("numeric_literal: SIMVAR")] - public string SimVarExpression(Token simvarToken) - { - var text = simvarToken.Value[2..]; - var bits = text.Split(","); - var varName = bits[0]; - var type = bits[1].Trim(); - return $"A:{varName}, {type}"; - } - - [Operand] - [Production("group : LPAREN Issue259ParserSubclass_expressions RPAREN")] - public string Group(Token _1, string child, Token _2) - { - return child; - } - - } -} diff --git a/ParserTests/Issue259/Issue259Tests.cs b/ParserTests/Issue259/Issue259Tests.cs index 23a5f07e..780bccb7 100644 --- a/ParserTests/Issue259/Issue259Tests.cs +++ b/ParserTests/Issue259/Issue259Tests.cs @@ -1,4 +1,6 @@ using System; +using sly.parser; +using sly.parser.generator; using Xunit; namespace CslyNullIssue @@ -8,9 +10,38 @@ public class Issue259Tests [Fact] public static void Issue259Test() { - var expression = "1 < 2 && 1 <= 2 && 1 == 2 && 1 >= 2 && 1 > 2 && 1 != 2 && 1 <> 2"; - var exception = Assert.Throws(() => Issue259ExpressionParser.Parse(expression)); - Assert.True(exception.Message.Contains(@"COMPARISON [<] @line 1, column 79")); + var expression = "1 < 2 AND 3 <= 4 AND 5 == 6 AND 7 >= 8 AND 9 > 10 AND 11 != 12 AND 13 <> 14"; + + + var startingRule = $"{typeof(Issue259Parser).Name}_expressions"; + var parserInstance = new Issue259Parser(); + var builder = new ParserBuilder("en"); + var parser = builder.BuildParser(parserInstance, ParserType.EBNF_LL_RECURSIVE_DESCENT, startingRule); + Assert.True(parser.IsOk); + Assert.NotNull(parser.Result); + + Console.WriteLine(parser.Result.Configuration.Dump()); + + var parseResult = parser.Result.Parse(expression); + + Assert.True(parseResult.IsError); + Assert.Single(parseResult.Errors); + + var error = parseResult.Errors[0]; + + var unexpectedTokenError = error as UnexpectedTokenSyntaxError; + Assert.NotNull(unexpectedTokenError); + Assert.Equal(Issue259ExpressionToken.COMPARISON, unexpectedTokenError.UnexpectedToken.TokenID); + Assert.Equal(">",unexpectedTokenError.UnexpectedToken.Value); + Assert.Contains(unexpectedTokenError.ExpectedTokens, x => x == Issue259ExpressionToken.ON); + Assert.Contains(unexpectedTokenError.ExpectedTokens, x => x == Issue259ExpressionToken.OFF); + Assert.Contains(unexpectedTokenError.ExpectedTokens, x => x == Issue259ExpressionToken.MINUS); + Assert.Contains(unexpectedTokenError.ExpectedTokens, x => x == Issue259ExpressionToken.HEX_NUMBER); + Assert.Contains(unexpectedTokenError.ExpectedTokens, x => x == Issue259ExpressionToken.DECIMAL_NUMBER); + Assert.Contains(unexpectedTokenError.ExpectedTokens, x => x == Issue259ExpressionToken.LVAR); + Assert.Contains(unexpectedTokenError.ExpectedTokens, x => x == Issue259ExpressionToken.SIMVAR); + Assert.Contains(unexpectedTokenError.ExpectedTokens, x => x == Issue259ExpressionToken.LPAREN); + } } } \ No newline at end of file diff --git a/sly/parser/generator/ExpressionRulesGenerator.cs b/sly/parser/generator/ExpressionRulesGenerator.cs index 349bf28b..35c0e928 100644 --- a/sly/parser/generator/ExpressionRulesGenerator.cs +++ b/sly/parser/generator/ExpressionRulesGenerator.cs @@ -223,7 +223,8 @@ private NonTerminal BuildPrecedenceNonTerminal(string name, string nextName, var rule = new Rule() { ExpressionAffix = Affix.InFix, - IsExpressionRule = true + IsExpressionRule = true, + NonTerminalName = name }; rule.Clauses.Add(new NonTerminalClause(nextName)); diff --git a/sly/parser/parser/llparser/EBNFRecursiveDescentSyntaxParser.cs b/sly/parser/parser/llparser/EBNFRecursiveDescentSyntaxParser.cs index 0c741e7e..b3667559 100644 --- a/sly/parser/parser/llparser/EBNFRecursiveDescentSyntaxParser.cs +++ b/sly/parser/parser/llparser/EBNFRecursiveDescentSyntaxParser.cs @@ -184,12 +184,6 @@ public virtual SyntaxParseResult ParseInfixExpressionRule(IList> t { if (firstResult.Root is SyntaxNode node) { - if (node.Name == "expr_17_MINUS") - { - ; - } - // node.IsByPassNode = true; - // node.HasByPassNodes = true; firstResult.Errors.AddRange(secondResult.Errors); firstResult.AddExpectings(secondResult.Expecting); return firstResult; @@ -225,14 +219,9 @@ public virtual SyntaxParseResult ParseInfixExpressionRule(IList> t if (third is NonTerminalClause thirdNonTerminal) { thirdResult = ParseNonTerminal(tokens, thirdNonTerminal, currentPosition); - if (thirdResult.IsError) { - if (firstResult.Root is SyntaxNode node) - { - firstResult.AddExpectings(thirdResult.Expecting); - return firstResult; - } + return thirdResult; } else { diff --git a/sly/parser/parser/llparser/RecursiveDescentSyntaxParser.cs b/sly/parser/parser/llparser/RecursiveDescentSyntaxParser.cs index 1d79382a..23449439 100644 --- a/sly/parser/parser/llparser/RecursiveDescentSyntaxParser.cs +++ b/sly/parser/parser/llparser/RecursiveDescentSyntaxParser.cs @@ -397,6 +397,7 @@ private SyntaxParseResult NoMatchingRuleError(IList> tokens, int c error.IsEnded = false; error.Errors = noRuleErrors; error.EndingPosition = currentPosition; + error.Expecting = allAcceptableTokens; return error; } From a992f339f4ffc71c696daf18d850c1fcdc2f83e7 Mon Sep 17 00:00:00 2001 From: b3b00 Date: Wed, 1 Dec 2021 13:32:34 +0100 Subject: [PATCH 6/6] bump to 2.8.0.1 --- sly/sly.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sly/sly.csproj b/sly/sly.csproj index d02525d5..89ac9eb2 100644 --- a/sly/sly.csproj +++ b/sly/sly.csproj @@ -6,11 +6,11 @@ $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb #LY is a parser generator halfway between parser combinators and parser generator like ANTLR b3b00 - 2.8.0 + 2.8.0.1 https://github.com/b3b00/sly https://github.com/b3b00/sly https://github.com/b3b00/sly/blob/master/LICENSE - 2.8.0 + 2.8.0.1 Library