OLD | NEW |
| (Empty) |
1 #!/usr/bin/env python | |
2 # Copyright 2008 The Closure Linter Authors. All Rights Reserved. | |
3 # | |
4 # Licensed under the Apache License, Version 2.0 (the "License"); | |
5 # you may not use this file except in compliance with the License. | |
6 # You may obtain a copy of the License at | |
7 # | |
8 # http://www.apache.org/licenses/LICENSE-2.0 | |
9 # | |
10 # Unless required by applicable law or agreed to in writing, software | |
11 # distributed under the License is distributed on an "AS-IS" BASIS, | |
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 # See the License for the specific language governing permissions and | |
14 # limitations under the License. | |
15 | |
16 """Parser for JavaScript files.""" | |
17 | |
18 | |
19 | |
20 from closure_linter import javascripttokens | |
21 from closure_linter import statetracker | |
22 from closure_linter import tokenutil | |
23 | |
24 # Shorthand | |
25 Type = javascripttokens.JavaScriptTokenType | |
26 | |
27 | |
28 class JsDocFlag(statetracker.DocFlag): | |
29 """Javascript doc flag object. | |
30 | |
31 Attribute: | |
32 flag_type: param, return, define, type, etc. | |
33 flag_token: The flag token. | |
34 type_start_token: The first token specifying the flag JS type, | |
35 including braces. | |
36 type_end_token: The last token specifying the flag JS type, | |
37 including braces. | |
38 type: The type spec string. | |
39 jstype: The type spec, a TypeAnnotation instance. | |
40 name_token: The token specifying the flag name. | |
41 name: The flag name | |
42 description_start_token: The first token in the description. | |
43 description_end_token: The end token in the description. | |
44 description: The description. | |
45 """ | |
46 | |
47 # Please keep these lists alphabetized. | |
48 | |
49 # Some projects use the following extensions to JsDoc. | |
50 # TODO(robbyw): determine which of these, if any, should be illegal. | |
51 EXTENDED_DOC = frozenset([ | |
52 'class', 'code', 'desc', 'final', 'hidden', 'inheritDoc', 'link', | |
53 'meaning', 'provideGoog', 'throws']) | |
54 | |
55 LEGAL_DOC = EXTENDED_DOC | statetracker.DocFlag.LEGAL_DOC | |
56 | |
57 | |
58 class JavaScriptStateTracker(statetracker.StateTracker): | |
59 """JavaScript state tracker. | |
60 | |
61 Inherits from the core EcmaScript StateTracker adding extra state tracking | |
62 functionality needed for JavaScript. | |
63 """ | |
64 | |
65 def __init__(self): | |
66 """Initializes a JavaScript token stream state tracker.""" | |
67 statetracker.StateTracker.__init__(self, JsDocFlag) | |
68 | |
69 def Reset(self): | |
70 self._scope_depth = 0 | |
71 self._block_stack = [] | |
72 super(JavaScriptStateTracker, self).Reset() | |
73 | |
74 def InTopLevel(self): | |
75 """Compute whether we are at the top level in the class. | |
76 | |
77 This function call is language specific. In some languages like | |
78 JavaScript, a function is top level if it is not inside any parenthesis. | |
79 In languages such as ActionScript, a function is top level if it is directly | |
80 within a class. | |
81 | |
82 Returns: | |
83 Whether we are at the top level in the class. | |
84 """ | |
85 return self._scope_depth == self.ParenthesesDepth() | |
86 | |
87 def InFunction(self): | |
88 """Returns true if the current token is within a function. | |
89 | |
90 This js-specific override ignores goog.scope functions. | |
91 | |
92 Returns: | |
93 True if the current token is within a function. | |
94 """ | |
95 return self._scope_depth != self.FunctionDepth() | |
96 | |
97 def InNonScopeBlock(self): | |
98 """Compute whether we are nested within a non-goog.scope block. | |
99 | |
100 Returns: | |
101 True if the token is not enclosed in a block that does not originate from | |
102 a goog.scope statement. False otherwise. | |
103 """ | |
104 return self._scope_depth != self.BlockDepth() | |
105 | |
106 def GetBlockType(self, token): | |
107 """Determine the block type given a START_BLOCK token. | |
108 | |
109 Code blocks come after parameters, keywords like else, and closing parens. | |
110 | |
111 Args: | |
112 token: The current token. Can be assumed to be type START_BLOCK | |
113 Returns: | |
114 Code block type for current token. | |
115 """ | |
116 last_code = tokenutil.SearchExcept(token, Type.NON_CODE_TYPES, reverse=True) | |
117 if last_code.type in (Type.END_PARAMETERS, Type.END_PAREN, | |
118 Type.KEYWORD) and not last_code.IsKeyword('return'): | |
119 return self.CODE | |
120 else: | |
121 return self.OBJECT_LITERAL | |
122 | |
123 def GetCurrentBlockStart(self): | |
124 """Gets the start token of current block. | |
125 | |
126 Returns: | |
127 Starting token of current block. None if not in block. | |
128 """ | |
129 if self._block_stack: | |
130 return self._block_stack[-1] | |
131 else: | |
132 return None | |
133 | |
134 def HandleToken(self, token, last_non_space_token): | |
135 """Handles the given token and updates state. | |
136 | |
137 Args: | |
138 token: The token to handle. | |
139 last_non_space_token: The last non space token encountered | |
140 """ | |
141 if token.type == Type.START_BLOCK: | |
142 self._block_stack.append(token) | |
143 if token.type == Type.IDENTIFIER and token.string == 'goog.scope': | |
144 self._scope_depth += 1 | |
145 if token.type == Type.END_BLOCK: | |
146 start_token = self._block_stack.pop() | |
147 if tokenutil.GoogScopeOrNoneFromStartBlock(start_token): | |
148 self._scope_depth -= 1 | |
149 super(JavaScriptStateTracker, self).HandleToken(token, | |
150 last_non_space_token) | |
OLD | NEW |