PageRenderTime 61ms CodeModel.GetById 34ms RepoModel.GetById 1ms app.codeStats 0ms

/frontend/tool/pylib/ecmascript/frontend/treeutil.py

https://github.com/eean/webrok
Python | 487 lines | 408 code | 11 blank | 68 comment | 13 complexity | d87868d403a27de57333bab06d7da2e2 MD5 | raw file
Possible License(s): Unlicense, CC-BY-SA-3.0
  1. #!/usr/bin/env python
  2. ################################################################################
  3. #
  4. # qooxdoo - the new era of web development
  5. #
  6. # http://qooxdoo.org
  7. #
  8. # Copyright:
  9. # 2006-2008 1&1 Internet AG, Germany, http://www.1und1.de
  10. #
  11. # License:
  12. # LGPL: http://www.gnu.org/licenses/lgpl.html
  13. # EPL: http://www.eclipse.org/org/documents/epl-v10.php
  14. # See the LICENSE file in the project's top-level directory for details.
  15. #
  16. # Authors:
  17. # * Sebastian Werner (wpbasti)
  18. # * Fabian Jakobs (fjakobs)
  19. #
  20. ################################################################################
  21. ##
  22. #<h2>Module Description</h2>
  23. #<pre>
  24. # NAME
  25. # module.py -- module short description
  26. #
  27. # SYNTAX
  28. # module.py --help
  29. #
  30. # or
  31. #
  32. # import module
  33. # result = module.func()
  34. #
  35. # DESCRIPTION
  36. # The module module does blah.
  37. #
  38. # CAVEATS
  39. #
  40. # KNOWN ISSUES
  41. # There are no known issues.
  42. #</pre>
  43. ##
  44. """
  45. This module contains a collection of helper functions to work with the
  46. JavaScript syntax tree.
  47. """
  48. import re
  49. from ecmascript.frontend import tree, tokenizer, treegenerator
  50. def findQxDefine(rootNode):
  51. if rootNode.type == "variable":
  52. try:
  53. variableName = (assembleVariable(rootNode))[0]
  54. except tree.NodeAccessException:
  55. return None
  56. if variableName in ["qx.Bootstrap.define", "qx.Class.define", "qx.Interface.define", "qx.Mixin.define", "qx.List.define"]:
  57. if rootNode.parent.parent.type == "call" and rootNode.parent.type == "operand":
  58. return rootNode.parent.parent
  59. if rootNode.hasChildren():
  60. for child in rootNode.children:
  61. foundNode = findQxDefine(child)
  62. if foundNode is not None:
  63. return foundNode
  64. else:
  65. return None
  66. ##
  67. # Some nice short description of foo(); this can contain html and
  68. # {@link #foo Links} to items in the current file.
  69. #
  70. # @param a Describe a positional parameter
  71. # @keyparam b Describe a keyword parameter
  72. # @def foo(name) # overwrites auto-generated function signature
  73. # @param name Describe aliased parameter
  74. # @return Description of the things returned
  75. # @defreturn The return type
  76. # @exception IOError The error it throws
  77. #
  78. def selectNode(node, path):
  79. """
  80. Selects a node using a XPath like path expresseion.
  81. This function returns None if no matching node was found.
  82. Warning: This function usys a depth first search without backtracking!!
  83. ".." navigates to the parent node
  84. "nodeName" navigates to the first child node of type nodeName
  85. "nodeName[3]" navigates to the third child node of type nodeName
  86. "nodeName[@key='members'] navigates to the first child node of type
  87. nodeName with the attribute "key" equals "member"
  88. "4" navigates to the fourth child node
  89. "@key" returns the value of the attribute "key" of the current node.
  90. This must be the last statement.
  91. Example: "../../params/1/keyvalue[@key='defer']/value/function/body/block"
  92. """
  93. re_indexedNode = re.compile("^(.*)\[(\d+)\]$")
  94. re_attributeNode = re.compile("^(.*)\[@(.+)=\'(.*)\'\]$")
  95. try:
  96. pathParts = path.split("/")
  97. for part in pathParts:
  98. # parent node
  99. if part == "..":
  100. node = node.parent
  101. else:
  102. # only index
  103. try:
  104. position = int(part)-1
  105. node = node.getChildByPosition(position)
  106. continue
  107. except ValueError:
  108. pass
  109. # indexed node
  110. match = re_indexedNode.match(part)
  111. if match:
  112. type = match.group(1)
  113. index = int(match.group(2))-1
  114. i = 0
  115. found = False
  116. for child in node.children:
  117. if child.type == type:
  118. if index == i:
  119. node = child
  120. found = True
  121. break
  122. i += 1
  123. if not found:
  124. return None
  125. else:
  126. continue
  127. match = re_attributeNode.match(part)
  128. if match:
  129. type = match.group(1)
  130. attribName = match.group(2)
  131. attribType = match.group(3)
  132. found = False
  133. if node.hasChildren():
  134. for child in node.children:
  135. if child.type == type:
  136. if child.get(attribName) == attribType:
  137. node = child
  138. found = True
  139. if not found:
  140. return None
  141. # attribute
  142. elif part[0] == "@":
  143. return node.get(part[1:])
  144. # normal node
  145. else:
  146. node = node.getChild(part)
  147. except tree.NodeAccessException:
  148. return None
  149. return node
  150. def getDefinitions(node, definitions=None):
  151. if definitions == None:
  152. definitions = []
  153. if node.type == "definition":
  154. definitions.append(node)
  155. if node.hasChildren():
  156. for child in node.children:
  157. if child.type != "function":
  158. definitions = getDefinitions(child, definitions)
  159. return definitions
  160. def findVariablePrefix(node, namePrefix, varNodes=None):
  161. """
  162. Search "node" for all variables starting with "namePrefix"
  163. """
  164. if varNodes == None:
  165. varNodes = []
  166. if node.type == "variable":
  167. try:
  168. nameParts = []
  169. for child in node.children:
  170. if child.type == "identifier":
  171. nameParts.append(child.get("name"))
  172. except tree.NodeAccessException:
  173. nameParts = []
  174. i = 0
  175. found = True
  176. prefixParts = namePrefix.split(".")
  177. if len(prefixParts) <= len (nameParts):
  178. for prefixPart in prefixParts:
  179. if prefixPart != nameParts[i]:
  180. found = False
  181. break
  182. i += 1
  183. else:
  184. found = False
  185. if found:
  186. varNodes.append(node)
  187. return varNodes
  188. if node.hasChildren():
  189. for child in node.children:
  190. varNodes = findVariablePrefix(child, namePrefix, varNodes)
  191. return varNodes
  192. def findVariable(node, varName, varNodes=None):
  193. """
  194. Return a list of all variable definitions inside "node" of name "varName".
  195. """
  196. if varNodes == None:
  197. varNodes = []
  198. if node.type == "variable":
  199. try:
  200. nameParts = []
  201. for child in node.children:
  202. if child.type == "identifier":
  203. nameParts.append(child.get("name"))
  204. name = u".".join(nameParts)
  205. except tree.NodeAccessException:
  206. name = ""
  207. if name == varName:
  208. varNodes.append(node)
  209. return varNodes
  210. if node.hasChildren():
  211. for child in node.children:
  212. varNodes = findVariable(child, varName, varNodes)
  213. return varNodes
  214. def mapNodeToMap(mapNode):
  215. """
  216. convert a "map" tree node into a python map.
  217. """
  218. if mapNode.type != "map":
  219. raise tree.NodeAccessException("Expected a map node!", mapNode)
  220. keys = {}
  221. if mapNode.hasChildren():
  222. for child in mapNode.children:
  223. if child.type == "keyvalue":
  224. keys[child.get("key")] = child.getChild("value")
  225. return keys
  226. def inlineIfStatement(ifNode, conditionValue):
  227. """
  228. Inline an if statement assuming that the condition of the if
  229. statement evaluates to "conditionValue" (True/False")
  230. """
  231. if ifNode.type != "loop" or ifNode.get("loopType") != "IF":
  232. raise tree.NodeAccessException("Expected a the LOOP node of an if statement!", mapNode)
  233. replacement = []
  234. newDefinitions = []
  235. reovedDefinitions = []
  236. if ifNode.getChild("elseStatement", False):
  237. if conditionValue:
  238. reovedDefinitions = getDefinitions(ifNode.getChild("elseStatement"))
  239. newDefinitions = getDefinitions(ifNode.getChild("statement"))
  240. replacement = ifNode.getChild("statement").children
  241. else:
  242. reovedDefinitions = getDefinitions(ifNode.getChild("statement"))
  243. newDefinitions = getDefinitions(ifNode.getChild("elseStatement"))
  244. replacement = ifNode.getChild("elseStatement").children
  245. else:
  246. if conditionValue:
  247. newDefinitions = getDefinitions(ifNode.getChild("statement"))
  248. replacement = ifNode.getChild("statement").children
  249. else:
  250. reovedDefinitions = getDefinitions(ifNode.getChild("statement"))
  251. newDefinitions = map(lambda x: x.get("identifier"), newDefinitions)
  252. definitions = []
  253. for definition in reovedDefinitions:
  254. if not definition.get("identifier") in newDefinitions:
  255. definitions.append(definition)
  256. if len(definitions) > 0:
  257. defList = tree.Node("definitionList")
  258. defList.set("line", ifNode.get("line"))
  259. for definition in definitions:
  260. if definition.hasChildren():
  261. del definition.children
  262. defList.addChild(definition)
  263. replacement.append(defList)
  264. if replacement != []:
  265. replaceChildWithNodes(ifNode.parent, ifNode, replacement)
  266. else:
  267. emptyBlock = tree.Node("block");
  268. emptyBlock.set("line", ifNode.get("line"))
  269. ifNode.parent.replaceChild(ifNode, emptyBlock)
  270. def replaceChildWithNodes(node, oldChild, newChildren):
  271. """
  272. Replace the child node "oldNode" of the node "node" with a
  273. list of new children ("newChildren")
  274. """
  275. index = getNodeIndex(node, oldChild)
  276. node.removeChild(oldChild)
  277. # copy list
  278. children = newChildren[:]
  279. for child in children:
  280. node.addChild(child, index)
  281. index += 1
  282. def getNodeIndex(parent, node):
  283. """
  284. Returns the index of a node.
  285. TODO: mode to tree?
  286. """
  287. return parent.children.index(node)
  288. def isStringLiteral(node):
  289. """
  290. Whether a node is a string literal
  291. """
  292. return node.type == "constant" and node.get("constantType") == "string"
  293. def assembleVariable(variableItem):
  294. """
  295. Return the full variable name from a variable node, and an isComplete flag if the name could
  296. be assembled completely.
  297. """
  298. if variableItem.type != "variable":
  299. raise tree.NodeAccessException("'variableItem' is no variable node", variableItem)
  300. assembled = ""
  301. for child in variableItem.children:
  302. if child.type == "commentsBefore":
  303. continue
  304. elif child.type != "identifier":
  305. # this means there is some accessor like part in the variable
  306. # e.g. foo["hello"]
  307. return assembled, False
  308. if len(assembled) != 0:
  309. assembled += "."
  310. assembled += child.get("name")
  311. return assembled, True
  312. def compileString(jsString, uniqueId=""):
  313. """
  314. Compile a string containing a JavaScript fragment into a syntax tree.
  315. """
  316. return treegenerator.createSyntaxTree(tokenizer.parseStream(jsString, uniqueId)).getFirstChild()
  317. def variableOrArrayNodeToArray(node):
  318. """
  319. Normalizes a variable node or an array node containing variables
  320. to a python array of the variable names
  321. """
  322. arr = []
  323. if node.type == "array":
  324. for child in node.children:
  325. if child.type == "variable":
  326. arr.append((assembleVariable(child))[0])
  327. elif node.type == "variable":
  328. arr.append((assembleVariable(node))[0])
  329. else:
  330. raise tree.NodeAccessException("'node' is no variable or array node", node)
  331. return arr
  332. def getLineAndColumnFromSyntaxItem(syntaxItem):
  333. """
  334. Returns a tupel of the line and the column of a tree node.
  335. """
  336. line = None
  337. column = None
  338. while line == None and column == None and syntaxItem:
  339. line = syntaxItem.get("line", False)
  340. column = syntaxItem.get("column", False)
  341. if syntaxItem.hasParent():
  342. syntaxItem = syntaxItem.parent
  343. else:
  344. syntaxItem = None
  345. return line, column
  346. def getFileFromSyntaxItem(syntaxItem):
  347. """
  348. Returns the file name of a tree node
  349. """
  350. file = None
  351. while file == None and syntaxItem:
  352. file = syntaxItem.get("file", False)
  353. if hasattr(syntaxItem, "parent"):
  354. syntaxItem = syntaxItem.parent
  355. else:
  356. syntaxItem = None
  357. return file
  358. def createPair(key, value, commentParent=None):
  359. par = tree.Node("keyvalue")
  360. sub = tree.Node("value")
  361. par.set("key", key)
  362. par.addChild(sub)
  363. sub.addChild(value)
  364. if commentParent and commentParent.hasChild("commentsBefore"):
  365. par.addChild(commentParent.getChild("commentsBefore"))
  366. return par
  367. def createConstant(type, value):
  368. constant = tree.Node("constant")
  369. constant.set("constantType", type)
  370. constant.set("value", value)
  371. if type == "string":
  372. constant.set("detail", "doublequotes")
  373. return constant
  374. def createVariable(l):
  375. var = tree.Node("variable")
  376. for name in l:
  377. iden = tree.Node("identifier")
  378. iden.set("name", name)
  379. var.addChild(iden)
  380. return var
  381. def createBlockComment(txt):
  382. l = "*****************************************************************************"
  383. s = ""
  384. s += "/*\n"
  385. s += "%s\n" % l
  386. s += " %s\n" % txt.upper()
  387. s += "%s\n" % l
  388. s += "*/"
  389. bef = tree.Node("commentsBefore")
  390. com = tree.Node("comment")
  391. bef.addChild(com)
  392. com.set("multiline", True)
  393. com.set("connection", "before")
  394. com.set("text", s)
  395. com.set("detail", comment.getFormat(s))
  396. return bef