From 2a43b4d5904cde254acaf67494ebc1f0222c1745 Mon Sep 17 00:00:00 2001 From: b3b00 Date: Tue, 24 Dec 2024 10:51:58 +0100 Subject: [PATCH] fluent indented while unit tests --- .../parser/IndentedWhileParserGeneric.cs | 2 +- .../FluentIndentedWhileParserBuilder.cs | 258 ++++++++++ .../samples/FluentIndentedWhileTests.cs | 471 ++++++++++++++++++ 3 files changed, 730 insertions(+), 1 deletion(-) create mode 100644 tests/ParserTests/samples/FluentIndentedWhileParserBuilder.cs create mode 100644 tests/ParserTests/samples/FluentIndentedWhileTests.cs diff --git a/src/samples/IndentedWhile/parser/IndentedWhileParserGeneric.cs b/src/samples/IndentedWhile/parser/IndentedWhileParserGeneric.cs index c3680494..c19c0538 100644 --- a/src/samples/IndentedWhile/parser/IndentedWhileParserGeneric.cs +++ b/src/samples/IndentedWhile/parser/IndentedWhileParserGeneric.cs @@ -317,7 +317,7 @@ public WhileAST binaryAndExpression(WhileAST left, Token operatorToken, WhileAST value) + public WhileAST unaryNotExpression(Token operatorToken, WhileAST value) { return new Not(value as Expression); } diff --git a/tests/ParserTests/samples/FluentIndentedWhileParserBuilder.cs b/tests/ParserTests/samples/FluentIndentedWhileParserBuilder.cs new file mode 100644 index 00000000..770a12b2 --- /dev/null +++ b/tests/ParserTests/samples/FluentIndentedWhileParserBuilder.cs @@ -0,0 +1,258 @@ +using System; +using System.Collections.Generic; +using csly.indentedWhileLang.parser; +using csly.whileLang.model; +using sly.buildresult; +using sly.lexer; +using sly.lexer.fluent; +using sly.parser; +using sly.parser.generator; +using sly.parser.parser; + +namespace ParserTests.samples; + +public class FluentIndentedWhileParserBuilder { + + public IFluentLexemeBuilder GetLexer() + { + var lexer = FluentLexerBuilder.NewBuilder() + .IgnoreEol(true) + .IgnoreWhiteSpace(true) + .IsIndentationAware(true) + .IgnoreKeywordCase(true) + .AlphaNumDashId(IndentedWhileTokenGeneric.IDENTIFIER) + .WithModes(ModeAttribute.DefaultLexerMode, "fstringExpression") + .Keyword(IndentedWhileTokenGeneric.IF, "if") + .Keyword(IndentedWhileTokenGeneric.THEN, "then") + .Keyword(IndentedWhileTokenGeneric.ELSE, "else") + .Keyword(IndentedWhileTokenGeneric.WHILE, "while") + .Keyword(IndentedWhileTokenGeneric.DO, "do") + .Keyword(IndentedWhileTokenGeneric.TRUE, "true") + .Keyword(IndentedWhileTokenGeneric.FALSE, "false") + .Keyword(IndentedWhileTokenGeneric.NOT, "not") + .Keyword(IndentedWhileTokenGeneric.AND, "and") + .Keyword(IndentedWhileTokenGeneric.OR, "or") + .Keyword(IndentedWhileTokenGeneric.PRINT, "print") + .Keyword(IndentedWhileTokenGeneric.RETURN, "return") + .Keyword(IndentedWhileTokenGeneric.SKIP, "skip") + .Int(IndentedWhileTokenGeneric.INT) + .Sugar(IndentedWhileTokenGeneric.GREATER, ">") + .WithModes(ModeAttribute.DefaultLexerMode, "fstringExpression") + .Sugar(IndentedWhileTokenGeneric.LESSER, "<").WithModes(ModeAttribute.DefaultLexerMode, "fstringExpression") + .Sugar(IndentedWhileTokenGeneric.EQUALS, "==") + .WithModes(ModeAttribute.DefaultLexerMode, "fstringExpression") + .Sugar(IndentedWhileTokenGeneric.DIFFERENT, "!=") + .WithModes(ModeAttribute.DefaultLexerMode, "fstringExpression") + .Sugar(IndentedWhileTokenGeneric.CONCAT, ".").WithModes(ModeAttribute.DefaultLexerMode, "fstringExpression") + .Sugar(IndentedWhileTokenGeneric.PLUS, "+").WithModes(ModeAttribute.DefaultLexerMode, "fstringExpression") + .Sugar(IndentedWhileTokenGeneric.MINUS, "-").WithModes(ModeAttribute.DefaultLexerMode, "fstringExpression") + .Sugar(IndentedWhileTokenGeneric.TIMES, "*").WithModes(ModeAttribute.DefaultLexerMode, "fstringExpression") + .Sugar(IndentedWhileTokenGeneric.DIVIDE, "/").WithModes(ModeAttribute.DefaultLexerMode, "fstringExpression") + .Sugar(IndentedWhileTokenGeneric.QUESTION, "?").WithModes("fstringExpression") + .Sugar(IndentedWhileTokenGeneric.ARROW, "->").WithModes(ModeAttribute.DefaultLexerMode, "fstringExpression") + .Sugar(IndentedWhileTokenGeneric.OPEN_PAREN, "(") + .WithModes(ModeAttribute.DefaultLexerMode, "fstringExpression") + .Sugar(IndentedWhileTokenGeneric.CLOSE_PAREN, ")") + .WithModes(ModeAttribute.DefaultLexerMode, "fstringExpression") + .Sugar(IndentedWhileTokenGeneric.SEMICOLON, ";") + .Sugar(IndentedWhileTokenGeneric.COLON, "|").WithModes(ModeAttribute.DefaultLexerMode, "fstringExpression") + .SingleLineComment(IndentedWhileTokenGeneric.COMMENT, "#") + + .Sugar(IndentedWhileTokenGeneric.OPEN_FSTRING_EXPPRESSION, "{").WithModes("fstring").PushToMode("fstringExpression") + .Sugar(IndentedWhileTokenGeneric.CLOSE_FSTRING_EXPPRESSION, "}").WithModes("fstringExpression").PopMode() + .Sugar(IndentedWhileTokenGeneric.OPEN_FSTRING, "$\"").WithModes(ModeAttribute.DefaultLexerMode, "fstringExpression").PushToMode("fstring") + .Sugar(IndentedWhileTokenGeneric.CLOSE_FSTRING, "\"").WithModes("fstring").PopMode() + .UpTo(IndentedWhileTokenGeneric.FSTRING_CONTENT, "{","\"").WithModes("fstring") + .Sugar(IndentedWhileTokenGeneric.ASSIGN, ":="); + return lexer; + } + + public BuildResult> GetParser() + { + var instance = new IndentedWhileParserGeneric(); + var builder = FluentEBNFParserBuilder.NewBuilder(instance,"program", "en"); + + var binary = (Func, WhileAST, WhileAST> instanceCallback) => + { + Func callback = (object[] args) => + { + WhileAST left = (WhileAST)args[0]; + Token op = (Token)args[1]; + WhileAST right = (WhileAST)args[2]; + return instanceCallback(left, op, right); + }; + return callback; + }; + + var prefix = (Func, WhileAST, WhileAST> instanceCallback) => + { + Func callback = (object[] args) => + { + Token op = (Token)args[0]; + WhileAST right = (WhileAST)args[1]; + return instanceCallback(op, right); + }; + return callback; + }; + + var postfix = (Func, WhileAST> instanceCallback) => + { + Func callback = (object[] args) => + { + Token op = (Token)args[0]; + WhileAST right = (WhileAST)args[1]; + return instanceCallback(right, op); + }; + return callback; + }; + + var comparisonCallback = binary((left, op, right) => + { + return instance.binaryComparisonExpression(left, op, right); + }); + + var stringCallback = binary((left, op, right) => + { + return instance.binaryStringExpression(left, op, right); + }); + var factorCallback = binary((left, op, right) => + { + return instance.binaryFactorNumericExpression(left, op, right); + }); + var termCallback = binary((left, op, right) => + { + return instance.binaryTermNumericExpression(left, op, right); + }); + + + var parser = builder + .UseAutoCloseIndentations(true) + .UseMemoization(true) + // expressions + .Right(IndentedWhileTokenGeneric.LESSER, 50, comparisonCallback) + .Right(IndentedWhileTokenGeneric.GREATER, 50, comparisonCallback) + .Right(IndentedWhileTokenGeneric.EQUALS, 50, comparisonCallback) + .Right(IndentedWhileTokenGeneric.DIFFERENT, 50, comparisonCallback) + .Right(IndentedWhileTokenGeneric.CONCAT, 50, stringCallback) + .Right(IndentedWhileTokenGeneric.PLUS, 10, termCallback) + .Right(IndentedWhileTokenGeneric.MINUS, 10, termCallback) + .Right(IndentedWhileTokenGeneric.TIMES, 50, factorCallback) + .Right(IndentedWhileTokenGeneric.DIVIDE, 50, factorCallback) + .Prefix(IndentedWhileTokenGeneric.MINUS, 100, + prefix((op, value) => instance.unaryNumericExpression(op, value))) + .Right(IndentedWhileTokenGeneric.OR, 10, + binary((left, op, right) => instance.binaryOrExpression(left, op, right))) + .Right(IndentedWhileTokenGeneric.AND, 50, + binary((left, op, right) => instance.binaryAndExpression(left, op, right))) + .Prefix(IndentedWhileTokenGeneric.NOT, 100, prefix((op, value) => instance.unaryNotExpression(op, value))) + // operands + .Production("primary : INT", (args) => + { + return instance.PrimaryInt((Token)args[0]); + }) + .Production("primary : IDENTIFIER", (args) => + { + return instance.PrimaryId((Token)args[0]); + }) + .Production("primary : [TRUE|FALSE]", (args) => + { + return instance.PrimaryBool((Token)args[0]); + }) + .Production("primary : OPEN_PAREN[d] IndentedWhileParserGeneric_expressions CLOSE_PAREN[d]", args => + { + return (WhileAST)args[0]; + }) + .Production( + "primary : QUESTION[d] IndentedWhileParserGeneric_expressions ARROW[d] IndentedWhileParserGeneric_expressions COLON[d] IndentedWhileParserGeneric_expressions", + args => + { + var condition = (WhileAST)args[0]; + var ifTrue = (WhileAST)args[1]; + var ifFalse = (WhileAST)args[2]; + return instance.TernaryQuestion(condition, ifTrue, ifFalse); + }) + .Operand("operand: primary", (args) => + { + return (WhileAST)args[0]; + }) + // fstrings + .Production("primary : OPEN_FSTRING[d] fstring_element* CLOSE_FSTRING[d]", args => + { + var elements = (List)args[0]; + return instance.fstring(elements); + ; + }) + .Production("fstring_element : FSTRING_CONTENT", args => + { + return instance.FStringContent((Token)args[0]); + }) + .Production( + "fstring_element : OPEN_FSTRING_EXPPRESSION[d] IndentedWhileParserGeneric_expressions CLOSE_FSTRING_EXPPRESSION[d]", + args => + { + return (WhileAST)args[0]; + }) + // main + .Production("program: sequence", args => + { + return (WhileAST)args[0]; + }) + .Production("block : INDENT[d] sequence UINDENT[d]", args => + { + return (WhileAST)args[0]; + }) + // statements + .Production("statement : block", args => + { + return (WhileAST)args[0]; + }) + .Production("sequence: statement*", args => + { + return instance.sequence((List)args[0]); + }) + .Production("statement: IF[d] IndentedWhileParserGeneric_expressions THEN[d] block (ELSE[d] block)?", + args => + { + var condition = (WhileAST)args[0]; + var ifTrue = (WhileAST)args[1]; + var ifFalse = (ValueOption>)args[2]; + return instance.ifStmt(condition, ifTrue, ifFalse); + }) + .Production("statement: WHILE[d] IndentedWhileParserGeneric_expressions DO[d] block", args => + { + var condition = (WhileAST)args[0]; + var block = (WhileAST)args[1]; + return instance.whileStmt(condition, block); + }) + .Production("statement: IDENTIFIER ASSIGN[d] IndentedWhileParserGeneric_expressions", args => + { + var id = (Token)args[0]; + var value = (Expression)args[1]; + return instance.assignStmt(id, value); + }) + .Production("statement: SKIP[d]", args => + { + return new SkipStatement(); + }) + .Production("statement: RETURN[d] IndentedWhileParserGeneric_expressions", args => + { + var value = (Expression)args[0]; + return new ReturnStatement(value); + }) + .Production("statement: PRINT[d] IndentedWhileParserGeneric_expressions", args => + { + var value = (Expression)args[0]; + return new PrintStatement(value); + }) + .WithLexerbuilder(GetLexer()) + .BuildParser(); + + + return parser; + + + + } + + +} \ No newline at end of file diff --git a/tests/ParserTests/samples/FluentIndentedWhileTests.cs b/tests/ParserTests/samples/FluentIndentedWhileTests.cs new file mode 100644 index 00000000..bab5fd81 --- /dev/null +++ b/tests/ParserTests/samples/FluentIndentedWhileTests.cs @@ -0,0 +1,471 @@ +using System.Linq; +using csly.indentedWhileLang.compiler; +using csly.indentedWhileLang.parser; +using csly.whileLang.interpreter; +using csly.whileLang.model; +using NFluent; +using sly.buildresult; +using sly.lexer; +using sly.parser; +using sly.parser.generator; +using sly.parser.generator.visitor; +using Xunit; + +namespace ParserTests.samples +{ + public class FluentIndentedWhileTests + { + private static BuildResult> Parser; + + + public BuildResult> buildParser() + { + if (Parser == null) + { + var whileParser = new IndentedWhileParserGeneric(); + FluentIndentedWhileParserBuilder builder = new FluentIndentedWhileParserBuilder(); + Parser = builder.GetParser(); + Check.That(Parser).IsOk(); + } + + return Parser; + } + + + [Fact] + public void TestAssignAdd() + { + var buildResult = buildParser(); + var parser = buildResult.Result; + var result = parser.Parse("a:=1+1"); + Check.That(result).IsOkParsing(); + + Check.That(result.Result).IsInstanceOf(); + var seq = result.Result as SequenceStatement; + Check.That(seq.Get(0)).IsInstanceOf(); + var assign = seq.Get(0) as AssignStatement; + Check.That(assign.VariableName).IsEqualTo("a"); + var val = assign.Value; + Check.That(val).IsInstanceOf(); + var bin = val as BinaryOperation; + Check.That(bin.Operator).IsEqualTo(BinaryOperator.ADD); + Check.That((bin.Left as IntegerConstant)?.Value).IsEqualTo(1); + Check.That((bin.Right as IntegerConstant)?.Value).IsEqualTo(1); + } + + [Fact] + public void TestBuildParser() + { + var buildResult = buildParser(); + var parser = buildResult.Result; + } + + [Fact] + public void TestCounterProgram() + { + var buildResult = buildParser(); + var parser = buildResult.Result; + string program = @" +a:=0 +while a < 10 do + print a + a := a +1 +"; + var result = parser.Parse(program); + Check.That(result).IsOkParsing(); + } + + [Fact] + public void TestCounterProgramExec() + { + var buildResult = buildParser(); + var parser = buildResult.Result; + string program = @" +a:=0 +while a < 10 do + print a + a := a +1 +"; + var result = parser.Parse(program); + Check.That(result).IsOkParsing(); + var interpreter = new Interpreter(); + var context = interpreter.Interprete(result.Result, true); + Check.That(context.variables).IsSingle(); + Check.That(context).HasVariableWithIntValue("a", 10); + + } + + [Fact] + public void TestFactorialProgramExec() + { + var program = @" +# TestFactorialProgramExec +r:=1 +i:=1 +while i < 11 do + r := r * i + print r + print i + i := i + 1 +"; + var buildResult = buildParser(); + var parser = buildResult.Result; + var result = parser.Parse(program); + Check.That(result).IsOkParsing(); + var interpreter = new Interpreter(); + var context = interpreter.Interprete(result.Result, true); + Check.That(context.variables).CountIs(2); + Check.That(context).HasVariableWithIntValue("i", 11); + Check.That(context).HasVariableWithIntValue("r", 3628800); + } + + + [Fact] + public void TestFactorialProgramExecAsIL() + { + var program = @" +# TestFactorialProgramExec +r:=1 +i:=1 +while i < 11 do + r := r * i + print $""r="".r + print $""i="".i + i := i + 1 +return r"; + var compiler = new IndentedWhileCompiler(); + var func = compiler.CompileToFunction(program,true); + Check.That(func).IsNotNull(); + var f = func(); + Check.That(f).IsEqualTo(3628800); + } + + [Fact] + public void TestIfThenElse() + { + var buildResult = buildParser(); + var parser = buildResult.Result; + var program = @" +# TestIfThenElse +if true then + a := $""hello"" +else + b := $""world"" +"; + var result = parser.Parse(program); + Check.That(result).IsOkParsing(); + + Check.That(result.Result).IsInstanceOf(); + var seq = result.Result as SequenceStatement; + Check.That(seq.Get(0)).IsInstanceOf(); + var si = seq.Get(0) as IfStatement; + var cond = si.Condition; + Check.That(cond).IsInstanceOf(); + Check.That((cond as BoolConstant).Value).IsTrue(); + var s = si.ThenStmt; + + Check.That(si.ThenStmt).IsInstanceOf(); + var thenBlock = si.ThenStmt as SequenceStatement; + Check.That(thenBlock).CountIs(1); + Check.That(thenBlock.Get(0)).IsInstanceOf(); + var thenAssign = thenBlock.Get(0) as AssignStatement; + Check.That(thenAssign.VariableName).IsEqualTo("a"); + Check.That(thenAssign.Value).IsInstanceOf(); + var fstring = thenAssign.Value as StringConstant; + Check.That(fstring).IsNotNull(); + Check.That(fstring.Value).IsEqualTo("hello"); + + Check.That(si.ElseStmt).IsInstanceOf(); + var elseBlock = si.ElseStmt as SequenceStatement; + Check.That(elseBlock).CountIs(1); + Check.That(elseBlock.Get(0)).IsInstanceOf(); + var elseAssign = elseBlock.Get(0) as AssignStatement; + Check.That(elseAssign.VariableName).IsEqualTo("b"); + Check.That(elseAssign.Value).IsInstanceOf(); + fstring = elseAssign.Value as StringConstant; + Check.That(fstring).IsNotNull(); + Check.That(fstring.Value).IsEqualTo("world"); + } + + [Fact] + public void TestNestedIfThenElse() + { + var program = @" +# TestIfThenElse +a := -111 +if true then + if true then + a := 1 + else + a := 2 +else + a := 3 + b := $""world"" +return a +"; + var compiler = new IndentedWhileCompiler(); + var func = compiler.CompileToFunction(program,true); + Check.That(func).IsNotNull(); + var f = func(); + Check.That(f).IsEqualTo(1); + } + + + [Fact] + public void TestFString() + { + var buildResult = buildParser(); + var parser = buildResult.Result; + var program = @" +# fstring +v1 := 48 +v2 := 152 +b := true +fstring := $""v1 :> {v1} < v2 :> {v2} < v3 :> {v1+v2} < v4 :>{$""hello,"".$"" world""}< v5 :>{(? b -> $""true"" | $""false"")}< - end"" +print fstring +return 100 +"; + + var result = parser.Parse(program); + Check.That(result).IsOkParsing(); + Check.That(result.Result).IsNotNull(); + Check.That(result.Result).IsInstanceOf(); + SequenceStatement seq = result.Result as SequenceStatement; + Check.That(seq.Count).IsEqualTo(6); + var fstringAssign = seq.Get(3) as AssignStatement; + Check.That(fstringAssign).IsNotNull(); + Check.That(fstringAssign.VariableName).IsEqualTo("fstring"); + Check.That(fstringAssign.Value).IsInstanceOf(); + var fString = fstringAssign.Value as BinaryOperation; + Check.That(fString.Operator).IsEqualTo(BinaryOperator.CONCAT); + var interpreter = new Interpreter(); + var context = interpreter.Interprete(result.Result, true); + Check.That(context.variables).CountIs(4); + Check.That(context).HasVariableWithIntValue("v1", 48); + Check.That(context).HasVariableWithIntValue("v2", 152); + Check.That(context).HasVariableWithBoolValue("b", true); + string expected = "v1 :> 48 < v2 :> 152 < v3 :> 200 < v4 :>hello, world< v5 :>true< - end"; + Check.That(context).HasVariableWithStringValue("fstring", expected); + + var compiler = new IndentedWhileCompiler(); + var func = compiler.CompileToFunction(program,true); + Check.That(func).IsNotNull(); + Printer.Clear(); + var f = func(); + Check.That(Printer.lines).CountIs(1); + Check.That(Printer.lines[0]).IsEqualTo(expected); + + } + + [Fact] + public void TestInfiniteWhile() + { + var buildResult = buildParser(); + var parser = buildResult.Result; + var program = @" +# infinite loop +while true do + skip +"; + var result = parser.Parse(program); + Check.That(result).IsOkParsing(); + + Check.That(result.Result).IsInstanceOf(); + var seq = result.Result as SequenceStatement; + Check.That(seq.Get(0)).IsInstanceOf(); + var whil = seq.Get(0) as WhileStatement; + var cond = whil.Condition; + Check.That(cond).IsInstanceOf(); + Check.That((cond as BoolConstant).Value).IsTrue(); + var s = whil.BlockStmt; + Check.That(whil.BlockStmt).IsInstanceOf(); + var seqBlock = whil.BlockStmt as SequenceStatement; + Check.That(seqBlock).CountIs(1); + Check.That(seqBlock.Get(0)).IsInstanceOf(); + } + + [Fact] + public void TestPrintBoolExpression() + { + var buildResult = buildParser(); + var parser = buildResult.Result; + var result = parser.Parse("print true and false"); + Check.That(result).IsOkParsing(); + + + Check.That(result.Result).IsInstanceOf(); + var seq = result.Result as SequenceStatement; + Check.That(seq.Get(0)).IsInstanceOf(); + var print = seq.Get(0) as PrintStatement; + var expr = print.Value; + Check.That(expr).IsInstanceOf(); + var bin = expr as BinaryOperation; + Check.That(bin.Operator).IsEqualTo(BinaryOperator.AND); + Check.That((bin.Left as BoolConstant)?.Value).IsEqualTo(true); + Check.That((bin.Right as BoolConstant)?.Value).IsEqualTo(false); + } + + + [Fact] + public void TestSkip() + { + var buildResult = buildParser(); + var parser = buildResult.Result; + var result = parser.Parse("skip"); + Check.That(result).IsOkParsing(); + + Check.That(result.Result).IsInstanceOf(); + var seq = result.Result as SequenceStatement; + Check.That(seq.Get(0)).IsInstanceOf(); + } + + [Fact] + public void TestSkipAssignSequence() + { + var buildResult = buildParser(); + var parser = buildResult.Result; + var program = @"a:=1 +b:=2 +c:=3"; + var result = parser.Parse(program); + Check.That(result).IsOkParsing(); + Check.That(result.Result).IsInstanceOf(); + var seq = result.Result as SequenceStatement; + Check.That(seq).CountIs(3); + + + + (string name, int value)[] values = new []{ ("a",1), ("b",2), ("c",3) }; + + Check.That(seq.Cast().Extracting(x => (x.VariableName,(x.Value as IntegerConstant).Value))).ContainsExactly(values); + + } + + + [Fact] + public void TestSkipSkipSkipSequence() + { + var buildResult = buildParser(); + var parser = buildResult.Result; + var result = parser.Parse(@" +skip +skip +skip"); + Check.That(result).IsOkParsing(); + Check.That(result.Result).IsInstanceOf(); + var seq = result.Result as SequenceStatement; + Check.That(seq).CountIs(3); + Check.That(seq.Get(0)).IsInstanceOf(); + Check.That(seq.Get(1)).IsInstanceOf(); + Check.That(seq.Get(2)).IsInstanceOf(); + } + + + [Fact] + public void TestIndentationError() + { + var buildResult = buildParser(); + var parser = buildResult.Result; + var result = parser.Parse(@" +# infinite loop +while true do + skip + skip"); + Check.That(result.IsError).IsTrue(); + Check.That(result.Errors).IsSingle(); + var error = result.Errors.First(); + Check.That(error.ErrorType).IsEqualTo(ErrorType.IndentationError); + Check.That(error.Line).IsEqualTo(4); + Check.That(error.ErrorMessage).Contains("Indentation error"); + + result = parser.Parse(@" +# infinite loop +while true do + skip +skip"); + Check.That(result.IsOk).IsTrue(); + // TODO : more tests? + } + + [Fact] + public void TestEmptyLines() + { + var buildResult = buildParser(); + var parser = buildResult.Result; + var program = @" +# infinite loop +i:= 2 + +while true do + + skip +"; + var result = parser.Parse(program); + Check.That(result).IsOkParsing(); + } + + [Fact] + public void TestAutoCloseMissingUIndents() + { + var buildResult = buildParser(); + var parser = buildResult.Result; + var program = @" +if true then + if false then + x := 28"; + var result = parser.Parse(program); + var root = result.Result; + Check.That(root).IsInstanceOf(); + var seq = result.Result as SequenceStatement; + var dump = seq.Dump(" "); + Check.That(seq.Statements).CountIs(1); + Check.That(seq.Statements[0]).IsInstanceOf(); + Check.That(result).IsOkParsing(); + } + + [Fact] + public void TestIssue413_incompleteProgram() + { + var buildResult = buildParser(); + var parser = buildResult.Result; + var program = @" +if +"; + var result = parser.Parse(program); + Check.That(result).Not.IsOkParsing(); + Check.That(result.Errors).CountIs(1); + var error = result.Errors.First(); + Check.That(error.ErrorType).IsEqualTo(ErrorType.UnexpectedEOS); + var unexpectedEosError = error as UnexpectedTokenSyntaxError; + Check.That(unexpectedEosError).IsNotNull(); + Check.That(unexpectedEosError.ExpectedTokens.Extracting(x => x.TokenId)).IsEquivalentTo( + IndentedWhileTokenGeneric.MINUS,IndentedWhileTokenGeneric.NOT, IndentedWhileTokenGeneric.QUESTION, + IndentedWhileTokenGeneric.OPEN_PAREN,IndentedWhileTokenGeneric.OPEN_FSTRING,IndentedWhileTokenGeneric.TRUE, + IndentedWhileTokenGeneric.INT,IndentedWhileTokenGeneric.FALSE,IndentedWhileTokenGeneric.IDENTIFIER); + } + + [Fact] + public void TestIndentationError_emptyIndentLine() + { + BuildResult> _lexer = LexerBuilder.BuildLexer(); + + Check.That(_lexer).IsOk(); + + var program = @" +if true then + + if false then + x := 28"; + var lexed = _lexer.Result.Tokenize(program); + Check.That(lexed).IsOkLexing(); + var mainTokens = lexed.Tokens.MainTokens(); + Check.That(mainTokens).Not.IsEmpty(); + Check.That(mainTokens.Last().IsEOS).IsTrue(); + var lastToken = mainTokens[mainTokens.Count - 2]; + Check.That(lastToken).IsNotNull(); + Check.That(lastToken.TokenID) + .IsEqualTo(IndentedWhileTokenGeneric.INT); + Check.That(lastToken.IntValue).IsEqualTo(28); + } + } +} \ No newline at end of file