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)); }