/* * SPDX-License-Identifier: AGPL-3.0-or-later * Copyright (C) 2025 Sergej Görzen * This file is part of xAPI4Unity. */ #if UNITY_EDITOR using System; using System.Collections.Generic; using System.Linq; using xAPI4Unity.Editor.Parser.Code.SyntaxTree; namespace xAPI4Unity.Editor.Parser.Code { /// /// Serializes source code out of a syntax tree /// internal abstract class SyntaxTreeSerializer { protected abstract IDictionary Modifiers { get; } /// /// Serializes a list of modifiers /// /// Modifiers /// Serialized modifiers string protected virtual string ModifiersToString(IReadOnlyList modifiers) { var str = string.Empty; // If there are modifiers, map known ones to their string form and join with spaces if (modifiers != null) { str = modifiers .Where(modifier => Modifiers.ContainsKey(modifier) && Modifiers[modifier] != "") .Aggregate(str, (current, modifier) => current + $"{Modifiers[modifier]} "); } return str; } /// /// Serializes a class /// /// Class /// Indentation depth to use in serialization /// Serialized class string protected virtual string ClassContentsToString(Class cls, int indentDepth) { var propsStr = PropertiesToString(cls.Properties, indentDepth); var constStr = ConstructorsToString(cls.Constructors, indentDepth); var getablePropsStr = GetablePropertiesToString(cls.GetableProperties, indentDepth); var methodsStr = MethodsToString(cls.Methods, indentDepth); return $"{propsStr}{(constStr == "" ? "" : Environment.NewLine)}{constStr}" + $"{(getablePropsStr == "" ? "" : Environment.NewLine)}{getablePropsStr}" + $"{(methodsStr == "" ? "" : Environment.NewLine)}{methodsStr}"; } /// /// Serializes properties /// /// Properties /// Indentation depth to use in serialization /// Serialized properties string protected virtual string PropertiesToString(IReadOnlyList properties, int indentDepth) { var propsStr = string.Empty; if (properties == null) return propsStr; for (var i = 0; i < properties.Count; ++i) { // Append current property propsStr += PropertyToString(properties[i], indentDepth); if (i < properties.Count - 1) { // Add a newline between properties, but not after the last one propsStr += Environment.NewLine; } } return propsStr; } /// /// Serializes constructors /// /// Constructors /// Indentation depth to use in serialization /// Serialized constructors string protected virtual string ConstructorsToString(IReadOnlyList constructors, int indentDepth) { var constStr = string.Empty; if (constructors == null) return constStr; for (var i = 0; i < constructors.Count; ++i) { // Append constructor serialization constStr += ConstructorToString(constructors[i], indentDepth); if (i < constructors.Count - 1) { // Separate constructors with newline constStr += Environment.NewLine; } } return constStr; } /// /// Serializes getable properties /// /// Getable properties /// Indentation depth to use in serialization /// Serialized getable properties string protected virtual string GetablePropertiesToString(IReadOnlyList properties, int indentDepth) { var getablePropsStr = string.Empty; if (properties == null) return getablePropsStr; for (var i = 0; i < properties.Count; ++i) { // Append current get-only property getablePropsStr += GetablePropertyToString(properties[i], indentDepth); if (i < properties.Count - 1) { // Add a newline between entries getablePropsStr += Environment.NewLine; } } return getablePropsStr; } /// /// Serializes methods /// /// Methods /// Indentation depth to use in serialization /// Serialized methods string protected virtual string MethodsToString(IReadOnlyList methods, int indentDepth) { var methodsStr = string.Empty; if (methods == null) return methodsStr; for (var i = 0; i < methods.Count; ++i) { // Append method serialization methodsStr += MethodToString(methods[i], indentDepth); if (i < methods.Count - 1) { // Separate methods with newline methodsStr += Environment.NewLine; } } return methodsStr; } /// /// Serializes an import /// /// Import /// Indentation depth to use in serialization /// Serialized import string protected abstract string ImportToString(Import import, int indentDepth); /// /// Serializes a class /// /// Class /// Indentation depth to use in serialization /// Serialized class string protected abstract string ClassToString(Class cls, int indentDepth); /// /// Serializes a getable property /// /// Property /// Indentation depth to use in serialization /// Serialized getable property string protected abstract string GetablePropertyToString(GetableProperty property, int indentDepth); /// /// Serializes a property /// /// Property /// Indentation depth to use in serialization /// Serialized property string protected abstract string PropertyToString(Property property, int indentDepth); /// /// Serializes a constructor /// /// Constructor /// Indentation depth to use in serialization /// Serialized constructor string protected abstract string ConstructorToString(Constructor constructor, int indentDepth); /// /// Serializes a method /// /// Method /// Indentation depth to use in serialization /// Serialized method string protected abstract string MethodToString(Method method, int indentDepth); /// /// Serializes a list of arguments /// /// Arguments /// Indentation depth to use in serialization /// Should every argument be in its own line? /// Should the name be included in the arguments list (i.e. "name: value") /// Serialized arguments string protected abstract string ArgumentsToString(IReadOnlyList args, int indentDepth, bool newLines = false, bool withName = false); /// /// Serializes a list of parameters /// /// Parameters /// Serialized parameters string protected abstract string ParametersToString(IReadOnlyList parameters); /// /// Serializes a property comment /// /// Property /// Indentation depth to use in serialization /// Serialized property comment string protected abstract string PropertyCommentToString(Property property, int indentDepth); /// /// Serializes a comment /// /// Comment /// Indentation depth to use in serialization /// Serialized comment string protected abstract string CommentToString(string comment, int indentDepth); /// /// Serializes a dictionary /// /// Dictionary /// Indentation depth to use in serialization /// Serialized dictionary string public abstract string DictionaryToString(IDictionary dict, int indentDepth); /// /// Serializes a root /// /// Root /// Serialized root string public abstract string RootToString(Root root); /// /// Serializes a Null /// /// Null /// Serialized null string public virtual string NullToString(Null n) => "null"; /// /// Serializes a method body /// /// Method body /// Indentation depth to use in serialization /// Serialized method body string protected virtual string MethodBodyToString(MethodBody body, int indentDepth) { var str = string.Empty; // Precompute indentation string for the body lines var indentBodyStr = CodeGenerator.Indent(indentDepth); if (body?.Statements == null) return str; // For each non-null statement value, split by lines and indent each non-empty line return body.Statements.Where(statement => statement.Value != null) .Aggregate(str, (current1, statement) => statement.Value.Split(Environment.NewLine.ToCharArray()) .Where(line => !string.IsNullOrEmpty(line)) .Aggregate(current1, (current, line) => current + $"{indentBodyStr}{line}{Environment.NewLine}")); } } } #endif