From f1e09b2ae6692a10e5bc7c3300ea4932ac72f44e Mon Sep 17 00:00:00 2001 From: Drew Galbraith Date: Thu, 30 Nov 2023 08:50:15 -0800 Subject: [PATCH] [Yunq] Add ability to namespace declarations using "package". --- yunq/client.cpp.jinja | 7 +++++++ yunq/client.h.jinja | 6 ++++++ yunq/example/example.yunq | 2 ++ yunq/example/example.yunq.client.cpp | 15 ++++++++++++++ yunq/example/example.yunq.client.h | 7 +++++++ yunq/example/example.yunq.cpp | 8 ++++++- yunq/example/example.yunq.h | 9 +++++++- yunq/example/example.yunq.server.cpp | 21 +++++++++++++++++++ yunq/example/example.yunq.server.h | 10 +++++++++ yunq/message.cpp.jinja | 7 +++++++ yunq/message.h.jinja | 7 +++++++ yunq/parser.py | 31 ++++++++++++++++++++++++++-- yunq/server.cpp.jinja | 7 +++++++ yunq/server.h.jinja | 8 +++++++ yunq/yunq.py | 18 ++++++++++------ 15 files changed, 153 insertions(+), 10 deletions(-) diff --git a/yunq/client.cpp.jinja b/yunq/client.cpp.jinja index e17b64b..6c61795 100644 --- a/yunq/client.cpp.jinja +++ b/yunq/client.cpp.jinja @@ -6,6 +6,9 @@ #include #include +{% if package != None %} +namespace {{package.cpp_namespace()}} { +{% endif %} {% for interface in interfaces %} {{interface.name}}Client::~{{interface.name}}Client() { @@ -61,3 +64,7 @@ glcr::ErrorCode {{interface.name}}Client::{{method.name}}(const {{method.request {% endfor %} {% endfor %} + +{% if package != None %} +} // namepace {{package.cpp_namespace()}} +{% endif %} diff --git a/yunq/client.h.jinja b/yunq/client.h.jinja index 5684d5f..4b764e0 100644 --- a/yunq/client.h.jinja +++ b/yunq/client.h.jinja @@ -8,6 +8,9 @@ #include "{{file}}.h" +{% if package != None %} +namespace {{package.cpp_namespace()}} { +{% endif %} {% for interface in interfaces -%} class {{interface.name}}Client { public: @@ -37,3 +40,6 @@ class {{interface.name}}Client { {%- endfor %} +{% if package != None %} +} // namepace {{package.cpp_namespace()}} +{% endif %} diff --git a/yunq/example/example.yunq b/yunq/example/example.yunq index 5565354..f286199 100644 --- a/yunq/example/example.yunq +++ b/yunq/example/example.yunq @@ -1,3 +1,5 @@ +package srv.file; + interface VFS { method open (OpenFileRequest) -> (File); } diff --git a/yunq/example/example.yunq.client.cpp b/yunq/example/example.yunq.client.cpp index 3c21e3b..1d3dd21 100644 --- a/yunq/example/example.yunq.client.cpp +++ b/yunq/example/example.yunq.client.cpp @@ -3,9 +3,20 @@ #include #include +#include #include +namespace srv::file { + + + +VFSClient::~VFSClient() { + if (endpoint_ != 0) { + check(ZCapRelease(endpoint_)); + } +} + @@ -46,3 +57,7 @@ glcr::ErrorCode VFSClient::open(const OpenFileRequest& request, File& response) } + + + +} // namepace srv::file diff --git a/yunq/example/example.yunq.client.h b/yunq/example/example.yunq.client.h index b74cab5..d9fdb3c 100644 --- a/yunq/example/example.yunq.client.h +++ b/yunq/example/example.yunq.client.h @@ -8,11 +8,15 @@ #include "example.yunq.h" + +namespace srv::file { + class VFSClient { public: VFSClient(z_cap_t VFS_cap) : endpoint_(VFS_cap) {} VFSClient(const VFSClient&) = delete; VFSClient(VFSClient&& other) : endpoint_(other.endpoint_) {other.endpoint_ = 0;}; + ~VFSClient(); z_cap_t Capability() { return endpoint_; } @@ -28,3 +32,6 @@ class VFSClient { uint64_t kCapBufferSize = 0x10; glcr::CapBuffer cap_buffer_{kCapBufferSize}; }; + + +} // namepace srv::file diff --git a/yunq/example/example.yunq.cpp b/yunq/example/example.yunq.cpp index eb8a025..1211542 100644 --- a/yunq/example/example.yunq.cpp +++ b/yunq/example/example.yunq.cpp @@ -1,6 +1,9 @@ // Generated file -- DO NOT MODIFY. #include "example.yunq.h" + +namespace srv::file { + namespace { const uint64_t header_size = 24; // 4x uint32, 1x uint64 @@ -201,4 +204,7 @@ uint64_t File::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset, glcr:: WriteHeader(bytes, offset, core_size, next_extension); return next_extension; -} \ No newline at end of file +} + + +} // namepace srv::file diff --git a/yunq/example/example.yunq.h b/yunq/example/example.yunq.h index 2c79cdb..1b9176e 100644 --- a/yunq/example/example.yunq.h +++ b/yunq/example/example.yunq.h @@ -6,6 +6,10 @@ #include #include #include + + +namespace srv::file { + class OpenFileRequest { public: OpenFileRequest() {} @@ -54,4 +58,7 @@ class File { // Parses everything except for caps. void ParseFromBytesInternal(const glcr::ByteBuffer&, uint64_t offset); -}; \ No newline at end of file +}; + + +} // namepace srv::file diff --git a/yunq/example/example.yunq.server.cpp b/yunq/example/example.yunq.server.cpp index 51a592f..555c8d7 100644 --- a/yunq/example/example.yunq.server.cpp +++ b/yunq/example/example.yunq.server.cpp @@ -4,6 +4,9 @@ #include #include + +namespace srv::file { + namespace { const uint32_t kSentinel = 0xBEEFDEAD; @@ -29,6 +32,18 @@ void VFSServerBaseThreadBootstrap(void* server_base) { ((VFSServerBase*)server_base)->ServerThread(); } +VFSServerBase::~VFSServerBase() { + if (endpoint_ != 0) { + check(ZCapRelease(endpoint_)); + } +} + +glcr::ErrorOr VFSServerBase::CreateClientCap() { + uint64_t client_cap; + RET_ERR(ZCapDuplicate(endpoint_, ~(kZionPerm_Read), &client_cap)); + return client_cap; +} + glcr::ErrorOr VFSServerBase::CreateClient() { uint64_t client_cap; RET_ERR(ZCapDuplicate(endpoint_, ~(kZionPerm_Read), &client_cap)); @@ -101,7 +116,9 @@ glcr::ErrorCode VFSServerBase::HandleRequest(const glcr::ByteBuffer& request, RET_ERR(Handleopen(yunq_request, yunq_response)); + resp_length = yunq_response.SerializeToBytes(response, kHeaderSize, resp_caps); + break; } default: { @@ -110,3 +127,7 @@ glcr::ErrorCode VFSServerBase::HandleRequest(const glcr::ByteBuffer& request, } return glcr::OK; } + + + +} // namepace srv::file diff --git a/yunq/example/example.yunq.server.h b/yunq/example/example.yunq.server.h index e17a6f9..e1cb556 100644 --- a/yunq/example/example.yunq.server.h +++ b/yunq/example/example.yunq.server.h @@ -9,13 +9,19 @@ #include "example.yunq.client.h" +namespace srv::file { + + + class VFSServerBase { public: VFSServerBase(z_cap_t VFS_cap) : endpoint_(VFS_cap) {} VFSServerBase(const VFSServerBase&) = delete; VFSServerBase(VFSServerBase&&) = delete; + virtual ~VFSServerBase(); + glcr::ErrorOr CreateClientCap(); glcr::ErrorOr CreateClient(); [[nodiscard]] Thread RunServer(); @@ -37,3 +43,7 @@ class VFSServerBase { glcr::CapBuffer& resp_caps); }; + + + +} // namepace srv::file diff --git a/yunq/message.cpp.jinja b/yunq/message.cpp.jinja index aff83b3..de4f6e5 100644 --- a/yunq/message.cpp.jinja +++ b/yunq/message.cpp.jinja @@ -1,6 +1,9 @@ // Generated file -- DO NOT MODIFY. #include "{{file}}.h" +{% if package != None %} +namespace {{package.cpp_namespace()}} { +{% endif %} namespace { const uint64_t header_size = 24; // 4x uint32, 1x uint64 @@ -188,3 +191,7 @@ uint64_t {{message.name}}::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t of return next_extension; } {%- endfor %} + +{% if package != None %} +} // namepace {{package.cpp_namespace()}} +{% endif %} diff --git a/yunq/message.h.jinja b/yunq/message.h.jinja index e64c0ee..5f00497 100644 --- a/yunq/message.h.jinja +++ b/yunq/message.h.jinja @@ -7,6 +7,9 @@ #include #include +{% if package != None %} +namespace {{package.cpp_namespace()}} { +{% endif %} {%- for message in messages %} class {{message.name}} { @@ -44,3 +47,7 @@ class {{message.name}} { void ParseFromBytesInternal(const glcr::ByteBuffer&, uint64_t offset); }; {%- endfor %} + +{% if package != None %} +} // namepace {{package.cpp_namespace()}} +{% endif %} diff --git a/yunq/parser.py b/yunq/parser.py index 4e94fff..d9c3eaa 100644 --- a/yunq/parser.py +++ b/yunq/parser.py @@ -16,6 +16,7 @@ class LexemeType(Enum): RIGHT_PAREN = 6 ARROW = 7 SEMICOLON = 8 + DOT = 9 class Lexeme(): @@ -55,6 +56,8 @@ def lexer(program: str): tokens.append(Lexeme(LexemeType.RIGHT_PAREN)) elif curr == ';': tokens.append(Lexeme(LexemeType.SEMICOLON)) + elif curr == '.': + tokens.append(Lexeme(LexemeType.DOT)) elif curr == '-': current += 1 if program[current] == '>': @@ -77,6 +80,12 @@ def lexer(program: str): return tokens +class Package(): + def __init__(self, names: list[str]): + self.names = names + + def cpp_namespace(self): + return "::".join(self.names) class Method(): def __init__(self, name: str, request: str, response: str): @@ -174,11 +183,25 @@ class Parser(): token = self.consume() if token.type != LexemeType.NAME: sys.exit("Unexpected token: %s", token) + if token.value == "package": + # TODO: Enforce that package decl comes before all messages and interface. + return self.package() if token.value == "message": return self.message() elif token.value == "interface": return self.interface() - sys.exit("Unexpected identifier '%s', expected message or interface" % token.value) + sys.exit("Unexpected identifier '%s', expected package, message, interface" % token.value) + + def package(self): + names = [self.consume_identifier()] + + while self.peektype() == LexemeType.DOT: + self.consume_check(LexemeType.DOT) + names += [self.consume_identifier()] + + self.consume_check(LexemeType.SEMICOLON) + + return Package(names) def interface(self): # "interface" consumed by decl. @@ -273,6 +296,8 @@ class Parser(): return Field(field_type, name, repeated) def type_check(decls: list[Decl]): + if sum(1 for decl in decls if type(decl) is Package) > 1: + sys.exit("Cannot have more than one package declaration") for decl in decls: if type(decl) is Interface: for method in decl.methods: @@ -291,7 +316,9 @@ def type_check(decls: list[Decl]): def print_ast(decls: list[Decl]): for decl in decls: - if type(decl) is Interface: + if type(decl) is Package: + print("%s (Package)" % decl.cpp_namespace()) + elif type(decl) is Interface: print("%s (Interface)" % decl.name) for method in decl.methods: print("\t%s (%s -> %s)" % (method.name, method.request, method.response)) diff --git a/yunq/server.cpp.jinja b/yunq/server.cpp.jinja index 1695ffb..c699829 100644 --- a/yunq/server.cpp.jinja +++ b/yunq/server.cpp.jinja @@ -4,6 +4,9 @@ #include #include +{% if package != None %} +namespace {{package.cpp_namespace()}} { +{% endif %} namespace { const uint32_t kSentinel = 0xBEEFDEAD; @@ -133,3 +136,7 @@ glcr::ErrorCode {{interface.name}}ServerBase::HandleRequest(const glcr::ByteBuff return glcr::OK; } {% endfor %} + +{% if package != None %} +} // namepace {{package.cpp_namespace()}} +{% endif %} diff --git a/yunq/server.h.jinja b/yunq/server.h.jinja index a053d63..8856900 100644 --- a/yunq/server.h.jinja +++ b/yunq/server.h.jinja @@ -8,6 +8,10 @@ #include "{{file}}.h" #include "{{file}}.client.h" +{% if package != None %} +namespace {{package.cpp_namespace()}} { +{% endif %} + {% for interface in interfaces %} class {{interface.name}}ServerBase { @@ -44,3 +48,7 @@ class {{interface.name}}ServerBase { }; {% endfor %} + +{% if package != None %} +} // namepace {{package.cpp_namespace()}} +{% endif %} diff --git a/yunq/yunq.py b/yunq/yunq.py index fa139e6..31ef8ef 100755 --- a/yunq/yunq.py +++ b/yunq/yunq.py @@ -21,42 +21,48 @@ def main(): parser = Parser(lexemes) ast = parser.parse() + print_ast(ast) type_check(ast) messages = [m for m in ast if type(m) is Message] interfaces = [i for i in ast if type(i) is Interface] + # We know there is only one package decl from the type_check. + packages = [p for p in ast if type(p) is Package] + package = None + if len(packages) == 1: + package = packages[0] jinja_env = Environment(loader=FileSystemLoader(pathlib.Path(__file__).parent.resolve())) message_header_tmpl = jinja_env.get_template("message.h.jinja") with open(filename + '.h', mode='w') as f: - message_header = message_header_tmpl.render(messages=messages) + message_header = message_header_tmpl.render(messages=messages, package=package) f.write(message_header) message_impl_tmpl = jinja_env.get_template("message.cpp.jinja") message_impl_tmpl.globals['Type'] = Type with open(filename + '.cpp', mode='w') as f: - message_impl = message_impl_tmpl.render(file=filebase, messages=messages) + message_impl = message_impl_tmpl.render(file=filebase, messages=messages, package=package) f.write(message_impl) client_header_tmpl = jinja_env.get_template("client.h.jinja") with open(filename + '.client.h', mode='w') as f: - client_header = client_header_tmpl.render(file=filebase, interfaces=interfaces) + client_header = client_header_tmpl.render(file=filebase, interfaces=interfaces, package=package) f.write(client_header) client_impl_tmpl = jinja_env.get_template("client.cpp.jinja") with open(filename + '.client.cpp', mode='w') as f: - client_impl = client_impl_tmpl.render(file=filebase, interfaces=interfaces) + client_impl = client_impl_tmpl.render(file=filebase, interfaces=interfaces, package=package) f.write(client_impl) server_header_tmpl = jinja_env.get_template("server.h.jinja") with open(filename + '.server.h', mode='w') as f: - server_header = server_header_tmpl.render(file=filebase, interfaces=interfaces) + server_header = server_header_tmpl.render(file=filebase, interfaces=interfaces, package=package) f.write(server_header) server_impl_tmpl = jinja_env.get_template("server.cpp.jinja") with open(filename + '.server.cpp', mode='w') as f: - server_impl = server_impl_tmpl.render(file=filebase, interfaces=interfaces) + server_impl = server_impl_tmpl.render(file=filebase, interfaces=interfaces, package=package) f.write(server_impl) if __name__ == "__main__":