From aa8a3d4a8949da4a571194b85b480a371c3390ab Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sat, 3 Aug 2019 15:15:11 +0200 Subject: [PATCH] IPCCompiler: Start working on a simple IPC definition language Instead of doing everything manually in C++, let's do some codegen. This patch adds a crude but effective IPC definition parser, along with two initial definition files for the AudioServer's client and server endpoints. --- DevTools/IPCCompiler/Makefile | 34 +++++ DevTools/IPCCompiler/main.cpp | 196 ++++++++++++++++++++++++++++ Kernel/makeall.sh | 1 + Servers/AudioServer/AudioClient.ipc | 4 + Servers/AudioServer/AudioServer.ipc | 9 ++ 5 files changed, 244 insertions(+) create mode 100644 DevTools/IPCCompiler/Makefile create mode 100644 DevTools/IPCCompiler/main.cpp create mode 100644 Servers/AudioServer/AudioClient.ipc create mode 100644 Servers/AudioServer/AudioServer.ipc diff --git a/DevTools/IPCCompiler/Makefile b/DevTools/IPCCompiler/Makefile new file mode 100644 index 0000000000..cb63bf9682 --- /dev/null +++ b/DevTools/IPCCompiler/Makefile @@ -0,0 +1,34 @@ +PROGRAM = IPCCompiler + +OBJS = \ + main.o \ + ../../AK/String.o \ + ../../AK/StringImpl.o \ + ../../AK/StringBuilder.o \ + ../../AK/StringView.o \ + ../../AK/JsonObject.o \ + ../../AK/JsonValue.o \ + ../../AK/JsonArray.o \ + ../../AK/JsonParser.o \ + ../../AK/LogStream.o \ + ../../Libraries/LibCore/CIODevice.o \ + ../../Libraries/LibCore/CFile.o \ + ../../Libraries/LibCore/CObject.o \ + ../../Libraries/LibCore/CEvent.o \ + ../../Libraries/LibCore/CSocket.o \ + ../../Libraries/LibCore/CLocalSocket.o \ + ../../Libraries/LibCore/CNotifier.o \ + ../../Libraries/LibCore/CEventLoop.o + +all: $(PROGRAM) + +CXXFLAGS = -std=c++17 -Wall -Wextra -ggdb3 + +%.o: %.cpp + $(PRE_CXX) $(CXX) $(CXXFLAGS) -I../ -I../../ -I../../Libraries/ -o $@ -c $< + +$(PROGRAM): $(OBJS) + $(CXX) $(LDFLAGS) -I../ -I../../ -I../../Libraries/ -o $(PROGRAM) $(OBJS) + +clean: + rm -f $(PROGRAM) $(OBJS) diff --git a/DevTools/IPCCompiler/main.cpp b/DevTools/IPCCompiler/main.cpp new file mode 100644 index 0000000000..f3472dc979 --- /dev/null +++ b/DevTools/IPCCompiler/main.cpp @@ -0,0 +1,196 @@ +#include +#include +#include +#include + +struct Parameter { + String type; + String name; +}; + +struct Message { + String name; + bool is_synchronous { false }; + Vector inputs; + Vector outputs; +}; + +struct Endpoint { + String name; + Vector messages; +}; + +int main(int argc, char** argv) +{ + if (argc != 2) { + printf("usage: %s \n", argv[0]); + return 0; + } + + CFile file(argv[1]); + if (!file.open(CIODevice::ReadOnly)) { + fprintf(stderr, "Error: Cannot open %s: %s\n", argv[1], file.error_string()); + return 1; + } + + auto file_contents = file.read_all(); + + Vector endpoints; + + Vector buffer; + + int index = 0; + + auto peek = [&]() -> char { + if (index < file_contents.size()) + return file_contents[index]; + return 0; + }; + + auto consume_one = [&]() -> char { + return file_contents[index++]; + }; + + auto extract_while = [&](Function condition) -> String { + StringBuilder builder; + while (condition(peek())) + builder.append(consume_one()); + return builder.to_string(); + }; + + auto consume_specific = [&](char ch) { + if (peek() != ch) { + dbg() << "consume_specific: wanted '" << ch << "', but got '" << peek() << "'"; + } + ASSERT(peek() == ch); + ++index; + return ch; + }; + + auto consume_string = [&](const char* str) { + for (size_t i = 0, length = strlen(str); i < length; ++i) + consume_specific(str[i]); + }; + + auto consume_whitespace = [&] { + while (isspace(peek())) + ++index; + }; + + auto parse_parameter = [&](Vector& storage) { + for (;;) { + Parameter parameter; + consume_whitespace(); + if (peek() == ')') + break; + parameter.type = extract_while([](char ch) { return !isspace(ch); }); + consume_whitespace(); + parameter.name = extract_while([](char ch) { return !isspace(ch) && ch != ')'; }); + consume_whitespace(); + storage.append(move(parameter)); + if (peek() == ',') { + consume_one(); + continue; + } + if (peek() == ')') + break; + } + }; + + auto parse_parameters = [&](Vector& storage) { + for (;;) { + consume_whitespace(); + parse_parameter(storage); + consume_whitespace(); + if (peek() == ',') { + consume_one(); + continue; + } + if (peek() == ')') + break; + } + }; + + auto parse_message = [&] { + Message message; + consume_whitespace(); + Vector buffer; + while (!isspace(peek()) && peek() != '(') + buffer.append(consume_one()); + message.name = String::copy(buffer); + consume_whitespace(); + consume_specific('('); + parse_parameters(message.inputs); + consume_specific(')'); + consume_whitespace(); + consume_specific('='); + + auto type = consume_one(); + if (type == '>') + message.is_synchronous = true; + else if (type == '|') + message.is_synchronous = false; + else + ASSERT_NOT_REACHED(); + + consume_whitespace(); + + if (message.is_synchronous) { + consume_specific('('); + parse_parameters(message.outputs); + consume_specific(')'); + } + + consume_whitespace(); + + endpoints.last().messages.append(move(message)); + }; + + auto parse_messages = [&] { + for (;;) { + consume_whitespace(); + parse_message(); + consume_whitespace(); + if (peek() == '}') + break; + } + }; + + auto parse_endpoint = [&] { + endpoints.empend(); + consume_whitespace(); + consume_string("endpoint"); + consume_whitespace(); + endpoints.last().name = extract_while([](char ch) { return !isspace(ch); }); + consume_whitespace(); + consume_specific('{'); + parse_messages(); + consume_specific('}'); + consume_whitespace(); + }; + + while (index < file_contents.size()) + parse_endpoint(); + + for (auto& endpoint : endpoints) { + dbg() << "Endpoint: '" << endpoint.name << "'"; + for (auto& message : endpoint.messages) { + dbg() << " Message: '" << message.name << "'"; + dbg() << " Sync: " << message.is_synchronous; + dbg() << " Inputs:"; + for (auto& parameter : message.inputs) + dbg() << " Parameter: " << parameter.name << " (" << parameter.type << ")"; + if (message.inputs.is_empty()) + dbg() << " (none)"; + if (message.is_synchronous) { + dbg() << " Outputs:"; + for (auto& parameter : message.outputs) + dbg() << " Parameter: " << parameter.name << " (" << parameter.type << ")"; + if (message.outputs.is_empty()) + dbg() << " (none)"; + } + } + } + + return 0; +} diff --git a/Kernel/makeall.sh b/Kernel/makeall.sh index ea041c6833..afe4b8258c 100755 --- a/Kernel/makeall.sh +++ b/Kernel/makeall.sh @@ -15,6 +15,7 @@ make_cmd="make -j $MAKEJOBS" build_targets="" build_targets="$build_targets ../DevTools/FormCompiler" +build_targets="$build_targets ../DevTools/IPCCompiler" build_targets="$build_targets ../Libraries/LibC" build_targets="$build_targets ../Libraries/LibM" build_targets="$build_targets ../Libraries/LibCore" diff --git a/Servers/AudioServer/AudioClient.ipc b/Servers/AudioServer/AudioClient.ipc new file mode 100644 index 0000000000..52a87281b0 --- /dev/null +++ b/Servers/AudioServer/AudioClient.ipc @@ -0,0 +1,4 @@ +endpoint AudioClient +{ + FinishedPlayingBuffer(i32 buffer_id) =| +} diff --git a/Servers/AudioServer/AudioServer.ipc b/Servers/AudioServer/AudioServer.ipc new file mode 100644 index 0000000000..32d8e2f24d --- /dev/null +++ b/Servers/AudioServer/AudioServer.ipc @@ -0,0 +1,9 @@ +endpoint AudioServer +{ + Greet(i32 client_pid) => (i32 server_pid, i32 client_id) + + GetMainMixVolume() => (i32 volume) + SetMainMixVolume(i32 volume) => () + + EnqueueBuffer(i32 buffer_id) => () +}