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.
This commit is contained in:
287
toolchain/ifc/parse.c
Normal file
287
toolchain/ifc/parse.c
Normal file
@@ -0,0 +1,287 @@
|
||||
#include "ctx.h"
|
||||
#include "interface.h"
|
||||
#include "lex.h"
|
||||
#include "msg.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
Reference in New Issue
Block a user