summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorKai Stevenson <kai@kaistevenson.com>2023-02-16 14:32:56 -0800
committerKai Stevenson <kai@kaistevenson.com>2023-02-16 14:32:56 -0800
commit05540fc6038f452f5dc5f18412c918c33ad96027 (patch)
treebb9f572a1cc6f3a66cbf9835593f62db87481e0a /src
parent10a1f7e4ad1bc90436f58269a52cddfd0587be33 (diff)
changed project structure
Diffstat (limited to 'src')
-rw-r--r--src/eval.c134
-rw-r--r--src/eval.h9
-rw-r--r--src/hashtable.c39
-rw-r--r--src/hashtable.h19
-rw-r--r--src/parse.c143
-rw-r--r--src/parse.h40
-rw-r--r--src/precedent.c104
-rw-r--r--src/precedent.h7
-rw-r--r--src/scope.c20
-rw-r--r--src/scope.h13
10 files changed, 528 insertions, 0 deletions
diff --git a/src/eval.c b/src/eval.c
new file mode 100644
index 0000000..b1d4e0a
--- /dev/null
+++ b/src/eval.c
@@ -0,0 +1,134 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "eval.h"
+#include "hashtable.h"
+#include "parse.h"
+
+hashtable_t* ht;
+const int hashtableSize = 1000;
+const int trueVal = 1;
+sym_t trueSym = {LITERAL, INT, (void*)&trueVal, NULL, 0};
+const int falseVal = 0;
+sym_t falseSym = {LITERAL, INT, (void*)&falseVal, NULL, 0};
+
+void init_eval() {
+ ht = init_hashtable(hashtableSize);
+}
+/* TODO: read base hashtable from file to allow modularity
+pair value value? */
+void exec_all(sym_t** symbols, int count) {
+ for (int i = 0; i < count; i++) {
+ /* dispose of result */
+ eval(symbols[i]);
+ }
+}
+sym_t* eval(sym_t* symbol) {
+ if (symbol->province == LITERAL) {
+ return(symbol);
+ }
+ else if (symbol->province == KEY_REF) {
+ sym_t* ir = try_eval_internal(symbol);
+ if (ir != NULL) {
+ return ir;
+ }
+ else {
+ /* not an internal function, search hashtable for
+ next symbol */
+ /* TODO: scope */
+ pair_t* p = hashtable_get_pair(ht, (char*)symbol->name);
+ if (p != NULL) {
+ return eval((sym_t*)p->value);
+ }
+ else {
+ printf("no matching pair in hashtable\n");
+ return NULL;
+ }
+ }
+ }
+ else {
+ printf("no symbol province\n");
+ return NULL;
+ }
+}
+/* internal functions?
+this function has side-effects */
+sym_t* try_eval_internal(sym_t* symbol) {
+ unsigned int hash = get_hash((char*)symbol->name);
+ /* TODO: return last arg. that's the function return line */
+ if (hash == get_hash("root")) {
+ /* execute all args as roots */
+ exec_all(symbol->args, symbol->argCount);
+ return &trueSym;
+ }
+ else if (hash == get_hash("store")) {
+ /* TODO: scope */
+ /* first arg is value, remaining args are names */
+ /* TODO: recursion for multiple pairs */
+ char* k = symtos(eval(symbol->args[1]));
+ sym_t* v = symbol->args[0];
+ pair_t* p = init_pair(k, (void*)v);
+ hashtable_add_pair(ht, p);
+ return &trueSym;
+ }
+ /* TODO: release()/free() method? */
+ else if (hash == get_hash("eq")) {
+ litType_t type = NO_TYPE;
+ void* lastVal = NULL;
+ for (int i = 0; i < symbol->argCount; i++) {
+ sym_t* v = eval(symbol->args[i]);
+ /* initialize */
+ if (type == NO_TYPE) {
+ type = v->litType;
+ lastVal = v->name;
+ }
+ else {
+ /* interpret type */
+ if (type == INT) {
+ if (symtoi(v) != *((int*)lastVal)) {
+ return &falseSym;
+ }
+ }
+ if (type == STRING) {
+ if (strcmp(symtos(v), (char*)lastVal) != 0) {
+ return &falseSym;
+ }
+ }
+ /* TODO: float */
+ }
+ }
+ return &trueSym;
+ }
+ else if (hash == get_hash("if")) {
+ sym_t* v = eval(symbol->args[0]);
+ if (symtoi(v) != 0) {
+ exec_all(symbol->args + 1, symbol->argCount - 1);
+ return &trueSym;
+ }
+ return &falseSym;
+ }
+ else if (hash == get_hash("not")) {
+ sym_t* v = eval(symbol->args[0]);
+ int b = symtoi(v);
+ return b ? &falseSym : &trueSym;
+ }
+ else if (hash == get_hash("print")) {
+ sym_t* v = eval(symbol->args[0]);
+ switch (v->litType) {
+ case NO_TYPE:
+ break;
+ case INT:
+ printf("%d\n", symtoi(v));
+ break;
+ case FLOAT:
+ break;
+ case STRING:
+ printf("%s\n", symtos(v));
+ break;
+ }
+ return &trueSym;
+ }
+ else {
+ return NULL;
+ }
+}
diff --git a/src/eval.h b/src/eval.h
new file mode 100644
index 0000000..b91c621
--- /dev/null
+++ b/src/eval.h
@@ -0,0 +1,9 @@
+#include "parse.h"
+#ifndef EVAL
+#define EVAL
+void init_eval(void);
+void throw(char* error, int symbolLocale);
+void exec_all(sym_t** symbols, int count);
+sym_t* eval(sym_t* symbol);
+sym_t* try_eval_internal(sym_t* symbol);
+#endif
diff --git a/src/hashtable.c b/src/hashtable.c
new file mode 100644
index 0000000..7cb253c
--- /dev/null
+++ b/src/hashtable.c
@@ -0,0 +1,39 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include "hashtable.h"
+
+const unsigned int fnvPrime = 16777619;
+const unsigned int fnvOffset = 2166136261U;
+
+pair_t* init_pair(char* key, void* value) {
+ pair_t* p = malloc(sizeof(pair_t));
+ p->key = key;
+ p->value = value;
+ return p;
+}
+hashtable_t* init_hashtable(unsigned int capacity) {
+ hashtable_t* ht = malloc(sizeof(hashtable_t));
+ ht->pairs = calloc(capacity, sizeof(pair_t*));
+ ht->capacity = capacity;
+ return ht;
+}
+//FNV-1a hash
+unsigned int get_hash(char* str) {
+ unsigned int hash = fnvOffset;
+ while (*str != '\0') {
+ hash = (hash ^ *str) * fnvPrime;
+ str++;
+ }
+ return hash;
+}
+/* TODO: handle collision
+TODO: return 0 if something goes wrong */
+unsigned int hashtable_add_pair(hashtable_t* hashtable, pair_t* pair) {
+ unsigned int hash = get_hash(pair->key);
+ hashtable->pairs[hash % hashtable->capacity] = pair;
+ return hash;
+}
+pair_t* hashtable_get_pair(hashtable_t* hashtable, char* key) {
+ unsigned int hash = get_hash(key);
+ return hashtable->pairs[hash % hashtable->capacity];
+}
diff --git a/src/hashtable.h b/src/hashtable.h
new file mode 100644
index 0000000..43ab23a
--- /dev/null
+++ b/src/hashtable.h
@@ -0,0 +1,19 @@
+#ifndef HASHTABLE
+#define HASHTABLE
+
+typedef struct HashPair {
+ char* key;
+ void* value;
+} pair_t;
+pair_t* init_pair(char* key, void* value);
+typedef struct HashTable {
+ pair_t** pairs;
+ int capacity;
+ int itemCount;
+} hashtable_t;
+hashtable_t* init_hashtable(unsigned int capacity);
+unsigned int get_hash(char* str);
+unsigned int hashtable_add_pair(hashtable_t* hashtable, pair_t* pair);
+pair_t* hashtable_get_pair(hashtable_t* hashtable, char* key);
+
+#endif
diff --git a/src/parse.c b/src/parse.c
new file mode 100644
index 0000000..27ee417
--- /dev/null
+++ b/src/parse.c
@@ -0,0 +1,143 @@
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include "parse.h"
+#include "hashtable.h"
+
+int symtoi(sym_t* symbol) {
+ return *((int*)symbol->name);
+}
+float symtof(sym_t* symbol) {
+ return *((float*)symbol->name);
+}
+char* symtos(sym_t* symbol) {
+ return (char*)symbol->name;
+}
+
+void symbol_add_arg(sym_t* symbol, sym_t* arg) {
+ symbol->argCount += 1;
+ if (symbol->argCount == 1) {
+ symbol->args = malloc(sizeof(sym_t*));
+ }
+ else {
+ symbol->args = realloc(symbol->args, symbol->argCount * (sizeof(sym_t*)));
+ }
+ (symbol->args)[symbol->argCount - 1] = arg;
+}
+void symset_add_symbol(symset_t* symbolSet, sym_t* symbol) {
+ symbolSet->count += 1;
+ if (symbolSet->count == 1) {
+ symbolSet->symbols = malloc(sizeof(sym_t*));
+ }
+ else {
+ symbolSet->symbols = realloc(symbolSet->symbols, symbolSet->count * (sizeof(sym_t*)));
+ }
+ (symbolSet->symbols)[symbolSet->count - 1] = symbol;
+}
+symset_t* parse_symbols(char** str) {
+ symset_t* symbolSet = malloc(sizeof(symset_t));
+ symbolSet->count = 0;
+ while (1) {
+ /* clear whitespace */
+ while (!isalnum(**str) && **str != '(' && **str != ')' && **str != '\0') {
+ (*str)++;
+ }
+ if (**str == ')') {
+ (*str)++;
+ return symbolSet;
+ }
+ else if (**str == '\0') {
+ return symbolSet;
+ }
+ char* symbolName = calloc(30, sizeof(char));
+ int i = 0;
+ while (1) {
+ symbolName[i] = **str;
+ if (**str == ',' || **str == ';') {
+ (*str)++;
+ sym_t* symbol = malloc(sizeof(sym_t));
+ symbol->province = LITERAL;
+ symbol->argCount = 0;
+ symbolName[i] = '\0';
+ /* TODO: float */
+ /* string */
+ if (isalpha(symbolName[0])) {
+ //symbolName = realloc(symbolName, i * sizeof(char));
+ symbol->name = symbolName;
+ symbol->litType = STRING;
+ }
+ /* int */
+ else {
+ int v = atoi(symbolName);
+ int* vp = malloc(sizeof(int));
+ *vp = v;
+ symbol->name = (void*)vp;
+ free(symbolName);
+ symbol->litType = INT;
+ }
+ symset_add_symbol(symbolSet, symbol);
+ break;
+ }
+ else if (**str == '(') {
+ (*str)++;
+ symbolName[i] = '\0';
+ sym_t* symbol = malloc(sizeof(sym_t));
+ symbol->province = KEY_REF;
+ symbol->argCount = 0;
+ if (i > 1) {
+ symbol->name = symbolName;
+ }
+ else {
+ symbol->name = "root";
+ }
+ symset_t* symbols = parse_symbols(str);
+ for (int s = 0; s < symbols->count; s++) {
+ symbol_add_arg(symbol, symbols->symbols[s]);
+ }
+ symset_add_symbol(symbolSet, symbol);
+ break;
+ }
+ i++;
+ (*str)++;
+ }
+ }
+}
+sym_t* parse_symbol_final(char* str) {
+ char** strp = malloc(sizeof(char**));
+ *strp = str;
+ symset_t* finalSymset = parse_symbols(strp);
+ sym_t* root = malloc(sizeof(sym_t));
+ root->name = "root";
+ root->province = KEY_REF;
+ root->argCount = 0;
+ for (int s = 0; s < finalSymset->count; s++) {
+ symbol_add_arg(root, finalSymset->symbols[s]);
+ }
+ return root;
+}
+void write_symbol_visual(sym_t* symbol, int offset) {
+ for (int o = 0; o < offset; o++) {
+ printf(" ");
+ }
+ if (symbol->province == KEY_REF) {
+ printf("%s (%s)\n", (char*)(symbol->name), "KEY");
+ }
+ else if (symbol->province == LITERAL) {
+ if (symbol->litType == INT) {
+ printf("%d (%s)\n", *(int*)(symbol->name), "LITERAL");
+ }
+ else if (symbol->litType == FLOAT) {
+ printf("%f (%s)\n", *(float*)(symbol->name), "LITERAL");
+ }
+ else if (symbol->litType == STRING) {
+ printf("%s (%s)\n", (char*)(symbol->name), "LITERAL");
+ }
+ }
+ else {
+ printf("symbol parse error\n");
+ }
+ for (int i = 0; i < symbol->argCount; i++) {
+ write_symbol_visual(symbol->args[i], offset + 4);
+ }
+}
diff --git a/src/parse.h b/src/parse.h
new file mode 100644
index 0000000..27ce444
--- /dev/null
+++ b/src/parse.h
@@ -0,0 +1,40 @@
+#ifndef PARSE
+#define PARSE
+
+/* keyword hashes */
+#define VAR_H 2317739966u
+#define DEFINE_H 1788839250
+
+typedef enum SymbolProvince {
+ NO_PROVINCE,
+ KEY_REF,
+ LITERAL
+} prov_t;
+typedef enum LiteralType {
+ NO_TYPE,
+ INT,
+ FLOAT,
+ STRING
+} litType_t;
+typedef struct Symbol {
+ prov_t province;
+ litType_t litType;
+ void* name;
+ struct Symbol** args;
+ int argCount;
+} sym_t;
+typedef struct SymbolSet {
+ sym_t** symbols;
+ int count;
+} symset_t;
+
+int symtoi(sym_t* symbol);
+float symtof(sym_t* symbol);
+char* symtos(sym_t* symbol);
+void symbol_add_arg(sym_t* symbol, sym_t* arg);
+void symset_add_symbol(symset_t* symbolSet, sym_t* symbol);
+symset_t* parse_symbols(char** str);
+sym_t* parse_symbol_final(char* str);
+void write_symbol_visual(sym_t* symbol, int offset);
+
+#endif
diff --git a/src/precedent.c b/src/precedent.c
new file mode 100644
index 0000000..81555b9
--- /dev/null
+++ b/src/precedent.c
@@ -0,0 +1,104 @@
+#include <stdlib.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <libgen.h>
+#include "precedent.h"
+#include "hashtable.h"
+#include "parse.h"
+#include "eval.h"
+
+int main(int argc, char** argv) {
+ int visual = 0;
+ if (argc > 1) {
+ for (int i = 1; i < argc; i++) {
+ if (strcmp("--version", argv[i]) == 0) {
+ printf("precedent, version 0.1\n");
+ printf("Copyright 2023 Kai Stevenson\n");
+ printf("All rights reserved. No part of the software may be sold\nor used in derivative works without explicit consent\n");
+ return 0;
+ }
+ else if (strcmp("--help", argv[i]) == 0) {
+ printf("usage: precedent <file> [--visualize] [--version] [--help]\n");
+ return 0;
+ }
+ else if (strcmp("--visualize", argv[i]) == 0) {
+ visual = 1;
+ }
+ }
+ }
+ else {
+ printf("usage: precedent <file> [--visualize] [--version] [--help]\n");
+ return 0;
+ }
+ /* dirname may modify path, copy first */
+ char* path = argv[1];
+ char* pathCpy = malloc((strlen(path) + 1) * sizeof(char));
+ strcpy(pathCpy, path);
+ char* dirName = dirname(pathCpy);
+ char* baseDir = malloc((strlen(dirName) + 2) * sizeof(char));
+ strcpy(baseDir, dirName);
+ strcat(baseDir, "/");
+ sym_t* finalSymbol = parse_symbol_final(read_file(path));
+ if (visual) {
+ printf("--BEGIN VISUALIZATION--\n");
+ write_symbol_visual(finalSymbol, 0);
+ printf("--END VISUALIZATION--\n");
+ }
+ init_eval();
+ eval(finalSymbol);
+ free(dirName);
+ free(pathCpy);
+ free(baseDir);
+ return 0;
+}
+char* read_file(char* path) {
+ FILE* file = fopen(path, "r");
+ fseek(file, 0, SEEK_END);
+ unsigned long int length = ftell(file);
+ fseek(file, 0, SEEK_SET);
+ char* out = malloc((length + 1) * sizeof(char));
+ fread(out, length, 1, file);
+ fclose(file);
+ out[length] = '\0';
+ return out;
+}
+/* wtf */
+char* preprocess(char* str, char* baseDir) {
+ char* final = str;
+ while (1) {
+ int i = 0;
+ while (str[i] != '@') {
+ i++;
+ if (str[i] == '\0') {
+ printf("%s\n", final);
+ return final;
+ }
+ }
+ i++;
+ char* includeName = malloc(20 * sizeof(char));
+ int f = 0;
+ while (isalnum(str[i]) || str[i] == '/' || str[i] == '.' || str[i] == '_') {
+ includeName[f] = str[i];
+ str[i] = ' ';
+ i++;
+ f++;
+ }
+ final = str;
+ includeName[f] = '\0';
+ char* path = malloc((strlen(baseDir) + strlen(includeName) + 1) * sizeof(char));
+ strcpy(path, baseDir);
+ strcat(path, includeName);
+ char* toInclude = preprocess(read_file(path), baseDir);
+ char* newStr = malloc((strlen(toInclude) + strlen(final) + 1) * sizeof(char));
+ strcpy(newStr, toInclude);
+ strcat(newStr, final);
+ /*
+ free(includeName);
+ free(path);
+ free(str);
+ free(toInclude);
+ */
+ final = newStr;
+ }
+}
diff --git a/src/precedent.h b/src/precedent.h
new file mode 100644
index 0000000..8a5110e
--- /dev/null
+++ b/src/precedent.h
@@ -0,0 +1,7 @@
+#include "parse.h"
+#include "hashtable.h"
+#ifndef PRECEDENT
+char* read_file(char* path);
+char* preprocess(char* str, char* baseDir);
+#define PRECEDENT
+#endif
diff --git a/src/scope.c b/src/scope.c
new file mode 100644
index 0000000..afb9d5c
--- /dev/null
+++ b/src/scope.c
@@ -0,0 +1,20 @@
+#include <stdlib.h>
+#include "scope.h"
+#include "hashtable.h"
+
+scope_t* init_scope(scope_t parent) {
+ scope_t* scope = malloc(sizeof(scope_t));
+ scope->hashtable = init_hashtable(1000);
+ *(scope->parent) = parent;
+ return scope;
+}
+pair_t* get_pair_global(scope_t* scope, char* key) {
+ while (scope->parent != NULL) {
+ pair_t* pair = hashtable_get_pair(scope->hashtable, key);
+ if (pair != NULL) {
+ return pair;
+ }
+ scope = scope->parent;
+ }
+ return NULL;
+}
diff --git a/src/scope.h b/src/scope.h
new file mode 100644
index 0000000..d1cda0d
--- /dev/null
+++ b/src/scope.h
@@ -0,0 +1,13 @@
+#include "hashtable.h"
+#ifndef SCOPE
+#define SCOPE
+
+typedef struct Scope {
+ hashtable_t* hashtable;
+ struct Scope* parent;
+} scope_t;
+scope_t* init_scope(scope_t parent);
+pair_t* get_pair_global(scope_t* scope, char* key);
+pair_t* get_pair_local(scope_t* scope, char* key);
+
+#endif