Thread: [javascriptlint-commit] SF.net SVN: javascriptlint: [147] trunk
Status: Beta
Brought to you by:
matthiasmiller
|
From: <mat...@us...> - 2008-03-01 17:21:12
|
Revision: 147
http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=147&view=rev
Author: matthiasmiller
Date: 2008-03-01 09:21:01 -0800 (Sat, 01 Mar 2008)
Log Message:
-----------
initial import of pyjsl
Added Paths:
-----------
trunk/COPYING
trunk/INSTALL
trunk/Makefile.SpiderMonkey
trunk/TODO
trunk/jsl.py
trunk/pyjsl/
trunk/pyjsl/__init__.py
trunk/pyjsl/conf.py
trunk/pyjsl/lint.py
trunk/pyjsl/parse.py
trunk/pyjsl/util.py
trunk/pyjsl/visitation.py
trunk/pyjsl/warnings.py
trunk/pyspidermonkey/
trunk/pyspidermonkey/pyspidermonkey.c
trunk/pyspidermonkey/tokens.tbl
trunk/setup.py
trunk/test.py
Property Changed:
----------------
trunk/
Property changes on: trunk
___________________________________________________________________
Name: svn:ignore
+ dist
build
*.pyc
Added: trunk/COPYING
===================================================================
--- trunk/COPYING (rev 0)
+++ trunk/COPYING 2008-03-01 17:21:01 UTC (rev 147)
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
Added: trunk/INSTALL
===================================================================
--- trunk/INSTALL (rev 0)
+++ trunk/INSTALL 2008-03-01 17:21:01 UTC (rev 147)
@@ -0,0 +1,14 @@
+
+BUILDING FROM THE SUBVERSION TRUNK
+* Windows Prequisites:
+ * Visual Studio 2003
+ * Python
+ * MozillaBuild (http://developer.mozilla.org/en/docs/Windows_Build_Prerequisites)
+
+ Launch the MozillaBuild 7.1 batch file. (You may have to run this as an Administrator
+ on Windows Vista.) Run the commands in that shell.
+
+On all platforms:
+ $ make -f Makefile.SpiderMonkey
+ $ python setup.py build
+
Added: trunk/Makefile.SpiderMonkey
===================================================================
--- trunk/Makefile.SpiderMonkey (rev 0)
+++ trunk/Makefile.SpiderMonkey 2008-03-01 17:21:01 UTC (rev 147)
@@ -0,0 +1,67 @@
+
+SPIDERMONKEY_SRC=spidermonkey/src
+
+# Load the SpiderMonkey config to find the OS define
+# Also use this for the SO_SUFFIX
+BUILD_OPT=1
+DEPTH=$(SPIDERMONKEY_SRC)
+include $(SPIDERMONKEY_SRC)/config.mk
+SPIDERMONKEY_OS=$(firstword $(patsubst -D%, %, $(filter -DXP_%, $(OS_CFLAGS))))
+
+ifdef USE_MSVC
+JS_LIB=js32.lib
+else
+JS_LIB=libjs.a
+endif
+
+BUILD_DIR=build/spidermonkey
+
+# Use a dynamically-created makefile to determine the distutils output dir
+DISTUTILS_DIR_MAKEFILE=$(BUILD_DIR)/Makefile-distutils
+include $(DISTUTILS_DIR_MAKEFILE)
+
+ORIG_LIB=$(SPIDERMONKEY_SRC)/$(OBJDIR)/$(JS_LIB)
+COPY_LIB=$(BUILD_DIR)/$(JS_LIB)
+ORIG_DLL=$(SPIDERMONKEY_SRC)/$(OBJDIR)/js32.dll
+COPY_DLL=$(DISTUTILS_DIR)/js32.dll
+OS_HEADER=$(BUILD_DIR)/js_operating_system.h
+ORIG_JSAUTOCFG_H=$(SPIDERMONKEY_SRC)/$(OBJDIR)/jsautocfg.h
+COPY_JSAUTOCFG_H=$(BUILD_DIR)/jsautocfg.h
+
+ALL_TARGETS=$(COPY_LIB) $(OS_HEADER)
+ifndef PREBUILT_CPUCFG
+ALL_TARGETS+=$(COPY_JSAUTOCFG_H)
+endif
+
+ifeq ($(SPIDERMONKEY_OS),XP_WIN)
+ALL_TARGETS+=$(COPY_DLL)
+endif
+
+all: $(ALL_TARGETS)
+
+clean:
+ make -f Makefile.ref -C $(SPIDERMONKEY_SRC) BUILD_OPT=$(BUILD_OPT) clean
+ rm $(ORIG_LIB)
+ rm -R $(BUILD_DIR)
+
+$(DISTUTILS_DIR_MAKEFILE): Makefile.SpiderMonkey $(BUILD_DIR)
+ python -c "import setup; print 'DISTUTILS_DIR='+setup.get_lib_path()" >> $(DISTUTILS_DIR_MAKEFILE)
+
+$(BUILD_DIR):
+ mkdir -p $(BUILD_DIR)
+
+$(COPY_LIB): $(BUILD_DIR) $(ORIG_LIB)
+ cp $(ORIG_LIB) $(COPY_LIB)
+
+$(COPY_DLL): $(BUILD_DIR) $(ORIG_LIB)
+ cp $(ORIG_DLL) $(COPY_DLL)
+
+$(OS_HEADER): $(BUILD_DIR)
+ echo "#define $(SPIDERMONKEY_OS)" > $(OS_HEADER)
+
+$(COPY_JSAUTOCFG_H): $(ORIG_JSAUTOCFG_H)
+ cp $(ORIG_JSAUTOCFG_H) $(COPY_JSAUTOCFG_H)
+
+$(ORIG_LIB):
+ make -f Makefile.ref -C $(SPIDERMONKEY_SRC) BUILD_OPT=$(BUILD_OPT)
+
Added: trunk/TODO
===================================================================
--- trunk/TODO (rev 0)
+++ trunk/TODO 2008-03-01 17:21:01 UTC (rev 147)
@@ -0,0 +1,13 @@
+
+> implement conf file
+
+> support HTML parsing
+
+> support JScript extensions
+
+> implement semicolons warning
+
+> implement line break warning
+
+> add test for syntax error
+
Added: trunk/jsl.py
===================================================================
--- trunk/jsl.py (rev 0)
+++ trunk/jsl.py 2008-03-01 17:21:01 UTC (rev 147)
@@ -0,0 +1,147 @@
+#!/usr/bin/python
+import codecs
+import getopt
+import glob
+import os
+import sys
+import unittest
+
+try:
+ import setup
+except ImportError:
+ pass
+else:
+ sys.path.append(setup.get_lib_path())
+
+import pyjsl.conf
+import pyjsl.parse
+import pyjsl.util
+import test
+
+_lint_results = {
+ 'warnings': 0,
+ 'errors': 0
+}
+
+def get_test_files():
+ # Get a list of test files.
+ dir_ = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'tests')
+
+ all_files = []
+ for root, dirs, files in os.walk(dir_):
+ all_files += [os.path.join(dir_, root, file) for file in files]
+ if '.svn' in dirs:
+ dirs.remove('.svn')
+ # TODO
+ if 'conf' in dirs:
+ dirs.remove('conf')
+ all_files.sort()
+ return all_files
+
+def run_tests():
+ for file in get_test_files():
+ if file.endswith('.htm') or file.endswith('.html'):
+ continue #TODO
+ elif file.endswith('.js'):
+ print file
+ try:
+ test.run(file)
+ except test.TestError, error:
+ print error
+
+def _dump(paths):
+ for path in paths:
+ script = pyjsl.util.readfile(path)
+ pyjsl.parse.dump_tree(script)
+
+def _lint(paths, conf):
+ def lint_error(path, line, col, errname):
+ _lint_results['warnings'] = _lint_results['warnings'] + 1
+ print '%s(%i): %s' % (path, line, errname)
+ pyjsl.lint.lint_files(paths, lint_error, conf=conf)
+
+def _resolve_paths(path, recurse):
+ if os.path.isfile(path):
+ return [path]
+ elif os.path.isdir(path):
+ dir = path
+ pattern = '*'
+ else:
+ dir, pattern = os.path.split(path)
+
+ # Build a list of directories
+ dirs = [dir]
+ if recurse:
+ for cur_root, cur_dirs, cur_files in os.walk(dir):
+ for name in cur_dirs:
+ dirs.append(os.path.join(cur_root, name))
+
+ # Glob all files.
+ paths = []
+ for dir in dirs:
+ paths.extend(glob.glob(os.path.join(dir, pattern)))
+ return paths
+
+def profile_enabled(func, *args, **kwargs):
+ import tempfile
+ import hotshot
+ import hotshot.stats
+ handle, filename = tempfile.mkstemp()
+ profile = hotshot.Profile(filename)
+ profile.runcall(func, *args, **kwargs)
+ profile.close()
+ stats = hotshot.stats.load(filename)
+ stats = stats.sort_stats("time")
+ stats.print_stats()
+def profile_disabled(func, *args, **kwargs):
+ func(*args, **kwargs)
+
+def usage():
+ print """
+Usage:
+ jsl [files]
+ --help (-h) print this help
+ --test (-t) run tests
+ --dump= dump this script
+"""
+
+if __name__ == '__main__':
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], 'ht:v', ['conf=', 'help', 'test', 'dump', 'unittest', 'profile'])
+ except getopt.GetoptError:
+ usage()
+ sys.exit(2)
+
+ dump = False
+ conf = pyjsl.conf.Conf()
+ profile_func = profile_disabled
+ for opt, val in opts:
+ if opt in ('-h', '--help'):
+ usage()
+ sys.exit()
+ if opt in ('--dump',):
+ dump = True
+ if opt in ('-t', '--test'):
+ profile_func(run_tests)
+ if opt in ('--unittest',):
+ unittest.main(pyjsl.parse, argv=sys.argv[:1])
+ if opt in ('--profile',):
+ profile_func = profile_enabled
+ if opt in ('--conf',):
+ conf.loadfile(val)
+
+ paths = []
+ for recurse, path in conf['paths']:
+ paths.extend(_resolve_paths(path, recurse))
+ for arg in args:
+ paths.extend(_resolve_paths(arg, False))
+ if dump:
+ profile_func(_dump, paths)
+ else:
+ profile_func(_lint, paths, conf)
+
+ if _lint_results['errors']:
+ sys.exit(3)
+ if _lint_results['warnings']:
+ sys.exit(1)
+
Property changes on: trunk/jsl.py
___________________________________________________________________
Name: svn:executable
+ *
Property changes on: trunk/pyjsl
___________________________________________________________________
Name: svn:ignore
+ *.pyc
Added: trunk/pyjsl/__init__.py
===================================================================
Added: trunk/pyjsl/conf.py
===================================================================
--- trunk/pyjsl/conf.py (rev 0)
+++ trunk/pyjsl/conf.py 2008-03-01 17:21:01 UTC (rev 147)
@@ -0,0 +1,134 @@
+import os
+
+import warnings
+
+class ConfError(Exception):
+ def __init__(self, error):
+ Exception.__init__(error)
+ self.lineno = None
+ self.path = None
+
+class Setting():
+ wants_parm = False
+ wants_dir = False
+
+class BooleanSetting(Setting):
+ wants_parm = False
+ def __init__(self, default):
+ self.value = default
+ def load(self, enabled):
+ self.value = enabled
+
+class StringSetting(Setting):
+ wants_parm = True
+ def __init__(self, default):
+ self.value = default
+ def load(self, enabled, parm):
+ if not enabled:
+ raise ConfError, 'Expected +.'
+ self.value = parm
+
+class DeclareSetting(Setting):
+ wants_parm = True
+ def __init__(self):
+ self.value = []
+ def load(self, enabled, parm):
+ if not enabled:
+ raise ConfError, 'Expected +.'
+ self.value.append(parm)
+
+class ProcessSetting(Setting):
+ wants_parm = True
+ wants_dir = True
+ def __init__(self, recurse_setting):
+ self.value = []
+ self._recurse = recurse_setting
+ def load(self, enabled, parm, dir):
+ if dir:
+ parm = os.path.join(dir, parm)
+ self.value.append((self._recurse.value, parm))
+
+class Conf():
+ def __init__(self):
+ recurse = BooleanSetting(False)
+ self._settings = {
+ 'recurse': recurse,
+ 'show_context': BooleanSetting(False),
+ 'output-format': StringSetting('TODO'),
+ 'lambda_assign_requires_semicolon': BooleanSetting(False),
+ 'legacy_control_comments': BooleanSetting(True),
+ 'jscript_function_extensions': BooleanSetting(False),
+ 'always_use_option_explicit': BooleanSetting(False),
+ 'define': DeclareSetting(),
+ 'context': BooleanSetting(False),
+ 'process': ProcessSetting(recurse),
+ # SpiderMonkey warnings
+ 'no_return_value': BooleanSetting(True),
+ 'equal_as_assign': BooleanSetting(True),
+ 'anon_no_return_value': BooleanSetting(True)
+ }
+ for klass in warnings.klasses:
+ self._settings[klass.__name__] = BooleanSetting(True)
+ self.loadline('-block_without_braces')
+
+ def loadfile(self, path):
+ path = os.path.abspath(path)
+ conf = open(path, 'r').read()
+ try:
+ self.loadtext(conf, dir=os.path.dirname(path))
+ except ConfError, error:
+ error.path = path
+ raise
+
+ def loadtext(self, conf, dir=None):
+ lines = conf.splitlines()
+ for lineno in range(0, len(lines)):
+ try:
+ self.loadline(lines[lineno], dir)
+ except ConfError, error:
+ error.lineno = lineno
+ raise
+
+ def loadline(self, line, dir=None):
+ assert not '\r' in line
+ assert not '\n' in line
+
+ # Allow comments
+ line = line.partition('#')[0]
+ line = line.rstrip()
+ if not line:
+ return
+
+ # Parse the +/-
+ if line.startswith('+'):
+ enabled = True
+ elif line.startswith('-'):
+ enabled = False
+ else:
+ raise ConfError, 'Expected + or -.'
+ line = line[1:]
+
+ # Parse the key/parms
+ name = line.split()[0].lower()
+ parm = line[len(name):].lstrip()
+
+ # Load the setting
+ setting = self._settings[name]
+ args = {
+ 'enabled': enabled
+ }
+ if setting.wants_parm:
+ args['parm'] = parm
+ elif parm:
+ raise ConfError, 'The %s setting does not expect a parameter.' % name
+ if setting.wants_dir:
+ args['dir'] = dir
+ setting.load(**args)
+
+ def __getitem__(self, name):
+ if name == 'paths':
+ name = 'process'
+ elif name == 'declarations':
+ name = 'define'
+ return self._settings[name].value
+
Added: trunk/pyjsl/lint.py
===================================================================
--- trunk/pyjsl/lint.py (rev 0)
+++ trunk/pyjsl/lint.py 2008-03-01 17:21:01 UTC (rev 147)
@@ -0,0 +1,288 @@
+#!/usr/bin/python
+import os.path
+import re
+
+import conf
+import parse
+import visitation
+import warni...
[truncated message content] |
|
From: <mat...@us...> - 2008-03-01 17:32:58
|
Revision: 148
http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=148&view=rev
Author: matthiasmiller
Date: 2008-03-01 09:32:55 -0800 (Sat, 01 Mar 2008)
Log Message:
-----------
include some notes on style guidelines and upgrading SpiderMonkey
Added Paths:
-----------
trunk/DEVELOPMENT
Removed Paths:
-------------
trunk/TODO
Copied: trunk/DEVELOPMENT (from rev 147, trunk/TODO)
===================================================================
--- trunk/DEVELOPMENT (rev 0)
+++ trunk/DEVELOPMENT 2008-03-01 17:32:55 UTC (rev 148)
@@ -0,0 +1,31 @@
+** STYLE GUIDELINES
+
+> Use tabs instead of spaces (for now)
+> All lines should be 79 characters or less
+> For everything else, follow http://www.python.org/dev/peps/pep-0008/ as much
+ as possible.
+
+
+** TODO
+
+> implement conf file
+> support HTML parsing
+> support JScript extensions
+> implement semicolons warning
+> implement line break warning
+> add test for syntax error
+
+
+** UPGRADING SPIDERMONKEY
+
+Use the following command to upgrade SpiderMonkey. Replace X.X.X with the
+version number. js-X.X.X is the directory containing the new version of
+SpiderMonkey. Use a relative path for pretty commit messages.
+
+svn_load_dirs.pl \
+ -t X.X.X \
+ -p svn_load_dirs.conf \
+ https://javascriptlint.svn.sourceforge.net/svnroot/javascriptlint/vendorsrc/Mozilla.org/js \
+ current \
+ js-X.X.X
+
Deleted: trunk/TODO
===================================================================
--- trunk/TODO 2008-03-01 17:21:01 UTC (rev 147)
+++ trunk/TODO 2008-03-01 17:32:55 UTC (rev 148)
@@ -1,13 +0,0 @@
-
-> implement conf file
-
-> support HTML parsing
-
-> support JScript extensions
-
-> implement semicolons warning
-
-> implement line break warning
-
-> add test for syntax error
-
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <mat...@us...> - 2008-03-01 18:44:55
|
Revision: 149
http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=149&view=rev
Author: matthiasmiller
Date: 2008-03-01 10:44:53 -0800 (Sat, 01 Mar 2008)
Log Message:
-----------
use numbers instead of strings for Node.kind and Node.opcode
Modified Paths:
--------------
trunk/pyjsl/lint.py
trunk/pyjsl/parse.py
trunk/pyjsl/warnings.py
trunk/pyspidermonkey/pyspidermonkey.c
Modified: trunk/pyjsl/lint.py
===================================================================
--- trunk/pyjsl/lint.py 2008-03-01 17:32:55 UTC (rev 148)
+++ trunk/pyjsl/lint.py 2008-03-01 18:44:53 UTC (rev 149)
@@ -8,6 +8,8 @@
import warnings
import util
+from pyspidermonkey import tok, op
+
_newline_kinds = (
'eof', 'comma', 'dot', 'semi', 'colon', 'lc', 'rc', 'lp', 'rb', 'assign',
'relop', 'hook', 'plus', 'minus', 'star', 'divop', 'eqop', 'shop', 'or',
@@ -29,14 +31,14 @@
_identifier = re.compile('^[A-Za-z_$][A-Za-z0-9_$]*$')
def _find_function(node):
- while node and node.kind != 'function':
+ while node and node.kind != tok.FUNCTION:
node = node.parent
return node
def _find_functions(node):
functions = []
while node:
- if node.kind == 'function':
+ if node.kind == tok.FUNCTION:
functions.append(node)
node = node.parent
return functions
@@ -70,7 +72,7 @@
class Scope():
def __init__(self, node):
- self._is_with_scope = node.kind == 'with'
+ self._is_with_scope = node.kind == tok.WITH
self._parent = None
self._kids = []
self._identifiers = {}
@@ -244,7 +246,7 @@
def warn_or_declare(name, node):
other = scope.get_identifier(name)
- if other and other.kind == 'function' and name in other.fn_args:
+ if other and other.kind == tok.FUNCTION and name in other.fn_args:
report(node, 'var_hides_arg')
elif other:
report(node, 'redeclared_var')
@@ -252,34 +254,34 @@
scope.add_declaration(name, node)
# Let the visitors warn.
- for kind in (node.kind, '%s:%s' % (node.kind, node.opcode)):
+ for kind in (node.kind, (node.kind, node.opcode)):
if kind in visitors:
for visitor in visitors[kind]:
warning_node = visitor(node)
if warning_node:
report(warning_node, visitor.im_class.__name__)
- if node.kind == 'name':
- if node.node_index == 0 and node.parent.kind == 'colon' and node.parent.parent.kind == 'rc':
+ if node.kind == tok.NAME:
+ if node.node_index == 0 and node.parent.kind == tok.COLON and node.parent.parent.kind == tok.RC:
pass # left side of object literal
- elif node.parent.kind == 'catch':
+ elif node.parent.kind == tok.CATCH:
scope.add_declaration(node.atom, node)
else:
scope.add_reference(node.atom, node)
# Push function identifiers
- if node.kind == 'function':
+ if node.kind == tok.FUNCTION:
if node.fn_name:
warn_or_declare(node.fn_name, node)
scope = scope.add_scope(node)
for var_name in node.fn_args:
scope.add_declaration(var_name, node)
- elif node.kind == 'lexicalscope':
+ elif node.kind == tok.LEXICALSCOPE:
scope = scope.add_scope(node)
- elif node.kind == 'with':
+ elif node.kind == tok.WITH:
scope = scope.add_scope(node)
- if node.parent and node.parent.kind == 'var':
+ if node.parent and node.parent.kind == tok.VAR:
warn_or_declare(node.atom, node)
for child in node.kids:
Modified: trunk/pyjsl/parse.py
===================================================================
--- trunk/pyjsl/parse.py 2008-03-01 17:32:55 UTC (rev 148)
+++ trunk/pyjsl/parse.py 2008-03-01 18:44:53 UTC (rev 149)
@@ -5,6 +5,7 @@
import unittest
import pyspidermonkey
+from pyspidermonkey import tok, op
class NodePos():
def __init__(self, line, col):
@@ -73,10 +74,8 @@
def _to_node(kid):
if kid:
return _Node(kid)
- kwargs['type'] = kwargs['type'].lower()
self.kind = kwargs['type']
- assert kwargs['opcode'].startswith('JSOP_')
- kwargs['opcode'] = kwargs['opcode'][5:].lower()
+ kwargs['opcode'] = kwargs['opcode']
self.opcode = kwargs['opcode']
self.kids = tuple([_to_node(kid) for kid in kwargs['kids']])
for kid in self.kids:
@@ -127,9 +126,9 @@
# Bail out for functions
if not are_functions_equiv:
- if self.kind == 'function':
+ if self.kind == tok.FUNCTION:
return False
- if self.kind == 'lp' and self.opcode == 'call':
+ if self.kind == tok.LP and self.opcode == op.CALL:
return False
if self.kind != other.kind:
@@ -138,11 +137,11 @@
return False
# Check atoms on names, properties, and string constants
- if self.kind in ('name', 'dot', 'string') and self.atom != other.atom:
+ if self.kind in (tok.NAME, tok.DOT, tok.STRING) and self.atom != other.atom:
return False
# Check values on numbers
- if self.kind == 'number' and self.dval != other.dval:
+ if self.kind == tok.NUMBER and self.dval != other.dval:
return False
# Compare child nodes
@@ -179,6 +178,7 @@
else:
comment_text = comment_text[2:]
opcode = 'JSOP_CPP_COMMENT'
+ opcode = opcode[5:].lower()
start_offset = match.start()+1
end_offset = match.end()
@@ -216,10 +216,10 @@
nodes = []
comment_ignore_ranges = NodeRanges()
def process(node):
- if node.kind == 'number':
+ if node.kind == tok.NUMBER:
node.atom = positions.text(node.start_pos(), node.end_pos())
- elif node.kind == 'string' or \
- (node.kind == 'object' and node.opcode == 'regexp'):
+ elif node.kind == tok.STRING or \
+ (node.kind == tok.OBJECT and node.opcode == op.REGEXP):
start_offset = positions.to_offset(node.start_pos())
end_offset = positions.to_offset(node.end_pos())
comment_ignore_ranges.add(start_offset, end_offset)
Modified: trunk/pyjsl/warnings.py
===================================================================
--- trunk/pyjsl/warnings.py 2008-03-01 17:32:55 UTC (rev 148)
+++ trunk/pyjsl/warnings.py 2008-03-01 18:44:53 UTC (rev 149)
@@ -4,7 +4,7 @@
The class can have one more more member functions to inspect nodes. The
function should be decorated with a @lookat call specifying the nodes it
-wants to examine. The node names may be in the 'kind' or 'kind:opcode'
+wants to examine. The node names may be in the tok.KIND or (tok.KIND, op.OPCODE)
format. To report a warning, the function should return the node causing
the warning.
@@ -12,7 +12,7 @@
class warning_name:
'questionable JavaScript coding style'
- @lookat('nodekind', 'nodekind:opcode')
+ @lookat(tok.NODEKIND, (tok.NODEKIND, op.OPCODE))
def _lint(self, node):
if questionable:
return node
@@ -22,17 +22,19 @@
import types
from visitation import visit as lookat
+from pyspidermonkey import tok, op
+
# TODO: document inspect, node:opcode, etc
def _get_branch_in_for(node):
" Returns None if this is not one of the branches in a 'for' "
- if node.parent and node.parent.kind == 'reserved' and \
- node.parent.parent.kind == 'for':
+ if node.parent and node.parent.kind == tok.RESERVED and \
+ node.parent.parent.kind == tok.FOR:
return node.node_index
return None
def _get_exit_points(node):
- if node.kind == 'lc':
+ if node.kind == tok.LC:
# Only if the last child contains it
exit_points = set([None])
for kid in node.kids:
@@ -41,13 +43,13 @@
exit_points.remove(None)
if kid:
exit_points |= _get_exit_points(kid)
- elif node.kind == 'if':
+ elif node.kind == tok.IF:
# Only if both branches have an exit point
cond_, if_, else_ = node.kids
exit_points = _get_exit_points(if_)
if else_:
exit_points |= _get_exit_points(else_)
- elif node.kind == 'switch':
+ elif node.kind == tok.SWITCH:
exit_points = set([None])
switch_has_default = False
@@ -57,7 +59,7 @@
for node in switch_stmts.kids:
case_val, case_stmt = node.kids
case_exit_points = _get_exit_points(case_stmt)
- switch_has_default = switch_has_default or node.kind == 'default'
+ switch_has_default = switch_has_default or node.kind == tok.DEFAULT
switch_has_final_fallthru = None in case_exit_points
exit_points |= case_exit_points
@@ -65,8 +67,8 @@
exit_points.remove(None)
# Check if the switch contained any break
- if 'break' in exit_points:
- exit_points.remove('break')
+ if tok.BREAK in exit_points:
+ exit_points.remove(tok.BREAK)
exit_points.add(None)
# Check if the switch had a default case
@@ -76,15 +78,15 @@
# Check if the final case statement had a fallthru
if switch_has_final_fallthru:
exit_points.add(None)
- elif node.kind == 'break':
- exit_points = set(['break'])
- elif node.kind == 'with':
+ elif node.kind == tok.BREAK:
+ exit_points = set([tok.BREAK])
+ elif node.kind == tok.WITH:
exit_points = _get_exit_points(node.kids[-1])
- elif node.kind == 'return':
- exit_points = set(['return'])
- elif node.kind == 'throw':
- exit_points = set(['throw'])
- elif node.kind == 'try':
+ elif node.kind == tok.RETURN:
+ exit_points = set([tok.RETURN])
+ elif node.kind == tok.THROW:
+ exit_points = set([tok.THROW])
+ elif node.kind == tok.TRY:
try_, catch_, finally_ = node.kids
exit_points = _get_exit_points(try_) | _get_exit_points(catch_)
@@ -100,24 +102,24 @@
class comparison_type_conv:
'comparisons against null, 0, true, false, or an empty string allowing implicit type conversion (use === or !==)'
- @lookat('eqop:eq')
+ @lookat((tok.EQOP, op.EQ))
def _lint(self, node):
lvalue, rvalue = node.kids
if not self._allow_coercive_compare(lvalue) or \
not self._allow_coercive_compare(rvalue):
return node
def _allow_coercive_compare(self, node):
- if node.kind == 'primary' and node.opcode in ('null', 'true', 'false'):
+ if node.kind == tok.PRIMARY and node.opcode in (op.NULL, op.TRUE, op.FALSE):
return False
- if node.kind == 'number' and not node.dval:
+ if node.kind == tok.NUMBER and not node.dval:
return False
- if node.kind == 'string' and not node.atom:
+ if node.kind == tok.STRING and not node.atom:
return False
return True
class default_not_at_end:
'the default case is not at the end of the switch statement'
- @lookat('default')
+ @lookat(tok.DEFAULT)
def _lint(self, node):
siblings = node.parent.kids
if node.node_index != len(siblings)-1:
@@ -125,7 +127,7 @@
class duplicate_case_in_switch:
'duplicate case in switch statement'
- @lookat('case')
+ @lookat(tok.CASE)
def _lint(self, node):
# Only look at previous siblings
siblings = node.parent.kids
@@ -133,30 +135,30 @@
# Compare values (first kid)
node_value = node.kids[0]
for sibling in siblings:
- if sibling.kind == 'case':
+ if sibling.kind == tok.CASE:
sibling_value = sibling.kids[0]
if node_value.is_equivalent(sibling_value, True):
return node
class missing_default_case:
'missing default case in switch statement'
- @lookat('switch')
+ @lookat(tok.SWITCH)
def _lint(self, node):
value, cases = node.kids
for case in cases.kids:
- if case.kind == 'default':
+ if case.kind == tok.DEFAULT:
return
return node
class with_statement:
'with statement hides undeclared variables; use temporary variable instead'
- @lookat('with')
+ @lookat(tok.WITH)
def _lint(self, node):
return node
class useless_comparison:
'useless comparison; comparing identical expressions'
- @lookat('eqop','relop')
+ @lookat(tok.EQOP,tok.RELOP)
def _lint(self, node):
lvalue, rvalue = node.kids
if lvalue.is_equivalent(rvalue):
@@ -164,45 +166,45 @@
class use_of_label:
'use of label'
- @lookat('colon:name')
+ @lookat((tok.COLON, op.NAME))
def _lint(self, node):
return node
class meaningless_block:
'meaningless block; curly braces have no impact'
- @lookat('lc')
+ @lookat(tok.LC)
def _lint(self, node):
- if node.parent and node.parent.kind == 'lc':
+ if node.parent and node.parent.kind == tok.LC:
return node
class misplaced_regex:
'regular expressions should be preceded by a left parenthesis, assignment, colon, or comma'
- @lookat('object:regexp')
+ @lookat((tok.OBJECT, op.REGEXP))
def _lint(self, node):
- if node.parent.kind == 'name' and node.parent.opcode == 'setname':
+ if node.parent.kind == tok.NAME and node.parent.opcode == op.SETNAME:
return # Allow in var statements
- if node.parent.kind == 'assign' and node.parent.opcode == 'nop':
+ if node.parent.kind == tok.ASSIGN and node.parent.opcode == op.NOP:
return # Allow in assigns
- if node.parent.kind == 'colon' and node.parent.parent.kind == 'rc':
+ if node.parent.kind == tok.COLON and node.parent.parent.kind == tok.RC:
return # Allow in object literals
- if node.parent.kind == 'lp' and node.parent.opcode == 'call':
+ if node.parent.kind == tok.LP and node.parent.opcode == op.CALL:
return # Allow in parameters
- if node.parent.kind == 'dot' and node.parent.opcode == 'getprop':
+ if node.parent.kind == tok.DOT and node.parent.opcode == op.GETPROP:
return # Allow in /re/.property
- if node.parent.kind == 'return':
+ if node.parent.kind == tok.RETURN:
return # Allow for return values
return node
class assign_to_function_call:
'assignment to a function call'
- @lookat('assign')
+ @lookat(tok.ASSIGN)
def _lint(self, node):
- if node.kids[0].kind == 'lp':
+ if node.kids[0].kind == tok.LP:
return node
class ambiguous_else_stmt:
'the else statement could be matched with one of multiple if statements (use curly braces to indicate intent'
- @lookat('if')
+ @lookat(tok.IF)
def _lint(self, node):
# Only examine this node if it has an else statement.
condition, if_, else_ = node.kids
@@ -212,79 +214,79 @@
tmp = node
while tmp:
# Curly braces always clarify if statements.
- if tmp.kind == 'lc':
+ if tmp.kind == tok.LC:
return
# Else is only ambiguous in the first branch of an if statement.
- if tmp.parent.kind == 'if' and tmp.node_index == 1:
+ if tmp.parent.kind == tok.IF and tmp.node_index == 1:
return else_
tmp = tmp.parent
class block_without_braces:
'block statement without curly braces'
- @lookat('if', 'while', 'do', 'for', 'with')
+ @lookat(tok.IF, tok.WHILE, tok.DO, tok.FOR, tok.WITH)
def _lint(self, node):
- if node.kids[1].kind != 'lc':
+ if node.kids[1].kind != tok.LC:
return node.kids[1]
class ambiguous_nested_stmt:
'block statements containing block statements should use curly braces to resolve ambiguity'
- _block_nodes = ('if', 'while', 'do', 'for', 'with')
+ _block_nodes = (tok.IF, tok.WHILE, tok.DO, tok.FOR, tok.WITH)
@lookat(*_block_nodes)
def _lint(self, node):
# Ignore "else if"
- if node.kind == 'if' and node.node_index == 2 and node.parent.kind == 'if':
+ if node.kind == tok.IF and node.node_index == 2 and node.parent.kind == tok.IF:
return
# If the parent is a block, it means a block statement
# was inside a block statement without clarifying curlies.
- # (Otherwise, the node type would be 'lc'.)
+ # (Otherwise, the node type would be tok.LC.)
if node.parent.kind in self._block_nodes:
return node
class inc_dec_within_stmt:
'increment (++) and decrement (--) operators used as part of greater statement'
- @lookat('inc', 'dec')
+ @lookat(tok.INC, tok.DEC)
def _lint(self, node):
- if node.parent.kind == 'semi':
+ if node.parent.kind == tok.SEMI:
return None
# Allow within the third part of the "for"
tmp = node
- while tmp and tmp.parent and tmp.parent.kind == 'comma':
+ while tmp and tmp.parent and tmp.parent.kind == tok.COMMA:
tmp = tmp.parent
if tmp and tmp.node_index == 2 and \
- tmp.parent.kind == 'reserved' and \
- tmp.parent.parent.kind == 'for':
+ tmp.parent.kind == tok.RESERVED and \
+ tmp.parent.parent.kind == tok.FOR:
return None
return node
def _is_for_ternary_stmt(self, node, branch=None):
- if node.parent and node.parent.kind == 'comma':
+ if node.parent and node.parent.kind == tok.COMMA:
return _is_for_ternary_stmt(node.parent, branch)
return node.node_index == branch and \
node.parent and \
- node.parent.kind == 'reserved' and \
- node.parent.parent.kind == 'for'
+ node.parent.kind == tok.RESERVED and \
+ node.parent.parent.kind == tok.FOR
class comma_separated_stmts:
'multiple statements separated by commas (use semicolons?)'
- @lookat('comma')
+ @lookat(tok.COMMA)
def _lint(self, node):
# Allow within the first and third part of "for(;;)"
if _get_branch_in_for(node) in (0, 2):
return
# This is an array
- if node.parent.kind == 'rb':
+ if node.parent.kind == tok.RB:
return
return node
class empty_statement:
'empty statement or extra semicolon'
- @lookat('semi')
+ @lookat(tok.SEMI)
def _semi(self, node):
if not node.kids[0]:
return node
- @lookat('lc')
+ @lookat(tok.LC)
def _lc(self, node):
if node.kids:
return
@@ -292,19 +294,19 @@
if not node.parent:
return
# Some empty blocks are meaningful.
- if node.parent.kind in ('catch', 'case', 'default', 'switch', 'function'):
+ if node.parent.kind in (tok.CATCH, tok.CASE, tok.DEFAULT, tok.SWITCH, tok.FUNCTION):
return
return node
class missing_break:
'missing break statement'
- @lookat('case', 'default')
+ @lookat(tok.CASE, tok.DEFAULT)
def _lint(self, node):
# The last item is handled separately
if node.node_index == len(node.parent.kids)-1:
return
case_contents = node.kids[1]
- assert case_contents.kind == 'lc'
+ assert case_contents.kind == tok.LC
# Ignore empty case statements
if not case_contents.kids:
return
@@ -313,88 +315,88 @@
class missing_break_for_last_case:
'missing break statement for last case in switch'
- @lookat('case', 'default')
+ @lookat(tok.CASE, tok.DEFAULT)
def _lint(self, node):
if node.node_index < len(node.parent.kids)-1:
return
case_contents = node.kids[1]
- assert case_contents.kind == 'lc'
+ assert case_contents.kind == tok.LC
if None in _get_exit_points(case_contents):
return node
class multiple_plus_minus:
'unknown order of operations for successive plus (e.g. x+++y) or minus (e.g. x---y) signs'
- @lookat('inc')
+ @lookat(tok.INC)
def _inc(self, node):
- if node.node_index == 0 and node.parent.kind == 'plus':
+ if node.node_index == 0 and node.parent.kind == tok.PLUS:
return node
- @lookat('dec')
+ @lookat(tok.DEC)
def _dec(self, node):
- if node.node_index == 0 and node.parent.kind == 'minus':
+ if node.node_index == 0 and node.parent.kind == tok.MINUS:
return node
class useless_assign:
'useless assignment'
- @lookat('name:setname')
+ @lookat((tok.NAME, op.SETNAME))
def _lint(self, node):
- if node.parent.kind == 'assign':
+ if node.parent.kind == tok.ASSIGN:
assert node.node_index == 0
value = node.parent.kids[1]
- elif node.parent.kind == 'var':
+ elif node.parent.kind == tok.VAR:
value = node.kids[0]
- if value and value.kind == 'name' and node.atom == value.atom:
+ if value and value.kind == tok.NAME and node.atom == value.atom:
return node
class unreachable_code:
'unreachable code'
- @lookat('break', 'continue', 'return', 'throw')
+ @lookat(tok.BREAK, tok.CONTINUE, tok.RETURN, tok.THROW)
def _lint(self, node):
- if node.parent.kind == 'lc' and \
+ if node.parent.kind == tok.LC and \
node.node_index != len(node.parent.kids)-1:
return node.parent.kids[node.node_index+1]
class meaningless_block:
'meaningless block; curly braces have no impact'
- #TODO: @lookat('if')
+ #TODO: @lookat(tok.IF)
def _lint(self, node):
condition, if_, else_ = node.kids
- if condition.kind == 'primary' and condition.opcode in ('true', 'false', 'null'):
+ if condition.kind == tok.PRIMARY and condition.opcode in (op.TRUE, op.FALSE, op.NULL):
return condition
- #TODO: @lookat('while')
+ #TODO: @lookat(tok.WHILE)
def _lint(self, node):
condition = node.kids[0]
- if condition.kind == 'primary' and condition.opcode in ('false', 'null'):
+ if condition.kind == tok.PRIMARY and condition.opcode in (op.FALSE, op.NULL):
return condition
- @lookat('lc')
+ @lookat(tok.LC)
def _lint(self, node):
- if node.parent and node.parent.kind == 'lc':
+ if node.parent and node.parent.kind == tok.LC:
return node
class useless_void:
'use of the void type may be unnecessary (void is always undefined)'
- @lookat('unaryop:void')
+ @lookat((tok.UNARYOP, op.VOID))
def _lint(self, node):
return node
class parseint_missing_radix:
'parseInt missing radix parameter'
- @lookat('lp:call')
+ @lookat((tok.LP, op.CALL))
def _lint(self, node):
- if node.kids[0].kind == 'name' and node.kids[0].atom == 'parseInt' and len(node.kids) <= 2:
+ if node.kids[0].kind == tok.NAME and node.kids[0].atom == 'parseInt' and len(node.kids) <= 2:
return node
class leading_decimal_point:
'leading decimal point may indicate a number or an object member'
- @lookat('number')
+ @lookat(tok.NUMBER)
def _lint(self, node):
if node.atom.startswith('.'):
return node
class trailing_decimal_point:
'trailing decimal point may indicate a number or an object member'
- @lookat('number')
+ @lookat(tok.NUMBER)
def _lint(self, node):
- if node.parent.kind == 'dot':
+ if node.parent.kind == tok.DOT:
return node
if node.atom.endswith('.'):
return node
@@ -402,14 +404,14 @@
class octal_number:
'leading zeros make an octal number'
_regexp = re.compile('^0[0-9]')
- @lookat('number')
+ @lookat(tok.NUMBER)
def _line(self, node):
if self._regexp.match(node.atom):
return node
class trailing_comma_in_array:
'extra comma is not recommended in array initializers'
- @lookat('rb')
+ @lookat(tok.RB)
def _line(self, node):
if node.end_comma:
return node
Modified: trunk/pyspidermonkey/pyspidermonkey.c
===================================================================
--- trunk/pyspidermonkey/pyspidermonkey.c 2008-03-01 17:32:55 UTC (rev 148)
+++ trunk/pyspidermonkey/pyspidermonkey.c 2008-03-01 18:44:53 UTC (rev 149)
@@ -41,7 +41,11 @@
};
JS_STATIC_ASSERT(ARRAY_COUNT(error_names) == JSErr_Limit);
+/* Use different numeric ranges to avoid accidental confusion. */
+#define TOK_TO_NUM(tok) (tok+1000)
+#define OPCODE_TO_NUM(op) (op+2000)
+
/** MODULE INITIALIZATION
*/
@@ -57,7 +61,45 @@
PyMODINIT_FUNC
initpyspidermonkey() {
- (void)Py_InitModule("pyspidermonkey", module_methods);
+ PyObject* module;
+ PyObject* class;
+ PyObject* tok;
+ PyObject* op;
+ int i;
+
+ module = Py_InitModule("pyspidermonkey", module_methods);
+ if (!module)
+ return;
+
+ class = PyClass_New(NULL, PyDict_New(), PyString_FromString("spidermonkey_constants"));
+ if (!class)
+ return;
+
+ /* set up tokens */
+ tok = PyInstance_New(class, NULL, NULL);
+ if (!tok)
+ return;
+ if (PyObject_SetAttrString(module, "tok", tok) == -1)
+ return;
+ for (i = 0; i < ARRAY_COUNT(tokens); i++) {
+ if (PyObject_SetAttrString(tok, tokens[i], PyLong_FromLong(TOK_TO_NUM(i))) == -1)
+ return;
+ }
+
+ /* set up opcodes */
+ op = PyInstance_New(class, NULL, NULL);
+ if (!op)
+ return;
+ if (PyObject_SetAttrString(module, "op", op) == -1)
+ return;
+ for (i = 0; i < ARRAY_COUNT(opcodes); i++) {
+ /* yank off the JSOP prefix */
+ const char* opcode = opcodes[i];
+ if (strlen(opcode) > 5)
+ opcode += 5;
+ if (PyObject_SetAttrString(op, opcode, PyLong_FromLong(OPCODE_TO_NUM(i))) == -1)
+ return;
+ }
}
PyMODINIT_FUNC
@@ -131,7 +173,7 @@
PyTuple_SET_ITEM(tuple, node_offset, kw);
- if (PyDict_SetItemString(kw, "type", PyString_FromString(tokens[jsnode->pn_type])) == -1)
+ if (PyDict_SetItemString(kw, "type", Py_BuildValue("i", TOK_TO_NUM(jsnode->pn_type))) == -1)
goto fail;
if (PyDict_SetItemString(kw, "node_index", Py_BuildValue("i", node_offset)) == -1)
goto fail;
@@ -153,7 +195,7 @@
goto fail;
}
- if (PyDict_SetItemString(kw, "opcode", PyString_FromString(opcodes[jsnode->pn_op])) == -1)
+ if (PyDict_SetItemString(kw, "opcode", Py_BuildValue("i", OPCODE_TO_NUM(jsnode->pn_op))) == -1)
goto fail;
if (jsnode->pn_type == TOK_NUMBER) {
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <mat...@us...> - 2008-03-03 18:26:20
|
Revision: 154
http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=154&view=rev
Author: matthiasmiller
Date: 2008-03-03 10:26:12 -0800 (Mon, 03 Mar 2008)
Log Message:
-----------
optimize _Node construction
Modified Paths:
--------------
trunk/pyjsl/parse.py
trunk/pyspidermonkey/pyspidermonkey.c
Modified: trunk/pyjsl/parse.py
===================================================================
--- trunk/pyjsl/parse.py 2008-03-02 03:30:04 UTC (rev 153)
+++ trunk/pyjsl/parse.py 2008-03-03 18:26:12 UTC (rev 154)
@@ -1,341 +1,325 @@
-#!/usr/bin/python
-""" Parses a script into nodes. """
-import bisect
-import re
-import unittest
-
-import pyspidermonkey
-from pyspidermonkey import tok, op
-
-_tok_names = dict(zip(
- [getattr(tok, prop) for prop in dir(tok)],
- ['tok.%s' % prop for prop in dir(tok)]
-))
-
-class NodePos():
- def __init__(self, line, col):
- self.line = line
- self.col = col
- def __cmp__(self, other):
- if self.line < other.line:
- return -1
- if self.line > other.line:
- return 1
- if self.col < other.col:
- return -1
- if self.col > other.col:
- return 1
- return 0
- def __repr__(self):
- return '(line %i, col %i)' % (self.line+1, self.col+1)
-
-class NodePositions():
- " Given a string, allows [x] lookups for NodePos line and column numbers."
- def __init__(self, text):
- # Find the length of each line and incrementally sum all of the lengths
- # to determine the ending position of each line.
- self._lines = text.splitlines(True)
- lines = [0] + [len(x) for x in self._lines]
- for x in range(1, len(lines)):
- lines[x] += lines[x-1]
- self._line_offsets = lines
- def from_offset(self, offset):
- line = bisect.bisect(self._line_offsets, offset)-1
- col = offset - self._line_offsets[line]
- return NodePos(line, col)
- def to_offset(self, pos):
- offset = self._line_offsets[pos.line] + pos.col
- assert offset <= self._line_offsets[pos.line+1] # out-of-bounds col num
- return offset
- def text(self, start, end):
- assert start <= end
- # Trim the ending first in case it's a single line.
- lines = self._lines[start.line:end.line+1]
- lines[-1] = lines[-1][:end.col+1]
- lines[0] = lines[0][start.col:]
- return ''.join(lines)
-
-class NodeRanges():
- def __init__(self):
- self._offsets = []
- def add(self, start, end):
- i = bisect.bisect_left(self._offsets, start)
- if i % 2 == 1:
- i -= 1
- start = self._offsets[i]
-
- end = end + 1
- j = bisect.bisect_left(self._offsets, end)
- if j % 2 == 1:
- end = self._offsets[j]
- j += 1
-
- self._offsets[i:j] = [start,end]
- def has(self, pos):
- return bisect.bisect_right(self._offsets, pos) % 2 == 1
-
-class _Node():
- def __init__(self, kwargs):
- def _to_node(kid):
- if kid:
- return _Node(kid)
- self.kind = kwargs['type']
- kwargs['opcode'] = kwargs['opcode']
- self.opcode = kwargs['opcode']
- self.kids = tuple([_to_node(kid) for kid in kwargs['kids']])
- for kid in self.kids:
- if kid:
- kid.parent = self
- if 'atom' in kwargs:
- self.atom = kwargs['atom']
- if 'dval' in kwargs:
- self.dval = kwargs['dval']
- if 'fn_name' in kwargs:
- self.fn_name = kwargs['fn_name']
- if 'fn_args' in kwargs:
- self.fn_args = kwargs['fn_args']
- if 'end_comma' in kwargs:
- self.end_comma = kwargs['end_comma']
- self.args = kwargs
- self.node_index = kwargs['node_index']
- self.parent = None
- self.start_line = kwargs['start_row']
- self._start_pos = None
- self._end_pos = None
-
- def add_child(self, node):
- if node:
- node.node_index = len(self.kids)
- node.parent = self
- self.kids.append(node)
-
- def start_pos(self):
- self._start_pos = self._start_pos or \
- NodePos(self.args['start_row'], self.args['start_col'])
- return self._start_pos
-
- def end_pos(self):
- self._end_pos = self._end_pos or \
- NodePos(self.args['end_row'], self.args['end_col'])
- return self._end_pos
-
- def __repr__(self):
- kind = self.kind
- if not kind:
- kind = '(none)'
- return '%s>%s' % (_tok_names[kind], str(self.kids))
-
- def is_equivalent(self, other, are_functions_equiv=False):
- if not other:
- return False
-
- # Bail out for functions
- if not are_functions_equiv:
- if self.kind == tok.FUNCTION:
- return False
- if self.kind == tok.LP and self.opcode == op.CALL:
- return False
-
- if self.kind != other.kind:
- return False
- if self.opcode != other.opcode:
- return False
-
- # Check atoms on names, properties, and string constants
- if self.kind in (tok.NAME, tok.DOT, tok.STRING) and self.atom != other.atom:
- return False
-
- # Check values on numbers
- if self.kind == tok.NUMBER and self.dval != other.dval:
- return False
-
- # Compare child nodes
- if len(self.kids) != len(other.kids):
- return False
- for i in range(0, len(self.kids)):
- # Watch for dead nodes
- if not self.kids[i]:
- if not other.kids[i]: return True
- else: return False
- if not self.kids[i].is_equivalent(other.kids[i]):
- return False
-
- return True
-
-def _parse_comments(script, root, node_positions, ignore_ranges):
- pos = 0
- single_line_re = r"//[^\r\n]*"
- multi_line_re = r"/\*(.*?)\*/"
- full_re = "(%s)|(%s)" % (single_line_re, multi_line_re)
- comment_re = re.compile(full_re, re.DOTALL)
-
- comments = []
- while True:
- match = comment_re.search(script, pos)
- if not match:
- return comments
-
- # Get the comment text
- comment_text = script[match.start():match.end()]
- if comment_text.startswith('/*'):
- comment_text = comment_text[2:-2]
- opcode = 'JSOP_C_COMMENT'
- else:
- comment_text = comment_text[2:]
- opcode = 'JSOP_CPP_COMMENT'
- opcode = opcode[5:].lower()
-
- start_offset = match.start()+1
- end_offset = match.end()
-
- # Make sure it doesn't start in a string or regexp
- if not ignore_ranges.has(start_offset):
- start_pos = node_positions.from_offset(start_offset)
- end_pos = node_positions.from_offset(end_offset)
- kwargs = {
- 'type': 'COMMENT',
- 'atom': comment_text,
- 'opcode': opcode,
- 'start_row': start_pos.line,
- 'start_col': start_pos.col,
- 'end_row': end_pos.line,
- 'end_col': end_pos.col,
- 'kids': [],
- 'node_index': None
- }
- comment_node = _Node(kwargs)
- comments.append(comment_node)
- pos = match.end()
- else:
- pos = match.start()+1
-
-def parse(script, error_callback):
- def _wrapped_callback(line, col, msg):
- assert msg.startswith('JSMSG_')
- msg = msg[6:].lower()
- error_callback(line, col, msg)
-
- positions = NodePositions(script)
-
- roots = []
- nodes = []
- comment_ignore_ranges = NodeRanges()
- def process(node):
- if node.kind == tok.NUMBER:
- node.atom = positions.text(node.start_pos(), node.end_pos())
- elif node.kind == tok.STRING or \
- (node.kind == tok.OBJECT and node.opcode == op.REGEXP):
- start_offset = positions.to_offset(node.start_pos())
- end_offset = positions.to_offset(node.end_pos())
- comment_ignore_ranges.add(start_offset, end_offset)
- for kid in node.kids:
- if kid:
- process(kid)
- def pop():
- nodes.pop()
-
- roots = pyspidermonkey.traverse(script, _wrapped_callback)
- assert len(roots) == 1
- root_node = _Node(roots[0])
- process(root_node)
-
- comments = _parse_comments(script, root_node, positions, comment_ignore_ranges)
- return root_node, comments
-
-def _dump_node(node, depth=0):
- print '. '*depth,
- if node is None:
- print '(none)'
- else:
- print _tok_names[node.kind], '\t', node.args
- for node in node.kids:
- _dump_node(node, depth+1)
-
-def dump_tree(script):
- def error_callback(line, col, msg):
- print '(%i, %i): %s', (line, col, msg)
- node, comments = parse(script, error_callback)
- _dump_node(node)
-
-class TestComments(unittest.TestCase):
- def _test(self, script, expected_comments):
- root, comments = parse(script, lambda line, col, msg: None)
- encountered_comments = [node.atom for node in comments]
- self.assertEquals(encountered_comments, list(expected_comments))
- def testSimpleComments(self):
- self._test('re = /\//g', ())
- self._test('re = /\///g', ())
- self._test('re = /\////g', ('g',))
- def testCComments(self):
- self._test('/*a*//*b*/', ('a', 'b'))
- self._test('/*a\r\na*//*b\r\nb*/', ('a\r\na', 'b\r\nb'))
- self._test('a//*b*/c', ('*b*/c',))
- self._test('a///*b*/c', ('/*b*/c',))
- self._test('a/*//*/;', ('//',))
- self._test('a/*b*/+/*c*/d', ('b', 'c'))
-
-class TestNodePositions(unittest.TestCase):
- def _test(self, text, expected_lines, expected_cols):
- # Get a NodePos list
- positions = NodePositions(text)
- positions = [positions.from_offset(i) for i in range(0, len(text))]
- encountered_lines = ''.join([str(x.line) for x in positions])
- encountered_cols = ''.join([str(x.col) for x in positions])
- self.assertEquals(encountered_lines, expected_lines.replace(' ', ''))
- self.assertEquals(encountered_cols, expected_cols.replace(' ', ''))
- def testSimple(self):
- self._test(
- 'abc\r\ndef\nghi\n\nj',
- '0000 0 1111 2222 3 4',
- '0123 4 0123 0123 0 0'
- )
- self._test(
- '\rabc',
- '0 111',
- '0 012'
- )
- def testText(self):
- pos = NodePositions('abc\r\ndef\n\nghi')
- self.assertEquals(pos.text(NodePos(0, 0), NodePos(0, 0)), 'a')
- self.assertEquals(pos.text(NodePos(0, 0), NodePos(0, 2)), 'abc')
- self.assertEquals(pos.text(NodePos(0, 2), NodePos(1, 2)), 'c\r\ndef')
- def testOffset(self):
- pos = NodePositions('abc\r\ndef\n\nghi')
- self.assertEquals(pos.to_offset(NodePos(0, 2)), 2)
- self.assertEquals(pos.to_offset(NodePos(1, 0)), 5)
- self.assertEquals(pos.to_offset(NodePos(3, 1)), 11)
-
-class TestNodeRanges(unittest.TestCase):
- def testAdd(self):
- r = NodeRanges()
- r.add(5, 10)
- self.assertEquals(r._offsets, [5,11])
- r.add(15, 20)
- self.assertEquals(r._offsets, [5,11,15,21])
- r.add(21,22)
- self.assertEquals(r._offsets, [5,11,15,23])
- r.add(4,5)
- self.assertEquals(r._offsets, [4,11,15,23])
- r.add(9,11)
- self.assertEquals(r._offsets, [4,12,15,23])
- r.add(10,20)
- self.assertEquals(r._offsets, [4,23])
- r.add(4,22)
- self.assertEquals(r._offsets, [4,23])
- r.add(30,30)
- self.assertEquals(r._offsets, [4,23,30,31])
- def testHas(self):
- r = NodeRanges()
- r.add(5, 10)
- r.add(15, 15)
- assert not r.has(4)
- assert r.has(5)
- assert r.has(6)
- assert r.has(9)
- assert r.has(10)
- assert not r.has(14)
- assert r.has(15)
- assert not r.has(16)
-if __name__ == '__main__':
- unittest.main()
-
+#!/usr/bin/python
+""" Parses a script into nodes. """
+import bisect
+import re
+import unittest
+
+import pyspidermonkey
+from pyspidermonkey import tok, op
+
+_tok_names = dict(zip(
+ [getattr(tok, prop) for prop in dir(tok)],
+ ['tok.%s' % prop for prop in dir(tok)]
+))
+
+class NodePos():
+ def __init__(self, line, col):
+ self.line = line
+ self.col = col
+ def __cmp__(self, other):
+ if self.line < other.line:
+ return -1
+ if self.line > other.line:
+ return 1
+ if self.col < other.col:
+ return -1
+ if self.col > other.col:
+ return 1
+ return 0
+ def __repr__(self):
+ return '(line %i, col %i)' % (self.line+1, self.col+1)
+
+class NodePositions():
+ " Given a string, allows [x] lookups for NodePos line and column numbers."
+ def __init__(self, text):
+ # Find the length of each line and incrementally sum all of the lengths
+ # to determine the ending position of each line.
+ self._lines = text.splitlines(True)
+ lines = [0] + [len(x) for x in self._lines]
+ for x in range(1, len(lines)):
+ lines[x] += lines[x-1]
+ self._line_offsets = lines
+ def from_offset(self, offset):
+ line = bisect.bisect(self._line_offsets, offset)-1
+ col = offset - self._line_offsets[line]
+ return NodePos(line, col)
+ def to_offset(self, pos):
+ offset = self._line_offsets[pos.line] + pos.col
+ assert offset <= self._line_offsets[pos.line+1] # out-of-bounds col num
+ return offset
+ def text(self, start, end):
+ assert start <= end
+ # Trim the ending first in case it's a single line.
+ lines = self._lines[start.line:end.line+1]
+ lines[-1] = lines[-1][:end.col+1]
+ lines[0] = lines[0][start.col:]
+ return ''.join(lines)
+
+class NodeRanges():
+ def __init__(self):
+ self._offsets = []
+ def add(self, start, end):
+ i = bisect.bisect_left(self._offsets, start)
+ if i % 2 == 1:
+ i -= 1
+ start = self._offsets[i]
+
+ end = end + 1
+ j = bisect.bisect_left(self._offsets, end)
+ if j % 2 == 1:
+ end = self._offsets[j]
+ j += 1
+
+ self._offsets[i:j] = [start,end]
+ def has(self, pos):
+ return bisect.bisect_right(self._offsets, pos) % 2 == 1
+
+class _Node():
+ def __init__(self, kids, parent=None, **kwargs):
+ _to_node = lambda kid: kid and _Node(parent=self, **kid)
+ self.__dict__.update(kwargs)
+ self.args = kwargs
+ self.parent = parent
+ self.kids = map(_to_node, kids)
+
+ def add_child(self, node):
+ if node:
+ node.node_index = len(self.kids)
+ node.parent = self
+ self.kids.append(node)
+
+ def start_pos(self):
+ try:
+ return self._start_pos
+ except AttributeError:
+ self._start_pos = NodePos(self._start_line, self._start_col)
+ return self._start_pos
+
+ def end_pos(self):
+ try:
+ return self._end_pos
+ except AttributeError:
+ self._end_pos = NodePos(self._end_line, self._end_col)
+ return self._end_pos
+
+ def __repr__(self):
+ kind = self.kind
+ if not kind:
+ kind = '(none)'
+ return '%s>%s' % (_tok_names[kind], str(self.kids))
+
+ def is_equivalent(self, other, are_functions_equiv=False):
+ if not other:
+ return False
+
+ # Bail out for functions
+ if not are_functions_equiv:
+ if self.kind == tok.FUNCTION:
+ return False
+ if self.kind == tok.LP and self.opcode == op.CALL:
+ return False
+
+ if self.kind != other.kind:
+ return False
+ if self.opcode != other.opcode:
+ return False
+
+ # Check atoms on names, properties, and string constants
+ if self.kind in (tok.NAME, tok.DOT, tok.STRING) and self.atom != other.atom:
+ return False
+
+ # Check values on numbers
+ if self.kind == tok.NUMBER and self.dval != other.dval:
+ return False
+
+ # Compare child nodes
+ if len(self.kids) != len(other.kids):
+ return False
+ for i in range(0, len(self.kids)):
+ # Watch for dead nodes
+ if not self.kids[i]:
+ if not other.kids[i]: return True
+ else: return False
+ if not self.kids[i].is_equivalent(other.kids[i]):
+ return False
+
+ return True
+
+def _parse_comments(script, root, node_positions, ignore_ranges):
+ pos = 0
+ single_line_re = r"//[^\r\n]*"
+ multi_line_re = r"/\*(.*?)\*/"
+ full_re = "(%s)|(%s)" % (single_line_re, multi_line_re)
+ comment_re = re.compile(full_re, re.DOTALL)
+
+ comments = []
+ while True:
+ match = comment_re.search(script, pos)
+ if not match:
+ return comments
+
+ # Get the comment text
+ comment_text = script[match.start():match.end()]
+ if comment_text.startswith('/*'):
+ comment_text = comment_text[2:-2]
+ opcode = 'JSOP_C_COMMENT'
+ else:
+ comment_text = comment_text[2:]
+ opcode = 'JSOP_CPP_COMMENT'
+ opcode = opcode[5:].lower()
+
+ start_offset = match.start()+1
+ end_offset = match.end()
+
+ # Make sure it doesn't start in a string or regexp
+ if not ignore_ranges.has(start_offset):
+ start_pos = node_positions.from_offset(start_offset)
+ end_pos = node_positions.from_offset(end_offset)
+ kwargs = {
+ 'type': 'COMMENT',
+ 'atom': comment_text,
+ 'opcode': opcode,
+ '_start_line': start_pos.line,
+ '_start_col': start_pos.col,
+ '_end_line': end_pos.line,
+ '_end_col': end_pos.col,
+ 'parent': None,
+ 'kids': [],
+ 'node_index': None
+ }
+ comment_node = _Node(**kwargs)
+ comments.append(comment_node)
+ pos = match.end()
+ else:
+ pos = match.start()+1
+
+def parse(script, error_callback):
+ def _wrapped_callback(line, col, msg):
+ assert msg.startswith('JSMSG_')
+ msg = msg[6:].lower()
+ error_callback(line, col, msg)
+
+ positions = NodePositions(script)
+
+ roots = []
+ nodes = []
+ comment_ignore_ranges = NodeRanges()
+ def process(node):
+ if node.kind == tok.NUMBER:
+ node.atom = positions.text(node.start_pos(), node.end_pos())
+ elif node.kind == tok.STRING or \
+ (node.kind == tok.OBJECT and node.opcode == op.REGEXP):
+ start_offset = positions.to_offset(node.start_pos())
+ end_offset = positions.to_offset(node.end_pos())
+ comment_ignore_ranges.add(start_offset, end_offset)
+ for kid in node.kids:
+ if kid:
+ process(kid)
+ def pop():
+ nodes.pop()
+
+ roots = pyspidermonkey.traverse(script, _wrapped_callback)
+ assert len(roots) == 1
+ root_node = _Node(**roots[0])
+ process(root_node)
+
+ comments = _parse_comments(script, root_node, positions, comment_ignore_ranges)
+ return root_node, comments
+
+def _dump_node(node, depth=0):
+ print '. '*depth,
+ if node is None:
+ print '(none)'
+ else:
+ print _tok_names[node.kind], '\t', node.args
+ for node in node.kids:
+ _dump_node(node, depth+1)
+
+def dump_tree(script):
+ def error_callback(line, col, msg):
+ print '(%i, %i): %s', (line, col, msg)
+ node, comments = parse(script, error_callback)
+ _dump_node(node)
+
+class TestComments(unittest.TestCase):
+ def _test(self, script, expected_comments):
+ root, comments = parse(script, lambda line, col, msg: None)
+ encountered_comments = [node.atom for node in comments]
+ self.assertEquals(encountered_comments, list(expected_comments))
+ def testSimpleComments(self):
+ self._test('re = /\//g', ())
+ self._test('re = /\///g', ())
+ self._test('re = /\////g', ('g',))
+ def testCComments(self):
+ self._test('/*a*//*b*/', ('a', 'b'))
+ self._test('/*a\r\na*//*b\r\nb*/', ('a\r\na', 'b\r\nb'))
+ self._test('a//*b*/c', ('*b*/c',))
+ self._test('a///*b*/c', ('/*b*/c',))
+ self._test('a/*//*/;', ('//',))
+ self._test('a/*b*/+/*c*/d', ('b', 'c'))
+
+class TestNodePositions(unittest.TestCase):
+ def _test(self, text, expected_lines, expected_cols):
+ # Get a NodePos list
+ positions = NodePositions(text)
+ positions = [positions.from_offset(i) for i in range(0, len(text))]
+ encountered_lines = ''.join([str(x.line) for x in positions])
+ encountered_cols = ''.join([str(x.col) for x in positions])
+ self.assertEquals(encountered_lines, expected_lines.replace(' ', ''))
+ self.assertEquals(encountered_cols, expected_cols.replace(' ', ''))
+ def testSimple(self):
+ self._test(
+ 'abc\r\ndef\nghi\n\nj',
+ '0000 0 1111 2222 3 4',
+ '0123 4 0123 0123 0 0'
+ )
+ self._test(
+ '\rabc',
+ '0 111',
+ '0 012'
+ )
+ def testText(self):
+ pos = NodePositions('abc\r\ndef\n\nghi')
+ self.assertEquals(pos.text(NodePos(0, 0), NodePos(0, 0)), 'a')
+ self.assertEquals(pos.text(NodePos(0, 0), NodePos(0, 2)), 'abc')
+ self.assertEquals(pos.text(NodePos(0, 2), NodePos(1, 2)), 'c\r\ndef')
+ def testOffset(self):
+ pos = NodePositions('abc\r\ndef\n\nghi')
+ self.assertEquals(pos.to_offset(NodePos(0, 2)), 2)
+ self.assertEquals(pos.to_offset(NodePos(1, 0)), 5)
+ self.assertEquals(pos.to_offset(NodePos(3, 1)), 11)
+
+class TestNodeRanges(unittest.TestCase):
+ def testAdd(self):
+ r = NodeRanges()
+ r.add(5, 10)
+ self.assertEquals(r._offsets, [5,11])
+ r.add(15, 20)
+ self.assertEquals(r._offsets, [5,11,15,21])
+ r.add(21,22)
+ self.assertEquals(r._offsets, [5,11,15,23])
+ r.add(4,5)
+ self.assertEquals(r._offsets, [4,11,15,23])
+ r.add(9,11)
+ self.assertEquals(r._offsets, [4,12,15,23])
+ r.add(10,20)
+ self.assertEquals(r._offsets, [4,23])
+ r.add(4,22)
+ self.assertEquals(r._offsets, [4,23])
+ r.add(30,30)
+ self.assertEquals(r._offsets, [4,23,30,31])
+ def testHas(self):
+ r = NodeRanges()
+ r.add(5, 10)
+ r.add(15, 15)
+ assert not r.has(4)
+ assert r.has(5)
+ assert r.has(6)
+ assert r.has(9)
+ assert r.has(10)
+ assert not r.has(14)
+ assert r.has(15)
+ assert not r.has(16)
+if __name__ == '__main__':
+ unittest.main()
+
Modified: trunk/pyspidermonkey/pyspidermonkey.c
===================================================================
--- trunk/pyspidermonkey/pyspidermonkey.c 2008-03-02 03:30:04 UTC (rev 153)
+++ trunk/pyspidermonkey/pyspidermonkey.c 2008-03-03 18:26:12 UTC (rev 154)
@@ -173,19 +173,19 @@
PyTuple_SET_ITEM(tuple, node_offset, kw);
- if (PyDict_SetItemString(kw, "type", Py_BuildValue("i", TOK_TO_NUM(jsnode->pn_type))) == -1)
+ if (PyDict_SetItemString(kw, "kind", Py_BuildValue("i", TOK_TO_NUM(jsnode->pn_type))) == -1)
goto fail;
if (PyDict_SetItemString(kw, "node_index", Py_BuildValue("i", node_offset)) == -1)
goto fail;
/* pass the position */
- if (PyDict_SetItemString(kw, "start_row", Py_BuildValue("i", jsnode->pn_pos.begin.lineno-1)) == -1)
+ if (PyDict_SetItemString(kw, "_start_line", Py_BuildValue("i", jsnode->pn_pos.begin.lineno-1)) == -1)
goto fail;
- if (PyDict_SetItemString(kw, "start_col", Py_BuildValue("i", jsnode->pn_pos.begin.index)) == -1)
+ if (PyDict_SetItemString(kw, "_start_col", Py_BuildValue("i", jsnode->pn_pos.begin.index)) == -1)
goto fail;
- if (PyDict_SetItemString(kw, "end_row", Py_BuildValue("i", jsnode->pn_pos.end.lineno-1)) == -1)
+ if (PyDict_SetItemString(kw, "_end_line", Py_BuildValue("i", jsnode->pn_pos.end.lineno-1)) == -1)
goto fail;
- if (PyDict_SetItemString(kw, "end_col", Py_BuildValue("i", jsnode->pn_pos.end.index)) == -1)
+ if (PyDict_SetItemString(kw, "_end_col", Py_BuildValue("i", jsnode->pn_pos.end.index)) == -1)
goto fail;
if ((jsnode->pn_type == TOK_NAME || jsnode->pn_type == TOK_DOT ||
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <mat...@us...> - 2008-03-03 18:31:32
|
Revision: 155
http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=155&view=rev
Author: matthiasmiller
Date: 2008-03-03 10:31:17 -0800 (Mon, 03 Mar 2008)
Log Message:
-----------
set eol styles
Property Changed:
----------------
trunk/COPYING
trunk/DEVELOPMENT
trunk/INSTALL
trunk/jsl.py
trunk/pyjsl/__init__.py
trunk/pyjsl/conf.py
trunk/pyjsl/lint.py
trunk/pyjsl/parse.py
trunk/pyjsl/util.py
trunk/pyjsl/visitation.py
trunk/pyjsl/warnings.py
trunk/pyspidermonkey/pyspidermonkey.c
trunk/pyspidermonkey/tokens.tbl
trunk/setup.py
trunk/test.py
Property changes on: trunk/COPYING
___________________________________________________________________
Name: eol-style
+ native
Property changes on: trunk/DEVELOPMENT
___________________________________________________________________
Name: eol-style
+ native
Property changes on: trunk/INSTALL
___________________________________________________________________
Name: eol-style
+ native
Property changes on: trunk/jsl.py
___________________________________________________________________
Name: eol-style
+ native
Property changes on: trunk/pyjsl/__init__.py
___________________________________________________________________
Name: eol-style
+ native
Property changes on: trunk/pyjsl/conf.py
___________________________________________________________________
Name: eol-style
+ native
Property changes on: trunk/pyjsl/lint.py
___________________________________________________________________
Name: eol-style
+ native
Property changes on: trunk/pyjsl/parse.py
___________________________________________________________________
Name: eol-style
+ native
Property changes on: trunk/pyjsl/util.py
___________________________________________________________________
Name: eol-style
+ native
Property changes on: trunk/pyjsl/visitation.py
___________________________________________________________________
Name: eol-style
+ native
Property changes on: trunk/pyjsl/warnings.py
___________________________________________________________________
Name: eol-style
+ native
Property changes on: trunk/pyspidermonkey/pyspidermonkey.c
___________________________________________________________________
Name: eol-style
+ native
Property changes on: trunk/pyspidermonkey/tokens.tbl
___________________________________________________________________
Name: eol-style
+ native
Property changes on: trunk/setup.py
___________________________________________________________________
Name: eol-style
+ native
Property changes on: trunk/test.py
___________________________________________________________________
Name: eol-style
+ native
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <mat...@us...> - 2008-03-03 22:43:59
|
Revision: 157
http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=157&view=rev
Author: matthiasmiller
Date: 2008-03-03 14:43:57 -0800 (Mon, 03 Mar 2008)
Log Message:
-----------
change the c module to instantiate the Node objects and set the node's attributes to speed up parsing
Modified Paths:
--------------
trunk/pyjsl/parse.py
trunk/pyspidermonkey/pyspidermonkey.c
Modified: trunk/pyjsl/parse.py
===================================================================
--- trunk/pyjsl/parse.py 2008-03-03 18:41:11 UTC (rev 156)
+++ trunk/pyjsl/parse.py 2008-03-03 22:43:57 UTC (rev 157)
@@ -75,13 +75,6 @@
return bisect.bisect_right(self._offsets, pos) % 2 == 1
class _Node():
- def __init__(self, kids, parent=None, **kwargs):
- _to_node = lambda kid: kid and _Node(parent=self, **kid)
- self.__dict__.update(kwargs)
- self.args = kwargs
- self.parent = parent
- self.kids = map(_to_node, kids)
-
def add_child(self, node):
if node:
node.node_index = len(self.kids)
@@ -187,7 +180,8 @@
'kids': [],
'node_index': None
}
- comment_node = _Node(**kwargs)
+ comment_node = _Node()
+ comment_node.__dict__.update(kwargs)
comments.append(comment_node)
pos = match.end()
else:
@@ -218,9 +212,9 @@
def pop():
nodes.pop()
- roots = pyspidermonkey.traverse(script, _wrapped_callback)
+ roots = pyspidermonkey.traverse(script, _Node, _wrapped_callback)
assert len(roots) == 1
- root_node = _Node(**roots[0])
+ root_node = roots[0]
process(root_node)
comments = _parse_comments(script, root_node, positions, comment_ignore_ranges)
@@ -231,7 +225,7 @@
if node is None:
print '(none)'
else:
- print _tok_names[node.kind], '\t', node.args
+ print '%s\t%s, %s' % (_tok_names[node.kind], node.start_pos(), node.end_pos())
for node in node.kids:
_dump_node(node, depth+1)
Modified: trunk/pyspidermonkey/pyspidermonkey.c
===================================================================
--- trunk/pyspidermonkey/pyspidermonkey.c 2008-03-03 18:41:11 UTC (rev 156)
+++ trunk/pyspidermonkey/pyspidermonkey.c 2008-03-03 22:43:57 UTC (rev 157)
@@ -112,6 +112,7 @@
*/
typedef struct JSContextData {
+ PyObject* node_class;
PyObject* error_callback;
} JSContextData;
@@ -154,7 +155,8 @@
/* returns 0 on success and -1 on failure */
static int
-traverse_node(JSContext* context, JSParseNode* jsnode, PyObject* tuple, int node_offset) {
+traverse_node(JSContext* context, JSParseNode* jsnode, PyObject* parent, PyObject* tuple, int node_offset) {
+ JSContextData* data = JS_GetContextPrivate(context);
PyObject* kw = NULL;
PyObject* kids = NULL;
@@ -167,39 +169,42 @@
}
/* pass in a dictionary of options */
- kw = PyDict_New();
+ kw = PyInstance_New(data->node_class, NULL, NULL);
if (!kw)
goto fail;
PyTuple_SET_ITEM(tuple, node_offset, kw);
- if (PyDict_SetItemString(kw, "kind", Py_BuildValue("i", TOK_TO_NUM(jsnode->pn_type))) == -1)
+ Py_INCREF(parent);
+ if (PyObject_SetAttrString(kw, "parent", parent) == -1)
goto fail;
- if (PyDict_SetItemString(kw, "node_index", Py_BuildValue("i", node_offset)) == -1)
+ if (PyObject_SetAttrString(kw, "kind", Py_BuildValue("i", TOK_TO_NUM(jsnode->pn_type))) == -1)
goto fail;
+ if (PyObject_SetAttrString(kw, "node_index", Py_BuildValue("i", node_offset)) == -1)
+ goto fail;
/* pass the position */
- if (PyDict_SetItemString(kw, "_start_line", Py_BuildValue("i", jsnode->pn_pos.begin.lineno-1)) == -1)
+ if (PyObject_SetAttrString(kw, "_start_line", Py_BuildValue("i", jsnode->pn_pos.begin.lineno-1)) == -1)
goto fail;
- if (PyDict_SetItemString(kw, "_start_col", Py_BuildValue("i", jsnode->pn_pos.begin.index)) == -1)
+ if (PyObject_SetAttrString(kw, "_start_col", Py_BuildValue("i", jsnode->pn_pos.begin.index)) == -1)
goto fail;
- if (PyDict_SetItemString(kw, "_end_line", Py_BuildValue("i", jsnode->pn_pos.end.lineno-1)) == -1)
+ if (PyObject_SetAttrString(kw, "_end_line", Py_BuildValue("i", jsnode->pn_pos.end.lineno-1)) == -1)
goto fail;
- if (PyDict_SetItemString(kw, "_end_col", Py_BuildValue("i", jsnode->pn_pos.end.index)) == -1)
+ if (PyObject_SetAttrString(kw, "_end_col", Py_BuildValue("i", jsnode->pn_pos.end.index)) == -1)
goto fail;
if ((jsnode->pn_type == TOK_NAME || jsnode->pn_type == TOK_DOT ||
jsnode->pn_type == TOK_STRING) && ATOM_IS_STRING(jsnode->pn_atom)) {
/* Convert the atom to a string. */
- if (PyDict_SetItemString(kw, "atom", atom_to_string(jsnode->pn_atom)) == -1)
+ if (PyObject_SetAttrString(kw, "atom", atom_to_string(jsnode->pn_atom)) == -1)
goto fail;
}
- if (PyDict_SetItemString(kw, "opcode", Py_BuildValue("i", OPCODE_TO_NUM(jsnode->pn_op))) == -1)
+ if (PyObject_SetAttrString(kw, "opcode", Py_BuildValue("i", OPCODE_TO_NUM(jsnode->pn_op))) == -1)
goto fail;
if (jsnode->pn_type == TOK_NUMBER) {
- if (PyDict_SetItemString(kw, "dval", Py_BuildValue("d", jsnode->pn_dval)) == -1)
+ if (PyObject_SetAttrString(kw, "dval", Py_BuildValue("d", jsnode->pn_dval)) == -1)
goto fail;
}
@@ -221,7 +226,7 @@
Py_INCREF(Py_None);
fn_name = Py_None;
}
- if (PyDict_SetItemString(kw, "fn_name", fn_name) == -1)
+ if (PyObject_SetAttrString(kw, "fn_name", fn_name) == -1)
goto fail;
/* get the function arguments */
@@ -251,19 +256,19 @@
name = atom_to_string(JSID_TO_ATOM(scope_property->id));
PyTuple_SET_ITEM(fn_args, (uint16)scope_property->shortid, name);
}
- if (PyDict_SetItemString(kw, "fn_args", fn_args) == -1)
+ if (PyObject_SetAttrString(kw, "fn_args", fn_args) == -1)
goto fail;
}
else if (jsnode->pn_type == TOK_RB) {
PyObject* end_comma = PyBool_FromLong(jsnode->pn_extra & PNX_ENDCOMMA);
- if (PyDict_SetItemString(kw, "end_comma", end_comma) == -1)
+ if (PyObject_SetAttrString(kw, "end_comma", end_comma) == -1)
goto fail;
}
switch (jsnode->pn_arity) {
case PN_FUNC:
kids = PyTuple_New(1);
- if (traverse_node(context, jsnode->pn_body, kids, 0) == -1)
+ if (traverse_node(context, jsnode->pn_body, kw, kids, 0) == -1)
return -1;
break;
@@ -272,7 +277,7 @@
int i;
kids = PyTuple_New(jsnode->pn_count);
for (i = 0, p = jsnode->pn_head; p; p = p->pn_next, i++) {
- if (traverse_node(context, p, kids, i) == -1)
+ if (traverse_node(context, p, kw, kids, i) == -1)
return -1;
}
}
@@ -280,31 +285,31 @@
case PN_TERNARY:
kids = PyTuple_New(3);
- if (traverse_node(context, jsnode->pn_kid1, kids, 0) == -1)
+ if (traverse_node(context, jsnode->pn_kid1, kw, kids, 0) == -1)
return -1;
- if (traverse_node(context, jsnode->pn_kid2, kids, 1) == -1)
+ if (traverse_node(context, jsnode->pn_kid2, kw, kids, 1) == -1)
return -1;
- if (traverse_node(context, jsnode->pn_kid3, kids, 2) == -1)
+ if (traverse_node(context, jsnode->pn_kid3, kw, kids, 2) == -1)
return -1;
break;
case PN_BINARY:
kids = PyTuple_New(2);
- if (traverse_node(context, jsnode->pn_left, kids, 0) == -1)
+ if (traverse_node(context, jsnode->pn_left, kw, kids, 0) == -1)
return -1;
- if (traverse_node(context, jsnode->pn_right, kids, 1) == -1)
+ if (traverse_node(context, jsnode->pn_right, kw, kids, 1) == -1)
return -1;
break;
case PN_UNARY:
kids = PyTuple_New(1);
- if (traverse_node(context, jsnode->pn_kid, kids, 0) == -1)
+ if (traverse_node(context, jsnode->pn_kid, kw, kids, 0) == -1)
return -1;
break;
case PN_NAME:
kids = PyTuple_New(1);
- if (traverse_node(context, jsnode->pn_expr, kids, 0) == -1)
+ if (traverse_node(context, jsnode->pn_expr, kw, kids, 0) == -1)
return -1;
break;
@@ -313,7 +318,7 @@
break;
}
- if (PyDict_SetItemString(kw, "kids", kids) == -1)
+ if (PyObject_SetAttrString(kw, "kids", kids) == -1)
goto fail;
return 0;
@@ -346,9 +351,14 @@
error = "encountered an unknown error";
/* validate arguments */
- if (!PyArg_ParseTuple(args, "sO", &m.script, &m.ctx_data.error_callback))
+ if (!PyArg_ParseTuple(args, "sOO", &m.script, &m.ctx_data.node_class, &m.ctx_data.error_callback))
return NULL;
+ if (!PyCallable_Check(m.ctx_data.node_class)) {
+ PyErr_SetString(PyExc_ValueError, "\"node_class\" must be callable");
+ return NULL;
+ }
+
if (!PyCallable_Check(m.ctx_data.error_callback)) {
PyErr_SetString(PyExc_ValueError, "\"error\" must be callable");
return NULL;
@@ -399,7 +409,7 @@
}
m.kids = PyTuple_New(1);
- if (traverse_node(m.context, m.jsnode, m.kids, 0) == -1) {
+ if (traverse_node(m.context, m.jsnode, Py_None, m.kids, 0) == -1) {
error = "";
goto cleanup;
}
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <mat...@us...> - 2008-03-04 03:10:55
|
Revision: 162
http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=162&view=rev
Author: matthiasmiller
Date: 2008-03-03 19:10:46 -0800 (Mon, 03 Mar 2008)
Log Message:
-----------
actually set the eol-style property
Modified Paths:
--------------
trunk/pyjsl/jsparse.py
trunk/pyjsl/util.py
Property Changed:
----------------
trunk/COPYING
trunk/DEVELOPMENT
trunk/INSTALL
trunk/jsl.py
trunk/pyjsl/__init__.py
trunk/pyjsl/conf.py
trunk/pyjsl/jsparse.py
trunk/pyjsl/lint.py
trunk/pyjsl/util.py
trunk/pyjsl/visitation.py
trunk/pyjsl/warnings.py
trunk/pyspidermonkey/pyspidermonkey.c
trunk/pyspidermonkey/tokens.tbl
trunk/setup.py
trunk/test.py
Property changes on: trunk/COPYING
___________________________________________________________________
Name: eol-style
- native
Name: svn:eol-style
+ native
Property changes on: trunk/DEVELOPMENT
___________________________________________________________________
Name: eol-style
- native
Name: svn:eol-style
+ native
Property changes on: trunk/INSTALL
___________________________________________________________________
Name: eol-style
- native
Name: svn:eol-style
+ native
Property changes on: trunk/jsl.py
___________________________________________________________________
Name: eol-style
- native
Name: svn:eol-style
+ native
Property changes on: trunk/pyjsl/__init__.py
___________________________________________________________________
Name: eol-style
- native
Name: svn:eol-style
+ native
Property changes on: trunk/pyjsl/conf.py
___________________________________________________________________
Name: eol-style
- native
Name: svn:eol-style
+ native
Modified: trunk/pyjsl/jsparse.py
===================================================================
--- trunk/pyjsl/jsparse.py 2008-03-04 01:34:39 UTC (rev 161)
+++ trunk/pyjsl/jsparse.py 2008-03-04 03:10:46 UTC (rev 162)
@@ -1,319 +1,319 @@
-#!/usr/bin/python
-""" Parses a script into nodes. """
-import bisect
-import re
-import unittest
-
-import pyspidermonkey
-from pyspidermonkey import tok, op
-
-_tok_names = dict(zip(
- [getattr(tok, prop) for prop in dir(tok)],
- ['tok.%s' % prop for prop in dir(tok)]
-))
-
-class NodePos():
- def __init__(self, line, col):
- self.line = line
- self.col = col
- def __cmp__(self, other):
- if self.line < other.line:
- return -1
- if self.line > other.line:
- return 1
- if self.col < other.col:
- return -1
- if self.col > other.col:
- return 1
- return 0
- def __str__(self):
- return '(line %i, col %i)' % (self.line+1, self.col+1)
-
-class NodePositions():
- " Given a string, allows [x] lookups for NodePos line and column numbers."
- def __init__(self, text):
- # Find the length of each line and incrementally sum all of the lengths
- # to determine the ending position of each line.
- self._lines = text.splitlines(True)
- lines = [0] + [len(x) for x in self._lines]
- for x in range(1, len(lines)):
- lines[x] += lines[x-1]
- self._line_offsets = lines
- def from_offset(self, offset):
- line = bisect.bisect(self._line_offsets, offset)-1
- col = offset - self._line_offsets[line]
- return NodePos(line, col)
- def to_offset(self, pos):
- offset = self._line_offsets[pos.line] + pos.col
- assert offset <= self._line_offsets[pos.line+1] # out-of-bounds col num
- return offset
- def text(self, start, end):
- assert start <= end
- # Trim the ending first in case it's a single line.
- lines = self._lines[start.line:end.line+1]
- lines[-1] = lines[-1][:end.col+1]
- lines[0] = lines[0][start.col:]
- return ''.join(lines)
-
-class NodeRanges():
- def __init__(self):
- self._offsets = []
- def add(self, start, end):
- i = bisect.bisect_left(self._offsets, start)
- if i % 2 == 1:
- i -= 1
- start = self._offsets[i]
-
- end = end + 1
- j = bisect.bisect_left(self._offsets, end)
- if j % 2 == 1:
- end = self._offsets[j]
- j += 1
-
- self._offsets[i:j] = [start,end]
- def has(self, pos):
- return bisect.bisect_right(self._offsets, pos) % 2 == 1
-
-class _Node():
- def add_child(self, node):
- if node:
- node.node_index = len(self.kids)
- node.parent = self
- self.kids.append(node)
-
- def start_pos(self):
- try:
- return self._start_pos
- except AttributeError:
- self._start_pos = NodePos(self._start_line, self._start_col)
- return self._start_pos
-
- def end_pos(self):
- try:
- return self._end_pos
- except AttributeError:
- self._end_pos = NodePos(self._end_line, self._end_col)
- return self._end_pos
-
- def __str__(self):
- kind = self.kind
- if not kind:
- kind = '(none)'
- return '%s>%s' % (_tok_names[kind], str(self.kids))
-
- def is_equivalent(self, other, are_functions_equiv=False):
- if not other:
- return False
-
- # Bail out for functions
- if not are_functions_equiv:
- if self.kind == tok.FUNCTION:
- return False
- if self.kind == tok.LP and self.opcode == op.CALL:
- return False
-
- if self.kind != other.kind:
- return False
- if self.opcode != other.opcode:
- return False
-
- # Check atoms on names, properties, and string constants
- if self.kind in (tok.NAME, tok.DOT, tok.STRING) and self.atom != other.atom:
- return False
-
- # Check values on numbers
- if self.kind == tok.NUMBER and self.dval != other.dval:
- return False
-
- # Compare child nodes
- if len(self.kids) != len(other.kids):
- return False
- for i in range(0, len(self.kids)):
- # Watch for dead nodes
- if not self.kids[i]:
- if not other.kids[i]: return True
- else: return False
- if not self.kids[i].is_equivalent(other.kids[i]):
- return False
-
- return True
-
-def _parse_comments(script, root, node_positions, ignore_ranges):
- pos = 0
- single_line_re = r"//[^\r\n]*"
- multi_line_re = r"/\*(.*?)\*/"
- full_re = "(%s)|(%s)" % (single_line_re, multi_line_re)
- comment_re = re.compile(full_re, re.DOTALL)
-
- comments = []
- while True:
- match = comment_re.search(script, pos)
- if not match:
- return comments
-
- # Get the comment text
- comment_text = script[match.start():match.end()]
- if comment_text.startswith('/*'):
- comment_text = comment_text[2:-2]
- opcode = 'JSOP_C_COMMENT'
- else:
- comment_text = comment_text[2:]
- opcode = 'JSOP_CPP_COMMENT'
- opcode = opcode[5:].lower()
-
- start_offset = match.start()+1
- end_offset = match.end()
-
- # Make sure it doesn't start in a string or regexp
- if not ignore_ranges.has(start_offset):
- start_pos = node_positions.from_offset(start_offset)
- end_pos = node_positions.from_offset(end_offset)
- kwargs = {
- 'type': 'COMMENT',
- 'atom': comment_text,
- 'opcode': opcode,
- '_start_line': start_pos.line,
- '_start_col': start_pos.col,
- '_end_line': end_pos.line,
- '_end_col': end_pos.col,
- 'parent': None,
- 'kids': [],
- 'node_index': None
- }
- comment_node = _Node()
- comment_node.__dict__.update(kwargs)
- comments.append(comment_node)
- pos = match.end()
- else:
- pos = match.start()+1
-
-def parse(script, error_callback):
- def _wrapped_callback(line, col, msg):
- assert msg.startswith('JSMSG_')
- msg = msg[6:].lower()
- error_callback(line, col, msg)
-
- positions = NodePositions(script)
-
- roots = []
- nodes = []
- comment_ignore_ranges = NodeRanges()
- def process(node):
- if node.kind == tok.NUMBER:
- node.atom = positions.text(node.start_pos(), node.end_pos())
- elif node.kind == tok.STRING or \
- (node.kind == tok.OBJECT and node.opcode == op.REGEXP):
- start_offset = positions.to_offset(node.start_pos())
- end_offset = positions.to_offset(node.end_pos())
- comment_ignore_ranges.add(start_offset, end_offset)
- for kid in node.kids:
- if kid:
- process(kid)
- def pop():
- nodes.pop()
-
- roots = pyspidermonkey.traverse(script, _Node, _wrapped_callback)
- assert len(roots) == 1
- root_node = roots[0]
- process(root_node)
-
- comments = _parse_comments(script, root_node, positions, comment_ignore_ranges)
- return root_node, comments
-
-def _dump_node(node, depth=0):
- print '. '*depth,
- if node is None:
- print '(none)'
- else:
- print '%s\t%s, %s' % (_tok_names[node.kind], node.start_pos(), node.end_pos())
- for node in node.kids:
- _dump_node(node, depth+1)
-
-def dump_tree(script):
- def error_callback(line, col, msg):
- print '(%i, %i): %s', (line, col, msg)
- node, comments = parse(script, error_callback)
- _dump_node(node)
-
-class TestComments(unittest.TestCase):
- def _test(self, script, expected_comments):
- root, comments = parse(script, lambda line, col, msg: None)
- encountered_comments = [node.atom for node in comments]
- self.assertEquals(encountered_comments, list(expected_comments))
- def testSimpleComments(self):
- self._test('re = /\//g', ())
- self._test('re = /\///g', ())
- self._test('re = /\////g', ('g',))
- def testCComments(self):
- self._test('/*a*//*b*/', ('a', 'b'))
- self._test('/*a\r\na*//*b\r\nb*/', ('a\r\na', 'b\r\nb'))
- self._test('a//*b*/c', ('*b*/c',))
- self._test('a///*b*/c', ('/*b*/c',))
- self._test('a/*//*/;', ('//',))
- self._test('a/*b*/+/*c*/d', ('b', 'c'))
-
-class TestNodePositions(unittest.TestCase):
- def _test(self, text, expected_lines, expected_cols):
- # Get a NodePos list
- positions = NodePositions(text)
- positions = [positions.from_offset(i) for i in range(0, len(text))]
- encountered_lines = ''.join([str(x.line) for x in positions])
- encountered_cols = ''.join([str(x.col) for x in positions])
- self.assertEquals(encountered_lines, expected_lines.replace(' ', ''))
- self.assertEquals(encountered_cols, expected_cols.replace(' ', ''))
- def testSimple(self):
- self._test(
- 'abc\r\ndef\nghi\n\nj',
- '0000 0 1111 2222 3 4',
- '0123 4 0123 0123 0 0'
- )
- self._test(
- '\rabc',
- '0 111',
- '0 012'
- )
- def testText(self):
- pos = NodePositions('abc\r\ndef\n\nghi')
- self.assertEquals(pos.text(NodePos(0, 0), NodePos(0, 0)), 'a')
- self.assertEquals(pos.text(NodePos(0, 0), NodePos(0, 2)), 'abc')
- self.assertEquals(pos.text(NodePos(0, 2), NodePos(1, 2)), 'c\r\ndef')
- def testOffset(self):
- pos = NodePositions('abc\r\ndef\n\nghi')
- self.assertEquals(pos.to_offset(NodePos(0, 2)), 2)
- self.assertEquals(pos.to_offset(NodePos(1, 0)), 5)
- self.assertEquals(pos.to_offset(NodePos(3, 1)), 11)
-
-class TestNodeRanges(unittest.TestCase):
- def testAdd(self):
- r = NodeRanges()
- r.add(5, 10)
- self.assertEquals(r._offsets, [5,11])
- r.add(15, 20)
- self.assertEquals(r._offsets, [5,11,15,21])
- r.add(21,22)
- self.assertEquals(r._offsets, [5,11,15,23])
- r.add(4,5)
- self.assertEquals(r._offsets, [4,11,15,23])
- r.add(9,11)
- self.assertEquals(r._offsets, [4,12,15,23])
- r.add(10,20)
- self.assertEquals(r._offsets, [4,23])
- r.add(4,22)
- self.assertEquals(r._offsets, [4,23])
- r.add(30,30)
- self.assertEquals(r._offsets, [4,23,30,31])
- def testHas(self):
- r = NodeRanges()
- r.add(5, 10)
- r.add(15, 15)
- assert not r.has(4)
- assert r.has(5)
- assert r.has(6)
- assert r.has(9)
- assert r.has(10)
- assert not r.has(14)
- assert r.has(15)
- assert not r.has(16)
-if __name__ == '__main__':
- unittest.main()
-
+#!/usr/bin/python
+""" Parses a script into nodes. """
+import bisect
+import re
+import unittest
+
+import pyspidermonkey
+from pyspidermonkey import tok, op
+
+_tok_names = dict(zip(
+ [getattr(tok, prop) for prop in dir(tok)],
+ ['tok.%s' % prop for prop in dir(tok)]
+))
+
+class NodePos():
+ def __init__(self, line, col):
+ self.line = line
+ self.col = col
+ def __cmp__(self, other):
+ if self.line < other.line:
+ return -1
+ if self.line > other.line:
+ return 1
+ if self.col < other.col:
+ return -1
+ if self.col > other.col:
+ return 1
+ return 0
+ def __str__(self):
+ return '(line %i, col %i)' % (self.line+1, self.col+1)
+
+class NodePositions():
+ " Given a string, allows [x] lookups for NodePos line and column numbers."
+ def __init__(self, text):
+ # Find the length of each line and incrementally sum all of the lengths
+ # to determine the ending position of each line.
+ self._lines = text.splitlines(True)
+ lines = [0] + [len(x) for x in self._lines]
+ for x in range(1, len(lines)):
+ lines[x] += lines[x-1]
+ self._line_offsets = lines
+ def from_offset(self, offset):
+ line = bisect.bisect(self._line_offsets, offset)-1
+ col = offset - self._line_offsets[line]
+ return NodePos(line, col)
+ def to_offset(self, pos):
+ offset = self._line_offsets[pos.line] + pos.col
+ assert offset <= self._line_offsets[pos.line+1] # out-of-bounds col num
+ return offset
+ def text(self, start, end):
+ assert start <= end
+ # Trim the ending first in case it's a single line.
+ lines = self._lines[start.line:end.line+1]
+ lines[-1] = lines[-1][:end.col+1]
+ lines[0] = lines[0][start.col:]
+ return ''.join(lines)
+
+class NodeRanges():
+ def __init__(self):
+ self._offsets = []
+ def add(self, start, end):
+ i = bisect.bisect_left(self._offsets, start)
+ if i % 2 == 1:
+ i -= 1
+ start = self._offsets[i]
+
+ end = end + 1
+ j = bisect.bisect_left(self._offsets, end)
+ if j % 2 == 1:
+ end = self._offsets[j]
+ j += 1
+
+ self._offsets[i:j] = [start,end]
+ def has(self, pos):
+ return bisect.bisect_right(self._offsets, pos) % 2 == 1
+
+class _Node():
+ def add_child(self, node):
+ if node:
+ node.node_index = len(self.kids)
+ node.parent = self
+ self.kids.append(node)
+
+ def start_pos(self):
+ try:
+ return self._start_pos
+ except AttributeError:
+ self._start_pos = NodePos(self._start_line, self._start_col)
+ return self._start_pos
+
+ def end_pos(self):
+ try:
+ return self._end_pos
+ except AttributeError:
+ self._end_pos = NodePos(self._end_line, self._end_col)
+ return self._end_pos
+
+ def __str__(self):
+ kind = self.kind
+ if not kind:
+ kind = '(none)'
+ return '%s>%s' % (_tok_names[kind], str(self.kids))
+
+ def is_equivalent(self, other, are_functions_equiv=False):
+ if not other:
+ return False
+
+ # Bail out for functions
+ if not are_functions_equiv:
+ if self.kind == tok.FUNCTION:
+ return False
+ if self.kind == tok.LP and self.opcode == op.CALL:
+ return False
+
+ if self.kind != other.kind:
+ return False
+ if self.opcode != other.opcode:
+ return False
+
+ # Check atoms on names, properties, and string constants
+ if self.kind in (tok.NAME, tok.DOT, tok.STRING) and self.atom != other.atom:
+ return False
+
+ # Check values on numbers
+ if self.kind == tok.NUMBER and self.dval != other.dval:
+ return False
+
+ # Compare child nodes
+ if len(self.kids) != len(other.kids):
+ return False
+ for i in range(0, len(self.kids)):
+ # Watch for dead nodes
+ if not self.kids[i]:
+ if not other.kids[i]: return True
+ else: return False
+ if not self.kids[i].is_equivalent(other.kids[i]):
+ return False
+
+ return True
+
+def _parse_comments(script, root, node_positions, ignore_ranges):
+ pos = 0
+ single_line_re = r"//[^\r\n]*"
+ multi_line_re = r"/\*(.*?)\*/"
+ full_re = "(%s)|(%s)" % (single_line_re, multi_line_re)
+ comment_re = re.compile(full_re, re.DOTALL)
+
+ comments = []
+ while True:
+ match = comment_re.search(script, pos)
+ if not match:
+ return comments
+
+ # Get the comment text
+ comment_text = script[match.start():match.end()]
+ if comment_text.startswith('/*'):
+ comment_text = comment_text[2:-2]
+ opcode = 'JSOP_C_COMMENT'
+ else:
+ comment_text = comment_text[2:]
+ opcode = 'JSOP_CPP_COMMENT'
+ opcode = opcode[5:].lower()
+
+ start_offset = match.start()+1
+ end_offset = match.end()
+
+ # Make sure it doesn't start in a string or regexp
+ if not ignore_ranges.has(start_offset):
+ start_pos = node_positions.from_offset(start_offset)
+ end_pos = node_positions.from_offset(end_offset)
+ kwargs = {
+ 'type': 'COMMENT',
+ 'atom': comment_text,
+ 'opcode': opcode,
+ '_start_line': start_pos.line,
+ '_start_col': start_pos.col,
+ '_end_line': end_pos.line,
+ '_end_col': end_pos.col,
+ 'parent': None,
+ 'kids': [],
+ 'node_index': None
+ }
+ comment_node = _Node()
+ comment_node.__dict__.update(kwargs)
+ comments.append(comment_node)
+ pos = match.end()
+ else:
+ pos = match.start()+1
+
+def parse(script, error_callback):
+ def _wrapped_callback(line, col, msg):
+ assert msg.startswith('JSMSG_')
+ msg = msg[6:].lower()
+ error_callback(line, col, msg)
+
+ positions = NodePositions(script)
+
+ roots = []
+ nodes = []
+ comment_ignore_ranges = NodeRanges()
+ def process(node):
+ if node.kind == tok.NUMBER:
+ node.atom = positions.text(node.start_pos(), node.end_pos())
+ elif node.kind == tok.STRING or \
+ (node.kind == tok.OBJECT and node.opcode == op.REGEXP):
+ start_offset = positions.to_offset(node.start_pos())
+ end_offset = positions.to_offset(node.end_pos())
+ comment_ignore_ranges.add(start_offset, end_offset)
+ for kid in node.kids:
+ if kid:
+ process(kid)
+ def pop():
+ nodes.pop()
+
+ roots = pyspidermonkey.traverse(script, _Node, _wrapped_callback)
+ assert len(roots) == 1
+ root_node = roots[0]
+ process(root_node)
+
+ comments = _parse_comments(script, root_node, positions, comment_ignore_ranges)
+ return root_node, comments
+
+def _dump_node(node, depth=0):
+ print '. '*depth,
+ if node is None:
+ print '(none)'
+ else:
+ print '%s\t%s, %s' % (_tok_names[node.kind], node.start_pos(), node.end_pos())
+ for node in node.kids:
+ _dump_node(node, depth+1)
+
+def dump_tree(script):
+ def error_callback(line, col, msg):
+ print '(%i, %i): %s', (line, col, msg)
+ node, comments = parse(script, error_callback)
+ _dump_node(node)
+
+class TestComments(unittest.TestCase):
+ def _test(self, script, expected_comments):
+ root, comments = parse(script, lambda line, col, msg: None)
+ encountered_comments = [node.atom for node in comments]
+ self.assertEquals(encountered_comments, list(expected_comments))
+ def testSimpleComments(self):
+ self._test('re = /\//g', ())
+ self._test('re = /\///g', ())
+ self._test('re = /\////g', ('g',))
+ def testCComments(self):
+ self._test('/*a*//*b*/', ('a', 'b'))
+ self._test('/*a\r\na*//*b\r\nb*/', ('a\r\na', 'b\r\nb'))
+ self._test('a//*b*/c', ('*b*/c',))
+ self._test('a///*b*/c', ('/*b*/c',))
+ self._test('a/*//*/;', ('//',))
+ self._test('a/*b*/+/*c*/d', ('b', 'c'))
+
+class TestNodePositions(unittest.TestCase):
+ def _test(self, text, expected_lines, expected_cols):
+ # Get a NodePos list
+ positions = NodePositions(text)
+ positions = [positions.from_offset(i) for i in range(0, len(text))]
+ encountered_lines = ''.join([str(x.line) for x in positions])
+ encountered_cols = ''.join([str(x.col) for x in positions])
+ self.assertEquals(encountered_lines, expected_lines.replace(' ', ''))
+ self.assertEquals(encountered_cols, expected_cols.replace(' ', ''))
+ def testSimple(self):
+ self._test(
+ 'abc\r\ndef\nghi\n\nj',
+ '0000 0 1111 2222 3 4',
+ '0123 4 0123 0123 0 0'
+ )
+ self._test(
+ '\rabc',
+ '0 111',
+ '0 012'
+ )
+ def testText(self):
+ pos = NodePositions('abc\r\ndef\n\nghi')
+ self.assertEquals(pos.text(NodePos(0, 0), NodePos(0, 0)), 'a')
+ self.assertEquals(pos.text(NodePos(0, 0), NodePos(0, 2)), 'abc')
+ self.assertEquals(pos.text(NodePos(0, 2), NodePos(1, 2)), 'c\r\ndef')
+ def testOffset(self):
+ pos = NodePositions('abc\r\ndef\n\nghi')
+ self.assertEquals(pos.to_offset(NodePos(0, 2)), 2)
+ self.assertEquals(pos.to_offset(NodePos(1, 0)), 5)
+ self.assertEquals(pos.to_offset(NodePos(3, 1)), 11)
+
+class TestNodeRanges(unittest.TestCase):
+ def testAdd(self):
+ r = NodeRanges()
+ r.add(5, 10)
+ self.assertEquals(r._offsets, [5,11])
+ r.add(15, 20)
+ self.assertEquals(r._offsets, [5,11,15,21])
+ r.add(21,22)
+ self.assertEquals(r._offsets, [5,11,15,23])
+ r.add(4,5)
+ self.assertEquals(r._offsets, [4,11,15,23])
+ r.add(9,11)
+ self.assertEquals(r._offsets, [4,12,15,23])
+ r.add(10,20)
+ self.assertEquals(r._offsets, [4,23])
+ r.add(4,22)
+ self.assertEquals(r._offsets, [4,23])
+ r.add(30,30)
+ self.assertEquals(r._offsets, [4,23,30,31])
+ def testHas(self):
+ r = NodeRanges()
+ r.add(5, 10)
+ r.add(15, 15)
+ assert not r.has(4)
+ assert r.has(5)
+ assert r.has(6)
+ assert r.has(9)
+ assert r.has(10)
+ assert not r.has(14)
+ assert r.has(15)
+ assert not r.has(16)
+if __name__ == '__main__':
+ unittest.main()
+
Property changes on: trunk/pyjsl/jsparse.py
___________________________________________________________________
Name: eol-style
- native
Name: svn:eol-style
+ native
Property changes on: trunk/pyjsl/lint.py
___________________________________________________________________
Name: eol-style
- native
Name: svn:eol-style
+ native
Modified: trunk/pyjsl/util.py
===================================================================
--- trunk/pyjsl/util.py 2008-03-04 01:34:39 UTC (rev 161)
+++ trunk/pyjsl/util.py 2008-03-04 03:10:46 UTC (rev 162)
@@ -1,16 +1,16 @@
-import codecs
-import os.path
-
-def readfile(path):
- file = codecs.open(path, 'r', 'utf-8')
- contents = file.read()
- if contents[0] == unicode(codecs.BOM_UTF8, 'utf8'):
- contents = contents[1:]
- return contents
-
-def normpath(path):
- path = os.path.abspath(path)
- path = os.path.normcase(path)
- path = os.path.normpath(path)
- return path
-
+import codecs
+import os.path
+
+def readfile(path):
+ file = codecs.open(path, 'r', 'utf-8')
+ contents = file.read()
+ if contents[0] == unicode(codecs.BOM_UTF8, 'utf8'):
+ contents = contents[1:]
+ return contents
+
+def normpath(path):
+ path = os.path.abspath(path)
+ path = os.path.normcase(path)
+ path = os.path.normpath(path)
+ return path
+
Property changes on: trunk/pyjsl/util.py
___________________________________________________________________
Name: eol-style
- native
Name: svn:eol-style
+ native
Property changes on: trunk/pyjsl/visitation.py
___________________________________________________________________
Name: eol-style
- native
Name: svn:eol-style
+ native
Property changes on: trunk/pyjsl/warnings.py
___________________________________________________________________
Name: eol-style
- native
Name: svn:eol-style
+ native
Property changes on: trunk/pyspidermonkey/pyspidermonkey.c
___________________________________________________________________
Name: eol-style
- native
Property changes on: trunk/pyspidermonkey/tokens.tbl
___________________________________________________________________
Name: eol-style
- native
Name: svn:eol-style
+ native
Property changes on: trunk/setup.py
___________________________________________________________________
Name: eol-style
- native
Name: svn:eol-style
+ native
Property changes on: trunk/test.py
___________________________________________________________________
Name: eol-style
- native
Name: svn:eol-style
+ native
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <mat...@us...> - 2008-03-04 03:44:14
|
Revision: 164
http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=164&view=rev
Author: matthiasmiller
Date: 2008-03-03 19:42:17 -0800 (Mon, 03 Mar 2008)
Log Message:
-----------
provide an interface to SpiderMonkey to determine if a script is a compilable unit
Modified Paths:
--------------
trunk/pyjsl/jsparse.py
trunk/pyspidermonkey/pyspidermonkey.c
Modified: trunk/pyjsl/jsparse.py
===================================================================
--- trunk/pyjsl/jsparse.py 2008-03-04 03:37:22 UTC (rev 163)
+++ trunk/pyjsl/jsparse.py 2008-03-04 03:42:17 UTC (rev 164)
@@ -306,14 +306,29 @@
r = NodeRanges()
r.add(5, 10)
r.add(15, 15)
- assert not r.has(4)
- assert r.has(5)
- assert r.has(6)
- assert r.has(9)
- assert r.has(10)
- assert not r.has(14)
- assert r.has(15)
- assert not r.has(16)
+ assert not r.has(4)
+ assert r.has(5)
+ assert r.has(6)
+ assert r.has(9)
+ assert r.has(10)
+ assert not r.has(14)
+ assert r.has(15)
+ assert not r.has(16)
+
+class TestCompilableUnit(unittest.TestCase):
+ def test(self):
+ tests = (
+ ('var s = "', False),
+ ('bogon()', True),
+ ('int syntax_error;', True),
+ ('a /* b', False),
+ ('re = /.*', False),
+ ('{ // missing curly', False)
+ )
+ for text, is_compilable_unit in tests:
+ self.assertEquals(pyspidermonkey.is_compilable_unit(text),
+ is_compilable_unit)
+
if __name__ == '__main__':
unittest.main()
Modified: trunk/pyspidermonkey/pyspidermonkey.c
===================================================================
--- trunk/pyspidermonkey/pyspidermonkey.c 2008-03-04 03:37:22 UTC (rev 163)
+++ trunk/pyspidermonkey/pyspidermonkey.c 2008-03-04 03:42:17 UTC (rev 164)
@@ -52,10 +52,16 @@
static PyObject*
module_traverse(PyObject *self, PyObject *args);
+static PyObject*
+is_compilable_unit(PyObject *self, PyObject *args);
+
static PyMethodDef module_methods[] = {
{"traverse", module_traverse, METH_VARARGS,
"Parses \"script\" and calls \"push\" and \"pop\" for each node."},
+ {"is_compilable_unit", is_compilable_unit, METH_VARARGS,
+ "Returns True if \"script\" is a compilable unit."},
+
{NULL, NULL, 0, NULL} /* Sentinel */
};
@@ -432,3 +438,67 @@
return m.kids;
}
+static PyObject*
+is_compilable_unit(PyObject *self, PyObject *args) {
+ struct {
+ const char* script;
+ JSRuntime* runtime;
+ JSContext* context;
+ JSObject* global;
+ JSBool is_compilable;
+ } m;
+ const char* error;
+
+ memset(&m, 0, sizeof(m));
+ error = "encountered an unknown error";
+
+ if (!PyArg_ParseTuple(args, "s", &m.script))
+ return NULL;
+
+ m.runtime = JS_NewRuntime(8L * 1024L * 1024L);
+ if (m.runtime == NULL) {
+ error = "cannot create runtime";
+ goto cleanup;
+ }
+
+ m.context = JS_NewContext(m.runtime, 8192);
+ if (m.context == NULL) {
+ error = "cannot create context";
+ goto cleanup;
+ }
+
+ m.global = JS_NewObject(m.context, NULL, NULL, NULL);
+ if (m.global == NULL) {
+ error = "cannot create global object";
+ goto cleanup;
+ }
+
+ if (!JS_InitStandardClasses(m.context, m.global)) {
+ error = "cannot initialize standard classes";
+ goto cleanup;
+ }
+
+ m.is_compilable = JS_BufferIsCompilableUnit(m.context, m.global,
+ m.script, strlen(m.script));
+ error = NULL;
+
+cleanup:
+ if (m.context)
+ JS_DestroyContext(m.context);
+ if (m.runtime)
+ JS_DestroyRuntime(m.runtime);
+
+ if (error) {
+ PyErr_SetString(PyExc_StandardError, error);
+ return NULL;
+ }
+ if (m.is_compilable) {
+ Py_INCREF(Py_True);
+ return Py_True;
+ }
+ else {
+ Py_INCREF(Py_False);
+ return Py_False;
+ }
+}
+
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <mat...@us...> - 2008-03-04 15:15:19
|
Revision: 167
http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=167&view=rev
Author: matthiasmiller
Date: 2008-03-04 07:15:14 -0800 (Tue, 04 Mar 2008)
Log Message:
-----------
pyspidermonkey: change traverse_node to jsnode_to_pynode to clarify reference counting responsibilities
Modified Paths:
--------------
trunk/pyjsl/jsparse.py
trunk/pyspidermonkey/pyspidermonkey.c
Modified: trunk/pyjsl/jsparse.py
===================================================================
--- trunk/pyjsl/jsparse.py 2008-03-04 03:56:49 UTC (rev 166)
+++ trunk/pyjsl/jsparse.py 2008-03-04 15:15:14 UTC (rev 167)
@@ -212,9 +212,7 @@
def pop():
nodes.pop()
- roots = pyspidermonkey.traverse(script, _Node, _wrapped_callback)
- assert len(roots) == 1
- root_node = roots[0]
+ root_node = pyspidermonkey.parse(script, _Node, _wrapped_callback)
process(root_node)
comments = _parse_comments(script, root_node, positions, comment_ignore_ranges)
Modified: trunk/pyspidermonkey/pyspidermonkey.c
===================================================================
--- trunk/pyspidermonkey/pyspidermonkey.c 2008-03-04 03:56:49 UTC (rev 166)
+++ trunk/pyspidermonkey/pyspidermonkey.c 2008-03-04 15:15:14 UTC (rev 167)
@@ -50,14 +50,14 @@
*/
static PyObject*
-module_traverse(PyObject *self, PyObject *args);
+module_parse(PyObject *self, PyObject *args);
static PyObject*
is_compilable_unit(PyObject *self, PyObject *args);
static PyMethodDef module_methods[] = {
- {"traverse", module_traverse, METH_VARARGS,
- "Parses \"script\" and calls \"push\" and \"pop\" for each node."},
+ {"parse", module_parse, METH_VARARGS,
+ "Parses \"script\" and returns a tree of \"node_class\"."},
{"is_compilable_unit", is_compilable_unit, METH_VARARGS,
"Returns True if \"script\" is a compilable unit."},
@@ -160,8 +160,8 @@
}
/* returns 0 on success and -1 on failure */
-static int
-traverse_node(JSContext* context, JSParseNode* jsnode, PyObject* parent, PyObject* tuple, int node_offset) {
+static PyObject*
+jsnode_to_pynode(JSContext* context, JSParseNode* jsnode) {
JSContextData* data = JS_GetContextPrivate(context);
PyObject* pynode = NULL;
PyObject* kids = NULL;
@@ -170,8 +170,7 @@
if (!jsnode) {
Py_INCREF(Py_None);
- PyTuple_SET_ITEM(tuple, node_offset, Py_None);
- return 0;
+ return Py_None;
}
/* pass in a dictionary of options */
@@ -179,15 +178,14 @@
if (!pynode)
goto fail;
- PyTuple_SET_ITEM(tuple, node_offset, pynode);
-
- Py_INCREF(parent);
- if (PyObject_SetAttrString(pynode, "parent", parent) == -1)
+ Py_INCREF(Py_None);
+ if (PyObject_SetAttrString(pynode, "parent", Py_None) == -1)
goto fail;
+ Py_INCREF(Py_None);
+ if (PyObject_SetAttrString(pynode, "node_index", Py_None) == -1)
+ goto fail;
if (PyObject_SetAttrString(pynode, "kind", Py_BuildValue("i", TOK_TO_NUM(jsnode->pn_type))) == -1)
goto fail;
- if (PyObject_SetAttrString(pynode, "node_index", Py_BuildValue("i", node_offset)) == -1)
- goto fail;
/* pass the position */
if (PyObject_SetAttrString(pynode, "_start_line", Py_BuildValue("i", jsnode->pn_pos.begin.lineno-1)) == -1)
@@ -274,8 +272,7 @@
switch (jsnode->pn_arity) {
case PN_FUNC:
kids = PyTuple_New(1);
- if (traverse_node(context, jsnode->pn_body, pynode, kids, 0) == -1)
- return -1;
+ PyTuple_SET_ITEM(kids, 0, jsnode_to_pynode(context, jsnode->pn_body));
break;
case PN_LIST: {
@@ -283,40 +280,32 @@
int i;
kids = PyTuple_New(jsnode->pn_count);
for (i = 0, p = jsnode->pn_head; p; p = p->pn_next, i++) {
- if (traverse_node(context, p, pynode, kids, i) == -1)
- return -1;
+ PyTuple_SET_ITEM(kids, i, jsnode_to_pynode(context, p));
}
}
break;
case PN_TERNARY:
kids = PyTuple_New(3);
- if (traverse_node(context, jsnode->pn_kid1, pynode, kids, 0) == -1)
- return -1;
- if (traverse_node(context, jsnode->pn_kid2, pynode, kids, 1) == -1)
- return -1;
- if (traverse_node(context, jsnode->pn_kid3, pynode, kids, 2) == -1)
- return -1;
+ PyTuple_SET_ITEM(kids, 0, jsnode_to_pynode(context, jsnode->pn_kid1));
+ PyTuple_SET_ITEM(kids, 1, jsnode_to_pynode(context, jsnode->pn_kid2));
+ PyTuple_SET_ITEM(kids, 2, jsnode_to_pynode(context, jsnode->pn_kid3));
break;
case PN_BINARY:
kids = PyTuple_New(2);
- if (traverse_node(context, jsnode->pn_left, pynode, kids, 0) == -1)
- return -1;
- if (traverse_node(context, jsnode->pn_right, pynode, kids, 1) == -1)
- return -1;
+ PyTuple_SET_ITEM(kids, 0, jsnode_to_pynode(context, jsnode->pn_left));
+ PyTuple_SET_ITEM(kids, 1, jsnode_to_pynode(context, jsnode->pn_right));
break;
case PN_UNARY:
kids = PyTuple_New(1);
- if (traverse_node(context, jsnode->pn_kid, pynode, kids, 0) == -1)
- return -1;
+ PyTuple_SET_ITEM(kids, 0, jsnode_to_pynode(context, jsnode->pn_kid));
break;
case PN_NAME:
kids = PyTuple_New(1);
- if (traverse_node(context, jsnode->pn_expr, pynode, kids, 0) == -1)
- return -1;
+ PyTuple_SET_ITEM(kids, 0, jsnode_to_pynode(context, jsnode->pn_expr));
break;
case PN_NULLARY:
@@ -324,23 +313,43 @@
break;
}
+ if (!kids)
+ goto fail;
+
if (PyObject_SetAttrString(pynode, "kids", kids) == -1)
goto fail;
- return 0;
+ {
+ int i;
+ for (i = 0; i < PyTuple_GET_SIZE(kids); i++) {
+ PyObject* kid = PyTuple_GET_ITEM(kids, i);
+ if (!kid)
+ goto fail;
+ if (kid == Py_None)
+ continue;
+ Py_INCREF(pynode);
+ if (PyObject_SetAttrString(kid, "parent", pynode) == -1)
+ goto fail;
+ if (PyObject_SetAttrString(kid, "node_index", Py_BuildValue("i", i)) == -1)
+ goto fail;
+ }
+ }
+
+ return pynode;
+
fail:
if (pynode) {
Py_XDECREF(pynode);
}
- return -1;
+ return NULL;
}
static PyObject*
-module_traverse(PyObject *self, PyObject *args) {
+module_parse(PyObject *self, PyObject *args) {
struct {
const char* script;
- PyObject* kids;
+ PyObject* pynode;
JSRuntime* runtime;
JSContext* context;
@@ -414,8 +423,8 @@
goto cleanup;
}
- m.kids = PyTuple_New(1);
- if (traverse_node(m.context, m.jsnode, Py_None, m.kids, 0) == -1) {
+ m.pynode = jsnode_to_pynode(m.context, m.jsnode);
+ if (!m.pynode) {
error = "";
goto cleanup;
}
@@ -434,8 +443,7 @@
}
return NULL;
}
- Py_INCREF(m.kids);
- return m.kids;
+ return m.pynode;
}
static PyObject*
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <mat...@us...> - 2008-03-20 21:31:09
|
Revision: 175
http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=175&view=rev
Author: matthiasmiller
Date: 2008-03-20 14:31:06 -0700 (Thu, 20 Mar 2008)
Log Message:
-----------
missing_break warning: fix try/catch/finally statements
Modified Paths:
--------------
trunk/pyjsl/warnings.py
trunk/tests/warnings/missing_break.js
Modified: trunk/pyjsl/warnings.py
===================================================================
--- trunk/pyjsl/warnings.py 2008-03-20 21:30:17 UTC (rev 174)
+++ trunk/pyjsl/warnings.py 2008-03-20 21:31:06 UTC (rev 175)
@@ -89,12 +89,28 @@
elif node.kind == tok.TRY:
try_, catch_, finally_ = node.kids
+ assert catch_.kind == tok.RESERVED
+ catch_, = catch_.kids
+ assert catch_.kind == tok.LEXICALSCOPE
+ catch_, = catch_.kids
+ assert catch_.kind == tok.CATCH
+ ignored, ignored, catch_ = catch_.kids
+ assert catch_.kind == tok.LC
+
exit_points = _get_exit_points(try_) | _get_exit_points(catch_)
if finally_:
- # Always if the finally has an exit point
- if None in exit_points:
- exit_points.remove(None)
- exit_points |= _get_exit_points(finally_)
+ finally_exit_points = _get_exit_points(finally_)
+ if None in finally_exit_points:
+ # The finally statement does not add a missing exit point.
+ finally_exit_points.remove(None)
+ else:
+ # If the finally statement always returns, the other
+ # exit points are irrelevant.
+ if None in exit_points:
+ exit_points.remove(None)
+
+ exit_points |= finally_exit_points
+
else:
exit_points = set([None])
@@ -311,7 +327,8 @@
if not case_contents.kids:
return
if None in _get_exit_points(case_contents):
- return node
+ # Show the warning on the *next* node.
+ return node.parent.kids[node.node_index+1]
class missing_break_for_last_case:
'missing break statement for last case in switch'
Modified: trunk/tests/warnings/missing_break.js
===================================================================
--- trunk/tests/warnings/missing_break.js 2008-03-20 21:30:17 UTC (rev 174)
+++ trunk/tests/warnings/missing_break.js 2008-03-20 21:31:06 UTC (rev 175)
@@ -52,7 +52,7 @@
}
case 6: /*warning:missing_break*/
- /*ok; finally statement never called*/
+ /*ok; finally statement does not affect it */
try {
i--;
break;
@@ -79,6 +79,19 @@
break;
}
+ case 8:
+ /*ok; return statement in finally*/
+ try {
+ i--;
+ }
+ catch (err) {
+ s = null;
+ }
+ finally {
+ i++;
+ return i;
+ }
+
default:
break;
}
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <mat...@us...> - 2008-03-31 13:45:22
|
Revision: 178
http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=178&view=rev
Author: matthiasmiller
Date: 2008-03-31 06:45:14 -0700 (Mon, 31 Mar 2008)
Log Message:
-----------
Use spaces instead of tabs.
Modified Paths:
--------------
trunk/DEVELOPMENT
trunk/jsl.py
trunk/pyjsl/conf.py
trunk/pyjsl/jsparse.py
trunk/pyjsl/lint.py
trunk/pyjsl/util.py
trunk/pyjsl/visitation.py
trunk/pyjsl/warnings.py
trunk/pyspidermonkey/pyspidermonkey.c
trunk/setup.py
trunk/test.py
Modified: trunk/DEVELOPMENT
===================================================================
--- trunk/DEVELOPMENT 2008-03-20 21:38:49 UTC (rev 177)
+++ trunk/DEVELOPMENT 2008-03-31 13:45:14 UTC (rev 178)
@@ -1,9 +1,7 @@
** STYLE GUIDELINES
-> Use tabs instead of spaces (for now)
> All lines should be 79 characters or less
-> For everything else, follow http://www.python.org/dev/peps/pep-0008/ as much
- as possible.
+> Follow http://www.python.org/dev/peps/pep-0008/ as much as possible.
** TODO
@@ -23,9 +21,9 @@
SpiderMonkey. Use a relative path for pretty commit messages.
svn_load_dirs.pl \
- -t X.X.X \
- -p svn_load_dirs.conf \
- https://javascriptlint.svn.sourceforge.net/svnroot/javascriptlint/vendorsrc/Mozilla.org/js \
- current \
- js-X.X.X
+ -t X.X.X \
+ -p svn_load_dirs.conf \
+ https://javascriptlint.svn.sourceforge.net/svnroot/javascriptlint/vendorsrc/Mozilla.org/js \
+ current \
+ js-X.X.X
Modified: trunk/jsl.py
===================================================================
--- trunk/jsl.py 2008-03-20 21:38:49 UTC (rev 177)
+++ trunk/jsl.py 2008-03-31 13:45:14 UTC (rev 178)
@@ -7,11 +7,11 @@
import unittest
try:
- import setup
+ import setup
except ImportError:
- pass
+ pass
else:
- sys.path.append(setup.get_lib_path())
+ sys.path.append(setup.get_lib_path())
import pyjsl.conf
import pyjsl.jsparse
@@ -19,84 +19,84 @@
import test
_lint_results = {
- 'warnings': 0,
- 'errors': 0
+ 'warnings': 0,
+ 'errors': 0
}
def get_test_files():
- # Get a list of test files.
- dir_ = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'tests')
+ # Get a list of test files.
+ dir_ = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'tests')
- all_files = []
- for root, dirs, files in os.walk(dir_):
- all_files += [os.path.join(dir_, root, file) for file in files]
- if '.svn' in dirs:
- dirs.remove('.svn')
- # TODO
- if 'conf' in dirs:
- dirs.remove('conf')
- all_files.sort()
- return all_files
+ all_files = []
+ for root, dirs, files in os.walk(dir_):
+ all_files += [os.path.join(dir_, root, file) for file in files]
+ if '.svn' in dirs:
+ dirs.remove('.svn')
+ # TODO
+ if 'conf' in dirs:
+ dirs.remove('conf')
+ all_files.sort()
+ return all_files
def run_tests():
- for file in get_test_files():
- if file.endswith('.htm') or file.endswith('.html'):
- continue #TODO
- elif file.endswith('.js'):
- try:
- test.run(file)
- except test.TestError, error:
- print error
+ for file in get_test_files():
+ if file.endswith('.htm') or file.endswith('.html'):
+ continue #TODO
+ elif file.endswith('.js'):
+ try:
+ test.run(file)
+ except test.TestError, error:
+ print error
def _dump(paths):
- for path in paths:
- script = pyjsl.util.readfile(path)
- pyjsl.jsparse.dump_tree(script)
+ for path in paths:
+ script = pyjsl.util.readfile(path)
+ pyjsl.jsparse.dump_tree(script)
def _lint(paths, conf):
- def lint_error(path, line, col, errname):
- _lint_results['warnings'] = _lint_results['warnings'] + 1
- print '%s(%i): %s' % (path, line, errname)
- pyjsl.lint.lint_files(paths, lint_error, conf=conf)
+ def lint_error(path, line, col, errname):
+ _lint_results['warnings'] = _lint_results['warnings'] + 1
+ print '%s(%i): %s' % (path, line, errname)
+ pyjsl.lint.lint_files(paths, lint_error, conf=conf)
def _resolve_paths(path, recurse):
- if os.path.isfile(path):
- return [path]
- elif os.path.isdir(path):
- dir = path
- pattern = '*'
- else:
- dir, pattern = os.path.split(path)
+ if os.path.isfile(path):
+ return [path]
+ elif os.path.isdir(path):
+ dir = path
+ pattern = '*'
+ else:
+ dir, pattern = os.path.split(path)
- # Build a list of directories
- dirs = [dir]
- if recurse:
- for cur_root, cur_dirs, cur_files in os.walk(dir):
- for name in cur_dirs:
- dirs.append(os.path.join(cur_root, name))
+ # Build a list of directories
+ dirs = [dir]
+ if recurse:
+ for cur_root, cur_dirs, cur_files in os.walk(dir):
+ for name in cur_dirs:
+ dirs.append(os.path.join(cur_root, name))
- # Glob all files.
- paths = []
- for dir in dirs:
- paths.extend(glob.glob(os.path.join(dir, pattern)))
- return paths
+ # Glob all files.
+ paths = []
+ for dir in dirs:
+ paths.extend(glob.glob(os.path.join(dir, pattern)))
+ return paths
def profile_enabled(func, *args, **kwargs):
- import tempfile
- import hotshot
- import hotshot.stats
- handle, filename = tempfile.mkstemp()
- profile = hotshot.Profile(filename)
- profile.runcall(func, *args, **kwargs)
- profile.close()
- stats = hotshot.stats.load(filename)
- stats = stats.sort_stats("time")
- stats.print_stats()
+ import tempfile
+ import hotshot
+ import hotshot.stats
+ handle, filename = tempfile.mkstemp()
+ profile = hotshot.Profile(filename)
+ profile.runcall(func, *args, **kwargs)
+ profile.close()
+ stats = hotshot.stats.load(filename)
+ stats = stats.sort_stats("time")
+ stats.print_stats()
def profile_disabled(func, *args, **kwargs):
- func(*args, **kwargs)
+ func(*args, **kwargs)
def usage():
- print """
+ print """
Usage:
jsl [files]
--help (-h) print this help
@@ -105,42 +105,42 @@
"""
if __name__ == '__main__':
- try:
- opts, args = getopt.getopt(sys.argv[1:], 'ht:v', ['conf=', 'help', 'test', 'dump', 'unittest', 'profile'])
- except getopt.GetoptError:
- usage()
- sys.exit(2)
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], 'ht:v', ['conf=', 'help', 'test', 'dump', 'unittest', 'profile'])
+ except getopt.GetoptError:
+ usage()
+ sys.exit(2)
- dump = False
- conf = pyjsl.conf.Conf()
- profile_func = profile_disabled
- for opt, val in opts:
- if opt in ('-h', '--help'):
- usage()
- sys.exit()
- if opt in ('--dump',):
- dump = True
- if opt in ('-t', '--test'):
- profile_func(run_tests)
- if opt in ('--unittest',):
- unittest.main(pyjsl.jsparse, argv=sys.argv[:1])
- if opt in ('--profile',):
- profile_func = profile_enabled
- if opt in ('--conf',):
- conf.loadfile(val)
+ dump = False
+ conf = pyjsl.conf.Conf()
+ profile_func = profile_disabled
+ for opt, val in opts:
+ if opt in ('-h', '--help'):
+ usage()
+ sys.exit()
+ if opt in ('--dump',):
+ dump = True
+ if opt in ('-t', '--test'):
+ profile_func(run_tests)
+ if opt in ('--unittest',):
+ unittest.main(pyjsl.jsparse, argv=sys.argv[:1])
+ if opt in ('--profile',):
+ profile_func = profile_enabled
+ if opt in ('--conf',):
+ conf.loadfile(val)
- paths = []
- for recurse, path in conf['paths']:
- paths.extend(_resolve_paths(path, recurse))
- for arg in args:
- paths.extend(_resolve_paths(arg, False))
- if dump:
- profile_func(_dump, paths)
- else:
- profile_func(_lint, paths, conf)
+ paths = []
+ for recurse, path in conf['paths']:
+ paths.extend(_resolve_paths(path, recurse))
+ for arg in args:
+ paths.extend(_resolve_paths(arg, False))
+ if dump:
+ profile_func(_dump, paths)
+ else:
+ profile_func(_lint, paths, conf)
- if _lint_results['errors']:
- sys.exit(3)
- if _lint_results['warnings']:
- sys.exit(1)
+ if _lint_results['errors']:
+ sys.exit(3)
+ if _lint_results['warnings']:
+ sys.exit(1)
Modified: trunk/pyjsl/conf.py
===================================================================
--- trunk/pyjsl/conf.py 2008-03-20 21:38:49 UTC (rev 177)
+++ trunk/pyjsl/conf.py 2008-03-31 13:45:14 UTC (rev 178)
@@ -3,133 +3,133 @@
import warnings
class ConfError(Exception):
- def __init__(self, error):
- Exception.__init__(error)
- self.lineno = None
- self.path = None
+ def __init__(self, error):
+ Exception.__init__(error)
+ self.lineno = None
+ self.path = None
class Setting:
- wants_parm = False
- wants_dir = False
+ wants_parm = False
+ wants_dir = False
class BooleanSetting(Setting):
- wants_parm = False
- def __init__(self, default):
- self.value = default
- def load(self, enabled):
- self.value = enabled
+ wants_parm = False
+ def __init__(self, default):
+ self.value = default
+ def load(self, enabled):
+ self.value = enabled
class StringSetting(Setting):
- wants_parm = True
- def __init__(self, default):
- self.value = default
- def load(self, enabled, parm):
- if not enabled:
- raise ConfError, 'Expected +.'
- self.value = parm
+ wants_parm = True
+ def __init__(self, default):
+ self.value = default
+ def load(self, enabled, parm):
+ if not enabled:
+ raise ConfError, 'Expected +.'
+ self.value = parm
class DeclareSetting(Setting):
- wants_parm = True
- def __init__(self):
- self.value = []
- def load(self, enabled, parm):
- if not enabled:
- raise ConfError, 'Expected +.'
- self.value.append(parm)
+ wants_parm = True
+ def __init__(self):
+ self.value = []
+ def load(self, enabled, parm):
+ if not enabled:
+ raise ConfError, 'Expected +.'
+ self.value.append(parm)
class ProcessSetting(Setting):
- wants_parm = True
- wants_dir = True
- def __init__(self, recurse_setting):
- self.value = []
- self._recurse = recurse_setting
- def load(self, enabled, parm, dir):
- if dir:
- parm = os.path.join(dir, parm)
- self.value.append((self._recurse.value, parm))
+ wants_parm = True
+ wants_dir = True
+ def __init__(self, recurse_setting):
+ self.value = []
+ self._recurse = recurse_setting
+ def load(self, enabled, parm, dir):
+ if dir:
+ parm = os.path.join(dir, parm)
+ self.value.append((self._recurse.value, parm))
class Conf:
- def __init__(self):
- recurse = BooleanSetting(False)
- self._settings = {
- 'recurse': recurse,
- 'show_context': BooleanSetting(False),
- 'output-format': StringSetting('TODO'),
- 'lambda_assign_requires_semicolon': BooleanSetting(False),
- 'legacy_control_comments': BooleanSetting(True),
- 'jscript_function_extensions': BooleanSetting(False),
- 'always_use_option_explicit': BooleanSetting(False),
- 'define': DeclareSetting(),
- 'context': BooleanSetting(False),
- 'process': ProcessSetting(recurse),
- # SpiderMonkey warnings
- 'no_return_value': BooleanSetting(True),
- 'equal_as_assign': BooleanSetting(True),
- 'anon_no_return_value': BooleanSetting(True)
- }
- for klass in warnings.klasses:
- self._settings[klass.__name__] = BooleanSetting(True)
- self.loadline('-block_without_braces')
+ def __init__(self):
+ recurse = BooleanSetting(False)
+ self._settings = {
+ 'recurse': recurse,
+ 'show_context': BooleanSetting(False),
+ 'output-format': StringSetting('TODO'),
+ 'lambda_assign_requires_semicolon': BooleanSetting(False),
+ 'legacy_control_comments': BooleanSetting(True),
+ 'jscript_function_extensions': BooleanSetting(False),
+ 'always_use_option_explicit': BooleanSetting(False),
+ 'define': DeclareSetting(),
+ 'context': BooleanSetting(False),
+ 'process': ProcessSetting(recurse),
+ # SpiderMonkey warnings
+ 'no_return_value': BooleanSetting(True),
+ 'equal_as_assign': BooleanSetting(True),
+ 'anon_no_return_value': BooleanSetting(True)
+ }
+ for klass in warnings.klasses:
+ self._settings[klass.__name__] = BooleanSetting(True)
+ self.loadline('-block_without_braces')
- def loadfile(self, path):
- path = os.path.abspath(path)
- conf = open(path, 'r').read()
- try:
- self.loadtext(conf, dir=os.path.dirname(path))
- except ConfError, error:
- error.path = path
- raise
+ def loadfile(self, path):
+ path = os.path.abspath(path)
+ conf = open(path, 'r').read()
+ try:
+ self.loadtext(conf, dir=os.path.dirname(path))
+ except ConfError, error:
+ error.path = path
+ raise
- def loadtext(self, conf, dir=None):
- lines = conf.splitlines()
- for lineno in range(0, len(lines)):
- try:
- self.loadline(lines[lineno], dir)
- except ConfError, error:
- error.lineno = lineno
- raise
+ def loadtext(self, conf, dir=None):
+ lines = conf.splitlines()
+ for lineno in range(0, len(lines)):
+ try:
+ self.loadline(lines[lineno], dir)
+ except ConfError, error:
+ error.lineno = lineno
+ raise
- def loadline(self, line, dir=None):
- assert not '\r' in line
- assert not '\n' in line
+ def loadline(self, line, dir=None):
+ assert not '\r' in line
+ assert not '\n' in line
- # Allow comments
- if '#' in line:
- line = line[:line.find('#')]
- line = line.rstrip()
- if not line:
- return
+ # Allow comments
+ if '#' in line:
+ line = line[:line.find('#')]
+ line = line.rstrip()
+ if not line:
+ return
- # Parse the +/-
- if line.startswith('+'):
- enabled = True
- elif line.startswith('-'):
- enabled = False
- else:
- raise ConfError, 'Expected + or -.'
- line = line[1:]
+ # Parse the +/-
+ if line.startswith('+'):
+ enabled = True
+ elif line.startswith('-'):
+ enabled = False
+ else:
+ raise ConfError, 'Expected + or -.'
+ line = line[1:]
- # Parse the key/parms
- name = line.split()[0].lower()
- parm = line[len(name):].lstrip()
+ # Parse the key/parms
+ name = line.split()[0].lower()
+ parm = line[len(name):].lstrip()
- # Load the setting
- setting = self._settings[name]
- args = {
- 'enabled': enabled
- }
- if setting.wants_parm:
- args['parm'] = parm
- elif parm:
- raise ConfError, 'The %s setting does not expect a parameter.' % name
- if setting.wants_dir:
- args['dir'] = dir
- setting.load(**args)
+ # Load the setting
+ setting = self._settings[name]
+ args = {
+ 'enabled': enabled
+ }
+ if setting.wants_parm:
+ args['parm'] = parm
+ elif parm:
+ raise ConfError, 'The %s setting does not expect a parameter.' % name
+ if setting.wants_dir:
+ args['dir'] = dir
+ setting.load(**args)
- def __getitem__(self, name):
- if name == 'paths':
- name = 'process'
- elif name == 'declarations':
- name = 'define'
- return self._settings[name].value
+ def __getitem__(self, name):
+ if name == 'paths':
+ name = 'process'
+ elif name == 'declarations':
+ name = 'define'
+ return self._settings[name].value
Modified: trunk/pyjsl/jsparse.py
===================================================================
--- trunk/pyjsl/jsparse.py 2008-03-20 21:38:49 UTC (rev 177)
+++ trunk/pyjsl/jsparse.py 2008-03-31 13:45:14 UTC (rev 178)
@@ -8,325 +8,325 @@
from pyspidermonkey import tok, op
_tok_names = dict(zip(
- [getattr(tok, prop) for prop in dir(tok)],
- ['tok.%s' % prop for prop in dir(tok)]
+ [getattr(tok, prop) for prop in dir(tok)],
+ ['tok.%s' % prop for prop in dir(tok)]
))
class NodePos:
- def __init__(self, line, col):
- self.line = line
- self.col = col
- def __cmp__(self, other):
- if self.line < other.line:
- return -1
- if self.line > other.line:
- return 1
- if self.col < other.col:
- return -1
- if self.col > other.col:
- return 1
- return 0
- def __str__(self):
- return '(line %i, col %i)' % (self.line+1, self.col+1)
+ def __init__(self, line, col):
+ self.line = line
+ self.col = col
+ def __cmp__(self, other):
+ if self.line < other.line:
+ return -1
+ if self.line > other.line:
+ return 1
+ if self.col < other.col:
+ return -1
+ if self.col > other.col:
+ return 1
+ return 0
+ def __str__(self):
+ return '(line %i, col %i)' % (self.line+1, self.col+1)
class NodePositions:
- " Given a string, allows [x] lookups for NodePos line and column numbers."
- def __init__(self, text):
- # Find the length of each line and incrementally sum all of the lengths
- # to determine the ending position of each line.
- self._lines = text.splitlines(True)
- lines = [0] + [len(x) for x in self._lines]
- for x in range(1, len(lines)):
- lines[x] += lines[x-1]
- self._line_offsets = lines
- def from_offset(self, offset):
- line = bisect.bisect(self._line_offsets, offset)-1
- col = offset - self._line_offsets[line]
- return NodePos(line, col)
- def to_offset(self, pos):
- offset = self._line_offsets[pos.line] + pos.col
- assert offset <= self._line_offsets[pos.line+1] # out-of-bounds col num
- return offset
- def text(self, start, end):
- assert start <= end
- # Trim the ending first in case it's a single line.
- lines = self._lines[start.line:end.line+1]
- lines[-1] = lines[-1][:end.col+1]
- lines[0] = lines[0][start.col:]
- return ''.join(lines)
+ " Given a string, allows [x] lookups for NodePos line and column numbers."
+ def __init__(self, text):
+ # Find the length of each line and incrementally sum all of the lengths
+ # to determine the ending position of each line.
+ self._lines = text.splitlines(True)
+ lines = [0] + [len(x) for x in self._lines]
+ for x in range(1, len(lines)):
+ lines[x] += lines[x-1]
+ self._line_offsets = lines
+ def from_offset(self, offset):
+ line = bisect.bisect(self._line_offsets, offset)-1
+ col = offset - self._line_offsets[line]
+ return NodePos(line, col)
+ def to_offset(self, pos):
+ offset = self._line_offsets[pos.line] + pos.col
+ assert offset <= self._line_offsets[pos.line+1] # out-of-bounds col num
+ return offset
+ def text(self, start, end):
+ assert start <= end
+ # Trim the ending first in case it's a single line.
+ lines = self._lines[start.line:end.line+1]
+ lines[-1] = lines[-1][:end.col+1]
+ lines[0] = lines[0][start.col:]
+ return ''.join(lines)
class NodeRanges:
- def __init__(self):
- self._offsets = []
- def add(self, start, end):
- i = bisect.bisect_left(self._offsets, start)
- if i % 2 == 1:
- i -= 1
- start = self._offsets[i]
+ def __init__(self):
+ self._offsets = []
+ def add(self, start, end):
+ i = bisect.bisect_left(self._offsets, start)
+ if i % 2 == 1:
+ i -= 1
+ start = self._offsets[i]
- end = end + 1
- j = bisect.bisect_left(self._offsets, end)
- if j % 2 == 1:
- end = self._offsets[j]
- j += 1
+ end = end + 1
+ j = bisect.bisect_left(self._offsets, end)
+ if j % 2 == 1:
+ end = self._offsets[j]
+ j += 1
- self._offsets[i:j] = [start,end]
- def has(self, pos):
- return bisect.bisect_right(self._offsets, pos) % 2 == 1
+ self._offsets[i:j] = [start,end]
+ def has(self, pos):
+ return bisect.bisect_right(self._offsets, pos) % 2 == 1
class _Node:
- def add_child(self, node):
- if node:
- node.node_index = len(self.kids)
- node.parent = self
- self.kids.append(node)
-
- def start_pos(self):
- try:
- return self._start_pos
- except AttributeError:
- self._start_pos = NodePos(self._start_line, self._start_col)
- return self._start_pos
+ def add_child(self, node):
+ if node:
+ node.node_index = len(self.kids)
+ node.parent = self
+ self.kids.append(node)
+
+ def start_pos(self):
+ try:
+ return self._start_pos
+ except AttributeError:
+ self._start_pos = NodePos(self._start_line, self._start_col)
+ return self._start_pos
- def end_pos(self):
- try:
- return self._end_pos
- except AttributeError:
- self._end_pos = NodePos(self._end_line, self._end_col)
- return self._end_pos
+ def end_pos(self):
+ try:
+ return self._end_pos
+ except AttributeError:
+ self._end_pos = NodePos(self._end_line, self._end_col)
+ return self._end_pos
- def __str__(self):
- kind = self.kind
- if not kind:
- kind = '(none)'
- return '%s>%s' % (_tok_names[kind], str(self.kids))
+ def __str__(self):
+ kind = self.kind
+ if not kind:
+ kind = '(none)'
+ return '%s>%s' % (_tok_names[kind], str(self.kids))
- def is_equivalent(self, other, are_functions_equiv=False):
- if not other:
- return False
+ def is_equivalent(self, other, are_functions_equiv=False):
+ if not other:
+ return False
- # Bail out for functions
- if not are_functions_equiv:
- if self.kind == tok.FUNCTION:
- return False
- if self.kind == tok.LP and self.opcode == op.CALL:
- return False
+ # Bail out for functions
+ if not are_functions_equiv:
+ if self.kind == tok.FUNCTION:
+ return False
+ if self.kind == tok.LP and self.opcode == op.CALL:
+ return False
- if self.kind != other.kind:
- return False
- if self.opcode != other.opcode:
- return False
+ if self.kind != other.kind:
+ return False
+ if self.opcode != other.opcode:
+ return False
- # Check atoms on names, properties, and string constants
- if self.kind in (tok.NAME, tok.DOT, tok.STRING) and self.atom != other.atom:
- return False
+ # Check atoms on names, properties, and string constants
+ if self.kind in (tok.NAME, tok.DOT, tok.STRING) and self.atom != other.atom:
+ return False
- # Check values on numbers
- if self.kind == tok.NUMBER and self.dval != other.dval:
- return False
+ # Check values on numbers
+ if self.kind == tok.NUMBER and self.dval != other.dval:
+ return False
- # Compare child nodes
- if len(self.kids) != len(other.kids):
- return False
- for i in range(0, len(self.kids)):
- # Watch for dead nodes
- if not self.kids[i]:
- if not other.kids[i]: return True
- else: return False
- if not self.kids[i].is_equivalent(other.kids[i]):
- return False
+ # Compare child nodes
+ if len(self.kids) != len(other.kids):
+ return False
+ for i in range(0, len(self.kids)):
+ # Watch for dead nodes
+ if not self.kids[i]:
+ if not other.kids[i]: return True
+ else: return False
+ if not self.kids[i].is_equivalent(other.kids[i]):
+ return False
- return True
+ return True
def _parse_comments(script, root, node_positions, ignore_ranges):
- pos = 0
- single_line_re = r"//[^\r\n]*"
- multi_line_re = r"/\*(.*?)\*/"
- full_re = "(%s)|(%s)" % (single_line_re, multi_line_re)
- comment_re = re.compile(full_re, re.DOTALL)
+ pos = 0
+ single_line_re = r"//[^\r\n]*"
+ multi_line_re = r"/\*(.*?)\*/"
+ full_re = "(%s)|(%s)" % (single_line_re, multi_line_re)
+ comment_re = re.compile(full_re, re.DOTALL)
- comments = []
- while True:
- match = comment_re.search(script, pos)
- if not match:
- return comments
+ comments = []
+ while True:
+ match = comment_re.search(script, pos)
+ if not match:
+ return comments
- # Get the comment text
- comment_text = script[match.start():match.end()]
- if comment_text.startswith('/*'):
- comment_text = comment_text[2:-2]
- opcode = 'JSOP_C_COMMENT'
- else:
- comment_text = comment_text[2:]
- opcode = 'JSOP_CPP_COMMENT'
- opcode = opcode[5:].lower()
+ # Get the comment text
+ comment_text = script[match.start():match.end()]
+ if comment_text.startswith('/*'):
+ comment_text = comment_text[2:-2]
+ opcode = 'JSOP_C_COMMENT'
+ else:
+ comment_text = comment_text[2:]
+ opcode = 'JSOP_CPP_COMMENT'
+ opcode = opcode[5:].lower()
- start_offset = match.start()+1
- end_offset = match.end()
+ start_offset = match.start()+1
+ end_offset = match.end()
- # Make sure it doesn't start in a string or regexp
- if not ignore_ranges.has(start_offset):
- start_pos = node_positions.from_offset(start_offset)
- end_pos = node_positions.from_offset(end_offset)
- kwargs = {
- 'type': 'COMMENT',
- 'atom': comment_text,
- 'opcode': opcode,
- '_start_line': start_pos.line,
- '_start_col': start_pos.col,
- '_end_line': end_pos.line,
- '_end_col': end_pos.col,
- 'parent': None,
- 'kids': [],
- 'node_index': None
- }
- comment_node = _Node()
- comment_node.__dict__.update(kwargs)
- comments.append(comment_node)
- pos = match.end()
- else:
- pos = match.start()+1
+ # Make sure it doesn't start in a string or regexp
+ if not ignore_ranges.has(start_offset):
+ start_pos = node_positions.from_offset(start_offset)
+ end_pos = node_positions.from_offset(end_offset)
+ kwargs = {
+ 'type': 'COMMENT',
+ 'atom': comment_text,
+ 'opcode': opcode,
+ '_start_line': start_pos.line,
+ '_start_col': start_pos.col,
+ '_end_line': end_pos.line,
+ '_end_col': end_pos.col,
+ 'parent': None,
+ 'kids': [],
+ 'node_index': None
+ }
+ comment_node = _Node()
+ comment_node.__dict__.update(kwargs)
+ comments.append(comment_node)
+ pos = match.end()
+ else:
+ pos = match.start()+1
def parse(script, error_callback):
- def _wrapped_callback(line, col, msg):
- assert msg.startswith('JSMSG_')
- msg = msg[6:].lower()
- error_callback(line, col, msg)
+ def _wrapped_callback(line, col, msg):
+ assert msg.startswith('JSMSG_')
+ msg = msg[6:].lower()
+ error_callback(line, col, msg)
- positions = NodePositions(script)
+ positions = NodePositions(script)
- roots = []
- nodes = []
- comment_ignore_ranges = NodeRanges()
- def process(node):
- if node.kind == tok.NUMBER:
- node.atom = positions.text(node.start_pos(), node.end_pos())
- elif node.kind == tok.STRING or \
- (node.kind == tok.OBJECT and node.opcode == op.REGEXP):
- start_offset = positions.to_offset(node.start_pos())
- end_offset = positions.to_offset(node.end_pos())
- comment_ignore_ranges.add(start_offset, end_offset)
- for kid in node.kids:
- if kid:
- process(kid)
- def pop():
- nodes.pop()
+ roots = []
+ nodes = []
+ comment_ignore_ranges = NodeRanges()
+ def process(node):
+ if node.kind == tok.NUMBER:
+ node.atom = positions.text(node.start_pos(), node.end_pos())
+ elif node.kind == tok.STRING or \
+ (node.kind == tok.OBJECT and node.opcode == op.REGEXP):
+ start_offset = positions.to_offset(node.start_pos())
+ end_offset = positions.to_offset(node.end_pos())
+ comment_ignore_ranges.add(start_offset, end_offset)
+ for kid in node.kids:
+ if kid:
+ process(kid)
+ def pop():
+ nodes.pop()
- root_node = pyspidermonkey.parse(script, _Node, _wrapped_callback)
- process(root_node)
+ root_node = pyspidermonkey.parse(script, _Node, _wrapped_callback)
+ process(root_node)
- comments = _parse_comments(script, root_node, positions, comment_ignore_ranges)
- return root_node, comments
+ comments = _parse_comments(script, root_node, positions, comment_ignore_ranges)
+ return root_node, comments
def _dump_node(node, depth=0):
- print '. '*depth,
- if node is None:
- print '(none)'
- else:
- print '%s\t%s, %s' % (_tok_names[node.kind], node.start_pos(), node.end_pos())
- for node in node.kids:
- _dump_node(node, depth+1)
+ print '. '*depth,
+ if node is None:
+ print '(none)'
+ else:
+ print '%s\t%s, %s' % (_tok_names[node.kind], node.start_pos(), node.end_pos())
+ for node in node.kids:
+ _dump_node(node, depth+1)
def dump_tree(script):
- def error_callback(line, col, msg):
- print '(%i, %i): %s', (line, col, msg)
- node, comments = parse(script, error_callback)
- _dump_node(node)
+ def error_callback(line, col, msg):
+ print '(%i, %i): %s', (line, col, msg)
+ node, comments = parse(script, error_callback)
+ _dump_node(node)
class TestComments(unittest.TestCase):
- def _test(self, script, expected_comments):
- root, comments = parse(script, lambda line, col, msg: None)
- encountered_comments = [node.atom for node in comments]
- self.assertEquals(encountered_comments, list(expected_comments))
- def testSimpleComments(self):
- self._test('re = /\//g', ())
- self._test('re = /\///g', ())
- self._test('re = /\////g', ('g',))
- def testCComments(self):
- self._test('/*a*//*b*/', ('a', 'b'))
- self._test('/*a\r\na*//*b\r\nb*/', ('a\r\na', 'b\r\nb'))
- self._test('a//*b*/c', ('*b*/c',))
- self._test('a///*b*/c', ('/*b*/c',))
- self._test('a/*//*/;', ('//',))
- self._test('a/*b*/+/*c*/d', ('b', 'c'))
+ def _test(self, script, expected_comments):
+ root, comments = parse(script, lambda line, col, msg: None)
+ encountered_comments = [node.atom for node in comments]
+ self.assertEquals(encountered_comments, list(expected_comments))
+ def testSimpleComments(s...
[truncated message content] |
|
From: <mat...@us...> - 2008-03-31 21:56:59
|
Revision: 183
http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=183&view=rev
Author: matthiasmiller
Date: 2008-03-31 14:56:57 -0700 (Mon, 31 Mar 2008)
Log Message:
-----------
Fix incorrect line number in error messages.
Modified Paths:
--------------
trunk/jsl.py
trunk/pyjsl/jsparse.py
Modified: trunk/jsl.py
===================================================================
--- trunk/jsl.py 2008-03-31 21:50:09 UTC (rev 182)
+++ trunk/jsl.py 2008-03-31 21:56:57 UTC (rev 183)
@@ -56,7 +56,7 @@
def _lint(paths, conf):
def lint_error(path, line, col, errname):
_lint_results['warnings'] = _lint_results['warnings'] + 1
- print '%s(%i): %s' % (path, line, errname)
+ print '%s(%i): %s' % (path, line+1, errname)
pyjsl.lint.lint_files(paths, lint_error, conf=conf)
def _resolve_paths(path, recurse):
Modified: trunk/pyjsl/jsparse.py
===================================================================
--- trunk/pyjsl/jsparse.py 2008-03-31 21:50:09 UTC (rev 182)
+++ trunk/pyjsl/jsparse.py 2008-03-31 21:56:57 UTC (rev 183)
@@ -13,6 +13,7 @@
))
class NodePos:
+ " Represents zero-based line and column number. "
def __init__(self, line, col):
self.line = line
self.col = col
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <mat...@us...> - 2008-04-02 14:26:01
|
Revision: 186
http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=186&view=rev
Author: matthiasmiller
Date: 2008-04-02 07:25:55 -0700 (Wed, 02 Apr 2008)
Log Message:
-----------
Add comments for vim modelines.
Modified Paths:
--------------
trunk/jsl.py
trunk/pyjsl/__init__.py
trunk/pyjsl/conf.py
trunk/pyjsl/jsparse.py
trunk/pyjsl/lint.py
trunk/pyjsl/util.py
trunk/pyjsl/visitation.py
trunk/pyjsl/warnings.py
trunk/pyspidermonkey/pyspidermonkey.c
trunk/setup.py
trunk/test.py
Modified: trunk/jsl.py
===================================================================
--- trunk/jsl.py 2008-04-01 01:54:10 UTC (rev 185)
+++ trunk/jsl.py 2008-04-02 14:25:55 UTC (rev 186)
@@ -1,4 +1,5 @@
#!/usr/bin/python
+# vim: ts=4 sw=4 expandtab
import codecs
import glob
import os
Modified: trunk/pyjsl/__init__.py
===================================================================
--- trunk/pyjsl/__init__.py 2008-04-01 01:54:10 UTC (rev 185)
+++ trunk/pyjsl/__init__.py 2008-04-02 14:25:55 UTC (rev 186)
@@ -0,0 +1,2 @@
+# vim: ts=4 sw=4 expandtab
+
Modified: trunk/pyjsl/conf.py
===================================================================
--- trunk/pyjsl/conf.py 2008-04-01 01:54:10 UTC (rev 185)
+++ trunk/pyjsl/conf.py 2008-04-02 14:25:55 UTC (rev 186)
@@ -1,3 +1,4 @@
+# vim: ts=4 sw=4 expandtab
import os
import warnings
Modified: trunk/pyjsl/jsparse.py
===================================================================
--- trunk/pyjsl/jsparse.py 2008-04-01 01:54:10 UTC (rev 185)
+++ trunk/pyjsl/jsparse.py 2008-04-02 14:25:55 UTC (rev 186)
@@ -1,4 +1,5 @@
#!/usr/bin/python
+# vim: ts=4 sw=4 expandtab
""" Parses a script into nodes. """
import bisect
import re
Modified: trunk/pyjsl/lint.py
===================================================================
--- trunk/pyjsl/lint.py 2008-04-01 01:54:10 UTC (rev 185)
+++ trunk/pyjsl/lint.py 2008-04-02 14:25:55 UTC (rev 186)
@@ -1,4 +1,5 @@
#!/usr/bin/python
+# vim: ts=4 sw=4 expandtab
import os.path
import re
Modified: trunk/pyjsl/util.py
===================================================================
--- trunk/pyjsl/util.py 2008-04-01 01:54:10 UTC (rev 185)
+++ trunk/pyjsl/util.py 2008-04-02 14:25:55 UTC (rev 186)
@@ -1,3 +1,4 @@
+# vim: ts=4 sw=4 expandtab
import codecs
import os.path
Modified: trunk/pyjsl/visitation.py
===================================================================
--- trunk/pyjsl/visitation.py 2008-04-01 01:54:10 UTC (rev 185)
+++ trunk/pyjsl/visitation.py 2008-04-02 14:25:55 UTC (rev 186)
@@ -1,3 +1,4 @@
+# vim: ts=4 sw=4 expandtab
""" This is an abstract module for visiting specific nodes. This is useed to
traverse the tree to generate warnings.
"""
Modified: trunk/pyjsl/warnings.py
===================================================================
--- trunk/pyjsl/warnings.py 2008-04-01 01:54:10 UTC (rev 185)
+++ trunk/pyjsl/warnings.py 2008-04-02 14:25:55 UTC (rev 186)
@@ -1,3 +1,4 @@
+# vim: ts=4 sw=4 expandtab
""" This module contains all the warnings. To add a new warning, define a
class. Its name should be in lowercase and words should be separated by
underscores. Its docstring should be the warning message.
Modified: trunk/pyspidermonkey/pyspidermonkey.c
===================================================================
--- trunk/pyspidermonkey/pyspidermonkey.c 2008-04-01 01:54:10 UTC (rev 185)
+++ trunk/pyspidermonkey/pyspidermonkey.c 2008-04-02 14:25:55 UTC (rev 186)
@@ -1,4 +1,4 @@
-
+/* vim: ts=4 sw=4 expandtab */
#include <Python.h>
#include <js_operating_system.h>
Modified: trunk/setup.py
===================================================================
--- trunk/setup.py 2008-04-01 01:54:10 UTC (rev 185)
+++ trunk/setup.py 2008-04-02 14:25:55 UTC (rev 186)
@@ -1,4 +1,5 @@
#!/usr/bin/python
+# vim: ts=4 sw=4 expandtab
from distutils.core import setup, Extension
import os
Modified: trunk/test.py
===================================================================
--- trunk/test.py 2008-04-01 01:54:10 UTC (rev 185)
+++ trunk/test.py 2008-04-02 14:25:55 UTC (rev 186)
@@ -1,3 +1,4 @@
+# vim: ts=4 sw=4 expandtab
import re
import pyjsl.conf
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <mat...@us...> - 2008-04-03 20:39:36
|
Revision: 189
http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=189&view=rev
Author: matthiasmiller
Date: 2008-04-03 13:39:28 -0700 (Thu, 03 Apr 2008)
Log Message:
-----------
Add warning against useless quotes.
Modified Paths:
--------------
trunk/pyjsl/warnings.py
Added Paths:
-----------
trunk/tests/warnings/useless_quotes.js
Modified: trunk/pyjsl/warnings.py
===================================================================
--- trunk/pyjsl/warnings.py 2008-04-02 19:11:36 UTC (rev 188)
+++ trunk/pyjsl/warnings.py 2008-04-03 20:39:28 UTC (rev 189)
@@ -434,6 +434,13 @@
if node.end_comma:
return node
+class useless_quotes:
+ 'the quotation marks are unnecessary'
+ @lookat(tok.STRING)
+ def _lint(self, node):
+ if node.node_index == 0 and node.parent.kind == tok.COLON:
+ return node
+
class mismatch_ctrl_comments:
'mismatched control comment; "ignore" and "end" control comments must have a one-to-one correspondence'
pass
Added: trunk/tests/warnings/useless_quotes.js
===================================================================
--- trunk/tests/warnings/useless_quotes.js (rev 0)
+++ trunk/tests/warnings/useless_quotes.js 2008-04-03 20:39:28 UTC (rev 189)
@@ -0,0 +1,8 @@
+function useless_quotes() {
+ var o = {
+ 'key': 1 /*warning:useless_quotes*/
+ };
+ o = {
+ key: '1'
+ };
+}
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <mat...@us...> - 2008-05-06 15:59:49
|
Revision: 200
http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=200&view=rev
Author: matthiasmiller
Date: 2008-05-06 08:59:24 -0700 (Tue, 06 May 2008)
Log Message:
-----------
Move NodePos into the C module.
Modified Paths:
--------------
trunk/pyjsl/jsparse.py
trunk/pyspidermonkey/pyspidermonkey.c
trunk/setup.py
Added Paths:
-----------
trunk/pyspidermonkey/nodepos.c
trunk/pyspidermonkey/nodepos.h
Modified: trunk/pyjsl/jsparse.py
===================================================================
--- trunk/pyjsl/jsparse.py 2008-04-26 05:14:50 UTC (rev 199)
+++ trunk/pyjsl/jsparse.py 2008-05-06 15:59:24 UTC (rev 200)
@@ -13,23 +13,7 @@
['tok.%s' % prop for prop in dir(tok)]
))
-class NodePos:
- " Represents zero-based line and column number. "
- def __init__(self, line, col):
- self.line = line
- self.col = col
- def __cmp__(self, other):
- if self.line < other.line:
- return -1
- if self.line > other.line:
- return 1
- if self.col < other.col:
- return -1
- if self.col > other.col:
- return 1
- return 0
- def __str__(self):
- return '(line %i, col %i)' % (self.line+1, self.col+1)
+NodePos = pyspidermonkey.NodePos
class NodePositions:
" Given a string, allows [x] lookups for NodePos line and column numbers."
Added: trunk/pyspidermonkey/nodepos.c
===================================================================
--- trunk/pyspidermonkey/nodepos.c (rev 0)
+++ trunk/pyspidermonkey/nodepos.c 2008-05-06 15:59:24 UTC (rev 200)
@@ -0,0 +1,117 @@
+/* vim: ts=4 sw=4 expandtab
+ */
+#include <Python.h>
+#include "structmember.h"
+
+#include "nodepos.h"
+
+typedef struct {
+ PyObject_HEAD
+ int line;
+ int col;
+} NodePosObject;
+
+static PyObject*
+NodePos_new(PyTypeObject* type, PyObject* args, PyObject* kwds)
+{
+ NodePosObject* self;
+
+ self = (NodePosObject*)type->tp_alloc(type, 0);
+ if (self == NULL)
+ return NULL;
+
+ self->line = -1;
+ self->col = -1;
+
+ return (PyObject*)self;
+}
+
+static int
+NodePos_init(NodePosObject* self, PyObject* args, PyObject* kwds)
+{
+ static char* kwlist[] = {"line", "col", NULL};
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "ii", kwlist, &self->line, &self->col))
+ return -1;
+
+ return 0;
+}
+
+static PyObject*
+NodePos_str(NodePosObject* self)
+{
+ return PyString_FromFormat("(line %i, col %i)", self->line+1, self->col+1);
+}
+
+static int
+NodePos_compare(NodePosObject* left, NodePosObject* right)
+{
+ if (left->line < right->line)
+ return -1;
+ if (left->line > right->line)
+ return 1;
+ if (left->col < right->col)
+ return -1;
+ if (left->col > right->col)
+ return 1;
+ return 0;
+}
+
+static PyMemberDef
+NodePos_members[] = {
+ {"line", T_INT, offsetof(NodePosObject, line), 0, "zero-based line number"},
+ {"col", T_INT, offsetof(NodePosObject, col), 0, "zero-based column number"},
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject NodePosType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "pyspidermonkey.NodePos", /*tp_name*/
+ sizeof(NodePosObject), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ 0, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ (cmpfunc)NodePos_compare, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ (reprfunc)NodePos_str, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "Represents zero-based line and column number.", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ NodePos_members, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)NodePos_init, /* tp_init */
+ 0, /* tp_alloc */
+ NodePos_new, /* tp_new */
+};
+
+void
+RegisterNodePosType(PyObject* module)
+{
+ if (PyType_Ready(&NodePosType) < 0)
+ return;
+
+ Py_INCREF(&NodePosType);
+ PyModule_AddObject(module, "NodePos", (PyObject*)&NodePosType);
+}
+
Property changes on: trunk/pyspidermonkey/nodepos.c
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/pyspidermonkey/nodepos.h
===================================================================
--- trunk/pyspidermonkey/nodepos.h (rev 0)
+++ trunk/pyspidermonkey/nodepos.h 2008-05-06 15:59:24 UTC (rev 200)
@@ -0,0 +1,10 @@
+/* vim: ts=4 sw=4 expandtab
+ */
+#ifndef NODEPOS_H
+#define NODEPOS_H
+
+void
+RegisterNodePosType(PyObject* module);
+
+#endif
+
Property changes on: trunk/pyspidermonkey/nodepos.h
___________________________________________________________________
Name: svn:eol-style
+ native
Modified: trunk/pyspidermonkey/pyspidermonkey.c
===================================================================
--- trunk/pyspidermonkey/pyspidermonkey.c 2008-04-26 05:14:50 UTC (rev 199)
+++ trunk/pyspidermonkey/pyspidermonkey.c 2008-05-06 15:59:24 UTC (rev 200)
@@ -15,6 +15,7 @@
#include <jsscope.h>
#include <jsstr.h>
+#include "nodepos.h"
#define ARRAY_COUNT(a) (sizeof(a) / sizeof(a[0]))
@@ -107,6 +108,8 @@
if (PyObject_SetAttrString(op, opcode, PyLong_FromLong(OPCODE_TO_NUM(i))) == -1)
return;
}
+
+ RegisterNodePosType(module);
}
PyMODINIT_FUNC
Modified: trunk/setup.py
===================================================================
--- trunk/setup.py 2008-04-26 05:14:50 UTC (rev 199)
+++ trunk/setup.py 2008-05-06 15:59:24 UTC (rev 200)
@@ -22,7 +22,10 @@
include_dirs = ['spidermonkey/src', 'build/spidermonkey'],
library_dirs = ['build/spidermonkey'],
libraries = [library],
- sources = ['pyspidermonkey/pyspidermonkey.c']
+ sources = [
+ 'pyspidermonkey/pyspidermonkey.c',
+ 'pyspidermonkey/nodepos.c'
+ ]
)
args = {}
args.update(
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <mat...@us...> - 2008-05-14 16:04:17
|
Revision: 201
http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=201&view=rev
Author: matthiasmiller
Date: 2008-05-14 09:04:10 -0700 (Wed, 14 May 2008)
Log Message:
-----------
fix reporting of syntax errors
Modified Paths:
--------------
trunk/pyjsl/jsparse.py
trunk/pyjsl/lint.py
trunk/pyspidermonkey/pyspidermonkey.c
Modified: trunk/pyjsl/jsparse.py
===================================================================
--- trunk/pyjsl/jsparse.py 2008-05-06 15:59:24 UTC (rev 200)
+++ trunk/pyjsl/jsparse.py 2008-05-14 16:04:10 UTC (rev 201)
@@ -199,7 +199,8 @@
nodes.pop()
root_node = pyspidermonkey.parse(script, _Node, _wrapped_callback)
- process(root_node)
+ if root_node:
+ process(root_node)
comments = _parse_comments(script, root_node, positions, comment_ignore_ranges)
return root_node, comments
Modified: trunk/pyjsl/lint.py
===================================================================
--- trunk/pyjsl/lint.py 2008-05-06 15:59:24 UTC (rev 200)
+++ trunk/pyjsl/lint.py 2008-05-14 16:04:10 UTC (rev 201)
@@ -75,7 +75,8 @@
class Scope:
def __init__(self, node):
- self._is_with_scope = node.kind == tok.WITH
+ """ node may be None """
+ self._is_with_scope = node and node.kind == tok.WITH
self._parent = None
self._kids = []
self._identifiers = {}
@@ -116,6 +117,9 @@
identifiers.append(node)
return identifiers
def find_scope(self, node):
+ if not self._node:
+ return None
+
for kid in self._kids:
scope = kid.find_scope(node)
if scope:
@@ -233,7 +237,8 @@
visitation.make_visitors(visitors, [_get_scope_checks(scope, report)])
# kickoff!
- _lint_node(root, visitors)
+ if root:
+ _lint_node(root, visitors)
# Process imports by copying global declarations into the universal scope.
imports |= set(conf['declarations'])
Modified: trunk/pyspidermonkey/pyspidermonkey.c
===================================================================
--- trunk/pyspidermonkey/pyspidermonkey.c 2008-05-06 15:59:24 UTC (rev 200)
+++ trunk/pyspidermonkey/pyspidermonkey.c 2008-05-14 16:04:10 UTC (rev 201)
@@ -423,8 +423,10 @@
m.jsnode = js_ParseTokenStream(m.context, m.global, m.token_stream);
if (!m.jsnode) {
- error = "parse error in file";
- goto cleanup;
+ if (!JS_ReportPendingException(m.context)) {
+ error = "parse error in file";
+ goto cleanup;
+ }
}
m.pynode = jsnode_to_pynode(m.context, m.jsnode);
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|