Parser was rendered obsolete upon port to Net Core Tool templates
This commit is contained in:
Chris Cheetham 2021-06-21 09:37:36 -04:00
Родитель c68aff6433
Коммит c769ff3970
8 изменённых файлов: 0 добавлений и 1110 удалений

Просмотреть файл

@ -1,157 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
namespace Steeltoe.InitializrApi.Parsers
{
/// <summary>
/// Parser for expressions.
/// </summary>
public class ExpressionParser
{
private IEnumerator<Token> _tokens;
/// <summary>
/// Initializes a new instance of the <see cref="ExpressionParser"/> class.
/// </summary>
/// <param name="expression">The expression in string form.</param>
public ExpressionParser(string expression)
{
_tokens = new Tokenizer().Scan(expression).GetEnumerator();
}
/// <summary>
/// Evaluates the expression using the specified parameters.
/// </summary>
/// <param name="context"> Expression parameter context.</param>
/// <returns>The evaluation.</returns>
public object Evaluate(Dictionary<string, object> context = null)
{
if (!_tokens.MoveNext())
{
return null;
}
var expression = BuildExpression();
return expression.Evaluate(context);
}
private Expression BuildExpression()
{
var operand = BuildOperand();
if (_tokens.Current is null)
{
return operand;
}
if (_tokens.Current is OrOperatorToken)
{
_tokens.MoveNext();
return new OrOperation(operand, BuildExpression());
}
if (_tokens.Current is GreaterThanOperatorToken)
{
_tokens.MoveNext();
return new GreaterThanOperation(operand, BuildExpression());
}
throw new ParserException($"Expected operator; actual: {_tokens.Current}");
}
private Expression BuildOperand()
{
if (_tokens.Current is null)
{
throw new ParserException($"Expected operand; reached end of expression.");
}
if (!(_tokens.Current is OperandToken))
{
throw new ParserException($"Expected operand; actual: {_tokens.Current}");
}
if (_tokens.Current is IntegerToken)
{
var iToken = _tokens.Current as IntegerToken;
_tokens.MoveNext();
return BuildInteger(iToken);
}
var nToken = _tokens.Current as NameToken;
_tokens.MoveNext();
if (_tokens.Current is ParenOpenToken)
{
_tokens.MoveNext();
return BuildFunction(nToken);
}
return BuildParameter(nToken);
}
private Expression BuildInteger(IntegerToken iToken)
{
return new Integer(iToken.Value);
}
private Expression BuildParameter(NameToken nToken)
{
return new Parameter(nToken.Name);
}
private Expression BuildFunction(NameToken nToken)
{
if (_tokens.Current is null)
{
throw new ParserException("Expected operand or close parenthesis; reached end of expression.");
}
if (!(_tokens.Current is NameToken || _tokens.Current is ParenCloseToken))
{
throw new ParserException($"Expected operand or close parenthesis; actual: {_tokens.Current}");
}
var parameters = new List<Expression>();
while (!(_tokens.Current is ParenCloseToken))
{
if (parameters.Count > 0)
{
if (!(_tokens.Current is CommaToken))
{
throw new ParserException("Expected comma or close parenthesis; reached end of expression.");
}
_tokens.MoveNext();
}
if (_tokens.Current is null)
{
throw new ParserException("Expected operand or close parenthesis; reached end of expression.");
}
if (!(_tokens.Current is NameToken || _tokens.Current is ParenCloseToken))
{
throw new ParserException($"Expected operand or close parenthesis; actual: {_tokens.Current}");
}
parameters.Add(BuildOperand());
}
if (!(_tokens.Current is ParenCloseToken))
{
throw new ParserException($"Expected operand or close parenthesis; actual: {_tokens.Current}");
}
_tokens.MoveNext();
if (nToken.Name.Equals("count"))
{
return new Count(parameters);
}
throw new ParserException($"Unknown function: {nToken}");
}
}
}

Просмотреть файл

