summaryrefslogtreecommitdiff
path: root/doc/_extensions/autojsdoc/parser/visitor.py
diff options
context:
space:
mode:
authorXavier Morel <xmo@odoo.com>2017-09-18 10:13:54 +0200
committerGéry Debongnie <ged@odoo.com>2017-09-18 11:54:36 +0200
commit313f69e9487e991e4db33c75682342b185da5c73 (patch)
treedfe8918e95a5f27888319e7a5c1a5187cd8c8751 /doc/_extensions/autojsdoc/parser/visitor.py
parent8defed9536711a05f147f28916b7109854f9932c (diff)
[ADD] doc: JS document extraction system
* Fix a bunch of ill-documented/incomplete/incorrect method docs * add start of Sphinx extension to extract & integrate jsdoc into Sphinx documentation: - parse JS files (and don't blow up), uses a fork of pyjsparser as the project currently does not parse comments - extract cross-module dependency information - parse JsDoc comments using pyjsdoc and infer structure from code & jsdoc - ``ast`` CLI printing a simplified AST of the input files - ``dependencies`` creating a dependency graph of either all modules in the provided input files or the modules matching the specified filters (warning: will not work if missing dependencies), generates a .dot file - ``extractor`` generating a plain text module documentation (mix of rst and markdown styles, not anything formal) * sphinx extension with an "automodule" directive taking a module name and generating the documentation for it
Diffstat (limited to 'doc/_extensions/autojsdoc/parser/visitor.py')
-rw-r--r--doc/_extensions/autojsdoc/parser/visitor.py126
1 files changed, 126 insertions, 0 deletions
diff --git a/doc/_extensions/autojsdoc/parser/visitor.py b/doc/_extensions/autojsdoc/parser/visitor.py
new file mode 100644
index 00000000000..e5529d511dc
--- /dev/null
+++ b/doc/_extensions/autojsdoc/parser/visitor.py
@@ -0,0 +1,126 @@
+from pyjsparser.pyjsparserdata import Syntax
+
+_binary = lambda n: [n['left'], n['right']]
+_function = lambda n: n['params'] + n['defaults'] + [n['body']]
+# yields children in visitation order
+_children = {
+ Syntax.ArrayExpression: lambda n: n['elements'],
+ Syntax.ArrayPattern: lambda n: n['elements'],
+ Syntax.ArrowFunctionExpression: _function,
+ Syntax.AssignmentExpression: _binary,
+ Syntax.AssignmentPattern: _binary,
+ Syntax.BinaryExpression: _binary,
+ Syntax.BlockStatement: lambda n: n['body'],
+ Syntax.BreakStatement: lambda n: [],
+ Syntax.CallExpression: lambda n: [n['callee']] + n['arguments'],
+ Syntax.CatchClause: lambda n: [n['param'], n['body']],
+ Syntax.ClassBody: lambda n: [n['body']],
+ Syntax.ClassDeclaration: lambda n: [n['superClass'], n['body']],
+ Syntax.ClassExpression: lambda n: [n['superClass'], n['body']],
+ Syntax.ConditionalExpression: lambda n: [n['test'], n['consequent'], n['alternate']],
+ Syntax.ContinueStatement: lambda n: [],
+ Syntax.DebuggerStatement: lambda n: [],
+ Syntax.DoWhileStatement: lambda n: [n['body'], n['test']],
+ Syntax.EmptyStatement: lambda n: [],
+ Syntax.ExportAllDeclaration: lambda n: [n['source']],
+ Syntax.ExportDefaultDeclaration: lambda n: [n['declaration']],
+ Syntax.ExportNamedDeclaration: lambda n: ([n['declaration']] if n['declaration'] else n['specifiers']) + [n['source']],
+ Syntax.ExportSpecifier: lambda n: [n['local'], n['exported']],
+ Syntax.ExpressionStatement: lambda n: [n['expression']],
+ Syntax.ForStatement: lambda n: [n['init'], n['test'], n['update'], n['body']],
+ Syntax.ForInStatement: lambda n: [n['left'], n['right'], n['body']],
+ Syntax.FunctionDeclaration: _function,
+ Syntax.FunctionExpression: _function,
+ Syntax.Identifier: lambda n: [],
+ Syntax.IfStatement: lambda n: [n['test'], n['consequent'], n['alternate']],
+ Syntax.ImportDeclaration: lambda n: n['specifiers'] + [n['source']],
+ Syntax.ImportDefaultSpecifier: lambda n: [n['local']],
+ Syntax.ImportNamespaceSpecifier: lambda n: [n['local']],
+ Syntax.ImportSpecifier: lambda n: [n['local'], n['imported']],
+ Syntax.LabeledStatement: lambda n: [n['body']],
+ Syntax.Literal: lambda n: [],
+ Syntax.LogicalExpression: _binary,
+ Syntax.MemberExpression: lambda n: [n['object'], n['property']],
+ #Syntax.MethodDefinition: lambda n: [],
+ Syntax.NewExpression: lambda n: [n['callee']] + n['arguments'],
+ Syntax.ObjectExpression: lambda n: n['properties'],
+ Syntax.ObjectPattern: lambda n: n['properties'],
+ Syntax.Program: lambda n: n['body'],
+ Syntax.Property: lambda n: [n['key'], n['value']],
+ Syntax.RestElement: lambda n: [n['argument']],
+ Syntax.ReturnStatement: lambda n: [n['argument']],
+ Syntax.SequenceExpression: lambda n: n['expressions'],
+ Syntax.SpreadElement: lambda n: [n['argument']],
+ Syntax.Super: lambda n: [],
+ Syntax.SwitchCase: lambda n: [n['test'], n['consequent']],
+ Syntax.SwitchStatement: lambda n: [n['discriminant']] + n['cases'],
+ Syntax.TaggedTemplateExpression: lambda n: [n['tag'], n['quasi']],
+ Syntax.TemplateElement: lambda n: [],
+ Syntax.TemplateLiteral: lambda n: n['quasis'] + n['expressions'],
+ Syntax.ThisExpression: lambda n: [],
+ Syntax.ThrowStatement: lambda n: [n['argument']],
+ Syntax.TryStatement: lambda n: [n['block'], n['handler'], n['finalizer']],
+ Syntax.UnaryExpression: lambda n: [n['argument']],
+ Syntax.UpdateExpression: lambda n: [n['argument']],
+ Syntax.VariableDeclaration: lambda n: n['declarations'],
+ Syntax.VariableDeclarator: lambda n: [n['id'], n['init']],
+ Syntax.WhileStatement: lambda n: [n['test'], n['body']],
+ Syntax.WithStatement: lambda n: [n['object'], n['body']],
+}
+
+SKIP = object()
+class Visitor(object):
+ """
+ Generic visitor for the pyjsparser AST.
+
+ Visitation is driven by the ``visit`` method, which iterates the tree in
+ depth-first pre-order.
+
+ For each node, calls ``enter_$NODETYPE``, visits the children then calls
+ ``exit_$NODETYPE``. If the enter or exit methods are not present on the
+ visitor, falls back on ``enter_generic`` and ``exit_generic``.
+
+ Any ``enter_`` method can return ``SKIP`` to suppress both the traversal
+ of the subtree *and* the call to the corresponding ``exit_`` method
+ (whether generic or specific).
+
+ For convenience, ``visit`` will return whatever is set as the visitor's
+ ``result`` attribute, ``None`` by default.
+
+ ``visit`` can be given multiple root nodes, and it can be called multiple
+ times. The ``result`` attribute is cleared at each call but not between
+ two roots of the same ``visit`` call.
+ """
+ def __init__(self):
+ super(Visitor, self).__init__()
+ self.result = None
+
+ def enter_generic(self, node): pass
+ def exit_generic(self, node): pass
+
+ def visit(self, nodes):
+ if isinstance(nodes, dict):
+ nodes = [nodes]
+ # if multiple nodes are passed in, we need to reverse the order in
+ # order to traverse front-to-back rather than the other way around
+ nodes = list(reversed(nodes))
+
+ while nodes:
+ node = nodes.pop()
+ # should probably filter None descendants in _children...
+ if node is None:
+ continue
+ node_type = node['type']
+ if node_type == '_exit':
+ node = node['node']
+ getattr(self, 'exit_' + node['type'], self.exit_generic)(node)
+ continue
+
+ if getattr(self, 'enter_' + node_type, self.enter_generic)(node) is SKIP:
+ continue
+
+ nodes.append({'type': '_exit', 'node': node})
+ nodes.extend(reversed(_children[node_type](node)))
+
+ return self.result
+