import sys, os, os.path, re, string, types
from crymp import *

RX_ID = re.compile(r'^[a-zA-Z_][a-zA-Z0-9_]*$')
RX_split = re.compile(r'\s*,\s*')
RX_trailing_whitespace = re.compile(r'\s*$')
RX_leading_whitespace = re.compile(r'^\s*')
RX_begin_annotation = re.compile(r'^\s*//%CM\b')
RX_empty = re.compile(r'^\s*$')
RX_leading_CPP_comment = re.compile(r'^\s*//')
RX_label = re.compile(r'^\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*:')
RX_ppdirective = re.compile(r'^\s*#')

indent_shift = None

def indent(width = None):
  """Create an indentation string.

  :Parameters:
    - `width`: The indentation width, or None.  If None is specified
      (default), then the current output indentation is used.

  :Return:
    The requested indentation, represented as a string of whitespace
    characters.
  """

  global indent_shift

  indent = ''
  if width is None:
    if indent_shift is None or len(indent_shift) != Options.shift_width:
      indent_shift = ''
      for index in range(0, Options.shift_width):
	indent_shift += ' '
    for index in range(0, Options.output_indent):
      indent += indent_shift
  else:
    for index in range(0, width):
      indent += ' '
  return indent

def expand(code, tab_size, return_lines = False):
  """Expand TABs and other whitespace characters in the specified code block.

  :Parameters:
    - `code`: The code block.  This is either a string holding the code or a
      list of code lines.
    - `tab_size`: The TAB size to be assumed for expansion.
    - `return_lines`: Flag indicating if the expanded code should be returned
      in one block or as a list of lines.

  :Return:
    The code block with all TABs expanded and the minimum intent level of the
    code block (returned as a tuple).  Lines starting with C/C++ labels or
    preprocessor directives do not contribute to the minumum indent level.  If
    the `return_lines` option was set, then the code block is returned as a
    list of lines.
  """

  if isinstance(code, types.StringType):
    code = code.replace('\r\n', '\n')
    lines = code.split('\n')
  else:
    lines = code

  # Expand TABs and trim trailing whitespace.  Determine the minimum indent
  # level.
  min_indent = -1
  for row in range(0, len(lines)):
    line = lines[row]
    if line.find('\t') != -1:
      line_expanded = ''
      column = 0
      for c in line:
	if c == '\t':
	  tab_stop = (column / tab_size + 1) * tab_size
	  assert tab_stop > column
	  for i in range(0, tab_stop - column):
	    line_expanded += ' '
	elif c.isspace():
	  line_expanded += ' '
	else:
	  line_expanded += c
      line = line_expanded
    line = RX_trailing_whitespace.sub('', line)
    if len(line) > 0:
      match = RX_leading_whitespace.match(line)
      if match is not None:
	leading_white = match.group(0)
      else:
	leading_white = ''
      is_label_or_ppdirective = False
      if RX_label.match(line):
	is_label_or_ppdirective = True
      elif RX_ppdirective.match(line):
        is_label_or_ppdirective = True
      if not is_label_or_ppdirective:
	if min_indent == -1 or len(leading_white) < min_indent:
	  min_indent = len(leading_white)
    lines[row] = line

  if return_lines:
    code = lines
  else:
    code = string.join(lines, '\n')

  return code, min_indent

def shift_indent(lines, shift):
  """Shift the indentation of the specified code block (represented as a list
  of lines).

  The function will leave empty lines unchanged.  Lines containing only
  whitespace are shifted.

  :Parameters:
    - `lines`: The code block, represented as a list of lines.
    - `shift`: The indentation shift.  Negative values reduce the indent
      level.

  :Return:
    The shifted code block, represented as a list of lines.
  """

  if shift == 0: return lines

  if shift < 0:
    rx_indent = re.compile(r'^ {,' + str(-shift) + r'}')
    for row in range(0, len(lines)):
      line = lines[row]
      line = rx_indent.sub('', line)
      lines[row] = line
  else:
    indent_prefix = indent(width = shift)
    for row in range(0, len(lines)):
      line = lines[row]
      if len(line) > 0:
	line = indent_prefix + line
      lines[row] = line
  return lines

def code_str(code, tag = None):
  """Output the specified code block.

  The code block wrapped into a <Code> XML element.  The code indentation is
  changed to match the indentation of the pseudo XML output.  The global
  variable 'Options.tab_size' is used for converting TAB characters in the
  code block.

  :Parameters:
    - `code`: The code block, represented as a string.
    - `tag`: The tag name to be used.  If this is None, then the default tag
      name 'Code' is used.

  :Return:
    The converted code block.
  """

  lines, min_indent = expand(code, Options.tab_size, return_lines = True)
  shift_indent(lines, (Options.output_indent + 1) * Options.shift_width - min_indent)
  if tag is None: tag = 'Code'

  s = indent() + '<' + tag + '>\n'
  first_row = -1
  for row in range(0, len(lines)):
    if len(lines[row]) > 0:
      first_row = row
      break
  last_row = -1
  for row in range(len(lines) - 1, -1, -1):
    if len(lines[row]) > 0:
      last_row = row
      break
  if first_row == -1:
    lines = [ ]
  else:
    assert last_row >= first_row
    lines = lines[first_row : last_row + 1]
  s += string.join(lines, '\n') + '\n'
  s += indent() + '</' + tag + '>\n'
  return s

def strip(buffer, start, end):
  """Strip the code between the specified offsets from the buffer.

  The resulting buffer will have the same line count as the original buffer.
  The stripped area is replaced by a sequence of newline characters or a
  single whitespace (if the area is within a single line).

  :Parameters:
    - `buffer`: The buffer.
    - `start`: The start offset of the area to be stripped.
    - `end`: The end offset.

  :Return:
    The function returns the stripped buffer.
  """

  line_count = buffer.count('\n', start, end)
  sep = ''
  for i in range(0, line_count): sep += '\n'
  if len(sep) == 0: sep = ' '
  return buffer[:start] + '\n' + buffer[end:]

def split_expr(expr):
  """Split the specified expression into sub-expressions.

  The expressions are split at top-level comma operators.

  :Parameters:
    - `expr`: The expression.

  Note: If an element is parsed as a type, then '<' and '>' are interpreted as
  delimiters.  As a consequence, the '<' and '>' operators can _not_ be used
  in a type specifier.

  :Return:
    The function returns a list of sub-expressions.  In case of a parse error,
    None is returned.
  """

  paren_stack = [ ]
  index = -1
  index_list = [ ]
  try:
    for c in expr:
      index += 1
      if c in ('(', '[', '{'):
	paren_stack.append(c)
      elif c == ')':
	if paren_stack.pop() != '(': return
      elif c == ']':
        if paren_stack.pop() != '[': return
      elif c == '}':
        if paren_stack.pop() != '{': return
      elif c == ',':
        if len(paren_stack) == 0:
	  index_list.append(index)
  except IndexError:
    return
  split_list = [ ]
  prev_index = 0
  for index in index_list:
    split_list.append(expr[prev_index:index].strip())
    prev_index = index + 1
  split_list.append(expr[prev_index:].strip())
  return split_list

