added variables

This commit is contained in:
TriMill 2023-02-20 10:19:58 -05:00
parent 603a06ec39
commit 3d6b2cbda5
13 changed files with 368 additions and 43 deletions

View file

@ -2,8 +2,8 @@ make: trie $(wildcard src/*.c)
mkdir -p bin mkdir -p bin
gcc src/*.c -Wall -Wextra -pedantic -ggdb -o bin/sysh gcc src/*.c -Wall -Wextra -pedantic -ggdb -o bin/sysh
trie: triegen.py commands trie: gen/triegen.py gen/commands
python triegen.py commands src/trie.c python gen/triegen.py gen/commands gen/syscalls_x86_64 src/trie.c
clean: clean:
rm -f src/trie.c rm -f src/trie.c

5
examples/cat.sysh Normal file
View 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
View file

@ -0,0 +1 @@
write 1 "Hello, world!\n" 14

10
gen/commands Normal file
View 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

View file

@ -9,17 +9,18 @@ if len(sys.argv) < 3:
print("Not enough arguments. Usage: triegen.py <input> <output>") print("Not enough arguments. Usage: triegen.py <input> <output>")
sys.exit(1) sys.exit(1)
input_file = sys.argv[1] output_file = sys.argv[len(sys.argv) - 1]
output_file = sys.argv[2]
with open(input_file, 'r') as f: data = []
data = [re.split('\s+', l.strip()) for l in f.read().split('\n') if len(l.strip()) > 0] 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]
trie = {} trie = {}
for line in data: for line in data:
key = line[0] + '\0' key = line[0] + '\0'
val = int(line[1]) val = line[1]
trie_local = trie trie_local = trie
for c in key: for c in key:
if c == '\0': if c == '\0':
@ -67,7 +68,7 @@ with open(output_file, 'w') as f:
f.write("#include <string.h>\n") f.write("#include <string.h>\n")
f.write("#include \"trie.h\"\n\n") f.write("#include \"trie.h\"\n\n")
f.write("/* auto-generated by triegen.py */\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) write_trie(f, trie, 0)
f.write(" return -1;\n}\n") f.write(" return -1;\n}\n")

View file

@ -3,34 +3,67 @@
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <errno.h> #include <errno.h>
#include <stdarg.h>
#include "eval.h" #include "eval.h"
#include "hashmap.h"
#include "parser.h" #include "parser.h"
#include "scanner.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) { if(line->len > 6) {
fprintf(stderr, "sysh: too many args for syscall\n"); log_error("too many arguments for syscall: got %d", line->len);
return 0; return -1;
} }
long args[6] = {0,0,0,0,0,0}; long args[6] = {0,0,0,0,0,0};
bool cloned[6] = {0,0,0,0,0,0}; bool cloned[6] = {0,0,0,0,0,0};
for(int i = 0; i < line->len; i++) { for(int i = 0; i < line->len; i++) {
Argument arg = line->args[i]; Argument arg = line->args[i];
if(arg.type == ARG_NUM) { if(!eval_arg(arg, &args[i], &cloned[i], vars)) {
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 {
for(int j = 0; j < line->len; j++) { for(int j = 0; j < line->len; j++) {
if(cloned[j]) free((void*)args[j]); if(cloned[j]) free((void*)args[j]);
} }
fprintf(stderr, "sysh: invalid arg type\n"); log_error("bad argument to syscall");
return 0; return -1;
} }
} }
long result = syscall(line->id, args[0], args[1], args[2], args[3], args[4], args[5]); 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; return result;
} }
static long eval_line(Line* line) { static long eval_alloc(Line* line, Hashmap* vars) {
if(line->id >= 0) { if(line->len != 1) {
return eval_syscall(line); 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 { } else {
// TODO hashmap_remove(vars, line->args[0].as.str);
return 0; }
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, Hashmap* vars) {
long eval_block(Block* block) {
long result = 0; long result = 0;
for(int i = 0; i < block->len; i++) { for(int i = 0; i < block->len; i++) {
result = eval_line(&block->lines[i]); result = eval_line(&block->lines[i], vars);
if(errno > 0) { if(errno > 0) {
fprintf(stderr, "E%d: %s\n", errno, strerror(errno)); fprintf(stderr, "E%d: %s\n", errno, strerror(errno));
} }
break;
} }
return result; return result;
} }

View file

@ -1,5 +1,6 @@
#pragma once #pragma once
#include "hashmap.h"
#include "parser.h" #include "parser.h"
long eval_block(Block* block); long eval_block(Block* block, Hashmap* vars);

107
src/hashmap.c Normal file
View 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
View 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);

View file

@ -1,9 +1,12 @@
#include <errno.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <assert.h> #include <assert.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include "eval.h" #include "eval.h"
#include "hashmap.h"
#include "parser.h" #include "parser.h"
#include "scanner.h" #include "scanner.h"
@ -11,9 +14,11 @@
#define PROMPT "[%ld]sysh$ " #define PROMPT "[%ld]sysh$ "
#define EPROMPT "[E]sysh$ " #define EPROMPT "[E]sysh$ "
void repl() { static long repl() {
char buf[LINE_LEN]; char buf[LINE_LEN];
printf(PROMPT, 0L); printf(PROMPT, 0L);
Hashmap vars;
hashmap_init(&vars);
while(fgets(buf, LINE_LEN, stdin)) { while(fgets(buf, LINE_LEN, stdin)) {
Scanner sc = init_scanner(buf); Scanner sc = init_scanner(buf);
BlockResult br = parse(&sc); BlockResult br = parse(&sc);
@ -21,14 +26,51 @@ void repl() {
printf("sysh: %s\n", br.as.err); printf("sysh: %s\n", br.as.err);
printf(EPROMPT); printf(EPROMPT);
} else if(br.as.ok.len > 0) { } 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); printf(PROMPT, result);
block_free(&br.as.ok); block_free(&br.as.ok);
} }
} }
} hashmap_free(&vars);
int main(void) {
repl();
return 0; 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;
}

View file

@ -65,8 +65,9 @@ void line_free(Line* line) {
free(line->args); 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 line;
line_init(&line, id); line_init(&line, id);
while(true) { while(true) {
@ -74,6 +75,10 @@ static LineResult parse_line(Scanner* sc, int id) {
switch(tok.type) { switch(tok.type) {
case TOK_EOF: case TOK_EOF:
case TOK_EOL: case TOK_EOL:
*brace_end = false;
return OK(line, LineResult);
case TOK_RBRACE:
*brace_end = true;
return OK(line, LineResult); return OK(line, LineResult);
case TOK_ERR: case TOK_ERR:
line_free(&line); line_free(&line);
@ -90,9 +95,17 @@ static LineResult parse_line(Scanner* sc, int id) {
case TOK_CMD: case TOK_CMD:
line_add(&line, (Argument){.type = ARG_CMD, .as.str = tok.as.str}); line_add(&line, (Argument){.type = ARG_CMD, .as.str = tok.as.str});
break; 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: default:
line_free(&line); 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); block_free(&block);
return ERR("invalid syscall or command name", BlockResult); 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) { if(!sr.is_ok) {
block_free(&block); block_free(&block);
return ERR(sr.as.err, BlockResult); 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); block_add(&block, sr.as.ok);
if(brace_end) {
return OK(block, BlockResult);
}
} break; } break;
default: default:
block_free(&block); block_free(&block);
return ERR("unexpected token", BlockResult); return ERR("unexpected token in block", BlockResult);
} }
} }
return OK(block, BlockResult); return OK(block, BlockResult);

View file

@ -1,3 +1,14 @@
#pragma once #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);