Compare commits

16 Commits

Author SHA1 Message Date
24f93ce750 Not sure what I just fixed. 2025-10-18 10:26:39 -04:00
2fc3f1e1df Fixed some bugs and memory leaks. 2025-10-18 10:24:59 -04:00
9525535323 Got arrow syntax working. 2025-10-11 11:04:23 -04:00
0fa1c176f4 Added force & preserve operators. 2025-10-11 10:09:17 -04:00
868dcb6788 Merge branch 'types'. 2025-10-11 09:27:15 -04:00
4fb698918c Simple type checking. 2025-10-04 11:16:59 -04:00
df74d29e54 Type parsing is good enough. 2025-10-04 10:04:15 -04:00
7181b31d4b Updated readme with tail-recursive factorial. 2025-10-04 09:33:47 -04:00
7630e4b99f Cleaned up remains of recursive destroy functions.
Not needed with new GC.
2025-09-27 13:35:34 -04:00
74d5a212cd Merge branch 'types'.
Just bumping master before it lags too out of date. Should be pretty
stable at this point.
2025-09-27 13:31:30 -04:00
90fce2fce4 Added types to ast. Added new call syntax. 2025-09-27 11:06:48 -04:00
94689be83b Refactoring.
VRef -> Ref
VDef -> Def
2025-09-27 10:13:57 -04:00
cb5fa27eb3 Plans. 2025-09-12 16:38:16 -04:00
7c08e8da4d Fixed depends. 2025-09-12 16:33:45 -04:00
fdf526750d Cleaned up more fdef stuff. 2025-09-12 16:32:58 -04:00
4e5b12b5b2 Updated README.md. 2025-09-01 00:18:59 -04:00
17 changed files with 466 additions and 257 deletions

View File