def parse_args(buffer, start = 0, delim = None, sep = ','):
  """Parse an argument list.

  The function parses a list of comma separated arguments enclosed in a
  matching pair of delimiters.  If the delimiter is already consumed by the
  caller, then the /opening/ delimiter must be passed to the function.  If no
  delimiter is specified, then the next valid delimiter is used.  Valid
  delimiters are '(', '[', and '{'.

  The function will ignore separators and delimiters within string and/or
  character literals.

  :Parameters:
    - `buffer`: The buffer to be parsed.
    - `start`: The start offset for parsing.  Default: 0.
    - `delim`: The delimiter consumed by the caller.  Default is None (no
      delimiter consumed by the caller).  Valid delimiters are '(', '[', '{',
      and '$'.  The special delimiter '$' indicates that the argument list is
      not enclosed in delimiters (i.e. the entire buffer from the start offset
      to the end should be parsed).
    - `sep`: The argument separators.  The default is ','.  The special value
      None is passed, then the enclosed text is not split.

  :Return:
    The function returns a tuple (end, delim, args) where `end` is the offset of
    the first character following the closing delimiter, `delim` is the
    closing delimiter (one of ')', ']', '}', '$'), `args` is the list of
    argument strings.  If `sep` is None, then args is the string enclosed
    between the matching delimiters.

    In case of a parse error, the function returns (None, None, None).
  """

  paren_stack = [ ]
  index_list = [ ]
  match_delim = { '(': ')', '[': ']', '{': '}' }

  # Consume the starting delimiter.
  if delim is None:
    for index in range(start, len(buffer)):
      c = buffer[index]
      if c.isspace(): continue
      if c not in ('(', '[', '{'): return None, None, None
      delim = c
      index += 1
      start = index
      break
  else:
    index = start

  # Match delimiters.  Indices of comma-separators are appended to the
  # index_list.
  end = None
  in_literal, in_escape, literal_type = False, False, None
  for index in range(index, len(buffer)):
    c = buffer[index]

    # Skip strings and character literals.
    if in_literal:
      if in_escape:
	in_escape = False
	continue
      if c == '\\':
	in_escape = True
	continue
      assert not in_escape
      if c == literal_type:
	in_literal = False
	literal_type = None
      continue
    elif c in ('"', "'"):
      in_literal = True
      literal_type = c
      continue

    # Match delimiters.
    assert not in_literal
    if c in ('(', '[', '{'):
      paren_stack.append(c)
    elif len(paren_stack) == 0 and delim != '$' and c == match_delim[delim]:
      end = index + 1
      break
    elif c in (')', ']', '}'):
      if len(paren_stack) == 0 or match_delim[paren_stack.pop()] != c:
	return None, None, None
    elif c == sep:
      if len(paren_stack) == 0:
	index_list.append(index)

  if delim == '$' and len(paren_stack) == 0:
    assert end is None
    end = len(buffer)
  if end is None:
    return None, None, None

  # Extract the arguments.
  if sep is not None:
    args = [ ]
    prev_index = start
    for index in index_list:
      args.append(buffer[prev_index:index].strip())
      prev_index = index + 1
    args.append(buffer[prev_index:end - 1].strip())
  else:
    assert len(index_list) == 0
    args = buffer[start:end - 1]

  if delim != '$': delim = match_delim[delim]

  return end, delim, args

class IncludeBlock:
  """Class representing a block of #include statements.
  """

  def __init__(self, filename, line, body):
    self.filename = filename
    self.line = line
    self.body = body
    self.code = None
    self.directives = [ ]
    self.__parse()

  # FIXME: This pattern will count all trailing backslashes as line
  # continuations, even if the trailing backslash itself is quoted by another
  # backslash.
  __rx_directive = re.compile(r'(^|\n)\s*(#([^\n]|\\\n)*)')

  def __parse(self):
    for match in self.__rx_directive.finditer(self.body):
      directive = match.group(2)
      self.directives.append(directive)

  def __repr__(self): return str(self)

  def __str__(self):
    if len(self.directives) > 0:
      s = indent() + '<IncludeBlock>\n'
      for directive in self.directives:
	s += indent() + '  ' + directive + '\n'
      s += indent() + '</IncludeBlock>\n'
    else:
      s = indent() + '<IncludeBlock/>\n'
    return s

class Annotation:
  """Class representing an annotation mapping.
  """

  __rx_annotation = re.compile(r'\b([a-zA-Z0-9_.-]+)\s*(\+?)=\s*([^;]*);')
  __rx_comment = re.compile(r'(^|\n)[ \t]*//[ \t]*')
  __rx_annot_comment = re.compile(r'#.*$', re.MULTILINE)

  def __init__(self, filename, line, annotation):
    """Constructor.

    :Parameters:
      - `filename`: The name of the source file.
      - `line`: The first line number of the annotated statement.  Used for
	error reporting.
      - `annotation`: The complete annotation string, including comment
	delimiters.  May be None.
    """

    self.filename = filename
    self.line = line
    self.annotation = annotation
    self.map = { }
    self.__parse()

  def __parse(self):
    if self.annotation is None: return
    buffer = self.annotation

    # Remove comment delimiters.
    buffer = self.__rx_comment.sub('\n', buffer)

    # Remove annotation comments.
    buffer = self.__rx_annot_comment.sub('\n', buffer)

    # Parse annotations.
    for match in self.__rx_annotation.finditer(buffer):
      key = match.group(1)
      plus = match.group(2)
      value = match.group(3).strip()
      if plus == '+' and key in self.map:
	self.map[key] = self.map[key] + value
      else:
	if key in self.map:
	  warning(loc(self.filename, self.line)
	      + 'Redefinition of annotation key ' + repr(key),
	      level = 1)
	self.map[key] = value

  def __getitem__(self, key):
    try:
      value = self.map[key]
    except KeyError:
      value = None
    return value

  def __repr__(self): return str(self)

  def __str__(self):
    if len(self.map) > 0:
      s = indent() + '<Annotation>\n'
      for key in self.map:
	s += indent() + '  ' + repr(key) + ' -> ' + repr(self.map[key]) + '\n'
      s += indent() + '</Annotation>\n'
    else:
      s = indent() + '<Annotation/>\n'
    return s

  def read_external(self, block_name, name):
    """Read annotations from the external annotation specifications.

    :Parameters:
     - `block_name`: The name of the containing block.
     - `name`: The name of the element within the block.  None if the calling
       entity is the parallel block itself.
    """

    pass # XXX


