26 Commits
v0.2 ... master

Author SHA1 Message Date
4e5b12b5b2 Updated README.md. 2025-09-01 00:18:59 -04:00
0e6bb7aa16 Cleaned up. 2025-08-28 11:12:19 -04:00
14ddf51f3c Bump version in README.md.
NOTE: changed versioning system slightly.
2025-08-27 12:30:28 -04:00
ac1d76361f Updated README.md. 2025-08-27 01:14:49 -04:00
b789193484 Recursion works.
FINALLY YESSSS 🎉🎉🎉
2025-08-27 01:10:58 -04:00
ab97f78fab Work on conditionals and equalities. 2025-08-27 00:41:48 -04:00
7c0b212ab4 Added a bit of familiar sugar to conditionals. 2025-08-26 23:50:06 -04:00
80c8038374 Added basic conditionals. 2025-08-26 23:47:15 -04:00
e3cd78e1b4 Added basic implementation of booleans. 2025-08-26 22:58:58 -04:00
b7b90f528b Created an awful hack for a clean exit on die(). 2025-08-26 18:30:08 -04:00
0ef44be808 Fixed some builtin arithmetic logic. 2025-08-26 18:24:28 -04:00
8924818ec4 Updated README.md. 2025-08-26 16:37:48 -04:00
73efa7e136 Cleaned up a bit. 2025-08-26 16:37:38 -04:00
4e8d7131d6 Merge branch 'lambda' 2025-08-26 16:26:06 -04:00
bfce18ab81 Completed lambda implementation of functions.
Hooray! 🎉
2025-08-26 16:25:20 -04:00
7b648c4bd7 Fixed negative expressions. 2025-08-26 16:17:54 -04:00
4ec5d1c075 Fixed everything.
- Cleaned up call stack
- Fixed builtin function execution
- Fixed lambda execution
- Fixed call parsing
2025-08-23 10:53:12 -04:00
5ba070ced7 Things are still in motion. Last thing worked on: call execution. 2025-07-05 11:00:11 -04:00
8256643c0b Updated example. 2025-06-30 01:24:26 -04:00
29a217928e Things are in motion. 2025-06-30 01:22:39 -04:00
482f2b4877 Big refactor to unclutter ast.h.
Moves all print functions to own file for sanity.

Also, just hit 2,000 lines; nice.
2025-06-28 12:25:10 -04:00
36fd838a8f Done with some things, saving here. 2025-06-28 12:05:07 -04:00
289243de38 Added lambda parsing. 2025-06-28 11:15:21 -04:00
67aafb3ead Updated syntax to abbreviate functions defs. 2025-06-28 10:55:15 -04:00
3b5bee0695 Added trap for GDB &c. 2025-06-18 17:21:45 -04:00
970fc39198 Cleaned up types with anonymous structs. 2025-06-14 11:22:07 -04:00
21 changed files with 748 additions and 388 deletions

View File

