Skip to content

Commit

Permalink
unit test for issue #251
Browse files Browse the repository at this point in the history
  • Loading branch information
b3b00 committed Nov 3, 2021
1 parent 8b601e1 commit c7472b4
Show file tree
Hide file tree
Showing 2 changed files with 153 additions and 0 deletions.
140 changes: 140 additions & 0 deletions ParserTests/Issue251/LexerAndParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@

using System;
using System.Data;
using System.Text;
using sly.lexer;
using sly.parser.generator;
namespace ParserTests.Issue251
{

public class Issue251Parser
{
public enum Issue251Tokens
{
[Lexeme("[0-9][0-9]*")] DIGITS,

[Lexeme("\"[^\"\\\\]*(\\\\.[^\"\\\\])*\"")]
LITERAL_STRING, // "[^"\\]*(\\.[^"\\]*)*" , which allowing escaped quotes in string.
[Lexeme("[a-zA-Z_][0-9a-zA-Z_]*")] IDENTIFIER,
[Lexeme("\\+")] OP_PLUS,
[Lexeme("-")] OP_MINUS,
[Lexeme("\\*")] OP_MULTIPLY,
[Lexeme("/")] OP_DIVIDE,
[Lexeme("%")] OP_MODULE,
[Lexeme("(")] PARENTHESIS_L,
[Lexeme(")")] PARENTHESIS_R,

[Lexeme("[ \\t][ \\t]*", isSkippable: true)]
IGNORED, // simply discarded in lexer
}

public class ExprClosure
{
public enum Types
{
INT,
STRING
}

public Types ResultType;

// C# doesn't have std::any or std::variant. Fuck it.
public int valueInt;
public string valueString;
}

private void SyntaxCheck(bool what, string msg = "")
{
if (!what)
throw new SyntaxErrorException("Syntax error: " + msg);
}

/*
* RnLang is a script language, so the input expressions
* are directly evaluated in parser.
*
* int ::= DIGITS
* string ::= LITERAL_STRING
* expr ::= int | string
* expr ::= expr OP_PLUS expr
* expr ::= expr OP_MINUS expr
* expr ::= expr OP_MULTIPLY expr
* expr ::= expr OP_DIVIDE expr
* expr ::= expr OP_MODULE expr
* expr ::= IDENTIFIER PARENTHESIS_L expr PARENTHESIS_R
* expr ::= PARENTHESIS_L expr PARENTHESIS_R
*/
[Production("int: DIGITS")]
public int exprIntL(Token<Issue251Tokens> expr) => expr.IntValue;

[Production("string: LITERAL_STRING")]
public string exprStringL(Token<Issue251Tokens> expr) =>
expr.StringWithoutQuotes.Replace("\\\"", "\""); // default string delimiter works.

[Production("expr: int")]
public ExprClosure exprInt(int what) =>
new ExprClosure() {ResultType = ExprClosure.Types.INT, valueInt = what};

[Production("expr: string")]
public ExprClosure exprString(string what) =>
new ExprClosure() {ResultType = ExprClosure.Types.STRING, valueString = what};

[Production("expr: expr OP_PLUS expr")]
public ExprClosure exprAdd(ExprClosure l, Token<Issue251Tokens> _, ExprClosure r)
{
SyntaxCheck(l.ResultType == r.ResultType, "Type error: expected int + int or string + string");
switch (l.ResultType)
{
case ExprClosure.Types.INT:
l.valueInt += r.valueInt;
return l;
case ExprClosure.Types.STRING:
l.valueString += r.valueString;
return l;
}
throw new ArgumentException("Internal Error");
}

[Production("expr: expr OP_MINUS expr")]
public ExprClosure exprMin(ExprClosure l, Token<Issue251Tokens> _, ExprClosure r)
{
SyntaxCheck(l.ResultType == ExprClosure.Types.INT, "Type error: expected int - int");
SyntaxCheck(r.ResultType == ExprClosure.Types.INT, "Type error: expected int - int");
l.valueInt -= r.valueInt;
return l;
}

[Production("expr: expr OP_MULTIPLY expr")]
public ExprClosure exprMul(ExprClosure l, Token<Issue251Tokens> _, ExprClosure r)
{
SyntaxCheck(r.ResultType == ExprClosure.Types.INT, "Syntax error: expected ? * int");
switch (l.ResultType)
{
case ExprClosure.Types.INT:
l.valueInt *= r.valueInt;
return l;
case ExprClosure.Types.STRING:
l.valueString = new StringBuilder().Insert(0, l.valueString, r.valueInt).ToString();
return l;
}
throw new ArgumentException("Internal Error");
}

// [Production("expr: int OP_DIVIDE expr")]
// public ExprClosure exprDiv(int l, Token<LexerTokens> _, ExprClosure r)
// {
// SyntaxCheck(r.ResultType == ExprClosure.Types.INT, "Syntax error: expected int / int, got int / string");
// r.valueInt = l / r.valueInt;
// return r;
// }

// [Production("expr: int OP_MODULE expr")]
// public ExprClosure exprImod(int l, Token<LexerTokens> _, ExprClosure r)
// {
// SyntaxCheck(r.ResultType == ExprClosure.Types.INT, "Syntax error: expected int % int, got int % string");
// r.valueInt = l % r.valueInt;
// return r;
// }
}
}

13 changes: 13 additions & 0 deletions ParserTests/IssuesTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using ParserTests.Issue251;
using sly.buildresult;
using sly.lexer;
using sly.parser.generator;
using Xunit;
Expand Down Expand Up @@ -206,5 +208,16 @@ public static void Issue219BNF()
var exception = Assert.Throws<Exception219>(() => parser.Parse("a = 1"));
Assert.Equal("visitor error",exception.Message);
}

[Fact]
public static void Issue251LeftrecForBNF() {
ParserBuilder<Issue251Parser.Issue251Tokens,Issue251Parser.ExprClosure> builder = new ParserBuilder<Issue251Parser.Issue251Tokens, Issue251Parser.ExprClosure>();
Issue251Parser instance = new Issue251Parser();
var bres = builder.BuildParser(instance,ParserType.LL_RECURSIVE_DESCENT, "expr");
Assert.False(bres.IsOk);
Assert.Equal(1,bres.Errors.Count);
var error = bres.Errors.First();
Assert.Equal(ErrorCodes.PARSER_LEFT_RECURSIVE, error.Code);
}
}
}

0 comments on commit c7472b4

Please sign in to comment.