-
Notifications
You must be signed in to change notification settings - Fork 119
Expand file tree
/
Copy pathTypeDescription.cs
More file actions
347 lines (295 loc) · 11.7 KB
/
Copy pathTypeDescription.cs
File metadata and controls
347 lines (295 loc) · 11.7 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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
/*----------------------------------------------------------
This Source Code Form is subject to the terms of the
Mozilla Public License, v.2.0. If a copy of the MPL
was not distributed with this file, You can obtain one
at http://mozilla.org/MPL/2.0/.
----------------------------------------------------------*/
using System;
using System.Collections.Generic;
using System.Text;
using OneScript.Contexts;
using OneScript.Exceptions;
using OneScript.StandardLibrary.Collections;
using OneScript.Types;
using OneScript.Values;
using ScriptEngine.Machine;
using ScriptEngine.Machine.Contexts;
namespace OneScript.StandardLibrary.TypeDescriptions
{
[ContextClass("ОписаниеТипов", "TypeDescription")]
public class TypeDescription : AutoContext<TypeDescription>
{
private readonly List<BslTypeValue> _types = new List<BslTypeValue>();
public TypeDescription(IEnumerable<BslTypeValue> types = null)
{
if (types != null)
{
_types.AddRange(types);
}
NumberQualifiers = new NumberQualifiers();
StringQualifiers = new StringQualifiers();
DateQualifiers = new DateQualifiers();
BinaryDataQualifiers = new BinaryDataQualifiers();
}
internal TypeDescription(IEnumerable<BslTypeValue> types,
NumberQualifiers numberQualifiers,
StringQualifiers stringQualifiers,
DateQualifiers dateQualifiers,
BinaryDataQualifiers binaryDataQualifiers)
{
if (types != null)
{
_types.AddRange(types);
}
NumberQualifiers = numberQualifiers;
StringQualifiers = stringQualifiers;
DateQualifiers = dateQualifiers;
BinaryDataQualifiers = binaryDataQualifiers;
}
[ContextProperty("КвалификаторыЧисла", "NumberQualifiers")]
public NumberQualifiers NumberQualifiers { get; }
[ContextProperty("КвалификаторыСтроки", "StringQualifiers")]
public StringQualifiers StringQualifiers { get; }
[ContextProperty("КвалификаторыДаты", "DateQualifiers")]
public DateQualifiers DateQualifiers { get; }
[ContextProperty("КвалификаторыДвоичныхДанных", "BinaryDataQualifiers")]
public BinaryDataQualifiers BinaryDataQualifiers { get; }
[ContextMethod("Типы", "Types")]
public ArrayImpl Types()
{
var result = ArrayImpl.Constructor();
foreach (var type in _types)
{
result.Add(type);
}
return result;
}
internal IEnumerable<BslTypeValue> TypesInternal()
{
return _types;
}
[ContextMethod("СодержитТип", "ContainsType")]
public bool ContainsType(IValue type)
{
if (type is BslTypeValue typeVal)
{
if (typeVal.TypeValue.Equals(BasicTypes.Undefined))
{
// тип "Неопределено" содержится в любом явно определенном составном типе
// и не содержится в типе Произвольный (когда явно не указан состав типов)
// или когда указан один конкретный тип
return (_types.Count > 1);
}
return _types.Contains(typeVal);
}
throw RuntimeException.InvalidArgumentType(nameof(type));
}
private IValueAdjuster GetAdjusterForType(BslTypeValue type)
{
var value = type.TypeValue;
if (value.Equals(BasicTypes.Number))
return NumberQualifiers;
if (value.Equals(BasicTypes.String))
return StringQualifiers;
if (value.Equals(BasicTypes.Date))
return DateQualifiers;
if (value.Equals(BasicTypes.Boolean))
return new BooleanTypeAdjuster();
if (value.Equals(BasicTypes.Undefined))
return new UndefinedTypeAdjuster();
return null;
}
[ContextMethod("ПривестиЗначение", "AdjustValue")]
public IValue AdjustValue(IValue value = null)
{
if (_types.Count == 0)
{
return value ?? ValueFactory.Create();
}
if (value != null)
{
var valueType = new BslTypeValue(value.SystemType);
if (ContainsType(valueType))
{
// Если такой тип у нас есть
var adjuster = GetAdjusterForType(valueType);
var adjustedValue = adjuster?.Adjust(value) ?? value;
return adjustedValue;
}
}
foreach (var type in _types)
{
var adjuster = GetAdjusterForType(type);
var adjustedValue = adjuster?.Adjust(value);
if (adjustedValue != null)
return adjustedValue;
}
return ValueFactory.Create();
}
public static TypeDescription StringType(int length = 0,
AllowedLengthEnum allowedLength = AllowedLengthEnum.Variable)
{
return new TypeDescription(new[] { new BslTypeValue(BasicTypes.String) },
new NumberQualifiers(),
new StringQualifiers(length, allowedLength),
new DateQualifiers(),
new BinaryDataQualifiers());
}
public static TypeDescription IntegerType(int length = 10,
AllowedSignEnum allowedSign = AllowedSignEnum.Any)
{
return new TypeDescription(new[] { new BslTypeValue(BasicTypes.Number) },
new NumberQualifiers(length, 0, allowedSign),
new StringQualifiers(),
new DateQualifiers(),
new BinaryDataQualifiers());
}
public static TypeDescription BooleanType()
{
return new TypeDescription(new[] { new BslTypeValue(BasicTypes.Boolean) },
new NumberQualifiers(),
new StringQualifiers(),
new DateQualifiers(),
new BinaryDataQualifiers());
}
[ScriptConstructor]
public static TypeDescription Constructor(
TypeActivationContext context,
BslValue source = null,
BslValue p2 = null,
BslValue p3 = null,
BslValue p4 = null,
BslValue p5 = null,
BslValue p6 = null,
BslValue p7 = null)
{
var builder = new TypeDescriptionBuilder();
// параметры, которые заведомо не квалификаторы, заменяем на null, но оставляем,
// чтобы указать номер параметра при выводе ошибки несоответствия типа
var qualifiers = new[] { null, p2, p3, p4, p5, p6, p7 };
if (source != null && source.SystemType != BasicTypes.Undefined)
{
if (source is TypeDescription typeDesc)
{
// Если 1 парарметр - ОписаниеТипов, то 2 - добавляемые типы, 3 - убираемые типы,
builder.SourceDescription(typeDesc);
var typesToAdd = CheckAndParseTypeList(context.TypeManager, p2, 2);
var typesToRemove = CheckAndParseTypeList(context.TypeManager, p3, 3);
builder.RemoveTypes(typesToRemove);
builder.AddTypes(typesToAdd);
qualifiers[1] = null; // эти параметры не квалификаторы
qualifiers[2] = null; // эти параметры не квалификаторы
}
else if (source.SystemType == BasicTypes.String || source is ArrayImpl)
{
// Если 1 парарметр - Массив или строка, то это набор конкретных типов
// остальные параметры (2 и далее) - клвалификаторы в произвольном порядке
var typesList = CheckAndParseTypeList(context.TypeManager, source, 1);
builder.AddTypes(typesList);
}
else
throw RuntimeException.InvalidArgumentValue();
} /* else
пустой первый параметр - нет объекта-основания
добавляемые/вычитаемые типы не допускаются, квалификаторы игнорируются
квалификакторы передаются только для контроля типов
*/
CheckAndAddQualifiers(builder, qualifiers);
return builder.Build();
}
/// <summary>
/// Преобразует входящий параметр в список типов.
/// </summary>
/// <param name="types">В качестве типов могут быть переданы Строка или Массив Типов</param>
/// <param name="nParam">Номер параметра, который будет указан в исключении, если параметр typeList задан неверно</param>
/// <exception cref="RuntimeException">Если typeList не может быть разобран как набор типов</exception>
/// <returns>Список переданных типов, приведенный к конкретным TypeTypeValue</returns>
private static List<BslTypeValue> CheckAndParseTypeList(ITypeManager typeManager, IValue types, int nParam)
{
if (types == null || types.SystemType == BasicTypes.Undefined)
return new List<BslTypeValue>();
if (types.SystemType == BasicTypes.String)
{
return FromTypeNames(typeManager, types.ToString());
}
if (types is ArrayImpl arrayOfTypes)
{
return FromArrayOfTypes(arrayOfTypes);
}
throw RuntimeException.InvalidNthArgumentType(nParam);
}
private static List<BslTypeValue> FromTypeNames(ITypeManager typeManager, string types)
{
var typeNames = types.Split(',');
var typesList = new List<BslTypeValue>();
foreach (var typeName in typeNames)
{
if (string.IsNullOrWhiteSpace(typeName))
continue;
var typeValue = new BslTypeValue(typeManager.GetTypeByName(typeName.Trim()));
if (!typesList.Contains(typeValue))
typesList.Add(typeValue);
}
return typesList;
}
private static List<BslTypeValue> FromArrayOfTypes(ArrayImpl arrayOfTypes)
{
var typesList = new List<BslTypeValue>();
foreach (var type in arrayOfTypes)
{
if (type is BslTypeValue rawType)
{
typesList.Add(rawType);
}
}
return typesList;
}
private static void CheckAndAddQualifiers(TypeDescriptionBuilder builder, BslValue[] parameters)
{
for (var i = 0; i < parameters.Length; i++)
{
var rawQualifier = parameters[i];
if (rawQualifier != null && !rawQualifier.Equals(ValueFactory.Create()))
{
CheckAndAddOneQualifier(builder, rawQualifier, i + 1);
}
}
}
/// <summary>
/// Проверяет, что переданный параметр является квалификатором типа.
/// Если тип параметра не является квалификатором, бросает исключение с указанием номера параметра.
/// </summary>
/// <param name="builder">Построитель описания типов, которому будет присвоен квалификатор</param>
/// <param name="qualifier">Проверяемый входящий параметр</param>
/// <param name="nParam">Порядковый номер параметра для выброса исключения</param>
/// <exception cref="RuntimeException">Если qualifier не является квалификатором типа</exception>
private static void CheckAndAddOneQualifier(TypeDescriptionBuilder builder, IValue qualifier, int nParam)
{
switch (qualifier)
{
case NumberQualifiers nq:
builder.SetNumberQualifiers(nq);
break;
case StringQualifiers sq:
builder.SetStringQualifiers(sq);
break;
case DateQualifiers dq:
builder.SetDateQualifiers(dq);
break;
case BinaryDataQualifiers bdq:
builder.SetBinaryDataQualifiers(bdq);
break;
default:
throw RuntimeException.InvalidNthArgumentType(nParam);
}
}
public override string ToString() => string.Join(", ", _types);
}
internal class UndefinedTypeAdjuster : IValueAdjuster
{
public IValue Adjust(IValue value)
{
return ValueFactory.Create();
}
}
}