@ -1,134 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
namespace Steeltoe.InitializrApi.Parsers
{
internal abstract class Expression
{
internal abstract object Evaluate(Dictionary<string, object> context);
}
internal class Integer : Expression
{
internal Integer(int i)
{
Value = i;
}
private int Value { get; }
internal override object Evaluate(Dictionary<string, object> context)
{
return Value;
}
}
internal class Parameter : Expression
{
internal Parameter(string name)
{
Name = name;
}
private string Name { get; }
internal override object Evaluate(Dictionary<string, object> context)
{
context.TryGetValue(Name, out var value);
return value;
}
}
internal class OrOperation : Expression
{
internal OrOperation(Expression left, Expression right)
{
Left = left;
Right = right;
}
private Expression Left { get; }
private Expression Right { get; }
internal override object Evaluate(Dictionary<string, object> context)
{
var left = ToBool(Left.Evaluate(context));
var right = ToBool(Right.Evaluate(context));
return left || right;
}
private static bool ToBool(object value)
{
if (value is null)
{
return false;
}
if (value is bool b)
{
return b;
}
return false;
}
}
internal class GreaterThanOperation : Expression
{
internal GreaterThanOperation(Expression left, Expression right)
{
Left = left;
Right = right;
}
private Expression Left { get; }
private Expression Right { get; }
internal override object Evaluate(Dictionary<string, object> context)
{
var left = (int)Left.Evaluate(context);
var right = (int)Right.Evaluate(context);
return left > right;
}
}
internal class Count : Expression
{
internal Count(IEnumerable<Expression> expressions)
{
Expressions = expressions;
}
private IEnumerable<Expression> Expressions { get; }
internal override object Evaluate(Dictionary<string, object> context)
{
int count = 0;
foreach (var expression in Expressions)
{
var value = expression.Evaluate(context);
if (value != null)
{
if (value is bool)
{
if ((bool)value)
{
++count;
}
}
else
{
++count;
}
}
}
return count;
}
}
}

Просмотреть файл

@ -1,22 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information.
using System;
namespace Steeltoe.InitializrApi.Parsers
{
/// <summary>
/// The exception that is throw when an parser encounters an error.
/// </summary>
public class ParserException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="ParserException"/> class.
/// </summary>
public ParserException(string message)
: base(message)
{
}
}
}

Просмотреть файл

@ -1,21 +0,0 @@
# Grammars
## Expression
```
<expression> := <operand> | <operand> <operator> <expression>
<operand> := <parameter> | <function> | <integer>
<parameter := <name>
<parameter-list> := <parameter> | <parameter> "," <parameter-list>
<function> := <name> "(" ")" | <name> "(" <parameter-list> ")"
<name> := <letter> | <letter> <char-list>
<char-list> := <char> | <char> <char-list>
<char> := <letter> | <digit> | <special>
<letter> := <upper> | <lower>
<upper> := "A" | "B" | ... | "Z"
<lower> := "a" | "b" | ... | "z"
<integer> := <digit> | <digit> <integer>
<digit> := "0" | "1" | ... | "9"
<special> := "-"
<operator> := "||"
```

Просмотреть файл

@ -1,116 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text;
[assembly: InternalsVisibleTo("Steeltoe.InitializrApi.Test.Unit")]
namespace Steeltoe.InitializrApi.Parsers
{
internal class Tokenizer
{
private const int EndOfString = -1;
internal IEnumerable<Token> Scan(string expression)
{
using var reader = new StringReader(expression);
var tokens = new List<Token>();
while (reader.Peek() != EndOfString)
{
var ch = (char)reader.Peek();
if (char.IsWhiteSpace(ch))
{
reader.Read();
}
else if (char.IsDigit(ch))
{
var i = ReadInteger(reader);
tokens.Add(new IntegerToken(i));
}
else if (char.IsLetter(ch))
{
var name = ReadName(reader);
tokens.Add(new NameToken(name));
}
else if (ch.Equals(','))
{
reader.Read();
tokens.Add(new CommaToken());
}
else if (ch.Equals('('))
{
reader.Read();
tokens.Add(new ParenOpenToken());
}
else if (ch.Equals(')'))
{
reader.Read();
tokens.Add(new ParenCloseToken());
}
else if (ch.Equals('|'))
{
reader.Read();
ch = (char)reader.Read();
if (!ch.Equals('|'))
{
throw new ArgumentException("expected: '|'");
}
tokens.Add(new OrOperatorToken());
}
else if (ch.Equals('>'))
{
reader.Read();
tokens.Add(new GreaterThanOperatorToken());
}
else
{
tokens.Add(new UnknownToken(ch));
reader.Read();
}
}
return tokens;
}
private int ReadInteger(StringReader reader)
{
var buf = new StringBuilder();
while (true)
{
var ch = (char)reader.Peek();
if (!char.IsDigit(ch))
{
break;
}
buf.Append((char)reader.Read());
}
return int.Parse(buf.ToString());
}
private string ReadName(StringReader reader)
{
var buf = new StringBuilder();
while (true)
{
var ch = (char)reader.Peek();
if (!(char.IsLetterOrDigit(ch) || ch == '-'))
{
break;
}
buf.Append((char)reader.Read());
}
return buf.ToString();
}
}
}

