mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-22 13:45:38 +00:00
rpcgen: add an XDR protocol lexer
This adds a lexer capable of handling the XDR protocol files. The lexical rquirements are detailed in https://www.rfc-editor.org/rfc/rfc4506#section-6.2 pytest is introduced as a build dependancy for testing python code. Reviewed-by: Michal Privoznik <mprivozn@redhat.com> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
This commit is contained in:
parent
a24ab56da8
commit
8c8b97685b
@ -287,6 +287,7 @@ BuildRequires: ninja-build
|
||||
BuildRequires: git
|
||||
BuildRequires: perl-interpreter
|
||||
BuildRequires: python3
|
||||
BuildRequires: python3-pytest
|
||||
%if %{with_libxl}
|
||||
BuildRequires: xen-devel
|
||||
%endif
|
||||
|
@ -823,6 +823,7 @@ optional_programs = [
|
||||
'ovs-vsctl',
|
||||
'passt',
|
||||
'pdwtags',
|
||||
'pytest',
|
||||
'rmmod',
|
||||
'scrub',
|
||||
'tc',
|
||||
|
@ -37,3 +37,5 @@ foreach name : scripts
|
||||
sname = name.split('.')[0].underscorify()
|
||||
set_variable('@0@_prog'.format(sname), find_program(name))
|
||||
endforeach
|
||||
|
||||
subdir('rpcgen')
|
||||
|
11
scripts/rpcgen/meson.build
Normal file
11
scripts/rpcgen/meson.build
Normal file
@ -0,0 +1,11 @@
|
||||
if pytest_prog.found()
|
||||
subdir('tests')
|
||||
|
||||
test(
|
||||
'rpcgen-pytest',
|
||||
python3_prog,
|
||||
args: [ '-mpytest' ] + rpcgen_tests,
|
||||
env: runutf8,
|
||||
workdir: meson.current_source_dir(),
|
||||
)
|
||||
endif
|
213
scripts/rpcgen/rpcgen/lexer.py
Normal file
213
scripts/rpcgen/rpcgen/lexer.py
Normal file
@ -0,0 +1,213 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
import abc
|
||||
|
||||
|
||||
class XDRReader:
|
||||
def __init__(self, fp):
|
||||
self.fp = fp
|
||||
self.lookahead = ""
|
||||
self.lookbehind = ""
|
||||
self.line = 1
|
||||
self.column = 0
|
||||
|
||||
def _read(self):
|
||||
if len(self.lookahead) > 0:
|
||||
c = self.lookahead[0:1]
|
||||
self.lookahead = self.lookahead[1:]
|
||||
return c
|
||||
return self.fp.read(1)
|
||||
|
||||
def peek(self, skip=0):
|
||||
need = 1 + skip
|
||||
if len(self.lookahead) < need:
|
||||
self.lookahead = self.lookahead + self.fp.read(need - len(self.lookahead))
|
||||
if len(self.lookahead) < need:
|
||||
return None
|
||||
|
||||
return self.lookahead[skip : skip + 1]
|
||||
|
||||
def last(self, skip=0):
|
||||
if (skip + 1) > len(self.lookbehind):
|
||||
return None
|
||||
return self.lookbehind[skip]
|
||||
|
||||
def next(self):
|
||||
c = self._read()
|
||||
line = self.line
|
||||
column = self.column
|
||||
if c == "\n":
|
||||
self.line = self.line + 1
|
||||
self.column = 0
|
||||
else:
|
||||
self.column = self.column + 1
|
||||
self.lookbehind = c + self.lookbehind
|
||||
if len(self.lookbehind) > 2:
|
||||
self.lookbehind = self.lookbehind[0:2]
|
||||
return c, line, column
|
||||
|
||||
|
||||
class XDRToken(abc.ABC):
|
||||
def __init__(self, line, column, value):
|
||||
self.line = line
|
||||
self.column = column
|
||||
self.value = value
|
||||
|
||||
def __eq__(self, other):
|
||||
return (
|
||||
type(self) == type(other)
|
||||
and self.line == other.line
|
||||
and self.column == other.column
|
||||
and self.value == other.value
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@abc.abstractmethod
|
||||
def start(cls, reader):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@abc.abstractmethod
|
||||
def end(cls, reader):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def consume(cls, reader):
|
||||
c, line, col = reader.next()
|
||||
buf = c
|
||||
while True:
|
||||
if cls.end(reader):
|
||||
break
|
||||
c, _, _ = reader.next()
|
||||
buf = buf + c
|
||||
return cls(line, col, buf)
|
||||
|
||||
def __repr__(self):
|
||||
return "%s{line=%d,col=%d,value={{{%s}}}}" % (
|
||||
self.__class__.__name__,
|
||||
self.line,
|
||||
self.column,
|
||||
self.value,
|
||||
)
|
||||
|
||||
|
||||
class XDRTokenComment(XDRToken):
|
||||
@classmethod
|
||||
def start(cls, reader):
|
||||
return reader.peek() == "/" and reader.peek(skip=1) == "*"
|
||||
|
||||
@classmethod
|
||||
def end(cls, reader):
|
||||
c1 = reader.last(skip=1)
|
||||
c2 = reader.last()
|
||||
if c1 == "*" and c2 == "/":
|
||||
return True
|
||||
|
||||
if reader.peek() is None:
|
||||
raise Exception(
|
||||
"EOF before closing comment starting at %d:%d"
|
||||
% (reader.line, reader.column)
|
||||
)
|
||||
|
||||
|
||||
class XDRTokenIdentifier(XDRToken):
|
||||
@classmethod
|
||||
def start(cls, reader):
|
||||
c = reader.peek()
|
||||
return c.isalpha()
|
||||
|
||||
@classmethod
|
||||
def end(cls, reader):
|
||||
c = reader.peek()
|
||||
if c is None:
|
||||
return True
|
||||
return not c.isalnum() and c != "_"
|
||||
|
||||
|
||||
class XDRTokenPunctuation(XDRToken):
|
||||
@classmethod
|
||||
def start(cls, reader):
|
||||
c = reader.peek()
|
||||
return c in [";", "=", "{", "}", ",", "[", "]", "<", ">", "*", "(", ")", ":"]
|
||||
|
||||
@classmethod
|
||||
def end(cls, reader):
|
||||
return True
|
||||
|
||||
|
||||
class XDRTokenConstant(XDRToken):
|
||||
@classmethod
|
||||
def start(cls, reader):
|
||||
c1 = reader.peek()
|
||||
c2 = reader.peek(skip=1)
|
||||
return c1.isdecimal() or (c1 == "-" and c2 is not None and c2.isdecimal())
|
||||
|
||||
@classmethod
|
||||
def end(cls, reader):
|
||||
c = reader.peek()
|
||||
return (
|
||||
not c.isdecimal()
|
||||
and not c == "."
|
||||
and not c.lower() in ["x", "a", "b", "c", "d", "e", "f"]
|
||||
)
|
||||
|
||||
|
||||
class XDRTokenCEscape(XDRToken):
|
||||
@classmethod
|
||||
def start(cls, reader):
|
||||
return reader.column == 0 and reader.peek() == "%"
|
||||
|
||||
@classmethod
|
||||
def end(cls, reader):
|
||||
return reader.peek() == "\n"
|
||||
|
||||
|
||||
class XDRTokenSpace(XDRToken):
|
||||
@classmethod
|
||||
def start(cls, reader):
|
||||
return reader.peek().isspace()
|
||||
|
||||
@classmethod
|
||||
def end(cls, reader):
|
||||
c = reader.peek()
|
||||
return c is None or not c.isspace()
|
||||
|
||||
|
||||
class XDRLexer:
|
||||
def __init__(self, fp):
|
||||
self.reader = XDRReader(fp)
|
||||
self.lookahead = []
|
||||
|
||||
def _token(self):
|
||||
tokenTypes = [
|
||||
XDRTokenComment,
|
||||
XDRTokenIdentifier,
|
||||
XDRTokenCEscape,
|
||||
XDRTokenPunctuation,
|
||||
XDRTokenConstant,
|
||||
XDRTokenSpace,
|
||||
]
|
||||
while True:
|
||||
if self.reader.peek() is None:
|
||||
return None
|
||||
|
||||
for tokenType in tokenTypes:
|
||||
if tokenType.start(self.reader):
|
||||
ret = tokenType.consume(self.reader)
|
||||
if type(ret) not in [XDRTokenSpace, XDRTokenComment]:
|
||||
return ret
|
||||
|
||||
def next(self):
|
||||
if len(self.lookahead) > 0:
|
||||
token = self.lookahead[0]
|
||||
self.lookahead = self.lookahead[1:]
|
||||
return token
|
||||
return self._token()
|
||||
|
||||
def peek(self):
|
||||
if len(self.lookahead) == 0:
|
||||
token = self._token()
|
||||
if token is None:
|
||||
return None
|
||||
self.lookahead.append(token)
|
||||
return self.lookahead[0]
|
3
scripts/rpcgen/tests/meson.build
Normal file
3
scripts/rpcgen/tests/meson.build
Normal file
@ -0,0 +1,3 @@
|
||||
rpcgen_tests = files([
|
||||
'test_lexer.py',
|
||||
])
|
35
scripts/rpcgen/tests/simple.x
Normal file
35
scripts/rpcgen/tests/simple.x
Normal file
@ -0,0 +1,35 @@
|
||||
/* Example from https://www.rfc-editor.org/rfc/rfc4506#section-7 */
|
||||
|
||||
const MAXUSERNAME = 32; /* max length of a user name */
|
||||
const MAXFILELEN = 65535; /* max length of a file */
|
||||
const MAXNAMELEN = 255; /* max length of a file name */
|
||||
|
||||
/*
|
||||
* Types of files:
|
||||
*/
|
||||
enum filekind {
|
||||
TEXT = 0, /* ascii data */
|
||||
DATA = 1, /* raw data */
|
||||
EXEC = 2 /* executable */
|
||||
};
|
||||
|
||||
/*
|
||||
* File information, per kind of file:
|
||||
*/
|
||||
union filetype switch (filekind kind) {
|
||||
case TEXT:
|
||||
void; /* no extra information */
|
||||
case DATA:
|
||||
string creator<MAXNAMELEN>; /* data creator */
|
||||
case EXEC:
|
||||
string interpretor<MAXNAMELEN>; /* program interpretor */
|
||||
};
|
||||
/*
|
||||
* A complete file:
|
||||
*/
|
||||
struct file {
|
||||
string filename<MAXNAMELEN>; /* name of file */
|
||||
filetype type; /* info about file */
|
||||
string owner<MAXUSERNAME>; /* owner of file */
|
||||
opaque data<MAXFILELEN>; /* file data */
|
||||
};
|
116
scripts/rpcgen/tests/test_lexer.py
Normal file
116
scripts/rpcgen/tests/test_lexer.py
Normal file
@ -0,0 +1,116 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from rpcgen.lexer import (
|
||||
XDRLexer,
|
||||
XDRTokenIdentifier,
|
||||
XDRTokenPunctuation,
|
||||
XDRTokenConstant,
|
||||
)
|
||||
|
||||
|
||||
def test_lexer():
|
||||
p = Path(Path(__file__).parent, "simple.x")
|
||||
with p.open("r") as fp:
|
||||
lexer = XDRLexer(fp)
|
||||
|
||||
tokens = []
|
||||
while True:
|
||||
tok = lexer.next()
|
||||
if tok is None:
|
||||
break
|
||||
tokens.append(tok)
|
||||
|
||||
assert tokens == [
|
||||
XDRTokenIdentifier(line=3, column=0, value="const"),
|
||||
XDRTokenIdentifier(line=3, column=6, value="MAXUSERNAME"),
|
||||
XDRTokenPunctuation(line=3, column=18, value="="),
|
||||
XDRTokenConstant(line=3, column=20, value="32"),
|
||||
XDRTokenPunctuation(line=3, column=22, value=";"),
|
||||
XDRTokenIdentifier(line=4, column=0, value="const"),
|
||||
XDRTokenIdentifier(line=4, column=6, value="MAXFILELEN"),
|
||||
XDRTokenPunctuation(line=4, column=17, value="="),
|
||||
XDRTokenConstant(line=4, column=19, value="65535"),
|
||||
XDRTokenPunctuation(line=4, column=24, value=";"),
|
||||
XDRTokenIdentifier(line=5, column=0, value="const"),
|
||||
XDRTokenIdentifier(line=5, column=6, value="MAXNAMELEN"),
|
||||
XDRTokenPunctuation(line=5, column=17, value="="),
|
||||
XDRTokenConstant(line=5, column=19, value="255"),
|
||||
XDRTokenPunctuation(line=5, column=22, value=";"),
|
||||
XDRTokenIdentifier(line=10, column=0, value="enum"),
|
||||
XDRTokenIdentifier(line=10, column=5, value="filekind"),
|
||||
XDRTokenPunctuation(line=10, column=14, value="{"),
|
||||
XDRTokenIdentifier(line=11, column=3, value="TEXT"),
|
||||
XDRTokenPunctuation(line=11, column=8, value="="),
|
||||
XDRTokenConstant(line=11, column=10, value="0"),
|
||||
XDRTokenPunctuation(line=11, column=11, value=","),
|
||||
XDRTokenIdentifier(line=12, column=3, value="DATA"),
|
||||
XDRTokenPunctuation(line=12, column=8, value="="),
|
||||
XDRTokenConstant(line=12, column=10, value="1"),
|
||||
XDRTokenPunctuation(line=12, column=11, value=","),
|
||||
XDRTokenIdentifier(line=13, column=3, value="EXEC"),
|
||||
XDRTokenPunctuation(line=13, column=8, value="="),
|
||||
XDRTokenConstant(line=13, column=10, value="2"),
|
||||
XDRTokenPunctuation(line=14, column=0, value="}"),
|
||||
XDRTokenPunctuation(line=14, column=1, value=";"),
|
||||
XDRTokenIdentifier(line=19, column=0, value="union"),
|
||||
XDRTokenIdentifier(line=19, column=6, value="filetype"),
|
||||
XDRTokenIdentifier(line=19, column=15, value="switch"),
|
||||
XDRTokenPunctuation(line=19, column=22, value="("),
|
||||
XDRTokenIdentifier(line=19, column=23, value="filekind"),
|
||||
XDRTokenIdentifier(line=19, column=32, value="kind"),
|
||||
XDRTokenPunctuation(line=19, column=36, value=")"),
|
||||
XDRTokenPunctuation(line=19, column=38, value="{"),
|
||||
XDRTokenIdentifier(line=20, column=0, value="case"),
|
||||
XDRTokenIdentifier(line=20, column=5, value="TEXT"),
|
||||
XDRTokenPunctuation(line=20, column=9, value=":"),
|
||||
XDRTokenIdentifier(line=21, column=3, value="void"),
|
||||
XDRTokenPunctuation(line=21, column=7, value=";"),
|
||||
XDRTokenIdentifier(line=22, column=0, value="case"),
|
||||
XDRTokenIdentifier(line=22, column=5, value="DATA"),
|
||||
XDRTokenPunctuation(line=22, column=9, value=":"),
|
||||
XDRTokenIdentifier(line=23, column=3, value="string"),
|
||||
XDRTokenIdentifier(line=23, column=10, value="creator"),
|
||||
XDRTokenPunctuation(line=23, column=17, value="<"),
|
||||
XDRTokenIdentifier(line=23, column=18, value="MAXNAMELEN"),
|
||||
XDRTokenPunctuation(line=23, column=28, value=">"),
|
||||
XDRTokenPunctuation(line=23, column=29, value=";"),
|
||||
XDRTokenIdentifier(line=24, column=0, value="case"),
|
||||
XDRTokenIdentifier(line=24, column=5, value="EXEC"),
|
||||
XDRTokenPunctuation(line=24, column=9, value=":"),
|
||||
XDRTokenIdentifier(line=25, column=3, value="string"),
|
||||
XDRTokenIdentifier(line=25, column=10, value="interpretor"),
|
||||
XDRTokenPunctuation(line=25, column=21, value="<"),
|
||||
XDRTokenIdentifier(line=25, column=22, value="MAXNAMELEN"),
|
||||
XDRTokenPunctuation(line=25, column=32, value=">"),
|
||||
XDRTokenPunctuation(line=25, column=33, value=";"),
|
||||
XDRTokenPunctuation(line=26, column=0, value="}"),
|
||||
XDRTokenPunctuation(line=26, column=1, value=";"),
|
||||
XDRTokenIdentifier(line=30, column=0, value="struct"),
|
||||
XDRTokenIdentifier(line=30, column=7, value="file"),
|
||||
XDRTokenPunctuation(line=30, column=12, value="{"),
|
||||
XDRTokenIdentifier(line=31, column=3, value="string"),
|
||||
XDRTokenIdentifier(line=31, column=10, value="filename"),
|
||||
XDRTokenPunctuation(line=31, column=18, value="<"),
|
||||
XDRTokenIdentifier(line=31, column=19, value="MAXNAMELEN"),
|
||||
XDRTokenPunctuation(line=31, column=29, value=">"),
|
||||
XDRTokenPunctuation(line=31, column=30, value=";"),
|
||||
XDRTokenIdentifier(line=32, column=3, value="filetype"),
|
||||
XDRTokenIdentifier(line=32, column=12, value="type"),
|
||||
XDRTokenPunctuation(line=32, column=16, value=";"),
|
||||
XDRTokenIdentifier(line=33, column=3, value="string"),
|
||||
XDRTokenIdentifier(line=33, column=10, value="owner"),
|
||||
XDRTokenPunctuation(line=33, column=15, value="<"),
|
||||
XDRTokenIdentifier(line=33, column=16, value="MAXUSERNAME"),
|
||||
XDRTokenPunctuation(line=33, column=27, value=">"),
|
||||
XDRTokenPunctuation(line=33, column=28, value=";"),
|
||||
XDRTokenIdentifier(line=34, column=3, value="opaque"),
|
||||
XDRTokenIdentifier(line=34, column=10, value="data"),
|
||||
XDRTokenPunctuation(line=34, column=14, value="<"),
|
||||
XDRTokenIdentifier(line=34, column=15, value="MAXFILELEN"),
|
||||
XDRTokenPunctuation(line=34, column=25, value=">"),
|
||||
XDRTokenPunctuation(line=34, column=26, value=";"),
|
||||
XDRTokenPunctuation(line=35, column=0, value="}"),
|
||||
XDRTokenPunctuation(line=35, column=1, value=";"),
|
||||
]
|
Loading…
Reference in New Issue
Block a user