lizard 1.17.14__py2.py3-none-any.whl → 1.17.16__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.
- {lizard-1.17.14.dist-info → lizard-1.17.16.dist-info}/METADATA +10 -1
- {lizard-1.17.14.dist-info → lizard-1.17.16.dist-info}/RECORD +21 -20
- lizard.py +37 -12
- lizard_ext/csvoutput.py +11 -25
- lizard_ext/version.py +1 -1
- lizard_languages/__init__.py +2 -0
- lizard_languages/code_reader.py +2 -1
- lizard_languages/csharp.py +43 -1
- lizard_languages/java.py +46 -1
- lizard_languages/javascript.py +2 -136
- lizard_languages/js_style_language_states.py +20 -21
- lizard_languages/js_style_regex_expression.py +41 -11
- lizard_languages/jsx.py +107 -7
- lizard_languages/script_language.py +1 -1
- lizard_languages/tsx.py +5 -25
- lizard_languages/typescript.py +89 -40
- lizard_languages/vue.py +34 -0
- {lizard-1.17.14.dist-info → lizard-1.17.16.dist-info}/LICENSE.txt +0 -0
- {lizard-1.17.14.dist-info → lizard-1.17.16.dist-info}/WHEEL +0 -0
- {lizard-1.17.14.dist-info → lizard-1.17.16.dist-info}/entry_points.txt +0 -0
- {lizard-1.17.14.dist-info → lizard-1.17.16.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: lizard
|
|
3
|
-
Version: 1.17.
|
|
3
|
+
Version: 1.17.16
|
|
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/
|
|
@@ -29,6 +29,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
29
29
|
Classifier: Programming Language :: Python :: 3.11
|
|
30
30
|
License-File: LICENSE.txt
|
|
31
31
|
Requires-Dist: pygments
|
|
32
|
+
Requires-Dist: pathspec
|
|
32
33
|
|
|
33
34
|
|Web Site| Lizard
|
|
34
35
|
=================
|
|
@@ -54,6 +55,7 @@ A list of supported languages:
|
|
|
54
55
|
- C# (C Sharp)
|
|
55
56
|
- JavaScript (With ES6 and JSX)
|
|
56
57
|
- TypeScript (With TSX)
|
|
58
|
+
- VueJS
|
|
57
59
|
- Objective-C
|
|
58
60
|
- Swift
|
|
59
61
|
- Python
|
|
@@ -137,6 +139,13 @@ Exclude anything in the tests folder:
|
|
|
137
139
|
|
|
138
140
|
lizard mySource/ -x"./tests/*"
|
|
139
141
|
|
|
142
|
+
Use .gitignore file:
|
|
143
|
+
|
|
144
|
+
::
|
|
145
|
+
|
|
146
|
+
lizard mySource/
|
|
147
|
+
|
|
148
|
+
If there is a .gitignore file in the given path, lizard will automatically use it as an additional filter to exclude files that match the gitignore patterns. This is useful when you want to analyze only the tracked files in your git repository.
|
|
140
149
|
|
|
141
150
|
Options
|
|
142
151
|
~~~~~~~
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
lizard.py,sha256=
|
|
1
|
+
lizard.py,sha256=m-wnNDtLaug8yvK65Ex1IMRTubo6uFQAfdcdRN9x5Mg,39927
|
|
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=
|
|
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,22 +25,22 @@ 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=
|
|
28
|
+
lizard_ext/version.py,sha256=s8Ej-6FrgTUH06Kp-lskbDvVReimxGKBZNzim7QUtbI,182
|
|
29
29
|
lizard_ext/xmloutput.py,sha256=-cbh0he4O_X-wX56gkv9AnSPNN0qvR7FACqlBeezUS4,5609
|
|
30
|
-
lizard_languages/__init__.py,sha256=
|
|
30
|
+
lizard_languages/__init__.py,sha256=2mvrPWMJVCq981kMjoAPEi2O07C8vo-vUWOG_wp7vKQ,1473
|
|
31
31
|
lizard_languages/clike.py,sha256=Lnwkma-jEu8FNVEPqTAKPkz1Hi0T_dP7H9Jyq0Bd0kQ,10725
|
|
32
|
-
lizard_languages/code_reader.py,sha256=
|
|
33
|
-
lizard_languages/csharp.py,sha256=
|
|
32
|
+
lizard_languages/code_reader.py,sha256=P0PkE4QZBWOEj6cFHA4hj3hcLZLcGnqH31EmtltxlaE,6240
|
|
33
|
+
lizard_languages/csharp.py,sha256=EfFAIOIcJXUUhXTlZApXGSlzG34NZvHM9OSe6m7hpv0,2141
|
|
34
34
|
lizard_languages/erlang.py,sha256=7YJS2cMyXDKEV_kpH8DzBARxFCFcjKuTOPSQ3K52auU,3860
|
|
35
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
|
-
lizard_languages/java.py,sha256=
|
|
40
|
-
lizard_languages/javascript.py,sha256=
|
|
41
|
-
lizard_languages/js_style_language_states.py,sha256=
|
|
42
|
-
lizard_languages/js_style_regex_expression.py,sha256=
|
|
43
|
-
lizard_languages/jsx.py,sha256=
|
|
39
|
+
lizard_languages/java.py,sha256=N9V1nbK_kGN4T2A1xpU1f9wZxGL6UEwVuOCA7D_tjPQ,3036
|
|
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
|
|
44
44
|
lizard_languages/kotlin.py,sha256=v_o2orEzA5gB9vM_0h-E4QXjrc5Yum-0K6W6_laOThc,2844
|
|
45
45
|
lizard_languages/lua.py,sha256=3nqBcunBzJrhv4Iqaf8xvbyqxZy3aSxJ-IiHimHFlac,1573
|
|
46
46
|
lizard_languages/objc.py,sha256=2a1teLdaXZBtCeFiIZer1j_sVx9LZ1CbF2XfnqlvLmk,2319
|
|
@@ -50,17 +50,18 @@ lizard_languages/ruby.py,sha256=gUrFsaS6ZJbjb9CixBHP9n0BxGOb8HsDEg7jJ2bz_80,2187
|
|
|
50
50
|
lizard_languages/rubylike.py,sha256=dAGZ2wqW8nqaESMU8HkeR9gwQ-q9fmZqE6AANvVZD1Q,3426
|
|
51
51
|
lizard_languages/rust.py,sha256=DG2RkR9JWwcD6gIw-BPzg-Yo_lxQtSvfgHioFWIQm9o,610
|
|
52
52
|
lizard_languages/scala.py,sha256=6Jr_TG945VYqB3o5weD7jN7S4beHt4aVj3r-fmKeMAM,1316
|
|
53
|
-
lizard_languages/script_language.py,sha256=
|
|
53
|
+
lizard_languages/script_language.py,sha256=UUO3Wjkoa-ZqwwvcwvhOr5tg1rVavcrEYx3dNdoYSBc,531
|
|
54
54
|
lizard_languages/solidity.py,sha256=Z0GD7U5bI5eUikdy7m_iKWeFD5yXRYq4r3zycscOhJQ,553
|
|
55
55
|
lizard_languages/swift.py,sha256=p8S2OAkQOx9YQ02yhoVXFkr7pMqUH1Nb3RVXPHRU_9M,2450
|
|
56
56
|
lizard_languages/tnsdl.py,sha256=pGcalA_lHY362v2wwPS86seYBOOBBjvmU6vd4Yy3A9g,2803
|
|
57
|
-
lizard_languages/tsx.py,sha256=
|
|
57
|
+
lizard_languages/tsx.py,sha256=aTxmM_-u-R-pRVXRVyITJxrrFZfCeMe9kxMgaoL-qfk,706
|
|
58
58
|
lizard_languages/ttcn.py,sha256=ygjw_raBmPF-4mgoM8m6CAdyEMpTI-n1kZJK1RL4Vxo,2131
|
|
59
|
-
lizard_languages/typescript.py,sha256=
|
|
59
|
+
lizard_languages/typescript.py,sha256=c_Kez4pOGMXhdo6q3HT-clEJDP7LuRIbBkkJSipComU,3568
|
|
60
|
+
lizard_languages/vue.py,sha256=KXUBUo2R1zNF8Pffrz_KsQEN44m5XFRMoGXylxKUeT0,1038
|
|
60
61
|
lizard_languages/zig.py,sha256=NX1iyBstBuJFeAGBOAIaRfrmeBREne2HX6Pt4fXZZTQ,586
|
|
61
|
-
lizard-1.17.
|
|
62
|
-
lizard-1.17.
|
|
63
|
-
lizard-1.17.
|
|
64
|
-
lizard-1.17.
|
|
65
|
-
lizard-1.17.
|
|
66
|
-
lizard-1.17.
|
|
62
|
+
lizard-1.17.16.dist-info/LICENSE.txt,sha256=05ZjgQ8Cl1dD9p0BhW-Txzkc5rhCogGJVEuf1GT2Y_M,1303
|
|
63
|
+
lizard-1.17.16.dist-info/METADATA,sha256=m4ndIad8zK__BY3lidM_6WBys_A7GQY0TNDWFq8XIk8,15745
|
|
64
|
+
lizard-1.17.16.dist-info/WHEEL,sha256=Kh9pAotZVRFj97E15yTA4iADqXdQfIVTHcNaZTjxeGM,110
|
|
65
|
+
lizard-1.17.16.dist-info/entry_points.txt,sha256=ZBqPhu-J3NoGGW5vn2Gfyoo0vdVlgBgM-wlNm0SGYUQ,39
|
|
66
|
+
lizard-1.17.16.dist-info/top_level.txt,sha256=5NTrTaOLhHuTzXaGcZPKfuaOgUv7WafNGe0Zl5aycpg,35
|
|
67
|
+
lizard-1.17.16.dist-info/RECORD,,
|
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
|
-
"
|
|
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
|
|
|
@@ -723,17 +722,13 @@ class OutputScheme(object):
|
|
|
723
722
|
if e.get("avg_caption", None)])
|
|
724
723
|
|
|
725
724
|
def clang_warning_format(self):
|
|
726
|
-
return (
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
"{{f.{ext[value]}}} {caption}"
|
|
730
|
-
.format(ext=e, caption=e['caption'].strip())
|
|
731
|
-
for e in self.items[:-1]
|
|
732
|
-
]))
|
|
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")
|
|
733
728
|
|
|
734
729
|
def msvs_warning_format(self):
|
|
735
730
|
return (
|
|
736
|
-
"{f.filename}({f.start_line}): warning: {f.name} has " +
|
|
731
|
+
"{f.filename}({f.start_line}): warning: {f.name} ({f.long_name}) has " +
|
|
737
732
|
", ".join([
|
|
738
733
|
"{{f.{ext[value]}}} {caption}"
|
|
739
734
|
.format(ext=e, caption=e['caption'].strip())
|
|
@@ -901,14 +896,43 @@ def md5_hash_file(full_path_name):
|
|
|
901
896
|
def get_all_source_files(paths, exclude_patterns, lans):
|
|
902
897
|
'''
|
|
903
898
|
Function counts md5 hash for the given file and checks if it isn't a
|
|
904
|
-
duplicate using set of hashes for previous files
|
|
899
|
+
duplicate using set of hashes for previous files.
|
|
900
|
+
|
|
901
|
+
If a .gitignore file is found in any of the given paths, it will be used
|
|
902
|
+
to filter out files that match the gitignore patterns.
|
|
903
|
+
'''
|
|
905
904
|
hash_set = set()
|
|
905
|
+
gitignore_spec = None
|
|
906
|
+
base_path = None
|
|
907
|
+
|
|
908
|
+
def _load_gitignore():
|
|
909
|
+
nonlocal gitignore_spec, base_path
|
|
910
|
+
try:
|
|
911
|
+
import pathspec
|
|
912
|
+
for path in paths:
|
|
913
|
+
gitignore_path = os.path.join(path, '.gitignore')
|
|
914
|
+
if os.path.exists(gitignore_path):
|
|
915
|
+
with open(gitignore_path, 'r') as gitignore_file:
|
|
916
|
+
# Read lines and strip whitespace and empty lines
|
|
917
|
+
patterns = [line.strip() for line in gitignore_file.readlines()]
|
|
918
|
+
patterns = [p for p in patterns if p and not p.startswith('#')]
|
|
919
|
+
gitignore_spec = pathspec.PathSpec.from_lines('gitwildmatch', patterns)
|
|
920
|
+
base_path = path
|
|
921
|
+
break
|
|
922
|
+
except ImportError:
|
|
923
|
+
pass
|
|
906
924
|
|
|
907
925
|
def _support(reader):
|
|
908
926
|
return not lans or set(lans).intersection(
|
|
909
927
|
reader.language_names)
|
|
910
928
|
|
|
911
929
|
def _validate_file(pathname):
|
|
930
|
+
if gitignore_spec is not None and base_path is not None:
|
|
931
|
+
rel_path = os.path.relpath(pathname, base_path)
|
|
932
|
+
# Normalize path separators for consistent matching
|
|
933
|
+
rel_path = rel_path.replace(os.sep, '/')
|
|
934
|
+
if gitignore_spec.match_file(rel_path):
|
|
935
|
+
return False
|
|
912
936
|
return (
|
|
913
937
|
pathname in paths or (
|
|
914
938
|
get_reader_for(pathname) and
|
|
@@ -931,6 +955,7 @@ def get_all_source_files(paths, exclude_patterns, lans):
|
|
|
931
955
|
for filename in files:
|
|
932
956
|
yield os.path.join(root, filename)
|
|
933
957
|
|
|
958
|
+
_load_gitignore()
|
|
934
959
|
return filter(_validate_file, all_listed_files(paths))
|
|
935
960
|
|
|
936
961
|
|
|
@@ -972,7 +997,7 @@ def parse_args(argv):
|
|
|
972
997
|
if inferred_printer:
|
|
973
998
|
if not opt.printer:
|
|
974
999
|
opt.printer = inferred_printer
|
|
975
|
-
|
|
1000
|
+
elif opt.printer != inferred_printer:
|
|
976
1001
|
msg = "Warning: overriding output file extension.\n"
|
|
977
1002
|
sys.stderr.write(msg)
|
|
978
1003
|
return opt
|
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 = "{},{}"
|
|
40
|
-
print("NLOC,CCN,token,PARAM,length,location,file,function," +
|
|
41
|
-
"long_name,start,end{}"
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
lizard_languages/__init__.py
CHANGED
|
@@ -23,6 +23,7 @@ from .fortran import FortranReader
|
|
|
23
23
|
from .solidity import SolidityReader
|
|
24
24
|
from .jsx import JSXReader
|
|
25
25
|
from .tsx import TSXReader
|
|
26
|
+
from .vue import VueReader
|
|
26
27
|
|
|
27
28
|
|
|
28
29
|
def languages():
|
|
@@ -50,6 +51,7 @@ def languages():
|
|
|
50
51
|
ZigReader,
|
|
51
52
|
JSXReader,
|
|
52
53
|
TSXReader,
|
|
54
|
+
VueReader,
|
|
53
55
|
]
|
|
54
56
|
|
|
55
57
|
|
lizard_languages/code_reader.py
CHANGED
|
@@ -8,7 +8,7 @@ from functools import reduce
|
|
|
8
8
|
from operator import or_
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
class CodeStateMachine
|
|
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
|
lizard_languages/csharp.py
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Language parser for C Sharp
|
|
3
3
|
'''
|
|
4
4
|
|
|
5
|
-
from .clike import CLikeReader
|
|
5
|
+
from .clike import CLikeReader, CLikeStates, CLikeNestingStackStates
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class CSharpReader(CLikeReader):
|
|
@@ -14,7 +14,49 @@ class CSharpReader(CLikeReader):
|
|
|
14
14
|
_conditions = set(['if', 'for', 'while', '&&', '||', '?', 'catch',
|
|
15
15
|
'case', '??'])
|
|
16
16
|
|
|
17
|
+
def __init__(self, context):
|
|
18
|
+
super(CSharpReader, self).__init__(context)
|
|
19
|
+
self.parallel_states = [
|
|
20
|
+
CSharpStates(context),
|
|
21
|
+
CLikeNestingStackStates(context)
|
|
22
|
+
]
|
|
23
|
+
|
|
17
24
|
@staticmethod
|
|
18
25
|
def generate_tokens(source_code, addition='', token_class=None):
|
|
19
26
|
return CLikeReader.generate_tokens(
|
|
20
27
|
source_code, r"|(?:\?\?)", token_class)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class CSharpStates(CLikeStates):
|
|
31
|
+
def __init__(self, context):
|
|
32
|
+
super(CSharpStates, self).__init__(context)
|
|
33
|
+
self.in_primary_constructor = False
|
|
34
|
+
self.class_name = None
|
|
35
|
+
|
|
36
|
+
def try_new_function(self, name):
|
|
37
|
+
if not self.in_primary_constructor:
|
|
38
|
+
super(CSharpStates, self).try_new_function(name)
|
|
39
|
+
if self.class_name and self.context.current_function:
|
|
40
|
+
self.context.current_function.name = f"{self.class_name}::{name}"
|
|
41
|
+
|
|
42
|
+
def _state_global(self, token):
|
|
43
|
+
if token in ("class", "struct", "record"):
|
|
44
|
+
self.class_name = None
|
|
45
|
+
self._state = self._state_class_declaration
|
|
46
|
+
else:
|
|
47
|
+
super(CSharpStates, self)._state_global(token)
|
|
48
|
+
|
|
49
|
+
def _state_class_declaration(self, token):
|
|
50
|
+
if token == '(': # Primary constructor
|
|
51
|
+
self.in_primary_constructor = True
|
|
52
|
+
self._state = self._state_primary_constructor
|
|
53
|
+
elif token == '{':
|
|
54
|
+
self._state = self._state_global
|
|
55
|
+
elif token[0].isalpha():
|
|
56
|
+
if not self.class_name: # Only set class name if not already set
|
|
57
|
+
self.class_name = token
|
|
58
|
+
|
|
59
|
+
@CLikeStates.read_inside_brackets_then("()", "_state_class_declaration")
|
|
60
|
+
def _state_primary_constructor(self, _):
|
|
61
|
+
"""Skip primary constructor parameters without counting them as a function"""
|
|
62
|
+
self.in_primary_constructor = False
|
lizard_languages/java.py
CHANGED
|
@@ -19,19 +19,39 @@ class JavaReader(CLikeReader):
|
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
class JavaStates(CLikeStates): # pylint: disable=R0903
|
|
22
|
+
def __init__(self, context):
|
|
23
|
+
super(JavaStates, self).__init__(context)
|
|
24
|
+
self.class_name = None
|
|
25
|
+
self.is_record = False
|
|
26
|
+
self.in_record_constructor = False
|
|
27
|
+
|
|
22
28
|
def _state_old_c_params(self, token):
|
|
23
29
|
if token == '{':
|
|
24
30
|
self._state_dec_to_imp(token)
|
|
25
31
|
|
|
26
32
|
def try_new_function(self, name):
|
|
33
|
+
# Don't create a function for record compact constructor
|
|
34
|
+
if self.is_record and name == self.class_name:
|
|
35
|
+
self.in_record_constructor = True
|
|
36
|
+
self._state = self._state_record_compact_constructor
|
|
37
|
+
return
|
|
27
38
|
self.context.try_new_function(name)
|
|
28
39
|
self._state = self._state_function
|
|
40
|
+
if self.class_name and self.context.current_function:
|
|
41
|
+
self.context.current_function.name = f"{self.class_name}::{name}"
|
|
29
42
|
|
|
30
43
|
def _state_global(self, token):
|
|
31
44
|
if token == '@':
|
|
32
45
|
self._state = self._state_decorator
|
|
33
46
|
return
|
|
34
|
-
|
|
47
|
+
if token in ("class", "record", "enum"):
|
|
48
|
+
self.class_name = None
|
|
49
|
+
self.is_record = token == "record"
|
|
50
|
+
self.in_record_constructor = False
|
|
51
|
+
self._state = self._state_class_declaration
|
|
52
|
+
return
|
|
53
|
+
if not self.in_record_constructor: # Only process as potential function if not in record constructor
|
|
54
|
+
super(JavaStates, self)._state_global(token)
|
|
35
55
|
|
|
36
56
|
def _state_decorator(self, _):
|
|
37
57
|
self._state = self._state_post_decorator
|
|
@@ -42,3 +62,28 @@ class JavaStates(CLikeStates): # pylint: disable=R0903
|
|
|
42
62
|
else:
|
|
43
63
|
self._state = self._state_global
|
|
44
64
|
self._state(token)
|
|
65
|
+
|
|
66
|
+
def _state_class_declaration(self, token):
|
|
67
|
+
if token == '{':
|
|
68
|
+
self._state = self._state_global
|
|
69
|
+
elif token == '(': # Record parameters
|
|
70
|
+
self._state = self._state_record_parameters
|
|
71
|
+
elif token[0].isalpha():
|
|
72
|
+
if not self.class_name: # Only set class name if not already set
|
|
73
|
+
self.class_name = token
|
|
74
|
+
|
|
75
|
+
def _state_record_parameters(self, token):
|
|
76
|
+
if token == ')':
|
|
77
|
+
self._state = self._state_class_declaration
|
|
78
|
+
|
|
79
|
+
def _state_record_compact_constructor(self, token):
|
|
80
|
+
if token == '{':
|
|
81
|
+
self._state = self._state_record_constructor_body
|
|
82
|
+
return
|
|
83
|
+
self._state = self._state_global
|
|
84
|
+
self._state(token)
|
|
85
|
+
|
|
86
|
+
def _state_record_constructor_body(self, token):
|
|
87
|
+
if token == '}':
|
|
88
|
+
self.in_record_constructor = False
|
|
89
|
+
self._state = self._state_global
|
lizard_languages/javascript.py
CHANGED
|
@@ -6,148 +6,14 @@ from .code_reader import CodeReader
|
|
|
6
6
|
from .clike import CCppCommentsMixin
|
|
7
7
|
from .js_style_regex_expression import js_style_regex_expression
|
|
8
8
|
from .js_style_language_states import JavaScriptStyleLanguageStates
|
|
9
|
+
from .typescript import TypeScriptReader, JSTokenizer
|
|
9
10
|
|
|
10
11
|
|
|
11
|
-
class JavaScriptReader(
|
|
12
|
+
class JavaScriptReader(TypeScriptReader):
|
|
12
13
|
# pylint: disable=R0903
|
|
13
14
|
|
|
14
15
|
ext = ['js']
|
|
15
16
|
language_names = ['javascript', 'js']
|
|
16
17
|
|
|
17
|
-
@staticmethod
|
|
18
|
-
@js_style_regex_expression
|
|
19
|
-
def generate_tokens(source_code, addition='', token_class=None):
|
|
20
|
-
addition = addition +\
|
|
21
|
-
r"|(?:\$\w+)" + \
|
|
22
|
-
r"|`.*?`"
|
|
23
|
-
js_tokenizer = JSTokenizer()
|
|
24
|
-
for token in CodeReader.generate_tokens(
|
|
25
|
-
source_code, addition, token_class):
|
|
26
|
-
for tok in js_tokenizer(token):
|
|
27
|
-
yield tok
|
|
28
|
-
|
|
29
18
|
def __init__(self, context):
|
|
30
19
|
super(JavaScriptReader, self).__init__(context)
|
|
31
|
-
self.parallel_states = [JavaScriptStyleLanguageStates(context)]
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
class Tokenizer(object):
|
|
35
|
-
def __init__(self):
|
|
36
|
-
self.sub_tokenizer = None
|
|
37
|
-
self._ended = False
|
|
38
|
-
|
|
39
|
-
def __call__(self, token):
|
|
40
|
-
if self.sub_tokenizer:
|
|
41
|
-
for tok in self.sub_tokenizer(token):
|
|
42
|
-
yield tok
|
|
43
|
-
if self.sub_tokenizer._ended:
|
|
44
|
-
self.sub_tokenizer = None
|
|
45
|
-
return
|
|
46
|
-
for tok in self.process_token(token):
|
|
47
|
-
yield tok
|
|
48
|
-
|
|
49
|
-
def stop(self):
|
|
50
|
-
self._ended = True
|
|
51
|
-
|
|
52
|
-
def process_token(self, token):
|
|
53
|
-
pass
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
class JSTokenizer(Tokenizer):
|
|
57
|
-
def __init__(self):
|
|
58
|
-
super(JSTokenizer, self).__init__()
|
|
59
|
-
self.depth = 1
|
|
60
|
-
|
|
61
|
-
def process_token(self, token):
|
|
62
|
-
if token == "<":
|
|
63
|
-
self.sub_tokenizer = XMLTagWithAttrTokenizer()
|
|
64
|
-
return
|
|
65
|
-
if token == "{":
|
|
66
|
-
self.depth += 1
|
|
67
|
-
elif token == "}":
|
|
68
|
-
self.depth -= 1
|
|
69
|
-
if self.depth == 0:
|
|
70
|
-
self.stop()
|
|
71
|
-
return
|
|
72
|
-
yield token
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
class XMLTagWithAttrTokenizer(Tokenizer):
|
|
76
|
-
def __init__(self):
|
|
77
|
-
super(XMLTagWithAttrTokenizer, self).__init__()
|
|
78
|
-
self.tag = None
|
|
79
|
-
self.state = self._global_state
|
|
80
|
-
self.cache = ['<']
|
|
81
|
-
|
|
82
|
-
def process_token(self, token):
|
|
83
|
-
self.cache.append(token)
|
|
84
|
-
if not token.isspace():
|
|
85
|
-
result = self.state(token)
|
|
86
|
-
if result is not None:
|
|
87
|
-
return result
|
|
88
|
-
return ()
|
|
89
|
-
|
|
90
|
-
def abort(self):
|
|
91
|
-
self.stop()
|
|
92
|
-
return self.cache
|
|
93
|
-
|
|
94
|
-
def flush(self):
|
|
95
|
-
tmp, self.cache = self.cache, []
|
|
96
|
-
return [''.join(tmp)]
|
|
97
|
-
|
|
98
|
-
def _global_state(self, token):
|
|
99
|
-
if not isidentifier(token):
|
|
100
|
-
return self.abort()
|
|
101
|
-
self.tag = token
|
|
102
|
-
self.state = self._after_tag
|
|
103
|
-
|
|
104
|
-
def _after_tag(self, token):
|
|
105
|
-
if token == '>':
|
|
106
|
-
self.state = self._body
|
|
107
|
-
elif token == "/":
|
|
108
|
-
self.state = self._expecting_self_closing
|
|
109
|
-
elif isidentifier(token):
|
|
110
|
-
self.state = self._expecting_equal_sign
|
|
111
|
-
else:
|
|
112
|
-
return self.abort()
|
|
113
|
-
|
|
114
|
-
def _expecting_self_closing(self, token):
|
|
115
|
-
if token == ">":
|
|
116
|
-
self.stop()
|
|
117
|
-
return self.flush()
|
|
118
|
-
return self.abort()
|
|
119
|
-
|
|
120
|
-
def _expecting_equal_sign(self, token):
|
|
121
|
-
if token == '=':
|
|
122
|
-
self.state = self._expecting_value
|
|
123
|
-
else:
|
|
124
|
-
return self.abort()
|
|
125
|
-
|
|
126
|
-
def _expecting_value(self, token):
|
|
127
|
-
if token[0] in "'\"":
|
|
128
|
-
self.state = self._after_tag
|
|
129
|
-
elif token == "{":
|
|
130
|
-
self.cache.append("}")
|
|
131
|
-
self.sub_tokenizer = JSTokenizer()
|
|
132
|
-
self.state = self._after_tag
|
|
133
|
-
|
|
134
|
-
def _body(self, token):
|
|
135
|
-
if token == "<":
|
|
136
|
-
self.sub_tokenizer = XMLTagWithAttrTokenizer()
|
|
137
|
-
self.cache.pop()
|
|
138
|
-
return self.flush()
|
|
139
|
-
|
|
140
|
-
if token.startswith("</"):
|
|
141
|
-
self.stop()
|
|
142
|
-
return self.flush()
|
|
143
|
-
|
|
144
|
-
if token == '{':
|
|
145
|
-
self.sub_tokenizer = JSTokenizer()
|
|
146
|
-
return self.flush()
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
def isidentifier(token):
|
|
150
|
-
try:
|
|
151
|
-
return token.isidentifier()
|
|
152
|
-
except AttributeError:
|
|
153
|
-
return token.encode(encoding='UTF-8')[0].isalpha()
|
|
@@ -11,8 +11,18 @@ class JavaScriptStyleLanguageStates(CodeStateMachine): # pylint: disable=R0903
|
|
|
11
11
|
self.last_tokens = ''
|
|
12
12
|
self.function_name = ''
|
|
13
13
|
self.started_function = None
|
|
14
|
+
self.as_object = False
|
|
14
15
|
|
|
15
16
|
def _state_global(self, token):
|
|
17
|
+
if self.as_object:
|
|
18
|
+
if token == ':':
|
|
19
|
+
self.function_name = self.last_tokens
|
|
20
|
+
return
|
|
21
|
+
elif token == '(':
|
|
22
|
+
self._function(self.last_tokens)
|
|
23
|
+
self.next(self._function, token)
|
|
24
|
+
return
|
|
25
|
+
|
|
16
26
|
if token in '.':
|
|
17
27
|
self._state = self._field
|
|
18
28
|
self.last_tokens += token
|
|
@@ -29,11 +39,11 @@ class JavaScriptStyleLanguageStates(CodeStateMachine): # pylint: disable=R0903
|
|
|
29
39
|
self.function_name = self.last_tokens
|
|
30
40
|
elif token == "(":
|
|
31
41
|
self.sub_state(
|
|
32
|
-
|
|
42
|
+
self.__class__(self.context))
|
|
33
43
|
elif token in '{':
|
|
34
44
|
if self.started_function:
|
|
35
45
|
self.sub_state(
|
|
36
|
-
|
|
46
|
+
self.__class__(self.context),
|
|
37
47
|
self._pop_function_from_stack)
|
|
38
48
|
else:
|
|
39
49
|
self.read_object()
|
|
@@ -46,7 +56,12 @@ class JavaScriptStyleLanguageStates(CodeStateMachine): # pylint: disable=R0903
|
|
|
46
56
|
self.last_tokens = token
|
|
47
57
|
|
|
48
58
|
def read_object(self):
|
|
49
|
-
|
|
59
|
+
def callback():
|
|
60
|
+
self.next(self._state_global)
|
|
61
|
+
|
|
62
|
+
object_reader = self.__class__(self.context)
|
|
63
|
+
object_reader.as_object = True
|
|
64
|
+
self.sub_state(object_reader, callback)
|
|
50
65
|
|
|
51
66
|
def statemachine_before_return(self):
|
|
52
67
|
self._pop_function_from_stack()
|
|
@@ -63,16 +78,14 @@ class JavaScriptStyleLanguageStates(CodeStateMachine): # pylint: disable=R0903
|
|
|
63
78
|
return
|
|
64
79
|
|
|
65
80
|
self.sub_state(
|
|
66
|
-
|
|
67
|
-
callback)
|
|
81
|
+
self.__class__(self.context), callback)
|
|
68
82
|
|
|
69
83
|
def _expecting_statement_or_block(self, token):
|
|
70
84
|
def callback():
|
|
71
85
|
self.next(self._state_global)
|
|
72
86
|
if token == "{":
|
|
73
87
|
self.sub_state(
|
|
74
|
-
|
|
75
|
-
callback)
|
|
88
|
+
self.__class__(self.context), callback)
|
|
76
89
|
else:
|
|
77
90
|
self.next(self._state_global, token)
|
|
78
91
|
|
|
@@ -116,17 +129,3 @@ class JavaScriptStyleLanguageStates(CodeStateMachine): # pylint: disable=R0903
|
|
|
116
129
|
if token != '{':
|
|
117
130
|
self.started_function = None
|
|
118
131
|
self.next(self._state_global, token)
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
class ES6ObjectStates(JavaScriptStyleLanguageStates): # pylint: disable=R0903
|
|
122
|
-
def __init__(self, context):
|
|
123
|
-
super(ES6ObjectStates, self).__init__(context)
|
|
124
|
-
|
|
125
|
-
def _state_global(self, token):
|
|
126
|
-
if token == ':':
|
|
127
|
-
self.function_name = self.last_tokens
|
|
128
|
-
elif token == '(':
|
|
129
|
-
self._function(self.last_tokens)
|
|
130
|
-
self.next(self._function, token)
|
|
131
|
-
else:
|
|
132
|
-
super(ES6ObjectStates, self)._state_global(token)
|
|
@@ -6,18 +6,48 @@ import re
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
def js_style_regex_expression(func):
|
|
9
|
-
def generate_tokens_with_regex(source_code,
|
|
9
|
+
def generate_tokens_with_regex(source_code, addition='', token_class=None):
|
|
10
10
|
regx_regx = r"\/(\S*?[^\s\\]\/)+?(igm)*"
|
|
11
11
|
regx_pattern = re.compile(regx_regx)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
12
|
+
tokens = list(func(source_code, addition, token_class))
|
|
13
|
+
result = []
|
|
14
|
+
i = 0
|
|
15
|
+
while i < len(tokens):
|
|
16
|
+
token = tokens[i]
|
|
17
|
+
if token == '/':
|
|
18
|
+
# Check if this could be a regex pattern
|
|
19
|
+
is_regex = False
|
|
20
|
+
if i == 0:
|
|
21
|
+
is_regex = True
|
|
22
|
+
elif i > 0:
|
|
23
|
+
prev_token = tokens[i-1].strip()
|
|
24
|
+
if prev_token and prev_token[-1] in '=,({[?:!&|;':
|
|
25
|
+
is_regex = True
|
|
26
|
+
|
|
27
|
+
if is_regex:
|
|
28
|
+
# This is likely a regex pattern start
|
|
29
|
+
regex_tokens = [token]
|
|
30
|
+
i += 1
|
|
31
|
+
while i < len(tokens) and not tokens[i].endswith('/'):
|
|
32
|
+
regex_tokens.append(tokens[i])
|
|
33
|
+
i += 1
|
|
34
|
+
if i < len(tokens):
|
|
35
|
+
regex_tokens.append(tokens[i])
|
|
36
|
+
i += 1
|
|
37
|
+
# Check for regex flags
|
|
38
|
+
if i < len(tokens) and re.match(r'^[igm]+$', tokens[i]):
|
|
39
|
+
regex_tokens.append(tokens[i])
|
|
40
|
+
i += 1
|
|
41
|
+
combined = ''.join(regex_tokens)
|
|
42
|
+
if regx_pattern.match(combined):
|
|
43
|
+
result.append(combined)
|
|
44
|
+
else:
|
|
45
|
+
result.extend(regex_tokens)
|
|
46
|
+
else:
|
|
47
|
+
# This is a division operator
|
|
48
|
+
result.append(token)
|
|
19
49
|
else:
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
50
|
+
result.append(token)
|
|
51
|
+
i += 1
|
|
52
|
+
return result
|
|
23
53
|
return generate_tokens_with_regex
|
lizard_languages/jsx.py
CHANGED
|
@@ -2,17 +2,14 @@
|
|
|
2
2
|
Language parser for JSX
|
|
3
3
|
'''
|
|
4
4
|
|
|
5
|
-
from .javascript import JavaScriptReader
|
|
5
|
+
from .javascript import JavaScriptReader
|
|
6
|
+
from .typescript import JSTokenizer, Tokenizer
|
|
6
7
|
from .code_reader import CodeReader
|
|
7
8
|
from .js_style_regex_expression import js_style_regex_expression
|
|
8
9
|
|
|
9
10
|
|
|
10
|
-
class
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
ext = ['jsx']
|
|
14
|
-
language_names = ['jsx']
|
|
15
|
-
|
|
11
|
+
class JSXMixin:
|
|
12
|
+
'''Base mixin class for JSX/TSX shared functionality'''
|
|
16
13
|
@staticmethod
|
|
17
14
|
@js_style_regex_expression
|
|
18
15
|
def generate_tokens(source_code, addition='', token_class=None):
|
|
@@ -25,3 +22,106 @@ class JSXReader(JavaScriptReader):
|
|
|
25
22
|
source_code, addition, token_class):
|
|
26
23
|
for tok in js_tokenizer(token):
|
|
27
24
|
yield tok
|
|
25
|
+
|
|
26
|
+
def _expecting_func_opening_bracket(self, token):
|
|
27
|
+
if token == '<':
|
|
28
|
+
self.next(self._expecting_jsx)
|
|
29
|
+
return
|
|
30
|
+
super()._expecting_func_opening_bracket(token)
|
|
31
|
+
|
|
32
|
+
def _expecting_jsx(self, token):
|
|
33
|
+
if token == '>':
|
|
34
|
+
self.next(self._expecting_func_opening_bracket)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class JSXReader(JavaScriptReader, JSXMixin):
|
|
38
|
+
# pylint: disable=R0903
|
|
39
|
+
|
|
40
|
+
ext = ['jsx']
|
|
41
|
+
language_names = ['jsx']
|
|
42
|
+
|
|
43
|
+
@staticmethod
|
|
44
|
+
@js_style_regex_expression
|
|
45
|
+
def generate_tokens(source_code, addition='', token_class=None):
|
|
46
|
+
return JSXMixin.generate_tokens(source_code, addition, token_class)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class XMLTagWithAttrTokenizer(Tokenizer):
|
|
50
|
+
def __init__(self):
|
|
51
|
+
super(XMLTagWithAttrTokenizer, self).__init__()
|
|
52
|
+
self.tag = None
|
|
53
|
+
self.state = self._global_state
|
|
54
|
+
self.cache = ['<']
|
|
55
|
+
|
|
56
|
+
def process_token(self, token):
|
|
57
|
+
self.cache.append(token)
|
|
58
|
+
if not token.isspace():
|
|
59
|
+
result = self.state(token)
|
|
60
|
+
if result is not None:
|
|
61
|
+
return result
|
|
62
|
+
return ()
|
|
63
|
+
|
|
64
|
+
def abort(self):
|
|
65
|
+
self.stop()
|
|
66
|
+
return self.cache
|
|
67
|
+
|
|
68
|
+
def flush(self):
|
|
69
|
+
tmp, self.cache = self.cache, []
|
|
70
|
+
return [''.join(tmp)]
|
|
71
|
+
|
|
72
|
+
def _global_state(self, token):
|
|
73
|
+
if not isidentifier(token):
|
|
74
|
+
return self.abort()
|
|
75
|
+
self.tag = token
|
|
76
|
+
self.state = self._after_tag
|
|
77
|
+
|
|
78
|
+
def _after_tag(self, token):
|
|
79
|
+
if token == '>':
|
|
80
|
+
self.state = self._body
|
|
81
|
+
elif token == "/":
|
|
82
|
+
self.state = self._expecting_self_closing
|
|
83
|
+
elif isidentifier(token):
|
|
84
|
+
self.state = self._expecting_equal_sign
|
|
85
|
+
else:
|
|
86
|
+
return self.abort()
|
|
87
|
+
|
|
88
|
+
def _expecting_self_closing(self, token):
|
|
89
|
+
if token == ">":
|
|
90
|
+
self.stop()
|
|
91
|
+
return self.flush()
|
|
92
|
+
return self.abort()
|
|
93
|
+
|
|
94
|
+
def _expecting_equal_sign(self, token):
|
|
95
|
+
if token == '=':
|
|
96
|
+
self.state = self._expecting_value
|
|
97
|
+
else:
|
|
98
|
+
return self.abort()
|
|
99
|
+
|
|
100
|
+
def _expecting_value(self, token):
|
|
101
|
+
if token[0] in "'\"":
|
|
102
|
+
self.state = self._after_tag
|
|
103
|
+
elif token == "{":
|
|
104
|
+
self.cache.append("}")
|
|
105
|
+
self.sub_tokenizer = JSTokenizer()
|
|
106
|
+
self.state = self._after_tag
|
|
107
|
+
|
|
108
|
+
def _body(self, token):
|
|
109
|
+
if token == "<":
|
|
110
|
+
self.sub_tokenizer = XMLTagWithAttrTokenizer()
|
|
111
|
+
self.cache.pop()
|
|
112
|
+
return self.flush()
|
|
113
|
+
|
|
114
|
+
if token.startswith("</"):
|
|
115
|
+
self.stop()
|
|
116
|
+
return self.flush()
|
|
117
|
+
|
|
118
|
+
if token == '{':
|
|
119
|
+
self.sub_tokenizer = JSTokenizer()
|
|
120
|
+
return self.flush()
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def isidentifier(token):
|
|
124
|
+
try:
|
|
125
|
+
return token.isidentifier()
|
|
126
|
+
except AttributeError:
|
|
127
|
+
return token.encode(encoding='UTF-8')[0].isalpha()
|
lizard_languages/tsx.py
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
Language parser for TSX
|
|
3
3
|
'''
|
|
4
4
|
|
|
5
|
-
from .typescript import TypeScriptReader, TypeScriptStates
|
|
6
|
-
from .
|
|
5
|
+
from .typescript import TypeScriptReader, TypeScriptStates, JSTokenizer
|
|
6
|
+
from .jsx import JSXMixin
|
|
7
7
|
from .code_reader import CodeReader
|
|
8
8
|
from .js_style_regex_expression import js_style_regex_expression
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
class TSXReader(TypeScriptReader):
|
|
11
|
+
class TSXReader(TypeScriptReader, JSXMixin):
|
|
12
12
|
# pylint: disable=R0903
|
|
13
13
|
|
|
14
14
|
ext = ['tsx']
|
|
@@ -17,28 +17,8 @@ class TSXReader(TypeScriptReader):
|
|
|
17
17
|
@staticmethod
|
|
18
18
|
@js_style_regex_expression
|
|
19
19
|
def generate_tokens(source_code, addition='', token_class=None):
|
|
20
|
-
|
|
21
|
-
r"|(?:\$\w+)" + \
|
|
22
|
-
r"|(?:\<\/\w+\>)" + \
|
|
23
|
-
r"|`.*?`"
|
|
24
|
-
js_tokenizer = JSTokenizer()
|
|
25
|
-
for token in CodeReader.generate_tokens(
|
|
26
|
-
source_code, addition, token_class):
|
|
27
|
-
for tok in js_tokenizer(token):
|
|
28
|
-
yield tok
|
|
20
|
+
return JSXMixin.generate_tokens(source_code, addition, token_class)
|
|
29
21
|
|
|
30
22
|
def __init__(self, context):
|
|
31
23
|
super(TSXReader, self).__init__(context)
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
class TSXStates(TypeScriptStates):
|
|
36
|
-
def _expecting_func_opening_bracket(self, token):
|
|
37
|
-
if token == '<':
|
|
38
|
-
self.next(self._expecting_jsx)
|
|
39
|
-
return
|
|
40
|
-
super(TSXStates, self)._expecting_func_opening_bracket(token)
|
|
41
|
-
|
|
42
|
-
def _expecting_jsx(self, token):
|
|
43
|
-
if token == '>':
|
|
44
|
-
self.next(self._expecting_func_opening_bracket)
|
|
24
|
+
# No need for parallel states since JSX handling is in the mixin
|
lizard_languages/typescript.py
CHANGED
|
@@ -3,12 +3,54 @@ Language parser for JavaScript
|
|
|
3
3
|
'''
|
|
4
4
|
|
|
5
5
|
import re
|
|
6
|
-
from .code_reader import CodeReader
|
|
6
|
+
from .code_reader import CodeReader, CodeStateMachine
|
|
7
7
|
from .clike import CCppCommentsMixin
|
|
8
8
|
from .js_style_language_states import JavaScriptStyleLanguageStates
|
|
9
9
|
from .js_style_regex_expression import js_style_regex_expression
|
|
10
10
|
|
|
11
11
|
|
|
12
|
+
class Tokenizer(object):
|
|
13
|
+
def __init__(self):
|
|
14
|
+
self.sub_tokenizer = None
|
|
15
|
+
self._ended = False
|
|
16
|
+
|
|
17
|
+
def __call__(self, token):
|
|
18
|
+
if self.sub_tokenizer:
|
|
19
|
+
for tok in self.sub_tokenizer(token):
|
|
20
|
+
yield tok
|
|
21
|
+
if self.sub_tokenizer._ended:
|
|
22
|
+
self.sub_tokenizer = None
|
|
23
|
+
return
|
|
24
|
+
for tok in self.process_token(token):
|
|
25
|
+
yield tok
|
|
26
|
+
|
|
27
|
+
def stop(self):
|
|
28
|
+
self._ended = True
|
|
29
|
+
|
|
30
|
+
def process_token(self, token):
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class JSTokenizer(Tokenizer):
|
|
35
|
+
def __init__(self):
|
|
36
|
+
super().__init__()
|
|
37
|
+
self.depth = 1
|
|
38
|
+
|
|
39
|
+
def process_token(self, token):
|
|
40
|
+
if token == "<":
|
|
41
|
+
from .jsx import XMLTagWithAttrTokenizer # Import only when needed
|
|
42
|
+
self.sub_tokenizer = XMLTagWithAttrTokenizer()
|
|
43
|
+
return
|
|
44
|
+
if token == "{":
|
|
45
|
+
self.depth += 1
|
|
46
|
+
elif token == "}":
|
|
47
|
+
self.depth -= 1
|
|
48
|
+
if self.depth == 0:
|
|
49
|
+
self.stop()
|
|
50
|
+
return
|
|
51
|
+
yield token
|
|
52
|
+
|
|
53
|
+
|
|
12
54
|
class TypeScriptReader(CodeReader, CCppCommentsMixin):
|
|
13
55
|
# pylint: disable=R0903
|
|
14
56
|
|
|
@@ -18,58 +60,65 @@ class TypeScriptReader(CodeReader, CCppCommentsMixin):
|
|
|
18
60
|
'catch', 'case'])
|
|
19
61
|
|
|
20
62
|
def __init__(self, context):
|
|
21
|
-
super(
|
|
63
|
+
super().__init__(context)
|
|
22
64
|
self.parallel_states = [TypeScriptStates(context)]
|
|
23
65
|
|
|
24
66
|
@staticmethod
|
|
25
67
|
@js_style_regex_expression
|
|
26
68
|
def generate_tokens(source_code, addition='', token_class=None):
|
|
27
69
|
addition = addition +\
|
|
28
|
-
r"|(
|
|
29
|
-
|
|
70
|
+
r"|(?:\$\w+)" + \
|
|
71
|
+
r"|(?:\w+\?)" + \
|
|
72
|
+
r"|`.*?`"
|
|
73
|
+
js_tokenizer = JSTokenizer()
|
|
74
|
+
for token in CodeReader.generate_tokens(
|
|
75
|
+
source_code, addition, token_class):
|
|
76
|
+
for tok in js_tokenizer(token):
|
|
77
|
+
yield tok
|
|
30
78
|
|
|
31
79
|
|
|
32
80
|
class TypeScriptStates(JavaScriptStyleLanguageStates):
|
|
81
|
+
def __init__(self, context):
|
|
82
|
+
super().__init__(context)
|
|
33
83
|
|
|
34
84
|
def _state_global(self, token):
|
|
35
|
-
if
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
return
|
|
41
|
-
if token == '=>':
|
|
42
|
-
# For arrow functions, we want to treat them as anonymous
|
|
43
|
-
self.function_name = ''
|
|
44
|
-
self._state = self._arrow_function
|
|
45
|
-
return
|
|
46
|
-
super(TypeScriptStates, self)._state_global(token)
|
|
47
|
-
|
|
48
|
-
def _type_annotation(self, token):
|
|
49
|
-
if token == '=':
|
|
50
|
-
# We're back to an assignment, restore the potential name
|
|
51
|
-
if hasattr(self, '_potential_name'):
|
|
52
|
-
self.last_tokens = self._potential_name
|
|
53
|
-
delattr(self, '_potential_name')
|
|
54
|
-
self.next(self._state_global, token)
|
|
55
|
-
else:
|
|
56
|
-
self.next(self._type_annotation)
|
|
85
|
+
if not self.as_object:
|
|
86
|
+
if token == ':':
|
|
87
|
+
self._consume_type_annotation()
|
|
88
|
+
return
|
|
89
|
+
super()._state_global(token)
|
|
57
90
|
|
|
58
91
|
def _expecting_func_opening_bracket(self, token):
|
|
59
92
|
if token == ':':
|
|
60
|
-
self.
|
|
61
|
-
|
|
62
|
-
|
|
93
|
+
self._consume_type_annotation()
|
|
94
|
+
else:
|
|
95
|
+
super()._expecting_func_opening_bracket(token)
|
|
96
|
+
|
|
97
|
+
def _consume_type_annotation(self):
|
|
98
|
+
typeStates = TypeScriptTypeAnnotationStates(self.context)
|
|
99
|
+
|
|
100
|
+
def callback():
|
|
101
|
+
if typeStates.saved_token:
|
|
102
|
+
self(typeStates.saved_token)
|
|
103
|
+
self.sub_state(typeStates, callback)
|
|
104
|
+
|
|
63
105
|
|
|
64
|
-
|
|
65
|
-
|
|
106
|
+
class TypeScriptTypeAnnotationStates(CodeStateMachine):
|
|
107
|
+
def __init__(self, context):
|
|
108
|
+
super().__init__(context)
|
|
109
|
+
self.saved_token = None
|
|
110
|
+
|
|
111
|
+
def _state_global(self, token):
|
|
66
112
|
if token == '{':
|
|
67
|
-
self.
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
self.
|
|
74
|
-
|
|
75
|
-
|
|
113
|
+
self.next(self._inline_type_annotation, token)
|
|
114
|
+
else:
|
|
115
|
+
self.next(self._state_simple_type, token)
|
|
116
|
+
|
|
117
|
+
def _state_simple_type(self, token):
|
|
118
|
+
if token in '{=;':
|
|
119
|
+
self.saved_token = token
|
|
120
|
+
self.statemachine_return()
|
|
121
|
+
|
|
122
|
+
@CodeStateMachine.read_inside_brackets_then("{}")
|
|
123
|
+
def _inline_type_annotation(self, _):
|
|
124
|
+
self.statemachine_return()
|
lizard_languages/vue.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
'''
|
|
2
|
+
Language parser for Vue.js files
|
|
3
|
+
'''
|
|
4
|
+
|
|
5
|
+
from .code_reader import CodeReader, CodeStateMachine
|
|
6
|
+
from .typescript import TypeScriptReader
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class VueReader(TypeScriptReader):
|
|
10
|
+
# pylint: disable=R0903
|
|
11
|
+
|
|
12
|
+
ext = ['vue']
|
|
13
|
+
language_names = ['vue', 'vuejs']
|
|
14
|
+
|
|
15
|
+
def __init__(self, context):
|
|
16
|
+
super(VueReader, self).__init__(context)
|
|
17
|
+
|
|
18
|
+
@staticmethod
|
|
19
|
+
def generate_tokens(source_code, addition='', token_class=None):
|
|
20
|
+
# Use the base token generator but ensure we capture Vue block tags
|
|
21
|
+
addition = addition + r"|(?:\<\/?\w+.*?\>)"
|
|
22
|
+
return TypeScriptReader.generate_tokens(source_code, addition, token_class)
|
|
23
|
+
|
|
24
|
+
def preprocess(self, tokens):
|
|
25
|
+
current_block = None
|
|
26
|
+
|
|
27
|
+
for token in tokens:
|
|
28
|
+
if token.startswith('<script'):
|
|
29
|
+
current_block = 'script'
|
|
30
|
+
elif token.startswith('</script'):
|
|
31
|
+
current_block = None
|
|
32
|
+
elif current_block == 'script':
|
|
33
|
+
if not token.isspace() or token == '\n':
|
|
34
|
+
yield token
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|