lizard 1.17.13__py2.py3-none-any.whl → 1.17.15__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lizard
3
- Version: 1.17.13
3
+ Version: 1.17.15
4
4
  Summary: A code analyzer without caring the C/C++ header files. It works with Java, C/C++, JavaScript, Python, Ruby, Swift, Objective C. Metrics includes cyclomatic complexity number etc.
5
5
  Home-page: http://www.lizard.ws
6
6
  Download-URL: https://pypi.python.org/lizard/
@@ -53,7 +53,8 @@ A list of supported languages:
53
53
  - Java
54
54
  - C# (C Sharp)
55
55
  - JavaScript (With ES6 and JSX)
56
- - TypeScript
56
+ - TypeScript (With TSX)
57
+ - VueJS
57
58
  - Objective-C
58
59
  - Swift
59
60
  - Python
@@ -69,6 +70,7 @@ A list of supported languages:
69
70
  - Kotlin
70
71
  - Solidity
71
72
  - Erlang
73
+ - Zig
72
74
 
73
75
  By default lizard will search for any source code that it knows and mix
74
76
  all the results together. This might not be what you want. You can use
@@ -386,5 +388,3 @@ Lizard is also used as a plugin for fastlane to help check code complexity and s
386
388
  - `European research project FASTEN (Fine-grained Analysis of SofTware Ecosystems as Networks, <http://fasten-project.eu/)>`_
387
389
  - `for a quality analyzer <https://github.com/fasten-project/quality-analyzer>`_
388
390
 
389
-
390
-
@@ -1,7 +1,7 @@
1
- lizard.py,sha256=KR3F6F-5GzGQP0GLxjWmpRRUsLbfL-Lthtz3jWLlyPY,38576
1
+ lizard.py,sha256=F6eF_WT_arglGadw__oqYk_NMgEVk-8idmybIrtd2OE,38568
2
2
  lizard_ext/__init__.py,sha256=Cz0pvH6QHyadK-rAeeEhQVsm0hW5M-fBQgV4FBaX9zk,500
3
3
  lizard_ext/auto_open.py,sha256=byD_RbeVhvSUhR2bJMRitvA3zcKEapFwv0-XaDJ6GFo,1096
4
- lizard_ext/csvoutput.py,sha256=IGTGICiK_IJcldRGDhd_jNDohxFWJEWYOVxkemaY20Q,3051
4
+ lizard_ext/csvoutput.py,sha256=43fhmo8kB85qcdujCwySGNuTC4FkKUPLqIApPeljPnA,2663
5
5
  lizard_ext/default_ordered_dict.py,sha256=YbVz6nPlQ6DjWc_EOFBz6AJN2XLo9dpnUdeyejQvUDE,831
6
6
  lizard_ext/extension_base.py,sha256=rnjUL2mqSGToUVYydju7fa8ZwynLPY8S1F17gIJP55I,346
7
7
  lizard_ext/htmloutput.py,sha256=oavhEzCIJtoJrFveiWIp0pMAOwg4pukJ6l07IpmPiag,4426
@@ -25,39 +25,43 @@ lizard_ext/lizardns.py,sha256=8pztUoRS_UWN24MawwxeHEJgYh49id5PWODUBb6O72U,4184
25
25
  lizard_ext/lizardoutside.py,sha256=FGm2tbBZ17-2OCgmQlD-vobUCfQKb0FAygf86eM3xuM,336
26
26
  lizard_ext/lizardstatementcount.py,sha256=xYk6ixSIItSE1BWQXzrWmduFgGhA3VR817SNKLffyVQ,1182
27
27
  lizard_ext/lizardwordcount.py,sha256=2QYXD7-AtkkgAbi9VSidunMbSsGQ7MKYb6IT-bS-cok,7575
28
- lizard_ext/version.py,sha256=cV8SELFV7_EfiIev7wFOD2wke6gb9on99S4p_3CSUAc,182
28
+ lizard_ext/version.py,sha256=bEnVphLi5lD3nsGDAkiBaNX1JY-mQDoiFcSVF8rk7zc,182
29
29
  lizard_ext/xmloutput.py,sha256=-cbh0he4O_X-wX56gkv9AnSPNN0qvR7FACqlBeezUS4,5609
