Skip to content

Commit 833683a

Browse files
authored
Merge pull request #5 from FSoft-AI4Code/develop
Develop
2 parents 848d66d + fa209c0 commit 833683a

File tree

10 files changed

+248
-20
lines changed

10 files changed

+248
-20
lines changed

src/codetext/parser/javascript_parser.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,12 @@ def get_comment_node(function_node):
4848

4949
@staticmethod
5050
def get_function_list(node):
51-
function_types = ['function_declaration', 'function', 'method_definition', 'generator_function_declaration']
51+
function_types = ['function_declaration',
52+
'function',
53+
'method_definition',
54+
'generator_function_declaration',
55+
'arrow_function',
56+
'generator_function']
5257
res = get_node_by_kind(node, function_types)
5358
for node in res[:]:
5459
if not node.children:
@@ -87,6 +92,16 @@ def get_function_metadata(function_node, blob: str=None) -> Dict[str, str]:
8792
return_statement = get_node_by_kind(function_node, ['return_statement'])
8893
if len(return_statement) > 0:
8994
metadata['return_type'] = '<not_specific>'
95+
96+
if function_node.type in ["function",
97+
"arrow_function",
98+
"generator_function"]:
99+
# function inside object property or variable declarator
100+
identifier = function_node.prev_named_sibling
101+
if identifier:
102+
if identifier.type in ["identifier"]:
103+
metadata["identifier"] = identifier.text.decode()
104+
90105
return metadata
91106

92107
@staticmethod

src/codetext/parser/php_parser.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@ def get_comment_node(function_node):
4949

5050
@staticmethod
5151
def get_class_list(node):
52-
res = get_node_by_kind(node, ['class_declaration', 'trait_declaration'])
52+
res = get_node_by_kind(node, ['class_declaration',
53+
'trait_declaration',
54+
'interface_declaration'])
5355
return res
5456

5557
@staticmethod

src/codetext/parser/python_parser.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,9 @@ def get_class_metadata(class_node, blob: str=None) -> Dict[str, str]:
114114
argument_list = get_node_text(child).split(',')
115115
for arg in argument_list:
116116
item = re.sub(r'[^a-zA-Z0-9\_]', ' ', arg).split()
117-
metadata['parameters'][item[0].strip()] = None
117+
# Handle class definitions with empty argument list class ABC()
118+
if len(item) > 0:
119+
metadata['parameters'][item[0].strip()] = None
118120

119121
# get __init__ function
120122
return metadata

src/codetext/parser/ruby_parser.py

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ class RubyParser(LanguageParser):
2020

2121
@staticmethod
2222
def get_function_list(node):
23-
res = get_node_by_kind(node, ['method'])
23+
res = get_node_by_kind(node, ['method',
24+
'singleton_method'])
2425
return res
2526

2627
@staticmethod
@@ -88,7 +89,7 @@ def get_function_metadata(function_node, blob=None) -> Dict[str, str]:
8889
}
8990

9091
assert type(function_node) == tree_sitter.Node
91-
assert function_node.type == 'method'
92+
assert function_node.type in ['method', 'singleton_method']
9293

9394
for child in function_node.children:
9495
if child.type == 'identifier':
@@ -133,3 +134,39 @@ def get_class_metadata(class_node, blob=None):
133134
def get_comment_node(function_node):
134135
comment_node = get_node_by_kind(function_node, kind='comment')
135136
return comment_node
137+
138+
@staticmethod
139+
def get_action_list(action_node):
140+
call_nodes = get_node_by_kind(action_node, ['call'])
141+
res = []
142+
for call_node in call_nodes:
143+
if get_node_by_kind(call_node, ["do_block"]):
144+
res.append(call_node)
145+
# print(res)
146+
return res
147+
148+
@staticmethod
149+
def get_action_metadata(action_node):
150+
metadata = {
151+
'identifier': '',
152+
'parameters': {},
153+
'return_type': None,
154+
}
155+
156+
for child in action_node.children:
157+
if child.type in ["identifier"]:
158+
metadata['identifier'] = get_node_text(child)
159+
if child.type in ["argument_list"]:
160+
symbol = get_node_by_kind(child, ["simple_symbol"])
161+
if symbol:
162+
metadata['identifier'] += get_node_text(symbol[0])
163+
164+
parameters = get_node_by_kind(action_node, ["block_parameters"])
165+
166+
if parameters:
167+
for param in get_node_by_kind(parameters[0], ["identifier"]):
168+
param_name = get_node_text(param)
169+
metadata['parameters'].update({param_name : None})
170+
171+
return metadata
172+

