ssc/ast/nodes.literals.cs
2015-12-06 23:15:02 -05:00

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; }
}
}