@@ -1,20 +1,20 @@
# SCL: Simple CAS Language # SCL: Simple CAS Language
Version v0.3 *v0.3*
SCL aims to be a human-friendly Computer Algebra System (CAS) inspired by 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 [maxima](https://maxima.sourceforge.io/) that feels like writing on paper. In
its current state, SCL can be used as a functional programming language capable 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 C, of performing simple arithmetic. The codebase is about 2,000 lines of
including a parser, interpreter, and runtime. It uses a linked environment handwritten C, including a parser, interpreter, and runtime. It uses a linked
scoping model. environment scoping model.
## Usage ## Usage
To download and run: To download and run:
```bash ```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 make release
./scl.out ./scl.out
``` ```
@@ -22,35 +22,25 @@ make release
### For Development ### For Development
```bash ```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 make all test
./scl.out ./scl.out
``` ```
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.
*Note that tests are currently in poor use. I hope to amend this in the future.*
## Syntax ## Syntax
As one would expect, you can evaluate simple infix expressions: SCL's syntax will feel familiar to other functional programming languages.
```scl ```scl
> 1 + 1 > x = 3 + 3 * 3; x + 1
= 13
> f(x) x + 1
> f(1)
= 2 = 2
```
You can also define your own functions and variables:
```scl
> f(x) 2 * x
> n = 3
> f(n)
= 6
```
Being a functional programming language at heart, one can of course use lambda functions:
```scl
> (\(x) 2 * x)(5) > (\(x) 2 * x)(5)
= 10 = 10
> f(g) g(2) > f(g) g(2)
@@ -58,17 +48,24 @@ Being a functional programming language at heart, one can of course use lambda f
= 4 = 4
``` ```
Here's a simple factorial function: Here's a simple factorial function, using recursion:
```scl ```scl
> factorial(n) { > fac(n) = {
> if (n == 0) { 1 } > f(n, a) = {
> else { n * factorial(n - 1) } > if n == 1
> a
> else
> f(n - 1, a * n);
> }
>
> f(n, 1);
> } > }
``` ```
Or, using SCL's more concise syntax: SCL's syntax is quite flexible. The above function could be more concisely
written as:
```scl ```scl
> factorial(n) ? n == 0 1 n * factorial(n - 1) > fac(n) (n, 1) -> f(n, a) ? n == 1 a f(n - 1, a * n)
``` ```

View File

@@ -1,3 +1,4 @@
Differentiate kind literals, constructors, and kinds themselves.
EXCEPTION HANDLING: exception ast type should have as data a giant enum of 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 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 handled under the exception type and print logic. For now, executor checks

View File

@@ -1,48 +1,3 @@
# Vectors x: Int = 3
# - Must have fixed size and type
# Lists <name> : <expression> = <expression>
# - 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">

View File

@@ -41,14 +41,17 @@ void ast_destroy(AST* ast) {
switch (ast->type) { switch (ast->type) {
case AST_TYPE_LIT_NUM: ast_num_data_destroy(ast->data); break; case AST_TYPE_LIT_NUM: ast_num_data_destroy(ast->data); break;
case AST_TYPE_LIT_BOOL: ast_bool_data_destroy(ast->data); break; case AST_TYPE_LIT_BOOL: ast_bool_data_destroy(ast->data); break;
case AST_TYPE_LIT_KIND: ast_kind_data_destroy(ast->data); break;
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_REF: ast_ref_data_destroy(ast->data); break;
case AST_TYPE_VDEF: ast_vdef_data_destroy(ast->data); break; case AST_TYPE_DEF: ast_def_data_destroy(ast->data); break;
case AST_TYPE_BLOCK: ast_block_data_destroy_psv(ast->data); break; case AST_TYPE_BLOCK: ast_block_data_destroy(ast->data); break;
case AST_TYPE_ARG: ast_arg_data_destroy(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_BIF: ast_bif_data_destroy(ast->data); break;
case AST_TYPE_EXC: ast_exc_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; case AST_TYPE_LAMBDA: ast_lambda_data_destroy(ast->data); break;
case AST_TYPE_FORCE: ast_force_data_destroy(ast->data); break;
case AST_TYPE_PRESERVE: ast_preserve_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);
} }
@@ -76,6 +79,21 @@ ASTBoolData* ast_bool_data_init(int val) {
void ast_bool_data_destroy(ASTBoolData* bol) { free(bol); } void ast_bool_data_destroy(ASTBoolData* bol) { free(bol); }
const char* ast_lit_kind_names[AST_LIT_KIND_MAX + 2] = {
[AST_LIT_KIND_NUM] = "Number",
[AST_LIT_KIND_BOOL] = "Boolean",
[AST_LIT_KIND_KIND] = "Type",
};
ASTKindData* ast_kind_data_init(ASTKindData val) {
talloc(ASTKindData, kind);
*kind = val;
return kind;
}
void ast_kind_data_destroy(ASTKindData* kind) { free(kind); }
ASTExcData* ast_exc_data_init(const char* msg, AST* trace) { ASTExcData* ast_exc_data_init(const char* msg, AST* trace) {
ASTExcData* data = malloc(sizeof(ASTExcData)); ASTExcData* data = malloc(sizeof(ASTExcData));
data->msg = msg; data->msg = msg;
@@ -129,35 +147,36 @@ void ast_call_data_destroy(ASTCallData* call) {
free(call); free(call);
} }
// VDef. // Def.
ASTVDefData* ast_vdef_data_init(char* name, AST* exp) { ASTDefData* ast_def_data_init(char* name, AST* kind, AST* exp) {
talloc(ASTVDefData, vdef); talloc(ASTDefData, def);
vdef->name = name; def->name = name;
vdef->exp = exp; def->kind = kind;
def->exp = exp;
return vdef; return def;
} }
void ast_vdef_data_destroy(ASTVDefData* vdef) { void ast_def_data_destroy(ASTDefData* vdef) {
free(vdef->name); free(vdef->name);
free(vdef); free(vdef);
} }
// VRef. // Ref.
ASTVrefData* ast_vref_data_init(char* to) { ASTRefData* ast_ref_data_init(char* to) {
talloc(ASTVrefData, vref); talloc(ASTRefData, ref);
vref->to = to; ref->to = to;
return vref; return ref;
} }
void ast_vref_data_destroy(ASTVrefData* vref) { void ast_ref_data_destroy(ASTRefData* ref) {
free(vref->to); free(ref->to);
free(vref); free(ref);
} }
ASTBlockData* ast_block_data_init(AST** inside, size_t ln) { ASTBlockData* ast_block_data_init(AST** inside, size_t ln) {
@@ -170,13 +189,6 @@ ASTBlockData* ast_block_data_init(AST** inside, size_t ln) {
} }
void ast_block_data_destroy(ASTBlockData* block) { void ast_block_data_destroy(ASTBlockData* block) {
for (size_t i = 0; i < block->ln; i++) { ast_destroy(block->inside[i]); }
free(block->inside);
free(block);
}
void ast_block_data_destroy_psv(ASTBlockData* block) {
free(block->inside); free(block->inside);
free(block); free(block);
} }
@@ -198,3 +210,19 @@ AST* ast_find(Scope* scope, char* name) {
return NULL; return NULL;
} }
ASTForceData* ast_force_data_init(AST* body) {
talloc(ASTForceData, force);
force->body = body;
return force;
}
void ast_force_data_destroy(ASTForceData* force) { free(force); }
ASTPreserveData* ast_preserve_data_init(AST* body) {
talloc(ASTPreserveData, preserve);
preserve->body = body;
return preserve;
}
void ast_preserve_data_destroy(ASTPreserveData* preserve) { free(preserve); }

View File

@@ -6,17 +6,18 @@
#include <stdio.h> #include <stdio.h>
static char* asttype_names[] = { static char* asttype_names[] = {
[AST_TYPE_CALL] = "FUNC CALL", [AST_TYPE_CALL] = "CALL",
[AST_TYPE_LIT_NUM] = "NUMBER", [AST_TYPE_LIT_NUM] = "LITERAL NUMBER",
[AST_TYPE_LIT_BOOL] = "BOOLEAN", [AST_TYPE_LIT_BOOL] = "LITERAL BOOLEAN",
[AST_TYPE_LIT_KIND] = "KIND", [AST_TYPE_REF] = "REFERENCE",
[AST_TYPE_VREF] = "VAR REFERENCE", [AST_TYPE_DEF] = "DEFINITION",
[AST_TYPE_VDEF] = "VAR DEFINITION",
[AST_TYPE_BLOCK] = "BLOCK", [AST_TYPE_BLOCK] = "BLOCK",
[AST_TYPE_EXC] = "EXCEPTION", [AST_TYPE_EXC] = "EXCEPTION",
[AST_TYPE_ARG] = "DEFINITION ARGUMENT", [AST_TYPE_ARG] = "DEFINITION ARGUMENT",
[AST_TYPE_LAMBDA] = "LAMBDA EXPRESSION", [AST_TYPE_LAMBDA] = "LAMBDA",
[AST_TYPE_BIF] = "BUILTIN FUNCTION" [AST_TYPE_BIF] = "BUILTIN FUNCTION",
[AST_TYPE_FORCE] = "FORCE",
[AST_TYPE_PRESERVE] = "PRESERVE"
}; };
void ast_print(AST* ast) { void ast_print(AST* ast) {
@@ -40,15 +41,23 @@ void ast_print_i(AST* ast, int i) {
*(ASTBoolData*)ast->data ? "true" : "false" *(ASTBoolData*)ast->data ? "true" : "false"
); );
break; break;
case AST_TYPE_CALL: ast_call_print(ast->data, i + 2); break; case AST_TYPE_LIT_KIND:
case AST_TYPE_EXC: ast_exc_print(ast->data, i + 2); break; printf(
case AST_TYPE_VREF: ast_vref_print(ast->data, i + 2); break; "%s %s\n", INDENT_spacing->buf,
case AST_TYPE_VDEF: ast_vdef_print(ast->data, i + 2); break; ast_lit_kind_names[*(ASTKindData*)ast->data]
case AST_TYPE_BLOCK: ast_block_print(ast->data, i + 2); break; );
case AST_TYPE_ARG: ast_arg_print(ast->data, i + 2); break; break;
case AST_TYPE_LAMBDA: ast_lambda_print(ast->data, i + 2); break; case AST_TYPE_CALL: ast_call_print(ast->data, i + 2); break;
case AST_TYPE_BIF: ast_bif_print(ast->data, i + 2); break; case AST_TYPE_EXC: ast_exc_print(ast->data, i + 2); break;
default: exit(1); case AST_TYPE_REF: ast_ref_print(ast->data, i + 2); break;
case AST_TYPE_DEF: ast_def_print(ast->data, i + 2); break;
case AST_TYPE_BLOCK: ast_block_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;
case AST_TYPE_FORCE: ast_force_print(ast->data, i + 2); break;
case AST_TYPE_PRESERVE: ast_preserve_print(ast->data, i + 2); break;
default: exit(1);
} }
INDENT_FIELD_NONL_END; INDENT_FIELD_NONL_END;
INDENT_END; INDENT_END;
@@ -97,22 +106,28 @@ void ast_call_print(ASTCallData* data, int i) {
INDENT_END; INDENT_END;
} }
void ast_vdef_print(ASTVDefData* vdef, int depth) { void ast_def_print(ASTDefData* def, int depth) {
INDENT_BEGIN(depth); INDENT_BEGIN(depth);
INDENT_TITLE("ASTVDefData", vdef); INDENT_TITLE("ASTDefData", def);
INDENT_FIELD("name", "%s", vdef->name); INDENT_FIELD("name", "%s", def->name);
if (def->kind) {
INDENT_FIELD_EXT_NONL_START("kind");
ast_print_i(def->kind, depth + 2);
INDENT_FIELD_NONL_END;
} else INDENT_FIELD("kind", "%s", "Any");
INDENT_FIELD_EXT_NONL_START("exp"); INDENT_FIELD_EXT_NONL_START("exp");
ast_print_i(vdef->exp, depth + 2); // 2 because already indented. ast_print_i(def->exp, depth + 2); // 2 because already indented.
INDENT_FIELD_NONL_END; INDENT_FIELD_NONL_END;
INDENT_END; INDENT_END;
} }
void ast_vref_print(ASTVrefData* data, int i) { void ast_ref_print(ASTRefData* data, int i) {
INDENT_BEGIN(i); INDENT_BEGIN(i);
INDENT_TITLE("ASTVrefData", data); INDENT_TITLE("ASTRefData", data);
INDENT_FIELD("to", "%s", data->to); INDENT_FIELD("to", "%s", data->to);
INDENT_END; INDENT_END;
@@ -160,3 +175,25 @@ void ast_bif_print(ASTBIFData* bif, int i) {
INDENT_FIELD("name", "%s", name); INDENT_FIELD("name", "%s", name);
INDENT_END; INDENT_END;
} }
void ast_force_print(ASTForceData* force, int i) {
INDENT_BEGIN(i);
INDENT_TITLE("ASTForceData", force);
INDENT_FIELD_EXT_NONL_START("body");
ast_print_i(force->body, i + 2);
INDENT_FIELD_NONL_END;
INDENT_END;
}
void ast_preserve_print(ASTPreserveData* preserve, int i) {
INDENT_BEGIN(i);
INDENT_TITLE("ASTPreserveData", preserve);
INDENT_FIELD_EXT_NONL_START("body");
ast_print_i(preserve->body, i + 2);
INDENT_FIELD_NONL_END;
INDENT_END;
}

View File

@@ -23,6 +23,18 @@ AST* exec_start(AST* ast) {
ast_init(AST_TYPE_BIF, ast_bif_data_init(BUILTIN_FNS[i].fn)) ast_init(AST_TYPE_BIF, ast_bif_data_init(BUILTIN_FNS[i].fn))
); );
AST* defnum =
ast_init(AST_TYPE_LIT_KIND, ast_kind_data_init(AST_LIT_KIND_NUM));
htab_ins(global->here, "Num", defnum);
AST* defbool =
ast_init(AST_TYPE_LIT_KIND, ast_kind_data_init(AST_LIT_KIND_BOOL));
htab_ins(global->here, "Bool", defbool);
AST* defkind =
ast_init(AST_TYPE_LIT_KIND, ast_kind_data_init(AST_LIT_KIND_KIND));
htab_ins(global->here, "Type", defkind);
log_dbg("Completed startup sequence."); log_dbg("Completed startup sequence.");
AST* res = exec_exp(ast, global); AST* res = exec_exp(ast, global);
@@ -42,11 +54,13 @@ AST* exec_exp(AST* ast, Scope* parent) {
return ast_init( return ast_init(
AST_TYPE_LIT_BOOL, ast_bool_data_init(*(ASTBoolData*)ast->data) AST_TYPE_LIT_BOOL, ast_bool_data_init(*(ASTBoolData*)ast->data)
); );
case AST_TYPE_VREF: return exec_vref(ast, parent); case AST_TYPE_REF: return exec_ref(ast, parent);
case AST_TYPE_VDEF: return exec_vdef(ast, parent); case AST_TYPE_DEF: return exec_def(ast, parent);
case AST_TYPE_BIF: case AST_TYPE_BIF:
case AST_TYPE_LAMBDA: return ast; case AST_TYPE_LAMBDA: return ast;
default: printf("what\n"); exit(1); case AST_TYPE_FORCE: return exec_force(ast, parent);
case AST_TYPE_PRESERVE: return exec_preserve(ast, parent);
default: printf("what\n"); exit(1);
} }
} }
@@ -70,8 +84,9 @@ AST* exec_call(AST* ast, Scope* parent) {
switch (exp->type) { switch (exp->type) {
case AST_TYPE_BIF: case AST_TYPE_BIF:
ASTBIFData bifdata = exp->data; return ((ASTBIFData)exp->data)(
return bifdata(calldata->argc, calldata->argv, parent); calldata->argc, calldata->argv, parent
);
case AST_TYPE_LAMBDA: case AST_TYPE_LAMBDA:
return exec_lambda(calldata->argc, calldata->argv, exp, parent); return exec_lambda(calldata->argc, calldata->argv, exp, parent);
default: default:
@@ -81,31 +96,52 @@ AST* exec_call(AST* ast, Scope* parent) {
} }
} }
AST* exec_vdef(AST* ast, Scope* parent) { AST* exec_def(AST* ast, Scope* parent) {
// Use parent's scope. // Use parent's scope.
exec_inherit_scope(ast, parent); exec_inherit_scope(ast, parent);
ASTVDefData* data = (ASTVDefData*)ast->data; ASTDefData* data = (ASTDefData*)ast->data;
AST* val = data->exp; AST* val = data->exp;
char* key = data->name; char* key = data->name;
if (data->kind) {
ASTKindData kind = *(ASTKindData*)exec_exp(data->kind, parent)->data ==
AST_LIT_KIND_NUM;
if (kind == AST_LIT_KIND_NUM && data->exp->type != AST_TYPE_LIT_NUM)
return ast_init(
AST_TYPE_EXC, ast_exc_data_init("Expected Num.", NULL)
);
if (kind == AST_LIT_KIND_BOOL && data->exp->type != AST_TYPE_LIT_BOOL)
return ast_init(
AST_TYPE_EXC, ast_exc_data_init("Expected Bool.", NULL)
);
if (kind == AST_LIT_KIND_KIND && data->exp->type != AST_TYPE_LIT_KIND)
return ast_init(
AST_TYPE_EXC, ast_exc_data_init("Expected Type.", NULL)
);
}
scope_add(parent, key, val); // Add variable definition to parent scope. scope_add(parent, key, val); // Add variable definition to parent scope.
return exec_exp(val, parent); return exec_exp(val, parent);
} }
AST* exec_vref(AST* ast, Scope* parent) { AST* exec_ref(AST* ast, Scope* parent) {
// Use parent's scope. // Use parent's scope.
exec_inherit_scope(ast, parent); exec_inherit_scope(ast, parent);
log_dbg("attempting to reference var"); log_dbg("attempting to reference var");
ASTVrefData* vref = (ASTVrefData*)ast->data; ASTRefData* ref = (ASTRefData*)ast->data;
AST* found = ast_find(parent, vref->to); AST* found = ast_find(parent, ref->to);
if (found == NULL) { if (found == NULL) {
// TODO: Better memory management here. // TODO: Better memory management here.
static char msg[256]; static char msg[256];
snprintf( snprintf(
msg, sizeof(msg), "Could not find value in scope for `%s`.", msg, sizeof(msg), "Could not find value in scope for `%s`.", ref->to
vref->to
); );
return ast_init(AST_TYPE_EXC, ast_exc_data_init(msg, NULL)); return ast_init(AST_TYPE_EXC, ast_exc_data_init(msg, NULL));
} }
@@ -114,15 +150,6 @@ AST* exec_vref(AST* ast, Scope* parent) {
return found; return found;
} }
AST* exec_fdef(AST* ast, Scope* parent) {
ast->scope = scope_init(parent);
ASTFDefData* fdef = (ASTFDefData*)ast->data;
AST* val = ast;
char* key = fdef->name;
scope_add(parent, key, val);
return fdef->body; // Function definitions return function body.
}
AST* exec_lambda(size_t argc, AST** argv, AST* exp, Scope* parent) { AST* exec_lambda(size_t argc, AST** argv, AST* exp, Scope* parent) {
Scope* callscope = scope_init(parent); Scope* callscope = scope_init(parent);
ASTLambdaData* lambda = (ASTLambdaData*)exp->data; ASTLambdaData* lambda = (ASTLambdaData*)exp->data;
@@ -135,6 +162,18 @@ AST* exec_lambda(size_t argc, AST** argv, AST* exp, Scope* parent) {
return exec_exp(lambda->body, callscope); return exec_exp(lambda->body, callscope);
} }
AST* exec_force(AST* ast, Scope* parent) {
AST* body = ((ASTForceData*)ast->data)->body;
if (body->type == AST_TYPE_REF) {
return exec_exp(exec_ref(body, parent), parent);
} else return exec_exp(body, parent);
}
AST* exec_preserve(AST* ast, Scope* parent) {
return ((ASTPreserveData*)ast->data)->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) { inline void exec_new_scope(AST* ast, Scope* inherit) {

View File

@@ -1,6 +1,9 @@
#include "include/fnv1a.h" #include "include/fnv1a.h"
#include "include/util.h"
#include "include/util.h"
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h>
uint64_t fnv1a_hash(char* key, size_t ln) { uint64_t fnv1a_hash(char* key, size_t ln) {
uint64_t hash = FNV1A_BASIS_64; uint64_t hash = FNV1A_BASIS_64;
@@ -10,5 +13,7 @@ uint64_t fnv1a_hash(char* key, size_t ln) {
hash *= FNV1A_PRIME_64; hash *= FNV1A_PRIME_64;
} }
log_dbgf("Hash of %s was %lu", key, hash);
return hash; return hash;
} }

View File

@@ -11,8 +11,6 @@ GC* gclist = NULL;
void gc_destroy(GC* gc) { free(gc); } void gc_destroy(GC* gc) { free(gc); }
void f() {}
void* gc_alloc(size_t sz, GCType type) { void* gc_alloc(size_t sz, GCType type) {
assert(type <= GC_TYPE_MAX); assert(type <= GC_TYPE_MAX);
@@ -40,7 +38,6 @@ void gc_hack_free() {
gclist = gclist->nxt; gclist = gclist->nxt;
switch (gc->type) { switch (gc->type) {
case GC_TYPE_AST: case GC_TYPE_AST:
f();
if (((AST*)gc->p)->type > AST_TYPE_MAX) { if (((AST*)gc->p)->type > AST_TYPE_MAX) {
log_dbgf( log_dbgf(
"Attempted to free invalid AST (%i > %i) to GC: gc:%p " "Attempted to free invalid AST (%i > %i) to GC: gc:%p "

View File

@@ -44,6 +44,9 @@
%token EQ // Equals =. %token EQ // Equals =.
%token DEQ // Double equals ==. %token DEQ // Double equals ==.
%token RARROW // Right arrow ->.
%token LARROW // Left arrow <-.
%token EXPSEP // Expression seperator ;. %token EXPSEP // Expression seperator ;.
%token<strval> WORD // Word, i.e. keyword. %token<strval> WORD // Word, i.e. keyword.
@@ -56,10 +59,19 @@
%token NL // Newline. %token NL // Newline.
%token COLON // Colon :.
%token STOP // Stop sign $.
%token FORCE // Force operator !.
%token PRESERVE // Preserve operator @.
%token BACKSLASH %token BACKSLASH
%left ADD SUB %left ADD SUB
%left MUL DIV %left MUL DIV
%left RARROW
%right LARROW
%nonassoc STOP
%precedence NEG %precedence NEG
%type<ast> exp; %type<ast> exp;
@@ -74,6 +86,8 @@
%% %%
maybe_stop: %empty | STOP;
inputstart: inputstart:
exp { exp {
DList* exps = dlist_init(); DList* exps = dlist_init();
@@ -132,9 +146,99 @@ block:
} }
; ;
exp: exp:
// Stop sign.
exp STOP { $$ = $1; }
// Variable reference.
| WORD {
$$ = ast_init(AST_TYPE_REF, ast_ref_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 (general hacky form).
| exp GROUPS GROUPE {
size_t argc = 1;
AST** argv = NULL;
$$ = ast_init(AST_TYPE_CALL, ast_call_data_init(
argc,
argv,
$1
));
}
// Call (right arrow general form).
| GROUPS arg GROUPE RARROW exp {
size_t argc = $2->ln;
AST** argv = $2->buf;
argarr_destroypsv($2);
$$ = ast_init(AST_TYPE_CALL, ast_call_data_init(
argc,
argv,
$5
));
}
| exp RARROW exp {
size_t argc = 1;
AST** argv = malloc(sizeof(AST*));
argv[0] = $1;
$$ = ast_init(AST_TYPE_CALL, ast_call_data_init(
argc,
argv,
$3
));
}
| exp LARROW exp {
size_t argc = 1;
AST** argv = malloc(sizeof(AST*));
argv[0] = $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(
argc,
argv,
ast_init(AST_TYPE_REF, ast_ref_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_REF, ast_ref_data_init($1))
));
}
// Number. // Number.
NUM { $$ = ast_init(AST_TYPE_LIT_NUM, ast_num_data_init($1)); } | NUM { $$ = ast_init(AST_TYPE_LIT_NUM, ast_num_data_init($1)); }
| BOOLT { $$ = ast_init(AST_TYPE_LIT_BOOL, ast_bool_data_init(1)); } | BOOLT { $$ = ast_init(AST_TYPE_LIT_BOOL, ast_bool_data_init(1)); }
| BOOLF { $$ = ast_init(AST_TYPE_LIT_BOOL, ast_bool_data_init(0)); } | BOOLF { $$ = ast_init(AST_TYPE_LIT_BOOL, ast_bool_data_init(0)); }
@@ -163,6 +267,7 @@ exp:
ast_init(AST_TYPE_BIF, ast_bif_data_init(builtin_if)) ast_init(AST_TYPE_BIF, ast_bif_data_init(builtin_if))
)); ));
} }
| IF exp exp ELSE exp { | IF exp exp ELSE exp {
AST** argv = calloc(3, sizeof(AST*)); AST** argv = calloc(3, sizeof(AST*));
argv[0] = $2; argv[0] = $2;
@@ -176,53 +281,14 @@ exp:
)); ));
} }
// Variable reference. // Function definitions. Convert to Def of Lambda.
| WORD { | WORD GROUPS arg GROUPE exp maybe_stop {
$$ = 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(
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; size_t parc = $3->ln;
AST** parv = $3->buf; AST** parv = $3->buf;
argarr_destroypsv($3); argarr_destroypsv($3);
$$ = ast_init(AST_TYPE_VDEF, ast_vdef_data_init( $$ = ast_init(AST_TYPE_DEF, ast_def_data_init(
$1, $1,
NULL,
ast_init(AST_TYPE_LAMBDA, ast_lambda_data_init( ast_init(AST_TYPE_LAMBDA, ast_lambda_data_init(
parc, parv, $5 parc, parv, $5
)) ))
@@ -237,9 +303,21 @@ exp:
$$ = ast_init(AST_TYPE_LAMBDA, ast_lambda_data_init(parc, parv, $5)); $$ = ast_init(AST_TYPE_LAMBDA, ast_lambda_data_init(parc, parv, $5));
} }
// Force operator.
| FORCE exp {
$$ = ast_init(AST_TYPE_FORCE, ast_force_data_init($2));
}
// Preserve operator.
| PRESERVE exp {
$$ = ast_init(AST_TYPE_PRESERVE, ast_preserve_data_init($2));
}
// Block. // Block.
| BLOCKS block BLOCKE { | BLOCKS block BLOCKE {
$$ = ast_init(AST_TYPE_BLOCK, ast_block_data_init((AST**) $2->buf, $2->ln)); AST** exps = (AST**) $2->buf;
dlist_destroypsv($2);
$$ = ast_init(AST_TYPE_BLOCK, ast_block_data_init((AST**) exps, $2->ln));
} }
// Negative. // Negative.
@@ -261,9 +339,23 @@ exp:
// Group. // Group.
| GROUPS exp GROUPE { $$ = $2; } | GROUPS exp GROUPE { $$ = $2; }
// Variable definition. // Definition with type annotation.
| WORD COLON exp EQ exp {
$$ = ast_init(AST_TYPE_DEF, ast_def_data_init($1, $3, $5));
}
// Definition with type annotation.
| WORD COLON WORD EQ exp {
$$ = ast_init(AST_TYPE_DEF, ast_def_data_init(
$1,
ast_init(AST_TYPE_REF, ast_ref_data_init($3)),
$5
));
}
// Definition.
| WORD EQ exp { | WORD EQ exp {
$$ = ast_init(AST_TYPE_VDEF, ast_vdef_data_init($1, $3)); $$ = ast_init(AST_TYPE_DEF, ast_def_data_init($1, NULL, $3));
} }
| exp ADD exp { | exp ADD exp {

View File

@@ -9,25 +9,30 @@ typedef enum {
// Primitive type literals. // Primitive type literals.
AST_TYPE_LIT_NUM, // A number (float) literal. AST_TYPE_LIT_NUM, // A number (float) literal.
AST_TYPE_LIT_BOOL, // A boolean literal. AST_TYPE_LIT_BOOL, // A boolean literal.
AST_TYPE_LIT_VEC, // A vector literal.
AST_TYPE_LIT_KIND, // A kind literal. AST_TYPE_LIT_KIND, // A kind literal.
AST_TYPE_INT, // An integer. AST_TYPE_EXC_CON, // Exception constructor `Exc`.
AST_TYPE_SYM, // A symbol. AST_TYPE_VEC_CON, // Vectpr constructor `Vec()`.
AST_TYPE_EXC, // Exception.
// Collection types: // Collection types:
AST_TYPE_VEC, // A vector (fixed size, fixed type). AST_TYPE_VEC, // A vector (fixed size, fixed type).
AST_TYPE_LIST, // A list (variable size, variable type). AST_TYPE_LIST, // A list (variable size, variable type).
// Syntactic types:
AST_TYPE_FORCE,
AST_TYPE_PRESERVE,
AST_TYPE_BLOCK, // A block of code (scope).
AST_TYPE_ARG, // A definition argument.
// Misc. types. // Misc. types.
AST_TYPE_BIF, // Built-in function. 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_DEF, // A definition.
AST_TYPE_VREF, // A variable reference. AST_TYPE_REF, // A variable reference.
AST_TYPE_BLOCK, // A block of code (scope).
AST_TYPE_LAMBDA, // An anonymous function definition. AST_TYPE_LAMBDA, // An anonymous function definition.
AST_TYPE_ARG, // A definition argument. AST_TYPE_EXC, // An exception.
AST_TYPE_MAX = AST_TYPE_ARG, AST_TYPE_MAX = AST_TYPE_EXC,
} ASTType; } ASTType;
// An Abstract Syntax Tree. // An Abstract Syntax Tree.
@@ -60,12 +65,22 @@ ASTBoolData* ast_bool_data_init(int val);
// Destroy an `ASTBoolData`. // Destroy an `ASTBoolData`.
void ast_bool_data_destroy(ASTBoolData* bol); void ast_bool_data_destroy(ASTBoolData* bol);
// A kind. // A literal kind.
typedef struct { typedef enum {
char* name; AST_LIT_KIND_BOOL,
AST_LIT_KIND_NUM,
AST_LIT_KIND_KIND,
AST_LIT_KIND_MAX = AST_LIT_KIND_KIND
} ASTKindData; } ASTKindData;
extern const char* ast_lit_kind_names[AST_LIT_KIND_MAX + 2];
// Create a new `ASTKindData`.
ASTKindData* ast_kind_data_init(ASTKindData);
// Destroy an `ASTKindData`.
void ast_kind_data_destroy(ASTKindData*);
// An exception. // An exception.
typedef struct ASTEXCDATA { typedef struct ASTEXCDATA {
const char* msg; // The exception message. const char* msg; // The exception message.
@@ -120,26 +135,27 @@ ASTCallData* ast_call_data_init(size_t argc, AST** argv, AST* exp);
// Destroy an `ASTCallData`. // Destroy an `ASTCallData`.
void ast_call_data_destroy(ASTCallData* call); void ast_call_data_destroy(ASTCallData* call);
// A variable definition. Associates a name with an expression. // A definition. Associates a name and kind with an expression.
typedef struct { typedef struct {
char* name; char* name;
AST* kind; // If NULL, assume `Any` kind.
AST* exp; AST* exp;
} ASTVDefData; } ASTDefData;
// Create a new `ASTVDefData`. // Create a new `ASTDefData`.
ASTVDefData* ast_vdef_data_init(char* name, AST* exp); ASTDefData* ast_def_data_init(char* name, AST* kind, AST* exp);
// Destroy an `ASTVDefData`. // Destroy an `ASTDefData`.
void ast_vdef_data_destroy(ASTVDefData* vdef); void ast_def_data_destroy(ASTDefData* vdef);
// A variable reference. // A reference.
typedef struct { typedef struct {
char* to; // What the reference's to. char* to; // What the reference's to.
} ASTVrefData; } ASTRefData;
// Create a new `ASTVRefData`. // Create a new `ASTRefData`.
ASTVrefData* ast_vref_data_init(char* to); ASTRefData* ast_ref_data_init(char* to);
// Destroy an `ASTVRefData`. // Destroy an `ASTRefData`.
void ast_vref_data_destroy(ASTVrefData* call); void ast_ref_data_destroy(ASTRefData* call);
// A code block. // A code block.
typedef struct { typedef struct {
@@ -151,8 +167,6 @@ typedef struct {
ASTBlockData* ast_block_data_init(AST** inside, size_t ln); ASTBlockData* ast_block_data_init(AST** inside, size_t ln);
// Destroy an `ASTBlockData`, recursively. // Destroy an `ASTBlockData`, recursively.
void ast_block_data_destroy(ASTBlockData* block); void ast_block_data_destroy(ASTBlockData* block);
// Destroy an `ASTBlockData`.
void ast_block_data_destroy_psv(ASTBlockData* block);
typedef struct { typedef struct {
char* name; // Argument name. char* name; // Argument name.
@@ -166,4 +180,14 @@ void ast_arg_data_destroy(ASTArgData* arg);
// Find the expression associated with a name in the nearest scope. // Find the expression associated with a name in the nearest scope.
AST* ast_find(Scope* scope, char* name); AST* ast_find(Scope* scope, char* name);
// A force operator.
typedef struct {AST* body;} ASTForceData;
ASTForceData* ast_force_data_init(AST* body);
void ast_force_data_destroy(ASTForceData* force);
// A preserve operator.
typedef struct {AST* body;} ASTPreserveData;
ASTPreserveData* ast_preserve_data_init(AST* body);
void ast_preserve_data_destroy(ASTPreserveData* preserve);
#endif #endif

View File

@@ -14,17 +14,20 @@ void ast_num_print(ASTNumData*, int i);
// Print an `ASTBoolData`. // Print an `ASTBoolData`.
void ast_bool_print(ASTBoolData*, int i); void ast_bool_print(ASTBoolData*, int i);
// Print an `ASTKindData`.
void ast_kind_print(ASTKindData*, int i);
// Print an `ASTExecData`. // Print an `ASTExecData`.
void ast_exc_print(ASTExcData*, int i); void ast_exc_print(ASTExcData*, int i);
// Print an `ASTCallData`. // Print an `ASTCallData`.
void ast_call_print(ASTCallData*, int i); void ast_call_print(ASTCallData*, int i);
// Print an `ASTVDefData`. // Print an `ASTDefData`.
void ast_vdef_print(ASTVDefData*, int depth); void ast_def_print(ASTDefData*, int depth);
// Print an `ASTVRefData`. // Print an `ASTRefData`.
void ast_vref_print(ASTVrefData*, int i); void ast_ref_print(ASTRefData*, int i);
// Print an `ASTBlockData`. // Print an `ASTBlockData`.
void ast_block_print(ASTBlockData*, int i); void ast_block_print(ASTBlockData*, int i);
@@ -38,4 +41,9 @@ void ast_lambda_print(ASTLambdaData* arg, int i);
// Print an `ASTBIFData`. // Print an `ASTBIFData`.
void ast_bif_print(ASTBIFData* arg, int i); void ast_bif_print(ASTBIFData* arg, int i);
// Print an `ASTForceData`.
void ast_force_print(ASTForceData* force, int i);
// Print an `ASTPreserveData`.
void ast_preserve_print(ASTPreserveData* preserve, int i);
#endif #endif

View File

@@ -12,14 +12,18 @@ AST* exec_exp(AST* ast, Scope* parent);
AST* exec_block(AST* ast, Scope* parent); AST* exec_block(AST* ast, Scope* parent);
// Execute a call. // Execute a call.
AST* exec_call(AST* ast, Scope* parent); AST* exec_call(AST* ast, Scope* parent);
// Execute a variable definition. // Execute a definition.
AST* exec_vdef(AST* ast, Scope* parent); AST* exec_def(AST* ast, Scope* parent);
// Execute a variable reference. // Execute a reference.
AST* exec_vref(AST* ast, Scope* parent); AST* exec_ref(AST* ast, Scope* parent);
// Execute a function definition. // Execute a function definition.
AST* exec_fdef(AST* ast, Scope* parent); AST* exec_fdef(AST* ast, Scope* parent);
// Execute a lambda expression. // Execute a lambda expression.
AST* exec_lambda(size_t argc, AST** argv, AST* exp, Scope* parent); AST* exec_lambda(size_t argc, AST** argv, AST* exp, Scope* parent);
// Execute a force expression.
AST* exec_force(AST* ast, Scope* parent);
// Execute a preserve expression.
AST* exec_preserve(AST* ast, Scope* parent);
// Print the result of an execution. // Print the result of an execution.
void exec_print(double n); void exec_print(double n);

View File

@@ -2,6 +2,7 @@
#define GC_H #define GC_H
#include <stdlib.h> #include <stdlib.h>
#include <stdbool.h>
// The type a GC can refer to. // The type a GC can refer to.
typedef enum { typedef enum {

View File

@@ -106,18 +106,17 @@ int yylex() {
// Assign & consume current character. // Assign & consume current character.
int c = *inp++; int c = *inp++;
if (c == '-' && *inp == '>' && inp++) { return RARROW; }
if (c == '<' && *inp == '-' && inp++) { return LARROW; }
if (c == '=' && *inp == '=' && inp++) { return DEQ; }
if (c == '=' && *inp != '=') { return EQ; }
// Check for NUM. // Check for NUM.
if (isdigit(c)) { if (isdigit(c)) {
yylval.fval = acc_float(c); // Set the token value. yylval.fval = acc_float(c); // Set the token value.
return NUM; return NUM;
} }
if (c == '=') {
yylval.strval = acc_deq(c);
if (!strcmp(yylval.strval, "=")) return EQ;
if (!strcmp(yylval.strval, "==")) return DEQ;
}
if (isalpha(c) || c == '_') { if (isalpha(c) || c == '_') {
yylval.strval = acc_word(c); yylval.strval = acc_word(c);
@@ -143,10 +142,12 @@ int yylex() {
case ';': return EXPSEP; case ';': return EXPSEP;
case '{': return BLOCKS; case '{': return BLOCKS;
case '}': return BLOCKE; case '}': return BLOCKE;
// case '=': return EQ;
case '\\': return BACKSLASH; case '\\': return BACKSLASH;
case '?': return IF; case '?': return IF;
case ':': return ELSE; case ':': return COLON;
case '$': return STOP;
case '!': return FORCE;
case '@': return PRESERVE;
default: fprintf(stderr, "Unexpected character: %c\n", c); default: fprintf(stderr, "Unexpected character: %c\n", c);
} }

View File

@@ -19,8 +19,8 @@ extern int yyparse();
int main(int argc, char** argv) { 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");
ast_print(root); // ast_print(root);
AST* eval = exec_start(root); AST* eval = exec_start(root);
ast_print(eval); ast_print(eval);
// ast_destroy(eval); // ast_destroy(eval);
@@ -54,7 +54,11 @@ int main(int argc, char** argv) {
inp = ln->buf; inp = ln->buf;
if (yyparse() == 0) { if (yyparse() == 0) {
log_dbg("Parsed successfully!\n"); log_dbg("Parsed successfully!\n");
} else printf("Parse error.\n"); } else {
printf("Parse error.\n");
dstr_destroy(ln);
continue;
}
#ifdef DBG #ifdef DBG
ast_print(root); ast_print(root);

View File

@@ -17,7 +17,7 @@ void test_ast_num() {
} }
void test_ast_call() { void test_ast_call() {
AST** argv = malloc(2*sizeof(AST*)); AST** argv = malloc(2 * sizeof(AST*));
argv[0] = ast_init(AST_TYPE_NUM, ast_num_data_init(1.0)); argv[0] = ast_init(AST_TYPE_NUM, ast_num_data_init(1.0));
argv[1] = ast_init(AST_TYPE_NUM, ast_num_data_init(2.0)); argv[1] = ast_init(AST_TYPE_NUM, ast_num_data_init(2.0));
@@ -37,23 +37,8 @@ void test_ast_call() {
ast_destroy(ast); ast_destroy(ast);
} }
void test_ast_vref() { // ast_destroy(ast);
char* s = malloc(2);
strcpy(s, "x");
ASTVrefData* vref = ast_vref_data_init(s);
AST* ast = ast_init(AST_TYPE_VREF, vref);
TEST_ASSERT_EQUAL(AST_TYPE_VREF, ast->type);
ASTVrefData data = *(ASTVrefData*)ast->data;
TEST_ASSERT_EQUAL_STRING("x", data.to);
//ast_destroy(ast);
} }
int main() { int main() {
UNITY_BEGIN(); UNITY_BEGIN();
RUN_TEST(test_ast_num);
//RUN_TEST(test_ast_call);
//RUN_TEST(test_ast_vref);
return UNITY_END();
}

31
type_notes.md Normal file
View File

@@ -0,0 +1,31 @@
n: Int = 3
- 'Int': integer constructor
- '3': integer literal
v: Vec(3, Int) = <1, 2, 3>
Point: Struct = { x: Int, y: Int }
p: Point = { .x = 1, .y = 2 }
p.x + p.y
VecOf(n: Int, t: Type): Type = Vec(n, t)
strings: VecOf(3, Str) = <"Hello", ",", " world.">
f(g) = g(2)
g(n: Int): Int = n * 2
g: Lambda(Int, Int) = \(n: Int):Int n * 2
Int, Vec, Str
f(g: \(Int):Int ):Int = g(2)
f(g: Lambda(Int, Int)): Int = g(2)
f: Lambda(Int, Str, Str) = \(s1: Str, s2: Str) length(s) + length(s2)
Bad: Type = if Until.time() % 2 == 0 Str Int
- Types are code.
- All types inherit from `Type` (the type of `Type` is `Type`).