30
- lizard_languages/__init__.py,sha256=iIbCcjN1AzmVtqCB7caRcLWQ4CdQwyT0UAiRKCkVeL0,1273
31
- lizard_languages/clike.py,sha256=YgB0dHYdQ5Ae1pN00zcstkuXl94n8Ugo-aTYUNmJr7o,10723
32
- lizard_languages/code_reader.py,sha256=95_juW_8vcTShV_BoWNmBiz5CmCk59VxRi0QWF67Bsw,6164
30
+ lizard_languages/__init__.py,sha256=2mvrPWMJVCq981kMjoAPEi2O07C8vo-vUWOG_wp7vKQ,1473
31
+ lizard_languages/clike.py,sha256=Lnwkma-jEu8FNVEPqTAKPkz1Hi0T_dP7H9Jyq0Bd0kQ,10725
32
+ lizard_languages/code_reader.py,sha256=P0PkE4QZBWOEj6cFHA4hj3hcLZLcGnqH31EmtltxlaE,6240
33
33
  lizard_languages/csharp.py,sha256=lVi-g-0JjHagqDR5r-lRdi2BuCdtR5aer9CYY-Nzw44,478
34
- lizard_languages/erlang.py,sha256=AaPfIGq_phVi2hRUVHNrrmXiQSdlEktbSVOvPxORWf8,3822
35
- lizard_languages/fortran.py,sha256=iXxS44sNJ2_h-nvJC58Pz6AGDyVx2HdBEVF-EeYGn84,7327
34
+ lizard_languages/erlang.py,sha256=7YJS2cMyXDKEV_kpH8DzBARxFCFcjKuTOPSQ3K52auU,3860
35
+ lizard_languages/fortran.py,sha256=KATDsnfjob5W3579A_VxbwrbTkK7Rx3p0eXdBgjx25I,8973
36
36
  lizard_languages/gdscript.py,sha256=KwlGoODilQnFgXvODpq_XlA6fV3hGbN9fd7bsiEUn78,637
37
37
  lizard_languages/go.py,sha256=dSbWxtWve7KSRW5i9w4DxhODtJ72EcBNrgp-7Xhn0Sg,465
38
38
  lizard_languages/golike.py,sha256=vRIfjTVvc0VmJf27lTOLht55ZF1AQ9wn0Fvu-9WabWk,2858
39
39
  lizard_languages/java.py,sha256=mfXCRicmZFZFxPW7QuTOQtAKzOZD3d-uh4tiALcGdX4,1186
40
- lizard_languages/javascript.py,sha256=agcEwJbrY0tFt8rGWwsO2pH-MKXvKmt9BMlcKkUB9m4,4314
41
- lizard_languages/js_style_language_states.py,sha256=2_dgrXLe113xEdfATByioohGquW428pPo9gI3I1duS4,4257
42
- lizard_languages/js_style_regex_expression.py,sha256=1d4eK5RR0GBAFg2eDb2cKA_YHIYlxjhG7PXAuMOKnRA,743
40
+ lizard_languages/javascript.py,sha256=sGZeTqFqCjcl87ncR-Rmj-HTp-4s4ptoTMxeDgc8Jvw,522
41
+ lizard_languages/js_style_language_states.py,sha256=Ie0eA15rd0gfahdNzUlSkQa-o_j5idTIB82JU32c378,4165
42
+ lizard_languages/js_style_regex_expression.py,sha256=Xgyogch4xElYtCG4EnBKvalHTl3tjRPcIIcIQRRd61I,1970
43
+ lizard_languages/jsx.py,sha256=tNoGFQ9hap2yS5NjRPgxMRlYZY-k5Gn07yI0o815Ghk,3566
43
44
  lizard_languages/kotlin.py,sha256=v_o2orEzA5gB9vM_0h-E4QXjrc5Yum-0K6W6_laOThc,2844
44
45
  lizard_languages/lua.py,sha256=3nqBcunBzJrhv4Iqaf8xvbyqxZy3aSxJ-IiHimHFlac,1573
45
46
  lizard_languages/objc.py,sha256=2a1teLdaXZBtCeFiIZer1j_sVx9LZ1CbF2XfnqlvLmk,2319
46
47
  lizard_languages/php.py,sha256=8OufyR2TIWyf3bXuTmp37Vwf_tEPF4YV5M3i5_jnguA,1377
47
- lizard_languages/python.py,sha256=JDXqQ7SEnNSXlbK5sMyUKwCFBTEto20Wg8_ZkzYisSI,3721
48
+ lizard_languages/python.py,sha256=5MwhyuzUqbUkhNyzeo_YsTRp4onMUVC19t61lLnDyjc,3761
48
49
  lizard_languages/ruby.py,sha256=gUrFsaS6ZJbjb9CixBHP9n0BxGOb8HsDEg7jJ2bz_80,2187
49
50
  lizard_languages/rubylike.py,sha256=dAGZ2wqW8nqaESMU8HkeR9gwQ-q9fmZqE6AANvVZD1Q,3426
