/
docNemo
/
clothes-graph
Обзор
Документация
Войти
/
docNemo
/
clothes-graph
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
main
src/ClothesGraph.Model/Formulas/FormulaWriter.cs
128 строк
5 KB
docNemo
Фундамент продукта: параметрическое построение выкроек, печать, установщик
08 авг 2026, 22:17
08 авг 2026, 22:17
cd47cce
Код
Авторство
О чём код?
using System; using System.Globalization; using System.Linq; using System.Text; using ClothesGraph.Model.Drafting; namespace ClothesGraph.Model.Formulas; /// <summary> /// Сборка текста формулы из дерева выражения по текущим именам. /// </summary> /// <remarks> /// Скобки расставляются по приоритетам, а не сохраняются из исходного ввода: /// дерево не помнит, где пользователь их поставил. Текст выходит канонический — /// лишние скобки исчезают, необходимые остаются. /// </remarks> internal static class FormulaWriter { public static string Write(Expression expression, IFormulaScope scope) { var text = new StringBuilder(); Write(expression, scope, text, parentPrecedence: 0, isRightOperand: false); return text.ToString(); } private static void Write( Expression expression, IFormulaScope scope, StringBuilder text, int parentPrecedence, bool isRightOperand) { switch (expression) { case NumberExpression number: text.Append(number.Value.ToString("0.############", CultureInfo.CurrentCulture)); break; case MeasurementExpression measurement: text.Append(Quote(scope.GetMeasurementName(measurement.Measurement) ?? "«удалённая мерка»")); break; case PropertyExpression property: text.Append(Quote(scope.GetNodeName(property.Node) ?? "«удалённое построение»")); text.Append('.'); text.Append(PropertyName(property.Property)); break; case NegateExpression negate: text.Append('-'); Write(negate.Operand, scope, text, 3, isRightOperand: false); break; case BinaryExpression binary: WriteBinary(binary, scope, text, parentPrecedence, isRightOperand); break; case CallExpression call: text.Append(call.Function).Append('('); for (var index = 0; index < call.Arguments.Count; index++) { if (index > 0) text.Append(", "); Write(call.Arguments[index], scope, text, 0, isRightOperand: false); } text.Append(')'); break; default: throw new InvalidOperationException($"Неизвестный узел формулы {expression.GetType().Name}"); } } private static void WriteBinary( BinaryExpression binary, IFormulaScope scope, StringBuilder text, int parentPrecedence, bool isRightOperand) { var precedence = Precedence(binary.Operator); // Скобки нужны, когда операция слабее охватывающей, либо когда она // равна ей по силе и стоит справа: a-(b-c) отличается от a-b-c. var needsParentheses = precedence < parentPrecedence || (precedence == parentPrecedence && isRightOperand); if (needsParentheses) text.Append('('); Write(binary.Left, scope, text, precedence, isRightOperand: false); text.Append(' ').Append(Symbol(binary.Operator)).Append(' '); Write(binary.Right, scope, text, precedence, isRightOperand: true); if (needsParentheses) text.Append(')'); } private static int Precedence(BinaryOperator op) => op switch { BinaryOperator.Add or BinaryOperator.Subtract => 1, BinaryOperator.Multiply or BinaryOperator.Divide => 2, BinaryOperator.Power => 4, _ => 0 }; private static string Symbol(BinaryOperator op) => op switch { BinaryOperator.Add => "+", BinaryOperator.Subtract => "-", BinaryOperator.Multiply => "*", BinaryOperator.Divide => "/", BinaryOperator.Power => "^", _ => "?" }; private static string PropertyName(NodeProperty property) => property switch { NodeProperty.Length => "длина", NodeProperty.Angle => "угол", NodeProperty.X => "x", NodeProperty.Y => "y", _ => "?" }; /// <summary>Имена с пробелами берутся в квадратные скобки — так их читает разбор.</summary> private static string Quote(string name) => name.Any(symbol => !char.IsLetterOrDigit(symbol) && symbol != '_') ? $"[{name}]" : name; }