372 lines
17 KiB
C#
Raw Normal View History

// ILTokenizer.cs
// Author: Sergey Chaban (serge@wildwestsoftware.com)
using System;
using System.IO;
using System.Text;
using System.Collections;
using System.Globalization;
namespace Mono.ILASM {
public delegate void NewTokenEvent (object sender, NewTokenEventArgs args);
public class NewTokenEventArgs : EventArgs {
public readonly ILToken Token;
public NewTokenEventArgs (ILToken token)
{
Token = token;
}
}
/// <summary>
/// </summary>
public class ILTokenizer : ITokenStream {
private static readonly string idchars = "_$@?.`";
private static Hashtable keywords;
private static Hashtable directives;
private ILToken lastToken;
private ILReader reader;
private StringHelper strBuilder;
private NumberHelper numBuilder;
private bool in_byte_array;
public event NewTokenEvent NewTokenEvent;
static ILTokenizer()
{
keywords = ILTables.Keywords;
directives = ILTables.Directives;
}
/// <summary>
/// </summary>
/// <param name="reader"></param>
public ILTokenizer (StreamReader reader)
{
this.reader = new ILReader (reader);
strBuilder = new StringHelper (this);
numBuilder = new NumberHelper (this);
lastToken = ILToken.Invalid.Clone () as ILToken;
}
public ILReader Reader {
get {
return reader;
}
}
public Location Location {
get {
return reader.Location;
}
}
public bool InByteArray {
get { return in_byte_array; }
set { in_byte_array = value; }
}
public ILToken GetNextToken ()
{
if (lastToken == ILToken.EOF) return ILToken.EOF;
int ch;
int next;
ILToken res = ILToken.EOF.Clone () as ILToken;
while ((ch = reader.Read ()) != -1) {
// Comments
if (ch == '/') {
next = reader.Peek ();
if (next == '/') {
// double-slash comment, skip to the end of the line.
for (reader.Read ();
next != -1 && next != '\n';
next = reader.Read ());
continue;
} else if (next == '*') {
reader.Read ();
for (next = reader.Read (); next != -1; next = reader.Read ()) {
if (next == '*' && reader.Peek () == '/') {
reader.Read ();
goto end;
}
}
end:
continue;
}
}
// HEXBYTES are flagged by the parser otherwise it is
// impossible to figure them out
if (in_byte_array) {
string hx = String.Empty;
if (Char.IsWhiteSpace ((char) ch))
continue;
if (ch == ')') {
res = ILToken.CloseParens;
break;
}
if (!is_hex (ch))
throw new ILTokenizingException (reader.Location, ((char) ch).ToString ());
hx += (char) ch;
if (is_hex (reader.Peek ()))
hx += (char) reader.Read ();
else if (!Char.IsWhiteSpace ((char) reader.Peek ()) && reader.Peek () != ')')
throw new ILTokenizingException (reader.Location,
((char) reader.Peek ()).ToString ());
res.token = Token.HEXBYTE;
res.val = Byte.Parse (hx, NumberStyles.HexNumber);
while (Char.IsWhiteSpace ((char) reader.Peek ()))
reader.Read ();
break;
}
// Ellipsis
if (ch == '.' && reader.Peek () == '.') {
reader.MarkLocation ();
int ch2 = reader.Read ();
if (reader.Peek () == '.') {
res = ILToken.Ellipsis;
reader.Read ();
break;
}
reader.Unread (ch2);
reader.RestoreLocation ();
}
if (ch == '.' || ch == '#') {
next = reader.Peek ();
if (ch == '.' && Char.IsDigit((char) next)) {
numBuilder.Start (ch);
reader.Unread (ch);
numBuilder.Build ();
if (numBuilder.ResultToken != ILToken.Invalid) {
res.CopyFrom (numBuilder.ResultToken);
break;
}
} else {
if (strBuilder.Start (next) && strBuilder.TokenId == Token.ID) {
reader.MarkLocation ();
string dirBody = strBuilder.Build ();
string dir = new string ((char) ch, 1) + dirBody;
if (IsDirective (dir)) {
res = ILTables.Directives [dir] as ILToken;
} else {
reader.Unread (dirBody.ToCharArray ());
reader.RestoreLocation ();
res = ILToken.Dot;
}
} else {
res = ILToken.Dot;
}
break;
}
}
// Numbers && Hexbytes
if (numBuilder.Start (ch)) {
if ((ch == '-') && !(Char.IsDigit ((char) reader.Peek ()))) {
res = ILToken.Dash;
break;
} else {
reader.Unread (ch);
numBuilder.Build ();
if (numBuilder.ResultToken != ILToken.Invalid) {
res.CopyFrom (numBuilder.ResultToken);
break;
}
}
}
// Punctuation
ILToken punct = ILToken.GetPunctuation (ch);
if (punct != null) {
if (punct == ILToken.Colon && reader.Peek () == ':') {
reader.Read ();
res = ILToken.DoubleColon;
} else {
res = punct;
}
break;
}
// ID | QSTRING | SQSTRING | INSTR_* | KEYWORD
if (strBuilder.Start (ch)) {
reader.Unread (ch);
string val = strBuilder.Build ();
if (strBuilder.TokenId == Token.ID) {
ILToken opcode;
next = reader.Peek ();
if (next == '.') {
reader.MarkLocation ();
reader.Read ();
next = reader.Peek ();
if (IsIdChar ((char) next)) {
string opTail = BuildId ();
string full_str = String.Format ("{0}.{1}", val, opTail);
opcode = InstrTable.GetToken (full_str);
if (opcode == null) {
if (strBuilder.TokenId != Token.ID) {
reader.Unread (opTail.ToCharArray ());
reader.Unread ('.');
reader.RestoreLocation ();
res.val = val;
} else {
res.token = Token.COMP_NAME;
res.val = full_str;
}
break;
} else {
res = opcode;
break;
}
} else if (Char.IsWhiteSpace ((char) next)) {
// Handle 'tail.' and 'unaligned.'
opcode = InstrTable.GetToken (val + ".");
if (opcode != null) {
res = opcode;
break;
}
// Let the parser handle the dot
reader.Unread ('.');
}
}
opcode = InstrTable.GetToken (val);
if (opcode != null) {
res = opcode;
break;
}
if (IsKeyword (val)) {
res = ILTables.Keywords [val] as ILToken;
break;
}
}
res.token = strBuilder.TokenId;
res.val = val;
break;
}
}
OnNewToken (res);
lastToken.CopyFrom (res);
return res;
}
/// <summary>
/// </summary>
public ILToken NextToken {
get {
return GetNextToken ();
}
}
/// <summary>
/// </summary>
public ILToken LastToken {
get {
return lastToken;
}
}
bool is_hex (int e)
{
return (e >= '0' && e <= '9') || (e >= 'A' && e <= 'F') || (e >= 'a' && e <= 'f');
}
private static bool IsIdStartChar (char ch)
{
return (Char.IsLetter (ch) || (idchars.IndexOf (ch) != -1));
}
private static bool IsIdChar (char ch)
{
return (Char.IsLetterOrDigit (ch) || (idchars.IndexOf (ch) != -1));
}
/// <summary>
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public static bool IsOpcode (string name)
{
return InstrTable.IsInstr (name);
}
/// <summary>
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public static bool IsDirective (string name)
{
char ch = name [0];
bool res = (ch == '.' || ch == '#');
if (res) {
res = directives.Contains (name);
}
return res;
}
private string BuildId ()
{
StringBuilder idsb = new StringBuilder ();
int ch, last;
last = -1;
while ((ch = reader.Read ()) != -1) {
if (IsIdChar ((char) ch) || ch == '.') {
idsb.Append ((char) ch);
} else {
reader.Unread (ch);
// Never end an id on a DOT
if (last == '.') {
reader.Unread (last);
idsb.Length -= 1;
}
break;
}
last = ch;
}
return idsb.ToString ();
}
/// <summary>
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public static bool IsKeyword (string name)
{
return keywords.Contains (name);
}
private void OnNewToken (ILToken token)
{
if (NewTokenEvent != null)
NewTokenEvent (this, new NewTokenEventArgs (token));
}
}
}