50
51
  lizard_languages/rust.py,sha256=DG2RkR9JWwcD6gIw-BPzg-Yo_lxQtSvfgHioFWIQm9o,610
51
52
  lizard_languages/scala.py,sha256=6Jr_TG945VYqB3o5weD7jN7S4beHt4aVj3r-fmKeMAM,1316
52
- lizard_languages/script_language.py,sha256=FOzB9Rg5-XTnFDeeRRyIrp2rDomRDHhaPsxsxeCx9GU,539
53
+ lizard_languages/script_language.py,sha256=UUO3Wjkoa-ZqwwvcwvhOr5tg1rVavcrEYx3dNdoYSBc,531
53
54
  lizard_languages/solidity.py,sha256=Z0GD7U5bI5eUikdy7m_iKWeFD5yXRYq4r3zycscOhJQ,553
54
55
  lizard_languages/swift.py,sha256=p8S2OAkQOx9YQ02yhoVXFkr7pMqUH1Nb3RVXPHRU_9M,2450
55
56
  lizard_languages/tnsdl.py,sha256=pGcalA_lHY362v2wwPS86seYBOOBBjvmU6vd4Yy3A9g,2803
57
+ lizard_languages/tsx.py,sha256=aTxmM_-u-R-pRVXRVyITJxrrFZfCeMe9kxMgaoL-qfk,706
56
58
  lizard_languages/ttcn.py,sha256=ygjw_raBmPF-4mgoM8m6CAdyEMpTI-n1kZJK1RL4Vxo,2131
57
- lizard_languages/typescript.py,sha256=yjUTVDkiONbg2v089pZC4wrCJSo1J5NVbQzbCfF4k6s,1547
58
- lizard-1.17.13.dist-info/LICENSE.txt,sha256=05ZjgQ8Cl1dD9p0BhW-Txzkc5rhCogGJVEuf1GT2Y_M,1303
59
- lizard-1.17.13.dist-info/METADATA,sha256=XwvrQB7WJSc664t-xQovyEcKuufzKizZCnyUPSTowq8,15403
60
- lizard-1.17.13.dist-info/WHEEL,sha256=fS9sRbCBHs7VFcwJLnLXN1MZRR0_TVTxvXKzOnaSFs8,110
61
- lizard-1.17.13.dist-info/entry_points.txt,sha256=pPMMwoHAltzGHqR2WeJQItLeeyR7pbX5R2S_POC-xoo,40
62
- lizard-1.17.13.dist-info/top_level.txt,sha256=5NTrTaOLhHuTzXaGcZPKfuaOgUv7WafNGe0Zl5aycpg,35
63
- lizard-1.17.13.dist-info/RECORD,,
59
+ lizard_languages/typescript.py,sha256=c_Kez4pOGMXhdo6q3HT-clEJDP7LuRIbBkkJSipComU,3568
60
+ lizard_languages/vue.py,sha256=KXUBUo2R1zNF8Pffrz_KsQEN44m5XFRMoGXylxKUeT0,1038
61
+ lizard_languages/zig.py,sha256=NX1iyBstBuJFeAGBOAIaRfrmeBREne2HX6Pt4fXZZTQ,586
62
+ lizard-1.17.15.dist-info/LICENSE.txt,sha256=05ZjgQ8Cl1dD9p0BhW-Txzkc5rhCogGJVEuf1GT2Y_M,1303
63
+ lizard-1.17.15.dist-info/METADATA,sha256=P5tZ8sJtxBdTVX6qYePWJc_NuXcA7QI3yp4gomL0lgA,15428
64
+ lizard-1.17.15.dist-info/WHEEL,sha256=Kh9pAotZVRFj97E15yTA4iADqXdQfIVTHcNaZTjxeGM,110
65
+ lizard-1.17.15.dist-info/entry_points.txt,sha256=ZBqPhu-J3NoGGW5vn2Gfyoo0vdVlgBgM-wlNm0SGYUQ,39
66
+ lizard-1.17.15.dist-info/top_level.txt,sha256=5NTrTaOLhHuTzXaGcZPKfuaOgUv7WafNGe0Zl5aycpg,35
67
+ lizard-1.17.15.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.44.0)
2
+ Generator: bdist_wheel (0.45.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py2-none-any
5
5
  Tag: py3-none-any
@@ -1,3 +1,2 @@
1
1
  [console_scripts]
2
2
  lizard = lizard:main
3
-
lizard.py CHANGED
@@ -298,8 +298,7 @@ class FunctionInfo(Nesting): # pylint: disable=R0902
298
298
  return self.name.split('::')[-1]
299
299
 
300
300
  location = property(lambda self:
301
- " %(name)s@%(start_line)s-%(end_line)s@%(filename)s"
302
- % self.__dict__)
301
+ f" {self.name}@{self.start_line}-{self.end_line}@{self.filename}")
303
302
 
