201 lines
5.1 KiB
C#
201 lines
5.1 KiB
C#
using System;
|
|
using System.Globalization;
|
|
using System.Text;
|
|
|
|
namespace arookas
|
|
{
|
|
class sunIntLiteral : sunToken<int> // base-10 integer
|
|
{
|
|
public sunIntLiteral(sunSourceLocation location, string token)
|
|
: base(location, token)
|
|
{
|
|
|
|
}
|
|
|
|
protected override int ParseValue(string token) { return Int32.Parse(token); }
|
|
|
|
public override void Compile(sunContext context)
|
|
{
|
|
context.Text.PushInt(Value);
|
|
}
|
|
}
|
|
|
|
class sunHexLiteral : sunIntLiteral // base-16 integer
|
|
{
|
|
public sunHexLiteral(sunSourceLocation location, string token)
|
|
: base(location, token)
|
|
{
|
|
|
|
}
|
|
|
|
protected override int ParseValue(string token)
|
|
{
|
|
// because .NET's hex parsing is gay and doesn't support
|
|
// leading signs, manually detect negative literals
|
|
var neg = (token[0] == '-');
|
|
var trim = neg ? 3 : 2;
|
|
var digits = token.Substring(trim); // trim the '0x' prefix before parsing
|
|
var value = Int32.Parse(token.Substring(2), NumberStyles.AllowHexSpecifier);
|
|
if (neg)
|
|
{
|
|
value = -value;
|
|
}
|
|
return value;
|
|
}
|
|
}
|
|
|
|
class sunFloatLiteral : sunToken<float>
|
|
{
|
|
public sunFloatLiteral(sunSourceLocation location, string token)
|
|
: base(location, token)
|
|
{
|
|
|
|
}
|
|
|
|
protected override float ParseValue(string image) { return Single.Parse(image); }
|
|
|
|
public override void Compile(sunContext context)
|
|
{
|
|
context.Text.PushFloat(Value);
|
|
}
|
|
}
|
|
|
|
class sunStringLiteral : sunToken<string>
|
|
{
|
|
public sunStringLiteral(sunSourceLocation location, string token)
|
|
: base(location, token)
|
|
{
|
|
|
|
}
|
|
|
|
protected override string ParseValue(string image) { return UnescapeString(image.Substring(1, image.Length - 2)); } // remove enclosing quotes
|
|
|
|
public override void Compile(sunContext context)
|
|
{
|
|
context.Text.PushData(context.DataTable.Add(Value));
|
|
}
|
|
|
|
// string unescaping utility
|
|
string UnescapeString(string value)
|
|
{
|
|
// based on Hans Passant's code
|
|
StringBuilder sb = new StringBuilder(value.Length);
|
|
for (int i = 0; i < value.Length;)
|
|
{
|
|
int j = value.IndexOf('\\', i);
|
|
if (j < 0 || j >= value.Length - 1)
|
|
{
|
|
j = value.Length;
|
|
}
|
|
sb.Append(value, i, j - i);
|
|
if (j >= value.Length)
|
|
{
|
|
break;
|
|
}
|
|
switch (value[j + 1])
|
|
{
|
|
case '\'': sb.Append('\''); break;
|
|
case '"': sb.Append('"'); break;
|
|
case '\\': sb.Append('\\'); break;
|
|
case '0': sb.Append('\0'); break;
|
|
case 'a': sb.Append('\a'); break;
|
|
case 'b': sb.Append('\b'); break;
|
|
case 'f': sb.Append('\f'); break;
|
|
case 'n': sb.Append('n'); break;
|
|
case 't': sb.Append('\t'); break;
|
|
case 'v': sb.Append('\v'); break;
|
|
case 'x': sb.Append(UnescapeHex(value, j + 2, out i)); continue;
|
|
case 'u': sb.Append(UnescapeUnicodeCodeUnit(value, j + 2, out i)); continue;
|
|
case 'U': sb.Append(UnescapeUnicodeSurrogatePair(value, j + 2, out i)); continue;
|
|
default: throw new sunEscapeSequenceException(this);
|
|
}
|
|
i = j + 2;
|
|
}
|
|
return sb.ToString();
|
|
}
|
|
char UnescapeHex(string value, int start, out int end)
|
|
{
|
|
if (start > value.Length)
|
|
{
|
|
throw new sunEscapeSequenceException(this); // we need at least one digit
|
|
}
|
|
StringBuilder sb = new StringBuilder(4);
|
|
int digits = 0;
|
|
while (digits < 4 && start < value.Length && IsHexDigit(value[start]))
|
|
{
|
|
sb.Append(value[start]);
|
|
++digits;
|
|
++start;
|
|
}
|
|
end = start;
|
|
return (char)Int32.Parse(sb.ToString(), NumberStyles.AllowHexSpecifier);
|
|
}
|
|
char UnescapeUnicodeCodeUnit(string value, int start, out int end)
|
|
{
|
|
if (start >= value.Length - 4)
|
|
{
|
|
throw new sunEscapeSequenceException(this); // we need four digits
|
|
}
|
|
end = start + 4;
|
|
return (char)Int32.Parse(value.Substring(start, 4), NumberStyles.AllowHexSpecifier);
|
|
}
|
|
string UnescapeUnicodeSurrogatePair(string value, int start, out int end)
|
|
{
|
|
if (start >= value.Length - 8)
|
|
{
|
|
throw new sunEscapeSequenceException(this); // we need eight digits
|
|
}
|
|
char high = (char)Int32.Parse(value.Substring(start, 4), NumberStyles.AllowHexSpecifier);
|
|
char low = (char)Int32.Parse(value.Substring(start + 4, 4), NumberStyles.AllowHexSpecifier);
|
|
if (!Char.IsHighSurrogate(high) || !Char.IsLowSurrogate(low))
|
|
{
|
|
throw new sunEscapeSequenceException(this); // characters are not a surrogate pair
|
|
}
|
|
end = start + 8;
|
|
return String.Concat(high, low);
|
|
}
|
|
static bool IsHexDigit(char c)
|
|
{
|
|
return (c >= '0' && c <= '9') ||
|
|
(c >= 'A' && c <= 'F') ||
|
|
(c >= 'a' && c <= 'f');
|
|
}
|
|
}
|
|
|
|
class sunIdentifier : sunRawToken
|
|
{
|
|
public sunIdentifier(sunSourceLocation location, string token)
|
|
: base(location, token)
|
|
{
|
|
// make sure it is a valid identifier name (i.e. not a keyword)
|
|
if (sunParser.IsKeyword(Value))
|
|
{
|
|
throw new sunIdentifierException(this);
|
|
}
|
|
}
|
|
|
|
// identifiers are compiled on a per-context basis (i.e. at a higher level)
|
|
}
|
|
|
|
class sunTrue : sunIntLiteral
|
|
{
|
|
public sunTrue(sunSourceLocation location, string token)
|
|
: base(location, token)
|
|
{
|
|
|
|
}
|
|
|
|
protected override int ParseValue(string token) { return 1; }
|
|
}
|
|
|
|
class sunFalse : sunIntLiteral
|
|
{
|
|
public sunFalse(sunSourceLocation location, string token)
|
|
: base(location, token)
|
|
{
|
|
|
|
}
|
|
|
|
protected override int ParseValue(string token) { return 0; }
|
|
}
|
|
}
|