Просмотреть файл

@ -1,85 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information.
namespace Steeltoe.InitializrApi.Parsers
{
internal abstract class Token
{
}
internal class UnknownToken : Token
{
internal UnknownToken(char value) => Value = value;
internal char Value { get; }
public override string ToString() => $"'{Value}' (unknown)";
}
internal abstract class OperandToken : Token
{
}
internal class IntegerToken : OperandToken
{
public IntegerToken(int i)
{
Value = i;
}
internal int Value { get; }
public override string ToString() => $"'{Value}' (integer)";
}
internal abstract class NamedToken : OperandToken
{
internal NamedToken(string name) => Name = name;
internal string Name { get; }
}
internal class NameToken : NamedToken
{
internal NameToken(string name)
: base(name)
{
}
public override string ToString() => $"'{Name}' (name)";
}
internal abstract class OperatorToken : Token
{
}
internal abstract class BooleanOperatorToken : OperatorToken
{
}
internal class OrOperatorToken : BooleanOperatorToken
{
public override string ToString() => $"'||' (or)";
}
internal class GreaterThanOperatorToken : BooleanOperatorToken
{
public override string ToString() => $"'>' (greater than)";
}
internal class ParenOpenToken : Token
{
public override string ToString() => $"'(' (paren open)";
}
internal class ParenCloseToken : Token
{
public override string ToString() => $"')' (paren close)";
}
internal class CommaToken : Token
{
public override string ToString() => $"',' (comma)";
}
}

Просмотреть файл