304
303
  parameter_count = property(lambda self: len(self.parameters))
305
304
 
@@ -572,7 +571,9 @@ class FileAnalyzer(object): # pylint: disable=R0903
572
571
  for _ in reader(tokens, reader):
573
572
  pass
574
573
  except RecursionError as e:
575
- sys.stderr.write("[skip] fail to process '%s' with RecursionError - %s\n" % (filename, e))
574
+ sys.stderr.write(
575
+ "[skip] fail to process '%s' with RecursionError - %s\n" %
576
+ (filename, e))
576
577
  return context.fileinfo
577
578
 
578
579
 
@@ -721,17 +722,13 @@ class OutputScheme(object):
721
722
  if e.get("avg_caption", None)])
722
723
 
723
724
  def clang_warning_format(self):
724
- return (
725
- "{f.filename}:{f.start_line}: warning: {f.name} has " +
726
- ", ".join([
727
- "{{f.{ext[value]}}} {caption}"
728
- .format(ext=e, caption=e['caption'].strip())
729
- for e in self.items[:-1]
730
- ]))
725
+ return ("{f.filename}:{f.start_line}: warning: {f.name} has {f.nloc} NLOC, "
726
+ "{f.cyclomatic_complexity} CCN, {f.token_count} token, {f.parameter_count} PARAM, "
727
+ "{f.length} length, {f.max_nesting_depth} ND")
731
728
 
732
729
  def msvs_warning_format(self):
