├── .gitignore ├── test ├── math.stvm ├── first.stvm └── calls.stvm ├── README.MD ├── include ├── bytecode.h ├── util.h ├── compiler.h ├── parser.h ├── bytebuffer.h ├── token.h └── runtime.h ├── docs └── current.md ├── Makefile ├── src ├── token.c ├── bytebuffer.c ├── main.c ├── compiler.c ├── util.c ├── parser.c └── runtime.c └── LICENSE.MD /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | *.o 3 | staple 4 | todo.txt -------------------------------------------------------------------------------- /test/math.stvm: -------------------------------------------------------------------------------- 1 | push #8 2 | push #12 3 | mul 4 | hlt -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | # Staple VM 2 | 3 | The virtual machine for the Staple language -------------------------------------------------------------------------------- /test/first.stvm: -------------------------------------------------------------------------------- 1 | push #10 2 | push #20 3 | push #30 4 | add 5 | add 6 | hlt -------------------------------------------------------------------------------- /test/calls.stvm: -------------------------------------------------------------------------------- 1 | mov %eax #5 2 | call @square 3 | hlt 4 | 5 | square: 6 | mul %eax %eax 7 | ret -------------------------------------------------------------------------------- /include/bytecode.h: -------------------------------------------------------------------------------- 1 | #ifndef STAPLE_BYTECODE_H 2 | #define STAPLE_BYTECODE_H 3 | 4 | enum _Opcode { 5 | NOP = 0x00, 6 | PUSH_CONST = 0x01, 7 | ADD_STACK = 0x02, 8 | SUB_STACK = 0x03, 9 | MUL_STACK = 0x04, 10 | HLT_OP = 0xFF 11 | }; 12 | 13 | #endif // STAPLE_BYTECODE_H -------------------------------------------------------------------------------- /docs/current.md: -------------------------------------------------------------------------------- 1 | # Staple VM Spec 2 | 3 | ## Symbols 4 | 5 | `#` means number constant 6 | 7 | ## Instructions 8 | 9 | * `PUSH` - Push an 8 bit number onto the stack 10 | * `ADD` - Pops last two numbers off of stack, adds, pushes result 11 | * `HLT` - Pops stack and returns result as exit code -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -c -std=c99 -g 3 | LDFLAGS = -g 4 | SRC = ${wildcard src/*.c} 5 | HDR = ${wildcard include/*.h} 6 | OBJ = ${SRC:.c=.o} 7 | EXEC = staple 8 | 9 | all: ${SRC} ${OBJ} ${EXEC} 10 | 11 | debug: all 12 | debug: CFLAGS += -DDEBUG 13 | 14 | ${EXEC}: ${OBJ} 15 | ${CC} ${LDFLAGS} $^ -o $@ 16 | 17 | %.o: %.c ${HDR} 18 | ${CC} ${CFLAGS} $< -o $@ 19 | 20 | clean: 21 | rm src/*.o ${EXEC} *.stbc -------------------------------------------------------------------------------- /include/util.h: -------------------------------------------------------------------------------- 1 | #ifndef STAPLE_UTIL_H 2 | #define STAPLE_UTIL_H 3 | 4 | #include 5 | #include 6 | 7 | #include "bytebuffer.h" 8 | 9 | char* read_ascii_file(const char* path); 10 | 11 | uint8_t* read_binary_file(const char* path); 12 | void write_binary_file(const char* path, ByteBuffer* bb); 13 | 14 | uint16_t read16(uint8_t* buffer, uint32_t index); 15 | uint32_t read32(uint8_t* buffer, uint32_t index); 16 | 17 | #endif // STAPLE_UTIL_H -------------------------------------------------------------------------------- /include/compiler.h: -------------------------------------------------------------------------------- 1 | #ifndef STAPLE_COMPILER_H 2 | #define STAPLE_COMPILER_H 3 | 4 | #include 5 | 6 | #include "bytecode.h" 7 | #include "token.h" 8 | #include "bytebuffer.h" 9 | 10 | enum _CompilerStatus { 11 | COMPILER_SUCCESS, 12 | COMPILER_ERROR, 13 | }; 14 | typedef enum _CompilerStatus CompilerStatus; 15 | 16 | struct _Compiler { 17 | CompilerStatus status; 18 | TokenList* tokens; 19 | ByteBuffer* bytecode; 20 | }; 21 | typedef struct _Compiler Compiler; 22 | 23 | void compiler_start(Compiler* c); 24 | 25 | #endif // STAPLE_COMPILER_H -------------------------------------------------------------------------------- /include/parser.h: -------------------------------------------------------------------------------- 1 | #ifndef STAPLE_PARSER_H 2 | #define STAPLE_PARSER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "token.h" 11 | 12 | enum _ParserStatus { 13 | PARSER_SUCCESS, 14 | PARSER_SYNTAX_ERROR 15 | }; 16 | typedef enum _ParserStatus ParserStatus; 17 | 18 | ParserStatus parser_start(TokenList* list, const char* source); 19 | uint32_t parser_get_number(const char* buf); 20 | TokenInst parser_get_inst(const char* buf); 21 | 22 | #endif // STAPLE_PARSER_H -------------------------------------------------------------------------------- /include/bytebuffer.h: -------------------------------------------------------------------------------- 1 | #ifndef STAPLE_BYTEBUFFER_H 2 | #define STAPLE_BYTEBUFFER_H 3 | 4 | #include 5 | #include 6 | 7 | struct _ByteBuffer { 8 | uint8_t* buffer; 9 | int ptr; 10 | int size; 11 | }; 12 | typedef struct _ByteBuffer ByteBuffer; 13 | 14 | ByteBuffer* byte_buffer_create(int size); 15 | void byte_buffer_write8(ByteBuffer* bb, uint8_t data); 16 | void byte_buffer_write16(ByteBuffer* bb, uint16_t data); 17 | void byte_buffer_write32(ByteBuffer* bb, uint32_t data); 18 | void byte_buffer_destroy(ByteBuffer* bb); 19 | 20 | #endif // STAPLE_BYTEBUFFER_H -------------------------------------------------------------------------------- /include/token.h: -------------------------------------------------------------------------------- 1 | #ifndef STAPLE_TOKEN_H 2 | #define STAPLE_TOKEN_H 3 | 4 | #include 5 | 6 | enum _TokenType { 7 | INST, 8 | NUMBER 9 | }; 10 | 11 | enum _TokenInst { 12 | PUSH, 13 | ADD, 14 | SUB, 15 | MUL, 16 | HLT 17 | }; 18 | typedef enum _TokenInst TokenInst; 19 | 20 | struct _Token { 21 | int type; 22 | int data; 23 | int line; 24 | }; 25 | typedef struct _Token Token; 26 | 27 | void token_create(Token* tok, int type, int data, int line); 28 | void token_destroy(Token* tok); 29 | 30 | struct _TokenList { 31 | Token* data; 32 | int ptr; 33 | int size; 34 | }; 35 | typedef struct _TokenList TokenList; 36 | 37 | void token_list_create(TokenList* list, int size); 38 | void token_list_add(TokenList* list, Token tok); 39 | Token* token_list_get(TokenList* list, int index); 40 | void token_list_destroy(TokenList* list); 41 | 42 | #endif // STAPLE_TOKEN_H -------------------------------------------------------------------------------- /include/runtime.h: -------------------------------------------------------------------------------- 1 | #ifndef STAPLE_RUNTIME_H 2 | #define STAPLE_RUNTIME_H 3 | 4 | #include 5 | 6 | #include "bytecode.h" 7 | #include "util.h" 8 | 9 | #define STACK_SIZE 1024 10 | 11 | enum _RuntimeStatus { 12 | RUNTIME_SUCCESS, 13 | RUNTIME_ERROR, 14 | }; 15 | typedef enum _RuntimeStatus RuntimeStatus; 16 | 17 | struct _Runtime { 18 | uint8_t* code; 19 | RuntimeStatus status; 20 | uint8_t stack[STACK_SIZE]; 21 | uint32_t sp; 22 | uint32_t ip; 23 | uint8_t exit; 24 | bool running; 25 | }; 26 | typedef struct _Runtime Runtime; 27 | 28 | void runtime_start(Runtime* runtime); 29 | 30 | // Stack functions 31 | void push8(Runtime* r, uint8_t data); 32 | void push16(Runtime* r, uint16_t data); 33 | void push32(Runtime* r, uint32_t data); 34 | uint8_t pop8(Runtime* r); 35 | uint16_t pop16(Runtime* r); 36 | uint32_t pop32(Runtime* r); 37 | 38 | #endif // STAPLE_RUNTIME_H -------------------------------------------------------------------------------- /src/token.c: -------------------------------------------------------------------------------- 1 | #include "../include/token.h" 2 | 3 | void token_create(Token* tok, int type, int data, int line) { 4 | tok->type = type; 5 | tok->data = data; 6 | tok->line = line; 7 | } 8 | 9 | void token_destroy(Token* tok) { 10 | // free(tok); 11 | } 12 | 13 | void token_list_create(TokenList* list, int size) { 14 | list->data = (Token*) malloc(sizeof(Token) * size); 15 | list->ptr = 0; 16 | list->size = size; 17 | } 18 | 19 | void token_list_add(TokenList* list, Token tok) { 20 | if (list->ptr >= list->size) { 21 | list->size *= 2; 22 | list->data = (Token*) realloc(list->data, sizeof(Token) * list->size); 23 | } 24 | 25 | Token* t = &list->data[list->ptr++]; 26 | token_create(t, tok.type, tok.data, tok.line); 27 | } 28 | 29 | Token* token_list_get(TokenList* list, int index) { 30 | return &list->data[index]; 31 | } 32 | 33 | void token_list_destroy(TokenList* list) { 34 | for (int i = 0; i < list->ptr; i++) { 35 | token_destroy(&list->data[i]); 36 | } 37 | free(list->data); 38 | } -------------------------------------------------------------------------------- /src/bytebuffer.c: -------------------------------------------------------------------------------- 1 | #include "../include/bytebuffer.h" 2 | 3 | ByteBuffer* byte_buffer_create(int size) { 4 | ByteBuffer* bb = (ByteBuffer*) malloc(sizeof(ByteBuffer)); 5 | bb->buffer = (uint8_t*) malloc(sizeof(uint8_t) * size); 6 | bb->ptr = 0; 7 | bb->size = size; 8 | return bb; 9 | } 10 | 11 | void byte_buffer_write8(ByteBuffer* bb, uint8_t data) { 12 | if (bb->ptr >= bb->size) { 13 | bb->size *= 2; 14 | bb->buffer = (uint8_t*) realloc(bb->buffer, sizeof(uint8_t) * bb->size); 15 | } 16 | 17 | bb->buffer[bb->ptr++] = data; 18 | } 19 | 20 | void byte_buffer_write16(ByteBuffer* bb, uint16_t data) { 21 | byte_buffer_write8(bb, (data & 0xFF00) >> 8); 22 | byte_buffer_write8(bb, (data & 0x00FF)); 23 | } 24 | 25 | void byte_buffer_write32(ByteBuffer* bb, uint32_t data) { 26 | byte_buffer_write8(bb, (data & 0xFF000000) >> 24); 27 | byte_buffer_write8(bb, (data & 0x00FF0000) >> 16); 28 | byte_buffer_write8(bb, (data & 0x0000FF00) >> 8); 29 | byte_buffer_write8(bb, (data & 0x000000FF)); 30 | } 31 | 32 | void byte_buffer_destroy(ByteBuffer* bb) { 33 | free(bb->buffer); 34 | free(bb); 35 | } -------------------------------------------------------------------------------- /LICENSE.MD: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Jonah Alligood 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "../include/util.h" 7 | #include "../include/parser.h" 8 | #include "../include/compiler.h" 9 | #include "../include/token.h" 10 | #include "../include/runtime.h" 11 | 12 | // staple compile file.stvm 13 | 14 | int main(int argc, char** argv) { 15 | if (argc <3) { 16 | printf("Too few arguments\n"); 17 | return 1; 18 | } 19 | 20 | // Compile 21 | if (strcmp(argv[1], "compile") == 0) { 22 | char* source = read_ascii_file(argv[2]); 23 | TokenList tokens; 24 | token_list_create(&tokens, 1); 25 | 26 | ParserStatus pstat = parser_start(&tokens, source); 27 | if (pstat != PARSER_SUCCESS) 28 | return 1; 29 | 30 | Compiler comp; 31 | comp.tokens = &tokens; 32 | compiler_start(&comp); 33 | if (comp.status != COMPILER_SUCCESS) 34 | return 1; 35 | 36 | write_binary_file("out.stbc", comp.bytecode); 37 | 38 | byte_buffer_destroy(comp.bytecode); 39 | token_list_destroy(&tokens); 40 | free(source); 41 | 42 | return 0; 43 | } 44 | 45 | else if (strcmp(argv[1], "run") == 0) { 46 | uint8_t* code = read_binary_file(argv[2]); 47 | 48 | Runtime runtime; 49 | runtime.code = code; 50 | runtime_start(&runtime); 51 | 52 | if (runtime.status == RUNTIME_ERROR) 53 | return 1; 54 | 55 | return runtime.exit; 56 | } 57 | 58 | return 1; 59 | } -------------------------------------------------------------------------------- /src/compiler.c: -------------------------------------------------------------------------------- 1 | #include "../include/compiler.h" 2 | 3 | void compiler_start(Compiler* c) { 4 | c->bytecode = byte_buffer_create(1); 5 | 6 | for (int i = 0; i < c->tokens->ptr; i++) { 7 | Token* t = token_list_get(c->tokens, i); 8 | 9 | if (t->type == INST) { 10 | switch (t->data) { 11 | 12 | // PUSH 13 | case PUSH: { 14 | // PUSH #N 15 | if (token_list_get(c->tokens, i + 1)->type == NUMBER) { 16 | byte_buffer_write8(c->bytecode, PUSH_CONST); 17 | byte_buffer_write32(c->bytecode, token_list_get(c->tokens, i + 1)->data); 18 | i++; 19 | } else { 20 | printf("Bad PUSH inst\n"); 21 | c->status = COMPILER_ERROR; 22 | return; 23 | } 24 | break; 25 | } 26 | 27 | // ADD 28 | case ADD: { 29 | byte_buffer_write8(c->bytecode, ADD_STACK); 30 | break; 31 | } 32 | 33 | // SUB 34 | case SUB: { 35 | byte_buffer_write8(c->bytecode, SUB_STACK); 36 | break; 37 | } 38 | 39 | // MUL 40 | case MUL: { 41 | byte_buffer_write8(c->bytecode, MUL_STACK); 42 | break; 43 | } 44 | 45 | // HLT 46 | case HLT: { 47 | byte_buffer_write8(c->bytecode, HLT_OP); 48 | break; 49 | } 50 | 51 | default: { 52 | printf("Unknown instruction token\n"); 53 | c->status = COMPILER_ERROR; 54 | return; 55 | } 56 | 57 | } 58 | } 59 | } 60 | 61 | c->status = COMPILER_SUCCESS; 62 | } -------------------------------------------------------------------------------- /src/util.c: -------------------------------------------------------------------------------- 1 | #include "../include/util.h" 2 | 3 | char* read_ascii_file(const char* path) { 4 | // Create a file 5 | FILE* file = fopen(path, "r"); 6 | if (!file) { 7 | printf("Could not open file '%s'\n", path); 8 | return NULL; 9 | } 10 | 11 | // Get the file size 12 | fseek(file, 0, SEEK_END); 13 | int size = ftell(file); 14 | fseek(file, 0, SEEK_SET); 15 | 16 | // Read file 17 | char* buf = (char*) malloc(sizeof(char) * (size + 1)); 18 | if (!buf) { 19 | printf("Could not allocate memory for file!\n"); 20 | return NULL; 21 | } 22 | fread(buf, 1, size, file); 23 | buf[size] = '\0'; 24 | fclose(file); 25 | 26 | // Return contents 27 | return buf; 28 | } 29 | 30 | uint8_t* read_binary_file(const char* path) { 31 | FILE* file = fopen(path, "rb"); 32 | if (!file) { 33 | printf("Could not open file '%s'\n", path); 34 | return NULL; 35 | } 36 | 37 | fseek(file, 0, SEEK_END); 38 | int size = ftell(file); 39 | fseek(file, 0, SEEK_SET); 40 | 41 | uint8_t* buffer = (uint8_t*) malloc(sizeof(uint8_t) * size); 42 | fread(buffer, 1, size, file); 43 | fclose(file); 44 | 45 | return buffer; 46 | } 47 | 48 | void write_binary_file(const char* path, ByteBuffer* bb) { 49 | FILE* file = fopen(path, "wb"); 50 | if (!file) { 51 | printf("Could not write to file '%s'\n", path); 52 | return; 53 | } 54 | 55 | fwrite(bb->buffer, 1, bb->ptr, file); 56 | fclose(file); 57 | } 58 | 59 | uint16_t read16(uint8_t* buffer, uint32_t index) { 60 | return (buffer[index] << 8) | (buffer[index + 1]); 61 | } 62 | 63 | uint32_t read32(uint8_t* buffer, uint32_t index) { 64 | return (buffer[index] << 24) | (buffer[index + 1] << 16) | 65 | (buffer[index + 2] << 8) | (buffer[index + 3]); 66 | } -------------------------------------------------------------------------------- /src/parser.c: -------------------------------------------------------------------------------- 1 | #include "../include/parser.h" 2 | 3 | ParserStatus parser_start(TokenList* list, const char* source) { 4 | char lex[256]; 5 | int lexi = 0; 6 | int i = 0; 7 | int line = 1; 8 | 9 | while (1) { 10 | // Loop until we find some delimiter 11 | while (source[i] != ' ' && source[i] != '\n' && source[i] != '\0' && source[i] != '\t') { 12 | lex[lexi++] = source[i++]; 13 | } 14 | lex[lexi] = '\0'; 15 | 16 | // Care about empty lines and tabs 17 | if (lexi == 0 && (source[i] == '\n' || source[i] == '\t')) { 18 | i++; 19 | if (source[i] == '\n') line++; 20 | continue; 21 | } 22 | 23 | Token token; 24 | 25 | // This is a numerical constant 26 | if (lex[0] == '#') { 27 | int num = parser_get_number(lex); 28 | // printf("NUMBER: %d\n", num); 29 | token_create(&token, NUMBER, num, line); 30 | token_list_add(list, token); 31 | } 32 | 33 | // Must be an instruction 34 | else { 35 | int inst = parser_get_inst(lex); 36 | if (inst >= 0) { 37 | // printf("INST: %s\n", lex); 38 | token_create(&token, INST, inst, line); 39 | token_list_add(list, token); 40 | } else { 41 | printf("%d: Syntax error: no such instruction '%s'\n", line, lex); 42 | return PARSER_SYNTAX_ERROR; 43 | } 44 | } 45 | 46 | // New line 47 | if (source[i] == '\n') { 48 | line++; 49 | } 50 | 51 | // End of file 52 | else if (source[i] == '\0') { 53 | break; 54 | } 55 | 56 | lexi = 0; 57 | i++; 58 | } 59 | 60 | return PARSER_SUCCESS; 61 | } 62 | 63 | uint32_t parser_get_number(const char* buf) { 64 | long num = atoi(&buf[1]); 65 | return (num <= UINT32_MAX) ? num : 0; 66 | } 67 | 68 | TokenInst parser_get_inst(const char* buf) { 69 | if (strcmp(buf, "push") == 0) 70 | return PUSH; 71 | if (strcmp(buf, "add") == 0) 72 | return ADD; 73 | if (strcmp(buf, "sub") == 0) 74 | return SUB; 75 | if (strcmp(buf, "mul") == 0) 76 | return MUL; 77 | if (strcmp(buf, "hlt") == 0) 78 | return HLT; 79 | return (TokenInst)-1; 80 | } -------------------------------------------------------------------------------- /src/runtime.c: -------------------------------------------------------------------------------- 1 | #include "../include/runtime.h" 2 | 3 | void runtime_start(Runtime* runtime) { 4 | runtime->sp = -1; 5 | runtime->exit = 123; 6 | runtime->ip = 0; 7 | runtime->running = true; 8 | 9 | while (runtime->running) { 10 | switch (runtime->code[runtime->ip++]) { 11 | 12 | // PUSH #N 13 | case PUSH_CONST: { 14 | push32(runtime, read32(runtime->code, runtime->ip)); 15 | runtime->ip += 4; 16 | break; 17 | } 18 | 19 | // ADD 20 | case ADD_STACK: { 21 | uint32_t a = pop32(runtime); 22 | uint32_t b = pop32(runtime); 23 | push32(runtime, a + b); 24 | break; 25 | } 26 | 27 | // SUB 28 | case SUB_STACK: { 29 | uint32_t a = pop32(runtime); 30 | uint32_t b = pop32(runtime); 31 | push32(runtime, a - b); 32 | break; 33 | } 34 | 35 | // MUL 36 | case MUL_STACK: { 37 | uint32_t a = pop32(runtime); 38 | uint32_t b = pop32(runtime); 39 | push32(runtime, a * b); 40 | break; 41 | } 42 | 43 | // HLT 44 | case HLT_OP: { 45 | runtime->exit = (uint8_t) pop8(runtime); 46 | runtime->status = RUNTIME_SUCCESS; 47 | runtime->running = false; 48 | break; 49 | } 50 | 51 | } 52 | } 53 | } 54 | 55 | void push8(Runtime* r, uint8_t data) { 56 | r->stack[++r->sp] = data; 57 | } 58 | 59 | void push16(Runtime* r, uint16_t data) { 60 | push8(r, (data & 0xFF00) >> 8); 61 | push8(r, (data & 0x00FF)); 62 | } 63 | 64 | void push32(Runtime* r, uint32_t data) { 65 | push8(r, (data & 0xFF000000) >> 24); 66 | push8(r, (data & 0x00FF0000) >> 16); 67 | push8(r, (data & 0x0000FF00) >> 8); 68 | push8(r, (data & 0x000000FF)); 69 | } 70 | 71 | uint8_t pop8(Runtime* r) { 72 | return r->stack[r->sp--]; 73 | } 74 | 75 | uint16_t pop16(Runtime* r) { 76 | uint16_t b = pop8(r); 77 | uint16_t a = pop8(r); 78 | return (a << 8) | b; 79 | } 80 | 81 | uint32_t pop32(Runtime* r){ 82 | uint8_t d = pop8(r); 83 | uint8_t c = pop8(r); 84 | uint8_t b = pop8(r); 85 | uint8_t a = pop8(r); 86 | return (a << 24) | (b << 16) | (c << 8) | d; 87 | } --------------------------------------------------------------------------------