this repo has no description
at master 7.1 kB view raw
1using GDWeave.Godot; 2using GDWeave.Modding; 3using ScriptTokenizer = Teemaw.Calico.Util.ScriptTokenizer; 4 5namespace Teemaw.Calico.LexicalTransformer; 6 7using MultiTokenPattern = Func<Token, bool>[]; 8 9public enum Operation 10{ 11 /// <summary> 12 /// Do not patch. 13 /// </summary> 14 None, 15 16 /// <summary> 17 /// Replace all tokens of the waiter. This is a buffered operation. Buffered rules do not support overlapping token 18 /// patterns with other buffered rules. 19 /// </summary> 20 ReplaceAll, 21 22 /// <summary> 23 /// Replace the final token of the waiter. 24 /// </summary> 25 ReplaceLast, 26 27 /// <summary> 28 /// Appends after the final token of the waiter. 29 /// </summary> 30 Append, 31 32 /// <summary> 33 /// Prepends before the first token of the waiter. This is a buffered operation. Buffered rules do not support 34 /// overlapping token patterns with other buffered rules. 35 /// </summary> 36 Prepend, 37} 38 39public static class OperationExtensions 40{ 41 public static bool RequiresBuffer(this Operation operation) 42 { 43 return operation is Operation.ReplaceAll or Operation.Prepend; 44 } 45 46 public static bool YieldTokenBeforeOperation(this Operation operation) 47 { 48 return operation is Operation.Append or Operation.None; 49 } 50 51 public static bool YieldTokenAfterOperation(this Operation operation) 52 { 53 return !operation.RequiresBuffer() && !operation.YieldTokenBeforeOperation() && 54 operation != Operation.ReplaceLast; 55 } 56} 57 58/// <summary> 59/// This holds the information required to perform a patch at a single locus. 60/// </summary> 61/// <param name="Name">The name of this descriptor. Used for logging.</param> 62/// <param name="Pattern">A list of checks to be used in a MultiTokenWaiter.</param> 63/// <param name="ScopePattern">A list of checks to be used in a MultiTokenWaiter marking the .</param> 64/// <param name="Tokens">A list of GDScript tokens which will be patched in.</param> 65/// <param name="Operation">The type of patch.</param> 66/// <param name="Times">The number of times this rule is expected to match.</param> 67/// <param name="Predicate">A predicate which must return true for the rule to match.</param> 68public record TransformationRule( 69 string Name, 70 MultiTokenPattern Pattern, 71 MultiTokenPattern ScopePattern, 72 IEnumerable<Token> Tokens, 73 Operation Operation, 74 uint Times, 75 Func<bool> Predicate) 76{ 77 78 public MultiTokenWaiter CreateMultiTokenWaiter() => new(Pattern); 79 80 public MultiTokenWaiter CreateMultiTokenWaiterForScope() => new(ScopePattern); 81} 82 83/// <summary> 84/// Builder for TransformationRule. Times defaults to 1. Operation defaults to <see cref="Operation.Append"/>. 85/// </summary> 86public class TransformationRuleBuilder 87{ 88 private string? _name; 89 private MultiTokenPattern? _pattern; 90 private MultiTokenPattern _scopePattern = []; 91 private IEnumerable<Token>? _tokens; 92 private uint _times = 1; 93 private Operation _operation = Operation.Append; 94 private Func<bool> _predicate = () => true; 95 96 /// <summary> 97 /// Sets the name for the TransformationRule. Used for logging. 98 /// </summary> 99 /// <param name="name"></param> 100 /// <returns></returns> 101 public TransformationRuleBuilder Named(string name) 102 { 103 _name = name; 104 return this; 105 } 106 107 /// <summary> 108 /// Sets the token pattern which will be matched by the TransformationRule. 109 /// </summary> 110 /// <param name="pattern"></param> 111 /// <returns></returns> 112 public TransformationRuleBuilder Matching(MultiTokenPattern pattern) 113 { 114 _pattern = pattern; 115 return this; 116 } 117 118 /// <summary> 119 /// Sets the token content which will be patched in for the TransformationRule. 120 /// </summary> 121 /// <param name="tokens"></param> 122 /// <returns></returns> 123 public TransformationRuleBuilder With(IEnumerable<Token> tokens) 124 { 125 _tokens = tokens; 126 return this; 127 } 128 129 /// <summary> 130 /// Sets the token content which will be patched in for the TransformationRule. 131 /// </summary> 132 /// <param name="token"></param> 133 /// <returns></returns> 134 public TransformationRuleBuilder With(Token token) 135 { 136 _tokens = [token]; 137 return this; 138 } 139 140 /// <summary> 141 /// Sets the token content which will be patched in for the TransformationRule with a GDScript snippet. 142 /// </summary> 143 /// <param name="snippet"></param> 144 /// <param name="indent">The base indentation level for the tokenizer.</param> 145 /// <returns></returns> 146 public TransformationRuleBuilder With(string snippet, uint indent = 0) 147 { 148 _tokens = ScriptTokenizer.Tokenize(snippet, indent); 149 return this; 150 } 151 152 /// <summary> 153 /// Sets the <see cref="Operation"/> of the TransformationRule. 154 /// </summary> 155 /// <param name="operation"></param> 156 /// <returns></returns> 157 public TransformationRuleBuilder Do(Operation operation) 158 { 159 _operation = operation; 160 return this; 161 } 162 163 /// <summary> 164 /// Sets the number of times the rule is expected to match. 165 /// </summary> 166 /// <param name="times"></param> 167 /// <returns></returns> 168 public TransformationRuleBuilder ExpectTimes(uint times) 169 { 170 _times = times; 171 return this; 172 } 173 174 /// <summary> 175 /// Sets the scope of this rule. 176 /// </summary> 177 /// <param name="scopePattern"></param> 178 /// <returns></returns> 179 public TransformationRuleBuilder ScopedTo(MultiTokenPattern scopePattern) 180 { 181 _scopePattern = scopePattern; 182 return this; 183 } 184 185 /// <summary> 186 /// Sets the predicate function whose return value will decide if this rule will be checked. 187 /// </summary> 188 /// <param name="predicate"></param> 189 /// <returns></returns> 190 public TransformationRuleBuilder When(Func<bool> predicate) 191 { 192 _predicate = predicate; 193 return this; 194 } 195 196 /// <summary> 197 /// Sets a value which will decide if this rule will be checked. 198 /// </summary> 199 /// <param name="eligible">If true, this rule will be checked.</param> 200 /// <returns></returns> 201 public TransformationRuleBuilder When(bool eligible) 202 { 203 _predicate = () => eligible; 204 return this; 205 } 206 207 /// <summary> 208 /// Builds the TransformationRule. 209 /// </summary> 210 /// <returns></returns> 211 /// <exception cref="ArgumentNullException">Thrown if any required fields were not set.</exception> 212 public TransformationRule Build() 213 { 214 if (string.IsNullOrEmpty(_name)) 215 { 216 throw new ArgumentNullException(nameof(_name), "Name cannot be null or empty"); 217 } 218 219 if (_pattern == null) 220 { 221 throw new ArgumentNullException(nameof(_pattern), "Pattern cannot be null"); 222 } 223 224 if (_tokens == null) 225 { 226 throw new ArgumentNullException(nameof(_tokens), "Tokens cannot be null"); 227 } 228 229 return new TransformationRule(_name, _pattern, _scopePattern, _tokens, _operation, _times, _predicate); 230 } 231}