#if UNITY_EDITOR || BURST_INTERNAL
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace Unity.Burst.Editor
{
internal partial class BurstDisassembler
{
///
/// Base class for providing extended information of an identifier
///
private abstract class AsmTokenKindProvider
{
// Internally using string slice instead of string
// to support faster lookup from AsmToken
private readonly Dictionary _tokenKinds;
private int _maximumLength;
protected AsmTokenKindProvider(int capacity)
{
_tokenKinds = new Dictionary(capacity);
}
protected void AddTokenKind(string text, AsmTokenKind kind)
{
_tokenKinds.Add(new StringSlice(text), kind);
if (text.Length > _maximumLength) _maximumLength = text.Length;
}
public AsmTokenKind FindTokenKind(StringSlice slice)
{
return slice.Length <= _maximumLength && _tokenKinds.TryGetValue(slice, out var tokenKind) ? tokenKind : AsmTokenKind.Identifier;
}
public virtual bool AcceptsCharAsIdentifierOrRegisterEnd(char c)
{
return false;
}
}
///
/// The ASM tokenizer
///
private struct AsmTokenizer
{
private readonly string _text;
private readonly AsmKind _asmKind;
private readonly AsmTokenKindProvider _tokenKindProvider;
private int _position;
private int _nextPosition;
private char _c;
private readonly char _commentStartChar;
public AsmTokenizer(string text, AsmKind asmKind, AsmTokenKindProvider tokenKindProvider)
{
_text = text;
_asmKind = asmKind;
_tokenKindProvider = tokenKindProvider;
_position = 0;
_nextPosition = 0;
_commentStartChar = (asmKind == AsmKind.Intel || asmKind == AsmKind.Wasm) ? '#' : ';';
_c = (char)0;
NextChar();
}
public bool TryGetNextToken(out AsmToken token)
{
token = new AsmToken();
while (true)
{
var startPosition = _position;
if (_c == 0)
{
return false;
}
if (_c == '.')
{
token = ParseDirective(startPosition);
return true;
}
// Like everywhere else in this file, we are inlining the matching characters instead
// of using helper functions, as Mono might not be enough good at inlining by itself
if (_c >= 'a' && _c <= 'z' || _c >= 'A' && _c <= 'Z' || _c == '_' || _c == '@')
{
token = ParseInstructionOrIdentifierOrRegister(startPosition);
return true;
}
if (_c >= '0' && _c <= '9' || _c == '-')
{
token = ParseNumber(startPosition);
return true;
}
if (_c == '"')
{
token = ParseString(startPosition);
return true;
}
if (_c == _commentStartChar)
{
token = ParseComment(startPosition);
return true;
}
if (_c == '\r')
{
if (PreviewChar() == '\n')
{
NextChar(); // skip \r
}
token = ParseNewLine(startPosition);
return true;
}
if (_c == '\n')
{
token = ParseNewLine(startPosition);
return true;
}
token = ParseMisc(startPosition);
return true;
}
}
private AsmToken ParseNewLine(int startPosition)
{
var endPosition = _position;
NextChar(); // Skip newline
return new AsmToken(AsmTokenKind.NewLine, startPosition, endPosition - startPosition + 1);
}
private AsmToken ParseMisc(int startPosition)
{
var endPosition = _position;
// Parse anything that is not a directive, instruction, number, string or comment
while (!((_c == (char)0) || (_c == '\r') || (_c == '\n') || (_c == '.') || (_c >= 'a' && _c <= 'z' || _c >= 'A' && _c <= 'Z' || _c == '_' || _c == '@') || (_c >= '0' && _c <= '9' || _c == '-') || (_c == '"') || (_c == _commentStartChar)))
{
endPosition = _position;
NextChar();
}
return new AsmToken(AsmTokenKind.Misc, startPosition, endPosition - startPosition + 1);
}
private AsmToken ParseDirective(int startPosition)
{
var endPosition = _position;
while (_c >= 'a' && _c <= 'z' || _c >= 'A' && _c <= 'Z' || _c >= '0' && _c <= '9' || _c == '.' || _c == '_' || _c == '@')
{
endPosition = _position;
NextChar();
}
return new AsmToken(AsmTokenKind.Directive, startPosition, endPosition - startPosition + 1);
}
private AsmToken ParseInstructionOrIdentifierOrRegister(int startPosition)
{
var endPosition = _position;
while (_c >= 'a' && _c <= 'z' || _c >= 'A' && _c <= 'Z' || _c >= '0' && _c <= '9' || _c == '_' || _c == '@')
{
endPosition = _position;
NextChar();
}
if (_tokenKindProvider.AcceptsCharAsIdentifierOrRegisterEnd(_c))
{
endPosition = _position;
NextChar();
}
// Resolve token kind for identifier
int length = endPosition - startPosition + 1;
var tokenKind = _tokenKindProvider.FindTokenKind(new StringSlice(_text, startPosition, length));
return new AsmToken(tokenKind, startPosition, endPosition - startPosition + 1);
}
private AsmToken ParseNumber(int startPosition)
{
var endPosition = _position;
if (_c == '-')
{
NextChar();
}
while (_c >= '0' && _c <= '9' || _c >= 'a' && _c <= 'f' || _c >= 'A' && _c <= 'F' || _c == 'x' || _c == '.')
{
endPosition = _position;
NextChar();
}
return new AsmToken(AsmTokenKind.Number, startPosition, endPosition - startPosition + 1);
}
private AsmToken ParseString(int startPosition)
{
var endPosition = _position;
// Skip first "
NextChar();
while (_c != (char)0 && _c != '"')
{
// Skip escape \"
if (_c == '\\' && PreviewChar() == '"')
{
NextChar();
}
endPosition = _position;
NextChar();
}
endPosition = _position;
NextChar(); // Skip trailing 0
return new AsmToken(AsmTokenKind.String, startPosition, endPosition - startPosition + 1);
}
private AsmToken ParseComment(int startPosition)
{
var endPosition = _position;
while (_c != (char)0 && (_c != '\n' && _c != '\r'))
{
endPosition = _position;
NextChar();
}
return new AsmToken(AsmTokenKind.Comment, startPosition, endPosition - startPosition + 1);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void NextChar()
{
if (_nextPosition < _text.Length)
{
_position = _nextPosition;
_c = _text[_position];
_nextPosition = _position + 1;
}
else
{
_c = (char)0;
}
}
private char PreviewChar()
{
return _nextPosition < _text.Length ? _text[_nextPosition] : (char)0;
}
}
///
/// A slice of a string from an original string.
///
public struct StringSlice : IEquatable
{
private readonly string _text;
public readonly int Position;
public readonly int Length;
public StringSlice(string text)
{
_text = text ?? throw new ArgumentNullException(nameof(text));
Position = 0;
Length = text.Length;
}
public StringSlice(string text, int position, int length)
{
_text = text ?? throw new ArgumentNullException(nameof(text));
Position = position;
Length = length;
}
public char this[int index] => _text[Position + index];
public bool Equals(StringSlice other)
{
if (Length != other.Length) return false;
for (int i = 0; i < Length; i++)
{
if (this[i] != other[i])
{
return false;
}
}
return true;
}
public override bool Equals(object obj)
{
return obj is StringSlice other && Equals(other);
}
public override int GetHashCode()
{
unchecked
{
var hashCode = Length;
for (int i = 0; i < Length; i++)
{
hashCode = (hashCode * 397) ^ this[i];
}
return hashCode;
}
}
public static bool operator ==(StringSlice left, StringSlice right)
{
return left.Equals(right);
}
public static bool operator !=(StringSlice left, StringSlice right)
{
return !left.Equals(right);
}
public override string ToString()
{
return _text.Substring(Position, Length);
}
}
///
/// An ASM token. The token doesn't contain the string of the token, but provides method and to extract it.
///
internal struct AsmToken
{
public AsmToken(AsmTokenKind kind, int position, int length)
{
Kind = kind;
Position = position;
Length = length;
}
public readonly AsmTokenKind Kind;
public readonly int Position;
public readonly int Length;
public StringSlice Slice(string text) => new StringSlice(text, Position, Length);
public string ToString(string text) => text.Substring(Position, Length);
public string ToFriendlyText(string text)
{
return $"{text.Substring(Position, Length)} : {Kind}";
}
}
///
/// Kind of an ASM token.
///
internal enum AsmTokenKind
{
Eof,
Directive,
Identifier,
Qualifier,
Instruction,
InstructionSIMD,
Register,
Number,
String,
Comment,
NewLine,
Misc
}
}
}
#endif