Thread: [javascriptlint-commit] SF.net SVN: javascriptlint:[301] trunk (Page 3)
Status: Beta
Brought to you by:
matthiasmiller
From: <mat...@us...> - 2010-10-03 20:18:17
|
Revision: 301 http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=301&view=rev Author: matthiasmiller Date: 2010-10-03 20:18:10 +0000 (Sun, 03 Oct 2010) Log Message: ----------- Add support for content types and differing JavaScript versions. Modified Paths: -------------- trunk/javascriptlint/conf.py trunk/javascriptlint/jsparse.py trunk/javascriptlint/lint.py trunk/javascriptlint/pyspidermonkey/pyspidermonkey.c trunk/javascriptlint/util.py trunk/javascriptlint/warnings.py Added Paths: ----------- trunk/tests/control_comments/conf-version-2.js trunk/tests/control_comments/conf-version.js trunk/tests/html/e4x.html trunk/tests/html/unsupported_version.html Modified: trunk/javascriptlint/conf.py =================================================================== --- trunk/javascriptlint/conf.py 2010-04-23 21:09:57 UTC (rev 300) +++ trunk/javascriptlint/conf.py 2010-10-03 20:18:10 UTC (rev 301) @@ -2,6 +2,7 @@ import os import unittest +import util import warnings def _getwarningsconf(): @@ -80,6 +81,11 @@ #+define document +### JavaScript Version +# To change the default JavaScript version: +#+default-type text/javascript;version=1.5 +#+default-type text/javascript;e4x=1 + ### Files # Specify which files to lint # Use "+recurse" to enable recursion (disabled by default). @@ -144,6 +150,17 @@ parm = os.path.join(dir, parm) self.value.append((self._recurse.value, parm)) +class JSVersionSetting(Setting): + wants_parm = True + value = util.JSVersion.default() + def load(self, enabled, parm): + if not enabled: + raise ConfError, 'Expected +.' + + self.value = util.JSVersion.fromtype(parm) + if not self.value: + raise ConfError, 'Invalid JavaScript version: %s' % parm + class Conf: def __init__(self): recurse = BooleanSetting(False) @@ -157,6 +174,7 @@ 'define': DeclareSetting(), 'context': BooleanSetting(True), 'process': ProcessSetting(recurse), + 'default-version': JSVersionSetting(), # SpiderMonkey warnings 'no_return_value': BooleanSetting(True), 'equal_as_assign': BooleanSetting(True), Modified: trunk/javascriptlint/jsparse.py =================================================================== --- trunk/javascriptlint/jsparse.py 2010-04-23 21:09:57 UTC (rev 300) +++ trunk/javascriptlint/jsparse.py 2010-10-03 20:18:10 UTC (rev 301) @@ -7,6 +7,7 @@ import spidermonkey from spidermonkey import tok, op +from util import JSVersion _tok_names = dict(zip( [getattr(tok, prop) for prop in dir(tok)], @@ -145,6 +146,11 @@ return True +def isvalidversion(jsversion): + if jsversion is None: + return True + return spidermonkey.is_valid_version(jsversion.version) + def findpossiblecomments(script, node_positions): pos = 0 single_line_re = r"//[^\r\n]*" @@ -193,7 +199,7 @@ # this one was within a string or a regexp. pos = match.start()+1 -def parse(script, error_callback, startpos=None): +def parse(script, jsversion, error_callback, startpos=None): """ All node positions will be relative to startpos. This allows scripts to be embedded in a file (for example, HTML). """ @@ -203,7 +209,10 @@ error_callback(line, col, msg) startpos = startpos or NodePos(0,0) - return spidermonkey.parse(script, _Node, _wrapped_callback, + jsversion = jsversion or JSVersion.default() + assert isvalidversion(jsversion) + return spidermonkey.parse(script, jsversion.version, jsversion.e4x, + _Node, _wrapped_callback, startpos.line, startpos.col) def filtercomments(possible_comments, node_positions, root_node): @@ -237,8 +246,10 @@ possible_comments = findpossiblecomments(script, node_positions) return filtercomments(possible_comments, node_positions, root_node) -def is_compilable_unit(script): - return spidermonkey.is_compilable_unit(script) +def is_compilable_unit(script, jsversion): + jsversion = jsversion or JSVersion.default() + assert isvalidversion(jsversion) + return spidermonkey.is_compilable_unit(script, jsversion.version, jsversion.e4x) def _dump_node(node, depth=0): if node is None: @@ -263,12 +274,12 @@ def dump_tree(script): def error_callback(line, col, msg): print '(%i, %i): %s', (line, col, msg) - node = parse(script, error_callback) + node = parse(script, None, error_callback) _dump_node(node) class TestComments(unittest.TestCase): def _test(self, script, expected_comments): - root = parse(script, lambda line, col, msg: None) + root = parse(script, None, lambda line, col, msg: None) comments = findcomments(script, root) encountered_comments = [node.atom for node in comments] self.assertEquals(encountered_comments, list(expected_comments)) @@ -365,10 +376,11 @@ ('re = /.*', False), ('{ // missing curly', False) ) - for text, result in tests: - self.assertEquals(is_compilable_unit(text), result) + for text, expected in tests: + encountered = is_compilable_unit(text, JSVersion.default()) + self.assertEquals(encountered, expected) # NOTE: This seems like a bug. - self.assert_(is_compilable_unit("/* test")) + self.assert_(is_compilable_unit("/* test", JSVersion.default())) class TestLineOffset(unittest.TestCase): def testErrorPos(self): @@ -376,7 +388,7 @@ errors = [] def onerror(line, col, msg): errors.append((line, col, msg)) - parse(script, onerror, startpos) + parse(script, None, onerror, startpos) self.assertEquals(len(errors), 1) return errors[0] self.assertEquals(geterror(' ?', None), (0, 1, 'syntax_error')) @@ -385,7 +397,7 @@ self.assertEquals(geterror('\n ?', NodePos(1,1)), (2, 1, 'syntax_error')) def testNodePos(self): def getnodepos(script, startpos): - root = parse(script, None, startpos) + root = parse(script, None, None, startpos) self.assertEquals(root.kind, tok.LC) var, = root.kids self.assertEquals(var.kind, tok.VAR) @@ -398,7 +410,7 @@ self.assertEquals(getnodepos('\n\n var x;', NodePos(3,4)), NodePos(5,1)) def testComments(self): def testcomment(comment, startpos, expectedpos): - root = parse(comment, None, startpos) + root = parse(comment, None, None, startpos) comment, = findcomments(comment, root, startpos) self.assertEquals(comment.start_pos(), expectedpos) for comment in ('/*comment*/', '//comment'): Modified: trunk/javascriptlint/lint.py =================================================================== --- trunk/javascriptlint/lint.py 2010-04-23 21:09:57 UTC (rev 300) +++ trunk/javascriptlint/lint.py 2010-10-03 20:18:10 UTC (rev 301) @@ -63,6 +63,7 @@ 'pass': (False), 'declare': (True), 'unused': (True), + 'content-type': (True), } if control_comment.lower() in control_comments: keyword = control_comment.lower() @@ -240,19 +241,21 @@ if global_: return global_ -def _findhtmlscripts(contents): +def _findhtmlscripts(contents, default_version): starttag = None nodepos = jsparse.NodePositions(contents) for tag in htmlparse.findscripttags(contents): if tag['type'] == 'start': # Ignore nested start tags. if not starttag: - starttag = tag + jsversion = util.JSVersion.fromattr(tag['attr'], default_version) + starttag = dict(tag, jsversion=jsversion) src = tag['attr'].get('src') if src: yield { 'type': 'external', - 'src': src + 'jsversion': jsversion, + 'src': src, } elif tag['type'] == 'end': if not starttag: @@ -268,25 +271,27 @@ endoffset = nodepos.to_offset(endpos) script = contents[startoffset:endoffset] - if jsparse.is_compilable_unit(script): - starttag = None + if not jsparse.isvalidversion(starttag['jsversion']) or \ + jsparse.is_compilable_unit(script, starttag['jsversion']): if script.strip(): yield { 'type': 'inline', + 'jsversion': starttag['jsversion'], 'pos': startpos, 'contents': script, } + starttag = None else: assert False, 'Invalid internal tag type %s' % tag['type'] def lint_files(paths, lint_error, conf=conf.Conf(), printpaths=True): - def lint_file(path, kind): - def import_script(import_path): + def lint_file(path, kind, jsversion): + def import_script(import_path, jsversion): # The user can specify paths using backslashes (such as when # linting Windows scripts on a posix environment. import_path = import_path.replace('\\', os.sep) import_path = os.path.join(os.path.dirname(path), import_path) - return lint_file(import_path, 'js') + return lint_file(import_path, 'js', jsversion) def _lint_error(*args): return lint_error(normpath, *args) @@ -300,14 +305,20 @@ script_parts = [] if kind == 'js': - script_parts.append((None, contents)) + script_parts.append((None, jsversion or conf['default-version'], contents)) elif kind == 'html': - for script in _findhtmlscripts(contents): + assert jsversion is None + for script in _findhtmlscripts(contents, conf['default-version']): + # TODO: Warn about foreign languages. + if not script['jsversion']: + continue + if script['type'] == 'external': - other = import_script(script['src']) + other = import_script(script['src'], script['jsversion']) lint_cache[normpath].importscript(other) elif script['type'] == 'inline': - script_parts.append((script['pos'], script['contents'])) + script_parts.append((script['pos'], script['jsversion'], + script['contents'])) else: assert False, 'Invalid internal script type %s' % \ script['type'] @@ -321,12 +332,12 @@ for path in paths: ext = os.path.splitext(path)[1] if ext.lower() in ['.htm', '.html']: - lint_file(path, 'html') + lint_file(path, 'html', None) else: - lint_file(path, 'js') + lint_file(path, 'js', None) -def _lint_script_part(scriptpos, script, script_cache, conf, ignores, - report_native, report_lint, import_callback): +def _lint_script_part(scriptpos, jsversion, script, script_cache, conf, + ignores, report_native, report_lint, import_callback): def parse_error(row, col, msg): if not msg in ('anon_no_return_value', 'no_return_value', 'redeclared_var', 'var_hides_arg'): @@ -375,7 +386,28 @@ node_positions = jsparse.NodePositions(script, scriptpos) possible_comments = jsparse.findpossiblecomments(script, node_positions) - root = jsparse.parse(script, parse_error, scriptpos) + # Check control comments for the correct version. It may be this comment + # isn't a valid comment (for example, it might be inside a string literal) + # After parsing, validate that it's legitimate. + jsversionnode = None + for comment in possible_comments: + cc = _parse_control_comment(comment) + if cc: + node, keyword, parms = cc + if keyword == 'content-type': + ccversion = util.JSVersion.fromtype(parms) + if ccversion: + jsversion = ccversion + jsversionnode = node + else: + report(node, 'unsupported_version', version=parms) + + if not jsparse.isvalidversion(jsversion): + report_lint(jsversionnode, 'unsupported_version', scriptpos, + version=jsversion.version) + return + + root = jsparse.parse(script, jsversion, parse_error, scriptpos) if not root: # Report errors and quit. for pos, msg in parse_errors: @@ -383,6 +415,11 @@ return comments = jsparse.filtercomments(possible_comments, node_positions, root) + + if jsversionnode is not None and jsversionnode not in comments: + # TODO + report(jsversionnode, 'incorrect_version') + start_ignore = None for comment in comments: cc = _parse_control_comment(comment) @@ -461,7 +498,7 @@ # Process imports by copying global declarations into the universal scope. for path in import_paths: - script_cache.importscript(import_callback(path)) + script_cache.importscript(import_callback(path, jsversion)) for name, node in declares: declare_scope = script_cache.scope.find_scope(node) @@ -494,9 +531,9 @@ return lint_error(pos.line, pos.col, errname, errdesc) - for scriptpos, script in script_parts: + for scriptpos, jsversion, script in script_parts: ignores = [] - _lint_script_part(scriptpos, script, script_cache, conf, ignores, + _lint_script_part(scriptpos, jsversion, script, script_cache, conf, ignores, report_native, report_lint, import_callback) scope = script_cache.scope @@ -620,8 +657,55 @@ </html> """ scripts = [(x.get('src'), x.get('contents')) - for x in _findhtmlscripts(html)] + for x in _findhtmlscripts(html, util.JSVersion.default())] self.assertEquals(scripts, [ ('test.js', None), (None, "<!--\nvar s = '<script></script>';\n-->") ]) + def testJSVersion(self): + def parsetag(starttag, default_version=None): + script, = _findhtmlscripts(starttag + '/**/</script>', \ + default_version) + return script + + script = parsetag('<script>') + self.assertEquals(script['jsversion'], None) + + script = parsetag('<script language="vbscript">') + self.assertEquals(script['jsversion'], None) + + script = parsetag('<script type="text/javascript">') + self.assertEquals(script['jsversion'], util.JSVersion.default()) + + script = parsetag('<SCRIPT TYPE="TEXT/JAVASCRIPT">') + self.assertEquals(script['jsversion'], util.JSVersion.default()) + + script = parsetag('<script type="text/javascript; version = 1.6 ">') + self.assertEquals(script['jsversion'], util.JSVersion('1.6', False)) + + script = parsetag('<script type="text/javascript; version = 1.6 ">') + self.assertEquals(script['jsversion'], util.JSVersion('1.6', False)) + + script = parsetag('<SCRIPT TYPE="TEXT/JAVASCRIPT; e4x = 1 ">') + self.assertEquals(script['jsversion'], util.JSVersion('default', True)) + + script = parsetag('<script type="" language="livescript">') + self.assertEquals(script['jsversion'], util.JSVersion.default()) + + script = parsetag('<script type="" language="MOCHA">') + self.assertEquals(script['jsversion'], util.JSVersion.default()) + + script = parsetag('<script type="" language="JavaScript1.2">') + self.assertEquals(script['jsversion'], util.JSVersion('1.2', False)) + + script = parsetag('<script type="text/javascript;version=1.2" language="javascript1.4">') + self.assertEquals(script['jsversion'], util.JSVersion('1.2', False)) + + # Test setting the default version. + script = parsetag('<script>', util.JSVersion('1.2', False)) + self.assertEquals(script['jsversion'], util.JSVersion('1.2', False)) + + script = parsetag('<script type="" language="mocha">', + util.JSVersion('1.2', False)) + self.assertEquals(script['jsversion'], util.JSVersion.default()) + Modified: trunk/javascriptlint/pyspidermonkey/pyspidermonkey.c =================================================================== --- trunk/javascriptlint/pyspidermonkey/pyspidermonkey.c 2010-04-23 21:09:57 UTC (rev 300) +++ trunk/javascriptlint/pyspidermonkey/pyspidermonkey.c 2010-10-03 20:18:10 UTC (rev 301) @@ -70,6 +70,9 @@ static PyObject* is_compilable_unit(PyObject *self, PyObject *args); +static PyObject* +is_valid_version(PyObject *self, PyObject *args); + static PyMethodDef module_methods[] = { {"parse", module_parse, METH_VARARGS, "Parses \"script\" and returns a tree of \"node_class\"."}, @@ -77,6 +80,9 @@ {"is_compilable_unit", is_compilable_unit, METH_VARARGS, "Returns True if \"script\" is a compilable unit."}, + {"is_valid_version", is_valid_version, METH_VARARGS, + "Returns True if \"strversion\" is a valid version."}, + {NULL, NULL, 0, NULL} /* Sentinel */ }; @@ -398,13 +404,22 @@ /* Returns NULL on success. Otherwise, it returns an error. * If the error is blank, an exception will be set. */ -static const char* create_jscontext(void* ctx_data, +static const char* create_jscontext(const char* strversion, PyObject* is_e4x, + void* ctx_data, JSRuntime** runtime, JSContext** context, JSObject** global) { + JSVersion jsversion; + + jsversion = JS_StringToVersion(strversion); + if (jsversion == JSVERSION_UNKNOWN) { + PyErr_SetString(PyExc_ValueError, "\"version\" is invalid"); + return ""; + } + *runtime = JS_NewRuntime(8L * 1024L * 1024L); if (*runtime == NULL) - return"cannot create runtime"; + return "cannot create runtime"; *context = JS_NewContext(*runtime, 8192); if (*context == NULL) @@ -413,6 +428,11 @@ JS_SetErrorReporter(*context, error_reporter); JS_SetContextPrivate(*context, ctx_data); JS_ToggleOptions(*context, JSOPTION_STRICT); + if (is_e4x == Py_True) + JS_ToggleOptions(*context, JSOPTION_XML); + else if (is_e4x != Py_False) + return "e4x is not a boolean"; + JS_SetVersion(*context, jsversion); *global = JS_NewObject(*context, NULL, NULL, NULL); if (*global == NULL) @@ -430,6 +450,8 @@ struct { char* scriptbuf; int scriptbuflen; + const char* jsversion; + PyObject* is_e4x; PyObject* pynode; JSRuntime* runtime; @@ -446,8 +468,9 @@ error = "encountered an unknown error"; /* validate arguments */ - if (!PyArg_ParseTuple(args, "es#OOll", "utf16", &m.scriptbuf, - &m.scriptbuflen, &m.ctx_data.node_class, &m.ctx_data.error_callback, + if (!PyArg_ParseTuple(args, "es#sO!OOll", "utf16", &m.scriptbuf, + &m.scriptbuflen, &m.jsversion, &PyBool_Type, &m.is_e4x, + &m.ctx_data.node_class, &m.ctx_data.error_callback, &m.ctx_data.first_lineno, &m.ctx_data.first_index)) { return NULL; } @@ -462,7 +485,8 @@ return NULL; } - error = create_jscontext(&m.ctx_data, &m.runtime, &m.context, &m.global); + error = create_jscontext(m.jsversion, m.is_e4x, &m.ctx_data, + &m.runtime, &m.context, &m.global); if (error) goto cleanup; @@ -511,6 +535,8 @@ struct { char* scriptbuf; int scriptbuflen; + const char* jsversion; + PyObject* is_e4x; JSRuntime* runtime; JSContext* context; JSObject* global; @@ -521,10 +547,13 @@ memset(&m, 0, sizeof(m)); error = "encountered an unknown error"; - if (!PyArg_ParseTuple(args, "es#", "utf16", &m.scriptbuf, &m.scriptbuflen)) + if (!PyArg_ParseTuple(args, "es#sO!", "utf16", &m.scriptbuf, + &m.scriptbuflen, &m.jsversion, &PyBool_Type, &m.is_e4x)) { return NULL; + } - error = create_jscontext(NULL, &m.runtime, &m.context, &m.global); + error = create_jscontext(m.jsversion, m.is_e4x, NULL, + &m.runtime, &m.context, &m.global); if (error) goto cleanup; @@ -546,13 +575,22 @@ 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; - } + if (m.is_compilable) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; } +static PyObject* +is_valid_version(PyObject *self, PyObject *args) { + const char* strversion = NULL; + + if (!PyArg_ParseTuple(args, "s", &strversion)) + return NULL; + + if (JS_StringToVersion(strversion) != JSVERSION_UNKNOWN) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; +} + Modified: trunk/javascriptlint/util.py =================================================================== --- trunk/javascriptlint/util.py 2010-04-23 21:09:57 UTC (rev 300) +++ trunk/javascriptlint/util.py 2010-10-03 20:18:10 UTC (rev 301) @@ -1,4 +1,5 @@ # vim: ts=4 sw=4 expandtab +import cgi import codecs import os.path import re @@ -6,6 +7,57 @@ _identifier = re.compile('^[A-Za-z_$][A-Za-z0-9_$]*$') +_contenttypes = ( + 'text/javascript', + 'text/ecmascript', + 'application/javascript', + 'application/ecmascript', + 'application/x-javascript', +) + +class JSVersion: + def __init__(self, jsversion, is_e4x): + self.version = jsversion + self.e4x = is_e4x + + def __eq__(self, other): + return self.version == other.version and \ + self.e4x == other.e4x + + @classmethod + def default(klass): + return klass('default', False) + + @classmethod + def fromattr(klass, attr, default_version=None): + if attr.get('type'): + return klass.fromtype(attr['type']) + if attr.get('language'): + return klass.fromlanguage(attr['language']) + return default_version + + @classmethod + def fromtype(klass, type_): + typestr, typeparms = cgi.parse_header(type_) + if typestr.lower() in _contenttypes: + jsversion = typeparms.get('version', 'default') + is_e4x = typeparms.get('e4x') == '1' + return klass(jsversion, is_e4x) + return None + + @classmethod + def fromlanguage(klass, language): + if language.lower() in ('javascript', 'livescript', 'mocha'): + return klass.default() + + # Simplistic parsing of javascript/x.y + if language.lower().startswith('javascript'): + language = language[len('javascript'):] + if language.replace('.', '').isdigit(): + return klass(language, False) + + return None + def isidentifier(text): return _identifier.match(text) @@ -91,7 +143,6 @@ self.assertEquals(format_error('encode:__ERROR_MSGENC__', r'c:\my\file', 1, 2, 'name', r'a\b'), r'a\\b') - if __name__ == '__main__': unittest.main() Modified: trunk/javascriptlint/warnings.py =================================================================== --- trunk/javascriptlint/warnings.py 2010-04-23 21:09:57 UTC (rev 300) +++ trunk/javascriptlint/warnings.py 2010-10-03 20:18:10 UTC (rev 301) @@ -94,7 +94,9 @@ 'invalid_pass': 'unexpected "pass" control comment', 'want_assign_or_call': 'expected an assignment or function call', 'no_return_value': 'function {name} does not always return a value', - 'anon_no_return_value': 'anonymous function does not always return value' + 'anon_no_return_value': 'anonymous function does not always return value', + 'unsupported_version': 'JavaScript {version} is not supported', + 'incorrect_version': 'Expected /*jsl:content-type*/ control comment. The script was parsed with the wrong version.', } def format_error(errname, **errargs): @@ -511,6 +513,8 @@ # NOTE: Don't handle comma-separated statements. if child.kind in (tok.ASSIGN, tok.INC, tok.DEC, tok.DELETE, tok.COMMA): return + if child.kind == tok.YIELD: + return # Ignore function calls. if child.kind == tok.LP and child.opcode == op.CALL: return Added: trunk/tests/control_comments/conf-version-2.js =================================================================== --- trunk/tests/control_comments/conf-version-2.js (rev 0) +++ trunk/tests/control_comments/conf-version-2.js 2010-10-03 20:18:10 UTC (rev 301) @@ -0,0 +1,7 @@ +/*conf:+default-version text/javascript;version=1.7*/ +/*jsl:content-type text/javascript;version=1.6*/ + +/* Make sure that the control comment overrides the config file. */ +function default_version() { + yield true; /*warning:semi_before_stmnt*/ +} Added: trunk/tests/control_comments/conf-version.js =================================================================== --- trunk/tests/control_comments/conf-version.js (rev 0) +++ trunk/tests/control_comments/conf-version.js 2010-10-03 20:18:10 UTC (rev 301) @@ -0,0 +1,4 @@ +/*conf:+default-version text/javascript;version=1.7*/ +function default_version() { + yield true; +} Added: trunk/tests/html/e4x.html =================================================================== --- trunk/tests/html/e4x.html (rev 0) +++ trunk/tests/html/e4x.html 2010-10-03 20:18:10 UTC (rev 301) @@ -0,0 +1,28 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN"> +<html> +<head> + <title>JavaScript Lint Test Page</title> + <!-- If e4x is disabled, HTML commments are treated as + comment-till-end-of-line. --> + <script type="text/javascript"> + // e4x is disabled by default, so HTML comments are single-line only. + var comment_a = <!-- + ? /*warning:syntax_error*/ + -->; + </script> + <script type="text/javascript;e4x=1"> + // Explicitly enable e4x. + var comment_c = <!-- + ? + -->; + </script> + <script type="text/javascript;version=1.6;e4x=0"> + // e4x is always enabled in 1.6+ + var comment_b = <!-- + ? + -->; + </script> +</head> +<body> +</body> +</html> Added: trunk/tests/html/unsupported_version.html =================================================================== --- trunk/tests/html/unsupported_version.html (rev 0) +++ trunk/tests/html/unsupported_version.html 2010-10-03 20:18:10 UTC (rev 301) @@ -0,0 +1,19 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN"> +<html> +<head> + <title>JavaScript Lint Test Page</title> + <script type="text/javascript;version=0.1"> /*warning:unsupported_version*/ + function x() { + return 10; + } + </script> + <script language="javascript10.0"> /*warning:unsupported_version*/ + function x() { + return 10; + } + </script> +</head> +<body> +</body> +</html> + This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2013-09-28 03:06:23
|
Revision: 304 http://sourceforge.net/p/javascriptlint/code/304 Author: matthiasmiller Date: 2013-09-28 03:06:19 +0000 (Sat, 28 Sep 2013) Log Message: ----------- Replace SpiderMonkey with a JavaScript parser written purely in JavaScript. Modified Paths: -------------- trunk/DEVELOPMENT trunk/INSTALL trunk/javascriptlint/jsparse.py trunk/javascriptlint/lint.py trunk/javascriptlint/warnings.py trunk/setup.py trunk/test.py trunk/tests/control_comments/conf-version.js trunk/tests/html/e4x.html trunk/tests/html/script_tag_in_js_comment.html trunk/tests/warnings/identifier_hides_another.js trunk/tests/warnings/spidermonkey/bad_backref.js trunk/tests/warnings/spidermonkey/invalid_backref.js trunk/tests/warnings/want_assign_or_call.js Added Paths: ----------- trunk/jsengine/ trunk/jsengine/__init__.py trunk/jsengine/parser/ trunk/jsengine/parser/__init__.py trunk/jsengine/parser/_constants_kind.py trunk/jsengine/parser/_constants_op.py trunk/jsengine/structs.py trunk/jsengine/tokenizer/ trunk/jsengine/tokenizer/__init__.py Removed Paths: ------------- trunk/Makefile.SpiderMonkey trunk/javascriptlint/pyspidermonkey/ trunk/javascriptlint/pyspidermonkey_/ trunk/javascriptlint/spidermonkey.py trunk/spidermonkey/ trunk/tests/warnings/spidermonkey/deprecated_usage.js Modified: trunk/DEVELOPMENT =================================================================== --- trunk/DEVELOPMENT 2011-12-02 18:49:10 UTC (rev 303) +++ trunk/DEVELOPMENT 2013-09-28 03:06:19 UTC (rev 304) @@ -15,17 +15,3 @@ > add test for syntax error > consider reimplementing abiguous_newline - -** 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 - Modified: trunk/INSTALL =================================================================== --- trunk/INSTALL 2011-12-02 18:49:10 UTC (rev 303) +++ trunk/INSTALL 2013-09-28 03:06:19 UTC (rev 304) @@ -1,14 +1,4 @@ -BUILDING FROM THE SUBVERSION TRUNK -* Windows Prequisites: - * Visual Studio 2008 Express - * Python 2.6 - * py2exe - * MozillaBuild (http://developer.mozilla.org/en/docs/Windows_Build_Prerequisites) - - Launch the MozillaBuild MSVC 9 batch file. (You may have to run this as an - Administrator on Windows Vista.) Run the commands in that shell. - On all platforms: $ python setup.py build Deleted: trunk/Makefile.SpiderMonkey =================================================================== --- trunk/Makefile.SpiderMonkey 2011-12-02 18:49:10 UTC (rev 303) +++ trunk/Makefile.SpiderMonkey 2013-09-28 03:06:19 UTC (rev 304) @@ -1,59 +0,0 @@ -## THIS IS AN INTERNAL MAKEFILE FOR setup.py -## DO NOT RUN THIS MAKEFILE DIRECTLY. - -SPIDERMONKEY_SRC=spidermonkey/src - -# Load the SpiderMonkey config to find the OS define -# Also use this for the SO_SUFFIX -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 - -ORIG_LIB=$(SPIDERMONKEY_SRC)/$(OBJDIR)/$(JS_LIB) -COPY_LIB=$(BUILD_DIR)/$(JS_LIB) -ORIG_DLL=$(SPIDERMONKEY_SRC)/$(OBJDIR)/js32.dll -COPY_DLL_DIR=$(DISTUTILS_DIR)/javascriptlint -COPY_DLL_PATH=$(COPY_DLL_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_PATH) -endif - -all: $(ALL_TARGETS) - -clean: - rm -f $(ORIG_LIB) - rm -Rf $(BUILD_DIR) - -$(BUILD_DIR): - mkdir -p $(BUILD_DIR) - -$(COPY_LIB): $(BUILD_DIR) $(ORIG_LIB) - cp $(ORIG_LIB) $(COPY_LIB) - -$(COPY_DLL_PATH): $(ORIG_DLL) - mkdir -p $(COPY_DLL_DIR) - cp $(ORIG_DLL) $(COPY_DLL_PATH) - -$(OS_HEADER): $(BUILD_DIR) - echo "#define $(SPIDERMONKEY_OS)" > $(OS_HEADER) - -$(COPY_JSAUTOCFG_H): $(ORIG_JSAUTOCFG_H) - cp $(ORIG_JSAUTOCFG_H) $(COPY_JSAUTOCFG_H) - Modified: trunk/javascriptlint/jsparse.py =================================================================== --- trunk/javascriptlint/jsparse.py 2011-12-02 18:49:10 UTC (rev 303) +++ trunk/javascriptlint/jsparse.py 2013-09-28 03:06:19 UTC (rev 304) @@ -1,155 +1,20 @@ #!/usr/bin/python # vim: ts=4 sw=4 expandtab """ Parses a script into nodes. """ -import bisect import re import unittest -import spidermonkey -from spidermonkey import tok, op -from util import JSVersion +import jsengine.parser +from jsengine.parser import kind as tok +from jsengine.parser import op +from jsengine.structs import * -_tok_names = dict(zip( - [getattr(tok, prop) for prop in dir(tok)], - ['tok.%s' % prop for prop in dir(tok)] -)) -_op_names = dict(zip( - [getattr(op, prop) for prop in dir(op)], - ['op.%s' % prop for prop in dir(op)] -)) +from .util import JSVersion -NodePos = spidermonkey.NodePos - -class NodePositions: - " Given a string, allows [x] lookups for NodePos line and column numbers." - def __init__(self, text, start_pos=None): - # Find the length of each line and incrementally sum all of the lengths - # to determine the ending position of each line. - self._start_pos = start_pos - 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] - if self._start_pos: - if line == 0: - col += self._start_pos.col - line += self._start_pos.line - return NodePos(line, col) - def to_offset(self, pos): - pos = self._to_rel_pos(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 - start, end = self._to_rel_pos(start), self._to_rel_pos(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) - def _to_rel_pos(self, pos): - " converts a position to a position relative to self._start_pos " - if not self._start_pos: - return pos - line, col = pos.line, pos.col - line -= self._start_pos.line - if line == 0: - col -= self._start_pos.col - assert line >= 0 and col >= 0 # out-of-bounds node position - return NodePos(line, col) - -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 isvalidversion(jsversion): if jsversion is None: return True - return spidermonkey.is_valid_version(jsversion.version) + return jsengine.parser.is_valid_version(jsversion.version) def findpossiblecomments(script, node_positions): pos = 0 @@ -168,31 +33,18 @@ comment_text = script[match.start():match.end()] if comment_text.startswith('/*'): comment_text = comment_text[2:-2] - opcode = 'JSOP_C_COMMENT' + opcode = op.C_COMMENT else: comment_text = comment_text[2:] - opcode = 'JSOP_CPP_COMMENT' - opcode = opcode[5:].lower() + opcode = op.CPP_COMMENT start_offset = match.start() end_offset = match.end()-1 start_pos = node_positions.from_offset(start_offset) end_pos = node_positions.from_offset(end_offset) - kwargs = { - 'kind': '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) + comment_node = ParseNode(kind.COMMENT, opcode, start_pos, end_pos, + comment_text, []) comments.append(comment_node) # Start searching immediately after the start of the comment in case @@ -203,28 +55,23 @@ """ All node positions will be relative to startpos. This allows scripts to be embedded in a file (for example, HTML). """ - def _wrapped_callback(line, col, msg): - assert msg.startswith('JSMSG_') - msg = msg[6:].lower() - error_callback(line, col, msg) - startpos = startpos or NodePos(0,0) jsversion = jsversion or JSVersion.default() - assert isvalidversion(jsversion) - return spidermonkey.parse(script, jsversion.version, jsversion.e4x, - _Node, _wrapped_callback, - startpos.line, startpos.col) + assert isvalidversion(jsversion), jsversion + if jsversion.e4x: + error_callback(startpos.line, startpos.col, 'e4x_deprecated', {}) + return jsengine.parser.parse(script, jsversion.version, + error_callback, + startpos) def filtercomments(possible_comments, node_positions, root_node): comment_ignore_ranges = NodeRanges() def process(node): - if node.kind == tok.NUMBER: - node.atom = node_positions.text(node.start_pos(), node.end_pos()) - elif node.kind == tok.STRING or \ + if node.kind == tok.STRING or \ (node.kind == tok.OBJECT and node.opcode == op.REGEXP): start_offset = node_positions.to_offset(node.start_pos()) - end_offset = node_positions.to_offset(node.end_pos()) - 1 + end_offset = node_positions.to_offset(node.end_pos()) comment_ignore_ranges.add(start_offset, end_offset) for kid in node.kids: if kid: @@ -249,7 +96,7 @@ def is_compilable_unit(script, jsversion): jsversion = jsversion or JSVersion.default() assert isvalidversion(jsversion) - return spidermonkey.is_compilable_unit(script, jsversion.version, jsversion.e4x) + return jsengine.parser.is_compilable_unit(script, jsversion.version) def _dump_node(node, depth=0): if node is None: @@ -258,7 +105,7 @@ print else: print ' '*depth, - print '%s, %s' % (_tok_names[node.kind], _op_names[node.opcode]) + print '%s, %s' % (repr(node.kind), repr(node.opcode)) print ' '*depth, print '%s - %s' % (node.start_pos(), node.end_pos()) if hasattr(node, 'atom'): @@ -379,22 +226,21 @@ for text, expected in tests: encountered = is_compilable_unit(text, JSVersion.default()) self.assertEquals(encountered, expected) - # NOTE: This seems like a bug. - self.assert_(is_compilable_unit("/* test", JSVersion.default())) + self.assert_(not is_compilable_unit("/* test", JSVersion.default())) class TestLineOffset(unittest.TestCase): def testErrorPos(self): def geterror(script, startpos): errors = [] - def onerror(line, col, msg): - errors.append((line, col, msg)) + def onerror(line, col, msg, msg_args): + errors.append((line, col, msg, msg_args)) parse(script, None, onerror, startpos) self.assertEquals(len(errors), 1) return errors[0] - self.assertEquals(geterror(' ?', None), (0, 1, 'syntax_error')) - self.assertEquals(geterror('\n ?', None), (1, 1, 'syntax_error')) - self.assertEquals(geterror(' ?', NodePos(1,1)), (1, 2, 'syntax_error')) - self.assertEquals(geterror('\n ?', NodePos(1,1)), (2, 1, 'syntax_error')) + self.assertEquals(geterror(' ?', None), (0, 1, 'syntax_error', {})) + self.assertEquals(geterror('\n ?', None), (1, 1, 'syntax_error', {})) + self.assertEquals(geterror(' ?', NodePos(1,1)), (1, 2, 'syntax_error', {})) + self.assertEquals(geterror('\n ?', NodePos(1,1)), (2, 1, 'syntax_error', {})) def testNodePos(self): def getnodepos(script, startpos): root = parse(script, None, None, startpos) Modified: trunk/javascriptlint/lint.py =================================================================== --- trunk/javascriptlint/lint.py 2011-12-02 18:49:10 UTC (rev 303) +++ trunk/javascriptlint/lint.py 2013-09-28 03:06:19 UTC (rev 304) @@ -12,7 +12,8 @@ import unittest import util -from spidermonkey import tok, op +from jsengine.parser import kind as tok +from jsengine.parser import op _newline_kinds = ( 'eof', 'comma', 'dot', 'semi', 'colon', 'lc', 'rc', 'lp', 'rb', 'assign', @@ -96,6 +97,7 @@ def add_declaration(self, name, node, type_): assert type_ in ('arg', 'function', 'var'), \ 'Unrecognized identifier type: %s' % type_ + assert isinstance(name, basestring) self._identifiers[name] = { 'node': node, 'type': type_ @@ -339,10 +341,10 @@ def _lint_script_part(scriptpos, jsversion, script, script_cache, conf, ignores, report_native, report_lint, import_callback): - def parse_error(row, col, msg): + def parse_error(row, col, msg, msg_args): if not msg in ('anon_no_return_value', 'no_return_value', 'redeclared_var', 'var_hides_arg'): - parse_errors.append((jsparse.NodePos(row, col), msg)) + parse_errors.append((jsparse.NodePos(row, col), msg, msg_args)) def report(node, errname, pos=None, **errargs): if errname == 'empty_statement' and node.kind == tok.LC: @@ -411,8 +413,8 @@ root = jsparse.parse(script, jsversion, parse_error, scriptpos) if not root: # Report errors and quit. - for pos, msg in parse_errors: - report_native(pos, msg) + for pos, msg, msg_args in parse_errors: + report_native(pos, msg, msg_args) return comments = jsparse.filtercomments(possible_comments, node_positions, root) @@ -457,7 +459,7 @@ elif keyword == 'pass': passes.append(node) else: - if comment.opcode == 'c_comment': + if comment.opcode == op.C_COMMENT: # Look for nested C-style comments. nested_comment = comment.atom.find('/*') if nested_comment < 0 and comment.atom.endswith('/'): @@ -514,9 +516,9 @@ errdesc = warnings.format_error(errname, **errargs) _report(pos or node.start_pos(), errname, errdesc, True) - def report_native(pos, errname): - # TODO: Format the error. - _report(pos, errname, errname, False) + def report_native(pos, errname, errargs): + errdesc = warnings.format_error(errname, **errargs) + _report(pos, errname, errdesc, False) def _report(pos, errname, errdesc, require_key): try: @@ -581,7 +583,7 @@ if other and parent_scope == scope: # Only warn about duplications in this scope. # Other scopes will be checked later. - if other.kind == tok.FUNCTION and name in other.fn_args: + if other.kind == tok.NAME and other.opcode == op.ARGNAME: report(node, 'var_hides_arg', name=name) else: report(node, 'redeclared_var', name=name) @@ -612,7 +614,9 @@ _warn_or_declare(scopes[-1], node.fn_name, 'function', node, report) self._push_scope(node) for var_name in node.fn_args: - scopes[-1].add_declaration(var_name, node, 'arg') + if scopes[-1].get_identifier(var_name.atom): + report(var_name, 'duplicate_formal', name=var_name.atom) + scopes[-1].add_declaration(var_name.atom, var_name, 'arg') @visitation.visit('push', tok.LEXICALSCOPE, tok.WITH) def _push_scope(self, node): Deleted: trunk/javascriptlint/spidermonkey.py =================================================================== --- trunk/javascriptlint/spidermonkey.py 2011-12-02 18:49:10 UTC (rev 303) +++ trunk/javascriptlint/spidermonkey.py 2013-09-28 03:06:19 UTC (rev 304) @@ -1,10 +0,0 @@ -# vim: ts=4 sw=4 expandtab - -# This is a wrapper script to make it easier for development. It tries to -# import the development version first, and if that fails, it goes after the -# real version. -try: - from pyspidermonkey_ import * -except ImportError: - from pyspidermonkey import * - Modified: trunk/javascriptlint/warnings.py =================================================================== --- trunk/javascriptlint/warnings.py 2011-12-02 18:49:10 UTC (rev 303) +++ trunk/javascriptlint/warnings.py 2013-09-28 03:06:19 UTC (rev 304) @@ -21,9 +21,10 @@ import util import visitation -from spidermonkey import tok, op +from jsengine.parser import kind as tok +from jsengine.parser import op -_ALL_TOKENS = tuple(filter(lambda x: x != tok.EOF, tok.__dict__.values())) +_ALL_TOKENS = tok.__dict__.values() def _get_assigned_lambda(node): """ Given a node "x = function() {}", returns "function() {}". @@ -53,6 +54,7 @@ 'use_of_label': 'use of label', 'misplaced_regex': 'regular expressions should be preceded by a left parenthesis, assignment, colon, or comma', 'assign_to_function_call': 'assignment to a function call', + 'equal_as_assign': 'test for equality (==) mistyped as assignment (=)?', 'ambiguous_else_stmt': 'the else statement could be matched with one of multiple if statements (use curly braces to indicate intent', 'block_without_braces': 'block statement without curly braces', 'ambiguous_nested_stmt': 'block statements containing block statements should use curly braces to resolve ambiguity', @@ -70,6 +72,7 @@ 'leading_decimal_point': 'leading decimal point may indicate a number or an object member', 'trailing_decimal_point': 'trailing decimal point may indicate a number or an object member', 'octal_number': 'leading zeros make an octal number', + 'trailing_comma': 'extra comma is not recommended in object initializers', 'trailing_comma_in_array': 'extra comma is not recommended in array initializers', 'useless_quotes': 'the quotation marks are unnecessary', 'mismatch_ctrl_comments': 'mismatched control comment; "ignore" and "end" control comments must have a one-to-one correspondence', @@ -99,8 +102,20 @@ 'incorrect_version': 'Expected /*jsl:content-type*/ control comment. The script was parsed with the wrong version.', } +errors = { + 'e4x_deprecated': 'e4x is deprecated', + 'semi_before_stmnt': 'missing semicolon before statement', + 'syntax_error': 'syntax error', + 'expected_tok': 'expected token: {token}', + 'unexpected_char': 'unexpected character: {char}', +} + def format_error(errname, **errargs): - errdesc = warnings[errname] + if errname in errors: + errdesc = errors[errname] + else: + errdesc = warnings[errname] + try: errdesc = re.sub(r"{(\w+)}", lambda match: errargs[match.group(1)], errdesc) except (TypeError, KeyError): @@ -295,9 +310,18 @@ @lookfor(tok.ASSIGN) def assign_to_function_call(node): - if node.kids[0].kind == tok.LP: + kid = node.kids[0] + # Unpack parens. + while kid.kind == tok.RP: + kid, = kid.kids + if kid.kind == tok.LP: raise LintWarning, node +@lookfor(tok.ASSIGN) +def equal_as_assign(node): + if not node.parent.kind in (tok.SEMI, tok.RESERVED, tok.RP, tok.COMMA): + raise LintWarning, node + @lookfor(tok.IF) def ambiguous_else_stmt(node): # Only examine this node if it has an else statement. @@ -492,6 +516,11 @@ if _octal_regexp.match(node.atom): raise LintWarning, node +@lookfor(tok.RC) +def trailing_comma(node): + if node.end_comma: + raise LintWarning, node + @lookfor(tok.RB) def trailing_comma_in_array(node): if node.end_comma: Index: trunk/jsengine =================================================================== --- trunk/jsengine 2011-12-02 18:49:10 UTC (rev 303) +++ trunk/jsengine 2013-09-28 03:06:19 UTC (rev 304) Property changes on: trunk/jsengine ___________________________________________________________________ Added: svn:ignore ## -0,0 +1 ## +*.pyc Added: trunk/jsengine/__init__.py =================================================================== --- trunk/jsengine/__init__.py (rev 0) +++ trunk/jsengine/__init__.py 2013-09-28 03:06:19 UTC (rev 304) @@ -0,0 +1,21 @@ +# vim: sw=4 ts=4 et + +_MESSAGES = ( + 'eof', + 'semi_before_stmnt', + 'syntax_error', + 'unterminated_comment', + 'expected_tok', + 'unexpected_char', +) + +class JSSyntaxError(BaseException): + def __init__(self, pos, msg, msg_args=None): + assert msg in _MESSAGES, msg + self.pos = pos + self.msg = msg + self.msg_args = msg_args or {} + def __unicode__(self): + return '%s: %s' % (self.pos, self.msg) + def __repr__(self): + return 'JSSyntaxError(%r, %r, %r)' % (self.pos, self.msg. self.msg_args) Added: trunk/jsengine/parser/__init__.py =================================================================== --- trunk/jsengine/parser/__init__.py (rev 0) +++ trunk/jsengine/parser/__init__.py 2013-09-28 03:06:19 UTC (rev 304) @@ -0,0 +1,924 @@ +# vim: sw=4 ts=4 et +import unittest + +from jsengine.tokenizer import tok +from jsengine import tokenizer + +from jsengine import JSSyntaxError +from _constants_kind import kind +from _constants_op import op + +from jsengine.structs import * + +_VERSIONS = [ + "default", + "1.0", + "1.1", + "1.2", + "1.3", + "1.4", + "1.5", + "1.6", + "1.7", +] + +def _auto_semicolon(t, kind_, op_, startpos, endpos, atom, kids): + nosemi = False + if t.peek_sameline().tok not in (tok.EOF, tok.EOL, tok.RBRACE): + x = t.advance() + if x.tok != tok.SEMI: + raise JSSyntaxError(x.startpos, 'semi_before_stmnt') + endpos = x.endpos + else: + nosemi = True + return ParseNode(kind_, op_, startpos, endpos, atom, kids, nosemi) + +def _function_arglist(t): + fn_args = [] + if t.peek().tok != tok.RPAREN: + while True: + x = t.expect(tok.NAME) + fn_args.append(ParseNode(kind.NAME, op.ARGNAME, + x.startpos, + x.endpos, x.atom, [])) + if t.peek().tok == tok.COMMA: + t.advance() + else: + break + return fn_args + +def _primary_expression(t): + x = t.next_withregexp() + if x.tok == tok.THIS: + return ParseNode(kind.PRIMARY, op.THIS, x.startpos, x.endpos, None, []) + elif x.tok == tok.NAME: + return ParseNode(kind.NAME, op.NAME, x.startpos, x.endpos, x.atom, [None]) + elif x.tok == tok.NULL: + return ParseNode(kind.PRIMARY, op.NULL, x.startpos, x.endpos, None, []) + elif x.tok == tok.TRUE: + return ParseNode(kind.PRIMARY, op.TRUE, x.startpos, x.endpos, None, []) + elif x.tok == tok.FALSE: + return ParseNode(kind.PRIMARY, op.FALSE, x.startpos, x.endpos, None, []) + elif x.tok == tok.STRING: + return ParseNode(kind.STRING, op.STRING, x.startpos, x.endpos, x.atom, []) + elif x.tok == tok.REGEXP: + return ParseNode(kind.OBJECT, op.REGEXP, x.startpos, x.endpos, None, []) + elif x.tok == tok.NUMBER: + return ParseNode(kind.NUMBER, None, x.startpos, x.endpos, x.atom, []) + elif x.tok == tok.LBRACKET: + startpos = x.startpos + items = [] + end_comma = None + if t.peek().tok != tok.RBRACKET: + while True: + # Conditionally add a value. If it isn't followed by a comma, + # quit in order to force an RBRACKET. + if t.peek().tok == tok.COMMA: + items.append(None) + else: + items.append(_assignment_expression(t, True)) + if not t.peek().tok == tok.COMMA: + break + + # Expect a comma and use it if the value was missing. + x = t.expect(tok.COMMA) + comma = ParseNode(kind.COMMA, None, + x.startpos, x.endpos, None, []) + items[-1] = items[-1] or comma + + # Check for the end. + if t.peek().tok == tok.RBRACKET: + end_comma = comma + break + endpos = t.expect(tok.RBRACKET).endpos + return ParseNode(kind.RB, None, startpos, endpos, None, items, + end_comma=end_comma) + elif x.tok == tok.LBRACE: + startpos = x.startpos + kids = [] + # TODO: get/set + end_comma = None + while True: + x = t.peek() + if x.tok == tok.RBRACE: + break + elif x.tok == tok.STRING: + t.expect(tok.STRING) + key = ParseNode(kind.STRING, None, x.startpos, + x.endpos, x.atom, []) + elif x.tok == tok.NUMBER: + t.expect(tok.NUMBER) + key = ParseNode(kind.NUMBER, None, x.startpos, + x.endpos, x.atom, []) + else: + x = t.expect_identifiername() + key = ParseNode(kind.NAME, None, x.startpos, x.endpos, + x.atom, []) + t.expect(tok.COLON) + value = _assignment_expression(t, True) + kids.append(ParseNode(kind.COLON, None, key.startpos, + value.endpos, None, [key, value])) + if t.peek().tok == tok.COMMA: + x = t.advance() + end_comma = ParseNode(kind.COMMA, None, + x.startpos, x.endpos, None, []) + else: + end_comma = None + break + endpos = t.expect(tok.RBRACE).endpos + return ParseNode(kind.RC, None, startpos, endpos, None, kids, + end_comma=end_comma) + elif x.tok == tok.LPAREN: + startpos = x.startpos + kid = _expression(t, True) + endpos = t.expect(tok.RPAREN).endpos + return ParseNode(kind.RP, None, startpos, endpos, None, [kid]) + else: + raise JSSyntaxError(x.startpos, 'syntax_error') + +def _function_declaration(t, named_opcode): + node = _function_expression(t, named_opcode) + + # Convert anonymous functions in expressions. + if node.opcode == op.ANONFUNOBJ: + node = _auto_semicolon(t, kind.SEMI, None, node.startpos, node.endpos, + None, [node]) + return node + + +def _function_expression(t, named_opcode): + startpos = t.expect(tok.FUNCTION).startpos + if t.peek().tok == tok.NAME: + fn_name = t.expect(tok.NAME).atom + opcode = named_opcode + else: + fn_name = None + opcode = op.ANONFUNOBJ + t.expect(tok.LPAREN) + fn_args = _function_arglist(t) + t.expect(tok.RPAREN) + fn_body_startpos = t.expect(tok.LBRACE).startpos + kids = _sourceelements(t, tok.RBRACE) + fn_body_endpos = t.expect(tok.RBRACE).endpos + fn_body = ParseNode(kind.LC, None, fn_body_startpos, + fn_body_endpos, None, kids) + return ParseNode(kind.FUNCTION, + op.ANONFUNOBJ if fn_name is None else op.NAMEDFUNOBJ, + startpos, fn_body.endpos, + fn_name, [fn_body], fn_args=fn_args) + +def _argument_list(t): + args = [] + if t.peek().tok != tok.RPAREN: + while True: + args.append(_assignment_expression(t, True)) + if t.peek().tok == tok.COMMA: + t.advance() + else: + break + return args + +def _new_expression(t): + startpos = t.expect(tok.NEW).startpos + expr = _member_expression(t) + # If no (), this is a variant of the NewExpression + if t.peek().tok == tok.LPAREN: + t.expect(tok.LPAREN) + args = _argument_list(t) + endpos = t.expect(tok.RPAREN).endpos + else: + args = [] + endpos = expr.endpos + return ParseNode(kind.NEW, op.NEW, startpos, endpos, + None, [expr] + args) + +def _member_expression(t, _recurse=True): + x = t.peek() + if x.tok == tok.NEW: + kid = _new_expression(t) + elif x.tok == tok.FUNCTION: + kid = _function_expression(t, op.NAMEDFUNOBJ) + else: + kid = _primary_expression(t) + + while True: + if t.peek().tok == tok.LBRACKET: + t.advance() + expr = _expression(t, True) + endpos = t.expect(tok.RBRACKET).endpos + kid = ParseNode(kind.LB, op.GETELEM, kid.startpos, endpos, + None, [kid, expr]) + elif t.peek().tok == tok.DOT: + t.advance() + expr = t.expect_identifiername() + kid = ParseNode(kind.DOT, op.GETPROP, kid.startpos, expr.endpos, + expr.atom, [kid]) + else: + return kid + +def _call_expression(t): + expr = _member_expression(t) + if t.peek().tok != tok.LPAREN: + return expr + + while True: + x = t.peek() + if x.tok == tok.LPAREN: + t.expect(tok.LPAREN) + args = _argument_list(t) + endpos = t.expect(tok.RPAREN).endpos + expr = ParseNode(kind.LP, op.CALL, expr.startpos, + endpos, None, [expr] + args) + elif x.tok == tok.LBRACKET: + t.expect(tok.LBRACKET) + lookup = _expression(t, True) + endpos = t.expect(tok.RBRACKET).endpos + expr = ParseNode(kind.LB, op.GETELEM, + expr.startpos, endpos, + None, [expr, lookup]) + elif x.tok == tok.DOT: + t.expect(tok.DOT) + lookup = t.expect_identifiername() + expr = ParseNode(kind.DOT, op.GETPROP, + expr.startpos, lookup.endpos, + lookup.atom, [expr]) + else: + return expr + +def _lefthandside_expression(t): + kid = _call_expression(t) + kid._lefthandside = True + return kid + +def _postfix_expression(t): + kid = _lefthandside_expression(t) + if t.peek_sameline().tok == tok.INC: + endpos = t.expect(tok.INC).endpos + if kid.kind == kind.DOT and kid.opcode == op.GETPROP: + opcode = op.PROPINC + else: + opcode = op.NAMEINC + return ParseNode(kind.INC, opcode, + kid.startpos, endpos, None, [kid]) + elif t.peek_sameline().tok == tok.DEC: + endpos = t.expect(tok.DEC).endpos + return ParseNode(kind.DEC, op.NAMEDEC, + kid.startpos, endpos, None, [kid]) + else: + return kid + +_UNARY = { + tok.DELETE: (kind.DELETE, None), + tok.VOID: (kind.UNARYOP, op.VOID), + tok.TYPEOF: (kind.UNARYOP, op.TYPEOF), + tok.INC: (kind.INC, op.INCNAME), + tok.DEC: (kind.DEC, op.DECNAME), + tok.ADD: (kind.UNARYOP, op.POS), + tok.SUB: (kind.UNARYOP, op.NEG), + tok.BIT_NOT: (kind.UNARYOP, op.BITNOT), + tok.LOGICAL_NOT: (kind.UNARYOP, op.NOT), +} +def _unary_expression(t): + x = t.peek() + if x.tok in _UNARY: + kind_, op_ = _UNARY[x.tok] + startpos = t.advance().startpos + kid = _unary_expression(t) + return ParseNode(kind_, op_, startpos, kid.endpos, None, [kid]) + else: + return _postfix_expression(t) + +def _binary_expression(t, dict_, child_expr_callback): + expr = child_expr_callback(t) + while True: + x = t.peek() + try: + kind_, op_ = dict_[x.tok] + except KeyError: + return expr + + kids = [expr] + while t.peek().tok == x.tok: + t.advance() + kids.append(child_expr_callback(t)) + expr = ParseNode(kind_, op_, + kids[0].startpos, kids[1].endpos, + None, kids) + +_MULTIPLICATIVE = { + tok.MUL: (kind.STAR, op.MUL), + tok.DIV: (kind.DIVOP, op.DIV), + tok.MOD: (kind.DIVOP, op.MOD), +} +def _multiplicative_expression(t): + return _binary_expression(t, _MULTIPLICATIVE, _unary_expression) + +_ADDITIVE = { + tok.ADD: (kind.PLUS, op.ADD), + tok.SUB: (kind.MINUS, op.SUB), +} +def _additive_expression(t): + return _binary_expression(t, _ADDITIVE, + _multiplicative_expression) + +_SHIFT = { + tok.LSHIFT: (kind.SHOP, op.LSH), + tok.RSHIFT: (kind.SHOP, op.RSH), + tok.URSHIFT: (kind.SHOP, op.URSH), +} +def _shift_expression(t): + return _binary_expression(t, _SHIFT, + _additive_expression) + +_RELATIONAL_NOIN = { + tok.LT: (kind.RELOP, op.LT), + tok.GT: (kind.RELOP, op.GT), + tok.LE: (kind.RELOP, op.LE), + tok.GE: (kind.RELOP, op.GE), + tok.INSTANCEOF: (kind.INSTANCEOF, op.INSTANCEOF), +} +_RELATIONAL_IN = dict(_RELATIONAL_NOIN) +_RELATIONAL_IN.update({ + tok.IN: (kind.IN, op.IN), +}) +def _relational_expression(t, allowin): + return _binary_expression(t, _RELATIONAL_IN if allowin else _RELATIONAL_NOIN, + _shift_expression) + +_EQUALITY = { + tok.EQ: (kind.EQOP, op.EQ), + tok.NE: (kind.EQOP, op.NE), + tok.EQ_STRICT: (kind.EQOP, op.NEW_EQ), + tok.NE_STRICT: (kind.EQOP, op.NEW_NE), +} +def _equality_expression(t, allowin): + return _binary_expression(t, _EQUALITY, + lambda t: _relational_expression(t, allowin)) + +def _bitwise_and_expression(t, allowin): + left = _equality_expression(t, allowin) + while t.peek().tok == tok.BIT_AND: + t.advance() + right = _equality_expression(t, allowin) + left = ParseNode(kind.BITAND, op.BITAND, + left.startpos, right.endpos, + None, [left, right]) + return left + +def _bitwise_xor_expression(t, allowin): + left = _bitwise_and_expression(t, allowin) + while t.peek().tok == tok.BIT_XOR: + t.advance() + right = _bitwise_and_expression(t, allowin) + left = ParseNode(kind.BITXOR, op.BITXOR, + left.startpos, right.endpos, + None, [left, right]) + return left + +def _bitwise_or_expression(t, allowin): + left = _bitwise_xor_expression(t, allowin) + while t.peek().tok == tok.BIT_OR: + t.advance() + right = _bitwise_xor_expression(t, allowin) + left = ParseNode(kind.BITOR, op.BITOR, + left.startpos, right.endpos, + None, [left, right]) + return left + +def _logical_and_expression(t, allowin): + exprs = [] + while True: + exprs.append(_bitwise_or_expression(t, allowin)) + if t.peek().tok == tok.LOGICAL_AND: + t.expect(tok.LOGICAL_AND) + else: + break + + while len(exprs) > 1: + right = exprs.pop() + left = exprs[-1] + exprs[-1] = ParseNode(kind.AND, op.AND, + left.startpos, right.endpos, + None, [left, right]) + return exprs[0] + +def _logical_or_expression(t, allowin): + exprs = [] + while True: + exprs.append(_logical_and_expression(t, allowin)) + if t.peek().tok == tok.LOGICAL_OR: + t.expect(tok.LOGICAL_OR) + else: + break + + while len(exprs) > 1: + right = exprs.pop() + left = exprs[-1] + exprs[-1] = ParseNode(kind.OR, op.OR, + left.startpos, right.endpos, + None, [left, right]) + return exprs[0] + +def _conditional_expression(t, allowin): + kid = _logical_or_expression(t, allowin) + if t.peek().tok == tok.QUESTION: + t.expect(tok.QUESTION) + if_ = _assignment_expression(t, True) + t.expect(tok.COLON) + else_ = _assignment_expression(t, allowin) + return ParseNode(kind.HOOK, None, + kid.startpos, else_.endpos, + None, [kid, if_, else_]) + else: + return kid + +_ASSIGNS = { + tok.ASSIGN: (kind.ASSIGN, None), + tok.ASSIGN_URSHIFT: (kind.ASSIGN, op.URSH), + tok.ASSIGN_LSHIFT: (kind.ASSIGN, op.LSH), + tok.ASSIGN_RSHIFT: (kind.ASSIGN, op.RSH), + tok.ASSIGN_ADD: (kind.ASSIGN, op.ADD), + tok.ASSIGN_SUB: (kind.ASSIGN, op.SUB), + tok.ASSIGN_MUL: (kind.ASSIGN, op.MUL), + tok.ASSIGN_MOD: (kind.ASSIGN, op.MOD), + tok.ASSIGN_BIT_AND: (kind.ASSIGN, op.BITAND), + tok.ASSIGN_BIT_OR: (kind.ASSIGN, op.BITOR), + tok.ASSIGN_BIT_XOR: (kind.ASSIGN, op.BITXOR), + tok.ASSIGN_DIV: (kind.ASSIGN, op.DIV), +} +def _assignment_expression(t, allowin): + left = _conditional_expression(t, allowin) + if t.peek().tok in _ASSIGNS: + kid = left + while kid.kind == kind.RP: + kid, = kid.kids + if kid.kind == kind.NAME: + assert kid.opcode == op.NAME + kid.opcode = op.SETNAME + elif kid.kind == kind.DOT: + assert kid.opcode == op.GETPROP, left.op + kid.opcode = op.SETPROP + elif kid.kind == kind.LB: + assert kid.opcode == op.GETELEM + kid.opcode = op.SETELEM + elif kid.kind == kind.LP: + assert kid.opcode == op.CALL + kid.opcode = op.SETCALL + else: + raise JSSyntaxError(left.startpos, 'invalid_assign') + kind_, op_ = _ASSIGNS[t.peek().tok] + t.advance() + right = _assignment_expression(t, allowin) + return ParseNode(kind_, op_, + left.startpos, right.endpos, None, [left, right]) + else: + return left + +def _expression(t, allowin): + items = [] + items.append(_assignment_expression(t, allowin)) + while t.peek().tok == tok.COMMA: + t.advance() + items.append(_assignment_expression(t, allowin)) + if len(items) > 1: + return ParseNode(kind.COMMA, None, items[0].startpos, + items[-1].endpos, None, items) + else: + return items[0] + +def _variable_declaration(t, allowin): + nodes = [] + while True: + x = t.expect(tok.NAME) + value = None + if t.peek().tok == tok.ASSIGN: + t.advance() + value = _assignment_expression(t, allowin) + nodes.append(ParseNode(kind.NAME, op.SETNAME if value else op.NAME, + x.startpos, + value.endpos if value else x.endpos, + x.atom, [value])) + + if t.peek().tok == tok.COMMA: + t.advance() + else: + return nodes + +def _block_statement(t): + kids = [] + startpos = t.expect(tok.LBRACE).startpos + while t.peek().tok != tok.RBRACE: + kids.append(_statement(t)) + endpos = t.expect(tok.RBRACE).endpos + return ParseNode(kind.LC, None, startpos, endpos, None, kids) + +def _empty_statement(t): + # EMPTY STATEMENT + x = t.expect(tok.SEMI) + return ParseNode(kind.SEMI, None, x.startpos, x.endpos, None, [None]) + +def _var_statement(t): + # VARIABLE STATEMENT + startpos = t.expect(tok.VAR).startpos + nodes = _variable_declaration(t, True) + return _auto_semicolon(t, kind.VAR, op.DEFVAR, + startpos, nodes[-1].endpos, None, nodes) + +def _if_statement(t): + # IF STATEMENT + startpos = t.expect(tok.IF).startpos + t.expect(tok.LPAREN) + condition = _expression(t, True) + t.expect(tok.RPAREN) + if_body = _statement(t) + if t.peek().tok == tok.ELSE: + t.advance() + else_body = _statement(t) + else: + else_body = None + endpos = else_body.endpos if else_body else if_body.endpos + return ParseNode(kind.IF, None, startpos, + endpos, None, [condition, if_body, else_body]) + +def _do_statement(t): + startpos = t.expect(tok.DO).startpos + code = _statement(t) + t.expect(tok.WHILE) + t.expect(tok.LPAREN) + expr = _expression(t, True) + endtoken = t.expect(tok.RPAREN) + return _auto_semicolon(t, kind.DO, None, + startpos, endtoken.endpos, None, [code, expr]) + +def _while_statement(t): + startpos = t.expect(tok.WHILE).startpos + t.expect(tok.LPAREN) + expr = _expression(t, True) + t.expect(tok.RPAREN) + code = _statement(t) + return ParseNode(kind.WHILE, None, + startpos, code.endpos, None, [expr, code]) + +def _for_statement(t): + for_startpos = t.expect(tok.FOR).startpos + t.expect(tok.LPAREN) + + for_exprs = [] + if t.peek().tok == tok.VAR: + var_startpos = t.advance().startpos + kids = _variable_declaration(t, False) + vars = ParseNode(kind.VAR, op.DEFVAR, var_startpos, kids[-1].endpos, + None, kids) + + if t.peek().tok == tok.IN: + t.advance() + in_ = _expression(t, True) + for_exprs = [vars, in_] + else: + for_exprs = [vars, None, None] + else: + if t.peek().tok != tok.SEMI: + expr = _expression(t, False) + else: + expr = None + + if t.peek().tok == tok.IN: + t.advance() + vars = expr + in_ = _expression(t, True) + for_exprs = [vars, in_] + else: + for_exprs = [expr, None, None] + + if len(for_exprs) == 2: + condition = ParseNode(kind.IN, None, for_exprs[0].startpos, + for_exprs[-1].endpos, None, for_exprs) + else: + x = t.expect(tok.SEMI) + if t.peek().tok != tok.SEMI: + for_exprs[1] = _expression(t, True) + t.expect(tok.SEMI) + if t.peek().tok != tok.RPAREN: + for_exprs[2] = _expression(t, True) + condition = ParseNode(kind.RESERVED, None, None, None, + None, for_exprs) + + t.expect(tok.RPAREN) + body = _statement(t) + return ParseNode(kind.FOR, + op.FORIN if condition.kind == kind.IN else None, + for_startpos, body.endpos, + None, [condition, body]) + +def _continue_statement(t): + endtoken = t.expect(tok.CONTINUE) + startpos = endtoken.startpos + + if t.peek_sameline().tok == tok.NAME: + endtoken = t.expect(tok.NAME) + name = endtoken.atom + else: + name = None + # TODO: Validate Scope Labels + return _auto_semicolon(t, kind.CONTINUE, None, startpos, endtoken.endpos, name, []) + +def _break_statement(t): + endtoken = t.expect(tok.BREAK) + startpos = endtoken.startpos + + if t.peek_sameline().tok == tok.NAME: + endtoken = t.expect(tok.NAME) + name = endtoken.atom + else: + name = None + # TODO: Validate Scope Labels + return _auto_semicolon(t, kind.BREAK, None, startpos, endtoken.endpos, name, []) + +def _return_statement(t): + endtoken = t.expect(tok.RETURN) + startpos = endtoken.startpos + + if t.peek_sameline().tok not in (tok.EOF, tok.EOL, tok.SEMI): + expr = _expression(t, True) + endtoken = expr + else: + expr = None + # TODO: Validate Scope Labels + return _auto_semicolon(t, kind.RETURN, None, startpos, endtoken.endpos, + None, [expr]) + +def _with_statement(t): + startpos = t.expect(tok.WITH).startpos + t.expect(tok.LPAREN) + expr = _expression(t, True) + t.expect(tok.RPAREN) + body = _statement(t) + return ParseNode(kind.WITH, None, startpos, body.endpos, None, [expr, body]) + +def _switch_statement(t): + switch_startpos = t.expect(tok.SWITCH).startpos + t.expect(tok.LPAREN) + expr = _expression(t, True) + t.expect(tok.RPAREN) + lc_startpos = t.expect(tok.LBRACE).startpos + cases = [] + while t.peek().tok != tok.RBRACE: + case_kind = None + case_expr = None + if t.peek().tok == tok.CASE: + case_startpos = t.advance().startpos + case_kind = kind.CASE + case_expr = _expression(t, True) + elif t.peek().tok == tok.DEFAULT: + case_startpos = t.advance().startpos + case_kind = kind.DEFAULT + else: + raise JSSyntaxError(t.peek().startpos, 'invalid_case') + + case_endpos = t.expect(tok.COLON).endpos + + statements = [] + while t.peek().tok not in (tok.DEFAULT, tok.CASE, tok.RBRACE): + statements.append(_statement(t)) + if statements: + statements_startpos = statements[0].startpos + statements_endpos = statements[-1].endpos + case_endpos = statements[-1].endpos + else: + statements_startpos = case_endpos + statements_endpos = case_endpos + + cases.append(ParseNode(case_kind, None, case_startpos, case_endpos, + None, [ + case_expr, + ParseNode(kind.LC, None, statements_startpos, + statements_endpos, None, statements) + ])) + + rc_endpos = t.expect(tok.RBRACE).endpos + return ParseNode(kind.SWITCH, None, switch_startpos, rc_endpos, + None, [expr, + ParseNode(kind.LC, None, lc_startpos, rc_endpos, None, cases)]) + +def _throw_statement(t): + # TODO: Validate Scope + startpos = t.expect(tok.THROW).startpos + if t.peek_sameline().tok == tok.EOL: + raise JSSyntaxError(t.peek_sameline().startpos, 'expected_statement') + expr = _expression(t, True) + return _auto_semicolon(t, kind.THROW, op.THROW, startpos, expr.endpos, + None, [expr]) + +def _try_statement(t): + try_startpos = t.expect(tok.TRY).startpos + + try_node = _block_statement(t) + catch_node = None + finally_node = None + try_endpos = None + + if t.peek().tok == tok.CATCH: + catch_startpos = t.advance().startpos + t.expect(tok.LPAREN) + x = t.expect(tok.NAME) + catch_expr = ParseNode(kind.NAME, None, x.startpos, x.endpos, + x.atom, [None]) + t.expect(tok.RPAREN) + catch_block = _block_statement(t) + catch_endpos = catch_block.endpos + catch_node = \ + ParseNode(kind.RESERVED, None, None, None, None, [ + ParseNode(kind.LEXICALSCOPE, op.LEAVEBLOCK, + catch_startpos, catch_endpos, None, [ + ParseNode(kind.CATCH, None, catch_startpos, + catch_endpos, None, + [catch_expr, None, catch_block]) + ]) + ]) + try_endpos = catch_endpos + + if t.peek().tok == tok.FINALLY: + t.advance() + finally_node = _block_statement(t) + try_endpos = finally_node.endpos + + if not catch_node and not finally_node: + raise JSSyntaxError(try_endpos, 'invalid_catch') + + return ParseNode(kind.TRY, None, try_startpos, try_endpos, + None, + [try_node, catch_node, finally_node]) + +def _statement(t): + # TODO: Labelled Statement + x = t.peek() + if x.tok == tok.LBRACE: + return _block_statement(t) + elif x.tok == tok.SEMI: + return _empty_statement(t) + elif x.tok == tok.VAR: + return _var_statement(t) + elif x.tok == tok.IF: + return _if_statement(t) + elif x.tok == tok.DO: + return _do_statement(t) + elif x.tok == tok.WHILE: + return _while_statement(t) + elif x.tok == tok.FOR: + return _for_statement(t) + elif x.tok == tok.CONTINUE: + return _continue_statement(t) + elif x.tok == tok.BREAK: + return _break_statement(t) + elif x.tok == tok.RETURN: + return _return_statement(t) + elif x.tok == tok.WITH: + return _with_statement(t) + elif x.tok == tok.SWITCH: + return _switch_statement(t) + elif x.tok == tok.THROW: + return _throw_statement(t) + elif x.tok == tok.TRY: + return _try_statement(t) + elif x.tok == tok.EOF: + raise JSSyntaxError(x.startpos, 'eof') + elif x.tok == tok.FUNCTION: + return _function_declaration(t, op.CLOSURE) #TODO: warn, since this is not reliable + + elif x.tok not in (tok.LBRACE, tok.FUNCTION): + expr = _expression(t, True) + if expr.kind == tok.NAME and t.peek().tok == tok.COLON: + t.expect(tok.COLON) + stmt = _statement(t) + return ParseNode(kind.COLON, op.NAME, expr.startpos, + stmt.endpos, expr.atom, [stmt]) + + return _auto_semicolon(t, kind.SEMI, None, expr.startpos, expr.endpos, + None, [expr]) + else: + raise JSSyntaxError(x.startpos, 'syntax_error') + +def _sourceelements(t, end_tok): + nodes = [] + while True: + x = t.peek() + if x.tok == tok.FUNCTION: + nodes.append(_function_declaration(t, None)) + elif x.tok == end_tok: + return nodes + else: + nodes.append(_statement(t)) + +def parsestring(s, startpos=None): + stream = tokenizer.TokenStream(s, startpos) + t = tokenizer.Tokenizer(stream) + nodes = _sourceelements(t, tok.EOF) + lc_endpos = t.expect(tok.EOF).endpos + lc_startpos = nodes[-1].startpos if nodes else lc_endpos + return ParseNode(kind.LC, None, lc_startpos, lc_endpos, None, nodes) + +def is_valid_version(version): + return version in _VERSIONS + +def _validate(node, depth=0): + for kid in node.kids: + if kid: + assert kid.parent is node + _validate(kid, depth+1) + +def parse(script, jsversion, + error_callback, startpos): + # TODO: respect version + assert is_valid_version(jsversion) + try: + root = parsestring(script, startpos) + except JSSyntaxError as error: + error_callback(error.pos.line, error.pos.col, error.msg, error.msg_args) + return None + _validate(root) + return root + +def is_compilable_unit(script, jsversion): + # TODO: respect version + assert is_valid_version(jsversion) + try: + parsestring(script) + except JSSyntaxError as error: + return error.msg not in ('eof', 'unterminated_comment') + return True + +class TestParser(unittest.TestCase): + def testCompilableUnit(self): + self.assert_(is_compilable_unit('', 'default')) + self.assert_(is_compilable_unit('/**/', 'default')) + self.assert_(not is_compilable_unit('/*', 'default')) + def testUnterminatedComment(self): + try: + parsestring('/*') + except JSSyntaxError as error: + self.assertEqual(error.pos, NodePos(0,1)) + else: + self.assert_(False) + def testObjectEndComma(self): + root = parsestring('a={a:1,}') + node, = root.kids + self.assertEquals(node.kind, kind.SEMI) + node, = node.kids + self.assertEquals(node.kind, kind.ASSIGN) + left, right = node.kids + self.assertEquals(left.atom, 'a') + self.assertEquals(right.kind, kind.RC) + node = right.end_comma + self.assertEquals(node.kind, tok.COMMA) + self.assertEquals(node.startpos, NodePos(0, 6)) + self.assertEquals(node.endpos, NodePos(0, 6)) + def _testArrayEndComma(self, script, col): + root = parsestring(script) + node, = root.kids + self.assertEquals(node.kind, kind.SEMI) + node, = node.kids + self.assertEquals(node.kind, kind.ASSIGN) + left, right = node.kids + self.assertEquals(left.atom, 'a') + self.assertEquals(right.kind, kind.RB) + node = right.end_comma + self.assertEquals(node is None, col is None) + if col is None: + self.assert_(node is None) + else: + self.assertEquals(node.kind, tok.COMMA) + self.assertEquals(node.startpos, NodePos(0, col)) + self.assertEquals(node.endpos, NodePos(0, col)) + def testArrayEndComma(self): + self._testArrayEndComma('a=[,]', 3) + self._testArrayEndComma('a=[a,]', 4) + self._testArrayEndComma('a=[a,b,c]', None) + def _testArrayCommas(self, script, items, end_comma): + root = parsestring(script) + node, = root.kids + self.assertEquals(node.kind, kind.SEMI) + node, = node.kids + self.assertEquals(node.kind, kind.ASSIGN) + left, right = node.kids + self.assertEquals(left.atom, 'a') + self.assertEquals(right.kind, kind.RB) + node = right + self.assertEquals(len(node.kids), len(items)) + for kid, item in zip(node.kids, items): + self.assertEquals(kid.atom, item) + self.assertEquals(bool(node.end_comma), end_comma) + def testArrayCommas(self): + self._testArrayCommas('a=[]', [], False) + self._testArrayCommas('a=[,]', [None], True) + self._testArrayCommas('a=[,,]', [None, None], True) + self._testArrayCommas('a=[,1]', [None, '1'], False) + self._testArrayCommas('a=[,,1]', [None, None, '1'], False) + self._testArrayCommas('a=[1,,1]', ['1', None, '1'], False) + self._testArrayCommas('a=[,1,]', [None, '1'], True) + def testParseArray(self): + try: + parsestring('a=[1 1]') + except JSSyntaxError as error: + pass + else: + self.assert_(False) Added: trunk/jsengine/parser/_constants_kind.py =================================================================== --- trunk/jsengine/parser/_constants_kind.py (rev 0) +++ trunk/jsengine/parser/_constants_kind.py 2013-09-28 03:06:19 UTC (rev 304) @@ -0,0 +1,79 @@ +# vim: sw=4 ts=4 et + +_KINDS = [ + 'AND', + 'BITAND', + 'BITOR', + 'BITXOR', + 'CATCH', + 'COMMENT', + 'DELETE', + 'DIVOP', + 'DOT', + 'EQ', + 'FINALLY', + 'FUNCTION', + 'HOOK', + 'IF', + 'IN', + 'INC', + 'INSTANCEOF', + 'LB', + 'LC', + 'LEXICALSCOPE', + 'LP', + 'MINUS', + 'NAME', + 'NEW', + 'OBJECT', + 'OR', + 'PLUS', + 'PRIMARY', + 'RB', + 'RC', + 'RELOP', + 'RESERVED', + 'RP', + 'SEMI', + 'SHOP', + 'STAR', + 'TRY', + 'UNARYOP', + 'VAR', + 'ASSIGN', + 'CASE', + 'COLON', + 'DEFAULT', + 'EQOP', + 'OBJECT', + 'RELOP', + 'SWITCH', + 'WITH', + 'WHILE', + 'DO', + 'FOR', + 'COMMA', + 'DEC', + 'BREAK', + 'CONTINUE', + 'THROW', + 'RETURN', + 'UNARYOP', + 'LP', + 'NUMBER', + 'RB', + 'STRING', + 'YIELD', # TODO +] +class _Kind(str): + def __repr__(self): + return 'kind.%s' % self + +class _Kinds: + def __init__(self): + for kind in _KINDS: + setattr(self, kind, _Kind(kind)) + def contains(self, item): + return isinstance(item, _Kind) and \ + getattr(self, item) is item +kind = _Kinds() Added: trunk/jsengine/parser/_constants_op.py =================================================================== --- trunk/jsengine/parser/_constants_op.py (rev 0) +++ trunk/jsengine/parser/_constants_op.py 2013-09-28 03:06:19 UTC (rev 304) @@ -0,0 +1,85 @@ +# vim: sw=4 ts=4 et + +_OPS = [ + 'ADD', + 'AND', + 'ANONFUNOBJ', + 'ARGNAME', + 'BITAND', + 'BITNOT', + 'BITOR', + 'BITXOR', + 'CALL', + 'C_COMMENT', + 'CLOSURE', + 'CPP_COMMENT', + 'DECNAME', + 'DEFVAR', + 'DIV', + 'EQOP', + 'FALSE', + 'FORIN', + 'GETELEM', + 'GETPROP', + 'GT', + 'GE', + 'HOOK', + 'HTMLCOMMENT', + 'IN', + 'INCNAME', + 'INSTANCEOF', + 'LEAVEBLOCK', + 'LSH', + 'LT', + 'LE', + 'MOD', + 'MUL', + 'NAME', + 'NAMEDEC', + 'NAMEINC', + 'NAMEDFUNOBJ', + 'NEG', + 'NE', + 'NEW', + 'NEW_EQ', + 'NEW_NE', + 'NOT', + 'NULL', + 'NUMBER', + 'OR', + 'POS', + 'PROPINC', + 'REGEXP', + 'RSH', + 'SETCALL', + 'SETELEM', + 'SETNAME', + 'SETPROP', + 'STRING', + 'SUB', + 'THIS', + 'TRUE', + 'THROW', + 'TYPEOF', + 'URSH', + 'VOID', + 'EQ', + 'NAME', + 'REGEXP', + 'SETNAME', + 'VOID', + 'CALL', +] +class _Op(str): + def __repr__(self): + return 'op.%s' % self + +class _Ops: + NOP = None # TODO! + def __init__(self): + for op in _OPS: + setattr(self, op, _Op(op)) + def contains(self, item): + return isinstance(item, _Op) and \ + getattr(self, item) is item +op = _Ops() Added: trunk/jsengine/structs.py =================================================================== --- trunk/jsengine/structs.py (rev 0) +++ trunk/jsengine/structs.py 2013-09-28 03:06:19 UTC (rev 304) @@ -0,0 +1,196 @@ +# vim: ts=4 sw=4 expandtab +import bisect +import functools + +from parser._constants_kind import kind +from parser._constants_op import op + +class NodePositions: + " Given a string, allows [x] lookups for NodePos line and col... [truncated message content] |
From: <mat...@us...> - 2013-09-28 03:38:26
|
Revision: 305 http://sourceforge.net/p/javascriptlint/code/305 Author: matthiasmiller Date: 2013-09-28 03:38:24 +0000 (Sat, 28 Sep 2013) Log Message: ----------- #41 python re-write doesn't support option explicit Move Edit Fix traceback mentioned in comment regarding invalid control comments Modified Paths: -------------- trunk/javascriptlint/lint.py trunk/tests/control_comments/control_comments.js Modified: trunk/javascriptlint/lint.py =================================================================== --- trunk/javascriptlint/lint.py 2013-09-28 03:06:19 UTC (rev 304) +++ trunk/javascriptlint/lint.py 2013-09-28 03:38:24 UTC (rev 305) @@ -54,29 +54,28 @@ control_comment = comment.atom[1:-1] else: return None + control_comment = control_comment.lower().rstrip() - control_comments = { - 'ignoreall': (False), - 'ignore': (False), - 'end': (False), - 'option explicit': (False), - 'import': (True), - 'fallthru': (False), - 'pass': (False), - 'declare': (True), - 'unused': (True), - 'content-type': (True), - } - if control_comment.lower() in control_comments: - keyword = control_comment.lower() - else: - keyword = control_comment.lower().split()[0] - if not keyword in control_comments: - return None + keywords = ( + 'ignoreall', + 'ignore', + 'end', + 'option explicit', + 'import', + 'fallthru', + 'pass', + 'declare', + 'unused', + 'content-type', + ) + for keyword in keywords: + # The keyword must either match or be separated by a space. + if control_comment == keyword or \ + (control_comment.startswith(keyword) and \ + control_comment[len(keyword)].isspace()): + parms = control_comment[len(keyword):].strip() + return (comment, keyword, parms.strip()) - parms = control_comment[len(keyword):].strip() - return (comment, keyword, parms) - class Scope: """ Outer-level scopes will never be associated with a node. Inner-level scopes will always be associated with a node. Modified: trunk/tests/control_comments/control_comments.js =================================================================== --- trunk/tests/control_comments/control_comments.js 2013-09-28 03:06:19 UTC (rev 304) +++ trunk/tests/control_comments/control_comments.js 2013-09-28 03:38:24 UTC (rev 305) @@ -29,5 +29,15 @@ /* illegal - don't forget to end */ /*jsl:ignore*/ /*warning:mismatch_ctrl_comments*/ + + // The following are illegal. Make sure jsl doesn't choke. + /*jsl:*/ /*warning:jsl_cc_not_understood*/ + if (a) + { + /*jsl:pass */ + } + /*jsl:ignoreal*/ /*warning:jsl_cc_not_understood*/ + /*jsl:declarebogus*/ /*warning:jsl_cc_not_understood*/ + /*jsl:declare bogus */ } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2013-09-28 03:45:40
|
Revision: 306 http://sourceforge.net/p/javascriptlint/code/306 Author: matthiasmiller Date: 2013-09-28 03:45:38 +0000 (Sat, 28 Sep 2013) Log Message: ----------- #45 jsl: declarations should allow for surrounding spaces Modified Paths: -------------- trunk/javascriptlint/lint.py trunk/tests/control_comments/control_comments.js Modified: trunk/javascriptlint/lint.py =================================================================== --- trunk/javascriptlint/lint.py 2013-09-28 03:38:24 UTC (rev 305) +++ trunk/javascriptlint/lint.py 2013-09-28 03:45:38 UTC (rev 306) @@ -48,13 +48,13 @@ def _parse_control_comment(comment): """ Returns None or (keyword, parms) """ - if comment.atom.lower().startswith('jsl:'): - control_comment = comment.atom[4:] - elif comment.atom.startswith('@') and comment.atom.endswith('@'): - control_comment = comment.atom[1:-1] + comment_atom = comment.atom.lower().strip() + if comment_atom.startswith('jsl:'): + control_comment = comment_atom[4:] + elif comment_atom.startswith('@') and comment_atom.endswith('@'): + control_comment = comment_atom[1:-1] else: return None - control_comment = control_comment.lower().rstrip() keywords = ( 'ignoreall', Modified: trunk/tests/control_comments/control_comments.js =================================================================== --- trunk/tests/control_comments/control_comments.js 2013-09-28 03:38:24 UTC (rev 305) +++ trunk/tests/control_comments/control_comments.js 2013-09-28 03:45:38 UTC (rev 306) @@ -32,10 +32,12 @@ // The following are illegal. Make sure jsl doesn't choke. /*jsl:*/ /*warning:jsl_cc_not_understood*/ - if (a) - { + if (a) { /*jsl:pass */ } + else if (b) { + /* jsl:pass */ //allow spaces on both sides + } /*jsl:ignoreal*/ /*warning:jsl_cc_not_understood*/ /*jsl:declarebogus*/ /*warning:jsl_cc_not_understood*/ /*jsl:declare bogus */ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2013-09-28 04:08:33
|
Revision: 308 http://sourceforge.net/p/javascriptlint/code/308 Author: matthiasmiller Date: 2013-09-28 04:08:31 +0000 (Sat, 28 Sep 2013) Log Message: ----------- #42 Failure when input file is not utf-8 encoded Allow passing the input file encoding on the command line. Modified Paths: -------------- trunk/javascriptlint/conf.py trunk/javascriptlint/fs.py trunk/javascriptlint/jsl.py trunk/javascriptlint/lint.py trunk/test.py Modified: trunk/javascriptlint/conf.py =================================================================== --- trunk/javascriptlint/conf.py 2013-09-28 03:50:06 UTC (rev 307) +++ trunk/javascriptlint/conf.py 2013-09-28 04:08:31 UTC (rev 308) @@ -187,7 +187,7 @@ def loadfile(self, path): path = os.path.abspath(path) - conf = fs.readfile(path) + conf = fs.readfile(path, 'utf-8') try: self.loadtext(conf, dir=os.path.dirname(path)) except ConfError, error: Modified: trunk/javascriptlint/fs.py =================================================================== --- trunk/javascriptlint/fs.py 2013-09-28 03:50:06 UTC (rev 307) +++ trunk/javascriptlint/fs.py 2013-09-28 04:08:31 UTC (rev 308) @@ -2,8 +2,8 @@ import codecs import os -def readfile(path): - file = codecs.open(path, 'r', 'utf-8') +def readfile(path, encoding): + file = codecs.open(path, 'r', encoding) contents = file.read() if contents and contents[0] == unicode(codecs.BOM_UTF8, 'utf8'): contents = contents[1:] Modified: trunk/javascriptlint/jsl.py =================================================================== --- trunk/javascriptlint/jsl.py 2013-09-28 03:50:06 UTC (rev 307) +++ trunk/javascriptlint/jsl.py 2013-09-28 04:08:31 UTC (rev 308) @@ -20,17 +20,17 @@ 'errors': 0 } -def _dump(paths): +def _dump(paths, encoding): for path in paths: - script = fs.readfile(path) + script = fs.readfile(path, encoding) jsparse.dump_tree(script) -def _lint(paths, conf_, printpaths): +def _lint(paths, conf_, printpaths, encoding): def lint_error(path, line, col, errname, errdesc): _lint_results['warnings'] = _lint_results['warnings'] + 1 print util.format_error(conf_['output-format'], path, line, col, errname, errdesc) - lint.lint_files(paths, lint_error, conf=conf_, printpaths=printpaths) + lint.lint_files(paths, lint_error, encoding, conf=conf_, printpaths=printpaths) def _resolve_paths(path, recurse): # Build a list of directories @@ -97,6 +97,8 @@ help="suppress lint summary") add("--help:conf", dest="showdefaultconf", action="store_true", default=False, help="display the default configuration file") + add("--encoding", dest="encoding", metavar="ENCODING", default="utf-8", + help="encoding for input file(s)") parser.set_defaults(verbosity=1) options, args = parser.parse_args() @@ -138,9 +140,9 @@ else: paths.append(arg) if options.dump: - profile_func(_dump, paths) + profile_func(_dump, paths, options.encoding) else: - profile_func(_lint, paths, conf_, options.printlisting) + profile_func(_lint, paths, conf_, options.printlisting, options.encoding) if options.printsummary: print '\n%i error(s), %i warnings(s)' % (_lint_results['errors'], Modified: trunk/javascriptlint/lint.py =================================================================== --- trunk/javascriptlint/lint.py 2013-09-28 03:50:06 UTC (rev 307) +++ trunk/javascriptlint/lint.py 2013-09-28 04:08:31 UTC (rev 308) @@ -286,14 +286,14 @@ else: assert False, 'Invalid internal tag type %s' % tag['type'] -def lint_files(paths, lint_error, conf=conf.Conf(), printpaths=True): - def lint_file(path, kind, jsversion): +def lint_files(paths, lint_error, encoding, conf=conf.Conf(), printpaths=True): + def lint_file(path, kind, jsversion, encoding): def import_script(import_path, jsversion): # The user can specify paths using backslashes (such as when # linting Windows scripts on a posix environment. import_path = import_path.replace('\\', os.sep) import_path = os.path.join(os.path.dirname(path), import_path) - return lint_file(import_path, 'js', jsversion) + return lint_file(import_path, 'js', jsversion, encoding) def _lint_error(*args): return lint_error(normpath, *args) @@ -302,7 +302,7 @@ return lint_cache[normpath] if printpaths: print normpath - contents = fs.readfile(path) + contents = fs.readfile(path, encoding) lint_cache[normpath] = _Script() script_parts = [] @@ -334,9 +334,9 @@ for path in paths: ext = os.path.splitext(path)[1] if ext.lower() in ['.htm', '.html']: - lint_file(path, 'html', None) + lint_file(path, 'html', None, encoding) else: - lint_file(path, 'js', None) + lint_file(path, 'js', None, encoding) def _lint_script_part(scriptpos, jsversion, script, script_cache, conf, ignores, report_native, report_lint, import_callback): Modified: trunk/test.py =================================================================== --- trunk/test.py 2013-09-28 03:50:06 UTC (rev 307) +++ trunk/test.py 2013-09-28 04:08:31 UTC (rev 308) @@ -61,7 +61,7 @@ else: unexpected_warnings.append(warning + (errdesc,)) - javascriptlint.lint.lint_files([path], lint_error, conf=conf) + javascriptlint.lint.lint_files([path], lint_error, 'utf-8', conf=conf) errors = [] if expected_warnings: This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2013-09-28 04:17:43
|
Revision: 309 http://sourceforge.net/p/javascriptlint/code/309 Author: matthiasmiller Date: 2013-09-28 04:17:38 +0000 (Sat, 28 Sep 2013) Log Message: ----------- #39 require variable names in "for .. in" statements Modified Paths: -------------- trunk/javascriptlint/warnings.py Added Paths: ----------- trunk/tests/warnings/for_in_missing_identifier.js Modified: trunk/javascriptlint/warnings.py =================================================================== --- trunk/javascriptlint/warnings.py 2013-09-28 04:08:31 UTC (rev 308) +++ trunk/javascriptlint/warnings.py 2013-09-28 04:17:38 UTC (rev 309) @@ -100,6 +100,7 @@ 'anon_no_return_value': 'anonymous function does not always return value', 'unsupported_version': 'JavaScript {version} is not supported', 'incorrect_version': 'Expected /*jsl:content-type*/ control comment. The script was parsed with the wrong version.', + 'for_in_missing_identifier': 'for..in should have identifier on left side', } errors = { @@ -587,6 +588,13 @@ if not node.fn_name: _check_return_value(node) +@lookfor((tok.FOR, op.FORIN)) +def for_in_missing_identifier(node): + assert node.kids[0].kind == tok.IN + left, right = node.kids[0].kids + if not left.kind in (tok.VAR, tok.NAME): + raise LintWarning, left + @lookfor() def mismatch_ctrl_comments(node): pass Added: trunk/tests/warnings/for_in_missing_identifier.js =================================================================== --- trunk/tests/warnings/for_in_missing_identifier.js (rev 0) +++ trunk/tests/warnings/for_in_missing_identifier.js 2013-09-28 04:17:38 UTC (rev 309) @@ -0,0 +1,12 @@ +/*jsl:option explicit*/ +function for_in_missing_identifier(o) { + var prop; + for (prop in o) + o[prop]++; + + for (var prop2 in o) + o[prop2]++; + + for (!prop in o) /*warning:for_in_missing_identifier*/ + o[prop]++; +} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2013-09-28 04:30:14
|
Revision: 311 http://sourceforge.net/p/javascriptlint/code/311 Author: matthiasmiller Date: 2013-09-28 04:30:11 +0000 (Sat, 28 Sep 2013) Log Message: ----------- Show test results when complete. Modified Paths: -------------- trunk/test.py trunk/tests/warnings/for_in_missing_identifier.js Modified: trunk/test.py =================================================================== --- trunk/test.py 2013-09-28 04:27:44 UTC (rev 310) +++ trunk/test.py 2013-09-28 04:30:11 UTC (rev 311) @@ -100,6 +100,11 @@ except TestError, error: haderrors = True print error + + if haderrors: + print '\nOne or more tests failed!' + else: + print '\nAll tests passed successfully.' sys.exit(haderrors) if __name__ == '__main__': Modified: trunk/tests/warnings/for_in_missing_identifier.js =================================================================== --- trunk/tests/warnings/for_in_missing_identifier.js 2013-09-28 04:27:44 UTC (rev 310) +++ trunk/tests/warnings/for_in_missing_identifier.js 2013-09-28 04:30:11 UTC (rev 311) @@ -1,12 +1,12 @@ /*jsl:option explicit*/ function for_in_missing_identifier(o) { - var prop; - for (prop in o) - o[prop]++; - - for (var prop2 in o) - o[prop2]++; + var prop; + for (prop in o) + o[prop]++; + + for (var prop2 in o) + o[prop2]++; - for (!prop in o) /*warning:for_in_missing_identifier*/ - o[prop]++; + for (!prop in o) /*warning:for_in_missing_identifier*/ + o[prop]++; } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2013-09-28 04:44:08
|
Revision: 312 http://sourceforge.net/p/javascriptlint/code/312 Author: matthiasmiller Date: 2013-09-28 04:44:05 +0000 (Sat, 28 Sep 2013) Log Message: ----------- #38 case with continue results in 'missing break statement' Treat CONTINUE as an exit point Modified Paths: -------------- trunk/javascriptlint/warnings.py trunk/tests/warnings/missing_break.js Modified: trunk/javascriptlint/warnings.py =================================================================== --- trunk/javascriptlint/warnings.py 2013-09-28 04:30:11 UTC (rev 311) +++ trunk/javascriptlint/warnings.py 2013-09-28 04:44:05 UTC (rev 312) @@ -202,6 +202,8 @@ exit_points.add(None) elif node.kind == tok.BREAK: exit_points = set([node]) + elif node.kind == tok.CONTINUE: + exit_points = set([node]) elif node.kind == tok.WITH: exit_points = _get_exit_points(node.kids[-1]) elif node.kind == tok.RETURN: Modified: trunk/tests/warnings/missing_break.js =================================================================== --- trunk/tests/warnings/missing_break.js 2013-09-28 04:30:11 UTC (rev 311) +++ trunk/tests/warnings/missing_break.js 2013-09-28 04:44:05 UTC (rev 312) @@ -102,5 +102,18 @@ break; } + for (;;) { + switch(i) { + case 1: + i++; + continue; + case 2: + if (i) + continue; + default: /*warning:missing_break*/ + break; + } + } + return ""; } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2013-09-28 05:00:33
|
Revision: 315 http://sourceforge.net/p/javascriptlint/code/315 Author: matthiasmiller Date: 2013-09-28 05:00:30 +0000 (Sat, 28 Sep 2013) Log Message: ----------- Warn against multiple useless comparison (e.g. a == a == b) Modified Paths: -------------- trunk/javascriptlint/warnings.py trunk/tests/warnings/useless_comparison.js Modified: trunk/javascriptlint/warnings.py =================================================================== --- trunk/javascriptlint/warnings.py 2013-09-28 04:50:25 UTC (rev 314) +++ trunk/javascriptlint/warnings.py 2013-09-28 05:00:30 UTC (rev 315) @@ -14,6 +14,7 @@ if questionable: raise LintWarning, node """ +import itertools import re import sys import types @@ -287,9 +288,9 @@ @lookfor(tok.EQOP,tok.RELOP) def useless_comparison(node): - lvalue, rvalue = node.kids - if lvalue.is_equivalent(rvalue): - raise LintWarning, node + for lvalue, rvalue in itertools.combinations(node.kids, 2): + if lvalue.is_equivalent(rvalue): + raise LintWarning, node @lookfor((tok.COLON, op.NAME)) def use_of_label(node): Modified: trunk/tests/warnings/useless_comparison.js =================================================================== --- trunk/tests/warnings/useless_comparison.js 2013-09-28 04:50:25 UTC (rev 314) +++ trunk/tests/warnings/useless_comparison.js 2013-09-28 05:00:30 UTC (rev 315) @@ -52,4 +52,12 @@ if (useless_comparison() == useless_comparison()) { return; } + + // Test multiple comparisons. + if (i == i == 3) /*warning:useless_comparison*/ + return; + if (i == 3 == i) /*warning:useless_comparison*/ + return; + if (i == 3 == j == 3) /*warning:useless_comparison*/ + return;} } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2013-09-28 19:39:24
|
Revision: 316 http://sourceforge.net/p/javascriptlint/code/316 Author: matthiasmiller Date: 2013-09-28 19:39:16 +0000 (Sat, 28 Sep 2013) Log Message: ----------- Fix unexpected-eof warning. Modified Paths: -------------- trunk/javascriptlint/warnings.py trunk/jsengine/__init__.py trunk/jsengine/parser/__init__.py trunk/jsengine/tokenizer/__init__.py trunk/tests/warnings/useless_comparison.js Modified: trunk/javascriptlint/warnings.py =================================================================== --- trunk/javascriptlint/warnings.py 2013-09-28 05:00:30 UTC (rev 315) +++ trunk/javascriptlint/warnings.py 2013-09-28 19:39:16 UTC (rev 316) @@ -110,6 +110,7 @@ 'syntax_error': 'syntax error', 'expected_tok': 'expected token: {token}', 'unexpected_char': 'unexpected character: {char}', + 'unexpected_eof': 'unexpected end of file', } def format_error(errname, **errargs): Modified: trunk/jsengine/__init__.py =================================================================== --- trunk/jsengine/__init__.py 2013-09-28 05:00:30 UTC (rev 315) +++ trunk/jsengine/__init__.py 2013-09-28 19:39:16 UTC (rev 316) @@ -1,7 +1,7 @@ # vim: sw=4 ts=4 et _MESSAGES = ( - 'eof', + 'unexpected_eof', 'semi_before_stmnt', 'syntax_error', 'unterminated_comment', Modified: trunk/jsengine/parser/__init__.py =================================================================== --- trunk/jsengine/parser/__init__.py 2013-09-28 05:00:30 UTC (rev 315) +++ trunk/jsengine/parser/__init__.py 2013-09-28 19:39:16 UTC (rev 316) @@ -781,7 +781,7 @@ elif x.tok == tok.TRY: return _try_statement(t) elif x.tok == tok.EOF: - raise JSSyntaxError(x.startpos, 'eof') + raise JSSyntaxError(x.startpos, 'unexpected_eof') elif x.tok == tok.FUNCTION: return _function_declaration(t, op.CLOSURE) #TODO: warn, since this is not reliable @@ -844,7 +844,7 @@ try: parsestring(script) except JSSyntaxError as error: - return error.msg not in ('eof', 'unterminated_comment') + return error.msg not in ('unexpected_eof', 'unterminated_comment') return True class TestParser(unittest.TestCase): Modified: trunk/jsengine/tokenizer/__init__.py =================================================================== --- trunk/jsengine/tokenizer/__init__.py 2013-09-28 05:00:30 UTC (rev 315) +++ trunk/jsengine/tokenizer/__init__.py 2013-09-28 19:39:16 UTC (rev 316) @@ -176,7 +176,7 @@ if self._pos < len(self._content): self._pos += 1 return self._content[self._pos - 1] - raise JSSyntaxError(self.getpos(-1), 'eof') + raise JSSyntaxError(self.getpos(-1), 'unexpected_eof') def readif(self, len_, seq): s = self.peekif(len_, seq) Modified: trunk/tests/warnings/useless_comparison.js =================================================================== --- trunk/tests/warnings/useless_comparison.js 2013-09-28 05:00:30 UTC (rev 315) +++ trunk/tests/warnings/useless_comparison.js 2013-09-28 19:39:16 UTC (rev 316) @@ -59,5 +59,5 @@ if (i == 3 == i) /*warning:useless_comparison*/ return; if (i == 3 == j == 3) /*warning:useless_comparison*/ - return;} + return; } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2013-09-30 20:54:01
|
Revision: 317 http://sourceforge.net/p/javascriptlint/code/317 Author: matthiasmiller Date: 2013-09-30 20:53:58 +0000 (Mon, 30 Sep 2013) Log Message: ----------- Fix number parsing. Modified Paths: -------------- trunk/jsengine/structs.py trunk/jsengine/tokenizer/__init__.py trunk/tests/warnings/useless_comparison.js Modified: trunk/jsengine/structs.py =================================================================== --- trunk/jsengine/structs.py 2013-09-28 19:39:16 UTC (rev 316) +++ trunk/jsengine/structs.py 2013-09-30 20:53:58 UTC (rev 317) @@ -125,7 +125,12 @@ else: assert fn_args is None if self.kind == kind.NUMBER: - self.dval = float(self.atom) + if self.atom.lower().startswith('0x'): + self.dval = int(self.atom, 16) + elif self.atom.startswith('0') and self.atom.isdigit(): + self.dval = int(self.atom, 8) + else: + self.dval = float(self.atom) def start_pos(self): return self.startpos Modified: trunk/jsengine/tokenizer/__init__.py =================================================================== --- trunk/jsengine/tokenizer/__init__.py 2013-09-28 19:39:16 UTC (rev 316) +++ trunk/jsengine/tokenizer/__init__.py 2013-09-30 20:53:58 UTC (rev 317) @@ -378,27 +378,32 @@ s = c # TODO stream.watch_reads() if c == '0' and stream.readif(1, 'xX'): + # Hex while stream.readif(1, _HEX_DIGITS): pass - return Token(tok.NUMBER, atom=stream.get_watched_reads()) - - if c != '.': + elif c == '0' and stream.readif(1, _DIGITS): + # Octal while stream.readif(1, _DIGITS): pass - stream.readif(1, '.') + else: + # Decimal + if c != '.': + while stream.readif(1, _DIGITS): + pass + stream.readif(1, '.') - while stream.readif(1, _DIGITS): - pass - - if stream.readif(1, 'eE'): - stream.readif(1, '+-') - stream.require(_DIGITS) while stream.readif(1, _DIGITS): pass - if stream.peekchr(_IDENT): - return Token(tok.ERROR) + if stream.readif(1, 'eE'): + stream.readif(1, '+-') + stream.require(_DIGITS) + while stream.readif(1, _DIGITS): + pass + if stream.peekchr(_IDENT): + return Token(tok.ERROR) + atom = s + stream.get_watched_reads() return Token(tok.NUMBER, atom=atom) Modified: trunk/tests/warnings/useless_comparison.js =================================================================== --- trunk/tests/warnings/useless_comparison.js 2013-09-28 19:39:16 UTC (rev 316) +++ trunk/tests/warnings/useless_comparison.js 2013-09-30 20:53:58 UTC (rev 317) @@ -60,4 +60,10 @@ return; if (i == 3 == j == 3) /*warning:useless_comparison*/ return; + + // Test bases + if (010 == 8) /*warning:useless_comparison*/ /*warning:octal_number*/ + return; + if (0xA == 10) /*warning:useless_comparison*/ + return; } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2013-09-30 20:59:58
|
Revision: 318 http://sourceforge.net/p/javascriptlint/code/318 Author: matthiasmiller Date: 2013-09-30 20:59:56 +0000 (Mon, 30 Sep 2013) Log Message: ----------- Fix case preservation in control comments. Modified Paths: -------------- trunk/javascriptlint/lint.py trunk/tests/warnings/unreferenced_identifier.js Modified: trunk/javascriptlint/lint.py =================================================================== --- trunk/javascriptlint/lint.py 2013-09-30 20:53:58 UTC (rev 317) +++ trunk/javascriptlint/lint.py 2013-09-30 20:59:56 UTC (rev 318) @@ -48,8 +48,8 @@ def _parse_control_comment(comment): """ Returns None or (keyword, parms) """ - comment_atom = comment.atom.lower().strip() - if comment_atom.startswith('jsl:'): + comment_atom = comment.atom.strip() + if comment_atom.lower().startswith('jsl:'): control_comment = comment_atom[4:] elif comment_atom.startswith('@') and comment_atom.endswith('@'): control_comment = comment_atom[1:-1] @@ -70,8 +70,8 @@ ) for keyword in keywords: # The keyword must either match or be separated by a space. - if control_comment == keyword or \ - (control_comment.startswith(keyword) and \ + if control_comment.lower() == keyword or \ + (control_comment.lower().startswith(keyword) and \ control_comment[len(keyword)].isspace()): parms = control_comment[len(keyword):].strip() return (comment, keyword, parms.strip()) Modified: trunk/tests/warnings/unreferenced_identifier.js =================================================================== --- trunk/tests/warnings/unreferenced_identifier.js 2013-09-30 20:53:58 UTC (rev 317) +++ trunk/tests/warnings/unreferenced_identifier.js 2013-09-30 20:59:56 UTC (rev 318) @@ -98,5 +98,9 @@ } } + function test_unused_camel_case(CamelCaseParm) { /*warning:unreferenced_function*/ + /*jsl:unused CamelCaseParm*/ + } + return get_callback(42); } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2013-09-30 21:03:26
|
Revision: 319 http://sourceforge.net/p/javascriptlint/code/319 Author: matthiasmiller Date: 2013-09-30 21:03:23 +0000 (Mon, 30 Sep 2013) Log Message: ----------- Allow chained assigns. Modified Paths: -------------- trunk/javascriptlint/warnings.py trunk/tests/warnings/equal_as_assign.js Modified: trunk/javascriptlint/warnings.py =================================================================== --- trunk/javascriptlint/warnings.py 2013-09-30 20:59:56 UTC (rev 318) +++ trunk/javascriptlint/warnings.py 2013-09-30 21:03:23 UTC (rev 319) @@ -324,7 +324,8 @@ @lookfor(tok.ASSIGN) def equal_as_assign(node): - if not node.parent.kind in (tok.SEMI, tok.RESERVED, tok.RP, tok.COMMA): + if not node.parent.kind in (tok.SEMI, tok.RESERVED, tok.RP, tok.COMMA, + tok.ASSIGN): raise LintWarning, node @lookfor(tok.IF) Modified: trunk/tests/warnings/equal_as_assign.js =================================================================== --- trunk/tests/warnings/equal_as_assign.js 2013-09-30 20:59:56 UTC (rev 318) +++ trunk/tests/warnings/equal_as_assign.js 2013-09-30 21:03:23 UTC (rev 319) @@ -4,4 +4,7 @@ while (a = b) { /*warning:equal_as_assign*/ a++; } + + var c; + a = b = c; } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2013-09-30 21:11:48
|
Revision: 320 http://sourceforge.net/p/javascriptlint/code/320 Author: matthiasmiller Date: 2013-09-30 21:11:45 +0000 (Mon, 30 Sep 2013) Log Message: ----------- Allow chained assignments in VAR statements. Modified Paths: -------------- trunk/javascriptlint/warnings.py trunk/tests/warnings/equal_as_assign.js Modified: trunk/javascriptlint/warnings.py =================================================================== --- trunk/javascriptlint/warnings.py 2013-09-30 21:03:23 UTC (rev 319) +++ trunk/javascriptlint/warnings.py 2013-09-30 21:11:45 UTC (rev 320) @@ -324,6 +324,10 @@ @lookfor(tok.ASSIGN) def equal_as_assign(node): + # Allow in VAR statements. + if node.parent.parent and node.parent.parent.kind == tok.VAR: + return + if not node.parent.kind in (tok.SEMI, tok.RESERVED, tok.RP, tok.COMMA, tok.ASSIGN): raise LintWarning, node Modified: trunk/tests/warnings/equal_as_assign.js =================================================================== --- trunk/tests/warnings/equal_as_assign.js 2013-09-30 21:03:23 UTC (rev 319) +++ trunk/tests/warnings/equal_as_assign.js 2013-09-30 21:11:45 UTC (rev 320) @@ -7,4 +7,7 @@ var c; a = b = c; + + var x, y, z; + var w = x = y = z; } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2013-09-30 21:19:29
|
Revision: 321 http://sourceforge.net/p/javascriptlint/code/321 Author: matthiasmiller Date: 2013-09-30 21:19:27 +0000 (Mon, 30 Sep 2013) Log Message: ----------- Don't warning against increment/decrement assignments in expressions. Modified Paths: -------------- trunk/javascriptlint/warnings.py trunk/tests/warnings/equal_as_assign.js Modified: trunk/javascriptlint/warnings.py =================================================================== --- trunk/javascriptlint/warnings.py 2013-09-30 21:11:45 UTC (rev 320) +++ trunk/javascriptlint/warnings.py 2013-09-30 21:19:27 UTC (rev 321) @@ -322,7 +322,7 @@ if kid.kind == tok.LP: raise LintWarning, node -@lookfor(tok.ASSIGN) +@lookfor((tok.ASSIGN, None)) def equal_as_assign(node): # Allow in VAR statements. if node.parent.parent and node.parent.parent.kind == tok.VAR: Modified: trunk/tests/warnings/equal_as_assign.js =================================================================== --- trunk/tests/warnings/equal_as_assign.js 2013-09-30 21:11:45 UTC (rev 320) +++ trunk/tests/warnings/equal_as_assign.js 2013-09-30 21:19:27 UTC (rev 321) @@ -4,6 +4,9 @@ while (a = b) { /*warning:equal_as_assign*/ a++; } + while (a -= b) { + a--; + } var c; a = b = c; This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2013-09-30 21:54:35
|
Revision: 322 http://sourceforge.net/p/javascriptlint/code/322 Author: matthiasmiller Date: 2013-09-30 21:54:32 +0000 (Mon, 30 Sep 2013) Log Message: ----------- Fix single-line returns without semicolons. Modified Paths: -------------- trunk/jsengine/parser/__init__.py Added Paths: ----------- trunk/tests/bugs/one_line_return.js Modified: trunk/jsengine/parser/__init__.py =================================================================== --- trunk/jsengine/parser/__init__.py 2013-09-30 21:19:27 UTC (rev 321) +++ trunk/jsengine/parser/__init__.py 2013-09-30 21:54:32 UTC (rev 322) @@ -638,7 +638,7 @@ endtoken = t.expect(tok.RETURN) startpos = endtoken.startpos - if t.peek_sameline().tok not in (tok.EOF, tok.EOL, tok.SEMI): + if t.peek_sameline().tok not in (tok.EOF, tok.EOL, tok.SEMI, tok.RBRACE): expr = _expression(t, True) endtoken = expr else: Added: trunk/tests/bugs/one_line_return.js =================================================================== --- trunk/tests/bugs/one_line_return.js (rev 0) +++ trunk/tests/bugs/one_line_return.js 2013-09-30 21:54:32 UTC (rev 322) @@ -0,0 +1,2 @@ +function one_line_return() { return } /*warning:missing_semicolon*/ + This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2013-09-30 23:16:40
|
Revision: 323 http://sourceforge.net/p/javascriptlint/code/323 Author: matthiasmiller Date: 2013-09-30 23:16:37 +0000 (Mon, 30 Sep 2013) Log Message: ----------- Function expressions should not declare a variable. Modified Paths: -------------- trunk/javascriptlint/lint.py trunk/jsengine/parser/__init__.py trunk/tests/control_comments/option_explicit.js Modified: trunk/javascriptlint/lint.py =================================================================== --- trunk/javascriptlint/lint.py 2013-09-30 21:54:32 UTC (rev 322) +++ trunk/javascriptlint/lint.py 2013-09-30 23:16:37 UTC (rev 323) @@ -609,7 +609,7 @@ @visitation.visit('push', tok.FUNCTION) def _push_func(self, node): - if node.fn_name: + if node.opcode != op.CLOSURE and node.fn_name: _warn_or_declare(scopes[-1], node.fn_name, 'function', node, report) self._push_scope(node) for var_name in node.fn_args: Modified: trunk/jsengine/parser/__init__.py =================================================================== --- trunk/jsengine/parser/__init__.py 2013-09-30 21:54:32 UTC (rev 322) +++ trunk/jsengine/parser/__init__.py 2013-09-30 23:16:37 UTC (rev 323) @@ -162,10 +162,8 @@ fn_body_endpos = t.expect(tok.RBRACE).endpos fn_body = ParseNode(kind.LC, None, fn_body_startpos, fn_body_endpos, None, kids) - return ParseNode(kind.FUNCTION, - op.ANONFUNOBJ if fn_name is None else op.NAMEDFUNOBJ, - startpos, fn_body.endpos, - fn_name, [fn_body], fn_args=fn_args) + return ParseNode(kind.FUNCTION, opcode, startpos, fn_body.endpos, + fn_name, [fn_body], fn_args=fn_args) def _argument_list(t): args = [] Modified: trunk/tests/control_comments/option_explicit.js =================================================================== --- trunk/tests/control_comments/option_explicit.js 2013-09-30 21:54:32 UTC (rev 322) +++ trunk/tests/control_comments/option_explicit.js 2013-09-30 23:16:37 UTC (rev 323) @@ -60,5 +60,12 @@ /* illegal */ y(); /*warning:undeclared_identifier*/ + // This should be undeclared because this is an expression, + // not a declaration. + (function func_expr() { /*warning:want_assign_or_call*/ + return 10; + }); + j = func_expr(); + return ""; } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2013-10-01 17:07:15
|
Revision: 324 http://sourceforge.net/p/javascriptlint/code/324 Author: matthiasmiller Date: 2013-10-01 17:07:11 +0000 (Tue, 01 Oct 2013) Log Message: ----------- Correctly handle top-level function declarations. Modified Paths: -------------- trunk/javascriptlint/lint.py trunk/tests/control_comments/option_explicit.js trunk/tests/warnings/redeclared_var.js trunk/tests/warnings/unreferenced_identifier.js Modified: trunk/javascriptlint/lint.py =================================================================== --- trunk/javascriptlint/lint.py 2013-09-30 23:16:37 UTC (rev 323) +++ trunk/javascriptlint/lint.py 2013-10-01 17:07:11 UTC (rev 324) @@ -609,7 +609,7 @@ @visitation.visit('push', tok.FUNCTION) def _push_func(self, node): - if node.opcode != op.CLOSURE and node.fn_name: + if node.opcode in (None, op.CLOSURE) and node.fn_name: _warn_or_declare(scopes[-1], node.fn_name, 'function', node, report) self._push_scope(node) for var_name in node.fn_args: Modified: trunk/tests/control_comments/option_explicit.js =================================================================== --- trunk/tests/control_comments/option_explicit.js 2013-09-30 23:16:37 UTC (rev 323) +++ trunk/tests/control_comments/option_explicit.js 2013-10-01 17:07:11 UTC (rev 324) @@ -65,7 +65,10 @@ (function func_expr() { /*warning:want_assign_or_call*/ return 10; }); - j = func_expr(); + j = func_expr(); /*warning:undeclared_identifier*/ return ""; } + +// Ensure that we can reference top-level functions. +option_explicit(null); Modified: trunk/tests/warnings/redeclared_var.js =================================================================== --- trunk/tests/warnings/redeclared_var.js 2013-09-30 23:16:37 UTC (rev 323) +++ trunk/tests/warnings/redeclared_var.js 2013-10-01 17:07:11 UTC (rev 324) @@ -7,4 +7,9 @@ return; } var myFunction; /*warning:redeclared_var*/ + + // myFunction isn't a redeclaration, since function names in function + // expressions don't matter. + var tmp = function myFunction(){}; + /*jsl:unused tmp*/ } Modified: trunk/tests/warnings/unreferenced_identifier.js =================================================================== --- trunk/tests/warnings/unreferenced_identifier.js 2013-09-30 23:16:37 UTC (rev 323) +++ trunk/tests/warnings/unreferenced_identifier.js 2013-10-01 17:07:11 UTC (rev 324) @@ -74,8 +74,10 @@ tmp = ref_dec--; /*warning:inc_dec_within_stmt*/ tmp = -tmp; - /* Test named functions as references. */ - var fn = function ref_func() { return 42; }; /*warning:unreferenced_function*/ + /* Test named functions as references. + * (The name is ignored since it's a function expression.) + */ + var fn = function ref_func() { return 42; }; fn(); /* Test nested scopes. */ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2013-10-01 17:12:24
|
Revision: 325 http://sourceforge.net/p/javascriptlint/code/325 Author: matthiasmiller Date: 2013-10-01 17:12:20 +0000 (Tue, 01 Oct 2013) Log Message: ----------- First cut at warnings to validate function expression names (disabled by default) Modified Paths: -------------- trunk/javascriptlint/conf.py trunk/javascriptlint/warnings.py trunk/tests/control_comments/option_explicit.js trunk/tests/warnings/want_assign_or_call.js Added Paths: ----------- trunk/tests/warnings/function_name_mismatch.js trunk/tests/warnings/function_name_missing.js trunk/tests/warnings/misplaced_function.js Modified: trunk/javascriptlint/conf.py =================================================================== --- trunk/javascriptlint/conf.py 2013-10-01 17:07:11 UTC (rev 324) +++ trunk/javascriptlint/conf.py 2013-10-01 17:12:20 UTC (rev 325) @@ -6,12 +6,18 @@ import util import warnings +_DISABLED_WARNINGS = ( + 'block_without_braces', + 'function_name_missing', + 'function_name_mismatch', +) + def _getwarningsconf(): lines = [] for name in sorted(warnings.warnings.keys()): message = warnings.warnings[name] sign = '+' - if name == 'block_without_braces': + if name in _DISABLED_WARNINGS: sign = '-' assert len(name) < 29 lines.append(sign + name.ljust(29) + '# ' + message) @@ -183,7 +189,8 @@ } for name in warnings.warnings: self._settings[name] = BooleanSetting(True) - self.loadline('-block_without_braces') + for warning in _DISABLED_WARNINGS: + self.loadline('-%s' % warning) def loadfile(self, path): path = os.path.abspath(path) Modified: trunk/javascriptlint/warnings.py =================================================================== --- trunk/javascriptlint/warnings.py 2013-10-01 17:07:11 UTC (rev 324) +++ trunk/javascriptlint/warnings.py 2013-10-01 17:12:20 UTC (rev 325) @@ -102,6 +102,9 @@ 'unsupported_version': 'JavaScript {version} is not supported', 'incorrect_version': 'Expected /*jsl:content-type*/ control comment. The script was parsed with the wrong version.', 'for_in_missing_identifier': 'for..in should have identifier on left side', + 'misplaced_function': 'unconventional use of function expression', + 'function_name_missing': 'anonymous function should be named to match property name {name}', + 'function_name_mismatch': 'function name {fn_name} does not match property name {prop_name}', } errors = { @@ -604,6 +607,87 @@ if not left.kind in (tok.VAR, tok.NAME): raise LintWarning, left +@lookfor(tok.FUNCTION) +def misplaced_function(node): + # Ignore function statements. + if node.opcode in (None, op.CLOSURE): + return + + # Ignore parens. + parent = node.parent + while parent.kind == tok.RP: + parent = parent.parent + + # Allow x = x || ... + if parent.kind == tok.OR and len(parent.kids) == 2 and \ + node is parent.kids[-1]: + parent = parent.parent + + if parent.kind == tok.NAME and parent.opcode == op.SETNAME: + return # Allow in var statements + if parent.kind == tok.ASSIGN and parent.opcode == op.NOP: + return # Allow in assigns + if parent.kind == tok.COLON and parent.parent.kind == tok.RC: + return # Allow in object literals + if parent.kind == tok.LP and parent.opcode in (op.CALL, op.SETCALL): + return # Allow in parameters + if parent.kind == tok.RETURN: + return # Allow for return values + if parent.kind == tok.NEW: + return # Allow as constructors + raise LintWarning, node + +def _get_expected_function_name(node): + # Ignore function statements. + if node.opcode in (None, op.CLOSURE): + return + + # Ignore parens. + parent = node.parent + while parent.kind == tok.RP: + parent = parent.parent + + # Allow x = x || ... + if parent.kind == tok.OR and len(parent.kids) == 2 and \ + node is parent.kids[-1]: + parent = parent.parent + + # Var assignment. + if parent.kind == tok.NAME and parent.opcode == op.SETNAME: + return parent.atom + + # Assignment. + if parent.kind == tok.ASSIGN and parent.opcode == op.NOP: + if parent.kids[0].kind == tok.NAME and \ + parent.kids[0].opcode == op.SETNAME: + return parent.kids[0].atom + return '<error>' + + # Object literal. + if parent.kind == tok.COLON and parent.parent.kind == tok.RC: + return parent.kids[0].atom + +@lookfor(tok.FUNCTION) +def function_name_missing(node): + if node.fn_name: + return + + expected_name = _get_expected_function_name(node) + if not expected_name is None: + raise LintWarning(node, name=expected_name) + +@lookfor(tok.FUNCTION) +def function_name_mismatch(node): + if not node.fn_name: + return + + expected_name = _get_expected_function_name(node) + if expected_name is None: + return + + if expected_name != node.fn_name: + raise LintWarning(node, fn_name=node.fn_name, prop_name=expected_name) + @lookfor() def mismatch_ctrl_comments(node): pass Modified: trunk/tests/control_comments/option_explicit.js =================================================================== --- trunk/tests/control_comments/option_explicit.js 2013-10-01 17:07:11 UTC (rev 324) +++ trunk/tests/control_comments/option_explicit.js 2013-10-01 17:12:20 UTC (rev 325) @@ -62,7 +62,7 @@ // This should be undeclared because this is an expression, // not a declaration. - (function func_expr() { /*warning:want_assign_or_call*/ + (function func_expr() { /*warning:misplaced_function*/ /*warning:want_assign_or_call*/ return 10; }); j = func_expr(); /*warning:undeclared_identifier*/ Added: trunk/tests/warnings/function_name_mismatch.js =================================================================== --- trunk/tests/warnings/function_name_mismatch.js (rev 0) +++ trunk/tests/warnings/function_name_mismatch.js 2013-10-01 17:12:20 UTC (rev 325) @@ -0,0 +1,36 @@ +/*conf:+function_name_mismatch*/ +function function_name_mismatch() { + var f = function bogus() { /*warning:function_name_mismatch*/ + }; + var g = function g() { + }; + + f = new function bogus() { + }; + f = new function() { + }; + + f = (function() { + return 10; + })(); + + var o = { + f: function bogus() { /*warning:function_name_mismatch*/ + return null; + } + }; + o.a.b = { + f: function bogus() { /*warning:function_name_mismatch*/ + return null; + } + }; + o.a.b = o.a.b || function bogus() { return 10; }; /*warning:function_name_mismatch*/ + + function closure(a) { + return function() { return a; }; + } + + function x() { + } +} + Added: trunk/tests/warnings/function_name_missing.js =================================================================== --- trunk/tests/warnings/function_name_missing.js (rev 0) +++ trunk/tests/warnings/function_name_missing.js 2013-10-01 17:12:20 UTC (rev 325) @@ -0,0 +1,30 @@ +/*conf:+function_name_missing*/ +function function_name_missing() { + var f = function() { /*warning:function_name_missing*/ + }; + f = new function() { + }; + f = (function() { + return 10; + })(); + + var o = { + f: function() { /*warning:function_name_missing*/ + return null; + } + }; + o.a.b = { + f: function() { /*warning:function_name_missing*/ + return null; + } + }; + o.a.b = o.a.b || function() { return 10; }; /*warning:function_name_missing*/ + + function closure(a) { + return function() { return a; }; + } + + function x() { + } +} + Added: trunk/tests/warnings/misplaced_function.js =================================================================== --- trunk/tests/warnings/misplaced_function.js (rev 0) +++ trunk/tests/warnings/misplaced_function.js 2013-10-01 17:12:20 UTC (rev 325) @@ -0,0 +1,33 @@ +/*conf:-want_assign_or_call*/ +function misplaced_functions() { + var f = function() { + }; + f = new function() { + }; + f = (function() { + return 10; + })(); + + var o = { + f: function() { + return null; + } + }; + o.a.b = { + f: function() { + return null; + } + }; + o.a.b = o.a.b || function() { return 10; }; + o.a.b = o.a.c || function() { return 10; }; /*TODO*/ + + function closure(a) { + return function() { return a; }; + } + + function x() { + } + + function() {}; /*warning:misplaced_function*/ +} + Modified: trunk/tests/warnings/want_assign_or_call.js =================================================================== --- trunk/tests/warnings/want_assign_or_call.js 2013-10-01 17:07:11 UTC (rev 324) +++ trunk/tests/warnings/want_assign_or_call.js 2013-10-01 17:12:20 UTC (rev 325) @@ -14,7 +14,7 @@ function test() { } - function() { /*warning:want_assign_or_call*/ + function() { /*warning:want_assign_or_call*/ /*warning:misplaced_function*/ return 42; }; This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2013-10-02 23:18:33
|
Revision: 327 http://sourceforge.net/p/javascriptlint/code/327 Author: matthiasmiller Date: 2013-10-02 23:18:30 +0000 (Wed, 02 Oct 2013) Log Message: ----------- pylint: fix raise syntax Modified Paths: -------------- trunk/javascriptlint/conf.py trunk/javascriptlint/visitation.py trunk/javascriptlint/warnings.py trunk/setup.py trunk/test.py trunk/www.py Modified: trunk/javascriptlint/conf.py =================================================================== --- trunk/javascriptlint/conf.py 2013-10-02 23:16:54 UTC (rev 326) +++ trunk/javascriptlint/conf.py 2013-10-02 23:18:30 UTC (rev 327) @@ -119,7 +119,7 @@ wants_parm = False value = None def load(self, enabled): - raise ConfError, 'This setting is deprecated.' + raise ConfError('This setting is deprecated.') class BooleanSetting(Setting): wants_parm = False @@ -134,7 +134,7 @@ self.value = default def load(self, enabled, parm): if not enabled: - raise ConfError, 'Expected +.' + raise ConfError('Expected +.') self.value = parm class DeclareSetting(Setting): @@ -143,7 +143,7 @@ self.value = [] def load(self, enabled, parm): if not enabled: - raise ConfError, 'Expected +.' + raise ConfError('Expected +.') self.value.append(parm) class ProcessSetting(Setting): @@ -162,11 +162,11 @@ value = util.JSVersion.default() def load(self, enabled, parm): if not enabled: - raise ConfError, 'Expected +.' + raise ConfError('Expected +.') self.value = util.JSVersion.fromtype(parm) if not self.value: - raise ConfError, 'Invalid JavaScript version: %s' % parm + raise ConfError('Invalid JavaScript version: %s' % parm) class Conf: def __init__(self): @@ -227,7 +227,7 @@ elif line.startswith('-'): enabled = False else: - raise ConfError, 'Expected + or -.' + raise ConfError('Expected + or -.') line = line[1:] # Parse the key/parms @@ -242,7 +242,7 @@ if setting.wants_parm: args['parm'] = parm elif parm: - raise ConfError, 'The %s setting does not expect a parameter.' % name + raise ConfError('The %s setting does not expect a parameter.' % name) if setting.wants_dir: args['dir'] = dir setting.load(**args) Modified: trunk/javascriptlint/visitation.py =================================================================== --- trunk/javascriptlint/visitation.py 2013-10-02 23:16:54 UTC (rev 326) +++ trunk/javascriptlint/visitation.py 2013-10-02 23:18:30 UTC (rev 327) @@ -27,9 +27,9 @@ # Intantiate an instance of each class for klass in klasses: if klass.__name__.lower() != klass.__name__: - raise ValueError, 'class names must be lowercase' + raise ValueError('class names must be lowercase') if not klass.__doc__: - raise ValueError, 'missing docstring on class %s' % klass.__name__ + raise ValueError('missing docstring on class %s' % klass.__name__) # Look for functions with the "_visit_nodes" property. visitor = klass() Modified: trunk/javascriptlint/warnings.py =================================================================== --- trunk/javascriptlint/warnings.py 2013-10-02 23:16:54 UTC (rev 326) +++ trunk/javascriptlint/warnings.py 2013-10-02 23:18:30 UTC (rev 327) @@ -12,7 +12,7 @@ @lookfor(tok.NODEKIND, (tok.NODEKIND, op.OPCODE)) def warning_name(node): if questionable: - raise LintWarning, node + raise LintWarning(node) """ import itertools import re @@ -125,7 +125,7 @@ try: errdesc = re.sub(r"{(\w+)}", lambda match: errargs[match.group(1)], errdesc) except (TypeError, KeyError): - raise KeyError, 'Invalid keyword in error: ' + errdesc + raise KeyError('Invalid keyword in error: ' + errdesc) return errdesc _visitors = [] @@ -253,17 +253,17 @@ def comparison_type_conv(node): for kid in node.kids: if kid.kind == tok.PRIMARY and kid.opcode in (op.NULL, op.TRUE, op.FALSE): - raise LintWarning, kid + raise LintWarning(kid) if kid.kind == tok.NUMBER and not kid.dval: - raise LintWarning, kid + raise LintWarning(kid) if kid.kind == tok.STRING and not kid.atom: - raise LintWarning, kid + raise LintWarning(kid) @lookfor(tok.DEFAULT) def default_not_at_end(node): siblings = node.parent.kids if node.node_index != len(siblings)-1: - raise LintWarning, siblings[node.node_index+1] + raise LintWarning(siblings[node.node_index+1]) @lookfor(tok.CASE) def duplicate_case_in_switch(node): @@ -276,7 +276,7 @@ if sibling.kind == tok.CASE: sibling_value = sibling.kids[0] if node_value.is_equivalent(sibling_value, True): - raise LintWarning, node + raise LintWarning(node) @lookfor(tok.SWITCH) def missing_default_case(node): @@ -284,21 +284,21 @@ for case in cases.kids: if case.kind == tok.DEFAULT: return - raise LintWarning, node + raise LintWarning(node) @lookfor(tok.WITH) def with_statement(node): - raise LintWarning, node + raise LintWarning(node) @lookfor(tok.EQOP,tok.RELOP) def useless_comparison(node): for lvalue, rvalue in itertools.combinations(node.kids, 2): if lvalue.is_equivalent(rvalue): - raise LintWarning, node + raise LintWarning(node) @lookfor((tok.COLON, op.NAME)) def use_of_label(node): - raise LintWarning, node + raise LintWarning(node) @lookfor((tok.OBJECT, op.REGEXP)) def misplaced_regex(node): @@ -314,7 +314,7 @@ return # Allow in /re/.property if node.parent.kind == tok.RETURN: return # Allow for return values - raise LintWarning, node + raise LintWarning(node) @lookfor(tok.ASSIGN) def assign_to_function_call(node): @@ -323,7 +323,7 @@ while kid.kind == tok.RP: kid, = kid.kids if kid.kind == tok.LP: - raise LintWarning, node + raise LintWarning(node) @lookfor((tok.ASSIGN, None)) def equal_as_assign(node): @@ -333,7 +333,7 @@ if not node.parent.kind in (tok.SEMI, tok.RESERVED, tok.RP, tok.COMMA, tok.ASSIGN): - raise LintWarning, node + raise LintWarning(node) @lookfor(tok.IF) def ambiguous_else_stmt(node): @@ -349,13 +349,13 @@ return # Else is only ambiguous in the first branch of an if statement. if tmp.parent.kind == tok.IF and tmp.node_index == 1: - raise LintWarning, else_ + raise LintWarning(else_) tmp = tmp.parent @lookfor(tok.IF, tok.WHILE, tok.DO, tok.FOR, tok.WITH) def block_without_braces(node): if node.kids[1].kind != tok.LC: - raise LintWarning, node.kids[1] + raise LintWarning(node.kids[1]) _block_nodes = (tok.IF, tok.WHILE, tok.DO, tok.FOR, tok.WITH) @lookfor(*_block_nodes) @@ -368,7 +368,7 @@ # was inside a block statement without clarifying curlies. # (Otherwise, the node type would be tok.LC.) if node.parent.kind in _block_nodes: - raise LintWarning, node + raise LintWarning(node) @lookfor(tok.INC, tok.DEC) def inc_dec_within_stmt(node): @@ -384,7 +384,7 @@ tmp.parent.parent.kind == tok.FOR: return - raise LintWarning, node + raise LintWarning(node) @lookfor(tok.COMMA) def comma_separated_stmts(node): @@ -394,12 +394,12 @@ # This is an array if node.parent.kind == tok.RB: return - raise LintWarning, node + raise LintWarning(node) @lookfor(tok.SEMI) def empty_statement(node): if not node.kids[0]: - raise LintWarning, node + raise LintWarning(node) @lookfor(tok.LC) def empty_statement_(node): if node.kids: @@ -410,7 +410,7 @@ # Some empty blocks are meaningful. if node.parent.kind in (tok.CATCH, tok.CASE, tok.DEFAULT, tok.SWITCH, tok.FUNCTION): return - raise LintWarning, node + raise LintWarning(node) @lookfor(tok.CASE, tok.DEFAULT) def missing_break(node): @@ -424,7 +424,7 @@ return if None in _get_exit_points(case_contents): # Show the warning on the *next* node. - raise LintWarning, node.parent.kids[node.node_index+1] + raise LintWarning(node.parent.kids[node.node_index+1]) @lookfor(tok.CASE, tok.DEFAULT) def missing_break_for_last_case(node): @@ -433,16 +433,16 @@ case_contents = node.kids[1] assert case_contents.kind == tok.LC if None in _get_exit_points(case_contents): - raise LintWarning, node + raise LintWarning(node) @lookfor(tok.INC) def multiple_plus_minus(node): if node.node_index == 0 and node.parent.kind == tok.PLUS: - raise LintWarning, node + raise LintWarning(node) @lookfor(tok.DEC) def multiple_plus_minus_(node): if node.node_index == 0 and node.parent.kind == tok.MINUS: - raise LintWarning, node + raise LintWarning(node) @lookfor((tok.NAME, op.SETNAME)) def useless_assign(node): @@ -452,7 +452,7 @@ elif node.parent.kind == tok.VAR: value = node.kids[0] if value and value.kind == tok.NAME and node.atom == value.atom: - raise LintWarning, node + raise LintWarning(node) @lookfor(tok.BREAK, tok.CONTINUE, tok.RETURN, tok.THROW) def unreachable_code(node): @@ -463,12 +463,12 @@ for variable in sibling.kids: value, = variable.kids if value: - raise LintWarning, value + raise LintWarning(value) elif sibling.kind == tok.FUNCTION: # Functions are always declared. pass else: - raise LintWarning, sibling + raise LintWarning(sibling) @lookfor(tok.FOR) def unreachable_code_(node): @@ -478,73 +478,73 @@ pre, condition, post = preamble.kids if post: if not None in _get_exit_points(code): - raise LintWarning, post + raise LintWarning(post) @lookfor(tok.DO) def unreachable_code__(node): # Warn if the do..while loop always exits. code, condition = node.kids if not None in _get_exit_points(code): - raise LintWarning, condition + raise LintWarning(condition) #TODO: @lookfor(tok.IF) def meaningless_block(node): condition, if_, else_ = node.kids if condition.kind == tok.PRIMARY and condition.opcode in (op.TRUE, op.FALSE, op.NULL): - raise LintWarning, condition + raise LintWarning(condition) #TODO: @lookfor(tok.WHILE) def meaningless_blocK_(node): condition = node.kids[0] if condition.kind == tok.PRIMARY and condition.opcode in (op.FALSE, op.NULL): - raise LintWarning, condition + raise LintWarning(condition) @lookfor(tok.LC) def meaningless_block__(node): if node.parent and node.parent.kind == tok.LC: - raise LintWarning, node + raise LintWarning(node) @lookfor((tok.UNARYOP, op.VOID)) def useless_void(node): - raise LintWarning, node + raise LintWarning(node) @lookfor((tok.LP, op.CALL)) def parseint_missing_radix(node): if node.kids[0].kind == tok.NAME and node.kids[0].atom == 'parseInt' and len(node.kids) <= 2: - raise LintWarning, node + raise LintWarning(node) @lookfor(tok.NUMBER) def leading_decimal_point(node): if node.atom.startswith('.'): - raise LintWarning, node + raise LintWarning(node) @lookfor(tok.NUMBER) def trailing_decimal_point(node): if node.parent.kind == tok.DOT: - raise LintWarning, node + raise LintWarning(node) if node.atom.endswith('.'): - raise LintWarning, node + raise LintWarning(node) _octal_regexp = re.compile('^0[0-9]') @lookfor(tok.NUMBER) def octal_number(node): if _octal_regexp.match(node.atom): - raise LintWarning, node + raise LintWarning(node) @lookfor(tok.RC) def trailing_comma(node): if node.end_comma: - raise LintWarning, node + raise LintWarning(node) @lookfor(tok.RB) def trailing_comma_in_array(node): if node.end_comma: - raise LintWarning, node + raise LintWarning(node) @lookfor(tok.STRING) def useless_quotes(node): if node.node_index == 0 and node.parent.kind == tok.COLON: # Only warn if the quotes could safely be removed. if util.isidentifier(node.atom): - raise LintWarning, node + raise LintWarning(node) @lookfor(tok.SEMI) def want_assign_or_call(node): @@ -566,7 +566,7 @@ grandchild = child.kids[0] if grandchild.kind == tok.FUNCTION: return - raise LintWarning, child + raise LintWarning(child) def _check_return_value(node): name = node.fn_name or '(anonymous function)' @@ -605,7 +605,7 @@ assert node.kids[0].kind == tok.IN left, right = node.kids[0].kids if not left.kind in (tok.VAR, tok.NAME): - raise LintWarning, left + raise LintWarning(left) @lookfor(tok.FUNCTION) def misplaced_function(node): @@ -635,7 +635,7 @@ return # Allow for return values if parent.kind == tok.NEW: return # Allow as constructors - raise LintWarning, node + raise LintWarning(node) def _get_expected_function_name(node): # Ignore function statements. @@ -724,7 +724,7 @@ def missing_semicolon(node): if node.no_semi: if not _get_assigned_lambda(node): - raise LintWarning, node + raise LintWarning(node) @lookfor(*_ALL_TOKENS) def missing_semicolon_for_lambda(node): @@ -733,7 +733,7 @@ # statements, so use the position of the lambda instead. lambda_ = _get_assigned_lambda(node) if lambda_: - raise LintWarning, lambda_ + raise LintWarning(lambda_) @lookfor() def ambiguous_newline(node): Modified: trunk/setup.py =================================================================== --- trunk/setup.py 2013-10-02 23:16:54 UTC (rev 326) +++ trunk/setup.py 2013-10-02 23:18:30 UTC (rev 327) @@ -16,7 +16,7 @@ stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = p.communicate() if p.returncode != 0: - raise _BuildError, 'Error running svnversion: %s' % stderr + raise _BuildError('Error running svnversion: %s' % stderr) version = stdout.strip().rstrip('M') return int(version) @@ -48,7 +48,7 @@ for exe in self.console_exe_files: ret = subprocess.call(['upx', '-9', exe]) if ret != 0: - raise _BuildError, 'Error running upx on %s' % exe + raise _BuildError('Error running upx on %s' % exe) args['cmdclass']['py2exe'] = _MyPy2Exe args.update( Modified: trunk/test.py =================================================================== --- trunk/test.py 2013-10-02 23:16:54 UTC (rev 326) +++ trunk/test.py 2013-10-02 23:18:30 UTC (rev 327) @@ -76,7 +76,7 @@ for line, warning, errdesc in unexpected_warnings: errors.append('\tline %i: %s/%s' % (line+1, warning, errdesc)) if errors: - raise TestError, '\n'.join(errors) + raise TestError('\n'.join(errors)) def _get_test_files(): # Get a list of test files. @@ -143,7 +143,6 @@ 'R0924', # Badly implemented 'W0109', # Duplicate key %r in dictionary 'W0120', # Else clause on loop without a break statement - 'W0121', # old-raise-syntax 'W0141', # Used builtin function %r 'W0201', # Attribute %r defined outside __init__ 'W0212', # Access to a protected member %s of a client class Modified: trunk/www.py =================================================================== --- trunk/www.py 2013-10-02 23:16:54 UTC (rev 326) +++ trunk/www.py 2013-10-02 23:18:30 UTC (rev 327) @@ -53,7 +53,7 @@ if not attrvalue.startswith('/'): targetpath = _get_path_for_url(attrvalue, self._filepath) if not targetpath: - raise ValueError, 'Could not resolve URL %s' % attrvalue + raise ValueError('Could not resolve URL %s' % attrvalue) # Get the folder of the parent path. parenturl = _get_relurl_for_filepath(self._filepath) @@ -89,7 +89,7 @@ root = os.path.dirname(parentpath) assert (root + os.sep).startswith(DOC_ROOT + os.sep) else: - raise ValueError, 'Tried resolving relative URL: %s' % url + raise ValueError('Tried resolving relative URL: %s' % url) urls = [ url.rstrip('/') + '/index.htm', @@ -136,11 +136,11 @@ channel = doc.createElement("channel") rss.appendChild(channel) if not title: - raise ValueError, 'Missing @title= setting.' + raise ValueError('Missing @title= setting.') if not link: - raise ValueError, 'Missing @link= setting.' + raise ValueError('Missing @link= setting.') if not desc: - raise ValueError, 'Missing @desc= setting.' + raise ValueError('Missing @desc= setting.') channel.appendChild(doc.createElement('title', textNode=title)) channel.appendChild(doc.createElement('link', textNode=link)) channel.appendChild(doc.createElement('description', textNode=desc)) @@ -153,7 +153,7 @@ for child in oldDocElement.childNodes: if child.type != "element": if child.value.strip(): - raise ValueError, 'Expected outer-level element, not text.' + raise ValueError('Expected outer-level element, not text.') continue if child.nodeName == 'h1': @@ -161,24 +161,24 @@ elif child.nodeName == "h2": link = len(child.childNodes) == 1 and child.childNodes[0] if not link or link.type != 'element' or link.nodeName != 'a': - raise ValueError, 'Each heading must be a link.' + raise ValueError('Each heading must be a link.') titlenode = len(link.childNodes) == 1 and link.childNodes[0] if not titlenode or titlenode.type != 'text': - raise ValueError, 'Each heading link must contain a ' + \ - 'single text node.' + raise ValueError('Each heading link must contain a ' + \ + 'single text node.') heading = titlenode.value.strip() # Combine the href with the linkbase. assert 'href' in link.attributes href = link.attribute_values['href'] if not linkbase.endswith('/'): - raise ValueError, 'The @linkbase must be a directory: %s' % \ - linkbase + raise ValueError('The @linkbase must be a directory: %s' % \ + linkbase) href = linkbase + href if href in guids: - raise ValueError, "Duplicate link: %s" % href + raise ValueError("Duplicate link: %s" % href) guids.append(href) item = doc.createElement("item") @@ -193,21 +193,21 @@ # The first paragraph is <p><em>pubDate</em></p> em = len(child.childNodes) == 1 and child.childNodes[0] if not em or em.type != 'element' or em.nodeName != 'em': - raise ValueError, 'The first paragraph must contain ' + \ - 'only an <em>.' + raise ValueError('The first paragraph must contain ' + \ + 'only an <em>.') emchild = len(em.childNodes) == 1 and em.childNodes[0] if not emchild or emchild.type != 'text': - raise ValueError, "The first paragraph's em must " + \ - "contain only text." + raise ValueError("The first paragraph's em must " + \ + "contain only text.") pubdate = emchild.value format = "%a, %d %b %Y %H:%M:%S +0000" dateobj = datetime.datetime.strptime(pubdate, format) normalized = dateobj.strftime(format) if normalized != pubdate: - raise ValueError, 'Encountered date %s but expected %s' % \ - (pubdate, normalized) + raise ValueError('Encountered date %s but expected %s' % \ + (pubdate, normalized)) item.appendChild(doc.createElement('pubDate', emchild.value)) @@ -218,7 +218,7 @@ item_desc.appendChild(cdata) else: - raise ValueError, 'Unsupported node type: %s' % child.nodeName + raise ValueError('Unsupported node type: %s' % child.nodeName) return doc.toxml() def _preprocess(path): @@ -227,13 +227,13 @@ # will resolve. url = match.group(1).strip() if '/' in url: - raise ValueError, 'Inclusions cannot cross directories' + raise ValueError('Inclusions cannot cross directories') # When including a file, update global settings and replace # with contents. includepath = _get_path_for_url(url, path) if not includepath: - raise ValueError, 'Unmatched URL: %s' % match.group(1) + raise ValueError('Unmatched URL: %s' % match.group(1)) settings, contents = _preprocess(includepath) childsettings.update(settings) return contents @@ -291,7 +291,7 @@ page = open(template_path).read() % keywords return 'text/html', page else: - raise ValueError, 'Invalid file type: %s' % path + raise ValueError('Invalid file type: %s' % path) class _Handler(BaseHTTPServer.BaseHTTPRequestHandler): def do_GET(self): @@ -339,7 +339,7 @@ yield relpath if not host or '/' in host: - raise ValueError, 'Host must be sub.domain.com' + raise ValueError('Host must be sub.domain.com') for relpath in findfiles(DOC_ROOT): sourcepath = os.path.join(DOC_ROOT, relpath) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2013-10-02 23:23:27
|
Revision: 328 http://sourceforge.net/p/javascriptlint/code/328 Author: matthiasmiller Date: 2013-10-02 23:23:24 +0000 (Wed, 02 Oct 2013) Log Message: ----------- pylint: clean up trailing whitespace Modified Paths: -------------- trunk/javascriptlint/conf.py trunk/javascriptlint/lint.py trunk/jsengine/parser/__init__.py trunk/jsengine/structs.py trunk/jsengine/tokenizer/__init__.py trunk/test.py Modified: trunk/javascriptlint/conf.py =================================================================== --- trunk/javascriptlint/conf.py 2013-10-02 23:18:30 UTC (rev 327) +++ trunk/javascriptlint/conf.py 2013-10-02 23:23:24 UTC (rev 328) @@ -163,14 +163,14 @@ def load(self, enabled, parm): if not enabled: raise ConfError('Expected +.') - + self.value = util.JSVersion.fromtype(parm) if not self.value: raise ConfError('Invalid JavaScript version: %s' % parm) class Conf: def __init__(self): - recurse = BooleanSetting(False) + recurse = BooleanSetting(False) self._settings = { 'recurse': recurse, 'output-format': StringSetting('__FILE__(__LINE__): __ERROR__'), Modified: trunk/javascriptlint/lint.py =================================================================== --- trunk/javascriptlint/lint.py 2013-10-02 23:18:30 UTC (rev 327) +++ trunk/javascriptlint/lint.py 2013-10-02 23:23:24 UTC (rev 328) @@ -19,7 +19,7 @@ 'eof', 'comma', 'dot', 'semi', 'colon', 'lc', 'rc', 'lp', 'rb', 'assign', 'relop', 'hook', 'plus', 'minus', 'star', 'divop', 'eqop', 'shop', 'or', 'and', 'bitor', 'bitxor', 'bitand', 'else', 'try' -) +) _globals = frozenset([ 'Array', 'Boolean', 'Math', 'Number', 'String', 'RegExp', 'Script', 'Date', @@ -27,7 +27,7 @@ 'eval', 'NaN', 'Infinity', 'escape', 'unescape', 'uneval', 'decodeURI', 'encodeURI', 'decodeURIComponent', 'encodeURIComponent', - 'Function', 'Object', + 'Function', 'Object', 'Error', 'InternalError', 'EvalError', 'RangeError', 'ReferenceError', 'SyntaxError', 'TypeError', 'URIError', 'arguments', 'undefined' Modified: trunk/jsengine/parser/__init__.py =================================================================== --- trunk/jsengine/parser/__init__.py 2013-10-02 23:18:30 UTC (rev 327) +++ trunk/jsengine/parser/__init__.py 2013-10-02 23:23:24 UTC (rev 328) @@ -86,7 +86,7 @@ x.startpos, x.endpos, None, []) items[-1] = items[-1] or comma - # Check for the end. + # Check for the end. if t.peek().tok == tok.RBRACKET: end_comma = comma break @@ -306,7 +306,7 @@ _MULTIPLICATIVE = { tok.MUL: (kind.STAR, op.MUL), tok.DIV: (kind.DIVOP, op.DIV), - tok.MOD: (kind.DIVOP, op.MOD), + tok.MOD: (kind.DIVOP, op.MOD), } def _multiplicative_expression(t): return _binary_expression(t, _MULTIPLICATIVE, _unary_expression) @@ -391,7 +391,7 @@ t.expect(tok.LOGICAL_AND) else: break - + while len(exprs) > 1: right = exprs.pop() left = exprs[-1] @@ -408,7 +408,7 @@ t.expect(tok.LOGICAL_OR) else: break - + while len(exprs) > 1: right = exprs.pop() left = exprs[-1] @@ -467,7 +467,7 @@ kind_, op_ = _ASSIGNS[t.peek().tok] t.advance() right = _assignment_expression(t, allowin) - return ParseNode(kind_, op_, + return ParseNode(kind_, op_, left.startpos, right.endpos, None, [left, right]) else: return left @@ -607,7 +607,7 @@ op.FORIN if condition.kind == kind.IN else None, for_startpos, body.endpos, None, [condition, body]) - + def _continue_statement(t): endtoken = t.expect(tok.CONTINUE) startpos = endtoken.startpos @@ -617,7 +617,7 @@ name = endtoken.atom else: name = None - # TODO: Validate Scope Labels + # TODO: Validate Scope Labels return _auto_semicolon(t, kind.CONTINUE, None, startpos, endtoken.endpos, name, []) def _break_statement(t): @@ -629,19 +629,19 @@ name = endtoken.atom else: name = None - # TODO: Validate Scope Labels + # TODO: Validate Scope Labels return _auto_semicolon(t, kind.BREAK, None, startpos, endtoken.endpos, name, []) def _return_statement(t): endtoken = t.expect(tok.RETURN) startpos = endtoken.startpos - + if t.peek_sameline().tok not in (tok.EOF, tok.EOL, tok.SEMI, tok.RBRACE): expr = _expression(t, True) endtoken = expr else: expr = None - # TODO: Validate Scope Labels + # TODO: Validate Scope Labels return _auto_semicolon(t, kind.RETURN, None, startpos, endtoken.endpos, None, [expr]) @@ -672,7 +672,7 @@ case_kind = kind.DEFAULT else: raise JSSyntaxError(t.peek().startpos, 'invalid_case') - + case_endpos = t.expect(tok.COLON).endpos statements = [] @@ -692,7 +692,7 @@ ParseNode(kind.LC, None, statements_startpos, statements_endpos, None, statements) ])) - + rc_endpos = t.expect(tok.RBRACE).endpos return ParseNode(kind.SWITCH, None, switch_startpos, rc_endpos, None, [expr, @@ -734,7 +734,7 @@ ]) ]) try_endpos = catch_endpos - + if t.peek().tok == tok.FINALLY: t.advance() finally_node = _block_statement(t) @@ -782,7 +782,7 @@ raise JSSyntaxError(x.startpos, 'unexpected_eof') elif x.tok == tok.FUNCTION: return _function_declaration(t, op.CLOSURE) #TODO: warn, since this is not reliable - + elif x.tok not in (tok.LBRACE, tok.FUNCTION): expr = _expression(t, True) if expr.kind == tok.NAME and t.peek().tok == tok.COLON: Modified: trunk/jsengine/structs.py =================================================================== --- trunk/jsengine/structs.py 2013-10-02 23:18:30 UTC (rev 327) +++ trunk/jsengine/structs.py 2013-10-02 23:23:24 UTC (rev 328) @@ -112,7 +112,7 @@ self.endpos = end_pos self.no_semi = no_semi self.end_comma = end_comma - + for i, kid in enumerate(self.kids): if kid: assert isinstance(kid, ParseNode) Modified: trunk/jsengine/tokenizer/__init__.py =================================================================== --- trunk/jsengine/tokenizer/__init__.py 2013-10-02 23:18:30 UTC (rev 327) +++ trunk/jsengine/tokenizer/__init__.py 2013-10-02 23:23:24 UTC (rev 328) @@ -41,7 +41,7 @@ "}": "RBRACE", "(": "LPAREN", ")": "RPAREN", - "[": "LBRACKET", + "[": "LBRACKET", "]": "RBRACKET", ".": "DOT", ";": "SEMI", @@ -58,7 +58,7 @@ "!": "LOGICAL_NOT", "~": "BIT_NOT", "?": "QUESTION", - ":": "COLON", + ":": "COLON", "=": "ASSIGN", "/": "DIV", "!": "LOGICAL_NOT", @@ -159,7 +159,7 @@ def getpos(self, offset=0): return self._nodepositions.from_offset(self._pos+offset) - + def watch_reads(self): self._watched_pos = self._pos @@ -316,7 +316,7 @@ def _next(self, parse_regexp=False): stream = self._stream - + if stream.eof(): return Token(tok.EOF) @@ -331,10 +331,10 @@ elif stream.readif(1, _WHITESPACE): pass else: - break + break if linebreak: return Token(tok.EOL) - else: + else: return Token(tok.SPACE) # COMMENTS Modified: trunk/test.py =================================================================== --- trunk/test.py 2013-10-02 23:18:30 UTC (rev 327) +++ trunk/test.py 2013-10-02 23:23:24 UTC (rev 328) @@ -96,7 +96,7 @@ class _CustomLintReporter(TextReporter): line_format = '{path}({line}): [{msg_id}({symbol}){obj}] {msg}' def __init__(self): - TextReporter.__init__(self) + TextReporter.__init__(self) self.msg_count = 0 def write_message(self, msg): @@ -129,7 +129,6 @@ 'C0103', # Invalid name "%s" (should match %s) 'C0202', # Class method should have "cls" as first argument 'C0301', # Line too long (%s/%s) - 'C0303', # Trailing whitespace 'C0321', # More than one statement on a single line 'C0323', # Operator not followed by a space 'C0324', # Comma not followed by a space This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2013-10-02 23:27:22
|
Revision: 329 http://sourceforge.net/p/javascriptlint/code/329 Author: matthiasmiller Date: 2013-10-02 23:27:18 +0000 (Wed, 02 Oct 2013) Log Message: ----------- pylint: fix more whitespace issues Modified Paths: -------------- trunk/javascriptlint/jsparse.py trunk/javascriptlint/warnings.py trunk/jsengine/parser/__init__.py trunk/jsengine/structs.py trunk/test.py trunk/www.py Modified: trunk/javascriptlint/jsparse.py =================================================================== --- trunk/javascriptlint/jsparse.py 2013-10-02 23:23:24 UTC (rev 328) +++ trunk/javascriptlint/jsparse.py 2013-10-02 23:27:18 UTC (rev 329) @@ -55,7 +55,7 @@ """ All node positions will be relative to startpos. This allows scripts to be embedded in a file (for example, HTML). """ - startpos = startpos or NodePos(0,0) + startpos = startpos or NodePos(0, 0) jsversion = jsversion or JSVersion.default() assert isvalidversion(jsversion), jsversion if jsversion.e4x: @@ -173,7 +173,7 @@ self.assertEquals(pos.to_offset(NodePos(1, 0)), 5) self.assertEquals(pos.to_offset(NodePos(3, 1)), 11) def testStartPos(self): - pos = NodePositions('abc\r\ndef\n\nghi', NodePos(3,4)) + pos = NodePositions('abc\r\ndef\n\nghi', NodePos(3, 4)) self.assertEquals(pos.to_offset(NodePos(3, 4)), 0) self.assertEquals(pos.to_offset(NodePos(3, 5)), 1) self.assertEquals(pos.from_offset(0), NodePos(3, 4)) @@ -185,21 +185,21 @@ def testAdd(self): r = NodeRanges() r.add(5, 10) - self.assertEquals(r._offsets, [5,11]) + 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]) + 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) @@ -239,8 +239,8 @@ return errors[0] self.assertEquals(geterror(' ?', None), (0, 1, 'syntax_error', {})) self.assertEquals(geterror('\n ?', None), (1, 1, 'syntax_error', {})) - self.assertEquals(geterror(' ?', NodePos(1,1)), (1, 2, 'syntax_error', {})) - self.assertEquals(geterror('\n ?', NodePos(1,1)), (2, 1, 'syntax_error', {})) + self.assertEquals(geterror(' ?', NodePos(1, 1)), (1, 2, 'syntax_error', {})) + self.assertEquals(geterror('\n ?', NodePos(1, 1)), (2, 1, 'syntax_error', {})) def testNodePos(self): def getnodepos(script, startpos): root = parse(script, None, None, startpos) @@ -248,24 +248,24 @@ var, = root.kids self.assertEquals(var.kind, tok.VAR) return var.start_pos() - self.assertEquals(getnodepos('var x;', None), NodePos(0,0)) - self.assertEquals(getnodepos(' var x;', None), NodePos(0,1)) - self.assertEquals(getnodepos('\n\n var x;', None), NodePos(2,1)) - self.assertEquals(getnodepos('var x;', NodePos(3,4)), NodePos(3,4)) - self.assertEquals(getnodepos(' var x;', NodePos(3,4)), NodePos(3,5)) - self.assertEquals(getnodepos('\n\n var x;', NodePos(3,4)), NodePos(5,1)) + self.assertEquals(getnodepos('var x;', None), NodePos(0, 0)) + self.assertEquals(getnodepos(' var x;', None), NodePos(0, 1)) + self.assertEquals(getnodepos('\n\n var x;', None), NodePos(2, 1)) + self.assertEquals(getnodepos('var x;', NodePos(3, 4)), NodePos(3, 4)) + self.assertEquals(getnodepos(' var x;', NodePos(3, 4)), NodePos(3, 5)) + self.assertEquals(getnodepos('\n\n var x;', NodePos(3, 4)), NodePos(5, 1)) def testComments(self): def testcomment(comment, startpos, expectedpos): root = parse(comment, None, None, startpos) comment, = findcomments(comment, root, startpos) self.assertEquals(comment.start_pos(), expectedpos) for comment in ('/*comment*/', '//comment'): - testcomment(comment, None, NodePos(0,0)) - testcomment(' %s' % comment, None, NodePos(0,1)) - testcomment('\n\n %s' % comment, None, NodePos(2,1)) - testcomment('%s' % comment, NodePos(3,4), NodePos(3,4)) - testcomment(' %s' % comment, NodePos(3,4), NodePos(3,5)) - testcomment('\n\n %s' % comment, NodePos(3,4), NodePos(5,1)) + testcomment(comment, None, NodePos(0, 0)) + testcomment(' %s' % comment, None, NodePos(0, 1)) + testcomment('\n\n %s' % comment, None, NodePos(2, 1)) + testcomment('%s' % comment, NodePos(3, 4), NodePos(3, 4)) + testcomment(' %s' % comment, NodePos(3, 4), NodePos(3, 5)) + testcomment('\n\n %s' % comment, NodePos(3, 4), NodePos(5, 1)) if __name__ == '__main__': unittest.main() Modified: trunk/javascriptlint/warnings.py =================================================================== --- trunk/javascriptlint/warnings.py 2013-10-02 23:23:24 UTC (rev 328) +++ trunk/javascriptlint/warnings.py 2013-10-02 23:27:18 UTC (rev 329) @@ -290,7 +290,7 @@ def with_statement(node): raise LintWarning(node) -@lookfor(tok.EQOP,tok.RELOP) +@lookfor(tok.EQOP, tok.RELOP) def useless_comparison(node): for lvalue, rvalue in itertools.combinations(node.kids, 2): if lvalue.is_equivalent(rvalue): Modified: trunk/jsengine/parser/__init__.py =================================================================== --- trunk/jsengine/parser/__init__.py 2013-10-02 23:23:24 UTC (rev 328) +++ trunk/jsengine/parser/__init__.py 2013-10-02 23:27:18 UTC (rev 329) @@ -854,7 +854,7 @@ try: parsestring('/*') except JSSyntaxError as error: - self.assertEqual(error.pos, NodePos(0,1)) + self.assertEqual(error.pos, NodePos(0, 1)) else: self.assert_(False) def testObjectEndComma(self): Modified: trunk/jsengine/structs.py =================================================================== --- trunk/jsengine/structs.py 2013-10-02 23:23:24 UTC (rev 328) +++ trunk/jsengine/structs.py 2013-10-02 23:27:18 UTC (rev 329) @@ -63,7 +63,7 @@ end = self._offsets[j] j += 1 - self._offsets[i:j] = [start,end] + self._offsets[i:j] = [start, end] def has(self, pos): return bisect.bisect_right(self._offsets, pos) % 2 == 1 Modified: trunk/test.py =================================================================== --- trunk/test.py 2013-10-02 23:23:24 UTC (rev 328) +++ trunk/test.py 2013-10-02 23:27:18 UTC (rev 329) @@ -130,8 +130,6 @@ 'C0202', # Class method should have "cls" as first argument 'C0301', # Line too long (%s/%s) 'C0321', # More than one statement on a single line - 'C0323', # Operator not followed by a space - 'C0324', # Comma not followed by a space 'C1001', # Old style class 'E0602', # Undefined variable %r 'E1101', # %s %r has no %r member Modified: trunk/www.py =================================================================== --- trunk/www.py 2013-10-02 23:23:24 UTC (rev 328) +++ trunk/www.py 2013-10-02 23:27:18 UTC (rev 329) @@ -361,12 +361,12 @@ if action == 'build': build(host) return - print >>sys.stderr, """\ + sys.stderr.write("""\ Usage: www.py [server|build] <host> server runs a test server on localhost build generates static HTML files from the markup -""" +""") sys.exit(1) if __name__ == '__main__': This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2013-10-03 19:16:49
|
Revision: 330 http://sourceforge.net/p/javascriptlint/code/330 Author: matthiasmiller Date: 2013-10-03 19:16:44 +0000 (Thu, 03 Oct 2013) Log Message: ----------- Embed version number and revision. Modified Paths: -------------- trunk/javascriptlint/conf.py trunk/javascriptlint/jsl.py trunk/setup.py Added Paths: ----------- trunk/javascriptlint/version.py Modified: trunk/javascriptlint/conf.py =================================================================== --- trunk/javascriptlint/conf.py 2013-10-02 23:27:18 UTC (rev 329) +++ trunk/javascriptlint/conf.py 2013-10-03 19:16:44 UTC (rev 330) @@ -4,6 +4,7 @@ import fs import util +import version import warnings _DISABLED_WARNINGS = ( @@ -100,7 +101,7 @@ # or "+process Folder\Path\*.htm". # """ % { - 'version': '', # TODO + 'version': version.version, 'warnings': _getwarningsconf(), } Modified: trunk/javascriptlint/jsl.py =================================================================== --- trunk/javascriptlint/jsl.py 2013-10-02 23:27:18 UTC (rev 329) +++ trunk/javascriptlint/jsl.py 2013-10-03 19:16:44 UTC (rev 330) @@ -14,6 +14,7 @@ import jsparse import lint import util +import version _lint_results = { 'warnings': 0, @@ -48,8 +49,7 @@ return paths or [path] def printlogo(): - # TODO: Print version number. - print "JavaScript Lint" + print "JavaScript Lint %s" % version.version print "Developed by Matthias Miller (http://www.JavaScriptLint.com)" def _profile_enabled(func, *args, **kwargs): Added: trunk/javascriptlint/version.py =================================================================== --- trunk/javascriptlint/version.py (rev 0) +++ trunk/javascriptlint/version.py 2013-10-03 19:16:44 UTC (rev 330) @@ -0,0 +1,19 @@ +import os.path +import subprocess + +try: + from __svnversion__ import version +except ImportError: + def _getrevnum(): + path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + p = subprocess.Popen(['svnversion', path], stdin=subprocess.PIPE, + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout, stderr = p.communicate() + if p.returncode != 0: + raise _BuildError('Error running svnversion: %s' % stderr) + version = stdout.strip().rstrip('M') + version = version.rpartition(':')[-1] + return int(version) + + version = '0.5.0/r%i' % _getrevnum() + Modified: trunk/setup.py =================================================================== --- trunk/setup.py 2013-10-02 23:27:18 UTC (rev 329) +++ trunk/setup.py 2013-10-03 19:16:44 UTC (rev 330) @@ -7,20 +7,12 @@ import subprocess import sys +from javascriptlint import version + class _BuildError(Exception): pass -def _getrevnum(): - path = os.path.dirname(os.path.abspath(__file__)) - p = subprocess.Popen(['svnversion', path], stdin=subprocess.PIPE, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) - stdout, stderr = p.communicate() - if p.returncode != 0: - raise _BuildError('Error running svnversion: %s' % stderr) - version = stdout.strip().rstrip('M') - return int(version) - -if __name__ == '__main__': +def _setup(): cmdclass = { 'build': distutils.command.build.build, 'clean': distutils.command.clean.clean, @@ -28,12 +20,12 @@ args = {} args.update( name = 'javascriptlint', - version = '0.0.0.%i' % _getrevnum(), + version = version.version, author = 'Matthias Miller', author_email = 'in...@ja...', url = 'http://www.javascriptlint.com/', cmdclass = cmdclass, - description = 'JavaScript Lint (pyjsl beta r%i)' % _getrevnum(), + description = 'JavaScript Lint %s' % version.version, packages = ['javascriptlint'], scripts = ['jsl'] ) @@ -64,3 +56,18 @@ ) setup(**args) +def _main(): + # Create a temporary __svnversion__.py to bundle the version + path = os.path.join(os.path.dirname(os.path.abspath(__file__)), + 'javascriptlint', '__svnversion__.py') + with open(path, 'w') as f: + f.write('version = %r' % version.version) + try: + _setup() + finally: + os.unlink(path) + if os.path.exists(path + 'c'): + os.unlink(path + 'c') + +if __name__ == '__main__': + _main() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2013-10-03 20:13:40
|
Revision: 333 http://sourceforge.net/p/javascriptlint/code/333 Author: matthiasmiller Date: 2013-10-03 20:13:37 +0000 (Thu, 03 Oct 2013) Log Message: ----------- Fix parsing numbers with exponents. Modified Paths: -------------- trunk/jsengine/tokenizer/__init__.py trunk/test.py Added Paths: ----------- trunk/tests/bugs/numbers.js Modified: trunk/jsengine/tokenizer/__init__.py =================================================================== --- trunk/jsengine/tokenizer/__init__.py 2013-10-03 19:48:25 UTC (rev 332) +++ trunk/jsengine/tokenizer/__init__.py 2013-10-03 20:13:37 UTC (rev 333) @@ -397,7 +397,8 @@ if stream.readif(1, 'eE'): stream.readif(1, '+-') - stream.require(_DIGITS) + if not stream.readif(1, _DIGITS): + raise JSSyntaxError(stream.getpos(), 'syntax_error') while stream.readif(1, _DIGITS): pass Modified: trunk/test.py =================================================================== --- trunk/test.py 2013-10-03 19:48:25 UTC (rev 332) +++ trunk/test.py 2013-10-03 20:13:37 UTC (rev 333) @@ -105,6 +105,10 @@ def _get_python_modules(dir_): for root, dirs, files in os.walk(dir_): + for exclude in ('build', 'dist'): + if exclude in dirs: + build.remove(exclude) + if '.svn' in dirs: dirs.remove('.svn') for name in files: Added: trunk/tests/bugs/numbers.js =================================================================== --- trunk/tests/bugs/numbers.js (rev 0) +++ trunk/tests/bugs/numbers.js 2013-10-03 20:13:37 UTC (rev 333) @@ -0,0 +1,5 @@ +function number() { + var i = 1.1e10; + i = 1.1e+10; + i = 1.1e-10; +} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2013-10-08 16:36:50
|
Revision: 334 http://sourceforge.net/p/javascriptlint/code/334 Author: matthiasmiller Date: 2013-10-08 16:36:44 +0000 (Tue, 08 Oct 2013) Log Message: ----------- Change the parser to only use offsets internally and to convert offsets to line/col only when reporting errors. Modified Paths: -------------- trunk/javascriptlint/htmlparse.py trunk/javascriptlint/jsparse.py trunk/javascriptlint/lint.py trunk/javascriptlint/warnings.py trunk/jsengine/__init__.py trunk/jsengine/parser/__init__.py trunk/jsengine/structs.py trunk/jsengine/tokenizer/__init__.py trunk/test.py Modified: trunk/javascriptlint/htmlparse.py =================================================================== --- trunk/javascriptlint/htmlparse.py 2013-10-03 20:13:37 UTC (rev 333) +++ trunk/javascriptlint/htmlparse.py 2013-10-08 16:36:44 UTC (rev 334) @@ -2,18 +2,25 @@ import HTMLParser import unittest +from jsengine.structs import NodePos, NodePositions + class _Parser(HTMLParser.HTMLParser): def __init__(self): HTMLParser.HTMLParser.__init__(self) self._tags = [] + self._node_positions = None + def feed(self, data): + # Reset line numbers whenever we get data. + self._node_positions = None + HTMLParser.HTMLParser.feed(self, data) + def handle_starttag(self, tag, attributes): if tag.lower() == 'script': attr = dict(attributes) self._tags.append({ 'type': 'start', - 'lineno': self.lineno, - 'offset': self.offset, + 'offset': self._getoffset(), 'len': len(self.get_starttag_text()), 'attr': attr }) @@ -22,8 +29,7 @@ if tag.lower() == 'script': self._tags.append({ 'type': 'end', - 'lineno': self.lineno, - 'offset': self.offset, + 'offset': self._getoffset(), }) def unknown_decl(self, data): @@ -33,9 +39,16 @@ def gettags(self): return self._tags + def _getoffset(self): + # htmlparse returns 1-based line numbers. Calculate the offset of the + # script's contents. + if self._node_positions is None: + self._node_positions = NodePositions(self.rawdata) + pos = NodePos(self.lineno - 1, self.offset) + return self._node_positions.to_offset(pos) + + def findscripttags(s): - """ Note that the lineno is 1-based. - """ parser = _Parser() parser.feed(s) parser.close() Modified: trunk/javascriptlint/jsparse.py =================================================================== --- trunk/javascriptlint/jsparse.py 2013-10-03 20:13:37 UTC (rev 333) +++ trunk/javascriptlint/jsparse.py 2013-10-08 16:36:44 UTC (rev 334) @@ -16,7 +16,8 @@ return True return jsengine.parser.is_valid_version(jsversion.version) -def findpossiblecomments(script, node_positions): +def findpossiblecomments(script, script_offset): + assert not script_offset is None pos = 0 single_line_re = r"//[^\r\n]*" multi_line_re = r"/\*(.*?)\*/" @@ -41,38 +42,34 @@ start_offset = match.start() end_offset = match.end()-1 - start_pos = node_positions.from_offset(start_offset) - end_pos = node_positions.from_offset(end_offset) - comment_node = ParseNode(kind.COMMENT, opcode, start_pos, end_pos, - comment_text, []) + comment_node = ParseNode(kind.COMMENT, opcode, + script_offset + start_offset, + script_offset + end_offset, comment_text, []) comments.append(comment_node) # Start searching immediately after the start of the comment in case # this one was within a string or a regexp. pos = match.start()+1 -def parse(script, jsversion, error_callback, startpos=None): - """ All node positions will be relative to startpos. This allows scripts - to be embedded in a file (for example, HTML). +def parse(script, jsversion, error_callback, start_offset=0): + """ All node positions will be relative to start_offset. This allows + scripts to be embedded in a file (for example, HTML). """ - startpos = startpos or NodePos(0, 0) + assert not start_offset is None jsversion = jsversion or JSVersion.default() assert isvalidversion(jsversion), jsversion if jsversion.e4x: - error_callback(startpos.line, startpos.col, 'e4x_deprecated', {}) + error_callback(start_offset, 'e4x_deprecated', {}) return jsengine.parser.parse(script, jsversion.version, - error_callback, - startpos) + error_callback, start_offset) -def filtercomments(possible_comments, node_positions, root_node): +def filtercomments(possible_comments, root_node): comment_ignore_ranges = NodeRanges() def process(node): if node.kind == tok.STRING or \ (node.kind == tok.OBJECT and node.opcode == op.REGEXP): - start_offset = node_positions.to_offset(node.start_pos()) - end_offset = node_positions.to_offset(node.end_pos()) - comment_ignore_ranges.add(start_offset, end_offset) + comment_ignore_ranges.add(node.start_offset, node.end_offset) for kid in node.kids: if kid: process(kid) @@ -80,25 +77,22 @@ comments = [] for comment in possible_comments: - start_offset = node_positions.to_offset(comment.start_pos()) - end_offset = node_positions.to_offset(comment.end_pos()) - if comment_ignore_ranges.has(start_offset): + if comment_ignore_ranges.has(comment.start_offset): continue - comment_ignore_ranges.add(start_offset, end_offset) + comment_ignore_ranges.add(comment.start_offset, comment.end_offset) comments.append(comment) return comments -def findcomments(script, root_node, start_pos=None): - node_positions = NodePositions(script, start_pos) - possible_comments = findpossiblecomments(script, node_positions) - return filtercomments(possible_comments, node_positions, root_node) +def findcomments(script, root_node, start_offset=0): + possible_comments = findpossiblecomments(script, start_offset) + return filtercomments(possible_comments, root_node) def is_compilable_unit(script, jsversion): jsversion = jsversion or JSVersion.default() assert isvalidversion(jsversion) return jsengine.parser.is_compilable_unit(script, jsversion.version) -def _dump_node(node, depth=0): +def _dump_node(node, node_positions, depth=0): if node is None: print ' '*depth, print '(None)' @@ -107,7 +101,8 @@ print ' '*depth, print '%s, %s' % (repr(node.kind), repr(node.opcode)) print ' '*depth, - print '%s - %s' % (node.start_pos(), node.end_pos()) + print '%s - %s' % (node_positions.from_offset(node.start_offset), + node_positions.from_offset(node.end_offset)) if hasattr(node, 'atom'): print ' '*depth, print 'atom: %s' % node.atom @@ -116,13 +111,14 @@ print '(no semicolon)' print for node in node.kids: - _dump_node(node, depth+1) + _dump_node(node, node_positions, depth+1) def dump_tree(script): def error_callback(line, col, msg, msg_args): print '(%i, %i): %s', (line, col, msg) node = parse(script, None, error_callback) - _dump_node(node) + node_positions = NodePositions(script) + _dump_node(node, node_positions) class TestComments(unittest.TestCase): def _test(self, script, expected_comments): @@ -230,42 +226,42 @@ class TestLineOffset(unittest.TestCase): def testErrorPos(self): - def geterror(script, startpos): + def geterror(script, start_offset): errors = [] - def onerror(line, col, msg, msg_args): - errors.append((line, col, msg, msg_args)) - parse(script, None, onerror, startpos) + def onerror(offset, msg, msg_args): + errors.append((offset, msg, msg_args)) + parse(script, None, onerror, start_offset) self.assertEquals(len(errors), 1) return errors[0] - self.assertEquals(geterror(' ?', None), (0, 1, 'syntax_error', {})) - self.assertEquals(geterror('\n ?', None), (1, 1, 'syntax_error', {})) - self.assertEquals(geterror(' ?', NodePos(1, 1)), (1, 2, 'syntax_error', {})) - self.assertEquals(geterror('\n ?', NodePos(1, 1)), (2, 1, 'syntax_error', {})) + self.assertEquals(geterror(' ?', 0), (1, 'syntax_error', {})) + self.assertEquals(geterror('\n ?', 0), (2, 'syntax_error', {})) + self.assertEquals(geterror(' ?', 2), (3, 'syntax_error', {})) + self.assertEquals(geterror('\n ?', 2), (4, 'syntax_error', {})) def testNodePos(self): - def getnodepos(script, startpos): - root = parse(script, None, None, startpos) + def getnodepos(script, start_offset): + root = parse(script, None, None, start_offset) self.assertEquals(root.kind, tok.LC) var, = root.kids self.assertEquals(var.kind, tok.VAR) - return var.start_pos() - self.assertEquals(getnodepos('var x;', None), NodePos(0, 0)) - self.assertEquals(getnodepos(' var x;', None), NodePos(0, 1)) - self.assertEquals(getnodepos('\n\n var x;', None), NodePos(2, 1)) - self.assertEquals(getnodepos('var x;', NodePos(3, 4)), NodePos(3, 4)) - self.assertEquals(getnodepos(' var x;', NodePos(3, 4)), NodePos(3, 5)) - self.assertEquals(getnodepos('\n\n var x;', NodePos(3, 4)), NodePos(5, 1)) + return var.start_offset + self.assertEquals(getnodepos('var x;', 0), 0) + self.assertEquals(getnodepos(' var x;', 0), 1) + self.assertEquals(getnodepos('\n\n var x;', 0), 3) + self.assertEquals(getnodepos('var x;', 7), 7) + self.assertEquals(getnodepos(' var x;', 7), 8) + self.assertEquals(getnodepos('\n\n var x;', 7), 10) def testComments(self): - def testcomment(comment, startpos, expectedpos): + def testcomment(comment, startpos, expected_offset): root = parse(comment, None, None, startpos) comment, = findcomments(comment, root, startpos) - self.assertEquals(comment.start_pos(), expectedpos) + self.assertEquals(comment.start_offset, expected_offset) for comment in ('/*comment*/', '//comment'): - testcomment(comment, None, NodePos(0, 0)) - testcomment(' %s' % comment, None, NodePos(0, 1)) - testcomment('\n\n %s' % comment, None, NodePos(2, 1)) - testcomment('%s' % comment, NodePos(3, 4), NodePos(3, 4)) - testcomment(' %s' % comment, NodePos(3, 4), NodePos(3, 5)) - testcomment('\n\n %s' % comment, NodePos(3, 4), NodePos(5, 1)) + testcomment(comment, 0, 0) + testcomment(' %s' % comment, 0, 1) + testcomment('\n\n %s' % comment, 0, 3) + testcomment('%s' % comment, 7, 7) + testcomment(' %s' % comment, 7, 8) + testcomment('\n\n %s' % comment, 7, 10) if __name__ == '__main__': unittest.main() Modified: trunk/javascriptlint/lint.py =================================================================== --- trunk/javascriptlint/lint.py 2013-10-03 20:13:37 UTC (rev 333) +++ trunk/javascriptlint/lint.py 2013-10-08 16:36:44 UTC (rev 334) @@ -140,7 +140,7 @@ # sorted by node position. unreferenced = [(key[0], key[1], node) for key, node in unreferenced.items()] - unreferenced.sort(key=lambda x: x[2].start_pos()) + unreferenced.sort(key=lambda x: x[2].start_offset) return { 'unreferenced': unreferenced, @@ -214,8 +214,8 @@ # Conditionally add it to an inner scope. assert self._node - if (node.start_pos() >= self._node.start_pos() and \ - node.end_pos() <= self._node.end_pos()): + if (node.start_offset >= self._node.start_offset and \ + node.end_offset <= self._node.end_offset): return self class _Script: @@ -245,7 +245,6 @@ def _findhtmlscripts(contents, default_version): starttag = None - nodepos = jsparse.NodePositions(contents) for tag in htmlparse.findscripttags(contents): if tag['type'] == 'start': # Ignore nested start tags. @@ -265,13 +264,9 @@ # htmlparse returns 1-based line numbers. Calculate the # position of the script's contents. - tagpos = jsparse.NodePos(starttag['lineno']-1, starttag['offset']) - tagoffset = nodepos.to_offset(tagpos) - startoffset = tagoffset + starttag['len'] - startpos = nodepos.from_offset(startoffset) - endpos = jsparse.NodePos(tag['lineno']-1, tag['offset']) - endoffset = nodepos.to_offset(endpos) - script = contents[startoffset:endoffset] + start_offset = starttag['offset'] + starttag['len'] + end_offset = tag['offset'] + script = contents[start_offset:end_offset] if not jsparse.isvalidversion(starttag['jsversion']) or \ jsparse.is_compilable_unit(script, starttag['jsversion']): @@ -279,7 +274,7 @@ yield { 'type': 'inline', 'jsversion': starttag['jsversion'], - 'pos': startpos, + 'offset': start_offset, 'contents': script, } starttag = None @@ -294,8 +289,9 @@ import_path = import_path.replace('\\', os.sep) import_path = os.path.join(os.path.dirname(path), import_path) return lint_file(import_path, 'js', jsversion, encoding) - def _lint_error(*args): - return lint_error(normpath, *args) + def _lint_error(offset, errname, errdesc): + pos = node_positions.from_offset(offset) + return lint_error(normpath, pos.line, pos.col, errname, errdesc) normpath = fs.normpath(path) if normpath in lint_cache: @@ -307,12 +303,13 @@ try: contents = fs.readfile(path, encoding) except IOError, error: - _lint_error(0, 0, 'io_error', unicode(error)) + lint_error(normpath, 0, 0, 'io_error', unicode(error)) return lint_cache[normpath] + node_positions = jsparse.NodePositions(contents) script_parts = [] if kind == 'js': - script_parts.append((None, jsversion or conf['default-version'], contents)) + script_parts.append((0, jsversion or conf['default-version'], contents)) elif kind == 'html': assert jsversion is None for script in _findhtmlscripts(contents, conf['default-version']): @@ -324,7 +321,7 @@ other = import_script(script['src'], script['jsversion']) lint_cache[normpath].importscript(other) elif script['type'] == 'inline': - script_parts.append((script['pos'], script['jsversion'], + script_parts.append((script['offset'], script['jsversion'], script['contents'])) else: assert False, 'Invalid internal script type %s' % \ @@ -343,18 +340,18 @@ else: lint_file(path, 'js', None, encoding) -def _lint_script_part(scriptpos, jsversion, script, script_cache, conf, +def _lint_script_part(script_offset, jsversion, script, script_cache, conf, ignores, report_native, report_lint, import_callback): - def parse_error(row, col, msg, msg_args): + def parse_error(offset, msg, msg_args): if not msg in ('anon_no_return_value', 'no_return_value', 'redeclared_var', 'var_hides_arg'): - parse_errors.append((jsparse.NodePos(row, col), msg, msg_args)) + parse_errors.append((offset, msg, msg_args)) - def report(node, errname, pos=None, **errargs): + def report(node, errname, offset=0, **errargs): if errname == 'empty_statement' and node.kind == tok.LC: for pass_ in passes: - if pass_.start_pos() > node.start_pos() and \ - pass_.end_pos() < node.end_pos(): + if pass_.start_offset > node.start_offset and \ + pass_.end_offset < node.end_offset: passes.remove(pass_) return @@ -363,12 +360,12 @@ # the next case/default. assert node.kind in (tok.CASE, tok.DEFAULT) prevnode = node.parent.kids[node.node_index-1] - expectedfallthru = prevnode.end_pos(), node.start_pos() + expectedfallthru = prevnode.end_offset, node.start_offset elif errname == 'missing_break_for_last_case': # Find the end of the current case/default and the end of the # switch. assert node.parent.kind == tok.LC - expectedfallthru = node.end_pos(), node.parent.end_pos() + expectedfallthru = node.end_offset, node.parent.end_offset else: expectedfallthru = None @@ -377,11 +374,11 @@ for fallthru in fallthrus: # Look for a fallthru between the end of the current case or # default statement and the beginning of the next token. - if fallthru.start_pos() > start and fallthru.end_pos() < end: + if fallthru.start_offset > start and fallthru.end_offset < end: fallthrus.remove(fallthru) return - report_lint(node, errname, pos, **errargs) + report_lint(node, errname, offset, **errargs) parse_errors = [] declares = [] @@ -390,8 +387,7 @@ fallthrus = [] passes = [] - node_positions = jsparse.NodePositions(script, scriptpos) - possible_comments = jsparse.findpossiblecomments(script, node_positions) + possible_comments = jsparse.findpossiblecomments(script, script_offset) # Check control comments for the correct version. It may be this comment # isn't a valid comment (for example, it might be inside a string literal) @@ -410,18 +406,18 @@ report(node, 'unsupported_version', version=parms) if not jsparse.isvalidversion(jsversion): - report_lint(jsversionnode, 'unsupported_version', scriptpos, + report_lint(jsversionnode, 'unsupported_version', script_offset, version=jsversion.version) return - root = jsparse.parse(script, jsversion, parse_error, scriptpos) + root = jsparse.parse(script, jsversion, parse_error, script_offset) if not root: # Report errors and quit. - for pos, msg, msg_args in parse_errors: - report_native(pos, msg, msg_args) + for offset, msg, msg_args in parse_errors: + report_native(offset, msg, msg_args) return - comments = jsparse.filtercomments(possible_comments, node_positions, root) + comments = jsparse.filtercomments(possible_comments, root) if jsversionnode is not None and jsversionnode not in comments: # TODO @@ -449,7 +445,7 @@ start_ignore = node elif keyword == 'end': if start_ignore: - ignores.append((start_ignore.start_pos(), node.end_pos())) + ignores.append((start_ignore.start_offset, node.end_offset)) start_ignore = None else: report(node, 'mismatch_ctrl_comments') @@ -471,8 +467,8 @@ # Report at the actual error of the location. Add two # characters for the opening two characters. if nested_comment >= 0: - pos = node_positions.from_offset(node_positions.to_offset(comment.start_pos()) + 2 + nested_comment) - report(comment, 'nested_comment', pos=pos) + offset = comment.start_offset + 2 + nested_comment + report(comment, 'nested_comment', offset=offset) if comment.atom.lower().startswith('jsl:'): report(comment, 'jsl_cc_not_understood') elif comment.atom.startswith('@'): @@ -481,8 +477,8 @@ report(start_ignore, 'mismatch_ctrl_comments') # Wait to report parse errors until loading jsl:ignore directives. - for pos, msg in parse_errors: - report_native(pos, msg) + for offset, msg in parse_errors: + report_native(offset, msg) # Find all visitors and convert them into "onpush" callbacks that call "report" visitors = { @@ -516,15 +512,15 @@ unused_scope.set_unused(name, node) def _lint_script_parts(script_parts, script_cache, lint_error, conf, import_callback): - def report_lint(node, errname, pos=None, **errargs): + def report_lint(node, errname, offset=0, **errargs): errdesc = warnings.format_error(errname, **errargs) - _report(pos or node.start_pos(), errname, errdesc, True) + _report(offset or node.start_offset, errname, errdesc, True) - def report_native(pos, errname, errargs): + def report_native(offset, errname, errargs): errdesc = warnings.format_error(errname, **errargs) - _report(pos, errname, errdesc, False) + _report(offset, errname, errdesc, False) - def _report(pos, errname, errdesc, require_key): + def _report(offset, errname, errdesc, require_key): try: if not conf[errname]: return @@ -533,14 +529,14 @@ raise for start, end in ignores: - if pos >= start and pos <= end: + if offset >= start and offset <= end: return - return lint_error(pos.line, pos.col, errname, errdesc) + return lint_error(offset, errname, errdesc) - for scriptpos, jsversion, script in script_parts: + for script_offset, jsversion, script in script_parts: ignores = [] - _lint_script_part(scriptpos, jsversion, script, script_cache, conf, ignores, + _lint_script_part(script_offset, jsversion, script, script_cache, conf, ignores, report_native, report_lint, import_callback) scope = script_cache.scope @@ -576,10 +572,10 @@ # TODO: This is ugly hardcoding to improve the error positioning of # "missing_semicolon" errors. if visitor.warning in ('missing_semicolon', 'missing_semicolon_for_lambda'): - pos = warning.node.end_pos() + offset = warning.node.end_offset else: - pos = None - report(warning.node, visitor.warning, pos=pos, **warning.errargs) + offset = None + report(warning.node, visitor.warning, offset=offset, **warning.errargs) return onpush def _warn_or_declare(scope, name, type_, node, report): Modified: trunk/javascriptlint/warnings.py =================================================================== --- trunk/javascriptlint/warnings.py 2013-10-03 20:13:37 UTC (rev 333) +++ trunk/javascriptlint/warnings.py 2013-10-08 16:36:44 UTC (rev 334) @@ -584,7 +584,7 @@ if filter(is_return_with_val, exit_points): # If the function returns a value, find all returns without a value. returns = filter(is_return_without_val, exit_points) - returns.sort(key=lambda node: node.start_pos()) + returns.sort(key=lambda node: node.start_offset) if returns: raise LintWarning(returns[0], name=name) # Warn if the function sometimes exits naturally. Modified: trunk/jsengine/__init__.py =================================================================== --- trunk/jsengine/__init__.py 2013-10-03 20:13:37 UTC (rev 333) +++ trunk/jsengine/__init__.py 2013-10-08 16:36:44 UTC (rev 334) @@ -10,12 +10,12 @@ ) class JSSyntaxError(BaseException): - def __init__(self, pos, msg, msg_args=None): + def __init__(self, offset, msg, msg_args=None): assert msg in _MESSAGES, msg - self.pos = pos + self.offset = offset self.msg = msg self.msg_args = msg_args or {} def __unicode__(self): - return '%s: %s' % (self.pos, self.msg) + return '%s: %s' % (self.offset, self.msg) def __repr__(self): - return 'JSSyntaxError(%r, %r, %r)' % (self.pos, self.msg. self.msg_args) + return 'JSSyntaxError(%r, %r, %r)' % (self.offset, self.msg. self.msg_args) Modified: trunk/jsengine/parser/__init__.py =================================================================== --- trunk/jsengine/parser/__init__.py 2013-10-03 20:13:37 UTC (rev 333) +++ trunk/jsengine/parser/__init__.py 2013-10-08 16:36:44 UTC (rev 334) @@ -22,16 +22,16 @@ "1.7", ] -def _auto_semicolon(t, kind_, op_, startpos, endpos, atom, kids): +def _auto_semicolon(t, kind_, op_, start_offset, end_offset, atom, kids): nosemi = False if t.peek_sameline().tok not in (tok.EOF, tok.EOL, tok.RBRACE): x = t.advance() if x.tok != tok.SEMI: - raise JSSyntaxError(x.startpos, 'semi_before_stmnt') - endpos = x.endpos + raise JSSyntaxError(x.start_offset, 'semi_before_stmnt') + end_offset = x.end_offset else: nosemi = True - return ParseNode(kind_, op_, startpos, endpos, atom, kids, nosemi) + return ParseNode(kind_, op_, start_offset, end_offset, atom, kids, nosemi) def _function_arglist(t): fn_args = [] @@ -39,8 +39,8 @@ while True: x = t.expect(tok.NAME) fn_args.append(ParseNode(kind.NAME, op.ARGNAME, - x.startpos, - x.endpos, x.atom, [])) + x.start_offset, + x.end_offset, x.atom, [])) if t.peek().tok == tok.COMMA: t.advance() else: @@ -50,23 +50,23 @@ def _primary_expression(t): x = t.next_withregexp() if x.tok == tok.THIS: - return ParseNode(kind.PRIMARY, op.THIS, x.startpos, x.endpos, None, []) + return ParseNode(kind.PRIMARY, op.THIS, x.start_offset, x.end_offset, None, []) elif x.tok == tok.NAME: - return ParseNode(kind.NAME, op.NAME, x.startpos, x.endpos, x.atom, [None]) + return ParseNode(kind.NAME, op.NAME, x.start_offset, x.end_offset, x.atom, [None]) elif x.tok == tok.NULL: - return ParseNode(kind.PRIMARY, op.NULL, x.startpos, x.endpos, None, []) + return ParseNode(kind.PRIMARY, op.NULL, x.start_offset, x.end_offset, None, []) elif x.tok == tok.TRUE: - return ParseNode(kind.PRIMARY, op.TRUE, x.startpos, x.endpos, None, []) + return ParseNode(kind.PRIMARY, op.TRUE, x.start_offset, x.end_offset, None, []) elif x.tok == tok.FALSE: - return ParseNode(kind.PRIMARY, op.FALSE, x.startpos, x.endpos, None, []) + return ParseNode(kind.PRIMARY, op.FALSE, x.start_offset, x.end_offset, None, []) elif x.tok == tok.STRING: - return ParseNode(kind.STRING, op.STRING, x.startpos, x.endpos, x.atom, []) + return ParseNode(kind.STRING, op.STRING, x.start_offset, x.end_offset, x.atom, []) elif x.tok == tok.REGEXP: - return ParseNode(kind.OBJECT, op.REGEXP, x.startpos, x.endpos, None, []) + return ParseNode(kind.OBJECT, op.REGEXP, x.start_offset, x.end_offset, None, []) elif x.tok == tok.NUMBER: - return ParseNode(kind.NUMBER, None, x.startpos, x.endpos, x.atom, []) + return ParseNode(kind.NUMBER, None, x.start_offset, x.end_offset, x.atom, []) elif x.tok == tok.LBRACKET: - startpos = x.startpos + start_offset = x.start_offset items = [] end_comma = None if t.peek().tok != tok.RBRACKET: @@ -83,18 +83,18 @@ # Expect a comma and use it if the value was missing. x = t.expect(tok.COMMA) comma = ParseNode(kind.COMMA, None, - x.startpos, x.endpos, None, []) + x.start_offset, x.end_offset, None, []) items[-1] = items[-1] or comma # Check for the end. if t.peek().tok == tok.RBRACKET: end_comma = comma break - endpos = t.expect(tok.RBRACKET).endpos - return ParseNode(kind.RB, None, startpos, endpos, None, items, + end_offset = t.expect(tok.RBRACKET).end_offset + return ParseNode(kind.RB, None, start_offset, end_offset, None, items, end_comma=end_comma) elif x.tok == tok.LBRACE: - startpos = x.startpos + start_offset = x.start_offset kids = [] # TODO: get/set end_comma = None @@ -104,50 +104,50 @@ break elif x.tok == tok.STRING: t.expect(tok.STRING) - key = ParseNode(kind.STRING, None, x.startpos, - x.endpos, x.atom, []) + key = ParseNode(kind.STRING, None, x.start_offset, + x.end_offset, x.atom, []) elif x.tok == tok.NUMBER: t.expect(tok.NUMBER) - key = ParseNode(kind.NUMBER, None, x.startpos, - x.endpos, x.atom, []) + key = ParseNode(kind.NUMBER, None, x.start_offset, + x.end_offset, x.atom, []) else: x = t.expect_identifiername() - key = ParseNode(kind.NAME, None, x.startpos, x.endpos, + key = ParseNode(kind.NAME, None, x.start_offset, x.end_offset, x.atom, []) t.expect(tok.COLON) value = _assignment_expression(t, True) - kids.append(ParseNode(kind.COLON, None, key.startpos, - value.endpos, None, [key, value])) + kids.append(ParseNode(kind.COLON, None, key.start_offset, + value.end_offset, None, [key, value])) if t.peek().tok == tok.COMMA: x = t.advance() end_comma = ParseNode(kind.COMMA, None, - x.startpos, x.endpos, None, []) + x.start_offset, x.end_offset, None, []) else: end_comma = None break - endpos = t.expect(tok.RBRACE).endpos - return ParseNode(kind.RC, None, startpos, endpos, None, kids, + end_offset = t.expect(tok.RBRACE).end_offset + return ParseNode(kind.RC, None, start_offset, end_offset, None, kids, end_comma=end_comma) elif x.tok == tok.LPAREN: - startpos = x.startpos + start_offset = x.start_offset kid = _expression(t, True) - endpos = t.expect(tok.RPAREN).endpos - return ParseNode(kind.RP, None, startpos, endpos, None, [kid]) + end_offset = t.expect(tok.RPAREN).end_offset + return ParseNode(kind.RP, None, start_offset, end_offset, None, [kid]) else: - raise JSSyntaxError(x.startpos, 'syntax_error') + raise JSSyntaxError(x.start_offset, 'syntax_error') def _function_declaration(t, named_opcode): node = _function_expression(t, named_opcode) # Convert anonymous functions in expressions. if node.opcode == op.ANONFUNOBJ: - node = _auto_semicolon(t, kind.SEMI, None, node.startpos, node.endpos, + node = _auto_semicolon(t, kind.SEMI, None, node.start_offset, node.end_offset, None, [node]) return node def _function_expression(t, named_opcode): - startpos = t.expect(tok.FUNCTION).startpos + start_offset = t.expect(tok.FUNCTION).start_offset if t.peek().tok == tok.NAME: fn_name = t.expect(tok.NAME).atom opcode = named_opcode @@ -157,12 +157,12 @@ t.expect(tok.LPAREN) fn_args = _function_arglist(t) t.expect(tok.RPAREN) - fn_body_startpos = t.expect(tok.LBRACE).startpos + fn_body_start_offset = t.expect(tok.LBRACE).start_offset kids = _sourceelements(t, tok.RBRACE) - fn_body_endpos = t.expect(tok.RBRACE).endpos - fn_body = ParseNode(kind.LC, None, fn_body_startpos, - fn_body_endpos, None, kids) - return ParseNode(kind.FUNCTION, opcode, startpos, fn_body.endpos, + fn_body_end_offset = t.expect(tok.RBRACE).end_offset + fn_body = ParseNode(kind.LC, None, fn_body_start_offset, + fn_body_end_offset, None, kids) + return ParseNode(kind.FUNCTION, opcode, start_offset, fn_body.end_offset, fn_name, [fn_body], fn_args=fn_args) def _argument_list(t): @@ -177,17 +177,17 @@ return args def _new_expression(t): - startpos = t.expect(tok.NEW).startpos + start_offset = t.expect(tok.NEW).start_offset expr = _member_expression(t) # If no (), this is a variant of the NewExpression if t.peek().tok == tok.LPAREN: t.expect(tok.LPAREN) args = _argument_list(t) - endpos = t.expect(tok.RPAREN).endpos + end_offset = t.expect(tok.RPAREN).end_offset else: args = [] - endpos = expr.endpos - return ParseNode(kind.NEW, op.NEW, startpos, endpos, + end_offset = expr.end_offset + return ParseNode(kind.NEW, op.NEW, start_offset, end_offset, None, [expr] + args) def _member_expression(t, _recurse=True): @@ -203,13 +203,13 @@ if t.peek().tok == tok.LBRACKET: t.advance() expr = _expression(t, True) - endpos = t.expect(tok.RBRACKET).endpos - kid = ParseNode(kind.LB, op.GETELEM, kid.startpos, endpos, + end_offset = t.expect(tok.RBRACKET).end_offset + kid = ParseNode(kind.LB, op.GETELEM, kid.start_offset, end_offset, None, [kid, expr]) elif t.peek().tok == tok.DOT: t.advance() expr = t.expect_identifiername() - kid = ParseNode(kind.DOT, op.GETPROP, kid.startpos, expr.endpos, + kid = ParseNode(kind.DOT, op.GETPROP, kid.start_offset, expr.end_offset, expr.atom, [kid]) else: return kid @@ -224,21 +224,21 @@ if x.tok == tok.LPAREN: t.expect(tok.LPAREN) args = _argument_list(t) - endpos = t.expect(tok.RPAREN).endpos - expr = ParseNode(kind.LP, op.CALL, expr.startpos, - endpos, None, [expr] + args) + end_offset = t.expect(tok.RPAREN).end_offset + expr = ParseNode(kind.LP, op.CALL, expr.start_offset, + end_offset, None, [expr] + args) elif x.tok == tok.LBRACKET: t.expect(tok.LBRACKET) lookup = _expression(t, True) - endpos = t.expect(tok.RBRACKET).endpos + end_offset = t.expect(tok.RBRACKET).end_offset expr = ParseNode(kind.LB, op.GETELEM, - expr.startpos, endpos, + expr.start_offset, end_offset, None, [expr, lookup]) elif x.tok == tok.DOT: t.expect(tok.DOT) lookup = t.expect_identifiername() expr = ParseNode(kind.DOT, op.GETPROP, - expr.startpos, lookup.endpos, + expr.start_offset, lookup.end_offset, lookup.atom, [expr]) else: return expr @@ -251,17 +251,17 @@ def _postfix_expression(t): kid = _lefthandside_expression(t) if t.peek_sameline().tok == tok.INC: - endpos = t.expect(tok.INC).endpos + end_offset = t.expect(tok.INC).end_offset if kid.kind == kind.DOT and kid.opcode == op.GETPROP: opcode = op.PROPINC else: opcode = op.NAMEINC return ParseNode(kind.INC, opcode, - kid.startpos, endpos, None, [kid]) + kid.start_offset, end_offset, None, [kid]) elif t.peek_sameline().tok == tok.DEC: - endpos = t.expect(tok.DEC).endpos + end_offset = t.expect(tok.DEC).end_offset return ParseNode(kind.DEC, op.NAMEDEC, - kid.startpos, endpos, None, [kid]) + kid.start_offset, end_offset, None, [kid]) else: return kid @@ -280,9 +280,9 @@ x = t.peek() if x.tok in _UNARY: kind_, op_ = _UNARY[x.tok] - startpos = t.advance().startpos + start_offset = t.advance().start_offset kid = _unary_expression(t) - return ParseNode(kind_, op_, startpos, kid.endpos, None, [kid]) + return ParseNode(kind_, op_, start_offset, kid.end_offset, None, [kid]) else: return _postfix_expression(t) @@ -300,7 +300,7 @@ t.advance() kids.append(child_expr_callback(t)) expr = ParseNode(kind_, op_, - kids[0].startpos, kids[1].endpos, + kids[0].start_offset, kids[1].end_offset, None, kids) _MULTIPLICATIVE = { @@ -359,7 +359,7 @@ t.advance() right = _equality_expression(t, allowin) left = ParseNode(kind.BITAND, op.BITAND, - left.startpos, right.endpos, + left.start_offset, right.end_offset, None, [left, right]) return left @@ -369,7 +369,7 @@ t.advance() right = _bitwise_and_expression(t, allowin) left = ParseNode(kind.BITXOR, op.BITXOR, - left.startpos, right.endpos, + left.start_offset, right.end_offset, None, [left, right]) return left @@ -379,7 +379,7 @@ t.advance() right = _bitwise_xor_expression(t, allowin) left = ParseNode(kind.BITOR, op.BITOR, - left.startpos, right.endpos, + left.start_offset, right.end_offset, None, [left, right]) return left @@ -396,7 +396,7 @@ right = exprs.pop() left = exprs[-1] exprs[-1] = ParseNode(kind.AND, op.AND, - left.startpos, right.endpos, + left.start_offset, right.end_offset, None, [left, right]) return exprs[0] @@ -413,7 +413,7 @@ right = exprs.pop() left = exprs[-1] exprs[-1] = ParseNode(kind.OR, op.OR, - left.startpos, right.endpos, + left.start_offset, right.end_offset, None, [left, right]) return exprs[0] @@ -425,7 +425,7 @@ t.expect(tok.COLON) else_ = _assignment_expression(t, allowin) return ParseNode(kind.HOOK, None, - kid.startpos, else_.endpos, + kid.start_offset, else_.end_offset, None, [kid, if_, else_]) else: return kid @@ -463,12 +463,12 @@ assert kid.opcode == op.CALL kid.opcode = op.SETCALL else: - raise JSSyntaxError(left.startpos, 'invalid_assign') + raise JSSyntaxError(left.start_offset, 'invalid_assign') kind_, op_ = _ASSIGNS[t.peek().tok] t.advance() right = _assignment_expression(t, allowin) return ParseNode(kind_, op_, - left.startpos, right.endpos, None, [left, right]) + left.start_offset, right.end_offset, None, [left, right]) else: return left @@ -479,8 +479,8 @@ t.advance() items.append(_assignment_expression(t, allowin)) if len(items) > 1: - return ParseNode(kind.COMMA, None, items[0].startpos, - items[-1].endpos, None, items) + return ParseNode(kind.COMMA, None, items[0].start_offset, + items[-1].end_offset, None, items) else: return items[0] @@ -493,8 +493,8 @@ t.advance() value = _assignment_expression(t, allowin) nodes.append(ParseNode(kind.NAME, op.SETNAME if value else op.NAME, - x.startpos, - value.endpos if value else x.endpos, + x.start_offset, + value.end_offset if value else x.end_offset, x.atom, [value])) if t.peek().tok == tok.COMMA: @@ -504,27 +504,27 @@ def _block_statement(t): kids = [] - startpos = t.expect(tok.LBRACE).startpos + start_offset = t.expect(tok.LBRACE).start_offset while t.peek().tok != tok.RBRACE: kids.append(_statement(t)) - endpos = t.expect(tok.RBRACE).endpos - return ParseNode(kind.LC, None, startpos, endpos, None, kids) + end_offset = t.expect(tok.RBRACE).end_offset + return ParseNode(kind.LC, None, start_offset, end_offset, None, kids) def _empty_statement(t): # EMPTY STATEMENT x = t.expect(tok.SEMI) - return ParseNode(kind.SEMI, None, x.startpos, x.endpos, None, [None]) + return ParseNode(kind.SEMI, None, x.start_offset, x.end_offset, None, [None]) def _var_statement(t): # VARIABLE STATEMENT - startpos = t.expect(tok.VAR).startpos + start_offset = t.expect(tok.VAR).start_offset nodes = _variable_declaration(t, True) return _auto_semicolon(t, kind.VAR, op.DEFVAR, - startpos, nodes[-1].endpos, None, nodes) + start_offset, nodes[-1].end_offset, None, nodes) def _if_statement(t): # IF STATEMENT - startpos = t.expect(tok.IF).startpos + start_offset = t.expect(tok.IF).start_offset t.expect(tok.LPAREN) condition = _expression(t, True) t.expect(tok.RPAREN) @@ -534,38 +534,38 @@ else_body = _statement(t) else: else_body = None - endpos = else_body.endpos if else_body else if_body.endpos - return ParseNode(kind.IF, None, startpos, - endpos, None, [condition, if_body, else_body]) + end_offset = else_body.end_offset if else_body else if_body.end_offset + return ParseNode(kind.IF, None, start_offset, + end_offset, None, [condition, if_body, else_body]) def _do_statement(t): - startpos = t.expect(tok.DO).startpos + start_offset = t.expect(tok.DO).start_offset code = _statement(t) t.expect(tok.WHILE) t.expect(tok.LPAREN) expr = _expression(t, True) endtoken = t.expect(tok.RPAREN) return _auto_semicolon(t, kind.DO, None, - startpos, endtoken.endpos, None, [code, expr]) + start_offset, endtoken.end_offset, None, [code, expr]) def _while_statement(t): - startpos = t.expect(tok.WHILE).startpos + start_offset = t.expect(tok.WHILE).start_offset t.expect(tok.LPAREN) expr = _expression(t, True) t.expect(tok.RPAREN) code = _statement(t) return ParseNode(kind.WHILE, None, - startpos, code.endpos, None, [expr, code]) + start_offset, code.end_offset, None, [expr, code]) def _for_statement(t): - for_startpos = t.expect(tok.FOR).startpos + for_start_offset = t.expect(tok.FOR).start_offset t.expect(tok.LPAREN) for_exprs = [] if t.peek().tok == tok.VAR: - var_startpos = t.advance().startpos + var_start_offset = t.advance().start_offset kids = _variable_declaration(t, False) - vars = ParseNode(kind.VAR, op.DEFVAR, var_startpos, kids[-1].endpos, + vars = ParseNode(kind.VAR, op.DEFVAR, var_start_offset, kids[-1].end_offset, None, kids) if t.peek().tok == tok.IN: @@ -589,8 +589,8 @@ for_exprs = [expr, None, None] if len(for_exprs) == 2: - condition = ParseNode(kind.IN, None, for_exprs[0].startpos, - for_exprs[-1].endpos, None, for_exprs) + condition = ParseNode(kind.IN, None, for_exprs[0].start_offset, + for_exprs[-1].end_offset, None, for_exprs) else: x = t.expect(tok.SEMI) if t.peek().tok != tok.SEMI: @@ -605,12 +605,12 @@ body = _statement(t) return ParseNode(kind.FOR, op.FORIN if condition.kind == kind.IN else None, - for_startpos, body.endpos, + for_start_offset, body.end_offset, None, [condition, body]) def _continue_statement(t): endtoken = t.expect(tok.CONTINUE) - startpos = endtoken.startpos + start_offset = endtoken.start_offset if t.peek_sameline().tok == tok.NAME: endtoken = t.expect(tok.NAME) @@ -618,11 +618,11 @@ else: name = None # TODO: Validate Scope Labels - return _auto_semicolon(t, kind.CONTINUE, None, startpos, endtoken.endpos, name, []) + return _auto_semicolon(t, kind.CONTINUE, None, start_offset, endtoken.end_offset, name, []) def _break_statement(t): endtoken = t.expect(tok.BREAK) - startpos = endtoken.startpos + start_offset = endtoken.start_offset if t.peek_sameline().tok == tok.NAME: endtoken = t.expect(tok.NAME) @@ -630,11 +630,11 @@ else: name = None # TODO: Validate Scope Labels - return _auto_semicolon(t, kind.BREAK, None, startpos, endtoken.endpos, name, []) + return _auto_semicolon(t, kind.BREAK, None, start_offset, endtoken.end_offset, name, []) def _return_statement(t): endtoken = t.expect(tok.RETURN) - startpos = endtoken.startpos + start_offset = endtoken.start_offset if t.peek_sameline().tok not in (tok.EOF, tok.EOL, tok.SEMI, tok.RBRACE): expr = _expression(t, True) @@ -642,108 +642,108 @@ else: expr = None # TODO: Validate Scope Labels - return _auto_semicolon(t, kind.RETURN, None, startpos, endtoken.endpos, + return _auto_semicolon(t, kind.RETURN, None, start_offset, endtoken.end_offset, None, [expr]) def _with_statement(t): - startpos = t.expect(tok.WITH).startpos + start_offset = t.expect(tok.WITH).start_offset t.expect(tok.LPAREN) expr = _expression(t, True) t.expect(tok.RPAREN) body = _statement(t) - return ParseNode(kind.WITH, None, startpos, body.endpos, None, [expr, body]) + return ParseNode(kind.WITH, None, start_offset, body.end_offset, None, [expr, body]) def _switch_statement(t): - switch_startpos = t.expect(tok.SWITCH).startpos + switch_start_offset = t.expect(tok.SWITCH).start_offset t.expect(tok.LPAREN) expr = _expression(t, True) t.expect(tok.RPAREN) - lc_startpos = t.expect(tok.LBRACE).startpos + lc_start_offset = t.expect(tok.LBRACE).start_offset cases = [] while t.peek().tok != tok.RBRACE: case_kind = None case_expr = None if t.peek().tok == tok.CASE: - case_startpos = t.advance().startpos + case_start_offset = t.advance().start_offset case_kind = kind.CASE case_expr = _expression(t, True) elif t.peek().tok == tok.DEFAULT: - case_startpos = t.advance().startpos + case_start_offset = t.advance().start_offset case_kind = kind.DEFAULT else: - raise JSSyntaxError(t.peek().startpos, 'invalid_case') + raise JSSyntaxError(t.peek().start_offset, 'invalid_case') - case_endpos = t.expect(tok.COLON).endpos + case_end_offset = t.expect(tok.COLON).end_offset statements = [] while t.peek().tok not in (tok.DEFAULT, tok.CASE, tok.RBRACE): statements.append(_statement(t)) if statements: - statements_startpos = statements[0].startpos - statements_endpos = statements[-1].endpos - case_endpos = statements[-1].endpos + statements_start_offset = statements[0].start_offset + statements_end_offset = statements[-1].end_offset + case_end_offset = statements[-1].end_offset else: - statements_startpos = case_endpos - statements_endpos = case_endpos + statements_start_offset = case_end_offset + statements_end_offset = case_end_offset - cases.append(ParseNode(case_kind, None, case_startpos, case_endpos, + cases.append(ParseNode(case_kind, None, case_start_offset, case_end_offset, None, [ case_expr, - ParseNode(kind.LC, None, statements_startpos, - statements_endpos, None, statements) + ParseNode(kind.LC, None, statements_start_offset, + statements_end_offset, None, statements) ])) - rc_endpos = t.expect(tok.RBRACE).endpos - return ParseNode(kind.SWITCH, None, switch_startpos, rc_endpos, + rc_end_offset = t.expect(tok.RBRACE).end_offset + return ParseNode(kind.SWITCH, None, switch_start_offset, rc_end_offset, None, [expr, - ParseNode(kind.LC, None, lc_startpos, rc_endpos, None, cases)]) + ParseNode(kind.LC, None, lc_start_offset, rc_end_offset, None, cases)]) def _throw_statement(t): # TODO: Validate Scope - startpos = t.expect(tok.THROW).startpos + start_offset = t.expect(tok.THROW).start_offset if t.peek_sameline().tok == tok.EOL: - raise JSSyntaxError(t.peek_sameline().startpos, 'expected_statement') + raise JSSyntaxError(t.peek_sameline().start_offset, 'expected_statement') expr = _expression(t, True) - return _auto_semicolon(t, kind.THROW, op.THROW, startpos, expr.endpos, + return _auto_semicolon(t, kind.THROW, op.THROW, start_offset, expr.end_offset, None, [expr]) def _try_statement(t): - try_startpos = t.expect(tok.TRY).startpos + try_start_offset = t.expect(tok.TRY).start_offset try_node = _block_statement(t) catch_node = None finally_node = None - try_endpos = None + try_end_offset = None if t.peek().tok == tok.CATCH: - catch_startpos = t.advance().startpos + catch_start_offset = t.advance().start_offset t.expect(tok.LPAREN) x = t.expect(tok.NAME) - catch_expr = ParseNode(kind.NAME, None, x.startpos, x.endpos, + catch_expr = ParseNode(kind.NAME, None, x.start_offset, x.end_offset, x.atom, [None]) t.expect(tok.RPAREN) catch_block = _block_statement(t) - catch_endpos = catch_block.endpos + catch_end_offset = catch_block.end_offset catch_node = \ ParseNode(kind.RESERVED, None, None, None, None, [ ParseNode(kind.LEXICALSCOPE, op.LEAVEBLOCK, - catch_startpos, catch_endpos, None, [ - ParseNode(kind.CATCH, None, catch_startpos, - catch_endpos, None, + catch_start_offset, catch_end_offset, None, [ + ParseNode(kind.CATCH, None, catch_start_offset, + catch_end_offset, None, [catch_expr, None, catch_block]) ]) ]) - try_endpos = catch_endpos + try_end_offset = catch_end_offset if t.peek().tok == tok.FINALLY: t.advance() finally_node = _block_statement(t) - try_endpos = finally_node.endpos + try_end_offset = finally_node.end_offset if not catch_node and not finally_node: - raise JSSyntaxError(try_endpos, 'invalid_catch') + raise JSSyntaxError(try_end_offset, 'invalid_catch') - return ParseNode(kind.TRY, None, try_startpos, try_endpos, + return ParseNode(kind.TRY, None, try_start_offset, try_end_offset, None, [try_node, catch_node, finally_node]) @@ -779,7 +779,7 @@ elif x.tok == tok.TRY: return _try_statement(t) elif x.tok == tok.EOF: - raise JSSyntaxError(x.startpos, 'unexpected_eof') + raise JSSyntaxError(x.start_offset, 'unexpected_eof') elif x.tok == tok.FUNCTION: return _function_declaration(t, op.CLOSURE) #TODO: warn, since this is not reliable @@ -788,13 +788,13 @@ if expr.kind == tok.NAME and t.peek().tok == tok.COLON: t.expect(tok.COLON) stmt = _statement(t) - return ParseNode(kind.COLON, op.NAME, expr.startpos, - stmt.endpos, expr.atom, [stmt]) + return ParseNode(kind.COLON, op.NAME, expr.start_offset, + stmt.end_offset, expr.atom, [stmt]) - return _auto_semicolon(t, kind.SEMI, None, expr.startpos, expr.endpos, + return _auto_semicolon(t, kind.SEMI, None, expr.start_offset, expr.end_offset, None, [expr]) else: - raise JSSyntaxError(x.startpos, 'syntax_error') + raise JSSyntaxError(x.start_offset, 'syntax_error') def _sourceelements(t, end_tok): nodes = [] @@ -807,13 +807,14 @@ else: nodes.append(_statement(t)) -def parsestring(s, startpos=None): - stream = tokenizer.TokenStream(s, startpos) +def parsestring(s, start_offset=0): + assert not start_offset is None + stream = tokenizer.TokenStream(s, start_offset) t = tokenizer.Tokenizer(stream) nodes = _sourceelements(t, tok.EOF) - lc_endpos = t.expect(tok.EOF).endpos - lc_startpos = nodes[-1].startpos if nodes else lc_endpos - return ParseNode(kind.LC, None, lc_startpos, lc_endpos, None, nodes) + lc_end_offset = t.expect(tok.EOF).end_offset + lc_start_offset = nodes[-1].start_offset if nodes else lc_end_offset + return ParseNode(kind.LC, None, lc_start_offset, lc_end_offset, None, nodes) def is_valid_version(version): return version in _VERSIONS @@ -824,14 +825,13 @@ assert kid.parent is node _validate(kid, depth+1) -def parse(script, jsversion, - error_callback, startpos): +def parse(script, jsversion, error_callback, start_offset): # TODO: respect version assert is_valid_version(jsversion) try: - root = parsestring(script, startpos) + root = parsestring(script, start_offset) except JSSyntaxError as error: - error_callback(error.pos.line, error.pos.col, error.msg, error.msg_args) + error_callback(error.offset, error.msg, error.msg_args) return None _validate(root) return root @@ -868,8 +868,8 @@ self.assertEquals(right.kind, kind.RC) node = right.end_comma self.assertEquals(node.kind, tok.COMMA) - self.assertEquals(node.startpos, NodePos(0, 6)) - self.assertEquals(node.endpos, NodePos(0, 6)) + self.assertEquals(node.start_offset, NodePos(0, 6)) + self.assertEquals(node.end_offset, NodePos(0, 6)) def _testArrayEndComma(self, script, col): root = parsestring(script) node, = root.kids @@ -885,8 +885,8 @@ self.assert_(node is None) else: self.assertEquals(node.kind, tok.COMMA) - self.assertEquals(node.startpos, NodePos(0, col)) - self.assertEquals(node.endpos, NodePos(0, col)) + self.assertEquals(node.start_offset, NodePos(0, col)) + self.assertEquals(node.end_offset, NodePos(0, col)) def testArrayEndComma(self): self._testArrayEndComma('a=[,]', 3) self._testArrayEndComma('a=[a,]', 4) Modified: trunk/jsengine/structs.py =================================================================== --- trunk/jsengine/structs.py 2013-10-03 20:13:37 UTC (rev 333) +++ trunk/jsengine/structs.py 2013-10-08 16:36:44 UTC (rev 334) @@ -90,26 +90,27 @@ class ParseNode: node_index = None parent = None - def __init__(self, kind_, op_, start_pos, end_pos, atom, kids, + def __init__(self, kind_, op_, start_offset, end_offset, atom, kids, no_semi=False, end_comma=None, fn_args=None): assert not kids is None assert kind.contains(kind_) assert op_ is None or op.contains(op_) if kind_ == kind.RESERVED: - assert start_pos is None - assert end_pos is None + assert start_offset is None + assert end_offset is None else: - assert isinstance(start_pos, NodePos), repr(start_pos) - assert isinstance(end_pos, NodePos), repr(end_pos) + assert isinstance(start_offset, int), repr(start_offset) + assert isinstance(end_offset, int), repr(end_offset) assert end_comma is None or isinstance(end_comma, ParseNode) - assert (start_pos is None and end_pos is None) or start_pos <= end_pos + assert (start_offset is None and end_offset is None) or start_offset <= end_offset, \ + (start_offset, end_offset) self.kind = kind_ self.opcode = op_ self.atom = atom self.kids = kids self._lefthandside = False - self.startpos = start_pos - self.endpos = end_pos + self.start_offset = start_offset + self.end_offset = end_offset self.no_semi = no_semi self.end_comma = end_comma @@ -132,11 +133,6 @@ else: self.dval = float(self.atom) - def start_pos(self): - return self.startpos - def end_pos(self): - return self.endpos - def is_equivalent(self, other, are_functions_equiv=False): if not other: return False Modified: trunk/jsengine/tokenizer/__init__.py =================================================================== --- trunk/jsengine/tokenizer/__init__.py 2013-10-03 20:13:37 UTC (rev 333) +++ trunk/jsengine/tokenizer/__init__.py 2013-10-08 16:36:44 UTC (rev 334) @@ -1,6 +1,5 @@ # vim: sw=4 ts=4 et from jsengine import JSSyntaxError -from jsengine.structs import NodePositions _WHITESPACE = u'\u0020\t\u000B\u000C\u00A0\uFFFF' _LINETERMINATOR = u'\u000A\u000D\u2028\u2029' @@ -141,59 +140,60 @@ def __init__(self, tok, atom=None): self.tok = tok self.atom = atom - self.startpos = None - self.endpos = None - def setpos(self, startpos, endpos): - self.startpos = startpos - self.endpos = endpos + self.start_offset = None + self.end_offset = None + def set_offset(self, start_offset, end_offset): + self.start_offset = start_offset + self.end_offset = end_offset def __repr__(self): return 'Token(%r, %r)' % \ (self.tok, self.atom) class TokenStream: - def __init__(self, content, startpos=None): + def __init__(self, content, start_offset=0): + assert isinstance(start_offset, int) self._content = content - self._pos = 0 - self._watched_pos = None - self._nodepositions = NodePositions(content, startpos) + self._start_offset = start_offset + self._offset = 0 + self._watched_offset = None - def getpos(self, offset=0): - return self._nodepositions.from_offset(self._pos+offset) + def get_offset(self, offset=0): + return self._start_offset + self._offset + offset def watch_reads(self): - self._watched_pos = self._pos + self._watched_offset = self._offset def get_watched_reads(self): - assert not self._watched_pos == None - s = self._content[self._watched_pos:self._pos]... [truncated message content] |