Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(82)

Unified Diff: tools/nixysa/nixysa/js_utils.py

Issue 2043006: WTF NPAPI extension. Early draft. Base URL: http://src.chromium.org/svn/trunk/src/
Patch Set: Created 10 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « tools/nixysa/nixysa/js_header_generator.py ('k') | tools/nixysa/nixysa/locking.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/nixysa/nixysa/js_utils.py
===================================================================
--- tools/nixysa/nixysa/js_utils.py (revision 0)
+++ tools/nixysa/nixysa/js_utils.py (revision 0)
@@ -0,0 +1,674 @@
+#!/usr/bin/python2.4
+#
+# Copyright 2008 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Utilities for Javascript code generation.
+
+This module contains a few utilities for Javascript code generation.
+"""
+
+import re
+import sys
+import naming
+import gflags
+import log
+import cpp_utils
+import writer
+
+
+_doxygen_tag_re = re.compile(r'\s*\\(\w+) ')
+_param_re = re.compile(r'\s*\\param (\w+) (.*?)$')
+_non_id_re = re.compile(r'[^A-Z0-9_]')
+
+
+def GetScopePrefix(scope, type_defn, scope_operator):
+ """Gets the prefix string to reference a type from a given scope.
+
+ This function returns a concatenation of js scope operators such as, in the
+ context of the given scope, when prefixing the name of the given type, it
+ will reference exactly that type.
+
+ For example, given:
+ namespace A {
+ namespace B {
+ class C;
+ }
+ namespace D {
+ void F();
+ }
+ }
+ To access C from F, one needs to refer to it by B::C. This function will
+ return the 'B::' part.
+
+ Args:
+ scope: the Definition for the scope from which the type must be accessed.
+ type_defn: the Definition for the type which must be accessed.
+ scope_operator: the scope operator for your language, ie '.' or '::'
+
+ Returns:
+ the prefix string.
+ """
+ return cpp_utils.GetScopePrefixWithScopeOperator(scope, type_defn, '.')
+
+
+def GetScopedName(scope, type_defn):
+ """Gets the prefix string to reference a type from a given scope.
+
+ This function returns a concatenation of C++ scope operators such as, in the
+ context of the given scope, when prefixing the name of the given type, it
+ will reference exactly that type.
+
+ For example, given:
+ namespace A {
+ namespace B {
+ class C;
+ }
+ namespace D {
+ void F();
+ }
+ }
+ To access C from F, one needs to refer to it by B::C. This function will
+ return exactly that.
+
+ Args:
+ scope: the Definition for the scope from which the type must be accessed.
+ type_defn: the Definition for the type which must be accessed.
+
+ Returns:
+ the scoped reference string.
+ """
+ return GetScopePrefix(scope, type_defn) + type_defn.name
+
+
+def GetFullyQualifiedScopePrefix(scope):
+ """Gets the fully qualified scope prefix.
+
+ Args:
+ scope: the Definition for the scope from which the type must be accessed.
+
+ Returns:
+ the fully qualified scope prefix string.
+ """
+ scope_stack = scope.GetParentScopeStack() + [scope]
+ return '.'.join([s.name for s in scope_stack[1:]] + [''])
+
+
+def GetFullyQualifiedTypeName(type_defn):
+ """Gets the fully qualified name for a type
+
+ Args:
+ type_defn: the type definition you want a name for.
+
+ Returns:
+ the fully qualified name string.
+ """
+ return type_defn.binding_model.JSDocTypeString(type_defn)
+
+
+def GetFullyQualifiedTypeString(type_defn):
+ """
+ """
+ type_defn = type_defn.GetFinalType()
+ type_stack = type_defn.GetParentScopeStack()
+ name = type_defn.name
+ return '.'.join([s.name for s in type_stack[1:]] + [name])
+
+
+def GetGetterName(field):
+ """Gets the name of the getter function for a member field.
+
+ Unless overridden by the 'getter' attribute in IDL, the default name for the
+ getter function is the name of the field, normalized to the lower-case
+ convention.
+
+ Args:
+ field: the Definition for the field.
+
+ Returns:
+ the name of the getter function.
+ """
+ return (field.attributes['getter'] or naming.Normalize(field.name,
+ naming.Lower))
+
+
+def GetSetterName(field):
+ """Gets the name of the setter function for a member field.
+
+ Unless overridden by the 'setter' attribute in IDL, the default name for the
+ setter function is 'set_' concatenated with the name of the field, normalized
+ to the lower-case convention.
+
+ Args:
+ field: the Definition for the field.
+
+ Returns:
+ the name of the setter function.
+ """
+ return (field.attributes['setter'] or
+ 'set_%s' % naming.Normalize(field.name, naming.Lower))
+
+
+def GetFunctionParamPrototype(scope, param):
+ """Gets the string needed to declare a parameter in a function prototype.
+
+ Args:
+ scope: the scope of the prototype.
+ param: the Function.Param to declare
+
+ Returns:
+ a (string, list) pair. The string is the declaration of the parameter in
+ the prototype. The list contains (nam, Definition) pairs, describing the
+ types that need to be forward-declared (bool is false) or defined (bool is
+ true).
+ """
+ bm = param.type_defn.binding_model
+ if param.mutable:
+ text, need_defn = bm.CppMutableParameterString(scope, param.type_defn)
+ else:
+ text, need_defn = bm.CppParameterString(scope, param.type_defn)
+ name = naming.Normalize(param.name, naming.Java)
+ return name, [(name, param.type_defn)]
+
+
+def GetFunctionPrototype(scope, obj, member):
+ """Gets the string needed to declare a function prototype.
+
+ Args:
+ scope: the scope of the prototype.
+ obj: the function to declare.
+ member: True if member function
+
+ Returns:
+ A string prototype.
+ """
+ id_prefix = GetFullyQualifiedScopePrefix(scope)
+ proto = ''
+ if member:
+ proto = 'prototype.'
+ param_strings = [GetFunctionParamPrototype(scope, p)[0] for p in obj.params]
+ param_string = ', '.join(param_strings)
+ prototype = '%s%s%s = function(%s) { };' % (
+ id_prefix, proto, naming.Normalize(obj.name, naming.Java), param_string)
+ return prototype
+
+
+def GetFunctionParamType(obj, param_name):
+ """Gets the type of a function param.
+
+ Args:
+ obj: The function.
+ param_name: The name of the parameter.
+
+ Returns
+ A string which is the type of the parameter.
+ """
+ if param_name[-1] == '?':
+ param_name = param_name[:-1]
+ for p in obj.params:
+ if p.name == param_name:
+ return GetFullyQualifiedTypeName(p.type_defn)
+ log.SourceError(obj.source, 'No param "%s" on function "%s"' %
+ (param_name, obj.name))
+ return '*'
+
+
+def GetCommentsForParams(func):
+ """Gets the comments for the params.
+
+ Args:
+ func: The function.
+ param: The parameter.
+ Returns:
+ a (string, dict) pair. The string is the comments minus the param parts.
+ The dict is a dict of param names to comments.
+ """
+ collecting_key = None
+ param_comments = {}
+ comments = []
+ comment_lines = func.attributes['__docs'].splitlines()
+ for line in comment_lines:
+ match = _doxygen_tag_re.match(line)
+ if match:
+ if match.group(1) == 'param':
+ match = _param_re.match(line)
+ if match:
+ collecting_key = match.group(1)
+ param_comments[collecting_key] = match.group(2)
+ else:
+ log.SourceError(func,
+ ('Incorrect format for param ' +
+ 'comment for param "%s" on function "%s"') %
+ (param_name, func.name))
+ else:
+ comments += [line]
+ collecting_key = None
+ elif collecting_key:
+ param_comments[collecting_key] += '\n' + line
+ else:
+ comments += [line]
+ return '\n'.join(comments), param_comments
+
+
+def GetParamSpec(obj, param_name):
+ """Gets the parameter specification string for a function parameter.
+
+ Args:
+ obj: The function.
+ param_name: The name of the paramter.
+
+ Returns:
+ a string in JSDOC format for the parameter.
+ """
+ type = GetFunctionParamType(obj, param_name)
+ return '@param {%s} %s ' % (type, naming.Normalize(param_name, naming.Java))
+
+
+def GetReturnSpec(obj, flags):
+ """Gets the return type specification string for a function.
+
+ Args:
+ obj: The function.
+ flags: An map of flags. The only one we care about is 'eat_lines' which
+ we'll set to True if the 'noreturndocs' attribute exists.
+
+ Returns:
+ a string in JSDOC format for the return type.
+ """
+ if gflags.FLAGS['no-return-docs'].value and 'noreturndocs' in obj.attributes:
+ flags['eat_lines'] = True
+ return ''
+ if obj.type_defn:
+ type = GetFullyQualifiedTypeName(obj.type_defn)
+ else:
+ type = "**unknown return type**"
+ return '@return {%s}' % type
+
+
+class JavascriptFileWriter(object):
+ """Javascript file writer class.
+
+ This class helps with generating a Javascript file by parts, by allowing
+ delayed construction of 'sections' inside the code, that can be filled later.
+ For example one can create a section for forward declarations, and add code to
+ that section as the rest of the file gets written.
+
+ It also provides facility to add #include lines, and header guards for header
+ files, as well as simplifies namespace openning and closing.
+
+ It helps 'beautifying' the code in simple cases.
+ """
+
+ class Section(object):
+ """Javascript writer section class."""
+ # this regexp is used for EmitTemplate. It maps {#SomeText} into 'section'
+ # groups and the rest of the text into 'text' groups in the match objects.
+ _template_re = re.compile(
+ r"""
+ ^\s* # skip whitespaces
+ (?: # non grouping ( )
+ \$\{\#(?P<section>[_A-Za-z0-9]*)\} # matches a '${#AnyText}' section
+ # tag, puts the 'AnyText' in a
+ # 'section' group
+ | # ... or ...
+ (?P<text>.*) # matches any text, puts it into
+ # a 'text' group
+ ) # close non-grouping ( )
+ \s*$ # skip whitespaces
+ """, re.MULTILINE | re.VERBOSE)
+
+ def __init__(self, indent_string, indent):
+ """Inits a JavascriptFileWriter.Section.
+
+ Args:
+ indent_string: the string for one indentation.
+ indent: the number of indentations for code inside the section.
+ """
+ self._indent_string = indent_string
+ self._code = []
+ self._fe_namespaces = []
+ self._be_namespaces = []
+ self._section_map = {}
+ self._indent = indent
+ self._need_validate = False
+
+ def EmitSection(self, section):
+ """Emits a section at the current position.
+
+ When calling GetLines, the code for the section passed in will be output
+ at this position.
+
+ Args:
+ section: the section to add.
+ """
+ self._ValidateNamespace()
+ self._code.append(section)
+
+ def CreateUnlinkedSection(self, name, indent=None):
+ """Creates a section, but without emitting it.
+
+ When calling GetLines, the code for the created section will not be
+ output unless EmitSection is called.
+
+ Args:
+ name: the name of the section.
+ indent: (optional) the number of indentations for the new section.
+
+ Returns:
+ the created section.
+ """
+ if not indent:
+ indent = self._indent
+ section = JavascriptFileWriter.Section(self._indent_string, indent)
+ self._section_map[name] = section
+ return section
+
+ def CreateSection(self, name):
+ """Creates a section, and emits it at the current position.
+
+ When calling GetLines, the code for the created section will be output
+ at this position.
+
+ Args:
+ name: the name of the section.
+
+ Returns:
+ the created section.
+ """
+ self._ValidateNamespace()
+ section = self.CreateUnlinkedSection(name, indent=self._indent)
+ self.EmitSection(section)
+ return section
+
+ def GetSection(self, name):
+ """Gets a section by name.
+
+ Args:
+ name: the name of the section.
+
+ Returns:
+ the section if found, None otherwise.
+ """
+ try:
+ return self._section_map[name]
+ except KeyError:
+ return None
+
+ def PushNamespace(self, name):
+ """Opens a namespace.
+
+ This function opens a namespace by emitting code at the current position.
+ This is done lazily so that openning, closing, then openning the same
+ namespace again doesn't add extra code.
+
+ Args:
+ name: the name of the namespace.
+ """
+ self._need_validate = True
+ self._fe_namespaces.append(name)
+
+ def PopNamespace(self):
+ """Closes the current namespace.
+
+ This function closes the current namespace by emitting code at the
+ current position. This is done lazily so that openning, closing, then
+ openning the same namespace again doesn't add extra code.
+
+ Returns:
+ the name of the namespace that was closed.
+ """
+ self._need_validate = True
+ return self._fe_namespaces.pop()
+
+ def _ValidateNamespace(self):
+ """Validates the current namespace by emitting all the necessary code."""
+ if not self._need_validate:
+ return
+ self._need_validate = False
+ l = cpp_utils.GetCommonPrefixLength(
+ self._fe_namespaces, self._be_namespaces)
+ while len(self._be_namespaces) > l:
+ name = self._be_namespaces.pop()
+ self._code.append('} // namespace %s' % name)
+ for name in self._fe_namespaces[l:]:
+ self._be_namespaces.append(name)
+ self._code.append('namespace %s {' % name)
+
+ def EmitCode(self, code):
+ """Emits code at the current position.
+
+ The code passed in will be output at the current position when GetLines
+ is called. The code is split into lines, and re-indented to match the
+ section indentation.
+
+ Args:
+ code: a string containing the code to emit.
+ """
+ self._ValidateNamespace()
+ for line in code.split('\n'):
+ if not line:
+ self._code.append('')
+ else:
+ self._code.append(line)
+
+ def EmitTemplate(self, template):
+ """Emits a template at the current position.
+
+ Somewhat similarly to string.template.substitute, this function takes a
+ string containing code and escape sequences. Every time an escape
+ sequence, of the form '${#SectionName}', is found, a section is created
+ (or re-used) and emitted at the current position. Otherwise the text is
+ treated as code and simply emitted as-is. For example take the following
+ string:
+
+ void MyFunction() {
+ ${#MyFunctionBody}
+ }
+
+ Calling EmitTemplate with that string is equivalent to:
+
+ section.EmitCode('void MyFunction() {')
+ section.CreateSection('MyFunctionBody')
+ section.EmitCode('}')
+
+ If a section of that particular name already exists, it is reused.
+
+ Args:
+ template: a string containing the template to emit.
+ """
+
+ def _Match(mo):
+ """Function called for template regexp matches.
+
+ Args:
+ mo: match object.
+
+ Returns:
+ empty string.
+ """
+ section_group = mo.group('section')
+ if section_group:
+ if section_group in self._section_map:
+ section = self._section_map[section_group]
+ self.EmitSection(section)
+ else:
+ self.CreateSection(section_group)
+ else:
+ self.EmitCode(mo.group('text'))
+ return ''
+ self._template_re.sub(_Match, template)
+
+ def IsEmpty(self):
+ """Queries whether the section is empty or not.
+
+ Returns:
+ True if the section is empty, False otherwise.
+ """
+ return not self._code
+
+ def AddPrefix(self, code):
+ """Adds code at the beginning of the section.
+
+ Args:
+ code: a single code line.
+ """
+ self._code = [code] + self._code
+
+ def GetLines(self):
+ """Retrieves the full contents of the section.
+
+ This function gathers all the code that was emitted, including in
+ children sections.
+
+ Returns:
+ a list of code lines.
+ """
+ # close open namespaces
+ self._fe_namespaces = []
+ self._need_validate = True
+ self._ValidateNamespace()
+ lines = []
+ for line in self._code:
+ if isinstance(line, JavascriptFileWriter.Section):
+ lines.extend(line.GetLines())
+ else:
+ lines.append(line)
+ return lines
+
+ def __init__(self, filename, is_header, header_token=None,
+ indent_string=' '):
+ """Inits a JavascriptFileWriter.
+
+ The file writer has a 'main section' where all the code will go. See
+ CreateSection, EmitCode.
+
+ Args:
+ filename: the name of the file.
+ is_header: a boolean, True if this is a header file. In that case, the
+ header guard will be generated.
+ header_token: (optional) a string for the header guard token. Defaults to
+ a generated one based on the filename.
+ indent_string: (optional) the string to be used for indentations.
+ Defaults to two spaces.
+ """
+ self._filename = filename
+ self._is_header = is_header
+ self._header_token = ''
+ self._includes = []
+ self._include_section = self.Section(indent_string, 0)
+ self._main_section = self.Section(indent_string, 0)
+
+ def AddInclude(self, name, system=False):
+ """Adds an include to the file.
+
+ Args:
+ name: the name of the include.
+ system: (optional) True if it is a 'system' include (uses the <file.h>
+ syntax). Defaults to False.
+ """
+ if system:
+ self._include_section.EmitCode('#include <%s>' % name)
+ else:
+ self._include_section.EmitCode('#include "%s"' % name)
+
+ def CreateSection(self, name):
+ """Creates a section within the main section.
+
+ Args:
+ name: the name of the section to be created.
+
+ Returns:
+ the created section.
+ """
+ return self._main_section.CreateSection(name)
+
+ def GetSection(self, name):
+ """Gets a section by name from the main section.
+
+ Args:
+ name: the name of the section to look for.
+
+ Returns:
+ the created section if found, None otherwise.
+ """
+ return self._main_section.GetSection(name)
+
+ def PushNamespace(self, name):
+ """Opens a namespace in the main section.
+
+ This function opens a namespace by emitting code at the current position.
+ This is done lazily so that openning, closing, then openning the same
+ namespace again doesn't add extra code.
+
+ Args:
+ name: the name of the namespace.
+ """
+ self._main_section.PushNamespace(name)
+
+ def PopNamespace(self):
+ """Closes the current namespace in the main section.
+
+ This function closes the current namespace by emitting code at the
+ current position. This is done lazily so that openning, closing, then
+ openning the same namespace again doesn't add extra code.
+
+ Returns:
+ the name of the namespace that was closed.
+ """
+ return self._main_section.PopNamespace()
+
+ def EmitCode(self, code):
+ """Emits code at the current position in the main section.
+
+ Args:
+ code: a string containing the code to emit.
+ """
+ self._main_section.EmitCode(code)
+
+ def GetLines(self):
+ """Retrieves the full contents of the file writer.
+
+ This function gathers all the code that was emitted, including the
+ header guard (if this is a header file), and the includes.
+
+ Returns:
+ a list of code lines.
+ """
+ lines = []
+ include_lines = self._include_section.GetLines()
+ if include_lines:
+ lines.append('')
+ lines.extend(include_lines)
+ main_lines = self._main_section.GetLines()
+ if main_lines:
+ lines.append('')
+ lines.extend(main_lines)
+ return lines
+
+ def Write(self):
+ """Writes the full contents to the file.
+
+ This function writes the full contents to the file specified by the
+ 'filename' parameter at creation time.
+ """
+ writer.WriteIfContentDifferent(self._filename,
+ '\n'.join(self.GetLines()) + '\n')
+
+
+def main():
+ pass
+
+
+if __name__ == '__main__':
+ main()
Property changes on: tools/nixysa/nixysa/js_utils.py
___________________________________________________________________
Added: svn:executable
+ *
Added: svn:eol-style
+ LF
« no previous file with comments | « tools/nixysa/nixysa/js_header_generator.py ('k') | tools/nixysa/nixysa/locking.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698