class PSection:

  def __init__(self, block, name, line, body, annotation):
    self.block = block
    self.name = name
    self.line = line
    self.body = body
    self.code = None
    self.annotation = Annotation(block.filename, line, annotation)
    self.waitfor = [ ]
    self.waitfor_names = [ ]
    self.__parse()
    self.annotation.read_external(block.name, name)

  __rx_WAITFOR = re.compile(r'\bCM_WAITFOR\(\s*([^\)]*)\s*\)\s*;?')

  def __parse(self):
    buffer = self.body
    block = self.block

    # evaluate and strip the CM_WAITFOR statement(s).
    waitfor_list = [ ]
    for match in self.__rx_WAITFOR.finditer(buffer):
      spec = match.group(1)
      for name in RX_split.split(spec):
	#if name not in block.element_map:
	#  line = self.line + buffer.count('\n', 0, match.start())
	#  error(loc(block.filename, line)
	#      + 'undefined section ' + repr(name) + ' in CM_WAITFOR')
	#  sys.exit(1)
	#psection = block.element_map[name]
	#self.waitfor.append(psection)
	line = self.line + buffer.count('\n', 0, match.start())
	self.waitfor_names.append((name, line))
      waitfor_list.append((match.start(), match.end()))
    waitfor_list.reverse()
    for start, end in waitfor_list:
      buffer = strip(buffer, start, end)

    # todo: insert #line directives into the code fragment.
    self.code = buffer

  def setup_deps(self):
    block = self.block
    for name, line in self.waitfor_names:
      if name not in block.element_map:
	error(loc(block.filename, line)
	    + 'undefined element ' + repr(name) + ' in CM_WAITFOR')
	sys.exit(1)
      pelement = block.element_map[name]
      self.waitfor.append(pelement)

  def __repr__(self): return str(self)

  def __str__(self):
    s = indent() + '<PSection>\n'
    s += indent() + '  block.name = ' + repr(self.block.name) + '\n'
    s += indent() + '  name = ' + repr(self.name) + '\n'
    s += indent() + '  line = ' + repr(self.line) + '\n'
    Options.output_indent += 1
    s += code_str(self.code)
    s += str(self.annotation)
    Options.output_indent -= 1
    s += indent() + '  waitfor = [ '
    for index in range(0, len(self.waitfor)):
      if index > 0: s += ', '
      s += repr(self.waitfor[index].name)
    if len(self.waitfor) > 0: s += ' '
    s += ']\n'
    s += indent() + '</PSection>\n'
    return s

  def get_context(self, parent):
    return Context({ 'section': self }, parent)

class PLoop:

  def __init__(self, block, type, args, line, body, annotation):
    self.block = block
    self.name = None
    self.type = type
    self.args = args
    self.line = line
    self.body = body
    self.code = None
    self.annotation = Annotation(block.filename, line, annotation)
    self.name = self.annotation['name']
    if self.name is None:
      self.name = block.new_loop_id()
    self.waitfor = [ ]
    self.__parse()
    self.annotation.read_external(block.name, self.name)

  def __parse(self):
    type = self.type
    block = self.block
    assert type in ('LOOP_FI', 'LOOP_F', 'LOOP', 'FOR', 'DO', 'WHILE')

    # For loop types 'FOR', 'DO', and 'WHILE' the loop name /must/ be
    # specified through an annotation 'name'.
    if type in ('FOR', 'DO', 'WHILE'):
      name = self.annotation['name']
      if name is None:
	name = 'LINE' + str(self.line)
	warning(loc(block.filename, self.line)
	    + 'No loop name specified for CM_' + type + ' loop, '
	    + 'using name ' + repr(name),
	    level = 2
	    )

    if type == 'LOOP':
      try:
	name, init, cond, step = self.args
      except ValueError:
        error(loc(block.filename, self.line)
	    + 'Loop specification syntax error')
	sys.exit(1)
      self.init = init
      self.cond = cond
      self.step = step
    elif type == 'LOOP_F':
      try:
        name, count, init, step = self.args
      except ValueError:
        error(loc(block.filename, self.line)
	    + 'Loop specification syntax error')
	sys.exit(1)
      self.count = count
      self.init = init
      self.step = step
    elif type == 'LOOP_FI':
      try:
        name, iterator, start, end, step = self.args
      except ValueError:
        error(loc(block.filename, self.line)
	    + 'Loop specification syntax error')
	sys.exit(1)
      if not RX_ID.match(iterator):
	error(loc(block.filename, self.line)
	    + 'Invalid loop iterator ' + repr(iterator))
	sys.exit(1)
      self.iterator = iterator
      self.iterator_type = self.annotation['iterator_type']
      if self.iterator_type is None:
	self.iterator_type = 'unsigned int'
      self.start = start
      self.end = end
      self.step = step
    elif type == 'FOR':
      try:
        init, cond, step = self.args
      except ValueError:
        error(loc(block.filename, self.line)
	    + 'Loop specification syntax error')
	sys.exit(1)
      self.init = init
      self.cond = cond
      self.step = step
    elif type == 'DO':
      cond = string.join(self.args, ', ')
      self.cond = cond
    elif type == 'WHILE':
      cond = string.join(self.args, ', ')
      self.cond = cond
    else:
      assert False
    self.name = name

    # Every loop waits for the previous loop.  Since we're processing the
    # loops in reverse order, we'll setup the dependencies in reverse order.
    if len(block.loops) > 0:
      block.loops[-1].waitfor.append(self)

    # TODO: insert #line directives into the code fragment.
    self.code = self.body

  def __repr__(self): return str(self)

  def __str__(self):
    s = indent() + '<PLoop>\n'
    if self.name is not None:
      s += indent() + '  name = ' + repr(self.name) + '\n'
    s += indent() + '  block.name = ' + repr(self.block.name) + '\n'
    s += indent() + '  line = ' + repr(self.line) + '\n'
    s += indent() + '  type = ' + repr(self.type) + '\n'
    type = self.type
    if type in ('LOOP', 'FOR'):
      s += indent() + '  init = ' + repr(self.init) + '\n'
      s += indent() + '  cond = ' + repr(self.cond) + '\n'
      s += indent() + '  step = ' + repr(self.step) + '\n'
    elif type == 'LOOP_F':
      s += indent() + '  count = ' + repr(self.count) + '\n'
      s += indent() + '  init = ' + repr(self.init) + '\n'
      s += indent() + '  step = ' + repr(self.step) + '\n'
    elif type == 'LOOP_FI':
      s += indent() + '  iterator = ' + repr(self.iterator) + '\n'
      s += indent() + '  start = ' + repr(self.start) + '\n'
      s += indent() + '  end = ' + repr(self.end) + '\n'
      s += indent() + '  step = ' + repr(self.step) + '\n'
    elif type in ('DO', 'WHILE'):
      s += indent() + '  cond = ' + repr(self.cond) + '\n'
    Options.output_indent += 1
    s += code_str(self.code)
    s += str(self.annotation)
    Options.output_indent -= 1
    s += indent() + '  waitfor = [ '
    for index in range(0, len(self.waitfor)):
      if index > 0: s += ', '
      s += repr(self.waitfor[index].name)
    if len(self.waitfor) > 0: s += ' '
    s += ']\n'
    s += indent() + '</PLoop>\n'
    return s

  def get_context(self, parent):
    return Context({ 'loop': self }, parent)

