diff --git a/Canon.Tests/Utils.cs b/Canon.Tests/Utils.cs deleted file mode 100644 index 9babb68..0000000 --- a/Canon.Tests/Utils.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Canon.Tests; - -public static class Utils -{ - public static LinkedList GetLinkedList(string content) - { - LinkedList list = []; - - foreach(char c in content) - { - list.AddLast(c); - } - - return list; - } -} diff --git a/Canon.Tests/Utils/StringSourceReader.cs b/Canon.Tests/Utils/StringSourceReader.cs new file mode 100644 index 0000000..f83ea4d --- /dev/null +++ b/Canon.Tests/Utils/StringSourceReader.cs @@ -0,0 +1,47 @@ +using System.Diagnostics.CodeAnalysis; +using Canon.Core.Abstractions; + +namespace Canon.Tests.Utils; + +/// +/// 从字符串中读取源代码 +/// +public sealed class StringSourceReader(string source) : ISourceReader, IDisposable +{ + private readonly IEnumerator _enumerator = + source.GetEnumerator(); + + public uint Line { get; private set; } = 1; + + public uint Pos { get; private set; } + + public string FileName => "string"; + + public bool TryReadChar([NotNullWhen(true)] out char? c) + { + if (Pos != 0 || Line != 1) + { + // 不是第一次读取 + if (_enumerator.Current == '\n') + { + Pos = 0; + Line += 1; + } + } + + if (!_enumerator.MoveNext()) + { + c = null; + return false; + } + + Pos += 1; + c = _enumerator.Current; + return true; + } + + public void Dispose() + { + _enumerator.Dispose(); + } +} diff --git a/Canon.Tests/Utils/StringSourceReaderTests.cs b/Canon.Tests/Utils/StringSourceReaderTests.cs new file mode 100644 index 0000000..21c41fe --- /dev/null +++ b/Canon.Tests/Utils/StringSourceReaderTests.cs @@ -0,0 +1,85 @@ +using Canon.Core.Abstractions; + +namespace Canon.Tests.Utils; + +public class StringSourceReaderTests +{ + [Fact] + public void LineFeedTest() + { + ISourceReader reader = new StringSourceReader("program Main;\nbegin\nend.\n"); + + Assert.Equal(0u, reader.Pos); + Assert.Equal(1u, reader.Line); + + // program + Assert.True(reader.TryReadChar(out char? c)); + Assert.Equal('p', c); + Assert.True(reader.TryReadChar(out char? _)); + Assert.True(reader.TryReadChar(out char? _)); + Assert.True(reader.TryReadChar(out char? _)); + Assert.True(reader.TryReadChar(out char? _)); + Assert.True(reader.TryReadChar(out char? _)); + Assert.True(reader.TryReadChar(out char? _)); + Assert.True(reader.TryReadChar(out c)); + Assert.Equal(' ', c); + + // main; + Assert.True(reader.TryReadChar(out char? _)); + Assert.True(reader.TryReadChar(out char? _)); + Assert.True(reader.TryReadChar(out char? _)); + Assert.True(reader.TryReadChar(out char? _)); + Assert.True(reader.TryReadChar(out char? _)); + + Assert.True(reader.TryReadChar(out c)); + Assert.Equal('\n', c); + + // begin + for (uint i = 1; i <= 5; i++) + { + Assert.True(reader.TryReadChar(out char? _)); + Assert.Equal(i, reader.Pos); + Assert.Equal(2u, reader.Line); + } + + // \n + Assert.True(reader.TryReadChar(out c)); + Assert.Equal('\n', c); + + // end. + foreach (char i in "end.") + { + Assert.True(reader.TryReadChar(out c)); + Assert.Equal(i, c); + } + } + + [Fact] + public void CarriageReturnLineFeedTest() + { + ISourceReader reader = new StringSourceReader("program Main;\r\nbegin\r\nend.\r\n"); + + // program Main; + foreach ((char value, uint index) in + "program Main;".Select((value, index) => (value, (uint)index))) + { + Assert.True(reader.TryReadChar(out char? c)); + Assert.Equal(value, c); + Assert.Equal(index + 1, reader.Pos); + Assert.Equal(1u, reader.Line); + } + + Assert.True(reader.TryReadChar(out _)); + Assert.True(reader.TryReadChar(out _)); + + // begin + foreach ((char value, uint index) in + "begin".Select((value, index) => (value, (uint)index))) + { + Assert.True(reader.TryReadChar(out char? c)); + Assert.Equal(value, c); + Assert.Equal(index + 1, reader.Pos); + Assert.Equal(2u, reader.Line); + } + } +}