@ -1,360 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using FluentAssertions;
using Steeltoe.InitializrApi.Parsers;
using Xunit;
namespace Steeltoe.InitializrApi.Test.Unit.Parsers
{
public class ExpressionParserTests
{
/* ----------------------------------------------------------------- *
* positive tests *
* ----------------------------------------------------------------- */
[Fact]
public void Empty_Token_List_Should_Evaluate_To_Null()
{
// Arrange
var expression = new ExpressionParser(String.Empty);
// Act
var result = expression.Evaluate();
// Assert
result.Should().BeNull();
}
[Fact]
public void Integer_Should_Evaluate_To_Integer()
{
// Arrange
var expression = new ExpressionParser("725");
// Act
var result = expression.Evaluate();
// Assert
result.Should().Be(725);
}
[Fact]
public void True_Parameter_Should_Evaluate_To_True()
{
// Arrange
var expression = new ExpressionParser("my-var");
var context = new Dictionary<string, object>
{
{ "my-var", true },
};
// Act
var result = expression.Evaluate(context);
// Assert
result.Should().Be(true);
}
[Fact]
public void False_Parameter_Should_Evaluate_To_False()
{
// Arrange
var expression = new ExpressionParser("my-var");
var context = new Dictionary<string, object>
{
{ "my-var", false },
};
// Act
var result = expression.Evaluate(context);
// Assert
result.Should().Be(false);
}
[Fact]
public void Null_Parameter_Should_Evaluate_To_Null()
{
// Arrange
var expression = new ExpressionParser("my-var");
var context = new Dictionary<string, object>();
// Act
var result = expression.Evaluate(context);
// Assert
result.Should().BeNull();
}
[Fact]
public void Or_Operand_Should_Should_Evaluate_Per_Boolean_Rules()
{
// Arrange
var expressionTt = new ExpressionParser("T1 || T2");
var expressionTf = new ExpressionParser("T1 || F1");
var expressionTn = new ExpressionParser("T1 || N1");
var expressionFt = new ExpressionParser("F1 || T1");
var expressionFf = new ExpressionParser("F1 || F2");
var expressionFn = new ExpressionParser("F1 || N1");
var expressionNt = new ExpressionParser("N1 || T1");
var expressionNf = new ExpressionParser("N1 || F1");
var expressionNn = new ExpressionParser("N1 || N2");
var context = new Dictionary<string, object>
{
{ "T1", true },
{ "T2", true },
{ "F1", false },
{ "F2", false },
};
// Act
var resultTt = expressionTt.Evaluate(context);
var resultTf = expressionTf.Evaluate(context);
var resultTn = expressionTn.Evaluate(context);
var resultFt = expressionFt.Evaluate(context);
var resultFf = expressionFf.Evaluate(context);
var resultFn = expressionFn.Evaluate(context);
var resultNt = expressionNt.Evaluate(context);
var resultNf = expressionNf.Evaluate(context);
var resultNn = expressionNn.Evaluate(context);
// Assert
resultTt.Should().Be(true);
resultTf.Should().Be(true);
resultTn.Should().Be(true);
resultFt.Should().Be(true);
resultFf.Should().Be(false);
resultFn.Should().Be(false);
resultNt.Should().Be(true);
resultNf.Should().Be(false);
resultNn.Should().Be(false);
}
[Fact]
public void GreaterThan_Operand_Should_Should_Evaluate_Any_Operand()
{
// Arrange
var expression1 = new ExpressionParser("1 > 0");
var expression2 = new ExpressionParser("1 > 1");
var expression3 = new ExpressionParser("1 > 2");
var expression4 = new ExpressionParser("count(T1,T2) > 1");
var expression5 = new ExpressionParser("count(T1,T2) > 3");
var context = new Dictionary<string, object>
{
{ "T1", true },
{ "T2", true },
};
// Act
var result1 = expression1.Evaluate(context);
var result2 = expression2.Evaluate(context);
var result3 = expression3.Evaluate(context);
var result4 = expression4.Evaluate(context);
var result5 = expression5.Evaluate(context);
// Assert
result1.Should().Be(true);
result2.Should().Be(false);
result3.Should().Be(false);
result4.Should().Be(true);
result5.Should().Be(false);
}
[Fact]
public void Complex_Or_Operand_Should_Should_Evaluate_Per_Boolean_Rules()
{
// Arrange
var expressionFff = new ExpressionParser("F1 || F2 || F3");
var expressionNnn = new ExpressionParser("N1 || N2 || N3");
var expressionTtt = new ExpressionParser("T1 || T2 || T3");
var expressionTfn = new ExpressionParser("T1 || F1 || N1");
var context = new Dictionary<string, object>
{
{ "T1", true },
{ "T2", true },
{ "T3", true },
{ "F1", false },
{ "F2", false },
{ "F3", false },
};
// Act
var resultFff = expressionFff.Evaluate(context);
var resultNnn = expressionNnn.Evaluate(context);
var resultTtt = expressionTtt.Evaluate(context);
var resultTfn = expressionTfn.Evaluate(context);
// Assert
resultFff.Should().Be(false);
resultNnn.Should().Be(false);
resultTtt.Should().Be(true);
resultTfn.Should().Be(true);
}
[Fact]
public void Deep_Expression_Should_Should_Evaluate_Per_Boolean_Rules()
{
// Arrange
var expressionNnnnn = new ExpressionParser("N1 || N2 || N3 || N4 || N5");
var expressionNnnnt = new ExpressionParser("N1 || N2 || N3 || N4 || T1");
var context = new Dictionary<string, object>
{
{ "T1", true },
};
// Act
var resultNnnnn = expressionNnnnn.Evaluate(context);
var resultNnnnt = expressionNnnnt.Evaluate(context);
// Assert
resultNnnnn.Should().Be(false);
resultNnnnt.Should().Be(true);
}
[Fact]
public void Count_Should_Count_True_And_NonNull_Values()
{
// Arrange
var expression1 = new ExpressionParser("count(F1)");
var expression2 = new ExpressionParser("count(T1)");
var expression3 = new ExpressionParser("count(N1)");
var expression4 = new ExpressionParser("count(V1)");
var expression5 = new ExpressionParser("count(V1,T1,N1,F1,V2,N2,F2)");
var context = new Dictionary<string, object>
{
{ "T1", true },
{ "V1", "val1" },
{ "V2", "val1" },
};
// Act
var result1 = expression1.Evaluate(context);
var result2 = expression2.Evaluate(context);
var result3 = expression3.Evaluate(context);
var result4 = expression4.Evaluate(context);
var result5 = expression5.Evaluate(context);
// Assert
result1.Should().Be(0);
result2.Should().Be(1);
result3.Should().Be(0);
result4.Should().Be(1);
result5.Should().Be(3);
}
/* ----------------------------------------------------------------- *
* negative tests *
* ----------------------------------------------------------------- */
[Fact]
public void Malformed_Expression_Should_Throw_Exception()
{
// Arrange
var expression1 = new ExpressionParser("-");
var expression2 = new ExpressionParser("(");
var expression3 = new ExpressionParser(")");
// Act
Action act1 = () => expression1.Evaluate();
Action act2 = () => expression2.Evaluate();
Action act3 = () => expression3.Evaluate();
// Assert
act1.Should().Throw<ParserException>().WithMessage("Expected operand; actual: '-' (unknown)");
act2.Should().Throw<ParserException>().WithMessage("Expected operand; actual: '(' (paren open)");
act3.Should().Throw<ParserException>().WithMessage("Expected operand; actual: ')' (paren close)");
}
[Fact]
public void Missing_Operand_Should_Throw_Exception()
{
// Arrange
var expression1 = new ExpressionParser("||");
var expression2 = new ExpressionParser("V ||");
var expression3 = new ExpressionParser("V || ||");
// Act
Action act1 = () => expression1.Evaluate();
Action act2 = () => expression2.Evaluate();
Action act3 = () => expression3.Evaluate();
// Assert
act1.Should().Throw<ParserException>().WithMessage("Expected operand; actual: '||' (or)");
act2.Should().Throw<ParserException>().WithMessage("Expected operand; reached end of expression.");
act3.Should().Throw<ParserException>().WithMessage("Expected operand; actual: '||' (or)");
}
[Fact]
public void Missing_Operator_Should_Throw_Exception()
{
// Arrange
var expression = new ExpressionParser("V V");
// Act
Action act = () => expression.Evaluate();
// Assert
act.Should().Throw<ParserException>().WithMessage("Expected operator; actual: 'V' (name)");
}
[Fact]
public void Function_Missing_Paren_Close_Should_Throw_Exception()
{
// Arrange
var expression1 = new ExpressionParser("myfunc(");
var expression2 = new ExpressionParser("myfunc(@");
var expression3 = new ExpressionParser("myfunc(,");
var expression4 = new ExpressionParser("myfunc(V");
var expression5 = new ExpressionParser("myfunc(V,");
var expression6 = new ExpressionParser("myfunc(V,@");
var expression7 = new ExpressionParser("myfunc(V0,V1");
var expression8 = new ExpressionParser("myfunc(V0,V1,");
var expression9 = new ExpressionParser("myfunc(V0,V1,@");
// Act
Action act1 = () => expression1.Evaluate();
Action act2 = () => expression2.Evaluate();
Action act3 = () => expression3.Evaluate();
Action act4 = () => expression4.Evaluate();
Action act5 = () => expression5.Evaluate();
Action act6 = () => expression6.Evaluate();
Action act7 = () => expression7.Evaluate();
Action act8 = () => expression8.Evaluate();
Action act9 = () => expression9.Evaluate();
// Assert
act1.Should().Throw<ParserException>()
.WithMessage("Expected operand or close parenthesis; reached end of expression.");
act2.Should().Throw<ParserException>().WithMessage("Expected operand or close parenthesis; actual: '@' (unknown)");
act3.Should().Throw<ParserException>()
.WithMessage("Expected operand or close parenthesis; actual: ',' (comma)");
act4.Should().Throw<ParserException>()
.WithMessage("Expected comma or close parenthesis; reached end of expression.");
act5.Should().Throw<ParserException>()
.WithMessage("Expected operand or close parenthesis; reached end of expression.");
act6.Should().Throw<ParserException>().WithMessage("Expected operand or close parenthesis; actual: '@' (unknown)");
act7.Should().Throw<ParserException>()
.WithMessage("Expected comma or close parenthesis; reached end of expression.");
act8.Should().Throw<ParserException>()
.WithMessage("Expected operand or close parenthesis; reached end of expression.");
act9.Should().Throw<ParserException>().WithMessage("Expected operand or close parenthesis; actual: '@' (unknown)");
}
[Fact]
public void Unknown_Function_Should_Throw_Exception()
{
// Arrange
var expression = new ExpressionParser("unknownF()");
// Act
Action act = () => expression.Evaluate();
// Assert
act.Should().Throw<ParserException>().WithMessage("Unknown function: 'unknownF' (name)");
}
}
}