class PDecl:

  TYPE_VARIABLE = 'variable'
  TYPE_LOCK = 'lock'
  TYPE_CONDITION = 'condition'

  LOCKTYPE_LOCK = 'lock'
  LOCKTYPE_RWLOCK = 'rwlock'
  LOCKTYPE_FASTLOCK = 'fastlock'
  LOCKTYPE_FASTRWLOCK = 'fastrwlock'

  def __init__(self, block, typespec, spec, line, annotation):
    self.block = block
    self.typespec = typespec
    self.spec = spec
    self.line = line
    self.annotation = Annotation(block.filename, line, annotation)
    self.type = None
    self.name = None
    self.__parse()
    self.annotation.read_external(block.name, self.name)

  def __parse(self):
    var_typespecs = '', '_LOCAL', '_LOCAL_INIT', '_SHARED', '_SHARED_INIT'
    lock_typespecs = '_LOCK', '_RWLOCK', '_FASTLOCK', '_FASTRWLOCK'
    cond_typespecs = '_CONDITION',

    if self.typespec in var_typespecs:
      self.__parse_variable()
    elif self.typespec in lock_typespecs:
      self.__parse_lock()
    elif self.typespec in cond_typespecs:
      self.__parse_cond()
    else:
      assert 0

  def __parse_variable(self):
    block = self.block
    self.type = PDecl.TYPE_VARIABLE

    try:
      if self.typespec in ('_LOCAL_INIT', '_SHARED_INIT'):
	var_type, var_name, var_init = split_expr(self.spec)
      else:
        var_type, var_name = split_expr(self.spec)
	var_init = None
    except ValueError:
      error(loc(block.filename, self.line)
	  + 'Invalid variable specification')
      sys.exit(1)
    if not RX_ID.match(var_name):
      error(loc(block.filename, self.line)
	  + 'Invalid variable name ' + repr(var_name))
      sys.exit(1)
    self.name = var_name
    self.var_type = var_type
    self.var_init = var_init
    self.var_shared = self.typespec in ('', '_SHARED', '_SHARED_INIT')
    self.var_shared_auto = self.typespec in ('_SHARED', '_SHARED_INIT')

  def __parse_lock(self):
    block = self.block
    self.type = PDecl.TYPE_LOCK

    self.lock_type = {
      '_LOCK' : PDecl.LOCKTYPE_LOCK,
      '_RWLOCK' : PDecl.LOCKTYPE_RWLOCK,
      '_FASTLOCK' : PDecl.LOCKTYPE_FASTLOCK,
      '_FASTRWLOCK' : PDecl.LOCKTYPE_FASTRWLOCK
    }[self.typespec]
    try:
      lock_name, = split_expr(self.spec)
    except ValueError:
      error(loc(block.filename, self.line)
	  + 'Invalid lock specification')
      sys.exit(1)
    if not RX_ID.match(lock_name):
      error(loc(block.filename, self.line)
	  + 'Invalid lock name ' + repr(lock_name))
      sys.exit(1)
    self.name = lock_name

  def __parse_cond(self):
    block = self.block
    self.type = PDecl.TYPE_CONDITION

    try:
      cond_name, = split_expr(self.spec)
    except ValueError:
      error(loc(block.filename, self.line)
	  + 'Invalid lock specification')
      sys.exit(1)
    if not RX_ID.match(cond_name):
      error(loc(block.filename, self.line)
	  + 'Invalid condition name ' + repr(cond_name))
      sys.exit(1)
    self.name = cond_name

  def __repr__(self): return str(self)

  def __str__(self):
    s = indent() + '<PDecl>\n'
    s += indent() + '  block.name = ' + repr(self.block.name) + '\n'
    s += indent() + '  name = ' + repr(self.name) + '\n'
    s += indent() + '  line = ' + repr(self.line) + '\n'
    s += indent() + '  typespec = ' + repr(self.typespec) + '\n'
    s += indent() + '  type = ' + repr(self.type) + '\n'
    if self.type == PDecl.TYPE_VARIABLE:
      s += indent() + '  var_type = ' + repr(self.var_type) + '\n'
      s += indent() + '  var_init = ' + repr(self.var_init) + '\n'
    elif self.type == PDecl.TYPE_LOCK:
      s += indent() + '  lock_type = ' + repr(self.lock_type) + '\n'
    elif self.type == PDecl.TYPE_CONDITION:
      pass
    Options.output_indent += 1
    s += str(self.annotation)
    Options.output_indent -= 1
    s += indent() + '</PDecl>\n'
    return s

  def get_context(self, parent):
    return Context({ 'decl': self }, parent)

  def get_lock_type(self):
    """Get the C/C++ type for a lock or condition.

    The method reads the type string from the skeleton.

    :Return:
      The C/C++ type.
    """

    assert self.type in (PDecl.TYPE_LOCK, PDecl.TYPE_CONDITION)
    if self.type == PDecl.TYPE_LOCK:
      key = {
	PDecl.LOCKTYPE_LOCK : 'lock_type',
	PDecl.LOCKTYPE_RWLOCK: 'rwlock_type',
	PDecl.LOCKTYPE_FASTLOCK: 'fastlock_type',
	PDecl.LOCKTYPE_FASTRWLOCK: 'fastrwlock_type'
      }[self.lock_type]
    else:
      key = 'condition_type'
    type = Options.skel.expand(key).strip()
    return type

  def get_prefix(self):
    """Get the member prefix string for the class fields assoicated with the
    declaration.

    :Return:
      The prefix.
    """

    if self.type == PDecl.TYPE_VARIABLE:
      if self.var_shared_auto:
	key = 'shared_auto_prefix'
      elif self.var_shared:
        key = 'shared_prefix'
      else:
	key = None
    elif self.type == PDecl.TYPE_LOCK:
      key = 'lock_prefix'
    elif self.type == PDecl.TYPE_CONDITION:
      key = 'condition_prefix'

    if key is None: return ''

    return Options.skel.expand(key).strip()

class PLocal:

  def __init__(self, block, name, body, line, annotation):
    self.block = block
    self.body = body
    self.line = line
    self.annotation = Annotation(block.filename, line, annotation)
    self.name = name
    self.waitfor = [ ]
    self.waitfor_names = [ ]
    self.__parse()
    self.annotation.read_external(block.name, self.name)

  __rx_WAITFOR = re.compile(r'\bCM_WAITFOR\(\s*([^\)]*)\s*\)\s*;?')

  def __parse(self):
    buffer = self.body
    block = self.block

    # evaluate and strip the CM_WAITFOR statement(s).
    waitfor_list = [ ]
    for match in self.__rx_WAITFOR.finditer(buffer):
      spec = match.group(1)
      for name in RX_split.split(spec):
	#if name not in block.element_map:
	#  line = self.line + buffer.count('\n', 0, match.start())
	#  error(loc(block.filename, line)
	#      + 'undefined section ' + repr(name) + ' in CM_WAITFOR')
	#  sys.exit(1)
	#psection = block.element_map[name]
	#self.waitfor.append(psection)
	line = self.line + buffer.count('\n', 0, match.start())
	self.waitfor_names.append((name, line))
      waitfor_list.append((match.start(), match.end()))
    waitfor_list.reverse()
    for start, end in waitfor_list:
      buffer = strip(buffer, start, end)

    # todo: insert #line directives into the code fragment.
    self.code = buffer

  def setup_deps(self):
    block = self.block
    for name, line in self.waitfor_names:
      if name not in block.element_map:
	line = self.line + buffer.count('\n', 0, match.start())
	error(loc(block.filename, line)
	    + 'undefined element ' + repr(name) + ' in CM_WAITFOR')
	sys.exit(1)
      pelement = block.element_map[name]
      self.waitfor.append(pelement)

  def __repr__(self): return str(self)

  def __str__(self):
    s = indent() + '<PLocal>\n'
    s += indent() + '  name = ' + repr(self.name) + '\n'
    s += indent() + '  block.name = ' + repr(self.block.name) + '\n'
    s += indent() + '  line = ' + repr(self.line) + '\n'
    Options.output_indent += 1
    s += code_str(self.code)
    Options.output_indent -= 1
    s += indent() + '</PLocal>\n'
    return s

