Compare commits

..

No commits in common. "master" and "v1.0-alpha" have entirely different histories.

39 changed files with 200 additions and 1541 deletions

2
.gitignore vendored
View File

@ -4,5 +4,5 @@ tags
*.out
.cache
build/*
vgcore.*
compile_commands.json
vgcore.*

View File

@ -1,4 +1,40 @@
include config.mk
NAME = scl
TARGET = $(NAME).out
SRC_DIR = src
INC_DIR = $(SRC_DIR)/include
BUILD_DIR = build
OBJ_DIR = $(BUILD_DIR)/obj
GRAM_DIR = $(BUILD_DIR)/grammars
TEST_DIR = test
TEST_BUILD_DIR = $(BUILD_DIR)/test
TEST_OBJ_DIR = $(TEST_BUILD_DIR)/obj
CC = clang
LINK = clang
CFLAGS = -Wall -DDBG -ggdb
LDFLAGS = -lm
BATS = bats
BISON = bison
PRINT = echo -e
SRC_FILES = $(wildcard $(SRC_DIR)/*.c)
OBJ_FILES = $(patsubst $(SRC_DIR)/%.c, $(OBJ_DIR)/%.o, $(SRC_FILES))
OBJ_FILES_NOMAIN = $(filter-out $(OBJ_DIR)/main.o, $(OBJ_FILES)) # Object files without main.c.
GRAM_FILES = $(GRAM_DIR)/grammar.tab.c $(GRAM_DIR)/grammar.tab.h
UNITY_DIR = $(TEST_DIR)/Unity
UNITY_C = $(UNITY_DIR)/src/unity.c
UNITY_H = $(UNITY_DIR)/src/unity.h
UNITY_OBJ = $(TEST_BUILD_DIR)/unity.o
TEST_SRC_FILES = $(wildcard $(TEST_DIR)/*.c)
TEST_OBJ_FILES = $(patsubst $(TEST_DIR)/%.c, $(TEST_OBJ_DIR)/%.o, $(TEST_SRC_FILES))
TEST_BIN_FILES = $(patsubst $(TEST_DIR)/%.c, $(TEST_BUILD_DIR)/%.out, $(TEST_SRC_FILES))
TEST_VAL_DIR = $(TEST_DIR)/validation
RESETCOLOR = \033[0m
WHITE = $(RESETCOLOR)\033[37m
WHITE_BOLD = $(RESETCOLOR)\033[37;1m
all: $(TARGET)
@ -45,33 +81,25 @@ $(UNITY_OBJ): $(UNITY_C) $(UNITY_H)
# Compile test object.
$(TEST_OBJ_DIR)/test_%.o: $(TEST_DIR)/test_%.c
@ mkdir -p $(TEST_OBJ_DIR)
@ $(PRINT) "$(WHITE_BOLD)Compiling test object $(WHITE)$@$(WHITE_BOLD)...$(RESETCOLOR)"
$(CC) $(CFLAGS) -c $< -o $@
# Link final test binary.
$(TEST_BUILD_DIR)/test_%.out: $(TEST_OBJ_DIR)/test_%.o $(OBJ_DIR)/grammar.o $(OBJ_FILES_NOMAIN) $(UNITY_OBJ)
@ mkdir -p $(TEST_BUILD_DIR)
$(TEST_BUILD_DIR)/test_%.out: $(TEST_OBJ_DIR)/test_%.o $(OBJ_DIR)/%.o $(UNITY_OBJ)
@ $(PRINT) "$(WHITE_BOLD)Linking test binary $(WHITE)$@$(WHITE_BOLD)...$(RESETCOLOR)"
$(LINK) -o $@ $? $(LDFLAGS)
# Run the test files.
test: $(TARGET) $(TEST_BIN_FILES)
@ $(PRINT) "$(WHITE_BOLD)Running unit tests...$(RESETCOLOR)"
for test in $(TEST_BIN_FILES); do ./$${test} || echo -e "$(RED_BOLD) BAD EXIT ON $${test} $(RESETCOLOR)"; done
for test in $(TEST_BIN_FILES); do ./$${test}; done
@ $(PRINT) "$(WHITE_BOLD)Running validation tests...$(RESETCOLOR)"
$(BATS) $(TEST_VAL_DIR)
# Clean out objects, binaries, and built artifacts.
clean:
@ $(PRINT) "$(WHITE_BOLD)Cleaning up...$(RESETCOLOR)"
rm -rf $(OBJ_DIR)/*.o $(TEST_OBJ_DIR)/*.o $(TEST_BUILD_DIR)/test.out $(TARGET) $(GRAM_DIR)/* $(UNITY_OBJ)
# Get LOC.
lines:
@ wc -l $(SRC_FILES) $(INC_FILES) $(GRAM_SRC)
.PHONY: all clean test nocolor release run lines
# Run this intermediary even though make thinks it's useless.
.PHONY: all clean test nocolor release run
.PRECIOUS: $(TEST_OBJ_FILES)

View File

@ -1,36 +1,24 @@
# SCL: Simple CAS Language
Version v1.0-beta
SCL aims to be a human-friendly Computer Algebra System (CAS) inspired by
[maxima](https://maxima.sourceforge.io/) that feels like writing on paper. In
its current state, SCL can be used as a basic 4-function calculator with order
of operations and local variables. The codebase is about 1,400 lines of C,
including a parser, interpreter, and runtime. It uses a linked environment
scoping model.
Version v1.0-alpha
## Usage
To download and run:
```bash
git clone https://git.signorovitch.org/jacob/scl -b stable && cd scl
make release
./scl.out
```
### For Development
```bash
git clone git@signorovitch.org:jacob/scl --recurse-submodules && cd scl
make all test
./scl.out
git clone https://git.signorovitch.org/jacob/scl && cd scl
make release # Build.
./scl # Run.
```
If you wish to run tests, make sure to run `git clone --recurse-submodules` to
include the [Unity](https://github.com/ThrowTheSwitch/Unity) test framework.
## Syntax
## Current State
See [STATUS.md](STATUS.md). Currently, one is able to use `scl` as a basic,
interactive, four-function calculator.
## Syntax (Planned)
As one would expect, you can evaluate simple infix expressions:
@ -39,30 +27,12 @@ As one would expect, you can evaluate simple infix expressions:
= 2
```
You can also define your own functions and variables:
You can also define your own functions:
```scl
> f(x) = 2x
> n = 3
> f(n)
= 6
```
As SCL uses a linked environment model for scope, arguments are passed by
reference by default. If you would like to pass by value (i.e., a copy) you may
use the syntax:
```scl
> f(x) = x = 1
> n = 4
> f($n) # Pass a copy of n.
= 1
> n
> f(2)
= 4
> f(n) # Pass a reference to n.
= 1
> n
> 1
```
Symbolic algebra is done in the following manner:
@ -77,13 +47,9 @@ SCL will dynamically decide on types, but you can state them explicitly as
well:
```scl
> f(x: int): int = 2x
> f(3)
= 6
> f(3.1)
! Traceback:
! In call to `f(x: int): int`:
! TypeError (58): Argument `x` must be of type `int`.
> f(x:int) = 2x
> f(2.2)
! f(x:int): x must be of type int.
```
Variables can be defined, with several attributes:

View File

@ -13,11 +13,11 @@
- [x] Parse function application
- [x] Parse order of operations with parenthesis
- [ ] Parse variable invocation
- [x] Parse variable definition
- [ ] Parse variable definition
- [ ] Parse types
- [ ] Parse function definition
- [ ] Parse lists/arrays/vectors
- [x] Parse blocks
- [ ] Parse blocks
- [ ] Parse control flow
- [ ] Parse `if` statements
- [ ] Parse `loop`s

View File

@ -1,7 +0,0 @@
0. Create file to describe properties of terminology used; param, arg, var, &c.
1. Differenciate parameters and arguments -- params for function definitions,
arguments for function calls
2. Add scope field to all ASTs, and new scope layer for those that need it.
Change editor to GNU Readline.
Make variables persist through lines in the editor.

View File

@ -1,40 +0,0 @@
NAME = scl
TARGET = $(NAME).out
SRC_DIR = src
INC_DIR = $(SRC_DIR)/include
BUILD_DIR = build
OBJ_DIR = $(BUILD_DIR)/obj
GRAM_DIR = $(BUILD_DIR)/grammars
TEST_DIR = test
TEST_BUILD_DIR = $(BUILD_DIR)/test
TEST_OBJ_DIR = $(TEST_BUILD_DIR)/obj
CC = clang -std=c23
LINK = clang
CFLAGS = -Wall -DDBG -ggdb -fsanitize=leak
LDFLAGS = -lm
BATS = bats
BISON = bison
PRINT = echo -e
SRC_FILES = $(wildcard $(SRC_DIR)/*.c)
INC_FILES = $(wildcard $(INC_DIR)/*.h)
OBJ_FILES = $(patsubst $(SRC_DIR)/%.c, $(OBJ_DIR)/%.o, $(SRC_FILES))
OBJ_FILES_NOMAIN = $(filter-out $(OBJ_DIR)/main.o, $(OBJ_FILES)) # Object files without main.c.
GRAM_SRC = $(SRC_DIR)/grammar.y
GRAM_FILES = $(GRAM_DIR)/grammar.tab.c $(GRAM_DIR)/grammar.tab.h
UNITY_DIR = $(TEST_DIR)/Unity
UNITY_C = $(UNITY_DIR)/src/unity.c
UNITY_H = $(UNITY_DIR)/src/unity.h
UNITY_OBJ = $(TEST_BUILD_DIR)/unity.o
TEST_SRC_FILES = $(wildcard $(TEST_DIR)/*.c)
TEST_OBJ_FILES = $(patsubst $(TEST_DIR)/%.c, $(TEST_OBJ_DIR)/%.o, $(TEST_SRC_FILES))
TEST_BIN_FILES = $(patsubst $(TEST_DIR)/%.c, $(TEST_BUILD_DIR)/%.out, $(TEST_SRC_FILES))
TEST_VAL_DIR = $(TEST_DIR)/val
RESETCOLOR = \033[0m
WHITE = $(RESETCOLOR)\033[37m
WHITE_BOLD = $(RESETCOLOR)\033[37;1m
RED_BOLD = $(RESETCOLOR)\033[31;1m

View File

@ -1,3 +0,0 @@
f(n) 2 * n
f(5)

View File

@ -1,48 +0,0 @@
# Vectors
# - Must have fixed size and type
# Lists
# - Variable size, variable type
# Define a variable of type int:
n: int = 3
# Define v as a 3-dimensional vector of integers:
v: int<3> = <4, 5, 6>
# Define l as list of length 3:
l: [3] = [1, 2, "These can be any type at all"]
# This would also work, as length values for lists are optional (and mutable),
# unlike vectors:
l: [] = [1, 2, "whatever"]
# This is also the same, being more explicit about the any type:
l: any[] = [1, 2, "whatever"]
# This must be a list of integers, however:
l: int[] = [1, 2, 3]
# Define a list of either integers or strings:
l: union(int, str)[] = ["hello", 4, "world"]
# union() is a complex type generator; "returns" a union of the int and str types.
# Use vectors wherever possible, as they are much faster than lists.
# Matrix
# - Must have a fixed size and type (just like vectors)
m: int<2, 2> = <<0, 1>, <2, 3>>
# Can also be written as
m: int<2, 2> = <<0, 1, 2, 3>>
# When using this syntax, will fill remaining space with default value of type
# to make data rectangular. E.g., to fill a 2 x 2 matrix with 0:
m: int<2, 2> = <<>>
# To define custom data types, e.g. structs:
typedef(Vec2, struct( a: int, b: int ))
vector_two: Vec2 = Vec2(2, 3)
typedef(IntOrStr, union(str, int))
one: IntOrStr = 1
alsoOne: IntOrStr = "one"
# To define custom data types with type arguments:
typedef(Vec1Of(T), T<1>)
hello: Vec1Of(str) = <"Hello">

256
src/ast.c
View File

@ -1,10 +1,7 @@
#include <inttypes.h>
#include <stdio.h>
#include "include/ast.h"
#include "include/dstr.h"
#include "include/gc.h"
#include "include/scope.h"
#include "include/util.h"
extern AST* root;
@ -12,37 +9,14 @@ extern AST* root;
static char* asttype_names[] = {
[AST_TYPE_CALL] = "FUNC CALL",
[AST_TYPE_NUM] = "NUMBER",
[AST_TYPE_VREF] = "VAR REFERENCE",
[AST_TYPE_VDEF] = "VAR DEFINITION",
[AST_TYPE_BLOCK] = "BLOCK",
[AST_TYPE_EXC] = "EXCEPTION",
[AST_TYPE_FDEF] = "FUNCTION DEFINITION",
[AST_TYPE_ARG] = "DEFINITION ARGUMENT"
[AST_TYPE_VREF] = "VAR REFERENCE"
};
AST* ast_init(ASTType type, void* data) {
AST* ast = gc_alloc(sizeof(AST), GC_TYPE_AST);
ast->type = type;
ast->data = data;
ast->scope = NULL;
if (ast->type > AST_TYPE_MAX) {
log_dbgf(
"Attempted to create invalid AST (%i > %i) to GC: ast:%p",
ast->type, AST_TYPE_MAX, ast
);
}
return ast;
}
AST* ast_init_scope(ASTType type, void* data, Scope* scope) {
AST* ast = malloc(sizeof(AST));
ast->type = type;
ast->data = data;
ast->scope = scope;
return ast;
}
@ -51,36 +25,8 @@ void ast_destroy(AST* ast) {
if (!ast) return;
switch (ast->type) {
case AST_TYPE_NUM: ast_num_data_destroy(ast->data); break;
case AST_TYPE_CALL: ast_call_data_destroy(ast->data); break;
case AST_TYPE_VREF: ast_vref_data_destroy(ast->data); break;
case AST_TYPE_VDEF: ast_vdef_data_destroy(ast->data); break;
case AST_TYPE_BLOCK: ast_block_data_destroy(ast->data); break;
case AST_TYPE_FDEF: ast_fdef_data_destroy(ast->data); break;
case AST_TYPE_ARG: ast_arg_data_destroy(ast->data); break;
default:
log_dbgf("Unknown ast type %d (max: %d)", ast->type, AST_TYPE_MAX);
}
// If there're no more `AST`s linked to the scope, free.
if (ast->scope && !--ast->scope->uses) scope_destroy_psv(ast->scope);
free(ast);
}
void ast_destroy_psv(AST* ast) {
if (!ast) return;
switch (ast->type) {
case AST_TYPE_NUM: ast_num_data_destroy(ast->data); break;
case AST_TYPE_CALL: ast_call_data_destroy_psv(ast->data); break;
case AST_TYPE_VREF: ast_vref_data_destroy(ast->data); break;
case AST_TYPE_VDEF: ast_vdef_data_destroy_psv(ast->data); break;
case AST_TYPE_BLOCK: ast_block_data_destroy_psv(ast->data); break;
case AST_TYPE_FDEF: ast_fdef_data_destroy_psv(ast->data); break;
case AST_TYPE_ARG: ast_arg_data_destroy(ast->data); break;
case AST_TYPE_BIF: ast_bif_data_destroy(ast->data); break;
case AST_TYPE_EXC: ast_exc_data_destroy(ast->data); break;
case AST_TYPE_NUM: ast_num_data_destroy(ast->data); break;
case AST_TYPE_CALL: ast_call_data_destroy(ast->data); break;
default:
log_dbgf("Unknown ast type %d (max: %d)", ast->type, AST_TYPE_MAX);
}
@ -88,30 +34,21 @@ void ast_destroy_psv(AST* ast) {
free(ast);
}
void ast_print(AST* ast) {
if (!ast) return;
ast_print_i(ast, 0);
}
void ast_print(AST* ast) { ast_print_i(ast, 0); }
void ast_print_i(AST* ast, int i) {
INDENT_BEGIN(i);
INDENT_TITLE("AST", ast);
INDENT_FIELD("type", "%s", asttype_names[ast->type]);
INDENT_FIELD("scope", "%p", ast->scope);
INDENT_FIELD_EXT_NONL_START("data");
switch (ast->type) {
case AST_TYPE_NUM:
printf("%s %lf\n", INDENT_spacing->buf, *(ASTNumData*)ast->data);
break;
case AST_TYPE_CALL: ast_call_print(ast->data, i + 2); break;
case AST_TYPE_EXC: ast_exc_print(ast->data, i + 2); break;
case AST_TYPE_VREF: ast_vref_print(ast->data, i + 2); break;
case AST_TYPE_VDEF: ast_vdef_print(ast->data, i + 2); break;
case AST_TYPE_BLOCK: ast_block_print(ast->data, i + 2); break;
case AST_TYPE_FDEF: ast_fdef_print(ast->data, i + 2); break;
case AST_TYPE_ARG: ast_arg_print(ast->data, i + 2); break;
default: exit(1);
case AST_TYPE_CALL: ast_call_print(ast->data, i + 2); break;
case AST_TYPE_VREF: ast_vref_print(ast->data, i + 2); break;
default: exit(1);
}
INDENT_FIELD_NONL_END;
INDENT_END;
@ -135,39 +72,6 @@ void ast_num_print(ASTNumData* data, int i) {
INDENT_END;
}
ASTExcData* ast_exc_data_init(const char* msg, AST* trace) {
ASTExcData* data = malloc(sizeof(ASTExcData));
data->msg = msg;
data->trace = trace;
return data;
}
void ast_exc_data_destroy(ASTExcData* exc) {
// `msg` is static, and `trace` will get freed in GC.
free(exc);
}
void ast_exc_print(ASTExcData* data, int i) {
INDENT_BEGIN(i);
INDENT_TITLE("ASTExcData", data);
INDENT_FIELD("msg", "\"%s\"", data->msg);
if (data->trace == NULL) {
INDENT_FIELD("trace", "%p", NULL)
} else {
INDENT_FIELD_EXT_NONL_START("trace");
ast_print_i(data->trace, i + 1);
INDENT_FIELD_NONL_END;
}
INDENT_END;
}
ASTBIFData* ast_bif_data_init(AST* fn(size_t, AST**, Scope*)) {
return (ASTBIFData*)fn;
}
void ast_bif_data_destroy(ASTBIFData* bif) { return; }
ASTCallData* ast_call_data_init(char* to, size_t argc, AST** argv) {
talloc(ASTCallData, call);
@ -188,15 +92,6 @@ void ast_call_data_destroy(ASTCallData* call) {
free(call);
}
void ast_call_data_destroy_psv(ASTCallData* call) {
if (!call) return;
free(call->to);
call->to = NULL;
free(call->argv);
call->argv = NULL;
free(call);
}
void ast_call_print(ASTCallData* data, int i) {
INDENT_BEGIN(i);
@ -208,50 +103,9 @@ void ast_call_print(ASTCallData* data, int i) {
INDENT_END;
}
ASTVDefData* ast_vdef_data_init(char* name, AST* val) {
talloc(ASTVDefData, vdef);
ASTVrefData* ast_vref_data_init(char* to) {}
vdef->name = name;
vdef->val = val;
return vdef;
}
void ast_vdef_data_destroy(ASTVDefData* vdef) {
ast_destroy(vdef->val);
free(vdef->name);
free(vdef);
}
void ast_vdef_data_destroy_psv(ASTVDefData* vdef) {
free(vdef->name);
free(vdef);
}
void ast_vdef_print(ASTVDefData* vdef, int depth) {
INDENT_BEGIN(depth);
INDENT_TITLE("ASTVDefData", vdef);
INDENT_FIELD("name", "%s", vdef->name);
INDENT_FIELD_EXT_NONL_START("val");
ast_print_i(vdef->val, depth + 2); // 2 because already indented.
INDENT_FIELD_NONL_END;
INDENT_END;
}
ASTVrefData* ast_vref_data_init(char* to) {
talloc(ASTVrefData, vref);
vref->to = to;
return vref;
}
void ast_vref_data_destroy(ASTVrefData* vref) {
free(vref->to);
free(vref);
}
void ast_vref_data_destroy(ASTVrefData* vref) {}
void ast_vref_print(ASTVrefData* data, int i) {
INDENT_BEGIN(i);
@ -261,95 +115,3 @@ void ast_vref_print(ASTVrefData* data, int i) {
INDENT_END;
}
ASTBlockData* ast_block_data_init(AST** inside, size_t ln) {
ASTBlockData* block = malloc(sizeof(ASTBlockData));
block->inside = inside;
block->ln = ln;
return block;
}
void ast_block_data_destroy(ASTBlockData* block) {
for (size_t i = 0; i < block->ln; i++) { ast_destroy(block->inside[i]); }
free(block->inside);
free(block);
}
void ast_block_data_destroy_psv(ASTBlockData* block) {
free(block->inside);
free(block);
}
void ast_block_print(ASTBlockData* data, int depth) {
INDENT_BEGIN(depth);
INDENT_TITLE("ASTBlockData", data);
INDENT_FIELD("ln", "%ld", data->ln);
INDENT_FIELD_LIST("inside", data->inside, data->ln, ast_print_i);
INDENT_END;
}
ASTFDefData*
ast_fdef_data_init(char* name, size_t argc, AST** argv, AST* body) {
ASTFDefData* fdef = malloc(sizeof(ASTFDefData));
fdef->name = name;
fdef->argc = argc;
fdef->argv = argv;
fdef->body = body;
return fdef;
}
void ast_fdef_data_destroy(ASTFDefData* fdef) {
free(fdef->name);
for (int i = 0; i < fdef->argc; ast_destroy(fdef->argv[i++]));
ast_destroy(fdef->body);
}
void ast_fdef_data_destroy_psv(ASTFDefData* fdef) {
free(fdef->name);
free(fdef->argv);
free(fdef);
}
void ast_fdef_print(ASTFDefData* fdef, int i) {
INDENT_BEGIN(i)
INDENT_TITLE("ASTFDefData", fdef);
INDENT_FIELD("name", "%s", fdef->name);
INDENT_FIELD("argc", "%ld", fdef->argc);
INDENT_FIELD_LIST("argv", fdef->argv, fdef->argc, ast_print_i);
INDENT_FIELD_EXT_NONL_START("body");
ast_print_i(fdef->body, i + 2);
INDENT_FIELD_NONL_END;
INDENT_END;
}
ASTArgData* ast_arg_data_init(char* name) {
ASTArgData* arg = malloc(sizeof(ASTArgData));
arg->name = name;
return arg;
}
void ast_arg_data_destroy(ASTArgData* arg) { free(arg->name); }
void ast_arg_print(ASTArgData* arg, int i) {
INDENT_BEGIN(i);
INDENT_TITLE("ASTArgData", arg);
INDENT_FIELD("name", "%s", arg->name);
INDENT_END;
}
AST* ast_find(Scope* scope, char* name) {
while (scope) {
AST* gotten = htab_get(scope->here, name);
if (gotten) return gotten;
else scope = scope->inherit;
}
return NULL;
}

View File

@ -1,133 +0,0 @@
#include "include/builtin.h"
#include "include/ast.h"
#include "include/exec.h"
#include "include/util.h"
#include <stdarg.h>
#include <stdio.h>
AST* builtin_sum(size_t argc, AST** argv, Scope* parent) {
ASTNumData total = 0;
for (int i = 0; i < argc; i++) {
AST* arg = exec_exp(argv[i], parent);
if (arg->type == AST_TYPE_EXC)
return ast_init(
AST_TYPE_EXC,
ast_exc_data_init("`sum` encountered an exception.", arg)
);
if (arg->type != AST_TYPE_NUM)
return ast_init(
AST_TYPE_EXC,
ast_exc_data_init("Sum can't sum some non-num arguments.", NULL)
);
total += *(ASTNumData*)arg->data;
}
return ast_init(AST_TYPE_NUM, ast_num_data_init(total));
}
AST* builtin_sub(size_t argc, AST** argv, Scope* parent) {
log_dbg("Got here");
AST* first = exec_exp(*argv, parent);
if (first->type == AST_TYPE_EXC)
return ast_init(
AST_TYPE_EXC,
ast_exc_data_init("`sub` encountered an exception.", first)
);
if (first->type != AST_TYPE_NUM)
return ast_init(
AST_TYPE_EXC,
ast_exc_data_init("Can't subtract non-num arguments.", NULL)
);
ASTNumData total = *(ASTNumData*)first->data;
for (int i = 1; i < argc; i++) {
AST* arg = exec_exp(argv[i], parent);
if (arg->type == AST_TYPE_EXC)
return ast_init(
AST_TYPE_EXC,
ast_exc_data_init("`sub` encountered an exception.", arg)
);
if (arg->type != AST_TYPE_NUM)
return ast_init(
AST_TYPE_EXC,
ast_exc_data_init("Can't subtract non-num arguments.", NULL)
);
total -= *(ASTNumData*)arg->data;
}
return ast_init(AST_TYPE_NUM, ast_num_data_init(total));
}
AST* builtin_mul(size_t argc, AST** argv, Scope* parent) {
log_dbg("Got here");
AST* first = exec_exp(*argv, parent);
if (first->type == AST_TYPE_EXC)
return ast_init(
AST_TYPE_EXC,
ast_exc_data_init("`mul` encountered an expection.", first)
);
if (first->type != AST_TYPE_NUM)
return ast_init(
AST_TYPE_EXC,
ast_exc_data_init("Can't multiply non-num arguments.", NULL)
);
ASTNumData total = *(ASTNumData*)first->data;
for (int i = 1; i < argc; i++) {
AST* arg = exec_exp(argv[i], parent);
if (arg->type == AST_TYPE_EXC)
return ast_init(
AST_TYPE_EXC,
ast_exc_data_init("`mul` encountered an execption.", arg)
);
if (arg->type != AST_TYPE_NUM)
return ast_init(
AST_TYPE_EXC,
ast_exc_data_init("Can't multiply non-num arguments.", NULL)
);
total *= *(ASTNumData*)arg->data;
}
return ast_init(AST_TYPE_NUM, ast_num_data_init(total));
}
AST* builtin_div(size_t argc, AST** argv, Scope* parent) {
log_dbg("Got here");
AST* first = exec_exp(*argv, parent);
if (first->type == AST_TYPE_EXC)
return ast_init(
AST_TYPE_EXC,
ast_exc_data_init("`div` encountered an exception.", first)
);
if (first->type != AST_TYPE_NUM)
return ast_init(
AST_TYPE_EXC,
ast_exc_data_init("Can't divide non-num arguments.", NULL)
);
ASTNumData total = *(ASTNumData*)first->data;
for (int i = 1; i < argc; i++) {
AST* arg = exec_exp(argv[i], parent);
if (arg->type == AST_TYPE_EXC)
return ast_init(
AST_TYPE_EXC,
ast_exc_data_init("`div` encountered an exception.", arg)
);
if (arg->type != AST_TYPE_NUM)
return ast_init(
AST_TYPE_EXC,
ast_exc_data_init("Can't divide non-num arguments.", NULL)
);
total /= *(ASTNumData*)arg->data;
}
return ast_init(AST_TYPE_NUM, ast_num_data_init(total));
}

View File

@ -1,44 +0,0 @@
#include "include/dlist.h"
#include "include/util.h"
#include <stddef.h>
#include <stdio.h>
DList* dlist_init(void) {
DList* dlist = malloc(sizeof(DList));
dlist->sz = DLIST_INITSZ;
dlist->buf = malloc(DLIST_INITSZ);
dlist->ln = 0;
return dlist;
}
void dlist_destroy(DList* dlist) {
free(dlist->buf);
free(dlist);
}
void dlist_destroypsv(DList* dlist) { free(dlist); }
// Check whether the buffer is about to overflow and resize it if necessary.
void dlist_check_resz(DList* dlist) {
while ((dlist->ln + 1) * sizeof(void*) >= dlist->sz) {
// Double the buffer size when overflown.
dlist->sz *= 2;
dlist->buf = realloc(dlist->buf, dlist->sz);
log_dbgf(
"dlist @ %p doubled from %ld to %ld", dlist, dlist->sz / 2,
dlist->sz
);
}
}
void dlist_append(DList* dest, void* src) {
dlist_check_resz(dest);
log_dbgf("added %p to dlist@%p", src, dest);
dest->buf[dest->ln] = src;
dest->ln++;
}

View File

@ -24,7 +24,7 @@ void dstr_destroy(Dstr* dstr) {
void dstr_destroypsv(Dstr* dstr) { free(dstr); }
// Check whether the buffer is overflowing and resize it if necessary.
void dstr_check_resz(Dstr* dstr, size_t ln) {
void check_resz(Dstr* dstr, size_t ln) {
while (dstr->ln + ln + 1 > dstr->sz) {
// Double the buffer size when overflown.
dstr->sz *= 2;
@ -36,7 +36,7 @@ void dstr_check_resz(Dstr* dstr, size_t ln) {
}
void dstr_append(Dstr* dest, char* src, size_t ln) {
dstr_check_resz(dest, ln);
check_resz(dest, ln);
// Overwrites the \0 at the end of the string, keeps the null from the given
// string.
@ -45,7 +45,7 @@ void dstr_append(Dstr* dest, char* src, size_t ln) {
}
void dstr_appendch(Dstr* dest, char ch) {
dstr_check_resz(dest, 1);
check_resz(dest, 1);
// Overwrites the preexisting null terminator, and adds one of its own.
dest->buf[dest->ln] = ch;

View File

@ -3,156 +3,68 @@
#include <string.h>
#include "include/ast.h"
#include "include/builtin.h"
#include "include/dlist.h"
#include "include/exec.h"
#include "include/htab.h"
#include "include/scope.h"
#include "include/util.h"
AST* exec_start(AST* ast) {
extern AST* root;
ASTNumData exec_exp(AST* ast) {
log_dbg("Started execution.");
if (!ast) return ast;
Scope* global = scope_init(NULL);
global->uses = 1;
for (int i = 0; i < BUILTIN_FNS_LN; i++)
htab_ins(
global->here, BUILTIN_FNS[i].name,
ast_init(AST_TYPE_BIF, ast_bif_data_init(BUILTIN_FNS[i].fn))
);
log_dbg("Completed startup sequence.");
AST* res = exec_exp(ast, global);
return res;
}
AST* exec_exp(AST* ast, Scope* parent) {
switch (ast->type) {
case AST_TYPE_BLOCK: return exec_block(ast, parent);
case AST_TYPE_CALL: return exec_call(ast, parent);
case AST_TYPE_NUM:
return ast_init(
AST_TYPE_NUM, ast_num_data_init(*(ASTNumData*)ast->data)
);
case AST_TYPE_VREF: return exec_vref(ast, parent);
case AST_TYPE_VDEF: return exec_vdef(ast, parent);
case AST_TYPE_FDEF: return exec_fdef(ast, parent);
default: printf("what\n"); exit(1);
case AST_TYPE_CALL: return exec_call(ast);
case AST_TYPE_NUM: return *(ASTNumData*)ast->data;
default: printf("what\n");
exit(1);
}
}
AST* exec_block(AST* ast, Scope* parent) {
ASTBlockData* block = (ASTBlockData*)ast->data;
exec_new_scope(ast, parent);
// Loop through all but last ast.
for (int i = 0; i < block->ln - 1; i++)
exec_exp(block->inside[i], ast->scope);
AST* last = exec_exp(block->inside[block->ln - 1], ast->scope);
return last;
}
AST* exec_call(AST* ast, Scope* parent) {
ASTNumData exec_call(AST* ast) {
log_dbg("Started call execution.");
ASTCallData* data = (ASTCallData*)ast->data;
size_t argc = data->argc;
AST** argv = data->argv;
char* fname = data->to;
fflush(stdout);
ASTCallData* calldata = (ASTCallData*)ast->data;
if (calldata->argc >= 1) {
if (!strcmp(calldata->to, "sum")) {
double total = exec_exp(calldata->argv[0]);
ast->scope = parent;
AST* fdef = ast_find(ast->scope, fname);
if (fdef == NULL)
return ast_init(
AST_TYPE_EXC, ast_exc_data_init("No such function found.", NULL)
for (
size_t i = 1;
i < calldata->argc;
total += exec_exp(calldata->argv[i++])
);
switch (fdef->type) {
case AST_TYPE_BIF:
ASTBIFData bifdata = fdef->data;
return bifdata(argc, argv, parent);
case AST_TYPE_FDEF: return exec_cf(fdef, argc, argv);
default:
return ast_init(AST_TYPE_EXC, ast_exc_data_init("Good job!", NULL));
}
}
return total;
} else if (!strcmp(calldata->to, "sub")) {
double total = exec_exp(calldata->argv[0]);
AST* exec_cf(AST* ast, size_t argc, AST** argv) {
Scope* callscope = scope_init(ast->scope);
ASTFDefData* fdef = (ASTFDefData*)ast->data;
for (int i = 0; i < argc; i++) {
char* key = ((ASTArgData*)fdef->argv[i]->data)->name;
AST* val = argv[i];
scope_add(callscope, key, val);
}
return exec_exp(fdef->body, callscope);
}
AST* exec_vdef(AST* ast, Scope* parent) {
// Use parent's scope.
exec_inherit_scope(ast, parent);
ASTVDefData* data = (ASTVDefData*)ast->data;
AST* val = data->val;
char* key = data->name;
scope_add(parent, key, val); // Add variable definition to parent scope.
return exec_exp(val, parent);
}
AST* exec_vref(AST* ast, Scope* parent) {
// Use parent's scope.
exec_inherit_scope(ast, parent);
log_dbg("attempting to reference var");
ASTVrefData* vref = (ASTVrefData*)ast->data;
AST* found = ast_find(parent, vref->to);
if (found == NULL) {
// TODO: Better memory management here.
static char msg[256];
snprintf(
msg, sizeof(msg), "Could not find value in scope for `%s`.",
vref->to
for (
size_t i = 1;
i < calldata->argc;
total -= exec_exp(calldata->argv[i++])
);
return ast_init(AST_TYPE_EXC, ast_exc_data_init(msg, NULL));
}
return exec_exp(found, ast->scope);
}
return total;
} else if (!strcmp(calldata->to, "mul")) {
double total = exec_exp(calldata->argv[0]);
AST* exec_fdef(AST* ast, Scope* parent) {
ast->scope = scope_init(parent);
ASTFDefData* fdef = (ASTFDefData*)ast->data;
log_dbgf("IS THIS SUSPICIOUS??? %i", fdef->body->type);
AST* val = ast;
char* key = fdef->name;
scope_add(parent, key, val);
// TODO: Create lambda functions.
return fdef->body; // Function definitions return function body.
for (
size_t i = 1;
i < calldata->argc;
total *= exec_exp(calldata->argv[i++])
);
return total;
} else if (!strcmp(calldata->to, "div")) {
double total = exec_exp(calldata->argv[0]);
for (
size_t i = 1;
i < calldata->argc;
total /= exec_exp(calldata->argv[i++])
);
return total;
}}
return -1000;
}
void exec_print(double n) { printf("= %lf\n", n); }
inline void exec_new_scope(AST* ast, Scope* inherit) {
Scope* scope = scope_init(inherit);
ast->scope = scope;
// Update linked status.
scope->uses++;
}
inline void exec_inherit_scope(AST* ast, Scope* inherit) {
ast->scope = inherit;
// Update uses.
inherit->uses++;
}

View File

@ -1,14 +0,0 @@
#include "include/fnv1a.h"
#include <stdlib.h>
uint64_t fnv1a_hash(char* key, size_t ln) {
uint64_t hash = FNV1A_BASIS_64;
for (size_t i = 0; i < ln; i++) {
hash ^= (unsigned char)key[i];
hash *= FNV1A_PRIME_64;
}
return hash;
}

View File

@ -1,57 +0,0 @@
#include "include/gc.h"
#include "include/ast.h"
#include "include/scope.h"
#include "include/util.h"
#include <assert.h>
#include <stdio.h>
#include <stddef.h>
GC* gclist = NULL;
void gc_destroy(GC* gc) { free(gc); }
void f() {}
void* gc_alloc(size_t sz, GCType type) {
assert(type <= GC_TYPE_MAX);
void* mem = malloc(sz);
GC* gc = malloc(sizeof(GC));
gc->p = mem;
gc->type = type;
gc->marked = false;
gc->nxt = gclist;
gclist = gc;
if (type == GC_TYPE_AST) {
log_dbgf("Alloc'd AST for GC: %p", mem);
} else if (type == GC_TYPE_SCOPE) {
log_dbgf("Alloc'd scope for GC: %p", mem);
}
return mem;
}
void gc_hack_free() {
while (gclist) {
GC* gc = gclist;
gclist = gclist->nxt;
switch (gc->type) {
case GC_TYPE_AST:
f();
if (((AST*)gc->p)->type > AST_TYPE_MAX) {
log_dbgf(
"Attempted to free invalid AST (%i > %i) to GC: gc:%p "
"ast:%p",
((AST*)gc->p)->type, AST_TYPE_MAX, gc, gc->p
);
}
ast_destroy_psv(gc->p);
break;
case GC_TYPE_SCOPE: scope_destroy_psv(gc->p); break;
}
gc_destroy(gc);
}
}

View File

@ -1,6 +0,0 @@
#include "include/global.h"
#include <stdlib.h>
// Global input text.
char* inp = NULL;

View File

@ -3,7 +3,6 @@
#include <stdio.h>
#include "../../src/include/ast.h"
#include "../../src/include/lexer.h"
#include "../../src/include/dlist.h"
int yylex(void);
void yyerror(char const*);
@ -13,7 +12,6 @@
%code requires {
#include "../../src/include/ast.h"
#include "../../src/include/dlist.h"
}
%union {
@ -21,73 +19,39 @@
char* strval;
AST* ast;
ArgArr* argarr;
DList* exps;
}
%define parse.error verbose
%token BLOCKS // Block start {.
%token BLOCKE // Block end }.
%token LGROUP
%token RGROUP
%token SEP
%token GROUPS // Group start (.
%token GROUPE // Group end ).
%token SEP // Seperator ,.
%token<strval> WORD
%token<fval> NUM
%token EQ // Equals =.
%token SUB
%token PLUS
%token MULT
%token DIV
%token EXPSEP // Expression seperator ;.
%token NL
%token<strval> WORD // Word, i.e. keyword.
%token<fval> NUM // Number.
%token SUB // Subtract -.
%token ADD // Addition *.
%token MUL // Multiplication *.
%token DIV // Division /.
%token NL // Newline.
%left ADD SUB
%left MUL DIV
%left PLUS SUB
%left MULT DIV
%precedence NEG
%type<ast> exp;
%type<argarr> arg;
%type<argarr> argstart;
%type<exps> blockstart;
%type<exps> block;
%type<exps> inputstart
%type<exps> input
%start inputend // This makes no sense but w/e.
%%
inputstart:
exp {
DList* exps = dlist_init();
dlist_append(exps, $1);
$$ = exps;
}
;
input:
inputstart {
$$ = $1;
}
| input EXPSEP exp {
dlist_append($1, $3);
$$ = $1;
}
%empty
| exp { root = $1; }
;
inputend:
%empty
| input {
root = ast_init(AST_TYPE_BLOCK, ast_block_data_init((AST**) $1->buf, $1->ln));
dlist_destroypsv($1);
}
;
argstart:
exp {
@ -105,37 +69,9 @@ arg:
}
;
blockstart:
exp {
DList* exps = dlist_init(); // List of expressions.
dlist_append(exps, $1);
$$ = exps;
}
;
block:
blockstart { $$ = $1; }
| block EXPSEP exp {
dlist_append($1, $3);
$$ = $1;
}
;
exp:
NUM { $$ = ast_init(AST_TYPE_NUM, ast_num_data_init($1)); }
// Function definitions.
| WORD GROUPS arg GROUPE EQ exp {
size_t argc = $3->ln;
AST** argv = $3->buf;
argarr_destroypsv($3);
$$ = ast_init(AST_TYPE_FDEF, ast_fdef_data_init($1, argc, argv, $6));
}
| BLOCKS block BLOCKE {
$$ = ast_init(AST_TYPE_BLOCK, ast_block_data_init((AST**) $2->buf, $2->ln));
}
| SUB exp {
AST** argv = calloc(2, sizeof(AST*));
argv[0] = ast_init(AST_TYPE_NUM, ast_num_data_init(-1));
@ -145,26 +81,19 @@ exp:
$$ = ast_init(AST_TYPE_CALL, ast_call_data_init(to, 2, argv));
}
| GROUPS exp GROUPE { $$ = $2; }
// Variable definition.
| WORD EQ exp {
$$ = ast_init(AST_TYPE_VDEF, ast_vdef_data_init($1, $3));
}
| LGROUP exp RGROUP { $$ = $2; }
// Variable reference.
| WORD {
$$ = ast_init(AST_TYPE_VREF, ast_vref_data_init($1));
}
//| WORD
| WORD GROUPS arg GROUPE {
| WORD LGROUP arg RGROUP {
size_t argc = $3->ln;
AST** argv = $3->buf;
argarr_destroypsv($3);
$$ = ast_init(AST_TYPE_CALL, ast_call_data_init($1, argc, argv));
}
| exp ADD exp {
| exp PLUS exp {
AST** argv = calloc(2, sizeof(AST*));
argv[0] = $1;
argv[1] = $3;
@ -182,7 +111,7 @@ exp:
$$ = ast_init(AST_TYPE_CALL, ast_call_data_init(to, 2, argv));
}
| exp MUL exp {
| exp MULT exp {
AST** argv = calloc(2, sizeof(AST*));
argv[0] = $1;
argv[1] = $3;

View File

@ -1,36 +0,0 @@
#include "include/htab.h"
#include "include/fnv1a.h"
#include "include/util.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
HTab* htab_init() {
HTab* htab = calloc(1, sizeof(HTab));
log_dbgf("HTAB %p", htab);
return htab;
}
void htab_destroy(HTab* htab) { free(htab); }
// Get the index of a key.
size_t geti(char* key) {
uint64_t hash = fnv1a_hash(key, strlen(key));
size_t i = hash & (HTAB_SPACE - 1); // Magic.
return i;
}
void* htab_get(HTab* htab, char* key) {
size_t i = geti(key);
log_dbgf("Getting something from hash table @ index %lu", i);
return (*htab)[i];
}
void htab_ins(HTab* htab, char* key, void* data) {
size_t i = geti(key);
(*htab)[i] = data;
log_dbgf("Inserted something to hash table @ index %lu", i);
}

View File

@ -1,169 +1,47 @@
#ifndef AST_H
#define AST_H
#include "scope.h"
#include <stdlib.h>
// The type of an `AST`.
typedef enum {
// Primitive types.
AST_TYPE_NUM, // A number (float).
AST_TYPE_STR, // A string
AST_TYPE_INT, // An integer.
AST_TYPE_SYM, // A symbol.
AST_TYPE_EXC, // Exception.
// Collection types:
AST_TYPE_VEC, // A vector (fixed size, fixed type).
AST_TYPE_LIST, // A list (variable size, variable type).
// Misc. types.
AST_TYPE_BIF, // Built-in function.
AST_TYPE_CALL, // A function call.
AST_TYPE_VDEF, // A variable definition.
AST_TYPE_VREF, // A variable reference.
AST_TYPE_BLOCK, // A block of code (scope).
AST_TYPE_FDEF, // A function definition.
AST_TYPE_ARG, // A definition argument.
AST_TYPE_MAX = AST_TYPE_ARG,
AST_TYPE_NUM, // A number.
AST_TYPE_CALL, // A function call.
AST_TYPE_VREF, // A variable reference.
AST_TYPE_MAX = AST_TYPE_CALL
} ASTType;
// An Abstract Syntax Tree.
typedef struct {
ASTType type; // The type of the `AST`.
void* data; // The data of the `AST`.
Scope* scope; // The scope of the `AST`.
ASTType type;
void* data;
} AST;
// Create a new `AST`.
AST* ast_init(ASTType type, void* data);
// Create a new `AST` with a specified scope.
AST* ast_init_scope(ASTType type, void* data, Scope* scope);
// Destroy an `AST`, recursively.
void ast_destroy(AST* ast);
// Destroy an `AST`.
void ast_destroy_psv(AST* ast);
// Print an `AST`, recursively.
void ast_print(AST* ast);
// Helper function to `ast_print()`, where `i` is indentation level.
void ast_print_i(AST* ast, int i);
// A number.
typedef double ASTNumData;
// Create a new `ASTNumData`.
ASTNumData* ast_num_data_init(double val);
// Destroy an `ASTNumData`.
void ast_num_data_destroy(ASTNumData* num);
// Print an `ASTNumData`.
void ast_num_print(ASTNumData*, int i);
// An exception.
typedef struct ASTEXCDATA {
const char* msg; // The exception message.
AST* trace; // The previous exception.
} ASTExcData;
// Create a new `ASTExecData. `msg` should be static.
ASTExcData* ast_exc_data_init(const char* msg, AST* trace);
// Destroy an `ASTExecData`.
void ast_exc_data_destroy(ASTExcData* exc);
// Print an `ASTExecData`.
void ast_exc_print(ASTExcData*, int i);
// A built-in function.
typedef AST* (*ASTBIFData)(size_t argc, AST** argv, Scope* scope);
// Create a built-in function.
ASTBIFData* ast_bif_data_init(AST* fn(size_t, AST**, Scope*));
// Destroy an `ASTBIFData`.
void ast_bif_data_destroy(ASTBIFData* bif);
// A call (to a function).
typedef struct {
char* to; // What the call's to.
size_t argc; // Argument count.
AST** argv; // Argument vector.
} ASTCallData;
// Create a new `ASTCallData`.
ASTCallData* ast_call_data_init(char* to, size_t argc, AST** argv);
// Destroy an `ASTCallData` recursively.
void ast_call_data_destroy(ASTCallData* call);
// Destroy an `ASTCallData`.
void ast_call_data_destroy_psv(ASTCallData *call);
// Print an `ASTCallData`.
void ast_call_print(ASTCallData*, int i);
// A variable definition's data.
typedef struct {
char* name;
AST* val;
} ASTVDefData;
// Create a new `ASTVDefData`.
ASTVDefData* ast_vdef_data_init(char* name, AST* val);
// Destroys the `ASTVDefData`, `ASTVDefData->name`, and `ASTVDefData->val`.
void ast_vdef_data_destroy(ASTVDefData* vdef);
// Destroy an `ASTVDefData`.
void ast_vdef_data_destroy_psv(ASTVDefData* vdef);
// Print an `ASTVDefData`.
void ast_vdef_print(ASTVDefData*, int depth);
// A variable reference's data.
typedef struct {
char* to; // What the reference's to.
} ASTVrefData;
// Create a new `ASTVRefData`.
ASTVrefData* ast_vref_data_init(char* to);
// Destroy an `ASTVRefData`.
void ast_vref_data_destroy(ASTVrefData* call);
// Print an `ASTVRefData`.
void ast_vref_print(ASTVrefData*, int i);
// A code block.
typedef struct {
AST** inside; // What's inside the block.
size_t ln; // How many ASTs are in the block.
} ASTBlockData;
// Create a new `ASTBlockData`.
ASTBlockData* ast_block_data_init(AST** inside, size_t ln);
// Destroy an `ASTBlockData`, recursively.
void ast_block_data_destroy(ASTBlockData* block);
// Destroy an `ASTBlockData`.
void ast_block_data_destroy_psv(ASTBlockData *block);
// Print an `ASTBlockData`.
void ast_block_print(ASTBlockData*, int i);
typedef struct {
char* name; // Function name.
size_t argc; // Argument count.
AST** argv; // Argument vector.
AST* body; // Function body.
} ASTFDefData;
// Create a new `ASTFDefData`.
ASTFDefData* ast_fdef_data_init(char* name, size_t argc, AST** argv, AST* body);
// Destroy an `ASTFDefData`, recursively.
void ast_fdef_data_destroy(ASTFDefData* fdef);
// Destroy an `ASTFDefData`.
void ast_fdef_data_destroy_psv(ASTFDefData* fdef);
// Print an `ASTFDefData`.
void ast_fdef_print(ASTFDefData* fdef, int i);
typedef struct {
char* name; // Argument name.
} ASTArgData;
// Create a new `ASTArgData`.
ASTArgData* ast_arg_data_init(char* name);
// Destroy an `ASTArgData`.
void ast_arg_data_destroy(ASTArgData* arg);
// Print an `ASTArgData`.
void ast_arg_print(ASTArgData* arg, int i);
// Find a name in the scope.
AST* ast_find(Scope* scope, char* name);
#endif

View File

@ -1,31 +0,0 @@
#ifndef BUILTIN_H
#define BUILTIN_H
#include "ast.h"
// Sum some nums.
AST* builtin_sum(size_t argc, AST** argv, Scope* parent);
// Subtract nums.
AST* builtin_sub(size_t argc, AST** argv, Scope* parent);
// Multiply nums.
AST* builtin_mul(size_t argc, AST** argv, Scope* parent);
// Divide nums.
AST* builtin_div(size_t argc, AST** argv, Scope* parent);
struct builtin_data {
char* name;
AST* (*fn)(size_t argc, AST** argv, Scope* parent);
};
static struct builtin_data BUILTIN_FNS[] = {
{ "sum", builtin_sum },
{ "sub", builtin_sub },
{ "mul", builtin_mul },
{ "div", builtin_div },
};
#define BUILTIN_FNS_LN (arrln(BUILTIN_FNS))
#endif

View File

@ -1,24 +0,0 @@
#ifndef DLIST_H
#define DLIST_H
#include <stdlib.h>
#define DLIST_INITSZ 128 * sizeof(void*)
typedef struct {
void** buf; // The buffer containing the list.
size_t sz; // The size of the buffer.
size_t ln; // The number of elements in the list.
} DList;
// Create a new `DList`.
DList* dlist_init(void);
// Destroy a `DList`.
void dlist_destroy(DList* dstr);
// Destroy `DList` structure but preserve `->buf`.
void dlist_destroypsv(DList* dstr);
// Append `src` to `dest`.
void dlist_append(DList* dest, void* src);
#endif

View File

@ -3,7 +3,7 @@
#include <stdlib.h>
#define DSTR_INITSZ 128
#define DSTR_INITSZ 2
typedef struct {
char* buf; // The buffer containing the string.
@ -11,17 +11,15 @@ typedef struct {
size_t ln; // The number of characters in the buffer.
} Dstr;
// Initialize a `DStr`.
Dstr* dstr_init(void);
// Destroy a `DStr`.
void dstr_destroy(Dstr* dstr);
// Destroy `DStr` structure but preserve `DStr->buf`.
// Destroy Dstr structure but preserve ->buf.
void dstr_destroypsv(Dstr* dstr);
// Append `ln` characters of `src` to `dest`.
// Append ln characters of src to dest.
void dstr_append(Dstr* dest, char* src, size_t ln);
// Append `ch` to `dest`.
// Append ch to dest.
void dstr_appendch(Dstr* dest, char ch);
#endif

View File

@ -2,31 +2,9 @@
#define EXEC_H
#include "ast.h"
#include "scope.h"
// Start executing at the root of the AST. Initialize the `scope`.
AST* exec_start(AST* ast);
// Execute an expression. Delegates to the other executor functions.
AST* exec_exp(AST* ast, Scope* parent);
// Execute the expressions of a block.
AST* exec_block(AST* ast, Scope* parent);
// Execute a call.
AST* exec_call(AST* ast, Scope* parent);
// Execute a custom function call.
AST* exec_cf(AST* ast, size_t argc, AST** argv);
// Execute a variable definition.
AST* exec_vdef(AST* ast, Scope* parent);
// Execute a variable reference.
AST* exec_vref(AST* ast, Scope* parent);
// Execute a function definition.
AST* exec_fdef(AST* ast, Scope* parent);
// Print the result of an execution.
ASTNumData exec_exp(AST* ast);
ASTNumData exec_call(AST* ast);
void exec_print(double n);
// Create a new scope and mark it as linked. Also update inherited scope.
void exec_new_scope(AST* ast, Scope* inherit);
// Inherit from another scope and mark it as linked.
void exec_inherit_scope(AST* ast, Scope* inherit);
#endif

View File

@ -1,18 +0,0 @@
#ifndef FNV1A_H
#define FNV1A_H
// Implements the FNV-1a hash algorithm.
#include <stdint.h>
#include <stdlib.h>
// FNV prime.
#define FNV1A_PRIME_64 0x00000100000001b3u
// Offset basis.
#define FNV1A_BASIS_64 0xcbf29ce484222325u
// Hash a string `str` of length `ln`.
uint64_t fnv1a_hash(char* str, size_t ln);
#endif

View File

@ -1,31 +0,0 @@
#ifndef GC_H
#define GC_H
#include <stdlib.h>
// The type a GC can refer to.
typedef enum {
GC_TYPE_AST,
GC_TYPE_SCOPE,
GC_TYPE_MAX = GC_TYPE_SCOPE
} GCType;
// Added to each AST and Scope; keep track of what's actually still accessible.
typedef struct GC_STRUCT {
void* p; // Pointer to the data.
struct GC_STRUCT* nxt; // The next GC in the linked list.
GCType type; // What type of data.
bool marked; // Whether the data is still accessible.
} GC;
GC* gc_init(void* p, GCType type, GC*);
// Does not free ->p or ->nxt.
void gc_destroy(GC* gc);
// Allocate for an object in the heap, and keep track of it in the GC.
void* gc_alloc(size_t sz, GCType type);
// Free everything, immediately.
void gc_hack_free();
#endif

View File

@ -1,4 +0,0 @@
#ifndef GLOBAL_H
#define GLOBAL_H
#endif

View File

@ -1,29 +0,0 @@
#ifndef HTAB_H
#define HTAB_H
#include <stddef.h>
#include <stdint.h>
#define HTAB_FN fnv1a_hash // Function used for hashing.
#define HTAB_SPACE \
1024 // Number of entries possible in the hash table; must be
// power of 2.
// Hash Table.
typedef void* HTab[HTAB_SPACE];
// Create a new hash table.
HTab* htab_init();
// Destroy a hash table, but not its elements.
void htab_destroy(HTab* htab);
// Get element at `hash`. Return its contents, or NULL if nothing found.
void* htab_get(HTab* htab, char* str);
// Insert `data` at index `hash`.
void htab_ins(HTab* htab, char* key, void* data);
// Gets the length of the hash table.
size_t htab_ln(HTab* htab);
#endif

View File

@ -1,22 +0,0 @@
#ifndef SCOPE_H
#define SCOPE_H
#include "htab.h"
// Represents the reverse linked tree of scope.
typedef struct SCOPE_T {
HTab* here; // This scope's hash table.
struct SCOPE_T* inherit; // The scope to inherit from.
int uses; // How many `AST`s are linked to this scope. // TODO: REMOVE.
} Scope;
// Create a new `Scope`. Creates new empty `HTab` for current scope.
Scope* scope_init(Scope* inherit);
// Destroy all linked `Scope`s this inherits from.
void scope_destroy(Scope* scope);
// Destroy the current `Scope` only.
void scope_destroy_psv(Scope *scope);
// Insert a key/val pair into the `HTab` of a `Scope`.
void scope_add(Scope* scope, char* key, void* val);
#endif

View File

@ -6,19 +6,18 @@
#define STACK_MAX 64
typedef struct {
size_t ln; // The length of the stack (i.e., how many elements).
void* buf[STACK_MAX]; // The stack itself.
size_t i; // Current index in the stack.
void* val[STACK_MAX]; // The stack itself.
} Stack;
// Create a `Stack`.
Stack* stack_init();
// Destroy a `Stack`.
// Note that `->i` must be `0`, i.e. the `Stack` must be empty.
// Destroy a stack.
// Note that `stack->i` must be `0`.
void stack_destroy(Stack* stack);
// Push a value to the `Stack`.
// Push a value to the stack.
void stack_push(Stack* stack, void* val);
// Pop a value from the `Stack`.
// Pop a value from the stack.
void* stack_pop(Stack* stack);
#endif

View File

@ -1,30 +1,17 @@
#ifndef UTIL_H
#define UTIL_H
// Most of this file is cursed printing macros for `ast_print()`. Do not attempt
// to comprehend.
// Allocate a pointer with a type.
#define talloc(T, X) T* X = malloc(sizeof(T));
// Get the length of an array.
#define arrln(A) (sizeof(A)/sizeof(*A))
#ifdef DBG // Debug macros
// Log a message.
#define log_dbg(msg) \
printf( \
"\033[37;1mdbg\033[0m:\033[37m%s\033[0m:\033[32m " msg "\033[0m\n", \
__func__ \
);
printf("\033[37;1mdbg\033[0m:\033[37m%s\033[0m:\033[32m " msg "\033[0m\n", \
__func__);
// Log a message with formatting.
#define log_dbgf(msg, ...) \
printf( \
"\033[37;1mdbg\033[0m:\033[37m%s\033[0m:\033[32m " msg "\033[0m\n", \
__func__, __VA_ARGS__ \
);
printf("\033[37;1mdbg\033[0m:\033[37m%s\033[0m:\033[32m " msg "\033[0m\n", \
__func__, __VA_ARGS__);
#else // ifdef DBG
#define log_dbg(msg)
@ -63,30 +50,23 @@
// Print & indent the title of a section.
#define INDENT_TITLE(THING, WHERE) \
printf( \
"%s" COL_BCYA THING COL_RESET " @" COL_MAG " %p\n" COL_RESET, \
INDENT_spacing->buf, WHERE \
);
printf("%s" COL_BCYA THING COL_RESET " @" COL_MAG " %p\n" COL_RESET, INDENT_spacing->buf, WHERE);
// Print & indent a thing.
#define INDENT_FIELD(FIELD, VAL, ...) \
printf( \
"%s " COL_BWHI FIELD ": " COL_RESET COL_WHI VAL COL_RESET "\n", \
INDENT_spacing->buf, __VA_ARGS__ \
);
printf("%s " COL_BWHI FIELD ": " COL_RESET COL_WHI VAL COL_RESET "\n", \
INDENT_spacing->buf, __VA_ARGS__);
// Print & indent a thing with a newline before the val.
#define INDENT_FIELD_NL(FIELD, VAL, ...) \
printf( \
"%s " COL_BWHI FIELD ":" COL_RESET "\n %s " COL_WHI VAL COL_RESET \
"\n", \
INDENT_spacing->buf, INDENT_spacing->buf, __VA_ARGS__ \
);
printf("%s " COL_BWHI FIELD ":" COL_RESET "\n %s " COL_WHI VAL COL_RESET \
"\n", \
INDENT_spacing->buf, INDENT_spacing->buf, __VA_ARGS__);
// Print & indent a thing without any newline.
#define INDENT_FIELD_EXT_NONL_START(FIELD) \
#define INDENT_FIELD_EXT_NONL_START(FIELD) \
printf("%s " COL_BWHI FIELD ":\n" COL_RESET COL_WHI, INDENT_spacing->buf);
#define INDENT_FIELD_NONL_END printf("\n" COL_RESET);
#define INDENT_FIELD_NONL_END printf( "\n" COL_RESET);
// Print an array A of N things, by calling the function F.
#define INDENT_FIELD_LIST(FIELD, A, N, F) \
@ -97,14 +77,9 @@
printf(COL_BWHI "%s ]\n" COL_RESET, INDENT_spacing->buf);
// End an indent block.
#define INDENT_END \
printf(COL_RESET); \
dstr_destroy(INDENT_spacing);
#define INDENT_END printf(COL_RESET); dstr_destroy(INDENT_spacing);
#define INDENT_FIELD_LIST_OPEN(FIELD) \
printf("%s " COL_BWHI FIELD ": [\n" COL_RESET, INDENT_spacing->buf);
#define INDENT_FIELD_LIST_CLOSE \
printf(COL_BWHI "%s ]\n" COL_RESET, INDENT_spacing->buf);
// Allocate a pointer with a type.
#define talloc(T, X) T* X = malloc(sizeof(T));
#endif

View File

@ -26,7 +26,7 @@ void argarr_destroy(ArgArr* argarr) {
void argarr_destroypsv(ArgArr* argarr) { free(argarr); }
void argarr_add(ArgArr* argarr, AST* arg) {
if ((argarr->ln + 1) * sizeof(AST*) > argarr->sz) {
if ((argarr->ln + 1) * argarr->sz > argarr->sz) {
argarr->sz *= 2;
argarr->buf = realloc(argarr->buf, argarr->sz);
log_dbgf(
@ -72,10 +72,10 @@ double acc_float(int c) {
char* acc_word(int c) {
Dstr* val = dstr_init();
while (isalpha(*inp)) {
do {
dstr_appendch(val, *(inp - 1));
inp++;
}
} while (isalpha(*inp));
dstr_appendch(val, *(inp - 1));
char* ret = val->buf;
@ -104,18 +104,14 @@ int yylex() {
}
switch (c) {
case '+': return ADD;
case '+': return PLUS;
case '\n': return NL;
case '-': return SUB;
case '*': return MUL;
case '*': return MULT;
case '/': return DIV;
case '(': return GROUPS;
case ')': return GROUPE;
case '(': return LGROUP;
case ')': return RGROUP;
case ',': return SEP;
case ';': return EXPSEP;
case '{': return BLOCKS;
case '}': return BLOCKE;
case '=': return EQ;
default: fprintf(stderr, "Unexpected character: %c\n", c);
}

View File

@ -4,7 +4,6 @@
#include "include/ast.h"
#include "include/dstr.h"
#include "include/exec.h"
#include "include/gc.h"
#include "include/lexer.h"
#include "include/util.h"
@ -12,19 +11,18 @@
// Global Abstract Syntax Tree.
extern AST* root;
extern char* inp;
// Global input text.
char* inp = NULL;
extern int yyparse();
int main(int argc, char** argv) {
if (argc - 1 && strlen(argv[1]) > 0 && (inp = argv[1]) && !yyparse()) {
log_dbg("Parsed successfully!\n");
ast_print(root);
AST* eval = exec_start(root);
ast_print(eval);
// ast_destroy(eval);
// ast_destroy(root);
gc_hack_free();
exec_print(exec_exp(root));
ast_destroy(root);
exit(0);
}
@ -41,7 +39,7 @@ int main(int argc, char** argv) {
switch (c) {
case EOF: dstr_destroy(ln); goto lnskip;
case '\n': goto lnend;
default: dstr_appendch(ln, c);
default: dstr_appendch(ln, c); log_dbgf("cchar: %c", c);
}
} while (1);
@ -55,13 +53,11 @@ int main(int argc, char** argv) {
log_dbg("Parsed successfully!\n");
} else printf("Parse error.\n");
exec_print(exec_exp(root));
#ifdef DBG
ast_print(root);
#endif
AST* eval = exec_start(root);
ast_print(eval);
gc_hack_free();
ast_destroy(root);
}
dstr_destroy(ln);

View File

@ -1,37 +0,0 @@
#include "include/scope.h"
#include "include/gc.h"
#include "include/htab.h"
#include "include/util.h"
#include <stdio.h>
#include <stdlib.h>
Scope* scope_init(Scope* inherit) {
Scope* scope = gc_alloc(sizeof(Scope), GC_TYPE_SCOPE);
scope->here = htab_init();
scope->inherit = inherit;
scope->uses = 0;
log_dbgf("%p: new scope, inherits from %p", scope, inherit);
return scope;
}
void scope_destroy(Scope* scope) {
if (!scope) return;
htab_destroy(scope->here);
if (scope->inherit != NULL) scope_destroy(scope->inherit);
free(scope);
}
void scope_destroy_psv(Scope* scope) {
if (!scope) return;
log_dbgf("%p: destroying", scope);
htab_destroy(scope->here);
scope->inherit = NULL;
free(scope);
}
inline void scope_add(Scope* scope, char* key, void* val) {
htab_ins(scope->here, key, val);
}

View File

@ -8,34 +8,33 @@
Stack* stack_init() {
talloc(Stack, stack);
memset(stack->buf, 0, sizeof(void*) * STACK_MAX);
stack->ln = 0;
memset(stack->val, 0, sizeof(void*) * STACK_MAX);
stack->i = 0;
return stack;
}
void stack_destroy(Stack* stack) {
// Can only free an empty stack.
assert(stack->ln == 0);
assert(stack->i == 0);
free(stack);
}
void stack_push(Stack* stack, void* val) {
if (stack->ln >= STACK_MAX) {
if (stack->i >= STACK_MAX) {
log_dbgf("Ran out of stack (max: %d)", STACK_MAX);
return;
}
stack->buf[stack->ln] = val;
stack->ln++;
log_dbgf("pushed to stack, inc ln to %ld", stack->ln);
stack->val[stack->i] = val;
stack->i++;
}
void* stack_pop(Stack* stack) {
if (stack->ln <= 0) {
if (stack->i <= 0) {
log_dbg("Can't pop empty stack.");
return (void*)-1;
}
return stack->buf[--stack->ln];
return stack->val[--stack->i];
}

View File

@ -1,59 +0,0 @@
#include "../src/include/ast.h"
#include "Unity/src/unity.h"
#include "Unity/src/unity_internals.h"
#include <string.h>
void setUp() {}
void tearDown() {}
void test_ast_num() {
ASTNumData* num = ast_num_data_init(12.0);
AST* ast = ast_init(AST_TYPE_NUM, num);
TEST_ASSERT_EQUAL(AST_TYPE_NUM, ast->type);
TEST_ASSERT_EQUAL(12.0, *(ASTNumData*)ast->data);
ast_destroy(ast);
}
void test_ast_call() {
AST** argv = malloc(2*sizeof(AST*));
argv[0] = ast_init(AST_TYPE_NUM, ast_num_data_init(1.0));
argv[1] = ast_init(AST_TYPE_NUM, ast_num_data_init(2.0));
char* name = malloc(2);
strcpy(name, "f");
ASTCallData* call = ast_call_data_init(name, 2, argv);
AST* ast = ast_init(AST_TYPE_CALL, call);
TEST_ASSERT_EQUAL(AST_TYPE_CALL, ast->type);
TEST_ASSERT_EQUAL(name, ((ASTCallData*)ast->data)->to);
TEST_ASSERT_EQUAL(2, ((ASTCallData*)ast->data)->argc);
TEST_ASSERT_EQUAL(1.0, ((ASTCallData*)ast->data)->argv[0]);
TEST_ASSERT_EQUAL(2.0, ((ASTCallData*)ast->data)->argv[1]);
ast_destroy(ast);
}
void test_ast_vref() {
char* s = malloc(2);
strcpy(s, "x");
ASTVrefData* vref = ast_vref_data_init(s);
AST* ast = ast_init(AST_TYPE_VREF, vref);
TEST_ASSERT_EQUAL(AST_TYPE_VREF, ast->type);
ASTVrefData data = *(ASTVrefData*)ast->data;
TEST_ASSERT_EQUAL_STRING("x", data.to);
//ast_destroy(ast);
}
int main() {
UNITY_BEGIN();
RUN_TEST(test_ast_num);
//RUN_TEST(test_ast_call);
//RUN_TEST(test_ast_vref);
return UNITY_END();
}

View File

@ -1,41 +0,0 @@
#include "../src/include/dlist.h"
#include "Unity/src/unity.h"
void setUp() {};
void tearDown() {};
void test_dlist_init() {
DList* dlist = dlist_init();
TEST_ASSERT_EQUAL(0, dlist->ln);
TEST_ASSERT_EQUAL(DLIST_INITSZ, dlist->sz);
}
void test_dlist_append() {
DList* dlist;
// Test simple appending.
dlist = dlist_init();
int* n = malloc(sizeof(int));
*n = 1;
dlist_append(dlist, n);
TEST_ASSERT_EQUAL(n, dlist->buf[0]);
TEST_ASSERT_EQUAL(1, dlist->ln);
dlist_destroy(dlist);
// Test buffer doubling.
dlist = dlist_init();
for (int i = 0; i < 129; i++) dlist_append(dlist, &i);
TEST_ASSERT_EQUAL(129, dlist->ln);
TEST_ASSERT_EQUAL(DLIST_INITSZ*2, dlist->sz);
}
int main() {
UNITY_BEGIN();
RUN_TEST(test_dlist_init);
RUN_TEST(test_dlist_append);
return UNITY_END();
}

View File

@ -9,7 +9,7 @@ void test_dstr_init() {
Dstr* dstr = dstr_init();
TEST_ASSERT_EQUAL(0, strlen(dstr->buf));
TEST_ASSERT_EQUAL(0, dstr->ln);
TEST_ASSERT_EQUAL(DSTR_INITSZ, dstr->sz);
TEST_ASSERT_EQUAL(DSTR_INITSZ, dstr->bufsz);
}
void test_dstr_append() {
@ -22,7 +22,7 @@ void test_dstr_append() {
TEST_ASSERT_EQUAL_STRING(hello_world, dstr->buf);
TEST_ASSERT_EQUAL(strlen(hello_world), dstr->ln);
TEST_ASSERT_EQUAL(DSTR_INITSZ, dstr->sz);
TEST_ASSERT_EQUAL(DSTR_INITSZ, dstr->bufsz);
dstr_destroy(dstr);
@ -36,7 +36,7 @@ void test_dstr_append() {
TEST_ASSERT_EQUAL_STRING(h, dstr->buf);
TEST_ASSERT_EQUAL(strlen(h), dstr->ln);
TEST_ASSERT_EQUAL(DSTR_INITSZ * 2, dstr->sz);
TEST_ASSERT_EQUAL(DSTR_INITSZ * 2, dstr->bufsz);
}
void test_dstr_appendch() {
@ -50,7 +50,7 @@ void test_dstr_appendch() {
TEST_ASSERT_EQUAL_STRING(c_str, dstr->buf);
TEST_ASSERT_EQUAL(strlen(c_str), dstr->ln);
TEST_ASSERT_EQUAL(DSTR_INITSZ, dstr->sz);
TEST_ASSERT_EQUAL(DSTR_INITSZ, dstr->bufsz);
dstr_destroy(dstr);
@ -66,10 +66,11 @@ void test_dstr_appendch() {
TEST_ASSERT_EQUAL_STRING(h, dstr->buf);
TEST_ASSERT_EQUAL(strlen(h), dstr->ln);
TEST_ASSERT_EQUAL(DSTR_INITSZ * 2, dstr->sz);
TEST_ASSERT_EQUAL(DSTR_INITSZ * 2, dstr->bufsz);
}
int main() {
// not needed when using generate_test_runner.rb
int main(void) {
UNITY_BEGIN();
RUN_TEST(test_dstr_init);
RUN_TEST(test_dstr_append);

View File

@ -1,27 +0,0 @@
#include "../src/include/htab.h"
#include "Unity/src/unity.h"
#include "Unity/src/unity_internals.h"
#include <string.h>
void setUp() {}
void tearDown() {}
void test_htab() {
char* key = "hello";
char* data = "world";
HTab* htab = htab_init();
htab_ins(htab, key, data);
TEST_ASSERT_EQUAL_STRING(data, htab_get(htab, key));
TEST_ASSERT_NOT_EQUAL(data, htab_get(htab, "h"));
}
int main() {
UNITY_BEGIN();
RUN_TEST(test_htab);
return UNITY_END();
}

View File

@ -7,7 +7,6 @@ bin() { ./scl.out $1 | tail -n1; }
[ "$output" = "= 2.000000" ]
run bin "-1+1"
echo $output
[ "$output" = "= 0.000000" ]
run bin "1+-1"
@ -83,49 +82,3 @@ bin() { ./scl.out $1 | tail -n1; }
run bin "-(-(1+2)*3)"
[ "$output" = "= 9.000000" ]
}
@test "basic blocks" {
run bin "{1}"
[ "$output" = "= 1.000000" ]
run bin "2+{3;4}"
[ "$output" = "= 6.000000" ]
run bin "5*{1+1;5*2}"
echo $output
[ "$output" = "= 50.000000" ]
}
@test "variable definition" {
run bin "x=1"
[ "$output" = "= 1.000000" ]
run bin "x=1;x+1"
[ "$output" = "= 2.000000" ]
run bin "h=7;j=2;k=8;l=4;h*h-l+j*k"
echo $output
[ "$output" = "= 61.000000" ]
}
#@test "function definition" {
# run bin "f(n)=2*n; f(2)"
# [ "$output" = "= 4.000000" ]
#}
@test "type stuff" {
run bin "x:int=1"
[ "$output" = "= 1" ]
run bin "x=1.5; type(x)"
[ "$output" = "= num"]
run bin "x:int=1.5; type(x)"
[ "$output" = "= int" ]
run bin "x:int=1.5"
[ "$output" = "= 1" ]
run bin "print(\"Hello, world!\")"
[ "$output" = "= Hello, world!" ]
}