commit e2a593bad570a404322edc032fb6dbc5a459de9f
Author: William Casarin <jb55@jb55.com>
Date: Sat, 13 Jun 2020 19:26:13 -0700
initial commit wip
Diffstat:
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 */