This thread contains a patchset. You're looking at the original emails,
but you may wish to use the patch review UI.
Review patch
7
[PATCH bare-py 0/7] Update bare-py to new schema
The schema in the latest BARE RFC [1] has changed. This set of patches
updates the bare-py to the newest schema version.
Please, let me know if you want the changes ordered or grouped
differently.
[1]: https://datatracker.ietf.org/doc/draft-devault-bare/
Jiri Vlasak (7):
Fix union pack, see 322e8d64
Update lex to current schema
Update parser to current schema
Make optional, list, and map nameable
Add example company test
Update example
Update bare-dump
README.md | 58 ++++----
bare/__init__.py | 18 ++-
bare/__main__.py | 29 ++--
bare/bare_ast.py | 38 ++---
bare/dump.py | 4 +-
bare/lex.py | 86 ++++++-----
bare/parser.py | 238 ++++++++++++++++++-------------
bare/test_aggregates.py | 14 +-
bare/test_encoded_messages.py | 98 +++++++++++++
bare/test_lexer.py | 148 ++++++++++---------
bare/test_parser.py | 162 +++++++++++++++------
bare/test_primitives.py | 2 +-
example/address.bin | Bin 55 -> 52 bytes
example/addressmaybe-address.bin | Bin 56 -> 53 bytes
example/cursor.bare | 8 +-
example/customer.bin | Bin 148 -> 145 bytes
example/employee.bin | Bin 164 -> 161 bytes
example/example-read.py | 5 +-
example/example.py | 11 +-
example/person-customer.bin | Bin 149 -> 146 bytes
example/person-employee.bin | Bin 165 -> 162 bytes
example/schema.bare | 46 +++---
example/schema.py | 142 +++++++++---------
23 files changed, 678 insertions(+), 429 deletions(-)
create mode 100644 bare/test_encoded_messages.py
--
2.30.2
[PATCH bare-py 1/7] Fix union pack, see 322e8d64
---
bare/__main__.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/bare/__main__.py b/bare/__main__.py
index 90bf59b..73b18db 100644
--- a/bare/__main__.py
@@ -45,7 +45,7 @@ def _gen_type(type):
result = '\t_ast = {}\n\n'.format(type.code())
result += '\t@classmethod\n'
result += '\tdef pack(cls, member):\n'
- result += '\t\treturn bare.pack(cls, member)\n\n'
+ result += '\t\treturn bare.pack(member, cls)\n\n'
result += '\t@classmethod\n'
result += '\tdef unpack(cls, data, offset=0):\n'
result += '\t\tinstance = cls()\n'
--
2.30.2
[PATCH bare-py 2/7] Update lex to current schema
---
bare/lex.py | 86 ++++++++++++++------------
bare/test_lexer.py | 148 ++++++++++++++++++++++++---------------------
2 files changed, 129 insertions(+), 105 deletions(-)
diff --git a/bare/lex.py b/bare/lex.py
index e97338b..98deaeb 100644
--- a/bare/lex.py
@@ -7,10 +7,7 @@ class LexError(Exception):
class Token(Enum):
- TTYPE = auto()
- TENUM = auto()
- TNAME = auto()
- TINTEGER = auto()
+ # primitive types
TUINT = auto()
TU8 = auto()
TU16 = auto()
@@ -24,27 +21,41 @@ class Token(Enum):
TF32 = auto()
TF64 = auto()
TBOOL = auto()
- TSTRING = auto()
+ TSTR = auto()
TDATA = auto()
TVOID = auto()
- TMAP = auto()
+ TENUM = auto()
+
+ # aggregate types
TOPTIONAL = auto()
- TLANGLE = auto()
- TRANGLE = auto()
- TLBRACE = auto()
- TRBRACE = auto()
- TLBRACKET = auto()
- TRBRACKET = auto()
- TLPAREN = auto()
- TRPAREN = auto()
- TCOMMA = auto()
- TPIPE = auto()
- TEQUAL = auto()
- TCOLON = auto()
+ TLIST = auto()
+ TMAP = auto()
+ TUNION = auto()
+ TSTRUCT = auto()
+ # user-defined types
+ TUSER = auto()
+ TNAME = auto()
-RE_TSTRING = re.compile('[a-zA-Z0-9_]+')
-RE_TINTEGER = re.compile('[0-9]+')
+ # for length
+ TLBRACKET = auto() # [
+ TRBRACKET = auto() # ]
+ TDIGIT = auto() # 0-9
+
+ # for type
+ TLANGLE = auto() # <
+ TRANGLE = auto() # >
+
+ # for members
+ TLBRACE = auto() # {
+ TRBRACE = auto() # }
+ TPIPE = auto() # |
+ TEQUAL = auto() # =
+ TCOLON = auto() # :
+
+
+RE_TSTR = re.compile('[a-zA-Z0-9_]+')
+RE_TDIGIT = re.compile('[0-9]+')
class LexedToken:
@@ -55,15 +66,12 @@ class LexedToken:
self.column = column
def __repr__(self):
- return '<LexedToken {} "{}" at {},{}>'.format(self.token, self.value, self.line, self.column)
+ return '<LexedToken {} "{}" at {},{}>'.format(
+ self.token, self.value, self.line, self.column)
def _lex_string(data, line, column):
- if data == 'type':
- return LexedToken(Token.TTYPE, "", line, column)
- elif data == 'enum':
- return LexedToken(Token.TENUM, "", line, column)
- elif data == 'uint':
+ if data == 'uint':
return LexedToken(Token.TUINT, "", line, column)
elif data == 'u8':
return LexedToken(Token.TU8, "", line, column)
@@ -89,16 +97,26 @@ def _lex_string(data, line, column):
return LexedToken(Token.TF64, "", line, column)
elif data == 'bool':
return LexedToken(Token.TBOOL, "", line, column)
- elif data == 'string':
- return LexedToken(Token.TSTRING, "", line, column)
+ elif data == 'str':
+ return LexedToken(Token.TSTR, "", line, column)
elif data == 'data':
return LexedToken(Token.TDATA, "", line, column)
elif data == 'void':
return LexedToken(Token.TVOID, "", line, column)
+ elif data == 'enum':
+ return LexedToken(Token.TENUM, "", line, column)
elif data == 'optional':
return LexedToken(Token.TOPTIONAL, "", line, column)
+ elif data == 'list':
+ return LexedToken(Token.TLIST, "", line, column)
elif data == 'map':
return LexedToken(Token.TMAP, "", line, column)
+ elif data == 'union':
+ return LexedToken(Token.TUNION, "", line, column)
+ elif data == 'struct':
+ return LexedToken(Token.TSTRUCT, "", line, column)
+ elif data == 'type':
+ return LexedToken(Token.TUSER, "", line, column)
else:
return LexedToken(Token.TNAME, data, line, column)
@@ -123,13 +141,13 @@ def lex_schema(data):
pointer += 1
continue
elif data[pointer].isalpha():
- part = RE_TSTRING.search(data[pointer:])[0]
+ part = RE_TSTR.search(data[pointer:])[0]
yield _lex_string(part, line, column)
column += len(part) - 1
pointer += len(part) - 1
elif data[pointer].isdigit():
- part = RE_TINTEGER.search(data[pointer:])[0]
- yield LexedToken(Token.TINTEGER, part, line, column)
+ part = RE_TDIGIT.search(data[pointer:])[0]
+ yield LexedToken(Token.TDIGIT, part, line, column)
column += len(part) - 1
pointer += len(part) - 1
elif data[pointer] == '#':
@@ -151,12 +169,6 @@ def lex_schema(data):
yield LexedToken(Token.TLBRACKET, "", line, column)
elif data[pointer] == ']':
yield LexedToken(Token.TRBRACKET, "", line, column)
- elif data[pointer] == '(':
- yield LexedToken(Token.TLPAREN, "", line, column)
- elif data[pointer] == ')':
- yield LexedToken(Token.TRPAREN, "", line, column)
- elif data[pointer] == ',':
- yield LexedToken(Token.TCOMMA, "", line, column)
elif data[pointer] == '|':
yield LexedToken(Token.TPIPE, "", line, column)
elif data[pointer] == '=':
diff --git a/bare/test_lexer.py b/bare/test_lexer.py
index 3f3f72e..3ee0bfb 100644
--- a/bare/test_lexer.py
@@ -1,76 +1,76 @@
from unittest import TestCase
-from bare.lex import lex_schema, LexedToken, Token
+from bare.lex import lex_schema, Token
class Test(TestCase):
def test_basic_lexing(self):
schema = """
- type PublicRSAKey data<128>
- type PublicED25519Key data<32>
- type Time string # ISO 8601
+ type PublicRSAKey data[128]
+ type PublicED25519Key data[32]
+ type Time str # ISO 8601
type Unemployed void
-
- enum Department {
+
+ type Department enum {
ACCOUNTING
ADMINISTRATION
CUSTOMER_SERVICE
DEVELOPMENT
-
+
# Reserved for the CEO
JSMITH = 99
}
-
- type Customer {
- name: string
+
+ type Address list<str>[4] # street, city, state, country
+
+ type Customer struct {
+ name: str
+ email: str
address: Address
- orders: []{
+ orders: list<struct {
orderId: i64
quantity: i32
- }
- metadata: map[string]data
+ }>
+ metadata: map<str><data>
}
-
- type Employee {
- name: string
+
+ type Employee struct {
+ name: str
+ email: str
address: Address
department: Department
hireDate: Time
- publicKey: optional<(PublicRSAKey|PublicED25519Key)>
- metadata: map[string]data
- }
-
- type Person (Customer | Employee | Unemployed)
-
- type Address {
- address: [4]string
- city: string
+ publicKey: optional<union {PublicRSAKey|PublicED25519Key}>
+ metadata: map<str><data>
}
+
+ type Person union {Customer | Employee | Unemployed}
"""
tokens = list(lex_schema(schema))
expected = [
- (Token.TTYPE, ""),
+ (Token.TUSER, ""),
(Token.TNAME, "PublicRSAKey"),
(Token.TDATA, ""),
- (Token.TLANGLE, ""),
- (Token.TINTEGER, "128"),
- (Token.TRANGLE, ""),
- (Token.TTYPE, ""),
+ (Token.TLBRACKET, ""),
+ (Token.TDIGIT, "128"),
+ (Token.TRBRACKET, ""),
+ (Token.TUSER, ""),
(Token.TNAME, "PublicED25519Key"),
(Token.TDATA, ""),
- (Token.TLANGLE, ""),
- (Token.TINTEGER, "32"),
- (Token.TRANGLE, ""),
- (Token.TTYPE, ""),
+ (Token.TLBRACKET, ""),
+ (Token.TDIGIT, "32"),
+ (Token.TRBRACKET, ""),
+ (Token.TUSER, ""),
(Token.TNAME, "Time"),
- (Token.TSTRING, ""),
- (Token.TTYPE, ""),
+ (Token.TSTR, ""),
+ (Token.TUSER, ""),
(Token.TNAME, "Unemployed"),
(Token.TVOID, ""),
+ (Token.TUSER, ""),
+ (Token.TNAME, "Department"),
(Token.TENUM, ""),
- (Token.TNAME, "Department"),
(Token.TLBRACE, ""),
(Token.TNAME, "ACCOUNTING"),
(Token.TNAME, "ADMINISTRATION"),
@@ -78,22 +78,37 @@ class Test(TestCase):
(Token.TNAME, "DEVELOPMENT"),
(Token.TNAME, "JSMITH"),
(Token.TEQUAL, ""),
- (Token.TINTEGER, "99"),
+ (Token.TDIGIT, "99"),
(Token.TRBRACE, ""),
- (Token.TTYPE, ""),
+ (Token.TUSER, ""),
+ (Token.TNAME, "Address"),
+ (Token.TLIST, ""),
+ (Token.TLANGLE, ""),
+ (Token.TSTR, ""),
+ (Token.TRANGLE, ""),
+ (Token.TLBRACKET, ""),
+ (Token.TDIGIT, "4"),
+ (Token.TRBRACKET, ""),
+
+ (Token.TUSER, ""),
(Token.TNAME, "Customer"),
+ (Token.TSTRUCT, ""),
(Token.TLBRACE, ""),
(Token.TNAME, "name"),
(Token.TCOLON, ""),
- (Token.TSTRING, ""),
+ (Token.TSTR, ""),
+ (Token.TNAME, "email"),
+ (Token.TCOLON, ""),
+ (Token.TSTR, ""),
(Token.TNAME, "address"),
(Token.TCOLON, ""),
(Token.TNAME, "Address"),
(Token.TNAME, "orders"),
(Token.TCOLON, ""),
- (Token.TLBRACKET, ""),
- (Token.TRBRACKET, ""),
+ (Token.TLIST, ""),
+ (Token.TLANGLE, ""),
+ (Token.TSTRUCT, ""),
(Token.TLBRACE, ""),
(Token.TNAME, "orderId"),
(Token.TCOLON, ""),
@@ -102,21 +117,28 @@ class Test(TestCase):
(Token.TCOLON, ""),
(Token.TI32, ""),
(Token.TRBRACE, ""),
+ (Token.TRANGLE, ""),
(Token.TNAME, "metadata"),
(Token.TCOLON, ""),
(Token.TMAP, ""),
- (Token.TLBRACKET, ""),
- (Token.TSTRING, ""),
- (Token.TRBRACKET, ""),
+ (Token.TLANGLE, ""),
+ (Token.TSTR, ""),
+ (Token.TRANGLE, ""),
+ (Token.TLANGLE, ""),
(Token.TDATA, ""),
+ (Token.TRANGLE, ""),
(Token.TRBRACE, ""),
- (Token.TTYPE, ""),
+ (Token.TUSER, ""),
(Token.TNAME, "Employee"),
+ (Token.TSTRUCT, ""),
(Token.TLBRACE, ""),
(Token.TNAME, "name"),
(Token.TCOLON, ""),
- (Token.TSTRING, ""),
+ (Token.TSTR, ""),
+ (Token.TNAME, "email"),
+ (Token.TCOLON, ""),
+ (Token.TSTR, ""),
(Token.TNAME, "address"),
(Token.TCOLON, ""),
(Token.TNAME, "Address"),
@@ -130,43 +152,33 @@ class Test(TestCase):
(Token.TCOLON, ""),
(Token.TOPTIONAL, ""),
(Token.TLANGLE, ""),
- (Token.TLPAREN, ""),
+ (Token.TUNION, ""),
+ (Token.TLBRACE, ""),
(Token.TNAME, "PublicRSAKey"),
(Token.TPIPE, ""),
(Token.TNAME, "PublicED25519Key"),
- (Token.TRPAREN, ""),
+ (Token.TRBRACE, ""),
(Token.TRANGLE, ""),
(Token.TNAME, "metadata"),
(Token.TCOLON, ""),
(Token.TMAP, ""),
- (Token.TLBRACKET, ""),
- (Token.TSTRING, ""),
- (Token.TRBRACKET, ""),
+ (Token.TLANGLE, ""),
+ (Token.TSTR, ""),
+ (Token.TRANGLE, ""),
+ (Token.TLANGLE, ""),
(Token.TDATA, ""),
+ (Token.TRANGLE, ""),
(Token.TRBRACE, ""),
- (Token.TTYPE, ""),
+ (Token.TUSER, ""),
(Token.TNAME, "Person"),
- (Token.TLPAREN, ""),
+ (Token.TUNION, ""),
+ (Token.TLBRACE, ""),
(Token.TNAME, "Customer"),
(Token.TPIPE, ""),
(Token.TNAME, "Employee"),
(Token.TPIPE, ""),
(Token.TNAME, "Unemployed"),
- (Token.TRPAREN, ""),
-
- (Token.TTYPE, ""),
- (Token.TNAME, "Address"),
- (Token.TLBRACE, ""),
- (Token.TNAME, "address"),
- (Token.TCOLON, ""),
- (Token.TLBRACKET, ""),
- (Token.TINTEGER, "4"),
- (Token.TRBRACKET, ""),
- (Token.TSTRING, ""),
- (Token.TNAME, "city"),
- (Token.TCOLON, ""),
- (Token.TSTRING, ""),
(Token.TRBRACE, ""),
]
--
2.30.2
[PATCH bare-py 3/7] Update parser to current schema
---
bare/__init__.py | 14 +--
bare/bare_ast.py | 38 ++++---
bare/parser.py | 238 +++++++++++++++++++++++-----------------
bare/test_aggregates.py | 14 +--
bare/test_parser.py | 162 +++++++++++++++++++--------
bare/test_primitives.py | 2 +-
6 files changed, 291 insertions(+), 177 deletions(-)
diff --git a/bare/__init__.py b/bare/__init__.py
index 02cb298..4bfec34 100644
--- a/bare/__init__.py
@@ -2,7 +2,7 @@ import struct
import sys
from enum import EnumMeta
-from bare.bare_ast import StructType, BarePrimitive, TypeKind, NamedType, ArrayType, MapType, OptionalType, UnionType
+from bare.bare_ast import StructType, BarePrimitive, TypeKind, NamedType, ListType, MapType, OptionalType, UnionType
def _pack_varint(data, signed=False):
@@ -84,8 +84,8 @@ def _pack_primitive(primitive, data):
return struct.pack('<?', data)
elif primitive.type == TypeKind.Void:
return b''
- elif primitive.type == TypeKind.String or primitive.type == TypeKind.Data:
- if primitive.type == TypeKind.String:
+ elif primitive.type == TypeKind.Str or primitive.type == TypeKind.Data:
+ if primitive.type == TypeKind.Str:
data = data.encode('utf-8')
length = len(data)
result = bytes()
@@ -130,11 +130,11 @@ def _unpack_primitive(primitive, data, offset):
return temp == 1, offset + 1
elif primitive.type == TypeKind.Void:
return None, offset
- elif primitive.type == TypeKind.String or primitive.type == TypeKind.Data:
+ elif primitive.type == TypeKind.Str or primitive.type == TypeKind.Data:
length, offset = _unpack_varint(data, offset)
result = struct.unpack_from('<{}s'.format(length), data, offset)[0]
offset += len(result)
- if primitive.type == TypeKind.String:
+ if primitive.type == TypeKind.Str:
result = result.decode('utf-8')
return result, offset
elif primitive.type == TypeKind.DataFixed:
@@ -168,7 +168,7 @@ def _pack_type(ast_node, data, module):
return _pack_type(BarePrimitive(TypeKind.UINT), data.value, module)
else:
return _pack_type(referenced._ast, data, module)
- elif isinstance(ast_node, ArrayType):
+ elif isinstance(ast_node, ListType):
result = bytes()
if ast_node.length is None:
result += _pack_primitive(BarePrimitive(TypeKind.UINT), len(data))
@@ -227,7 +227,7 @@ def _unpack_type(ast_node, instance, module, data, offset):
instance = referenced()
instance, offset = _unpack_type(referenced._ast, instance, module, data, offset)
return instance, offset
- elif isinstance(ast_node, ArrayType):
+ elif isinstance(ast_node, ListType):
if ast_node.length is None:
length, offset = _unpack_primitive(BarePrimitive(TypeKind.UINT), data, offset)
else:
diff --git a/bare/bare_ast.py b/bare/bare_ast.py
index 8d93779..1e61279 100644
--- a/bare/bare_ast.py
@@ -3,6 +3,7 @@ from collections import OrderedDict
class TypeKind(Enum):
+ # primitive types
UINT = auto()
U8 = auto()
U16 = auto()
@@ -16,16 +17,21 @@ class TypeKind(Enum):
F32 = auto()
F64 = auto()
Bool = auto()
- String = auto()
+ Str = auto()
Data = auto()
DataFixed = auto()
Void = auto()
+ # Enum is separate type
+
+ # aggregate types
Optional = auto()
- Array = auto()
- ArrayFixed = auto()
+ List = auto()
+ ListFixed = auto()
Map = auto()
- TaggedUnion = auto()
+ Union = auto()
Struct = auto()
+
+ # user-defined types
UserType = auto()
@@ -43,12 +49,10 @@ class BareType:
class BareEnum:
def __init__(self):
- self.name = None
- self.type = None
self.values = []
def __repr__(self):
- result = '<BareEnum {} {}\n'.format(self.name, self.type)
+ result = '<BareEnum\n'
for value in self.values:
result += ' ' + repr(value) + '\n'
return result + '>'
@@ -69,10 +73,10 @@ class BarePrimitive:
return '<BarePrimitive {}<{}> >'.format(self.type, self.length)
def code(self):
- if self.length is not None:
- return 'BarePrimitive({}, {})'.format(self.type, self.length)
- else:
+ if self.length is None:
return 'BarePrimitive({})'.format(self.type)
+ else:
+ return 'BarePrimitive({}, {})'.format(self.type, self.length)
class OptionalType:
@@ -115,22 +119,22 @@ class StructType:
return result + "))"
-class ArrayType:
+class ListType:
def __init__(self, subtype, length=None):
self.subtype = subtype
self.length = length
def __repr__(self):
if self.length is None:
- return '<ArrayType {}>'.format(self.subtype)
+ return '<ListType {}>'.format(self.subtype)
else:
- return '<ArrayType {}[{}]>'.format(self.subtype, self.length)
+ return '<ListType {}[{}]>'.format(self.subtype, self.length)
def code(self):
if self.length is None:
- return 'ArrayType({})'.format(self.subtype.code())
+ return 'ListType({})'.format(self.subtype.code())
else:
- return 'ArrayType({}, {})'.format(self.subtype.code(), self.length)
+ return 'ListType({}, {})'.format(self.subtype.code(), self.length)
class MapType:
@@ -142,7 +146,9 @@ class MapType:
return '<MapType [{}] {}>'.format(self.keytype, self.valuetype)
def code(self):
- return 'MapType({}, {})'.format(self.keytype.code(), self.valuetype.code())
+ return 'MapType({}, {})'.format(
+ self.keytype.code(),
+ self.valuetype.code())
class UnionType:
diff --git a/bare/parser.py b/bare/parser.py
index b6c1d31..f50e278 100644
--- a/bare/parser.py
@@ -1,13 +1,29 @@
from bare.lex import lex_schema, Token
-from bare.bare_ast import BareType, BareEnum, BarePrimitive, TypeKind, OptionalType, StructType, NamedType, ArrayType, \
- MapType, EnumValue, UnionType, UnionValue
+from bare.bare_ast import (
+ TypeKind,
+ BareType,
+ BarePrimitive,
+ BareEnum,
+ EnumValue,
+ OptionalType,
+ ListType,
+ MapType,
+ UnionType,
+ UnionValue,
+ StructType,
+ NamedType,
+)
class UnexpectedTokenError(Exception):
def __init__(self, token, expected):
self.token = token
self.expected = expected
- msg = 'Unexpected token {}, expected {} (at {}, {})'.format(token.token, expected, token.line, token.column)
+ msg = 'Unexpected token {}, expected {} (at {}, {})'.format(
+ token.token,
+ expected,
+ token.line,
+ token.column)
super().__init__(msg)
@@ -51,71 +67,32 @@ def parse(schema):
types = []
while True:
try:
- st = _parse_schema_type(tokens)
+ st = _parse_user_type(tokens)
except StopIteration:
break
types.append(st)
return types
-def _parse_schema_type(tokens):
+def _parse_user_type(tokens):
token = tokens.__next__()
+ if token.token != Token.TUSER:
+ raise UnexpectedTokenError(token, "'type'")
- if token.token == Token.TTYPE:
- return _parse_schema_user_type(tokens)
- elif token.token == Token.TENUM:
- return _parse_schema_user_enum(tokens)
- else:
- raise UnexpectedTokenError(token, "'type' or 'enum'")
-
-
-def _parse_schema_user_type(tokens):
token = tokens.__next__()
if token.token != Token.TNAME:
raise UnexpectedTokenError(token, 'type name')
new_type = BareType()
new_type.name = token.value
- new_type.type = _parse_type(tokens)
+ new_type.type = _parse_any_type(tokens)
return new_type
-def _parse_schema_user_enum(tokens):
+def _parse_any_type(tokens):
token = tokens.__next__()
- if token.token != Token.TNAME:
- raise UnexpectedTokenError(token, 'enum name')
- new_enum = BareEnum()
- new_enum.name = token.value
- token = tokens.__next__()
- if token.token != Token.TLBRACE:
- raise UnexpectedTokenError(token, '{')
-
- value = 0
- while True:
- token = tokens.__next__()
- if token.token == Token.TRBRACE:
- break
- if token.token != Token.TNAME:
- raise UnexpectedTokenError(token, 'value name')
-
- value_name = token.value
-
- if tokens.peek.token == Token.TEQUAL:
- tokens.__next__()
- token = tokens.__next__()
- if token.token != Token.TINTEGER:
- raise UnexpectedTokenError(token, 'integer')
- new_enum.values.append(EnumValue(value_name, int(token.value)))
- value = int(token.value) + 1
- else:
- new_enum.values.append(EnumValue(value_name, value))
- value += 1
- return new_enum
-
-
-def _parse_type(tokens):
- token = tokens.__next__()
+ # primitive types
if token.token == Token.TUINT:
return BarePrimitive(TypeKind.UINT)
elif token.token == Token.TU8:
@@ -142,89 +119,149 @@ def _parse_type(tokens):
return BarePrimitive(TypeKind.F64)
elif token.token == Token.TBOOL:
return BarePrimitive(TypeKind.Bool)
- elif token.token == Token.TSTRING:
- return BarePrimitive(TypeKind.String)
+ elif token.token == Token.TSTR:
+ return BarePrimitive(TypeKind.Str)
+
+ elif token.token == Token.TDATA:
+ length = _parse_maybe_length(tokens)
+ if length:
+ return BarePrimitive(TypeKind.DataFixed, length=length)
+ else:
+ return BarePrimitive(TypeKind.Data)
+
elif token.token == Token.TVOID:
return BarePrimitive(TypeKind.Void)
+
+ elif token.token == Token.TENUM:
+ token = tokens.__next__()
+ if token.token != Token.TLBRACE:
+ raise UnexpectedTokenError(token, "{")
+ return _parse_enum_values(tokens)
+
+ # aggregate types
elif token.token == Token.TOPTIONAL:
- token = tokens.__next__()
- if token.token != Token.TLANGLE:
- raise UnexpectedTokenError(token, "<")
+ return OptionalType(_parse_type(tokens))
+
+ elif token.token == Token.TLIST:
subtype = _parse_type(tokens)
- token = tokens.__next__()
- if token.token != Token.TRANGLE:
- raise UnexpectedTokenError(Token, ">")
- return OptionalType(subtype)
- elif token.token == Token.TDATA:
- if tokens.peek.token == Token.TLANGLE:
- tokens.__next__()
- token = tokens.__next__()
- if token.token != Token.TINTEGER:
- raise UnexpectedTokenError(token, 'length')
- length = int(token.value)
- token = tokens.__next__()
- if token.token != Token.TRANGLE:
- raise UnexpectedTokenError(token, '>')
- return BarePrimitive(TypeKind.DataFixed, length=length)
+ length = _parse_maybe_length(tokens)
+ if length:
+ return ListType(subtype, length)
else:
- return BarePrimitive(TypeKind.Data)
- elif token.token == Token.TLBRACE:
- return _parse_struct(tokens)
- elif token.token == Token.TLBRACKET:
- token = tokens.__next__()
- if token.token == Token.TRBRACKET:
- subtype = _parse_type(tokens)
- return ArrayType(subtype)
- elif token.token == Token.TINTEGER:
- length = int(token.value)
- token = tokens.__next__()
- if token.token != Token.TRBRACKET:
- raise UnexpectedTokenError(token, ']')
- subtype = _parse_type(tokens)
- return ArrayType(subtype, length)
- else:
- raise UnexpectedTokenError(token, "']' or array length")
+ return ListType(subtype)
+
elif token.token == Token.TMAP:
- token = tokens.__next__()
- if token.token != Token.TLBRACKET:
- raise UnexpectedTokenError(token, '[')
keytype = _parse_type(tokens)
- token = tokens.__next__()
- if token.token != Token.TRBRACKET:
- raise UnexpectedTokenError(token, ']')
valuetype = _parse_type(tokens)
return MapType(keytype, valuetype)
- elif token.token == Token.TLPAREN:
+
+ elif token.token == Token.TUNION:
+ token = tokens.__next__()
+ if token.token != Token.TLBRACE:
+ raise UnexpectedTokenError(token, "{")
+ if tokens.peek.token == Token.TPIPE:
+ tokens.__next__() # | before first member
types = []
index = 0
while True:
- unionvalue = UnionValue(_parse_type(tokens), index)
+ unionvalue = UnionValue(_parse_any_type(tokens), index)
token = tokens.__next__()
if token.token == Token.TEQUAL:
token = tokens.__next__()
- if token.token != Token.TINTEGER:
+ if token.token != Token.TDIGIT:
raise UnexpectedTokenError(token, 'integer')
index = int(token.value)
+ if index <= unionvalue.value:
+ raise ValueError("Union tags must be ascending,"
+ f" but {index} <= {unionvalue.value}")
unionvalue.value = index
token = tokens.__next__()
-
if token.token == Token.TPIPE:
types.append(unionvalue)
+ if tokens.peek.token == Token.TRBRACE: # | after last member
+ tokens.__next__()
+ break
index += 1
continue
- elif token.token == Token.TRPAREN:
+ elif token.token == Token.TRBRACE:
types.append(unionvalue)
break
else:
- raise UnexpectedTokenError(token, "'|' or ')'")
+ raise UnexpectedTokenError(token, "'|' or '}'")
return UnionType(types)
+
+ elif token.token == Token.TSTRUCT:
+ return _parse_struct(tokens)
+
+ # user-defined types
elif token.token == Token.TNAME:
return NamedType(token.value)
+
else:
raise UnexpectedTokenError(token, 'a type')
+def _parse_maybe_length(tokens):
+ if tokens.peek.token == Token.TLBRACKET:
+ token = tokens.__next__()
+ if token.token != Token.TLBRACKET:
+ raise UnexpectedTokenError(token, "[")
+ token = tokens.__next__()
+ if token.token != Token.TDIGIT:
+ raise UnexpectedTokenError(token, "length")
+ length = int(token.value)
+ token = tokens.__next__()
+ if token.token != Token.TRBRACKET:
+ raise UnexpectedTokenError(token, "]")
+ return length
+ else:
+ return False
+
+
+def _parse_enum_values(tokens):
+ new_enum = BareEnum()
+ value = 0
+ while True:
+ token = tokens.__next__()
+ if token.token == Token.TRBRACE:
+ break
+ if token.token != Token.TNAME:
+ raise UnexpectedTokenError(token, "enum value name")
+
+ value_name = token.value
+
+ if tokens.peek.token == Token.TEQUAL:
+ tokens.__next__()
+ token = tokens.__next__()
+ if token.token != Token.TDIGIT:
+ raise UnexpectedTokenError(token, "digit")
+ index = int(token.value)
+ if index <= value:
+ raise ValueError("Enum values must be ascending,"
+ f" but {index} <= {value}")
+ new_enum.values.append(EnumValue(value_name, index))
+ value = index + 1
+ else:
+ new_enum.values.append(EnumValue(value_name, value))
+ value += 1
+ return new_enum
+
+
+def _parse_type(tokens):
+ token = tokens.__next__()
+ if token.token != Token.TLANGLE:
+ raise UnexpectedTokenError(token, "<")
+ any_type = _parse_any_type(tokens)
+ token = tokens.__next__()
+ if token.token != Token.TRANGLE:
+ raise UnexpectedTokenError(Token, ">")
+ return any_type
+
+
def _parse_struct(tokens):
+ token = tokens.__next__()
+ if token.token != Token.TLBRACE:
+ raise UnexpectedTokenError(token, "{")
result = StructType()
while True:
token = tokens.__next__()
@@ -242,10 +279,7 @@ def _parse_struct(tokens):
token = tokens.__next__()
result.fields[name] = NamedType(token.value)
else:
- result.fields[name] = _parse_type(tokens)
-
- if tokens.peek.token == Token.TCOMMA:
- tokens.__next__()
+ result.fields[name] = _parse_any_type(tokens)
if __name__ == '__main__':
diff --git a/bare/test_aggregates.py b/bare/test_aggregates.py
index 2e6ffa5..af7fabb 100644
--- a/bare/test_aggregates.py
@@ -3,7 +3,7 @@ from unittest import TestCase
from hypothesis import given, note, seed, example
from hypothesis.strategies import integers, lists, text, dictionaries
-from bare import BarePrimitive, TypeKind, OptionalType, _pack_type, _unpack_type, ArrayType, MapType
+from bare import BarePrimitive, TypeKind, OptionalType, _pack_type, _unpack_type, ListType, MapType
class Test(TestCase):
@@ -20,7 +20,7 @@ class Test(TestCase):
@given(lists(integers(min_value=0, max_value=255)))
def test_array_fixedsubtype(self, numbers):
- ast = ArrayType(BarePrimitive(TypeKind.U8))
+ ast = ListType(BarePrimitive(TypeKind.U8))
packed = _pack_type(ast, numbers, None)
note(repr(packed))
result = _unpack_type(ast, None, None, packed, 0)[0]
@@ -29,7 +29,7 @@ class Test(TestCase):
@given(lists(text()))
def test_array_nonfixedsubtype(self, texts):
- ast = ArrayType(BarePrimitive(TypeKind.String))
+ ast = ListType(BarePrimitive(TypeKind.Str))
packed = _pack_type(ast, texts, None)
note(repr(packed))
result = _unpack_type(ast, None, None, packed, 0)[0]
@@ -38,7 +38,7 @@ class Test(TestCase):
@given(lists(integers(min_value=0, max_value=255), min_size=64, max_size=64))
def test_fixedarray_fixedsubtype(self, numbers):
- ast = ArrayType(BarePrimitive(TypeKind.U8), length=64)
+ ast = ListType(BarePrimitive(TypeKind.U8), length=64)
packed = _pack_type(ast, numbers, None)
note(repr(packed))
result = _unpack_type(ast, None, None, packed, 0)[0]
@@ -47,7 +47,7 @@ class Test(TestCase):
@given(lists(text(), min_size=32, max_size=32))
def test_fixedarray_nonfixedsubtype(self, texts):
- ast = ArrayType(BarePrimitive(TypeKind.String), length=32)
+ ast = ListType(BarePrimitive(TypeKind.Str), length=32)
packed = _pack_type(ast, texts, None)
note(repr(packed))
result = _unpack_type(ast, None, None, packed, 0)[0]
@@ -56,7 +56,7 @@ class Test(TestCase):
@given(dictionaries(integers(min_value=0, max_value=255), text()))
def test_map_int_string(self, map):
- ast = MapType(BarePrimitive(TypeKind.U8), BarePrimitive(TypeKind.String))
+ ast = MapType(BarePrimitive(TypeKind.U8), BarePrimitive(TypeKind.Str))
packed = _pack_type(ast, map, None)
note(repr(packed))
result = _unpack_type(ast, None, None, packed, 0)[0]
@@ -65,7 +65,7 @@ class Test(TestCase):
@given(dictionaries(text(), text()))
def test_map_string_string(self, map):
- ast = MapType(BarePrimitive(TypeKind.String), BarePrimitive(TypeKind.String))
+ ast = MapType(BarePrimitive(TypeKind.Str), BarePrimitive(TypeKind.Str))
packed = _pack_type(ast, map, None)
note(repr(packed))
result = _unpack_type(ast, None, None, packed, 0)[0]
diff --git a/bare/test_parser.py b/bare/test_parser.py
index 03f70e0..9639b29 100644
--- a/bare/test_parser.py
@@ -1,6 +1,17 @@
from unittest import TestCase
-from bare.bare_ast import BareType, TypeKind, BarePrimitive, BareEnum, StructType, NamedType
+from bare.bare_ast import (
+ BareType,
+ TypeKind,
+ BarePrimitive,
+ BareEnum,
+ StructType,
+ NamedType,
+ ListType,
+ OptionalType,
+ UnionType,
+ MapType,
+)
from bare.lex import lex_schema, LexedToken, Token
from bare.parser import parse
@@ -8,51 +19,50 @@ from bare.parser import parse
class Test(TestCase):
def test_basic_parsing(self):
schema = """
- type PublicRSAKey data<128>
- type PublicED25519Key data<32>
- type Time string # ISO 8601
+ type PublicRSAKey data[128]
+ type PublicED25519Key data[32]
+ type Time str # ISO 8601
type Unemployed void
-
- enum Department {
+
+ type Department enum {
ACCOUNTING
ADMINISTRATION
CUSTOMER_SERVICE = 10
DEVELOPMENT
-
+
# Reserved for the CEO
JSMITH = 99
}
-
- type Customer {
- name: string
+
+ type Address list<str>[4] # street, city, state, country
+
+ type Customer struct {
+ name: str
+ email: str
address: Address
- orders: []{
+ orders: list<struct {
orderId: i64
quantity: i32
- }
- metadata: map[string]data
+ }>
+ metadata: map<str><data>
}
-
- type Employee {
- name: string
+
+ type Employee struct {
+ name: str
+ email: str
address: Address
department: Department
hireDate: Time
- publicKey: optional<(PublicRSAKey|PublicED25519Key)>
- metadata: map[string]data
- }
-
- type Person (Customer | Employee | Unemployed)
-
- type Address {
- address: [4]string
- city: string
+ publicKey: optional<union {PublicRSAKey|PublicED25519Key}>
+ metadata: map<str><data>
}
+
+ type Person union {Customer | Employee | Unemployed}
"""
types = parse(schema)
- # type PublicRSAKey data<128>
+ # type PublicRSAKey data[128]
type = types[0]
types = types[1:]
self.assertIsInstance(type, BareType)
@@ -61,7 +71,7 @@ class Test(TestCase):
self.assertEqual(TypeKind.DataFixed, type.type.type)
self.assertEqual(128, type.type.length)
- # type PublicED25519Key data<32>
+ # type PublicED25519Key data[32]
type = types[0]
types = types[1:]
self.assertIsInstance(type, BareType)
@@ -70,13 +80,13 @@ class Test(TestCase):
self.assertEqual(TypeKind.DataFixed, type.type.type)
self.assertEqual(32, type.type.length)
- # type Time string # ISO 8601
+ # type Time str # ISO 8601
type = types[0]
types = types[1:]
self.assertIsInstance(type, BareType)
self.assertEqual("Time", type.name)
self.assertIsInstance(type.type, BarePrimitive)
- self.assertEqual(TypeKind.String, type.type.type)
+ self.assertEqual(TypeKind.Str, type.type.type)
self.assertEqual(None, type.type.length)
# type Unemployed void
@@ -89,20 +99,33 @@ class Test(TestCase):
self.assertEqual(None, type.type.length)
# enum Department
- type = types[0]
+ t = types[0]
types = types[1:]
- self.assertIsInstance(type, BareEnum)
- self.assertEqual("Department", type.name)
- self.assertEqual("ACCOUNTING", type.values[0].name)
- self.assertEqual("ADMINISTRATION", type.values[1].name)
- self.assertEqual("CUSTOMER_SERVICE", type.values[2].name)
- self.assertEqual("DEVELOPMENT", type.values[3].name)
- self.assertEqual("JSMITH", type.values[4].name)
- self.assertEqual(0, type.values[0].value)
- self.assertEqual(1, type.values[1].value)
- self.assertEqual(10, type.values[2].value)
- self.assertEqual(11, type.values[3].value)
- self.assertEqual(99, type.values[4].value)
+ isinstance(t, BareType)
+ isinstance(t, NamedType)
+ assert t.name == "Department"
+ assert isinstance(t.type, BareEnum)
+ t = t.type
+ expected = [
+ ["ACCOUNTING", 0],
+ ["ADMINISTRATION", 1],
+ ["CUSTOMER_SERVICE", 10],
+ ["DEVELOPMENT", 11],
+ ["JSMITH", 99]]
+ for v, e in zip(t.values, expected):
+ assert v.name == e[0]
+ assert v.value == e[1]
+
+ # type Address
+ t = types[0]
+ types = types[1:]
+ assert isinstance(t, BareType)
+ assert t.name == "Address"
+ assert isinstance(t.type, ListType)
+ t = t.type
+ assert isinstance(t.subtype, BarePrimitive)
+ assert t.subtype.type == TypeKind.Str
+ assert t.length == 4
# type Customer
type = types[0]
@@ -115,9 +138,60 @@ class Test(TestCase):
self.assertIn('orders', type.type.fields)
self.assertIn('metadata', type.type.fields)
self.assertIsInstance(type.type.fields['name'], BarePrimitive)
- self.assertEqual(TypeKind.String, type.type.fields['name'].type)
+ self.assertEqual(TypeKind.Str, type.type.fields['name'].type)
self.assertEqual(None, type.type.fields['name'].length)
self.assertIsInstance(type.type.fields['address'], NamedType)
self.assertEqual("Address", type.type.fields['address'].name)
- pass
+ # type Employee
+ t = types[0]
+ types = types[1:]
+ assert isinstance(t, BareType)
+ assert t.name == "Employee"
+ assert isinstance(t.type, StructType)
+ t = t.type
+
+ for fn in ["name", "email"]:
+ assert fn in t.fields
+ f = t.fields[fn]
+ assert isinstance(f, BarePrimitive)
+ assert f.type == TypeKind.Str
+
+ for fn, tn in zip(
+ ["address", "department", "hireDate"],
+ ["Address", "Department", "Time"]):
+ assert fn in t.fields
+ f = t.fields[fn]
+ assert isinstance(f, NamedType)
+ assert f.name == tn
+
+ assert "publicKey" in t.fields
+ st = t.fields["publicKey"]
+ assert isinstance(st, OptionalType)
+ assert isinstance(st.subtype, UnionType)
+ expected = [["PublicRSAKey", 0], ["PublicED25519Key", 1]]
+ for uv, ev in zip(st.subtype.types, expected):
+ assert isinstance(uv.type, NamedType)
+ assert uv.type.name == ev[0]
+ assert uv.value == ev[1]
+
+ assert "metadata" in t.fields
+ st = t.fields["metadata"]
+ assert isinstance(st, MapType)
+ assert isinstance(st.keytype, BarePrimitive)
+ assert isinstance(st.valuetype, BarePrimitive)
+
+ # type Person
+ t = types[0]
+ types = types[1:]
+ assert isinstance(t, BareType)
+ assert t.name == "Person"
+ assert isinstance(t.type, UnionType)
+ t = t.type
+ expected = [["Customer", 0], ["Employee", 1], ["Unemployed", 2]]
+ for uv, ev in zip(t.types, expected):
+ assert isinstance(uv.type, NamedType)
+ assert uv.type.name == ev[0]
+ assert uv.value == ev[1]
+
+ assert len(types) == 0
diff --git a/bare/test_primitives.py b/bare/test_primitives.py
index badec6f..da58ee5 100644
--- a/bare/test_primitives.py
@@ -144,7 +144,7 @@ class Test(TestCase):
@given(text())
def test_string(self, data):
- ast = BarePrimitive(TypeKind.String)
+ ast = BarePrimitive(TypeKind.Str)
packed = _pack_primitive(ast, data)
note(repr(packed))
result = _unpack_primitive(ast, packed, 0)[0]
--
2.30.2
[PATCH bare-py 4/7] Make optional, list, and map nameable
---
bare/__init__.py | 4 ++++
bare/__main__.py | 27 ++++++++++++++++++---------
2 files changed, 22 insertions(+), 9 deletions(-)
diff --git a/bare/__init__.py b/bare/__init__.py
index 4bfec34..c9d8110 100644
--- a/bare/__init__.py
@@ -169,6 +169,8 @@ def _pack_type(ast_node, data, module):
else:
return _pack_type(referenced._ast, data, module)
elif isinstance(ast_node, ListType):
+ if hasattr(data, 'value'):
+ data = data.value
result = bytes()
if ast_node.length is None:
result += _pack_primitive(BarePrimitive(TypeKind.UINT), len(data))
@@ -178,6 +180,8 @@ def _pack_type(ast_node, data, module):
result += _pack_type(ast_node.subtype, item, module)
return result
elif isinstance(ast_node, MapType):
+ if hasattr(data, 'value'):
+ data = data.value
result = bytes()
result += _pack_primitive(BarePrimitive(TypeKind.UINT), len(data))
for key in data:
diff --git a/bare/__main__.py b/bare/__main__.py
index 73b18db..6d12fd1 100644
--- a/bare/__main__.py
@@ -1,6 +1,6 @@
import argparse
import bare.parser
-from bare.bare_ast import BareType, BarePrimitive, StructType, TypeKind, BareEnum, UnionType
+from bare.bare_ast import BareType, BarePrimitive, StructType, TypeKind, BareEnum, UnionType, OptionalType, ListType, MapType
def _gen_enum(type):
@@ -12,7 +12,14 @@ def _gen_enum(type):
def _gen_type(type):
- if isinstance(type, BarePrimitive):
+ if (isinstance(type, BarePrimitive)
+ or isinstance(type, OptionalType)
+ or isinstance(type, ListType)
+ or isinstance(type, MapType)):
+ if isinstance(type, BarePrimitive):
+ is_primitive = True
+ else:
+ is_primitive = False
result = '\t_ast = {}\n\n'.format(type.code())
result += '\tdef __init__(self, value=None):\n'
result += '\t\tself.value = value\n\n'
@@ -21,7 +28,8 @@ def _gen_type(type):
result += '\t@classmethod\n'
result += '\tdef unpack(cls, data, offset=0):\n'
result += '\t\tinstance = cls()\n'
- result += '\t\treturn bare.unpack(instance, data, offset=offset, primitive=True)\n\n'
+ result += '\t\treturn bare.unpack(instance, data, offset=offset,'
+ result += f' primitive={is_primitive})\n\n'
result += '\n'
return result
@@ -62,7 +70,7 @@ def codegen(schema, output, indent, skip=None):
result = 'from collections import OrderedDict\n'
result += 'from enum import Enum\n\n'
result += 'import bare\n'
- result += 'from bare.bare_ast import TypeKind, BarePrimitive, StructType, OptionalType, NamedType, ArrayType, ' \
+ result += 'from bare.bare_ast import TypeKind, BarePrimitive, StructType, OptionalType, NamedType, ListType, ' \
'MapType, UnionType, UnionValue\n\n\n'
schema = bare.parser.parse(schema)
@@ -72,11 +80,12 @@ def codegen(schema, output, indent, skip=None):
continue
if isinstance(type, BareType):
- result += 'class {}:\n'.format(type.name)
- result += _gen_type(type.type)
- if isinstance(type, BareEnum):
- result += 'class {}(Enum):\n'.format(type.name)
- result += _gen_enum(type)
+ if isinstance(type.type, BareEnum):
+ result += 'class {}(Enum):\n'.format(type.name)
+ result += _gen_enum(type.type)
+ else:
+ result += 'class {}:\n'.format(type.name)
+ result += _gen_type(type.type)
if indent != '\t':
result = result.replace('\t', indent)
--
2.30.2
[PATCH bare-py 5/7] Add example company test
---
bare/test_encoded_messages.py | 98 +++++++++++++++++++++++++++++++++++
1 file changed, 98 insertions(+)
create mode 100644 bare/test_encoded_messages.py
diff --git a/bare/test_encoded_messages.py b/bare/test_encoded_messages.py
new file mode 100644
index 0000000..b0bfd74
--- /dev/null
@@ -0,0 +1,98 @@
+"""Test Example Company from BARE RFC Appendix B."""
+
+SCHEMA = """\
+type PublicKey data[128]
+type Time str # ISO 8601
+
+type Department enum {
+ ACCOUNTING
+ ADMINISTRATION
+ CUSTOMER_SERVICE
+ DEVELOPMENT
+
+ # Reserved for the CEO
+ JSMITH = 99
+}
+
+type Address list<str>[4] # street, city, state, country
+
+type Customer struct {
+ name: str
+ email: str
+ address: Address
+ orders: list<struct {
+ orderId: i64
+ quantity: i32
+ }>
+ metadata: map<str><data>
+}
+
+type Employee struct {
+ name: str
+ email: str
+ address: Address
+ department: Department
+ hireDate: Time
+ publicKey: optional<PublicKey>
+ metadata: map<str><data>
+}
+
+type TerminatedEmployee void
+
+type Person union {Customer | Employee | TerminatedEmployee}
+"""
+
+JSMITH = b"""\
+\x00\x0b\x4a\x61\x6d\x65\x73\x20\x53\x6d\x69\x74\x68\x12\x6a\x73\
+\x6d\x69\x74\x68\x40\x65\x78\x61\x6d\x70\x6c\x65\x2e\x6f\x72\x67\
+\x0b\x31\x32\x33\x20\x4d\x61\x69\x6e\x20\x53\x74\x0c\x50\x68\x69\
+\x6c\x61\x64\x65\x6c\x70\x68\x69\x61\x02\x50\x41\x0d\x55\x6e\x69\
+\x74\x65\x64\x20\x53\x74\x61\x74\x65\x73\x01\xb2\x41\xde\xfc\x00\
+\x00\x00\x00\x05\x00\x00\x00\x00\
+"""
+
+TIFFANYD = b"""\
+\x01\x0b\x54\x69\x66\x66\x61\x6e\x79\x20\x44\x6f\x65\x12\x74\x69\
+\x66\x66\x61\x6e\x79\x64\x40\x61\x63\x6d\x65\x2e\x63\x6f\x72\x70\
+\x0b\x31\x32\x33\x20\x4d\x61\x69\x6e\x20\x53\x74\x0c\x50\x68\x69\
+\x6c\x61\x64\x65\x6c\x70\x68\x69\x61\x02\x50\x41\x0d\x55\x6e\x69\
+\x74\x65\x64\x20\x53\x74\x61\x74\x65\x73\x01\x14\x32\x30\x32\x30\
+\x2d\x30\x36\x2d\x32\x31\x54\x32\x31\x3a\x31\x38\x3a\x30\x35\x5a\
+\x00\x00\
+"""
+
+TERMINATED = b"\x02"
+
+
+def test_encoded_messages(tmp_path):
+ from bare.__main__ import codegen
+ f = tmp_path / "sch.py"
+ f.write_text(codegen(SCHEMA, None, " "))
+
+ import importlib.util
+ import sys
+ spec = importlib.util.spec_from_file_location("sch", f"{tmp_path}/sch.py")
+ sch = importlib.util.module_from_spec(spec)
+ sys.modules["sch"] = sch
+ spec.loader.exec_module(sch)
+
+ c = sch.Customer()
+ c.name = "James Smith"
+ c.email = "jsmith@example.org"
+ c.address = ["123 Main St", "Philadelphia", "PA", "United States"]
+ c.orders = [{"orderId": 4242424242, "quantity": 5}]
+ c.metadata = {}
+
+ e = sch.Employee()
+ e.name = "Tiffany Doe"
+ e.email = "tiffanyd@acme.corp"
+ e.address = ["123 Main St", "Philadelphia", "PA", "United States"]
+ e.department = sch.Department.ADMINISTRATION
+ e.hireDate = "2020-06-21T21:18:05Z"
+ e.metadata = {}
+
+ t = sch.TerminatedEmployee()
+
+ assert sch.Person.pack(c) == JSMITH
+ assert sch.Person.pack(e) == TIFFANYD
+ assert sch.Person.pack(t) == TERMINATED
--
2.30.2
[PATCH bare-py 6/7] Update example
---
README.md | 58 ++++++-------
example/address.bin | Bin 55 -> 52 bytes
example/addressmaybe-address.bin | Bin 56 -> 53 bytes
example/customer.bin | Bin 148 -> 145 bytes
example/employee.bin | Bin 164 -> 161 bytes
example/example-read.py | 5 +-
example/example.py | 11 +--
example/person-customer.bin | Bin 149 -> 146 bytes
example/person-employee.bin | Bin 165 -> 162 bytes
example/schema.bare | 46 +++++-----
example/schema.py | 142 ++++++++++++++++---------------
11 files changed, 131 insertions(+), 131 deletions(-)
diff --git a/README.md b/README.md
index f668644..66c1368 100644
--- a/README.md
+++ b/README.md
@@ -12,10 +12,11 @@ structure.
The schema:
```bare
-type PublicKey data<128>
-type Time string # ISO 8601
+type PublicKey data[128]
+type PublicED25519Key data[32]
+type Time str # ISO 8601
-enum Department {
+type Department enum {
ACCOUNTING
ADMINISTRATION
CUSTOMER_SERVICE
@@ -25,35 +26,34 @@ enum Department {
JSMITH = 99
}
-type Customer {
- name: string
- email: string
+type Address list<str>[4] # street, city, state, country
+type Maybe void
+type AddressMaybe union {Address | Maybe}
+
+type Customer struct {
+ name: str
+ email: str
address: Address
- orders: []{
+ orders: list<struct {
orderId: i64
quantity: i32
- }
- metadata: map[string]data
+ }>
+ metadata: map<str><data>
}
-type Employee {
- name: string
- email: string
+type Employee struct {
+ name: str
+ email: str
address: Address
department: Department
hireDate: Time
- publicKey: optional<PublicKey>
- metadata: map[string]data
+ publicKey: optional<union{PublicKey|PublicED25519Key}>
+ metadata: map<str><data>
}
-type Person (Customer | Employee)
+type TerminatedEmployee void
-type Address {
- address: [4]string
- city: string
- state: string
- country: string
-}
+type Person union {Customer | Employee | TerminatedEmployee}
```
Generate `schema.py` with the `bare` command
@@ -67,11 +67,12 @@ $ bare schema.bare schema.py
```python
from schema import Customer, Address, Person
-address = Address()
-address.address = ["Address line 1", "", "", ""]
-address.city = "The big city"
-address.state = "Drenthe"
-address.country = "The Netherlands"
+address = Address([
+ "Address line 1",
+ "The big city",
+ "Drenthe",
+ "The Netherlands"
+])
customer = Customer()
customer.name = "Martijn Braam"
@@ -107,7 +108,6 @@ $ xxd example.bin
00000070: 0373 7368 0b6a 6166 736c 3864 6661 6632 .ssh.jafsl8dfaf2
00000080: 0367 7067 0f6a 6f66 6138 6632 6a64 6c61 .gpg.jofa8f2jdla
00000090: 7366 6a38 sfj8
-
```
### Unpacking data
@@ -119,7 +119,7 @@ with open('address.bin', 'rb') as handle:
raw = handle.read()
address = Address.unpack(raw)
-print(address.city)
+print(address[0][1])
```
### Debugging messages
@@ -134,4 +134,4 @@ struct Cursor
|next 01 union, tag = 1 (INT)
||INT A8 94 A6 D3 0B INT = 1563739412
|search 00 String =
-```
\ No newline at end of file
+```
diff --git a/example/address.bin b/example/address.bin
index e972171aa6e0716d0100f5f1aedc24a4903cc7a8..8c368b49a9f49d5d060f466ff10966296581127d 100644
GIT binary patch
delta 21
ccmXpv;pcNqNhwM#E>_6N%u7`;oG7FU073KyFaQ7m
delta 24
fcmXpp7vghFNhwM#E>_6N%u7`;WME*J$gc_jQe_4(
diff --git a/example/addressmaybe-address.bin b/example/addressmaybe-address.bin
index 07b1ab23d60a6660d029dc38bb3efb43bea0ff5e..8e883991816dd7c51c4a103db39c11aec4168de2 100644
GIT binary patch
delta 22
dcmcC86=2|VOi3w9EiP8b$;?YtFq|l?3IIZ|1~dQw
delta 25
gcmXrD5N6<WOi3w9EiP8b$;?YtFl1n0m?)qM08=dnGynhq
diff --git a/example/customer.bin b/example/customer.bin
index c966d92d23c1f363cfc92a09fa7c983bbdc0d120..233be17ab5afe07581412af2c7d408b2e00c6628 100644
GIT binary patch
delta 9
QcmbQjIFWIJ-bBM>01rX~3jhEB
delta 13
UcmbQpIE8V79y0?2!$iGg02bo|3jhEB
diff --git a/example/employee.bin b/example/employee.bin
index ee9903f7939b9d84e171e7a20ddac7f604140177..f344d7918f82877f7db4eb186933e9fbb92c018d 100644
GIT binary patch
delta 9
QcmZ3&xR7yz-bBM<01#3GI{*Lx
delta 13
UcmZ3;xP)<n9y0?2!$iGe02p%vI{*Lx
diff --git a/example/example-read.py b/example/example-read.py
index ca155b6..9d04a6f 100644
--- a/example/example-read.py
+++ b/example/example-read.py
@@ -5,10 +5,7 @@ with open('address.bin', 'rb') as handle:
address = Address.unpack(raw)
-print("Address: {}, {}, {}, {}".format(*address.address))
-print("City: {}".format(address.city))
-print("State: {}".format(address.state))
-print("Country: {}".format(address.country))
+print("Address: {}, {}, {}, {}".format(*address[0]))
with open('person-employee.bin', 'rb') as handle:
raw = handle.read()
diff --git a/example/example.py b/example/example.py
index 08ad5cf..9af329d 100644
--- a/example/example.py
+++ b/example/example.py
@@ -1,10 +1,11 @@
from schema import Customer, Address, Employee, Department, Person, PublicED25519Key, AddressMaybe, Maybe
-address = Address()
-address.address = ["Address line 1", "", "", ""]
-address.city = "The big city"
-address.state = "Drenthe"
-address.country = "The Netherlands"
+address = Address([
+ "Address line 1",
+ "The big city",
+ "Drenthe",
+ "The Netherlands"
+])
customer = Customer()
customer.name = "Martijn Braam"
diff --git a/example/person-customer.bin b/example/person-customer.bin
index 17ba1a107d5ed4a74ae62035a7b621337290a1bc..bc68cc044cccf11a73c46f1e257d6d1bbcb40307 100644
GIT binary patch
delta 9
QcmbQrIEitB{zRi>01sFL4*&oF
delta 13
UcmbQlIF)gNJ~IOY!$kdL02c)V4*&oF
diff --git a/example/person-employee.bin b/example/person-employee.bin
index d417afd3dc768975f7d4f6fa2956e4c3ff377013..7c0d1db9e0918dd99eafd3d113124669a42e68d5 100644
GIT binary patch
delta 9
QcmZ3=xQKCr{zRi<01#*cKL7v#
delta 13
UcmZ3)xRh~%J~IOY!$kdJ02q}6KL7v#
diff --git a/example/schema.bare b/example/schema.bare
index 9840183..ee30937 100644
--- a/example/schema.bare
+++ b/example/schema.bare
@@ -1,8 +1,8 @@
-type PublicRSAKey data<128>
-type PublicED25519Key data<32>
-type Time string # ISO 8601
+type PublicKey data[128]
+type PublicED25519Key data[32]
+type Time str # ISO 8601
-enum Department {
+type Department enum {
ACCOUNTING
ADMINISTRATION
CUSTOMER_SERVICE
@@ -12,35 +12,31 @@ enum Department {
JSMITH = 99
}
-type Customer {
- name: string
- email: string
+type Address list<str>[4] # street, city, state, country
+type Maybe void
+type AddressMaybe union {Address | Maybe}
+
+type Customer struct {
+ name: str
+ email: str
address: Address
- orders: []{
+ orders: list<struct {
orderId: i64
quantity: i32
- }
- metadata: map[string]data
+ }>
+ metadata: map<str><data>
}
-type Employee {
- name: string
- email: string
+type Employee struct {
+ name: str
+ email: str
address: Address
department: Department
hireDate: Time
- publicKey: optional<(PublicRSAKey|PublicED25519Key)>
- metadata: map[string]data
+ publicKey: optional<union{PublicKey|PublicED25519Key}>
+ metadata: map<str><data>
}
-type Person (Customer | Employee)
+type TerminatedEmployee void
-type Address {
- address: [4]string
- city: string
- state: string
- country: string
-}
-
-type Maybe void
-type AddressMaybe (Address | Maybe)
\ No newline at end of file
+type Person union {Customer | Employee | TerminatedEmployee}
diff --git a/example/schema.py b/example/schema.py
index 6ddf8a4..1342857 100644
--- a/example/schema.py
+++ b/example/schema.py
@@ -2,11 +2,10 @@ from collections import OrderedDict
from enum import Enum
import bare
-from bare.bare_ast import TypeKind, BarePrimitive, StructType, OptionalType, NamedType, ArrayType, MapType, UnionType, \
- UnionValue
+from bare.bare_ast import TypeKind, BarePrimitive, StructType, OptionalType, NamedType, ListType, MapType, UnionType, UnionValue
-class PublicRSAKey:
+class PublicKey:
_ast = BarePrimitive(TypeKind.DataFixed, 128)
def __init__(self, value=None):
@@ -37,7 +36,7 @@ class PublicED25519Key:
class Time:
- _ast = BarePrimitive(TypeKind.String)
+ _ast = BarePrimitive(TypeKind.Str)
def __init__(self, value=None):
self.value = value
@@ -59,16 +58,62 @@ class Department(Enum):
JSMITH = 99
+class Address:
+ _ast = ListType(BarePrimitive(TypeKind.Str), 4)
+
+ def __init__(self, value=None):
+ self.value = value
+
+ def pack(self):
+ return bare.pack(self)
+
+ @classmethod
+ def unpack(cls, data, offset=0):
+ instance = cls()
+ return bare.unpack(instance, data, offset=offset, primitive=False)
+
+
+class Maybe:
+ _ast = BarePrimitive(TypeKind.Void)
+
+ def __init__(self, value=None):
+ self.value = value
+
+ def pack(self):
+ return bare.pack(self)
+
+ @classmethod
+ def unpack(cls, data, offset=0):
+ instance = cls()
+ return bare.unpack(instance, data, offset=offset, primitive=True)
+
+
+class AddressMaybe:
+ _ast = UnionType([
+ UnionValue(NamedType("Address"), 0),
+ UnionValue(NamedType("Maybe"), 1)
+ ])
+
+ @classmethod
+ def pack(cls, member):
+ return bare.pack(member, cls)
+
+ @classmethod
+ def unpack(cls, data, offset=0):
+ instance = cls()
+ return bare.unpack(instance, data, offset=offset)
+
+
class Customer:
_ast = StructType(OrderedDict(
- name=BarePrimitive(TypeKind.String),
- email=BarePrimitive(TypeKind.String),
+ name=BarePrimitive(TypeKind.Str),
+ email=BarePrimitive(TypeKind.Str),
address=NamedType("Address"),
- orders=ArrayType(StructType(OrderedDict(
+ orders=ListType(StructType(OrderedDict(
orderId=BarePrimitive(TypeKind.I64),
quantity=BarePrimitive(TypeKind.I32),
))),
- metadata=MapType(BarePrimitive(TypeKind.String), BarePrimitive(TypeKind.Data)),
+ metadata=MapType(BarePrimitive(TypeKind.Str), BarePrimitive(TypeKind.Data)),
))
def __init__(self):
@@ -90,16 +135,16 @@ class Customer:
class Employee:
_ast = StructType(OrderedDict(
- name=BarePrimitive(TypeKind.String),
- email=BarePrimitive(TypeKind.String),
+ name=BarePrimitive(TypeKind.Str),
+ email=BarePrimitive(TypeKind.Str),
address=NamedType("Address"),
department=NamedType("Department"),
hireDate=NamedType("Time"),
publicKey=OptionalType(UnionType([
- UnionValue(NamedType("PublicRSAKey"), 0),
+ UnionValue(NamedType("PublicKey"), 0),
UnionValue(NamedType("PublicED25519Key"), 1)
])),
- metadata=MapType(BarePrimitive(TypeKind.String), BarePrimitive(TypeKind.Data)),
+ metadata=MapType(BarePrimitive(TypeKind.Str), BarePrimitive(TypeKind.Data)),
))
def __init__(self):
@@ -121,65 +166,26 @@ class Employee:
return instance
+class TerminatedEmployee:
+ _ast = BarePrimitive(TypeKind.Void)
+
+ def __init__(self, value=None):
+ self.value = value
+
+ def pack(self):
+ return bare.pack(self)
+
+ @classmethod
+ def unpack(cls, data, offset=0):
+ instance = cls()
+ return bare.unpack(instance, data, offset=offset, primitive=True)
+
+
class Person:
_ast = UnionType([
UnionValue(NamedType("Customer"), 0),
- UnionValue(NamedType("Employee"), 1)
- ])
-
- @classmethod
- def pack(cls, member):
- return bare.pack(member, cls)
-
- @classmethod
- def unpack(cls, data, offset=0):
- instance = cls()
- return bare.unpack(instance, data, offset=offset)
-
-
-class Address:
- _ast = StructType(OrderedDict(
- address=ArrayType(BarePrimitive(TypeKind.String), 4),
- city=BarePrimitive(TypeKind.String),
- state=BarePrimitive(TypeKind.String),
- country=BarePrimitive(TypeKind.String),
- ))
-
- def __init__(self):
- self.address = None
- self.city = None
- self.state = None
- self.country = None
-
- def pack(self):
- return bare.pack(self)
-
- @classmethod
- def unpack(cls, data, offset=0):
- instance = cls()
- bare.unpack(instance, data, offset=offset)
- return instance
-
-
-class Maybe:
- _ast = BarePrimitive(TypeKind.Void)
-
- def __init__(self, value=None):
- self.value = value
-
- def pack(self):
- return bare.pack(self)
-
- @classmethod
- def unpack(cls, data, offset=0):
- instance = cls()
- return bare.unpack(instance, data, offset=offset, primitive=True)
-
-
-class AddressMaybe:
- _ast = UnionType([
- UnionValue(NamedType("Address"), 0),
- UnionValue(NamedType("Maybe"), 1)
+ UnionValue(NamedType("Employee"), 1),
+ UnionValue(NamedType("TerminatedEmployee"), 2)
])
@classmethod
--
2.30.2
[PATCH bare-py 7/7] Update bare-dump
---
bare/dump.py | 4 ++--
example/cursor.bare | 8 ++++----
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/bare/dump.py b/bare/dump.py
index 4739397..b5f9bb9 100644
--- a/bare/dump.py
@@ -9,7 +9,7 @@ from itertools import zip_longest as zipl
from bare import _unpack_primitive
from bare.__main__ import codegen
-from bare.bare_ast import BareType, UnionType, BarePrimitive, TypeKind, StructType, MapType, NamedType, ArrayType, \
+from bare.bare_ast import BareType, UnionType, BarePrimitive, TypeKind, StructType, MapType, NamedType, ListType, \
OptionalType
@@ -114,7 +114,7 @@ def dump(data, type, module, name):
newnode = (referenced, label)
nodelist = [newnode] + nodelist
end_offset = offset
- elif isinstance(node, ArrayType):
+ elif isinstance(node, ListType):
if node.length is None:
length, end_offset = _unpack_primitive(BarePrimitive(TypeKind.UINT), data, offset)
dec = '[]{}, length = {}'.format(node.subtype.name, length)
diff --git a/example/cursor.bare b/example/cursor.bare
index a1b8fb6..8f272d2 100644
--- a/example/cursor.bare
+++ b/example/cursor.bare
@@ -1,5 +1,5 @@
-type Cursor {
+type Cursor struct {
count: int
- next: (string | int)
- search: string
- }
\ No newline at end of file
+ next: union{str | int}
+ search: str
+ }
--
2.30.2