On 14.11.19 10:15, Markus Armbruster wrote:
> Max Reitz <mre...@redhat.com> writes:
> 
>> Signed-off-by: Max Reitz <mre...@redhat.com>
>> ---
>>  tests/qapi-schema/bad-type-int.json      |  1 -
>>  tests/qapi-schema/enum-int-member.json   |  1 -
>>  scripts/qapi/common.py                   | 25 ++++++++++++++++++++----
>>  scripts/qapi/introspect.py               |  2 ++
>>  tests/qapi-schema/bad-type-int.err       |  2 +-
>>  tests/qapi-schema/enum-int-member.err    |  2 +-
>>  tests/qapi-schema/leading-comma-list.err |  2 +-
>>  7 files changed, 26 insertions(+), 9 deletions(-)
>>
>> diff --git a/tests/qapi-schema/bad-type-int.json 
>> b/tests/qapi-schema/bad-type-int.json
>> index 56fc6f8126..81355eb196 100644
>> --- a/tests/qapi-schema/bad-type-int.json
>> +++ b/tests/qapi-schema/bad-type-int.json
>> @@ -1,3 +1,2 @@
>>  # we reject an expression with a metatype that is not a string
>> -# FIXME: once the parser understands integer inputs, improve the error 
>> message
>>  { 'struct': 1, 'data': { } }
>> diff --git a/tests/qapi-schema/enum-int-member.json 
>> b/tests/qapi-schema/enum-int-member.json
>> index 6c9c32e149..6958440c6d 100644
>> --- a/tests/qapi-schema/enum-int-member.json
>> +++ b/tests/qapi-schema/enum-int-member.json
>> @@ -1,3 +1,2 @@
>>  # we reject any enum member that is not a string
>> -# FIXME: once the parser understands integer inputs, improve the error 
>> message
>>  { 'enum': 'MyEnum', 'data': [ 1 ] }
>> diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
>> index d61bfdc526..3396ea4a09 100644
>> --- a/scripts/qapi/common.py
>> +++ b/scripts/qapi/common.py
>> @@ -498,6 +498,8 @@ class QAPISchemaParser(object):
>>              raise QAPISemError(info, "Unknown pragma '%s'" % name)
>>  
>>      def accept(self, skip_comment=True):
>> +        num_match = re.compile(r'([-+]?inf|nan|[-+0-9.][0-9a-f.ex]*)')
>> +
>>          while True:
>>              self.tok = self.src[self.cursor]
>>              self.pos = self.cursor
> 
> This is yet another extension over plain JSON.  RFC 8259:
> 
>       number = [ minus ] int [ frac ] [ exp ]
>       decimal-point = %x2E       ; .
>       digit1-9 = %x31-39         ; 1-9
>       e = %x65 / %x45            ; e E
>       exp = e [ minus / plus ] 1*DIGIT
>       frac = decimal-point 1*DIGIT
>       int = zero / ( digit1-9 *DIGIT )
>       minus = %x2D               ; -
>       plus = %x2B                ; +
>       zero = %x30                ; 0
> 
> Extensions are acceptable when we have an actual use for it, and we
> document them properly.

Well, it isn’t really an extension, because this isn’t a JSON parser but
just something that accepts anything that looks like a number and then
lets Python try a conversion on it.

> Isn't the parenthesis in your regular expression redundant?

You’re right, but on second thought, maybe I should surround it by \<
and \>.

> What use do you have in mind for 'inf' and 'nan'?

I could imagine inf being a useful default value, actually.  nan,
probably not so much.

> Why accept leading '+' as in '+123'?
> 
> Why accept empty integral part as in '.123'?
> 
> Why accept '.xe.'?  Kidding you, that must be a bug in your regexp.

Well, kind of.

I wanted to accept anything that looks in any way like a number and then
let Python try to convert it.  That’s also the reason why the case comes
last.

For that reason, I decided to keep the regex as simple as possible,
because the attempted conversions would reject anything that isn’t (to
Python) a valid number later.

It was my impression that the QAPI schema isn’t really JSON anyway and
that our QAPI schema parser isn’t a JSON parser.  Under that assumption
it simply seemed useful to me to accept anything that could potentially
be a number to Python and convert it.

Now, honestly, I still don’t see the point of having a strict JSON
“parser” here, but if you insist.  Seems possible to do in a regex.

Though I do think it makes sense to support hex integers as an extension.

