using System.Collections.Generic;
namespace codelanguage;
/// Individual articles for preprocessing directives and pragma forms.
internal static class PreprocessorReferenceTopics
{
private sealed record Directive(string Name, string Summary, string Beginner, string Expert, string Related);
private static readonly Directive[] Items =
{
new("#if", "Compiles the following source only when a preprocessor expression is true.", "#if DM_VERSION >= 516\n#define HAS_MODERN_API 1\n#endif", "#if defined(HAS_MODERN_API) && DM_BUILD >= 1680\n #define USE_NEW_RENDERER 1\n#else\n #define USE_NEW_RENDERER 0\n#endif", "#elif,#else,#endif,#define"),
new("#elif", "Tests another preprocessor condition after an earlier #if or #elif failed.", "#if DM_VERSION >= 516\n #define API_LEVEL 516\n#elif DM_VERSION >= 515\n #define API_LEVEL 515\n#endif", "#if defined(PLATFORM_A)\n #define PLATFORM_NAME \"A\"\n#elif defined(PLATFORM_B)\n #define PLATFORM_NAME \"B\"\n#else\n #define PLATFORM_NAME \"other\"\n#endif", "#if,#else,#endif"),
new("#else", "Provides the alternate preprocessing branch when previous conditions are false.", "#ifdef DEBUG\n #define LOGGING 1\n#else\n #define LOGGING 0\n#endif", "#if DM_VERSION >= 516\n #define HAS_VECTORS 1\n#else\n #define HAS_VECTORS 0\n#endif", "#if,#ifdef,#ifndef,#endif"),
new("#endif", "Ends a conditional preprocessing region.", "#ifdef DEBUG\n world.log << \"Debug build\"\n#endif", "#if FEATURE_A\n #if FEATURE_B\n #define FEATURES_COMBINED 1\n #endif\n#endif", "#if,#ifdef,#ifndef,#else"),
new("#ifndef", "Compiles a region when a macro has not been defined.", "#ifndef CONFIG_INCLUDED\n#define CONFIG_INCLUDED 1\n#endif", "#ifndef HEADER_GUARD_GAME_DM\n#define HEADER_GUARD_GAME_DM 1\n#include \"game.dm\"\n#endif", "#ifdef,#define,#undef,#endif"),
new("#ifdef", "Compiles a region only when the named macro has been defined.", "#ifdef DEBUG\nworld.log << \"Debug build\"\n#endif", "#ifdef OPTIONAL_RENDERER\n #define USE_RENDERER 1\n#else\n #define USE_RENDERER 0\n#endif", "#ifndef,#define,#undef,#endif"),
new("#define", "Defines a macro name and an optional replacement value for later preprocessing.", "#define MAX_PLAYERS 32\n#if MAX_PLAYERS > 0\nworld.log << MAX_PLAYERS\n#endif", "#define FEATURE_ENABLED 1\n#ifdef FEATURE_ENABLED\n #define FEATURE_NAME \"enabled\"\n#endif", "#ifdef,#ifndef,#undef,#include"),
new("#include", "Reads another source file into the current preprocessed source at this location.", "#include \"shared/constants.dm\"", "#include \"shared/constants.dm\"\n#include \"world/areas.dm\"\n#include \"world/items.dm\"", "#define,#pragma,#if"),
new("#undef", "Removes a macro definition for subsequent preprocessing.", "#define TEMP_FEATURE 1\n#undef TEMP_FEATURE", "#ifdef OPTIONAL_RENDERER\n #undef OPTIONAL_RENDERER\n#endif", "#define,#ifdef,#ifndef"),
new("#error", "Stops preprocessing and reports an error message.", "#if DM_VERSION < 516\n#error This project requires language version 516.\n#endif", "#ifndef REQUIRED_ASSET_PATH\n#error REQUIRED_ASSET_PATH must be defined by the project.\n#endif", "#if,#define,#pragma"),
new("#pragma", "Changes a compiler or language mode for a source region.", "#pragma syntax C\nfor(var/i = 0; i < 3; i++)\n world << i", "#pragma push\n#pragma compatibility 516\n// version-specific declarations\n#pragma pop", "#pragma compatibility,#pragma syntax,#pragma math,#pragma push/pop"),
new("#pragma compatibility", "Selects a compatibility version for supported compiler behaviors.", "#pragma compatibility 516", "#pragma push\n#pragma compatibility 516\n#ifdef DM_VERSION\n #define USE_516_BEHAVIOR 1\n#endif\n#pragma pop", "#pragma,#if,#define"),
new("#pragma syntax", "Selects DM-style or C-style syntax where supported.", "#pragma syntax DM", "#pragma push\n#pragma syntax C\nfor(var/i = 0; i < 10; i++)\n process(i)\n#pragma pop", "#pragma,#if,for"),
new("#pragma math", "Selects a supported math precision or speed mode.", "#pragma math accurate", "#pragma push\n#pragma math fast\nvar/approximation = sin(angle) * radius\n#pragma pop", "#pragma,operators,sin"),
new("#pragma multiple", "Allows a source file to be included more than once when required.", "#pragma multiple\n// declarations in a deliberately repeatable include", "#pragma multiple\n#define REGISTER_TYPE(path) path\n// Include this file from separate registration units.", "#include,#pragma,#define"),
new("#pragma push/pop", "Saves and restores pragma settings around a local source region.", "#pragma push\n#pragma syntax C\n// C-style syntax here\n#pragma pop", "#pragma push\n#pragma compatibility 516\n#pragma math accurate\n// declarations under selected modes\n#pragma pop", "#pragma,#pragma syntax,#pragma compatibility,#pragma math"),
};
public static IEnumerable Create()
{
foreach (var item in Items)
yield return new ReferenceTopic
{
Id = "directive-" + Slug(item.Name),
Title = item.Name,
Summary = item.Summary,
Categories = { "preprocessor", "syntax" },
SeeAlso = item.Related,
Sections =
{
new Section("Format", new CodeContentLine(Format(item.Name))),
new Section("Directive", new CodeContentLine(item.Name)),
new Section("What it does", new ParagraphContentLine(item.Summary)),
new Section("Operands and placement", new ParagraphContentLine(OperandDescription(item.Name))),
new Section("Beginner example", new CodeContentLine(item.Beginner)),
new Section("Expert example", new CodeContentLine(item.Expert)),
}
};
}
private static string Format(string name) => name.ToLowerInvariant() switch
{
"#if" or "#elif" => name + " expression",
"#ifdef" or "#ifndef" or "#undef" => name + " MACRO_NAME",
"#define" => "#define MACRO_NAME [replacement]",
"#include" => "#include \"relative/path.dm\"",
"#error" => name + " message",
"#pragma" or "#pragma compatibility" or "#pragma syntax" or "#pragma math" => name + " setting [value]",
"#pragma multiple" or "#pragma push/pop" => name,
_ => name
};
private static string OperandDescription(string name) => name.ToLowerInvariant() switch
{
"#if" or "#elif" => "expression is a preprocessor-time condition. #elif is checked only if earlier branches in the same #if group were false.",
"#ifdef" or "#ifndef" => "MACRO_NAME is a macro identifier, not a quoted string. The body runs when its defined state matches the directive.",
"#define" => "MACRO_NAME is the identifier to define. replacement is optional text substituted at later uses. Keep definitions before the code that uses them.",
"#undef" => "MACRO_NAME is the identifier to remove; later #ifdef checks then see it as undefined.",
"#include" => "The quoted path names a source file to include at this point. Resolve it relative to the project/source include rules.",
"#error" => "message is diagnostic text. Reaching this directive stops preprocessing and reports that text.",
"#else" => "Takes no operand. It selects the remaining branch when no earlier branch in the group matched.",
"#endif" => "Takes no operand. It closes the nearest open conditional directive; every conditional group needs a matching #endif.",
"#pragma" or "#pragma compatibility" or "#pragma syntax" or "#pragma math" => "setting selects a supported compiler mode or value. Only use settings accepted by the selected Craft++/BYOND compatibility version.",
"#pragma push/pop" => "Takes no operand. Push saves the current pragma state; pop restores that saved state.",
"#pragma multiple" => "Takes no operand. It marks a file as intentionally includable more than once; repeated inclusion can duplicate declarations.",
_ => "Check the format and example above for the operand names and placement required by this directive."
};
private static string Slug(string value) => string.Join("-", value.ToLowerInvariant().Split(new[] { '#', ' ', '/', '\\' }, System.StringSplitOptions.RemoveEmptyEntries));
}