class PBarrier:

  def __init__(self, block, spec, line, annotation):
    self.block = block
    self.spec = spec
    self.line = line
    self.annotation = Annotation(block,filename, line, annotation)
    self.name = self.annotation['name']
    self.waitfor = [ ]
    self.__parse()
    if self.name is not None:
      self.annotation.read_external(block.name, self.name)

  def __parse(self):
    # Implementation note: At the time the barriers are parsed all sections
    # are already known.  We must explicitely check the line numbers of the
    # listed sections to find out if a section is relevant for a barrier.
    block = self.block
    spec = self.spec

    # Every barrier implicitely syncs the previous barrier (if it exists).
    if len(block.barriers) > 0:
      self.waitfor.append(block.barriers[-1])

    # Every barrier also syncs on the previous _and_ following loop (if it
    # exists).
    prev_loop = None
    next_loop = None
    for loop in block.loops:
      if loop.line < self.line:
	prev_loop = loop
      else:
	next_loop = loop
	break
    if prev_loop is not None:
      self.waitfor.append(prev_loop)
    if next_loop is not None:
      next_loop.waitfor.append(self)
      if prev_loop in next_loop.waitfor:
	next_loop.waitfor.remove(prev_loop)

    if spec is None:
      # The barrier waits for all sections and loops above the barrier that
      # are not already synced through a previous barrier.
      for section in block.sections:
	if section.line > self.line: continue
	skip = False
	for barrier in block.barriers:
	  if section in barrier.waitfor:
	    skip = True
	    break
	if skip: continue
	self.waitfor.append(section)
    else:
      for name in RX_split.split(spec):
	if not RX_ID.match(name):
	  error(loc(block.filename, self.line)
	      + 'Invalid element name in CM_BARRIER')
	  sys.exit(1)
	if name not in block.element_map:
	  error(loc(block.filename, self.line)
	      + 'Unknown element name ' + repr(name) + ' in CM_BARRIER')
	  sys.exit(1)
	section = block.element_map[name]
	if section.line > self.line:
	  error(loc(block.filename, self.line)
	      + 'Section ' + repr(name) + ' not before CM_BARRIER statement')
	  sys.exit(1)
	skip = False
	for barrier in block.barriers:
	  if section in barrier.waitfor:
	    skip = True
	    warning(loc(block.filename, self.line)
		+ 'Section ' + repr(name) + ' already synced in CM_BARRIER',
		level = 1)
	    break
	if skip: continue
	self.waitfor.append(section)

    # All sections following the barrier will wait for the barrier.  All
    # previous barriers are removed from the waitfor lists of the following
    # sections.
    for section in block.sections:
      if section.line < self.line: continue
      for barrier in self.barriers:
	if barrier in section.waitfor:
	  section.waitfor.remove(barrier)
      section.waitfor.append(self)

  def __repr__(self): return str(self)

  def __str__(self):
    s = indent() + '<PBarrier>\n'
    Options.output_indent += 1
    s += indent() + 'block.name = ' + repr(self.block.name) + '\n'
    s += indent() + 'line = ' + repr(self.line) + '\n'
    s += indent() + 'spec = ' + repr(self.spec) + '\n'
    s += str(self.annotation)
    s += indent() + 'waitfor = [ '
    for index in range(0, len(self.waitfor)):
      if index > 0: s += ', '
      s += repr(self.waitfor[index].name)
    if len(self.waitfor) > 0: s += ' '
    s += ']\n'
    Options.output_indent -= 1
    s += indent() + '</PBarrier>\n'
    return s

