using System; using System.Globalization; using System.Text; namespace arookas { class sunIntLiteral : sunToken // 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 { 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 { 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; } } }