Utilities/ClassBrowsers/jsclbr.py

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

eric ide

mercurial