class PBlock:

  block_map = { }

  def __init__(
      self,
      name,
      filename,
      line,
      body,
      annotation = None,
      include_blocks = [ ]):
    assert name not in PBlock.block_map
    self.name = name
    self.filename = filename
    self.line = line
    self.body = body
    self.annotation = Annotation(filename, line, annotation)
    self.include_blocks = include_blocks
    self.element_map = { }
    self.sections = [ ]
    self.loops = [ ]
    self.decls = [ ]
    self.locals = [ ]
    self.barriers = [ ]
    self.loop_id_counter = 0
    self.__parse()
    PBlock.block_map[name] = self
    self.annotation.read_external(name, None)
    self.__process()

  def new_loop_id(self):
    """Create a new loop ID string.

    :Return:
      A (per block) unique loop ID string.
    """

    self.loop_id_counter += 1
    return 'loop' + str(self.loop_id_counter)

  __rx_BEGIN = re.compile(
      r'\bCM_BEGIN\(\s*([a-zA-Z0-9_]+)\s*\);')
  __rx_SECTION = re.compile(
      r'\bCM_SECTION\(\s*([a-zA-Z0-9_]+)\s*\);')
  __rx_ENDSECTION = re.compile(
      r'\bCM_ENDSECTION\b\s*;?')
  __rx_LOOP_X = re.compile(
      r'\bCM_(LOOP_FI|LOOP_F|LOOP|FOR|DO|WHILE)\b')
  __rx_ENDLOOP = re.compile(
      r'\bCM_ENDLOOP\b\s*;?')
  __rx_DOWHILE = re.compile(
      r'\bCM_DOWHILE\b')
  __rx_DECL_X = re.compile(
      r'\bCM_DECL(|_[A-Z_]+)\(\s*([^;]+)\s*\);')
  __rx_LOCAL = re.compile(
      r'\bCM_LOCAL\(\s*([a-zA-Z0-9_]+)\s*\);')
  __rx_ENDLOCAL = re.compile(
      r'\bCM_ENDLOCAL\b\s*;?')
  __rx_BARRIER_X = re.compile(
      r'\bCM_BARRIER((_SELECT\([^\)]*\))?);')
  __rx_INCLUDE = re.compile(
      r'\bCM_INCLUDE\b')
  __rx_ENDINCLUDE = re.compile(
      r'\bCM_ENDINCLUDE\b')

  __decl_types = ('', '_LOCAL', '_LOCAL_INIT', '_SHARED', '_SHARED_INIT',
      '_LOCK', '_RWLOCK', '_CONDITION', '_FASTLOCK', '_FASTRWLOCK')

  def __parse(self):
    """Find all sections and loops of the parallel block.

    The function will first strip all nested CM_BEGIN/CM_END blocks, so that
    nested blocks are handled correctly.
    """

    buffer = self.body

    # Strip nested CM_BEGIN/CM_END blocks.  Annotation comments of nested
    # blocks are also removed to make sure they are not attributed to an
    # unrelated statement.
    nested_blocks = [ ]
    block_start = 0
    block_end = 0
    while True:
      match = PBlock.__rx_BEGIN.search(buffer, block_end)
      if match is None: break
      block_start = match.start()
      annot_start = self.find_annotation(buffer, block_start)
      if annot_start != -1: block_start = annot_start
      name = match.group(1)
      rx_CM_END = re.compile(r'\bCM_END\(\s*' + name + '\s*\)')
      match_end = rx_CM_END.search(buffer, match.end())
      if match_end is None:
	line = self.line + buffer.count('\n', 0, block_start)
	error(loc(self.filename, line)
	    + 'Missing CM_END for CM_BEGIN(' + name + ')')
	sys.exit(1)
      block_end = match_end.end()
      nested_blocks.append((name, block_start, block_end))
    nested_blocks.reverse()
    for name, block_start, block_end in nested_blocks:
      buffer = strip(buffer, block_start, block_end)
    self.body_stripped = buffer

    # Extract all named parallel sections.
    section_list = [ ]
    section_start = 0
    section_end = 0
    while True:
      match = PBlock.__rx_SECTION.search(buffer, section_end)
      if match is None: break
      section_start = match.start()
      name = match.group(1)
      annot_start = self.find_annotation(buffer, section_start)
      if annot_start == -1:
	annotation = None
      else:
	annotation = buffer[annot_start:section_start]
      body_start = match.end()
      match_end = PBlock.__rx_ENDSECTION.search(buffer, body_start)
      if match_end is None:
	line = self.line + buffer.count('\n', 0, section_start)
	error(loc(self.filename, line) + 'CM_SECTION without CM_ENDSECTION')
	sys.exit(1)
      body_end = match_end.start()
      section_end = match_end.end()
      section_list.append(
	  ( name,
	    buffer[body_start:body_end],
	    section_start,
	    section_end,
	    annotation
	  ))

    # Create all section instances and strip section declarations.
    section_list.reverse()
    for name, body, section_start, section_end, annotation in section_list:
      buffer = strip(buffer, section_start, section_end)
      line = self.line + buffer.count('\n', 0, section_start)
      if name in self.element_map:
	error(loc(self.filename, line) + 'Redefinition of element '
	    + repr(name) + ' (in block ' + repr(self.name) + ')')
	pelement = self.element_map[name]
	error(loc(self.filename, pelement.line) + 'Previous definition')
	sys.exit(1)
      psection = PSection(self, name, line, body, annotation)
      self.element_map[name] = psection
      self.sections.append(psection)
    self.sections.reverse()

    # Extract all loops and strip loop declarations.
    loop_list = [ ]
    loop_start = 0
    loop_end = 0
    while True:
      match = PBlock.__rx_LOOP_X.search(buffer, loop_end)
      if match is None: break
      loop_start = match.start()
      type = match.group(1) # LOOP_FI, LOOP_F, LOOP, FOR, DO, WHILE.
      annot_start = self.find_annotation(buffer, loop_start)
      if annot_start == -1:
	annotation = None
      else:
	annotation = buffer[annot_start:loop_start]
      sep = ','
      if type == 'FOR': sep = ';'
      if type != 'DO':
	body_start, delim, args = parse_args(buffer, match.end(), sep = sep)
      else:
	body_start, delim, args = match.end(), ')', None
      if delim != ')':
	line = self.line + buffer.count('\n', 0, loop_start)
	error(loc(self.filename, line)
	    + 'Parse error in CM_' + type + ' arguments')
	sys.exit(1)
      if type in ('LOOP', 'LOOP_F', 'LOOP_FI'):
	match_end = PBlock.__rx_ENDLOOP.search(buffer, body_start)
	if match_end is None:
	  line = self.line + buffer.count('\n', 0, loop_start)
	  error(loc(self.filename, line)
	      + 'CM_' + type + ' without CM_ENDLOOP')
	  sys.exit(1)
	body_end = match_end.start()
	loop_end = match_end.end()
	body = buffer[body_start:body_end]
      else:
	assert type in ('FOR', 'DO', 'WHILE')
	body_end, delim, body = parse_args(buffer, body_start, sep = None)
	if delim != '}':
	  line = self.line + buffer.count('\n', 0, loop_start)
	  error(loc(self.filename, line) + 'Syntax error in CM_' + type)
	  sys.exit(1)
	if type != 'DO':
	  loop_end = body_end
	else:
	  match_end = self.__rx_DOWHILE.search(buffer, body_end)
	  if (match_end is None
	      or not buffer[body_end:match_end.start()].isspace()):
	    line = self.line + buffer.count('\n', 0, loop_start)
	    error(loc(self.filename, line) + 'CM_DO without CM_DOWHILE')
	    sys.exit(1)
	  loop_end, delim, args = parse_args(buffer, body_end)
	  if delim != ')':
	    line = self.line + buffer.count('\n', 0, loop_start)
	    error(loc(self.filename, line) + 'Syntax error in CM_' + type)
	    sys.exit(1)

      loop_list.append(
          ( type,
	    args,
	    body,
	    loop_start,
	    loop_end,
	    annotation
	  ))

    # Create all loop instances.
    loop_list.reverse()
    for type, args, body, loop_start, loop_end, annotation in loop_list:
      buffer = strip(buffer, loop_start, loop_end)
      line = self.line + buffer.count('\n', 0, loop_start)
      ploop = PLoop(self, type, args, line, body, annotation)
      self.loops.append(ploop)
      name = ploop.name
      if name is not None:
	if name in self.element_map:
	  error(loc(self.filename, line) + 'Redefinition of element '
	      + repr(name) + ' (in block ' + repr(self.name) + ')')
	  pelement = self.element_map[name]
	  error(loc(self.filename, pelement.line) + 'Previous definition')
	  sys.exit(1)
	self.element_map[name] = ploop
    self.loops.reverse()

    # Extract all declarations.
    decl_list = [ ]
    for match in PBlock.__rx_DECL_X.finditer(buffer):
      type = match.group(1)
      spec = match.group(2)
      decl_start = match.start()
      decl_end = match.end()
      annot_start = self.find_annotation(buffer, decl_start)
      if annot_start == -1:
	annotation = None
      else:
	annotation = buffer[annot_start:decl_start]
      if type not in PBlock.__decl_types:
	line = self.line + buffer.count('\n', 0, decl_start)
	warning(loc(self.filename, line)
	    + 'Unrecognized declaration CM_DECL' + type + ' (ignored)')
	continue
      decl_list.append((type, spec, decl_start, annotation))

    # Create all declaration instances.
    for type, spec, decl_start, annotation in decl_list:
      line = self.line + buffer.count('\n', 0, decl_start)
      self.decls.append(PDecl(self, type, spec, line, annotation))

    # Extract local blocks.
    local_list = [ ]
    local_start = 0
    local_end = 0
    while True:
      match = PBlock.__rx_LOCAL.search(buffer, local_end)
      if match is None: break
      name = match.group(1)
      local_start = match.start()
      annot_start = self.find_annotation(buffer, local_start)
      if annot_start == -1:
	annotation = None
      else:
	annotation = buffer[annot_start:local_start]
      body_start = match.end()
      match_end = PBlock.__rx_ENDLOCAL.search(buffer, body_start)
      if match_end is None:
	line = self.line + buffer.count('\n', 0, local_start)
	error(loc(self.filename, line) + 'CM_LOCAL without CM_ENDLOCAL')
	sys.exit(1)
      body_end = match_end.start()
      local_end = match_end.end()
      local_list.append(
	  ( name,
	    buffer[body_start:body_end],
	    local_start,
	    local_end,
	    annotation
	  ))

    # Create all local instances and strip declarations.
    local_list.reverse()
    for name, body, local_start, local_end, annotation in local_list:
      buffer = strip(buffer, local_start, local_end)
      line = self.line + buffer.count('\n', 0, local_start)
      plocal = PLocal(self, name, body, line, annotation)
      self.locals.append(plocal)
      if name in self.element_map:
	error(loc(self.filename, line) + 'Redefinition of element '
	    + repr(name) + ' (in block ' + repr(self.name) + ')')
	pelement = self.element_map[name]
	error(loc(self.filename, pelement.line) + 'Previous definition')
	sys.exit(1)
      self.element_map[name] = plocal
    self.locals.reverse()

    # Extract all barriers.
    barrier_list = [ ]
    for match in PBlock.__rx_BARRIER_X.finditer(buffer):
      type = match.group(1)
      if len(type) > 0:
	match_select = re.match(r'_SELECT\(\s*([^\)]*)\s*\)')
	assert match_select is not None
	spec = match_select.group(1)
      else:
	spec = None
      barrier_start = match.start()
      barrier_end = match.end()
      annot_start = self.find_annotation(buffer, barrier_start)
      if annot_start == -1:
	annotation = None
      else:
	annotation = buffer[annot_start:barrier_start]
      barrier_list.append((spec, barrier_start, barrier_end, annotation))

    # Create all barrier instances.
    for spec, barrier_start, barrier_end, annotation in barrier_list:
      line = self.line + buffer.count('\n', 0, barrier_start)
      self.barriers.append(PBarrier(self, spec, line, annotation))

    # Setup section and local block dependencies.
    for psection in self.sections:
      psection.setup_deps()
    for plocal in self.locals:
      plocal.setup_deps()

  def __process(self):
    """Process the parallel block.

    This method generates a topologically sorted list of job objects for the
    parallel block.  The job list is stored to the 'jobs' property of the
    block object.

    The job and dependency analysis is located in the 'crymp.process' module.
    """

    # Platform independant.
    process.process(self)

  # staticmethod
  def find_annotation(buffer, start):
    """Find an annotation comment block in the specified buffer.

    :Parameters:
      - `buffer`: The buffer holding the C++ code.
      - `start`: The start offset of the block to be annotated (e.g. the
	offset of a CM_BEGIN macro).

    :Return:
      If an annotation block is found, then the offset of the first character
      of the annotation block is returned.  The method returns -1 if no
      annotation block is found.
    """

    while True:
      if start == 0: break
      line_start = buffer.rfind('\n', 0, start)
      if line_start == -1: line_start = 0
      line = buffer[line_start:start]
      if RX_begin_annotation.match(line):
	return line_start
      if RX_empty.match(line) or RX_leading_CPP_comment.match(line):
	start = line_start
	continue
      return -1

  find_annotation = staticmethod(find_annotation)

  # staticmethod
  def process(buffer, filename):
    """Process an input file.

    :Parameters:
      - `buffer`: The buffer holding the C++ program.
      - `filename`: The name of the input C++ file.
    """

    # Check if the file contains explicit CM_INCLUDE/CM_ENDINCLUDE blocks.
    explicit_include = False
    if PBlock.__rx_INCLUDE.search(buffer) is not None:
      explicit_include = True

    include_blocks = [ ]

    # Extract all parallel blocks.  We'll iterate over all CM_BEGIN macros and
    # independently scan for the assoicated CM_END macros.  Nested
    # CM_BEGIN/CM_END blocks will be handled correctly.
    block_list = [ ]
    block_start = 0
    block_end = 0
    while True:
      match = PBlock.__rx_BEGIN.search(buffer, block_end)
      if match is None: break
      block_start = match.start()
      include_blocks.extend(PBlock.scan_includes(
	  buffer,
	  filename,
	  block_end,
	  block_start,
	  explicit_include))
      annot_start = PBlock.find_annotation(buffer, block_start)
      if annot_start != -1:
	annotation = buffer[annot_start:block_start]
      else:
	annotation = None
      body_start = match.end()
      name = match.group(1)
      rx_CM_END = re.compile(r'\bCM_END\(\s*' + name + '\s*\)')
      match_end = rx_CM_END.search(buffer, body_start)
      if match_end is None:
	line = buffer.count('\n', 0, start) + 1
	error(loc(filename, line)
	    + 'Missing CM_END for CM_BEGIN(' + name + ')')
	sys.exit(1)
      body_end = match_end.start()
      block_end = match_end.end()
      block_list.append(
	  ( name,
	    buffer[body_start:body_end],
	    block_start,
	    annotation,
	    include_blocks[:]
	  ))
    if len(block_list) == 0: return

    # Create PBlock instances for all blocks.
    for name, body, start, annotation, include_blocks in block_list:
      line = buffer.count('\n', 0, start) + 1
      if name in PBlock.block_map:
	error(loc(filename, line) + 'Redefinition of block ' + repr(name))
	pblock = PBlock.block_map[name]
	error(loc(pblock.filename, pblock.line) + 'Previous definition')
	sys.exit(1)
      pblock = PBlock(name, filename, line, body, annotation, include_blocks)

  process = staticmethod(process)

  # staticmethod
  def scan_includes(buffer, filename, start, end, explicit_include):
    """Scan the specified buffer for #include statements.

    :Parameters:
      - `buffer`: The buffer to be scanned.
      - `filename`: The filename.
      - `start`: The start offset.
      - `end`: The end offset.
      - `explicit_include`: Flag indicating if #include statements are marked
	with CM_INCLUDE/CM_ENDINCLUDE.

    :Return:
      A list of IncludeBlock instances.
    """

    if explicit_include:
      include_blocks = [ ]
      for match in PBlock.__rx_INCLUDE.finditer(buffer, start, end):
	block_start = match.start()
	line = buffer.count('\n', 0, block_start) + 1
	match_end = PBlock.__rx_ENDINCLUDE.search(buffer, match.end(), end)
	if match_end is None:
	  error(loc(filename, line) + 'Unbalanced CM_INCLUDE')
	  sys.exit(1)
	block_end = match_end.end()
	include_block = IncludeBlock(
	    filename,
	    line,
	    buffer[block_start:block_end])
	include_blocks.append(include_block)
    else:
      line = buffer.count('\n', 0, start) + 1
      include_block = IncludeBlock(
	  filename,
	  line,
	  buffer[start:end])
      include_blocks = [ include_block ]
    return include_blocks

  scan_includes = staticmethod(scan_includes)

  def __repr__(self): return str(self)

  # staticmethod
  def __str_elements(elements, tag):
    if len(elements) > 0:
      s = indent() + '<' + tag + '>\n'
      Options.output_indent += 1
      for element in elements:
	s += str(element)
      Options.output_indent -= 1
      s += indent() + '</' + tag + '>\n'
    else:
      s = indent() + '<' + tag + '/>\n'
    return s

  __str_elements = staticmethod(__str_elements)

  def __str__(self):
    s = indent() + '<PBlock>\n'
    Options.output_indent += 1
    s += indent() + 'name = ' + repr(self.name) + '\n'
    s += indent() + 'filename = ' + repr(self.filename) + '\n'
    s += indent() + 'line = ' + repr(self.line) + '\n'
    s += code_str(self.body, tag = 'Body')
    if self.annotation is None:
      s += indent() + 'annotation = None\n'
    else:
      s += str(self.annotation)
    s += self.__str_elements(self.sections, 'Sections')
    s += self.__str_elements(self.loops, 'Loops')
    s += self.__str_elements(self.decls, 'Decls')
    s += self.__str_elements(self.locals, 'Locals')
    s += self.__str_elements(self.barriers, 'Barriers')
    s += self.__str_elements(self.include_blocks, 'IncludeBlocks')
    Options.output_indent -= 1
    s += indent() + '</PBlock>\n'
    return s

  def get_context(self, parent):
    """Get the evaluation context for the parallel block.

    :Parameters:
      - `parent`: The parent context.

    :Return:
      The context.
    """

    dict = {
      'block': self,
      'Block': PBlock,
      'Section': PSection,
      'Loop': PLoop,
      'Decl': PDecl
    }
    return Context(dict, parent)

  def get_shared_params(self, leading_comma = False, skip_auto = False):
    """Generate a C++ parameter list for the shared variable declarations,
    locks, and conditions.

    The method returns declarations for all shared variables as a comma
    separated parameter list.

    This method is typically called from the skeleton file.

    :Parameters:
      - `leading_comma`: Flag indicating if the generated list should start
	with a leading comma.  No comma is emitted if the generated list is
	empty.  The default is False.
      - `skip_auto`: Flag indicating if automatic shared variables, locks, and
	conditions should be skipped.  The default is False.

    :Return:
      The C++ parameter list represented as a string.
    """

    output = ''
    index = 0
    for decl in self.decls:
      if decl.type == PDecl.TYPE_VARIABLE:
	if not decl.var_shared: continue
	if skip_auto and decl.var_shared_auto: continue
      else:
	if skip_auto and decl.type != PDecl.TYPE_VARIABLE:
	  continue
      if index > 0 or leading_comma: output += ', '
      index += 1
      if decl.type == PDecl.TYPE_VARIABLE:
	output += decl.var_type + ' '
      else:
	output += decl.get_lock_type() + ' '
      if decl.type != PDecl.TYPE_VARIABLE or decl.var_shared:
	output += '&'
      prefix = ''
      if decl.type == PDecl.TYPE_LOCK:
	prefix = Options.skel.expand('lock_prefix').strip()
      elif decl.type == PDecl.TYPE_CONDITION:
	prefix = Options.skel.expand('condition_prefix').strip()
      output += prefix + decl.name
    return output

  def get_shared_args(
      self,
      leading_comma = False,
      add_prefix = False,
      skip_auto = False
      ):
    """Generate a C/C++ argument list for the shared variable declarations,
    locks, and conditions.

    The method returns the names of all shared variable, separated by commas.

    This method is typically called from the skeleton file.

    :Parameters:
      - `leading_comma`: Flag indicating if the generated list should start
	with a leading comma.  No comma is emitted if the generated list is
	empty.  The default is False.
      - `add_prefix`: Flag indicating if a prefix should be added to the
	variable names.  If this flag is specified, then a '_ref_' prefix is
	added to shared variables (skeleton key 'shared_prefix') and a
	'_shared_' prefix is added to automatic shared variables (skeleton key
	'shared_auto_prefix').  Note that a prefix is always applied to locks
	and conditions.
      - `skip_auto`: Flag indicating if automatic shared variables should
	be skipped.  The default is False.

    :Return:
      The C/C++ argument list represented as a string.
    """

    output = ''
    index = 0
    for decl in self.decls:
      if decl.type == PDecl.TYPE_VARIABLE:
	if not decl.var_shared: continue
	if skip_auto and decl.var_shared_auto: continue
      else:
	if skip_auto: continue
      if index > 0 or leading_comma: output += ', '
      index += 1
      prefix = ''
      if add_prefix or decl.type != PDecl.TYPE_VARIABLE:
	prefix = decl.get_prefix()
      output += prefix + decl.name
    return output

  def get_local_consinit(
      self,
      leading_comma = False,
      leading_colon = True,
      init_param = None):
    """Generate the C++ member construcor calls for the local variable
    declarations, locks, and conditions.

    The method creates a construcor call for every variable - shared or local.
    Shared variables are initialized through a parameter of the same name as
    the variable.  Local variable are initialized through the assoicated
    initializer (if specified).

    This method is typically called from the skeleton file.

    :Parameters:
      - `leading_comma`: Flag indicating if the generated list should start
	with a leading comma.  No comma is emitted if the generated list is
	empty.  The default is False.
      - `leading_colon`: Flag indicating if a leading colon should be
	generated for the generated construcor call list.  No colon is empty
	if the generated list is empty.  The default is True.
      - `init_param`: The name of the initialization parameter, or None.  If
	not None, then this is the name of the parameter for passing an
	instance of the parallel block class.

    :Return:
      The C++ construcor call list represented as a string.
    """

    output = ''
    index = 0
    for decl in self.decls:
      if decl.type == PDecl.TYPE_VARIABLE:
	if not decl.var_shared and decl.var_init is None: continue
      if index == 0 and leading_colon: output += ': '
      if index > 0 or leading_comma: output += ', '
      index += 1
      if init_param is None:
	if decl.type == PDecl.TYPE_VARIABLE:
	  if decl.var_shared:
	    output += decl.name + '(' + decl.name + ')'
	  else:
	    output += decl.name + decl.var_init
	else:
	  prefix = decl.get_prefix()
	  output += prefix + decl.name + '(' + prefix + decl.name + ')'
      else:
	if decl.type == PDecl.TYPE_VARIABLE:
	  if decl.var_shared:
	    field_name = decl.get_prefix() + decl.name
	    output += decl.name + '(' + init_param + '.' + field_name + ')'
	  else:
	    output += decl.name + decl.var_init
	else:
	  field_name = decl.get_prefix() + decl.name
	  output += field_name + '(' + init_param + '.' + field_name + ')'

    return output

  def get_shared_consinit(
      self,
      leading_comma = False,
      leading_colon = True,
      add_prefix = False
      ):
    """Generate the C++ member construcor calls for the shared variable
    declarations.

    The method creates a construcor call for every shared variable.  Shared
    variables are initialized through a parameter of the same name as
    the variable.  Automatic shared variables (i.e. shared variables that are
    defined through the declaration) are initialized through the assoicated
    initializer (if specified).

    This method is typically called from the skeleton file.

    :Parameters:
      - `leading_comma`: Flag indicating if the generated list should start
	with a leading comma.  No comma is emitted if the generated list is
	empty.  The default is False.  If this is True, then the leading_colon
	parameter is ignored.
      - `leading_colon`: Flag indicating if a leading colon should be
	generated for the generated construcor call list.  No colon is empty
	if the generated list is empty.  The default is True.
      - `add_prefix`: Flag indicating if a prefix should be added to the
	variable names.  If this flag is specified, then a '_ref_' prefix is
	added to shared variables (skeleton key 'shared_prefix') and a
	'_shared_' prefix is added to automatic shared variables (skeleton key
	'shared_auto_prefix').  Note that a prefix is always applied to locks
	and conditions.

    :Return:
      The C++ construcor call list represented as a string.
    """

    output = ''
    index = 0
    if leading_comma: leading_colon = False
    for decl in self.decls:
      if decl.type != PDecl.TYPE_VARIABLE: continue
      if not decl.var_shared: continue
      if decl.var_shared_auto and not decl.var_init: continue
      if index == 0 and leading_colon: output += ': '
      if index > 0 or leading_comma: output += ', '
      index += 1
      prefix = ''
      param_prefix = ''
      if add_prefix or decl.type != PDecl.TYPE_VARIABLE:
	prefix = decl.get_prefix()
	if decl.type != PDecl.TYPE_VARIABLE: param_prefix = prefix
      if decl.type != PDecl.TYPE_VARIABLE or not decl.var_shared_auto:
	output += prefix + decl.name + '(' + param_prefix + decl.name + ')'
      else:
	output += prefix + decl.name + decl.var_init
    return output

  def get_final_deps(self, prefix = None):
    """Get the final dependencies of the parallel block.

    This is the list of job IDs that are not already synced withing the job
    mesh.  The final dependencies are returned as a comma separated list of
    job IDs.

    Note: The method evaluates the 'jobs' field initialized by the 'process'
    module.

    :Parameters:
      - `prefix`: Prefix to prepend to each job ID.

    :Return:
      The list of final dependencies, represented as a comma separated string.
      Returns None if there are no final dependencies.
    """

    s = None
    if prefix is None: prefix = ''
    for job in self.jobs:
      if len(job.depending) == 0:
	if s is None:
	  s = ''
	else:
	  s += ', '
	s += prefix + str(job.id)
    return s

# vim: ts=8 sw=2

