mirror of
https://github.com/openbullet/openbullet.git
synced 2023-10-21 07:33:42 -05:00
234 lines
9.2 KiB
C#
234 lines
9.2 KiB
C#
using System;
|
|
using System.Reflection;
|
|
using System.Text.RegularExpressions;
|
|
using System.Windows.Media;
|
|
|
|
namespace RuriLib.LS
|
|
{
|
|
/// <summary>
|
|
/// The allowed types of tokens that can be parsed from a line.
|
|
/// </summary>
|
|
public enum TokenType
|
|
{
|
|
/// <summary>A block label.</summary>
|
|
Label,
|
|
|
|
/// <summary>A generic parameter, usually an enum value.</summary>
|
|
Parameter,
|
|
|
|
/// <summary>A string between double quotes.</summary>
|
|
Literal,
|
|
|
|
/// <summary>The character sequence ->.</summary>
|
|
Arrow,
|
|
|
|
/// <summary>A boolean value in the format Name=Value where Name is the name of the property.</summary>
|
|
Boolean,
|
|
|
|
/// <summary>An integer value.</summary>
|
|
Integer
|
|
}
|
|
|
|
/// <summary>
|
|
/// Contains methods used to parse tokens from a LoliScript line of code.
|
|
/// </summary>
|
|
public static class LineParser
|
|
{
|
|
/// <summary>
|
|
/// Parses a token of a given type from a line.
|
|
/// </summary>
|
|
/// <param name="input">The reference to the line of code</param>
|
|
/// <param name="type">The type of token to parse</param>
|
|
/// <param name="essential">Whether an exception should be thrown if the token cannot be parsed</param>
|
|
/// <param name="proceed">Whether to remove the token from the original line after parsing it</param>
|
|
/// <returns>The parsed token or, if parse fails but the token is not essential, an empty string</returns>
|
|
public static string ParseToken(ref string input, TokenType type, bool essential, bool proceed = true)
|
|
{
|
|
var pattern = GetPattern(type);
|
|
var token = "";
|
|
|
|
Regex r = new Regex(pattern);
|
|
Match m = r.Match(input);
|
|
if (m.Success)
|
|
{
|
|
token = m.Value;
|
|
|
|
if (proceed)
|
|
input = input.Substring(token.Length).Trim();
|
|
|
|
if (type == TokenType.Literal)
|
|
token = token.Substring(1, token.Length - 2).Replace("\\\\", "\\").Replace("\\\"", "\"");
|
|
}
|
|
else
|
|
{
|
|
if (essential)
|
|
{
|
|
throw new ArgumentException("Cannot parse token");
|
|
}
|
|
}
|
|
|
|
return token;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets a boolean property by parsing its name and value from a line.
|
|
/// </summary>
|
|
/// <param name="input">The reference to the line of code</param>
|
|
/// <param name="instance">The instance of the object containing the boolean property</param>
|
|
public static void SetBool(ref string input, object instance)
|
|
{
|
|
var result = ParseToken(ref input, TokenType.Parameter, true, true).Split('=');
|
|
PropertyInfo prop = null;
|
|
try
|
|
{
|
|
prop = instance.GetType().GetProperty(result[0]);
|
|
}
|
|
catch { throw new ArgumentException($"There is no property called {result[0]} in the type {instance.GetType().ToString()}"); }
|
|
var propVal = prop.GetValue(instance);
|
|
if (propVal.GetType() != typeof(bool)) throw new InvalidCastException($"The property {result[0]} is not a boolean");
|
|
|
|
switch (result[1].ToUpper())
|
|
{
|
|
case "TRUE":
|
|
prop.SetValue(instance, true);
|
|
break;
|
|
|
|
case "FALSE":
|
|
prop.SetValue(instance, false);
|
|
break;
|
|
|
|
default:
|
|
throw new ArgumentException($"Expected bool value for '{prop.Name}'");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parses an enum value from a line.
|
|
/// </summary>
|
|
/// <param name="input">The reference to the line of code</param>
|
|
/// <param name="label">Debug information about the expected enum</param>
|
|
/// <param name="enumType">The type of the enum</param>
|
|
/// <returns>An enum of the provided enumType</returns>
|
|
public static dynamic ParseEnum(ref string input, string label, Type enumType)
|
|
{
|
|
// Parse the token first
|
|
var token = "";
|
|
try { token = ParseToken(ref input, TokenType.Parameter, true); }
|
|
catch { throw new ArgumentException($"Missing '{label}'"); }
|
|
try { return Enum.Parse(enumType, token, true); }
|
|
catch { throw new ArgumentException($"Invalid '{label}'"); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parses a literal value from a line.
|
|
/// </summary>
|
|
/// <param name="input">The reference to the line of code</param>
|
|
/// <param name="label">Debug information about the expected literal</param>
|
|
/// <param name="replace">Whether to perform variable replacement in the literal</param>
|
|
/// <param name="data">The BotData needed for variable replacement</param>
|
|
/// <returns>The literal without the leading and trailing double quotes</returns>
|
|
public static string ParseLiteral(ref string input, string label, bool replace = false, BotData data = null)
|
|
{
|
|
try {
|
|
if (replace) return BlockBase.ReplaceValues(ParseToken(ref input, TokenType.Literal, true), data);
|
|
else return ParseToken(ref input, TokenType.Literal, true);
|
|
}
|
|
catch { throw new ArgumentException($"Expected Literal value for '{label}'"); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parses an integer value from a line.
|
|
/// </summary>
|
|
/// <param name="input">The reference to the line of code</param>
|
|
/// <param name="label">Debug information about the expected integer</param>
|
|
/// <returns>The integer value</returns>
|
|
public static int ParseInt(ref string input, string label)
|
|
{
|
|
try { return int.Parse(LineParser.ParseToken(ref input, TokenType.Parameter, true)); }
|
|
catch { throw new ArgumentException($"Expected Integer value for '{label}'"); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parses a block label from a line.
|
|
/// </summary>
|
|
/// <param name="input">The reference to the line of code</param>
|
|
/// <returns>The label of the block, if defined</returns>
|
|
public static string ParseLabel(ref string input)
|
|
{
|
|
return ParseToken(ref input, TokenType.Label, false).Substring(1);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Makes sure that a specified identifier is present and moves past it. An exception will be thrown if the identifier is not present.
|
|
/// </summary>
|
|
/// <param name="input">The reference to the line of code</param>
|
|
/// <param name="id">The expected identifier</param>
|
|
public static void EnsureIdentifier(ref string input, string id)
|
|
{
|
|
var token = ParseToken(ref input, TokenType.Parameter, true);
|
|
if (token.ToUpper() != id.ToUpper())
|
|
throw new ArgumentException($"Expected identifier '{id}'");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the type of the first token in the given line of code.
|
|
/// </summary>
|
|
/// <param name="input">The reference to the line of code</param>
|
|
/// <returns>The type of the token</returns>
|
|
public static TokenType Lookahead(ref string input)
|
|
{
|
|
var i = 0;
|
|
var token = ParseToken(ref input, TokenType.Parameter, true, false);
|
|
if (token.Contains("\"")) return TokenType.Literal;
|
|
else if (token == "->") return TokenType.Arrow;
|
|
else if (token.StartsWith("#")) return TokenType.Label;
|
|
else if (token.ToUpper().Contains("=TRUE") || token.ToUpper().Contains("=FALSE")) return TokenType.Boolean;
|
|
else if (int.TryParse(token, out i)) return TokenType.Integer;
|
|
else return TokenType.Parameter;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks if the next token is a given identifier.
|
|
/// </summary>
|
|
/// <param name="input">The reference to the line of code</param>
|
|
/// <param name="id">The identifier to check</param>
|
|
/// <returns>Whether the token and the identifier are equal</returns>
|
|
public static bool CheckIdentifier(ref string input, string id)
|
|
{
|
|
try
|
|
{
|
|
var token = ParseToken(ref input, TokenType.Parameter, true, false);
|
|
return token.ToUpper() == id.ToUpper();
|
|
}
|
|
catch { return false; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the regex pattern to match a given token type.
|
|
/// </summary>
|
|
/// <param name="type">The token type</param>
|
|
/// <returns>The regex pattern to parse the token type</returns>
|
|
private static string GetPattern(TokenType type)
|
|
{
|
|
switch (type)
|
|
{
|
|
case TokenType.Label:
|
|
return "^#[^ ]*";
|
|
|
|
case TokenType.Parameter:
|
|
return "^[^ ]*";
|
|
|
|
case TokenType.Literal:
|
|
//return "\\\"(\\\\.|[^\\\"])*\\\"";
|
|
return "\"(\\\\.|[^\\\"])*\"";
|
|
|
|
case TokenType.Arrow:
|
|
return "^->";
|
|
|
|
default:
|
|
return "";
|
|
}
|
|
}
|
|
}
|
|
}
|