namespace marlin.core.util; using System.Text; using System.Text.RegularExpressions; public class VariableExpander { public static string Expand(string format, Dictionary variables) { StringBuilder output = new(); for (int i = 0; i < format.Length; i++) { char c = format[i]; if (c == '{') { if (i + 1 >= format.Length) throw new UnmatchedBraceException("Trailing expression '{'", i, format[i..]); if (format[i + 1] == '{') { output.Append('{'); i++; } else { string expr = GetExpression(format, ref i); string value = EvaluateExpression(expr, variables); output.Append(value); } } else if (c == '}') { if (i + 1 >= format.Length) throw new UnmatchedBraceException("Uninitiated expression at '}'", i, format[i..]); if (format[i + 1] == '}') { output.Append('}'); i++; } else { throw new UnmatchedBraceException("Uninitiated expression at '}'", i, format[i..]); } } else output.Append(c); } string result = output.ToString(); // Clean up extra spaces: collapse multiple consecutive spaces (but preserve other whitespace) result = Regex.Replace(result, @" {2,}", " "); result = result.Trim(); return result; } private static string GetExpression(string format, ref int pos) { StringBuilder part = new(); if (format[pos] != '{') throw new InvalidExpressionException(format[pos..], "Expected '{' at start of expression"); pos++; for (; pos < format.Length; pos++) { if (format[pos] == '}') break; part.Append(format[pos]); } if (pos >= format.Length || format[pos] != '}') throw new UnmatchedBraceException($"Trailing expression {format[pos..]}", pos, format[pos..]); return part.ToString(); } private static string EvaluateExpression(string expr, Dictionary variables) { Match match; const string identifier = "[a-zA-Z0-9-_.]+"; // Variable value match = Regex.Match(expr, $"^{identifier}$"); if (match.Success) return GetVariableOrThrow(expr, variables); // Conditional argument: only included if variable is set. // --hello var match = Regex.Match(expr, $"^(--?{identifier})\\s+({identifier})$"); if (match.Success) { if (variables.TryGetValue(match.Groups[2].Value, out var value)) return $"{match.Groups[1].Value} {value}"; else return ""; } // Default value, if value is unset, uses default match = Regex.Match(expr, $@"^({identifier})\s*=\s*(.*)$"); if (match.Success) { if (variables.TryGetValue(match.Groups[1].Value, out var value)) return value; else return match.Groups[2].Value; } // Use value if set match = Regex.Match(expr, $@"^({identifier})\?\s*(.*)$"); if (match.Success) { if (variables.TryGetValue(match.Groups[1].Value, out var value)) { if (string.IsNullOrEmpty(value) || value == "null" || value == "false") return ""; return match.Groups[2].Value; } else return ""; } throw new InvalidExpressionException(expr); } private static string GetVariableOrThrow(string variable, Dictionary variables) { return variables.TryGetValue(variable, out var value) ? value : throw new VariableNotFoundException(variable); } }