@@ -1,20 +1,20 @@
# SCL: Simple CAS Language
Version v1.0-beta
*v0.3*
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.
its current state, SCL can be used as a functional programming language capable
of performing simple arithmetic. The codebase is about 2,000 lines of
handwritten C, including a parser, interpreter, and runtime. It uses a linked
environment scoping model.
## Usage
To download and run:
```bash
git clone https://git.signorovitch.org/jacob/scl -b stable && cd scl
git clone https://git.signorovitch.org/scl/scl -b stable && cd scl
make release
./scl.out
```
@@ -22,75 +22,44 @@ make release
### For Development
```bash
git clone git@signorovitch.org:jacob/scl --recurse-submodules && cd scl
git clone git@signorovitch.org:scl/scl --recurse-submodules && cd scl
make all test
./scl.out
```
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.
*Note that tests are currently in poor use. I hope to amend this in the future.*
## Syntax
As one would expect, you can evaluate simple infix expressions:
SCL's syntax will feel familiar to other functional programming languages.
```scl
> 1 + 1
> x = 3 + 3 * 3; x + 1
= 13
> f(x) x + 1
> f(1)
= 2
```
You can also define your own functions and variables:
```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
> (\(x) 2 * x)(5)
= 10
> f(g) g(2)
> f(\(x) 2 * x)
= 4
> f(n) # Pass a reference to n.
= 1
> n
> 1
```
Symbolic algebra is done in the following manner:
Here's a simple factorial function, using recursion:
```scl
> f(x) = x^4
> diff(f, x:sym, 2)
= 12x^2
> factorial(n) {
> if (n == 0) { 1 }
> else { n * factorial(n - 1) }
> }
```
SCL will dynamically decide on types, but you can state them explicitly as
well:
SCL's syntax is quite flexible. The above function could be more concisely
written as:
```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`.
```
Variables can be defined, with several attributes:
```scl
> a = 1 // Interpret type automatically.
> b:int = 1 // Must be int.
> c:const:int = 1 // Constant: value can never change.
> x:sym // Treated symbolicaly.
> factorial(n) ? n == 0 1 n * factorial(n - 1)
```

View File

@@ -12,10 +12,10 @@
- [x] Order of operations
- [x] Parse function application
- [x] Parse order of operations with parenthesis
- [ ] Parse variable invocation
- [x] Parse variable invocation
- [x] Parse variable definition
- [ ] Parse types
- [ ] Parse function definition
- [x] Parse function definition
- [ ] Parse lists/arrays/vectors
- [x] Parse blocks
- [ ] Parse control flow
@@ -32,9 +32,9 @@
- [ ] Executer
- [x] Exec function calls
- [ ] Exec variable use
- [ ] Exec variable definition
- [ ] Exec function definition
- [x] Exec variable use
- [x] Exec variable definition
- [x] Exec function definition
- [ ] Exec symbolic variables
- [ ] Exec control flow statements
- [ ] Exec variadic functions

10
TODO.md
View File

@@ -1,7 +1,9 @@
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.
EXCEPTION HANDLING: exception ast type should have as data a giant enum of
possible types, rather than a char* message. A description of each type could be
handled under the exception type and print logic. For now, executor checks
message for special exceptions e.g. exit().
Change editor to GNU Readline.
Make variables persist through lines in the editor.
Return syntax errors as exceptions.

25
definitions.md Normal file
View File

@@ -0,0 +1,25 @@
f = \(x) 2 * x
g = \(h) \(x) h(h(x))
f(2) => 4
g(f)(2) => 8
CALL
argc: 1
argv: [ 2 ]
to:
CALL
argc: 1
argv: [
VREF
name: f
]
to:
VREF
name: g
fname: NULL
expression + arguments = call = expression
expression + parameters = lambda = expression
expression + name = variable = expression

View File

@@ -1,3 +1,2 @@
f(n) = 2 * n
f(5)
apply(f, x) f(x)
apply(\(x) x + 1, 2)

214
src/ast.c
View File

@@ -2,24 +2,12 @@
#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;
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* ast_init(ASTType type, void* data) {
AST* ast = gc_alloc(sizeof(AST), GC_TYPE_AST);
@@ -51,36 +39,17 @@ 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_BOOL: ast_bool_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_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_LAMBDA: ast_lambda_data_destroy(ast->data); break;
default:
log_dbgf("Unknown ast type %d (max: %d)", ast->type, AST_TYPE_MAX);
}
@@ -88,35 +57,6 @@ void ast_destroy_psv(AST* ast) {
free(ast);
}
void ast_print(AST* ast) {
if (!ast) return;
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);
}
INDENT_FIELD_NONL_END;
INDENT_END;
}
ASTNumData* ast_num_data_init(double val) {
talloc(ASTNumData, num);
@@ -127,14 +67,16 @@ ASTNumData* ast_num_data_init(double val) {
void ast_num_data_destroy(ASTNumData* num) { free(num); }
void ast_num_print(ASTNumData* data, int i) {
INDENT_BEGIN(i);
ASTBoolData* ast_bool_data_init(int val) {
talloc(ASTBoolData, bol);
INDENT_FIELD("data", "%lf", *data);
*bol = val;
INDENT_END;
return bol;
}
void ast_bool_data_destroy(ASTBoolData* bol) { free(bol); }
ASTExcData* ast_exc_data_init(const char* msg, AST* trace) {
ASTExcData* data = malloc(sizeof(ASTExcData));
data->msg = msg;
@@ -147,33 +89,35 @@ void ast_exc_data_destroy(ASTExcData* exc) {
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) {
// Lambda.
ASTLambdaData* ast_lambda_data_init(size_t parc, AST** parv, AST* body) {
talloc(ASTLambdaData, lambda);
lambda->parc = parc;
lambda->parv = parv;
lambda->body = body;
return lambda;
}
void ast_lambda_data_destroy(ASTLambdaData* lambda) {
free(lambda->parv);
free(lambda);
}
// Call.
ASTCallData* ast_call_data_init(size_t argc, AST** argv, AST* exp) {
talloc(ASTCallData, call);
log_dbgf("to: %s", to);
call->to = to;
call->exp = exp;
call->argc = argc;
call->argv = argv;
@@ -182,63 +126,27 @@ ASTCallData* ast_call_data_init(char* to, size_t argc, AST** argv) {
void ast_call_data_destroy(ASTCallData* call) {
if (!call) return;
free(call->to);
for (size_t i = 0; i < call->argc; i++) ast_destroy(call->argv[i]);
free(call->argv);
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);
}
// VDef.
void ast_call_print(ASTCallData* data, int i) {
INDENT_BEGIN(i);
INDENT_TITLE("ASTCallData", data);
INDENT_FIELD("to", "%s", data->to);
INDENT_FIELD("argc", "%ld", data->argc);
INDENT_FIELD_LIST("argv", data->argv, data->argc, ast_print_i);
INDENT_END;
}
ASTVDefData* ast_vdef_data_init(char* name, AST* val) {
ASTVDefData* ast_vdef_data_init(char* name, AST* exp) {
talloc(ASTVDefData, vdef);
vdef->name = name;
vdef->val = val;
vdef->exp = exp;
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;
}
// VRef.
ASTVrefData* ast_vref_data_init(char* to) {
talloc(ASTVrefData, vref);
@@ -253,15 +161,6 @@ void ast_vref_data_destroy(ASTVrefData* vref) {
free(vref);
}
void ast_vref_print(ASTVrefData* data, int i) {
INDENT_BEGIN(i);
INDENT_TITLE("ASTVrefData", data);
INDENT_FIELD("to", "%s", data->to);
INDENT_END;
}
ASTBlockData* ast_block_data_init(AST** inside, size_t ln) {
ASTBlockData* block = malloc(sizeof(ASTBlockData));
@@ -283,16 +182,6 @@ void ast_block_data_destroy_psv(ASTBlockData* block) {
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));
@@ -317,18 +206,6 @@ void ast_fdef_data_destroy_psv(ASTFDefData* fdef) {
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;
@@ -337,13 +214,6 @@ ASTArgData* ast_arg_data_init(char* name) {
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);

175
src/ast_print.c Normal file
View File

@@ -0,0 +1,175 @@
#include "include/ast_print.h"
#include "include/ast.h"
#include "include/builtin.h"
#include "include/dstr.h"
#include "include/util.h"
#include <stdio.h>
static char* asttype_names[] = {
[AST_TYPE_CALL] = "FUNC CALL",
[AST_TYPE_NUM] = "NUMBER",
[AST_TYPE_BOOL] = "BOOLEAN",
[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_LAMBDA] = "LAMBDA EXPRESSION",
[AST_TYPE_BIF] = "BUILTIN FUNCTION"
};
void ast_print(AST* ast) {
if (!ast) return;
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_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_BOOL:
printf(
"%s %s\n", INDENT_spacing->buf,
*(ASTBoolData*)ast->data ? "true" : "false"
);
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;
case AST_TYPE_LAMBDA: ast_lambda_print(ast->data, i + 2); break;
case AST_TYPE_BIF: ast_bif_print(ast->data, i + 2); break;
default: exit(1);
}
INDENT_FIELD_NONL_END;
INDENT_END;
}
void ast_num_print(ASTNumData* data, int i) {
INDENT_BEGIN(i);
INDENT_FIELD("data", "%lf", *data);
INDENT_END;
}
void ast_bool_print(ASTBoolData* data, int i) {
INDENT_BEGIN(i);
INDENT_FIELD("data", "%s", *data ? "true" : "false");
INDENT_END;
}
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;
}
void ast_call_print(ASTCallData* data, int i) {
INDENT_BEGIN(i);
INDENT_TITLE("ASTCallData", data);
INDENT_FIELD("argc", "%ld", data->argc);
INDENT_FIELD_LIST("argv", data->argv, data->argc, ast_print_i);
INDENT_FIELD_EXT_NONL_START("exp");
ast_print_i(data->exp, i + 2);
INDENT_FIELD_NONL_END;
INDENT_END;
}
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("exp");
ast_print_i(vdef->exp, depth + 2); // 2 because already indented.
INDENT_FIELD_NONL_END;
INDENT_END;
}
void ast_vref_print(ASTVrefData* data, int i) {
INDENT_BEGIN(i);
INDENT_TITLE("ASTVrefData", data);
INDENT_FIELD("to", "%s", data->to);
INDENT_END;
}
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;
}
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;
}
void ast_arg_print(ASTArgData* arg, int i) {
INDENT_BEGIN(i);
INDENT_TITLE("ASTArgData", arg);
INDENT_FIELD("name", "%s", arg->name);
INDENT_END;
}
void ast_lambda_print(ASTLambdaData* lambda, int i) {
INDENT_BEGIN(i)
INDENT_TITLE("ASTLambdaData", lambda);
INDENT_FIELD("parc", "%ld", lambda->parc);
INDENT_FIELD_LIST("parv", lambda->parv, lambda->parc, ast_print_i);
INDENT_FIELD_EXT_NONL_START("body");
ast_print_i(lambda->body, i + 2);
INDENT_FIELD_NONL_END;
INDENT_END;
}
void ast_bif_print(ASTBIFData* bif, int i) {
INDENT_BEGIN(i);
INDENT_TITLE("ASTBIFData", bif);
char* name = "unknown";
for (int i = 0; i < BUILTIN_FNS_LN; i++)
if ((void*)BUILTIN_FNS[i].fn == bif) {
name = BUILTIN_FNS[i].name;
break;
}
INDENT_FIELD("name", "%s", name);
INDENT_END;
}

View File

@@ -1,10 +1,10 @@
#include "include/builtin.h"
#include "include/ast.h"
#include "include/exec.h"
#include "include/util.h"
#include <stdarg.h>
#include <stdio.h>
#include "include/ast.h"
#include "include/builtin.h"
#include "include/exec.h"
AST* builtin_sum(size_t argc, AST** argv, Scope* parent) {
ASTNumData total = 0;
@@ -28,7 +28,8 @@ AST* builtin_sum(size_t argc, AST** argv, Scope* parent) {
}
AST* builtin_sub(size_t argc, AST** argv, Scope* parent) {
log_dbg("Got here");
if (argc <= 0) return ast_init(AST_TYPE_NUM, ast_num_data_init(0));
AST* first = exec_exp(*argv, parent);
if (first->type == AST_TYPE_EXC)
return ast_init(
@@ -63,7 +64,8 @@ AST* builtin_sub(size_t argc, AST** argv, Scope* parent) {
}
AST* builtin_mul(size_t argc, AST** argv, Scope* parent) {
log_dbg("Got here");
if (argc <= 0) return ast_init(AST_TYPE_NUM, ast_num_data_init(0));
AST* first = exec_exp(*argv, parent);
if (first->type == AST_TYPE_EXC)
return ast_init(
@@ -98,7 +100,8 @@ AST* builtin_mul(size_t argc, AST** argv, Scope* parent) {
}
AST* builtin_div(size_t argc, AST** argv, Scope* parent) {
log_dbg("Got here");
if (argc <= 0) return ast_init(AST_TYPE_NUM, ast_num_data_init(0));
AST* first = exec_exp(*argv, parent);
if (first->type == AST_TYPE_EXC)
return ast_init(
@@ -131,3 +134,77 @@ AST* builtin_div(size_t argc, AST** argv, Scope* parent) {
return ast_init(AST_TYPE_NUM, ast_num_data_init(total));
}
AST* builtin_die(size_t argc, AST** argv, Scope* parent) {
return ast_init(AST_TYPE_EXC, ast_exc_data_init("8", NULL));
}
AST* builtin_if(size_t argc, AST** argv, Scope* parent) {
if (argc != 3)
return ast_init(
AST_TYPE_EXC,
ast_exc_data_init("If invoked with too few args.", NULL)
);
AST* pred = exec_exp(argv[0], parent);
AST* body = argv[1];
AST* alt = argv[2];
if (pred->type != AST_TYPE_BOOL) {
if (pred->type == AST_TYPE_EXC) {
return ast_init(
AST_TYPE_EXC, ast_exc_data_init("if touched an error", pred)
);
} else {
return ast_init(
AST_TYPE_EXC,
ast_exc_data_init("if works on booleans idiot", NULL)
);
}
}
if (*(ASTBoolData*)pred->data) return exec_exp(body, parent);
else return exec_exp(alt, parent);
}
AST* builtin_eq(size_t argc, AST** argv, Scope* parent) {
if (argc < 1) return ast_init(AST_TYPE_EXC, ast_exc_data_init("bad", NULL));
else if (argc == 1) return ast_init(AST_TYPE_BOOL, ast_bool_data_init(1));
AST* first = exec_exp(argv[0], parent);
ASTType type = first->type;
AST* second = exec_exp(argv[1], parent);
if (first->type == AST_TYPE_EXC)
return ast_init(
AST_TYPE_EXC, ast_exc_data_init("first was bad", first)
);
if (second->type == AST_TYPE_EXC)
return ast_init(
AST_TYPE_EXC, ast_exc_data_init("second was bad", second)
);
if (second->type != type)
return ast_init(
AST_TYPE_EXC,
ast_exc_data_init("apples and oranges or something idk", NULL)
);
// Later when I put together an anctual type system I'll have this
// delegated to each type. For now this works.
switch (type) {
case AST_TYPE_NUM:
if (*(ASTNumData*)first->data == *(ASTNumData*)second->data)
return ast_init(AST_TYPE_BOOL, ast_bool_data_init(1));
else return ast_init(AST_TYPE_BOOL, ast_bool_data_init(0));
case AST_TYPE_BOOL:
if (*(ASTNumData*)first->data == *(ASTNumData*)second->data)
return ast_init(AST_TYPE_BOOL, ast_bool_data_init(1));
else return ast_init(AST_TYPE_BOOL, ast_bool_data_init(0));
default:
return ast_init(
AST_TYPE_BOOL, ast_bool_data_init(0)
); // Can't equate nonprimatives. I think. Maybe.
}
}

View File

@@ -1,9 +1,9 @@
#include <stddef.h>
#include <stdio.h> // IWYU pragma: keep. Req by util macros.
#include "include/dlist.h"
#include "include/util.h"
#include <stddef.h>
#include <stdio.h>
DList* dlist_init(void) {
DList* dlist = malloc(sizeof(DList));

View File

@@ -4,7 +4,6 @@
#include "include/ast.h"
#include "include/builtin.h"
#include "include/dlist.h"
#include "include/exec.h"
#include "include/htab.h"
#include "include/scope.h"
@@ -39,10 +38,16 @@ AST* exec_exp(AST* ast, Scope* parent) {
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_BOOL:
return ast_init(
AST_TYPE_BOOL, ast_bool_data_init(*(ASTBoolData*)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);
case AST_TYPE_BIF:
case AST_TYPE_LAMBDA: return ast;
default: printf("what\n"); exit(1);
}
}
@@ -60,49 +65,29 @@ AST* exec_block(AST* ast, Scope* parent) {
}
AST* exec_call(AST* ast, Scope* parent) {
log_dbg("Started call execution.");
ASTCallData* data = (ASTCallData*)ast->data;
size_t argc = data->argc;
AST** argv = data->argv;
char* fname = data->to;
ASTCallData* calldata = (ASTCallData*)ast->data;
ast->scope = parent;
AST* exp = exec_exp(calldata->exp, 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)
);
switch (fdef->type) {
switch (exp->type) {
case AST_TYPE_BIF:
ASTBIFData bifdata = fdef->data;
return bifdata(argc, argv, parent);
case AST_TYPE_FDEF: return exec_cf(fdef, argc, argv);
ASTBIFData bifdata = exp->data;
return bifdata(calldata->argc, calldata->argv, parent);
case AST_TYPE_LAMBDA:
return exec_lambda(calldata->argc, calldata->argv, exp, parent);
default:
return ast_init(AST_TYPE_EXC, ast_exc_data_init("Good job!", NULL));
return ast_init(
AST_TYPE_EXC, ast_exc_data_init("Uncallable.", NULL)
);
}
}
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;
AST* val = data->exp;
char* key = data->name;
scope_add(parent, key, val); // Add variable definition to parent scope.
return exec_exp(val, parent);
@@ -126,20 +111,31 @@ AST* exec_vref(AST* ast, Scope* parent) {
return ast_init(AST_TYPE_EXC, ast_exc_data_init(msg, NULL));
}
return exec_exp(found, ast->scope);
// return exec_exp(found, ast->scope);
return found;
}
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.
}
AST* exec_lambda(size_t argc, AST** argv, AST* exp, Scope* parent) {
Scope* callscope = scope_init(parent);
ASTLambdaData* lambda = (ASTLambdaData*)exp->data;
for (int i = 0; i < argc; i++) {
char* key = ((ASTArgData*)lambda->parv[i]->data)->name;
AST* val = exec_exp(argv[i], parent);
scope_add(callscope, key, val);
}
return exec_exp(lambda->body, callscope);
}
void exec_print(double n) { printf("= %lf\n", n); }
inline void exec_new_scope(AST* ast, Scope* inherit) {

View File

@@ -48,7 +48,7 @@ void gc_hack_free() {
((AST*)gc->p)->type, AST_TYPE_MAX, gc, gc->p
);
}
ast_destroy_psv(gc->p);
ast_destroy(gc->p);
break;
case GC_TYPE_SCOPE: scope_destroy_psv(gc->p); break;
}

View File

@@ -4,6 +4,7 @@
#include "../../src/include/ast.h"
#include "../../src/include/lexer.h"
#include "../../src/include/dlist.h"
#include "../../src/include/builtin.h"
int yylex(void);
void yyerror(char const*);
@@ -14,6 +15,7 @@
%code requires {
#include "../../src/include/ast.h"
#include "../../src/include/dlist.h"
#include "../../src/include/builtin.h"
}
%union {
@@ -26,6 +28,12 @@
%define parse.error verbose
%token BOOLT // Boolean true (TRUE or T).
%token BOOLF // Boolean false (FALSE or F).
%token IF // if or ?.
%token ELSE // else or :.
%token BLOCKS // Block start {.
%token BLOCKE // Block end }.
@@ -34,6 +42,7 @@
%token SEP // Seperator ,.
%token EQ // Equals =.
%token DEQ // Double equals ==.
%token EXPSEP // Expression seperator ;.
@@ -47,6 +56,8 @@
%token NL // Newline.
%token BACKSLASH
%left ADD SUB
%left MUL DIV
%precedence NEG
@@ -122,34 +133,47 @@ block:
;
exp:
// Number.
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));
}
| BOOLT { $$ = ast_init(AST_TYPE_BOOL, ast_bool_data_init(1)); }
| BOOLF { $$ = ast_init(AST_TYPE_BOOL, ast_bool_data_init(0)); }
| BLOCKS block BLOCKE {
$$ = ast_init(AST_TYPE_BLOCK, ast_block_data_init((AST**) $2->buf, $2->ln));
}
| SUB exp {
| exp DEQ exp {
AST** argv = calloc(2, sizeof(AST*));
argv[0] = ast_init(AST_TYPE_NUM, ast_num_data_init(-1));
argv[1] = $2;
char* to = malloc(4);
strcpy(to, "mul");
$$ = ast_init(AST_TYPE_CALL, ast_call_data_init(to, 2, argv));
argv[0] = $1;
argv[1] = $3;
$$ = ast_init(AST_TYPE_CALL, ast_call_data_init(
2,
argv,
ast_init(AST_TYPE_BIF, ast_bif_data_init(builtin_eq))
));
}
| GROUPS exp GROUPE { $$ = $2; }
| IF exp exp exp {
AST** argv = calloc(3, sizeof(AST*));
argv[0] = $2;
argv[1] = $3;
argv[2] = $4;
// Variable definition.
| WORD EQ exp {
$$ = ast_init(AST_TYPE_VDEF, ast_vdef_data_init($1, $3));
$$ = ast_init(AST_TYPE_CALL, ast_call_data_init(
3,
argv,
ast_init(AST_TYPE_BIF, ast_bif_data_init(builtin_if))
));
}
| IF exp exp ELSE exp {
AST** argv = calloc(3, sizeof(AST*));
argv[0] = $2;
argv[1] = $3;
argv[2] = $5;
$$ = ast_init(AST_TYPE_CALL, ast_call_data_init(
3,
argv,
ast_init(AST_TYPE_BIF, ast_bif_data_init(builtin_if))
));
}
// Variable reference.
@@ -157,46 +181,140 @@ exp:
$$ = ast_init(AST_TYPE_VREF, ast_vref_data_init($1));
}
// Call (general form).
| exp GROUPS arg GROUPE {
size_t argc = $3->ln;
AST** argv = $3->buf;
argarr_destroypsv($3);
$$ = ast_init(AST_TYPE_CALL, ast_call_data_init(
argc,
argv,
$1
));
}
// Call (convenient form).
| WORD GROUPS arg GROUPE {
size_t argc = $3->ln;
AST** argv = $3->buf;
argarr_destroypsv($3);
$$ = ast_init(AST_TYPE_CALL, ast_call_data_init($1, argc, argv));
$$ = ast_init(AST_TYPE_CALL, ast_call_data_init(
argc,
argv,
ast_init(AST_TYPE_VREF, ast_vref_data_init($1))
));
}
// Call (hacky convenient form).
| WORD GROUPS GROUPE {
size_t argc = 0;
AST** argv = NULL;
$$ = ast_init(AST_TYPE_CALL, ast_call_data_init(
argc,
argv,
ast_init(AST_TYPE_VREF, ast_vref_data_init($1))
));
}
// Function definitions. Convert to VDef of Lambda.
| WORD GROUPS arg GROUPE exp {
size_t parc = $3->ln;
AST** parv = $3->buf;
argarr_destroypsv($3);
$$ = ast_init(AST_TYPE_VDEF, ast_vdef_data_init(
$1,
ast_init(AST_TYPE_LAMBDA, ast_lambda_data_init(
parc, parv, $5
))
));
}
// Lambda definitions.
| BACKSLASH GROUPS arg GROUPE exp {
size_t parc = $3->ln;
AST** parv = $3->buf;
argarr_destroypsv($3);
$$ = ast_init(AST_TYPE_LAMBDA, ast_lambda_data_init(parc, parv, $5));
}
// Block.
| BLOCKS block BLOCKE {
$$ = ast_init(AST_TYPE_BLOCK, ast_block_data_init((AST**) $2->buf, $2->ln));
}
// Negative.
| SUB exp {
AST** argv = calloc(2, sizeof(AST*));
argv[0] = ast_init(AST_TYPE_NUM, ast_num_data_init(-1));
argv[1] = $2;
$$ = ast_init(AST_TYPE_CALL,
ast_call_data_init(
2,
argv,
ast_init(AST_TYPE_BIF,
ast_bif_data_init(builtin_mul)
)
)
);
}
// Group.
| GROUPS exp GROUPE { $$ = $2; }
// Variable definition.
| WORD EQ exp {
$$ = ast_init(AST_TYPE_VDEF, ast_vdef_data_init($1, $3));
}
| exp ADD exp {
AST** argv = calloc(2, sizeof(AST*));
argv[0] = $1;
argv[1] = $3;
char* to = malloc(4);
strcpy(to, "sum");
$$ = ast_init(AST_TYPE_CALL, ast_call_data_init(to, 2, argv));
$$ = ast_init(AST_TYPE_CALL,
ast_call_data_init(
2,
argv,
ast_init(AST_TYPE_BIF, ast_bif_data_init(builtin_sum))
)
);
}
| exp SUB exp {
AST** argv = calloc(2, sizeof(AST*));
argv[0] = $1;
argv[1] = $3;
char* to = malloc(4);
strcpy(to, "sub");
$$ = ast_init(AST_TYPE_CALL, ast_call_data_init(to, 2, argv));
$$ = ast_init(AST_TYPE_CALL,
ast_call_data_init(
2,
argv,
ast_init(AST_TYPE_BIF, ast_bif_data_init(builtin_sub))
)
);
}
| exp MUL exp {
AST** argv = calloc(2, sizeof(AST*));
argv[0] = $1;
argv[1] = $3;
char* to = malloc(4);
strcpy(to, "mul");
$$ = ast_init(AST_TYPE_CALL, ast_call_data_init(to, 2, argv));
$$ = ast_init(AST_TYPE_CALL,
ast_call_data_init(
2,
argv,
ast_init(AST_TYPE_BIF, ast_bif_data_init(builtin_mul))
)
);
}
| exp DIV exp {
AST** argv = calloc(2, sizeof(AST*));
argv[0] = $1;
argv[1] = $3;
char* to = malloc(4);
strcpy(to, "div");
$$ = ast_init(AST_TYPE_CALL, ast_call_data_init(to, 2, argv));
$$ = ast_init(AST_TYPE_CALL,
ast_call_data_init(
2,
argv,
ast_init(AST_TYPE_BIF, ast_bif_data_init(builtin_div))
)
);
}
%%

View File

@@ -7,24 +7,26 @@
// 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.
AST_TYPE_NUM, // A number (float).
AST_TYPE_STR, // A string
AST_TYPE_INT, // An integer.
AST_TYPE_BOOL, // A boolean.
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_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_LAMBDA, // An anonymous function definition.
AST_TYPE_ARG, // A definition argument.
AST_TYPE_MAX = AST_TYPE_ARG,
} ASTType;
@@ -39,14 +41,8 @@ typedef struct {
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);
void ast_destroy(AST* ast);
// A number.
typedef double ASTNumData;
@@ -55,20 +51,38 @@ typedef double 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);
// A boolean.
typedef int ASTBoolData;
// Create a new `ASTBoolData`.
ASTBoolData* ast_bool_data_init(int val);
// Destroy an `ASTBoolData`.
void ast_bool_data_destroy(ASTBoolData* bol);
// An exception.
typedef struct ASTEXCDATA {
const char* msg; // The exception message.
AST* trace; // The previous exception.
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);
// Argument list as anonymous struct.
#define ARGS \
struct { \
size_t argc; \
AST** argv; \
}
// Parameter list as anonymous struct.
#define PARS \
struct { \
size_t parc; \
AST** parv; \
}
// A built-in function.
typedef AST* (*ASTBIFData)(size_t argc, AST** argv, Scope* scope);
@@ -78,38 +92,40 @@ 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).
// A lambda.
typedef struct {
char* to; // What the call's to.
size_t argc; // Argument count.
AST** argv; // Argument vector.
PARS; // The parameters the lambda can accept.
AST* body; // The body expression to be executed.
} ASTLambdaData;
// Creates a new `ASTLambdaData`.
ASTLambdaData* ast_lambda_data_init(size_t parc, AST** parv, AST* body);
// Destroy an `ASTLambdaData`.
void ast_lambda_data_destroy(ASTLambdaData*);
// A call.
typedef struct {
ARGS; // The arguments the call is made with.
AST* exp; // The expression the call is to.
} 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);
ASTCallData* ast_call_data_init(size_t argc, AST** argv, AST* exp);
// Destroy an `ASTCallData`.
void ast_call_data_destroy_psv(ASTCallData *call);
// Print an `ASTCallData`.
void ast_call_print(ASTCallData*, int i);
void ast_call_data_destroy(ASTCallData* call);
// A variable definition's data.
// A variable definition. Associates a name with an expression.
typedef struct {
char* name;
AST* val;
AST* exp;
} 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);
ASTVDefData* ast_vdef_data_init(char* name, AST* exp);
// Destroy an `ASTVDefData`.
void ast_vdef_data_destroy_psv(ASTVDefData* vdef);
// Print an `ASTVDefData`.
void ast_vdef_print(ASTVDefData*, int depth);
void ast_vdef_data_destroy(ASTVDefData* vdef);
// A variable reference's data.
// A variable reference.
typedef struct {
char* to; // What the reference's to.
} ASTVrefData;
@@ -118,8 +134,6 @@ typedef struct {
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 {
@@ -132,15 +146,12 @@ 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);
void ast_block_data_destroy_psv(ASTBlockData* block);
typedef struct {
char* name; // Function name.
size_t argc; // Argument count.
AST** argv; // Argument vector.
AST* body; // Function body.
char* name; // Function name.
ARGS; // Function args.
AST* body; // Function body.
} ASTFDefData;
// Create a new `ASTFDefData`.
@@ -149,8 +160,6 @@ ASTFDefData* ast_fdef_data_init(char* name, size_t argc, AST** argv, AST* body);
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.
@@ -160,10 +169,8 @@ typedef struct {
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.
// Find the expression associated with a name in the nearest scope.
AST* ast_find(Scope* scope, char* name);
#endif

44
src/include/ast_print.h Normal file
View File

@@ -0,0 +1,44 @@
#ifndef AST_PRINT_H
#define AST_PRINT_H
#include "ast.h"
// 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);
// Print an `ASTNumData`.
void ast_num_print(ASTNumData*, int i);
// Print an `ASTBoolData`.
void ast_bool_print(ASTBoolData*, int i);
// Print an `ASTExecData`.
void ast_exc_print(ASTExcData*, int i);
// Print an `ASTCallData`.
void ast_call_print(ASTCallData*, int i);
// Print an `ASTVDefData`.
void ast_vdef_print(ASTVDefData*, int depth);
// Print an `ASTVRefData`.
void ast_vref_print(ASTVrefData*, int i);
// Print an `ASTBlockData`.
void ast_block_print(ASTBlockData*, int i);
// Print an `ASTFDefData`.
void ast_fdef_print(ASTFDefData* fdef, int i);
// Print an `ASTArgData`.
void ast_arg_print(ASTArgData* arg, int i);
// Print an `ASTLambdaData`.
void ast_lambda_print(ASTLambdaData* arg, int i);
// Print an `ASTBIFData`.
void ast_bif_print(ASTBIFData* arg, int i);
#endif

View File

@@ -2,6 +2,7 @@
#define BUILTIN_H
#include "ast.h"
#include <stddef.h>
// Sum some nums.
AST* builtin_sum(size_t argc, AST** argv, Scope* parent);
@@ -15,6 +16,15 @@ AST* builtin_mul(size_t argc, AST** argv, Scope* parent);
// Divide nums.
AST* builtin_div(size_t argc, AST** argv, Scope* parent);
// Die.
AST* builtin_die(size_t argc, AST** argv, Scope* parent);
// If statement.
AST* builtin_if(size_t argc, AST** argv, Scope* parent);
// Equality.
AST* builtin_eq(size_t argc, AST** argv, Scope* parent);
struct builtin_data {
char* name;
AST* (*fn)(size_t argc, AST** argv, Scope* parent);
@@ -25,6 +35,9 @@ static struct builtin_data BUILTIN_FNS[] = {
{ "sub", builtin_sub },
{ "mul", builtin_mul },
{ "div", builtin_div },
{ "die", builtin_die },
{"_if", builtin_if},
{"eq", builtin_eq},
};
#define BUILTIN_FNS_LN (arrln(BUILTIN_FNS))

View File

@@ -12,14 +12,14 @@ AST* exec_exp(AST* ast, Scope* parent);
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);
// Execute a lambda expression.
AST* exec_lambda(size_t argc, AST** argv, AST* exp, Scope* parent);
// Print the result of an execution.
void exec_print(double n);

View File

@@ -1,6 +1,8 @@
#ifndef UTIL_H
#define UTIL_H
#include <signal.h>
// Most of this file is cursed printing macros for `ast_print()`. Do not attempt
// to comprehend.
@@ -10,6 +12,9 @@
// Get the length of an array.
#define arrln(A) (sizeof(A)/sizeof(*A))
// Trap GDB &c.
#define TRAP() RAISE(SIGTRAP)
#ifdef DBG // Debug macros
// Log a message.

View File

@@ -3,6 +3,7 @@
#include <limits.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
#include "include/dstr.h"
#include "include/lexer.h"
@@ -72,7 +73,20 @@ double acc_float(int c) {
char* acc_word(int c) {
Dstr* val = dstr_init();
while (isalpha(*inp)) {
while (isalpha(*inp) || *inp == '_') {
dstr_appendch(val, *(inp - 1));
inp++;
}
dstr_appendch(val, *(inp - 1));
char* ret = val->buf;
dstr_destroypsv(val);
return ret;
}
char* acc_deq(int c) {
Dstr* val = dstr_init();
while (*inp == '=') {
dstr_appendch(val, *(inp - 1));
inp++;
}
@@ -98,8 +112,22 @@ int yylex() {
return NUM;
}
if (isalpha(c)) {
if (c == '=') {
yylval.strval = acc_deq(c);
if (!strcmp(yylval.strval, "=")) return EQ;
if (!strcmp(yylval.strval, "==")) return DEQ;
}
if (isalpha(c) || c == '_') {
yylval.strval = acc_word(c);
if (!strcmp(yylval.strval, "TRUE") || !strcmp(yylval.strval, "T"))
return BOOLT;
if (!strcmp(yylval.strval, "FALSE") || !strcmp(yylval.strval, "F"))
return BOOLF;
if (!strcmp(yylval.strval, "if")) return IF;
if (!strcmp(yylval.strval, "else")) return ELSE;
return WORD;
}
@@ -115,7 +143,10 @@ int yylex() {
case ';': return EXPSEP;
case '{': return BLOCKS;
case '}': return BLOCKE;
case '=': return EQ;
// case '=': return EQ;
case '\\': return BACKSLASH;
case '?': return IF;
case ':': return ELSE;
default: fprintf(stderr, "Unexpected character: %c\n", c);
}

View File

@@ -2,6 +2,7 @@
#include <string.h>
#include "include/ast.h"
#include "include/ast_print.h"
#include "include/dstr.h"
#include "include/exec.h"
#include "include/gc.h"
@@ -61,6 +62,13 @@ int main(int argc, char** argv) {
AST* eval = exec_start(root);
ast_print(eval);
// Awful hack to exit when die() is called, until proper exception
// handling is implemented. TODO TODO TODO PLSFIX.
if (eval->type == AST_TYPE_EXC &&
((ASTExcData*)eval->data)->msg[0] == '8') {
gc_hack_free();
exit(1);
}
gc_hack_free();
}

View File

@@ -1,5 +1,5 @@
#include <assert.h>
#include <stdio.h>
#include <stdio.h> // IWYU pragma: keep. Req by util macros.
#include <string.h>
#include "include/stack.h"

21
type-notes.md Normal file
View File

@@ -0,0 +1,21 @@
Primitive Types
- num
- bool
- str
```
fact(x) = ? (x <= 1) 1 : f(x-1) * x
> lambda
fact(5) == 120
> True
```
Ordered collections.
| Name | Homogeneous | Fixed Size | Unique |
|---|---|---|---|
| Collection | No | No | No |
| List | Yes | No | No |
| Array | Yes | Yes | No |
| Group | No | Yes | No |
| Set | Yes | No | Yes |
| Perm | Yes | Yes | Yes |