12da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#!/usr/bin/env python
22da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#
32da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# Copyright 2010 The Closure Linter Authors. All Rights Reserved.
42da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#
52da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# Licensed under the Apache License, Version 2.0 (the "License");
62da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# you may not use this file except in compliance with the License.
72da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# You may obtain a copy of the License at
82da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#
92da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#      http://www.apache.org/licenses/LICENSE-2.0
102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#
112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# Unless required by applicable law or agreed to in writing, software
122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# distributed under the License is distributed on an "AS-IS" BASIS,
132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# See the License for the specific language governing permissions and
152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# limitations under the License.
162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis"""Metadata pass for annotating tokens in EcmaScript files."""
182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis__author__ = ('robbyw@google.com (Robert Walker)')
202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
212da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom closure_linter import javascripttokens
222da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom closure_linter import tokenutil
232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
252da489cd246702bee5938545b18a6f710ed214bcJamie GennisTokenType = javascripttokens.JavaScriptTokenType
262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
282da489cd246702bee5938545b18a6f710ed214bcJamie Gennisclass ParseError(Exception):
292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  """Exception indicating a parse error at the given token.
302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  Attributes:
322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    token: The token where the parse error occurred.
332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  """
342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def __init__(self, token, message=None):
362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Initialize a parse error at the given token with an optional message.
372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Args:
392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      token: The token where the parse error occurred.
402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      message: A message describing the parse error.
412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Exception.__init__(self, message)
432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.token = token
442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
462da489cd246702bee5938545b18a6f710ed214bcJamie Gennisclass EcmaContext(object):
472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  """Context object for EcmaScript languages.
482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  Attributes:
502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    type: The context type.
512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    start_token: The token where this context starts.
522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    end_token: The token where this context ends.
532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    parent: The parent context.
542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  """
552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  # The root context.
572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  ROOT = 'root'
582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  # A block of code.
602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  BLOCK = 'block'
612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  # A pseudo-block of code for a given case or default section.
632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  CASE_BLOCK = 'case_block'
642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  # Block of statements in a for loop's parentheses.
662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  FOR_GROUP_BLOCK = 'for_block'
672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  # An implied block of code for 1 line if, while, and for statements
692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  IMPLIED_BLOCK = 'implied_block'
702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  # An index in to an array or object.
722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  INDEX = 'index'
732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  # An array literal in [].
752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  ARRAY_LITERAL = 'array_literal'
762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  # An object literal in {}.
782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  OBJECT_LITERAL = 'object_literal'
792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  # An individual element in an array or object literal.
812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  LITERAL_ELEMENT = 'literal_element'
822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  # The portion of a ternary statement between ? and :
842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  TERNARY_TRUE = 'ternary_true'
852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  # The portion of a ternary statment after :
872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  TERNARY_FALSE = 'ternary_false'
882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  # The entire switch statment.  This will contain a GROUP with the variable
902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  # and a BLOCK with the code.
912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  # Since that BLOCK is not a normal block, it can not contain statements except
932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  # for case and default.
942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  SWITCH = 'switch'
952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  # A normal comment.
972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  COMMENT = 'comment'
982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  # A JsDoc comment.
1002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  DOC = 'doc'
1012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  # An individual statement.
1032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  STATEMENT = 'statement'
1042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  # Code within parentheses.
1062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  GROUP = 'group'
1072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  # Parameter names in a function declaration.
1092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  PARAMETERS = 'parameters'
1102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  # A set of variable declarations appearing after the 'var' keyword.
1122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  VAR = 'var'
1132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  # Context types that are blocks.
1152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  BLOCK_TYPES = frozenset([
1162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ROOT, BLOCK, CASE_BLOCK, FOR_GROUP_BLOCK, IMPLIED_BLOCK])
1172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def __init__(self, type, start_token, parent):
1192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Initializes the context object.
1202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Args:
1222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      type: The context type.
1232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      start_token: The token where this context starts.
1242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      parent: The parent context.
1252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
1262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.type = type
1272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.start_token = start_token
1282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.end_token = None
1292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.parent = parent
1302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def __repr__(self):
1322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Returns a string representation of the context object."""
1332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    stack = []
1342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    context = self
1352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    while context:
1362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      stack.append(context.type)
1372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      context = context.parent
1382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return 'Context(%s)' % ' > '.join(stack)
1392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1412da489cd246702bee5938545b18a6f710ed214bcJamie Gennisclass EcmaMetaData(object):
1422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  """Token metadata for EcmaScript languages.
1432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  Attributes:
1452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    last_code: The last code token to appear before this one.
1462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    context: The context this token appears in.
1472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    operator_type: The operator type, will be one of the *_OPERATOR constants
1482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        defined below.
1492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  """
1502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  UNARY_OPERATOR = 'unary'
1522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  UNARY_POST_OPERATOR = 'unary_post'
1542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  BINARY_OPERATOR = 'binary'
1562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  TERNARY_OPERATOR = 'ternary'
1582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def __init__(self):
1602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Initializes a token metadata object."""
1612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.last_code = None
1622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.context = None
1632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.operator_type = None
1642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.is_implied_semicolon = False
1652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.is_implied_block = False
1662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.is_implied_block_close = False
1672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def __repr__(self):
1692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Returns a string representation of the context object."""
1702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    parts = ['%r' % self.context]
1712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if self.operator_type:
1722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      parts.append('optype: %r' % self.operator_type)
1732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if self.is_implied_semicolon:
1742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      parts.append('implied;')
1752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return 'MetaData(%s)' % ', '.join(parts)
1762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def IsUnaryOperator(self):
1782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return self.operator_type in (EcmaMetaData.UNARY_OPERATOR,
1792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                                  EcmaMetaData.UNARY_POST_OPERATOR)
1802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def IsUnaryPostOperator(self):
1822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return self.operator_type == EcmaMetaData.UNARY_POST_OPERATOR
1832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1852da489cd246702bee5938545b18a6f710ed214bcJamie Gennisclass EcmaMetaDataPass(object):
1862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  """A pass that iterates over all tokens and builds metadata about them."""
1872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def __init__(self):
1892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Initialize the meta data pass object."""
1902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.Reset()
1912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def Reset(self):
1932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Resets the metadata pass to prepare for the next file."""
1942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self._token = None
1952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self._context = None
1962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self._AddContext(EcmaContext.ROOT)
1972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self._last_code = None
1982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def _CreateContext(self, type):
2002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Overridable by subclasses to create the appropriate context type."""
2012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return EcmaContext(type, self._token, self._context)
2022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def _CreateMetaData(self):
2042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Overridable by subclasses to create the appropriate metadata type."""
2052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return EcmaMetaData()
2062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def _AddContext(self, type):
2082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Adds a context of the given type to the context stack.
2092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Args:
2112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      type: The type of context to create
2122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
2132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self._context  = self._CreateContext(type)
2142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def _PopContext(self):
2162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Moves up one level in the context stack.
2172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Returns:
2192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      The former context.
2202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Raises:
2222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ParseError: If the root context is popped.
2232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
2242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    top_context = self._context
2252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    top_context.end_token = self._token
2262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self._context = top_context.parent
2272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if self._context:
2282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      return top_context
2292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    else:
2302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      raise ParseError(self._token)
2312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def _PopContextType(self, *stop_types):
2332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Pops the context stack until a context of the given type is popped.
2342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Args:
2362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      stop_types: The types of context to pop to - stops at the first match.
2372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Returns:
2392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      The context object of the given type that was popped.
2402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
2412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    last = None
2422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    while not last or last.type not in stop_types:
2432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      last = self._PopContext()
2442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return last
2452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def _EndStatement(self):
2472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Process the end of a statement."""
2482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self._PopContextType(EcmaContext.STATEMENT)
2492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if self._context.type == EcmaContext.IMPLIED_BLOCK:
2502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self._token.metadata.is_implied_block_close = True
2512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self._PopContext()
2522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def _ProcessContext(self):
2542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Process the context at the current token.
2552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Returns:
2572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      The context that should be assigned to the current token, or None if
2582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      the current context after this method should be used.
2592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Raises:
2612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ParseError: When the token appears in an invalid context.
2622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
2632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    token = self._token
2642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    token_type = token.type
2652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if self._context.type in EcmaContext.BLOCK_TYPES:
2672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      # Whenever we're in a block, we add a statement context.  We make an
2682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      # exception for switch statements since they can only contain case: and
2692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      # default: and therefore don't directly contain statements.
2702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      # The block we add here may be immediately removed in some cases, but
2712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      # that causes no harm.
2722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      parent = self._context.parent
2732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if not parent or parent.type != EcmaContext.SWITCH:
2742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._AddContext(EcmaContext.STATEMENT)
2752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    elif self._context.type == EcmaContext.ARRAY_LITERAL:
2772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self._AddContext(EcmaContext.LITERAL_ELEMENT)
2782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if token_type == TokenType.START_PAREN:
2802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if self._last_code and self._last_code.IsKeyword('for'):
2812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # for loops contain multiple statements in the group unlike while,
2822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # switch, if, etc.
2832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._AddContext(EcmaContext.FOR_GROUP_BLOCK)
2842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      else:
2852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._AddContext(EcmaContext.GROUP)
2862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    elif token_type == TokenType.END_PAREN:
2882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      result = self._PopContextType(EcmaContext.GROUP,
2892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                                    EcmaContext.FOR_GROUP_BLOCK)
2902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      keyword_token = result.start_token.metadata.last_code
2912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      # keyword_token will not exist if the open paren is the first line of the
2922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      # file, for example if all code is wrapped in an immediately executed
2932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      # annonymous function.
2942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if keyword_token and keyword_token.string in ('if', 'for', 'while'):
2952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        next_code = tokenutil.SearchExcept(token, TokenType.NON_CODE_TYPES)
2962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if next_code.type != TokenType.START_BLOCK:
2972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          # Check for do-while.
2982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          is_do_while = False
2992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          pre_keyword_token = keyword_token.metadata.last_code
3002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          if (pre_keyword_token and
3012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis              pre_keyword_token.type == TokenType.END_BLOCK):
3022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            start_block_token = pre_keyword_token.metadata.context.start_token
3032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            is_do_while = start_block_token.metadata.last_code.string == 'do'
3042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          # If it's not do-while, it's an implied block.
3062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          if not is_do_while:
3072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self._AddContext(EcmaContext.IMPLIED_BLOCK)
3082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            token.metadata.is_implied_block = True
3092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      return result
3112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    # else (not else if) with no open brace after it should be considered the
3132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    # start of an implied block, similar to the case with if, for, and while
3142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    # above.
3152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    elif (token_type == TokenType.KEYWORD and
3162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          token.string == 'else'):
3172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      next_code = tokenutil.SearchExcept(token, TokenType.NON_CODE_TYPES)
3182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if (next_code.type != TokenType.START_BLOCK and
3192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          (next_code.type != TokenType.KEYWORD or next_code.string != 'if')):
3202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._AddContext(EcmaContext.IMPLIED_BLOCK)
3212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        token.metadata.is_implied_block = True
3222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    elif token_type == TokenType.START_PARAMETERS:
3242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self._AddContext(EcmaContext.PARAMETERS)
3252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    elif token_type == TokenType.END_PARAMETERS:
3272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      return self._PopContextType(EcmaContext.PARAMETERS)
3282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    elif token_type == TokenType.START_BRACKET:
3302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if (self._last_code and
3312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          self._last_code.type in TokenType.EXPRESSION_ENDER_TYPES):
3322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._AddContext(EcmaContext.INDEX)
3332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      else:
3342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._AddContext(EcmaContext.ARRAY_LITERAL)
3352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    elif token_type == TokenType.END_BRACKET:
3372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      return self._PopContextType(EcmaContext.INDEX, EcmaContext.ARRAY_LITERAL)
3382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    elif token_type == TokenType.START_BLOCK:
3402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if (self._last_code.type in (TokenType.END_PAREN,
3412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                                   TokenType.END_PARAMETERS) or
3422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          self._last_code.IsKeyword('else') or
3432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          self._last_code.IsKeyword('do') or
3442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          self._last_code.IsKeyword('try') or
3452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          self._last_code.IsKeyword('finally') or
3462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          (self._last_code.IsOperator(':') and
3472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis           self._last_code.metadata.context.type == EcmaContext.CASE_BLOCK)):
3482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # else, do, try, and finally all might have no () before {.
3492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # Also, handle the bizzare syntax case 10: {...}.
3502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._AddContext(EcmaContext.BLOCK)
3512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      else:
3522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._AddContext(EcmaContext.OBJECT_LITERAL)
3532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    elif token_type == TokenType.END_BLOCK:
3552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      context = self._PopContextType(EcmaContext.BLOCK,
3562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                                     EcmaContext.OBJECT_LITERAL)
3572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if self._context.type == EcmaContext.SWITCH:
3582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # The end of the block also means the end of the switch statement it
3592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # applies to.
3602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return self._PopContext()
3612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      return context
3622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    elif token.IsKeyword('switch'):
3642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self._AddContext(EcmaContext.SWITCH)
3652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    elif (token_type == TokenType.KEYWORD and
3672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          token.string in ('case', 'default')):
3682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      # Pop up to but not including the switch block.
3692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      while self._context.parent.type != EcmaContext.SWITCH:
3702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._PopContext()
3712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    elif token.IsOperator('?'):
3732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self._AddContext(EcmaContext.TERNARY_TRUE)
3742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    elif token.IsOperator(':'):
3762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if self._context.type == EcmaContext.OBJECT_LITERAL:
3772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._AddContext(EcmaContext.LITERAL_ELEMENT)
3782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      elif self._context.type == EcmaContext.TERNARY_TRUE:
3802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._PopContext()
3812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._AddContext(EcmaContext.TERNARY_FALSE)
3822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      # Handle nested ternary statements like:
3842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      # foo = bar ? baz ? 1 : 2 : 3
3852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      # When we encounter the second ":" the context is
3862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      # ternary_false > ternary_true > statement > root
3872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      elif (self._context.type == EcmaContext.TERNARY_FALSE and
3882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self._context.parent.type == EcmaContext.TERNARY_TRUE):
3892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis           self._PopContext() # Leave current ternary false context.
3902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis           self._PopContext() # Leave current parent ternary true
3912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis           self._AddContext(EcmaContext.TERNARY_FALSE)
3922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      elif self._context.parent.type == EcmaContext.SWITCH:
3942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._AddContext(EcmaContext.CASE_BLOCK)
3952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    elif token.IsKeyword('var'):
3972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self._AddContext(EcmaContext.VAR)
3982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    elif token.IsOperator(','):
4002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      while self._context.type not in (EcmaContext.VAR,
4012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                                       EcmaContext.ARRAY_LITERAL,
4022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                                       EcmaContext.OBJECT_LITERAL,
4032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                                       EcmaContext.STATEMENT,
4042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                                       EcmaContext.PARAMETERS,
4052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                                       EcmaContext.GROUP):
4062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._PopContext()
4072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    elif token_type == TokenType.SEMICOLON:
4092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self._EndStatement()
4102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def Process(self, first_token):
4122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Processes the token stream starting with the given token."""
4132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self._token = first_token
4142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    while self._token:
4152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self._ProcessToken()
4162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if self._token.IsCode():
4182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._last_code = self._token
4192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self._token = self._token.next
4212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    try:
4232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self._PopContextType(self, EcmaContext.ROOT)
4242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    except ParseError:
4252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      # Ignore the "popped to root" error.
4262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      pass
4272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def _ProcessToken(self):
4292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Process the given token."""
4302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    token = self._token
4312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    token.metadata = self._CreateMetaData()
4322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    context = (self._ProcessContext() or self._context)
4332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    token.metadata.context = context
4342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    token.metadata.last_code = self._last_code
4352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    # Determine the operator type of the token, if applicable.
4372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if token.type == TokenType.OPERATOR:
4382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      token.metadata.operator_type = self._GetOperatorType(token)
4392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    # Determine if there is an implied semicolon after the token.
4412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if token.type != TokenType.SEMICOLON:
4422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      next_code = tokenutil.SearchExcept(token, TokenType.NON_CODE_TYPES)
4432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      # A statement like if (x) does not need a semicolon after it
4442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      is_implied_block = self._context == EcmaContext.IMPLIED_BLOCK
4452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      is_last_code_in_line = token.IsCode() and (
4462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          not next_code or next_code.line_number != token.line_number)
4472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      is_continued_identifier = (token.type == TokenType.IDENTIFIER and
4482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                                 token.string.endswith('.'))
4492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      is_continued_operator = (token.type == TokenType.OPERATOR and
4502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                               not token.metadata.IsUnaryPostOperator())
4512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      is_continued_dot = token.string == '.'
4522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      next_code_is_operator = next_code and next_code.type == TokenType.OPERATOR
4532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      next_code_is_dot = next_code and next_code.string == '.'
4542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      is_end_of_block = (token.type == TokenType.END_BLOCK and
4552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          token.metadata.context.type != EcmaContext.OBJECT_LITERAL)
4562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      is_multiline_string = token.type == TokenType.STRING_TEXT
4572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      next_code_is_block = next_code and next_code.type == TokenType.START_BLOCK
4582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if (is_last_code_in_line and
4592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          self._StatementCouldEndInContext() and
4602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          not is_multiline_string and
4612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          not is_end_of_block and
4622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          not is_continued_identifier and
4632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          not is_continued_operator and
4642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          not is_continued_dot and
4652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          not next_code_is_dot and
4662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          not next_code_is_operator and
4672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          not is_implied_block and
4682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          not next_code_is_block):
4692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        token.metadata.is_implied_semicolon = True
4702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._EndStatement()
4712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def _StatementCouldEndInContext(self):
4732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Returns whether the current statement (if any) may end in this context."""
4742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    # In the basic statement or variable declaration context, statement can
4752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    # always end in this context.
4762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if self._context.type in (EcmaContext.STATEMENT, EcmaContext.VAR):
4772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      return True
4782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    # End of a ternary false branch inside a statement can also be the
4802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    # end of the statement, for example:
4812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    # var x = foo ? foo.bar() : null
4822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    # In this case the statement ends after the null, when the context stack
4832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    # looks like ternary_false > var > statement > root.
4842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if (self._context.type == EcmaContext.TERNARY_FALSE and
4852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._context.parent.type in (EcmaContext.STATEMENT, EcmaContext.VAR)):
4862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      return True
4872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    # In all other contexts like object and array literals, ternary true, etc.
4892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    # the statement can't yet end.
4902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return False
4912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def _GetOperatorType(self, token):
4932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Returns the operator type of the given operator token.
4942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Args:
4962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      token: The token to get arity for.
4972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Returns:
4992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      The type of the operator.  One of the *_OPERATOR constants defined in
5002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      EcmaMetaData.
5012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
5022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if token.string == '?':
5032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      return EcmaMetaData.TERNARY_OPERATOR
5042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if token.string in TokenType.UNARY_OPERATORS:
5062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      return EcmaMetaData.UNARY_OPERATOR
5072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    last_code = token.metadata.last_code
5092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if not last_code or last_code.type == TokenType.END_BLOCK:
5102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      return EcmaMetaData.UNARY_OPERATOR
5112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if (token.string in TokenType.UNARY_POST_OPERATORS and
5132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        last_code.type in TokenType.EXPRESSION_ENDER_TYPES):
5142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      return EcmaMetaData.UNARY_POST_OPERATOR
5152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if (token.string in TokenType.UNARY_OK_OPERATORS and
5172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        last_code.type not in TokenType.EXPRESSION_ENDER_TYPES and
5182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        last_code.string not in TokenType.UNARY_POST_OPERATORS):
5192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      return EcmaMetaData.UNARY_OPERATOR
5202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return EcmaMetaData.BINARY_OPERATOR
522