733
730
  return (
734
- "{f.filename}({f.start_line}): warning: {f.name} has " +
731
+ "{f.filename}({f.start_line}): warning: {f.name} ({f.long_name}) has " +
735
732
  ", ".join([
736
733
  "{{f.{ext[value]}}} {caption}"
737
734
  .format(ext=e, caption=e['caption'].strip())
lizard_ext/csvoutput.py CHANGED
@@ -36,9 +36,9 @@ def csv_output(result, options):
36
36
  if options.verbose:
37
37
  extension_caption = ""
38
38
  for caption in extension_captions:
39
- extension_caption = "{},{}".format(extension_caption, caption)
40
- print("NLOC,CCN,token,PARAM,length,location,file,function," +
41
- "long_name,start,end{}".format(extension_caption))
39
+ extension_caption = f"{extension_caption},{caption}"
40
+ print(f"NLOC,CCN,token,PARAM,length,location,file,function," +
41
+ f"long_name,start,end{extension_caption}")
42
42
 
43
43
  for source_file in result:
44
44
  if source_file:
@@ -46,25 +46,11 @@ def csv_output(result, options):
46
46
  if source_function:
47
47
  extension_string = ''
48
48
  for variable in extension_variables:
49
- extension_string = '{},{}'.\
50
- format(extension_string,
51
- source_function.__getattribute__(variable))
52
- print('{},{},{},{},{},"{}","{}","{}","{}",{},{}{}'.format(
53
- source_function.nloc,
54
- source_function.cyclomatic_complexity,
55
- source_function.token_count,
56
- len(source_function.parameters),
57
- source_function.length,
58
- "{}@{}-{}@{}".format(
59
- source_function.name.replace("\"", "'"),
60
- source_function.start_line,
61
- source_function.end_line,
62
- source_file.filename
63
- ),
64
- source_file.filename,
65
- source_function.name.replace("\"", "'"),
66
- source_function.long_name.replace("\"", "'"),
67
- source_function.start_line,
68
- source_function.end_line,
69
- extension_string
70
- ))
49
+ extension_string = f"{extension_string},{source_function.__getattribute__(variable)}"
50
+ print(f"{source_function.nloc},{source_function.cyclomatic_complexity},"
51
+ f"{source_function.token_count},{len(source_function.parameters)},"
52
+ f'{source_function.length},"{source_function.name.replace(chr(34), chr(39))}@'
53
+ f'{source_function.start_line}-{source_function.end_line}@{source_file.filename}",'
54
+ f'"{source_file.filename}","{source_function.name.replace(chr(34), chr(39))}",'
55
+ f'"{source_function.long_name.replace(chr(34), chr(39))}",'
56
+ f"{source_function.start_line},{source_function.end_line}{extension_string}")
lizard_ext/version.py CHANGED
@@ -3,4 +3,4 @@
3
3
  #
4
4
  # pylint: disable=missing-docstring,invalid-name
5
5
 
6
- version = "1.17.13"
6
+ version = "1.17.15"
@@ -1,5 +1,6 @@
1
1
  ''' programming languages of lizard '''
2
2
 
3
+ from lizard_languages.zig import ZigReader
3
4
  from .clike import CLikeReader
4
5
  from .erlang import ErlangReader
5
6
  from .java import JavaReader
@@ -20,6 +21,9 @@ from .rust import RustReader
20
21
  from .typescript import TypeScriptReader
21
22
  from .fortran import FortranReader
22
23
  from .solidity import SolidityReader
24
+ from .jsx import JSXReader
25
+ from .tsx import TSXReader
26
+ from .vue import VueReader
23
27
 
24
28
 
25
29
  def languages():
@@ -44,6 +48,10 @@ def languages():
44
48
  KotlinReader,
45
49
  SolidityReader,
46
50
  ErlangReader,
51
+ ZigReader,
52
+ JSXReader,
53
+ TSXReader,
54
+ VueReader,
47
55
  ]
48
56
 
49
57
 
lizard_languages/clike.py CHANGED
@@ -182,7 +182,7 @@ class CLikeStates(CodeStateMachine):
182
182
  self.context.add_to_function_name(' ' + token)
183
183
 
184
184
  def _state_name_with_space(self, token):
185
- self._state = self._state_operator\
185
+ self._state = self._state_operator \
186
186
  if token == 'operator' else self._state_function
187
187
  self.context.add_to_function_name(token)
188
188
 
@@ -196,7 +196,7 @@ class CLikeStates(CodeStateMachine):
196
196
  else:
197
197
  self.next(self._state_global)
198
198
  elif len(self.bracket_stack) == 1:
199
- if token != 'void': # void is a reserved keyword, meaning no parameters
199
+ if token != 'void': # void is a reserved keyword, meaning no parameters
200
200
  self.context.parameter(token)
201
201
  return
202
202
  self.context.add_to_long_function_name(token)
@@ -8,7 +8,7 @@ from functools import reduce
8
8
  from operator import or_
9
9
 
10
10
 
11
- class CodeStateMachine(object):
11
+ class CodeStateMachine:
12
12
  """ the state machine """
13
13
  # pylint: disable=R0903
14
14
  # pylint: disable=R0902
@@ -50,6 +50,7 @@ class CodeStateMachine(object):
50
50
  self.next(self.saved_state)
51
51
  if self.callback:
52
52
  self.callback()
53
+ self.callback = None
53
54
  self.last_token = token
54
55
  if self.to_exit:
55
56
  return True
@@ -117,11 +118,13 @@ class CodeReader:
117
118
  def _generate_tokens(source, add, flags=0):
118
119
  # DO NOT put any sub groups in the regex. Good for performance
119
120
  _until_end = r"(?:\\\n|[^\n])*"
120
- combined_symbols = ["<<=", ">>=", "||", "&&", "===", "!==",
121
- "==", "!=", "<=", ">=", "->", "=>",
122
- "++", "--", '+=', '-=',
123
- "+", "-", '*', '/',
124
- '*=', '/=', '^=', '&=', '|=', "..."]
121
+ combined_symbols = [
122
+ "<<=", ">>=", "||", "&&", "===", "!==",
123
+ "==", "!=", "<=", ">=", "->", "=>",
124
+ "++", "--", '+=', '-=',
125
+ "+", "-", '*', '/',
126
+ '*=', '/=', '^=', '&=', '|=', "..."
127
+ ]
125
128
  token_pattern = re.compile(
126
129
  r"(?:" +
127
130
  r"\/\*.*?\*\/" +
@@ -171,8 +174,12 @@ class CodeReader:
171
174
  pattern = re.compile(r'\(\?[aiLmsux]+\)')
172
175
  re_flags = ''.join(opt[2:-1] for opt in pattern.findall(addition))
173
176
  flags = reduce(or_, (flag_dict[flag] for flag in re_flags), 0)
177
+ cleaned_addition = pattern.sub('', addition)
174
178
 
175
- return _generate_tokens(source_code, pattern.sub('', addition), flags=flags)
179
+ return _generate_tokens(
180
+ source_code,
181
+ cleaned_addition,
182
+ flags=flags)
176
183
 
177
184
  def __call__(self, tokens, reader):
178
185
  self.context = reader.context
@@ -26,8 +26,12 @@ class ErlangReader(CodeReader):
26
26
 
27
27
  @staticmethod
28
28
  def generate_tokens(source_code, addition='', token_class=None):
29
- return map(lambda x: x[1], filter(lambda x: x[0] != py_token.Whitespace,
30
- lex(source_code, lexer=lexers.get_lexer_by_name('erlang'))))
29
+ lexer = lexers.get_lexer_by_name('erlang')
30
+ tokens = lex(source_code, lexer=lexer)
31
+ return map(
32
+ lambda x: x[1],
33
+ filter(lambda x: x[0] != py_token.Whitespace, tokens)
34
+ )
31
35
 
32
36
 
33
37
  class ErlangStates(CodeStateMachine):
@@ -77,8 +81,8 @@ class ErlangStates(CodeStateMachine):
77
81
  if token == '-':
78
82
  self.punctuated = True
79
83
  elif token == '>' and self.punctuated:
80
- if len(self.context.stacked_functions) <= 1 or \
81
- self.context.current_function.name == 'fun':
84
+ if (len(self.context.stacked_functions) <= 1 or
85
+ self.context.current_function.name == 'fun'):
82
86
  self.next(self._state_func_first_line, token)
83
87
  else:
84
88
  self.func_match_failed(token)
@@ -93,8 +97,8 @@ class ErlangStates(CodeStateMachine):
93
97
 
94
98
  def _state_nested_end(self, token):
95
99
  if token == '.' or token == ',':
96
- if len(self.context.stacked_functions) > 1 \
97
- and self.context.stacked_functions[-1].name == 'fun':
100
+ if (len(self.context.stacked_functions) > 1 and
101
+ self.context.stacked_functions[-1].name == 'fun'):
98
102
  self.statemachine_return()
99
103
  return
100
104
 
@@ -6,57 +6,65 @@ import re
6
6
  from .code_reader import CodeStateMachine, CodeReader
7
7
 
8
8
 
9
- # pylint: disable=R0903
10
- class FortranCommentsMixin(object):
9
+ class FortranCommentsMixin:
11
10
  @staticmethod
12
11
  def get_comment_from_token(token):
13
12
  if token.startswith('!'):
14
13
  return token[1:]
15
14
 
16
15
 
17
- # pylint: disable=R0903
18
16
  class FortranReader(CodeReader, FortranCommentsMixin):
19
- ''' This is the reader for Fortran. '''
17
+ '''This is the reader for Fortran.'''
20
18
 
21
19
  ext = ['f70', 'f90', 'f95', 'f03', 'f08', 'f', 'for', 'ftn', 'fpp']
22
20
  language_names = ['fortran']
23
- _conditions = set((
21
+
22
+ # Conditions need to have all the cases because the matching is case-insensitive
23
+ # and is not done here.
24
+ _conditions = {
24
25
  'IF', 'DO', '.AND.', '.OR.', 'CASE',
25
- 'if', 'do', '.and.', '.or.', 'case'))
26
- _blocks = ['PROGRAM', 'MODULE', 'SUBROUTINE', 'FUNCTION', 'TYPE', 'INTERFACE', 'BLOCK', 'IF', 'DO', 'FORALL', 'WHERE', 'SELECT', 'ASSOCIATE']
26
+ 'if', 'do', '.and.', '.or.', 'case'
27
+ }
28
+ _blocks = [
29
+ 'PROGRAM', 'MODULE', 'SUBMODULE', 'SUBROUTINE', 'FUNCTION', 'TYPE',
30
+ 'INTERFACE', 'BLOCK', 'IF', 'DO', 'FORALL', 'WHERE', 'SELECT', 'ASSOCIATE'
31
+ ]
27
32
 
28
33
  def __init__(self, context):
29
- super(FortranReader, self).__init__(context)
34
+ super().__init__(context)
30
35
  self.macro_disabled = False
31
36
  self.parallel_states = [FortranStates(context, self)]
32
37
 
33
38
  @staticmethod
34
39
  def generate_tokens(source_code, addition='', token_class=None):
35
40
  _until_end = r'(?:\\\n|[^\n])*'
36
- return CodeReader.generate_tokens(
37
- source_code,
41
+ block_endings = '|'.join(r'END\s*{0}'.format(_) for _ in FortranReader._blocks)
42
+ # Include all patterns and the (?i) flag in addition
43
+ addition = (
38
44
  r'(?i)'
39
- r'\/\/' +
40
- r'|\#' + _until_end +
41
- r'|\!' + _until_end +
42
- r'|^\*' + _until_end +
43
- r'|\.OR\.' +
44
- r'|\.AND\.' +
45
- r'|ELSE +IF' +
46
- ''.join(r'|END[ \t]+{0}'.format(_) for _ in FortranReader._blocks) +
47
- addition,
48
- token_class)
45
+ r'\/\/|'
46
+ r'\#' + _until_end + r'|'
47
+ r'\!' + _until_end + r'|'
48
+ r'^\*' + _until_end + r'|'
49
+ r'\.OR\.|'
50
+ r'\.AND\.|'
51
+ r'ELSE\s+IF|'
52
+ r'MODULE\s+PROCEDURE|'
53
+ + block_endings + addition
54
+ )
55
+ return CodeReader.generate_tokens(
56
+ source_code, addition=addition, token_class=token_class)
49
57
 
50
58
  def preprocess(self, tokens):
51
59
  macro_depth = 0
52
60
  new_line = True
53
61
  for token in tokens:
54
- if new_line and token[0].upper() in ('c', 'C', '*'):
55
- token = '!'+token[1:]
62
+ if new_line and token[0].upper() in ('C', '*'):
63
+ token = '!' + token[1:]
56
64
  new_line = token == '\n'
57
- macro = re.match(r'#\s*(\w+)', token)
58
- if macro:
59
- macro = macro.group(1).lower()
65
+ macro_match = re.match(r'#\s*(\w+)', token)
66
+ if macro_match:
67
+ macro = macro_match.group(1).lower()
60
68
  if macro in ('if', 'ifdef', 'ifndef', 'elif'):
61
69
  self.context.add_condition()
62
70
  if macro_depth > 0:
@@ -66,22 +74,29 @@ class FortranReader(CodeReader, FortranCommentsMixin):
66
74
  macro_depth -= 1
67
75
  elif macro in ('else', 'elif'):
68
76
  macro_depth += 1
69
- # In order to don't mess the nesting,
70
- # only the first branch of #if #elif #else
71
- # is read by the FortranStateMachine
77
+ # Only the first branch of #if #elif #else is read
72
78
  self.macro_disabled = macro_depth != 0
73
79
  elif not token.isspace() or token == '\n':
74
80
  yield token
75
81
 
76
- # pylint: disable=R0903
82
+
77
83
  class FortranStates(CodeStateMachine):
78
- # pylint: disable=line-too-long
79
- # pylint: disable=protected-access
80
- _ends = re.compile('(?:'+'|'.join(r'END\s*{0}'.format(_) for _ in FortranReader._blocks)+')', re.I)
84
+ _ends = re.compile(
85
+ '|'.join(r'END\s*{0}'.format(_) for _ in FortranReader._blocks), re.I)
86
+
87
+ # Define token groups to eliminate duplication
88
+ IGNORE_NEXT_TOKENS = {'%', '::', 'SAVE', 'DATA'}
89
+ IGNORE_VAR_TOKENS = {'INTEGER', 'REAL', 'COMPLEX', 'LOGICAL', 'CHARACTER'}
90
+ RESET_STATE_TOKENS = {'RECURSIVE', 'ELEMENTAL'}
91
+ FUNCTION_NAME_TOKENS = {'SUBROUTINE', 'FUNCTION'}
92
+ NESTING_KEYWORDS = {'FORALL', 'WHERE', 'SELECT', 'INTERFACE', 'ASSOCIATE'}
93
+ PROCEDURE_TOKENS = {'PROCEDURE', 'MODULE PROCEDURE'}
81
94
 
82
95
  def __init__(self, context, reader):
83
- super(FortranStates, self).__init__(context)
96
+ super().__init__(context)
84
97
  self.reader = reader
98
+ self.last_token = None
99
+ self.in_interface = False
85
100
 
86
101
  def __call__(self, token, reader=None):
87
102
  if self.reader.macro_disabled:
@@ -96,28 +111,35 @@ class FortranStates(CodeStateMachine):
96
111
 
97
112
  def _state_global(self, token):
98
113
  token_upper = token.upper()
99
- if token_upper in ('%', '::', 'SAVE', 'DATA'):
114
+ if token_upper in self.IGNORE_NEXT_TOKENS:
100
115
  self._state = self._ignore_next
101
- elif token_upper in ('INTEGER', 'REAL','COMPLEX','LOGICAL', 'CHARACTER'):
116
+ elif token_upper in self.IGNORE_VAR_TOKENS:
102
117
  self._state = self._ignore_var
103
118
  elif token == '(':
104
119
  self.next(self._ignore_expr, token)
105
- elif token_upper in ('PROGRAM',):
120
+ elif token_upper in self.RESET_STATE_TOKENS:
121
+ self.reset_state()
122
+ elif token_upper in self.FUNCTION_NAME_TOKENS:
123
+ self._state = self._function_name
124
+ elif token_upper == 'PROGRAM':
106
125
  self._state = self._namespace
107
126
  elif token_upper == 'MODULE':
127
+ self._state = self._module_or_procedure
128
+ elif token_upper == 'SUBMODULE':
108
129
  self._state = self._module
109
- elif token_upper in ('SUBROUTINE', 'FUNCTION'):
110
- self._state = self._function_name
130
+ self._module(token)
111
131
  elif token_upper == 'TYPE':
112
132
  self._state = self._type
113
133
  elif token_upper == 'IF':
114
134
  self._state = self._if
115
- elif token_upper in ('BLOCK',):
135
+ elif token_upper == 'BLOCK':
116
136
  self._state = self._ignore_if_paren
117
- elif token_upper in ('DO',):
137
+ elif token_upper == 'DO':
118
138
  self._state = self._ignore_if_label
119
- elif token_upper in ('FORALL', 'WHERE', 'SELECT', 'INTERFACE', 'ASSOCIATE'):
139
+ elif token_upper in self.NESTING_KEYWORDS:
120
140
  self.context.add_bare_nesting()
141
+ if token_upper == 'INTERFACE':
142
+ self.in_interface = True
121
143
  elif token_upper == 'ELSE':
122
144
  self.context.pop_nesting()
123
145
  self.context.add_bare_nesting()
@@ -127,6 +149,9 @@ class FortranStates(CodeStateMachine):
127
149
  self.context.add_condition()
128
150
  self._state = self._if
129
151
  elif token_upper == 'END' or self._ends.match(token):
152
+ end_token_upper = token_upper.replace(' ', '')
153
+ if end_token_upper.startswith('ENDINTERFACE'):
154
+ self.in_interface = False
130
155
  self.context.pop_nesting()
131
156
 
132
157
  def reset_state(self, token=None):
@@ -134,12 +159,11 @@ class FortranStates(CodeStateMachine):
134
159
  if token is not None:
135
160
  self._state_global(token)
136
161
 
137
- # pylint: disable=unused-argument
138
162
  def _ignore_next(self, token):
139
163
  self.reset_state()
140
164
 
141
165
  def _ignore_var(self, token):
142
- if token.upper() in ('SUBROUTINE', 'FUNCTION'):
166
+ if token.upper() in self.FUNCTION_NAME_TOKENS:
143
167
  self.reset_state(token)
144
168
  else:
145
169
  self.reset_state()
@@ -152,7 +176,7 @@ class FortranStates(CodeStateMachine):
152
176
  self.reset_state()
153
177
 
154
178
  def _ignore_if_label(self, token):
155
- if all(char in "0123456789" for char in token):
179
+ if token.isdigit():
156
180
  self.reset_state()
157
181
  else:
158
182
  self.context.add_bare_nesting()
@@ -184,11 +208,24 @@ class FortranStates(CodeStateMachine):
184
208
  self.reset_state(token)
185
209
 
186
210
  def _module(self, token):
187
- if token.upper() == 'PROCEDURE':
188
- self.reset_state()
211
+ if token.upper() in self.FUNCTION_NAME_TOKENS:
212
+ self._state = self._function_name
213
+ elif token.upper() in self.PROCEDURE_TOKENS:
214
+ self._state = self._procedure
189
215
  else:
190
216
  self._namespace(token)
191
217
 
218
+ def _procedure(self, token):
219
+ # Start a new function regardless of context
220
+ if self.last_token and self.last_token.upper() == 'MODULE':
221
+ # For "module procedure" case, use the current token as function name
222
+ self.context.restart_new_function(token)
223
+ else:
224
+ # For standalone "procedure" case
225
+ self.context.restart_new_function(token)
226
+ self.context.add_bare_nesting()
227
+ self.reset_state()
228
+
192
229
  def _type(self, token):
193
230
  if token in (',', '::') or token[0].isalpha():
194
231
  self._namespace(token)
@@ -210,8 +247,15 @@ class FortranStates(CodeStateMachine):
210
247
  pass
211
248
 
212
249
  def _if_then(self, token):
213
- if token.upper() == 'THEN':
250
+ token_upper = token.upper()
251
+ if token_upper == 'THEN':
214
252
  self.context.add_bare_nesting()
215
253
  self.reset_state()
216
254
  else:
217
255
  self.reset_state(token)
256
+
257
+ def _module_or_procedure(self, token):
258
+ if token.upper() == 'PROCEDURE':
259
+ self._state = self._procedure
260
+ else:
261
+ self._module(token)