Commit bca1637b authored by Per Cederqvist's avatar Per Cederqvist
Browse files

(defined_types): New variable.

(number_suffixed): New function.
(prot_a_type): New class, with several derived classes.
(reader.error): Allow the line number to be a string, such as '*builtin*'.
(lexer.__init__): Added 'ENUMERATION-OF' to the set of builtin
	aggregates.  Add builtin types to defined_types.
(lexer): Ignore @dots{}.
(lexer.toplevel_bye): Check that all defined types are used.
(lexer.toplevel_tindex): Pass a list of the @tindex entries to
	__parse_userdefined_types, so that that function can check that
	they are all defined.
(lexer.__parse_userdefined_types): Implemented.  Several helper
	functions introduced.
(lexer.__parse_type): Simplified.  Do some error checking and
	bookkeeping.  Return a tuple, so that it is easier for the caller
	to see if an array was used.  All callers updated.
(lexer.__bad_arg): Accept "reservedX" and "flgX" for numerical values of X.
(lexer.__get_token): Handle the new tokens "::=", "|" and "=".
	Ignore comments (introduced with "!").
parent 2ce0b7e8
......@@ -3,9 +3,124 @@
import sys
import types
# A mapping from type names (as strings) to prot_a_* objects.
defined_types = {}
def number_suffixed(s, base):
if not s.startswith(base):
return 0
s = s[len(base):]
if len(s) < 1:
return 0
try:
int(s)
return 1
except:
return 0
class reader_eof(Exception):
pass
class prot_a_type:
__usage_count = 0
def __init__(self, line):
self.__line = line
def line(self):
return self.__line
def use(self):
self.__usage_count += 1
if self.__usage_count == 1:
self.use_recurse()
def usage(self):
return self.__usage_count
class prot_a_builtin(prot_a_type):
def use_recurse(self):
pass
class prot_a_simple(prot_a_type):
def __init__(self, line, typ, is_array):
self.__type = typ
self.__array = is_array
prot_a_type.__init__(self, line)
def use_recurse(self):
defined_types[self.__type].use()
class prot_a_alternate(prot_a_type):
def __init__(self, line, type_a, type_b):
self.__type_a = type_a
self.__type_b = type_b
prot_a_type.__init__(self, line)
def use_recurse(self):
defined_types[self.__type_a].use()
defined_types[self.__type_b].use()
class prot_a_struct(prot_a_type):
def __init__(self, line):
prot_a_type.__init__(self, line)
self.__fields = []
self.__used_names = {}
def add_field(self, field_name, type_name, is_array):
if self.__used_names.has_key(field_name):
return "field name ``%s'' used twice" % field_name
o = (field_name, type_name, is_array)
self.__used_names[field_name] = o
self.__fields.append(o)
return None
def use_recurse(self):
for (fn, tn, ar) in self.__fields:
defined_types[tn].use()
class prot_a_bitstring(prot_a_type):
def add_field(self, field_name):
pass
def use_recurse(self):
pass
class prot_a_selection(prot_a_type):
def __init__(self, line):
prot_a_type.__init__(self, line)
self.__fields = []
self.__used_nums = {}
self.__used_names = {}
self.__used_tailnames = {}
def add_variant(self, number, name, tailname, tail_type, is_array):
if self.__used_nums.has_key(number):
return "selection number ``%s'' used twice" % number
if self.__used_names.has_key(name):
return "selection name ``%s'' used twice" % name
if self.__used_tailnames.has_key(tailname):
return "selection tailname ``%s'' used twice" % tailname
o = (number, name, tailname, tail_type, is_array)
self.__used_nums[number] = o
self.__used_names[name] = o
self.__used_tailnames[tailname] = o
self.__fields.append(o)
def use_recurse(self):
for (number, name, tailname, tail_type, is_array) in self.__fields:
defined_types[tail_type].use()
class prot_a_enumration_of(prot_a_type):
def __init__(self, line, base):
prot_a_type.__init__(self, line)
self.__base = base
def use_recurse(self):
defined_types[self.__base].use()
class reader:
parens = {'(': ')',
'[': ']',
......@@ -30,7 +145,7 @@ class reader:
return self.__filename
def error(self, line_no, errmsg):
sys.stderr.write("%s:%d:%s\n" % (self.filename(), line_no, errmsg))
sys.stderr.write("%s:%s:%s\n" % (self.filename(), line_no, errmsg))
self.__errfound = 1
def errfound(self):
......@@ -100,11 +215,13 @@ class lexer:
self.__builtin_types = ["BOOL", "INT8", "INT16", "INT32",
"HOLLERITH"]
self.__builtin_aggregates = ["BITSTRING", "ENUMERATION",
"ENUMERATION-OF",
"ARRAY", "SELECTION", "RPC"]
self.__builtin_typelike = (self.__builtin_types
+ self.__builtin_aggregates)
self.__tindex_seen = {}
self.__types_used = {}
for t in self.__builtin_types:
defined_types[t] = prot_a_builtin('*builtin*')
def run(self):
while 1:
......@@ -234,6 +351,7 @@ class lexer:
toplevel_printindex = ignore
toplevel_example = ignore
toplevel_unmacro = ignore
toplevel_dots = ignore
toplevel_tex = ignore
def toplevel_node(self, arg, line_no):
......@@ -280,8 +398,9 @@ class lexer:
def toplevel_bye(self, arg, line_no):
if self.__findex != None:
self.error(self.__reader.line_no(), "unterminated @findex node")
# FIXME: check __tindex_seen and __tindex_used. They should
# contain the same keys. Report any discrepancies.
for (n, o) in defined_types.items():
if o.usage() < 1:
self.error(o.line(), "unused type ``%s''" % n)
def toplevel_reqdlink(self, arg, line_no):
self.__assert_no_linkhere()
......@@ -311,24 +430,203 @@ class lexer:
self.__bad_type(arg)
tindexed_types = [arg]
self.__tokens = []
t = self.__get_token()
while t == '@tindex':
t = self.__get_token()
self.__seen_type(t, self.__reader.line_no())
self.__bad_type(t)
tindexed_types.append(t)
t = self.__get_token()
if t != '@example':
self.error(self.__reader.line_no(),
"missing @example after @tindex for defined type")
return
self.__parse_userdefined_types()
self.__parse_userdefined_types(tindexed_types)
def __parse_userdefined_types(self, tindexed_types):
"""Parse type definitions for TINDEXED_TYPES.
TINDEXED_TYPES is a list of type names, as strings, that
originates from the @tindex statements above this @example
section. An error message is given if TINDEXED_TYPES doesn't
correspond to the types actually defined in this @example
section.
"""
seen_types = []
while 1:
newtype = self.__get_token()
line = self.__reader.line_no()
if newtype == '@end':
token = self.__get_token()
if token != 'example':
self.error(self.__reader.line_no(),
"``@end example'' expected")
break
token = self.__get_token()
if token != '::=':
self.error(self.__reader.line_no(), "``::='' expected")
return
token = self.__get_token()
if token == '(':
typedef = self.__parse_userdefined_struct(line)
elif token == 'SELECTION':
typedef = self.__parse_userdefined_selection(line)
elif token == 'BITSTRING':
typedef = self.__parse_userdefined_bitstring(line)
elif token == 'ENUMERATION-OF':
typedef = self.__parse_userdefined_enumeration_of(line)
else:
if token == 'ARRAY':
name = self.__get_token()
array = 1
else:
array = 0
name = token
token = self.__get_token()
if token == '|':
token = self.__get_token()
if array or token == 'ARRAY':
self.error(self.__reader.line_no(),
"cannot mix ARRAY and ``|''")
return
typedef = prot_a_alternate(line, name, token)
token = self.__get_token()
else:
typedef = prot_a_simple(line, name, array)
if token != ';':
# This is not a fatal error; we try to recover
# from it.
self.error(self.__reader.line_no(), "missing ;")
if typedef != None:
if newtype not in tindexed_types:
self.error(self.__reader.line_no(),
"missing @tindex entry for %s" % newtype)
seen_types.append(newtype)
if defined_types.has_key(newtype):
self.error(self.__reader.line_no(),
"redefinition of ``%s''" % newtype)
else:
defined_types[newtype] = typedef
def __parse_userdefined_types(self):
# FIXME
# FIXME: Check that the defined type has a @tindex above.
# FIXME: Parse the type.
pass
# Check that all types in the @tindex were defined.
for typename in tindexed_types:
if typename not in seen_types:
self.error(self.__reader.line_no(),
"type %s not defined but @tindex entry exists" %
typename)
def __parse_userdefined_struct(self, line):
res = prot_a_struct(line)
while 1:
token = self.__get_token()
if token == ')':
return res
name = token
token = self.__get_token()
if token != ':':
self.error(self.__reader.line_no(),
"missing ``:'' near %s" % token)
token = self.__get_token()
if token == 'ARRAY':
array = 1
token = self.__get_token()
else:
array = 0
if not self.__bad_type(token) and not self.__bad_arg(name):
ret = res.add_field(name, token, array)
if ret:
self.error(self.__reader.line_no(), ret)
token = self.__get_token()
if token != ';':
self.error(self.__reader.line_no(),
"missing ``;''")
def __parse_userdefined_bitstring(self, line):
res = prot_a_bitstring(line)
token = self.__get_token()
if token != '(':
self.error(self.__reader.line_no(),
"expected ``('' after BITSTRING, not ``%s''" % token)
return None
while 1:
token = self.__get_token()
if token == ')':
return res
name = token
if not self.__bad_arg(name):
res.add_field(name)
token = self.__get_token()
if token != ';':
self.error(self.__reader.line_no(),
"missing ``;''")
def __parse_userdefined_selection(self, line):
res = prot_a_selection(line)
token = self.__get_token()
if token != '(':
self.error(self.__reader.line_no(),
"expected ``('' after SELECTION, not ``%s''" % token)
return None
while 1:
token = self.__get_token()
if token == ')':
return res
number = token
try:
int(number)
except:
self.error(self.__reader.line_no(),
"bad number ``%s''" % number)
return None
token = self.__get_token()
if token != '=':
self.error(self.__reader.line_no(),
"missing ``:'' near %s" % token)
name = self.__get_token()
tail_name = self.__get_token()
token = self.__get_token()
if token != ':':
self.error(self.__reader.line_no(),
"missing ``:'' near %s" % token)
token = self.__get_token()
if token == 'ARRAY':
array = 1
token = self.__get_token()
else:
array = 0
if (not self.__bad_type(token) and not self.__bad_arg(name)
and not self.__bad_arg(tail_name)):
ret = res.add_variant(number, name, tail_name, token, array)
if ret:
self.error(self.__reader.line_no(), ret)
token = self.__get_token()
if token != ';':
self.error(self.__reader.line_no(),
"missing ``;''")
def __parse_userdefined_enumeration_of(self, line):
token = self.__get_token()
if token != '(':
self.error(self.__reader.line_no(),
"expected ``('' after ENUMERATION-OF, not ``%s''" %
token)
return None
name = self.__get_token()
token = self.__get_token()
if token != ')':
self.error(self.__reader.line_no(),
"missing close ``)'', got ``%s''" %
token)
return None
if self.__bad_type(name):
return None
return prot_a_enumration_of(line, name)
def __parse_request(self):
self.__tokens = []
......@@ -387,7 +685,7 @@ class lexer:
next = self.__get_token()
if next != ')':
self.__unget_token(next)
ret_type = self.__parse_type()
(ret_type, ret_array) = self.__parse_type()
next = self.__get_token()
if next != ')':
self.error(self.__reader.line_no(), "missing ``)'' for result")
......@@ -406,14 +704,17 @@ class lexer:
def __parse_type(self):
token = self.__get_token()
if token == 'ARRAY':
tp = self.__get_token()
if self.__bad_type(tp):
return None
return token + " " + tp
token = self.__get_token()
array = 1
else:
if self.__bad_type(token):
return None
return token
array = 0
if self.__bad_type(token):
return (None, 0)
if not defined_types.has_key(token):
self.error(self.__reader.line_no(),
"undefined type ``%s''" % token)
defined_types[token].use()
return (token, array)
def __bad_type(self, tp):
if tp in self.__builtin_types:
......@@ -447,6 +748,10 @@ class lexer:
return not ok
def __bad_arg(self, arg):
if number_suffixed(arg, 'reserved'):
return 0
if number_suffixed(arg, 'flg'):
return 0
ok = 1
if len(arg) < 0:
ok = 0
......@@ -474,7 +779,7 @@ class lexer:
if self.__get_token() != ':':
self.error(self.__reader.line_no(), "missing ``:'' after argument")
return
tp = self.__parse_type()
(tp, array) = self.__parse_type()
if tp == None:
return
if self.__args.has_key(argname):
......@@ -505,7 +810,18 @@ class lexer:
self.__reader.ungetc(c)
return res
res = res + c
elif c in '[];:':
elif c == ':':
d = self.__reader.getc()
if d != ':':
self.__reader.ungetc(d)
return c
d = self.__reader.getc()
if d != '=':
self.__reader.ungetc(d)
self.__reader.ungetc(':')
return c
return '::='
elif c in '[];|=':
return c
elif c in '()':
d = self.__reader.getc()
......@@ -545,6 +861,15 @@ class lexer:
self.__reader.ungetc(c)
return res
res = res + c
elif c == '!':
# This is a comment. Skip it. We *should* parse it like
# toplevel Texinfo code until the next end of line, but
# doing so is too hard -- there is only a single comment
# in the entire document.
while 1:
c = self.__reader.getc()
if c == '\n':
return self.__get_token()
else:
self.error(self.__reader.line_no(),
"bad character ``%s''" % (c,))
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment