Compare commits

62 Commits

Author SHA1 Message Date
8d3e43d7dc Bump version in README.md. 2025-06-14 14:09:54 +00:00
d4293e87f3 Everything works. 2025-06-14 10:02:04 -04:00
abb8ff6b58 Finished garbage collector.
🎉
2025-06-14 09:23:49 -04:00
80122b6572 I try. 2025-05-31 19:13:26 -04:00
a4afd3b58a Fixed segfault on spacey input. 2025-05-31 18:25:11 -04:00
90c8c91410 Somethings working. 2025-05-31 18:19:04 -04:00
f5ab0e9cb0 Fixed memory leak. 2025-05-24 10:52:27 -04:00
0fb1f1d55f Began work on garbage collector. 2025-05-24 10:37:29 -04:00
1d83aa65a4 Well it mostly works now.
Fixed some memory leaks. Implemented some memory leaks.

May need to look into garbage collection in the future.
2025-05-17 11:01:20 -04:00
3f30662cde Fixed some memory bugs. 2025-05-10 10:43:36 -04:00
743d16f696 Fixed bad free on stack.
Still need to figure that one out.
2025-05-07 08:54:19 -04:00
7b99292547 Builtin functions now work with new scope. 2025-05-07 08:38:10 -04:00
4a516d8edb Updated builtin functions for scope. 2025-05-07 08:16:05 -04:00
5b0950cabb Finished linked env scope on user functions.
Doesn't work for builtin functions yet, so doesn't compile. TODO.
2025-04-26 11:04:21 -04:00
40051de9ae Fix deprecated version name.
Co-authored-by: bgiobbe-tcs <bgiobbe.tcs@gmail.com>
2025-04-26 10:58:09 -04:00
2d01b09ee9 Added scope structures.
Not yet used in exec.c.
2025-04-26 09:45:46 -04:00
ff68b756ef Updated for Scope. 2025-04-26 09:45:34 -04:00
c2f2658e9c Start removing old cursed stack frame scope code.
It never really worked anyway.
2025-04-23 13:12:03 -04:00
3e80f6430d Updated output formatting for scope. 2025-04-23 13:06:54 -04:00
5b50d423fa Updated README for new scoping system. 2025-04-14 09:07:28 -04:00
a04d318e45 Started implementing the linked env scope. 2025-04-14 08:58:41 -04:00
7258f162c9 Clean up build process and add more doc. 2025-04-12 14:36:43 -04:00
6e05f1334a Updated grammar rules for new scope system.
Need to implement scope fields still though.
2025-04-12 14:29:39 -04:00
4e66f7875a Added grammars for function definitions. 2025-04-12 09:47:34 -04:00
845a7f87b2 Updated exception execution.
Now represents a sort of traceback.
2025-03-30 17:05:29 -04:00
5b163b26dd Fixed static allocation of builting functions.
Co-authored-by: bgiobbe-tcs <bgiobbe.tcs@gmail.com>
2025-03-30 16:09:09 -04:00
f8ee7cc66e Oh boy do I love formatting. 2025-03-26 18:16:13 -04:00
d699f492fa Updated to use the builtin functions list.
Wow, the type declaration for builtin_fns[] is cursed.

Also, first commit from zed :D.
2025-03-26 18:06:17 -04:00
018404eea1 Wait why's that still there it's done already. 2025-03-25 15:07:22 -04:00
18c61d86dc Fixed crash on unknown var name. 2025-03-11 17:56:04 +09:00
eaf65697de Reformatted comments to be more consistent & clear. 2025-03-03 10:15:39 -05:00
ce6e558761 Updated README.md. 2025-02-28 13:10:19 -05:00
db6e591d77 Added back all the old functions. 2025-02-26 12:43:55 -05:00
58554e8727 Fixed some expression mishandling. 2025-02-25 08:17:17 -05:00
4b9f269938 Updated Unity. 2025-02-25 08:13:42 -05:00
3d0f534017 Plans. 2025-02-25 08:11:22 -05:00
38c6a9113c Added sub to the builtin functions. 2025-02-25 08:04:43 -05:00
109bcb3fa5 Fixed function calls on builtin.
Random pointer indirection.
2025-02-25 07:56:25 -05:00
79b60e4853 Fixed name error handling. 2025-02-25 07:52:02 -05:00
8cab531129 Fixed memory error in exception printing.
Conflation between char* & char**.
2025-02-25 07:47:08 -05:00
eb3fbca030 Some things are broken.
EVERYTHING IS BROKEN
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.
2025-02-15 11:03:51 -05:00
40b91b96bd Adds type infrastructure.
Only floats (nums) are available at the moment.
2025-02-08 11:01:56 -05:00
67f659e263 Merge branch 'master' into types. 2025-02-08 10:23:15 -05:00
b8ce193299 Scope works. 2025-02-08 10:21:22 -05:00
11401e75a7 Fixed. 2025-02-08 09:25:54 -05:00
a6be116a2d Adds test for integer arithmetic. 2025-02-07 17:33:18 -05:00
3678940537 Adds preliminary type enums. 2025-02-07 17:29:30 -05:00
cddf5eed76 Creates config.mk.
Moves a bunch of stuff clutter out of the Makefile.
2025-02-04 18:11:30 -05:00
c685d6e55f TODO.md. 2025-02-04 17:59:39 -05:00
6cbc28e092 Maybe fixed some memory leaks.
I don't know cauase lsan isn't working.
2025-02-04 17:55:47 -05:00
408a5a46c0 Can set and ref vars in main scope.
One can now assign and reference variables in the main scope, as it has
been put in a block through the parser.

Also added some tests for this purpose.
2025-02-04 17:28:06 -05:00
6903aeb383 Added Makefile rule to count LOC. 2025-02-04 17:03:55 -05:00
fbabf71d64 Variable references in blocks now work.
:D :D :D
2025-02-04 17:00:28 -05:00
78e31c3e27 Fixed block parsing for real this time.
I am such an idiot, ast_block_data_init() just ignored the given array
lol.
2025-02-04 16:50:04 -05:00
be3baee74e Fixed dlists.
Also added tests.
2025-02-04 16:40:58 -05:00
d5a07fae56 Started adding scope.
Doesn't really do anything yet as blocks aren't fully implemented.
2025-02-04 11:30:45 -05:00
4153903443 Added types example.
This is gonna be hard to implement...
2025-02-03 13:37:21 -05:00
77d58bd69e Updated STATUS.md. 2025-02-03 13:10:48 -05:00
47a3143752 Merge remote-tracking branch 'refs/remotes/origin/master' 2025-02-03 13:07:10 -05:00
4fb73b3c6f Fixed word accumulator. 2025-02-03 13:02:47 -05:00
e5c58d5fc5 Fixed tests.
Can't have spaces for some reason. Not a real fix.
2025-02-03 13:02:28 -05:00
5ecdf2d89a Sorted .gitignore again because that's important. 2025-02-01 21:38:45 -05:00
32 changed files with 1127 additions and 232 deletions

2
.gitignore vendored
View File

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

View File

