diff --git a/src/OneScript.Core/Commons/Utils.cs b/src/OneScript.Core/Commons/Utils.cs index b110f0713..e64603519 100644 --- a/src/OneScript.Core/Commons/Utils.cs +++ b/src/OneScript.Core/Commons/Utils.cs @@ -35,7 +35,20 @@ public static void ForEach(this IEnumerable input, Action action) action(data); } } - + public static string NameAndValuePresentation(string name, object value) + { + var list = new List(); + if (!string.IsNullOrEmpty(name)) + { + list.Add(name); + } + if (value != null) + { + list.Add(value.ToString()); + } + return string.Join("=", list); + } + public static bool IsMonoRuntime => Type.GetType("Mono.Runtime") != null; } diff --git a/src/OneScript.Core/Contexts/BslAnnotationAttribute.cs b/src/OneScript.Core/Contexts/BslAnnotationAttribute.cs index 446c86a12..eb32971e8 100644 --- a/src/OneScript.Core/Contexts/BslAnnotationAttribute.cs +++ b/src/OneScript.Core/Contexts/BslAnnotationAttribute.cs @@ -5,9 +5,10 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ +using OneScript.Commons; +using OneScript.Values; using System; using System.Collections.Generic; -using OneScript.Values; namespace OneScript.Contexts { @@ -37,7 +38,7 @@ public void SetParameters(IEnumerable parameters) public class BslAnnotationParameter { - public BslAnnotationParameter(string name, BslPrimitiveValue value) + public BslAnnotationParameter(string name, BslPrimitiveValue value = null) { Name = name; Value = value; @@ -47,6 +48,10 @@ public BslAnnotationParameter(string name, BslPrimitiveValue value) public BslPrimitiveValue Value { get; } - public int ConstantValueIndex { get; set; } = -1; + public override string ToString() + { + return Utils.NameAndValuePresentation(Name, Value); + } + } } \ No newline at end of file diff --git a/src/OneScript.Core/Values/BslAnnotationValue.cs b/src/OneScript.Core/Values/BslAnnotationValue.cs new file mode 100644 index 000000000..88b89b997 --- /dev/null +++ b/src/OneScript.Core/Values/BslAnnotationValue.cs @@ -0,0 +1,58 @@ +/*---------------------------------------------------------- +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 OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Localization; +using OneScript.Types; +using System.Collections.Generic; +using System.Text; + +namespace OneScript.Values +{ + public sealed class BslAnnotationValue : BslPrimitiveValue + { + public BslAnnotationValue(string name) { + Name = name; + } + + public string Name { get; } + + public List Parameters { get; } = new List(); + + + public override int CompareTo(BslValue other) { + var msg = new BilingualString("Сравнение на больше/меньше для данного типа не поддерживается", + "Comparison for less/greater is not supported for this type"); + + throw new RuntimeException(msg); + } + + public override bool Equals(BslValue other) { + return ReferenceEquals(this, other); + } + + public override string ToString() + { + var sb = new StringBuilder("&"); + sb.Append(Name); + if (Parameters.Count != 0) + { + var prefix = "("; + foreach (var parameter in Parameters) + { + sb.Append(prefix); + sb.Append(parameter); + prefix = ","; + } + sb.Append(")"); + } + return sb.ToString(); + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/AstNodes/AnnotationParameterNode.cs b/src/OneScript.Language/SyntaxAnalysis/AstNodes/AnnotationParameterNode.cs index 084d29895..4c1669784 100644 --- a/src/OneScript.Language/SyntaxAnalysis/AstNodes/AnnotationParameterNode.cs +++ b/src/OneScript.Language/SyntaxAnalysis/AstNodes/AnnotationParameterNode.cs @@ -17,19 +17,29 @@ public AnnotationParameterNode() : base(NodeKind.AnnotationParameter) protected override void OnChildAdded(BslSyntaxNode child) { - var node = (TerminalNode) child; - if (child.Kind == NodeKind.AnnotationParameterName) + if (child.Kind == NodeKind.Annotation) { - Name = node.Lexem.Content; + AnnotationNode = (AnnotationNode)child; } - if (child.Kind == NodeKind.AnnotationParameterValue) + else { - Value = node.Lexem; + var node = (TerminalNode)child; + if (child.Kind == NodeKind.AnnotationParameterName) + { + Name = node.Lexem.Content; + } + if (child.Kind == NodeKind.AnnotationParameterValue) + { + Value = node.Lexem; + } } } public string Name { get; private set; } public Lexem Value { get; private set; } + + public AnnotationNode AnnotationNode { get; private set; } + } } \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs b/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs index ee986e78c..7f6b980ca 100644 --- a/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs +++ b/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs @@ -541,13 +541,18 @@ private void BuildAnnotations() { while (_lastExtractedLexem.Type == LexemType.Annotation) { - var node = new AnnotationNode(NodeKind.Annotation, _lastExtractedLexem); + var node = BuildAnnotationDefinition(); _annotations.Add(node); - NextLexem(); - BuildAnnotationParameters(node); } } - + private AnnotationNode BuildAnnotationDefinition() { + var node = new AnnotationNode(NodeKind.Annotation, _lastExtractedLexem); + NextLexem(); + BuildAnnotationParameters(node); + return node; + } + + private void BuildAnnotationParameters(AnnotationNode annotation) { if (_lastExtractedLexem.Token != Token.OpenPar) @@ -599,11 +604,16 @@ private void BuildAnnotationParameter(AnnotationNode annotation) private bool BuildAnnotationParamValue(AnnotationParameterNode annotationParam) { + if (_lastExtractedLexem.Type == LexemType.Annotation) { + var annotation = BuildAnnotationDefinition(); + annotationParam.AddChild(annotation); + return true; + } return BuildDefaultParameterValue(annotationParam, NodeKind.AnnotationParameterValue); } - + #endregion - + private void BuildCodeBatch(params Token[] endTokens) { PushStructureToken(endTokens); diff --git a/src/OneScript.Native/Compiler/CompilerHelpers.cs b/src/OneScript.Native/Compiler/CompilerHelpers.cs index 2afe69439..e8cc930a1 100644 --- a/src/OneScript.Native/Compiler/CompilerHelpers.cs +++ b/src/OneScript.Native/Compiler/CompilerHelpers.cs @@ -90,6 +90,16 @@ private static IEnumerable GetAnnotationParameters(Annot private static BslAnnotationParameter MakeAnnotationParameter(AnnotationParameterNode param) { BslAnnotationParameter result; + if (param.AnnotationNode != null) + { + var runtimeValue = new BslAnnotationValue(param.AnnotationNode.Name); + foreach (var child in param.AnnotationNode.Children) + { + runtimeValue.Parameters.Add(MakeAnnotationParameter((AnnotationParameterNode)child)); + } + result = new BslAnnotationParameter(param.Name, runtimeValue); + } + else if (param.Value.Type != LexemType.NotALexem) { var runtimeValue = ValueFromLiteral(param.Value); diff --git a/src/OneScript.StandardLibrary/Reflector.cs b/src/OneScript.StandardLibrary/Reflector.cs index cde26d601..f36dd01e1 100644 --- a/src/OneScript.StandardLibrary/Reflector.cs +++ b/src/OneScript.StandardLibrary/Reflector.cs @@ -163,27 +163,41 @@ private static ValueTable CreateAnnotationTable(BslAnnotationAttribute[] annotat { annotationRow.Set(annotationNameColumn, ValueFactory.Create(annotation.Name)); } - var parametersTable = new ValueTable(); - var parameterNameColumn = parametersTable.Columns.Add("Имя"); - var parameterValueColumn = parametersTable.Columns.Add("Значение"); - + var parametersTable = FillAnnotationParameters(annotation.Parameters); annotationRow.Set(annotationParamsColumn, parametersTable); - if (annotation.Parameters.Any()) - { + } - foreach (var annotationParameter in annotation.Parameters) - { - var parameterRow = parametersTable.Add(); - if (annotationParameter.Name != null) - { - parameterRow.Set(parameterNameColumn, ValueFactory.Create(annotationParameter.Name)); - } - parameterRow.Set(parameterValueColumn, annotationParameter.Value); - } + return annotationsTable; + } + + private static ValueTable FillAnnotationParameters(IEnumerable parameters) + { + var parametersTable = new ValueTable(); + var parameterNameColumn = parametersTable.Columns.Add("Имя"); + var parameterValueColumn = parametersTable.Columns.Add("Значение"); + + foreach (var annotationParameter in parameters) + { + var parameterRow = parametersTable.Add(); + if (annotationParameter.Name != null) + { + parameterRow.Set(parameterNameColumn, ValueFactory.Create(annotationParameter.Name)); + } + if (annotationParameter.Value is BslAnnotationValue annotationValue) + { + var expandedValue = EmptyAnnotationsTable(); + var row = expandedValue.Add(); + row.Set(expandedValue.Columns.FindColumnByName("Имя"), ValueFactory.Create(annotationValue.Name)); + row.Set(expandedValue.Columns.FindColumnByName("Параметры"), FillAnnotationParameters(annotationValue.Parameters)); + parameterRow.Set(parameterValueColumn, row); + } + else + { + parameterRow.Set(parameterValueColumn, annotationParameter.Value); } } - return annotationsTable; + return parametersTable; } private static bool MethodExistsForType(BslTypeValue type, string methodName) diff --git a/src/ScriptEngine/Compiler/StackMachineCodeGenerator.cs b/src/ScriptEngine/Compiler/StackMachineCodeGenerator.cs index b9342f507..6cf990d20 100644 --- a/src/ScriptEngine/Compiler/StackMachineCodeGenerator.cs +++ b/src/ScriptEngine/Compiler/StackMachineCodeGenerator.cs @@ -12,6 +12,7 @@ This Source Code Form is subject to the terms of the using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; +using System.Text; using OneScript.Compilation; using OneScript.Compilation.Binding; using OneScript.Contexts; @@ -1170,23 +1171,35 @@ private IEnumerable GetAnnotationParameters(AnnotationNo private BslAnnotationParameter MakeAnnotationParameter(AnnotationParameterNode param) { - BslAnnotationParameter result; + var runtimeValue = MakeAnnotationParameterValueConstant(param); + return new BslAnnotationParameter(param.Name, runtimeValue); + } + + private BslPrimitiveValue MakeAnnotationParameterValueConstant(AnnotationParameterNode param) + { + if (param.AnnotationNode != null) + { + var runtimeValue = new BslAnnotationValue(param.AnnotationNode.Name); + foreach (var child in param.AnnotationNode.Children) + { + var parameter = (AnnotationParameterNode)child; + var parameterValue = MakeAnnotationParameterValueConstant(parameter); + runtimeValue.Parameters.Add(new BslAnnotationParameter(parameter.Name, parameterValue)); + } + return runtimeValue; + } + else if (param.Value.Type != LexemType.NotALexem) { var constDef = CreateConstDefinition(param.Value); var constNumber = GetConstNumber(constDef); var runtimeValue = _module.Constants[constNumber]; - result = new BslAnnotationParameter(param.Name, runtimeValue) - { - ConstantValueIndex = constNumber - }; + return runtimeValue; } else { - result = new BslAnnotationParameter(param.Name, null); + return null; } - - return result; } private IEnumerable GetAnnotations(AnnotatableNode parent) @@ -1235,7 +1248,7 @@ private static ConstDefinition CreateConstDefinition(in Lexem lex) }; return cDef; } - + private int GetConstNumber(in ConstDefinition cDef) { var idx = _constMap.IndexOf(cDef); diff --git a/src/ScriptEngine/Machine/AnnotationDefinition.cs b/src/ScriptEngine/Machine/AnnotationDefinition.cs new file mode 100644 index 000000000..4a8aa2634 --- /dev/null +++ b/src/ScriptEngine/Machine/AnnotationDefinition.cs @@ -0,0 +1,17 @@ +/*---------------------------------------------------------- +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; + +namespace ScriptEngine.Machine +{ + public struct AnnotationDefinition + { + public string Name; + public AnnotationParameter[] Parameters; + public readonly int ParamCount => Parameters?.Length ?? 0; + } +} diff --git a/src/ScriptEngine/Machine/AnnotationParameter.cs b/src/ScriptEngine/Machine/AnnotationParameter.cs new file mode 100644 index 000000000..2193df9d4 --- /dev/null +++ b/src/ScriptEngine/Machine/AnnotationParameter.cs @@ -0,0 +1,23 @@ +/*---------------------------------------------------------- +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 OneScript.Commons; + +namespace ScriptEngine.Machine +{ + public struct AnnotationParameter + { + public string Name; + + public IValue RuntimeValue; + + public override readonly string ToString() + { + return Utils.NameAndValuePresentation(Name, RuntimeValue); + } + + } +} diff --git a/src/ScriptEngine/Machine/Core.cs b/src/ScriptEngine/Machine/Core.cs index 0403ccffe..a80ebf919 100644 --- a/src/ScriptEngine/Machine/Core.cs +++ b/src/ScriptEngine/Machine/Core.cs @@ -181,38 +181,4 @@ public bool Equals(ConstDefinition other) } } - - [Serializable] - public struct AnnotationDefinition - { - public string Name; - public AnnotationParameter[] Parameters; - - public int ParamCount => Parameters?.Length ?? 0; - } - - [Serializable] - public struct AnnotationParameter - { - public string Name; - public int ValueIndex; - - [NonSerialized] - public IValue RuntimeValue; - - public const int UNDEFINED_VALUE_INDEX = -1; - - public override string ToString() - { - if (string.IsNullOrEmpty(Name)) - { - return string.Format("[{0}]", ValueIndex); - } - if (ValueIndex == UNDEFINED_VALUE_INDEX) - { - return Name; - } - return String.Format("{0}=[{1}]", Name, ValueIndex); - } - } } diff --git a/src/ScriptEngine/Machine/StackRuntimeAdoptionExtensions.cs b/src/ScriptEngine/Machine/StackRuntimeAdoptionExtensions.cs index 3afa430e7..449209845 100644 --- a/src/ScriptEngine/Machine/StackRuntimeAdoptionExtensions.cs +++ b/src/ScriptEngine/Machine/StackRuntimeAdoptionExtensions.cs @@ -52,7 +52,6 @@ public static AnnotationParameter ToMachineDefinition(this BslAnnotationParamete return new AnnotationParameter { Name = parameter.Name, - ValueIndex = parameter.ConstantValueIndex, RuntimeValue = parameter.Value, }; } @@ -82,10 +81,7 @@ public static BslAnnotationAttribute MakeBslAttribute(this in AnnotationDefiniti if (annotation.ParamCount > 0) { attribute.SetParameters(annotation.Parameters.Select(p => - new BslAnnotationParameter(p.Name, (BslPrimitiveValue) p.RuntimeValue) - { - ConstantValueIndex = p.ValueIndex - })); + new BslAnnotationParameter(p.Name, (BslPrimitiveValue) p.RuntimeValue))); } return attribute; diff --git a/src/Tests/OneScript.Core.Tests/CodeGenerationTests.cs b/src/Tests/OneScript.Core.Tests/CodeGenerationTests.cs index ca65c5291..4f6ad55af 100644 --- a/src/Tests/OneScript.Core.Tests/CodeGenerationTests.cs +++ b/src/Tests/OneScript.Core.Tests/CodeGenerationTests.cs @@ -14,8 +14,10 @@ This Source Code Form is subject to the terms of the using OneScript.Language.SyntaxAnalysis; using OneScript.Language.SyntaxAnalysis.AstNodes; using OneScript.Sources; +using OneScript.Values; using ScriptEngine.Compiler; using ScriptEngine.Machine; +using System.Linq; using Xunit; namespace OneScript.Core.Tests @@ -53,6 +55,36 @@ public void Variables_Are_Registered_In_Image() var image = BuildModule(code, Mock.Of()); image.Fields.Should().HaveCount(2); } + + [Fact] + public void AnnotationsAsValuesInCode() { + var code = @" + &Аннотация(Параметр = &ТожеАннотация(&СТожеПараметромАннотацией, П2 = &СТожеПараметромАннотацией)) + Процедура Процедура1() Экспорт + КонецПроцедуры"; + var image = BuildModule(code, Mock.Of()); + image.Should().NotBeNull(); + image.Constants.Should().HaveCount(0); + image.Methods.Should().HaveCount(1); + image.Fields.Should().BeEmpty(); + + var method = image.Methods[0]; + method.GetAnnotations().Should().HaveCount(1); + + var annotation = method.GetAnnotations()[0]; + annotation.Parameters.Should().HaveCount(1); + + var annotationParameter = annotation.Parameters.First(); + annotationParameter.Value.Should().NotBeNull(); + annotationParameter.Value.Should().BeOfType(); + + var parameterValue = (BslAnnotationValue)annotationParameter.Value; + parameterValue.Parameters.Should().HaveCount(2); + parameterValue.Parameters.ElementAt(0).Value.Should().BeOfType(); + parameterValue.Parameters.ElementAt(1).Name.Should().Be("П2"); + parameterValue.Parameters.ElementAt(1).Value.Should().BeOfType(); + } + private static StackRuntimeModule BuildModule(string code, IBslProcess process) { diff --git a/src/Tests/OneScript.Language.Tests/ParserTests.cs b/src/Tests/OneScript.Language.Tests/ParserTests.cs index df02a0f01..23fef46f2 100644 --- a/src/Tests/OneScript.Language.Tests/ParserTests.cs +++ b/src/Tests/OneScript.Language.Tests/ParserTests.cs @@ -144,6 +144,30 @@ public void Check_Annotation_Parameters() } + [Fact] + public void CheckBuild_Of_AnnotationAsValue() + { + var code = @" + &Аннотация(Параметр = &ТожеАннотация(&СТожеПараметромАннотацией, П2 = &СТожеПараметромАннотацией)) + Процедура Процедура1() Экспорт + КонецПроцедуры"; + + var treeValidator = ParseModuleAndGetValidator(code); + + treeValidator.Is(NodeKind.MethodsSection); + + var methodNode = treeValidator.NextChild(); + methodNode.Is(NodeKind.Method); + var annotationNode = methodNode.NextChild(); + annotationNode.Is(NodeKind.Annotation); + var annotationParameter = annotationNode.NextChild(); + annotationParameter.Is(NodeKind.AnnotationParameter); + + annotationParameter + .NextChildIs(NodeKind.AnnotationParameterName) + .NextChildIs(NodeKind.Annotation); + } + [Fact] public void Check_Method_Parameters() { diff --git a/tests/annotations.os b/tests/annotations.os index 7fef57889..a095cf5c5 100644 --- a/tests/annotations.os +++ b/tests/annotations.os @@ -14,10 +14,37 @@ ВсеТесты.Добавить("ТестДолжен_ПроверитьАннотацииПолейЗагрузитьСценарий"); ВсеТесты.Добавить("ТестДолжен_ПроверитьАннотацииПолейЗагрузитьСценарийИзСтроки"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьАннотациюКакЗначениеПараметраАннотации"); + Возврат ВсеТесты; КонецФункции +&Аннотация(Параметр = &ТожеАннотация(&СТожеПараметромАннотацией, П2 = &СТожеПараметромАннотацией)) +Процедура ТестДолжен_ПроверитьАннотациюКакЗначениеПараметраАннотации() Экспорт + + Рефлектор = Новый Рефлектор; + ТаблицаМетодов = Рефлектор.ПолучитьТаблицуМетодов(ЭтотОбъект); + + юТест.ПроверитьНеРавенство(ТаблицаМетодов.Колонки.Найти("Аннотации"), Неопределено, "Есть колонка Аннотации"); + + СтрокаМетода = ТаблицаМетодов.Найти("ТестДолжен_ПроверитьАннотациюКакЗначениеПараметраАннотации", "Имя"); + ПерваяАннотация = СтрокаМетода.Аннотации[0]; + ПервыйПараметрПервойАннотации = ПерваяАннотация.Параметры[0]; + + юТест.ПроверитьТип(ПервыйПараметрПервойАннотации.Значение, Тип("СтрокаТаблицыЗначений")); + + юТест.ПроверитьРавенство(ПервыйПараметрПервойАннотации.Значение.Имя, "ТожеАннотация"); + юТест.ПроверитьТип(ПервыйПараметрПервойАннотации.Значение.Параметры, Тип("ТаблицаЗначений")); + + юТест.ПроверитьТип(ПервыйПараметрПервойАннотации.Значение.Параметры[0].Значение, Тип("СтрокаТаблицыЗначений")); + юТест.ПроверитьРавенство(ПервыйПараметрПервойАннотации.Значение.Параметры[0].Значение.Имя, "СТожеПараметромАннотацией"); + + юТест.ПроверитьРавенство(ПервыйПараметрПервойАннотации.Значение.Параметры[1].Имя, "П2"); + юТест.ПроверитьРавенство(ПервыйПараметрПервойАннотации.Значение.Параметры[1].Значение.Имя, "СТожеПараметромАннотацией"); + +КонецПроцедуры + Процедура САннотированнымиПараметрами( &АннотацияДляПараметра @@ -197,4 +224,4 @@ КонецЕсли; -КонецПроцедуры \ No newline at end of file +КонецПроцедуры