> Please decide what number syntax you'd like to accept, then specify it
> in docs/devel/qapi-code-gen.txt, so we can first discuss the
> specification, and then check the regexp implements it.
> 
> docs/devel/qapi-code-gen.txt update goes here:
> 
>     === Schema syntax ===
> 
>     Syntax is loosely based on JSON (http://www.ietf.org/rfc/rfc8259.txt).
>     Differences:
> 
>     * Comments: start with a hash character (#) that is not part of a
>       string, and extend to the end of the line.
> 
>     * Strings are enclosed in 'single quotes', not "double quotes".
> 
>     * Strings are restricted to printable ASCII, and escape sequences to
>       just '\\'.
> 
> --> * Numbers and null are not supported.

OK.

> Hrmm, commit 9d55380b5a "qapi: Remove null from schema language" left
> two instances in error messages behind.  I'll fix them.
> 
>> @@ -584,7 +586,22 @@ class QAPISchemaParser(object):
>>                      return
>>                  self.line += 1
>>                  self.line_pos = self.cursor
>> -            elif not self.tok.isspace():
>> +            elif self.tok.isspace():
>> +                pass
>> +            elif num_match.match(self.src[self.pos:]):
>> +                match = num_match.match(self.src[self.pos:]).group(0)
> 
> Sadly, the walrus operator is Python 3.8.
> 
>> +                try:
>> +                    self.val = int(match, 0)
>> +                except ValueError:
>> +                    try:
>> +                        self.val = float(match)
>> +                    except ValueError:
>> +                        raise QAPIParseError(self,
>> +                                '"%s" is not a valid integer or float' % 
>> match)
>> +
>> +                self.cursor += len(match) - 1
>> +                return
>> +            else:
>>                  raise QAPIParseError(self, 'Stray "%s"' % self.tok)
> 
> Any particular reason for putting the number case last?

Because the match is so broad.

>>  
>>      def get_members(self):
>> @@ -617,9 +634,9 @@ class QAPISchemaParser(object):
>>          if self.tok == ']':
>>              self.accept()
>>              return expr
>> -        if self.tok not in "{['tfn":
>> +        if self.tok not in "{['tfn-+0123456789.i":
> 
> This is getting a bit ugly.  Let's not worry about it now.
> 
>>              raise QAPIParseError(self, 'Expected "{", "[", "]", string, '
>> -                                 'boolean or "null"')
>> +                                 'boolean, number or "null"')
>>          while True:
>>              expr.append(self.get_expr(True))
>>              if self.tok == ']':
>> @@ -638,7 +655,7 @@ class QAPISchemaParser(object):
>>          elif self.tok == '[':
>>              self.accept()
>>              expr = self.get_values()
>> -        elif self.tok in "'tfn":
>> +        elif self.tok in "'tfn-+0123456789.i":
>>              expr = self.val
>>              self.accept()
>>          else:
>> diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py
>> index f62cf0a2e1..6a61dd831f 100644
>> --- a/scripts/qapi/introspect.py
>> +++ b/scripts/qapi/introspect.py
>> @@ -57,6 +57,8 @@ def to_qlit(obj, level=0, suppress_first_indent=False):
>>          ret += indent(level) + '}))'
>>      elif isinstance(obj, bool):
>>          ret += 'QLIT_QBOOL(%s)' % ('true' if obj else 'false')
>> +    elif isinstance(obj, int) and obj >= -(2 ** 63) and obj < 2 ** 63:
>> +        ret += 'QLIT_QNUM(%i)' % obj
> 
> Please explain the range check.

Will do.

>>      else:
>>          assert False                # not implemented
>>      if level > 0:
>> diff --git a/tests/qapi-schema/bad-type-int.err 
>> b/tests/qapi-schema/bad-type-int.err
>> index da89895404..e22fb4f655 100644
>> --- a/tests/qapi-schema/bad-type-int.err
>> +++ b/tests/qapi-schema/bad-type-int.err
>> @@ -1 +1 @@
>> -tests/qapi-schema/bad-type-int.json:3:13: Stray "1"
>> +tests/qapi-schema/bad-type-int.json:2: 'struct' key must have a string value
> 
> Test needs a rename, assuming it's not redundant now.

I’m not adding a test here, it’s just the value has changed in
4d42815587063d.

Thanks for reviewing!

Max

>> diff --git a/tests/qapi-schema/enum-int-member.err 
>> b/tests/qapi-schema/enum-int-member.err
>> index 071c5213d8..112175f79d 100644
>> --- a/tests/qapi-schema/enum-int-member.err
>> +++ b/tests/qapi-schema/enum-int-member.err
>> @@ -1 +1 @@
>> -tests/qapi-schema/enum-int-member.json:3:31: Stray "1"
>> +tests/qapi-schema/enum-int-member.json:2: Member of enum 'MyEnum' requires 
>> a string name
> 
> This one's name is still good.
> 
>> diff --git a/tests/qapi-schema/leading-comma-list.err 
>> b/tests/qapi-schema/leading-comma-list.err
>> index f5c870bb9c..fa9c80aa57 100644
>> --- a/tests/qapi-schema/leading-comma-list.err
>> +++ b/tests/qapi-schema/leading-comma-list.err
>> @@ -1 +1 @@
>> -tests/qapi-schema/leading-comma-list.json:2:13: Expected "{", "[", "]", 
>> string, boolean or "null"
>> +tests/qapi-schema/leading-comma-list.json:2:13: Expected "{", "[", "]", 
>> string, boolean, number or "null"


Attachment: signature.asc
Description: OpenPGP digital signature

Reply via email to