|
|
9ae3a8 |
From db918ab21be13a3c1f3c65d3821c06cf12528099 Mon Sep 17 00:00:00 2001
|
|
|
9ae3a8 |
Message-Id: <db918ab21be13a3c1f3c65d3821c06cf12528099.1387369730.git.minovotn@redhat.com>
|
|
|
9ae3a8 |
In-Reply-To: <091eecc4fa42754760dfff393dabcc2b444e9693.1387369730.git.minovotn@redhat.com>
|
|
|
9ae3a8 |
References: <091eecc4fa42754760dfff393dabcc2b444e9693.1387369730.git.minovotn@redhat.com>
|
|
|
9ae3a8 |
From: Markus Armbruster <armbru@redhat.com>
|
|
|
9ae3a8 |
Date: Tue, 10 Dec 2013 15:29:13 +0100
|
|
|
9ae3a8 |
Subject: [PATCH 13/21] qapi.py: Restructure lexer and parser
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
RH-Author: Markus Armbruster <armbru@redhat.com>
|
|
|
9ae3a8 |
Message-id: <1386689361-30281-11-git-send-email-armbru@redhat.com>
|
|
|
9ae3a8 |
Patchwork-id: 56127
|
|
|
9ae3a8 |
O-Subject: [PATCH 7.0 qemu-kvm 10/18] qapi.py: Restructure lexer and parser
|
|
|
9ae3a8 |
Bugzilla: 997915
|
|
|
9ae3a8 |
RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
|
|
|
9ae3a8 |
RH-Acked-by: Kevin Wolf <kwolf@redhat.com>
|
|
|
9ae3a8 |
RH-Acked-by: Luiz Capitulino <lcapitulino@redhat.com>
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
From: Markus Armbruster <armbru@redhat.com>
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
The parser has a rather unorthodox structure:
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
Until EOF:
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
Read a section:
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
Generator function get_expr() yields one section after the
|
|
|
9ae3a8 |
other, as a string. An unindented, non-empty line that
|
|
|
9ae3a8 |
isn't a comment starts a new section.
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
Lexing:
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
Split section into a list of tokens (strings), with help
|
|
|
9ae3a8 |
of generator function tokenize().
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
Parsing:
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
Parse the first expression from the list of tokens, with
|
|
|
9ae3a8 |
parse(), throw away any remaining tokens.
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
In parse_schema(): record value of an enum, union or
|
|
|
9ae3a8 |
struct key (if any) in the appropriate global table,
|
|
|
9ae3a8 |
append expression to the list of expressions.
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
Return list of expressions.
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
Known issues:
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
(1) Indentation is significant, unlike in real JSON.
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
(2) Neither lexer nor parser have any idea of source positions. Error
|
|
|
9ae3a8 |
reporting is hard, let's go shopping.
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
(3) The one error we bother to detect, we "report" via raise.
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
(4) The lexer silently ignores invalid characters.
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
(5) If everything in a section gets ignored, the parser crashes.
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
(6) The lexer treats a string containing a structural character exactly
|
|
|
9ae3a8 |
like the structural character.
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
(7) Tokens trailing the first expression in a section are silently
|
|
|
9ae3a8 |
ignored.
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
(8) The parser accepts any token in place of a colon.
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
(9) The parser treats comma as optional.
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
(10) parse() crashes on unexpected EOF.
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
(11) parse_schema() crashes when a section's expression isn't a JSON
|
|
|
9ae3a8 |
object.
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
Replace this piece of original art by a thoroughly unoriginal design.
|
|
|
9ae3a8 |
Takes care of (1), (2), (5), (6) and (7), and lays the groundwork for
|
|
|
9ae3a8 |
addressing the others. Generated source files remain unchanged.
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
Signed-off-by: Markus Armbruster <armbru@redhat.com>
|
|
|
9ae3a8 |
Reviewed-by: Eric Blake <eblake@redhat.com>
|
|
|
9ae3a8 |
Message-id: 1374939721-7876-4-git-send-email-armbru@redhat.com
|
|
|
9ae3a8 |
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
|
|
|
9ae3a8 |
(cherry picked from commit c7a3f25200c8692e969f21c7f2555630ec0d0d30)
|
|
|
9ae3a8 |
---
|
|
|
9ae3a8 |
scripts/qapi.py | 163 +++++++++++++------------
|
|
|
9ae3a8 |
tests/qapi-schema/indented-expr.out | 2 +-
|
|
|
9ae3a8 |
tests/qapi-schema/missing-colon.out | 4 +-
|
|
|
9ae3a8 |
tests/qapi-schema/quoted-structural-chars.err | 1 +
|
|
|
9ae3a8 |
tests/qapi-schema/quoted-structural-chars.exit | 2 +-
|
|
|
9ae3a8 |
tests/qapi-schema/quoted-structural-chars.out | 3 -
|
|
|
9ae3a8 |
6 files changed, 88 insertions(+), 87 deletions(-)
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
Signed-off-by: Michal Novotny <minovotn@redhat.com>
|
|
|
9ae3a8 |
---
|
|
|
9ae3a8 |
scripts/qapi.py | 163 +++++++++++++------------
|
|
|
9ae3a8 |
tests/qapi-schema/indented-expr.out | 2 +-
|
|
|
9ae3a8 |
tests/qapi-schema/missing-colon.out | 4 +-
|
|
|
9ae3a8 |
tests/qapi-schema/quoted-structural-chars.err | 1 +
|
|
|
9ae3a8 |
tests/qapi-schema/quoted-structural-chars.exit | 2 +-
|
|
|
9ae3a8 |
tests/qapi-schema/quoted-structural-chars.out | 3 -
|
|
|
9ae3a8 |
6 files changed, 88 insertions(+), 87 deletions(-)
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
diff --git a/scripts/qapi.py b/scripts/qapi.py
|
|
|
9ae3a8 |
index 38c808e..58e315b 100644
|
|
|
9ae3a8 |
--- a/scripts/qapi.py
|
|
|
9ae3a8 |
+++ b/scripts/qapi.py
|
|
|
9ae3a8 |
@@ -2,9 +2,11 @@
|
|
|
9ae3a8 |
# QAPI helper library
|
|
|
9ae3a8 |
#
|
|
|
9ae3a8 |
# Copyright IBM, Corp. 2011
|
|
|
9ae3a8 |
+# Copyright (c) 2013 Red Hat Inc.
|
|
|
9ae3a8 |
#
|
|
|
9ae3a8 |
# Authors:
|
|
|
9ae3a8 |
# Anthony Liguori <aliguori@us.ibm.com>
|
|
|
9ae3a8 |
+# Markus Armbruster <armbru@redhat.com>
|
|
|
9ae3a8 |
#
|
|
|
9ae3a8 |
# This work is licensed under the terms of the GNU GPLv2.
|
|
|
9ae3a8 |
# See the COPYING.LIB file in the top-level directory.
|
|
|
9ae3a8 |
@@ -32,91 +34,92 @@ builtin_type_qtypes = {
|
|
|
9ae3a8 |
'uint64': 'QTYPE_QINT',
|
|
|
9ae3a8 |
}
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
-def tokenize(data):
|
|
|
9ae3a8 |
- while len(data):
|
|
|
9ae3a8 |
- ch = data[0]
|
|
|
9ae3a8 |
- data = data[1:]
|
|
|
9ae3a8 |
- if ch in ['{', '}', ':', ',', '[', ']']:
|
|
|
9ae3a8 |
- yield ch
|
|
|
9ae3a8 |
- elif ch in ' \n':
|
|
|
9ae3a8 |
- None
|
|
|
9ae3a8 |
- elif ch == "'":
|
|
|
9ae3a8 |
- string = ''
|
|
|
9ae3a8 |
- esc = False
|
|
|
9ae3a8 |
- while True:
|
|
|
9ae3a8 |
- if (data == ''):
|
|
|
9ae3a8 |
- raise Exception("Mismatched quotes")
|
|
|
9ae3a8 |
- ch = data[0]
|
|
|
9ae3a8 |
- data = data[1:]
|
|
|
9ae3a8 |
- if esc:
|
|
|
9ae3a8 |
- string += ch
|
|
|
9ae3a8 |
- esc = False
|
|
|
9ae3a8 |
- elif ch == "\\":
|
|
|
9ae3a8 |
- esc = True
|
|
|
9ae3a8 |
- elif ch == "'":
|
|
|
9ae3a8 |
- break
|
|
|
9ae3a8 |
- else:
|
|
|
9ae3a8 |
- string += ch
|
|
|
9ae3a8 |
- yield string
|
|
|
9ae3a8 |
-
|
|
|
9ae3a8 |
-def parse(tokens):
|
|
|
9ae3a8 |
- if tokens[0] == '{':
|
|
|
9ae3a8 |
- ret = OrderedDict()
|
|
|
9ae3a8 |
- tokens = tokens[1:]
|
|
|
9ae3a8 |
- while tokens[0] != '}':
|
|
|
9ae3a8 |
- key = tokens[0]
|
|
|
9ae3a8 |
- tokens = tokens[1:]
|
|
|
9ae3a8 |
-
|
|
|
9ae3a8 |
- tokens = tokens[1:] # :
|
|
|
9ae3a8 |
-
|
|
|
9ae3a8 |
- value, tokens = parse(tokens)
|
|
|
9ae3a8 |
-
|
|
|
9ae3a8 |
- if tokens[0] == ',':
|
|
|
9ae3a8 |
- tokens = tokens[1:]
|
|
|
9ae3a8 |
-
|
|
|
9ae3a8 |
- ret[key] = value
|
|
|
9ae3a8 |
- tokens = tokens[1:]
|
|
|
9ae3a8 |
- return ret, tokens
|
|
|
9ae3a8 |
- elif tokens[0] == '[':
|
|
|
9ae3a8 |
- ret = []
|
|
|
9ae3a8 |
- tokens = tokens[1:]
|
|
|
9ae3a8 |
- while tokens[0] != ']':
|
|
|
9ae3a8 |
- value, tokens = parse(tokens)
|
|
|
9ae3a8 |
- if tokens[0] == ',':
|
|
|
9ae3a8 |
- tokens = tokens[1:]
|
|
|
9ae3a8 |
- ret.append(value)
|
|
|
9ae3a8 |
- tokens = tokens[1:]
|
|
|
9ae3a8 |
- return ret, tokens
|
|
|
9ae3a8 |
- else:
|
|
|
9ae3a8 |
- return tokens[0], tokens[1:]
|
|
|
9ae3a8 |
-
|
|
|
9ae3a8 |
-def evaluate(string):
|
|
|
9ae3a8 |
- return parse(map(lambda x: x, tokenize(string)))[0]
|
|
|
9ae3a8 |
-
|
|
|
9ae3a8 |
-def get_expr(fp):
|
|
|
9ae3a8 |
- expr = ''
|
|
|
9ae3a8 |
-
|
|
|
9ae3a8 |
- for line in fp:
|
|
|
9ae3a8 |
- if line.startswith('#') or line == '\n':
|
|
|
9ae3a8 |
- continue
|
|
|
9ae3a8 |
-
|
|
|
9ae3a8 |
- if line.startswith(' '):
|
|
|
9ae3a8 |
- expr += line
|
|
|
9ae3a8 |
- elif expr:
|
|
|
9ae3a8 |
- yield expr
|
|
|
9ae3a8 |
- expr = line
|
|
|
9ae3a8 |
+class QAPISchema:
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ def __init__(self, fp):
|
|
|
9ae3a8 |
+ self.fp = fp
|
|
|
9ae3a8 |
+ self.src = fp.read()
|
|
|
9ae3a8 |
+ if self.src == '' or self.src[-1] != '\n':
|
|
|
9ae3a8 |
+ self.src += '\n'
|
|
|
9ae3a8 |
+ self.cursor = 0
|
|
|
9ae3a8 |
+ self.exprs = []
|
|
|
9ae3a8 |
+ self.accept()
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ while self.tok != None:
|
|
|
9ae3a8 |
+ self.exprs.append(self.get_expr())
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ def accept(self):
|
|
|
9ae3a8 |
+ while True:
|
|
|
9ae3a8 |
+ bol = self.cursor == 0 or self.src[self.cursor-1] == '\n'
|
|
|
9ae3a8 |
+ self.tok = self.src[self.cursor]
|
|
|
9ae3a8 |
+ self.cursor += 1
|
|
|
9ae3a8 |
+ self.val = None
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ if self.tok == '#' and bol:
|
|
|
9ae3a8 |
+ self.cursor = self.src.find('\n', self.cursor)
|
|
|
9ae3a8 |
+ elif self.tok in ['{', '}', ':', ',', '[', ']']:
|
|
|
9ae3a8 |
+ return
|
|
|
9ae3a8 |
+ elif self.tok == "'":
|
|
|
9ae3a8 |
+ string = ''
|
|
|
9ae3a8 |
+ esc = False
|
|
|
9ae3a8 |
+ while True:
|
|
|
9ae3a8 |
+ ch = self.src[self.cursor]
|
|
|
9ae3a8 |
+ self.cursor += 1
|
|
|
9ae3a8 |
+ if ch == '\n':
|
|
|
9ae3a8 |
+ raise Exception("Mismatched quotes")
|
|
|
9ae3a8 |
+ if esc:
|
|
|
9ae3a8 |
+ string += ch
|
|
|
9ae3a8 |
+ esc = False
|
|
|
9ae3a8 |
+ elif ch == "\\":
|
|
|
9ae3a8 |
+ esc = True
|
|
|
9ae3a8 |
+ elif ch == "'":
|
|
|
9ae3a8 |
+ self.val = string
|
|
|
9ae3a8 |
+ return
|
|
|
9ae3a8 |
+ else:
|
|
|
9ae3a8 |
+ string += ch
|
|
|
9ae3a8 |
+ elif self.tok == '\n':
|
|
|
9ae3a8 |
+ if self.cursor == len(self.src):
|
|
|
9ae3a8 |
+ self.tok = None
|
|
|
9ae3a8 |
+ return
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ def get_members(self):
|
|
|
9ae3a8 |
+ expr = OrderedDict()
|
|
|
9ae3a8 |
+ while self.tok != '}':
|
|
|
9ae3a8 |
+ key = self.val
|
|
|
9ae3a8 |
+ self.accept()
|
|
|
9ae3a8 |
+ self.accept() # :
|
|
|
9ae3a8 |
+ expr[key] = self.get_expr()
|
|
|
9ae3a8 |
+ if self.tok == ',':
|
|
|
9ae3a8 |
+ self.accept()
|
|
|
9ae3a8 |
+ self.accept()
|
|
|
9ae3a8 |
+ return expr
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ def get_values(self):
|
|
|
9ae3a8 |
+ expr = []
|
|
|
9ae3a8 |
+ while self.tok != ']':
|
|
|
9ae3a8 |
+ expr.append(self.get_expr())
|
|
|
9ae3a8 |
+ if self.tok == ',':
|
|
|
9ae3a8 |
+ self.accept()
|
|
|
9ae3a8 |
+ self.accept()
|
|
|
9ae3a8 |
+ return expr
|
|
|
9ae3a8 |
+
|
|
|
9ae3a8 |
+ def get_expr(self):
|
|
|
9ae3a8 |
+ if self.tok == '{':
|
|
|
9ae3a8 |
+ self.accept()
|
|
|
9ae3a8 |
+ expr = self.get_members()
|
|
|
9ae3a8 |
+ elif self.tok == '[':
|
|
|
9ae3a8 |
+ self.accept()
|
|
|
9ae3a8 |
+ expr = self.get_values()
|
|
|
9ae3a8 |
else:
|
|
|
9ae3a8 |
- expr += line
|
|
|
9ae3a8 |
-
|
|
|
9ae3a8 |
- if expr:
|
|
|
9ae3a8 |
- yield expr
|
|
|
9ae3a8 |
+ expr = self.val
|
|
|
9ae3a8 |
+ self.accept()
|
|
|
9ae3a8 |
+ return expr
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
def parse_schema(fp):
|
|
|
9ae3a8 |
+ schema = QAPISchema(fp)
|
|
|
9ae3a8 |
exprs = []
|
|
|
9ae3a8 |
|
|
|
9ae3a8 |
- for expr in get_expr(fp):
|
|
|
9ae3a8 |
- expr_eval = evaluate(expr)
|
|
|
9ae3a8 |
-
|
|
|
9ae3a8 |
+ for expr_eval in schema.exprs:
|
|
|
9ae3a8 |
if expr_eval.has_key('enum'):
|
|
|
9ae3a8 |
add_enum(expr_eval['enum'])
|
|
|
9ae3a8 |
elif expr_eval.has_key('union'):
|
|
|
9ae3a8 |
diff --git a/tests/qapi-schema/indented-expr.out b/tests/qapi-schema/indented-expr.out
|
|
|
9ae3a8 |
index 98ae692..98af89a 100644
|
|
|
9ae3a8 |
--- a/tests/qapi-schema/indented-expr.out
|
|
|
9ae3a8 |
+++ b/tests/qapi-schema/indented-expr.out
|
|
|
9ae3a8 |
@@ -1,3 +1,3 @@
|
|
|
9ae3a8 |
-[OrderedDict([('id', 'eins')])]
|
|
|
9ae3a8 |
+[OrderedDict([('id', 'eins')]), OrderedDict([('id', 'zwei')])]
|
|
|
9ae3a8 |
[]
|
|
|
9ae3a8 |
[]
|
|
|
9ae3a8 |
diff --git a/tests/qapi-schema/missing-colon.out b/tests/qapi-schema/missing-colon.out
|
|
|
9ae3a8 |
index 50f827e..e67068c 100644
|
|
|
9ae3a8 |
--- a/tests/qapi-schema/missing-colon.out
|
|
|
9ae3a8 |
+++ b/tests/qapi-schema/missing-colon.out
|
|
|
9ae3a8 |
@@ -1,3 +1,3 @@
|
|
|
9ae3a8 |
-[OrderedDict([('enum', ','), ('data', ['good', 'bad', 'ugly'])])]
|
|
|
9ae3a8 |
-[',']
|
|
|
9ae3a8 |
+[OrderedDict([('enum', None), ('data', ['good', 'bad', 'ugly'])])]
|
|
|
9ae3a8 |
+[None]
|
|
|
9ae3a8 |
[]
|
|
|
9ae3a8 |
diff --git a/tests/qapi-schema/quoted-structural-chars.err b/tests/qapi-schema/quoted-structural-chars.err
|
|
|
9ae3a8 |
index e69de29..48c849d 100644
|
|
|
9ae3a8 |
--- a/tests/qapi-schema/quoted-structural-chars.err
|
|
|
9ae3a8 |
+++ b/tests/qapi-schema/quoted-structural-chars.err
|
|
|
9ae3a8 |
@@ -0,0 +1 @@
|
|
|
9ae3a8 |
+Crashed: <type 'exceptions.AttributeError'>
|
|
|
9ae3a8 |
diff --git a/tests/qapi-schema/quoted-structural-chars.exit b/tests/qapi-schema/quoted-structural-chars.exit
|
|
|
9ae3a8 |
index 573541a..d00491f 100644
|
|
|
9ae3a8 |
--- a/tests/qapi-schema/quoted-structural-chars.exit
|
|
|
9ae3a8 |
+++ b/tests/qapi-schema/quoted-structural-chars.exit
|
|
|
9ae3a8 |
@@ -1 +1 @@
|
|
|
9ae3a8 |
-0
|
|
|
9ae3a8 |
+1
|
|
|
9ae3a8 |
diff --git a/tests/qapi-schema/quoted-structural-chars.out b/tests/qapi-schema/quoted-structural-chars.out
|
|
|
9ae3a8 |
index 85405be..e69de29 100644
|
|
|
9ae3a8 |
--- a/tests/qapi-schema/quoted-structural-chars.out
|
|
|
9ae3a8 |
+++ b/tests/qapi-schema/quoted-structural-chars.out
|
|
|
9ae3a8 |
@@ -1,3 +0,0 @@
|
|
|
9ae3a8 |
-[OrderedDict([('key1', 'value1'), ('key2', [])])]
|
|
|
9ae3a8 |
-[]
|
|
|
9ae3a8 |
-[]
|
|
|
9ae3a8 |
--
|
|
|
9ae3a8 |
1.7.11.7
|
|
|
9ae3a8 |
|