qapi: New QAPISourceInfo, replacing dict
We track source locations with a dict of the form {'file': FNAME, 'line': LINENO, 'parent': PARENT} where PARENT is None for the main file, and the include directive's source location for included files. This is serviceable enough, but the next commit will add information, and that's going to come out cleaner if we turn this into a class. So do that. Signed-off-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com> Message-Id: <20190927134639.4284-4-armbru@redhat.com>
This commit is contained in:
parent
57608a5299
commit
19e950d9d4
@ -13,6 +13,7 @@
|
|||||||
|
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
import copy
|
||||||
import errno
|
import errno
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
@ -53,34 +54,50 @@ struct_types = {}
|
|||||||
union_types = {}
|
union_types = {}
|
||||||
all_names = {}
|
all_names = {}
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Parsing the schema into expressions
|
# Parsing the schema into expressions
|
||||||
#
|
#
|
||||||
|
|
||||||
|
class QAPISourceInfo(object):
|
||||||
|
def __init__(self, fname, line, parent):
|
||||||
|
self.fname = fname
|
||||||
|
self.line = line
|
||||||
|
self.parent = parent
|
||||||
|
|
||||||
def error_path(parent):
|
def next_line(self):
|
||||||
res = ''
|
info = copy.copy(self)
|
||||||
while parent:
|
info.line += 1
|
||||||
res = ('In file included from %s:%d:\n' % (parent['file'],
|
return info
|
||||||
parent['line'])) + res
|
|
||||||
parent = parent['parent']
|
def loc(self):
|
||||||
return res
|
return '%s:%d' % (self.fname, self.line)
|
||||||
|
|
||||||
|
def include_path(self):
|
||||||
|
ret = ''
|
||||||
|
parent = self.parent
|
||||||
|
while parent:
|
||||||
|
ret = 'In file included from %s:\n' % parent.loc() + ret
|
||||||
|
parent = parent.parent
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.include_path() + self.loc()
|
||||||
|
|
||||||
|
|
||||||
class QAPIError(Exception):
|
class QAPIError(Exception):
|
||||||
def __init__(self, fname, line, col, incl_info, msg):
|
def __init__(self, info, col, msg):
|
||||||
Exception.__init__(self)
|
Exception.__init__(self)
|
||||||
self.fname = fname
|
self.info = info
|
||||||
self.line = line
|
|
||||||
self.col = col
|
self.col = col
|
||||||
self.info = incl_info
|
|
||||||
self.msg = msg
|
self.msg = msg
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
loc = '%s:%d' % (self.fname, self.line)
|
loc = str(self.info)
|
||||||
if self.col is not None:
|
if self.col is not None:
|
||||||
|
assert self.info.line is not None
|
||||||
loc += ':%s' % self.col
|
loc += ':%s' % self.col
|
||||||
return error_path(self.info) + '%s: %s' % (loc, self.msg)
|
return loc + ': ' + self.msg
|
||||||
|
|
||||||
|
|
||||||
class QAPIParseError(QAPIError):
|
class QAPIParseError(QAPIError):
|
||||||
@ -91,14 +108,12 @@ class QAPIParseError(QAPIError):
|
|||||||
col = (col + 7) % 8 + 1
|
col = (col + 7) % 8 + 1
|
||||||
else:
|
else:
|
||||||
col += 1
|
col += 1
|
||||||
QAPIError.__init__(self, parser.fname, parser.line, col,
|
QAPIError.__init__(self, parser.info, col, msg)
|
||||||
parser.incl_info, msg)
|
|
||||||
|
|
||||||
|
|
||||||
class QAPISemError(QAPIError):
|
class QAPISemError(QAPIError):
|
||||||
def __init__(self, info, msg):
|
def __init__(self, info, msg):
|
||||||
QAPIError.__init__(self, info['file'], info['line'], None,
|
QAPIError.__init__(self, info, None, msg)
|
||||||
info['parent'], msg)
|
|
||||||
|
|
||||||
|
|
||||||
class QAPIDoc(object):
|
class QAPIDoc(object):
|
||||||
@ -382,12 +397,11 @@ class QAPISchemaParser(object):
|
|||||||
def __init__(self, fp, previously_included=[], incl_info=None):
|
def __init__(self, fp, previously_included=[], incl_info=None):
|
||||||
self.fname = fp.name
|
self.fname = fp.name
|
||||||
previously_included.append(os.path.abspath(fp.name))
|
previously_included.append(os.path.abspath(fp.name))
|
||||||
self.incl_info = incl_info
|
|
||||||
self.src = fp.read()
|
self.src = fp.read()
|
||||||
if self.src == '' or self.src[-1] != '\n':
|
if self.src == '' or self.src[-1] != '\n':
|
||||||
self.src += '\n'
|
self.src += '\n'
|
||||||
self.cursor = 0
|
self.cursor = 0
|
||||||
self.line = 1
|
self.info = QAPISourceInfo(self.fname, 1, incl_info)
|
||||||
self.line_pos = 0
|
self.line_pos = 0
|
||||||
self.exprs = []
|
self.exprs = []
|
||||||
self.docs = []
|
self.docs = []
|
||||||
@ -395,8 +409,7 @@ class QAPISchemaParser(object):
|
|||||||
cur_doc = None
|
cur_doc = None
|
||||||
|
|
||||||
while self.tok is not None:
|
while self.tok is not None:
|
||||||
info = {'file': self.fname, 'line': self.line,
|
info = self.info
|
||||||
'parent': self.incl_info}
|
|
||||||
if self.tok == '#':
|
if self.tok == '#':
|
||||||
self.reject_expr_doc(cur_doc)
|
self.reject_expr_doc(cur_doc)
|
||||||
cur_doc = self.get_doc(info)
|
cur_doc = self.get_doc(info)
|
||||||
@ -456,9 +469,9 @@ class QAPISchemaParser(object):
|
|||||||
# catch inclusion cycle
|
# catch inclusion cycle
|
||||||
inf = info
|
inf = info
|
||||||
while inf:
|
while inf:
|
||||||
if incl_abs_fname == os.path.abspath(inf['file']):
|
if incl_abs_fname == os.path.abspath(inf.fname):
|
||||||
raise QAPISemError(info, "Inclusion loop for %s" % include)
|
raise QAPISemError(info, "Inclusion loop for %s" % include)
|
||||||
inf = inf['parent']
|
inf = inf.parent
|
||||||
|
|
||||||
# skip multiple include of the same file
|
# skip multiple include of the same file
|
||||||
if incl_abs_fname in previously_included:
|
if incl_abs_fname in previously_included:
|
||||||
@ -552,7 +565,7 @@ class QAPISchemaParser(object):
|
|||||||
if self.cursor == len(self.src):
|
if self.cursor == len(self.src):
|
||||||
self.tok = None
|
self.tok = None
|
||||||
return
|
return
|
||||||
self.line += 1
|
self.info = self.info.next_line()
|
||||||
self.line_pos = self.cursor
|
self.line_pos = self.cursor
|
||||||
elif not self.tok.isspace():
|
elif not self.tok.isspace():
|
||||||
# Show up to next structural, whitespace or quote
|
# Show up to next structural, whitespace or quote
|
||||||
@ -1172,7 +1185,7 @@ class QAPISchemaEntity(object):
|
|||||||
def check(self, schema):
|
def check(self, schema):
|
||||||
assert not self._checked
|
assert not self._checked
|
||||||
if self.info:
|
if self.info:
|
||||||
self._module = os.path.relpath(self.info['file'],
|
self._module = os.path.relpath(self.info.fname,
|
||||||
os.path.dirname(schema.fname))
|
os.path.dirname(schema.fname))
|
||||||
self._checked = True
|
self._checked = True
|
||||||
|
|
||||||
@ -1781,9 +1794,9 @@ class QAPISchema(object):
|
|||||||
include = expr['include']
|
include = expr['include']
|
||||||
assert doc is None
|
assert doc is None
|
||||||
main_info = info
|
main_info = info
|
||||||
while main_info['parent']:
|
while main_info.parent:
|
||||||
main_info = main_info['parent']
|
main_info = main_info.parent
|
||||||
fname = os.path.relpath(include, os.path.dirname(main_info['file']))
|
fname = os.path.relpath(include, os.path.dirname(main_info.fname))
|
||||||
self._def_entity(QAPISchemaInclude(fname, info))
|
self._def_entity(QAPISchemaInclude(fname, info))
|
||||||
|
|
||||||
def _def_builtin_type(self, name, json_type, c_type):
|
def _def_builtin_type(self, name, json_type, c_type):
|
||||||
|
Loading…
Reference in New Issue
Block a user