toolchain: replace ifc interface compiler with xpcg
xpcg is used to generate xpc interfaces
This commit is contained in:
7
toolchain/xpcg/CMakeLists.txt
Normal file
7
toolchain/xpcg/CMakeLists.txt
Normal 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
13
toolchain/xpcg/backend.h
Normal 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
|
||||
1128
toolchain/xpcg/backend/c-mpc/backend.c
Normal file
1128
toolchain/xpcg/backend/c-mpc/backend.c
Normal file
File diff suppressed because it is too large
Load Diff
64
toolchain/xpcg/ctx.c
Normal file
64
toolchain/xpcg/ctx.c
Normal 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
18
toolchain/xpcg/ctx.h
Normal 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
|
||||
12
toolchain/xpcg/file-span.h
Normal file
12
toolchain/xpcg/file-span.h
Normal 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
|
||||
63
toolchain/xpcg/interface.c
Normal file
63
toolchain/xpcg/interface.c
Normal 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);
|
||||
}
|
||||
26
toolchain/xpcg/interface.h
Normal file
26
toolchain/xpcg/interface.h
Normal 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
743
toolchain/xpcg/lex.c
Normal 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
52
toolchain/xpcg/lex.h
Normal 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
|
||||
164
toolchain/xpcg/line-source.c
Normal file
164
toolchain/xpcg/line-source.c
Normal 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);
|
||||
}
|
||||
39
toolchain/xpcg/line-source.h
Normal file
39
toolchain/xpcg/line-source.h
Normal 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
169
toolchain/xpcg/main.c
Normal 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
114
toolchain/xpcg/msg.c
Normal 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, ¶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;
|
||||
}
|
||||
44
toolchain/xpcg/msg.h
Normal file
44
toolchain/xpcg/msg.h
Normal 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
346
toolchain/xpcg/parse.c
Normal 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, ¶m_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
12
toolchain/xpcg/parse.h
Normal 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
20
toolchain/xpcg/status.h
Normal 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
64
toolchain/xpcg/token.c
Normal 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
73
toolchain/xpcg/token.h
Normal 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
26
toolchain/xpcg/type.c
Normal 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
20
toolchain/xpcg/type.h
Normal 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
|
||||
Reference in New Issue
Block a user