286 lines
7.0 KiB
C#
286 lines
7.0 KiB
C#
namespace Ghost.Shader;
|
|
|
|
internal static class TokenStreamImple
|
|
{
|
|
public static Token Peek(ReadOnlySpan<Token> tokens, ref int index, int length)
|
|
{
|
|
return index + length < tokens.Length ? tokens[index + length] : throw new InvalidOperationException("No more tokens available");
|
|
}
|
|
|
|
public static bool TryPeek(ReadOnlySpan<Token> tokens, ref int index, int length, out Token token)
|
|
{
|
|
if (index + length < tokens.Length)
|
|
{
|
|
token = tokens[index + length];
|
|
return true;
|
|
}
|
|
|
|
token = default;
|
|
return false;
|
|
}
|
|
|
|
public static bool TryConsume(ReadOnlySpan<Token> tokens, ref int index, out Token token)
|
|
{
|
|
if (index < tokens.Length)
|
|
{
|
|
token = tokens[index++];
|
|
return true;
|
|
}
|
|
|
|
token = default;
|
|
return false;
|
|
}
|
|
|
|
public static Token Consume(ReadOnlySpan<Token> tokens, ref int index)
|
|
{
|
|
return index < tokens.Length ? tokens[index++] : throw new InvalidOperationException("No more tokens available");
|
|
}
|
|
|
|
public static bool Match(ReadOnlySpan<Token> tokens, ref int index, TokenType type, string? lexeme = null)
|
|
{
|
|
var t = Peek(tokens, ref index, 0);
|
|
if (!t.Match(type, lexeme))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
index++;
|
|
return true;
|
|
}
|
|
|
|
public static Token Expect(ReadOnlySpan<Token> tokens, ref int index, TokenType type, string? lexeme = null)
|
|
{
|
|
if (!TryPeek(tokens, ref index, 0, out var t))
|
|
{
|
|
throw new InvalidOperationException("Expected token but reached end of stream");
|
|
}
|
|
|
|
if (!t.Match(type, lexeme))
|
|
{
|
|
throw new InvalidOperationException($"Expected token {type}('{lexeme ?? "*"}') but got {t}");
|
|
}
|
|
|
|
index++;
|
|
return t;
|
|
}
|
|
}
|
|
|
|
internal class TokenStream
|
|
{
|
|
private readonly Token[] _tokens;
|
|
private int _index = 0;
|
|
|
|
public int Length => _tokens.Length;
|
|
public int Remaining => _tokens.Length - _index;
|
|
public bool HasMore => _index < _tokens.Length;
|
|
public int Position
|
|
{
|
|
get => _index;
|
|
set
|
|
{
|
|
if (value < 0 || value > _tokens.Length)
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(value), "Position must be within the bounds of the token stream.");
|
|
}
|
|
_index = value;
|
|
}
|
|
}
|
|
|
|
public TokenStream(Token[] tokens)
|
|
{
|
|
_tokens = tokens;
|
|
}
|
|
|
|
public Token Peek(int length = 0)
|
|
{
|
|
return TokenStreamImple.Peek(_tokens, ref _index, length);
|
|
}
|
|
|
|
public bool TryPeek(out Token token)
|
|
{
|
|
return TokenStreamImple.TryPeek(_tokens, ref _index, 0, out token);
|
|
}
|
|
|
|
public bool TryPeek(int length, out Token token)
|
|
{
|
|
return TokenStreamImple.TryPeek(_tokens, ref _index, length, out token);
|
|
}
|
|
|
|
public bool TryConsume(out Token token)
|
|
{
|
|
return TokenStreamImple.TryConsume(_tokens, ref _index, out token);
|
|
}
|
|
|
|
public Token Consume()
|
|
{
|
|
return TokenStreamImple.Consume(_tokens, ref _index);
|
|
}
|
|
|
|
public bool Match(TokenType type, string? lexeme = null)
|
|
{
|
|
return TokenStreamImple.Match(_tokens, ref _index, type, lexeme);
|
|
}
|
|
|
|
public Token Expect(TokenType type, string? lexeme = null)
|
|
{
|
|
return TokenStreamImple.Expect(_tokens, ref _index, type, lexeme);
|
|
}
|
|
|
|
public TokenStreamSlice Slice(int length = -1)
|
|
{
|
|
if (length <= 0)
|
|
{
|
|
length = _tokens.Length - _index;
|
|
}
|
|
|
|
var slice = _tokens.AsSpan().Slice(_index, length);
|
|
_index += length;
|
|
return new TokenStreamSlice(slice);
|
|
}
|
|
|
|
public TokenStreamSlice SliceNextBlock()
|
|
{
|
|
var length = 0;
|
|
var lBraceCount = 0;
|
|
var rBraceCount = 0;
|
|
|
|
Token nextToken;
|
|
|
|
do
|
|
{
|
|
nextToken = Peek(length);
|
|
|
|
if (length > 0 && lBraceCount > 0 && lBraceCount == rBraceCount)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (nextToken.Match(TokenType.LBrace))
|
|
{
|
|
lBraceCount++;
|
|
}
|
|
else if (nextToken.Match(TokenType.RBrace))
|
|
{
|
|
rBraceCount++;
|
|
}
|
|
|
|
length++;
|
|
}
|
|
while (_index + length < _tokens.Length);
|
|
|
|
return Slice(length);
|
|
}
|
|
}
|
|
|
|
internal ref struct TokenStreamSlice
|
|
{
|
|
private readonly ReadOnlySpan<Token> _tokens;
|
|
private int _index;
|
|
|
|
public readonly int Length => _tokens.Length;
|
|
public readonly int Remaining => _tokens.Length - _index;
|
|
public readonly bool HasMore => _index < _tokens.Length;
|
|
|
|
public int Position
|
|
{
|
|
readonly get => _index;
|
|
set
|
|
{
|
|
if (value < 0 || value > _tokens.Length)
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(value), "Position must be within the bounds of the token stream.");
|
|
}
|
|
_index = value;
|
|
}
|
|
}
|
|
|
|
internal TokenStreamSlice(ReadOnlySpan<Token> tokens)
|
|
{
|
|
_tokens = tokens;
|
|
_index = 0;
|
|
}
|
|
|
|
public Token Peek(int length = 0)
|
|
{
|
|
return TokenStreamImple.Peek(_tokens, ref _index, length);
|
|
}
|
|
|
|
public bool TryPeek(out Token token)
|
|
{
|
|
return TokenStreamImple.TryPeek(_tokens, ref _index, 0, out token);
|
|
}
|
|
|
|
public bool TryPeek(int length, out Token token)
|
|
{
|
|
return TokenStreamImple.TryPeek(_tokens, ref _index, length, out token);
|
|
}
|
|
|
|
public bool TryConsume(out Token token)
|
|
{
|
|
return TokenStreamImple.TryConsume(_tokens, ref _index, out token);
|
|
}
|
|
|
|
public Token Consume()
|
|
{
|
|
return TokenStreamImple.Consume(_tokens, ref _index);
|
|
}
|
|
|
|
public bool Match(TokenType type, string? lexeme = null)
|
|
{
|
|
return TokenStreamImple.Match(_tokens, ref _index, type, lexeme);
|
|
}
|
|
|
|
public Token Expect(TokenType type, string? lexeme = null)
|
|
{
|
|
return TokenStreamImple.Expect(_tokens, ref _index, type, lexeme);
|
|
}
|
|
|
|
public TokenStreamSlice Slice(int length = -1)
|
|
{
|
|
if (length <= 0)
|
|
{
|
|
length = _tokens.Length - _index;
|
|
}
|
|
|
|
var slice = _tokens.Slice(_index, length);
|
|
_index += length;
|
|
return new TokenStreamSlice(slice);
|
|
}
|
|
|
|
public TokenStreamSlice SliceNextBlock()
|
|
{
|
|
var length = 0;
|
|
var lBraceCount = 0;
|
|
var rBraceCount = 0;
|
|
|
|
do
|
|
{
|
|
var nextToken = Peek(length);
|
|
|
|
if (length > 0 && lBraceCount > 0 && lBraceCount == rBraceCount)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (nextToken.Match(TokenType.LBrace))
|
|
{
|
|
lBraceCount++;
|
|
}
|
|
else if (nextToken.Match(TokenType.RBrace))
|
|
{
|
|
rBraceCount++;
|
|
}
|
|
|
|
length++;
|
|
}
|
|
while (_index + length < _tokens.Length);
|
|
|
|
if (lBraceCount != rBraceCount)
|
|
{
|
|
throw new InvalidOperationException("Unmatched braces in token stream.");
|
|
}
|
|
|
|
return Slice(length);
|
|
}
|
|
}
|