~martijnbraam/public-inbox

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

Details
Message ID
<20250314063147.278401-1-jiri.hubacek@gmail.com>
Sender timestamp
1741937500
DKIM signature
pass
Download raw message
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

Details
Message ID
<20250314063147.278401-2-jiri.hubacek@gmail.com>
In-Reply-To
<20250314063147.278401-1-jiri.hubacek@gmail.com> (view parent)
Sender timestamp
1741937501
DKIM signature
pass
Download raw message
Patch: +1 -1
---
 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

Details
Message ID
<20250314063147.278401-3-jiri.hubacek@gmail.com>
In-Reply-To
<20250314063147.278401-1-jiri.hubacek@gmail.com> (view parent)
Sender timestamp
1741937502
DKIM signature
pass
Download raw message
Patch: +129 -105
---
 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

Details
Message ID
<20250314063147.278401-4-jiri.hubacek@gmail.com>
In-Reply-To
<20250314063147.278401-1-jiri.hubacek@gmail.com> (view parent)
Sender timestamp
1741937503
DKIM signature
pass
Download raw message
Patch: +291 -177
---
 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

Details
Message ID
<20250314063147.278401-5-jiri.hubacek@gmail.com>
In-Reply-To
<20250314063147.278401-1-jiri.hubacek@gmail.com> (view parent)
Sender timestamp
1741937504
DKIM signature
pass
Download raw message
Patch: +22 -9
---
 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

Details
Message ID
<20250314063147.278401-6-jiri.hubacek@gmail.com>
In-Reply-To
<20250314063147.278401-1-jiri.hubacek@gmail.com> (view parent)
Sender timestamp
1741937505
DKIM signature
pass
Download raw message
Patch: +98 -0
---
 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

Details
Message ID
<20250314063147.278401-7-jiri.hubacek@gmail.com>
In-Reply-To
<20250314063147.278401-1-jiri.hubacek@gmail.com> (view parent)
Sender timestamp
1741937506
DKIM signature
pass
Download raw message
Patch: +131 -131
---
 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

Details
Message ID
<20250314063147.278401-8-jiri.hubacek@gmail.com>
In-Reply-To
<20250314063147.278401-1-jiri.hubacek@gmail.com> (view parent)
Sender timestamp
1741937507
DKIM signature
pass
Download raw message
Patch: +6 -6
---
 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
Reply to thread Export thread (mbox)