1#!/usr/bin/env python 2# 3# Copyright 2008 The Closure Linter Authors. All Rights Reserved. 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS-IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17"""Classes to represent JavaScript tokens.""" 18 19__author__ = ('robbyw@google.com (Robert Walker)', 20 'ajp@google.com (Andy Perelson)') 21 22from closure_linter.common import tokens 23 24class JavaScriptTokenType(tokens.TokenType): 25 """Enumeration of JavaScript token types, and useful sets of token types.""" 26 NUMBER = 'number' 27 START_SINGLE_LINE_COMMENT = '//' 28 START_BLOCK_COMMENT = '/*' 29 START_DOC_COMMENT = '/**' 30 END_BLOCK_COMMENT = '*/' 31 END_DOC_COMMENT = 'doc */' 32 COMMENT = 'comment' 33 SINGLE_QUOTE_STRING_START = "'string" 34 SINGLE_QUOTE_STRING_END = "string'" 35 DOUBLE_QUOTE_STRING_START = '"string' 36 DOUBLE_QUOTE_STRING_END = 'string"' 37 STRING_TEXT = 'string' 38 START_BLOCK = '{' 39 END_BLOCK = '}' 40 START_PAREN = '(' 41 END_PAREN = ')' 42 START_BRACKET = '[' 43 END_BRACKET = ']' 44 REGEX = '/regex/' 45 FUNCTION_DECLARATION = 'function(...)' 46 FUNCTION_NAME = 'function functionName(...)' 47 START_PARAMETERS = 'startparams(' 48 PARAMETERS = 'pa,ra,ms' 49 END_PARAMETERS = ')endparams' 50 SEMICOLON = ';' 51 DOC_FLAG = '@flag' 52 DOC_INLINE_FLAG = '{@flag ...}' 53 DOC_START_BRACE = 'doc {' 54 DOC_END_BRACE = 'doc }' 55 DOC_PREFIX = 'comment prefix: * ' 56 DOC_TYPE_START_BLOCK = 'Type <' 57 DOC_TYPE_END_BLOCK = 'Type >' 58 DOC_TYPE_MODIFIER = 'modifier' 59 SIMPLE_LVALUE = 'lvalue=' 60 KEYWORD = 'keyword' 61 OPERATOR = 'operator' 62 IDENTIFIER = 'identifier' 63 64 STRING_TYPES = frozenset([ 65 SINGLE_QUOTE_STRING_START, SINGLE_QUOTE_STRING_END, 66 DOUBLE_QUOTE_STRING_START, DOUBLE_QUOTE_STRING_END, STRING_TEXT]) 67 68 COMMENT_TYPES = frozenset([ 69 START_SINGLE_LINE_COMMENT, COMMENT, 70 START_BLOCK_COMMENT, START_DOC_COMMENT, 71 END_BLOCK_COMMENT, END_DOC_COMMENT, 72 DOC_START_BRACE, DOC_END_BRACE, 73 DOC_FLAG, DOC_INLINE_FLAG, DOC_PREFIX, 74 DOC_TYPE_START_BLOCK, DOC_TYPE_END_BLOCK, DOC_TYPE_MODIFIER]) 75 76 FLAG_DESCRIPTION_TYPES = frozenset([ 77 DOC_INLINE_FLAG, COMMENT, DOC_START_BRACE, DOC_END_BRACE, 78 DOC_TYPE_START_BLOCK, DOC_TYPE_END_BLOCK, DOC_TYPE_MODIFIER]) 79 80 FLAG_ENDING_TYPES = frozenset([DOC_FLAG, END_DOC_COMMENT]) 81 82 NON_CODE_TYPES = COMMENT_TYPES | frozenset([ 83 tokens.TokenType.WHITESPACE, tokens.TokenType.BLANK_LINE]) 84 85 UNARY_OPERATORS = ['!', 'new', 'delete', 'typeof', 'void'] 86 87 UNARY_OK_OPERATORS = ['--', '++', '-', '+'] + UNARY_OPERATORS 88 89 UNARY_POST_OPERATORS = ['--', '++'] 90 91 # An expression ender is any token that can end an object - i.e. we could have 92 # x.y or [1, 2], or (10 + 9) or {a: 10}. 93 EXPRESSION_ENDER_TYPES = [tokens.TokenType.NORMAL, IDENTIFIER, NUMBER, 94 SIMPLE_LVALUE, END_BRACKET, END_PAREN, END_BLOCK, 95 SINGLE_QUOTE_STRING_END, DOUBLE_QUOTE_STRING_END] 96 97 98class JavaScriptToken(tokens.Token): 99 """JavaScript token subclass of Token, provides extra instance checks. 100 101 The following token types have data in attached_object: 102 - All JsDoc flags: a parser.JsDocFlag object. 103 """ 104 105 def IsKeyword(self, keyword): 106 """Tests if this token is the given keyword. 107 108 Args: 109 keyword: The keyword to compare to. 110 111 Returns: 112 True if this token is a keyword token with the given name. 113 """ 114 return self.type == JavaScriptTokenType.KEYWORD and self.string == keyword 115 116 def IsOperator(self, operator): 117 """Tests if this token is the given operator. 118 119 Args: 120 operator: The operator to compare to. 121 122 Returns: 123 True if this token is a operator token with the given name. 124 """ 125 return self.type == JavaScriptTokenType.OPERATOR and self.string == operator 126 127 def IsAssignment(self): 128 """Tests if this token is an assignment operator. 129 130 Returns: 131 True if this token is an assignment operator. 132 """ 133 return (self.type == JavaScriptTokenType.OPERATOR and 134 self.string.endswith('=') and 135 self.string not in ('==', '!=', '>=', '<=', '===', '!==')) 136 137 def IsComment(self): 138 """Tests if this token is any part of a comment. 139 140 Returns: 141 True if this token is any part of a comment. 142 """ 143 return self.type in JavaScriptTokenType.COMMENT_TYPES 144 145 def IsCode(self): 146 """Tests if this token is code, as opposed to a comment or whitespace.""" 147 return self.type not in JavaScriptTokenType.NON_CODE_TYPES 148 149 def __repr__(self): 150 return '<JavaScriptToken: %d, %s, "%s", %r, %r>' % (self.line_number, 151 self.type, self.string, 152 self.values, 153 self.metadata) 154