Inintal commit
This commit is contained in:
+267
@@ -0,0 +1,267 @@
|
||||
-- SCL Parser utilities for the LSP server
|
||||
local M = {}
|
||||
|
||||
function M.extract_variables(content)
|
||||
local variables = {}
|
||||
local variable_positions = {}
|
||||
|
||||
local lines = {}
|
||||
for line in content:gmatch("([^\n]*)\n") do
|
||||
table.insert(lines, line)
|
||||
end
|
||||
|
||||
local in_var_section = false
|
||||
local var_type = "unknown"
|
||||
local current_block = nil
|
||||
|
||||
local function process_line(line, line_num)
|
||||
local trimmed = line:gsub("^%s+", ""):gsub("%s+$", "")
|
||||
|
||||
-- Detect block start
|
||||
local fb_name = trimmed:match('FUNCTION_BLOCK%s+"([^"]+)"')
|
||||
if fb_name then
|
||||
current_block = { name = fb_name, kind = "function_block" }
|
||||
in_var_section = false
|
||||
return
|
||||
end
|
||||
|
||||
local ob_name = trimmed:match('ORGANIZATION_BLOCK%s+"([^"]+)"')
|
||||
if ob_name then
|
||||
current_block = { name = ob_name, kind = "organization_block" }
|
||||
in_var_section = false
|
||||
return
|
||||
end
|
||||
|
||||
local fn_name = trimmed:match('FUNCTION%s+"([^"]+)"')
|
||||
if fn_name then
|
||||
current_block = { name = fn_name, kind = "function" }
|
||||
in_var_section = false
|
||||
return
|
||||
end
|
||||
|
||||
if trimmed:match("^END_FUNCTION_BLOCK") or
|
||||
trimmed:match("^END_ORGANIZATION_BLOCK") or
|
||||
trimmed:match("^END_FUNCTION") then
|
||||
current_block = nil
|
||||
in_var_section = false
|
||||
return
|
||||
end
|
||||
|
||||
if trimmed:match("^VAR[_A-Z]*%s*$") or
|
||||
trimmed:match("^VAR_INPUT%s*$") or
|
||||
trimmed:match("^VAR_OUTPUT%s*$") or
|
||||
trimmed:match("^VAR_IN_OUT%s*$") or
|
||||
trimmed:match("^VAR_TEMP%s*$") or
|
||||
trimmed:match("^VAR[_ ]*CONSTANT%s*$") or
|
||||
trimmed:match("^VAR[_ ]*RETAIN%s*$") or
|
||||
trimmed:match("^VAR[_ ]*NON_RETAIN%s*$") then
|
||||
in_var_section = true
|
||||
if trimmed:match("INPUT") then var_type = "VAR_INPUT"
|
||||
elseif trimmed:match("OUTPUT") then var_type = "VAR_OUTPUT"
|
||||
elseif trimmed:match("IN_OUT") then var_type = "VAR_IN_OUT"
|
||||
elseif trimmed:match("TEMP") then var_type = "VAR_TEMP"
|
||||
elseif trimmed:match("CONSTANT") then var_type = "VAR_CONSTANT"
|
||||
elseif trimmed:match("RETAIN") then var_type = "VAR_RETAIN"
|
||||
elseif trimmed:match("NON_RETAIN") then var_type = "VAR_NON_RETAIN"
|
||||
else var_type = "VAR" end
|
||||
return
|
||||
end
|
||||
|
||||
if trimmed:match("^END_VAR%s*$") then
|
||||
in_var_section = false
|
||||
return
|
||||
end
|
||||
|
||||
if not in_var_section then return end
|
||||
|
||||
if trimmed:match("^%(%*") or trimmed:match("^%s*//") then
|
||||
return
|
||||
end
|
||||
|
||||
local var_name = trimmed:match("^([%w_]+)%s*:")
|
||||
if not var_name then
|
||||
var_name = trimmed:match("^([%w_]+)%s*,")
|
||||
end
|
||||
|
||||
if var_name and not variables[var_name] then
|
||||
local col = line:find(var_name, 1, true)
|
||||
if col then
|
||||
local type_start = line:find(":", 1, true)
|
||||
local var_data_type = "unknown"
|
||||
if type_start then
|
||||
local type_part = line:sub(type_start + 1)
|
||||
type_part = type_part:gsub("%s*:=.*$", "")
|
||||
type_part = type_part:gsub("%s*;.*$", "")
|
||||
type_part = type_part:gsub("^%s+", "")
|
||||
type_part = type_part:gsub("%s+$", "")
|
||||
var_data_type = type_part
|
||||
end
|
||||
|
||||
variables[var_name] = {
|
||||
type = var_type,
|
||||
data_type = var_data_type,
|
||||
line = line_num,
|
||||
character = col - 1,
|
||||
}
|
||||
variable_positions[var_name] = {
|
||||
line = line_num,
|
||||
character = col - 1,
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for i, line in ipairs(lines) do
|
||||
process_line(line, i - 1)
|
||||
end
|
||||
|
||||
return variables, variable_positions
|
||||
end
|
||||
|
||||
function M.extract_types(content)
|
||||
local types = {}
|
||||
|
||||
local lines = {}
|
||||
for line in content:gmatch("([^\n]*)\n") do
|
||||
table.insert(lines, line)
|
||||
end
|
||||
|
||||
local in_type_section = false
|
||||
local current_type = nil
|
||||
local brace_depth = 0
|
||||
|
||||
for i, line in ipairs(lines) do
|
||||
local trimmed = line:gsub("^%s+", ""):gsub("%s+$", "")
|
||||
|
||||
if trimmed:match("^TYPE$") or trimmed:match("^TYPE%s+:=") then
|
||||
in_type_section = true
|
||||
brace_depth = 0
|
||||
elseif in_type_section and trimmed:match("^END_TYPE") then
|
||||
in_type_section = false
|
||||
current_type = nil
|
||||
elseif in_type_section then
|
||||
local type_name = trimmed:match("^(%w+):")
|
||||
if type_name then
|
||||
local struct_start = trimmed:find("{")
|
||||
local array_match = trimmed:find("ARRAY")
|
||||
|
||||
if struct_start then
|
||||
current_type = {
|
||||
name = type_name,
|
||||
kind = "struct",
|
||||
fields = {},
|
||||
start_line = i - 1,
|
||||
}
|
||||
types[type_name] = current_type
|
||||
elseif array_match then
|
||||
local of_type = trimmed:match("OF%s+(%w+)")
|
||||
if of_type then
|
||||
current_type = {
|
||||
name = type_name,
|
||||
kind = "array",
|
||||
element_type = of_type,
|
||||
start_line = i - 1,
|
||||
}
|
||||
types[type_name] = current_type
|
||||
end
|
||||
else
|
||||
local field_type = trimmed:match(":%s*(%w+)")
|
||||
if field_type then
|
||||
current_type = {
|
||||
name = type_name,
|
||||
kind = "alias",
|
||||
base_type = field_type,
|
||||
start_line = i - 1,
|
||||
}
|
||||
types[type_name] = current_type
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if current_type and current_type.kind == "struct" then
|
||||
for open in line:gmatch("{") do brace_depth = brace_depth + 1 end
|
||||
for close in line:gmatch("}") do brace_depth = brace_depth - 1 end
|
||||
|
||||
local field_name = line:match("^%s*(%w+):")
|
||||
if field_name and field_name ~= current_type.name then
|
||||
local field_type = line:match(":%s*(%w+)")
|
||||
table.insert(current_type.fields, {
|
||||
name = field_name,
|
||||
type = field_type or "unknown",
|
||||
})
|
||||
end
|
||||
|
||||
if brace_depth == 0 then
|
||||
current_type = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return types
|
||||
end
|
||||
|
||||
function M.extract_functions(content)
|
||||
local functions = {}
|
||||
|
||||
local lines = {}
|
||||
for line in content:gmatch("([^\n]*)\n") do
|
||||
table.insert(lines, line)
|
||||
end
|
||||
|
||||
local function find_block_end(start_line, block_name, end_markers)
|
||||
local depth = 1
|
||||
for i = start_line + 1, #lines do
|
||||
local line = lines[i]
|
||||
if line:match("^%s*" .. block_name) then
|
||||
depth = depth + 1
|
||||
end
|
||||
for _, marker in ipairs(end_markers) do
|
||||
if line:match("^%s*" .. marker) then
|
||||
depth = depth - 1
|
||||
if depth == 0 then return i end
|
||||
end
|
||||
end
|
||||
end
|
||||
return #lines
|
||||
end
|
||||
|
||||
for i, line in ipairs(lines) do
|
||||
local func_name = line:match('FUNCTION%s+"([^"]+)"')
|
||||
if func_name then
|
||||
local end_line = find_block_end(i, "END_FUNCTION", { "END_FUNCTION" })
|
||||
table.insert(functions, {
|
||||
name = func_name,
|
||||
kind = "function",
|
||||
start = i - 1,
|
||||
final = end_line - 1,
|
||||
})
|
||||
end
|
||||
|
||||
local fb_name = line:match('FUNCTION_BLOCK%s+"([^"]+)"')
|
||||
if fb_name then
|
||||
local end_line = find_block_end(i, "END_FUNCTION_BLOCK", { "END_FUNCTION_BLOCK" })
|
||||
table.insert(functions, {
|
||||
name = fb_name,
|
||||
kind = "function_block",
|
||||
start = i - 1,
|
||||
final = end_line - 1,
|
||||
})
|
||||
end
|
||||
|
||||
local ob_name = line:match('ORGANIZATION_BLOCK%s+"([^"]+)"')
|
||||
if ob_name then
|
||||
local end_line = find_block_end(i, "END_ORGANIZATION_BLOCK", { "END_ORGANIZATION_BLOCK" })
|
||||
table.insert(functions, {
|
||||
name = ob_name,
|
||||
kind = "organization_block",
|
||||
start = i - 1,
|
||||
final = end_line - 1,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
return functions
|
||||
end
|
||||
|
||||
return M
|
||||
Reference in New Issue
Block a user