--- Shared structural rules for GNU assembly dialects
-- @module common

local patterns = require("core.format.patterns")

local common = {}

-- Known C preprocessor directives before hash comments; backslash escapes newline
common.preprocessor_patterns = {
	{ pattern = { "%s*#%s*define%f[%W]", "\n", "\\" }, type = "preprocessor", line_start = true },
	{ pattern = { "%s*#%s*undef%f[%W]", "\n", "\\" }, type = "preprocessor", line_start = true },
	{ pattern = { "%s*#%s*include%f[%W]", "\n", "\\" }, type = "preprocessor", line_start = true },
	{ pattern = { "%s*#%s*include_next%f[%W]", "\n", "\\" }, type = "preprocessor", line_start = true },
	{ pattern = { "%s*#%s*if%f[%W]", "\n", "\\" }, type = "preprocessor", line_start = true },
	{ pattern = { "%s*#%s*ifdef%f[%W]", "\n", "\\" }, type = "preprocessor", line_start = true },
	{ pattern = { "%s*#%s*ifndef%f[%W]", "\n", "\\" }, type = "preprocessor", line_start = true },
	{ pattern = { "%s*#%s*elif%f[%W]", "\n", "\\" }, type = "preprocessor", line_start = true },
	{ pattern = { "%s*#%s*elifdef%f[%W]", "\n", "\\" }, type = "preprocessor", line_start = true },
	{ pattern = { "%s*#%s*elifndef%f[%W]", "\n", "\\" }, type = "preprocessor", line_start = true },
	{ pattern = { "%s*#%s*else%f[%W]", "\n", "\\" }, type = "preprocessor", line_start = true },
	{ pattern = { "%s*#%s*endif%f[%W]", "\n", "\\" }, type = "preprocessor", line_start = true },
	{ pattern = { "%s*#%s*line%f[%W]", "\n", "\\" }, type = "preprocessor", line_start = true },
	{ pattern = { "%s*#%s*error%f[%W]", "\n", "\\" }, type = "preprocessor", line_start = true },
	{ pattern = { "%s*#%s*warning%f[%W]", "\n", "\\" }, type = "preprocessor", line_start = true },
	{ pattern = { "%s*#%s*pragma%f[%W]", "\n", "\\" }, type = "preprocessor", line_start = true },
	{ pattern = { "%s*#%s*embed%f[%W]", "\n", "\\" }, type = "preprocessor", line_start = true },
}

-- Labels, strings, numeric forms stable across GNU target architectures
common.structure_patterns = {
	{ pattern = "%s*(%d+)(:)", type = { "label", "operator" }, line_start = true },
	{ pattern = "%s*([%.%a_][%w_%.]*%b())(:)", type = { "label", "operator" }, line_start = true },
	{ pattern = "%s*([%.%a_][%w_%.]*)(:)", type = { "label", "operator" }, line_start = true },
	{ pattern = "%d+[fb]", type = "constant" },
	{ pattern = { '"', '"', "\\" }, type = "string", delim_type = "operator" },
	{ pattern = { "'", "'", "\\" }, type = "string", delim_type = "operator" },
	{ pattern = "0[xX][%da-fA-F_]+%.[%da-fA-F_]*[pP][%+%-]?%d+", type = "number" },
	{ pattern = "%d[%d_]*%.%d*[eE][%+%-]?%d+", type = "number" },
	{ pattern = "%d[%d_]*%.%d*", type = "number" },
	{ pattern = "0[xX][%da-fA-F_]+", type = "number" },
	{ pattern = "0[bB][01_]+", type = "number" },
	{ pattern = "0[oO][0-7_]+", type = "number" },
	{ pattern = "%d[%d_]*", type = "number" },
}

-- ARM uses '#' for immediate operands, so these rules must precede hash comments
common.arm_immediate_patterns = {
	{ pattern = "#[%+%-]?0[xX][%da-fA-F_]+", type = "number" },
	{ pattern = "#[%+%-]?%d+", type = "number" },
	{ pattern = "#[%a_][%w_%.%-]*", type = "constant" },
}

-- ARM's '@' comment marker is valid at line start or after whitespace, not inside symbol suffixes
common.arm_comment_patterns = {
	{ pattern = "%s*(@.-\n)", type = { "comment" }, line_start = true },
	{ pattern = "([ \t]+)(@.-\n)", type = { "normal", "comment" } },
}

-- GNU source may pass through the C preprocessor before reaching assembler
common.c_comment_patterns = {
	patterns.line_comment("//"),
	patterns.block_comment(),
}

-- GNU directives are shared even when operands depend on target architecture
common.gnu_symbols = {
	[".align"] = "keyword",
	[".arch"] = "keyword",
	[".ascii"] = "keyword",
	[".asciz"] = "keyword",
	[".balign"] = "keyword",
	[".bss"] = "keyword",
	[".byte"] = "keyword",
	[".comm"] = "keyword",
	[".data"] = "keyword",
	[".double"] = "keyword",
	[".equ"] = "keyword",
	[".extern"] = "keyword",
	[".file"] = "keyword",
	[".float"] = "keyword",
	[".global"] = "keyword",
	[".globl"] = "keyword",
	[".hidden"] = "keyword",
	[".include"] = "keyword",
	[".long"] = "keyword",
	[".macro"] = "keyword",
	[".p2align"] = "keyword",
	[".section"] = "keyword",
	[".set"] = "keyword",
	[".short"] = "keyword",
	[".size"] = "keyword",
	[".string"] = "keyword",
	[".text"] = "keyword",
	[".type"] = "keyword",
	[".word"] = "keyword",
}

--- Return GNU directives combined with one architecture vocabulary
-- @param vocabulary table|nil architecture-specific symbols
-- @return table symbols
function common.with_gnu_symbols(vocabulary)
	local symbols = {}

	for name, token_type in pairs(common.gnu_symbols) do
		symbols[name] = token_type
	end
	for name, token_type in pairs(vocabulary or {}) do
		symbols[name] = token_type
	end

	return symbols
end

-- Unknown GNU mnemonics still receive structural instruction highlighting at line start
common.instruction_patterns = {
	{ pattern = "%s*([%a_][%w_%.]*)([ \t]+)", type = { "keyword", "normal" }, line_start = true },
}

-- Dotted directives and ordinary identifiers share one vocabulary lookup path
common.symbol_patterns = {
	{ pattern = "[%.%a_][%w_%.]*", type = "symbol" },
}

-- Shared block-comment editing shape for preprocessed GNU assembly
common.block_comment = {
	block_open = "/*",
	block_close = "*/",
	block_line_prefix = " * ",
	close_line_prefix = " ",
	open_on_own_line = false,
	close_on_own_line = true,
	strip_line_prefix = "^%s*%*%s?",
	allow_nested = false,
}

-- Named labels provide common assembly outline
common.outline_patterns = {
	{ pattern = "%s*[%.%a_][%w_%.]*%b():", line_start = true },
	{ pattern = "%s*[%.%a_][%w_%.]*:", line_start = true },
}

return common