@@ -1,41 +1,4 @@
NAME = scl include config.mk
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)/val
RESETCOLOR = \033[0m
WHITE = $(RESETCOLOR)\033[37m
WHITE_BOLD = $(RESETCOLOR)\033[37;1m
RED_BOLD = $(RESETCOLOR)\033[31;1m
all: $(TARGET) all: $(TARGET)
@@ -99,10 +62,16 @@ test: $(TARGET) $(TEST_BIN_FILES)
@ $(PRINT) "$(WHITE_BOLD)Running validation tests...$(RESETCOLOR)" @ $(PRINT) "$(WHITE_BOLD)Running validation tests...$(RESETCOLOR)"
$(BATS) $(TEST_VAL_DIR) $(BATS) $(TEST_VAL_DIR)
# Clean out objects, binaries, and built artifacts.
clean: clean:
@ $(PRINT) "$(WHITE_BOLD)Cleaning up...$(RESETCOLOR)" @ $(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) rm -rf $(OBJ_DIR)/*.o $(TEST_OBJ_DIR)/*.o $(TEST_BUILD_DIR)/test.out $(TARGET) $(GRAM_DIR)/* $(UNITY_OBJ)
.PHONY: all clean test nocolor release run # 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.
.PRECIOUS: $(TEST_OBJ_FILES) .PRECIOUS: $(TEST_OBJ_FILES)

View File

@@ -1,12 +1,20 @@
# SCL: Simple CAS Language # SCL: Simple CAS Language
Version v1.0-alpha 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.
## Usage ## Usage
To download and run:
```bash ```bash
git clone https://git.signorovitch.org/jacob/scl -b stable git clone https://git.signorovitch.org/jacob/scl -b stable && cd scl
cd scl
make release make release
./scl.out ./scl.out
``` ```
@@ -22,12 +30,7 @@ make all test
If you wish to run tests, make sure to run `git clone --recurse-submodules` to 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. include the [Unity](https://github.com/ThrowTheSwitch/Unity) test framework.
## Current State ## Syntax
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: As one would expect, you can evaluate simple infix expressions:
@@ -36,12 +39,30 @@ As one would expect, you can evaluate simple infix expressions:
= 2 = 2
``` ```
You can also define your own functions: You can also define your own functions and variables:
```scl ```scl
> f(x) = 2x > f(x) = 2x
> f(2) > 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
= 4 = 4
> f(n) # Pass a reference to n.
= 1
> n
> 1
``` ```
Symbolic algebra is done in the following manner: Symbolic algebra is done in the following manner:
@@ -56,9 +77,13 @@ SCL will dynamically decide on types, but you can state them explicitly as
well: well:
```scl ```scl
> f(x:int) = 2x > f(x: int): int = 2x
> f(2.2) > f(3)
! f(x:int): x must be of type int. = 6
> f(3.1)
! Traceback:
! In call to `f(x: int): int`:
! TypeError (58): Argument `x` must be of type `int`.
``` ```
Variables can be defined, with several attributes: Variables can be defined, with several attributes:

View File

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

7
TODO.md Normal file
View File

@@ -0,0 +1,7 @@
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.

40
config.mk Normal file
View File

@@ -0,0 +1,40 @@
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

48
examples/types.scl Normal file
View File

@@ -0,0 +1,48 @@
# 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">

183
src/ast.c
View File

@@ -1,7 +1,10 @@
#include <inttypes.h>
#include <stdio.h> #include <stdio.h>
#include "include/ast.h" #include "include/ast.h"
#include "include/dstr.h" #include "include/dstr.h"
#include "include/gc.h"
#include "include/scope.h"
#include "include/util.h" #include "include/util.h"
extern AST* root; extern AST* root;
@@ -12,13 +15,34 @@ static char* asttype_names[] = {
[AST_TYPE_VREF] = "VAR REFERENCE", [AST_TYPE_VREF] = "VAR REFERENCE",
[AST_TYPE_VDEF] = "VAR DEFINITION", [AST_TYPE_VDEF] = "VAR DEFINITION",
[AST_TYPE_BLOCK] = "BLOCK", [AST_TYPE_BLOCK] = "BLOCK",
[AST_TYPE_EXC] = "EXCEPTION",
[AST_TYPE_FDEF] = "FUNCTION DEFINITION",
[AST_TYPE_ARG] = "DEFINITION ARGUMENT"
}; };
AST* ast_init(ASTType type, void* data) { 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* ast = malloc(sizeof(AST));
ast->type = type; ast->type = type;
ast->data = data; ast->data = data;
ast->scope = scope;
return ast; return ast;
} }
@@ -31,6 +55,32 @@ void ast_destroy(AST* ast) {
case AST_TYPE_CALL: ast_call_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_VREF: ast_vref_data_destroy(ast->data); break;
case AST_TYPE_VDEF: ast_vdef_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;
default: default:
log_dbgf("Unknown ast type %d (max: %d)", ast->type, AST_TYPE_MAX); log_dbgf("Unknown ast type %d (max: %d)", ast->type, AST_TYPE_MAX);
} }
@@ -38,21 +88,29 @@ void ast_destroy(AST* ast) {
free(ast); free(ast);
} }
void ast_print(AST* ast) { ast_print_i(ast, 0); } void ast_print(AST* ast) {
if (!ast) return;
ast_print_i(ast, 0);
}
void ast_print_i(AST* ast, int i) { void ast_print_i(AST* ast, int i) {
INDENT_BEGIN(i); INDENT_BEGIN(i);
INDENT_TITLE("AST", ast); INDENT_TITLE("AST", ast);
INDENT_FIELD("type", "%s", asttype_names[ast->type]); INDENT_FIELD("type", "%s", asttype_names[ast->type]);
INDENT_FIELD("scope", "%p", ast->scope);
INDENT_FIELD_EXT_NONL_START("data"); INDENT_FIELD_EXT_NONL_START("data");
switch (ast->type) { switch (ast->type) {
case AST_TYPE_NUM: case AST_TYPE_NUM:
printf("%s %lf\n", INDENT_spacing->buf, *(ASTNumData*)ast->data); printf("%s %lf\n", INDENT_spacing->buf, *(ASTNumData*)ast->data);
break; break;
case AST_TYPE_CALL: ast_call_print(ast->data, i + 2); 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_VREF: ast_vref_print(ast->data, i + 2); break;
case AST_TYPE_VDEF: ast_vdef_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); default: exit(1);
} }
INDENT_FIELD_NONL_END; INDENT_FIELD_NONL_END;
@@ -77,6 +135,39 @@ void ast_num_print(ASTNumData* data, int i) {
INDENT_END; 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) { ASTCallData* ast_call_data_init(char* to, size_t argc, AST** argv) {
talloc(ASTCallData, call); talloc(ASTCallData, call);
@@ -97,6 +188,15 @@ void ast_call_data_destroy(ASTCallData* call) {
free(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) { void ast_call_print(ASTCallData* data, int i) {
INDENT_BEGIN(i); INDENT_BEGIN(i);
@@ -123,6 +223,11 @@ void ast_vdef_data_destroy(ASTVDefData* vdef) {
free(vdef); free(vdef);
} }
void ast_vdef_data_destroy_psv(ASTVDefData* vdef) {
free(vdef->name);
free(vdef);
}
void ast_vdef_print(ASTVDefData* vdef, int depth) { void ast_vdef_print(ASTVDefData* vdef, int depth) {
INDENT_BEGIN(depth); INDENT_BEGIN(depth);
@@ -160,27 +265,91 @@ void ast_vref_print(ASTVrefData* data, int i) {
ASTBlockData* ast_block_data_init(AST** inside, size_t ln) { ASTBlockData* ast_block_data_init(AST** inside, size_t ln) {
ASTBlockData* block = malloc(sizeof(ASTBlockData)); ASTBlockData* block = malloc(sizeof(ASTBlockData));
block->inside = calloc(ln, sizeof(AST)); block->inside = inside;
block->ln = ln; block->ln = ln;
return block; return block;
} }
void ast_block_data_destroy(ASTBlockData* block) { void ast_block_data_destroy(ASTBlockData* block) {
for (size_t i = 0; i < block->ln; i++) { for (size_t i = 0; i < block->ln; i++) { ast_destroy(block->inside[i]); }
ast_destroy(block->inside[i]);
}
free(block->inside); free(block->inside);
free(block); free(block);
} }
void ast_block_data_print(ASTBlockData* data, int depth) { 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_BEGIN(depth);
INDENT_TITLE("BLOCK", data); INDENT_TITLE("ASTBlockData", data);
INDENT_FIELD("ln", "%ld", data->ln); INDENT_FIELD("ln", "%ld", data->ln);
INDENT_FIELD_LIST("inside", data->inside, data->ln, ast_print_i); INDENT_FIELD_LIST("inside", data->inside, data->ln, ast_print_i);
INDENT_END; 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;
}

133
src/builtin.c Normal file
View File

@@ -0,0 +1,133 @@
#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

@@ -21,21 +21,24 @@ void dlist_destroy(DList* dlist) {
void dlist_destroypsv(DList* dlist) { free(dlist); } void dlist_destroypsv(DList* dlist) { free(dlist); }
// Check whether the buffer is overflowing and resize it if necessary. // Check whether the buffer is about to overflow and resize it if necessary.
void dlist_check_resz(DList* dlist, size_t ln) { void dlist_check_resz(DList* dlist) {
while (dlist->ln + ln + 1 > dlist->sz) { while ((dlist->ln + 1) * sizeof(void*) >= dlist->sz) {
// Double the buffer size when overflown. // Double the buffer size when overflown.
dlist->sz *= 2; dlist->sz *= 2;
dlist->buf = realloc(dlist->buf, dlist->sz); dlist->buf = realloc(dlist->buf, dlist->sz);
log_dbgf( log_dbgf(
"dlist @ %p doubled from %ld to %ld", dlist, dlist->sz / 2, dlist->sz "dlist @ %p doubled from %ld to %ld", dlist, dlist->sz / 2,
dlist->sz
); );
} }
} }
void dlist_append(DList* dest, void* src) { void dlist_append(DList* dest, void* src) {
dlist_check_resz(dest, 1); dlist_check_resz(dest);
log_dbgf("added %p to dlist@%p", src, dest);
dest->buf[dest->ln] = src; dest->buf[dest->ln] = src;
dest->ln += 1; dest->ln++;
} }

View File

@@ -3,80 +3,156 @@
#include <string.h> #include <string.h>
#include "include/ast.h" #include "include/ast.h"
#include "include/builtin.h"
#include "include/dlist.h"
#include "include/exec.h" #include "include/exec.h"
#include "include/htab.h"
#include "include/scope.h"
#include "include/util.h" #include "include/util.h"
extern AST* root; AST* exec_start(AST* ast) {
ASTNumData exec_exp(AST* ast) {
log_dbg("Started execution."); 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) { switch (ast->type) {
case AST_TYPE_CALL: return exec_call(ast); case AST_TYPE_BLOCK: return exec_block(ast, parent);
case AST_TYPE_NUM: return *(ASTNumData*)ast->data; case AST_TYPE_CALL: return exec_call(ast, parent);
case AST_TYPE_VREF: return exec_vref(ast); case AST_TYPE_NUM:
case AST_TYPE_VDEF: return exec_vdef(ast); return ast_init(
default: printf("what\n"); AST_TYPE_NUM, ast_num_data_init(*(ASTNumData*)ast->data)
exit(1); );
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);
} }
} }
ASTNumData exec_call(AST* ast) { 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) {
log_dbg("Started call execution."); log_dbg("Started call execution.");
fflush(stdout); ASTCallData* data = (ASTCallData*)ast->data;
ASTCallData* calldata = (ASTCallData*)ast->data; size_t argc = data->argc;
if (calldata->argc >= 1) { AST** argv = data->argv;
if (!strcmp(calldata->to, "sum")) { char* fname = data->to;
double total = exec_exp(calldata->argv[0]);
for ( ast->scope = parent;
size_t i = 1;
i < calldata->argc; AST* fdef = ast_find(ast->scope, fname);
total += exec_exp(calldata->argv[i++])
if (fdef == NULL)
return ast_init(
AST_TYPE_EXC, ast_exc_data_init("No such function found.", NULL)
); );
return total; switch (fdef->type) {
} else if (!strcmp(calldata->to, "sub")) { case AST_TYPE_BIF:
double total = exec_exp(calldata->argv[0]); ASTBIFData bifdata = fdef->data;
return bifdata(argc, argv, parent);
for ( case AST_TYPE_FDEF: return exec_cf(fdef, argc, argv);
size_t i = 1; default:
i < calldata->argc; return ast_init(AST_TYPE_EXC, ast_exc_data_init("Good job!", NULL));
total -= exec_exp(calldata->argv[i++]) }
);
return total;
} else if (!strcmp(calldata->to, "mul")) {
double total = exec_exp(calldata->argv[0]);
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;
} }
ASTNumData exec_vdef(AST* ast) { 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; ASTVDefData* data = (ASTVDefData*)ast->data;
AST* val = data->val; AST* val = data->val;
return exec_exp(val); char* key = data->name;
scope_add(parent, key, val); // Add variable definition to parent scope.
return exec_exp(val, parent);
} }
ASTNumData exec_vref(AST* ast) { AST* exec_vref(AST* ast, Scope* parent) {
return *ast_num_data_init(42.42); // 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
);
return ast_init(AST_TYPE_EXC, ast_exc_data_init(msg, NULL));
}
return exec_exp(found, ast->scope);
}
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.
} }
void exec_print(double n) { printf("= %lf\n", n); } 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++;
}

57
src/gc.c Normal file
View File

@@ -0,0 +1,57 @@
#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

@@ -56,13 +56,37 @@
%type<argarr> argstart; %type<argarr> argstart;
%type<exps> blockstart; %type<exps> blockstart;
%type<exps> block; %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: input:
inputstart {
$$ = $1;
}
| input EXPSEP exp {
dlist_append($1, $3);
$$ = $1;
}
;
inputend:
%empty %empty
| exp { root = $1; } | input {
| input EXPSEP exp { root = $3; } root = ast_init(AST_TYPE_BLOCK, ast_block_data_init((AST**) $1->buf, $1->ln));
dlist_destroypsv($1);
}
; ;
argstart: argstart:
@@ -100,11 +124,16 @@ block:
exp: exp:
NUM { $$ = ast_init(AST_TYPE_NUM, ast_num_data_init($1)); } NUM { $$ = ast_init(AST_TYPE_NUM, ast_num_data_init($1)); }
| BLOCKS exp BLOCKE { $$ = $2; } // 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 { | BLOCKS block BLOCKE {
size_t i = $2->ln - 1; $$ = ast_init(AST_TYPE_BLOCK, ast_block_data_init((AST**) $2->buf, $2->ln));
$$ = $2->buf[i];
} }
| SUB exp { | SUB exp {
@@ -124,9 +153,9 @@ exp:
} }
// Variable reference. // Variable reference.
//| WORD { | WORD {
// $$ = ast_init(AST_TYPE_VREF, ast_vref_data_init($1)); $$ = ast_init(AST_TYPE_VREF, ast_vref_data_init($1));
//} }
| WORD GROUPS arg GROUPE { | WORD GROUPS arg GROUPE {
size_t argc = $3->ln; size_t argc = $3->ln;

View File

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

View File

@@ -1,41 +1,97 @@
#ifndef AST_H #ifndef AST_H
#define AST_H #define AST_H
#include "scope.h"
#include <stdlib.h> #include <stdlib.h>
// The type of an `AST`.
typedef enum { typedef enum {
AST_TYPE_NUM, // A number. // 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_CALL, // A function call.
AST_TYPE_VDEF, // A variable definition. AST_TYPE_VDEF, // A variable definition.
AST_TYPE_VREF, // A variable reference. AST_TYPE_VREF, // A variable reference.
AST_TYPE_BLOCK, // A block of code (scope). AST_TYPE_BLOCK, // A block of code (scope).
AST_TYPE_MAX = AST_TYPE_BLOCK, AST_TYPE_FDEF, // A function definition.
AST_TYPE_ARG, // A definition argument.
AST_TYPE_MAX = AST_TYPE_ARG,
} ASTType; } ASTType;
// An Abstract Syntax Tree.
typedef struct { typedef struct {
ASTType type; ASTType type; // The type of the `AST`.
void* data; void* data; // The data of the `AST`.
Scope* scope; // The scope of the `AST`.
} AST; } AST;
// Create a new `AST`.
AST* ast_init(ASTType type, void* data); 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); void ast_destroy(AST* ast);
// Destroy an `AST`.
void ast_destroy_psv(AST* ast);
// Print an `AST`, recursively.
void ast_print(AST* ast); void ast_print(AST* ast);
// Helper function to `ast_print()`, where `i` is indentation level.
void ast_print_i(AST* ast, int i); void ast_print_i(AST* ast, int i);
// A number.
typedef double ASTNumData; typedef double ASTNumData;
// Create a new `ASTNumData`.
ASTNumData* ast_num_data_init(double val); ASTNumData* ast_num_data_init(double val);
// Destroy an `ASTNumData`.
void ast_num_data_destroy(ASTNumData* num); void ast_num_data_destroy(ASTNumData* num);
// Print an `ASTNumData`.
void ast_num_print(ASTNumData*, int i); 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 { typedef struct {
char* to; // What the call's to. char* to; // What the call's to.
size_t argc; // Argument count. size_t argc; // Argument count.
AST** argv; // Argument vector. AST** argv; // Argument vector.
} ASTCallData; } ASTCallData;
// Create a new `ASTCallData`.
ASTCallData* ast_call_data_init(char* to, size_t argc, AST** argv); ASTCallData* ast_call_data_init(char* to, size_t argc, AST** argv);
// Destroy an `ASTCallData` recursively.
void ast_call_data_destroy(ASTCallData* call); 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); void ast_call_print(ASTCallData*, int i);
// A variable definition's data. // A variable definition's data.
@@ -44,9 +100,13 @@ typedef struct {
AST* val; AST* val;
} ASTVDefData; } ASTVDefData;
// Create a new `ASTVDefData`.
ASTVDefData* ast_vdef_data_init(char* name, AST* val); ASTVDefData* ast_vdef_data_init(char* name, AST* val);
// Destroys the vdef, its name, and its ->val. // Destroys the `ASTVDefData`, `ASTVDefData->name`, and `ASTVDefData->val`.
void ast_vdef_data_destroy(ASTVDefData* vdef); 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); void ast_vdef_print(ASTVDefData*, int depth);
// A variable reference's data. // A variable reference's data.
@@ -54,18 +114,56 @@ typedef struct {
char* to; // What the reference's to. char* to; // What the reference's to.
} ASTVrefData; } ASTVrefData;
// Create a new `ASTVRefData`.
ASTVrefData* ast_vref_data_init(char* to); ASTVrefData* ast_vref_data_init(char* to);
// Destroy an `ASTVRefData`.
void ast_vref_data_destroy(ASTVrefData* call); void ast_vref_data_destroy(ASTVrefData* call);
// Print an `ASTVRefData`.
void ast_vref_print(ASTVrefData*, int i); void ast_vref_print(ASTVrefData*, int i);
// A code block.
typedef struct { typedef struct {
AST** inside; // What's inside the block. AST** inside; // What's inside the block.
size_t ln; // How many ASTs are in the block. size_t ln; // How many ASTs are in the block.
} ASTBlockData; } ASTBlockData;
// Create a new `ASTBlockData`.
ASTBlockData* ast_block_data_init(AST** inside, size_t ln); ASTBlockData* ast_block_data_init(AST** inside, size_t ln);
// Destroy a block. Also destroy all ASTs inside. // Destroy an `ASTBlockData`, recursively.
void ast_block_data_destroy(ASTBlockData* block); void ast_block_data_destroy(ASTBlockData* block);
void ast_block_data_print(ASTBlockData*, int i); // 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 #endif

31
src/include/builtin.h Normal file
View File

@@ -0,0 +1,31 @@
#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

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

View File

@@ -11,15 +11,17 @@ typedef struct {
size_t ln; // The number of characters in the buffer. size_t ln; // The number of characters in the buffer.
} Dstr; } Dstr;
// Initialize a `DStr`.
Dstr* dstr_init(void); Dstr* dstr_init(void);
// Destroy a `DStr`.
void dstr_destroy(Dstr* dstr); void dstr_destroy(Dstr* dstr);
// Destroy Dstr structure but preserve ->buf. // Destroy `DStr` structure but preserve `DStr->buf`.
void dstr_destroypsv(Dstr* dstr); 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); 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); void dstr_appendch(Dstr* dest, char ch);
#endif #endif

View File

@@ -2,11 +2,31 @@
#define EXEC_H #define EXEC_H
#include "ast.h" #include "ast.h"
#include "scope.h"
ASTNumData exec_exp(AST* ast); // Start executing at the root of the AST. Initialize the `scope`.
ASTNumData exec_call(AST* ast); AST* exec_start(AST* ast);
ASTNumData exec_vref(AST* ast); // Execute an expression. Delegates to the other executor functions.
ASTNumData exec_vdef(AST* ast); 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.
void exec_print(double n); 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 #endif

View File

@@ -12,6 +12,7 @@
// Offset basis. // Offset basis.
#define FNV1A_BASIS_64 0xcbf29ce484222325u #define FNV1A_BASIS_64 0xcbf29ce484222325u
// Hash a string `str` of length `ln`.
uint64_t fnv1a_hash(char* str, size_t ln); uint64_t fnv1a_hash(char* str, size_t ln);
#endif #endif

31
src/include/gc.h Normal file
View File

@@ -0,0 +1,31 @@
#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

@@ -5,7 +5,8 @@
#include <stdint.h> #include <stdint.h>
#define HTAB_FN fnv1a_hash // Function used for hashing. #define HTAB_FN fnv1a_hash // Function used for hashing.
#define HTAB_SPACE 1024 // Number of entries possible in the hash table; must be #define HTAB_SPACE \
1024 // Number of entries possible in the hash table; must be
// power of 2. // power of 2.
// Hash Table. // Hash Table.

22
src/include/scope.h Normal file
View File

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

View File

@@ -1,17 +1,30 @@
#ifndef UTIL_H #ifndef UTIL_H
#define 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 #ifdef DBG // Debug macros
// Log a message. // Log a message.
#define log_dbg(msg) \ #define log_dbg(msg) \
printf("\033[37;1mdbg\033[0m:\033[37m%s\033[0m:\033[32m " msg "\033[0m\n", \ printf( \
__func__); "\033[37;1mdbg\033[0m:\033[37m%s\033[0m:\033[32m " msg "\033[0m\n", \
__func__ \
);
// Log a message with formatting. // Log a message with formatting.
#define log_dbgf(msg, ...) \ #define log_dbgf(msg, ...) \
printf("\033[37;1mdbg\033[0m:\033[37m%s\033[0m:\033[32m " msg "\033[0m\n", \ printf( \
__func__, __VA_ARGS__); "\033[37;1mdbg\033[0m:\033[37m%s\033[0m:\033[32m " msg "\033[0m\n", \
__func__, __VA_ARGS__ \
);
#else // ifdef DBG #else // ifdef DBG
#define log_dbg(msg) #define log_dbg(msg)
@@ -50,18 +63,25 @@
// Print & indent the title of a section. // Print & indent the title of a section.
#define INDENT_TITLE(THING, WHERE) \ #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. // Print & indent a thing.
#define INDENT_FIELD(FIELD, VAL, ...) \ #define INDENT_FIELD(FIELD, VAL, ...) \
printf("%s " COL_BWHI FIELD ": " COL_RESET COL_WHI VAL COL_RESET "\n", \ printf( \
INDENT_spacing->buf, __VA_ARGS__); "%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. // Print & indent a thing with a newline before the val.
#define INDENT_FIELD_NL(FIELD, VAL, ...) \ #define INDENT_FIELD_NL(FIELD, VAL, ...) \
printf("%s " COL_BWHI FIELD ":" COL_RESET "\n %s " COL_WHI VAL COL_RESET \ printf( \
"%s " COL_BWHI FIELD ":" COL_RESET "\n %s " COL_WHI VAL COL_RESET \
"\n", \ "\n", \
INDENT_spacing->buf, INDENT_spacing->buf, __VA_ARGS__); INDENT_spacing->buf, INDENT_spacing->buf, __VA_ARGS__ \
);
// Print & indent a thing without any newline. // Print & indent a thing without any newline.
#define INDENT_FIELD_EXT_NONL_START(FIELD) \ #define INDENT_FIELD_EXT_NONL_START(FIELD) \
@@ -77,9 +97,14 @@
printf(COL_BWHI "%s ]\n" COL_RESET, INDENT_spacing->buf); printf(COL_BWHI "%s ]\n" COL_RESET, INDENT_spacing->buf);
// End an indent block. // End an indent block.
#define INDENT_END printf(COL_RESET); dstr_destroy(INDENT_spacing); #define INDENT_END \
printf(COL_RESET); \
dstr_destroy(INDENT_spacing);
// Allocate a pointer with a type. #define INDENT_FIELD_LIST_OPEN(FIELD) \
#define talloc(T, X) T* X = malloc(sizeof(T)); 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);
#endif #endif

View File

@@ -26,7 +26,7 @@ void argarr_destroy(ArgArr* argarr) {
void argarr_destroypsv(ArgArr* argarr) { free(argarr); } void argarr_destroypsv(ArgArr* argarr) { free(argarr); }
void argarr_add(ArgArr* argarr, AST* arg) { void argarr_add(ArgArr* argarr, AST* arg) {
if ((argarr->ln + 1) * argarr->sz > argarr->sz) { if ((argarr->ln + 1) * sizeof(AST*) > argarr->sz) {
argarr->sz *= 2; argarr->sz *= 2;
argarr->buf = realloc(argarr->buf, argarr->sz); argarr->buf = realloc(argarr->buf, argarr->sz);
log_dbgf( log_dbgf(
@@ -72,10 +72,10 @@ double acc_float(int c) {
char* acc_word(int c) { char* acc_word(int c) {
Dstr* val = dstr_init(); Dstr* val = dstr_init();
do { while (isalpha(*inp)) {
dstr_appendch(val, *(inp - 1)); dstr_appendch(val, *(inp - 1));
inp++; inp++;
} while (isalpha(*inp)); }
dstr_appendch(val, *(inp - 1)); dstr_appendch(val, *(inp - 1));
char* ret = val->buf; char* ret = val->buf;

View File

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

37
src/scope.c Normal file
View File

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

41
test/test_dlist.c Normal file
View File

@@ -0,0 +1,41 @@
#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

@@ -84,26 +84,48 @@ bin() { ./scl.out $1 | tail -n1; }
[ "$output" = "= 9.000000" ] [ "$output" = "= 9.000000" ]
} }
#@test "basic blocks" { @test "basic blocks" {
# run bin "{ 1 }" run bin "{1}"
# [ "$output" = "= 1.000000" ] [ "$output" = "= 1.000000" ]
#
# run bin "2 + { 3; 4 }" run bin "2+{3;4}"
# [ "$output" = "= 6.000000" ] [ "$output" = "= 6.000000" ]
#
# run bin "5 * { 1 + 1; 5 * 2 }" run bin "5*{1+1;5*2}"
# [ "$output" = "50.000000" ] 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 "variable definition" {
# run bin "x = 1"
# [ "$output" = "= 1.000000" ]
#
# run bin "x = 1; x + 1"
# [ "$output" = "= 2.000000" ]
#}
#
#@test "function definition" { #@test "function definition" {
# run bin "f(n)=2*n; f(2)" # run bin "f(n)=2*n; f(2)"
# [ "$output" = "= 4.000000" ] # [ "$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!" ]
}