protoverse

A metaverse protocol
git clone git://jb55.com/protoverse
Log | Files | Refs | README | LICENSE

commit e2a593bad570a404322edc032fb6dbc5a459de9f
Author: William Casarin <jb55@jb55.com>
Date:   Sat, 13 Jun 2020 19:26:13 -0700

initial commit wip

Diffstat:
A.dir-locals.el | 7+++++++
A.gitignore | 2++
AMakefile | 11+++++++++++
AREADME.md | 7+++++++
Aio.c | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Aio.h | 14++++++++++++++
Aparse.c | 326+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aparse.h | 9+++++++++
Aprotoverse.c | 22++++++++++++++++++++++
Asatoshis-citadel.space | 19+++++++++++++++++++
Atypedefs.h | 8++++++++
11 files changed, 476 insertions(+), 0 deletions(-)

diff --git a/.dir-locals.el b/.dir-locals.el @@ -0,0 +1,7 @@ +((c-mode . ((c-file-style . "linux") + (indent-tabs-mode . t) + (show-trailing-whitespace . t) + (c-basic-offset . 8) + (tab-width . 8) + )) + ) diff --git a/.gitignore b/.gitignore @@ -0,0 +1,2 @@ +*.o +/protoverse diff --git a/Makefile b/Makefile @@ -0,0 +1,11 @@ + +CFLAGS = -O1 -g -std=c89 -Wall -Wextra -Werror -Wstrict-prototypes -Wold-style-definition -Wmissing-prototypes -Wmissing-declarations -Wdeclaration-after-statement + +OBJS = io.o parse.o + +protoverse: protoverse.c $(OBJS) + $(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@ + + +clean: + rm -f protoverse diff --git a/README.md b/README.md @@ -0,0 +1,6 @@ + +# Protoverse + +Minimal, accessible metaverse protocol with progressive level of detail. + +WIP+ \ No newline at end of file diff --git a/io.c b/io.c @@ -0,0 +1,51 @@ + +#include "io.h" + +#include <string.h> + +int read_fd(FILE *fd, unsigned char *buf, size_t buflen, size_t *written) +{ + unsigned char *p = buf; + int len = 0; + *written = 0; + + do { + len = fread(p, 1, 4096, fd); + *written += len; + p += len; + if (p > buf + buflen) + return 0; + } while (len == 4096); + + return 1; +} + + +int read_file(const char *filename, unsigned char *buf, size_t buflen, + size_t *written) +{ + FILE *file = NULL; + int ok; + + file = fopen(filename, "rb"); + if (file == NULL) { + *written = strlen(filename) + 1; + strncpy((char*)buf, filename, buflen); + return 1; + } + + ok = read_fd(file, buf, buflen, written); + fclose(file); + return ok; +} + + +int read_file_or_stdin(const char *filename, unsigned char *buf, + size_t buflen, size_t *written) +{ + if (filename == NULL) { + return read_fd(stdin, buf, buflen, written); + } + + return read_file(filename, buf, buflen, written); +} diff --git a/io.h b/io.h @@ -0,0 +1,14 @@ + +#ifndef PROTOVERSE_IO_H +#define PROTOVERSE_IO_H + +#include <stdio.h> + +int read_fd(FILE *fd, unsigned char *buf, size_t buflen, size_t *written); +int read_file(const char *filename, unsigned char *buf, size_t buflen, + size_t *written); +int read_file_or_stdin(const char *filename, unsigned char *buf, + size_t buflen, size_t *written); + + +#endif /* PROTOVERSE_IO_H */ diff --git a/parse.c b/parse.c @@ -0,0 +1,326 @@ + +#include "parse.h" +#include <stdio.h> +#include <string.h> +#include <ctype.h> + +#define tokdebug printf + +enum token_error { + TE_OK, + TE_IDENT_START_CHAR, + TE_IDENT_CHAR, + TE_IDENT_OVERFLOW, +}; + +enum known_identifier { + I_OBJECT, + I_SPACE, + I_OBJECTS, + I_NAME, + I_TYPE, + I_SHAPE, + I_WIDTH, + I_DEPTH, + I_HEIGHT, + I_CONDITION, + I_LOCATION, + I_MATERIAL, +}; + +enum token_type { + T_OPEN, + T_CLOSE, + T_STRING, + T_IDENTIFIER, + T_NUMBER, +}; + +enum tok_state { + TS_OPEN, + TS_CLOSE, + TS_ATOM, +}; + +struct tok_str { + u8 *data; + int len; +}; + +union token { + struct tok_str str; +}; + +struct cursor { + u8 *p; + u8 *end; + enum token_error err; + union { + char c; + } err_data; +}; + +static int pull_byte(struct cursor *cursor, u8 *c) +{ + if (cursor->p + 1 >= cursor->end) + return 0; + + *c = *cursor->p; + cursor->p++; + + return 1; +} + + +static int push_byte(struct cursor *cursor, u8 c) +{ + if (cursor->p + 1 >= cursor->end) { + return 0; + } + + *cursor->p = c; + cursor->p++; + + return 1; +} + + +static int push_data(struct cursor *cursor, u8 *data, int len) +{ + if (cursor->p + len >= cursor->end) { + printf("push_data oob\n"); + return 0; + } + + memcpy(cursor->p, data, len); + cursor->p += len; + + return 1; +} + +static int push_token_data(struct cursor *tokens, + enum token_type token, + void *token_data, int token_data_size) +{ + struct tok_str *str; + int ok; + ok = push_byte(tokens, token); + if (!ok) return 0; + + switch (token) { + case T_NUMBER: + tokdebug("NUM"); + break; + + case T_OPEN: + tokdebug("("); + break; + + case T_CLOSE: + tokdebug(")"); + break; /* nothing to write after these tokens */ + + case T_IDENTIFIER: + tokdebug("I_"); + + /* fallthrough */ + case T_STRING: + str = (struct tok_str*)token_data; + if (token == T_STRING) { + tokdebug("\"%.*s\" ", str->len, str->data); + } + else { + tokdebug("%.*s ", str->len, str->data); + } + ok = push_data(tokens, token_data, token_data_size); + if (!ok) return 0; + break; + } + + return 1; +} + + +static int push_token(struct cursor *tokens, enum token_type token) +{ + return push_token_data(tokens, token, NULL, 0); +} + +static int push_identifier(struct cursor *tokens, struct tok_str ident) +{ + return push_token_data(tokens, T_IDENTIFIER, &ident, sizeof(ident)); +} + +static int push_string(struct cursor *tokens, struct tok_str str) +{ + return push_token_data(tokens, T_STRING, &str, sizeof(str)); +} + + +static int is_start_ident_char(char c) +{ + return c >= 'a' && c <= 'z'; +} + +static int is_ident_char(char c) +{ + return is_start_ident_char(c) || c == '-' || c == '_' || + (c >= '0' && c <= '9'); +} + +static int pull_string(struct cursor *cursor, u8 *buf, int buf_len) +{ + (void)cursor; + (void)buf; + (void)buf_len; + return 0; +} + +static int pull_identifier(struct cursor *cursor, u8 *buf, int buf_len) +{ + int ok = 1; + int chars = 0; + u8 c; + + struct cursor temp; + struct cursor ident_cursor; + + temp.p = cursor->p; + temp.end = cursor->end; + + ident_cursor.p = buf; + ident_cursor.end = buf + buf_len; + + while (1) { + ok = pull_byte(&temp, &c); + if (!ok) return 0; + + /* first char should start with a letter */ + if (chars == 0 && !is_start_ident_char(c)) { + cursor->err = TE_IDENT_START_CHAR; + cursor->err_data.c = c; + return 0; + } else if (chars > 0 && isspace(c)) { + /* we're done here */ + break; + } else if (chars > 0 && !is_ident_char(c)) { + cursor->err = TE_IDENT_CHAR; + cursor->err_data.c = c; + return 0; + } + + ok = push_byte(&ident_cursor, c); + chars++; + + if (!ok) { + cursor->err = TE_IDENT_OVERFLOW; + return 0; + } + } + + ok = push_byte(&ident_cursor, 0); + chars++; + + if (!ok) { + cursor->err = TE_IDENT_OVERFLOW; + return 0; + } + + cursor->p = temp.p; + cursor->end = temp.end; + cursor->err = TE_OK; + + return chars; +} + +static int read_and_push_atom(struct cursor *cursor, struct cursor *tokens) +{ + u8 buf[255]; + struct tok_str str; + int ok; + + ok = pull_identifier(cursor, buf, sizeof(buf)); + if (ok) { + str.len = ok; + str.data = buf; + ok = push_identifier(tokens, str); + if (!ok) { + printf("read_and_push_atom identifier push overflow\n"); + return 0; + } + return 1; + } + + ok = pull_string(cursor, buf, sizeof(buf)); + if (ok) { + str.len = ok; + str.data = buf; + ok = push_string(tokens, str); + if (!ok) { + printf("read_and_push_atom string push overflow\n"); + return 0; + } + return 1; + } + + /* TODO: read_number */ + + return 0; +} + +int tokenize_space(u8 *buf, int buf_size, u8 *token_buf, int token_buf_size) +{ + enum tok_state state; + struct cursor cursor; + struct cursor tokens; + u8 c; + int ok; + + cursor.p = buf; + cursor.end = buf + buf_size; + + tokens.p = token_buf; + tokens.end = token_buf + token_buf_size; + + state = TS_OPEN; + + while (cursor.p < cursor.end) { + ok = pull_byte(&cursor, &c); + if (!ok) return 0; + + if (state == TS_OPEN) { + if (isspace(c)) + continue; + + if (c != '(') { + printf("expected '(' or whitespace\n"); + return 0; + } + + push_token(&tokens, T_OPEN); + state = TS_ATOM; + continue; + } + else if (state == TS_ATOM) { + if (isspace(c)) + continue; + + if (c == '(') { + push_token(&tokens, T_OPEN); + continue; + } + + if (c == ')') { + push_token(&tokens, T_CLOSE); + continue; + } + + cursor.p--; + ok = read_and_push_atom(&cursor, &tokens); + if (!ok) return 0; + + } + } + + return 1; +} diff --git a/parse.h b/parse.h @@ -0,0 +1,9 @@ + +#ifndef PROTOVERSE_PARSE_H +#define PROTOVERSE_PARSE_H + +#include "typedefs.h" + +int tokenize_space(unsigned char *buf, int buf_size, unsigned char *token_buf, int token_buf_size); + +#endif /* PROTOVERSE_PARSE_H */ diff --git a/protoverse.c b/protoverse.c @@ -0,0 +1,22 @@ + +#include "io.h" +#include "parse.h" + + +int main(int argc, const char *argv[]) { + static u8 file_buf[4096]; + static u8 token_buf[2048]; + + size_t count; + + const char *space = argc == 2 ? argv[1] : "satoshis-citadel.space"; + int ok = read_file(space, file_buf, sizeof(file_buf), &count); + if (!ok) { + printf("failed to load '%s'\n", space); + return 1; + } + + tokenize_space(file_buf, count, token_buf, sizeof(token_buf)); + + return 0; +} diff --git a/satoshis-citadel.space b/satoshis-citadel.space @@ -0,0 +1,19 @@ +(space (name "Satoshi's Citadel") + (type root) + (space (name "Foyer") + (type room) + (shape rectangle) + (width 10) (depth 10) (height 100) + (objects + (object (type table) + (id welcome-desk) + (name "Welcome desk") + (material marble) + (condition clean new) + (width 1) (depth 2) (height 1) + (location center) + ) + ) + ) +) + diff --git a/typedefs.h b/typedefs.h @@ -0,0 +1,8 @@ + +#ifndef PROTOVERSE_TYPEDEFS_H +#define PROTOVERSE_TYPEDEFS_H + +typedef unsigned char u8; + + +#endif /* PROTOVERSE_TYPEDEFS_H */