toolchain: replace ifc interface compiler with xpcg

xpcg is used to generate xpc interfaces
This commit is contained in:
2026-03-10 19:12:14 +00:00
parent 3a06c18e10
commit 26a49162e6
24 changed files with 427 additions and 878 deletions

View File

@@ -0,0 +1,7 @@
file(GLOB sources
*.c *.h
backend/c-mpc/*.c backend/c-mpc/*.h)
add_executable(xpcg ${sources})
target_link_libraries(xpcg Bluelib::Core Bluelib::Ds Bluelib::Cmd Bluelib::Io)

13
toolchain/xpcg/backend.h Normal file
View File

@@ -0,0 +1,13 @@
#ifndef XPCG_BACKEND_H_
#define XPCG_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

File diff suppressed because it is too large Load Diff

64
toolchain/xpcg/ctx.c Normal file
View File

@@ -0,0 +1,64 @@
#include "ctx.h"
#include <stdlib.h>
#include <string.h>
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);
}
if (!strcmp(name, "handle")) {
return ctx_get_builtin_type(ctx, TYPE_HANDLE);
}
if (!strcmp(name, "size")) {
return ctx_get_builtin_type(ctx, TYPE_SIZE);
}
if (!strcmp(name, "buffer")) {
return ctx_get_builtin_type(ctx, TYPE_BUFFER);
}
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];
}

18
toolchain/xpcg/ctx.h Normal file
View File

@@ -0,0 +1,18 @@
#ifndef XPCG_CTX_H_
#define XPCG_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

View File

@@ -0,0 +1,12 @@
#ifndef XPCG_FILE_SPAN_H_
#define XPCG_FILE_SPAN_H_
struct file_cell {
unsigned int c_row, c_col;
};
struct file_span {
struct file_cell s_start, s_end;
};
#endif

View File

@@ -0,0 +1,63 @@
#include "interface.h"
#include "msg.h"
#include <blue/ds/string.h>
#include <stdlib.h>
#include <string.h>
struct interface_definition *interface_definition_create(
const char *name,
long long id)
{
struct interface_definition *out = malloc(sizeof *out);
if (!out) {
return NULL;
}
memset(out, 0x0, sizeof *out);
out->if_name = b_strdup(name);
out->if_id = id;
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);
}

View File

@@ -0,0 +1,26 @@
#ifndef XPCG_INTERFACE_H_
#define XPCG_INTERFACE_H_
#include <blue/core/queue.h>
struct msg_definition;
struct interface_definition {
char *if_name;
long long if_id;
b_queue if_msg;
};
extern struct interface_definition *interface_definition_create(
const char *name,
long long id);
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

743
toolchain/xpcg/lex.c Normal file
View File

@@ -0,0 +1,743 @@
#include "lex.h"
#include "line-source.h"
#include "token.h"
#include <blue/core/hash.h>
#include <blue/core/misc.h>
#include <blue/core/queue.h>
#include <blue/ds/dict.h>
#include <blue/ds/number.h>
#include <blue/ds/string.h>
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wctype.h>
#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_BRACKET, "["),
LEX_TOKEN_DEF(SYM_RIGHT_BRACKET, "]"),
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_COLON, ":"),
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_FUNC, "func"),
};
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;
}

52
toolchain/xpcg/lex.h Normal file
View File

@@ -0,0 +1,52 @@
#ifndef XPCG_LEX_H_
#define XPCG_LEX_H_
#include "status.h"
#include "token.h"
#include <blue/core/queue.h>
#include <blue/ds/dict.h>
#include <blue/ds/string.h>
#include <stdint.h>
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

View File

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

View File

@@ -0,0 +1,39 @@
#ifndef LINE_SOURCE_H_
#define LINE_SOURCE_H_
#include "file-span.h"
#include "status.h"
#include <blue/core/stream.h>
#include <blue/ds/array.h>
#include <blue/ds/string.h>
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

169
toolchain/xpcg/main.c Normal file
View File

@@ -0,0 +1,169 @@
#include "backend.h"
#include "ctx.h"
#include "interface.h"
#include "lex.h"
#include "line-source.h"
#include "msg.h"
#include "parse.h"
#include <blue/cmd.h>
#include <blue/io/file.h>
#include <blue/io/path.h>
#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 xpcg(
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("xpcg");
B_COMMAND_DESC("xpc interface generator.");
B_COMMAND_FLAGS(B_COMMAND_SHOW_HELP_BY_DEFAULT);
B_COMMAND_FUNCTION(xpcg);
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);
}
}

114
toolchain/xpcg/msg.c Normal file
View File

@@ -0,0 +1,114 @@
#include "msg.h"
#include <blue/ds/string.h>
#include <stdlib.h>
#include <string.h>
struct msg_definition *msg_definition_create(const char *name, long long id)
{
struct msg_definition *out = malloc(sizeof *out);
if (!out) {
return NULL;
}
memset(out, 0x0, sizeof *out);
out->msg_name = b_strdup(name);
out->msg_id = id;
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, &param->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, &param->p_entry);
return 0;
}

44
toolchain/xpcg/msg.h Normal file
View File

@@ -0,0 +1,44 @@
#ifndef XPCG_MSG_H_
#define XPCG_MSG_H_
#include <blue/core/queue.h>
struct type;
struct msg_parameter {
char *p_name;
const struct type *p_type;
b_queue_entry p_entry;
};
struct msg_definition {
char *msg_name;
long long msg_id;
b_queue_entry msg_entry;
b_queue msg_params;
b_queue msg_results;
};
extern struct msg_definition *msg_definition_create(
const char *name,
long long id);
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

346
toolchain/xpcg/parse.c Normal file
View File

@@ -0,0 +1,346 @@
#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;
}
static bool parse_int(struct lex *lex, long long *out)
{
struct token *tok = lex_peek(lex);
if (!tok || tok->tok_type != TOK_INT) {
return false;
}
*out = tok->tok_int;
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_FUNC)) {
report_error("expected `func` keyword\n");
goto fail;
}
char *msg_name = NULL;
if (!parse_word(lex, &msg_name)) {
report_error("expected function identifier\n");
goto fail;
}
long long msg_id = 0;
if (!parse_symbol(lex, SYM_LEFT_BRACKET)) {
report_error("expected `[` after function identifier\n");
goto fail;
}
if (!parse_int(lex, &msg_id)) {
report_error("expected function id number after `[`\n");
goto fail;
}
if (!parse_symbol(lex, SYM_RIGHT_BRACKET)) {
report_error("expected `]` after function id number\n");
goto fail;
}
msg = msg_definition_create(msg_name, msg_id);
free(msg_name);
if (!parse_symbol(lex, SYM_LEFT_PAREN)) {
report_error(
"expected `(` after function id number specifier\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 function "
"parameter\n");
ok = false;
break;
}
char *param_name;
if (!parse_word(lex, &param_name)) {
report_error("expected function parameter name\n");
ok = false;
break;
}
if (!parse_symbol(lex, SYM_COLON)) {
report_error(
"expected `:` after function parameter "
"name\n");
ok = false;
break;
}
char *type_name;
if (!parse_word(lex, &type_name)) {
report_error("expected function parameter type\n");
ok = false;
break;
}
const struct type *type = ctx_get_type(ctx, type_name);
if (!type) {
report_error(
"function parameter has unknown type "
"'%s'\n",
type_name);
free(type_name);
ok = false;
break;
}
free(type_name);
bool duplicate = msg_definition_has_param(msg, param_name)
|| msg_definition_has_result(msg, param_name);
if (duplicate) {
free(param_name);
report_error(
"function 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 function parameter "
"list\n");
goto fail;
}
if (!parse_symbol(lex, SYM_LEFT_PAREN)) {
report_error("expected `(` for function 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 function "
"result\n");
ok = false;
break;
}
char *result_name;
if (!parse_word(lex, &result_name)) {
report_error("expected function parameter name\n");
ok = false;
break;
}
if (!parse_symbol(lex, SYM_COLON)) {
report_error(
"expected `:` after function result "
"name\n");
ok = false;
break;
}
char *type_name;
if (!parse_word(lex, &type_name)) {
report_error("expected function result type\n");
ok = false;
break;
}
const struct type *type = ctx_get_type(ctx, type_name);
if (!type) {
report_error(
"function result has unknown type "
"'%s'\n",
type_name);
free(type_name);
ok = false;
break;
}
free(type_name);
bool duplicate = msg_definition_has_param(msg, result_name)
|| msg_definition_has_result(msg, result_name);
if (duplicate) {
report_error(
"function 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);
i++;
}
if (!parse_symbol(lex, SYM_SEMICOLON)) {
report_error("expected `;` after function 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 name");
goto fail;
}
long long if_id = 0;
if (!parse_int(lex, &if_id)) {
report_error("expected interface id number");
goto fail;
}
if (!parse_symbol(lex, SYM_SEMICOLON)) {
report_error("expected `;` after interface definition");
goto fail;
}
iface = interface_definition_create(if_name, if_id);
free(if_name);
if (!iface) {
goto fail;
}
bool ok = true;
while (ok) {
if (!lex_peek(lex) && lex_get_status(lex) == ERR_EOF) {
break;
}
if (peek_keyword(lex, KW_FUNC)) {
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 eof or function definition\n");
ok = false;
break;
}
if (!ok) {
goto fail;
}
return iface;
fail:
if (iface) {
interface_definition_destroy(iface);
}
return NULL;
}

12
toolchain/xpcg/parse.h Normal file
View File

@@ -0,0 +1,12 @@
#ifndef XPCG_PARSE_H_
#define XPCG_PARSE_H_
struct ctx;
struct lex;
struct interface_definition;
extern struct interface_definition *parse_interface_definition(
struct ctx *ctx,
struct lex *lex);
#endif

20
toolchain/xpcg/status.h Normal file
View File

@@ -0,0 +1,20 @@
#ifndef XPCG_STATUS_H_
#define XPCG_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

64
toolchain/xpcg/token.c Normal file
View File

@@ -0,0 +1,64 @@
#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_COLON);
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_FUNC);
default:
return "";
}
}

73
toolchain/xpcg/token.h Normal file
View File

@@ -0,0 +1,73 @@
#ifndef XPCG_TOKEN_H_
#define XPCG_TOKEN_H_
#include "file-span.h"
#include <blue/core/queue.h>
#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_COLON,
SYM_LEFT_BRACKET,
SYM_RIGHT_BRACKET,
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_FUNC,
};
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

26
toolchain/xpcg/type.c Normal file
View File

@@ -0,0 +1,26 @@
#include "type.h"
#include <stdio.h>
void type_print(const struct type *ty)
{
switch (ty->ty_id) {
case TYPE_INT:
printf("int");
break;
case TYPE_STRING:
printf("string");
break;
case TYPE_SIZE:
printf("size");
break;
case TYPE_BUFFER:
printf("buffer");
break;
case TYPE_HANDLE:
printf("handle");
break;
default:
break;
}
}

20
toolchain/xpcg/type.h Normal file
View File

@@ -0,0 +1,20 @@
#ifndef XPCG_TYPE_H_
#define XPCG_TYPE_H_
enum type_id {
TYPE_NONE,
TYPE_INT,
TYPE_SIZE,
TYPE_STRING,
TYPE_BUFFER,
TYPE_HANDLE,
TYPE_OTHER,
};
struct type {
enum type_id ty_id;
};
extern void type_print(const struct type *ty);
#endif