-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathTextValue.cs
More file actions
146 lines (121 loc) · 5.01 KB
/
Copy pathTextValue.cs
File metadata and controls
146 lines (121 loc) · 5.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
using System.Text.RegularExpressions;
using SER.Code.Extensions;
using SER.Code.Helpers.ResultSystem;
using SER.Code.ScriptSystem;
using SER.Code.TokenSystem;
using SER.Code.TokenSystem.Slices;
using SER.Code.TokenSystem.Structures;
using SER.Code.TokenSystem.Tokens;
using SER.Code.TokenSystem.Tokens.ExpressionTokens;
using SER.Code.ValueSystem.PropertySystem;
using ValueType = SER.Code.ValueSystem.Other.ValueType;
namespace SER.Code.ValueSystem;
public abstract class TextValue : LiteralValue<string>, IValueWithProperties
{
private static readonly Regex ExpressionRegex = new(
@"~?\{([^{}]|(?<open>\{)|(?<-open>\}))*(?(open)(?!))\}",
RegexOptions.Compiled);
/// <summary>
/// text needs to be dynamically parsed when the value is requested, so script needs to be provided
/// </summary>
/// <param name="text">the text itself</param>
/// <param name="script">script context used to parse formatting, use null when formatting is not applicable</param>
protected TextValue(string text, Script? script) : base(
script is null
? text.Replace("<br>", "\n")
: () => ParseValue(text, script).Replace("<br>", "\n"))
{
}
public static implicit operator string(TextValue value)
{
return value.Value;
}
public override string StringRep => Value;
[UsedImplicitly]
public new static string FriendlyName => "text value";
public static TryGet<ExpressionToken?> ParseExpression(string text, Script script)
{
if (text.StartsWith("~")) return null as ExpressionToken;
if (Tokenizer.SliceLine(text).HasErrored(out var error, out var enumSlices))
{
return error;
}
if (enumSlices.ToArray() is not [CollectionSlice { Type: CollectionBrackets.Curly } collection])
{
return "Parsing failed";
}
// ReSharper disable once DuplicatedSequentialIfBodies
if (ExpressionToken.TryParse(collection, script).HasErrored(out error, out var token))
{
return error;
}
return token;
}
public static bool HasExpression(string text) => ExpressionRegex.IsMatch(text);
public static string ParseValue(string text, Script script) => ExpressionRegex.Replace(text, match =>
{
if (ParseExpression(match.Value, script).HasErrored(out var error, out var token)
|| token is null
|| ((BaseToken)token).TryGet<LiteralValue>().HasErrored(out error, out var value))
{
if (string.IsNullOrEmpty(error)) return match.Value[1..];
script.Warn(error!);
return "[ERROR]";
}
return value.StringRep;
});
public static Result Lint(string text, Script script)
{
foreach (var match in ExpressionRegex.Matches(text).Cast<Match>())
{
if (ParseExpression(match.Value, script).HasErrored(out var error))
{
return error;
}
}
return true;
}
private class Prop<T>(Func<TextValue, T> handler, string? description)
: IValueWithProperties.PropInfo<TextValue, T>(handler, description) where T : Value;
private static Dictionary<string, IValueWithProperties.PropInfo>? _properties;
public Dictionary<string, IValueWithProperties.PropInfo> Properties => _properties ??= new()
{
["length"] = new Prop<NumberValue>(t => t.Value.Length, "Amount of characters in the text"),
["upper"] = new Prop<StaticTextValue>(t => t.Value.ToUpper(), "Upper case of the text"),
["lower"] = new Prop<StaticTextValue>(t => t.Value.ToLowerInvariant(), "Lower case of the text"),
["trim"] = new Prop<StaticTextValue>(t => t.Value.Trim(), "Trimmed text"),
["isEmpty"] = new Prop<BoolValue>(t => string.IsNullOrEmpty(t.Value), "Whether the text is empty"),
["valType"] = new Prop<EnumValue<ValueType>>(_ => ValueType.Text, "The type of the value")
};
public override TryGet<object> ToCSharpObject(Type? targetType)
{
if (targetType is null) return TryGet<object>.Success(Value);
if (targetType.IsInstanceOfType(Value)) return Value;
if (targetType.IsEnum)
{
try { return Enum.Parse(targetType, Value, true); }
catch { return $"Cannot parse '{Value}' as {targetType.Name}"; }
}
return base.ToCSharpObject(targetType);
}
}
[UsedImplicitly]
public class DynamicTextValue(string text, Script script) : TextValue(text, script)
{
[UsedImplicitly]
public DynamicTextValue() : this(string.Empty, null!) {}
[UsedImplicitly]
public new static string FriendlyName => "text value";
}
[UsedImplicitly]
public class StaticTextValue(string text) : TextValue(text, null)
{
[UsedImplicitly]
public StaticTextValue() : this(string.Empty) {}
public static implicit operator StaticTextValue(string text)
{
return new(text);
}
[UsedImplicitly]
public new static string FriendlyName => "text value";
}