/* * lang_c.hpp * C and C++ highlighting * Shared by the MontaukOS text editor (GUI) and edit.elf (CLI) * Copyright (c) 2026 Daniel Hammer */ // Included by ; not standalone. #pragma once // ============================================================================ // Keyword / type classification // ============================================================================ inline SynToken syn_classify_c_word(const char* buf, int len) { static const char* keywords[] = { "auto", "break", "case", "const", "continue", "default", "do", "else", "enum", "extern", "for", "goto", "if", "inline", "register", "restrict", "return", "sizeof", "static", "struct", "switch", "typedef", "union", "volatile", "while", "NULL", "true", "false", "nullptr", }; static const char* types[] = { "void", "char", "short", "int", "long", "float", "double", "signed", "unsigned", "bool", "_Bool", "int8_t", "int16_t", "int32_t", "int64_t", "uint8_t", "uint16_t", "uint32_t", "uint64_t", "size_t", "ssize_t", "ptrdiff_t", "intptr_t", "uintptr_t", "FILE", }; for (int i = 0; i < (int)(sizeof(keywords) / sizeof(keywords[0])); i++) { if (syn_streq(buf, len, keywords[i])) return SYN_KEYWORD; } for (int i = 0; i < (int)(sizeof(types) / sizeof(types[0])); i++) { if (syn_streq(buf, len, types[i])) return SYN_TYPE; } return SYN_NORMAL; } inline SynToken syn_classify_cpp_word(const char* buf, int len) { // C++ keywords (on top of the C set, which is checked first) static const char* keywords[] = { "alignas", "alignof", "and", "and_eq", "asm", "bitand", "bitor", "catch", "class", "compl", "concept", "consteval", "constexpr", "constinit", "const_cast", "co_await", "co_return", "co_yield", "decltype", "delete", "dynamic_cast", "explicit", "export", "final", "friend", "mutable", "namespace", "new", "noexcept", "not", "not_eq", "operator", "or", "or_eq", "override", "private", "protected", "public", "reinterpret_cast", "requires", "static_assert", "static_cast", "template", "this", "thread_local", "throw", "try", "typeid", "typename", "using", "virtual", "xor", "xor_eq", }; // C++ library / builtin types static const char* types[] = { "char8_t", "char16_t", "char32_t", "wchar_t", "nullptr_t", "std", "string", "string_view", "vector", "array", "span", "map", "set", "unordered_map", "unordered_set", "deque", "list", "pair", "tuple", "optional", "variant", "function", "unique_ptr", "shared_ptr", "weak_ptr", "initializer_list", }; SynToken c = syn_classify_c_word(buf, len); if (c != SYN_NORMAL) return c; for (int i = 0; i < (int)(sizeof(keywords) / sizeof(keywords[0])); i++) { if (syn_streq(buf, len, keywords[i])) return SYN_KEYWORD; } for (int i = 0; i < (int)(sizeof(types) / sizeof(types[0])); i++) { if (syn_streq(buf, len, types[i])) return SYN_TYPE; } return SYN_NORMAL; } // ============================================================================ // C highlighting // ============================================================================ // C / C++ raw string helpers // ============================================================================ inline bool syn_is_raw_string_prefix(const char* buf, int len) { return syn_streq(buf, len, "R") || syn_streq(buf, len, "LR") || syn_streq(buf, len, "uR") || syn_streq(buf, len, "UR") || syn_streq(buf, len, "u8R"); } inline bool syn_is_string_prefix(const char* buf, int len) { return syn_streq(buf, len, "L") || syn_streq(buf, len, "u") || syn_streq(buf, len, "U") || syn_streq(buf, len, "u8"); } // Colors a raw-string body from `i` up to and including the closing )delim". // If the terminator is not on this line, leaves state.in_raw_string set so the // next line continues the literal. inline void syn_consume_raw_body(const char* line, int len, int& i, SynToken* out, int out_len, SynState& state) { while (i < len) { if (line[i] == ')') { int j = i + 1; int k = 0; while (k < state.raw_delim_len && j < len && line[j] == state.raw_delim[k]) { j++; k++; } if (k == state.raw_delim_len && j < len && line[j] == '"') { syn_fill_tokens(out, out_len, i, j + 1, SYN_STRING); i = j + 1; state.in_raw_string = false; state.raw_delim_len = 0; return; } } syn_set_token(out, out_len, i, SYN_STRING); i++; } } // Enters a raw string; `line[i]` must be the opening quote. inline void syn_consume_raw_string(const char* line, int len, int& i, SynToken* out, int out_len, SynState& state) { syn_set_token(out, out_len, i, SYN_STRING); i++; state.raw_delim_len = 0; while (i < len && line[i] != '(' && state.raw_delim_len < SYN_RAW_DELIM_MAX) { state.raw_delim[state.raw_delim_len++] = line[i]; syn_set_token(out, out_len, i, SYN_STRING); i++; } if (i < len && line[i] == '(') { syn_set_token(out, out_len, i, SYN_STRING); i++; state.in_raw_string = true; syn_consume_raw_body(line, len, i, out, out_len, state); return; } // Malformed (or an over-long delimiter): treat the rest of the line as string syn_fill_tokens(out, out_len, i, len, SYN_STRING); i = len; } // ============================================================================ // C / C++ highlighting // ============================================================================ inline void syn_highlight_line_c(const char* line, int len, SynToken* out, int out_len, SynState& state, SynLanguage lang = SYN_LANG_C) { const bool cpp = (lang == SYN_LANG_CPP); int i = 0; while (i < len) { // ---- Raw string continuation ---- if (state.in_raw_string) { syn_consume_raw_body(line, len, i, out, out_len, state); continue; } // ---- Block comment continuation ---- if (state.in_block_comment) { while (i < len) { if (i + 1 < len && line[i] == '*' && line[i + 1] == '/') { syn_set_token(out, out_len, i, SYN_COMMENT); syn_set_token(out, out_len, i + 1, SYN_COMMENT); i += 2; state.in_block_comment = false; break; } syn_set_token(out, out_len, i, SYN_COMMENT); i++; } continue; } char c = line[i]; // ---- Line comment ---- if (c == '/' && i + 1 < len && line[i + 1] == '/') { syn_fill_tokens(out, out_len, i, len, SYN_COMMENT); break; } // ---- Block comment start ---- if (c == '/' && i + 1 < len && line[i + 1] == '*') { state.in_block_comment = true; syn_set_token(out, out_len, i, SYN_COMMENT); syn_set_token(out, out_len, i + 1, SYN_COMMENT); i += 2; continue; } // ---- Preprocessor directive ---- if (c == '#') { // Check that only whitespace precedes the # bool is_pp = true; for (int j = 0; j < i; j++) { if (line[j] != ' ' && line[j] != '\t') { is_pp = false; break; } } if (is_pp) { while (i < len) { // Handle line-comment inside preprocessor if (i + 1 < len && line[i] == '/' && line[i + 1] == '/') { syn_fill_tokens(out, out_len, i, len, SYN_COMMENT); break; } // Handle block comment start inside preprocessor if (i + 1 < len && line[i] == '/' && line[i + 1] == '*') { state.in_block_comment = true; syn_set_token(out, out_len, i, SYN_COMMENT); syn_set_token(out, out_len, i + 1, SYN_COMMENT); i += 2; // Continue consuming as comment while (i < len) { if (i + 1 < len && line[i] == '*' && line[i + 1] == '/') { syn_set_token(out, out_len, i, SYN_COMMENT); syn_set_token(out, out_len, i + 1, SYN_COMMENT); i += 2; state.in_block_comment = false; break; } syn_set_token(out, out_len, i, SYN_COMMENT); i++; } continue; } syn_set_token(out, out_len, i, SYN_PREPROCESSOR); i++; } continue; } } // ---- String literal ---- if (c == '"') { syn_set_token(out, out_len, i, SYN_STRING); i++; while (i < len) { if (line[i] == '\\' && i + 1 < len) { syn_set_token(out, out_len, i, SYN_STRING); syn_set_token(out, out_len, i + 1, SYN_STRING); i += 2; continue; } if (line[i] == '"') { syn_set_token(out, out_len, i, SYN_STRING); i++; break; } syn_set_token(out, out_len, i, SYN_STRING); i++; } continue; } // ---- Character literal ---- if (c == '\'') { syn_set_token(out, out_len, i, SYN_CHAR); i++; while (i < len) { if (line[i] == '\\' && i + 1 < len) { syn_set_token(out, out_len, i, SYN_CHAR); syn_set_token(out, out_len, i + 1, SYN_CHAR); i += 2; continue; } if (line[i] == '\'') { syn_set_token(out, out_len, i, SYN_CHAR); i++; break; } syn_set_token(out, out_len, i, SYN_CHAR); i++; } continue; } // ---- Numbers ---- if (syn_is_digit(c) || (c == '.' && i + 1 < len && syn_is_digit(line[i + 1]))) { syn_consume_number(line, len, i, out, out_len, true, cpp); continue; } // ---- Identifiers / keywords / types / literal prefixes ---- if (syn_is_alpha(c)) { int start = i; while (i < len && syn_is_alnum(line[i])) i++; if (cpp && i < len && line[i] == '"' && syn_is_raw_string_prefix(line + start, i - start)) { syn_fill_tokens(out, out_len, start, i, SYN_STRING); syn_consume_raw_string(line, len, i, out, out_len, state); continue; } if (cpp && i < len && line[i] == '"' && syn_is_string_prefix(line + start, i - start)) { // The quote itself is handled on the next iteration syn_fill_tokens(out, out_len, start, i, SYN_STRING); continue; } if (cpp && i < len && line[i] == '\'' && syn_is_string_prefix(line + start, i - start)) { syn_fill_tokens(out, out_len, start, i, SYN_CHAR); continue; } SynToken tok = syn_classify_word(lang, line + start, i - start); syn_fill_tokens(out, out_len, start, i, tok); continue; } // ---- Operators ---- if (c == '+' || c == '-' || c == '*' || c == '/' || c == '%' || c == '=' || c == '!' || c == '<' || c == '>' || c == '&' || c == '|' || c == '^' || c == '~' || c == '?' || c == ':') { syn_set_token(out, out_len, i, SYN_OPERATOR); i++; continue; } // ---- Everything else (whitespace, braces, parens, etc.) ---- syn_set_token(out, out_len, i, SYN_NORMAL); i++; } }