text: from pyparsing import *
  from functools import reduce
  
  import pyparsing
  pyparsing.ParserElement.enablePackrat()
  
  X, Y = 0, 1
  
  ## TODO: design this out
  COLORMAP = {
      'F.Cu' : '#e18335',
      'F.Fab': '#819595',
      'F.SilkS': '#e4cc37',
      'F.CrtYd': '#8fc93a',
      'B.Cu' : '#0072bb',
      'B.Fab': '#564d4a',
      'B.SilkS': '#1e91d6',
      'B.CrtYd': '#8fc93a'
  }
  
  text = dblQuotedString | Word(printables + alphas8bit, excludeChars=')')
  # text = pyparsing.quotedString.addParseAction(pyparsing.removeQuotes)
  # number = Combine(Optional('-') + Word(nums) + Optional(Word('.') + Word(nums)))
  # number = pp.Regex(r"\d+\.\d+([Ee][+-]?\d+)?")
  number = pyparsing.pyparsing_common.number
  integer = Word(nums)
  hex = Word(hexnums)
  
  dblQuotedString.setParseAction(removeQuotes)
  number.setParseAction(lambda tokens: float(tokens[0]))
  integer.setParseAction(lambda tokens: int(tokens[0]))
  
  def boolean_schema(attr, true, false):
      return {
          '0': {
              '_attr': attr,
              '_parser': (Keyword(true) | false)
              .setParseAction(lambda toks: True if toks[0] == true else False),
              '_printer': lambda b: true if b else false
          }
      }
  
  def boolean(attr):
      return boolean_schema(attr, 'true', 'false')
  
  def yes_no(attr):
      return boolean_schema(attr, 'yes', 'no')
  
  def allowed(attr):
      return boolean_schema(attr, 'allowed', 'not_allowed')
  
  def tuple_parser(i, parser=number):
      tparser = Empty()
      for j in range(i):
          tparser += parser
      tparser.setParseAction(lambda t: tuple(t))
      return tparser
  
  def extend_schema(schema, **kwargs):
      schema.update(kwargs)
      return schema
  
  # Python 2 compatibility
  try:
      basestring
  except NameError:
      basestring = str
  
  def flag(name):
      return {
          '_parser': Keyword(name).setParseAction(lambda x: True),
          '_printer': lambda flag: name if flag else '',
          '_tag': False,
          '_attr': name
      }
  
  def parse_action(tokens):
      if len(tokens) < 2:
          return tokens[0]
      if isinstance(tokens[0], dict):
          res = {}
          for token in tokens:
              merge_dict(res, token)
          return res
      return list(tokens)
  
  def leaf_parse_action(attr):
      def action(tokens):
          return {attr: parse_action(tokens[0])}
      return action
  
  def ast_parse_action(attr, ast):
      def action(tokens=None):
          if tokens is None:
              return
          return {attr: ast(**parse_action(tokens[0]))}
      return action
  
  def reduce_parser_list(parsers, func):
      if isinstance(parsers, list) and len(parsers) > 0:
          return reduce(func, parsers)
      return Empty()
  
  def sexpr(name, positional=None, single=None, multiple=None):
      if isinstance(positional, ParserElement):
          parser = positional
      else:
          positional = reduce_parser_list(positional, lambda x, y: x + y)
          single = reduce_parser_list(single, lambda x, y: x & y)
          multiple = reduce_parser_list(multiple, lambda x, y: x & y)
          parser = positional + single + multiple
  
      if name == False:
          return parser
  
      name = Empty() if name == '' else Suppress(name)
      return Suppress('(') + name + parser + Suppress(')')
  
  def generate_parser(tag, schema, attr=None, optional=False):
      '''A schema is either a ParserElement, a AST subclass or a dict.  When
      a dict contains a _parser key it is a leaf.
      A leaf has an _attr name.  When no _attr name is given explicitly it
      defaults to using the tag name.
      '''
      def leaf(parser, attr):
          return Group(parser).setParseAction(leaf_parse_action(attr))
  
      def ast(ast, attr):
          return Group(ast.parser()).setParseAction(ast_parse_action(attr, ast))
  
      # Case 1: schema is a ParserElement
      if isinstance(schema, ParserElement):
          parser = schema
  
          if isinstance(tag, basestring):
              # The tag is a string so the leaf is an sexpr.
              parser = sexpr(tag, parser)
  
          # Default to using the tag as the attr value
          if attr is None:
              attr = tag
  
          # Assert that attr is a string, if it is not
          # something is terribly wrong.
          assert isinstance(attr, basestring)
  
          # Apply the default leaf parse action.
          parser = leaf(parser, attr)
  
          # Make it optional if necessary
          if optional:
              parser = Optional(parser)
  
          return parser
  
      # Case 2: schema is a subclass of AST
      if type(schema) == type and issubclass(schema, AST):
  
          if attr is None:
              attr = tag
  
          assert isinstance(attr, basestring)
  
          parser = ast(schema, attr)
  
          if optional:
              parser = Optional(parser)
  
          return parser
  
      # Case 3: schema is a dict or something is wrong
      assert isinstance(schema, dict)
  
      # Subcase 1: schema is a leaf node
      if '_parser' in schema:
          parser = schema['_parser']
  
          # Something is wrong if parser is a dict
          assert not isinstance(parser, dict)
  
          tag = schema.get('_tag', tag)
          attr = schema.get('_attr', tag)
          parser = generate_parser(tag, parser, attr)
      # Subcase 2: schema is not a leaf node
      else:
          # Determine positional arguments. Positional arguments are
          # arguments that are required to be in a certain position within
          # an sexpression. They are by default required.
          i, positional = 0, []
          while str(i) in schema:
              # Default tag to False for positional arguments. Positional arguments
              # are required to have an _attr, _tag key or have a subschema.
              positional.append(generate_parser(tag=False, schema=schema[str(i)],
                                                attr=None, optional=False))
              i += 1
  
          # Determine the rest of the arguments. Here the position doesn't matter
          # within the sexpr. These default to being optional.
          single = []
          multiple = []
          for key, value in schema.items():
              # A key is either a number representing a positional argument a
              # special key starting with an underscore or a subschema.
              if not (key.isdigit() or key[0] == '_'):
                  # Tags that have _multiple set need to go after those that don't
                  if isinstance(value, dict) and '_multiple' in value:
                      children = multiple
                  else:
                      children = single
                  children.append(generate_parser(tag=key, schema=value,
                                                  attr=None, optional=True))
  
          parser = sexpr(tag, positional, single, multiple)
  
      if schema.get('_multiple', False):
          parser = ZeroOrMore(parser)
      elif schema.get('_optional', optional):
          parser = Optional(parser)
  
      return parser
  
  
  def find_attr(attr, value, schema):
      '''
  
      '''
      def is_leaf_node(schema):
          if not isinstance(schema, dict):
              return True
          if '_parser' in schema:
              return True
          return False
  
      def printer(schema):
          def closure(printer):
              return (lambda value: ' '.join(map(printer, value))
                      if isinstance(value, list) else printer(value))
  
          if type(schema) == type:
              return schema.to_string
  
          if not isinstance(schema, dict):
              return False
  
          printer = False
          parser = schema.get('_parser', False)
          if type(parser) == type and issubclass(parser, AST):
              printer = parser.to_string
  
          printer = schema.get('_printer', printer)
  
          if schema.get('_multiple', False):
              printer = closure(printer)
  
          return printer
  
      # Case 1: Schema is a leaf node
      if is_leaf_node(schema):
          if not isinstance(schema, dict):
              return None
  
          if schema.get('_attr', schema.get('_tag', False)) == attr:
              printer = printer(schema)
              if printer:
                  value = ('_', printer(value))
  
              if schema.get('_tag', False):
                  value = {schema['_tag']: value}
  
              return value
  
          return None
  
      # Case 2: Subschema is a leaf node and the schema does not
      # overwrite the _attr.
      if attr in schema and is_leaf_node(schema[attr]) and \
         (not isinstance(schema[attr], dict) or
              (schema[attr].get('_attr', attr) == attr)):
  
          subschema = schema[attr]
          tag = attr
  
          if isinstance(subschema, dict):
              tag = subschema.get('_tag', tag)
  
          printer = printer(subschema)
          if printer:
              value = printer(value)
              tag = '_' + attr
  
          return {tag: value}
  
      # Default: Search in subschemas
      for key, subschema in schema.items():
          if key[0] == '_':
              continue
  
          found = find_attr(attr, value, subschema)
          if not found is None:
              return {key: found}
  
      return None
  
  
  def tree_to_string(tree, level=0):
      if isinstance(tree, basestring):
          if tree == '':
              return '""'
          if ' ' in tree or '(' in tree or ')' in tree:
              return '"%s"' % tree
          return tree
      if isinstance(tree, tuple):
          if tree[0] == '_':
              return tree[1]
          return ' '.join(map(tree_to_string, tree))
      if isinstance(tree, float):
          return '%.10f' % tree
      if isinstance(tree, int):
          return str(tree)
      if isinstance(tree, list):
          return ' '.join(map(tree_to_string, tree))
      if isinstance(tree, AST):
          return tree.to_string()
  
      assert isinstance(tree, dict)
  
      keys = [key for key in tree.keys() if key.isdigit()]
      keys.sort()
      pos = []
      for key in keys:
          pos.append(tree_to_string(tree[key], level))
  
      single = []
      multiple = []
      for key, value in tree.items():
          if len(key) > 0 and key[0] == '_':
              if not value == '':
                  multiple.append(value)
          elif not key.isdigit():
              value = tree_to_string(value, level + 1)
              if not value == '':
                  single.append('\n%s(%s %s)' % (level * '    ', key, value))
  
      return ' '.join(pos + single + multiple)
  
  def merge_dict(d1, d2):
      for key, value in d2.items():
          if key in d1:
              if isinstance(d1[key], dict):
                  merge_dict(d1[key], value)
              else:
                  if not isinstance(d1[key], list):
                      d1[key] = [d1[key]]
                  d1[key].append(value)
          else:
              d1[key] = value
  
  class AST(object):
      '''
      Abstract Syntax Tree (AST)
      Extraordinarily undocumented AST class
      '''
      tag = 'sexpr'
      schema = text
  
      def __init__(self, **kwargs):
          '''Set attributes as kwargs passed to AST initializer'''
          self.attributes = kwargs
  
      def __getattr__(self, attr):
          '''Checks to see if attr is in attributes otherwise raise AttrError'''
          try:
              return self.attributes.get(attr)
          except KeyError as e:
              raise AttributeError(e)
  
      def __setattr__(self, attr, value):
          '''If attr in attributes then set otherwise set attr regularly'''
          if not attr == 'attributes' and attr in self.attributes:
              self.attributes[attr] = value
          else:
              super(AST, self).__setattr__(attr, value)
  
      def __eq__(self, other):
          '''Check to see that attributes are equivalent'''
          return self.attributes == other.attributes
  
      def __repr__(self):
          attrs = {}
          for key, value in self.attributes.items():
              if value is not None:
                  attrs[key] = value
          return '(%s %s)' % (self.tag, repr(attrs))
  
      def __deepcopy__(self, memo):
          import copy
          return self.__class__(**copy.deepcopy(self.__dict__['attributes']))
  
      def __str__(self):
          return self.to_string()[1:]
  
      def to_string(self, attributes=None):
          if attributes is None:
              attributes = self.attributes.items()
          tree = {}
          for attr, value in attributes:
              if value is None:
                  continue
              found = find_attr(attr, value, self.schema)
              if found is None:
                  continue
              merge_dict(tree, found)
          if tree == {}:
              tree = self.attributes
          else:
              tree = {self.tag: tree}
          return tree_to_string(tree)
  
      def init_list(self, arg, default):
          '''Helper to initialize lists. Since default arguments are initialized
          at import time and lists are reference types, all lists of all instances
          point to the same list.'''
          if arg is None:
              return default
          if not isinstance(arg, list):
              return [arg]
          return arg
  
      @classmethod
      def parser(cls):
          return generate_parser(cls.tag, cls.schema)
  
      @classmethod
      def parse(cls, string):
          '''Parses str and returns instance of class passed into func'''
          if not hasattr(cls, '_parser'):
              cls._parser = cls.parser()
          parse_result = cls._parser.parseString(string)
          result = {}
          for res in parse_result:
              if len(list(res.keys())) < 1:
                  continue
              key = next(iter(res.keys()))
              if not key in result:
                  result.update(res)
              else:
                  if not isinstance(result[key], list):
                      result[key] = [result[key]]
                  result[key].append(res[key])
          return cls(**result)
  
      @classmethod
      def from_schema(cls, tag, schema):
          """Only for testing purposes."""
          cls.tag = tag
          cls.schema = schema
          cls._parser = generate_parser(tag, schema)
          return cls
  