Просмотреть файл

@ -1,215 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information.
using System;
using System.Linq;
using FluentAssertions;
using Steeltoe.InitializrApi.Parsers;
using Xunit;
namespace Steeltoe.InitializrApi.Test.Unit.Parsers
{
public class TokenizerTests
{
/* ----------------------------------------------------------------- *
* positive tests *
* ----------------------------------------------------------------- */
[Fact]
public void Empty_String_Should_Return_Zero_Tokens()
{
// Arrange
var expr = string.Empty;
var tokenizer = new Tokenizer();
// Act
var tokens = tokenizer.Scan(expr).ToList();
// Asserty
tokens.Count.Should().Be(0);
}
[Fact]
public void Whitespace_Should_Return_Zero_Tokens()
{
// Arrange
var expr = " \f\n\r\t\v";
var tokenizer = new Tokenizer();
// Act
var tokens = tokenizer.Scan(expr).ToList();
// Assert
tokens.Count.Should().Be(0);
}
[Fact]
public void Scan_Should_Yield_UnknownToken()
{
// Arrange
var expr = "@";
var tokenizer = new Tokenizer();
// Act
var tokens = tokenizer.Scan(expr).ToList();
// Assert
tokens.Count.Should().Be(1);
var token = Assert.IsType<UnknownToken>(tokens[0]);
token.Value.Should().Be('@');
}
[Fact]
public void Scan_Should_Yield_IntegerToken()
{
// Arrange
var expr = "123";
var tokenizer = new Tokenizer();
// Act
var tokens = tokenizer.Scan(expr).ToList();
// Assert
tokens.Count.Should().Be(1);
var token = Assert.IsType<IntegerToken>(tokens[0]);
token.Value.Should().Be(123);
}
[Fact]
public void Scan_Should_Yield_NameToken()
{
// Arrange
var expr = " my-name ";
var tokenizer = new Tokenizer();
// Act
var tokens = tokenizer.Scan(expr).ToList();
// Assert
tokens.Count.Should().Be(1);
var token = Assert.IsType<NameToken>(tokens[0]);
token.Name.Should().Be("my-name");
}
[Fact]
public void Scan_Should_Yield_OrOperatorToken()
{
// Arrange
var expr = "||";
var tokenizer = new Tokenizer();
// Act
var tokens = tokenizer.Scan(expr).ToList();
// Assert
tokens.Count.Should().Be(1);
Assert.IsType<OrOperatorToken>(tokens[0]);
}
[Fact]
public void Scan_Should_Yield_GreaterThanOperatorToken()
{
// Arrange
var expr = ">";
var tokenizer = new Tokenizer();
// Act
var tokens = tokenizer.Scan(expr).ToList();
// Assert
tokens.Count.Should().Be(1);
Assert.IsType<GreaterThanOperatorToken>(tokens[0]);
}
[Fact]
public void Scan_Should_Yield_ParenOpenToken()
{
// Arrange
var expr = "(";
var tokenizer = new Tokenizer();
// Act
var tokens = tokenizer.Scan(expr).ToList();
// Assert
tokens.Count.Should().Be(1);
Assert.IsType<ParenOpenToken>(tokens[0]);
}
[Fact]
public void Scan_Should_Yield_ParenCloseToken()
{
// Arrange
var expr = ")";
var tokenizer = new Tokenizer();
// Act
var tokens = tokenizer.Scan(expr).ToList();
// Assert
tokens.Count.Should().Be(1);
Assert.IsType<ParenCloseToken>(tokens[0]);
}
[Fact]
public void Scan_Should_Yield_CommaToken()
{
// Arrange
var expr = ",";
var tokenizer = new Tokenizer();
// Act
var tokens = tokenizer.Scan(expr).ToList();
// Assert
tokens.Count.Should().Be(1);
Assert.IsType<CommaToken>(tokens[0]);
}
[Fact]
public void Scan_Should_Find_Multiple_Tokens()
{
// Arrange
var expr = "|| MyName";
var tokenizer = new Tokenizer();
// Act
var tokens = tokenizer.Scan(expr).ToList();
// Assert
tokens.Count.Should().Be(2);
Assert.IsType<OrOperatorToken>(tokens[0]);
Assert.IsType<NameToken>(tokens[1]);
}
/* ----------------------------------------------------------------- *
* negative tests *
* ----------------------------------------------------------------- */
[Fact]
public void Incomplete_Or_Operator_Should_Throw_ArgumentException()
{
// Arrange
var expr = "|";
var tokenizer = new Tokenizer();
// Act
Action act = () => tokenizer.Scan(expr);
// Assert
act.Should().Throw<ArgumentException>().WithMessage("Expected: '|'");
}
}
}