-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathparser.py
More file actions
163 lines (122 loc) · 4.94 KB
/
parser.py
File metadata and controls
163 lines (122 loc) · 4.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# *****************************************************************************
# Python AST parser for the ADL project
# Version 0.1.0, Teemu Sirkia
# Reads a given Python program and creates a JSON object
# describing line-by-line which language elements exist
# in the code.
#
# For the list of the available nodes, see:
# https://docs.python.org/3/library/ast.html#abstract-grammar
# *****************************************************************************
import ast, json, sys
# These nodes will be ignored and are not added to the node list.
#
# For example, the operators are skipped because the actual operator
# will be added to the list instead of the operator family.
nodesToSkip = {'Store', 'Load', 'Name', 'Expr', 'arguments', 'Subscript', 'BoolOp', 'BinOp', 'Compare', 'UnaryOp'}
# *****************************************************************************
# Special handlers for some nodes
def handleNameConstant(node, line):
""" Converts the NameConstant node to represent the type of the value (True, False, None). """
return str(node.value)
def handleNum(node, line):
""" Converts the Num node to represent the type of the value (Int, Float). """
return node.n.__class__.__name__.capitalize()
handlers = {'Num' : handleNum, 'NameConstant' : handleNameConstant}
# *****************************************************************************
def simpleTraverse(node, line, nodes):
name = node.__class__.__name__
# Only some nodes contain line number
if hasattr(node, 'lineno'):
line = node.lineno
if name not in nodesToSkip:
if line not in nodes['lines']:
nodes['lines'][line] = set()
if name not in handlers:
nodes['lines'][line].add(name)
else:
nodes['lines'][line].add(handlers[name](node, line))
for child in ast.iter_child_nodes(node):
simpleTraverse(child, line, nodes)
def complexTraverse(node, line, nodes):
name = node.__class__.__name__
# Only some nodes contain line number
if hasattr(node, 'lineno'):
line = node.lineno
endLine = line
current = {'name': name, 'startLine': line}
if name not in nodesToSkip:
if line not in nodes['lines']:
nodes['lines'][line] = []
if name not in handlers:
nodes['lines'][line].append(current)
else:
current['name'] = handlers[name](node, line)
nodes['lines'][line].append(current)
maxLine = endLine
for child in ast.iter_child_nodes(node):
maxLine = max(maxLine, complexTraverse(child, line, nodes))
if maxLine != line:
current['endLine'] = maxLine
return maxLine
def hierarchicalTraverse(node, line, currentNode):
name = node.__class__.__name__
# Only some nodes contain line number
if hasattr(node, 'lineno'):
line = node.lineno
endLine = line
current = {'name': name, 'startLine': line, 'children': []}
if name not in nodesToSkip:
if name not in handlers:
currentNode['children'].append(current)
else:
current['name'] = handlers[name](node, line)
currentNode['children'].append(current)
else:
current = currentNode
maxLine = endLine
for child in ast.iter_child_nodes(node):
maxLine = max(maxLine, hierarchicalTraverse(child, line, current))
if maxLine != line:
current['endLine'] = maxLine
return maxLine
def main():
data = input()
parsed = json.loads(data)
code = parsed['code']
mode = parsed.get('mode', 'simple')
nodes = {'lines' : {}}
try:
tree = ast.parse(code)
startNode = {'name': 'root', 'children': []}
# Traverse all the nodes in the AST
if mode == 'complex':
for node in ast.iter_child_nodes(tree):
complexTraverse(node, 0, nodes)
elif mode == 'hierarchical':
for node in ast.iter_child_nodes(tree):
hierarchicalTraverse(node, 0, startNode)
elif mode in ('simple', 'concepts'):
for node in ast.iter_child_nodes(tree):
simpleTraverse(node, 0, nodes)
else:
print('Parsing failed!\n\nError occurred: Unknown parsing mode', file=sys.stderr)
sys.exit(1)
# Convert sets to lists before JSON transformation
if mode == 'simple' or mode == 'complex':
for line in nodes['lines']:
nodes['lines'][line] = list(nodes['lines'][line])
elif mode == 'hierarchical':
nodes = startNode
elif mode == 'concepts':
concepts = set()
for line in nodes['lines']:
for concept in list(nodes['lines'][line]):
concepts.add(concept)
nodes = list(concepts)
print(json.dumps(nodes))
except Exception as e:
print('Parsing failed!\n\nError occurred: ' + str(e), file=sys.stderr)
sys.exit(1)
if __name__ == '__main__':
main()