-- Common functions used by different modules of the static analyzer explcheck.

-- Convert a byte number in a file to a line and column number in a file.
local function convert_byte_to_line_and_column(line_starting_byte_numbers, byte_number)
 local line_number = 0
 for _, line_starting_byte_number in ipairs(line_starting_byte_numbers) do
   if line_starting_byte_number > byte_number then
     break
   end
   line_number = line_number + 1
 end
 assert(line_number > 0)
 local line_starting_byte_number = line_starting_byte_numbers[line_number]
 assert(line_starting_byte_number <= byte_number)
 local column_number = byte_number - line_starting_byte_number + 1
 return line_number, column_number
end

-- Convert a pathname of a file to the suffix of the file.
local function get_suffix(pathname)
 return pathname:gsub(".*%.", "."):lower()
end

-- Convert a pathname of a file to the base name of the file.
local function get_basename(pathname)
 return pathname:gsub(".*[\\/]", "")
end

-- Convert a pathname of a file to the stem of the file.
local function get_stem(pathname)
 return get_basename(pathname):gsub("%..*", "")
end

-- Convert a pathname of a file to the pathname of its parent directory.
local function get_parent(pathname)
 if pathname:find("[\\/]") then
   return pathname:gsub("(.*)[\\/].*", "%1")
 else
   return "."
 end
end

-- Return all parameters unchanged, mostly used for no-op map-back and map-forward functions.
local function identity(...)
 return ...
end

-- Run all processing steps.
local function process_with_all_steps(pathname, content, issues, analysis_results, options)
 local get_option = require("explcheck-config").get_option
 local preprocessing = require("explcheck-preprocessing")
 local lexical_analysis = require("explcheck-lexical-analysis")
 local syntactic_analysis = require("explcheck-syntactic-analysis")
 local semantic_analysis = require("explcheck-semantic-analysis")
 local steps = {preprocessing, lexical_analysis, syntactic_analysis, semantic_analysis}
 for _, step in ipairs(steps) do
   step.process(pathname, content, issues, analysis_results, options)
   -- If a processing step ended with error, skip all following steps.
   if #issues.errors > 0 and get_option('fail_fast', options, pathname) then
     return
   end
 end
end

return {
 convert_byte_to_line_and_column = convert_byte_to_line_and_column,
 get_basename = get_basename,
 get_parent = get_parent,
 get_stem = get_stem,
 get_suffix = get_suffix,
 identity = identity,
 process_with_all_steps = process_with_all_steps,
}