QSIG fully implemented
[obnox/wireshark/wip.git] / tools / asn2wrs.py
index 17ca881374b081ec99da31bb36d23da15478a39e..e4757202d37d32e5e30309f6a16e2d09968521b6 100755 (executable)
@@ -49,7 +49,6 @@ import time
 import getopt
 import traceback
 
-import __main__ # XXX blech!
 import lex
 import yacc
 
@@ -144,9 +143,10 @@ oid_names = {
 }
 
 def asn2c(id):
-  return id.replace('-', '_').replace('.', '_')
+  return id.replace('-', '_').replace('.', '_').replace('&', '_')
 
 input_file = None
+g_conform = None
 lexer = None
 in_oid = False
 
@@ -200,13 +200,13 @@ static_tokens = {
   r'\[' : 'LBRACK',
   r'\]' : 'RBRACK',
   r'-'  : 'MINUS',
-#  r':'  : 'COLON',
+  r':'  : 'COLON',
   #r'='  : 'EQ',
   #r'"'  : 'QUOTATION',
   #r"'"  : 'APOSTROPHE',
   r';'  : 'SEMICOLON',
   r'@'  : 'AT',
-  #r'\!' : 'EXCLAMATION',
+  r'\!' : 'EXCLAMATION',
   r'\^' : 'CIRCUMFLEX',
   r'\&' : 'AMPERSAND',
   r'\|' : 'BAR'
@@ -234,7 +234,7 @@ reserved_words = {
   'CONTAINING'  : 'CONTAINING',
   'DEFAULT'     : 'DEFAULT',
   'DEFINITIONS' : 'DEFINITIONS',
-#  'EMBEDDED'    : 'EMBEDDED',
+  'EMBEDDED'    : 'EMBEDDED',
 #  'ENCODED'     : 'ENCODED',
   'END'         : 'END',
   'ENUMERATED'  : 'ENUMERATED',
@@ -251,7 +251,7 @@ reserved_words = {
 #  'IMPLIED'     : 'IMPLIED',
   'IMPORTS'     : 'IMPORTS',
   'INCLUDES'    : 'INCLUDES',
-#  'INSTANCE'    : 'INSTANCE',
+  'INSTANCE'    : 'INSTANCE',
   'INTEGER'     : 'INTEGER',
   'INTERSECTION' : 'INTERSECTION',
   'MAX'         : 'MAX',
@@ -264,7 +264,7 @@ reserved_words = {
   'OF'          : 'OF',
   'OPTIONAL'    : 'OPTIONAL',
   'PATTERN'     : 'PATTERN',
-#  'PDV'         : 'PDV',
+  'PDV'         : 'PDV',
   'PLUS-INFINITY' : 'PLUS_INFINITY',
   'PRESENT'     : 'PRESENT',
   'PRIVATE'     : 'PRIVATE',
@@ -279,7 +279,7 @@ reserved_words = {
   'TRUE'        : 'TRUE',
   'TYPE-IDENTIFIER' : 'TYPE_IDENTIFIER',
   'UNION'       : 'UNION',
-#  'UNIQUE'      : 'UNIQUE',
+  'UNIQUE'      : 'UNIQUE',
   'UNIVERSAL'   : 'UNIVERSAL',
   'UTCTime'     : 'UTCTime',
   'WITH'        : 'WITH',
@@ -287,9 +287,9 @@ reserved_words = {
   'ANY'         : 'ANY',
 }
 
-for k in static_tokens.keys ():
-    if static_tokens [k] == None:
-        static_tokens [k] = k
+for k in static_tokens.keys():
+  if static_tokens [k] == None:
+    static_tokens [k] = k
 
 StringTypes = ['Numeric', 'Printable', 'IA5', 'BMP', 'Universal', 'UTF8',
                'Teletex', 'T61', 'Videotex', 'Graphic', 'ISO646', 'Visible',
@@ -305,8 +305,10 @@ tokens = static_tokens.values() \
             'REAL_NUMBER', 'NUMBER', 'PYQUOTE']
 
 
+cur_mod = __import__ (__name__) # XXX blech!
+
 for (k, v) in static_tokens.items ():
-  __main__.__dict__['t_' + v] = k
+    cur_mod.__dict__['t_' + v] = k
 
 # 11.10 Binary strings
 def t_BSTRING (t):
@@ -325,7 +327,7 @@ def t_QSTRING (t):
 def t_UCASE_IDENT (t):
     r"[A-Z](-[a-zA-Z0-9]|[a-zA-Z0-9])*" # can't end w/ '-'
     if (is_class_ident(t.value)): t.type = 'CLASS_IDENT'
-    if (is_x880_syntax(t.value)): t.type = t.value
+    if (is_class_syntax(t.value)): t.type = t.value
     t.type = reserved_words.get(t.value, t.type)
     return t
 
@@ -383,6 +385,7 @@ def t_braceignore_rbrace(t):
 
 def t_braceignore_QSTRING (t):
   r'"([^"]|"")*"'
+  t.lexer.lineno += t.value.count("\n")
 
 def t_braceignore_COMMENT(t):
   r"--(-[^\-\n]|[^\-\n])*(--|\n|-\n|$|-$)"
@@ -546,9 +549,12 @@ class EthCtx:
     self.default_oid_variant = ''
     self.default_opentype_variant = ''
     self.default_containing_variant = '_pdu_new'
+    self.default_embedded_pdv_cb = None
     self.default_external_type_cb = None
+    self.emitted_pdu = {}
     self.module = {}
     self.module_ord = []
+    self.all_type_attr = {}
     self.all_tags = {}
     self.all_vals = {}
 
@@ -569,6 +575,9 @@ class EthCtx:
   def Module(self):  # current module name
     return self.modules[-1][0]
 
+  def groups(self):
+    return self.group_by_prot or (self.conform.last_group > 0)
+
   def dbg(self, d):
     if (self.dbgopt.find(d) >= 0):
       return True
@@ -601,10 +610,12 @@ class EthCtx:
       pass
     return "MIN((%s),(%s))" % (a, b) 
 
-  def value_get_eth(self, nm):
-    ethname = nm
-    if self.value.has_key(nm):
-      ethname = self.value[nm]['ethname']
+  def value_get_eth(self, val):
+    if isinstance(val, Value):
+      return val.to_str(self)
+    ethname = val
+    if self.value.has_key(val):
+      ethname = self.value[val]['ethname']
     return ethname
 
   def value_get_val(self, nm):
@@ -635,8 +646,23 @@ class EthCtx:
     attr = {}
     while len(types):
       t = types.pop()
-      attr.update(self.type[t]['attr'])
-      attr.update(self.eth_type[self.type[t]['ethname']]['attr'])
+      if (self.type[t]['import']):
+        attr.update(self.type[t]['attr'])
+        attr.update(self.eth_get_type_attr_from_all(t, self.type[t]['import']))
+      elif (self.type[t]['val'].type == 'SelectionType'):
+        val = self.type[t]['val']
+        (ftype, display) = val.eth_ftype(self)
+        attr.update({ 'TYPE' : ftype, 'DISPLAY' : display,
+                      'STRINGS' : val.eth_strings(), 'BITMASK' : '0' });
+      else:
+        attr.update(self.type[t]['attr'])
+        attr.update(self.eth_type[self.type[t]['ethname']]['attr'])
+    return attr
+
+  def eth_get_type_attr_from_all(self, type, module):
+    attr = {}
+    if self.all_type_attr.has_key(module) and self.all_type_attr[module].has_key(type):
+      attr = self.all_type_attr[module][type]
     return attr
 
   def get_ttag_from_all(self, type, module):
@@ -651,14 +677,58 @@ class EthCtx:
       val = self.all_vals[module][nm]
     return val
 
+  def get_obj_repr(self, ident, restr):
+    def set_type_fn(cls, field, fnfield):
+      obj[fnfield + '_fn'] = 'NULL'
+      obj[fnfield + '_pdu'] = 'NULL'
+      if val.has_key(field) and isinstance(val[field], Type_Ref):
+        p = val[field].eth_type_default_pars(self, '')
+        obj[fnfield + '_fn'] = p['TYPE_REF_FN']
+        obj[fnfield + '_fn'] = obj[fnfield + '_fn'] % p  # one iteration
+        if (self.conform.check_item('PDU', cls + '.' + field)):
+          obj[fnfield + '_pdu'] = 'dissect_' + self.field[val[field].val]['ethname']
+      return
+    # end of get_type_fn()
+    obj = { '_name' : ident, '_ident' : asn2c(ident)}
+    obj['_class'] = self.oassign[ident].cls
+    val = self.oassign[ident].val
+    fld = None
+    fld_neg = False
+    if len(restr) > 0:
+      fld = restr[0]
+      if fld[0] == '!':
+        fld_neg = True
+        fld = fld[1:]
+    if fld:
+      if fld_neg:
+        if val.has_key(fld):
+          return None
+      else:
+        if not val.has_key(fld):
+          return None
+    for f in val.keys():
+      if isinstance(val[f], Node):
+        obj[f] = val[f].fld_obj_repr(self)
+      else:
+        obj[f] = str(val[f])
+    if (obj['_class'] == 'TYPE-IDENTIFIER') or (obj['_class'] == 'ABSTRACT-SYNTAX'):
+      set_type_fn(obj['_class'], '&Type', '_type')
+    if (obj['_class'] == 'OPERATION'):
+      set_type_fn(obj['_class'], '&ArgumentType', '_argument')
+      set_type_fn(obj['_class'], '&ResultType', '_result')
+    if (obj['_class'] == 'ERROR'):
+      set_type_fn(obj['_class'], '&ParameterType', '_parameter')
+    return obj
+
   #--- eth_reg_module -----------------------------------------------------------
   def eth_reg_module(self, module):
     #print "eth_reg_module(module='%s')" % (module)
-    self.modules.append((module, self.proto))
-    if self.module.has_key(module):
-      raise "Duplicate module for " + module
-    self.module[module] = []
-    self.module_ord.append(module)
+    name = module.get_name()
+    self.modules.append([name, module.get_proto(self)])
+    if self.module.has_key(name):
+      raise "Duplicate module for " + name
+    self.module[name] = []
+    self.module_ord.append(name)
 
   #--- eth_module_dep_add ------------------------------------------------------------
   def eth_module_dep_add(self, module, dep):
@@ -699,6 +769,19 @@ class EthCtx:
     if  (self.exports_all):
       self.vexports.append(ident)
 
+  #--- eth_reg_oassign --------------------------------------------------------
+  def eth_reg_oassign(self, oassign):
+    ident = oassign.ident
+    #print "eth_reg_oassign(ident='%s')" % (ident)
+    if self.oassign.has_key(ident):
+      if self.oassign[ident] == oassign:
+        return  # OK - already defined
+      else:
+        raise "Duplicate information object assignment for " + ident
+    self.oassign[ident] = oassign
+    self.oassign_ord.append(ident)
+    self.oassign_cls.setdefault(oassign.cls, []).append(ident)
+
   #--- eth_import_type --------------------------------------------------------
   def eth_import_type(self, ident, mod, proto):
     #print "eth_import_type(ident='%s', mod='%s', prot='%s')" % (ident, mod, proto)
@@ -802,7 +885,7 @@ class EthCtx:
     self.type[ident]['no_emit'] = self.conform.use_item('NO_EMIT', ident)
     self.type[ident]['tname'] = self.conform.use_item('TYPE_RENAME', ident, val_dflt=self.type[ident]['tname'])
     self.type[ident]['ethname'] = ''
-    if val.type == 'Type_Ref':
+    if (val.type == 'Type_Ref') or (val.type == 'SelectionType') :
       self.type[ident]['attr'] = {}
     else:
       (ftype, display) = val.eth_ftype(self)
@@ -810,18 +893,26 @@ class EthCtx:
                                    'STRINGS' : val.eth_strings(), 'BITMASK' : '0' }
     self.type[ident]['attr'].update(self.conform.use_item('TYPE_ATTR', ident))
     self.type_ord.append(ident)
+    # PDU
+    if (self.conform.check_item('PDU', ident)):
+      self.eth_reg_field(ident, ident, impl=val.HasImplicitTag(self), pdu=self.conform.use_item('PDU', ident))
 
   #--- eth_reg_objectclass ----------------------------------------------------------
-  def eth_reg_objectclass(self, ident):
+  def eth_reg_objectclass(self, ident, val):
     #print "eth_reg_objectclass(ident='%s')" % (ident)
     if self.objectclass.has_key(ident):
       if self.objectclass[ident]['import'] and (self.objectclass[ident]['import'] == self.Module()) :
         # replace imported object class
         del self.objectclass[ident]
         self.objectclass_imp.remove(ident)
+      elif isinstance(self.objectclass[ident]['val'], Class_Ref) and \
+           isinstance(val, Class_Ref) and \
+           (self.objectclass[ident]['val'].val == val.val):
+        pass  # ignore duplicated CLASS1 ::= CLASS2
       else:
         raise "Duplicate object class for " + ident
     self.objectclass[ident] = { 'import' : None, 'module' : self.Module(), 'proto' : self.proto }
+    self.objectclass[ident]['val'] = val
     self.objectclass[ident]['export'] = self.conform.use_item('EXPORTS', ident)
     self.objectclass_ord.append(ident)
 
@@ -850,7 +941,10 @@ class EthCtx:
   def eth_reg_field(self, ident, type, idx='', parent=None, impl=False, pdu=None):
     #print "eth_reg_field(ident='%s', type='%s')" % (ident, type)
     if self.field.has_key(ident):
-      raise "Duplicate field for " + ident
+      if pdu and (type == self.field[ident]['type']):
+        pass  # OK already created PDU
+      else:
+        raise "Duplicate field for " + ident
     self.field[ident] = {'type' : type, 'idx' : idx, 'impl' : impl, 'pdu' : pdu,
                          'modified' : '', 'attr' : {} , 'create_field' : False }
     name = ident.split('/')[-1]
@@ -895,6 +989,9 @@ class EthCtx:
     self.objectclass = {}
     self.objectclass_ord = []
     self.objectclass_imp = []
+    self.oassign = {}
+    self.oassign_ord = []
+    self.oassign_cls = {}
     #--- Modules ------------
     self.modules = []
     self.exports_all = False
@@ -1094,6 +1191,8 @@ class EthCtx:
       nm = asn2c(nm)
       if (self.field[f]['pdu']): 
         nm += '_PDU'
+        if (not self.merge_modules):
+          nm = self.eproto + '_' + nm
       t = self.field[f]['type']
       if self.type.has_key(t):
         ethtype = self.type[t]['ethname']
@@ -1129,7 +1228,7 @@ class EthCtx:
         self.eth_hfpdu_ord.append(nm)
       else:
         self.eth_hf_ord.append(nm)
-      fullname = "hf_%s_%s" % (self.eproto, nm)
+      fullname = 'hf_%s_%s' % (self.eproto, nm)
       attr = self.eth_get_type_attr(self.field[f]['type']).copy()
       attr.update(self.field[f]['attr'])
       if (self.NAPI() and attr.has_key('NAME')):
@@ -1167,6 +1266,9 @@ class EthCtx:
       if not self.all_tags.has_key(m):
         self.all_tags[m] = {}
       self.all_tags[m][t] = self.type[t]['val'].GetTTag(self)
+      if not self.all_type_attr.has_key(m):
+        self.all_type_attr[m] = {}
+      self.all_type_attr[m][t] = self.eth_get_type_attr(t).copy()
     for v in self.vexports:
       if not self.value.has_key(v):
         continue
@@ -1306,18 +1408,20 @@ class EthCtx:
       out += "dissect_%s_%s(gboolean implicit_tag _U_, tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {\n" % (self.eth_type[tname]['proto'], tname)
     elif (self.Per()):
       out += "dissect_%s_%s(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) {\n" % (self.eth_type[tname]['proto'], tname)
-    if self.conform.get_fn_presence(tname):
-      out += self.conform.get_fn_text(tname, 'FN_HDR')
-    elif self.conform.get_fn_presence(self.eth_type[tname]['ref'][0]):
+    #if self.conform.get_fn_presence(tname):
+    #  out += self.conform.get_fn_text(tname, 'FN_HDR')
+    #el
+    if self.conform.get_fn_presence(self.eth_type[tname]['ref'][0]):
       out += self.conform.get_fn_text(self.eth_type[tname]['ref'][0], 'FN_HDR')
     return out
 
   #--- eth_type_fn_ftr --------------------------------------------------------
   def eth_type_fn_ftr(self, tname):
     out = '\n'
-    if self.conform.get_fn_presence(tname):
-      out += self.conform.get_fn_text(tname, 'FN_FTR')
-    elif self.conform.get_fn_presence(self.eth_type[tname]['ref'][0]):
+    #if self.conform.get_fn_presence(tname):
+    #  out += self.conform.get_fn_text(tname, 'FN_FTR')
+    #el
+    if self.conform.get_fn_presence(self.eth_type[tname]['ref'][0]):
       out += self.conform.get_fn_text(self.eth_type[tname]['ref'][0], 'FN_FTR')
     out += "  return offset;\n"
     out += "}\n"
@@ -1326,9 +1430,10 @@ class EthCtx:
   #--- eth_type_fn_body -------------------------------------------------------
   def eth_type_fn_body(self, tname, body, pars=None):
     out = body
-    if self.conform.get_fn_body_presence(tname):
-      out = self.conform.get_fn_text(tname, 'FN_BODY')
-    elif self.conform.get_fn_body_presence(self.eth_type[tname]['ref'][0]):
+    #if self.conform.get_fn_body_presence(tname):
+    #  out = self.conform.get_fn_text(tname, 'FN_BODY')
+    #el
+    if self.conform.get_fn_body_presence(self.eth_type[tname]['ref'][0]):
       out = self.conform.get_fn_text(self.eth_type[tname]['ref'][0], 'FN_BODY')
     if pars:
       try:
@@ -1427,6 +1532,17 @@ class EthCtx:
     for (m, p) in self.modules:
       fx.write("%-*s  %s\n" % (maxw, m, p))
     fx.write('#.END\n\n')
+    for cls in self.objectclass_ord:
+      if self.objectclass[cls]['export']:
+        fx.write('#.CLASS %s\n' % (cls))
+        maxw = 2
+        for fld in self.objectclass[cls]['val'].fields:
+          w = len(fld.fld_repr()[0])  
+          if (w > maxw): maxw = w
+        for fld in self.objectclass[cls]['val'].fields:
+          repr = fld.fld_repr()
+          fx.write('%-*s  %s\n' % (maxw, repr[0], ' '.join(repr[1:])))
+        fx.write('#.END\n\n')
     if self.Ber():
       fx.write('#.IMPORT_TAG\n')
       for t in self.eth_export_ord:  # tags
@@ -1615,7 +1731,11 @@ class EthCtx:
       fx.write('/*--- PDUs ---*/\n\n')
       for f in self.eth_hfpdu_ord:
         if (self.eth_hf[f]['pdu']):
-          fx.write(out_pdu(f))
+          if (self.emitted_pdu.has_key(f)):
+            fx.write("  /* %s already emitted */\n" % (f))
+          else:
+            fx.write(out_pdu(f))
+            self.emitted_pdu[f] = True
       fx.write('\n')
     fempty = pos == fx.tell()
     self.output.file_close(fx, discard=fempty)
@@ -1685,6 +1805,38 @@ class EthCtx:
     fx.write('\n')
     self.output.file_close(fx, discard=fempty)
 
+  #--- eth_output_table -----------------------------------------------------
+  def eth_output_table(self):
+    for num in self.conform.report.keys():
+      fx = self.output.file_open('table' + num)
+      for rep in self.conform.report[num]:
+        if rep['type'] == 'HDR':
+          fx.write('\n')
+        if rep['var']:
+          var = rep['var']
+          var_list = var.split('.')
+          cls = var_list[0]
+          del var_list[0]
+          if (self.oassign_cls.has_key(cls)):
+            for ident in self.oassign_cls[cls]:
+             obj = self.get_obj_repr(ident, var_list)
+             if not obj:
+               continue
+             obj['_LOOP'] = var
+             obj['_DICT'] = str(obj)
+             try:
+               text = rep['text'] % obj
+             except (KeyError):
+               raise sys.exc_type, "%s:%s invalid key %s for information object %s of %s" % (rep['fn'], rep['lineno'], sys.exc_value, ident, var)
+             fx.write(text)
+          else:
+            fx.write("/* Unknown or empty loop list %s */\n" % (var))
+        else:
+          fx.write(rep['text'])
+        if rep['type'] == 'FTR':
+          fx.write('\n')
+      self.output.file_close(fx)
+
   #--- dupl_report -----------------------------------------------------
   def dupl_report(self):
     # types
@@ -1719,6 +1871,9 @@ class EthCtx:
       print "\n# Value assignments"
       for a in self.vassign_ord:
         print ' ', a
+      print "\n# Information object assignments"
+      for a in self.oassign_ord:
+        print " %-12s (%s)" % (a, self.oassign[a].cls)
     if self.dbg('t'):
       print "\n# Imported Types"
       print "%-40s %-24s %-24s" % ("ASN.1 name", "Module", "Protocol")
@@ -1814,6 +1969,7 @@ class EthCtx:
     self.eth_output_dis_hnd()
     self.eth_output_dis_reg()
     self.eth_output_dis_tab()
+    self.eth_output_table()
 
   def dbg_modules(self):
     def print_mod(m):
@@ -1840,7 +1996,8 @@ class EthCtx:
         print_mod(m)
     if mod_cyc:
       print "\nCyclic dependencies:"
-      print "mod_cyc = ", mod_cyc
+      for i in (range(len(mod_cyc))):
+        print "%02d: %s" % (i + 1, str(mod_cyc[i]))
 
 
 #--- EthCnf -------------------------------------------------------------------
@@ -1851,6 +2008,7 @@ class EthCnf:
     self.table = {}
     self.order = {}
     self.fn = {}
+    self.report = {}
     self.suppress_line = False
     self.include_path = []
     #                                   Value name             Default value       Duplicity check   Usage check
@@ -1873,6 +2031,7 @@ class EthCnf:
     self.tblcfg['ETYPE_ATTR']      = { 'val_nm' : 'attr',     'val_dflt' : {},    'chk_dup' : True, 'chk_use' : False }
     self.tblcfg['FIELD_ATTR']      = { 'val_nm' : 'attr',     'val_dflt' : {},    'chk_dup' : True, 'chk_use' : True }
     self.tblcfg['EFIELD_ATTR']     = { 'val_nm' : 'attr',     'val_dflt' : {},    'chk_dup' : True, 'chk_use' : True }
+    self.tblcfg['ASSIGNED_ID']     = { 'val_nm' : 'ids',      'val_dflt' : {},    'chk_dup' : False,'chk_use' : False }
 
 
     for k in self.tblcfg.keys() :
@@ -1893,6 +2052,7 @@ class EthCnf:
     if not self.table[table].has_key(key):
       self.table[table][key] = {'fn' : fn, 'lineno' : lineno, 'used' : False}
       self.order[table].append(key)
+      self.table[table][key][self.tblcfg[table]['val_nm']] = {}
     self.table[table][key][self.tblcfg[table]['val_nm']].update(kw[self.tblcfg[table]['val_nm']])
 
   def get_order(self, table):
@@ -2043,7 +2203,8 @@ class EthCnf:
       return par
 
     f = open(fn, "r")
-    directive = re.compile(r'^\s*#\.(?P<name>[A-Z_]+)\s+')
+    directive = re.compile(r'^\s*#\.(?P<name>[A-Z_][A-Z_0-9]*)\s+')
+    report = re.compile(r'^TABLE(?P<num>\d*)_(?P<type>HDR|BODY|FTR)$')
     comment = re.compile(r'^\s*#[^.]')
     empty = re.compile(r'^\s*$')
     lineno = 0
@@ -2069,6 +2230,7 @@ class EthCnf:
       if comment.search(line): continue
       result = directive.search(line)
       if result:  # directive
+        rep_result = report.search(result.group('name'))
         if result.group('name') == 'END_OF_CNF':
           f.close()
         elif result.group('name') == 'OPT':
@@ -2167,6 +2329,24 @@ class EthCnf:
           if not name.isupper():
             warnings.warn_explicit("No lower-case letters shall be included in information object class name (%s)" % (name),
                                     UserWarning, fn, lineno)
+        elif result.group('name') == 'ASSIGNED_OBJECT_IDENTIFIER':
+          par = get_par(line[result.end():], 1, 1, fn=fn, lineno=lineno)
+          if not par: continue
+          self.update_item('ASSIGNED_ID', 'OBJECT_IDENTIFIER', ids={par[0] : par[0]}, fn=fn, lineno=lineno)
+        elif rep_result:  # Reports
+          num = rep_result.group('num')
+          type = rep_result.group('type')
+          if type == 'BODY':
+            par = get_par(line[result.end():], 1, 1, fn=fn, lineno=lineno)
+            if not par: continue
+          else:
+            par = get_par(line[result.end():], 0, 0, fn=fn, lineno=lineno)
+          rep = { 'type' : type, 'var' : None, 'text' : '', 'fn' : fn, 'lineno' : lineno }
+          if len(par) > 0:
+            rep['var'] = par[0]
+          self.report.setdefault(num, []).append(rep)
+          ctx = 'TABLE'
+          name = num
         elif result.group('name') == 'INCLUDE':
           par = get_par(line[result.end():], 1, 1, fn=fn, lineno=lineno)
           if not par: 
@@ -2347,11 +2527,11 @@ class EthCnf:
         if empty.match(line): continue
         par = get_par(line, 1, 3, fn=fn, lineno=lineno)
         if not par: continue
-        if (len(par) < 2): par.append('OpenType')
-        if (len(par) < 3): par.append(None)
-        if not set_type_to_class(name, par[0], par[1], par[2]):
+        if not set_type_to_class(name, par[0], par[1:]):
           warnings.warn_explicit("Could not set type of class member %s.&%s to %s" % (name, par[0], par[1]),
                                   UserWarning, fn, lineno)
+      elif ctx == 'TABLE':
+        self.report[name][-1]['text'] += line
 
   def set_opt(self, opt, par, fn, lineno):
     #print "set_opt: %s, %s" % (opt, par)
@@ -2397,6 +2577,9 @@ class EthCnf:
     elif opt in ("-S",):
       par = self.check_par(par, 0, 0, fn, lineno)
       self.ectx.merge_modules = True
+    elif opt in ("GROUP_BY_PROT",):
+      par = self.check_par(par, 0, 0, fn, lineno)
+      self.ectx.group_by_prot = True
     elif opt in ("-o",):
       par = self.check_par(par, 1, 1, fn, lineno)
       if not par: return
@@ -2415,6 +2598,10 @@ class EthCnf:
     elif opt in ("-L",):
       par = self.check_par(par, 0, 0, fn, lineno)
       self.suppress_line = True
+    elif opt in ("EMBEDDED_PDV_CB",):
+      par = self.check_par(par, 1, 1, fn, lineno)
+      if not par: return
+      self.ectx.default_embedded_pdv_cb = par[0]
     elif opt in ("EXTERNAL_TYPE_CB",):
       par = self.check_par(par, 1, 1, fn, lineno)
       if not par: return
@@ -2516,7 +2703,17 @@ class EthOut:
         fx.write(self.fhdr(fn))
     if not self.ectx.merge_modules:
       fx.write('\n')
-      fx.write(self.outcomment("--- Module %s --- --- ---" % (self.ectx.Module()), comment))
+      mstr = "--- "
+      if self.ectx.groups():
+        mstr += "Module"
+        if (len(self.ectx.modules) > 1):
+          mstr += "s"
+        for (m, p) in self.ectx.modules:
+          mstr += " %s" % (m)
+      else:
+        mstr += "Module %s" % (self.ectx.Module())
+      mstr += " --- --- ---"
+      fx.write(self.outcomment(mstr, comment))
       fx.write('\n')
     return fx
   #--- file_close -------------------------------------------------------
@@ -2637,7 +2834,7 @@ class Node:
         l.append ("".join (map (lambda (k,v): self.str_child (k, v, depth + 1),
                                 self.__dict__.items ())))
         return "\n".join (l)
-    def __str__(self):
+    def __repr__(self):
         return "\n" + self.str_depth (0)
     def to_python (self, ctx):
         return self.str_depth (ctx.indent_lev)
@@ -2645,8 +2842,11 @@ class Node:
     def eth_reg(self, ident, ectx):
         pass
 
-#--- value_assign -------------------------------------------------------------
-class value_assign (Node):
+    def fld_obj_repr(self, ectx):
+        return "/* TO DO %s */" % (str(self))
+
+#--- ValueAssignment -------------------------------------------------------------
+class ValueAssignment (Node):
   def __init__(self,*args, **kw) :
     Node.__init__ (self,*args, **kw)
 
@@ -2655,6 +2855,52 @@ class value_assign (Node):
     ectx.eth_reg_vassign(self)
     ectx.eth_reg_value(self.ident, self.typ, self.val)
 
+#--- ObjectAssignment -------------------------------------------------------------
+class ObjectAssignment (Node):
+  def __init__(self,*args, **kw) :
+    Node.__init__ (self,*args, **kw)
+
+  def __eq__(self, other):
+    if self.cls != other.cls:
+      return False
+    if len(self.val) != len(other.val):
+      return False
+    for f in (self.val.keys()):
+      if not other.val.has_key(f):
+        return False
+      if isinstance(self.val[f], Node) and isinstance(other.val[f], Node):
+        if not self.val[f].fld_obj_eq(other.val[f]):
+          return False
+      else:
+        if str(self.val[f]) != str(other.val[f]):
+          return False
+    return True
+
+  def eth_reg(self, ident, ectx):
+    def make_virtual_type(cls, field, prefix):
+      if isinstance(self.val, str): return
+      if self.val.has_key(field) and not isinstance(self.val[field], Type_Ref):
+        vnm = prefix + '-' + self.ident
+        virtual_tr = Type_Ref(val = vnm)
+        t = self.val[field]
+        self.val[field] = virtual_tr
+        ectx.eth_reg_assign(vnm, t, virt=True)
+        ectx.eth_reg_type(vnm, t)
+        t.eth_reg_sub(vnm, ectx)
+      if self.val.has_key(field) and ectx.conform.check_item('PDU', cls + '.' + field):
+        ectx.eth_reg_field(self.val[field].val, self.val[field].val, impl=self.val[field].HasImplicitTag(ectx), pdu=ectx.conform.use_item('PDU', cls + '.' + field))
+      return
+    # end of make_virtual_type()
+    if ectx.conform.omit_assignment('V', self.ident, ectx.Module()): return # Assignment to omit
+    ectx.eth_reg_oassign(self)
+    if (self.cls == 'TYPE-IDENTIFIER') or (self.cls == 'ABSTRACT-SYNTAX'):
+      make_virtual_type(self.cls, '&Type', 'TYPE')
+    if (self.cls == 'OPERATION'):
+      make_virtual_type(self.cls, '&ArgumentType', 'ARG')
+      make_virtual_type(self.cls, '&ResultType', 'RES')
+    if (self.cls == 'ERROR'):
+      make_virtual_type(self.cls, '&ParameterType', 'PAR')
+
 
 #--- Type ---------------------------------------------------------------------
 class Type (Node):
@@ -2662,6 +2908,7 @@ class Type (Node):
     self.name = None
     self.constr = None
     self.tags = []
+    self.named_list = None
     Node.__init__ (self,*args, **kw)
 
   def IsNamed(self):
@@ -2757,6 +3004,9 @@ class Type (Node):
     print "#Selection '%s' required for non-CHOICE type %s" % (sel, self.type)
     print self.str_depth(1)
     
+  def fld_obj_eq(self, other):
+    return isinstance(other, Type) and (self.eth_tname() == other.eth_tname())
+
   def eth_reg(self, ident, ectx, tstrip=0, tagflag=False, selflag=False, idx='', parent=None):
     #print "eth_reg(): %s, ident=%s, tstrip=%d, tagflag=%s, selflag=%s, parent=%s" %(self.type, ident, tstrip, str(tagflag), str(selflag), str(parent))
     if (ectx.Tag() and (len(self.tags) > tstrip)):
@@ -2779,11 +3029,9 @@ class Type (Node):
       ectx.eth_reg_assign(nm, self)
       if self.type == 'Type_Ref':
         ectx.eth_reg_type(nm, self)
-      if (ectx.conform.check_item('PDU', nm)):
-        ectx.eth_reg_field(nm, nm, impl=self.HasImplicitTag(ectx), pdu=ectx.conform.use_item('PDU', nm))
     virtual_tr = Type_Ref(val=ectx.conform.use_item('SET_TYPE', nm))
     if (self.type == 'Type_Ref') or ectx.conform.check_item('SET_TYPE', nm):
-      if ident and (ectx.conform.check_item('TYPE_RENAME', nm) or ectx.conform.get_fn_presence(nm)):
+      if ident and (ectx.conform.check_item('TYPE_RENAME', nm) or ectx.conform.get_fn_presence(nm) or selflag):
         if ectx.conform.check_item('SET_TYPE', nm):
           ectx.eth_reg_type(nm, virtual_tr)  # dummy Type Reference
         else:
@@ -2886,8 +3134,10 @@ class Type (Node):
     }
     if (ectx.eth_type[tname]['tree']):
       pars['ETT_INDEX'] = ectx.eth_type[tname]['tree']
-    if (not ectx.Per()):
-      pars['PINFO'] = 'pinfo'
+    if (ectx.merge_modules):
+      pars['PROTOP'] = ''
+    else:
+      pars['PROTOP'] = ectx.eth_type[tname]['proto'] + '_'
     return pars
 
   def eth_type_fn(self, proto, tname, ectx):
@@ -2899,7 +3149,11 @@ class Type (Node):
       pars.update(ectx.conform.use_item('FN_PARS', ectx.eth_type[tname]['ref'][0]))
     pars['DEFAULT_BODY'] = body
     for i in range(4):
-      for k in pars.keys(): pars[k] = pars[k] % pars
+      for k in pars.keys(): 
+        try:
+          pars[k] = pars[k] % pars
+        except (TypeError):
+          raise sys.exc_type, "%s\n%s" % (str(pars), sys.exc_value)
     out = '\n'
     out += self.eth_type_default_table(ectx, tname) % pars
     out += ectx.eth_type_fn_hdr(tname)
@@ -2917,11 +3171,19 @@ class Value (Node):
     self.name = name
 
   def to_str(self, ectx):
-    return str(self)
+    return str(self.val)
 
   def get_dep(self):
     return None
 
+  def fld_obj_repr(self, ectx):
+    return self.to_str(ectx)
+
+#--- Value_Ref -----------------------------------------------------------------
+class Value_Ref (Value):
+  def to_str(self, ectx):
+    return asn2c(self.val)
+
 #--- ObjectClass ---------------------------------------------------------------------
 class ObjectClass (Node):
   def __init__(self,*args, **kw) :
@@ -2934,7 +3196,19 @@ class ObjectClass (Node):
 
   def eth_reg(self, ident, ectx):
     if ectx.conform.omit_assignment('C', self.name, ectx.Module()): return # Assignment to omit
-    ectx.eth_reg_objectclass(self.name)
+    ectx.eth_reg_objectclass(self.name, self)
+
+#--- Class_Ref -----------------------------------------------------------------
+class Class_Ref (ObjectClass):
+  pass
+
+#--- ObjectClassDefn ---------------------------------------------------------------------
+class ObjectClassDefn (ObjectClass):
+  def reg_types(self):
+    for fld in self.fields:
+      repr = fld.fld_repr()
+      set_type_to_class(self.name, repr[0], repr[1:])
+
 
 #--- Tag ---------------------------------------------------------------
 class Tag (Node):
@@ -2970,6 +3244,9 @@ class Constraint (Node):
   def __str__ (self):
     return "Constraint: type=%s, subtype=%s" % (self.type, self.subtype)
 
+  def eth_tname(self):
+    return '#' + self.type + '_' + str(id(self))
+
   def IsSize(self):
     return (self.type == 'Size' and self.subtype.IsValue()) \
            or (self.type == 'Intersection' and (self.subtype[0].IsSize() or self.subtype[1].IsSize())) \
@@ -3083,7 +3360,7 @@ class Constraint (Node):
 
   def IsNegativ(self):
     def is_neg(sval):
-      return sval[0] == '-'
+      return isinstance(sval, str) and (sval[0] == '-')
     if self.type == 'SingleValue':
       return is_neg(self.subtype)
     elif self.type == 'ValueRange':
@@ -3093,6 +3370,8 @@ class Constraint (Node):
 
   def eth_constrname(self):
     def int2str(val):
+      if isinstance(val, Value_Ref):
+        return asn2c(val.val)
       try:
         if (int(val) < 0):
           return 'M' + str(-int(val))
@@ -3120,12 +3399,21 @@ class Module (Node):
     return """#%s
 %s""" % (self.ident, self.body.to_python (ctx))
 
-  def to_eth (self, ectx):
+  def get_name(self):
+    return self.ident.val
+
+  def get_proto(self, ectx):
+    if (ectx.proto):
+      prot = ectx.proto
+    else:
+      prot = ectx.conform.use_item('MODULE', self.get_name(), val_dflt=self.get_name())
+    return prot
+
+  def to_eth(self, ectx):
     ectx.tags_def = 'EXPLICIT' # default = explicit
-    if (not ectx.proto):
-      ectx.proto = ectx.conform.use_item('MODULE', self.ident.val, val_dflt=self.ident.val)
+    ectx.proto = self.get_proto(ectx)
     ectx.tag_def = self.tag_def.dfl_tag
-    ectx.eth_reg_module(self.ident.val)
+    ectx.eth_reg_module(self)
     self.body.to_eth(ectx)
 
 class Module_Body (Node):
@@ -3146,10 +3434,13 @@ class Module_Body (Node):
       for s in i.symbol_list:
         if isinstance(s, Type_Ref):
           ectx.eth_import_type(s.val, mod, proto)
+        elif isinstance(s, Value_Ref):
+          ectx.eth_import_value(s.val, mod, proto)
         elif isinstance(s, Class_Ref):
           ectx.eth_import_class(s.val, mod, proto)
         else:
-          ectx.eth_import_value(s, mod, proto)
+          msg = 'Unknown kind of imported symbol %s from %s' % (str(s), mod)
+          warnings.warn_explicit(msg, UserWarning, '', '')
     # AssignmentList
     for a in self.assign_list:
       a.eth_reg('', ectx)
@@ -3200,14 +3491,6 @@ class PyQuote (Node):
     def to_python (self, ctx):
         return ctx.register_pyquote (self.val)
 
-#--- Class_Ref -----------------------------------------------------------------
-class Class_Ref (Type):
-  def to_python (self, ctx):
-    return self.val
-
-  def eth_tname(self):
-    return asn2c(self.val)
-
 #--- Type_Ref -----------------------------------------------------------------
 class Type_Ref (Type):
   def to_python (self, ctx):
@@ -3219,6 +3502,9 @@ class Type_Ref (Type):
   def eth_tname(self):
     return asn2c(self.val)
 
+  def fld_obj_repr(self, ectx):
+    return self.val
+
   def get_components(self, ectx):
     if not ectx.type.has_key(self.val) or ectx.type[self.val]['import']:
       msg = "Can not get COMPONENTS OF %s which is imported type" % (self.val)
@@ -3248,7 +3534,10 @@ class Type_Ref (Type):
       return ectx.type[self.val]['val'].IndetermTag(ectx)
 
   def eth_type_default_pars(self, ectx, tname):
-    pars = Type.eth_type_default_pars(self, ectx, tname)
+    if tname:
+      pars = Type.eth_type_default_pars(self, ectx, tname)
+    else:
+      pars = {}
     t = ectx.type[self.val]['ethname']
     pars['TYPE_REF_PROTO'] = ectx.eth_type[t]['proto']
     pars['TYPE_REF_TNAME'] = t
@@ -3281,6 +3570,12 @@ class SelectionType (Type):
     self.seltype = ectx.eth_sel_req(self.typ.val, self.sel)
     ectx.eth_dep_add(ident, self.seltype)
 
+  def eth_ftype(self, ectx):
+    (ftype, display) = ('FT_NONE', 'BASE_NONE')
+    if self.sel_of_typeref() and not ectx.type[self.seltype]['import']:
+      (ftype, display) = ectx.type[self.typ.val]['val'].eth_ftype_sel(self.sel, ectx)
+    return (ftype, display)
+
   def GetTTag(self, ectx):
     #print "GetTTag(%s)\n" % self.seltype;
     if (ectx.type[self.seltype]['import']):
@@ -3414,7 +3709,9 @@ class SeqType (SqType):
     lst = self.elt_list[:]
     if hasattr(self, 'ext_list'):
       lst.extend(self.ext_list)
-    for e in (self.elt_list):
+    if hasattr(self, 'elt_list2'):
+      lst.extend(self.elt_list2)
+    for e in (lst):
       if e.type == 'components_of':
         return True
     return False
@@ -3432,9 +3729,18 @@ class SeqType (SqType):
             comp = self.ext_list[i].typ.get_components(ectx)
             self.ext_list[i:i+1] = comp
             break
+      if hasattr(self, 'elt_list2'):
+        for i in range(len(self.elt_list2)):
+          if self.elt_list2[i].type == 'components_of':
+            comp = self.elt_list2[i].typ.get_components(ectx)
+            self.elt_list2[i:i+1] = comp
+            break
 
   def get_components(self, ectx):
-    return self.elt_list[:]
+    lst = self.elt_list[:]
+    if hasattr(self, 'elt_list2'):
+      lst.extend(self.elt_list2)
+    return lst
 
   def eth_type_default_table(self, ectx, tname):
     #print "eth_type_default_table(tname='%s')" % (tname)
@@ -3457,6 +3763,10 @@ class SeqType (SqType):
       for e in (self.ext_list):
         f = fname + '/' + e.val.name
         table += self.out_item(f, e.val, e.optional, 'ASN1_NOT_EXTENSION_ROOT', ectx)
+    if hasattr(self, 'elt_list2'):
+      for e in (self.elt_list2):
+        f = fname + '/' + e.val.name
+        table += self.out_item(f, e.val, e.optional, ext, ectx)
     if (ectx.Ber()):
       if (ectx.NewBer()):
         table += "  { NULL, 0, 0, 0, NULL }\n};\n"
@@ -3526,7 +3836,7 @@ class SequenceOfType (SeqOfType):
   def eth_type_default_pars(self, ectx, tname):
     pars = Type.eth_type_default_pars(self, ectx, tname)
     (pars['MIN_VAL'], pars['MAX_VAL'], pars['EXT']) = self.eth_get_size_constr(ectx)
-    pars['TABLE'] = '%(TNAME)s_sequence_of'
+    pars['TABLE'] = '%(PROTOP)s%(TNAME)s_sequence_of'
     return pars
 
   def eth_type_default_body(self, ectx, tname):
@@ -3583,7 +3893,7 @@ class SetOfType (SeqOfType):
   def eth_type_default_pars(self, ectx, tname):
     pars = Type.eth_type_default_pars(self, ectx, tname)
     (pars['MIN_VAL'], pars['MAX_VAL'], pars['EXT']) = self.eth_get_size_constr(ectx)
-    pars['TABLE'] = '%(TNAME)s_set_of'
+    pars['TABLE'] = '%(PROTOP)s%(TNAME)s_set_of'
     return pars
 
   def eth_type_default_body(self, ectx, tname):
@@ -3676,6 +3986,9 @@ class SequenceType (SeqType):
     if hasattr(self, 'ext_list'):
         for e in (self.ext_list):
             e.val.eth_reg(ident, ectx, tstrip=1, parent=ident)
+    if hasattr(self, 'elt_list2'):
+        for e in (self.elt_list2):
+            e.val.eth_reg(ident, ectx, tstrip=1, parent=ident)
 
   def eth_need_tree(self):
     return True
@@ -3685,7 +3998,7 @@ class SequenceType (SeqType):
 
   def eth_type_default_pars(self, ectx, tname):
     pars = Type.eth_type_default_pars(self, ectx, tname)
-    pars['TABLE'] = '%(TNAME)s_sequence'
+    pars['TABLE'] = '%(PROTOP)s%(TNAME)s_sequence'
     return pars
 
   def eth_type_default_body(self, ectx, tname):
@@ -3730,7 +4043,7 @@ class SetType(SeqType):
 
   def eth_type_default_pars(self, ectx, tname):
     pars = Type.eth_type_default_pars(self, ectx, tname)
-    pars['TABLE'] = '%(TNAME)s_set'
+    pars['TABLE'] = '%(PROTOP)s%(TNAME)s_set'
     return pars
 
   def eth_type_default_body(self, ectx, tname):
@@ -3816,7 +4129,7 @@ class ChoiceType (Type):
     return ee
 
   def sel_req(self, ident, sel, ectx):
-    #print "sel_req(ident='%s', sel=%s)" % (ident, sel)
+    #print "sel_req(ident='%s', sel=%s)\n%s" % (ident, sel, str(self))
     ee = self.sel_item(ident, sel, ectx)
     if ee:
       ee.eth_reg(ident, ectx, tstrip=0, selflag=True)
@@ -3824,6 +4137,13 @@ class ChoiceType (Type):
   def eth_ftype(self, ectx):
     return ('FT_UINT32', 'BASE_DEC')
 
+  def eth_ftype_sel(self, sel, ectx):
+    ee = self.sel_item('', sel, ectx)
+    if ee:
+      return ee.eth_ftype(ectx)
+    else:
+      return ('FT_NONE', 'BASE_NONE')
+
   def eth_strings(self):
     return '$$'
 
@@ -3899,7 +4219,7 @@ class ChoiceType (Type):
 
   def eth_type_default_pars(self, ectx, tname):
     pars = Type.eth_type_default_pars(self, ectx, tname)
-    pars['TABLE'] = '%(TNAME)s_choice'
+    pars['TABLE'] = '%(PROTOP)s%(TNAME)s_choice'
     return pars
 
   def eth_type_default_table(self, ectx, tname):
@@ -4006,6 +4326,14 @@ class ChoiceType (Type):
       body = '#error Can not decode %s' % (tname)
     return body
    
+#--- ChoiceValue ----------------------------------------------------
+class ChoiceValue (Value):
+  def to_str(self, ectx):
+    return self.val.to_str(ectx)
+
+  def fld_obj_eq(self, other):
+    return isinstance(other, ChoiceValue) and (self.choice == other.choice) and (str(self.val.val) == str(other.val.val))
+
 #--- EnumeratedType -----------------------------------------------------------
 class EnumeratedType (Type):
   def to_python (self, ctx):
@@ -4101,7 +4429,7 @@ class EnumeratedType (Type):
     pars['EXT'] = ext
     pars['EXT_NUM'] = str(ext_num)
     if (map_table):
-      pars['TABLE'] = '%(TNAME)s_value_map'
+      pars['TABLE'] = '%(PROTOP)s%(TNAME)s_value_map'
     else:
       pars['TABLE'] = 'NULL'
     return pars
@@ -4128,6 +4456,36 @@ class EnumeratedType (Type):
       body = '#error Can not decode %s' % (tname)
     return body
 
+#--- EmbeddedPDVType -----------------------------------------------------------
+class EmbeddedPDVType (Type):
+  def eth_tname(self):
+    return 'EMBEDDED_PDV'
+
+  def eth_ftype(self, ectx):
+    return ('FT_NONE', 'BASE_NONE')
+
+  def GetTTag(self, ectx):
+    return ('BER_CLASS_UNI', 'BER_UNI_TAG_EMBEDDED_PDV')
+
+  def eth_type_default_pars(self, ectx, tname):
+    pars = Type.eth_type_default_pars(self, ectx, tname)
+    if ectx.default_embedded_pdv_cb:
+      pars['TYPE_REF_FN'] = ectx.default_embedded_pdv_cb
+    else:
+      pars['TYPE_REF_FN'] = 'NULL'
+    return pars
+
+  def eth_type_default_body(self, ectx, tname):
+    if (ectx.Ber()):  
+      body = ectx.eth_fn_call('dissect_%(ER)s_EmbeddedPDV_Type', ret='offset',
+                              par=(('%(IMPLICIT_TAG)s', '%(TREE)s', '%(TVB)s', '%(OFFSET)s', '%(ACTX)s', '%(HF_INDEX)s', '%(TYPE_REF_FN)s',),))
+    elif (ectx.Per()):
+      body = ectx.eth_fn_call('dissect_%(ER)s_embedded_pdv', ret='offset',
+                              par=(('%(TVB)s', '%(OFFSET)s', '%(ACTX)s', '%(TREE)s', '%(HF_INDEX)s', '%(TYPE_REF_FN)s',),))
+    else:
+      body = '#error Can not decode %s' % (tname)
+    return body
+
 #--- ExternalType -----------------------------------------------------------
 class ExternalType (Type):
   def eth_tname(self):
@@ -4209,6 +4567,35 @@ class OpenType (Type):
       body = '#error Can not decode %s' % (tname)
     return body
 
+#--- InstanceOfType -----------------------------------------------------------
+class InstanceOfType (Type):
+  def eth_tname(self):
+    return 'INSTANCE_OF'
+
+  def eth_ftype(self, ectx):
+    return ('FT_NONE', 'BASE_NONE')
+
+  def GetTTag(self, ectx):
+    return ('BER_CLASS_UNI', 'BER_UNI_TAG_EXTERNAL')
+
+  def eth_type_default_pars(self, ectx, tname):
+    pars = Type.eth_type_default_pars(self, ectx, tname)
+    if ectx.default_external_type_cb:
+      pars['TYPE_REF_FN'] = ectx.default_external_type_cb
+    else:
+      pars['TYPE_REF_FN'] = 'NULL'
+    return pars
+
+  def eth_type_default_body(self, ectx, tname):
+    if (ectx.Ber()):  
+      body = ectx.eth_fn_call('dissect_%(ER)s_external_type', ret='offset',
+                              par=(('%(IMPLICIT_TAG)s', '%(TREE)s', '%(TVB)s', '%(OFFSET)s', '%(ACTX)s', '%(HF_INDEX)s', '%(TYPE_REF_FN)s',),))
+    elif (ectx.Per()):
+      body = '#error Can not decode %s' % (tname)
+    else:
+      body = '#error Can not decode %s' % (tname)
+    return body
+
 #--- AnyType -----------------------------------------------------------
 class AnyType (Type):
   def to_python (self, ctx):
@@ -4250,6 +4637,11 @@ class NullType (Type):
       body = '#error Can not decode %s' % (tname)
     return body
 
+#--- NullValue ----------------------------------------------------
+class NullValue (Value):
+  def to_str(self, ectx):
+    return 'NULL'
+
 #--- RealType -----------------------------------------------------------------
 class RealType (Type):
   def to_python (self, ctx):
@@ -4742,7 +5134,7 @@ class BitStringType (Type):
       pars['ETT_INDEX'] = '-1'
     pars['TABLE'] = 'NULL'
     if self.eth_named_bits():
-      pars['TABLE'] = '%(TNAME)s_bits'
+      pars['TABLE'] = '%(PROTOP)s%(TNAME)s_bits'
     if self.HasContentsConstraint():
       pars['FN_VARIANT'] = ectx.default_containing_variant
       t = self.constr.GetContents(ectx)
@@ -4817,6 +5209,53 @@ class BStringValue (Value):
       vv += bstring_tab[v[i:i+4]]
     return vv
 
+#--- HStringValue ------------------------------------------------------------
+class HStringValue (Value):
+  def to_str(self, ectx):
+    vv = '0x'
+    vv += self.val[1:-2]
+    return vv
+
+#--- FieldSpec ----------------------------------------------------------------
+class FieldSpec (Node):
+  def __init__(self,*args, **kw) :
+    self.name = None
+    Node.__init__ (self,*args, **kw)
+
+  def SetName(self, name):
+    self.name = name
+
+  def get_repr(self):
+    return ['#UNSUPPORTED_' + self.type]
+
+  def fld_repr(self):
+    repr = [self.name]
+    repr.extend(self.get_repr())
+    return repr
+
+class TypeFieldSpec (FieldSpec):
+  def get_repr(self):
+    return []
+
+class FixedTypeValueFieldSpec (FieldSpec):
+  def get_repr(self):
+    if isinstance(self.typ, Type_Ref):
+      repr = ['TypeReference', self.typ.val]
+    else:
+      repr = [self.typ.type]
+    return repr
+
+class FixedTypeValueSetFieldSpec (FieldSpec):
+  pass
+
+class ObjectFieldSpec (FieldSpec):
+  def get_repr(self):
+    return ['ClassReference', self.cls]
+
+class ObjectSetFieldSpec (FieldSpec):
+  def get_repr(self):
+    return ['ClassReference', self.cls]
+
 #==============================================================================
     
 def p_module_list_1 (t):
@@ -4846,7 +5285,7 @@ def p_identifier (t):
 # 11.4 Value references
 def p_valuereference (t):
   'valuereference : LCASE_IDENT'
-  t[0] = t[1]
+  t[0] = Value_Ref(val=t[1])
 
 # 11.5 Module references
 def p_modulereference (t):
@@ -4926,11 +5365,18 @@ def p_exp_sym_list_2 (t):
     
 
 def p_Imports_1 (t):
-  'Imports : IMPORTS SymbolsImported SEMICOLON'
-  t[0] = t[2]
+  'Imports : importsbegin IMPORTS SymbolsImported SEMICOLON'
+  t[0] = t[3]
   global lcase_ident_assigned
   lcase_ident_assigned = {}
 
+def p_importsbegin (t):
+  'importsbegin : '
+  global lcase_ident_assigned
+  global g_conform
+  lcase_ident_assigned = {}
+  lcase_ident_assigned.update(g_conform.use_item('ASSIGNED_ID', 'OBJECT_IDENTIFIER'))
+
 def p_Imports_2 (t):
   'Imports : '
   t[0] = []
@@ -4955,7 +5401,7 @@ def p_SymbolsFromModule (t):
   'SymbolsFromModule : SymbolList FROM GlobalModuleReference'
   t[0] = Node ('SymbolList', symbol_list = t[1], module = t[3])
   for s in (t[0].symbol_list): 
-    if (isinstance(s, str) and s[0].islower()): lcase_ident_assigned[s] = t[3]
+    if (isinstance(s, Value_Ref)): lcase_ident_assigned[s.val] = t[3]
   if t[0].module.val == 'Remote-Operations-Information-Objects':
     for i in range(len(t[0].symbol_list)):
       s = t[0].symbol_list[i]
@@ -4993,12 +5439,16 @@ def p_Symbol (t):
             | ParameterizedReference'''
   t[0] = t[1]
 
-def p_Reference (t):
+def p_Reference_1 (t):
   '''Reference : type_ref
                | valuereference
-               | objectclassreference'''
+               | objectclassreference '''
   t[0] = t[1]
 
+def p_Reference_2 (t):
+  '''Reference : LCASE_IDENT_ASSIGNED'''
+  t[0] = Value_Ref (val=t[1])
+
 def p_AssignmentList_1 (t):
   'AssignmentList : AssignmentList Assignment'
   t[0] = t[1] + [t[2]]
@@ -5014,11 +5464,12 @@ def p_AssignmentList_3 (t):
 def p_Assignment (t):
   '''Assignment : TypeAssignment
                 | ValueAssignment
+                | ValueSetTypeAssignment
                 | ObjectClassAssignment
                 | ObjectAssignment
                 | ObjectSetAssignment
-                | ParameterizedTypeAssignment
-                | pyquote'''
+                | ParameterizedAssignment
+                | pyquote '''
   t[0] = t[1]
 
 
@@ -5033,7 +5484,7 @@ def p_DefinedType (t):
 
 def p_DefinedValue(t):
   '''DefinedValue : ExternalValueReference
-                  | identifier'''
+                  | valuereference'''
   t[0] = t[1]
 
 # 13.6
@@ -5056,8 +5507,8 @@ def p_TypeAssignment (t):
 
 # 15.2
 def p_ValueAssignment (t):
-  'ValueAssignment : valuereference ValueType ASSIGNMENT Value'
-  t[0] = value_assign (ident = t[1], typ = t[2], val = t[4])
+  'ValueAssignment : LCASE_IDENT ValueType ASSIGNMENT Value'
+  t[0] = ValueAssignment(ident = t[1], typ = t[2], val = t[4])
 
 # only "simple" types are supported to simplify grammer
 def p_ValueType (t):
@@ -5066,10 +5517,20 @@ def p_ValueType (t):
                | IntegerType
                | ObjectIdentifierType
                | OctetStringType
-               | RealType'''
+               | RealType '''
 
   t[0] = t[1]
 
+# 15.6
+def p_ValueSetTypeAssignment (t):
+  'ValueSetTypeAssignment : UCASE_IDENT ValueType ASSIGNMENT ValueSet'
+  t[0] = Node('ValueSetTypeAssignment', name=t[1], typ=t[2], val=t[4])
+
+# 15.7
+def p_ValueSet (t):
+  'ValueSet : lbraceignore rbraceignore'
+  t[0] = None
+
 
 # 16 Definition of types and values -------------------------------------------
 
@@ -5087,8 +5548,10 @@ def p_BuiltinType (t):
                  | BooleanType
                  | CharacterStringType
                  | ChoiceType
+                 | EmbeddedPDVType
                  | EnumeratedType
                  | ExternalType
+                 | InstanceOfType
                  | IntegerType
                  | NullType
                  | ObjectClassFieldType
@@ -5118,12 +5581,14 @@ def p_NamedType (t):
 # 16.7
 def p_Value (t):
   '''Value : BuiltinValue
-           | ReferencedValue'''
+           | ReferencedValue
+           | ObjectClassFieldValue'''
   t[0] = t[1]
 
 # 16.9
 def p_BuiltinValue (t):
   '''BuiltinValue : BooleanValue
+                  | ChoiceValue
                   | IntegerValue
                   | ObjectIdentifierValue
                   | RealValue
@@ -5135,7 +5600,8 @@ def p_BuiltinValue (t):
 
 # 16.11
 def p_ReferencedValue (t):
-  '''ReferencedValue : DefinedValue'''
+  '''ReferencedValue : DefinedValue
+                     | ValueFromObject'''
   t[0] = t[1]
 
 # 16.13
@@ -5288,13 +5754,13 @@ def p_OctetStringType (t):
 
 # 23.1
 def p_NullType (t):
-    'NullType : NULL'
-    t[0] = NullType ()
+  'NullType : NULL'
+  t[0] = NullType ()
 
 # 23.3
-#def p_NullValue (t):
-#    'NullValue : NULL'
-#    t[0] = t[1]
+def p_NullValue (t):
+  'NullValue : NULL'
+  t[0] = NullValue ()
 
 
 # 24 Notation for sequence types ----------------------------------------------
@@ -5306,10 +5772,11 @@ def p_SequenceType_1 (t):
 
 def p_SequenceType_2 (t):
   'SequenceType : SEQUENCE LBRACE ComponentTypeLists RBRACE'
+  t[0] = SequenceType (elt_list = t[3]['elt_list'])
   if t[3].has_key('ext_list'):
-      t[0] = SequenceType (elt_list = t[3]['elt_list'], ext_list = t[3]['ext_list'])
-  else:
-      t[0] = SequenceType (elt_list = t[3]['elt_list'])
+    t[0].ext_list = t[3]['ext_list']
+  if t[3].has_key('elt_list2'):
+    t[0].ext_list = t[3]['elt_list2']
 
 def p_ExtensionAndException_1 (t):
   'ExtensionAndException : ELLIPSIS'
@@ -5328,32 +5795,52 @@ def p_ComponentTypeLists_1 (t):
   t[0] = {'elt_list' : t[1]}
 
 def p_ComponentTypeLists_2 (t):
-    'ComponentTypeLists : ComponentTypeList COMMA ExtensionAndException extension_additions OptionalExtensionMarker'
-    t[0] = {'elt_list' : t[1], 'ext_list' : t[4]}
+    'ComponentTypeLists : ComponentTypeList COMMA ExtensionAndException OptionalExtensionMarker'
+    t[0] = {'elt_list' : t[1], 'ext_list' : []}
 
 def p_ComponentTypeLists_3 (t):
-    'ComponentTypeLists : ExtensionAndException extension_additions OptionalExtensionMarker'
+    'ComponentTypeLists : ComponentTypeList COMMA ExtensionAndException ExtensionAdditionList OptionalExtensionMarker'
+    t[0] = {'elt_list' : t[1], 'ext_list' : t[4]}
+
+def p_ComponentTypeLists_4 (t):
+    'ComponentTypeLists : ComponentTypeList COMMA ExtensionAndException ExtensionEndMarker COMMA ComponentTypeList'
+    t[0] = {'elt_list' : t[1], 'ext_list' : [], 'elt_list2' : t[6]}
+
+def p_ComponentTypeLists_5 (t):
+    'ComponentTypeLists : ComponentTypeList COMMA ExtensionAndException ExtensionAdditionList ExtensionEndMarker COMMA ComponentTypeList'
+    t[0] = {'elt_list' : t[1], 'ext_list' : t[4], 'elt_list2' : t[7]}
+
+def p_ComponentTypeLists_6 (t):
+    'ComponentTypeLists : ExtensionAndException OptionalExtensionMarker'
+    t[0] = {'elt_list' : [], 'ext_list' : []}
+
+def p_ComponentTypeLists_7 (t):
+    'ComponentTypeLists : ExtensionAndException ExtensionAdditionList OptionalExtensionMarker'
     t[0] = {'elt_list' : [], 'ext_list' : t[2]}
 
 #def p_RootComponentTypeList (t):
 #    'RootComponentTypeList : ComponentTypeList'
 #    t[0] = t[1]
 
-def p_extension_additions_1 (t):
-    'extension_additions : extension_addition_list'
-    t[0] = t[1]
+def p_ExtensionEndMarker (t):
+  'ExtensionEndMarker : COMMA ELLIPSIS'
+  pass
 
-def p_extension_additions_2 (t):
-    'extension_additions : '
-    t[0] = []
+#def p_extension_additions_1 (t):
+#    'extension_additions : extension_addition_list'
+#    t[0] = t[1]
 
-def p_extension_addition_list_1 (t):
-    'extension_addition_list : COMMA extension_addition'
-    t[0] = [t[2]]
+#def p_extension_additions_2 (t):
+#    'extension_additions : '
+#    t[0] = []
 
-def p_extension_addition_list_2 (t):
-    'extension_addition_list : extension_addition_list COMMA extension_addition'
-    t[0] = t[1] + [t[3]]
+def p_ExtensionAdditionList_1 (t):
+  'ExtensionAdditionList : COMMA extension_addition'
+  t[0] = [t[2]]
+
+def p_ExtensionAdditionList_2 (t):
+  'ExtensionAdditionList : ExtensionAdditionList COMMA extension_addition'
+  t[0] = t[1] + [t[3]]
 
 def p_extension_addition_1 (t):
     'extension_addition : ComponentType'
@@ -5386,11 +5873,13 @@ def p_ComponentType_4 (t):
 def p_DefaultValue_1 (t):
   '''DefaultValue : ReferencedValue 
                   | BooleanValue
+                  | ChoiceValue
                   | IntegerValue
                   | RealValue
                   | hex_string
                   | binary_string
-                  | char_string'''
+                  | char_string
+                  | ObjectClassFieldValue'''
   t[0] = t[1]
 
 def p_DefaultValue_2 (t):
@@ -5429,16 +5918,15 @@ def p_SequenceOfType (t):
 
 # 26.1
 def p_SetType_1 (t):
-    'SetType : SET LBRACE RBRACE'
-    if t[3].has_key('ext_list'):
-        t[0] = SetType (elt_list = [])
+  'SetType : SET LBRACE RBRACE'
+  t[0] = SetType (elt_list = [])
 
 def p_SetType_2 (t):
-    'SetType : SET LBRACE ComponentTypeLists RBRACE'
-    if t[3].has_key('ext_list'):
-        t[0] = SetType (elt_list = t[3]['elt_list'], ext_list = t[3]['ext_list'])
-    else:
-        t[0] = SetType (elt_list = t[3]['elt_list'])
+  'SetType : SET LBRACE ComponentTypeLists RBRACE'
+  if t[3].has_key('ext_list'):
+    t[0] = SetType (elt_list = t[3]['elt_list'], ext_list = t[3]['ext_list'])
+  else:
+    t[0] = SetType (elt_list = t[3]['elt_list'])
 
 
 # 27 Notation for set-of types ------------------------------------------------
@@ -5495,6 +5983,15 @@ def p_alternative_type_list_2 (t):
     'alternative_type_list : alternative_type_list COMMA NamedType'
     t[0] = t[1] + [t[3]]
 
+# 28.10
+def p_ChoiceValue_1 (t):
+  '''ChoiceValue : identifier COLON Value
+                 | identifier COLON NullValue '''
+  val = t[3]
+  if not isinstance(val, Value):
+    val = Value(val=val)
+  t[0] = ChoiceValue (choice = t[1], val = val)
+
 # 29 Notation for selection types
 
 # 29.1
@@ -5586,6 +6083,12 @@ def p_NameAndNumberForm (t):
   '''NameAndNumberForm : LCASE_IDENT_ASSIGNED LPAREN NumberForm RPAREN
                        | LCASE_IDENT LPAREN NumberForm RPAREN'''
   t[0] = Node('name_and_number', ident = t[1], number = t[3])
+  
+# 33 Notation for the embedded-pdv type -------------------------------------------
+# 33.1
+def p_EmbeddedPDVType (t):
+  'EmbeddedPDVType : EMBEDDED PDV'
+  t[0] = EmbeddedPDVType()
 
 # 34 Notation for the external type -------------------------------------------
 
@@ -5773,7 +6276,7 @@ def p_IElems (t):
   t[0] = t[1]
 
 def p_IntersectionElements (t):
-  'IntersectionElements : SubtypeElements'
+  'IntersectionElements : Elements'
   t[0] = t[1]
 
 def p_UnionMark (t):
@@ -5784,6 +6287,15 @@ def p_IntersectionMark (t):
   '''IntersectionMark : CIRCUMFLEX
                       | INTERSECTION'''
 
+# 46.5
+def p_Elements_1 (t):
+  'Elements : SubtypeElements'
+  t[0] = t[1]
+
+def p_Elements_2 (t):
+  'Elements : LPAREN ElementSetSpec RPAREN'
+  t[0] = t[2]
+
 # 47 Subtype elements ---------------------------------------------------------
 
 # 47.1 General
@@ -5939,9 +6451,19 @@ def p_PatternConstraint (t):
 # 49 The exception identifier
 
 # 49.4
-def p_ExceptionSpec (t):
-    'ExceptionSpec : '
-    pass
+def p_ExceptionSpec_1 (t):
+  'ExceptionSpec : EXCLAMATION ExceptionIdentification'
+  pass
+
+def p_ExceptionSpec_2 (t):
+  'ExceptionSpec : '
+  pass
+
+def p_ExceptionIdentification (t):
+  '''ExceptionIdentification : SignedNumber
+                             | DefinedValue
+                             | Type COLON Value '''
+  pass
 
 #  /*-----------------------------------------------------------------------*/
 #  /* Value Notation Productions */
@@ -5950,12 +6472,12 @@ def p_ExceptionSpec (t):
 
 
 def p_binary_string (t):
-    'binary_string : BSTRING'
-    t[0] = BStringValue(val = t[1])
+  'binary_string : BSTRING'
+  t[0] = BStringValue(val = t[1])
 
 def p_hex_string (t):
-    'hex_string : HSTRING'
-    t[0] = t[1]
+  'hex_string : HSTRING'
+  t[0] = HStringValue(val = t[1])
 
 def p_char_string (t):
     'char_string : QSTRING'
@@ -5984,21 +6506,28 @@ def p_objectreference (t):
 
 # 7.3 Information object set references
 
-def p_objectsetreference (t):
-  'objectsetreference : UCASE_IDENT'
-  t[0] = t[1]
+#def p_objectsetreference (t):
+#  'objectsetreference : UCASE_IDENT'
+#  t[0] = t[1]
 
 # 7.4 Type field references
-
-def p_typefieldreference (t):
-  'typefieldreference : AMPERSAND UCASE_IDENT'
-  t[0] = t[2]
-
+# ucasefieldreference
 # 7.5 Value field references
-
-def p_valuefieldreference (t):
-  'valuefieldreference : AMPERSAND LCASE_IDENT'
-  t[0] = t[2]
+# lcasefieldreference
+# 7.6 Value set field references
+# ucasefieldreference
+# 7.7 Object field references
+# lcasefieldreference
+# 7.8 Object set field references
+# ucasefieldreference
+
+def p_ucasefieldreference (t):
+  'ucasefieldreference : AMPERSAND UCASE_IDENT'
+  t[0] = '&' + t[2]
+
+def p_lcasefieldreference (t):
+  'lcasefieldreference : AMPERSAND LCASE_IDENT'
+  t[0] = '&' + t[2]
 
 # 8 Referencing definitions
 
@@ -6007,6 +6536,8 @@ def p_DefinedObjectClass (t):
   '''DefinedObjectClass : objectclassreference
                         | UsefulObjectClassReference'''
   t[0] = t[1]
+  global obj_class
+  obj_class = t[0].val
 
 def p_DefinedObject (t):
   '''DefinedObject : objectreference'''
@@ -6016,7 +6547,7 @@ def p_DefinedObject (t):
 def p_UsefulObjectClassReference (t):
   '''UsefulObjectClassReference : TYPE_IDENTIFIER 
                                 | ABSTRACT_SYNTAX'''
-  t[0] = t[1]
+  t[0] = Class_Ref(val=t[1])
 
 # 9 Information object class definition and assignment
 
@@ -6026,51 +6557,240 @@ def p_ObjectClassAssignment (t):
                            | UCASE_IDENT ASSIGNMENT ObjectClass'''
   t[0] = t[3]
   t[0].SetName(t[1])
+  if isinstance(t[0], ObjectClassDefn):
+    t[0].reg_types()
 
 # 9.2
 def p_ObjectClass (t):
-  '''ObjectClass : ObjectClassDefn'''
+  '''ObjectClass : DefinedObjectClass
+                 | ObjectClassDefn'''
   t[0] = t[1]
 
 # 9.3
 def p_ObjectClassDefn (t):
-  '''ObjectClassDefn : CLASS lbraceignore rbraceignore
-                     | CLASS lbraceignore rbraceignore WithSyntaxSpec'''
-  t[0] = ObjectClass()
+  '''ObjectClassDefn : CLASS LBRACE FieldSpecs RBRACE
+                     | CLASS LBRACE FieldSpecs RBRACE WithSyntaxSpec'''
+  t[0] = ObjectClassDefn(fields = t[3])
+
+def p_FieldSpecs_1 (t):
+  'FieldSpecs : FieldSpec'
+  t[0] = [t[1]]
+
+def p_FieldSpecs_2 (t):
+  'FieldSpecs : FieldSpecs COMMA FieldSpec'
+  t[0] = t[1] + [t[3]]
 
 def p_WithSyntaxSpec (t):
   'WithSyntaxSpec : WITH SYNTAX lbraceignore rbraceignore'
   t[0] = None
 
-# 9.14
-def p_FieldName (t):
-  '''FieldName : typefieldreference
-               | valuefieldreference'''
+# 9.4
+def p_FieldSpec (t):
+  '''FieldSpec : TypeFieldSpec
+               | FixedTypeValueFieldSpec
+               | FixedTypeValueSetFieldSpec
+               | ObjectFieldSpec
+               | ObjectSetFieldSpec '''
+  t[0] = t[1]
+
+# 9.5
+def p_TypeFieldSpec (t):
+  '''TypeFieldSpec : ucasefieldreference
+                   | ucasefieldreference TypeOptionalitySpec '''
+  t[0] = TypeFieldSpec()
+  t[0].SetName(t[1])
+
+def p_TypeOptionalitySpec_1 (t):
+  'TypeOptionalitySpec ::= OPTIONAL'
+  pass
+
+def p_TypeOptionalitySpec_2 (t):
+  'TypeOptionalitySpec ::= DEFAULT Type'
+  pass
+
+# 9.6
+def p_FixedTypeValueFieldSpec (t):
+  '''FixedTypeValueFieldSpec : lcasefieldreference Type
+                             | lcasefieldreference Type UNIQUE
+                             | lcasefieldreference Type ValueOptionalitySpec
+                             | lcasefieldreference Type UNIQUE ValueOptionalitySpec '''
+  t[0] = FixedTypeValueFieldSpec(typ = t[2])
+  t[0].SetName(t[1])
+
+def p_ValueOptionalitySpec_1 (t):
+  'ValueOptionalitySpec ::= OPTIONAL'
+  pass
+
+def p_ValueOptionalitySpec_2 (t):
+  'ValueOptionalitySpec ::= DEFAULT Value'
+  pass
+
+# 9.9
+def p_FixedTypeValueSetFieldSpec (t):
+  '''FixedTypeValueSetFieldSpec : ucasefieldreference Type
+                                | ucasefieldreference Type ValueSetOptionalitySpec '''
+  t[0] = FixedTypeValueSetFieldSpec()
+  t[0].SetName(t[1])
+
+def p_ValueSetOptionalitySpec_1 (t):
+  'ValueSetOptionalitySpec ::= OPTIONAL'
+  pass
+
+def p_ValueSetOptionalitySpec_2 (t):
+  'ValueSetOptionalitySpec ::= DEFAULT ValueSet'
+  pass
+
+# 9.11
+def p_ObjectFieldSpec (t):
+  '''ObjectFieldSpec : lcasefieldreference DefinedObjectClass
+                     | lcasefieldreference DefinedObjectClass ObjectOptionalitySpec '''
+  t[0] = ObjectFieldSpec(cls=t[2])
+  t[0].SetName(t[1])
+  global obj_class
+  obj_class = None
+
+def p_ObjectOptionalitySpec_1 (t):
+  'ObjectOptionalitySpec ::= OPTIONAL'
+  pass
+
+def p_ObjectOptionalitySpec_2 (t):
+  'ObjectOptionalitySpec ::= DEFAULT Object'
+  pass
+
+# 9.12
+def p_ObjectSetFieldSpec (t):
+  '''ObjectSetFieldSpec : ucasefieldreference DefinedObjectClass
+                        | ucasefieldreference DefinedObjectClass ObjectSetOptionalitySpec '''
+  t[0] = ObjectSetFieldSpec(cls=t[2])
+  t[0].SetName(t[1])
+
+def p_ObjectSetOptionalitySpec_1 (t):
+  'ObjectSetOptionalitySpec ::= OPTIONAL'
+  pass
+
+def p_ObjectSetOptionalitySpec_2 (t):
+  'ObjectSetOptionalitySpec ::= DEFAULT ObjectSet'
+  pass
+
+# 9.13
+def p_PrimitiveFieldName (t):
+  '''PrimitiveFieldName : ucasefieldreference
+                        | lcasefieldreference '''
   t[0] = t[1]
 
+# 9.13
+def p_FieldName_1 (t):
+  'FieldName : PrimitiveFieldName'
+  t[0] = t[1]
+
+def p_FieldName_2 (t):
+  'FieldName : FieldName DOT PrimitiveFieldName'
+  t[0] = t[1] + '.' + t[3]
+
 # 11 Information object definition and assignment
 
 # 11.1
 def p_ObjectAssignment (t):
-  'ObjectAssignment : objectreference CLASS_IDENT ASSIGNMENT Object'
-  t[0] = Node('ObjectAssignment', name=t[1], cls=t[2], val=t[4])
+  'ObjectAssignment : objectreference DefinedObjectClass ASSIGNMENT Object'
+  t[0] = ObjectAssignment (ident = t[1], cls=t[2].val, val=t[4])
+  global obj_class
+  obj_class = None
 
 # 11.3
 def p_Object (t):
   '''Object : DefinedObject
-            | ObjectDefn'''
+            | ObjectDefn
+            | ParameterizedObject'''
   t[0] = t[1]
 
 # 11.4
 def p_ObjectDefn (t):
-  'ObjectDefn : lbraceignore rbraceignore'
-  t[0] = None
+  'ObjectDefn : lbraceobject bodyobject rbraceobject'
+  t[0] = t[2]
+
+#  {...} block of object definition
+def p_lbraceobject(t):
+  'lbraceobject : braceobjectbegin LBRACE'
+  t[0] = t[1]
+
+def p_braceobjectbegin(t):
+  'braceobjectbegin : '
+  global lexer
+  global obj_class
+  if set_class_syntax(obj_class):
+    state = 'INITIAL'
+  else:
+    lexer.level = 1
+    state = 'braceignore'
+  lexer.push_state(state)
+
+def p_rbraceobject(t):
+  'rbraceobject : braceobjectend RBRACE'
+  t[0] = t[2]
+
+def p_braceobjectend(t):
+  'braceobjectend : '
+  global lexer
+  lexer.pop_state()
+  set_class_syntax(None)
+
+def p_bodyobject_1 (t):
+  'bodyobject : '
+  t[0] = { }
+
+def p_bodyobject_2 (t):
+  'bodyobject : cls_syntax_list'
+  t[0] = t[1]
+
+def p_cls_syntax_list_1 (t):
+  'cls_syntax_list : cls_syntax_list cls_syntax'
+  t[0] = t[1]
+  t[0].update(t[2])
+
+def p_cls_syntax_list_2 (t):
+  'cls_syntax_list : cls_syntax'
+  t[0] = t[1]
+
+# X.681
+def p_cls_syntax_1 (t):
+  'cls_syntax : Type IDENTIFIED BY Value'
+  t[0] = { get_class_fieled(' ') : t[1], get_class_fieled(' '.join((t[2], t[3]))) : t[4] }
+
+def p_cls_syntax_2 (t):
+  'cls_syntax : HAS PROPERTY Value'
+  t[0] = { get_class_fieled(' '.join(t[1:-1])) : t[-1:][0] }
+
+# X.880
+def p_cls_syntax_3 (t):
+  '''cls_syntax : ERRORS ObjectSet
+                 | LINKED ObjectSet
+                 | RETURN RESULT BooleanValue 
+                 | SYNCHRONOUS BooleanValue
+                 | INVOKE PRIORITY Value 
+                 | RESULT_PRIORITY Value 
+                 | PRIORITY Value 
+                 | ALWAYS RESPONDS BooleanValue
+                 | IDEMPOTENT BooleanValue '''
+  t[0] = { get_class_fieled(' '.join(t[1:-1])) : t[-1:][0] }
+
+def p_cls_syntax_4 (t):
+  '''cls_syntax : ARGUMENT Type
+                 | RESULT Type
+                 | PARAMETER Type
+                 | CODE Value '''
+  t[0] = { get_class_fieled(t[1]) : t[2] }
+
+def p_cls_syntax_5 (t):
+  '''cls_syntax : ARGUMENT Type OPTIONAL BooleanValue
+                 | RESULT Type OPTIONAL BooleanValue
+                 | PARAMETER Type OPTIONAL BooleanValue '''
+  t[0] = { get_class_fieled(t[1]) : t[2], get_class_fieled(' '.join((t[1], t[3]))) : t[4] }
 
 # 12 Information object set definition and assignment
 
 # 12.1
 def p_ObjectSetAssignment (t):
-  'ObjectSetAssignment : objectsetreference CLASS_IDENT ASSIGNMENT ObjectSet'
+  'ObjectSetAssignment : UCASE_IDENT CLASS_IDENT ASSIGNMENT ObjectSet'
   t[0] = Node('ObjectSetAssignment', name=t[1], cls=t[2], val=t[4])
 
 # 12.3
@@ -6085,21 +6805,54 @@ def p_ObjectClassFieldType (t):
   'ObjectClassFieldType : DefinedObjectClass DOT FieldName'
   t[0] = get_type_from_class(t[1], t[3])
 
+# 14.6
+def p_ObjectClassFieldValue (t):
+  '''ObjectClassFieldValue : OpenTypeFieldVal'''
+  t[0] = t[1]
+
+def p_OpenTypeFieldVal (t):
+  '''OpenTypeFieldVal : Type COLON Value
+                      | NullType COLON NullValue'''
+  t[0] = t[3]
+
+
+# 15 Information from objects -------------------------------------------------
+
+# 15.1
+
+def p_ValueFromObject (t):
+  'ValueFromObject : LCASE_IDENT DOT FieldName'
+  t[0] = t[1] + '.' + t[3]
+
+
+# Annex C - The instance-of type ----------------------------------------------
+
+# C.2
+def p_InstanceOfType (t):
+  'InstanceOfType : INSTANCE OF DefinedObjectClass'
+  t[0] = InstanceOfType()
+
+
+# ---  tables ---
+
 useful_object_class_types = {
   # Annex A
-  'TYPE-IDENTIFIER/id'   : lambda : ObjectIdentifierType(),
-  'TYPE-IDENTIFIER/Type' : lambda : OpenType(),
+  'TYPE-IDENTIFIER.&id'   : lambda : ObjectIdentifierType(),
+  'TYPE-IDENTIFIER.&Type' : lambda : OpenType(),
   # Annex B
-  'ABSTRACT-SYNTAX/id'       : lambda : ObjectIdentifierType(),
-  'ABSTRACT-SYNTAX/Type'     : lambda : OpenType(),
-  'ABSTRACT-SYNTAX/property' : lambda : BitStringType(),
+  'ABSTRACT-SYNTAX.&id'       : lambda : ObjectIdentifierType(),
+  'ABSTRACT-SYNTAX.&Type'     : lambda : OpenType(),
+  'ABSTRACT-SYNTAX.&property' : lambda : BitStringType(),
 }
 
 object_class_types = { }
 
 object_class_typerefs = { }
 
+object_class_classrefs = { }
+
 class_types_creator = {
+  'BooleanType'          : lambda : BooleanType(),
   'IntegerType'          : lambda : IntegerType(),
   'ObjectIdentifierType' : lambda : ObjectIdentifierType(),
   'OpenType'             : lambda : OpenType(),
@@ -6107,6 +6860,71 @@ class_types_creator = {
 
 class_names = { }
 
+x681_syntaxes = {
+  'TYPE-IDENTIFIER' : {
+    ' '             : '&Type',
+    'IDENTIFIED'    : 'IDENTIFIED',
+    #'BY'            : 'BY',         
+    'IDENTIFIED BY' : '&id',         
+  },
+  'ABSTRACT-SYNTAX' : {
+    ' '             : '&Type',
+    'IDENTIFIED'    : 'IDENTIFIED',
+    #'BY'            : 'BY',         
+    'IDENTIFIED BY' : '&id',         
+    'HAS'           : 'HAS',
+    'PROPERTY'      : 'PROPERTY',         
+    'HAS PROPERTY'  : '&property',         
+  },
+}
+
+class_syntaxes_enabled = { 
+  'TYPE-IDENTIFIER' : True,
+  'ABSTRACT-SYNTAX' : True,
+}
+
+class_syntaxes = {
+  'TYPE-IDENTIFIER' : x681_syntaxes['TYPE-IDENTIFIER'],
+  'ABSTRACT-SYNTAX' : x681_syntaxes['ABSTRACT-SYNTAX'],
+}
+
+class_current_syntax = None
+
+def get_syntax_tokens(syntaxes):
+  tokens = { }
+  for s in (syntaxes):
+    for k in (syntaxes[s].keys()):
+      if k.find(' ') < 0:
+        tokens[k] = k
+        tokens[k] = tokens[k].replace('-', '_')
+  return tokens.values()
+
+tokens = tokens + get_syntax_tokens(x681_syntaxes)
+
+def set_class_syntax(syntax):
+  global class_syntaxes_enabled
+  global class_current_syntax
+  #print "set_class_syntax", syntax, class_current_syntax
+  if class_syntaxes_enabled.get(syntax, False):
+    class_current_syntax = syntax
+    return True
+  else:
+    class_current_syntax = None
+    return False
+
+def is_class_syntax(name):
+  global class_syntaxes
+  global class_current_syntax
+  #print "is_class_syntax", name, class_current_syntax
+  if not class_current_syntax:
+    return False
+  return class_syntaxes[class_current_syntax].has_key(name)
+
+def get_class_fieled(name):
+  if not class_current_syntax:
+    return None
+  return class_syntaxes[class_current_syntax][name]
+
 def is_class_ident(name):
   return class_names.has_key(name)
 
@@ -6114,10 +6932,14 @@ def add_class_ident(name):
   class_names[name] = name
 
 def get_type_from_class(cls, fld):
+  flds = fld.split('.')
   if (isinstance(cls, Class_Ref)):
-    key = cls.val + '/' + fld
+    key = cls.val + '.' + flds[0]
   else:
-    key = cls + '/' + fld
+    key = cls + '.' + flds[0]
+
+  if object_class_classrefs.has_key(key):
+    return get_type_from_class(object_class_classrefs[key], '.'.join(flds[1:]))
 
   if object_class_typerefs.has_key(key):
     return Type_Ref(val=object_class_typerefs[key])
@@ -6127,10 +6949,39 @@ def get_type_from_class(cls, fld):
   creator = object_class_types.get(key, creator)
   return creator()
 
-def set_type_to_class(cls, fld, typename, typeref = None):
-  key = cls + '/' + fld
-  if object_class_types.has_key(key): return False
-  if object_class_typerefs.has_key(key): return False
+def set_type_to_class(cls, fld, pars):
+  key = cls + '.' + fld
+  typename = 'OpenType'
+  if (len(pars) > 0):
+    typename = pars[0]
+  else:
+    pars.append(typename)
+  typeref = None
+  if (len(pars) > 1):
+    if (isinstance(pars[1], Class_Ref)):
+      pars[1] = pars[1].val
+    typeref = pars[1]
+
+  msg = None
+  if object_class_types.has_key(key): 
+    msg = object_class_types[key]().type
+  if object_class_typerefs.has_key(key):
+    msg = "TypeReference " + object_class_typerefs[key]
+  if object_class_classrefs.has_key(key):
+    msg = "ClassReference " + object_class_classrefs[key]
+
+  if msg == ' '.join(pars): 
+    msg = None
+
+  if msg:
+    msg0 = "Can not define CLASS field %s as '%s'\n" % (key, ' '.join(pars))
+    msg1 = "Already defined as '%s'" % (msg)
+    raise msg0 + msg1
+
+  if (typename == 'ClassReference'):
+    if not typeref: return False
+    object_class_classrefs[key] = typeref
+    return True
 
   if (typename == 'TypeReference'):
     if not typeref: return False
@@ -6176,7 +7027,7 @@ def p_UserDefinedConstraintParameterList_3 (t):
 
 # 9.3
 def p_UserDefinedConstraintParameter (t):
-  'UserDefinedConstraintParameter : type_ref'
+  'UserDefinedConstraintParameter : Type'
   t[0] = t[1]
 
 # 10 Table constraints, including component relation constraints --------------
@@ -6193,8 +7044,40 @@ def p_SimpleTableConstraint (t):
 
 # 10.7
 def p_ComponentRelationConstraint (t):
-  'ComponentRelationConstraint : LBRACE UCASE_IDENT RBRACE LBRACE AT LCASE_IDENT RBRACE'
-  t[0] = t[2] + '@' + t[6]
+  'ComponentRelationConstraint : LBRACE UCASE_IDENT RBRACE LBRACE AtNotations RBRACE'
+  t[0] = t[2] + str(t[5])
+
+def p_AtNotations_1 (t):
+  'AtNotations : AtNotation'
+  t[0] = [t[1]]
+
+def p_AtNotations_2 (t):
+  'AtNotations : AtNotations COMMA  AtNotation'
+  t[0] = t[1] + [t[3]]
+
+def p_AtNotation_1 (t):
+  'AtNotation : AT ComponentIdList'
+  t[0] = '@' + t[2]
+
+def p_AtNotation_2 (t):
+  'AtNotation : AT DOT Level ComponentIdList'
+  t[0] = '@.' + t[3] + t[4]
+
+def p_Level_1 (t):
+  'Level : DOT Level'
+  t[0] = '.' + t[2]
+
+def p_Level_2 (t):
+  'Level : '
+  t[0] = ''
+
+def p_ComponentIdList_1 (t):
+  'ComponentIdList : LCASE_IDENT'
+  t[0] = t[1]
+
+def p_ComponentIdList_2 (t):
+  'ComponentIdList : ComponentIdList DOT LCASE_IDENT'
+  t[0] = t[1] + '.' + t[3]
 
 # 11 Contents constraints -----------------------------------------------------
 
@@ -6209,6 +7092,11 @@ def p_ContentsConstraint (t):
 # 8 Parameterized assignments -------------------------------------------------
 
 # 8.1
+def p_ParameterizedAssignment (t):
+  '''ParameterizedAssignment : ParameterizedTypeAssignment
+                             | ParameterizedObjectAssignment
+                             | ParameterizedObjectSetAssignment'''
+  t[0] = t[1]
 
 # 8.2
 def p_ParameterizedTypeAssignment (t):
@@ -6216,6 +7104,16 @@ def p_ParameterizedTypeAssignment (t):
   t[0] = t[4]
   t[0].SetName(t[1])  # t[0].SetName(t[1] + 'xxx')
 
+def p_ParameterizedObjectAssignment (t):
+  'ParameterizedObjectAssignment : objectreference ParameterList DefinedObjectClass ASSIGNMENT Object'
+  t[0] = ObjectAssignment (ident = t[1], cls=t[3].val, val=t[5])
+  global obj_class
+  obj_class = None
+
+def p_ParameterizedObjectSetAssignment (t):
+  'ParameterizedObjectSetAssignment : UCASE_IDENT ParameterList DefinedObjectClass ASSIGNMENT ObjectSet'
+  t[0] = Node('ObjectSetAssignment', name=t[1], cls=t[3].val, val=t[5])
+
 # 8.3
 def p_ParameterList (t):
   'ParameterList : lbraceignore rbraceignore'
@@ -6245,7 +7143,7 @@ def p_ParameterList (t):
 
 # 9.1
 def p_ParameterizedReference (t):
-  'ParameterizedReference : type_ref LBRACE RBRACE'
+  'ParameterizedReference : Reference LBRACE RBRACE'
   t[0] = t[1]
   #t[0].val += 'xxx'
 
@@ -6255,6 +7153,12 @@ def p_ParameterizedType (t):
   t[0] = t[1]
   #t[0].val += 'xxx'
 
+
+def p_ParameterizedObject (t):
+  'ParameterizedObject : DefinedObject ActualParameterList'
+  t[0] = t[1]
+  #t[0].val += 'xxx'
+
 # 9.5
 def p_ActualParameterList (t):
   'ActualParameterList : lbraceignore rbraceignore'
@@ -6279,23 +7183,27 @@ def p_ActualParameterList (t):
 
 #--- ITU-T Recommendation X.880 -----------------------------------------------
 
-x880_syntaxes = {
+x880_classes = {
   'OPERATION' : {
-    'ARGUMENT'       : 'ARGUMENT',
-    'RESULT'         : 'RESULT',         
-    'RETURN'         : 'RETURN',         
-    'ERRORS'         : 'ERRORS',         
-    'LINKED'         : 'LINKED',         
-    'SYNCHRONOUS'    : 'SYNCHRONOUS',    
-    'IDEMPOTENT'     : 'IDEMPOTENT',     
-    'ALWAYS'         : 'ALWAYS',         
-    'RESPONDS'       : 'RESPONDS',       
-    'INVOKE'         : 'INVOKE',         
-    'PRIORITY'       : 'PRIORITY',       
-    'RESULT-PRIORITY': 'RESULT-PRIORITY',
-    'CODE'           : 'CODE',           
+    '&ArgumentType'         : [],      
+    '&argumentTypeOptional' : [ 'BooleanType' ],
+    '&returnResult'         : [ 'BooleanType' ],
+    '&ResultType'           : [],            
+    '&resultTypeOptional'   : [ 'BooleanType' ],
+    '&Errors'               : [ 'ClassReference', 'ERROR' ],
+    '&Linked'               : [ 'ClassReference', 'OPERATION' ],
+    '&synchronous'          : [ 'BooleanType' ],
+    '&idempotent'           : [ 'BooleanType' ],
+    '&alwaysReturns'        : [ 'BooleanType' ],
+#    '&InvokePriority'         #UNSUPPORTED_FixedTypeValueSetFieldSpec
+#    '&ResultPriority'         #UNSUPPORTED_FixedTypeValueSetFieldSpec
+    '&operationCode'        : [ 'TypeReference', 'Code' ],
   },
   'ERROR' : {
+    '&ParameterType'         : [],                
+    '&parameterTypeOptional' : [ 'BooleanType' ],
+#    '&ErrorPriority'          #UNSUPPORTED_FixedTypeValueSetFieldSpec
+    '&errorCode'             : [ 'TypeReference', 'Code' ],
   },
   'OPERATION-PACKAGE' : {
   },
@@ -6305,19 +7213,55 @@ x880_syntaxes = {
   },
   'ROS-OBJECT-CLASS' : {
   },
-
 }
 
-x880_syntaxes_enabled = { }
-x880_current_syntaxes = None
+x880_syntaxes = {
+  'OPERATION' : {
+    'ARGUMENT'       : '&ArgumentType',
+    'ARGUMENT OPTIONAL' : '&argumentTypeOptional',
+    'RESULT'         : '&ResultType',         
+    'RESULT OPTIONAL' : '&resultTypeOptional',         
+    'RETURN'         : 'RETURN',
+    'RETURN RESULT'  : '&returnResult',
+    'ERRORS'         : '&Errors',         
+    'LINKED'         : '&Linked',         
+    'SYNCHRONOUS'    : '&synchronous',    
+    'IDEMPOTENT'     : '&idempotent',     
+    'ALWAYS'         : 'ALWAYS',         
+    'RESPONDS'       : 'RESPONDS',
+    'ALWAYS RESPONDS' : '&alwaysReturns',       
+    'INVOKE'         : 'INVOKE',         
+    'PRIORITY'       : 'PRIORITY',
+    'INVOKE PRIORITY' : '&InvokePriority',
+    'RESULT-PRIORITY': '&ResultPriority',
+    'CODE'           : '&operationCode',           
+  },
+  'ERROR' : {
+    'PARAMETER'      : '&ParameterType',       
+    'PARAMETER OPTIONAL' : '&parameterTypeOptional',       
+    'PRIORITY'       : '&ErrorPriority',       
+    'CODE'           : '&errorCode',           
+  },
+#  'OPERATION-PACKAGE' : {
+#  },
+#  'CONNECTION-PACKAGE' : {
+#  },
+#  'CONTRACT' : {
+#  },
+#  'ROS-OBJECT-CLASS' : {
+#  },
+}
 
 def x880_import(name):
   if x880_syntaxes.has_key(name):
-    x880_syntaxes_enabled[name] = True
+    class_syntaxes_enabled[name] = True
+    class_syntaxes[name] = x880_syntaxes[name]
+  if x880_classes.has_key(name):
     add_class_ident(name)
+    for f in (x880_classes[name].keys()):
+      set_type_to_class(name, f, x880_classes[name][f])
 
-def is_x880_syntax(name):
-  return False
+tokens = tokens + get_syntax_tokens(x880_syntaxes)
 
 #  {...} OID value
 #def p_lbrace_oid(t):
@@ -6347,7 +7291,7 @@ def p_braceignorebegin(t):
   'braceignorebegin : '
   global lexer
   lexer.level = 1
-  lexer.begin('braceignore')
+  lexer.push_state('braceignore')
 
 def p_rbraceignore(t):
   'rbraceignore : braceignoreend RBRACE'
@@ -6356,7 +7300,7 @@ def p_rbraceignore(t):
 def p_braceignoreend(t):
   'braceignoreend : '
   global lexer
-  lexer.begin('INITIAL')
+  lexer.pop_state()
 
 def p_error(t):
   global input_file
@@ -6434,6 +7378,7 @@ asn2wrs [-h|?] [-d dbg] [-b] [-p proto] [-c conform_file] [-e] input_file(s) ...
 
 def eth_main():
   global input_file
+  global g_conform
   global lexer
   print "ASN.1 to Wireshark dissector compiler";
   try:
@@ -6457,6 +7402,8 @@ def eth_main():
   ectx.new = True
   ectx.expcnf = False
   ectx.merge_modules = False
+  ectx.group_by_prot = False
+  ectx.conform.last_group = 0
   ectx.conform.suppress_line = False;
   ectx.output.outnm = None
   ectx.output.single_file = None
@@ -6487,6 +7434,7 @@ def eth_main():
   if ectx.dbg('p'): pd = 2
   lexer = lex.lex(debug=ld)
   yacc.yacc(method='LALR', debug=yd)
+  g_conform = ectx.conform
   ast = []
   for fn in args:
     input_file = fn
@@ -6495,15 +7443,36 @@ def eth_main():
     ast.extend(yacc.parse(f.read(), lexer=lexer, debug=pd))
     f.close ()
   ectx.eth_clean()
-  for module in ast:
-    eth_do_module(module, ectx)
-    if (not ectx.merge_modules):  # output for each module
-      ectx.eth_prepare()
-      ectx.eth_do_output()
-      ectx.eth_clean()
   if (ectx.merge_modules):  # common output for all module
+    ectx.eth_clean()
+    for module in ast:
+      eth_do_module(module, ectx)
     ectx.eth_prepare()
     ectx.eth_do_output()
+  elif (ectx.groups()):  # group by protocols/group
+    groups = []
+    pr2gr = {}
+    if (ectx.group_by_prot):  # group by protocols
+      for module in ast:
+        prot = module.get_proto(ectx)
+        if not pr2gr.has_key(prot):
+          pr2gr[prot] = len(groups)
+          groups.append([])
+        groups[pr2gr[prot]].append(module)
+    else:  # group by groups
+      pass
+    for gm in (groups):
+      ectx.eth_clean()
+      for module in gm:
+        eth_do_module(module, ectx)
+      ectx.eth_prepare()
+      ectx.eth_do_output()
+  else:   # output for each module
+    for module in ast:
+      ectx.eth_clean()
+      eth_do_module(module, ectx)
+      ectx.eth_prepare()
+      ectx.eth_do_output()
 
   if ectx.dbg('m'):
     ectx.dbg_modules()