Coverage for src/castep_linter/fortran/argument_parser.py: 89%

1"""Module containing an argument list parser"""

2 from enum import Enum, auto

3 from typing import ClassVar, Dict, List, Optional, Tuple

4

5 from castep_linter.fortran.fortran_node import Fortran, FortranNode, WrongNodeError

6 from castep_linter.fortran.identifier import Identifier

7

8

9 class ArgType(Enum):

10 """Types of arguments for fortran functions/subroutines"""

11

12 KEYWORD = auto()

13 POSITION = auto()

14 # NONE = auto()

15

16

17 class ArgParser:

18 """Parser for fortran argument lists"""

19

20 ALLOWED_NODES: ClassVar[List[Fortran]] = [Fortran.ARGUMENT_LIST]

21 ArgType = ArgType

22

23 def __init__(self, arg_list: Optional[FortranNode] = None):

24 if arg_list:

25 self.args, self.kwargs = parse_arg_list(arg_list)

26 else:

27 self.args, self.kwargs = [], {}

28

29 def get(self, keyword: Identifier, position: Optional[int] = None):

30 """Return a value from a fortran argument list by keyword and optionally position"""

31 if position and len(self.args) >= position:

32 return ArgType.POSITION, self.args[position - 1]

33 if keyword in self.kwargs:

34 return ArgType.KEYWORD, self.kwargs[keyword]

35

36 err = f"Argument {keyword} not found in argument list"

37 raise KeyError(err)

38

39

40 def parse_arg_list(node: FortranNode) -> Tuple[List[FortranNode], Dict[Identifier, FortranNode]]:

41 """

42 Convert a fortran argument list into a args, kwargs pair.

43 The keyword arguments will be converted into identifiers.

44 """

45 if not node.is_type(Fortran.ARGUMENT_LIST): 45 ↛ 46line 45 didn't jump to line 46, because the condition on line 45 was never true

46 err = "Expected argument list"

47 raise WrongNodeError(err)

48

49 args = []

50 kwargs = {}

51

52 parsing_arg_list = True

53

54 for child in node.children[1:-1:2]:

55 if child.is_type(Fortran.KEYWORD_ARGUMENT):

56 parsing_arg_list = False

57

58 if parsing_arg_list:

59 args.append(child)

60 elif child.is_type(Fortran.KEYWORD_ARGUMENT): 60 ↛ 64line 60 didn't jump to line 64, because the condition on line 60 was never false

61 key, _, value = child.children

62 kwargs[Identifier.from_node(key)] = value

63 else:

64 err = f"Unknown argument list item in keyword arguments: {child.type}"

65 raise ValueError(err)

66

67 return args, kwargs