|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable disable
using System;
using System.Diagnostics;
using System.Xaml.MS.Impl;
namespace MS.Internal.Xaml.Parser
{
internal enum GenericTypeNameScannerToken { NONE, ERROR, OPEN, CLOSE, COLON, COMMA, SUBSCRIPT, NAME }
internal class GenericTypeNameScanner : Sample_StringParserBase
{
internal enum State { START, INNAME, INSUBSCRIPT }
public const char Space = ' ';
public const char OpenParen = '(';
public const char CloseParen = ')';
public const char Comma = ',';
public const char OpenBracket = '[';
public const char CloseBracket = ']';
public const char Colon = ':';
private GenericTypeNameScannerToken _token;
private string _tokenText;
private State _state;
private GenericTypeNameScannerToken _pushedBackSymbol;
private int _multiCharTokenStartIdx;
private int _multiCharTokenLength;
private char _lastChar;
public GenericTypeNameScanner(string text)
:base(text)
{
_state = State.START;
_pushedBackSymbol = GenericTypeNameScannerToken.NONE;
}
public GenericTypeNameScannerToken Token { get { return _token; } }
public string MultiCharTokenText { get { return _tokenText; } }
public char ErrorCurrentChar { get { return _lastChar; } }
public void Read()
{
if (_pushedBackSymbol != GenericTypeNameScannerToken.NONE)
{
_token = _pushedBackSymbol;
_pushedBackSymbol = GenericTypeNameScannerToken.NONE;
return;
}
_token = GenericTypeNameScannerToken.NONE;
_tokenText = string.Empty;
_multiCharTokenStartIdx = -1;
_multiCharTokenLength = 0;
while (_token == GenericTypeNameScannerToken.NONE)
{
if(IsAtEndOfInput)
{
if (_state == State.INNAME)
{
_token = GenericTypeNameScannerToken.NAME;
_state = State.START;
}
if (_state == State.INSUBSCRIPT)
{
_token = GenericTypeNameScannerToken.ERROR;
_state = State.START;
}
break;
}
switch (_state)
{
case State.START:
State_Start();
break;
case State.INNAME:
State_InName();
break;
case State.INSUBSCRIPT:
State_InSubscript();
break;
}
}
if (_token == GenericTypeNameScannerToken.NAME || _token == GenericTypeNameScannerToken.SUBSCRIPT)
{
_tokenText = CollectMultiCharToken();
}
}
// Parse a single subscript (e.g. [] or [,]) at the given position, returning its rank
// Returns 0 if the parse failed
internal static int ParseSubscriptSegment(string subscript, ref int pos)
{
bool openBracketFound = false;
int rank = 1;
do
{
switch (subscript[pos])
{
case OpenBracket:
if (openBracketFound)
{
return 0;
}
openBracketFound = true;
break;
case Comma:
if (!openBracketFound)
{
return 0;
}
rank++;
break;
case CloseBracket:
if (!openBracketFound)
{
return 0;
}
pos++;
return rank;
default:
// Whitespace is allowed inside subscripts
if (!IsWhitespaceChar(subscript[pos]))
{
return 0;
}
break;
}
pos++;
}
while (pos < subscript.Length);
//unterminated string
return 0;
}
// strips the subscript off the end of typeName, and returns it
internal static string StripSubscript(string typeName, out string subscript)
{
int openBracketNdx = typeName.IndexOf(OpenBracket);
if (openBracketNdx < 0)
{
subscript = null;
return typeName;
}
subscript = typeName.Substring(openBracketNdx);
return typeName.Substring(0, openBracketNdx);
}
private void State_Start()
{
AdvanceOverWhitespace();
if (IsAtEndOfInput)
{
_token = GenericTypeNameScannerToken.NONE;
return;
}
switch (CurrentChar)
{
case OpenParen:
_token = GenericTypeNameScannerToken.OPEN;
break;
case CloseParen:
_token = GenericTypeNameScannerToken.CLOSE;
break;
case Comma:
_token = GenericTypeNameScannerToken.COMMA;
break;
case Colon:
_token = GenericTypeNameScannerToken.COLON;
break;
case OpenBracket:
StartMultiCharToken();
_state = State.INSUBSCRIPT;
// No _token set so continue to scan.
break;
default:
if(XamlName.IsValidNameStartChar(CurrentChar))
{
StartMultiCharToken();
_state = State.INNAME;
// No _token set so continue to scan.
}
else
{
_token = GenericTypeNameScannerToken.ERROR;
}
break;
}
_lastChar = CurrentChar;
Advance();
}
private void State_InName()
{
if(IsAtEndOfInput || IsWhitespaceChar(CurrentChar) || CurrentChar == OpenBracket)
{
_token = GenericTypeNameScannerToken.NAME;
_state = State.START;
return;
}
switch(CurrentChar)
{
case OpenParen:
_pushedBackSymbol = GenericTypeNameScannerToken.OPEN;
_token = GenericTypeNameScannerToken.NAME;
_state = State.START;
break;
case CloseParen:
_pushedBackSymbol = GenericTypeNameScannerToken.CLOSE;
_token = GenericTypeNameScannerToken.NAME;
_state = State.START;
break;
case Comma:
_pushedBackSymbol = GenericTypeNameScannerToken.COMMA;
_token = GenericTypeNameScannerToken.NAME;
_state = State.START;
break;
case Colon:
_pushedBackSymbol = GenericTypeNameScannerToken.COLON;
_token = GenericTypeNameScannerToken.NAME;
_state = State.START;
break;
default:
if (XamlName.IsValidQualifiedNameChar(CurrentChar))
{
AddToMultiCharToken();
// No _token set so continue to scan.
}
else
{
_token = GenericTypeNameScannerToken.ERROR;
}
break;
}
_lastChar = CurrentChar;
Advance();
}
private void State_InSubscript()
{
if (IsAtEndOfInput)
{
_token = GenericTypeNameScannerToken.ERROR;
_state = State.START;
return;
}
switch (CurrentChar)
{
case Comma:
AddToMultiCharToken();
break;
case CloseBracket:
AddToMultiCharToken();
_token = GenericTypeNameScannerToken.SUBSCRIPT;
_state = State.START;
break;
default:
if (IsWhitespaceChar(CurrentChar))
{
AddToMultiCharToken();
}
else
{
_token = GenericTypeNameScannerToken.ERROR;
}
break;
}
_lastChar = CurrentChar;
Advance();
}
private void StartMultiCharToken()
{
Debug.Assert(_multiCharTokenStartIdx == -1 && _multiCharTokenLength == 0);
_multiCharTokenStartIdx = _idx;
_multiCharTokenLength = 1;
}
private void AddToMultiCharToken()
{
Debug.Assert(_multiCharTokenStartIdx != -1 && _multiCharTokenLength > 0);
_multiCharTokenLength += 1;
}
private string CollectMultiCharToken()
{
if (_multiCharTokenStartIdx == 0 && _multiCharTokenLength == _inputText.Length)
{
return _inputText;
}
string result = _inputText.Substring(_multiCharTokenStartIdx, _multiCharTokenLength);
return result;
}
}
internal class Sample_StringParserBase
{
protected const char NullChar = '\0';
protected String _inputText;
protected int _idx;
public Sample_StringParserBase(string text)
{
_inputText = text;
_idx = 0;
}
protected char CurrentChar
{
get { return _inputText[_idx]; }
}
public bool IsAtEndOfInput
{
get { return (_idx >= _inputText.Length); }
}
protected bool Advance()
{
++_idx;
if (IsAtEndOfInput)
{
_idx = _inputText.Length;
return false;
}
return true;
}
protected static bool IsWhitespaceChar(char ch)
{
Debug.Assert(KnownStrings.WhitespaceChars.Length == 5);
if (ch == KnownStrings.WhitespaceChars[0] ||
ch == KnownStrings.WhitespaceChars[1] ||
ch == KnownStrings.WhitespaceChars[2] ||
ch == KnownStrings.WhitespaceChars[3] ||
ch == KnownStrings.WhitespaceChars[4])
{
return true;
}
return false;
}
protected bool AdvanceOverWhitespace()
{
bool sawWhitespace = true;
while (!IsAtEndOfInput && IsWhitespaceChar(CurrentChar))
{
sawWhitespace = true;
Advance();
}
return sawWhitespace;
}
}
}
|