Squashed 'third_party/ctemplate/' content from commit 6742f62

Change-Id: I828e4e4c906f13ba19944d78a8a78652b62949af
git-subtree-dir: third_party/ctemplate
git-subtree-split: 6742f6233db12f545e90baa8f34f5c29c4eb396a
diff --git a/src/htmlparser/fsm_config.py b/src/htmlparser/fsm_config.py
new file mode 100755
index 0000000..ff27c89
--- /dev/null
+++ b/src/htmlparser/fsm_config.py
@@ -0,0 +1,221 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2008, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+# ---
+#
+# Create a state machine object based on a definition file.
+#
+
+__author__ = 'falmeida@google.com (Filipe Almeida)'
+
+class OrderedDict:
+  """Ordered dictionary implementation."""
+
+  # Define the minimum functionality we need for our application.
+  # Easiser would be to subclass from UserDict.DictMixin, and only
+  # define __getitem__, __setitem__, __delitem__, and keys, but that's
+  # not as portable.  We don't need to define much more, so we just do.
+
+  def __init__(self):
+    self._dict = {}
+    self._keys = []
+
+  def __getitem__(self, key):
+    return self._dict[key]
+
+  def __setitem__(self, key, value):
+    if key not in self._keys:
+      self._keys.append(key)
+    self._dict[key] = value
+
+  def __delitem__(self, key):
+    self._keys.remove(key)
+    del self._dict[key]
+
+  def keys(self):
+    return self._keys
+
+  # Below are all we have to define in addition to what DictMixin would need
+  def __len__(self):
+    return len(self.keys())
+
+  def __contains__(self, key):
+    return self.has_key(key)
+
+  def __iter__(self):
+    # It's not as portable -- though it would be more space-efficient -- to do
+    #   for k in self.keys(): yield k
+    return iter(self.keys())
+
+class State(object):
+  """Contains information about a specific state."""
+
+  def __init__(self):
+    pass
+
+  name = None
+  external_name = None
+  transitions = []
+
+
+class Transition(object):
+  """Contains information about a specific transition."""
+
+  def __init__(self, condition, source, destination):
+    self.condition = condition
+    self.source = source
+    self.destination = destination
+
+
+class FSMConfig(object):
+  """Container for the statemachine definition."""
+
+  sm = {}  # dictionary that contains the finite state machine definition
+           # loaded from a config file.
+  transitions = []  # List of transitions.
+  conditions = {}   # Mapping between the condition name and the bracket
+                    # expression.
+  states = OrderedDict()  # Ordered dictionary of states.
+  name = None
+  comment = None
+
+  def AddState(self, **dic):
+    """Called from the definition file with the description of the state.
+
+    Receives a dictionary and populates internal structures based on it. The
+    dictionary is in the following format:
+
+    {'name': state_name,
+     'external': exposed state name,
+     'transitions': [
+       [condition, destination_state ],
+       [condition, destination_state ]
+     ]
+    }
+
+    """
+
+    state = State()
+    state.name = dic['name']
+    state.external_name = dic['external']
+
+    state_transitions = []
+
+    for (condition, destination) in dic['transitions']:
+      transition = Transition(condition, state.name, destination)
+      state_transitions.append(transition)
+
+    self.transitions.extend(state_transitions)
+    state.transitions = state_transitions
+    self.states[state.name] = state
+
+  def AddCondition(self, name, expression):
+    """Called from the definition file with the definition of a condition.
+
+    Receives the name of the condition and it's expression.
+    """
+    self.conditions[name] = expression
+
+  def Load(self, filename):
+    """Load the state machine definition file.
+
+    In the definition file, which is based on the python syntax, the following
+    variables and functions are defined.
+
+    name: Name of the state machine
+    comment: Comment line on the generated file.
+    condition(): A mapping between condition names and bracket expressions.
+    state(): Defines a state and it's transitions. It accepts the following
+             attributes:
+      name: name of the state
+      external: exported name of the state. The exported name can be used
+                multiple times in order to create a super state.
+      transitions: List of pairs containing the condition for the transition
+                   and the destination state. Transitions are ordered so if
+                   a default rule is used, it must be the last one in the list.
+
+    Example:
+
+    name = 'c comment parser'
+
+    condition('/', '/')
+    condition('*', '*')
+    condition('linefeed', '\\n')
+    condition('default', '[:default:]')
+
+    state(name = 'text',
+          external = 'comment',
+          transitions = [
+            [ '/', 'comment_start' ],
+            [ 'default', 'text' ]
+          ])
+
+    state(name = 'comment_start',
+          external = 'comment',
+          transitions = [
+            [ '/', 'comment_line' ],
+            [ '*', 'comment_multiline' ],
+            [ 'default', 'text' ]
+          ])
+
+    state(name = 'comment_line',
+          external = 'comment',
+          transitions = [
+            [ 'linefeed', 'text' ],
+            [ 'default', 'comment_line' ]
+          ])
+
+    state(name = 'comment_multiline',
+          external = 'comment',
+          transitions = [
+            [ '*', 'comment_multiline_close' ],
+            [ 'default', 'comment_multiline' ]
+          ])
+
+    state(name = 'comment_multiline_close',
+          external = 'comment',
+          transitions = [
+            [ '/', 'text' ],
+            [ 'default', 'comment_multiline' ]
+          ])
+
+    """
+
+    self.sm['state'] = self.AddState
+    self.sm['condition'] = self.AddCondition
+    execfile(filename, self.sm)
+    self.name = self.sm['name']
+    if not self.name.isalnum():
+      raise Exception("State machine name must consist of only alphanumeric"
+                      "characters.")
+    self.comment = self.sm['comment']
+
+  def __init__(self):
+    pass