📜 ⬆️ ⬇️

Roslyn to automatically translate C # code into 1C code

An idea appeared to see how an object-oriented approach will look like in 1C, the language of which is very limited in means and does not provide for the definition of classes. A program to automatically translate C # class definitions into another language would allow the generated code to be changed as new ideas appear. The search for implementation tools led to the Roslyn project - the open source C # compiler.

Roslyn is an open source compilation platform for C # and Visual Basic. Roslyn performs two basic actions: it builds a syntax tree (parsing) and compiles a syntax tree. Additionally, it allows analyzing source code, recursively bypassing it, working with Visual Studio projects, and executing code on the fly.

Note that Roslyn is currently in Beta. Based on this, over time, the compiler may change something.

')

Roslyn - open C # compiler



You can connect Roslyn to the project via Nuget:
Install-Package Microsoft.CodeAnalysis –Pre 

For convenience, the code is better to immediately connect the three namespaces
 using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis; 

You can get the syntax tree of a code from a string (or file) like this:
 SyntaxTree tree = CSharpSyntaxTree.ParseText(codeString); 

The syntax tree is a hierarchy of objects inherited from SyntaxNode. Objects are created for all occasions. Examples: ClassDeclarationSyntax - class definition, NamespaceDeclarationSyntax - namespace definition, PropertyDeclarationSyntax - property definition, AccessorDeclarationSyntax - property access method definition (get / set), BlockSyntax - block content (between curly brackets), ExpressionStatementSyntax - expression, etc.

If there is a task to recursively go through all the elements of the tree, you can create your own Walker class and inherit it from CSharpSyntaxWalker. The base class allows you to override the common Visit method (SyntaxNode node) or a large number of specialized ones: void VisitNamespaceDeclaration (NamespaceDeclarationSyntax node), void VisitClassDeclaration (ClassDeclarationSyntax node), void VisitConstructorDeclaration (ConstructorDeclarationSyntax node), etc. Do not forget to call the base method in each redefined method in order not to stop the recursion.

The recursive traversal call can be started as follows:
 var walker = new Walker(); walker.Visit(tree.GetRoot()); 

There is no type information in the syntax tree. Information about the types used appears after the call:
 var compilation = CSharpCompilation.Create("1ccode").WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)).AddReferences(new MetadataFileReference(typeof(object).Assembly.Location)).AddSyntaxTrees(tree); var Model = compilation.GetSemanticModel(tree); 

After this call, you can get information about the types used, for example, by calling
 var classSymbol = Model.GetDeclaredSymbol(classDeclarationSyntax); 

Now, having information about the type, you can find out which class the given type inherited:
 var type = type.BaseType; 

Or get all type members via type. GetMembers ()

Automatic translation of C # code to 1C code



The code does not claim to be complete and correct, as it has the goal to get a general idea of ​​the OOP approach in 1C.

To translate the C # code into the 1C code, a Walker class was created, inherited from CSharpSyntaxWalker. Walker enumerates all definitions and builds on output 1C code.

The class performs the following transformations.

The namespace is translated by the VisitNamespaceDeclaration method into the 1C module, where dots in the name are replaced with underscores.

There is no concept class in 1C, so the class definition is skipped in the VisitClassDeclaration method. The name of the class will be present in the name of each function and procedure 1C to denote belonging to the same type. The methods present in the base classes, but missing in the current class through the DeclareBaseClassMethodsToImplement and the DeclareBaseClassPropertiesToImplement, are defined with the call of the “basic” functions / procedures 1C.

Constructors in VisitConstructorDeclaration are translated into definitions of 1C functions with a class name, the first _this parameter and a list of parameters. If there is no call to another constructor of this class, all the fields of the class in the structure are initialized. The call of other constructors is determined.

Property definitions in VisitPropertyDeclaration are skipped. Definitions of their access methods are important.

Methods for accessing properties in VisitAccessorDeclaration are translated into definitions with the names <class name> _ Get_ <property name> and <class name> _ Set_ <property name>. If they are auto-implemented, then the access code for the variable _this._private_ <class name> _ <property name> is generated.

For methods in VisitMethodDeclaration, 1C-procedure definitions are generated.

Expressions and “returns” in VisitExpressionStatement and VisitReturnStatement are commented via // and inserted into the text as is.

