From b631fca0fd833e83d16b58bab48a4a20a045ba31 Mon Sep 17 00:00:00 2001 From: Max Wash Date: Thu, 26 Feb 2026 19:44:21 +0000 Subject: [PATCH] toolchain: add a program for compiling ipc interface definitions ifc can be used to compile .if files into self-contained header-only C libraries, which can be used to send/receive messages that conform to the described interface. --- configure-build | 28 +- toolchain/CMakeLists.txt | 17 + toolchain/cmake/FindBluelib.cmake | 212 ++++ toolchain/ifc/CMakeLists.txt | 7 + toolchain/ifc/backend.h | 13 + toolchain/ifc/backend/c-mpc/backend.c | 1457 +++++++++++++++++++++++++ toolchain/ifc/ctx.c | 52 + toolchain/ifc/ctx.h | 18 + toolchain/ifc/file-span.h | 12 + toolchain/ifc/interface.c | 60 + toolchain/ifc/interface.h | 24 + toolchain/ifc/lex.c | 740 +++++++++++++ toolchain/ifc/lex.h | 52 + toolchain/ifc/line-source.c | 164 +++ toolchain/ifc/line-source.h | 39 + toolchain/ifc/main.c | 166 +++ toolchain/ifc/msg.c | 113 ++ toolchain/ifc/msg.h | 41 + toolchain/ifc/parse.c | 287 +++++ toolchain/ifc/parse.h | 12 + toolchain/ifc/status.h | 20 + toolchain/ifc/token.c | 63 ++ toolchain/ifc/token.h | 70 ++ toolchain/ifc/type.c | 17 + toolchain/ifc/type.h | 17 + 25 files changed, 3692 insertions(+), 9 deletions(-) create mode 100644 toolchain/CMakeLists.txt create mode 100644 toolchain/cmake/FindBluelib.cmake create mode 100644 toolchain/ifc/CMakeLists.txt create mode 100644 toolchain/ifc/backend.h create mode 100644 toolchain/ifc/backend/c-mpc/backend.c create mode 100644 toolchain/ifc/ctx.c create mode 100644 toolchain/ifc/ctx.h create mode 100644 toolchain/ifc/file-span.h create mode 100644 toolchain/ifc/interface.c create mode 100644 toolchain/ifc/interface.h create mode 100644 toolchain/ifc/lex.c create mode 100644 toolchain/ifc/lex.h create mode 100644 toolchain/ifc/line-source.c create mode 100644 toolchain/ifc/line-source.h create mode 100644 toolchain/ifc/main.c create mode 100644 toolchain/ifc/msg.c create mode 100644 toolchain/ifc/msg.h create mode 100644 toolchain/ifc/parse.c create mode 100644 toolchain/ifc/parse.h create mode 100644 toolchain/ifc/status.h create mode 100644 toolchain/ifc/token.c create mode 100644 toolchain/ifc/token.h create mode 100644 toolchain/ifc/type.c create mode 100644 toolchain/ifc/type.h diff --git a/configure-build b/configure-build index 85824fd..4e9f2d5 100755 --- a/configure-build +++ b/configure-build @@ -10,7 +10,7 @@ fi build_type=Debug source_dir=$(realpath $(dirname "$0")) -native_build_dir=$source_dir/build-native +toolchain_build_dir=$source_dir/build/toolchain target_build_dir=$source_dir/build sysroot_dir=$target_build_dir/sysroot @@ -19,25 +19,35 @@ if [ ! -d "$source_dir/arch/$target_arch" ]; then exit -1 fi -rm -rf $native_build_dir $target_build_dir -mkdir -p $native_build_dir $target_build_dir +rm -rf $toolchain_build_dir $target_build_dir +mkdir -p $toolchain_build_dir $target_build_dir -pushd $native_build_dir > /dev/null +pushd $toolchain_build_dir > /dev/null cmake \ - -DCMAKE_RUNTIME_OUTPUT_DIRECTORY="$native_build_dir/bin" \ - -DCMAKE_LIBRARY_OUTPUT_DIRECTORY="$native_build_dir/lib" \ - -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY="$native_build_dir/lib" \ -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ -DCMAKE_BUILD_TYPE=$build_type \ - $source_dir/kernel/tools + $source_dir/toolchain + +case $CMAKE_GENERATOR in + Ninja) + ninja + ;; + "Unix Makefiles") + make + ;; + + *) + make + ;; +esac popd pushd $target_build_dir > /dev/null cmake \ - -DBUILD_TOOLS_DIR=$native_build_dir/bin \ + -DBUILD_TOOLS_DIR=$toolchain_build_dir/bin \ -DCMAKE_INSTALL_PREFIX=$sysroot_dir \ -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ -DTARGET_ARCH=$target_arch \ diff --git a/toolchain/CMakeLists.txt b/toolchain/CMakeLists.txt new file mode 100644 index 0000000..6fcd468 --- /dev/null +++ b/toolchain/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.14) +project(rosetta-toolchain C CXX) + +set(CMAKE_CXX_STANDARD 17) + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib) + +find_package(Bluelib REQUIRED COMPONENTS Core Ds Term Cmd Io) + +add_subdirectory( + ../kernel/tools + ${CMAKE_CURRENT_BINARY_DIR}/kernel-tools) + +add_subdirectory(ifc) diff --git a/toolchain/cmake/FindBluelib.cmake b/toolchain/cmake/FindBluelib.cmake new file mode 100644 index 0000000..22f453f --- /dev/null +++ b/toolchain/cmake/FindBluelib.cmake @@ -0,0 +1,212 @@ +#[=======================================================================[.rst: +FindBluelib +------------ + +Find the Bluelib library and header directories + +Imported Targets +^^^^^^^^^^^^^^^^ + +This module defines the following :prop_tgt:`IMPORTED` target: + +``Bluelib::Bluelib`` +The Bluelib library, if found + +Result Variables +^^^^^^^^^^^^^^^^ + +This module will set the following variables in your project: + +``Bluelib_FOUND`` +true if the Bluelib C headers and libraries were found +``Bluelib_INCLUDE_DIR`` +directories containing the Bluelib C headers. + +``Bluelib_LIBRARY`` +the C library to link against + +Hints +^^^^^ + +The user may set the environment variable ``Bluelib_PREFIX`` to the root +directory of a Bluelib library installation. +#]=======================================================================] + +set (Bluelib_SEARCH_PATHS + ~/Library/Frameworks + /Library/Frameworks + /usr/local + /usr/local/share + /usr + /sw # Fink + /opt/local # DarwinPorts + /opt/csw # Blastwave + /opt + ${Bluelib_PREFIX} + $ENV{Bluelib_PREFIX}) + +if (Bluelib_STATIC) + set(_lib_suffix "-s") +endif () + +set(supported_components + Core Ds Term Cmd Io Serial Compress + Core-MM) +set(components ${Bluelib_FIND_COMPONENTS}) +string(REPLACE ";" ", " supported_components_string_list "${supported_components}") + +if (NOT components) + set(components ${supported_components}) +endif () + +set(required_vars) + +foreach (component ${components}) + if (NOT "${component}" IN_LIST supported_components) + message(FATAL_ERROR "'${component}' is not a valid Bluelib module.\nSupported modules: ${supported_components_string_list}") + endif () + + string(TOLOWER ${component} header_name) + string(REPLACE "-mm" "" header_name "${header_name}") + + string(TOLOWER ${component} lib_name) + set(lib_name ${lib_name}${_lib_suffix}) + + set(cmake_lib_name ${component}) + string(REPLACE "-" "_" cmake_lib_name "${cmake_lib_name}") + + if (NOT Bluelib_${cmake_lib_dir}_INCLUDE_DIR) + find_path(Bluelib_${cmake_lib_name}_INCLUDE_DIR + NAMES blue/${header_name}.h blue/${header_name}.hpp + ${Bluelib_FIND_ARGS} + PATH_SUFFIXES include + PATHS ${Bluelib_SEARCH_PATHS}) + endif () + + if (NOT Bluelib_${cmake_lib_dir}_LIBRARY) + find_library(Bluelib_${cmake_lib_name}_LIBRARY + NAMES blue-${lib_name} ${Bluelib_FIND_ARGS} + PATH_SUFFIXES lib + PATHS ${Bluelib_SEARCH_PATHS}) + else () + # on Windows, ensure paths are in canonical format (forward slahes): + file(TO_CMAKE_PATH "${Bluelib_${cmake_lib_name}_LIBRARY}" Bluelib_${cmake_lib_name}_LIBRARY) + endif() + + list(APPEND required_vars Bluelib_${cmake_lib_name}_INCLUDE_DIR Bluelib_${cmake_lib_name}_LIBRARY) +endforeach (component) + +unset(Bluelib_FIND_ARGS) + +include(FindPackageHandleStandardArgs) + +find_package_handle_standard_args(Bluelib + REQUIRED_VARS ${required_vars}) + +if (Bluelib_FOUND) + set(created_targets) + foreach (component ${components}) + string(TOLOWER ${component} header_name) + string(REPLACE "-mm" "" header_name "${header_name}") + + string(TOLOWER ${component} lib_name) + set(lib_name ${lib_name}${_lib_suffix}) + + set(cmake_lib_name ${component}) + string(REPLACE "-" "_" cmake_lib_name "${cmake_lib_name}") + + if(NOT TARGET Bluelib::${component}) + add_library(Bluelib::${component} UNKNOWN IMPORTED) + set_target_properties(Bluelib::${component} PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${Bluelib_${cmake_lib_name}_INCLUDE_DIR}") + target_compile_definitions(Bluelib::${component} INTERFACE _CRT_SECURE_NO_WARNINGS=1) + + if (Bluelib_STATIC) + target_compile_definitions(Bluelib::${component} INTERFACE BLUELIB_STATIC=1) + endif () + + set_target_properties(Bluelib::${component} PROPERTIES + IMPORTED_LINK_INTERFACE_LANGUAGES "C" + IMPORTED_LOCATION "${Bluelib_${cmake_lib_name}_LIBRARY}") + set(created_targets ${created_targets} ${component}) + endif () + endforeach (component) + + foreach (component ${created_targets}) + if ("${component}" STREQUAL "Ds") + if (NOT TARGET Bluelib::Core) + message(FATAL_ERROR "Bluelib: Module 'Ds' depends on 'Core', which was not specified in find_package()") + endif () + + target_link_libraries(Bluelib::Ds INTERFACE Bluelib::Core) + endif () + + if ("${component}" STREQUAL "Term") + if (NOT TARGET Bluelib::Core) + message(FATAL_ERROR "Bluelib: Module 'Term' depends on 'Core', which was not specified in find_package()") + endif () + + if (NOT TARGET Bluelib::Ds) + message(FATAL_ERROR "Bluelib: Module 'Term' depends on 'Ds', which was not specified in find_package()") + endif () + + target_link_libraries(Bluelib::Term INTERFACE Bluelib::Core Bluelib::Ds) + endif () + + if ("${component}" STREQUAL "Serial") + if (NOT TARGET Bluelib::Core) + message(FATAL_ERROR "Bluelib: Module 'Serial' depends on 'Core', which was not specified in find_package()") + endif () + + if (NOT TARGET Bluelib::Ds) + message(FATAL_ERROR "Bluelib: Module 'Serial' depends on 'Ds', which was not specified in find_package()") + endif () + + target_link_libraries(Bluelib::Serial INTERFACE Bluelib::Core Bluelib::Ds) + endif () + + if ("${component}" STREQUAL "Cmd") + if (NOT TARGET Bluelib::Core) + message(FATAL_ERROR "Bluelib: Module 'Cmd' depends on 'Core', which was not specified in find_package()") + endif () + + if (NOT TARGET Bluelib::Ds) + message(FATAL_ERROR "Bluelib: Module 'Cmd' depends on 'Ds', which was not specified in find_package()") + endif () + + if (NOT TARGET Bluelib::Term) + message(FATAL_ERROR "Bluelib: Module 'Cmd' depends on 'Term', which was not specified in find_package()") + endif () + + target_link_libraries(Bluelib::Cmd INTERFACE Bluelib::Core Bluelib::Ds Bluelib::Term) + endif () + + if ("${component}" STREQUAL "Io") + if (NOT TARGET Bluelib::Core) + message(FATAL_ERROR "Bluelib: Module 'Io' depends on 'Core', which was not specified in find_package()") + endif () + + if (NOT TARGET Bluelib::Ds) + message(FATAL_ERROR "Bluelib: Module 'Io' depends on 'Ds', which was not specified in find_package()") + endif () + + target_link_libraries(Bluelib::Io INTERFACE Bluelib::Core Bluelib::Ds) + endif () + + if ("${component}" STREQUAL "Compress") + if (NOT TARGET Bluelib::Core) + message(FATAL_ERROR "Bluelib: Module 'Compress' depends on 'Core', which was not specified in find_package()") + endif () + + target_link_libraries(Bluelib::Compress INTERFACE Bluelib::Core Bluelib::Ds) + endif () + + if ("${component}" STREQUAL "Core-MM") + if (NOT TARGET Bluelib::Core) + message(FATAL_ERROR "Bluelib: Module 'Core-MM' depends on 'Core', which was not specified in find_package()") + endif () + + target_link_libraries(Bluelib::Core-MM INTERFACE Bluelib::Core) + endif () + endforeach (component) +endif() diff --git a/toolchain/ifc/CMakeLists.txt b/toolchain/ifc/CMakeLists.txt new file mode 100644 index 0000000..400f265 --- /dev/null +++ b/toolchain/ifc/CMakeLists.txt @@ -0,0 +1,7 @@ +file(GLOB sources + *.c *.h + backend/c-mpc/*.c backend/c-mpc/*.h) + +add_executable(ifc ${sources}) + +target_link_libraries(ifc Bluelib::Core Bluelib::Ds Bluelib::Cmd Bluelib::Io) diff --git a/toolchain/ifc/backend.h b/toolchain/ifc/backend.h new file mode 100644 index 0000000..6bf8a1b --- /dev/null +++ b/toolchain/ifc/backend.h @@ -0,0 +1,13 @@ +#ifndef IFC_BACKEND_H_ +#define IFC_BACKEND_H_ + +struct interface_definition; + +struct backend { + const char *b_name; + int (*b_emit)(const struct interface_definition *); +}; + +extern const struct backend *c_mpc_backend(void); + +#endif diff --git a/toolchain/ifc/backend/c-mpc/backend.c b/toolchain/ifc/backend/c-mpc/backend.c new file mode 100644 index 0000000..cc5844f --- /dev/null +++ b/toolchain/ifc/backend/c-mpc/backend.c @@ -0,0 +1,1457 @@ +#include "../../backend.h" + +#include "../../interface.h" +#include "../../msg.h" +#include "../../type.h" + +#include +#include +#include +#include +#include + +struct emit_ctx { + b_stream *ctx_out; +}; + +static void emit(struct emit_ctx *ctx, const char *format, ...) +{ + va_list arg; + va_start(arg, format); + b_stream_write_vfmt(ctx->ctx_out, NULL, format, arg); + va_end(arg); +} + +static void emit_indent(struct emit_ctx *ctx) +{ + b_stream_push_indent(ctx->ctx_out, 4); +} + +static void emit_indent_zero(struct emit_ctx *ctx) +{ + b_stream_push_indent(ctx->ctx_out, -100); +} + +static void emit_unindent(struct emit_ctx *ctx) +{ + b_stream_pop_indent(ctx->ctx_out); +} + +static int emit_include(struct emit_ctx *ctx, const char *path, bool system) +{ + if (system) { + emit(ctx, "#include <%s>\n", path); + } else { + emit(ctx, "#include \"%s\"\n", path); + } + + return 0; +} + +static int emit_header_guard_start( + struct emit_ctx *ctx, + const struct interface_definition *iface) +{ + b_string *header_guard = b_string_create(); + b_string_append_cstrf(header_guard, "%s_H_", iface->if_name); + b_string_toupper(header_guard); + + emit(ctx, + "#ifndef %s\n#define %s\n\n", + b_string_ptr(header_guard), + b_string_ptr(header_guard)); + b_string_unref(header_guard); + return 0; +} + +static int emit_header_guard_end( + struct emit_ctx *ctx, + const struct interface_definition *iface) +{ + emit(ctx, "\n#endif"); + return 0; +} + +static int emit_header_lib_impl(struct emit_ctx *ctx) +{ + emit(ctx, "kern_status_t msg_recv_generic(\n"); + emit_indent(ctx); + emit(ctx, + "kern_handle_t channel,\nmsgid_t *out_id,\nstruct msg_header " + "*out_hdr)\n"); + emit_unindent(ctx); + emit(ctx, "{\n"); + emit_indent(ctx); + + /* clang-format off */ + emit(ctx, + "struct iovec iov = IOVEC(out_hdr, sizeof *out_hdr);\n" + "struct msg kmsg = MSG(&iov, 1, NULL, 0);\n" + "kern_status_t status = msg_recv(channel, 0, out_id, &kmsg);\n" + "if (status != KERN_OK) return status;\n" + "if (out_hdr->hdr_magic != MSG_MAGIC) return KERN_INVALID_ARGUMENT;\n" + "return KERN_OK;\n"); + /* clang-format on */ + emit_unindent(ctx); + emit(ctx, "}\n\n"); + + emit(ctx, "kern_status_t msg_read_generic(\n"); + emit_indent(ctx); + emit(ctx, + "kern_handle_t channel,\nmsgid_t id,\nstruct msg_header " + "*out_hdr)\n"); + emit_unindent(ctx); + emit(ctx, "{\n"); + emit_indent(ctx); + + /* clang-format off */ + emit(ctx, + "struct iovec iov = IOVEC(out_hdr, sizeof *out_hdr);\n" + "size_t r = 0;\n" + "kern_status_t status = msg_read(channel, id, 0, &iov, 1, &r);\n" + "if (status != KERN_OK) return status;\n" + "if (r != sizeof *out_hdr) return KERN_INVALID_ARGUMENT;\n" + "if (out_hdr->hdr_magic != MSG_MAGIC) return KERN_INVALID_ARGUMENT;\n" + "return KERN_OK;\n"); + /* clang-format on */ + + emit_unindent(ctx); + emit(ctx, "}\n\n"); + + emit(ctx, "kern_status_t msg_reply_generic(\n"); + emit_indent(ctx); + emit(ctx, + "kern_handle_t channel,\nmsgid_t id,\nconst struct msg_header " + "*msg, uint16_t status)\n"); + emit_unindent(ctx); + emit(ctx, "{\n"); + emit_indent(ctx); + + /* clang-format off */ + emit(ctx, + "struct msg_header resp_data = {\n"); + emit_indent(ctx); + emit(ctx, + ".hdr_magic = MSG_MAGIC,\n.hdr_status = status,\n"); + emit_unindent(ctx); + emit(ctx, "};\n"); + emit(ctx, + "if (msg) {\n"); + emit_indent(ctx); + emit(ctx, + "resp_data.hdr_protocol = msg->hdr_protocol;\n" + "resp_data.hdr_func = msg->hdr_func;\n"); + emit_unindent(ctx); + emit(ctx, "}\n"); + + emit(ctx, + "struct iovec iov = IOVEC(&resp_data, sizeof resp_data);\n" + "struct msg resp = MSG(&iov, 1, NULL, 0);\n" + "return msg_reply(channel, 0, id, &resp);\n"); + /* clang-format on */ + + emit_unindent(ctx); + emit(ctx, "}\n\n"); + + return 0; +} + +static int emit_header_lib_definitions(struct emit_ctx *ctx) +{ + emit(ctx, "#if !defined(MSG_STRINGBUF_SIZE)\n"); + emit(ctx, "#define MSG_STRINGBUF_SIZE 256\n"); + emit(ctx, "#endif\n\n"); + + emit(ctx, "#if !defined(MSG_LIB_DEFINED)\n"); + emit(ctx, "#define MSG_LIB_DEFINED\n\n"); + + emit(ctx, "#define MSG_MAGIC 0x9AB07D10U\n\n"); + + emit(ctx, "struct msg_header {\n"); + emit_indent(ctx); + + emit(ctx, "uint32_t hdr_magic;\n"); + emit(ctx, "uint32_t hdr_protocol;\n"); + emit(ctx, "uint16_t hdr_func;\n"); + emit(ctx, "uint16_t hdr_status;\n"); + + emit_unindent(ctx); + emit(ctx, "};\n\n"); + + emit(ctx, "struct msg_string {\n"); + emit_indent(ctx); + + emit(ctx, "char *str_buf;\n"); + emit(ctx, "size_t str_len;\n"); + emit(ctx, "size_t str_max;\n"); + + emit_unindent(ctx); + emit(ctx, "};\n\n"); + + emit(ctx, "extern kern_status_t msg_recv_generic(\n"); + emit_indent(ctx); + emit(ctx, + "kern_handle_t channel,\nmsgid_t *out_id,\nstruct msg_header " + "*out_hdr);\n", + NULL); + emit_unindent(ctx); + + emit(ctx, "extern kern_status_t msg_read_generic(\n"); + emit_indent(ctx); + emit(ctx, + "kern_handle_t channel,\nmsgid_t id,\nstruct msg_header " + "*out_hdr);\n", + NULL); + emit_unindent(ctx); + + emit(ctx, "extern kern_status_t msg_reply_generic(\n"); + emit_indent(ctx); + emit(ctx, + "kern_handle_t channel,\nmsgid_t id,\nconst struct msg_header " + "*msg, uint16_t status);\n", + NULL); + emit_unindent(ctx); + + emit(ctx, "#if defined(MSG_IMPLEMENTATION)\n\n"); + emit_header_lib_impl(ctx); + emit(ctx, "#endif\n\n"); + + emit(ctx, "#endif\n\n"); + return 0; +} + +static int emit_interface_id_macros( + struct emit_ctx *ctx, + const struct interface_definition *iface) +{ + b_string *iface_define_name = b_string_create(); + b_string_append_cstr(iface_define_name, iface->if_name); + b_string_toupper(iface_define_name); + + uint32_t protocol_id = b_hash_cstr(iface->if_name) & 0xFFFFFFFF; + + emit(ctx, + "/* %s protocol ID */\n#define PROTOCOL_%s 0x%08xU\n\n", + iface->if_name, + b_string_ptr(iface_define_name), + protocol_id); + b_string_unref(iface_define_name); + + b_string *msg_name = b_string_create(); + b_queue_entry *entry = b_queue_first(&iface->if_msg); + while (entry) { + const struct msg_definition *msg + = b_unbox(struct msg_definition, entry, msg_entry); + b_string_clear(msg_name); + b_string_append_cstrf( + msg_name, + "%s.%s", + iface->if_name, + msg->msg_name); + + emit(ctx, "/* %s message ID */\n", b_string_ptr(msg_name)); + uint16_t msg_id = b_hash_cstr(b_string_ptr(msg_name)) & 0xFFFF; + + b_string_clear(msg_name); + b_string_append_cstrf( + msg_name, + "%s_%s", + iface->if_name, + msg->msg_name); + b_string_toupper(msg_name); + emit(ctx, + "#define MSG_%s 0x%04xU\n", + b_string_ptr(msg_name), + msg_id); + + entry = b_queue_next(entry); + } + + emit(ctx, "\n"); + b_string_unref(msg_name); + + return 0; +} + +static int emit_msg_struct_member( + struct emit_ctx *ctx, + const struct msg_parameter *param) +{ + switch (param->p_type->ty_id) { + case TYPE_INT: + emit(ctx, "uint32_t %s;\n", param->p_name); + break; + case TYPE_STRING: + emit(ctx, "uint16_t %s_offset;\n", param->p_name); + emit(ctx, "uint16_t %s_len;\n", param->p_name); + break; + default: + break; + } + + return 0; +} + +static int emit_msg_struct_params( + struct emit_ctx *ctx, + const struct msg_definition *msg) +{ + b_queue_entry *entry = b_queue_first(&msg->msg_params); + while (entry) { + const struct msg_parameter *param + = b_unbox(struct msg_parameter, entry, p_entry); + emit_msg_struct_member(ctx, param); + entry = b_queue_next(entry); + } + + entry = b_queue_first(&msg->msg_results); + while (entry) { + const struct msg_parameter *param + = b_unbox(struct msg_parameter, entry, p_entry); + if (param->p_type->ty_id != TYPE_STRING) { + entry = b_queue_next(entry); + continue; + } + + emit(ctx, "uint16_t %s_max;\n", param->p_name); + + entry = b_queue_next(entry); + } + + return 0; +} + +static int emit_msg_struct_results( + struct emit_ctx *ctx, + const struct msg_definition *msg) +{ + b_queue_entry *entry = b_queue_first(&msg->msg_results); + while (entry) { + const struct msg_parameter *param + = b_unbox(struct msg_parameter, entry, p_entry); + emit_msg_struct_member(ctx, param); + entry = b_queue_next(entry); + } + + return 0; +} + +static int emit_interface_msg_struct( + struct emit_ctx *ctx, + const struct interface_definition *iface, + const struct msg_definition *msg) +{ + b_string *tmp = b_string_create(); + + emit(ctx, "struct msg_%s_%s {\n", iface->if_name, msg->msg_name); + emit_indent(ctx); + + emit(ctx, "struct msg_header msg_header;\n"); + emit(ctx, "union {\n"); + emit_indent(ctx); + + /* msg_request inner struct */ + emit(ctx, "struct {\n"); + emit_indent(ctx); + + emit_msg_struct_params(ctx, msg); + + emit_unindent(ctx); + emit(ctx, "} msg_request;\n"); + + /* msg_response inner struct */ + emit(ctx, "struct {\n"); + emit_indent(ctx); + + emit_msg_struct_results(ctx, msg); + + emit_unindent(ctx); + emit(ctx, "} msg_response;\n"); + + emit_unindent(ctx); + emit(ctx, "};\n"); + + emit_unindent(ctx); + emit(ctx, "};\n"); + + b_string_unref(tmp); + return 0; +} + +static int emit_interface_msg_structs( + struct emit_ctx *ctx, + const struct interface_definition *iface) +{ + b_queue_entry *entry = b_queue_first(&iface->if_msg); + while (entry) { + const struct msg_definition *msg + = b_unbox(struct msg_definition, entry, msg_entry); + + emit_interface_msg_struct(ctx, iface, msg); + + entry = b_queue_next(entry); + } + + emit(ctx, "\n"); + return 0; +} + +static int emit_interface_msg_function_send_impl( + struct emit_ctx *ctx, + const struct interface_definition *iface, + const struct msg_definition *msg) +{ + b_string *iface_ucase = b_string_create_from_cstr(iface->if_name); + b_string_toupper(iface_ucase); + b_string *msg_ucase = b_string_create_from_cstr(msg->msg_name); + b_string_toupper(msg_ucase); + + emit(ctx, + "struct msg_%s_%s msg = {0};\n", + iface->if_name, + msg->msg_name); + emit(ctx, "msg.msg_header.hdr_magic = MSG_MAGIC;\n"); + emit(ctx, + "msg.msg_header.hdr_protocol = PROTOCOL_%s;\n", + b_string_ptr(iface_ucase)); + emit(ctx, + "msg.msg_header.hdr_func = MSG_%s_%s;\n\n", + b_string_ptr(iface_ucase), + b_string_ptr(msg_ucase)); + + b_string_unref(iface_ucase); + b_string_unref(msg_ucase); + + emit(ctx, "uint16_t offset = sizeof msg;\n\n"); + + b_queue_entry *entry = b_queue_first(&msg->msg_params); + while (entry) { + struct msg_parameter *param + = b_unbox(struct msg_parameter, entry, p_entry); + switch (param->p_type->ty_id) { + case TYPE_INT: + emit(ctx, + "msg.msg_request.%s = %s;\n\n", + param->p_name, + param->p_name); + break; + case TYPE_STRING: + emit(ctx, + "msg.msg_request.%s_offset = offset;\n", + param->p_name); + emit(ctx, + "msg.msg_request.%s_len = strlen(%s);\n", + param->p_name, + param->p_name); + emit(ctx, + "offset += msg.msg_request.%s_len;\n", + param->p_name); + break; + default: + break; + } + + entry = b_queue_next(entry); + } + + entry = b_queue_first(&msg->msg_results); + while (entry) { + struct msg_parameter *param + = b_unbox(struct msg_parameter, entry, p_entry); + switch (param->p_type->ty_id) { + case TYPE_STRING: + emit(ctx, + "msg.msg_request.%s_max = out_%s->str_max;\n", + param->p_name, + param->p_name); + break; + default: + break; + } + + entry = b_queue_next(entry); + } + + emit(ctx, "struct iovec req_iov[] = {\n"); + emit_indent(ctx); + emit(ctx, "IOVEC(&msg, sizeof msg),\n"); + size_t nr_req_iov = 1; + + entry = b_queue_first(&msg->msg_params); + while (entry) { + struct msg_parameter *param + = b_unbox(struct msg_parameter, entry, p_entry); + if (param->p_type->ty_id != TYPE_STRING) { + entry = b_queue_next(entry); + continue; + } + + emit(ctx, + "IOVEC(%s, msg.msg_request.%s_len),\n", + param->p_name, + param->p_name); + + nr_req_iov++; + entry = b_queue_next(entry); + } + + emit_unindent(ctx); + emit(ctx, "};\n\n"); + + emit(ctx, "struct iovec resp_iov[] = {\n"); + emit_indent(ctx); + emit(ctx, "IOVEC(&msg, sizeof msg),\n"); + size_t nr_resp_iov = 1; + + entry = b_queue_first(&msg->msg_results); + while (entry) { + struct msg_parameter *param + = b_unbox(struct msg_parameter, entry, p_entry); + if (param->p_type->ty_id != TYPE_STRING) { + entry = b_queue_next(entry); + continue; + } + + emit(ctx, + "IOVEC(out_%s->str_buf, out_%s->str_max),\n", + param->p_name, + param->p_name); + + nr_resp_iov++; + entry = b_queue_next(entry); + } + + emit_unindent(ctx); + emit(ctx, "};\n\n"); + + emit(ctx, "struct msg req = MSG(req_iov, %zu, NULL, 0);\n", nr_req_iov); + emit(ctx, + "struct msg resp = MSG(resp_iov, %zu, NULL, 0);\n", + nr_resp_iov); + + emit(ctx, "kern_status_t status = msg_send(port, 0, &req, &resp);\n"); + emit(ctx, "if (status != KERN_OK) return status;\n\n"); + + entry = b_queue_first(&msg->msg_results); + while (entry) { + struct msg_parameter *param + = b_unbox(struct msg_parameter, entry, p_entry); + switch (param->p_type->ty_id) { + case TYPE_STRING: + emit(ctx, + "out_%s->str_len = msg.msg_response.%s_len;\n", + param->p_name, + param->p_name); + emit(ctx, + "out_%s->str_buf[out_%s->str_len < " + "out_%s->str_max ? out_%s->str_len : " + "(out_%s->str_max - 1)] = '\\0';\n", + param->p_name, + param->p_name, + param->p_name, + param->p_name, + param->p_name); + break; + default: + emit(ctx, + "*out_%s = msg.msg_response.%s;\n", + param->p_name, + param->p_name); + + break; + } + + entry = b_queue_next(entry); + } + + emit(ctx, "return status;\n"); + return 0; +} + +static int emit_interface_msg_function_recv_impl( + struct emit_ctx *ctx, + const struct interface_definition *iface, + const struct msg_definition *msg) +{ + emit(ctx, "struct msg_%s_%s msg;\n", iface->if_name, msg->msg_name); + emit(ctx, "struct iovec iov = IOVEC(&msg, sizeof msg);\n"); + emit(ctx, "size_t r = 0;\n\n"); + + emit(ctx, + "kern_status_t status = msg_read(channel, id, 0, &iov, 1, " + "&r);\n"); + + emit(ctx, "if (status != KERN_OK) return status;\n"); + emit(ctx, "if (r != sizeof msg) return KERN_INVALID_ARGUMENT;\n\n"); + + b_queue_entry *entry = b_queue_first(&msg->msg_params); + while (entry) { + struct msg_parameter *param + = b_unbox(struct msg_parameter, entry, p_entry); + switch (param->p_type->ty_id) { + case TYPE_INT: + emit(ctx, + "*out_%s = msg.msg_request.%s;\n", + param->p_name, + param->p_name); + break; + case TYPE_STRING: + emit(ctx, + "out_%s->str_len = msg.msg_request.%s_len;\n", + param->p_name, + param->p_name); + + emit(ctx, + "if (out_%s->str_max > 1) {\n", + param->p_name); + emit_indent(ctx); + + emit(ctx, + "iov.io_base = (virt_addr_t)out_%s->str_buf;\n", + param->p_name); + emit(ctx, + "iov.io_len = out_%s->str_max - 1;\n", + param->p_name); + emit(ctx, + "if (iov.io_len > out_%s->str_len) iov.io_len " + "= out_%s->str_len;\n", + param->p_name, + param->p_name); + + emit(ctx, "status = msg_read(\n"); + emit_indent(ctx); + emit(ctx, "channel,\n"); + emit(ctx, "id,\n"); + emit(ctx, + "msg.msg_request.%s_offset,\n", + param->p_name); + emit(ctx, "&iov,\n"); + emit(ctx, "1,\n"); + emit(ctx, "&r);\n"); + emit_unindent(ctx); + + emit(ctx, "if (status != KERN_OK) return status;\n\n"); + emit(ctx, + "if (r != iov.io_len) return " + "KERN_INVALID_ARGUMENT;\n\n"); + emit(ctx, + "out_%s->str_buf[iov.io_len] = '\\0';\n", + param->p_name); + + emit_unindent(ctx); + emit(ctx, "} else if (out_%s->str_max > 0) {\n"); + emit_indent(ctx); + emit(ctx, + "out_%s->str_buf[0] = '\\0';\n", + param->p_name); + emit_unindent(ctx); + emit(ctx, "}\n\n"); + break; + default: + break; + } + + entry = b_queue_next(entry); + } + + emit(ctx, "return status;\n"); + return 0; +} + +static int emit_interface_msg_function_reply_impl( + struct emit_ctx *ctx, + const struct interface_definition *iface, + const struct msg_definition *msg) +{ + b_string *iface_ucase = b_string_create_from_cstr(iface->if_name); + b_string_toupper(iface_ucase); + b_string *msg_ucase = b_string_create_from_cstr(msg->msg_name); + b_string_toupper(msg_ucase); + + emit(ctx, + "struct msg_%s_%s msg = {0};\n", + iface->if_name, + msg->msg_name); + emit(ctx, "msg.msg_header.hdr_magic = MSG_MAGIC;\n"); + emit(ctx, + "msg.msg_header.hdr_protocol = PROTOCOL_%s;\n", + b_string_ptr(iface_ucase)); + emit(ctx, + "msg.msg_header.hdr_func = MSG_%s_%s;\n\n", + b_string_ptr(iface_ucase), + b_string_ptr(msg_ucase)); + + b_string_unref(iface_ucase); + b_string_unref(msg_ucase); + + b_queue_entry *entry = b_queue_first(&msg->msg_results); + while (entry) { + struct msg_parameter *param + = b_unbox(struct msg_parameter, entry, p_entry); + switch (param->p_type->ty_id) { + case TYPE_INT: + emit(ctx, + "msg.msg_response.%s = %s;\n\n", + param->p_name, + param->p_name); + break; + case TYPE_STRING: + emit(ctx, + "msg.msg_response.%s_len = strlen(%s);\n", + param->p_name, + param->p_name); + break; + default: + break; + } + + entry = b_queue_next(entry); + } + + emit(ctx, "\nstruct iovec iov[] = {\n"); + emit_indent(ctx); + emit(ctx, "IOVEC(&msg, sizeof msg),\n"); + size_t nr_iov = 1; + + entry = b_queue_first(&msg->msg_results); + while (entry) { + struct msg_parameter *param + = b_unbox(struct msg_parameter, entry, p_entry); + switch (param->p_type->ty_id) { + case TYPE_STRING: + emit(ctx, + "IOVEC(%s, msg.msg_response.%s_len),\n", + param->p_name, + param->p_name); + nr_iov++; + break; + default: + break; + } + + entry = b_queue_next(entry); + } + + emit_unindent(ctx); + emit(ctx, "};\n\n"); + + emit(ctx, "struct msg reply = MSG(iov, %zu, NULL, 0);\n", nr_iov); + emit(ctx, "return msg_reply(channel, 0, id, &reply);\n"); + + return 0; +} + +static int emit_interface_msg_function_send( + struct emit_ctx *ctx, + const struct interface_definition *iface, + const struct msg_definition *msg, + bool prototype_only) +{ + if (prototype_only) { + emit(ctx, "extern "); + } + + emit(ctx, "kern_status_t %s_%s(\n", iface->if_name, msg->msg_name); + + emit_indent(ctx); + + emit(ctx, "kern_handle_t port"); + + b_queue_entry *entry = b_queue_first(&msg->msg_params); + while (entry) { + struct msg_parameter *param + = b_unbox(struct msg_parameter, entry, p_entry); + emit(ctx, ",\n"); + switch (param->p_type->ty_id) { + case TYPE_INT: + emit(ctx, "int %s", param->p_name); + break; + case TYPE_STRING: + emit(ctx, "const char *%s", param->p_name); + break; + default: + break; + } + entry = b_queue_next(entry); + } + + entry = b_queue_first(&msg->msg_results); + while (entry) { + struct msg_parameter *param + = b_unbox(struct msg_parameter, entry, p_entry); + emit(ctx, ",\n"); + switch (param->p_type->ty_id) { + case TYPE_INT: + emit(ctx, "int *out_%s", param->p_name); + break; + case TYPE_STRING: + emit(ctx, "struct msg_string *out_%s", param->p_name); + break; + default: + break; + } + entry = b_queue_next(entry); + } + + emit(ctx, ")"); + emit_unindent(ctx); + + if (prototype_only) { + emit(ctx, ";\n"); + return 0; + } + + emit(ctx, "\n{\n"); + emit_indent(ctx); + + emit_interface_msg_function_send_impl(ctx, iface, msg); + + emit_unindent(ctx); + emit(ctx, "}\n\n"); + + return 0; +} + +static int emit_interface_msg_function_recv( + struct emit_ctx *ctx, + const struct interface_definition *iface, + const struct msg_definition *msg, + bool prototype_only) +{ + if (prototype_only) { + emit(ctx, "extern "); + } + + emit(ctx, "kern_status_t %s_%s_recv(\n", iface->if_name, msg->msg_name); + + emit_indent(ctx); + + emit(ctx, "kern_handle_t channel,\nmsgid_t id"); + + b_queue_entry *entry = b_queue_first(&msg->msg_params); + while (entry) { + struct msg_parameter *param + = b_unbox(struct msg_parameter, entry, p_entry); + emit(ctx, ",\n"); + switch (param->p_type->ty_id) { + case TYPE_INT: + emit(ctx, "int *out_%s", param->p_name); + break; + case TYPE_STRING: + emit(ctx, "struct msg_string *out_%s", param->p_name); + break; + default: + break; + } + entry = b_queue_next(entry); + } + + emit(ctx, ")"); + emit_unindent(ctx); + + if (prototype_only) { + emit(ctx, ";\n"); + return 0; + } + + emit(ctx, "\n{\n"); + emit_indent(ctx); + + emit_interface_msg_function_recv_impl(ctx, iface, msg); + + emit_unindent(ctx); + emit(ctx, "}\n\n"); + + return 0; +} + +static int emit_interface_msg_function_reply( + struct emit_ctx *ctx, + const struct interface_definition *iface, + const struct msg_definition *msg, + bool prototype_only) +{ + if (prototype_only) { + emit(ctx, "extern "); + } + + emit(ctx, + "kern_status_t %s_%s_reply(\n", + iface->if_name, + msg->msg_name); + + emit_indent(ctx); + + emit(ctx, "kern_handle_t channel,\nmsgid_t id"); + + b_queue_entry *entry = b_queue_first(&msg->msg_results); + while (entry) { + struct msg_parameter *param + = b_unbox(struct msg_parameter, entry, p_entry); + emit(ctx, ",\n"); + switch (param->p_type->ty_id) { + case TYPE_INT: + emit(ctx, "int %s", param->p_name); + break; + case TYPE_STRING: + emit(ctx, "const char *%s", param->p_name); + break; + default: + break; + } + entry = b_queue_next(entry); + } + + emit(ctx, ")"); + emit_unindent(ctx); + + if (prototype_only) { + emit(ctx, ";\n"); + return 0; + } + + emit(ctx, "\n{\n"); + emit_indent(ctx); + + emit_interface_msg_function_reply_impl(ctx, iface, msg); + + emit_unindent(ctx); + emit(ctx, "}\n\n"); + + return 0; +} + +static int emit_interface_msg_functions( + struct emit_ctx *ctx, + const struct interface_definition *iface, + const struct msg_definition *msg, + bool prototype_only) +{ + emit_interface_msg_function_send(ctx, iface, msg, prototype_only); + emit_interface_msg_function_recv(ctx, iface, msg, prototype_only); + emit_interface_msg_function_reply(ctx, iface, msg, prototype_only); + return 0; +} + +static int emit_string_destructor_list( + struct emit_ctx *ctx, + b_queue_entry *param_entry) +{ + while (param_entry) { + struct msg_parameter *param + = b_unbox(struct msg_parameter, param_entry, p_entry); + + if (param->p_type->ty_id == TYPE_STRING) { + emit(ctx, "free(%s.str_buf);\n", param->p_name); + } + + param_entry = b_queue_prev(param_entry); + } + + return 0; +} + +static int emit_interface_dispatcher_impl_msg( + struct emit_ctx *ctx, + const struct interface_definition *iface, + const struct msg_definition *msg) +{ + emit(ctx, + "if (!vtable->%s) return msg_reply_generic(channel, id, msg, " + "KERN_UNIMPLEMENTED);\n", + msg->msg_name); + b_queue_entry *entry = b_queue_first(&msg->msg_params); + while (entry) { + struct msg_parameter *param + = b_unbox(struct msg_parameter, entry, p_entry); + switch (param->p_type->ty_id) { + case TYPE_INT: + emit(ctx, "int %s;\n", param->p_name); + break; + case TYPE_STRING: + emit(ctx, + "struct msg_string %s = {0};\n", + param->p_name); + emit_indent_zero(ctx); + emit(ctx, "#if defined(MSG_NO_MALLOC)\n"); + emit_unindent(ctx); + emit(ctx, + "char %s_buf[MSG_STRINGBUF_SIZE];\n", + param->p_name); + emit(ctx, + "%s.str_buf = %s_buf;\n", + param->p_name, + param->p_name); + emit(ctx, + "%s.str_max = MSG_STRINGBUF_SIZE;\n", + param->p_name); + + emit_indent_zero(ctx); + emit(ctx, "#endif\n"); + emit_unindent(ctx); + break; + default: + break; + } + + entry = b_queue_next(entry); + } + + entry = b_queue_first(&msg->msg_results); + while (entry) { + struct msg_parameter *param + = b_unbox(struct msg_parameter, entry, p_entry); + switch (param->p_type->ty_id) { + case TYPE_INT: + emit(ctx, "int %s;\n", param->p_name); + break; + case TYPE_STRING: + emit(ctx, + "struct msg_string %s = {0};\n", + param->p_name); + emit_indent_zero(ctx); + emit(ctx, "#if defined(MSG_NO_MALLOC)\n"); + emit_unindent(ctx); + emit(ctx, + "char %s_buf[MSG_STRINGBUF_SIZE];\n", + param->p_name); + emit(ctx, + "%s.str_buf = %s_buf;\n", + param->p_name, + param->p_name); + emit(ctx, + "%s.str_max = MSG_STRINGBUF_SIZE;\n", + param->p_name); + + emit_indent_zero(ctx); + emit(ctx, "#endif\n"); + emit_unindent(ctx); + break; + default: + break; + } + + entry = b_queue_next(entry); + } + + emit(ctx, "status = %s_%s_recv(\n", iface->if_name, msg->msg_name); + emit_indent(ctx); + emit(ctx, "channel,\nid"); + entry = b_queue_first(&msg->msg_params); + while (entry) { + struct msg_parameter *param + = b_unbox(struct msg_parameter, entry, p_entry); + emit(ctx, ",\n&%s", param->p_name); + + entry = b_queue_next(entry); + } + + emit(ctx, ");\n"); + emit_unindent(ctx); + + emit(ctx, "if (status != KERN_OK) {\n"); + emit_indent(ctx); + emit(ctx, "msg_reply_generic(channel, id, msg, status);\n"); + emit(ctx, "return status;\n"); + emit_unindent(ctx); + emit(ctx, "}\n"); + + emit_indent_zero(ctx); + emit(ctx, "#if !defined(MSG_NO_MALLOC)\n"); + emit_unindent(ctx); + + entry = b_queue_first(&msg->msg_params); + while (entry) { + struct msg_parameter *param + = b_unbox(struct msg_parameter, entry, p_entry); + switch (param->p_type->ty_id) { + case TYPE_STRING: + emit(ctx, + "%s.str_max = %s.str_len + 1;\n", + param->p_name, + param->p_name); + emit(ctx, + "%s.str_buf = malloc(%s.str_max);\n", + param->p_name, + param->p_name); + + emit(ctx, "if (!%s.str_buf) {\n", param->p_name); + emit_indent(ctx); + emit_string_destructor_list(ctx, b_queue_prev(entry)); + emit(ctx, "return KERN_NO_MEMORY;\n"); + emit_unindent(ctx); + emit(ctx, "}\n", param->p_name); + break; + default: + break; + } + + entry = b_queue_next(entry); + } + + emit(ctx, "status = %s_%s_recv(\n", iface->if_name, msg->msg_name); + emit_indent(ctx); + emit(ctx, "channel,\nid"); + entry = b_queue_first(&msg->msg_params); + while (entry) { + struct msg_parameter *param + = b_unbox(struct msg_parameter, entry, p_entry); + emit(ctx, ",\n&%s", param->p_name); + + entry = b_queue_next(entry); + } + emit(ctx, ");\n"); + emit_unindent(ctx); + + emit(ctx, "if (status != KERN_OK) {\n"); + emit_indent(ctx); + emit_string_destructor_list(ctx, b_queue_last(&msg->msg_params)); + emit(ctx, "msg_reply_generic(channel, id, msg, status);\n"); + emit(ctx, "return status;\n"); + emit_unindent(ctx); + emit(ctx, "}\n"); + + emit_indent_zero(ctx); + emit(ctx, "#endif\n"); + emit_unindent(ctx); + + emit(ctx, "status = vtable->%s(\n", msg->msg_name); + emit_indent(ctx); + + size_t i = 0; + entry = b_queue_first(&msg->msg_params); + while (entry) { + struct msg_parameter *param + = b_unbox(struct msg_parameter, entry, p_entry); + if (i > 0) { + emit(ctx, ",\n"); + } + + switch (param->p_type->ty_id) { + case TYPE_INT: + emit(ctx, "%s", param->p_name); + break; + case TYPE_STRING: + emit(ctx, "%s.str_buf", param->p_name); + break; + default: + break; + } + + i++; + entry = b_queue_next(entry); + } + + entry = b_queue_first(&msg->msg_results); + while (entry) { + struct msg_parameter *param + = b_unbox(struct msg_parameter, entry, p_entry); + if (i > 0) { + emit(ctx, ",\n"); + } + + emit(ctx, "&%s", param->p_name); + + i++; + entry = b_queue_next(entry); + } + + emit(ctx, ");\n"); + emit_unindent(ctx); + + emit_indent_zero(ctx); + emit(ctx, "#if !defined(MSG_NO_MALLOC)\n"); + emit_unindent(ctx); + emit_string_destructor_list(ctx, b_queue_last(&msg->msg_params)); + emit_indent_zero(ctx); + emit(ctx, "#endif\n"); + emit_unindent(ctx); + + emit(ctx, "if (status != KERN_OK) {\n"); + emit_indent(ctx); + emit(ctx, "msg_reply_generic(channel, id, msg, status);\n"); + emit(ctx, "return status;\n"); + emit_unindent(ctx); + emit(ctx, "}\n\n"); + + emit(ctx, "status = %s_%s_reply(\n", iface->if_name, msg->msg_name); + emit_indent(ctx); + emit(ctx, "channel,\nid"); + entry = b_queue_first(&msg->msg_results); + while (entry) { + struct msg_parameter *param + = b_unbox(struct msg_parameter, entry, p_entry); + + switch (param->p_type->ty_id) { + case TYPE_INT: + emit(ctx, ",\n%s", param->p_name); + break; + case TYPE_STRING: + emit(ctx, ",\n%s.str_buf", param->p_name); + break; + default: + break; + } + + entry = b_queue_next(entry); + } + + emit(ctx, ");\n"); + emit_unindent(ctx); + + emit(ctx, "if (status != KERN_OK) return status;\n"); + + return 0; +} + +static int emit_interface_dispatcher_impl( + struct emit_ctx *ctx, + const struct interface_definition *iface) +{ + b_string *iface_ucase = b_string_create_from_cstr(iface->if_name); + b_string_toupper(iface_ucase); + b_string *msg_ucase = b_string_create(); + + emit(ctx, + "if (msg->hdr_protocol != PROTOCOL_%s) return " + "KERN_INVALID_ARGUMENT;\n\n", + b_string_ptr(iface_ucase)); + + emit(ctx, "kern_status_t status = KERN_OK;\n"); + emit(ctx, "switch (msg->hdr_func) {\n"); + + b_queue_entry *entry = b_queue_first(&iface->if_msg); + while (entry) { + const struct msg_definition *msg + = b_unbox(struct msg_definition, entry, msg_entry); + + b_string_clear(msg_ucase); + b_string_append_cstr(msg_ucase, msg->msg_name); + b_string_toupper(msg_ucase); + + emit(ctx, + "case MSG_%s_%s: {\n", + b_string_ptr(iface_ucase), + b_string_ptr(msg_ucase)); + emit_indent(ctx); + emit_interface_dispatcher_impl_msg(ctx, iface, msg); + emit(ctx, "break;\n"); + emit_unindent(ctx); + emit(ctx, "}\n"); + + entry = b_queue_next(entry); + } + + b_string_unref(iface_ucase); + + emit(ctx, "default:\n"); + emit_indent(ctx); + emit(ctx, "status = KERN_INVALID_ARGUMENT;\nbreak;\n"); + emit_unindent(ctx); + emit(ctx, "}\n\nreturn status;\n"); + + return 0; +} + +static int emit_interface_dispatcher( + struct emit_ctx *ctx, + const struct interface_definition *iface, + bool prototype_only) +{ + if (prototype_only) { + emit(ctx, "extern "); + } + + emit(ctx, "kern_status_t %s_dispatch(\n", iface->if_name); + emit_indent(ctx); + emit(ctx, "kern_handle_t channel,\n"); + emit(ctx, "const struct %s_vtable *vtable,\n", iface->if_name); + emit(ctx, "msgid_t id,\n"); + emit(ctx, "const struct msg_header *msg)", iface->if_name); + emit_unindent(ctx); + + if (prototype_only) { + emit(ctx, ";\n"); + return 0; + } + + emit(ctx, "\n{\n"); + emit_indent(ctx); + + emit_interface_dispatcher_impl(ctx, iface); + + emit_unindent(ctx); + emit(ctx, "}\n\n"); + + return 0; +} + +static int emit_interface_functions( + struct emit_ctx *ctx, + const struct interface_definition *iface, + bool prototype_only) +{ + b_queue_entry *entry = b_queue_first(&iface->if_msg); + while (entry) { + const struct msg_definition *msg + = b_unbox(struct msg_definition, entry, msg_entry); + + emit_interface_msg_functions(ctx, iface, msg, prototype_only); + + entry = b_queue_next(entry); + } + + emit_interface_dispatcher(ctx, iface, prototype_only); + + return 0; +} + +static int emit_interface_vtable_entry( + struct emit_ctx *ctx, + const struct interface_definition *iface, + const struct msg_definition *msg) +{ + emit(ctx, "kern_status_t(*%s)(\n", msg->msg_name); + emit_indent(ctx); + + size_t i = 0; + b_queue_entry *entry = b_queue_first(&msg->msg_params); + while (entry) { + struct msg_parameter *param + = b_unbox(struct msg_parameter, entry, p_entry); + if (i > 0) { + emit(ctx, ",\n"); + } + + switch (param->p_type->ty_id) { + case TYPE_INT: + emit(ctx, "int %s", param->p_name); + break; + case TYPE_STRING: + emit(ctx, "const char *%s", param->p_name); + break; + default: + break; + } + + i++; + entry = b_queue_next(entry); + } + + entry = b_queue_first(&msg->msg_results); + while (entry) { + struct msg_parameter *param + = b_unbox(struct msg_parameter, entry, p_entry); + if (i > 0) { + emit(ctx, ",\n"); + } + + switch (param->p_type->ty_id) { + case TYPE_INT: + emit(ctx, "int *out_%s", param->p_name); + break; + case TYPE_STRING: + emit(ctx, "struct msg_string *out_%s", param->p_name); + break; + default: + break; + } + + i++; + entry = b_queue_next(entry); + } + + emit(ctx, ");\n", msg->msg_name); + emit_unindent(ctx); + + return 0; +} + +static int emit_interface_vtable( + struct emit_ctx *ctx, + const struct interface_definition *iface) +{ + emit(ctx, "struct %s_vtable {\n", iface->if_name); + emit_indent(ctx); + + b_queue_entry *entry = b_queue_first(&iface->if_msg); + while (entry) { + const struct msg_definition *msg + = b_unbox(struct msg_definition, entry, msg_entry); + + emit_interface_vtable_entry(ctx, iface, msg); + + entry = b_queue_next(entry); + } + + emit_unindent(ctx); + emit(ctx, "};\n\n"); + return 0; +} + +static int emit_header( + struct emit_ctx *ctx, + const struct interface_definition *iface) +{ + emit_header_guard_start(ctx, iface); + emit_include(ctx, "stdint.h", true); + emit_include(ctx, "stddef.h", true); + emit_include(ctx, "string.h", true); + emit(ctx, "#if !defined(MSG_NO_MALLOC)\n"); + emit_include(ctx, "stdlib.h", true); + emit(ctx, "#endif\n"); + emit_include(ctx, "mango/msg.h", true); + emit(ctx, "\n"); + + emit_header_lib_definitions(ctx); + emit_interface_id_macros(ctx, iface); + emit_interface_vtable(ctx, iface); + emit_interface_msg_structs(ctx, iface); + emit_interface_functions(ctx, iface, true); + + emit(ctx, "\n#if defined(MSG_IMPLEMENTATION)\n"); + + emit_interface_functions(ctx, iface, false); + + emit(ctx, "#endif\n"); + + emit_header_guard_end(ctx, iface); + return 0; +} + +static int emit_interface(const struct interface_definition *iface) +{ + char path[4096]; + b_file *file = NULL; + + snprintf(path, sizeof path, "%s.h", iface->if_name); + b_result result = b_file_open( + NULL, + B_RV_PATH(path), + B_FILE_WRITE_ONLY | B_FILE_CREATE, + &file); + if (b_result_is_error(result)) { + b_throw(result); + fprintf(stderr, "cannot create C header file %s\n", path); + return -1; + } + + struct emit_ctx ctx = { + .ctx_out = file, + }; + + int err = emit_header(&ctx, iface); + b_file_unref(file); + + return err; +} + +static struct backend backend = { + .b_name = "c-mpc", + .b_emit = emit_interface, +}; + +const struct backend *c_mpc_backend(void) +{ + return &backend; +} diff --git a/toolchain/ifc/ctx.c b/toolchain/ifc/ctx.c new file mode 100644 index 0000000..3ece174 --- /dev/null +++ b/toolchain/ifc/ctx.c @@ -0,0 +1,52 @@ +#include "ctx.h" + +#include +#include + +static void init_builtin_types(struct ctx *ctx) +{ + for (size_t i = 0; i < TYPE_OTHER; i++) { + ctx->ctx_builtin_types[i].ty_id = i; + } +} + +struct ctx *ctx_create(void) +{ + struct ctx *out = malloc(sizeof *out); + if (!out) { + return NULL; + } + + memset(out, 0x0, sizeof *out); + + init_builtin_types(out); + + return out; +} + +void ctx_destroy(struct ctx *ctx) +{ + free(ctx); +} + +const struct type *ctx_get_type(struct ctx *ctx, const char *name) +{ + if (!strcmp(name, "string")) { + return ctx_get_builtin_type(ctx, TYPE_STRING); + } + + if (!strcmp(name, "int")) { + return ctx_get_builtin_type(ctx, TYPE_INT); + } + + return NULL; +} + +const struct type *ctx_get_builtin_type(struct ctx *ctx, enum type_id id) +{ + if (id >= TYPE_OTHER) { + return NULL; + } + + return &ctx->ctx_builtin_types[id]; +} diff --git a/toolchain/ifc/ctx.h b/toolchain/ifc/ctx.h new file mode 100644 index 0000000..2eee056 --- /dev/null +++ b/toolchain/ifc/ctx.h @@ -0,0 +1,18 @@ +#ifndef IFC_CTX_H_ +#define IFC_CTX_H_ + +#include "type.h" + +struct ctx { + struct type ctx_builtin_types[TYPE_OTHER]; +}; + +extern struct ctx *ctx_create(void); +extern void ctx_destroy(struct ctx *ctx); + +extern const struct type *ctx_get_type(struct ctx *ctx, const char *name); +extern const struct type *ctx_get_builtin_type( + struct ctx *ctx, + enum type_id id); + +#endif diff --git a/toolchain/ifc/file-span.h b/toolchain/ifc/file-span.h new file mode 100644 index 0000000..95d060d --- /dev/null +++ b/toolchain/ifc/file-span.h @@ -0,0 +1,12 @@ +#ifndef IFC_FILE_SPAN_H_ +#define IFC_FILE_SPAN_H_ + +struct file_cell { + unsigned int c_row, c_col; +}; + +struct file_span { + struct file_cell s_start, s_end; +}; + +#endif diff --git a/toolchain/ifc/interface.c b/toolchain/ifc/interface.c new file mode 100644 index 0000000..c715e05 --- /dev/null +++ b/toolchain/ifc/interface.c @@ -0,0 +1,60 @@ +#include "interface.h" + +#include "msg.h" + +#include +#include +#include + +struct interface_definition *interface_definition_create(const char *name) +{ + struct interface_definition *out = malloc(sizeof *out); + if (!out) { + return NULL; + } + + memset(out, 0x0, sizeof *out); + + out->if_name = b_strdup(name); + + return out; +} + +void interface_definition_destroy(struct interface_definition *iface) +{ + b_queue_entry *entry = b_queue_pop_front(&iface->if_msg); + while (entry) { + struct msg_definition *msg + = b_unbox(struct msg_definition, entry, msg_entry); + msg_definition_destroy(msg); + entry = b_queue_pop_front(&iface->if_msg); + } + + free(iface->if_name); + free(iface); +} + +bool interface_definition_has_msg( + const struct interface_definition *iface, + const char *name) +{ + b_queue_entry *entry = b_queue_first(&iface->if_msg); + + while (entry) { + struct msg_definition *msg + = b_unbox(struct msg_definition, entry, msg_entry); + if (!strcmp(msg->msg_name, name)) { + return true; + } + entry = b_queue_next(entry); + } + + return false; +} + +void interface_definition_add_msg( + struct interface_definition *iface, + struct msg_definition *msg) +{ + b_queue_push_back(&iface->if_msg, &msg->msg_entry); +} diff --git a/toolchain/ifc/interface.h b/toolchain/ifc/interface.h new file mode 100644 index 0000000..7ebc8df --- /dev/null +++ b/toolchain/ifc/interface.h @@ -0,0 +1,24 @@ +#ifndef IFC_INTERFACE_H_ +#define IFC_INTERFACE_H_ + +#include + +struct msg_definition; + +struct interface_definition { + char *if_name; + b_queue if_msg; +}; + +extern struct interface_definition *interface_definition_create( + const char *name); +extern void interface_definition_destroy(struct interface_definition *iface); + +extern bool interface_definition_has_msg( + const struct interface_definition *iface, + const char *name); +extern void interface_definition_add_msg( + struct interface_definition *iface, + struct msg_definition *msg); + +#endif diff --git a/toolchain/ifc/lex.c b/toolchain/ifc/lex.c new file mode 100644 index 0000000..6e3f0ec --- /dev/null +++ b/toolchain/ifc/lex.c @@ -0,0 +1,740 @@ +#include "lex.h" + +#include "line-source.h" +#include "token.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LINEBUF_DEFAULT_CAPACITY 1024 + +#define LEX_TOKEN_DEF(i, n) {.id = (i), .name = (n)} + +#define IS_VALID_IDENT_CHAR(c) \ + (b_wchar_is_alnum(c) || c == '.' || c == '-' || c == '_') +#define IS_VALID_IDENT_START_CHAR(c) \ + (b_wchar_is_alpha(c) || c == '.' || c == '_') +#define IS_VALID_REG_START_CHAR(c) (b_wchar_is_alnum(c) || c == '.' || c == '_') + +static struct lex_token_def symbols[] = { + LEX_TOKEN_DEF(SYM_COMMA, ","), + LEX_TOKEN_DEF(SYM_HYPHEN, "-"), + LEX_TOKEN_DEF(SYM_LEFT_BRACE, "{"), + LEX_TOKEN_DEF(SYM_RIGHT_BRACE, "}"), + LEX_TOKEN_DEF(SYM_LEFT_PAREN, "("), + LEX_TOKEN_DEF(SYM_RIGHT_PAREN, ")"), + LEX_TOKEN_DEF(SYM_SEMICOLON, ";"), + LEX_TOKEN_DEF(SYM_HYPHEN_RIGHT_ANGLE, "->"), +}; +static const size_t nr_symbols = sizeof symbols / sizeof symbols[0]; + +static struct lex_token_def keywords[] = { + LEX_TOKEN_DEF(KW_INTERFACE, "interface"), + LEX_TOKEN_DEF(KW_MSG, "msg"), +}; +static const size_t nr_keywords = sizeof keywords / sizeof keywords[0]; + +static struct lex_symbol_node *get_symbol_node( + struct lex_symbol_node *node, + char c) +{ + b_queue_entry *entry = b_queue_first(&node->s_children); + while (entry) { + struct lex_symbol_node *child + = b_unbox(struct lex_symbol_node, entry, s_entry); + if (child->s_char == c) { + return child; + } + + entry = b_queue_next(entry); + } + + return NULL; +} + +static b_string *get_temp_string(struct lex *lex) +{ + if (!lex->lex_temp) { + lex->lex_temp = b_string_create(); + } + + b_string_clear(lex->lex_temp); + return lex->lex_temp; +} + +static enum status put_symbol( + struct lex_symbol_node *tree, + struct lex_token_def *sym) +{ + for (size_t i = 0; sym->name[i]; i++) { + char c = sym->name[i]; + struct lex_symbol_node *child = get_symbol_node(tree, c); + if (child) { + tree = child; + continue; + } + + child = malloc(sizeof *child); + if (!child) { + return ERR_NO_MEMORY; + } + + memset(child, 0x0, sizeof *child); + + child->s_def = NULL; + child->s_char = c; + + b_queue_push_back(&tree->s_children, &child->s_entry); + tree = child; + } + + tree->s_def = sym; + return SUCCESS; +} + +static void destroy_symbol_tree(struct lex_symbol_node *tree) +{ + b_queue_entry *entry = b_queue_first(&tree->s_children); + while (entry) { + struct lex_symbol_node *node + = b_unbox(struct lex_symbol_node, entry, s_entry); + b_queue_entry *next = b_queue_next(entry); + b_queue_delete(&tree->s_children, entry); + + destroy_symbol_tree(node); + + entry = next; + } + + free(tree); +} + +static struct lex_symbol_node *build_symbol_tree(void) +{ + struct lex_symbol_node *root = malloc(sizeof *root); + if (!root) { + return NULL; + } + + memset(root, 0x0, sizeof *root); + root->s_def = NULL; + + enum status status = SUCCESS; + for (size_t i = 0; i < nr_symbols; i++) { + status = put_symbol(root, &symbols[i]); + + if (status != SUCCESS) { + destroy_symbol_tree(root); + return NULL; + } + } + + return root; +} + +struct lex *lex_create(struct line_source *src) +{ + struct lex *lex = malloc(sizeof *lex); + if (!lex) { + return NULL; + } + + memset(lex, 0x0, sizeof *lex); + + lex->lex_status = SUCCESS; + lex->lex_source = src; + + lex->lex_sym_tree = build_symbol_tree(); + if (!lex->lex_sym_tree) { + lex_destroy(lex); + return NULL; + } + + return lex; +} + +void lex_destroy(struct lex *lex) +{ + b_queue_entry *entry = b_queue_first(&lex->lex_queue); + + while (entry) { + struct token *tok = b_unbox(struct token, entry, tok_entry); + b_queue_entry *next = b_queue_next(entry); + b_queue_delete(&lex->lex_queue, entry); + + token_destroy(tok); + + entry = next; + } + + if (lex->lex_sym_tree) { + destroy_symbol_tree(lex->lex_sym_tree); + } + + if (lex->lex_temp) { + b_string_unref(lex->lex_temp); + } + + free(lex); +} + +enum status lex_get_status(const struct lex *lex) +{ + return lex->lex_status; +} + +struct line_source *lex_get_line_source(const struct lex *lex) +{ + return lex->lex_source; +} + +const struct file_cell *lex_get_cursor(const struct lex *lex) +{ + return &lex->lex_source->s_cursor; +} + +static bool char_can_begin_symbol(char c) +{ + for (size_t i = 0; i < nr_symbols; i++) { + if (symbols[i].name[0] == c) { + return true; + } + } + + return false; +} + +static struct token *create_token(enum token_type type) +{ + struct token *tok = malloc(sizeof *tok); + if (!tok) { + return NULL; + } + + memset(tok, 0x0, sizeof *tok); + + tok->tok_type = type; + return tok; +} + +static void set_token_start(struct lex *lex) +{ + lex->lex_token_start = *line_source_get_cursor(lex->lex_source); +} + +static void set_token_end(struct lex *lex) +{ + lex->lex_token_end = *line_source_get_cursor(lex->lex_source); +} + +static enum status push_token(struct lex *lex, struct token *tok) +{ + tok->tok_location.s_start = lex->lex_token_start; + tok->tok_location.s_end = lex->lex_token_end; + + b_queue_push_back(&lex->lex_queue, &tok->tok_entry); + return SUCCESS; +} + +static enum status push_symbol(struct lex *lex, enum token_symbol sym) +{ + struct token *tok = malloc(sizeof *tok); + if (!tok) { + return ERR_NO_MEMORY; + } + + memset(tok, 0x0, sizeof *tok); + + tok->tok_type = TOK_SYMBOL; + tok->tok_value_type = TOK_V_SYMBOL; + tok->tok_sym = sym; + return push_token(lex, tok); +} + +static enum status push_keyword(struct lex *lex, enum token_keyword kw) +{ + struct token *tok = malloc(sizeof *tok); + if (!tok) { + return ERR_NO_MEMORY; + } + + memset(tok, 0x0, sizeof *tok); + + tok->tok_type = TOK_KEYWORD; + tok->tok_value_type = TOK_V_KEYWORD; + tok->tok_kw = kw; + return push_token(lex, tok); +} + +static enum status push_string_token( + struct lex *lex, + enum token_type type, + char *s) +{ + struct token *tok = malloc(sizeof *tok); + if (!tok) { + return ERR_NO_MEMORY; + } + + char *ep = NULL; + long long v = strtoll(s, &ep, 10); + + memset(tok, 0x0, sizeof *tok); + + tok->tok_type = type; + + if (*ep == '\0') { + tok->tok_int = v; + tok->tok_value_type = TOK_V_INT; + free(s); + } else { + tok->tok_str = s; + tok->tok_value_type = TOK_V_STRING; + } + + return push_token(lex, tok); +} + +static enum status push_int(struct lex *lex, unsigned long long v) +{ + struct token *tok = malloc(sizeof *tok); + if (!tok) { + return ERR_NO_MEMORY; + } + + memset(tok, 0x0, sizeof *tok); + + tok->tok_type = TOK_INT; + tok->tok_value_type = TOK_V_INT; + tok->tok_int = v; + return push_token(lex, tok); +} + +static enum status read_line_comment(struct lex *lex) +{ + while (true) { + b_wchar c = line_source_getc(lex->lex_source); + + if (c == -ERR_EOF || c == '\n') { + break; + } + + if (c < 0) { + return -c; + } + } + + return SUCCESS; +} + +static enum status read_number(struct lex *lex, bool negate) +{ + int token_len = 0; + int base = 10; + int dots = 0; + b_string *str = get_temp_string(lex); + + if (!negate) { + set_token_start(lex); + } + + while (true) { + b_wchar c = line_source_peekc(lex->lex_source); + if (c == -ERR_EOF) { + break; + } + + if (c < 0) { + return -c; + } + + if (c == '_') { + token_len++; + set_token_end(lex); + line_source_getc(lex->lex_source); + continue; + } + + if (c == '.') { + return ERR_BAD_SYNTAX; + } + + if (b_wchar_is_space(c) || b_wchar_is_punct(c)) { + break; + } + + if (c == 'x' && token_len == 1) { + base = 16; + token_len++; + set_token_end(lex); + line_source_getc(lex->lex_source); + continue; + } + + if (c == 'b' && token_len == 1) { + base = 2; + token_len++; + set_token_end(lex); + line_source_getc(lex->lex_source); + continue; + } + + if (base == 2 && c != '0' && c != '1') { + return ERR_BAD_SYNTAX; + } + + if (base == 10 && !isdigit(c)) { + return ERR_BAD_SYNTAX; + } + + if (base == 16 && !isxdigit(c)) { + return ERR_BAD_SYNTAX; + } + + b_string_append_wc(str, c); + set_token_end(lex); + line_source_getc(lex->lex_source); + token_len++; + } + + if (token_len == 1 && base == 7) { + return push_int(lex, 0); + } + + const char *s = b_string_ptr(str); + char *ep = NULL; + + /* negative numbers will be lexed as a hyphen followed by a positive + * number. */ + + unsigned long long v = strtoull(s, &ep, base); + + if (*ep != '\0') { + return ERR_BAD_SYNTAX; + } + + if (negate) { + v *= -1; + } + + return push_int(lex, v); +} + +static enum token_keyword find_keyword(const char *s) +{ + for (size_t i = 0; i < nr_keywords; i++) { + if (!strcmp(keywords[i].name, s)) { + return keywords[i].id; + } + } + + return KW_NONE; +} + +static enum status read_ident(struct lex *lex, enum token_type type) +{ + int dots = 0; + b_string *str = get_temp_string(lex); + b_wchar prev = 0; + + if (type == TOK_NONE) { + set_token_start(lex); + } + + while (1) { + b_wchar c = line_source_peekc(lex->lex_source); + + if ((c == '.' || c == '-') && prev == c) { + return ERR_BAD_SYNTAX; + } + + if (c == '.') { + dots++; + } + + if (!IS_VALID_IDENT_CHAR(c)) { + break; + } + + prev = c; + b_string_append_wc(str, c); + set_token_end(lex); + line_source_getc(lex->lex_source); + } + + if (type == TOK_NONE) { + type = dots > 0 ? TOK_NAME : TOK_WORD; + } + + char *s = b_string_steal(str); + enum token_keyword kw = find_keyword(s); + if (kw != KW_NONE) { + free(s); + return push_keyword(lex, kw); + } + + return push_string_token(lex, type, s); +} + +static enum status read_string(struct lex *lex) +{ + b_string *str = get_temp_string(lex); + + b_wchar c = line_source_peekc(lex->lex_source); + bool esc = false; + + if (c != '"') { + return ERR_BAD_SYNTAX; + } + + line_source_getc(lex->lex_source); + + while (1) { + b_wchar c = line_source_peekc(lex->lex_source); + + if (esc) { + switch (c) { + case '\\': + case '"': + b_string_append_wc(str, c); + break; + default: + return ERR_BAD_SYNTAX; + } + + esc = false; + line_source_getc(lex->lex_source); + continue; + } + + if (c == '\\') { + esc = true; + line_source_getc(lex->lex_source); + continue; + } + + if (c == '"') { + line_source_getc(lex->lex_source); + break; + } + + b_string_append_wc(str, c); + line_source_getc(lex->lex_source); + } + + char *s = b_string_steal(str); + return push_string_token(lex, TOK_STRING, s); +} + +static enum status read_symbol(struct lex *lex) +{ + struct lex_symbol_node *node = lex->lex_sym_tree; + set_token_start(lex); + b_wchar prev = 0; + + while (true) { + b_wchar c = line_source_peekc(lex->lex_source); + if (c < 0) { + break; + } + + struct lex_symbol_node *next = get_symbol_node(node, c); + if (!next) { + prev = c; + break; + } + + node = next; + set_token_end(lex); + line_source_getc(lex->lex_source); + prev = c; + } + + if (!node || node->s_def == NULL) { + return ERR_BAD_SYNTAX; + } + + if (node->s_def->id == SYM_HYPHEN && isdigit(prev)) { + return read_number(lex, true); + } + + return push_symbol(lex, node->s_def->id); +} + +static void skip_whitespace(struct lex *lex) +{ + b_wchar c = line_source_peekc(lex->lex_source); + + while (b_wchar_is_space(c)) { + line_source_getc(lex->lex_source); + c = line_source_peekc(lex->lex_source); + } +} + +static bool should_skip(b_wchar c, bool skip_linefeeds) +{ + bool skip = b_wchar_is_space(c); + + if (!skip_linefeeds) { + skip = (skip && c != '\n'); + } + + return skip; +} + +static void skip_ignored_chars(struct lex *lex, bool include_linefeeds) +{ + b_wchar c = line_source_peekc(lex->lex_source); + + while (1) { + while (should_skip(c, include_linefeeds)) { + line_source_getc(lex->lex_source); + c = line_source_peekc(lex->lex_source); + } + + if (c != '#') { + break; + } + + line_source_getc(lex->lex_source); + c = line_source_peekc(lex->lex_source); + + while (c != '\n') { + line_source_getc(lex->lex_source); + c = line_source_peekc(lex->lex_source); + } + + line_source_getc(lex->lex_source); + c = line_source_peekc(lex->lex_source); + } +} + +static enum status pump_tokens(struct lex *lex) +{ + b_wchar c = line_source_peekc(lex->lex_source); + + if (c < 0) { + return -c; + } + + while (1) { + if (c == '#' || (b_wchar_is_space(c) && c != '\n')) { + skip_ignored_chars(lex, false); + } else { + break; + } + + c = line_source_peekc(lex->lex_source); + } + + if (c == '\\') { + line_source_getc(lex->lex_source); + skip_ignored_chars(lex, true); + c = line_source_peekc(lex->lex_source); + } + + if (c == '\n') { + set_token_start(lex); + set_token_end(lex); + + while (c == '\n') { + line_source_getc(lex->lex_source); + + if (!line_source_input_available(lex->lex_source)) { + break; + } + + c = line_source_peekc(lex->lex_source); + } + + if (c < 0) { + return -c; + } + + return SUCCESS; + } + + while (b_wchar_is_space(c) && c != '\n') { + line_source_getc(lex->lex_source); + c = line_source_peekc(lex->lex_source); + } + + if (IS_VALID_IDENT_START_CHAR(c)) { + return read_ident(lex, TOK_NONE); + } + + if (char_can_begin_symbol(c)) { + return read_symbol(lex); + } + + if (c == '"') { + return read_string(lex); + } + + if (isdigit(c)) { + return read_number(lex, false); + } + + return ERR_BAD_SYNTAX; +} + +struct token *lex_peek(struct lex *lex) +{ + enum status status = SUCCESS; + + while (b_queue_empty(&lex->lex_queue)) { + status = pump_tokens(lex); + + if (status != SUCCESS) { + lex->lex_status = status; + return NULL; + } + } + + lex->lex_status = status; + + b_queue_entry *entry = b_queue_first(&lex->lex_queue); + struct token *tok = b_unbox(struct token, entry, tok_entry); + return tok; +} + +void lex_advance(struct lex *lex) +{ + enum status status = SUCCESS; + + while (b_queue_empty(&lex->lex_queue)) { + status = pump_tokens(lex); + + if (status != SUCCESS) { + lex->lex_status = status; + return; + } + } + + b_queue_entry *entry = b_queue_pop_front(&lex->lex_queue); + struct token *tok = b_unbox(struct token, entry, tok_entry); + token_destroy(tok); +} + +bool lex_tokens_available(struct lex *lex) +{ + if (!b_queue_empty(&lex->lex_queue)) { + return true; + } + + if (line_source_input_available(lex->lex_source)) { + return true; + } + + return false; +} diff --git a/toolchain/ifc/lex.h b/toolchain/ifc/lex.h new file mode 100644 index 0000000..47e325d --- /dev/null +++ b/toolchain/ifc/lex.h @@ -0,0 +1,52 @@ +#ifndef IFC_LEX_H_ +#define IFC_LEX_H_ + +#include "status.h" +#include "token.h" + +#include +#include +#include +#include + +struct lex { + struct lex_symbol_node *lex_sym_tree; + struct line_source *lex_source; + enum status lex_status; + + b_queue lex_queue; + + b_string *lex_temp; + b_queue lex_state; + unsigned int lex_brace_depth; + + struct file_cell lex_token_start, lex_token_end; +}; + +struct lex_symbol_node { + char s_char; + struct lex_token_def *s_def; + + b_queue_entry s_entry; + b_queue s_children; +}; + +struct lex_token_def { + int id; + const char *name; + uint64_t name_hash; +}; + +struct token; +struct line_source; + +extern struct lex *lex_create(struct line_source *src); +extern void lex_destroy(struct lex *lex); + +extern enum status lex_get_status(const struct lex *lex); +extern struct line_source *lex_get_line_source(const struct lex *lex); +extern const struct file_cell *lex_get_cursor(const struct lex *lex); +extern struct token *lex_peek(struct lex *lex); +extern void lex_advance(struct lex *lex); + +#endif diff --git a/toolchain/ifc/line-source.c b/toolchain/ifc/line-source.c new file mode 100644 index 0000000..865bc39 --- /dev/null +++ b/toolchain/ifc/line-source.c @@ -0,0 +1,164 @@ +#include "line-source.h" + +enum status line_source_init( + struct line_source *src, + const char *path, + b_stream *stream) +{ + memset(src, 0x0, sizeof *src); + + src->s_lines = b_array_create(); + + if (!src->s_lines) { + return ERR_NO_MEMORY; + } + + src->s_stream = stream; + src->s_path = path; + src->s_cursor.c_col = 1; + src->s_cursor.c_row = 1; + + return SUCCESS; +} + +void line_source_cleanup(struct line_source *src) +{ + if (src->s_linebuf_ptr) { + b_iterator_unref(src->s_linebuf_ptr); + } + + if (src->s_lines) { + b_array_unref(src->s_lines); + } + + memset(src, 0x0, sizeof *src); +} + +const char *line_source_get_path(const struct line_source *src) +{ + return src->s_path; +} + +const struct file_cell *line_source_get_cursor(const struct line_source *src) +{ + return &src->s_cursor; +} + +static enum status refill_linebuf(struct line_source *src) +{ + if (!src->s_stream) { + return ERR_EOF; + } + + if (src->s_linebuf_ptr) { + b_iterator_unref(src->s_linebuf_ptr); + src->s_linebuf_ptr = NULL; + } + + b_stringstream *s = b_stringstream_create(); + + b_status status = b_stream_read_line_s(src->s_stream, s); + + if (status == B_ERR_NO_DATA) { + return ERR_EOF; + } + + if (!B_OK(status)) { + return ERR_INTERNAL_FAILURE; + } + + b_string *line = b_string_create(); + b_string_replace_all_with_stringstream(line, s); + b_stringstream_unref(s); + + b_array_append(src->s_lines, line); + b_string_unref(line); + + src->s_linebuf = line; + src->s_linebuf_ptr = b_iterator_begin(src->s_linebuf); + + return SUCCESS; +} + +static int peek(struct line_source *src) +{ + enum status status = SUCCESS; + + if (!src->s_linebuf_ptr || !b_iterator_is_valid(src->s_linebuf_ptr)) { + status = refill_linebuf(src); + } + + if (status != SUCCESS) { + return -status; + } + + if (b_string_get_size(src->s_linebuf, B_STRLEN_NORMAL) == 0) { + return -ERR_EOF; + } + + b_wchar c = b_iterator_get_value(src->s_linebuf_ptr).v_int; + return c; +} + +static int advance(struct line_source *src) +{ + enum status status = SUCCESS; + + if (!b_iterator_is_valid(src->s_linebuf_ptr)) { + status = refill_linebuf(src); + } + + if (status != SUCCESS) { + return -status; + } + + if (b_string_get_size(src->s_linebuf, B_STRLEN_NORMAL) == 0) { + return -ERR_EOF; + } + + b_wchar c = b_iterator_get_value(src->s_linebuf_ptr).v_int; + b_iterator_move_next(src->s_linebuf_ptr); + + src->s_cursor.c_col++; + if (c == '\n') { + src->s_cursor.c_col = 1; + src->s_cursor.c_row++; + } + return c; +} + +b_wchar line_source_peekc(struct line_source *src) +{ + return peek(src); +} + +b_wchar line_source_getc(struct line_source *src) +{ + return advance(src); +} + +enum status line_source_get_row( + struct line_source *src, + size_t row, + const b_string **out) +{ + if (row == 0) { + return ERR_INVALID_ARGUMENT; + } + + row--; + + if (row >= b_array_size(src->s_lines)) { + return ERR_EOF; + } + + b_string *line = b_array_at(src->s_lines, row); + *out = line; + + return SUCCESS; +} + +bool line_source_input_available(struct line_source *src) +{ + return src->s_linebuf_ptr && b_iterator_is_valid(src->s_linebuf_ptr); +} diff --git a/toolchain/ifc/line-source.h b/toolchain/ifc/line-source.h new file mode 100644 index 0000000..d0f9fc7 --- /dev/null +++ b/toolchain/ifc/line-source.h @@ -0,0 +1,39 @@ +#ifndef LINE_SOURCE_H_ +#define LINE_SOURCE_H_ + +#include "file-span.h" +#include "status.h" + +#include +#include +#include + +struct line_source { + b_stream *s_stream; + const char *s_path; + b_string *s_linebuf; + b_iterator *s_linebuf_ptr; + b_array *s_lines; + + struct file_cell s_cursor; +}; + +extern enum status line_source_init( + struct line_source *src, + const char *path, + b_stream *stream); +extern void line_source_cleanup(struct line_source *src); + +extern const char *line_source_get_path(const struct line_source *src); +extern const struct file_cell *line_source_get_cursor( + const struct line_source *src); +extern b_wchar line_source_peekc(struct line_source *src); +extern b_wchar line_source_getc(struct line_source *src); +extern enum status line_source_get_row( + struct line_source *src, + size_t row, + const b_string **out); + +extern bool line_source_input_available(struct line_source *src); + +#endif diff --git a/toolchain/ifc/main.c b/toolchain/ifc/main.c new file mode 100644 index 0000000..60c5bb9 --- /dev/null +++ b/toolchain/ifc/main.c @@ -0,0 +1,166 @@ +#include "backend.h" +#include "ctx.h" +#include "interface.h" +#include "lex.h" +#include "line-source.h" +#include "msg.h" +#include "parse.h" + +#include +#include +#include + +#define CMD_ID 0 + +enum { + ARG_SRCPATH, + OPT_BACKEND, + OPT_BACKEND_ARG_NAME, +}; + +int main(int argc, const char **argv) +{ + return b_command_dispatch(CMD_ID, argc, argv); +} + +static void print_msg(struct msg_definition *msg) +{ + printf(" msg: %s\n", msg->msg_name); + + b_queue_entry *entry = b_queue_first(&msg->msg_params); + while (entry) { + struct msg_parameter *param + = b_unbox(struct msg_parameter, entry, p_entry); + + printf(" param:"); + type_print(param->p_type); + printf(" %s\n", param->p_name); + + entry = b_queue_next(entry); + } + + entry = b_queue_first(&msg->msg_results); + while (entry) { + struct msg_parameter *param + = b_unbox(struct msg_parameter, entry, p_entry); + + printf(" result:"); + type_print(param->p_type); + printf(" %s\n", param->p_name); + + entry = b_queue_next(entry); + } +} + +static void print_interface(struct interface_definition *iface) +{ + printf("interface: %s\n", iface->if_name); + b_queue_entry *entry = b_queue_first(&iface->if_msg); + while (entry) { + struct msg_definition *msg + = b_unbox(struct msg_definition, entry, msg_entry); + print_msg(msg); + entry = b_queue_next(entry); + } +} + +static int ifc(const b_command *self, const b_arglist *opt, const b_array *args) +{ + const char *path = NULL; + b_arglist_get_string(opt, B_COMMAND_INVALID_ID, ARG_SRCPATH, 0, &path); + if (!path) { + b_arglist_report_missing_args( + opt, + B_COMMAND_INVALID_ID, + ARG_SRCPATH, + 0); + return -1; + } + + b_file *file = NULL; + b_result result + = b_file_open(NULL, B_RV_PATH(path), B_FILE_READ_ONLY, &file); + if (b_result_is_error(result)) { + b_throw(result); + return -1; + } + + struct line_source src; + line_source_init(&src, path, file); + + struct ctx *ctx = ctx_create(); + struct lex *lex = lex_create(&src); +#if 0 + struct token *tok = lex_peek(lex); + while (tok) { + printf("%s", token_type_to_string(tok->tok_type)); + + switch (tok->tok_value_type) { + case TOK_V_INT: + printf(" %lld", tok->tok_int); + break; + case TOK_V_STRING: + printf(" %s", tok->tok_str); + break; + case TOK_V_SYMBOL: + printf(" %s", token_symbol_to_string(tok->tok_sym)); + break; + case TOK_V_KEYWORD: + printf(" %s", token_keyword_to_string(tok->tok_kw)); + break; + default: + break; + } + + printf("\n"); + lex_advance(lex); + tok = lex_peek(lex); + } +#endif + + struct interface_definition *iface + = parse_interface_definition(ctx, lex); + if (!iface) { + return -1; + } + + const struct backend *backend = c_mpc_backend(); + int err = backend->b_emit(iface); + + return err; +} + +B_COMMAND(CMD_ID, B_COMMAND_INVALID_ID) +{ + B_COMMAND_NAME("ifc"); + B_COMMAND_DESC("interface definition compiler."); + B_COMMAND_FLAGS(B_COMMAND_SHOW_HELP_BY_DEFAULT); + B_COMMAND_FUNCTION(ifc); + B_COMMAND_HELP_OPTION(); + + B_COMMAND_ARG(ARG_SRCPATH) + { + B_ARG_NAME("source-file"); + B_ARG_DESC("the interface file to compile."); + B_ARG_NR_VALUES(1); + } + + B_COMMAND_OPTION(OPT_BACKEND) + { + B_OPTION_LONG_NAME("backend"); + B_OPTION_SHORT_NAME('b'); + B_OPTION_DESC("which backend to use."); + B_OPTION_ARG(OPT_BACKEND_ARG_NAME) + { + B_ARG_NAME("backend-name"); + B_ARG_NR_VALUES(1); + B_ARG_ALLOWED_VALUES("c-mpc"); + } + } + + B_COMMAND_USAGE() + { + B_COMMAND_USAGE_ARG(ARG_SRCPATH); + B_COMMAND_USAGE_OPT(OPT_BACKEND); + } +} diff --git a/toolchain/ifc/msg.c b/toolchain/ifc/msg.c new file mode 100644 index 0000000..822d607 --- /dev/null +++ b/toolchain/ifc/msg.c @@ -0,0 +1,113 @@ +#include "msg.h" + +#include +#include +#include + +struct msg_definition *msg_definition_create(const char *name) +{ + struct msg_definition *out = malloc(sizeof *out); + if (!out) { + return NULL; + } + + memset(out, 0x0, sizeof *out); + + out->msg_name = b_strdup(name); + + return out; +} + +void msg_definition_destroy(struct msg_definition *msg) +{ + b_queue_entry *entry = b_queue_pop_front(&msg->msg_params); + while (entry) { + struct msg_parameter *param + = b_unbox(struct msg_parameter, entry, p_entry); + free(param->p_name); + free(param); + entry = b_queue_pop_front(&msg->msg_params); + } + + entry = b_queue_pop_front(&msg->msg_results); + while (entry) { + struct msg_parameter *param + = b_unbox(struct msg_parameter, entry, p_entry); + free(param->p_name); + free(param); + entry = b_queue_pop_front(&msg->msg_results); + } + + free(msg->msg_name); + free(msg); +} + +bool msg_definition_has_param(struct msg_definition *msg, const char *name) +{ + b_queue_entry *entry = b_queue_first(&msg->msg_params); + while (entry) { + struct msg_parameter *param + = b_unbox(struct msg_parameter, entry, p_entry); + if (!strcmp(param->p_name, name)) { + return true; + } + + entry = b_queue_next(entry); + } + + return false; +} + +bool msg_definition_has_result(struct msg_definition *msg, const char *name) +{ + b_queue_entry *entry = b_queue_first(&msg->msg_results); + while (entry) { + struct msg_parameter *param + = b_unbox(struct msg_parameter, entry, p_entry); + if (!strcmp(param->p_name, name)) { + return true; + } + + entry = b_queue_next(entry); + } + + return false; +} + +int msg_definition_add_param( + struct msg_definition *msg, + const struct type *type, + const char *name) +{ + struct msg_parameter *param = malloc(sizeof *param); + if (!param) { + return -1; + } + + memset(param, 0x0, sizeof *param); + + param->p_type = type; + param->p_name = b_strdup(name); + + b_queue_push_back(&msg->msg_params, ¶m->p_entry); + return 0; +} + +int msg_definition_add_result( + struct msg_definition *msg, + const struct type *type, + const char *name) +{ + struct msg_parameter *param = malloc(sizeof *param); + if (!param) { + return -1; + } + + memset(param, 0x0, sizeof *param); + + param->p_type = type; + param->p_name = b_strdup(name); + + b_queue_push_back(&msg->msg_results, ¶m->p_entry); + return 0; +} diff --git a/toolchain/ifc/msg.h b/toolchain/ifc/msg.h new file mode 100644 index 0000000..9c8c067 --- /dev/null +++ b/toolchain/ifc/msg.h @@ -0,0 +1,41 @@ +#ifndef IFC_MSG_H_ +#define IFC_MSG_H_ + +#include + +struct type; + +struct msg_parameter { + char *p_name; + const struct type *p_type; + b_queue_entry p_entry; +}; + +struct msg_definition { + char *msg_name; + b_queue_entry msg_entry; + + b_queue msg_params; + b_queue msg_results; +}; + +extern struct msg_definition *msg_definition_create(const char *name); +extern void msg_definition_destroy(struct msg_definition *msg); + +extern bool msg_definition_has_param( + struct msg_definition *msg, + const char *name); +extern bool msg_definition_has_result( + struct msg_definition *msg, + const char *name); + +extern int msg_definition_add_param( + struct msg_definition *msg, + const struct type *type, + const char *name); +extern int msg_definition_add_result( + struct msg_definition *msg, + const struct type *type, + const char *name); + +#endif diff --git a/toolchain/ifc/parse.c b/toolchain/ifc/parse.c new file mode 100644 index 0000000..0a9b52c --- /dev/null +++ b/toolchain/ifc/parse.c @@ -0,0 +1,287 @@ +#include "ctx.h" +#include "interface.h" +#include "lex.h" +#include "msg.h" + +#include + +#define report_error(...) fprintf(stderr, __VA_ARGS__) + +static bool peek_keyword(struct lex *lex, enum token_keyword kw) +{ + struct token *tok = lex_peek(lex); + return (tok && tok->tok_type == TOK_KEYWORD && tok->tok_kw == kw); +} + +static bool parse_keyword(struct lex *lex, enum token_keyword kw) +{ + struct token *tok = lex_peek(lex); + if (!tok || tok->tok_type != TOK_KEYWORD || tok->tok_kw != kw) { + return false; + } + + lex_advance(lex); + return true; +} + +static bool parse_symbol(struct lex *lex, enum token_symbol sym) +{ + struct token *tok = lex_peek(lex); + if (!tok || tok->tok_type != TOK_SYMBOL || tok->tok_sym != sym) { + return false; + } + + lex_advance(lex); + return true; +} + +static bool parse_word(struct lex *lex, char **out) +{ + struct token *tok = lex_peek(lex); + if (!tok || tok->tok_type != TOK_WORD) { + return false; + } + + *out = tok->tok_str; + tok->tok_str = NULL; + lex_advance(lex); + return true; +} + +struct msg_definition *parse_msg_definition(struct ctx *ctx, struct lex *lex) +{ + struct msg_definition *msg = NULL; + + if (!parse_keyword(lex, KW_MSG)) { + report_error("expected `msg` keyword\n"); + goto fail; + } + + char *msg_name = NULL; + if (!parse_word(lex, &msg_name)) { + report_error("expected message identifier\n"); + goto fail; + } + + msg = msg_definition_create(msg_name); + free(msg_name); + + if (!parse_symbol(lex, SYM_LEFT_PAREN)) { + report_error("expected `(` after message identifier\n"); + goto fail; + } + + size_t i = 0; + bool ok = true; + while (ok) { + if (parse_symbol(lex, SYM_RIGHT_PAREN)) { + break; + } + + if (i > 0 && !parse_symbol(lex, SYM_COMMA)) { + report_error("expected `,` after message parameter\n"); + ok = false; + break; + } + + char *type_name; + if (!parse_word(lex, &type_name)) { + report_error("expected message parameter type\n"); + ok = false; + break; + } + + const struct type *type = ctx_get_type(ctx, type_name); + if (!type) { + report_error( + "message parameter has unknown type " + "'%s'\n", + type_name); + free(type_name); + ok = false; + break; + } + + free(type_name); + + char *param_name; + if (!parse_word(lex, ¶m_name)) { + report_error("expected message parameter name\n"); + ok = false; + break; + } + + bool duplicate = msg_definition_has_param(msg, param_name) + || msg_definition_has_result(msg, param_name); + if (duplicate) { + free(param_name); + report_error( + "message has multiple parameters/results with " + "name '%s'\n", + param_name); + ok = false; + break; + } + + msg_definition_add_param(msg, type, param_name); + free(param_name); + i++; + } + + if (!ok) { + goto fail; + } + + if (!parse_symbol(lex, SYM_HYPHEN_RIGHT_ANGLE)) { + report_error("expected `->` after message parameter list\n"); + goto fail; + } + + if (!parse_symbol(lex, SYM_LEFT_PAREN)) { + report_error("expected `(` for message results list\n"); + goto fail; + } + + i = 0; + while (ok) { + if (parse_symbol(lex, SYM_RIGHT_PAREN)) { + break; + } + + if (i > 0 && !parse_symbol(lex, SYM_COMMA)) { + report_error( + "expected `,` or `)` after message result\n"); + ok = false; + break; + } + + char *type_name; + if (!parse_word(lex, &type_name)) { + report_error("expected message result type\n"); + ok = false; + break; + } + + const struct type *type = ctx_get_type(ctx, type_name); + if (!type) { + report_error( + "message result has unknown type " + "'%s'\n", + type_name); + free(type_name); + ok = false; + break; + } + free(type_name); + + char *result_name; + if (!parse_word(lex, &result_name)) { + report_error("expected message parameter name\n"); + ok = false; + break; + } + + bool duplicate = msg_definition_has_param(msg, result_name) + || msg_definition_has_result(msg, result_name); + if (duplicate) { + report_error( + "message has multiple parameters/results with " + "name '%s'\n", + result_name); + free(result_name); + ok = false; + break; + } + + msg_definition_add_result(msg, type, result_name); + free(result_name); + } + + if (!parse_symbol(lex, SYM_SEMICOLON)) { + report_error("expected `;` after message definition\n"); + goto fail; + } + + return msg; + +fail: + if (msg) { + msg_definition_destroy(msg); + } + + return NULL; +} + +struct interface_definition *parse_interface_definition( + struct ctx *ctx, + struct lex *lex) +{ + struct interface_definition *iface = NULL; + + if (!parse_keyword(lex, KW_INTERFACE)) { + report_error("expected `interface` keyword"); + goto fail; + } + + char *if_name = NULL; + if (!parse_word(lex, &if_name)) { + report_error("expected interface identifier"); + goto fail; + } + + iface = interface_definition_create(if_name); + free(if_name); + if (!iface) { + goto fail; + } + + if (!parse_symbol(lex, SYM_LEFT_BRACE)) { + report_error("expected `{` after interface identifier"); + goto fail; + } + + bool ok = true; + + while (ok) { + if (parse_symbol(lex, SYM_RIGHT_BRACE)) { + break; + } + + if (peek_keyword(lex, KW_MSG)) { + struct msg_definition *msg + = parse_msg_definition(ctx, lex); + if (!msg) { + ok = false; + break; + } + + if (interface_definition_has_msg( + iface, + msg->msg_name)) { + msg_definition_destroy(msg); + ok = false; + break; + } + + interface_definition_add_msg(iface, msg); + continue; + } + + report_error("expected `}` or message definition\n"); + ok = false; + break; + } + + if (!ok) { + goto fail; + } + + return iface; + +fail: + if (iface) { + interface_definition_destroy(iface); + } + + return NULL; +} diff --git a/toolchain/ifc/parse.h b/toolchain/ifc/parse.h new file mode 100644 index 0000000..57c6065 --- /dev/null +++ b/toolchain/ifc/parse.h @@ -0,0 +1,12 @@ +#ifndef IFC_PARSE_H_ +#define IFC_PARSE_H_ + +struct ctx; +struct lex; +struct interface_definition; + +extern struct interface_definition *parse_interface_definition( + struct ctx *ctx, + struct lex *lex); + +#endif diff --git a/toolchain/ifc/status.h b/toolchain/ifc/status.h new file mode 100644 index 0000000..da6c732 --- /dev/null +++ b/toolchain/ifc/status.h @@ -0,0 +1,20 @@ +#ifndef IFC_STATUS_H_ +#define IFC_STATUS_H_ + +enum status { + SUCCESS = 0, + ERR_EOF, + ERR_BAD_SYNTAX, + ERR_BAD_FORMAT, + ERR_BAD_STATE, + ERR_INVALID_VALUE, + ERR_INVALID_ARGUMENT, + ERR_NO_MEMORY, + ERR_NO_ENTRY, + ERR_NO_DATA, + ERR_NAME_EXISTS, + ERR_NOT_SUPPORTED, + ERR_INTERNAL_FAILURE, +}; + +#endif diff --git a/toolchain/ifc/token.c b/toolchain/ifc/token.c new file mode 100644 index 0000000..5460ca5 --- /dev/null +++ b/toolchain/ifc/token.c @@ -0,0 +1,63 @@ +#include "token.h" + +void token_destroy(struct token *tok) +{ + switch (tok->tok_value_type) { + case TOK_V_STRING: + if (tok->tok_str) { + free(tok->tok_str); + } + break; + default: + break; + } + + free(tok); +} + +#define ENUM_STR(x) \ + case x: \ + return #x + +const char *token_type_to_string(enum token_type type) +{ + switch (type) { + ENUM_STR(TOK_NONE); + ENUM_STR(TOK_INT); + ENUM_STR(TOK_SYMBOL); + ENUM_STR(TOK_WORD); + ENUM_STR(TOK_NAME); + ENUM_STR(TOK_STRING); + ENUM_STR(TOK_KEYWORD); + default: + return ""; + } +} + +const char *token_symbol_to_string(enum token_symbol sym) +{ + switch (sym) { + ENUM_STR(SYM_NONE); + ENUM_STR(SYM_COMMA); + ENUM_STR(SYM_SEMICOLON); + ENUM_STR(SYM_HYPHEN); + ENUM_STR(SYM_LEFT_BRACE); + ENUM_STR(SYM_RIGHT_BRACE); + ENUM_STR(SYM_LEFT_PAREN); + ENUM_STR(SYM_RIGHT_PAREN); + ENUM_STR(SYM_HYPHEN_RIGHT_ANGLE); + default: + return ""; + } +} + +const char *token_keyword_to_string(enum token_keyword kw) +{ + switch (kw) { + ENUM_STR(KW_NONE); + ENUM_STR(KW_INTERFACE); + ENUM_STR(KW_MSG); + default: + return ""; + } +} diff --git a/toolchain/ifc/token.h b/toolchain/ifc/token.h new file mode 100644 index 0000000..4c47f2e --- /dev/null +++ b/toolchain/ifc/token.h @@ -0,0 +1,70 @@ +#ifndef IFC_TOKEN_H_ +#define IFC_TOKEN_H_ + +#include "file-span.h" + +#include + +#define TOKEN_TYPE(tok) ((tok) ? (tok)->tok_type : TOK_NONE) +#define TOKEN_IS(tok, type) ((tok) && ((tok)->tok_type & (type)) != 0) +#define TOKEN_IS_SYMBOL(tok, sym) \ + ((tok) && (tok)->tok_type == TOK_SYMBOL && (tok)->tok_sym == (sym)) + +enum token_type { + TOK_NONE = 0, + TOK_SYMBOL = 0x0001u, + TOK_KEYWORD = 0x0002u, + TOK_WORD = 0x0004u, + TOK_NAME = 0x0008u, + TOK_INT = 0x0010u, + TOK_STRING = 0x0020u, + __TOK_UBOUND = 0x0040u, +}; + +enum token_value_type { + TOK_V_NONE = 0, + TOK_V_INT, + TOK_V_STRING, + TOK_V_SYMBOL, + TOK_V_KEYWORD, +}; + +enum token_symbol { + SYM_NONE = 0, + SYM_COMMA = __TOK_UBOUND, + SYM_HYPHEN, + SYM_SEMICOLON, + SYM_LEFT_BRACE, + SYM_RIGHT_BRACE, + SYM_LEFT_PAREN, + SYM_RIGHT_PAREN, + SYM_HYPHEN_RIGHT_ANGLE, + __SYM_UBOUND, +}; + +enum token_keyword { + KW_NONE = 0, + KW_INTERFACE = __SYM_UBOUND, + KW_MSG, +}; + +struct token { + struct file_span tok_location; + enum token_type tok_type; + enum token_value_type tok_value_type; + b_queue_entry tok_entry; + + union { + char *tok_str; + enum token_symbol tok_sym; + enum token_keyword tok_kw; + long long tok_int; + }; +}; + +extern void token_destroy(struct token *tok); +extern const char *token_type_to_string(enum token_type type); +extern const char *token_symbol_to_string(enum token_symbol sym); +extern const char *token_keyword_to_string(enum token_keyword kw); + +#endif diff --git a/toolchain/ifc/type.c b/toolchain/ifc/type.c new file mode 100644 index 0000000..9e48103 --- /dev/null +++ b/toolchain/ifc/type.c @@ -0,0 +1,17 @@ +#include "type.h" + +#include + +void type_print(const struct type *ty) +{ + switch (ty->ty_id) { + case TYPE_INT: + printf("int"); + break; + case TYPE_STRING: + printf("string"); + break; + default: + break; + } +} diff --git a/toolchain/ifc/type.h b/toolchain/ifc/type.h new file mode 100644 index 0000000..2dd4ae6 --- /dev/null +++ b/toolchain/ifc/type.h @@ -0,0 +1,17 @@ +#ifndef IFC_TYPE_H_ +#define IFC_TYPE_H_ + +enum type_id { + TYPE_NONE, + TYPE_INT, + TYPE_STRING, + TYPE_OTHER, +}; + +struct type { + enum type_id ty_id; +}; + +extern void type_print(const struct type *ty); + +#endif