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.
This commit is contained in:
Andreas Kling 2019-08-03 15:15:11 +02:00
parent c5903ec4bb
commit aa8a3d4a89
Notes: sideshowbarker 2024-07-19 12:55:32 +09:00
5 changed files with 244 additions and 0 deletions

View file

@ -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)

View file

@ -0,0 +1,196 @@
#include <AK/StringBuilder.h>
#include <LibCore/CFile.h>
#include <ctype.h>
#include <stdio.h>
struct Parameter {
String type;
String name;
};
struct Message {
String name;
bool is_synchronous { false };
Vector<Parameter> inputs;
Vector<Parameter> outputs;
};
struct Endpoint {
String name;
Vector<Message> messages;
};
int main(int argc, char** argv)
{
if (argc != 2) {
printf("usage: %s <IPC endpoint definition file>\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<Endpoint> endpoints;
Vector<char> 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<bool(char)> 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<Parameter>& 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<Parameter>& 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<char> 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;
}

View file

@ -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"

View file

@ -0,0 +1,4 @@
endpoint AudioClient
{
FinishedPlayingBuffer(i32 buffer_id) =|
}

View file

@ -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) => ()
}