From 990dd4c1e1d58fc2951ac0a6f07acfe13998d06d Mon Sep 17 00:00:00 2001 From: Drew Galbraith Date: Fri, 13 Oct 2023 15:04:25 -0700 Subject: [PATCH] [yunq] Add codegen for message parsing and serialization methods. --- yunq/codegen_message.py | 152 +++++++++++++++++++++++++++++++---- yunq/example.yunq.cpp | 174 ++++++++++++++++++++++++++++++++++++++++ yunq/example.yunq.h | 74 +++++++++++++++++ yunq/yunq.py | 7 +- 4 files changed, 390 insertions(+), 17 deletions(-) create mode 100644 yunq/example.yunq.cpp create mode 100644 yunq/example.yunq.h diff --git a/yunq/codegen_message.py b/yunq/codegen_message.py index 15e27f4..9eb043d 100644 --- a/yunq/codegen_message.py +++ b/yunq/codegen_message.py @@ -6,6 +6,7 @@ HEADER_PRELUDE = """ #pragma once #include +#include """ @@ -18,7 +19,9 @@ class {name} {{ {name}({name}&&) = delete; void ParseFromBytes(const glcr::ByteBuffer&); - glcr::ByteBuffer SerializeToBytes(); + void ParseFromBytes(const glcr::ByteBuffer&, const glcr::CapBuffer&); + void SerializeToBytes(glcr::ByteBuffer&); + void SerializeToBytes(glcr::ByteBuffer&, glcr::CapBuffer&); """ MESSAGE_CLASS_SET_GET = """ @@ -37,14 +40,14 @@ MESSAGE_CLASS_FIELD = """ """ MESSAGE_CLASS_SUFFIX = """ -} +}; """ type_to_str = { Type.U64: "uint64_t", Type.I64: "int64_t", Type.STRING: "glcr::String", - Type.CAPABILITY: "zcap_t", + Type.CAPABILITY: "z_cap_t", Type.BYTES: "glcr::Vector" } @@ -89,6 +92,22 @@ struct ExtPointer {{ uint32_t length; }} +void CheckHeader(const glcr::ByteBuffer& bytes) {{ + // TODO: Check ident. + // TODO: Parse core size. + // TODO: Parse extension size. + // TODO: Check CRC32 + // TODO: Parse options. +}} + +void WriteHeader(glcr::ByteBuffer& bytes, uint32_t core_size, uint32_t extension_size) {{ + bytes.WriteAt(0, 0xDEADBEEF); // TODO: Chose a more unique ident sequence. + bytes.WriteAt(4, core_size); + bytes.WriteAt(8, extension_size); + bytes.WriteAt(12, 0); // TODO: Calculate CRC32. + bytes.WriteAt(16, 0); // TODO: Add options. +}} + }} // namespace """ @@ -97,6 +116,11 @@ void {name}::ParseFromBytes(const glcr::ByteBuffer& bytes) {{ CheckHeader(); """ +IMPL_PARSE_DEF_CAP = """ +void {name}::ParseFromBytes(const glcr::ByteBuffer& bytes, const glcr::CapBuffer& caps) {{ + CheckHeader(); +""" + IMPL_PARSE_U64 = """ set_{name}(bytes.At(header_size + (8 * {offset}))); """ @@ -111,32 +135,128 @@ IMPL_PARSE_STRING = """ set_{name}(bytes.StringAt({name}_pointer.offset, {name}_pointer.length)); """ +IMPL_SET_CAP_EMPTY = """ + set_{name}(0); +""" + +IMPL_PARSE_CAP = """ + uint64_t {name}_ptr = bytes.At(header_size + (8 * {offset})); + + set_{name}(caps.at({name}_ptr)); +""" + IMPL_PARSE_DEF_END = """ } """ -def _generate_message_class_impl(message: Message) -> str: - impl = IMPL_PARSE_DEF.format(name=message.name) +def _generate_message_parse_impl(message: Message) -> str: + impl = "" - for offset, field in enumerate(message.fields): - if field.type == Type.U64: - impl += IMPL_PARSE_U64.format(name = field.name, offset = offset) - elif field.type == Type.I64: - impl += IMPL_PARSE_I64.format(name = field.name, offset = offset) - elif field.type == Type.STRING: - impl += IMPL_PARSE_STRING.format(name = field.name, offset = offset); - else: - impl += "\n{} unimplemented\n".format(field.type.name) + for with_cap in (False, True): + impl += IMPL_PARSE_DEF.format(name=message.name) if not with_cap else IMPL_PARSE_DEF_CAP.format(name=message.name) + for offset, field in enumerate(message.fields): + if field.type == Type.U64: + impl += IMPL_PARSE_U64.format(name = field.name, offset = offset) + elif field.type == Type.I64: + impl += IMPL_PARSE_I64.format(name = field.name, offset = offset) + elif field.type == Type.STRING: + impl += IMPL_PARSE_STRING.format(name = field.name, offset = offset); + elif field.type == Type.CAPABILITY: + if with_cap: + impl += IMPL_PARSE_CAP.format(name = field.name, offset = offset) + else: + impl += IMPL_SET_CAP_EMPTY.format(name = field.name) + else: + impl += "\n{} unimplemented\n".format(field.type.name) - impl += IMPL_PARSE_DEF_END + impl += IMPL_PARSE_DEF_END return impl +IMPL_SER_DEF = """ +{name}::SerializeToBytes(glcr::ByteBuffer& bytes) {{ + + uint32_t next_extension = header_size + 8 * {num_fields}; + const uint32_t core_size = next_extension; +""" + +IMPL_SER_CAP_DEF = """ +{name}::SerializeToBytes(glcr::ByteBuffer& bytes, glcr::CapBuffer& caps) {{ + + uint32_t next_extension = header_size + 8 * {num_fields}; + const uint32_t core_size = next_extension; + uint64_t next_cap = 0; +""" + +IMPL_SER_U64 = """ + bytes.WriteAt(header_size + (8 * {offset}), {name}()); +""" + +IMPL_SER_I64 = """ + bytes.WriteAt(header_size + (8 * {offset}), {name}()); +""" + +IMPL_SER_STRING = """ + ExtPointer {name}_ptr{{ + .offset = next_extension, + // FIXME: Check downcast of str length. + .length = {name}().length(), + }}; + + bytes.WriteStringAt(next_extension, {name}()); + next_extension += {name}_ptr.length; + + bytes.WriteAt(header_size + (8 * {offset}), {name}_ptr); +""" + +IMPL_SER_CAP_EMPTY = """ + // FIXME: Warn or error on serialization +""" + +IMPL_SER_CAP = """ + caps.Write(next_cap, {name}()); + bytes.WriteAt(header_size + (8 * {offset}), next_cap++); +""" + +IMPL_SER_DEF_END = """ + // The next extension pointer is the length of the message. + WriteHeader(bytes, core_size, next_extension); +} +""" +def _generate_message_serialize_impl(message: Message) -> str: + impl = "" + + for with_cap in (False, True): + if with_cap: + impl += IMPL_SER_CAP_DEF.format(name = message.name, num_fields = len(message.fields)) + else: + impl += IMPL_SER_DEF.format(name = message.name, num_fields = len(message.fields)) + + for offset, field in enumerate(message.fields): + if field.type == Type.U64: + impl += IMPL_SER_U64.format(name = field.name, offset = offset) + elif field.type == Type.I64: + impl += IMPL_SER_I64.format(name = field.name, offset = offset) + elif field.type == Type.STRING: + impl += IMPL_SER_STRING.format(name = field.name, offset = offset) + elif field.type == Type.CAPABILITY: + if with_cap: + impl += IMPL_SER_CAP.format(name = field.name, offset = offset) + else: + impl += IMPL_SER_CAP_EMPTY + else: + impl += "\n{} unimplemented\n".format(field.type.name) + impl += IMPL_SER_DEF_END + + return impl + +def _generate_message_class_impl(message: Message) -> str: + return _generate_message_parse_impl(message) + _generate_message_serialize_impl(message) + def generate_message_impl(file: str, ast: list[Decl]) -> str: impl = IMPL_PRELUDE.format(file=file) - for decl in ast: if type(decl) != Message: continue diff --git a/yunq/example.yunq.cpp b/yunq/example.yunq.cpp new file mode 100644 index 0000000..4b29f0c --- /dev/null +++ b/yunq/example.yunq.cpp @@ -0,0 +1,174 @@ + +#include "example.yunq.h" + +namespace { + +const uint64_t header_size = 24; // 4x uint32, 1x uint64 + +struct ExtPointer { + uint32_t offset; + uint32_t length; +} + +void CheckHeader(const glcr::ByteBuffer& bytes) { + // TODO: Check ident. + // TODO: Parse core size. + // TODO: Parse extension size. + // TODO: Check CRC32 + // TODO: Parse options. +} + +void WriteHeader(glcr::ByteBuffer& bytes, uint32_t core_size, uint32_t extension_size) { + bytes.WriteAt(0, 0xDEADBEEF); // TODO: Chose a more unique ident sequence. + bytes.WriteAt(4, core_size); + bytes.WriteAt(8, extension_size); + bytes.WriteAt(12, 0); // TODO: Calculate CRC32. + bytes.WriteAt(16, 0); // TODO: Add options. +} + +} // namespace + +void OpenFileRequest::ParseFromBytes(const glcr::ByteBuffer& bytes) { + CheckHeader(); + + auto path_pointer = bytes.At(header_size + (8 * 0)); + + set_path(bytes.StringAt(path_pointer.offset, path_pointer.length)); + + set_options(bytes.At(header_size + (8 * 1))); + +} + +void OpenFileRequest::ParseFromBytes(const glcr::ByteBuffer& bytes, const glcr::CapBuffer& caps) { + CheckHeader(); + + auto path_pointer = bytes.At(header_size + (8 * 0)); + + set_path(bytes.StringAt(path_pointer.offset, path_pointer.length)); + + set_options(bytes.At(header_size + (8 * 1))); + +} + +OpenFileRequest::SerializeToBytes(glcr::ByteBuffer& bytes) { + + uint32_t next_extension = header_size + 8 * 2; + const uint32_t core_size = next_extension; + + ExtPointer path_ptr{ + .offset = next_extension, + // FIXME: Check downcast of str length. + .length = path().length(), + }; + + bytes.WriteStringAt(next_extension, path()); + next_extension += path_ptr.length; + + bytes.WriteAt(header_size + (8 * 0), path_ptr); + + bytes.WriteAt(header_size + (8 * 1), options()); + + // The next extension pointer is the length of the message. + WriteHeader(bytes, core_size, next_extension); +} + +OpenFileRequest::SerializeToBytes(glcr::ByteBuffer& bytes, glcr::CapBuffer& caps) { + + uint32_t next_extension = header_size + 8 * 2; + const uint32_t core_size = next_extension; + uint64_t next_cap = 0; + + ExtPointer path_ptr{ + .offset = next_extension, + // FIXME: Check downcast of str length. + .length = path().length(), + }; + + bytes.WriteStringAt(next_extension, path()); + next_extension += path_ptr.length; + + bytes.WriteAt(header_size + (8 * 0), path_ptr); + + bytes.WriteAt(header_size + (8 * 1), options()); + + // The next extension pointer is the length of the message. + WriteHeader(bytes, core_size, next_extension); +} + +void File::ParseFromBytes(const glcr::ByteBuffer& bytes) { + CheckHeader(); + + auto path_pointer = bytes.At(header_size + (8 * 0)); + + set_path(bytes.StringAt(path_pointer.offset, path_pointer.length)); + + set_attrs(bytes.At(header_size + (8 * 1))); + + set_mem_cap(0); + +} + +void File::ParseFromBytes(const glcr::ByteBuffer& bytes, const glcr::CapBuffer& caps) { + CheckHeader(); + + auto path_pointer = bytes.At(header_size + (8 * 0)); + + set_path(bytes.StringAt(path_pointer.offset, path_pointer.length)); + + set_attrs(bytes.At(header_size + (8 * 1))); + + uint64_t mem_cap_ptr = bytes.At(header_size + (8 * 2)); + + set_mem_cap(caps.at(mem_cap_ptr)); + +} + +File::SerializeToBytes(glcr::ByteBuffer& bytes) { + + uint32_t next_extension = header_size + 8 * 3; + const uint32_t core_size = next_extension; + + ExtPointer path_ptr{ + .offset = next_extension, + // FIXME: Check downcast of str length. + .length = path().length(), + }; + + bytes.WriteStringAt(next_extension, path()); + next_extension += path_ptr.length; + + bytes.WriteAt(header_size + (8 * 0), path_ptr); + + bytes.WriteAt(header_size + (8 * 1), attrs()); + + // FIXME: Warn or error on serialization + + // The next extension pointer is the length of the message. + WriteHeader(bytes, core_size, next_extension); +} + +File::SerializeToBytes(glcr::ByteBuffer& bytes, glcr::CapBuffer& caps) { + + uint32_t next_extension = header_size + 8 * 3; + const uint32_t core_size = next_extension; + uint64_t next_cap = 0; + + ExtPointer path_ptr{ + .offset = next_extension, + // FIXME: Check downcast of str length. + .length = path().length(), + }; + + bytes.WriteStringAt(next_extension, path()); + next_extension += path_ptr.length; + + bytes.WriteAt(header_size + (8 * 0), path_ptr); + + bytes.WriteAt(header_size + (8 * 1), attrs()); + + caps.Write(next_cap, mem_cap()); + bytes.WriteAt(header_size + (8 * 2), next_cap++); + + // The next extension pointer is the length of the message. + WriteHeader(bytes, core_size, next_extension); +} diff --git a/yunq/example.yunq.h b/yunq/example.yunq.h new file mode 100644 index 0000000..3648617 --- /dev/null +++ b/yunq/example.yunq.h @@ -0,0 +1,74 @@ + +// Generated file - DO NOT MODIFY +#pragma once + +#include +#include + + +class OpenFileRequest { + public: + OpenFileRequest() {} + // Delete copy and move until implemented. + OpenFileRequest(const OpenFileRequest&) = delete; + OpenFileRequest(OpenFileRequest&&) = delete; + + void ParseFromBytes(const glcr::ByteBuffer&); + void ParseFromBytes(const glcr::ByteBuffer&, const glcr::CapBuffer&); + void SerializeToBytes(glcr::ByteBuffer&); + void SerializeToBytes(glcr::ByteBuffer&, glcr::CapBuffer&); + + glcr::String path() { return path_; } + void set_path(glcr::String value) { + path_ = value; + } + + uint64_t options() { return options_; } + void set_options(uint64_t value) { + options_ = value; + } + + private: + + glcr::String path_; + + uint64_t options_; + +}; + +class File { + public: + File() {} + // Delete copy and move until implemented. + File(const File&) = delete; + File(File&&) = delete; + + void ParseFromBytes(const glcr::ByteBuffer&); + void ParseFromBytes(const glcr::ByteBuffer&, const glcr::CapBuffer&); + void SerializeToBytes(glcr::ByteBuffer&); + void SerializeToBytes(glcr::ByteBuffer&, glcr::CapBuffer&); + + glcr::String path() { return path_; } + void set_path(glcr::String value) { + path_ = value; + } + + uint64_t attrs() { return attrs_; } + void set_attrs(uint64_t value) { + attrs_ = value; + } + + z_cap_t mem_cap() { return mem_cap_; } + void set_mem_cap(z_cap_t value) { + mem_cap_ = value; + } + + private: + + glcr::String path_; + + uint64_t attrs_; + + z_cap_t mem_cap_; + +}; diff --git a/yunq/yunq.py b/yunq/yunq.py index 7ca1311..4e07328 100644 --- a/yunq/yunq.py +++ b/yunq/yunq.py @@ -10,6 +10,7 @@ def main(): filename = sys.argv[1] + ast = None with open(filename, mode='r') as f: filedata = f.read() lexemes = lexer(filedata) @@ -18,7 +19,11 @@ def main(): ast = parser.parse() type_check(ast) - print(generate_message_impl(filename, ast)) + + with open(filename + '.h', mode='w') as f: + f.write(generate_message_header(ast)) + with open(filename + '.cpp', mode='w') as f: + f.write(generate_message_impl(filename, ast)) if __name__ == "__main__": main()