tests/test_parser/test_javascript.py

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@ class Car {
7676
def test_get_function_metadata(self):
7777
root = self.root_node
7878

79-
function = JavascriptParser.get_function_list(root)[1]
80-
metadata = JavascriptParser.get_function_metadata(function)
79+
_function = JavascriptParser.get_function_list(root)[1]
80+
metadata = JavascriptParser.get_function_metadata(_function)
8181

8282
for key in ['identifier', 'parameters', 'return_type']:
8383
self.assertTrue(key in metadata.keys())
@@ -109,6 +109,57 @@ def test_get_class_metadata(self):
109109
def test_extract_docstring(self):
110110
pass
111111

112+
113+
def test_metadata_with_arrow_function(self):
114+
code_sample = '''
115+
export const parseModel = async (mesh) =>
116+
new Promise((resolve) => {
117+
exporter.parse(
118+
mesh,
119+
(gltf) => {
120+
const blob = new Blob([gltf], { type: "application/octet-stream" });
121+
resolve(blob);
122+
return blob;
123+
},
124+
(error) => {
125+
console.log(error);
126+
return error;
127+
128+
}
129+
);
130+
});
131+
'''
132+
root = parse_code(code_sample, 'javascript').root_node
133+
fn = JavascriptParser.get_function_list(root)[0]
134+
metadata = JavascriptParser.get_function_metadata(fn)
135+
136+
identifier = metadata['identifier']
137+
self.assertEqual(identifier, 'parseModel')
138+
139+
def test_metadata_with_undecleared_functions(self):
140+
code_sample = """
141+
const asyncFunctionExpression = async function() {
142+
// async function expression definition
143+
return a
144+
};
145+
146+
const generatorFunctionExpression = function*() {
147+
// generator function expression definition
148+
return b
149+
};
150+
"""
151+
root = parse_code(code_sample, 'javascript').root_node
152+
fn1, fn2 = JavascriptParser.get_function_list(root)
153+
154+
self.assertEqual(fn1.type, 'function')
155+
self.assertEqual(fn2.type, 'generator_function')
156+
157+
metadata1 = JavascriptParser.get_function_metadata(fn1)
158+
metadata2 = JavascriptParser.get_function_metadata(fn2)
159+
160+
self.assertEqual(metadata1['identifier'], 'asyncFunctionExpression')
161+
self.assertEqual(metadata2['identifier'], 'generatorFunctionExpression')
162+
112163

113164
if __name__ == '__main__':
114165
unittest.main()

tests/test_parser/test_php.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,14 @@ def test_get_function_list(self):
2222

2323
function_list = PhpParser.get_function_list(root)
2424

25-
self.assertEqual(len(function_list), 3)
25+
self.assertEqual(len(function_list), 5)
2626

2727
def test_get_class_list(self):
2828
root = self.root_node
2929

3030
class_list = PhpParser.get_class_list(root)
3131

32-
self.assertEqual(len(class_list), 1)
32+
self.assertEqual(len(class_list), 3)
3333

3434
def test_get_docstring(self):
3535
code_sample = """
@@ -104,11 +104,17 @@ def test_metadata_without_return_statement(self):
104104
def test_get_class_metadata(self):
105105
root = self.root_node
106106

107-
classes = list(PhpParser.get_class_list(root))[0]
108-
metadata = PhpParser.get_class_metadata(classes)
107+
_class, interface, trait = list(PhpParser.get_class_list(root))
108+
class_metadata = PhpParser.get_class_metadata(_class)
109109

110-
self.assertEqual(metadata['parameters'], {'AbstractSQLServerDriver': None})
111-
self.assertEqual(metadata['identifier'], 'Driver')
110+
self.assertEqual(class_metadata['parameters'], {'AbstractSQLServerDriver': None})
111+
self.assertEqual(class_metadata['identifier'], 'Driver')
112+
113+
interface_metadata = PhpParser.get_class_metadata(interface)
114+
self.assertEqual(interface_metadata['identifier'], 'MyInterface')
115+
116+
trait_metadata = PhpParser.get_class_metadata(trait)
117+
self.assertEqual(trait_metadata['identifier'], 'MyTrait')
112118

113119

114120
if __name__ == '__main__':

tests/test_parser/test_python.py

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ def test_get_class_list(self):
2727
root = self.root_node
2828

2929
class_list = PythonParser.get_class_list(root)
30-
3130
self.assertEqual(len(class_list), 1)
3231

3332
def test_get_docstring(self):
@@ -59,21 +58,41 @@ def test_sample(arg1: str = "string", arg2 = "another_string"):
5958

6059
def test_get_class_metadata(self):
6160
code_sample = '''
61+
class ABC():
62+
pass
63+
6264
class Sample(ABC):
6365
def __init__(self):
6466
pass
6567
6668
def test_sample(self, arg1: str = "string", arg2 = "another_string"):
6769
return NotImplement()
70+
71+
class ThisIsalsoAclass(ABC, Sample):
72+
pass
6873
'''
6974
root = parse_code(code_sample, 'python').root_node
7075

71-
classes = list(PythonParser.get_class_list(root))[0]
72-
metadata = PythonParser.get_class_metadata(classes)
73-
76+
77+
classes = list(PythonParser.get_class_list(root))
78+
self.assertEqual(len(classes), 3)
79+
80+
metadata = PythonParser.get_class_metadata(classes[0])
81+
self.assertEqual(metadata['parameters'], {})
82+
self.assertEqual(metadata['identifier'], 'ABC')
83+
84+
85+
metadata = PythonParser.get_class_metadata(classes[1])
7486
self.assertEqual(metadata['parameters'], {'ABC': None})
7587
self.assertEqual(metadata['identifier'], 'Sample')
7688

89+
90+
metadata = PythonParser.get_class_metadata(classes[2])
91+
self.assertEqual(metadata['parameters'], {'ABC': None, 'Sample': None})
92+
self.assertEqual(metadata['identifier'], 'ThisIsalsoAclass')
93+
94+
95+
7796
def test_get_comment_list(self):
7897
root = self.root_node
7998

tests/test_parser/test_ruby.py

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ def test_get_function_list(self):
2222

2323
function_list = RubyParser.get_function_list(root)
2424

25-
self.assertEqual(len(function_list), 1)
25+
self.assertEqual(len(function_list), 2)
2626

2727
def test_get_class_list(self):
2828
root = self.root_node
@@ -76,15 +76,23 @@ def squeeze
7676
def test_get_function_metadata(self):
7777
root = self.root_node
7878

79-
function = RubyParser.get_function_list(root)[0]
80-
metadata = RubyParser.get_function_metadata(function)
79+
_function = RubyParser.get_function_list(root)[0]
80+
metadata = RubyParser.get_function_metadata(_function)
8181

8282
for key in ['identifier', 'parameters', 'return_type']:
8383
self.assertTrue(key in metadata.keys())
8484
self.assertEqual(metadata['identifier'], 'search')
8585
self.assertEqual(metadata['parameters'], {'query': None, 'options': None})
8686
self.assertEqual(metadata['return_type'], None)
8787

88+
_singleton = RubyParser.get_function_list(root)[1]
89+
metadata = RubyParser.get_function_metadata(_singleton)
90+
for key in ['identifier', 'parameters', 'return_type']:
91+
self.assertTrue(key in metadata.keys())
92+
self.assertEqual(metadata['identifier'], 'my_method')
93+
self.assertEqual(metadata['parameters'], {'a': None})
94+
self.assertEqual(metadata['return_type'], '<not_specific>')
95+
8896

8997
def test_metadata_without_return_statement(self):
9098
code_sample = '''
@@ -114,6 +122,26 @@ def test_get_class_metadata(self):
114122
self.assertEqual(metadata['identifier'], 'Client')
115123
self.assertEqual(metadata['parameters'], {'API': None})
116124

125+
def test_get_action_list(self):
126+
root = self.root_node
127+
actions = RubyParser.get_action_list(root)
128+
129+
self.assertEqual(len(actions), 5)
130+
131+
def test_get_action_metadata(self):
132+
root = self.root_node
133+
actions = RubyParser.get_action_list(root)
134+
metadatas = [ RubyParser.get_action_metadata(action) for action in actions]
135+
self.assertEqual(metadatas[0]["identifier"], "load_current_value")
136+
self.assertEqual(metadatas[1]["identifier"], "action:install")
137+
self.assertEqual(metadatas[2]["identifier"], "converge_by")
138+
139+
self.assertEqual(metadatas[3]["identifier"], "action:reinstall")
140+
self.assertEqual(metadatas[4]["identifier"], "converge_by")
141+
142+
self.assertEqual(metadatas[0]["parameters"]["new_resource"], None)
143+
self.assertEqual(metadatas[0]["parameters"]["old_resource"], None)
144+
117145

118146
if __name__ == '__main__':
119147
unittest.main()

tests/test_parser/test_sample/php_test_sample.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,3 +88,25 @@ private function getConnectionOptionsDsn(array $connectionOptions): string
8888
return $connectionOptionsDsn;
8989
}
9090
}
91+
92+
interface MyInterface {
93+
public function myMethod() {
94+
// Method implementation
95+
}
96+
97+
}
98+
99+
trait MyTrait {
100+
101+
public function setBackgroundImage(Drawing $objDrawing): self
102+
{
103+
if (!array_key_exists($objDrawing->getType(), Drawing::IMAGE_TYPES_CONVERTION_MAP)) {
104+
throw new PhpSpreadsheetException('Unsupported image type in comment background. Supported types: PNG, JPEG, BMP, GIF.');
105+
}
106+
$this->backgroundImage = $objDrawing;
107+
108+
return $this;
109+
}
110+
111+
}
112+

0 commit comments

Comments
 (0)