Skip to content

Commit

Permalink
Merge pull request #388 from fortran-lang/refactor/small-improvements
Browse files Browse the repository at this point in the history
refactor/small improvements
  • Loading branch information
gnikit authored May 7, 2024
2 parents 9d582ba + 09e87a6 commit 3f7da8b
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 64 deletions.
118 changes: 60 additions & 58 deletions fortls/parsers/internal/ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@
class FortranAST:
def __init__(self, file_obj=None):
self.file = file_obj
self.path: str = None
if file_obj is not None:
self.path = file_obj.path
self.path: str | None = file_obj.path if file_obj is not None else None
self.global_dict: dict = {}
self.scope_list: list = []
self.variable_list: list = []
Expand All @@ -39,10 +37,10 @@ def __init__(self, file_obj=None):
self.none_scope = None
self.inc_scope = None
self.current_scope = None
self.END_SCOPE_REGEX: Pattern = None
self.enc_scope_name: str = None
self.end_scope_regex: Pattern | None = None
self.enc_scope_name: str | None = None
self.last_obj = None
self.pending_doc: str = None
self.pending_doc: str | None = None

def create_none_scope(self):
"""Create empty scope to hold non-module contained items"""
Expand All @@ -60,7 +58,7 @@ def get_enc_scope_name(self):
def add_scope(
self,
new_scope: Scope,
END_SCOPE_REGEX: Pattern[str],
end_scope_regex: Pattern[str],
exportable: bool = True,
req_container: bool = False,
):
Expand All @@ -80,10 +78,10 @@ def add_scope(
else:
self.current_scope.add_child(new_scope)
self.scope_stack.append(self.current_scope)
if self.END_SCOPE_REGEX is not None:
self.end_stack.append(self.END_SCOPE_REGEX)
if self.end_scope_regex is not None:
self.end_stack.append(self.end_scope_regex)
self.current_scope = new_scope
self.END_SCOPE_REGEX = END_SCOPE_REGEX
self.end_scope_regex = end_scope_regex
self.enc_scope_name = self.get_enc_scope_name()
self.last_obj = new_scope
if self.pending_doc is not None:
Expand All @@ -102,9 +100,9 @@ def end_scope(self, line_number: int, check: bool = True):
else:
self.current_scope = None
if len(self.end_stack) > 0:
self.END_SCOPE_REGEX = self.end_stack.pop()
self.end_scope_regex = self.end_stack.pop()
else:
self.END_SCOPE_REGEX = None
self.end_scope_regex = None
self.enc_scope_name = self.get_enc_scope_name()

def add_variable(self, new_var: Variable):
Expand Down Expand Up @@ -170,11 +168,11 @@ def add_error(self, msg: str, sev: int, ln: int, sch: int, ech: int = None):
def start_ppif(self, line_number: int):
self.pp_if.append([line_number - 1, -1])

def end_ppif(self, line_number):
def end_ppif(self, line_number: int):
if len(self.pp_if) > 0:
self.pp_if[-1][1] = line_number - 1

def get_scopes(self, line_number: int = None):
def get_scopes(self, line_number: int | None = None):
"""Get a list of all the scopes present in the line number provided.
Parameters
Expand All @@ -191,65 +189,69 @@ def get_scopes(self, line_number: int = None):
return self.scope_list
scope_list = []
for scope in self.scope_list:
if (line_number >= scope.sline) and (line_number <= scope.eline):
if type(scope.parent) is Interface:
for use_stmnt in scope.use:
if type(use_stmnt) is not Import:
continue
# Exclude the parent and all other scopes
if use_stmnt.import_type == ImportTypes.NONE:
return [scope]
scope_list.append(scope)
scope_list.extend(iter(scope.get_ancestors()))
if not scope.sline <= line_number <= scope.eline:
continue
if type(scope.parent) is Interface:
for use_stmnt in scope.use:
if type(use_stmnt) is not Import:
continue
# Exclude the parent and all other scopes
if use_stmnt.import_type == ImportTypes.NONE:
return [scope]
scope_list.append(scope)
scope_list.extend(iter(scope.get_ancestors()))
if scope_list or self.none_scope is None:
return scope_list
else:
return [self.none_scope]
return [self.none_scope]

def get_inner_scope(self, line_number: int):
scope_sline = -1
curr_scope = None
for scope in self.scope_list:
if scope.sline > scope_sline and (
(line_number >= scope.sline) and (line_number <= scope.eline)
):
if scope.sline > scope_sline and scope.sline <= line_number <= scope.eline:
curr_scope = scope
scope_sline = scope.sline
if (curr_scope is None) and (self.none_scope is not None):
return self.none_scope
return curr_scope

def get_object(self, FQSN: str):
FQSN_split = FQSN.split("::")
curr_obj = self.global_dict.get(FQSN_split[0])
if curr_obj is None:
# Look for non-exportable scopes
for scope in self.scope_list:
if FQSN_split[0] == scope.FQSN:
curr_obj = scope
break
if curr_obj is None:
def find_child_by_name(parent, name):
for child in parent.children:
if child.name == name:
return child
if child.name.startswith("#GEN_INT"):
found = next(
(
int_child
for int_child in child.get_children()
if int_child.name == name
),
None,
)
if found:
return found
return None
if len(FQSN_split) > 1:
for name in FQSN_split[1:]:
next_obj = None
for child in curr_obj.children:
if child.name.startswith("#GEN_INT"):
for int_child in child.get_children():
if int_child.name == name:
next_obj = int_child
break
if next_obj is not None:
break
if child.name == name:
next_obj = child
break
if next_obj is None:
return None
curr_obj = next_obj
return curr_obj

def resolve_includes(self, workspace, path: str = None):

parts = FQSN.split("::")
current = self.global_dict.get(parts[0])

# Look for non-exportable scopes
if current is None:
current = next(
(scope for scope in self.scope_list if scope.FQSN == parts[0]), None
)
if current is None:
return None

for part in parts[1:]:
current = find_child_by_name(current, part)
if current is None:
return None

return current

def resolve_includes(self, workspace, path: str | None = None):
file_dir = os.path.dirname(self.path)
for inc in self.include_statements:
file_path = os.path.normpath(os.path.join(file_dir, inc.path))
Expand Down
9 changes: 4 additions & 5 deletions fortls/parsers/internal/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
from fortls.constants import (
DO_TYPE_ID,
INTERFACE_TYPE_ID,
MODULE_TYPE_ID,
SELECT_TYPE_ID,
SUBMODULE_TYPE_ID,
FRegex,
Expand Down Expand Up @@ -1352,7 +1351,7 @@ def parse(
line = multi_lines.pop()
line_stripped = line
# Test for scope end
if file_ast.END_SCOPE_REGEX is not None:
if file_ast.end_scope_regex is not None:
match = FRegex.END_WORD.match(line_no_comment)
# Handle end statement
if self.parse_end_scope_word(line_no_comment, line_no, file_ast, match):
Expand Down Expand Up @@ -1657,10 +1656,10 @@ def parse(
msg = "Visibility statement without enclosing scope"
file_ast.add_error(msg, Severity.error, line_no, 0)
else:
if (len(obj_info.obj_names) == 0) and (obj_info.type == 1):
if len(obj_info.obj_names) == 0 and obj_info.type == 1: # private
file_ast.current_scope.set_default_vis(-1)
else:
if obj_info.type == MODULE_TYPE_ID:
if obj_info.type == 1: # private
for word in obj_info.obj_names:
file_ast.add_private(word)
else:
Expand Down Expand Up @@ -1766,7 +1765,7 @@ def parse_end_scope_word(
):
file_ast.end_errors.append([ln, file_ast.current_scope.sline])
else:
scope_match = file_ast.END_SCOPE_REGEX.match(line[match.start(1) :])
scope_match = file_ast.end_scope_regex.match(line[match.start(1) :])
if scope_match is not None:
end_scope_word = scope_match.group(0)
if end_scope_word is not None:
Expand Down
8 changes: 8 additions & 0 deletions test/test_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,11 @@ def test_submodule():
except Exception as e:
print(e)
assert False


def test_private_visibility_interfaces():
file_path = test_dir / "vis" / "private.f90"
file = FortranFile(str(file_path))
err_str, _ = file.load_from_disk()
file.parse()
assert err_str is None
3 changes: 2 additions & 1 deletion test/test_source/.fortls
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"./diag/",
"docs",
"rename",
"parse"
"parse",
"vis"
]

}
17 changes: 17 additions & 0 deletions test/test_source/vis/private.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module visibility
private :: name
private :: generic_interf
interface name
module procedure :: name_sp
end interface name
interface
subroutine generic_interf(noop)
integer, intent(in) :: noop
end subroutine generic_interf
end interface
contains
subroutine name_sp(val)
real(4), intent(in) :: val
print *, 'name_sp', val
end subroutine name_sp
end module visibility

0 comments on commit 3f7da8b

Please sign in to comment.