added variables
This commit is contained in:
parent
603a06ec39
commit
3d6b2cbda5
13 changed files with 368 additions and 43 deletions
4
Makefile
4
Makefile
|
@ -2,8 +2,8 @@ make: trie $(wildcard src/*.c)
|
|||
mkdir -p bin
|
||||
gcc src/*.c -Wall -Wextra -pedantic -ggdb -o bin/sysh
|
||||
|
||||
trie: triegen.py commands
|
||||
python triegen.py commands src/trie.c
|
||||
trie: gen/triegen.py gen/commands
|
||||
python gen/triegen.py gen/commands gen/syscalls_x86_64 src/trie.c
|
||||
|
||||
clean:
|
||||
rm -f src/trie.c
|
||||
|
|
5
examples/cat.sysh
Normal file
5
examples/cat.sysh
Normal file
|
@ -0,0 +1,5 @@
|
|||
.set $buf { .alloc 1024 }
|
||||
.set $read { read 0 $buf 1024 }
|
||||
write 1 $buf $read
|
||||
.free $buf
|
||||
|
1
examples/helloworld.sysh
Normal file
1
examples/helloworld.sysh
Normal file
|
@ -0,0 +1 @@
|
|||
write 1 "Hello, world!\n" 14
|
10
gen/commands
Normal file
10
gen/commands
Normal file
|
@ -0,0 +1,10 @@
|
|||
.alloc C_ALLOC
|
||||
.realloc C_REALLOC
|
||||
.free C_FREE
|
||||
.set C_SET
|
||||
.cpy C_CPY
|
||||
.deref C_DEREF
|
||||
.strerror C_STRERROR
|
||||
.if C_IF
|
||||
.while C_WHILE
|
||||
.for C_FOR
|
|
@ -9,17 +9,18 @@ if len(sys.argv) < 3:
|
|||
print("Not enough arguments. Usage: triegen.py <input> <output>")
|
||||
sys.exit(1)
|
||||
|
||||
input_file = sys.argv[1]
|
||||
output_file = sys.argv[2]
|
||||
output_file = sys.argv[len(sys.argv) - 1]
|
||||
|
||||
data = []
|
||||
for input_file in sys.argv[1:-1]:
|
||||
with open(input_file, 'r') as f:
|
||||
data = [re.split('\s+', l.strip()) for l in f.read().split('\n') if len(l.strip()) > 0]
|
||||
data += [re.split('\s+', l.strip()) for l in f.read().split('\n') if len(l.strip()) > 0]
|
||||
|
||||
trie = {}
|
||||
|
||||
for line in data:
|
||||
key = line[0] + '\0'
|
||||
val = int(line[1])
|
||||
val = line[1]
|
||||
trie_local = trie
|
||||
for c in key:
|
||||
if c == '\0':
|
||||
|
@ -67,7 +68,7 @@ with open(output_file, 'w') as f:
|
|||
f.write("#include <string.h>\n")
|
||||
f.write("#include \"trie.h\"\n\n")
|
||||
f.write("/* auto-generated by triegen.py */\n\n")
|
||||
f.write("int trie_get(const char* key) {\n ")
|
||||
f.write("long trie_get(const char* key) {\n ")
|
||||
write_trie(f, trie, 0)
|
||||
f.write(" return -1;\n}\n")
|
||||
|
146
src/eval.c
146
src/eval.c
|
@ -3,34 +3,67 @@
|
|||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "eval.h"
|
||||
#include "hashmap.h"
|
||||
#include "parser.h"
|
||||
#include "scanner.h"
|
||||
#include "trie.h"
|
||||
|
||||
static long eval_syscall(Line* line) {
|
||||
#define LOGERROR(msg) fprintf(stderr, "sysh: " msg ## "\n")
|
||||
|
||||
static void log_error(const char* format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
fprintf(stderr, "sysh: ");
|
||||
vfprintf(stderr, format, args);
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
bool eval_arg(Argument arg, long* result, bool* cloned, Hashmap* vars) {
|
||||
if(arg.type == ARG_NUM) {
|
||||
*result = arg.as.num;
|
||||
if(cloned) *cloned = false;
|
||||
return true;
|
||||
} else if(arg.type == ARG_STR && cloned != NULL) {
|
||||
int len = strlen(arg.as.str);
|
||||
char* buf = malloc(len + 1);
|
||||
strcpy(buf, arg.as.str);
|
||||
*result = (long)buf;
|
||||
*cloned = true;
|
||||
return true;
|
||||
} else if(arg.type == ARG_BLOCK) {
|
||||
*result = eval_block(&arg.as.block, vars);
|
||||
if(cloned) *cloned = false;
|
||||
return true;
|
||||
} else if(arg.type == ARG_VAR) {
|
||||
if(hashmap_get(vars, arg.as.str, result)) {
|
||||
if(cloned) *cloned = false;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static long eval_syscall(Line* line, Hashmap* vars) {
|
||||
if(line->len > 6) {
|
||||
fprintf(stderr, "sysh: too many args for syscall\n");
|
||||
return 0;
|
||||
log_error("too many arguments for syscall: got %d", line->len);
|
||||
return -1;
|
||||
}
|
||||
long args[6] = {0,0,0,0,0,0};
|
||||
bool cloned[6] = {0,0,0,0,0,0};
|
||||
for(int i = 0; i < line->len; i++) {
|
||||
Argument arg = line->args[i];
|
||||
if(arg.type == ARG_NUM) {
|
||||
args[i] = arg.as.num;
|
||||
} else if(arg.type == ARG_STR) {
|
||||
int len = strlen(arg.as.str);
|
||||
char* buf = malloc(len + 1);
|
||||
strcpy(buf, arg.as.str);
|
||||
args[i] = (long)buf;
|
||||
cloned[i] = true;
|
||||
} else {
|
||||
if(!eval_arg(arg, &args[i], &cloned[i], vars)) {
|
||||
for(int j = 0; j < line->len; j++) {
|
||||
if(cloned[j]) free((void*)args[j]);
|
||||
}
|
||||
fprintf(stderr, "sysh: invalid arg type\n");
|
||||
return 0;
|
||||
log_error("bad argument to syscall");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
long result = syscall(line->id, args[0], args[1], args[2], args[3], args[4], args[5]);
|
||||
|
@ -40,24 +73,93 @@ static long eval_syscall(Line* line) {
|
|||
return result;
|
||||
}
|
||||
|
||||
static long eval_line(Line* line) {
|
||||
if(line->id >= 0) {
|
||||
return eval_syscall(line);
|
||||
} else {
|
||||
// TODO
|
||||
static long eval_alloc(Line* line, Hashmap* vars) {
|
||||
if(line->len != 1) {
|
||||
log_error(".alloc expected 1 arg, got %d", line->len);
|
||||
return -1;
|
||||
}
|
||||
long val;
|
||||
if(!eval_arg(line->args[0], &val, NULL, vars)) {
|
||||
log_error("bad argument to .alloc");
|
||||
return -1;
|
||||
}
|
||||
return (long)malloc(val);
|
||||
}
|
||||
|
||||
static long eval_realloc(Line* line, Hashmap* vars) {
|
||||
if(line->len != 2) {
|
||||
log_error(".realloc expected 2 args, got %d", line->len);
|
||||
return -1;
|
||||
}
|
||||
long val1;
|
||||
long val2;
|
||||
if(!eval_arg(line->args[0], &val1, NULL, vars)) {
|
||||
log_error("bad argument to .realloc");
|
||||
return -1;
|
||||
}
|
||||
if(!eval_arg(line->args[1], &val2, NULL, vars)) {
|
||||
log_error("bad argument to .realloc");
|
||||
return -1;
|
||||
}
|
||||
return (long)realloc((void*)val1, val2);
|
||||
}
|
||||
|
||||
static long eval_free(Line* line, Hashmap* vars) {
|
||||
if(line->len != 1) {
|
||||
log_error(".free expected 1 args, got %d", line->len);
|
||||
return -1;
|
||||
}
|
||||
long val;
|
||||
if(!eval_arg(line->args[0], &val, NULL, vars)) {
|
||||
log_error("bad argument to .free");
|
||||
return -1;
|
||||
}
|
||||
free((void*)val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long eval_set(Line* line, Hashmap* vars) {
|
||||
if(line->len < 0 || line->len > 2) {
|
||||
log_error(".set expected 1 or 2 args, got %d", line->len);
|
||||
return -1;
|
||||
}
|
||||
if(line->args[0].type != ARG_VAR) {
|
||||
log_error("bad argument to .set");
|
||||
return -1;
|
||||
}
|
||||
if(line->len == 2) {
|
||||
long val;
|
||||
bool cloned;
|
||||
if(!eval_arg(line->args[1], &val, &cloned, vars)) {
|
||||
log_error("bad argument to .set");
|
||||
return -1;
|
||||
}
|
||||
hashmap_add(vars, strdup(line->args[0].as.str), val);
|
||||
} else {
|
||||
hashmap_remove(vars, line->args[0].as.str);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long eval_line(Line* line, Hashmap* vars) {
|
||||
if(line->id >= 0) {
|
||||
return eval_syscall(line, vars);
|
||||
} else switch(line->id) {
|
||||
case C_ALLOC: return eval_alloc(line, vars);
|
||||
case C_REALLOC: return eval_realloc(line, vars);
|
||||
case C_FREE: return eval_free(line, vars);
|
||||
case C_SET: return eval_set(line, vars);
|
||||
default: return 0; // unreachable
|
||||
}
|
||||
}
|
||||
|
||||
long eval_block(Block* block) {
|
||||
long eval_block(Block* block, Hashmap* vars) {
|
||||
long result = 0;
|
||||
for(int i = 0; i < block->len; i++) {
|
||||
result = eval_line(&block->lines[i]);
|
||||
result = eval_line(&block->lines[i], vars);
|
||||
if(errno > 0) {
|
||||
fprintf(stderr, "E%d: %s\n", errno, strerror(errno));
|
||||
}
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "hashmap.h"
|
||||
#include "parser.h"
|
||||
|
||||
long eval_block(Block* block);
|
||||
long eval_block(Block* block, Hashmap* vars);
|
||||
|
|
107
src/hashmap.c
Normal file
107
src/hashmap.c
Normal file
|
@ -0,0 +1,107 @@
|
|||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "hashmap.h"
|
||||
|
||||
static uint32_t hash_string(const char* key) {
|
||||
uint32_t hash = 2166136261u;
|
||||
while(*key != '\0') {
|
||||
hash ^= (uint8_t)(*key);
|
||||
hash *= 16777619;
|
||||
key++;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
void hashmap_init(Hashmap* hashmap) {
|
||||
hashmap->len = 0;
|
||||
hashmap->capacity = 0;
|
||||
hashmap->entries = NULL;
|
||||
}
|
||||
|
||||
void hashmap_free(Hashmap* hashmap) {
|
||||
for(int i = 0; i < hashmap->capacity; i++) {
|
||||
Entry e = hashmap->entries[i];
|
||||
if(e.key != NULL) free((char*)e.key);
|
||||
}
|
||||
free(hashmap->entries);
|
||||
hashmap_init(hashmap);
|
||||
}
|
||||
|
||||
static Entry* hashmap_find(Entry* entries, int capacity, const char* key) {
|
||||
uint32_t index = hash_string(key) % capacity;
|
||||
Entry* tombstone = NULL;
|
||||
while(true) {
|
||||
Entry* entry = &entries[index];
|
||||
if(entry->key == NULL) {
|
||||
if(entry->value == 1) {
|
||||
if(tombstone == NULL) tombstone = entry;
|
||||
} else {
|
||||
return (tombstone == NULL) ? entry : tombstone;
|
||||
}
|
||||
} else if(strcmp(entry->key, key) == 0) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void hashmap_grow(Hashmap* map, int capacity) {
|
||||
Entry* entries = malloc(capacity * sizeof(Entry));
|
||||
for(int i = 0; i < capacity; i++) {
|
||||
entries[i].key = NULL;
|
||||
entries[i].value = 0;
|
||||
}
|
||||
map->len = 0;
|
||||
for(int i = 0; i < map->capacity; i++) {
|
||||
Entry* entry = &map->entries[i];
|
||||
if(entry->key == NULL) continue;
|
||||
|
||||
Entry* dest = hashmap_find(entries, capacity, entry->key);
|
||||
dest->key = entry->key;
|
||||
dest->value = entry->value;
|
||||
map->len++;
|
||||
}
|
||||
free(map->entries);
|
||||
|
||||
map->entries = entries;
|
||||
map->capacity = capacity;
|
||||
}
|
||||
|
||||
bool hashmap_get(Hashmap* hashmap, const char* key, long* value) {
|
||||
if(hashmap->len == 0) return false;
|
||||
|
||||
Entry* e = hashmap_find(hashmap->entries, hashmap->capacity, key);
|
||||
if(e->key == NULL) return false;
|
||||
|
||||
*value = e->value;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hashmap_add(Hashmap* hashmap, char* key, long value) {
|
||||
if(hashmap->len + 1 > hashmap->capacity * TABLE_MAX_LOAD) {
|
||||
int capacity = (hashmap->capacity == 0 ? 8 : (2 * hashmap->capacity));
|
||||
hashmap_grow(hashmap, capacity);
|
||||
}
|
||||
Entry* e = hashmap_find(hashmap->entries, hashmap->capacity, key);
|
||||
bool new_key = e->key == NULL;
|
||||
if(new_key) {
|
||||
hashmap->len++;
|
||||
}
|
||||
e->key = key;
|
||||
e->value = value;
|
||||
return new_key;
|
||||
}
|
||||
|
||||
bool hashmap_remove(Hashmap* hashmap, const char* key) {
|
||||
if(hashmap->len == 0) return false;
|
||||
|
||||
Entry* e = hashmap_find(hashmap->entries, hashmap->capacity, key);
|
||||
if(e->key == NULL) return false;
|
||||
|
||||
e->key = NULL;
|
||||
e->value = 1;
|
||||
return true;
|
||||
}
|
23
src/hashmap.h
Normal file
23
src/hashmap.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
#pragma once
|
||||
#include <stdbool.h>
|
||||
|
||||
#define TABLE_MAX_LOAD 0.75
|
||||
|
||||
typedef struct {
|
||||
const char* key;
|
||||
long value;
|
||||
} Entry;
|
||||
|
||||
typedef struct {
|
||||
int len;
|
||||
int capacity;
|
||||
Entry* entries;
|
||||
} Hashmap;
|
||||
|
||||
void hashmap_init(Hashmap* hashmap);
|
||||
void hashmap_free(Hashmap* hashmap);
|
||||
|
||||
bool hashmap_add(Hashmap* hashmap, char* key, long value);
|
||||
bool hashmap_get(Hashmap* hashmap, const char* key, long* value);
|
||||
bool hashmap_remove(Hashmap* hashmap, const char* key);
|
||||
|
54
src/main.c
54
src/main.c
|
@ -1,9 +1,12 @@
|
|||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "eval.h"
|
||||
#include "hashmap.h"
|
||||
#include "parser.h"
|
||||
#include "scanner.h"
|
||||
|
||||
|
@ -11,9 +14,11 @@
|
|||
#define PROMPT "[%ld]sysh$ "
|
||||
#define EPROMPT "[E]sysh$ "
|
||||
|
||||
void repl() {
|
||||
static long repl() {
|
||||
char buf[LINE_LEN];
|
||||
printf(PROMPT, 0L);
|
||||
Hashmap vars;
|
||||
hashmap_init(&vars);
|
||||
while(fgets(buf, LINE_LEN, stdin)) {
|
||||
Scanner sc = init_scanner(buf);
|
||||
BlockResult br = parse(&sc);
|
||||
|
@ -21,14 +26,51 @@ void repl() {
|
|||
printf("sysh: %s\n", br.as.err);
|
||||
printf(EPROMPT);
|
||||
} else if(br.as.ok.len > 0) {
|
||||
long result = eval_block(&br.as.ok);
|
||||
long result = eval_block(&br.as.ok, &vars);
|
||||
printf(PROMPT, result);
|
||||
block_free(&br.as.ok);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
repl();
|
||||
hashmap_free(&vars);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long run_file(const char* name) {
|
||||
FILE* file = fopen(name, "r");
|
||||
if(file == NULL) {
|
||||
fprintf(stderr, "%s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
fseek(file, 0, SEEK_END);
|
||||
long fsize = ftell(file);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
|
||||
char* buf = malloc(fsize + 1);
|
||||
fread(buf, fsize, 1, file);
|
||||
fclose(file);
|
||||
|
||||
buf[fsize] = '\0';
|
||||
|
||||
Hashmap vars;
|
||||
hashmap_init(&vars);
|
||||
Scanner sc = init_scanner(buf);
|
||||
BlockResult br = parse(&sc);
|
||||
if(!br.is_ok) {
|
||||
printf("sysh: %s\n", br.as.err);
|
||||
} else if(br.as.ok.len > 0) {
|
||||
eval_block(&br.as.ok, &vars);
|
||||
block_free(&br.as.ok);
|
||||
}
|
||||
hashmap_free(&vars);
|
||||
free(buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, const char** argv) {
|
||||
if(argc < 1) return 1;
|
||||
if(argc == 1) return repl();
|
||||
if(argc == 2) return run_file(argv[1]);
|
||||
fprintf(stderr, "usage: %s [file]\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
|
30
src/parser.c
30
src/parser.c
|
@ -65,8 +65,9 @@ void line_free(Line* line) {
|
|||
free(line->args);
|
||||
}
|
||||
|
||||
static BlockResult parse_block(Scanner* sc, bool braced);
|
||||
|
||||
static LineResult parse_line(Scanner* sc, int id) {
|
||||
static LineResult parse_line(Scanner* sc, int id, bool* brace_end) {
|
||||
Line line;
|
||||
line_init(&line, id);
|
||||
while(true) {
|
||||
|
@ -74,6 +75,10 @@ static LineResult parse_line(Scanner* sc, int id) {
|
|||
switch(tok.type) {
|
||||
case TOK_EOF:
|
||||
case TOK_EOL:
|
||||
*brace_end = false;
|
||||
return OK(line, LineResult);
|
||||
case TOK_RBRACE:
|
||||
*brace_end = true;
|
||||
return OK(line, LineResult);
|
||||
case TOK_ERR:
|
||||
line_free(&line);
|
||||
|
@ -90,9 +95,17 @@ static LineResult parse_line(Scanner* sc, int id) {
|
|||
case TOK_CMD:
|
||||
line_add(&line, (Argument){.type = ARG_CMD, .as.str = tok.as.str});
|
||||
break;
|
||||
case TOK_LBRACE: {
|
||||
BlockResult br = parse_block(sc, true);
|
||||
if(!br.is_ok) {
|
||||
line_free(&line);
|
||||
return ERR(br.as.err, LineResult);
|
||||
}
|
||||
line_add(&line, (Argument){.type = ARG_BLOCK, .as.block = br.as.ok});
|
||||
} break;
|
||||
default:
|
||||
line_free(&line);
|
||||
return ERR("unexpected token", LineResult);
|
||||
return ERR("unexpected token in line: %d", LineResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -118,16 +131,25 @@ static BlockResult parse_block(Scanner* sc, bool braced) {
|
|||
block_free(&block);
|
||||
return ERR("invalid syscall or command name", BlockResult);
|
||||
}
|
||||
LineResult sr = parse_line(sc, id);
|
||||
bool brace_end;
|
||||
LineResult sr = parse_line(sc, id, &brace_end);
|
||||
if(!sr.is_ok) {
|
||||
block_free(&block);
|
||||
return ERR(sr.as.err, BlockResult);
|
||||
}
|
||||
if(brace_end && !braced) {
|
||||
line_free(&sr.as.ok);
|
||||
block_free(&block);
|
||||
return ERR("unexpected token in block", BlockResult);
|
||||
}
|
||||
block_add(&block, sr.as.ok);
|
||||
if(brace_end) {
|
||||
return OK(block, BlockResult);
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
block_free(&block);
|
||||
return ERR("unexpected token", BlockResult);
|
||||
return ERR("unexpected token in block", BlockResult);
|
||||
}
|
||||
}
|
||||
return OK(block, BlockResult);
|
||||
|
|
13
src/trie.h
13
src/trie.h
|
@ -1,3 +1,14 @@
|
|||
#pragma once
|
||||
|
||||
int trie_get(const char* key);
|
||||
#define C_ALLOC -2
|
||||
#define C_REALLOC -3
|
||||
#define C_FREE -4
|
||||
#define C_SET -5
|
||||
#define C_CPY -6
|
||||
#define C_DEREF -7
|
||||
#define C_STRERROR -8
|
||||
#define C_IF -9
|
||||
#define C_WHILE -10
|
||||
#define C_FOR -11
|
||||
|
||||
long trie_get(const char* key);
|
||||
|
|
Loading…
Reference in a new issue