Walker.cs source code
 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.IO; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis; namespace Roslyn { public class Walker : CSharpSyntaxWalker { SyntaxTree Tree { get; set; } CSharpCompilation Compilation { get; set; } SemanticModel Model { get; set; } TextWriter Writer { get; set; } public Walker(TextWriter writer, SyntaxTree tree, CSharpCompilation compilation) : base() { Writer = writer; Tree = tree; Compilation = compilation; Model = Compilation.GetSemanticModel(tree); } Dictionary<ClassDeclarationSyntax, FieldDeclarationSyntax[]> _classFields = new Dictionary<ClassDeclarationSyntax, FieldDeclarationSyntax[]>(); NamespaceDeclarationSyntax _currentNamespace; ClassDeclarationSyntax _currentClass; PropertyDeclarationSyntax _currentProperty; private int Tabs = 0; public override void Visit(SyntaxNode node) { //Tabs++; //var indents = new String('\t', Tabs); //Writer.WriteLine(indents + node.GetType().Name + "/" + node.CSharpKind()); base.Visit(node); //Tabs--; } public override void VisitNamespaceDeclaration(NamespaceDeclarationSyntax node) { _currentNamespace = node; Writer.WriteLine(" " + node.Name.ToString().Replace(".", "_")); base.VisitNamespaceDeclaration(node); } public override void VisitClassDeclaration(ClassDeclarationSyntax node) { _currentClass = node; var fields = node.ChildNodes().OfType<FieldDeclarationSyntax>().ToArray(); _classFields[node] = fields; Writer.WriteLine(); Writer.WriteLine(string.Format("// {0}", node.Identifier)); base.VisitClassDeclaration(node); DeclareBaseClassPropertiesToImplement(node); DeclareBaseClassMethodsToImplement(node); } void DeclareBaseClassMethodsToImplement(ClassDeclarationSyntax classNode) { var classSymbol = Model.GetDeclaredSymbol(classNode); List<string> processedMembers = new List<string>(); var type = classSymbol; while (type != null) { foreach(var member in type.GetMembers()) { var declarators = member.DeclaringSyntaxReferences; if (declarators == null || declarators.Length == 0) continue; if (declarators.Length != 1) throw new NotImplementedException(); var memberNode = declarators[0].GetSyntax() as MethodDeclarationSyntax; if (memberNode == null) continue; if (processedMembers.Any(m=>m == member.Name)) continue; processedMembers.Add(member.Name); if (type == classSymbol) //Skip original class members. Declare only base classes continue; Writer.WriteLine(); Writer.WriteLine(string.Format(" {0}_{1}(_this)", _currentClass.Identifier, memberNode.Identifier)); Writer.WriteLine(string.Format(" {0}_{1}(_this);", type.Name, member.Name)); Writer.WriteLine(string.Format(";")); } type = type.BaseType; } } void DeclareBaseClassPropertiesToImplement(ClassDeclarationSyntax classNode) { var classSymbol = Model.GetDeclaredSymbol(classNode); List<string> processedMembers = new List<string>(); var type = classSymbol; while (type != null) { foreach(var member in type.GetMembers()) { var declarators = member.DeclaringSyntaxReferences; if (declarators == null || declarators.Length == 0) continue; if (declarators.Length != 1) throw new NotImplementedException(); var memberNode = declarators[0].GetSyntax() as PropertyDeclarationSyntax; if (memberNode == null) continue; if (processedMembers.Any(m => m == memberNode.Identifier.ToString())) continue; processedMembers.Add(memberNode.Identifier.ToString()); if (type == classSymbol) //Skip original class members. Declare only base classes continue; Writer.WriteLine(); Writer.WriteLine(string.Format(" {0}__{1}(_this)", _currentClass.Identifier, memberNode.Identifier)); Writer.WriteLine(string.Format("  {0}__{1}(_this);", type.Name, member.Name)); Writer.WriteLine(string.Format(";")); Writer.WriteLine(); Writer.WriteLine(string.Format(" {0}__{1}(_this, value)", _currentClass.Identifier, memberNode.Identifier)); Writer.WriteLine(string.Format(" {0}__{1}(_this);", type.Name, member.Name)); Writer.WriteLine(string.Format(";")); } type = type.BaseType; } } public override void VisitConstructorDeclaration(ConstructorDeclarationSyntax node) { Writer.WriteLine(); var symbol = Model.GetDeclaredSymbol(node); List<string> parameters = new List<string>(); parameters.Add("_this"); parameters.AddRange(node.ParameterList.Parameters.Select(m => m.Identifier.ToString()).ToArray()); Writer.WriteLine(string.Format(" {0}({1}){2}", node.Identifier, String.Join(", ", parameters), " ")); Writer.WriteLine(); Tabs++; var indents = new String('\t', Tabs); //Initialize members first if no this constructor initializer (:this()) call if (!node.DescendantNodes().OfType<ConstructorInitializerSyntax>().Any(m=>m.CSharpKind() == SyntaxKind.ThisConstructorInitializer) && _classFields.ContainsKey(_currentClass)) { Writer.WriteLine(indents + String.Format("// ")); //Writer.WriteLine(String.Format("_this =  ();")); foreach (var field in _classFields[_currentClass]) { Writer.WriteLine(String.Format(indents + "_this.(\"{0}\", {1})", field.Declaration.Variables[0].Identifier, field.Declaration.Variables[0].Initializer.Value)); } } if (node.Initializer != null) { List<string> arguments = new List<string>(); arguments.Add("_this"); arguments.AddRange(node.Initializer.ArgumentList.Arguments.Select(m => m.Expression.ToString()).ToArray()); if (node.Initializer.ThisOrBaseKeyword.CSharpKind() == SyntaxKind.BaseKeyword) { Writer.WriteLine(indents + String.Format("//   ")); Writer.WriteLine(indents + String.Format("{0}({1});", _currentClass.BaseList.Types[0], String.Join(", ", arguments))); } else if (node.Initializer.CSharpKind() == SyntaxKind.ThisConstructorInitializer) { Writer.WriteLine(indents + String.Format("//  ")); Writer.WriteLine(indents + String.Format("{0}({1});", _currentClass.Identifier, String.Join(", ", arguments))); } } Writer.WriteLine(String.Format(indents + "_this.(\"__type\", \"{0}.{1}\")", symbol.ContainingNamespace.Name, symbol.ContainingType.Name)); base.VisitConstructorDeclaration(node); Tabs--; Writer.WriteLine(indents + string.Format(" _this;")); Writer.WriteLine(string.Format("; //{0}({1}){2}", node.Identifier, String.Join(", ", parameters), " ")); } public override void VisitPropertyDeclaration(PropertyDeclarationSyntax node) { _currentProperty = node; var symbol = Model.GetDeclaredSymbol(node); base.VisitPropertyDeclaration(node); } public override void VisitAccessorDeclaration(AccessorDeclarationSyntax node) { Writer.WriteLine(); if (node.CSharpKind() == SyntaxKind.GetAccessorDeclaration) { Writer.WriteLine(string.Format(" {0}__{1}(_this)", _currentClass.Identifier, _currentProperty.Identifier)); } else if (node.CSharpKind() == SyntaxKind.SetAccessorDeclaration) { Writer.WriteLine(string.Format(" {0}__{1}(_this, value)", _currentClass.Identifier, _currentProperty.Identifier)); } Tabs++; if (node.Body == null) { //auto implemented var indents = new String('\t', Tabs); if (node.CSharpKind() == SyntaxKind.GetAccessorDeclaration) { Writer.WriteLine(indents + string.Format(" _this._private_{0}_{1};", _currentClass.Identifier, _currentProperty.Identifier)); } else if (node.CSharpKind() == SyntaxKind.SetAccessorDeclaration) { Writer.WriteLine(indents + string.Format("_this._private_{0}_{1} = value;", _currentClass.Identifier, _currentProperty.Identifier)); } } base.VisitAccessorDeclaration(node); Tabs--; if (node.CSharpKind() == SyntaxKind.GetAccessorDeclaration) { Writer.WriteLine(string.Format(";")); } else if (node.CSharpKind() == SyntaxKind.SetAccessorDeclaration) { Writer.WriteLine(string.Format(";")); } } public override void VisitMethodDeclaration(MethodDeclarationSyntax node) { Writer.WriteLine(); Writer.WriteLine(string.Format(" {0}_{1}(_this)", _currentClass.Identifier, node.Identifier)); Tabs++; base.VisitMethodDeclaration(node); Tabs--; Writer.WriteLine(string.Format(";")); } public override void VisitExpressionStatement(ExpressionStatementSyntax node) { var indents = new String('\t', Tabs); Writer.WriteLine(("\r\n" + node.ToString()).Replace("\r\n", "\r\n" + indents + "//")); base.VisitExpressionStatement(node); } public override void VisitReturnStatement(ReturnStatementSyntax node) { var indents = new String('\t', Tabs); Writer.WriteLine(("\r\n" + node.ToString()).Replace("\r\n", "\r\n" + indents + "//")); base.VisitReturnStatement(node); } //public override void VisitBlock(BlockSyntax node) //{ // Writer.WriteLine(node.ToString()); // base.VisitBlock(node); //} } } 



Work result



As a result, the code

C # source code
 namespace 1.2 { public class  { public () { 1 = " "; } private int _1 = 10; public int 1 {get {return _1;} set {_1 = value;}} public string 1 {get; set;} public void 1() { 1 = "1"; } } public class  :  { private int _1 = 20; public () : base() { 1 = " "; 1(); } public (int i) : this() { 1 = " (int i)"; 1(); } } } 



Will be translated into 1C: Enterprise code

Source Code 1C: Enterprise
  1_2 //   (_this)  //  _this.("_1", 10) _this.("__type", "2.") //1 = " ";  _this; ; //(_this)   __1(_this) //return _1; ;  __1(_this, value) //_1 = value; ;  __1(_this)  _this._private__1; ;  __1(_this, value) _this._private__1 = value; ;  _1(_this) //1 = "1"; ; //   (_this)  //  _this.("_1", 20) //    (_this); _this.("__type", "2.") //1 = " "; //1();  _this; ; //(_this)   (_this, i)  //   (_this); _this.("__type", "2.") //1 = " (int i)"; //1();  _this; ; //(_this, i)   __1(_this)  __1(_this); ;  __1(_this, value) __1(_this); ;  __1(_this)  __1(_this); ;  __1(_this, value) __1(_this); ;  _1(_this) _1(_this); ; 

Source: https://habr.com/ru/post/245453/


All Articles