Utilities/ClassBrowsers/jsclbr.py

branch
Py2 comp.
changeset 2847
1843ef6e2656
parent 2779
4d433896b6d6
child 3057
10516539f238
equal deleted inserted replaced
2846:b852fe4d153a 2847:1843ef6e2656
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2013 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Parse a JavaScript file and retrieve variables and functions.
8
9 It uses the JavaScript parser contained in the jasy web framework.
10 """
11
12 from __future__ import unicode_literals # __IGNORE_WARNING__
13
14 import ThirdParty.Jasy.jasy.js.parse.Parser as jsParser
15
16 import Utilities
17 import Utilities.ClassBrowsers as ClassBrowsers
18 from . import ClbrBaseClasses
19
20 SUPPORTED_TYPES = [ClassBrowsers.JS_SOURCE]
21
22 _modules = {} # cache of modules we've seen
23
24
25 class VisibilityMixin(ClbrBaseClasses.ClbrVisibilityMixinBase):
26 """
27 Mixin class implementing the notion of visibility.
28 """
29 def __init__(self):
30 """
31 Method to initialize the visibility.
32 """
33 if self.name.startswith('__'):
34 self.setPrivate()
35 elif self.name.startswith('_'):
36 self.setProtected()
37 else:
38 self.setPublic()
39
40
41 class Function(ClbrBaseClasses.Function, VisibilityMixin):
42 """
43 Class to represent a Python function.
44 """
45 def __init__(self, module, name, file, lineno, signature='', separator=','):
46 """
47 Constructor
48
49 @param module name of the module containing this function
50 @param name name of this function
51 @param file filename containing this class
52 @param lineno linenumber of the class definition
53 @param signature parameterlist of the method
54 @param separator string separating the parameters
55 """
56 ClbrBaseClasses.Function.__init__(self, module, name, file, lineno,
57 signature, separator)
58 VisibilityMixin.__init__(self)
59
60
61 class Attribute(ClbrBaseClasses.Attribute, VisibilityMixin):
62 """
63 Class to represent a class attribute.
64 """
65 def __init__(self, module, name, file, lineno):
66 """
67 Constructor
68
69 @param module name of the module containing this class
70 @param name name of this class
71 @param file filename containing this attribute
72 @param lineno linenumber of the class definition
73 """
74 ClbrBaseClasses.Attribute.__init__(self, module, name, file, lineno)
75 VisibilityMixin.__init__(self)
76
77
78 class Visitor(object):
79 """
80 Class implementing a visitor going through the parsed tree.
81 """
82 def __init__(self, src, module, filename):
83 """
84 Constructor
85
86 @param src source to be parsed (string)
87 @param module name of the module (string)
88 @param filename file name (string)
89 """
90 self.__dict = {}
91 self.__dict_counts = {}
92 self.__root = None
93 self.__stack = []
94
95 self.__source = src
96 self.__module = module
97 self.__file = filename
98
99 def parse(self):
100 """
101 Public method to parse the source.
102
103 @return dictionary containing the parsed information
104 """
105 try:
106 self.__root = jsParser.parse(self.__source)
107 self.__visit(self.__root)
108 except jsParser.SyntaxError:
109 # ignore syntax errors
110 pass
111
112 return self.__dict
113
114 def __visit(self, root):
115 """
116 Private method implementing the visit logic delegating to interesting methods.
117 """
118 call = lambda n: getattr(self, "visit_{0}".format(n.type), self.visit_noop)(n)
119 call(root)
120 for node in root:
121 self.__visit(node)
122
123 def visit_noop(self, node):
124 """
125 Public method to ignore the given node.
126
127 @param node reference to the node (jasy.js.parse.Node.Node)
128 """
129 pass
130
131 def visit_function(self, node):
132 """
133 Public method to treat a function node.
134
135 @param node reference to the node (jasy.js.parse.Node.Node)
136 """
137 if node.type == "function" and \
138 getattr(node, "name", None) and \
139 node.functionForm == "declared_form":
140 if self.__stack and self.__stack[-1].endlineno < node.line:
141 del self.__stack[-1]
142 endline = node.line + self.__source.count('\n', node.start, node.end)
143 if getattr(node, "params", None):
144 func_sig = ", ".join([p.value for p in node.params])
145 else:
146 func_sig = ""
147 if self.__stack:
148 # it's a nested function
149 cur_func = self.__stack[-1]
150 f = Function(None, node.name,
151 self.__file, node.line, func_sig)
152 f.setEndLine(endline)
153 cur_func._addmethod(node.name, f)
154 else:
155 f = Function(self.__module, node.name,
156 self.__file, node.line, func_sig)
157 f.setEndLine(endline)
158 func_name = node.name
159 if func_name in self.__dict_counts:
160 self.__dict_counts[func_name] += 1
161 func_name = "{0}_{1:d}".format(
162 func_name, self.__dict_counts[func_name])
163 else:
164 self.__dict_counts[func_name] = 0
165 self.__dict[func_name] = f
166 self.__stack.append(f)
167
168 def visit_property_init(self, node):
169 """
170 Public method to treat a property_init node.
171
172 @param node reference to the node (jasy.js.parse.Node.Node)
173 """
174 if node.type == "property_init" and node[1].type == "function":
175 if self.__stack and self.__stack[-1].endlineno < node[0].line:
176 del self.__stack[-1]
177 endline = node[0].line + self.__source.count('\n', node.start, node[1].end)
178 if getattr(node[1], "params", None):
179 func_sig = ", ".join([p.value for p in node[1].params])
180 else:
181 func_sig = ""
182 if self.__stack:
183 # it's a nested function
184 cur_func = self.__stack[-1]
185 f = Function(None, node[0].value,
186 self.__file, node[0].line, func_sig)
187 f.setEndLine(endline)
188 cur_func._addmethod(node[0].value, f)
189 else:
190 f = Function(self.__module, node[0].value,
191 self.__file, node[0].line, func_sig)
192 f.setEndLine(endline)
193 func_name = node[0].value
194 if func_name in self.__dict_counts:
195 self.__dict_counts[func_name] += 1
196 func_name = "{0}_{1:d}".format(
197 func_name, self.__dict_counts[func_name])
198 else:
199 self.__dict_counts[func_name] = 0
200 self.__dict[func_name] = f
201 self.__stack.append(f)
202
203 def visit_var(self, node):
204 """
205 Public method to treat a variable node.
206
207 @param node reference to the node (jasy.js.parse.Node.Node)
208 """
209 if node.type == "var" and \
210 node.parent.type == "script" and \
211 node.getChildrenLength():
212 if self.__stack and self.__stack[-1].endlineno < node[0].line:
213 del self.__stack[-1]
214 if self.__stack:
215 # function variables
216 for var in node:
217 attr = Attribute(self.__module, var.name, self.__file, var.line)
218 self.__stack[-1]._addattribute(attr)
219 else:
220 # global variable
221 if "@@Globals@@" not in self.__dict:
222 self.__dict["@@Globals@@"] = \
223 ClbrBaseClasses.ClbrBase(self.__module, "Globals", self.__file, 0)
224 for var in node:
225 self.__dict["@@Globals@@"]._addglobal(
226 Attribute(self.__module, var.name, self.__file, var.line))
227
228 def visit_const(self, node):
229 """
230 Public method to treat a constant node.
231
232 @param node reference to the node (jasy.js.parse.Node.Node)
233 """
234 if node.type == "const" and \
235 node.parent.type == "script" and \
236 node.getChildrenLength():
237 if self.__stack and self.__stack[-1].endlineno < node[0].line:
238 del self.__stack[-1]
239 if self.__stack:
240 # function variables
241 for var in node:
242 attr = Attribute(self.__module, "const " + var.name,
243 self.__file, var.line)
244 self.__stack[-1]._addattribute(attr)
245 else:
246 # global variable
247 if "@@Globals@@" not in self.__dict:
248 self.__dict["@@Globals@@"] = \
249 ClbrBaseClasses.ClbrBase(self.__module, "Globals", self.__file, 0)
250 for var in node:
251 self.__dict["@@Globals@@"]._addglobal(
252 Attribute(self.__module, "const " + var.name,
253 self.__file, var.line))
254
255
256 def readmodule_ex(module, path=[]):
257 '''
258 Read a JavaScript file and return a dictionary of functions and variables.
259
260 @param module name of the JavaScript file (string)
261 @param path path the file should be searched in (list of strings)
262 @return the resulting dictionary
263 '''
264 global _modules
265
266 dict = {}
267
268 if module in _modules:
269 # we've seen this file before...
270 return _modules[module]
271
272 # search the path for the file
273 f = None
274 fullpath = list(path)
275 f, file, (suff, mode, type) = ClassBrowsers.find_module(module, fullpath)
276 if f:
277 f.close()
278 if type not in SUPPORTED_TYPES:
279 # not CORBA IDL source, can't do anything with this module
280 _modules[module] = dict
281 return dict
282
283 _modules[module] = dict
284 try:
285 src = Utilities.readEncodedFile(file)[0]
286 except (UnicodeError, IOError):
287 # can't do anything with this module
288 _modules[module] = dict
289 return dict
290
291 visitor = Visitor(src, module, file)
292 dict = visitor.parse()
293 _modules[module] = dict
294 return dict

eric ide

mercurial