2024-03-18 09:47:07 -07:00
|
|
|
// Compilation logic
|
2024-02-04 12:23:59 -08:00
|
|
|
#include <ctype.h>
|
|
|
|
#include <gc/cord.h>
|
|
|
|
#include <gc.h>
|
|
|
|
#include <stdio.h>
|
2024-03-03 16:12:53 -08:00
|
|
|
#include <uninorm.h>
|
2024-02-04 12:23:59 -08:00
|
|
|
|
|
|
|
#include "ast.h"
|
2024-03-03 15:15:45 -08:00
|
|
|
#include "builtins/text.h"
|
2024-02-04 15:04:41 -08:00
|
|
|
#include "compile.h"
|
2024-02-24 12:24:44 -08:00
|
|
|
#include "enums.h"
|
2024-02-24 10:27:49 -08:00
|
|
|
#include "structs.h"
|
2024-02-17 13:56:19 -08:00
|
|
|
#include "environment.h"
|
2024-02-17 14:00:21 -08:00
|
|
|
#include "typecheck.h"
|
2024-03-09 21:03:21 -08:00
|
|
|
#include "builtins/util.h"
|
2024-02-04 12:23:59 -08:00
|
|
|
|
2024-03-22 10:53:23 -07:00
|
|
|
static CORD compile_to_pointer_depth(env_t *env, ast_t *ast, int64_t target_depth, bool allow_optional);
|
|
|
|
|
2024-03-17 17:40:40 -07:00
|
|
|
CORD compile_type_ast(env_t *env, type_ast_t *t)
|
2024-02-04 12:23:59 -08:00
|
|
|
{
|
|
|
|
switch (t->tag) {
|
2024-03-17 17:40:40 -07:00
|
|
|
case VarTypeAST: return CORD_all(env->file_prefix, Match(t, VarTypeAST)->name, "_t");
|
|
|
|
case PointerTypeAST: return CORD_cat(compile_type_ast(env, Match(t, PointerTypeAST)->pointed), "*");
|
2024-02-17 23:34:39 -08:00
|
|
|
case TableTypeAST: return "table_t";
|
|
|
|
case ArrayTypeAST: return "array_t";
|
|
|
|
case FunctionTypeAST: return "const void*";
|
2024-02-17 13:56:19 -08:00
|
|
|
default: code_err(t, "Not implemented");
|
2024-02-04 12:23:59 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-09 13:03:38 -08:00
|
|
|
static bool promote(env_t *env, CORD *code, type_t *actual, type_t *needed)
|
|
|
|
{
|
|
|
|
if (type_eq(actual, needed))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (!can_promote(actual, needed))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (actual->tag == IntType || actual->tag == NumType)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// Automatic dereferencing:
|
|
|
|
if (actual->tag == PointerType && !Match(actual, PointerType)->is_optional
|
|
|
|
&& can_promote(Match(actual, PointerType)->pointed, needed)) {
|
|
|
|
*code = CORD_all("*(", *code, ")");
|
|
|
|
return promote(env, code, Match(actual, PointerType)->pointed, needed);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Optional promotion:
|
|
|
|
if (actual->tag == PointerType && needed->tag == PointerType)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (needed->tag == ClosureType && actual->tag == FunctionType && type_eq(actual, Match(needed, ClosureType)->fn)) {
|
|
|
|
*code = CORD_all("(closure_t){", *code, ", NULL}");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2024-04-16 10:50:07 -07:00
|
|
|
CORD compile_declaration(env_t *env, type_t *t, CORD name)
|
2024-03-09 11:02:19 -08:00
|
|
|
{
|
|
|
|
if (t->tag == FunctionType) {
|
|
|
|
auto fn = Match(t, FunctionType);
|
2024-03-17 17:40:40 -07:00
|
|
|
CORD code = CORD_all(compile_type(env, fn->ret), " (*", name, ")(");
|
2024-03-09 11:02:19 -08:00
|
|
|
for (arg_t *arg = fn->args; arg; arg = arg->next) {
|
2024-03-17 17:40:40 -07:00
|
|
|
code = CORD_all(code, compile_type(env, arg->type));
|
2024-03-09 11:02:19 -08:00
|
|
|
if (arg->next) code = CORD_cat(code, ", ");
|
|
|
|
}
|
|
|
|
return CORD_all(code, ")");
|
|
|
|
} else {
|
2024-03-17 17:40:40 -07:00
|
|
|
return CORD_all(compile_type(env, t), " ", name);
|
2024-03-09 11:02:19 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-17 17:40:40 -07:00
|
|
|
CORD compile_type(env_t *env, type_t *t)
|
2024-02-17 23:34:39 -08:00
|
|
|
{
|
|
|
|
switch (t->tag) {
|
|
|
|
case AbortType: return "void";
|
|
|
|
case VoidType: return "void";
|
|
|
|
case MemoryType: return "void";
|
|
|
|
case BoolType: return "Bool_t";
|
2024-03-03 10:37:05 -08:00
|
|
|
case IntType: return Match(t, IntType)->bits == 64 ? "Int_t" : CORD_asprintf("Int%ld_t", Match(t, IntType)->bits);
|
|
|
|
case NumType: return Match(t, NumType)->bits == 64 ? "Num_t" : CORD_asprintf("Num%ld_t", Match(t, NumType)->bits);
|
2024-03-03 15:15:45 -08:00
|
|
|
case TextType: {
|
2024-03-21 22:52:00 -07:00
|
|
|
auto text = Match(t, TextType);
|
|
|
|
return text->lang ? CORD_all(text->env->file_prefix, text->lang, "_t") : "Text_t";
|
2024-02-17 23:34:39 -08:00
|
|
|
}
|
|
|
|
case ArrayType: return "array_t";
|
|
|
|
case TableType: return "table_t";
|
2024-03-09 11:02:19 -08:00
|
|
|
case FunctionType: {
|
|
|
|
auto fn = Match(t, FunctionType);
|
2024-03-17 17:40:40 -07:00
|
|
|
CORD code = CORD_all(compile_type(env, fn->ret), " (*)(");
|
2024-03-09 11:02:19 -08:00
|
|
|
for (arg_t *arg = fn->args; arg; arg = arg->next) {
|
2024-03-17 17:40:40 -07:00
|
|
|
code = CORD_all(code, compile_type(env, arg->type));
|
2024-03-09 11:02:19 -08:00
|
|
|
if (arg->next) code = CORD_cat(code, ", ");
|
|
|
|
}
|
|
|
|
return CORD_all(code, ")");
|
|
|
|
}
|
|
|
|
case ClosureType: return "closure_t";
|
2024-03-17 17:40:40 -07:00
|
|
|
case PointerType: return CORD_cat(compile_type(env, Match(t, PointerType)->pointed), "*");
|
2024-03-21 22:52:00 -07:00
|
|
|
case StructType: {
|
|
|
|
auto s = Match(t, StructType);
|
|
|
|
return CORD_all(s->env->file_prefix, s->name, "_t");
|
|
|
|
}
|
|
|
|
case EnumType: {
|
|
|
|
auto e = Match(t, EnumType);
|
|
|
|
return CORD_all(e->env->file_prefix, e->name, "_t");
|
|
|
|
}
|
2024-02-17 23:34:39 -08:00
|
|
|
case TypeInfoType: return "TypeInfo";
|
|
|
|
default: compiler_err(NULL, NULL, NULL, "Not implemented");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-24 11:28:20 -07:00
|
|
|
static CORD compile_lvalue(env_t *env, ast_t *ast)
|
2024-03-14 10:28:30 -07:00
|
|
|
{
|
|
|
|
if (!can_be_mutated(env, ast)) {
|
|
|
|
if (ast->tag == Index || ast->tag == FieldAccess) {
|
|
|
|
ast_t *subject = ast->tag == Index ? Match(ast, Index)->indexed : Match(ast, FieldAccess)->fielded;
|
2024-03-22 10:53:23 -07:00
|
|
|
code_err(subject, "This is an immutable value, you can't assign to it");
|
2024-03-14 10:28:30 -07:00
|
|
|
} else {
|
|
|
|
code_err(ast, "This is a value of type %T and can't be assigned to", get_type(env, ast));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-24 11:28:20 -07:00
|
|
|
if (ast->tag == Index) {
|
|
|
|
auto index = Match(ast, Index);
|
2024-03-22 10:53:23 -07:00
|
|
|
type_t *container_t = get_type(env, index->indexed);
|
2024-03-24 11:28:20 -07:00
|
|
|
if (!index->index && container_t->tag == PointerType) {
|
|
|
|
if (Match(container_t, PointerType)->is_optional)
|
|
|
|
code_err(index->indexed, "This pointer might be null, so it can't be safely assigned to");
|
|
|
|
return compile(env, ast);
|
|
|
|
}
|
2024-03-22 10:53:23 -07:00
|
|
|
container_t = value_type(container_t);
|
2024-03-24 11:28:20 -07:00
|
|
|
if (container_t->tag == ArrayType) {
|
2024-03-22 10:53:23 -07:00
|
|
|
CORD target_code = compile_to_pointer_depth(env, index->indexed, 1, false);
|
|
|
|
type_t *item_type = Match(container_t, ArrayType)->item_type;
|
2024-04-16 10:50:07 -07:00
|
|
|
return CORD_all("Array_lvalue(", compile_type(env, item_type), ", ", target_code, ", ",
|
2024-03-24 11:28:20 -07:00
|
|
|
compile(env, index->index), ", ", compile_type_info(env, container_t),
|
2024-03-29 09:54:31 -07:00
|
|
|
", ", Text$quoted(ast->file->filename, false), ", ", heap_strf("%ld", ast->start - ast->file->text),
|
2024-03-24 11:28:20 -07:00
|
|
|
", ", heap_strf("%ld", ast->end - ast->file->text), ")");
|
|
|
|
} else if (container_t->tag == TableType) {
|
2024-03-22 10:53:23 -07:00
|
|
|
CORD target_code = compile_to_pointer_depth(env, index->indexed, 1, false);
|
2024-03-24 11:28:20 -07:00
|
|
|
type_t *value_t = Match(container_t, TableType)->value_type;
|
2024-03-29 09:54:31 -07:00
|
|
|
return CORD_all("*(", compile_type(env, value_t), "*)Table$reserve_value(", target_code, ", ",
|
2024-03-24 11:28:20 -07:00
|
|
|
compile(env, index->index),", ", compile_type_info(env, container_t), ")");
|
|
|
|
} else {
|
|
|
|
code_err(ast, "I don't know how to assign to this target");
|
2024-03-22 10:53:23 -07:00
|
|
|
}
|
2024-03-24 11:28:20 -07:00
|
|
|
} else if (ast->tag == Var || ast->tag == FieldAccess) {
|
|
|
|
return compile(env, ast);
|
|
|
|
} else {
|
|
|
|
code_err(ast, "I don't know how to assign to this");
|
2024-03-22 10:53:23 -07:00
|
|
|
}
|
2024-03-24 11:28:20 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static CORD compile_assignment(env_t *env, ast_t *target, CORD value)
|
|
|
|
{
|
|
|
|
return CORD_all(compile_lvalue(env, target), " = ", value, ";\n");
|
2024-03-22 10:53:23 -07:00
|
|
|
}
|
|
|
|
|
2024-02-13 11:42:33 -08:00
|
|
|
CORD compile_statement(env_t *env, ast_t *ast)
|
2024-02-04 15:04:41 -08:00
|
|
|
{
|
|
|
|
switch (ast->tag) {
|
2024-03-14 10:28:30 -07:00
|
|
|
case When: {
|
|
|
|
auto when = Match(ast, When);
|
|
|
|
type_t *subject_t = get_type(env, when->subject);
|
|
|
|
auto enum_t = Match(subject_t, EnumType);
|
2024-04-16 10:50:07 -07:00
|
|
|
CORD code = CORD_all("{ ", compile_type(env, subject_t), " subject = ", compile(env, when->subject), ";\n"
|
|
|
|
"switch (subject.$tag) {");
|
2024-03-14 10:28:30 -07:00
|
|
|
type_t *result_t = get_type(env, ast);
|
|
|
|
(void)result_t;
|
|
|
|
for (when_clause_t *clause = when->clauses; clause; clause = clause->next) {
|
|
|
|
const char *clause_tag_name = Match(clause->tag_name, Var)->name;
|
2024-03-17 18:47:40 -07:00
|
|
|
code = CORD_all(code, "case $tag$", env->file_prefix, enum_t->name, "$", clause_tag_name, ": {\n");
|
2024-03-14 10:28:30 -07:00
|
|
|
type_t *tag_type = NULL;
|
|
|
|
for (tag_t *tag = enum_t->tags; tag; tag = tag->next) {
|
|
|
|
if (streq(tag->name, clause_tag_name)) {
|
|
|
|
tag_type = tag->type;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert(tag_type);
|
|
|
|
env_t *scope = env;
|
|
|
|
if (clause->var) {
|
2024-04-16 10:50:07 -07:00
|
|
|
code = CORD_all(code, compile_type(env, tag_type), " ", compile(env, clause->var), " = subject.", clause_tag_name, ";\n");
|
2024-03-14 10:28:30 -07:00
|
|
|
scope = fresh_scope(env);
|
|
|
|
set_binding(scope, Match(clause->var, Var)->name, new(binding_t, .type=tag_type));
|
|
|
|
}
|
|
|
|
code = CORD_all(code, compile_statement(scope, clause->body), "\nbreak;\n}\n");
|
|
|
|
}
|
|
|
|
if (when->else_body) {
|
|
|
|
code = CORD_all(code, "default: {\n", compile_statement(env, when->else_body), "\nbreak;\n}");
|
|
|
|
}
|
|
|
|
code = CORD_all(code, "\n}\n}");
|
|
|
|
return code;
|
|
|
|
}
|
|
|
|
case DocTest: {
|
|
|
|
auto test = Match(ast, DocTest);
|
|
|
|
type_t *expr_t = get_type(env, test->expr);
|
|
|
|
if (!expr_t)
|
|
|
|
code_err(test->expr, "I couldn't figure out the type of this expression");
|
|
|
|
|
|
|
|
CORD output = NULL;
|
|
|
|
if (test->output) {
|
|
|
|
const uint8_t *raw = (const uint8_t*)CORD_to_const_char_star(test->output);
|
|
|
|
uint8_t buf[128] = {0};
|
|
|
|
size_t norm_len = sizeof(buf);
|
|
|
|
uint8_t *norm = u8_normalize(UNINORM_NFD, (uint8_t*)raw, strlen((char*)raw)+1, buf, &norm_len);
|
|
|
|
assert(norm[norm_len-1] == 0);
|
|
|
|
output = CORD_from_char_star((char*)norm);
|
|
|
|
if (norm && norm != buf) free(norm);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (test->expr->tag == Declare) {
|
|
|
|
auto decl = Match(test->expr, Declare);
|
2024-04-21 08:22:11 -07:00
|
|
|
if (decl->value->tag == Use) {
|
|
|
|
assert(compile_statement(env, test->expr) == CORD_EMPTY);
|
|
|
|
return CORD_asprintf(
|
|
|
|
"test(NULL, NULL, %r, %r, %ld, %ld);",
|
|
|
|
compile(env, WrapAST(test->expr, TextLiteral, .cord=output)),
|
|
|
|
compile(env, WrapAST(test->expr, TextLiteral, .cord=test->expr->file->filename)),
|
|
|
|
(int64_t)(test->expr->start - test->expr->file->text),
|
|
|
|
(int64_t)(test->expr->end - test->expr->file->text));
|
|
|
|
} else {
|
|
|
|
return CORD_asprintf(
|
|
|
|
"%r\n"
|
|
|
|
"test(&%r, %r, %r, %r, %ld, %ld);",
|
|
|
|
compile_statement(env, test->expr),
|
|
|
|
compile(env, decl->var),
|
|
|
|
compile_type_info(env, get_type(env, decl->value)),
|
|
|
|
compile(env, WrapAST(test->expr, TextLiteral, .cord=output)),
|
|
|
|
compile(env, WrapAST(test->expr, TextLiteral, .cord=test->expr->file->filename)),
|
|
|
|
(int64_t)(test->expr->start - test->expr->file->text),
|
|
|
|
(int64_t)(test->expr->end - test->expr->file->text));
|
|
|
|
}
|
2024-03-14 10:28:30 -07:00
|
|
|
} else if (test->expr->tag == Assign) {
|
|
|
|
auto assign = Match(test->expr, Assign);
|
|
|
|
if (!assign->targets->next && assign->targets->ast->tag == Var) {
|
|
|
|
// Common case: assigning to one variable:
|
|
|
|
CORD var = compile(env, assign->targets->ast);
|
2024-03-22 10:53:23 -07:00
|
|
|
CORD code = compile_assignment(env, assign->targets->ast, compile(env, assign->values->ast));
|
2024-04-16 10:50:07 -07:00
|
|
|
CORD_appendf(&code, "test(&%r, %r, %r, %r, %ld, %ld);",
|
2024-03-14 10:28:30 -07:00
|
|
|
var, compile_type_info(env, get_type(env, assign->targets->ast)),
|
|
|
|
compile(env, WrapAST(test->expr, TextLiteral, .cord=test->output)),
|
|
|
|
compile(env, WrapAST(test->expr, TextLiteral, .cord=test->expr->file->filename)),
|
|
|
|
(int64_t)(test->expr->start - test->expr->file->text),
|
|
|
|
(int64_t)(test->expr->end - test->expr->file->text));
|
|
|
|
return code;
|
|
|
|
} else {
|
|
|
|
// Multi-assign or assignment to potentially non-idempotent targets
|
|
|
|
if (test->output && assign->targets->next)
|
|
|
|
code_err(ast, "Sorry, but doctesting with '=' is not supported for multi-assignments");
|
|
|
|
|
|
|
|
CORD code = "{ // Assignment\n";
|
|
|
|
int64_t i = 1;
|
2024-04-23 09:54:56 -07:00
|
|
|
for (ast_list_t *target = assign->targets, *value = assign->values; target && value; target = target->next, value = value->next) {
|
|
|
|
type_t *target_type = get_type(env, target->ast);
|
|
|
|
type_t *value_type = get_type(env, value->ast);
|
|
|
|
CORD val_code = compile(env, value->ast);
|
|
|
|
if (!promote(env, &val_code, value_type, target_type))
|
|
|
|
code_err(value->ast, "This %T value cannot be converted to a %T type", value_type, target_type);
|
|
|
|
CORD_appendf(&code, "%r $%ld = %r;\n", compile_type(env, target_type), i++, val_code);
|
|
|
|
}
|
2024-03-14 10:28:30 -07:00
|
|
|
i = 1;
|
2024-03-22 10:53:23 -07:00
|
|
|
for (ast_list_t *target = assign->targets; target; target = target->next)
|
|
|
|
code = CORD_all(code, compile_assignment(env, target->ast, CORD_asprintf("$%ld", i++)));
|
2024-03-14 10:28:30 -07:00
|
|
|
|
2024-04-16 10:50:07 -07:00
|
|
|
CORD_appendf(&code, "test(&$1, %r, %r, %r, %ld, %ld);",
|
2024-03-14 10:28:30 -07:00
|
|
|
compile_type_info(env, get_type(env, assign->targets->ast)),
|
|
|
|
compile(env, WrapAST(test->expr, TextLiteral, .cord=test->output)),
|
|
|
|
compile(env, WrapAST(test->expr, TextLiteral, .cord=test->expr->file->filename)),
|
|
|
|
(int64_t)(test->expr->start - test->expr->file->text),
|
|
|
|
(int64_t)(test->expr->end - test->expr->file->text));
|
|
|
|
return CORD_cat(code, "\n}");
|
|
|
|
}
|
|
|
|
} else if (expr_t->tag == VoidType || expr_t->tag == AbortType) {
|
|
|
|
return CORD_asprintf(
|
2024-03-18 15:06:20 -07:00
|
|
|
"%r\n"
|
2024-04-16 10:50:07 -07:00
|
|
|
"test(NULL, NULL, NULL, %r, %ld, %ld);",
|
2024-03-14 10:28:30 -07:00
|
|
|
compile_statement(env, test->expr),
|
|
|
|
compile(env, WrapAST(test->expr, TextLiteral, .cord=test->expr->file->filename)),
|
|
|
|
(int64_t)(test->expr->start - test->expr->file->text),
|
|
|
|
(int64_t)(test->expr->end - test->expr->file->text));
|
|
|
|
} else {
|
|
|
|
return CORD_asprintf(
|
2024-03-17 19:08:05 -07:00
|
|
|
"{ // Test:\n"
|
|
|
|
"%r = %r;\n"
|
2024-04-16 10:50:07 -07:00
|
|
|
"test(&expr, %r, %r, %r, %ld, %ld);\n"
|
2024-03-14 10:28:30 -07:00
|
|
|
"}",
|
2024-04-16 10:50:07 -07:00
|
|
|
compile_declaration(env, expr_t, "expr"),
|
2024-03-14 10:28:30 -07:00
|
|
|
compile(env, test->expr),
|
|
|
|
compile_type_info(env, expr_t),
|
|
|
|
compile(env, WrapAST(test->expr, TextLiteral, .cord=output)),
|
|
|
|
compile(env, WrapAST(test->expr, TextLiteral, .cord=test->expr->file->filename)),
|
|
|
|
(int64_t)(test->expr->start - test->expr->file->text),
|
|
|
|
(int64_t)(test->expr->end - test->expr->file->text));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case Declare: {
|
|
|
|
auto decl = Match(ast, Declare);
|
2024-03-19 11:22:03 -07:00
|
|
|
if (decl->value->tag == Use) {
|
2024-04-24 10:53:37 -07:00
|
|
|
return compile_statement(env, decl->value);
|
2024-03-19 11:22:03 -07:00
|
|
|
} else {
|
|
|
|
type_t *t = get_type(env, decl->value);
|
|
|
|
if (t->tag == AbortType || t->tag == VoidType)
|
|
|
|
code_err(ast, "You can't declare a variable with a %T value", t);
|
2024-04-16 10:50:07 -07:00
|
|
|
return CORD_all(compile_declaration(env, t, CORD_cat("$", Match(decl->var, Var)->name)), " = ", compile(env, decl->value), ";");
|
2024-03-19 11:22:03 -07:00
|
|
|
}
|
2024-03-14 10:28:30 -07:00
|
|
|
}
|
|
|
|
case Assign: {
|
|
|
|
auto assign = Match(ast, Assign);
|
|
|
|
// Single assignment:
|
|
|
|
if (assign->targets && !assign->targets->next)
|
2024-03-22 10:53:23 -07:00
|
|
|
return compile_assignment(env, assign->targets->ast, compile(env, assign->values->ast));
|
2024-03-14 10:28:30 -07:00
|
|
|
|
|
|
|
CORD code = "{ // Assignment\n";
|
|
|
|
int64_t i = 1;
|
|
|
|
for (ast_list_t *value = assign->values; value; value = value->next)
|
2024-03-17 17:40:40 -07:00
|
|
|
CORD_appendf(&code, "%r $%ld = %r;\n", compile_type(env, get_type(env, value->ast)), i++, compile(env, value->ast));
|
2024-03-14 10:28:30 -07:00
|
|
|
i = 1;
|
|
|
|
for (ast_list_t *target = assign->targets; target; target = target->next) {
|
2024-03-22 10:53:23 -07:00
|
|
|
code = CORD_cat(code, compile_assignment(env, target->ast, CORD_asprintf("$%ld", i++)));
|
2024-03-14 10:28:30 -07:00
|
|
|
}
|
|
|
|
return CORD_cat(code, "\n}");
|
|
|
|
}
|
|
|
|
case UpdateAssign: {
|
|
|
|
auto update = Match(ast, UpdateAssign);
|
2024-03-24 11:28:20 -07:00
|
|
|
CORD lhs = compile_lvalue(env, update->lhs);
|
2024-03-14 10:28:30 -07:00
|
|
|
CORD rhs = compile(env, update->rhs);
|
|
|
|
|
|
|
|
type_t *lhs_t = get_type(env, update->lhs);
|
|
|
|
type_t *rhs_t = get_type(env, update->rhs);
|
|
|
|
type_t *operand_t;
|
|
|
|
if (promote(env, &rhs, rhs_t, lhs_t))
|
|
|
|
operand_t = lhs_t;
|
|
|
|
else if (promote(env, &lhs, lhs_t, rhs_t))
|
|
|
|
operand_t = rhs_t;
|
|
|
|
else if (lhs_t->tag == ArrayType && promote(env, &rhs, rhs_t, Match(lhs_t, ArrayType)->item_type))
|
|
|
|
operand_t = lhs_t;
|
|
|
|
else
|
|
|
|
code_err(ast, "I can't do operations between %T and %T", lhs_t, rhs_t);
|
2024-03-24 11:28:20 -07:00
|
|
|
|
2024-03-14 10:28:30 -07:00
|
|
|
switch (update->op) {
|
|
|
|
case BINOP_MULT: return CORD_asprintf("%r *= %r;", lhs, rhs);
|
|
|
|
case BINOP_DIVIDE: return CORD_asprintf("%r /= %r;", lhs, rhs);
|
2024-03-14 10:50:24 -07:00
|
|
|
case BINOP_MOD: return CORD_asprintf("%r = %r %% %r;", lhs, lhs, rhs);
|
|
|
|
case BINOP_MOD1: return CORD_asprintf("%r = (%r %% %r) + 1;", lhs, lhs, rhs);
|
2024-03-14 10:28:30 -07:00
|
|
|
case BINOP_PLUS: return CORD_asprintf("%r += %r;", lhs, rhs);
|
|
|
|
case BINOP_MINUS: return CORD_asprintf("%r -= %r;", lhs, rhs);
|
|
|
|
case BINOP_POWER: {
|
|
|
|
if (lhs_t->tag != NumType)
|
|
|
|
code_err(ast, "'^=' is only supported for Num types");
|
|
|
|
if (lhs_t->tag == NumType && Match(lhs_t, NumType)->bits == 32)
|
2024-03-18 15:06:20 -07:00
|
|
|
return CORD_all(lhs, " = powf(", lhs, ", ", rhs, ");");
|
2024-03-14 10:28:30 -07:00
|
|
|
else
|
2024-03-18 15:06:20 -07:00
|
|
|
return CORD_all(lhs, " = pow(", lhs, ", ", rhs, ");");
|
2024-03-14 10:28:30 -07:00
|
|
|
}
|
|
|
|
case BINOP_LSHIFT: return CORD_asprintf("%r <<= %r;", lhs, rhs);
|
|
|
|
case BINOP_RSHIFT: return CORD_asprintf("%r >>= %r;", lhs, rhs);
|
|
|
|
case BINOP_AND: {
|
|
|
|
if (operand_t->tag == BoolType)
|
|
|
|
return CORD_asprintf("if (%r) %r = %r;", lhs, lhs, rhs);
|
|
|
|
else if (operand_t->tag == IntType)
|
|
|
|
return CORD_asprintf("%r &= %r;", lhs, rhs);
|
|
|
|
else
|
|
|
|
code_err(ast, "'or=' is not implemented for %T types", operand_t);
|
|
|
|
}
|
|
|
|
case BINOP_OR: {
|
|
|
|
if (operand_t->tag == BoolType)
|
|
|
|
return CORD_asprintf("if (!(%r)) %r = %r;", lhs, lhs, rhs);
|
|
|
|
else if (operand_t->tag == IntType)
|
|
|
|
return CORD_asprintf("%r |= %r;", lhs, rhs);
|
|
|
|
else
|
|
|
|
code_err(ast, "'or=' is not implemented for %T types", operand_t);
|
|
|
|
}
|
|
|
|
case BINOP_XOR: return CORD_asprintf("%r ^= %r;", lhs, rhs);
|
|
|
|
case BINOP_CONCAT: {
|
|
|
|
if (operand_t->tag == TextType) {
|
|
|
|
return CORD_asprintf("%r = CORD_cat(%r, %r);", lhs, lhs, rhs);
|
|
|
|
} else if (operand_t->tag == ArrayType) {
|
|
|
|
if (promote(env, &rhs, rhs_t, Match(lhs_t, ArrayType)->item_type)) {
|
|
|
|
// arr ++= item
|
|
|
|
if (update->lhs->tag == Var)
|
2024-04-16 10:50:07 -07:00
|
|
|
return CORD_all("Array$insert(&", lhs, ", stack(", rhs, "), 0, ", compile_type_info(env, operand_t), ");");
|
2024-03-14 10:28:30 -07:00
|
|
|
else
|
2024-04-16 10:50:07 -07:00
|
|
|
return CORD_all(lhs, "Array$concat(", lhs, ", Array(", rhs, "), ", compile_type_info(env, operand_t), ");");
|
2024-03-14 10:28:30 -07:00
|
|
|
} else {
|
|
|
|
// arr ++= [...]
|
|
|
|
if (update->lhs->tag == Var)
|
2024-03-29 09:54:31 -07:00
|
|
|
return CORD_all("Array$insert_all(&", lhs, ", ", rhs, ", 0, ", compile_type_info(env, operand_t), ");");
|
2024-03-14 10:28:30 -07:00
|
|
|
else
|
2024-03-29 09:54:31 -07:00
|
|
|
return CORD_all(lhs, "Array$concat(", lhs, ", ", rhs, ", ", compile_type_info(env, operand_t), ");");
|
2024-03-14 10:28:30 -07:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
code_err(ast, "'++=' is not implemented for %T types", operand_t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
default: code_err(ast, "Update assignments are not implemented for this operation");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case StructDef: {
|
|
|
|
compile_struct_def(env, ast);
|
|
|
|
return CORD_EMPTY;
|
|
|
|
}
|
|
|
|
case EnumDef: {
|
|
|
|
compile_enum_def(env, ast);
|
|
|
|
return CORD_EMPTY;
|
|
|
|
}
|
|
|
|
case LangDef: {
|
|
|
|
// TODO: implement
|
|
|
|
auto def = Match(ast, LangDef);
|
2024-03-17 17:40:40 -07:00
|
|
|
CORD_appendf(&env->code->typedefs, "typedef CORD %r%s_t;\n", env->file_prefix, def->name);
|
|
|
|
CORD_appendf(&env->code->typedefs, "extern const TypeInfo %r%s;\n", env->file_prefix, def->name);
|
2024-03-21 10:32:52 -07:00
|
|
|
CORD_appendf(&env->code->typeinfos, "public const TypeInfo %r%s = {%zu, %zu, {.tag=TextInfo, .TextInfo={%r}}};\n",
|
2024-03-17 17:40:40 -07:00
|
|
|
env->file_prefix, def->name, sizeof(CORD), __alignof__(CORD),
|
2024-03-29 09:54:31 -07:00
|
|
|
Text$quoted(def->name, false));
|
2024-03-14 10:28:30 -07:00
|
|
|
compile_namespace(env, def->name, def->namespace);
|
|
|
|
return CORD_EMPTY;
|
|
|
|
}
|
|
|
|
case FunctionDef: {
|
|
|
|
auto fndef = Match(ast, FunctionDef);
|
2024-04-17 10:44:01 -07:00
|
|
|
bool is_private = Match(fndef->name, Var)->name[0] == '_';
|
|
|
|
CORD name = is_private ? CORD_cat("$", Match(fndef->name, Var)->name) : compile(env, fndef->name);
|
2024-03-14 10:28:30 -07:00
|
|
|
type_t *ret_t = fndef->ret_type ? parse_type_ast(env, fndef->ret_type) : Type(VoidType);
|
|
|
|
|
|
|
|
CORD arg_signature = "(";
|
|
|
|
for (arg_ast_t *arg = fndef->args; arg; arg = arg->next) {
|
|
|
|
type_t *arg_type = get_arg_ast_type(env, arg);
|
2024-04-16 10:50:07 -07:00
|
|
|
arg_signature = CORD_cat(arg_signature, compile_declaration(env, arg_type, CORD_cat("$", arg->name)));
|
2024-03-14 10:28:30 -07:00
|
|
|
if (arg->next) arg_signature = CORD_cat(arg_signature, ", ");
|
|
|
|
}
|
|
|
|
arg_signature = CORD_cat(arg_signature, ")");
|
|
|
|
|
2024-03-17 17:40:40 -07:00
|
|
|
CORD ret_type_code = compile_type(env, ret_t);
|
2024-03-14 10:28:30 -07:00
|
|
|
|
2024-04-17 10:44:01 -07:00
|
|
|
if (is_private)
|
2024-03-14 10:28:30 -07:00
|
|
|
env->code->staticdefs = CORD_all(env->code->staticdefs, "static ", ret_type_code, " ", name, arg_signature, ";\n");
|
|
|
|
else
|
|
|
|
env->code->fndefs = CORD_all(env->code->fndefs, ret_type_code, " ", name, arg_signature, ";\n");
|
|
|
|
|
|
|
|
CORD code;
|
|
|
|
if (fndef->cache) {
|
|
|
|
code = CORD_all("static ", ret_type_code, " ", name, "$uncached", arg_signature);
|
|
|
|
} else {
|
|
|
|
code = CORD_all(ret_type_code, " ", name, arg_signature);
|
|
|
|
if (fndef->is_inline)
|
|
|
|
code = CORD_cat("inline ", code);
|
2024-04-17 10:44:01 -07:00
|
|
|
if (!is_private)
|
2024-03-14 10:28:30 -07:00
|
|
|
code = CORD_cat("public ", code);
|
|
|
|
}
|
|
|
|
|
2024-04-10 08:49:43 -07:00
|
|
|
env_t *body_scope = fresh_scope(env);
|
2024-03-14 10:28:30 -07:00
|
|
|
for (arg_ast_t *arg = fndef->args; arg; arg = arg->next) {
|
|
|
|
type_t *arg_type = get_arg_ast_type(env, arg);
|
2024-04-16 10:50:07 -07:00
|
|
|
set_binding(body_scope, arg->name, new(binding_t, .type=arg_type, .code=CORD_cat("$", arg->name)));
|
2024-03-14 10:28:30 -07:00
|
|
|
}
|
|
|
|
|
2024-03-15 10:35:30 -07:00
|
|
|
fn_ctx_t fn_ctx = (fn_ctx_t){
|
2024-03-14 10:28:30 -07:00
|
|
|
.return_type=ret_t,
|
|
|
|
.closure_scope=NULL,
|
|
|
|
.closed_vars=NULL,
|
|
|
|
};
|
|
|
|
body_scope->fn_ctx = &fn_ctx;
|
|
|
|
|
|
|
|
CORD body = compile_statement(body_scope, fndef->body);
|
|
|
|
if (CORD_fetch(body, 0) != '{')
|
|
|
|
body = CORD_asprintf("{\n%r\n}", body);
|
|
|
|
env->code->funcs = CORD_all(env->code->funcs, code, " ", body, "\n");
|
|
|
|
|
|
|
|
if (fndef->cache && fndef->cache->tag == Int && Match(fndef->cache, Int)->i > 0) {
|
|
|
|
const char *arg_type_name = heap_strf("%s$args", CORD_to_const_char_star(name));
|
|
|
|
ast_t *args_def = FakeAST(StructDef, .name=arg_type_name, .fields=fndef->args);
|
|
|
|
bind_statement(env, args_def);
|
|
|
|
(void)compile_statement(env, args_def);
|
2024-03-29 09:54:31 -07:00
|
|
|
type_t *args_t = Table$str_get(*env->types, arg_type_name);
|
2024-03-14 10:28:30 -07:00
|
|
|
assert(args_t);
|
|
|
|
|
|
|
|
CORD all_args = CORD_EMPTY;
|
|
|
|
for (arg_ast_t *arg = fndef->args; arg; arg = arg->next)
|
2024-04-16 10:50:07 -07:00
|
|
|
all_args = CORD_all(all_args, "$", arg->name, arg->next ? ", " : CORD_EMPTY);
|
2024-03-14 10:28:30 -07:00
|
|
|
|
|
|
|
CORD pop_code = CORD_EMPTY;
|
|
|
|
if (fndef->cache->tag == Int && Match(fndef->cache, Int)->i < INT64_MAX) {
|
2024-04-16 10:50:07 -07:00
|
|
|
pop_code = CORD_all("if (Table$length(cache) > ", compile(body_scope, fndef->cache),
|
|
|
|
") Table$remove(&cache, NULL, table_info);\n");
|
2024-03-14 10:28:30 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
CORD wrapper = CORD_all(
|
2024-04-17 10:44:01 -07:00
|
|
|
is_private ? CORD_EMPTY : "public ", ret_type_code, " ", name, arg_signature, "{\n"
|
2024-04-16 10:50:07 -07:00
|
|
|
"static table_t cache = {};\n",
|
|
|
|
compile_type(env, args_t), " args = {", all_args, "};\n"
|
|
|
|
"static const TypeInfo *table_type = $TableInfo(", compile_type_info(env, args_t), ", ", compile_type_info(env, ret_t), ");\n",
|
|
|
|
ret_type_code, "*cached = Table$get_raw(cache, &args, table_type);\n"
|
|
|
|
"if (cached) return *cached;\n",
|
|
|
|
ret_type_code, " ret = ", name, "$uncached(", all_args, ");\n",
|
2024-03-14 10:28:30 -07:00
|
|
|
pop_code,
|
2024-04-16 10:50:07 -07:00
|
|
|
"Table$set(&cache, &args, &ret, table_type);\n"
|
|
|
|
"return ret;\n"
|
2024-03-14 10:28:30 -07:00
|
|
|
"}\n");
|
|
|
|
env->code->funcs = CORD_cat(env->code->funcs, wrapper);
|
|
|
|
}
|
|
|
|
|
|
|
|
return CORD_EMPTY;
|
|
|
|
}
|
|
|
|
case Skip: {
|
2024-03-15 10:35:30 -07:00
|
|
|
const char *target = Match(ast, Skip)->target;
|
|
|
|
for (loop_ctx_t *ctx = env->loop_ctx; ctx; ctx = ctx->next) {
|
2024-03-15 10:45:25 -07:00
|
|
|
if (!target || CORD_cmp(target, ctx->loop_name) == 0
|
|
|
|
|| CORD_cmp(target, ctx->key_name) == 0 || CORD_cmp(target, ctx->value_name) == 0) {
|
2024-03-15 10:35:30 -07:00
|
|
|
if (!ctx->skip_label) {
|
|
|
|
static int64_t skip_label_count = 1;
|
|
|
|
CORD_sprintf(&ctx->skip_label, "skip_%ld", skip_label_count);
|
|
|
|
++skip_label_count;
|
|
|
|
}
|
|
|
|
return CORD_all("goto ", ctx->skip_label, ";");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (env->loop_ctx)
|
|
|
|
code_err(ast, "This 'skip' is not inside any loop");
|
|
|
|
else if (target)
|
|
|
|
code_err(ast, "No loop target named '%s' was found", target);
|
|
|
|
else
|
|
|
|
code_err(ast, "I couldn't figure out how to make this skip work!");
|
2024-03-14 10:28:30 -07:00
|
|
|
}
|
|
|
|
case Stop: {
|
2024-03-15 10:35:30 -07:00
|
|
|
const char *target = Match(ast, Stop)->target;
|
|
|
|
for (loop_ctx_t *ctx = env->loop_ctx; ctx; ctx = ctx->next) {
|
2024-03-15 10:45:25 -07:00
|
|
|
if (!target || CORD_cmp(target, ctx->loop_name) == 0
|
|
|
|
|| CORD_cmp(target, ctx->key_name) == 0 || CORD_cmp(target, ctx->value_name) == 0) {
|
2024-03-15 10:35:30 -07:00
|
|
|
if (!ctx->stop_label) {
|
|
|
|
static int64_t stop_label_count = 1;
|
|
|
|
CORD_sprintf(&ctx->stop_label, "stop_%ld", stop_label_count);
|
|
|
|
++stop_label_count;
|
|
|
|
}
|
|
|
|
return CORD_all("goto ", ctx->stop_label, ";");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (env->loop_ctx)
|
|
|
|
code_err(ast, "This 'stop' is not inside any loop");
|
|
|
|
else if (target)
|
|
|
|
code_err(ast, "No loop target named '%s' was found", target);
|
|
|
|
else
|
|
|
|
code_err(ast, "I couldn't figure out how to make this stop work!");
|
2024-03-14 10:28:30 -07:00
|
|
|
}
|
|
|
|
case Pass: return ";";
|
|
|
|
case Return: {
|
|
|
|
if (!env->fn_ctx) code_err(ast, "This return statement is not inside any function");
|
|
|
|
auto ret = Match(ast, Return)->value;
|
|
|
|
assert(env->fn_ctx->return_type);
|
|
|
|
if (ret) {
|
|
|
|
type_t *ret_t = get_type(env, ret);
|
|
|
|
CORD value = compile(env, ret);
|
|
|
|
if (!promote(env, &value, ret_t, env->fn_ctx->return_type))
|
|
|
|
code_err(ast, "This function expects a return value of type %T, but this return has type %T",
|
|
|
|
env->fn_ctx->return_type, ret_t);
|
|
|
|
return CORD_all("return ", value, ";");
|
|
|
|
} else {
|
|
|
|
if (env->fn_ctx->return_type->tag != VoidType)
|
|
|
|
code_err(ast, "This function expects a return value of type %T", env->fn_ctx->return_type->tag);
|
|
|
|
return "return;";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case While: {
|
|
|
|
auto while_ = Match(ast, While);
|
2024-03-15 10:45:25 -07:00
|
|
|
env_t *scope = fresh_scope(env);
|
|
|
|
loop_ctx_t loop_ctx = (loop_ctx_t){
|
|
|
|
.loop_name="while",
|
|
|
|
.next=env->loop_ctx,
|
|
|
|
};
|
|
|
|
scope->loop_ctx = &loop_ctx;
|
|
|
|
CORD body = compile_statement(scope, while_->body);
|
|
|
|
if (loop_ctx.skip_label)
|
|
|
|
body = CORD_all(body, "\n", loop_ctx.skip_label, ": continue;");
|
|
|
|
CORD loop = CORD_all("while (", compile(scope, while_->condition), ") {\n\t", body, "\n}");
|
|
|
|
if (loop_ctx.stop_label)
|
|
|
|
loop = CORD_all(loop, "\n", loop_ctx.stop_label, ":;");
|
|
|
|
return loop;
|
2024-03-14 10:28:30 -07:00
|
|
|
}
|
|
|
|
case For: {
|
|
|
|
auto for_ = Match(ast, For);
|
2024-03-17 12:59:06 -07:00
|
|
|
// TODO: optimize case for iterating over comprehensions so we don't need to create
|
|
|
|
// an intermediary array/table
|
2024-03-14 10:28:30 -07:00
|
|
|
type_t *iter_t = get_type(env, for_->iter);
|
2024-03-15 10:35:30 -07:00
|
|
|
env_t *body_scope = for_scope(env, ast);
|
|
|
|
loop_ctx_t loop_ctx = (loop_ctx_t){
|
2024-03-15 10:45:25 -07:00
|
|
|
.loop_name="for",
|
2024-04-16 10:50:07 -07:00
|
|
|
.key_name=for_->index ? Match(for_->index, Var)->name : CORD_EMPTY,
|
|
|
|
.value_name=for_->value ? Match(for_->value, Var)->name : CORD_EMPTY,
|
2024-03-15 10:35:30 -07:00
|
|
|
.next=body_scope->loop_ctx,
|
|
|
|
};
|
|
|
|
body_scope->loop_ctx = &loop_ctx;
|
|
|
|
CORD body = compile_statement(body_scope, for_->body);
|
2024-03-15 10:38:25 -07:00
|
|
|
if (loop_ctx.skip_label)
|
|
|
|
body = CORD_all(body, "\n", loop_ctx.skip_label, ": continue;");
|
|
|
|
CORD stop = loop_ctx.stop_label ? CORD_all("\n", loop_ctx.stop_label, ":;") : CORD_EMPTY;
|
2024-03-15 10:35:30 -07:00
|
|
|
|
2024-03-14 10:28:30 -07:00
|
|
|
switch (iter_t->tag) {
|
|
|
|
case ArrayType: {
|
|
|
|
type_t *item_t = Match(iter_t, ArrayType)->item_type;
|
2024-04-16 10:50:07 -07:00
|
|
|
CORD index = for_->index ? compile(env, for_->index) : "i";
|
2024-03-14 10:28:30 -07:00
|
|
|
CORD value = compile(env, for_->value);
|
2024-04-16 10:50:07 -07:00
|
|
|
CORD array = is_idempotent(for_->iter) ? compile(env, for_->iter) : "arr";
|
|
|
|
CORD loop = CORD_all("ARRAY_INCREF(", array, ");\n"
|
2024-03-26 11:02:48 -07:00
|
|
|
"for (int64_t ", index, " = 1; ", index, " <= ", array, ".length; ++", index, ") {\n",
|
|
|
|
compile_type(env, item_t), " ", value,
|
|
|
|
" = *(", compile_type(env, item_t), "*)(", array, ".data + (",index,"-1)*", array, ".stride);\n",
|
|
|
|
body, "\n}");
|
|
|
|
if (for_->empty)
|
|
|
|
loop = CORD_all("if (", array, ".length > 0) {\n", loop, "\n} else ", compile_statement(env, for_->empty));
|
2024-04-16 10:50:07 -07:00
|
|
|
loop = CORD_all(loop, stop, "\nARRAY_DECREF(", array, ");\n");
|
2024-03-26 11:02:48 -07:00
|
|
|
if (!is_idempotent(for_->iter))
|
|
|
|
loop = CORD_all("{\narray_t ",array," = ", compile(env, for_->iter), ";\n", loop, "\n}");
|
|
|
|
return loop;
|
2024-03-14 10:28:30 -07:00
|
|
|
}
|
|
|
|
case TableType: {
|
|
|
|
type_t *key_t = Match(iter_t, TableType)->key_type;
|
|
|
|
type_t *value_t = Match(iter_t, TableType)->value_type;
|
2024-03-26 11:02:48 -07:00
|
|
|
|
2024-04-16 10:50:07 -07:00
|
|
|
CORD table = is_idempotent(for_->iter) ? compile(env, for_->iter) : "table";
|
|
|
|
CORD loop = CORD_all("ARRAY_INCREF(", table, ".entries);\n"
|
|
|
|
"for (int64_t i = 0; i < ",table,".entries.length; ++i) {\n");
|
2024-03-14 10:28:30 -07:00
|
|
|
if (for_->index) {
|
2024-03-26 11:02:48 -07:00
|
|
|
loop = CORD_all(loop, compile_type(env, key_t), " ", compile(env, for_->index), " = *(", compile_type(env, key_t), "*)(",
|
2024-04-16 10:50:07 -07:00
|
|
|
table,".entries.data + i*", table, ".entries.stride);\n");
|
2024-03-14 10:28:30 -07:00
|
|
|
|
|
|
|
size_t value_offset = type_size(key_t);
|
|
|
|
if (type_align(value_t) > 1 && value_offset % type_align(value_t))
|
|
|
|
value_offset += type_align(value_t) - (value_offset % type_align(value_t)); // padding
|
2024-03-26 11:02:48 -07:00
|
|
|
loop = CORD_all(loop, compile_type(env, value_t), " ", compile(env, for_->value), " = *(", compile_type(env, value_t), "*)(",
|
2024-04-16 10:50:07 -07:00
|
|
|
table,".entries.data + i*", table, ".entries.stride + ", heap_strf("%zu", value_offset), ");\n");
|
2024-03-14 10:28:30 -07:00
|
|
|
} else {
|
2024-03-26 11:02:48 -07:00
|
|
|
loop = CORD_all(loop, compile_type(env, key_t), " ", compile(env, for_->value), " = *(", compile_type(env, key_t), "*)(",
|
2024-04-16 10:50:07 -07:00
|
|
|
table,".entries.data + i*", table, ".entries.stride);\n");
|
2024-03-14 10:28:30 -07:00
|
|
|
}
|
2024-03-26 11:02:48 -07:00
|
|
|
loop = CORD_all(loop, body, "\n}");
|
|
|
|
if (for_->empty)
|
|
|
|
loop = CORD_all("if (", table, ".entries.length > 0) {\n", loop, "\n} else ", compile_statement(env, for_->empty));
|
2024-04-16 10:50:07 -07:00
|
|
|
loop = CORD_all(loop, stop, "\nARRAY_DECREF(", table, ".entries);\n");
|
2024-03-26 11:02:48 -07:00
|
|
|
if (!is_idempotent(for_->iter))
|
|
|
|
loop = CORD_all("{\ntable_t ",table," = ", compile(env, for_->iter), ";\n", loop, "\n}");
|
|
|
|
return loop;
|
2024-03-14 10:28:30 -07:00
|
|
|
}
|
|
|
|
case IntType: {
|
|
|
|
CORD value = compile(env, for_->value);
|
|
|
|
CORD n = compile(env, for_->iter);
|
|
|
|
CORD index = for_->index ? compile(env, for_->index) : CORD_EMPTY;
|
|
|
|
if (for_->empty && index) {
|
|
|
|
return CORD_all(
|
|
|
|
"{\n"
|
2024-04-16 10:50:07 -07:00
|
|
|
"int64_t n = ", n, ";\n"
|
|
|
|
"if (n > 0) {\n"
|
|
|
|
"for (int64_t ", index, " = 1, ", value, "; (", value, "=", index,") <= n; ++", index, ") {\n"
|
2024-03-15 10:38:25 -07:00
|
|
|
"\t", body, "\n}"
|
2024-03-15 10:35:30 -07:00
|
|
|
"\n} else ", compile_statement(env, for_->empty),
|
2024-03-15 10:38:25 -07:00
|
|
|
stop, "\n}");
|
2024-03-14 10:28:30 -07:00
|
|
|
} else if (for_->empty) {
|
|
|
|
return CORD_all(
|
|
|
|
"{\n"
|
2024-04-16 10:50:07 -07:00
|
|
|
"int64_t n = ", n, ";\n"
|
|
|
|
"if (n > 0) {\n"
|
|
|
|
"for (int64_t ", value, " = 1; ", value, " <= n; ++", value, ") {\n"
|
2024-03-15 10:35:30 -07:00
|
|
|
"\t", body,
|
|
|
|
"\n}"
|
|
|
|
"\n} else ", compile_statement(env, for_->empty),
|
2024-03-15 10:38:25 -07:00
|
|
|
stop,
|
2024-03-14 10:28:30 -07:00
|
|
|
"\n}");
|
|
|
|
} else if (index) {
|
|
|
|
return CORD_all(
|
2024-04-16 10:50:07 -07:00
|
|
|
"for (int64_t ", value, ", ", index, " = 1, n = ", n, "; (", value, "=", index,") <= n; ++", value, ") {\n"
|
2024-03-15 10:35:30 -07:00
|
|
|
"\t", body,
|
|
|
|
"\n}",
|
2024-03-15 10:38:25 -07:00
|
|
|
stop,
|
2024-03-15 10:35:30 -07:00
|
|
|
"\n");
|
2024-03-14 10:28:30 -07:00
|
|
|
} else {
|
|
|
|
return CORD_all(
|
2024-04-16 10:50:07 -07:00
|
|
|
"for (int64_t ", value, " = 1, n = ", compile(env, for_->iter), "; ", value, " <= n; ++", value, ") {\n"
|
2024-03-15 10:35:30 -07:00
|
|
|
"\t", body,
|
|
|
|
"\n}",
|
2024-03-15 10:38:25 -07:00
|
|
|
stop,
|
2024-03-15 10:35:30 -07:00
|
|
|
"\n");
|
2024-03-14 10:28:30 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
default: code_err(for_->iter, "Iteration is not implemented for type: %T", iter_t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case If: {
|
|
|
|
auto if_ = Match(ast, If);
|
|
|
|
if (if_->condition->tag == Declare) {
|
|
|
|
auto decl = Match(if_->condition, Declare);
|
|
|
|
env_t *true_scope = fresh_scope(env);
|
|
|
|
const char *name = Match(decl->var, Var)->name;
|
|
|
|
CORD var_code = CORD_cat(env->scope_prefix, name);
|
|
|
|
type_t *var_t = get_type(env, decl->value);
|
|
|
|
if (var_t->tag == PointerType) {
|
|
|
|
auto ptr = Match(var_t, PointerType);
|
|
|
|
if (!ptr->is_optional)
|
|
|
|
code_err(if_->condition, "This pointer will always be non-null, so it should not be used in a conditional.");
|
|
|
|
var_t = Type(PointerType, .pointed=ptr->pointed, .is_optional=false, .is_stack=ptr->is_stack, .is_readonly=ptr->is_readonly);
|
|
|
|
} else {
|
|
|
|
code_err(if_->condition, "Only optional pointer types can be used in 'if var := ...' statements (this is a %T)", var_t);
|
|
|
|
}
|
|
|
|
set_binding(true_scope, name, new(binding_t, .type=var_t, .code=var_code));
|
|
|
|
CORD code = CORD_all("{\n",
|
2024-03-17 17:40:40 -07:00
|
|
|
compile_type(env, var_t), " ", var_code, " = ", compile(env, decl->value), ";\n"
|
2024-03-14 10:28:30 -07:00
|
|
|
"if (", var_code, ") ", compile_statement(true_scope, if_->body));
|
|
|
|
if (if_->else_body)
|
|
|
|
code = CORD_all(code, "\nelse ", compile_statement(env, if_->else_body));
|
|
|
|
code = CORD_cat(code, "\n}");
|
|
|
|
return code;
|
|
|
|
} else {
|
|
|
|
type_t *cond_t = get_type(env, if_->condition);
|
|
|
|
if (cond_t->tag == PointerType) {
|
|
|
|
if (!Match(cond_t, PointerType)->is_optional)
|
|
|
|
code_err(if_->condition, "This pointer will always be non-null, so it should not be used in a conditional.");
|
|
|
|
} else if (cond_t->tag != BoolType) {
|
|
|
|
code_err(if_->condition, "Only boolean values and optional pointers can be used in conditionals (this is a %T)", cond_t);
|
|
|
|
}
|
|
|
|
CORD code;
|
|
|
|
CORD_sprintf(&code, "if (%r) %r", compile(env, if_->condition), compile_statement(env, if_->body));
|
|
|
|
if (if_->else_body)
|
|
|
|
code = CORD_all(code, "\nelse ", compile_statement(env, if_->else_body));
|
|
|
|
return code;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case Block: {
|
|
|
|
ast_list_t *stmts = Match(ast, Block)->statements;
|
|
|
|
if (stmts && !stmts->next)
|
|
|
|
return compile_statement(env, stmts->ast);
|
|
|
|
|
|
|
|
CORD code = "{\n";
|
|
|
|
env = fresh_scope(env);
|
|
|
|
for (ast_list_t *stmt = stmts; stmt; stmt = stmt->next) {
|
|
|
|
bind_statement(env, stmt->ast);
|
|
|
|
code = CORD_all(code, compile_statement(env, stmt->ast), "\n");
|
|
|
|
}
|
|
|
|
return CORD_cat(code, "}");
|
|
|
|
}
|
2024-03-17 12:26:25 -07:00
|
|
|
case Comprehension: {
|
|
|
|
auto comp = Match(ast, Comprehension);
|
|
|
|
assert(env->comprehension_var);
|
|
|
|
if (comp->expr->tag == Comprehension) { // Nested comprehension
|
|
|
|
ast_t *body = comp->filter ? WrapAST(ast, If, .condition=comp->filter, .body=comp->expr) : comp->expr;
|
|
|
|
ast_t *loop = WrapAST(ast, For, .index=comp->key, .value=comp->value, .iter=comp->iter, .body=body);
|
|
|
|
return compile_statement(env, loop);
|
|
|
|
} else if (comp->expr->tag == TableEntry) { // Table comprehension
|
|
|
|
auto e = Match(comp->expr, TableEntry);
|
|
|
|
ast_t *body = WrapAST(ast, MethodCall, .name="set", .self=FakeAST(StackReference, FakeAST(Var, env->comprehension_var)),
|
|
|
|
.args=new(arg_ast_t, .value=e->key, .next=new(arg_ast_t, .value=e->value)));
|
|
|
|
if (comp->filter)
|
|
|
|
body = WrapAST(body, If, .condition=comp->filter, .body=body);
|
|
|
|
ast_t *loop = WrapAST(ast, For, .index=comp->key, .value=comp->value, .iter=comp->iter, .body=body);
|
|
|
|
return compile_statement(env, loop);
|
|
|
|
} else { // Array comprehension
|
|
|
|
ast_t *body = WrapAST(comp->expr, MethodCall, .name="insert", .self=FakeAST(StackReference, FakeAST(Var, env->comprehension_var)),
|
|
|
|
.args=new(arg_ast_t, .value=comp->expr));
|
|
|
|
if (comp->filter)
|
|
|
|
body = WrapAST(body, If, .condition=comp->filter, .body=body);
|
|
|
|
ast_t *loop = WrapAST(ast, For, .index=comp->key, .value=comp->value, .iter=comp->iter, .body=body);
|
|
|
|
return compile_statement(env, loop);
|
|
|
|
}
|
|
|
|
}
|
2024-03-24 12:06:59 -07:00
|
|
|
case Extern: {
|
|
|
|
auto ext = Match(ast, Extern);
|
|
|
|
type_t *t = parse_type_ast(env, ext->type);
|
|
|
|
CORD decl;
|
|
|
|
if (t->tag == ClosureType) {
|
|
|
|
t = Match(t, ClosureType)->fn;
|
|
|
|
auto fn = Match(t, FunctionType);
|
|
|
|
decl = CORD_all(compile_type(env, fn->ret), " ", ext->name, "(");
|
|
|
|
for (arg_t *arg = fn->args; arg; arg = arg->next) {
|
|
|
|
decl = CORD_all(decl, compile_type(env, arg->type));
|
|
|
|
if (arg->next) decl = CORD_cat(decl, ", ");
|
|
|
|
}
|
|
|
|
decl = CORD_cat(decl, ")");
|
|
|
|
} else {
|
|
|
|
decl = compile_declaration(env, t, ext->name);
|
|
|
|
}
|
|
|
|
env->code->fndefs = CORD_all(env->code->fndefs, "extern ", decl, ";\n");
|
|
|
|
return CORD_EMPTY;
|
|
|
|
}
|
2024-03-14 10:28:30 -07:00
|
|
|
case InlineCCode: return Match(ast, InlineCCode)->code;
|
2024-04-24 10:53:37 -07:00
|
|
|
case Use: {
|
|
|
|
auto use = Match(ast, Use);
|
|
|
|
const char *path = use->raw_path;
|
|
|
|
const char *name = file_base_name(path);
|
|
|
|
if (strncmp(path, "./", 2) == 0 || strncmp(path, "../", 3) == 0) {
|
|
|
|
env->code->imports = CORD_all(env->code->imports, "#include \"", path, ".h\"\n");
|
|
|
|
env->code->object_files = CORD_all(env->code->object_files, "'", path, ".o' ");
|
|
|
|
} else {
|
|
|
|
env->code->imports = CORD_all(env->code->imports, "#include <", path, ".h>\n");
|
|
|
|
env->code->object_files = CORD_all(env->code->object_files, "-l", name, " ");
|
|
|
|
}
|
|
|
|
return CORD_EMPTY;
|
|
|
|
}
|
2024-02-04 15:04:41 -08:00
|
|
|
default:
|
2024-03-14 10:28:30 -07:00
|
|
|
return CORD_asprintf("(void)%r;", compile(env, ast));
|
2024-02-04 15:04:41 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-13 12:20:42 -07:00
|
|
|
// CORD compile_statement(env_t *env, ast_t *ast) {
|
|
|
|
// CORD stmt = _compile_statement(env, ast);
|
|
|
|
// if (!stmt)
|
|
|
|
// return stmt;
|
|
|
|
// int64_t line = get_line_number(ast->file, ast->start);
|
|
|
|
// return CORD_asprintf("#line %ld\n%r", line, stmt);
|
|
|
|
// }
|
|
|
|
|
2024-03-09 15:32:36 -08:00
|
|
|
CORD expr_as_text(env_t *env, CORD expr, type_t *t, CORD color)
|
2024-02-17 17:47:43 -08:00
|
|
|
{
|
|
|
|
switch (t->tag) {
|
2024-04-16 10:50:07 -07:00
|
|
|
case MemoryType: return CORD_asprintf("Memory$as_text(stack(%r), %r, &$Memory)", expr, color);
|
|
|
|
case BoolType: return CORD_asprintf("Bool$as_text(stack(%r), %r, &$Bool)", expr, color);
|
2024-03-03 10:37:05 -08:00
|
|
|
case IntType: {
|
|
|
|
CORD name = type_to_cord(t);
|
2024-04-16 10:50:07 -07:00
|
|
|
return CORD_asprintf("%r$as_text(stack(%r), %r, &$%r)", name, expr, color, name);
|
2024-03-03 10:37:05 -08:00
|
|
|
}
|
|
|
|
case NumType: {
|
|
|
|
CORD name = type_to_cord(t);
|
2024-04-20 12:18:20 -07:00
|
|
|
return CORD_asprintf("%r$as_text(stack(%r), %r, &$%r)", name, expr, color, name);
|
2024-03-03 15:16:33 -08:00
|
|
|
}
|
2024-03-09 15:32:36 -08:00
|
|
|
case TextType: {
|
|
|
|
const char *lang = Match(t, TextType)->lang;
|
2024-04-16 10:50:07 -07:00
|
|
|
return CORD_asprintf("Text$as_text(stack(%r), %r, &%s)", expr, color, lang ? lang : "$Text");
|
2024-03-09 15:32:36 -08:00
|
|
|
}
|
2024-04-16 10:50:07 -07:00
|
|
|
case ArrayType: return CORD_asprintf("Array$as_text(stack(%r), %r, %r)", expr, color, compile_type_info(env, t));
|
|
|
|
case TableType: return CORD_asprintf("Table$as_text(stack(%r), %r, %r)", expr, color, compile_type_info(env, t));
|
2024-04-21 11:58:33 -07:00
|
|
|
case FunctionType: case ClosureType: return CORD_asprintf("Func$as_text(stack(%r), %r, %r)", expr, color, compile_type_info(env, t));
|
2024-04-16 10:50:07 -07:00
|
|
|
case PointerType: return CORD_asprintf("Pointer$as_text(stack(%r), %r, %r)", expr, color, compile_type_info(env, t));
|
|
|
|
case StructType: case EnumType: return CORD_asprintf("(%r)->CustomInfo.as_text(stack(%r), %r, %r)",
|
2024-02-17 18:04:35 -08:00
|
|
|
compile_type_info(env, t), expr, color, compile_type_info(env, t));
|
2024-02-17 17:47:43 -08:00
|
|
|
default: compiler_err(NULL, NULL, NULL, "Stringifying is not supported for %T", t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CORD compile_string(env_t *env, ast_t *ast, CORD color)
|
|
|
|
{
|
|
|
|
type_t *t = get_type(env, ast);
|
|
|
|
CORD expr = compile(env, ast);
|
2024-03-09 15:32:36 -08:00
|
|
|
return expr_as_text(env, expr, t, color);
|
2024-02-17 17:47:43 -08:00
|
|
|
}
|
|
|
|
|
2024-03-22 10:53:23 -07:00
|
|
|
CORD compile_to_pointer_depth(env_t *env, ast_t *ast, int64_t target_depth, bool allow_optional)
|
2024-02-18 11:28:35 -08:00
|
|
|
{
|
|
|
|
CORD val = compile(env, ast);
|
|
|
|
type_t *t = get_type(env, ast);
|
|
|
|
int64_t depth = 0;
|
|
|
|
for (type_t *tt = t; tt->tag == PointerType; tt = Match(tt, PointerType)->pointed)
|
|
|
|
++depth;
|
|
|
|
|
|
|
|
while (depth != target_depth) {
|
|
|
|
if (depth < target_depth) {
|
|
|
|
if (ast->tag == Var && target_depth == 1)
|
2024-03-03 11:34:13 -08:00
|
|
|
val = CORD_all("(&", val, ")");
|
2024-02-18 11:28:35 -08:00
|
|
|
else
|
2024-04-16 10:50:07 -07:00
|
|
|
val = CORD_all("stack(", val, ")");
|
2024-02-18 11:28:35 -08:00
|
|
|
t = Type(PointerType, .pointed=t, .is_stack=true);
|
|
|
|
++depth;
|
|
|
|
} else {
|
|
|
|
auto ptr = Match(t, PointerType);
|
|
|
|
if (ptr->is_optional)
|
|
|
|
code_err(ast, "You can't dereference this value, since it's not guaranteed to be non-null");
|
|
|
|
val = CORD_all("*(", val, ")");
|
|
|
|
t = ptr->pointed;
|
|
|
|
--depth;
|
|
|
|
}
|
|
|
|
}
|
2024-02-20 10:06:03 -08:00
|
|
|
if (!allow_optional) {
|
|
|
|
while (t->tag == PointerType) {
|
|
|
|
auto ptr = Match(t, PointerType);
|
|
|
|
if (ptr->is_optional)
|
|
|
|
code_err(ast, "You can't dereference this value, since it's not guaranteed to be non-null");
|
|
|
|
t = ptr->pointed;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-18 11:28:35 -08:00
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
2024-03-08 11:23:16 -08:00
|
|
|
static CORD compile_arguments(env_t *env, ast_t *call_ast, arg_t *spec_args, arg_ast_t *call_args)
|
|
|
|
{
|
|
|
|
table_t used_args = {};
|
|
|
|
CORD code = CORD_EMPTY;
|
2024-03-09 13:03:38 -08:00
|
|
|
env_t *default_scope = global_scope(env);
|
2024-03-08 11:23:16 -08:00
|
|
|
for (arg_t *spec_arg = spec_args; spec_arg; spec_arg = spec_arg->next) {
|
|
|
|
// Find keyword:
|
|
|
|
if (spec_arg->name) {
|
|
|
|
for (arg_ast_t *call_arg = call_args; call_arg; call_arg = call_arg->next) {
|
|
|
|
if (call_arg->name && streq(call_arg->name, spec_arg->name)) {
|
|
|
|
type_t *actual_t = get_type(env, call_arg->value);
|
2024-03-09 13:03:38 -08:00
|
|
|
CORD value = compile(env, call_arg->value);
|
|
|
|
if (!promote(env, &value, actual_t, spec_arg->type))
|
2024-03-08 11:23:16 -08:00
|
|
|
code_err(call_arg->value, "This argument is supposed to be a %T, but this value is a %T", spec_arg->type, actual_t);
|
2024-03-29 09:54:31 -07:00
|
|
|
Table$str_set(&used_args, call_arg->name, call_arg);
|
2024-03-08 11:23:16 -08:00
|
|
|
if (code) code = CORD_cat(code, ", ");
|
2024-03-09 13:03:38 -08:00
|
|
|
code = CORD_cat(code, value);
|
2024-03-08 11:23:16 -08:00
|
|
|
goto found_it;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Find positional:
|
|
|
|
int64_t i = 1;
|
|
|
|
for (arg_ast_t *call_arg = call_args; call_arg; call_arg = call_arg->next) {
|
|
|
|
if (call_arg->name) continue;
|
|
|
|
const char *pseudoname = heap_strf("%ld", i++);
|
2024-03-29 09:54:31 -07:00
|
|
|
if (!Table$str_get(used_args, pseudoname)) {
|
2024-03-08 11:23:16 -08:00
|
|
|
type_t *actual_t = get_type(env, call_arg->value);
|
2024-03-09 13:03:38 -08:00
|
|
|
CORD value = compile(env, call_arg->value);
|
|
|
|
if (!promote(env, &value, actual_t, spec_arg->type))
|
2024-03-08 11:23:16 -08:00
|
|
|
code_err(call_arg->value, "This argument is supposed to be a %T, but this value is a %T", spec_arg->type, actual_t);
|
2024-03-29 09:54:31 -07:00
|
|
|
Table$str_set(&used_args, pseudoname, call_arg);
|
2024-03-08 11:23:16 -08:00
|
|
|
if (code) code = CORD_cat(code, ", ");
|
2024-03-09 13:03:38 -08:00
|
|
|
code = CORD_cat(code, value);
|
2024-03-08 11:23:16 -08:00
|
|
|
goto found_it;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (spec_arg->default_val) {
|
|
|
|
if (code) code = CORD_cat(code, ", ");
|
|
|
|
code = CORD_cat(code, compile(default_scope, spec_arg->default_val));
|
|
|
|
goto found_it;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(spec_arg->name);
|
|
|
|
code_err(call_ast, "The required argument '%s' was not provided", spec_arg->name);
|
|
|
|
found_it: continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
int64_t i = 1;
|
|
|
|
for (arg_ast_t *call_arg = call_args; call_arg; call_arg = call_arg->next) {
|
|
|
|
if (call_arg->name) {
|
2024-03-29 09:54:31 -07:00
|
|
|
if (!Table$str_get(used_args, call_arg->name))
|
2024-03-08 11:23:16 -08:00
|
|
|
code_err(call_arg->value, "There is no argument with the name '%s'", call_arg->name);
|
|
|
|
} else {
|
|
|
|
const char *pseudoname = heap_strf("%ld", i++);
|
2024-03-29 09:54:31 -07:00
|
|
|
if (!Table$str_get(used_args, pseudoname))
|
2024-03-08 11:23:16 -08:00
|
|
|
code_err(call_arg->value, "This is one argument too many!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return code;
|
|
|
|
}
|
|
|
|
|
2024-02-13 11:42:33 -08:00
|
|
|
CORD compile(env_t *env, ast_t *ast)
|
2024-02-04 12:23:59 -08:00
|
|
|
{
|
|
|
|
switch (ast->tag) {
|
2024-04-12 10:09:31 -07:00
|
|
|
case Nil: {
|
|
|
|
type_t *t = parse_type_ast(env, Match(ast, Nil)->type);
|
|
|
|
return CORD_all("((", compile_type(env, t), "*)NULL)");
|
|
|
|
}
|
2024-02-12 00:00:31 -08:00
|
|
|
case Bool: return Match(ast, Bool)->b ? "yes" : "no";
|
2024-02-29 10:28:39 -08:00
|
|
|
case Var: {
|
|
|
|
binding_t *b = get_binding(env, Match(ast, Var)->name);
|
|
|
|
if (b)
|
2024-04-16 10:50:07 -07:00
|
|
|
return b->code ? b->code : CORD_cat("$", Match(ast, Var)->name);
|
|
|
|
return CORD_cat("$", Match(ast, Var)->name);
|
2024-02-29 10:49:24 -08:00
|
|
|
// code_err(ast, "I don't know of any variable by this name");
|
2024-02-29 10:28:39 -08:00
|
|
|
}
|
2024-02-17 16:32:30 -08:00
|
|
|
case Int: return CORD_asprintf("I%ld(%ld)", Match(ast, Int)->bits, Match(ast, Int)->i);
|
2024-02-13 20:37:24 -08:00
|
|
|
case Num: {
|
|
|
|
// HACK: since the cord library doesn't support the '%a' specifier, this workaround
|
|
|
|
// is necessary:
|
2024-02-17 16:32:30 -08:00
|
|
|
char *buf = asprintfa(Match(ast, Num)->bits == 64 ? "%a" : "%af", Match(ast, Num)->n);
|
2024-02-13 20:37:24 -08:00
|
|
|
return CORD_from_char_star(buf);
|
|
|
|
}
|
2024-02-17 22:27:25 -08:00
|
|
|
case Length: {
|
|
|
|
ast_t *expr = Match(ast, Length)->value;
|
|
|
|
type_t *t = get_type(env, expr);
|
2024-02-18 11:28:35 -08:00
|
|
|
switch (value_type(t)->tag) {
|
2024-03-03 15:15:45 -08:00
|
|
|
case TextType: {
|
2024-02-20 10:06:03 -08:00
|
|
|
CORD str = compile_to_pointer_depth(env, expr, 0, false);
|
2024-03-29 09:54:31 -07:00
|
|
|
return CORD_all("Text$num_clusters(", str, ")");
|
2024-02-18 11:28:35 -08:00
|
|
|
}
|
|
|
|
case ArrayType: {
|
|
|
|
if (t->tag == PointerType) {
|
2024-02-20 10:06:03 -08:00
|
|
|
CORD arr = compile_to_pointer_depth(env, expr, 1, false);
|
2024-02-18 11:28:35 -08:00
|
|
|
return CORD_all("I64((", arr, ")->length)");
|
|
|
|
} else {
|
2024-02-20 10:06:03 -08:00
|
|
|
CORD arr = compile_to_pointer_depth(env, expr, 0, false);
|
2024-02-18 11:28:35 -08:00
|
|
|
return CORD_all("I64((", arr, ").length)");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case TableType: {
|
|
|
|
if (t->tag == PointerType) {
|
2024-02-20 10:06:03 -08:00
|
|
|
CORD table = compile_to_pointer_depth(env, expr, 1, false);
|
2024-02-18 11:28:35 -08:00
|
|
|
return CORD_all("I64((", table, ")->entries.length)");
|
|
|
|
} else {
|
2024-02-20 10:06:03 -08:00
|
|
|
CORD table = compile_to_pointer_depth(env, expr, 0, false);
|
2024-02-18 11:28:35 -08:00
|
|
|
return CORD_all("I64((", table, ").entries.length)");
|
|
|
|
}
|
2024-02-17 22:27:25 -08:00
|
|
|
}
|
|
|
|
default: code_err(ast, "Length is only supported for strings, arrays, and tables, not: %T", t);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2024-03-14 10:47:40 -07:00
|
|
|
case Not: {
|
|
|
|
type_t *t = get_type(env, ast);
|
|
|
|
ast_t *value = Match(ast, Not)->value;
|
|
|
|
if (t->tag == BoolType)
|
|
|
|
return CORD_all("!(", compile(env, value), ")");
|
|
|
|
else if (t->tag == IntType)
|
|
|
|
return CORD_all("~(", compile(env, value), ")");
|
|
|
|
else if (t->tag == ArrayType || t->tag == TableType)
|
|
|
|
return CORD_all("!(", compile(env, WrapAST(ast, Length, value)), ")");
|
|
|
|
else if (t->tag == TextType)
|
|
|
|
return CORD_all("!(", compile(env, value), ")");
|
|
|
|
else
|
|
|
|
code_err(ast, "I don't know how to negate values of type %T", t);
|
|
|
|
}
|
2024-02-17 20:27:02 -08:00
|
|
|
case Negative: return CORD_asprintf("-(%r)", compile(env, Match(ast, Negative)->value));
|
2024-04-16 10:50:07 -07:00
|
|
|
case HeapAllocate: return CORD_asprintf("heap(%r)", compile(env, Match(ast, HeapAllocate)->value));
|
2024-02-25 09:24:38 -08:00
|
|
|
case StackReference: {
|
2024-02-25 10:04:35 -08:00
|
|
|
ast_t *subject = Match(ast, StackReference)->value;
|
|
|
|
if (can_be_mutated(env, subject))
|
2024-03-03 11:34:13 -08:00
|
|
|
return CORD_all("(&", compile(env, subject), ")");
|
2024-04-16 10:50:07 -07:00
|
|
|
return CORD_all("stack(", compile(env, subject), ")");
|
2024-02-25 09:24:38 -08:00
|
|
|
}
|
2024-02-04 12:23:59 -08:00
|
|
|
case BinaryOp: {
|
|
|
|
auto binop = Match(ast, BinaryOp);
|
2024-02-13 11:42:33 -08:00
|
|
|
CORD lhs = compile(env, binop->lhs);
|
|
|
|
CORD rhs = compile(env, binop->rhs);
|
2024-02-17 17:21:01 -08:00
|
|
|
|
2024-02-22 10:18:47 -08:00
|
|
|
type_t *lhs_t = get_type(env, binop->lhs);
|
|
|
|
type_t *rhs_t = get_type(env, binop->rhs);
|
|
|
|
type_t *operand_t;
|
2024-03-09 13:03:38 -08:00
|
|
|
if (promote(env, &rhs, rhs_t, lhs_t))
|
2024-02-22 10:18:47 -08:00
|
|
|
operand_t = lhs_t;
|
2024-03-09 13:03:38 -08:00
|
|
|
else if (promote(env, &lhs, lhs_t, rhs_t))
|
2024-02-22 10:18:47 -08:00
|
|
|
operand_t = rhs_t;
|
|
|
|
else
|
2024-02-22 10:26:43 -08:00
|
|
|
code_err(ast, "I can't do operations between %T and %T", lhs_t, rhs_t);
|
2024-02-17 17:21:01 -08:00
|
|
|
|
2024-02-04 12:23:59 -08:00
|
|
|
switch (binop->op) {
|
2024-02-22 10:09:46 -08:00
|
|
|
case BINOP_POWER: {
|
2024-02-22 10:18:47 -08:00
|
|
|
if (operand_t->tag != NumType && operand_t->tag != IntType)
|
|
|
|
code_err(ast, "Exponentiation is only supported for numeric types");
|
|
|
|
if (operand_t->tag == NumType && Match(operand_t, NumType)->bits == 32)
|
2024-02-22 10:09:46 -08:00
|
|
|
return CORD_all("powf(", lhs, ", ", rhs, ")");
|
|
|
|
else
|
|
|
|
return CORD_all("pow(", lhs, ", ", rhs, ")");
|
|
|
|
}
|
2024-02-17 17:25:31 -08:00
|
|
|
case BINOP_MULT: {
|
2024-02-22 10:18:47 -08:00
|
|
|
if (operand_t->tag != IntType && operand_t->tag != NumType)
|
2024-02-17 17:25:31 -08:00
|
|
|
code_err(ast, "Math operations are only supported for numeric types");
|
|
|
|
return CORD_asprintf("(%r * %r)", lhs, rhs);
|
|
|
|
}
|
|
|
|
case BINOP_DIVIDE: {
|
2024-02-22 10:18:47 -08:00
|
|
|
if (operand_t->tag != IntType && operand_t->tag != NumType)
|
2024-02-17 17:25:31 -08:00
|
|
|
code_err(ast, "Math operations are only supported for numeric types");
|
|
|
|
return CORD_asprintf("(%r / %r)", lhs, rhs);
|
|
|
|
}
|
|
|
|
case BINOP_MOD: {
|
2024-02-22 10:18:47 -08:00
|
|
|
if (operand_t->tag != IntType && operand_t->tag != NumType)
|
2024-02-17 17:25:31 -08:00
|
|
|
code_err(ast, "Math operations are only supported for numeric types");
|
2024-03-14 10:50:24 -07:00
|
|
|
return CORD_asprintf("(%r %% %r)", lhs, rhs);
|
2024-02-17 17:25:31 -08:00
|
|
|
}
|
|
|
|
case BINOP_MOD1: {
|
2024-02-22 10:18:47 -08:00
|
|
|
if (operand_t->tag != IntType && operand_t->tag != NumType)
|
2024-02-17 17:25:31 -08:00
|
|
|
code_err(ast, "Math operations are only supported for numeric types");
|
2024-03-14 10:50:24 -07:00
|
|
|
return CORD_asprintf("((%r %% %r) + 1)", lhs, rhs);
|
2024-02-17 17:25:31 -08:00
|
|
|
}
|
|
|
|
case BINOP_PLUS: {
|
2024-02-22 10:18:47 -08:00
|
|
|
if (operand_t->tag != IntType && operand_t->tag != NumType)
|
2024-02-17 17:25:31 -08:00
|
|
|
code_err(ast, "Math operations are only supported for numeric types");
|
|
|
|
return CORD_asprintf("(%r + %r)", lhs, rhs);
|
|
|
|
}
|
|
|
|
case BINOP_MINUS: {
|
2024-02-22 10:18:47 -08:00
|
|
|
if (operand_t->tag != IntType && operand_t->tag != NumType)
|
2024-02-17 17:25:31 -08:00
|
|
|
code_err(ast, "Math operations are only supported for numeric types");
|
|
|
|
return CORD_asprintf("(%r - %r)", lhs, rhs);
|
|
|
|
}
|
|
|
|
case BINOP_LSHIFT: {
|
2024-02-22 10:18:47 -08:00
|
|
|
if (operand_t->tag != IntType && operand_t->tag != NumType)
|
2024-02-17 17:25:31 -08:00
|
|
|
code_err(ast, "Math operations are only supported for numeric types");
|
|
|
|
return CORD_asprintf("(%r << %r)", lhs, rhs);
|
|
|
|
}
|
|
|
|
case BINOP_RSHIFT: {
|
2024-02-22 10:18:47 -08:00
|
|
|
if (operand_t->tag != IntType && operand_t->tag != NumType)
|
2024-02-17 17:25:31 -08:00
|
|
|
code_err(ast, "Math operations are only supported for numeric types");
|
|
|
|
return CORD_asprintf("(%r >> %r)", lhs, rhs);
|
|
|
|
}
|
2024-02-17 17:21:01 -08:00
|
|
|
case BINOP_EQ: {
|
2024-02-22 10:18:47 -08:00
|
|
|
switch (operand_t->tag) {
|
2024-02-17 17:21:01 -08:00
|
|
|
case BoolType: case IntType: case NumType: case PointerType: case FunctionType:
|
|
|
|
return CORD_asprintf("(%r == %r)", lhs, rhs);
|
|
|
|
default:
|
2024-04-16 10:50:07 -07:00
|
|
|
return CORD_asprintf("generic_equal(stack(%r), stack(%r), %r)", lhs, rhs, compile_type_info(env, operand_t));
|
2024-02-17 17:21:01 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
case BINOP_NE: {
|
2024-02-22 10:18:47 -08:00
|
|
|
switch (operand_t->tag) {
|
2024-02-17 17:21:01 -08:00
|
|
|
case BoolType: case IntType: case NumType: case PointerType: case FunctionType:
|
|
|
|
return CORD_asprintf("(%r != %r)", lhs, rhs);
|
|
|
|
default:
|
2024-04-16 10:50:07 -07:00
|
|
|
return CORD_asprintf("!generic_equal(stack(%r), stack(%r), %r)", lhs, rhs, compile_type_info(env, operand_t));
|
2024-02-17 17:21:01 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
case BINOP_LT: {
|
2024-02-22 10:18:47 -08:00
|
|
|
switch (operand_t->tag) {
|
2024-02-17 17:21:01 -08:00
|
|
|
case BoolType: case IntType: case NumType: case PointerType: case FunctionType:
|
|
|
|
return CORD_asprintf("(%r < %r)", lhs, rhs);
|
|
|
|
default:
|
2024-04-16 10:50:07 -07:00
|
|
|
return CORD_asprintf("(generic_compare(stack(%r), stack(%r), %r) < 0)", lhs, rhs, compile_type_info(env, operand_t));
|
2024-02-17 17:21:01 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
case BINOP_LE: {
|
2024-02-22 10:18:47 -08:00
|
|
|
switch (operand_t->tag) {
|
2024-02-17 17:21:01 -08:00
|
|
|
case BoolType: case IntType: case NumType: case PointerType: case FunctionType:
|
|
|
|
return CORD_asprintf("(%r <= %r)", lhs, rhs);
|
|
|
|
default:
|
2024-04-16 10:50:07 -07:00
|
|
|
return CORD_asprintf("(generic_compare(stack(%r), stack(%r), %r) <= 0)", lhs, rhs, compile_type_info(env, operand_t));
|
2024-02-17 17:21:01 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
case BINOP_GT: {
|
2024-02-22 10:18:47 -08:00
|
|
|
switch (operand_t->tag) {
|
2024-02-17 17:21:01 -08:00
|
|
|
case BoolType: case IntType: case NumType: case PointerType: case FunctionType:
|
|
|
|
return CORD_asprintf("(%r > %r)", lhs, rhs);
|
|
|
|
default:
|
2024-04-16 10:50:07 -07:00
|
|
|
return CORD_asprintf("(generic_compare(stack(%r), stack(%r), %r) > 0)", lhs, rhs, compile_type_info(env, operand_t));
|
2024-02-17 17:21:01 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
case BINOP_GE: {
|
2024-02-22 10:18:47 -08:00
|
|
|
switch (operand_t->tag) {
|
2024-02-17 17:21:01 -08:00
|
|
|
case BoolType: case IntType: case NumType: case PointerType: case FunctionType:
|
|
|
|
return CORD_asprintf("(%r >= %r)", lhs, rhs);
|
|
|
|
default:
|
2024-04-16 10:50:07 -07:00
|
|
|
return CORD_asprintf("(generic_compare(stack(%r), stack(%r), %r) >= 0)", lhs, rhs, compile_type_info(env, operand_t));
|
2024-02-17 17:21:01 -08:00
|
|
|
}
|
|
|
|
}
|
2024-02-17 17:25:31 -08:00
|
|
|
case BINOP_AND: {
|
2024-02-22 10:18:47 -08:00
|
|
|
if (operand_t->tag == BoolType)
|
2024-02-17 17:25:31 -08:00
|
|
|
return CORD_asprintf("(%r && %r)", lhs, rhs);
|
2024-02-22 10:18:47 -08:00
|
|
|
else if (operand_t->tag == IntType)
|
2024-02-17 17:25:31 -08:00
|
|
|
return CORD_asprintf("(%r & %r)", lhs, rhs);
|
|
|
|
else
|
|
|
|
code_err(ast, "Boolean operators are only supported for Bool and integer types");
|
|
|
|
}
|
2024-04-02 10:08:06 -07:00
|
|
|
case BINOP_CMP: {
|
2024-04-16 10:50:07 -07:00
|
|
|
return CORD_all("generic_compare(stack(", lhs, "), stack(", rhs, "), ", compile_type_info(env, operand_t), ")");
|
2024-04-02 10:08:06 -07:00
|
|
|
}
|
2024-02-17 17:25:31 -08:00
|
|
|
case BINOP_OR: {
|
2024-02-22 10:18:47 -08:00
|
|
|
if (operand_t->tag == BoolType)
|
2024-02-17 17:25:31 -08:00
|
|
|
return CORD_asprintf("(%r || %r)", lhs, rhs);
|
2024-02-22 10:18:47 -08:00
|
|
|
else if (operand_t->tag == IntType)
|
2024-02-17 17:25:31 -08:00
|
|
|
return CORD_asprintf("(%r | %r)", lhs, rhs);
|
|
|
|
else
|
|
|
|
code_err(ast, "Boolean operators are only supported for Bool and integer types");
|
|
|
|
}
|
2024-03-14 10:28:30 -07:00
|
|
|
case BINOP_XOR: {
|
|
|
|
if (operand_t->tag == BoolType || operand_t->tag == IntType)
|
|
|
|
return CORD_asprintf("(%r ^ %r)", lhs, rhs);
|
|
|
|
else
|
|
|
|
code_err(ast, "Boolean operators are only supported for Bool and integer types");
|
|
|
|
}
|
|
|
|
case BINOP_CONCAT: {
|
|
|
|
switch (operand_t->tag) {
|
|
|
|
case TextType: {
|
|
|
|
return CORD_all("CORD_cat(", lhs, ", ", rhs, ")");
|
|
|
|
}
|
|
|
|
case ArrayType: {
|
2024-03-29 09:54:31 -07:00
|
|
|
return CORD_all("Array$concat(", lhs, ", ", rhs, ", ", compile_type_info(env, operand_t), ")");
|
2024-03-14 10:28:30 -07:00
|
|
|
}
|
|
|
|
default:
|
|
|
|
code_err(ast, "Concatenation isn't supported for %T types", operand_t);
|
2024-02-23 10:29:20 -08:00
|
|
|
}
|
2024-02-23 10:24:06 -08:00
|
|
|
}
|
2024-03-14 10:28:30 -07:00
|
|
|
default: break;
|
2024-02-04 12:23:59 -08:00
|
|
|
}
|
2024-03-14 10:28:30 -07:00
|
|
|
code_err(ast, "unimplemented binop");
|
2024-02-04 12:23:59 -08:00
|
|
|
}
|
2024-03-03 15:15:45 -08:00
|
|
|
case TextLiteral: {
|
|
|
|
CORD literal = Match(ast, TextLiteral)->cord;
|
2024-02-11 12:31:30 -08:00
|
|
|
if (literal == CORD_EMPTY)
|
2024-02-11 12:22:32 -08:00
|
|
|
return "(CORD)CORD_EMPTY";
|
2024-02-24 11:36:08 -08:00
|
|
|
CORD code = "(CORD)\"";
|
2024-02-11 12:31:30 -08:00
|
|
|
CORD_pos i;
|
|
|
|
CORD_FOR(i, literal) {
|
2024-02-16 10:29:02 -08:00
|
|
|
char c = CORD_pos_fetch(i);
|
2024-02-11 12:31:30 -08:00
|
|
|
switch (c) {
|
2024-02-04 15:04:41 -08:00
|
|
|
case '\\': code = CORD_cat(code, "\\\\"); break;
|
|
|
|
case '"': code = CORD_cat(code, "\\\""); break;
|
|
|
|
case '\a': code = CORD_cat(code, "\\a"); break;
|
|
|
|
case '\b': code = CORD_cat(code, "\\b"); break;
|
|
|
|
case '\n': code = CORD_cat(code, "\\n"); break;
|
|
|
|
case '\r': code = CORD_cat(code, "\\r"); break;
|
|
|
|
case '\t': code = CORD_cat(code, "\\t"); break;
|
|
|
|
case '\v': code = CORD_cat(code, "\\v"); break;
|
2024-02-04 12:23:59 -08:00
|
|
|
default: {
|
2024-02-11 12:31:30 -08:00
|
|
|
if (isprint(c))
|
|
|
|
code = CORD_cat_char(code, c);
|
2024-02-04 12:23:59 -08:00
|
|
|
else
|
2024-03-03 14:49:40 -08:00
|
|
|
CORD_sprintf(&code, "%r\\x%02X", code, (uint8_t)c);
|
2024-02-04 12:23:59 -08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-02-04 15:04:41 -08:00
|
|
|
return CORD_cat_char(code, '"');
|
2024-02-04 12:23:59 -08:00
|
|
|
}
|
2024-03-03 15:15:45 -08:00
|
|
|
case TextJoin: {
|
2024-03-09 15:22:12 -08:00
|
|
|
const char *lang = Match(ast, TextJoin)->lang;
|
2024-03-29 09:54:31 -07:00
|
|
|
type_t *text_t = Table$str_get(*env->types, lang ? lang : "Text");
|
2024-03-09 15:47:56 -08:00
|
|
|
if (!text_t || text_t->tag != TextType)
|
|
|
|
code_err(ast, "%s is not a valid text language name", lang);
|
2024-03-21 10:33:10 -07:00
|
|
|
env_t *lang_env = lang ? Match(get_binding(env, lang)->type, TypeInfoType)->env : NULL;
|
2024-03-03 15:15:45 -08:00
|
|
|
ast_list_t *chunks = Match(ast, TextJoin)->children;
|
2024-02-10 12:33:35 -08:00
|
|
|
if (!chunks) {
|
2024-02-11 12:22:32 -08:00
|
|
|
return "(CORD)CORD_EMPTY";
|
2024-03-09 15:22:12 -08:00
|
|
|
} else if (!chunks->next && chunks->ast->tag == TextLiteral) {
|
|
|
|
return compile(env, chunks->ast);
|
2024-02-10 12:33:35 -08:00
|
|
|
} else {
|
2024-02-18 01:26:26 -08:00
|
|
|
CORD code = "CORD_all(";
|
2024-02-10 12:33:35 -08:00
|
|
|
for (ast_list_t *chunk = chunks; chunk; chunk = chunk->next) {
|
2024-03-09 15:22:12 -08:00
|
|
|
CORD chunk_code;
|
2024-02-18 01:26:26 -08:00
|
|
|
type_t *chunk_t = get_type(env, chunk->ast);
|
2024-03-09 15:22:12 -08:00
|
|
|
if (chunk->ast->tag == TextLiteral) {
|
|
|
|
chunk_code = compile(env, chunk->ast);
|
|
|
|
} else if (chunk_t->tag == TextType && streq(Match(chunk_t, TextType)->lang, lang)) {
|
|
|
|
chunk_code = compile(env, chunk->ast);
|
2024-03-21 10:33:10 -07:00
|
|
|
} else if (lang && lang_env) {
|
2024-03-09 15:22:12 -08:00
|
|
|
// Get conversion function:
|
|
|
|
chunk_code = compile(env, chunk->ast);
|
2024-03-29 09:54:31 -07:00
|
|
|
for (int64_t i = 1; i <= Table$length(*lang_env->locals); i++) {
|
|
|
|
struct {const char *name; binding_t *b; } *entry = Table$entry(*lang_env->locals, i);
|
2024-03-09 15:22:12 -08:00
|
|
|
if (entry->b->type->tag != FunctionType) continue;
|
2024-03-09 15:47:56 -08:00
|
|
|
if (!(streq(entry->name, "escape") || strncmp(entry->name, "escape_", strlen("escape_")) == 0))
|
|
|
|
continue;
|
2024-03-09 15:22:12 -08:00
|
|
|
auto fn = Match(entry->b->type, FunctionType);
|
|
|
|
if (!fn->args || fn->args->next) continue;
|
|
|
|
if (fn->ret->tag != TextType || !streq(Match(fn->ret, TextType)->lang, lang))
|
|
|
|
continue;
|
|
|
|
if (!promote(env, &chunk_code, chunk_t, fn->args->type))
|
|
|
|
continue;
|
|
|
|
chunk_code = CORD_all(entry->b->code, "(", chunk_code, ")");
|
|
|
|
goto found_conversion;
|
|
|
|
}
|
2024-03-09 15:32:36 -08:00
|
|
|
code_err(chunk->ast, "I don't know how to convert %T to %T", chunk_t, text_t);
|
2024-03-09 15:22:12 -08:00
|
|
|
found_conversion:;
|
|
|
|
} else {
|
|
|
|
chunk_code = compile_string(env, chunk->ast, "no");
|
|
|
|
}
|
|
|
|
code = CORD_cat(code, chunk_code);
|
2024-02-18 01:26:26 -08:00
|
|
|
if (chunk->next) code = CORD_cat(code, ", ");
|
2024-02-10 12:33:35 -08:00
|
|
|
}
|
2024-02-18 01:26:26 -08:00
|
|
|
return CORD_cat(code, ")");
|
2024-02-04 12:23:59 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
case Block: {
|
2024-02-04 15:04:41 -08:00
|
|
|
ast_list_t *stmts = Match(ast, Block)->statements;
|
|
|
|
if (stmts && !stmts->next)
|
2024-03-14 10:28:30 -07:00
|
|
|
return compile(env, stmts->ast);
|
2024-02-04 15:04:41 -08:00
|
|
|
|
2024-03-14 10:28:30 -07:00
|
|
|
CORD code = "({\n";
|
2024-02-17 14:00:21 -08:00
|
|
|
env = fresh_scope(env);
|
2024-02-04 15:04:41 -08:00
|
|
|
for (ast_list_t *stmt = stmts; stmt; stmt = stmt->next) {
|
2024-02-24 11:29:40 -08:00
|
|
|
bind_statement(env, stmt->ast);
|
2024-03-14 10:28:30 -07:00
|
|
|
if (stmt->next) {
|
|
|
|
code = CORD_all(code, compile_statement(env, stmt->ast), "\n");
|
|
|
|
} else {
|
|
|
|
code = CORD_all(code, compile(env, stmt->ast), ";\n");
|
|
|
|
}
|
2024-02-25 10:13:36 -08:00
|
|
|
}
|
2024-03-14 10:28:30 -07:00
|
|
|
return CORD_cat(code, "})");
|
2024-02-04 12:23:59 -08:00
|
|
|
}
|
2024-03-05 11:46:01 -08:00
|
|
|
case Min: case Max: {
|
|
|
|
type_t *t = get_type(env, ast);
|
|
|
|
ast_t *key = ast->tag == Min ? Match(ast, Min)->key : Match(ast, Max)->key;
|
|
|
|
ast_t *lhs = ast->tag == Min ? Match(ast, Min)->lhs : Match(ast, Max)->lhs;
|
|
|
|
ast_t *rhs = ast->tag == Min ? Match(ast, Min)->rhs : Match(ast, Max)->rhs;
|
2024-03-07 09:15:38 -08:00
|
|
|
const char *key_name = "$";
|
2024-03-05 11:46:01 -08:00
|
|
|
if (key == NULL) key = FakeAST(Var, key_name);
|
|
|
|
|
|
|
|
env_t *expr_env = fresh_scope(env);
|
2024-04-16 10:50:07 -07:00
|
|
|
set_binding(expr_env, key_name, new(binding_t, .type=t, .code="ternary$lhs"));
|
2024-03-05 11:46:01 -08:00
|
|
|
CORD lhs_key = compile(expr_env, key);
|
|
|
|
|
2024-04-16 10:50:07 -07:00
|
|
|
set_binding(expr_env, key_name, new(binding_t, .type=t, .code="ternary$rhs"));
|
2024-03-05 11:46:01 -08:00
|
|
|
CORD rhs_key = compile(expr_env, key);
|
|
|
|
|
|
|
|
type_t *key_t = get_type(expr_env, key);
|
|
|
|
CORD comparison;
|
|
|
|
if (key_t->tag == IntType || key_t->tag == NumType || key_t->tag == BoolType || key_t->tag == PointerType)
|
|
|
|
comparison = CORD_all("((", lhs_key, ")", (ast->tag == Min ? "<=" : ">="), "(", rhs_key, "))");
|
|
|
|
else if (key_t->tag == TextType)
|
|
|
|
comparison = CORD_all("CORD_cmp(", lhs_key, ", ", rhs_key, ")", (ast->tag == Min ? "<=" : ">="), "0");
|
|
|
|
else
|
2024-04-16 10:50:07 -07:00
|
|
|
comparison = CORD_all("generic_compare(stack(", lhs_key, "), stack(", rhs_key, "), ", compile_type_info(env, key_t), ")",
|
2024-03-05 11:46:01 -08:00
|
|
|
(ast->tag == Min ? "<=" : ">="), "0");
|
|
|
|
|
|
|
|
return CORD_all(
|
|
|
|
"({\n",
|
2024-04-16 10:50:07 -07:00
|
|
|
compile_type(env, t), " ternary$lhs = ", compile(env, lhs), ", ternary$rhs = ", compile(env, rhs), ";\n",
|
|
|
|
comparison, " ? ternary$lhs : ternary$rhs;\n"
|
2024-03-05 11:46:01 -08:00
|
|
|
"})");
|
2024-02-10 14:36:04 -08:00
|
|
|
}
|
2024-02-04 18:13:50 -08:00
|
|
|
case Array: {
|
|
|
|
auto array = Match(ast, Array);
|
|
|
|
if (!array->items)
|
2024-02-25 12:28:46 -08:00
|
|
|
return "(array_t){.length=0}";
|
2024-03-05 23:27:01 -08:00
|
|
|
|
2024-03-13 23:48:07 -07:00
|
|
|
type_t *array_type = get_type(env, ast);
|
2024-03-13 23:37:56 -07:00
|
|
|
|
2024-03-05 23:27:01 -08:00
|
|
|
int64_t n = 0;
|
2024-03-13 23:37:56 -07:00
|
|
|
for (ast_list_t *item = array->items; item; item = item->next) {
|
2024-03-05 23:27:01 -08:00
|
|
|
++n;
|
2024-03-17 11:46:36 -07:00
|
|
|
if (item->ast->tag == Comprehension)
|
2024-03-13 23:37:56 -07:00
|
|
|
goto array_comprehension;
|
|
|
|
}
|
2024-03-05 23:27:01 -08:00
|
|
|
|
2024-03-13 23:48:07 -07:00
|
|
|
{
|
|
|
|
type_t *item_type = Match(array_type, ArrayType)->item_type;
|
2024-04-16 10:50:07 -07:00
|
|
|
CORD code = CORD_all("TypedArrayN(", compile_type(env, item_type), CORD_asprintf(", %ld", n));
|
2024-03-13 23:48:07 -07:00
|
|
|
for (ast_list_t *item = array->items; item; item = item->next)
|
|
|
|
code = CORD_all(code, ", ", compile(env, item->ast));
|
|
|
|
return CORD_cat(code, ")");
|
|
|
|
}
|
2024-03-13 23:37:56 -07:00
|
|
|
|
|
|
|
array_comprehension:
|
|
|
|
{
|
|
|
|
env_t *scope = fresh_scope(env);
|
2024-03-17 12:26:25 -07:00
|
|
|
static int64_t comp_num = 1;
|
2024-04-16 10:50:07 -07:00
|
|
|
scope->comprehension_var = heap_strf("arr$%ld", comp_num++);
|
2024-03-17 12:26:25 -07:00
|
|
|
CORD code = CORD_all("({ array_t ", scope->comprehension_var, " = {};");
|
|
|
|
set_binding(scope, scope->comprehension_var, new(binding_t, .type=array_type, .code=scope->comprehension_var));
|
2024-03-13 23:37:56 -07:00
|
|
|
for (ast_list_t *item = array->items; item; item = item->next) {
|
2024-03-17 11:46:36 -07:00
|
|
|
if (item->ast->tag == Comprehension) {
|
2024-03-17 12:26:25 -07:00
|
|
|
code = CORD_all(code, "\n", compile_statement(scope, item->ast));
|
2024-03-13 23:37:56 -07:00
|
|
|
} else {
|
|
|
|
CORD insert = compile_statement(
|
2024-03-17 12:26:25 -07:00
|
|
|
scope, WrapAST(item->ast, MethodCall, .name="insert", .self=FakeAST(StackReference, FakeAST(Var, scope->comprehension_var)),
|
2024-03-13 23:37:56 -07:00
|
|
|
.args=new(arg_ast_t, .value=item->ast)));
|
|
|
|
code = CORD_all(code, "\n", insert);
|
|
|
|
}
|
|
|
|
}
|
2024-03-17 12:26:25 -07:00
|
|
|
code = CORD_all(code, " ", scope->comprehension_var, "; })");
|
2024-03-13 23:37:56 -07:00
|
|
|
return code;
|
|
|
|
}
|
2024-02-04 18:13:50 -08:00
|
|
|
}
|
2024-02-17 21:12:23 -08:00
|
|
|
case Table: {
|
|
|
|
auto table = Match(ast, Table);
|
|
|
|
if (!table->entries) {
|
|
|
|
CORD code = "(table_t){";
|
|
|
|
if (table->fallback)
|
|
|
|
code = CORD_all(code, ".fallback=", compile(env, table->fallback),",");
|
|
|
|
if (table->default_value)
|
2024-04-16 10:50:07 -07:00
|
|
|
code = CORD_all(code, ".default_value=heap(", compile(env, table->default_value),"),");
|
2024-02-17 21:12:23 -08:00
|
|
|
return CORD_cat(code, "}");
|
|
|
|
}
|
2024-03-17 11:46:36 -07:00
|
|
|
|
2024-03-13 23:48:07 -07:00
|
|
|
type_t *table_type = get_type(env, ast);
|
|
|
|
type_t *key_t = Match(table_type, TableType)->key_type;
|
|
|
|
type_t *value_t = Match(table_type, TableType)->value_type;
|
2024-02-18 01:26:26 -08:00
|
|
|
|
2024-03-17 11:46:36 -07:00
|
|
|
for (ast_list_t *entry = table->entries; entry; entry = entry->next) {
|
|
|
|
if (entry->ast->tag == Comprehension)
|
|
|
|
goto table_comprehension;
|
|
|
|
}
|
|
|
|
|
|
|
|
{ // No comprehension:
|
2024-04-16 10:50:07 -07:00
|
|
|
CORD code = CORD_all("Table(",
|
2024-03-17 17:40:40 -07:00
|
|
|
compile_type(env, key_t), ", ",
|
|
|
|
compile_type(env, value_t), ", ",
|
2024-03-17 11:46:36 -07:00
|
|
|
compile_type_info(env, key_t), ", ",
|
|
|
|
compile_type_info(env, value_t));
|
|
|
|
if (table->fallback)
|
2024-04-16 10:50:07 -07:00
|
|
|
code = CORD_all(code, ", /*fallback:*/ heap(", compile(env, table->fallback), ")");
|
2024-03-17 11:46:36 -07:00
|
|
|
else
|
|
|
|
code = CORD_all(code, ", /*fallback:*/ NULL");
|
2024-02-18 01:26:26 -08:00
|
|
|
|
2024-03-17 11:46:36 -07:00
|
|
|
if (table->default_value)
|
2024-04-16 10:50:07 -07:00
|
|
|
code = CORD_all(code, ", /*default:*/ heap(", compile(env, table->default_value), ")");
|
2024-03-17 11:46:36 -07:00
|
|
|
else
|
|
|
|
code = CORD_all(code, ", /*default:*/ NULL");
|
2024-03-03 13:08:38 -08:00
|
|
|
|
2024-03-17 11:46:36 -07:00
|
|
|
size_t n = 0;
|
|
|
|
for (ast_list_t *entry = table->entries; entry; entry = entry->next)
|
|
|
|
++n;
|
|
|
|
CORD_appendf(&code, ", %zu", n);
|
|
|
|
|
|
|
|
for (ast_list_t *entry = table->entries; entry; entry = entry->next) {
|
|
|
|
auto e = Match(entry->ast, TableEntry);
|
|
|
|
code = CORD_all(code, ",\n\t{", compile(env, e->key), ", ", compile(env, e->value), "}");
|
|
|
|
}
|
|
|
|
return CORD_cat(code, ")");
|
|
|
|
}
|
|
|
|
|
|
|
|
table_comprehension:
|
|
|
|
{
|
2024-03-17 12:26:25 -07:00
|
|
|
static int64_t comp_num = 1;
|
|
|
|
env_t *scope = fresh_scope(env);
|
2024-04-16 10:50:07 -07:00
|
|
|
scope->comprehension_var = heap_strf("table$%ld", comp_num++);
|
2024-03-17 12:26:25 -07:00
|
|
|
|
|
|
|
CORD code = CORD_all("({ table_t ", scope->comprehension_var, " = {");
|
2024-03-17 11:46:36 -07:00
|
|
|
if (table->fallback)
|
2024-04-16 10:50:07 -07:00
|
|
|
code = CORD_all(code, ".fallback=heap(", compile(env, table->fallback), "), ");
|
2024-03-17 11:46:36 -07:00
|
|
|
|
|
|
|
if (table->default_value)
|
2024-04-16 10:50:07 -07:00
|
|
|
code = CORD_all(code, ".default_value=heap(", compile(env, table->default_value), "), ");
|
2024-03-17 11:46:36 -07:00
|
|
|
code = CORD_cat(code, "};");
|
|
|
|
|
2024-03-17 12:26:25 -07:00
|
|
|
set_binding(scope, scope->comprehension_var, new(binding_t, .type=table_type, .code=scope->comprehension_var));
|
2024-03-17 11:46:36 -07:00
|
|
|
for (ast_list_t *entry = table->entries; entry; entry = entry->next) {
|
|
|
|
if (entry->ast->tag == Comprehension) {
|
2024-03-17 12:26:25 -07:00
|
|
|
code = CORD_all(code, "\n", compile_statement(scope, entry->ast));
|
2024-03-17 11:46:36 -07:00
|
|
|
} else {
|
|
|
|
auto e = Match(entry->ast, TableEntry);
|
|
|
|
CORD set = compile_statement(
|
2024-03-17 12:26:25 -07:00
|
|
|
scope, WrapAST(entry->ast, MethodCall, .name="set", .self=FakeAST(StackReference, FakeAST(Var, scope->comprehension_var)),
|
2024-03-17 11:46:36 -07:00
|
|
|
.args=new(arg_ast_t, .value=e->key, .next=new(arg_ast_t, .value=e->value))));
|
|
|
|
code = CORD_all(code, "\n", set);
|
|
|
|
}
|
|
|
|
}
|
2024-03-17 12:26:25 -07:00
|
|
|
code = CORD_all(code, " ", scope->comprehension_var, "; })");
|
2024-03-17 11:46:36 -07:00
|
|
|
return code;
|
2024-02-17 21:12:23 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2024-03-17 11:46:36 -07:00
|
|
|
case Comprehension: {
|
2024-03-17 12:59:06 -07:00
|
|
|
ast_t *base = Match(ast, Comprehension)->expr;
|
|
|
|
while (base->tag == Comprehension)
|
|
|
|
base = Match(ast, Comprehension)->expr;
|
|
|
|
if (base->tag == TableEntry)
|
|
|
|
return compile(env, WrapAST(ast, Table, .entries=new(ast_list_t, .ast=ast)));
|
|
|
|
else
|
|
|
|
return compile(env, WrapAST(ast, Array, .items=new(ast_list_t, .ast=ast)));
|
2024-03-17 11:46:36 -07:00
|
|
|
}
|
2024-03-09 11:02:19 -08:00
|
|
|
case Lambda: {
|
|
|
|
auto lambda = Match(ast, Lambda);
|
|
|
|
static int64_t lambda_number = 1;
|
|
|
|
CORD name = CORD_asprintf("lambda$%ld", lambda_number++);
|
|
|
|
|
|
|
|
env_t *body_scope = fresh_scope(env);
|
|
|
|
for (arg_ast_t *arg = lambda->args; arg; arg = arg->next) {
|
|
|
|
type_t *arg_type = get_arg_ast_type(env, arg);
|
2024-04-16 10:50:07 -07:00
|
|
|
set_binding(body_scope, arg->name, new(binding_t, .type=arg_type, .code=CORD_cat("$", arg->name)));
|
2024-03-09 11:02:19 -08:00
|
|
|
}
|
2024-03-09 13:03:38 -08:00
|
|
|
|
2024-03-09 11:02:19 -08:00
|
|
|
type_t *ret_t = get_type(body_scope, lambda->body);
|
|
|
|
|
2024-03-15 10:35:30 -07:00
|
|
|
fn_ctx_t fn_ctx = (fn_ctx_t){
|
2024-03-09 13:03:38 -08:00
|
|
|
.return_type=ret_t,
|
|
|
|
.closure_scope=body_scope->locals->fallback,
|
|
|
|
.closed_vars=new(table_t),
|
|
|
|
};
|
|
|
|
body_scope->fn_ctx = &fn_ctx;
|
|
|
|
body_scope->locals->fallback = env->globals;
|
|
|
|
|
2024-03-17 17:40:40 -07:00
|
|
|
CORD code = CORD_all("static ", compile_type(env, ret_t), " ", name, "(");
|
2024-03-09 11:02:19 -08:00
|
|
|
for (arg_ast_t *arg = lambda->args; arg; arg = arg->next) {
|
|
|
|
type_t *arg_type = get_arg_ast_type(env, arg);
|
2024-04-16 10:50:07 -07:00
|
|
|
code = CORD_all(code, compile_type(env, arg_type), " $", arg->name, ", ");
|
2024-03-09 11:02:19 -08:00
|
|
|
}
|
2024-03-09 13:03:38 -08:00
|
|
|
|
|
|
|
for (ast_list_t *stmt = Match(lambda->body, Block)->statements; stmt; stmt = stmt->next)
|
|
|
|
(void)compile_statement(body_scope, stmt->ast);
|
|
|
|
|
|
|
|
CORD userdata;
|
2024-03-29 09:54:31 -07:00
|
|
|
if (Table$length(*fn_ctx.closed_vars) == 0) {
|
2024-04-16 10:50:07 -07:00
|
|
|
code = CORD_cat(code, "void *userdata)");
|
2024-03-09 13:03:38 -08:00
|
|
|
userdata = "NULL";
|
|
|
|
} else {
|
|
|
|
CORD def = "typedef struct {";
|
|
|
|
userdata = CORD_all("new(", name, "$userdata_t");
|
2024-03-29 09:54:31 -07:00
|
|
|
for (int64_t i = 1; i <= Table$length(*fn_ctx.closed_vars); i++) {
|
|
|
|
struct { const char *name; binding_t *b; } *entry = Table$entry(*fn_ctx.closed_vars, i);
|
2024-03-17 17:40:40 -07:00
|
|
|
def = CORD_all(def, compile_declaration(env, entry->b->type, entry->name), "; ");
|
2024-03-09 13:03:38 -08:00
|
|
|
userdata = CORD_all(userdata, ", ", entry->b->code);
|
|
|
|
}
|
|
|
|
userdata = CORD_all(userdata, ")");
|
|
|
|
def = CORD_all(def, "} ", name, "$userdata_t;");
|
|
|
|
env->code->typedefs = CORD_cat(env->code->typedefs, def);
|
2024-04-16 10:50:07 -07:00
|
|
|
code = CORD_all(code, name, "$userdata_t *userdata)");
|
2024-03-09 13:03:38 -08:00
|
|
|
}
|
2024-03-09 11:02:19 -08:00
|
|
|
|
2024-03-09 11:09:18 -08:00
|
|
|
CORD body = CORD_EMPTY;
|
|
|
|
for (ast_list_t *stmt = Match(lambda->body, Block)->statements; stmt; stmt = stmt->next) {
|
|
|
|
if (stmt->next || ret_t->tag == VoidType || ret_t->tag == AbortType)
|
|
|
|
body = CORD_all(body, compile_statement(body_scope, stmt->ast), "\n");
|
|
|
|
else
|
|
|
|
body = CORD_all(body, compile_statement(body_scope, FakeAST(Return, stmt->ast)), "\n");
|
|
|
|
}
|
|
|
|
env->code->funcs = CORD_all(env->code->funcs, code, " {\n", body, "\n}");
|
2024-03-09 13:03:38 -08:00
|
|
|
return CORD_all("(closure_t){", name, ", ", userdata, "}");
|
2024-03-09 11:02:19 -08:00
|
|
|
}
|
2024-03-08 11:23:16 -08:00
|
|
|
case MethodCall: {
|
|
|
|
auto call = Match(ast, MethodCall);
|
|
|
|
type_t *self_t = get_type(env, call->self);
|
|
|
|
type_t *self_value_t = value_type(self_t);
|
|
|
|
switch (self_value_t->tag) {
|
|
|
|
case ArrayType: {
|
|
|
|
// TODO: check for readonly
|
2024-04-02 10:08:06 -07:00
|
|
|
type_t *item_t = Match(self_value_t, ArrayType)->item_type;
|
2024-03-08 11:23:16 -08:00
|
|
|
if (streq(call->name, "insert")) {
|
|
|
|
CORD self = compile_to_pointer_depth(env, call->self, 1, false);
|
2024-03-13 23:37:56 -07:00
|
|
|
arg_t *arg_spec = new(arg_t, .name="item", .type=item_t,
|
2024-03-08 11:23:16 -08:00
|
|
|
.next=new(arg_t, .name="at", .type=Type(IntType, .bits=64), .default_val=FakeAST(Int, .i=0, .bits=64)));
|
2024-03-29 09:54:31 -07:00
|
|
|
return CORD_all("Array$insert_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
|
2024-03-08 11:23:16 -08:00
|
|
|
compile_type_info(env, self_value_t), ")");
|
2024-03-08 11:25:17 -08:00
|
|
|
} else if (streq(call->name, "insert_all")) {
|
|
|
|
CORD self = compile_to_pointer_depth(env, call->self, 1, false);
|
|
|
|
arg_t *arg_spec = new(arg_t, .name="items", .type=self_value_t,
|
|
|
|
.next=new(arg_t, .name="at", .type=Type(IntType, .bits=64), .default_val=FakeAST(Int, .i=0, .bits=64)));
|
2024-03-29 09:54:31 -07:00
|
|
|
return CORD_all("Array$insert_all(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
|
2024-03-08 11:25:17 -08:00
|
|
|
compile_type_info(env, self_value_t), ")");
|
2024-03-08 11:23:16 -08:00
|
|
|
} else if (streq(call->name, "remove")) {
|
|
|
|
CORD self = compile_to_pointer_depth(env, call->self, 1, false);
|
|
|
|
arg_t *arg_spec = new(arg_t, .name="index", .type=Type(IntType, .bits=64), .default_val=FakeAST(Int, .i=-1, .bits=64),
|
|
|
|
.next=new(arg_t, .name="count", .type=Type(IntType, .bits=64), .default_val=FakeAST(Int, .i=1, .bits=64)));
|
2024-03-29 09:54:31 -07:00
|
|
|
return CORD_all("Array$remove(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
|
2024-03-08 11:23:16 -08:00
|
|
|
compile_type_info(env, self_value_t), ")");
|
|
|
|
} else if (streq(call->name, "random")) {
|
|
|
|
CORD self = compile_to_pointer_depth(env, call->self, 0, false);
|
2024-04-02 10:08:06 -07:00
|
|
|
(void)compile_arguments(env, ast, NULL, call->args);
|
2024-03-29 09:54:31 -07:00
|
|
|
return CORD_all("Array$random(", self, ")");
|
2024-04-02 20:28:59 -07:00
|
|
|
} else if (streq(call->name, "sample")) {
|
|
|
|
CORD self = compile_to_pointer_depth(env, call->self, 0, false);
|
|
|
|
arg_t *arg_spec = new(arg_t, .name="count", .type=Type(IntType, .bits=64),
|
2024-04-02 20:40:18 -07:00
|
|
|
.next=new(arg_t, .name="weights", .type=Type(ArrayType, .item_type=Type(NumType, .bits=64)),
|
|
|
|
.default_val=FakeAST(Array, .type=new(type_ast_t, .tag=VarTypeAST, .__data.VarTypeAST.name="Num"))));
|
2024-04-02 20:28:59 -07:00
|
|
|
return CORD_all("Array$sample(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
|
|
|
|
compile_type_info(env, self_value_t), ")");
|
2024-03-08 11:23:16 -08:00
|
|
|
} else if (streq(call->name, "shuffle")) {
|
|
|
|
CORD self = compile_to_pointer_depth(env, call->self, 1, false);
|
2024-04-02 10:08:06 -07:00
|
|
|
(void)compile_arguments(env, ast, NULL, call->args);
|
2024-03-29 09:54:31 -07:00
|
|
|
return CORD_all("Array$shuffle(", self, ", ", compile_type_info(env, self_value_t), ")");
|
2024-04-02 10:13:33 -07:00
|
|
|
} else if (streq(call->name, "sort") || streq(call->name, "sorted")) {
|
|
|
|
CORD self = compile_to_pointer_depth(env, call->self, streq(call->name, "sort") ? 1 : 0, false);
|
2024-04-02 10:08:06 -07:00
|
|
|
CORD comparison;
|
|
|
|
if (call->args) {
|
|
|
|
type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_stack=true, .is_readonly=true);
|
|
|
|
type_t *fn_t = Type(FunctionType, .args=new(arg_t, .name="x", .type=item_ptr, .next=new(arg_t, .name="y", .type=item_ptr)),
|
|
|
|
.ret=Type(IntType, .bits=32));
|
|
|
|
arg_t *arg_spec = new(arg_t, .name="by", .type=Type(ClosureType, .fn=fn_t));
|
|
|
|
comparison = compile_arguments(env, ast, arg_spec, call->args);
|
|
|
|
} else {
|
|
|
|
comparison = CORD_all("(closure_t){.fn=generic_compare, .userdata=(void*)", compile_type_info(env, item_t), "}");
|
|
|
|
}
|
2024-04-02 10:13:33 -07:00
|
|
|
return CORD_all("Array$", call->name, "(", self, ", ", comparison, ", ", compile_type_info(env, self_value_t), ")");
|
2024-04-19 10:29:04 -07:00
|
|
|
} else if (streq(call->name, "heapify")) {
|
|
|
|
CORD self = compile_to_pointer_depth(env, call->self, 1, false);
|
|
|
|
CORD comparison;
|
|
|
|
if (call->args) {
|
|
|
|
type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_stack=true);
|
|
|
|
type_t *fn_t = Type(FunctionType, .args=new(arg_t, .name="x", .type=item_ptr, .next=new(arg_t, .name="y", .type=item_ptr)),
|
|
|
|
.ret=Type(IntType, .bits=32));
|
|
|
|
arg_t *arg_spec = new(arg_t, .name="by", .type=Type(ClosureType, .fn=fn_t));
|
|
|
|
comparison = compile_arguments(env, ast, arg_spec, call->args);
|
|
|
|
} else {
|
|
|
|
comparison = CORD_all("((closure_t){.fn=generic_compare, .userdata=(void*)", compile_type_info(env, item_t), "})");
|
|
|
|
}
|
|
|
|
return CORD_all("Array$heapify(", self, ", ", comparison, ", ", compile_type_info(env, self_value_t), ")");
|
|
|
|
} else if (streq(call->name, "heap_push")) {
|
|
|
|
CORD self = compile_to_pointer_depth(env, call->self, 1, false);
|
|
|
|
type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_stack=true);
|
|
|
|
type_t *fn_t = Type(FunctionType, .args=new(arg_t, .name="x", .type=item_ptr, .next=new(arg_t, .name="y", .type=item_ptr)),
|
|
|
|
.ret=Type(IntType, .bits=32));
|
|
|
|
ast_t *default_cmp = FakeAST(InlineCCode, CORD_all("((closure_t){.fn=generic_compare, .userdata=(void*)", compile_type_info(env, item_t), "})"));
|
|
|
|
arg_t *arg_spec = new(arg_t, .name="item", .type=item_t, .next=new(arg_t, .name="by", .type=Type(ClosureType, .fn=fn_t), .default_val=default_cmp));
|
|
|
|
CORD arg_code = compile_arguments(env, ast, arg_spec, call->args);
|
|
|
|
return CORD_all("Array$heap_push_value(", self, ", ", arg_code, ", ", compile_type_info(env, self_value_t), ")");
|
|
|
|
} else if (streq(call->name, "heap_pop")) {
|
|
|
|
CORD self = compile_to_pointer_depth(env, call->self, 1, false);
|
|
|
|
type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_stack=true);
|
|
|
|
type_t *fn_t = Type(FunctionType, .args=new(arg_t, .name="x", .type=item_ptr, .next=new(arg_t, .name="y", .type=item_ptr)),
|
|
|
|
.ret=Type(IntType, .bits=32));
|
|
|
|
ast_t *default_cmp = FakeAST(InlineCCode, CORD_all("((closure_t){.fn=generic_compare, .userdata=(void*)", compile_type_info(env, item_t), "})"));
|
|
|
|
arg_t *arg_spec = new(arg_t, .name="by", .type=Type(ClosureType, .fn=fn_t), .default_val=default_cmp);
|
|
|
|
CORD arg_code = compile_arguments(env, ast, arg_spec, call->args);
|
|
|
|
return CORD_all("Array$heap_pop_value(", self, ", ", arg_code, ", ", compile_type_info(env, self_value_t), ", ", compile_type(env, item_t), ")");
|
2024-03-08 11:23:16 -08:00
|
|
|
} else if (streq(call->name, "clear")) {
|
|
|
|
CORD self = compile_to_pointer_depth(env, call->self, 1, false);
|
2024-04-02 10:08:06 -07:00
|
|
|
(void)compile_arguments(env, ast, NULL, call->args);
|
2024-03-29 09:54:31 -07:00
|
|
|
return CORD_all("Array$clear(", self, ")");
|
2024-03-08 11:23:16 -08:00
|
|
|
} else if (streq(call->name, "slice")) {
|
|
|
|
CORD self = compile_to_pointer_depth(env, call->self, 1, false);
|
|
|
|
arg_t *arg_spec = new(arg_t, .name="first", .type=Type(IntType, .bits=64), .default_val=FakeAST(Int, .i=1, .bits=64),
|
|
|
|
.next=new(arg_t, .name="length", .type=Type(IntType, .bits=64), .default_val=FakeAST(Int, .i=INT64_MAX, .bits=64),
|
|
|
|
.next=new(arg_t, .name="stride", .type=Type(IntType, .bits=64), .default_val=FakeAST(Int, .i=1, .bits=64))));
|
2024-03-29 09:54:31 -07:00
|
|
|
return CORD_all("Array$slice(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
|
2024-03-08 11:23:16 -08:00
|
|
|
compile_type_info(env, self_value_t), ")");
|
2024-03-26 11:59:52 -07:00
|
|
|
} else if (streq(call->name, "reversed")) {
|
|
|
|
CORD self = compile_to_pointer_depth(env, call->self, 0, false);
|
2024-04-02 10:08:06 -07:00
|
|
|
(void)compile_arguments(env, ast, NULL, call->args);
|
2024-03-29 09:54:31 -07:00
|
|
|
return CORD_all("Array$reversed(", self, ")");
|
2024-03-08 11:23:16 -08:00
|
|
|
} else code_err(ast, "There is no '%s' method for arrays", call->name);
|
|
|
|
}
|
2024-03-08 23:48:09 -08:00
|
|
|
case TableType: {
|
|
|
|
auto table = Match(self_value_t, TableType);
|
|
|
|
if (streq(call->name, "get")) {
|
|
|
|
CORD self = compile_to_pointer_depth(env, call->self, 0, false);
|
|
|
|
arg_t *arg_spec = new(arg_t, .name="key", .type=Type(PointerType, .pointed=table->key_type, .is_stack=true, .is_readonly=true));
|
2024-03-29 09:54:31 -07:00
|
|
|
return CORD_all("Table$get(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
|
2024-03-08 23:48:09 -08:00
|
|
|
compile_type_info(env, self_value_t), ")");
|
2024-03-14 10:28:30 -07:00
|
|
|
} else if (streq(call->name, "set")) {
|
|
|
|
CORD self = compile_to_pointer_depth(env, call->self, 1, false);
|
2024-03-17 11:46:36 -07:00
|
|
|
arg_t *arg_spec = new(arg_t, .name="key", .type=table->key_type,
|
|
|
|
.next=new(arg_t, .name="value", .type=table->value_type));
|
2024-03-29 09:54:31 -07:00
|
|
|
return CORD_all("Table$set_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
|
2024-03-14 10:28:30 -07:00
|
|
|
compile_type_info(env, self_value_t), ")");
|
|
|
|
} else if (streq(call->name, "remove")) {
|
|
|
|
CORD self = compile_to_pointer_depth(env, call->self, 1, false);
|
|
|
|
arg_t *arg_spec = new(arg_t, .name="key", .type=Type(PointerType, .pointed=table->key_type, .is_stack=true, .is_readonly=true));
|
2024-03-29 09:54:31 -07:00
|
|
|
return CORD_all("Table$remove(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
|
2024-03-14 10:28:30 -07:00
|
|
|
compile_type_info(env, self_value_t), ")");
|
|
|
|
} else if (streq(call->name, "clear")) {
|
|
|
|
CORD self = compile_to_pointer_depth(env, call->self, 1, false);
|
2024-04-02 10:08:06 -07:00
|
|
|
(void)compile_arguments(env, ast, NULL, call->args);
|
2024-03-29 09:54:31 -07:00
|
|
|
return CORD_all("Table$clear(", self, ")");
|
2024-03-14 10:28:30 -07:00
|
|
|
} else code_err(ast, "There is no '%s' method for tables", call->name);
|
2024-02-22 09:45:12 -08:00
|
|
|
}
|
2024-03-14 10:28:30 -07:00
|
|
|
default: {
|
|
|
|
auto methodcall = Match(ast, MethodCall);
|
|
|
|
type_t *fn_t = get_method_type(env, methodcall->self, methodcall->name);
|
|
|
|
arg_ast_t *args = new(arg_ast_t, .value=methodcall->self, .next=methodcall->args);
|
|
|
|
binding_t *b = get_namespace_binding(env, methodcall->self, methodcall->name);
|
|
|
|
if (!b) code_err(ast, "No such method");
|
|
|
|
return CORD_all(b->code, "(", compile_arguments(env, ast, Match(fn_t, FunctionType)->args, args), ")");
|
2024-02-22 09:45:12 -08:00
|
|
|
}
|
2024-02-18 01:26:26 -08:00
|
|
|
}
|
2024-03-14 10:28:30 -07:00
|
|
|
}
|
|
|
|
case FunctionCall: {
|
|
|
|
auto call = Match(ast, FunctionCall);
|
|
|
|
type_t *fn_t = get_type(env, call->fn);
|
|
|
|
if (fn_t->tag == FunctionType) {
|
|
|
|
CORD fn = compile(env, call->fn);
|
|
|
|
return CORD_all(fn, "(", compile_arguments(env, ast, Match(fn_t, FunctionType)->args, call->args), ")");
|
|
|
|
} else if (fn_t->tag == TypeInfoType) {
|
|
|
|
type_t *t = Match(fn_t, TypeInfoType)->type;
|
2024-04-23 10:12:49 -07:00
|
|
|
if (t->tag == StructType) {
|
|
|
|
fn_t = Type(FunctionType, .args=Match(t, StructType)->fields, .ret=t);
|
|
|
|
CORD fn = compile(env, call->fn);
|
|
|
|
return CORD_all(fn, "(", compile_arguments(env, ast, Match(fn_t, FunctionType)->args, call->args), ")");
|
|
|
|
} else if (t->tag == IntType || t->tag == NumType) {
|
|
|
|
if (!call->args || call->args->next)
|
|
|
|
code_err(call->fn, "This constructor takes exactly 1 argument");
|
|
|
|
type_t *actual = get_type(env, call->args->value);
|
|
|
|
if (actual->tag != IntType && actual->tag != NumType)
|
|
|
|
code_err(call->args->value, "This %T value cannot be converted to a %T", actual, t);
|
|
|
|
return CORD_all("((", compile_type(env, t), ")(", compile(env, call->args->value), "))");
|
|
|
|
} else {
|
2024-03-14 10:28:30 -07:00
|
|
|
code_err(call->fn, "This is not a type that has a constructor");
|
2024-04-23 10:12:49 -07:00
|
|
|
}
|
2024-03-14 10:28:30 -07:00
|
|
|
} else if (fn_t->tag == ClosureType) {
|
|
|
|
fn_t = Match(fn_t, ClosureType)->fn;
|
|
|
|
arg_t *type_args = Match(fn_t, FunctionType)->args;
|
2024-02-18 01:26:26 -08:00
|
|
|
|
2024-03-14 10:28:30 -07:00
|
|
|
arg_t *closure_fn_args = NULL;
|
|
|
|
for (arg_t *arg = Match(fn_t, FunctionType)->args; arg; arg = arg->next)
|
|
|
|
closure_fn_args = new(arg_t, .name=arg->name, .type=arg->type, .default_val=arg->default_val, .next=closure_fn_args);
|
2024-04-16 10:50:07 -07:00
|
|
|
closure_fn_args = new(arg_t, .name="userdata", .type=Type(PointerType, .pointed=Type(MemoryType)), .next=closure_fn_args);
|
2024-03-14 10:28:30 -07:00
|
|
|
REVERSE_LIST(closure_fn_args);
|
2024-03-17 17:40:40 -07:00
|
|
|
CORD fn_type_code = compile_type(env, Type(FunctionType, .args=closure_fn_args, .ret=Match(fn_t, FunctionType)->ret));
|
2024-03-14 10:28:30 -07:00
|
|
|
|
|
|
|
CORD closure = compile(env, call->fn);
|
|
|
|
CORD arg_code = compile_arguments(env, ast, type_args, call->args);
|
|
|
|
if (arg_code) arg_code = CORD_cat(arg_code, ", ");
|
|
|
|
if (call->fn->tag == Var) {
|
|
|
|
return CORD_all("((", fn_type_code, ")", closure, ".fn)(", arg_code, closure, ".userdata)");
|
2024-03-06 09:56:30 -08:00
|
|
|
} else {
|
2024-04-16 10:50:07 -07:00
|
|
|
return CORD_all("({ closure_t closure = ", closure, "; ((", fn_type_code, ")closure.fn)(",
|
|
|
|
arg_code, "closure.userdata); })");
|
2024-03-06 09:56:30 -08:00
|
|
|
}
|
2024-03-14 10:28:30 -07:00
|
|
|
} else {
|
|
|
|
code_err(call->fn, "This is not a function, it's a %T", fn_t);
|
2024-02-18 01:26:26 -08:00
|
|
|
}
|
2024-03-14 10:28:30 -07:00
|
|
|
}
|
|
|
|
case When:
|
|
|
|
code_err(ast, "'when' expressions are not yet implemented");
|
|
|
|
case If: {
|
|
|
|
auto if_ = Match(ast, If);
|
|
|
|
if (!if_->else_body)
|
|
|
|
code_err(ast, "'if' expressions can only be used if you also have an 'else' block");
|
|
|
|
|
|
|
|
type_t *t = get_type(env, ast);
|
|
|
|
if (t->tag == VoidType || t->tag == AbortType)
|
|
|
|
code_err(ast, "This expression has a %T type, but it needs to have a real value", t);
|
|
|
|
|
|
|
|
if (if_->condition->tag == Declare) {
|
|
|
|
CORD condition = Match(Match(if_->condition, Declare)->var, Var)->name;
|
|
|
|
CORD decl = compile_statement(env, if_->condition);
|
2024-03-15 10:07:41 -07:00
|
|
|
env_t *true_scope = fresh_scope(env);
|
|
|
|
bind_statement(true_scope, if_->condition);
|
|
|
|
type_t *true_type = get_type(true_scope, if_->body);
|
|
|
|
type_t *false_type = get_type(env, if_->else_body);
|
|
|
|
if (true_type->tag == AbortType) {
|
|
|
|
return CORD_all("({ ", decl, "\nif (", condition, ") ", compile_statement(true_scope, if_->body), " ",
|
|
|
|
compile(env, if_->else_body), "; })");
|
|
|
|
} else if (false_type->tag == AbortType) {
|
|
|
|
return CORD_all("({ ", decl, "\nif (!(", condition, ")) ", compile_statement(env, if_->else_body), " ",
|
|
|
|
compile(true_scope, if_->body), "; })");
|
|
|
|
|
|
|
|
} else {
|
|
|
|
return CORD_all("({ ", decl, "\n(", condition, ") ? ",
|
|
|
|
compile(true_scope, if_->body), " : ",
|
|
|
|
compile(env, if_->else_body), "; })");
|
|
|
|
}
|
2024-03-14 10:28:30 -07:00
|
|
|
} else {
|
2024-03-15 10:07:41 -07:00
|
|
|
type_t *true_type = get_type(env, if_->body);
|
|
|
|
type_t *false_type = get_type(env, if_->else_body);
|
|
|
|
if (true_type->tag == AbortType)
|
|
|
|
return CORD_all("({ if (", compile(env, if_->condition), ") ", compile_statement(env, if_->body),
|
|
|
|
"\n", compile(env, if_->else_body), "; })");
|
|
|
|
else if (false_type->tag == AbortType)
|
|
|
|
return CORD_all("({ if (!(", compile(env, if_->condition), ")) ", compile_statement(env, if_->else_body),
|
|
|
|
"\n", compile(env, if_->body), "; })");
|
|
|
|
else
|
|
|
|
return CORD_all("((", compile(env, if_->condition), ") ? ",
|
|
|
|
compile(env, if_->body), " : ", compile(env, if_->else_body), ")");
|
2024-02-18 01:26:26 -08:00
|
|
|
}
|
2024-02-04 18:13:50 -08:00
|
|
|
}
|
2024-02-25 12:28:46 -08:00
|
|
|
case Reduction: {
|
|
|
|
auto reduction = Match(ast, Reduction);
|
|
|
|
type_t *t = get_type(env, ast);
|
|
|
|
CORD code = CORD_all(
|
|
|
|
"({ // Reduction:\n",
|
2024-04-16 10:50:07 -07:00
|
|
|
compile_declaration(env, t, "reduction"), ";\n"
|
2024-02-25 12:28:46 -08:00
|
|
|
);
|
|
|
|
env_t *scope = fresh_scope(env);
|
2024-03-05 11:46:01 -08:00
|
|
|
ast_t *result = FakeAST(Var, "$reduction");
|
2024-04-16 10:50:07 -07:00
|
|
|
set_binding(scope, "$reduction", new(binding_t, .type=t, .code="reduction"));
|
2024-02-25 13:02:36 -08:00
|
|
|
ast_t *empty = NULL;
|
2024-02-25 12:28:46 -08:00
|
|
|
if (reduction->fallback) {
|
|
|
|
type_t *fallback_type = get_type(scope, reduction->fallback);
|
|
|
|
if (fallback_type->tag == AbortType) {
|
2024-02-25 13:02:36 -08:00
|
|
|
empty = reduction->fallback;
|
2024-02-25 12:28:46 -08:00
|
|
|
} else {
|
2024-02-25 13:02:36 -08:00
|
|
|
empty = FakeAST(Assign, .targets=new(ast_list_t, .ast=result), .values=new(ast_list_t, .ast=reduction->fallback));
|
2024-02-25 12:28:46 -08:00
|
|
|
}
|
|
|
|
} else {
|
2024-02-25 13:02:36 -08:00
|
|
|
empty = FakeAST(
|
|
|
|
InlineCCode,
|
2024-03-21 10:32:52 -07:00
|
|
|
CORD_asprintf("fail_source(%r, %ld, %ld, \"This collection was empty!\");\n",
|
2024-03-29 09:54:31 -07:00
|
|
|
Text$quoted(ast->file->filename, false), (long)(reduction->iter->start - reduction->iter->file->text),
|
2024-02-25 13:02:36 -08:00
|
|
|
(long)(reduction->iter->end - reduction->iter->file->text)));
|
2024-02-25 12:28:46 -08:00
|
|
|
}
|
|
|
|
ast_t *i = FakeAST(Var, "$i");
|
2024-03-05 11:46:01 -08:00
|
|
|
ast_t *item = FakeAST(Var, "$iter_value");
|
2024-04-16 10:50:07 -07:00
|
|
|
set_binding(scope, "$iter_value", new(binding_t, .type=t, .code="iter_value"));
|
|
|
|
ast_t *body = FakeAST(InlineCCode, CORD_all("reduction = $$i == 1 ? iter_value : ", compile(scope, reduction->combination), ";"));
|
2024-03-09 11:54:36 -08:00
|
|
|
ast_t *loop = FakeAST(For, .index=i, .value=item, .iter=reduction->iter, .body=body, .empty=empty);
|
2024-04-16 10:50:07 -07:00
|
|
|
code = CORD_all(code, compile_statement(scope, loop), "\nreduction;})");
|
2024-02-25 12:28:46 -08:00
|
|
|
return code;
|
|
|
|
}
|
2024-02-11 22:46:32 -08:00
|
|
|
case FieldAccess: {
|
|
|
|
auto f = Match(ast, FieldAccess);
|
2024-02-17 17:47:43 -08:00
|
|
|
type_t *fielded_t = get_type(env, f->fielded);
|
2024-02-20 10:06:03 -08:00
|
|
|
type_t *value_t = value_type(fielded_t);
|
|
|
|
switch (value_t->tag) {
|
2024-03-03 10:04:50 -08:00
|
|
|
case TypeInfoType: {
|
|
|
|
auto info = Match(value_t, TypeInfoType);
|
2024-03-21 10:33:10 -07:00
|
|
|
binding_t *b = get_binding(info->env, f->field);
|
2024-03-03 10:04:50 -08:00
|
|
|
if (!b) code_err(ast, "I couldn't find the field '%s' on this type", f->field);
|
|
|
|
if (!b->code) code_err(ast, "I couldn't figure out how to compile this field");
|
|
|
|
return b->code;
|
|
|
|
}
|
2024-02-20 10:06:03 -08:00
|
|
|
case StructType: {
|
|
|
|
for (arg_t *field = Match(value_t, StructType)->fields; field; field = field->next) {
|
|
|
|
if (streq(field->name, f->field)) {
|
|
|
|
if (fielded_t->tag == PointerType) {
|
|
|
|
CORD fielded = compile_to_pointer_depth(env, f->fielded, 1, false);
|
|
|
|
return CORD_asprintf("(%r)->%s", fielded, f->field);
|
|
|
|
} else {
|
|
|
|
CORD fielded = compile(env, f->fielded);
|
|
|
|
return CORD_asprintf("(%r).%s", fielded, f->field);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
code_err(ast, "The field '%s' is not a valid field name of %T", f->field, value_t);
|
|
|
|
}
|
|
|
|
case EnumType: {
|
|
|
|
auto enum_ = Match(value_t, EnumType);
|
|
|
|
for (tag_t *tag = enum_->tags; tag; tag = tag->next) {
|
|
|
|
if (streq(tag->name, f->field)) {
|
|
|
|
CORD fielded = compile_to_pointer_depth(env, f->fielded, 0, false);
|
2024-03-17 17:40:40 -07:00
|
|
|
return CORD_asprintf("$tagged(%r, %r%s, %s)", fielded, env->file_prefix, enum_->name, f->field);
|
2024-02-20 10:06:03 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
code_err(ast, "The field '%s' is not a valid field name of %T", f->field, value_t);
|
|
|
|
}
|
2024-02-25 11:35:25 -08:00
|
|
|
case TableType: {
|
|
|
|
if (streq(f->field, "keys")) {
|
2024-04-16 10:50:07 -07:00
|
|
|
return CORD_all("({ table_t *t = ", compile_to_pointer_depth(env, f->fielded, 1, false), ";\n"
|
|
|
|
"t->entries.data_refcount = 3;\n"
|
|
|
|
"t->entries; })");
|
2024-02-25 11:35:25 -08:00
|
|
|
} else if (streq(f->field, "values")) {
|
|
|
|
auto table = Match(value_t, TableType);
|
|
|
|
size_t offset = type_size(table->key_type);
|
|
|
|
size_t align = type_align(table->value_type);
|
|
|
|
if (align > 1 && offset % align > 0)
|
|
|
|
offset += align - (offset % align);
|
2024-04-16 10:50:07 -07:00
|
|
|
return CORD_all("({ table_t *t = ", compile_to_pointer_depth(env, f->fielded, 1, false), ";\n"
|
|
|
|
"t->entries.data_refcount = 3;\n"
|
|
|
|
"(array_t){.data = t->entries.data + ", CORD_asprintf("%zu", offset),
|
|
|
|
",\n .length=t->entries.length,\n .stride=t->entries.stride,\n .data_refcount=3};})");
|
2024-02-25 11:35:25 -08:00
|
|
|
} else if (streq(f->field, "fallback")) {
|
|
|
|
return CORD_all("(", compile_to_pointer_depth(env, f->fielded, 0, false), ").fallback");
|
|
|
|
} else if (streq(f->field, "default")) {
|
|
|
|
return CORD_all("(", compile_to_pointer_depth(env, f->fielded, 0, false), ").default_value");
|
|
|
|
}
|
|
|
|
code_err(ast, "There is no '%s' field on tables", f->field);
|
|
|
|
}
|
2024-03-19 11:22:03 -07:00
|
|
|
case ModuleType: {
|
|
|
|
const char *name = Match(value_t, ModuleType)->name;
|
2024-03-29 09:54:31 -07:00
|
|
|
env_t *module_env = Table$str_get(*env->imports, name);
|
2024-03-19 11:22:03 -07:00
|
|
|
return compile(module_env, WrapAST(ast, Var, f->field));
|
|
|
|
}
|
2024-02-20 10:06:03 -08:00
|
|
|
default:
|
2024-03-19 11:22:03 -07:00
|
|
|
code_err(ast, "Field accesses are not supported on %T values", fielded_t);
|
2024-02-17 17:47:43 -08:00
|
|
|
}
|
2024-02-11 22:46:32 -08:00
|
|
|
}
|
2024-02-18 11:28:35 -08:00
|
|
|
case Index: {
|
|
|
|
auto indexing = Match(ast, Index);
|
2024-02-25 09:20:25 -08:00
|
|
|
type_t *indexed_type = get_type(env, indexing->indexed);
|
|
|
|
if (!indexing->index && indexed_type->tag == PointerType) {
|
2024-02-29 10:00:28 -08:00
|
|
|
auto ptr = Match(indexed_type, PointerType);
|
|
|
|
if (ptr->is_optional)
|
|
|
|
code_err(ast, "This pointer is potentially null, so it can't be safely dereferenced");
|
|
|
|
if (ptr->pointed->tag == ArrayType) {
|
2024-04-16 10:50:07 -07:00
|
|
|
return CORD_all("({ array_t *arr = ", compile(env, indexing->indexed), "; arr->data_refcount = 3; *arr; })");
|
2024-02-29 10:00:28 -08:00
|
|
|
} else if (ptr->pointed->tag == TableType) {
|
2024-04-16 10:50:07 -07:00
|
|
|
return CORD_all("({ table_t *t = ", compile(env, indexing->indexed), "; Table$mark_copy_on_write(t); *t; })");
|
2024-02-29 10:00:28 -08:00
|
|
|
} else {
|
|
|
|
return CORD_all("*(", compile(env, indexing->indexed), ")");
|
|
|
|
}
|
2024-02-25 09:20:25 -08:00
|
|
|
}
|
|
|
|
type_t *container_t = value_type(indexed_type);
|
2024-02-18 11:28:35 -08:00
|
|
|
type_t *index_t = get_type(env, indexing->index);
|
|
|
|
switch (container_t->tag) {
|
|
|
|
case ArrayType: {
|
|
|
|
if (index_t->tag != IntType)
|
|
|
|
code_err(indexing->index, "Arrays can only be indexed by integers, not %T", index_t);
|
|
|
|
type_t *item_type = Match(container_t, ArrayType)->item_type;
|
2024-02-25 10:04:35 -08:00
|
|
|
CORD arr = compile_to_pointer_depth(env, indexing->indexed, 0, false);
|
2024-02-18 11:28:35 -08:00
|
|
|
CORD index = compile(env, indexing->index);
|
2024-02-18 11:53:52 -08:00
|
|
|
file_t *f = indexing->index->file;
|
|
|
|
if (indexing->unchecked)
|
2024-04-16 10:50:07 -07:00
|
|
|
return CORD_all("Array_get_unchecked", compile_type(env, item_type), ", ", arr, ", ", index, ")");
|
2024-02-18 11:53:52 -08:00
|
|
|
else
|
2024-04-16 10:50:07 -07:00
|
|
|
return CORD_all("Array_get(", compile_type(env, item_type), ", ", arr, ", ", index, ", ",
|
2024-03-29 09:54:31 -07:00
|
|
|
Text$quoted(f->filename, false), ", ", CORD_asprintf("%ld", (int64_t)(indexing->index->start - f->text)), ", ",
|
2024-02-18 11:53:52 -08:00
|
|
|
CORD_asprintf("%ld", (int64_t)(indexing->index->end - f->text)),
|
|
|
|
")");
|
2024-02-18 11:28:35 -08:00
|
|
|
}
|
|
|
|
case TableType: {
|
|
|
|
type_t *key_t = Match(container_t, TableType)->key_type;
|
|
|
|
type_t *value_t = Match(container_t, TableType)->value_type;
|
2024-03-08 11:33:54 -08:00
|
|
|
CORD table = compile_to_pointer_depth(env, indexing->indexed, 0, false);
|
2024-02-18 11:28:35 -08:00
|
|
|
CORD key = compile(env, indexing->index);
|
2024-03-09 13:03:38 -08:00
|
|
|
if (!promote(env, &key, index_t, key_t))
|
|
|
|
code_err(indexing->index, "This value has type %T, but this table can only be index with keys of type %T", index_t, key_t);
|
2024-02-18 11:53:52 -08:00
|
|
|
file_t *f = indexing->index->file;
|
2024-04-16 10:50:07 -07:00
|
|
|
return CORD_all("Table_get(", table, ", ", compile_type(env, key_t), ", ", compile_type(env, value_t), ", ",
|
2024-02-18 11:53:52 -08:00
|
|
|
key, ", ", compile_type_info(env, container_t), ", ",
|
2024-03-29 09:54:31 -07:00
|
|
|
Text$quoted(f->filename, false), ", ", CORD_asprintf("%ld", (int64_t)(indexing->index->start - f->text)), ", ",
|
2024-02-18 11:53:52 -08:00
|
|
|
CORD_asprintf("%ld", (int64_t)(indexing->index->end - f->text)),
|
|
|
|
")");
|
2024-02-18 11:28:35 -08:00
|
|
|
}
|
|
|
|
default: code_err(ast, "Indexing is not supported for type: %T", container_t);
|
|
|
|
}
|
|
|
|
}
|
2024-02-25 13:02:36 -08:00
|
|
|
case InlineCCode: return Match(ast, InlineCCode)->code;
|
2024-04-24 10:53:37 -07:00
|
|
|
case Use: return CORD_EMPTY;
|
2024-02-29 10:49:24 -08:00
|
|
|
case LinkerDirective: code_err(ast, "Linker directives are not supported yet");
|
|
|
|
case Extern: code_err(ast, "Externs are not supported yet");
|
|
|
|
case TableEntry: code_err(ast, "Table entries should not be compiled directly");
|
2024-03-14 10:28:30 -07:00
|
|
|
case Declare: case Assign: case UpdateAssign: case For: case While: case StructDef: case LangDef:
|
|
|
|
case EnumDef: case FunctionDef: case Skip: case Stop: case Pass: case Return: case DocTest:
|
|
|
|
code_err(ast, "This is not a valid expression");
|
2024-03-09 11:02:19 -08:00
|
|
|
case Unknown: code_err(ast, "Unknown AST");
|
2024-02-04 12:23:59 -08:00
|
|
|
}
|
2024-02-29 10:49:24 -08:00
|
|
|
code_err(ast, "Unknown AST: %W", ast);
|
2024-04-24 10:53:37 -07:00
|
|
|
return CORD_EMPTY;
|
2024-02-04 12:23:59 -08:00
|
|
|
}
|
|
|
|
|
2024-03-06 09:41:18 -08:00
|
|
|
void compile_namespace(env_t *env, const char *ns_name, ast_t *block)
|
|
|
|
{
|
|
|
|
env_t *ns_env = namespace_env(env, ns_name);
|
|
|
|
for (ast_list_t *stmt = block ? Match(block, Block)->statements : NULL; stmt; stmt = stmt->next) {
|
|
|
|
ast_t *ast = stmt->ast;
|
|
|
|
switch (ast->tag) {
|
2024-03-09 20:21:44 -08:00
|
|
|
case FunctionDef: {
|
2024-03-06 09:41:18 -08:00
|
|
|
CORD code = compile_statement(ns_env, ast);
|
|
|
|
env->code->funcs = CORD_cat(env->code->funcs, code);
|
|
|
|
break;
|
2024-03-09 20:21:44 -08:00
|
|
|
}
|
2024-03-06 09:41:18 -08:00
|
|
|
case Declare: {
|
|
|
|
auto decl = Match(ast, Declare);
|
|
|
|
type_t *t = get_type(ns_env, decl->value);
|
|
|
|
|
2024-04-23 09:50:30 -07:00
|
|
|
if (!is_constant(env, decl->value))
|
2024-04-12 10:09:31 -07:00
|
|
|
code_err(decl->value, "This value is supposed to be a compile-time constant, but I can't figure out how to make it one");
|
|
|
|
CORD var_decl = CORD_all(compile_type(env, t), " ", compile(ns_env, decl->var), " = ", compile(ns_env, decl->value), ";\n");
|
2024-03-06 09:41:18 -08:00
|
|
|
env->code->staticdefs = CORD_cat(env->code->staticdefs, var_decl);
|
|
|
|
|
2024-03-17 17:40:40 -07:00
|
|
|
env->code->fndefs = CORD_all(env->code->fndefs, "extern ", compile_type(env, t), " ", compile(ns_env, decl->var), ";\n");
|
2024-03-06 09:41:18 -08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
default: {
|
|
|
|
CORD code = compile_statement(ns_env, ast);
|
2024-04-12 10:09:31 -07:00
|
|
|
assert(!code);
|
2024-03-06 09:41:18 -08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-17 15:38:29 -08:00
|
|
|
CORD compile_type_info(env_t *env, type_t *t)
|
|
|
|
{
|
|
|
|
switch (t->tag) {
|
2024-03-29 09:54:31 -07:00
|
|
|
case BoolType: case IntType: case NumType: return CORD_asprintf("&$%r", type_to_cord(t));
|
2024-03-21 22:52:00 -07:00
|
|
|
case TextType: {
|
|
|
|
auto text = Match(t, TextType);
|
2024-03-29 09:54:31 -07:00
|
|
|
return text->lang ? CORD_all("(&", text->env->file_prefix, text->lang, ")") : "&$Text";
|
2024-03-21 22:52:00 -07:00
|
|
|
}
|
|
|
|
case StructType: {
|
|
|
|
auto s = Match(t, StructType);
|
|
|
|
return CORD_all("(&", s->env->file_prefix, s->name, ")");
|
|
|
|
}
|
|
|
|
case EnumType: {
|
|
|
|
auto e = Match(t, EnumType);
|
|
|
|
return CORD_all("(&", e->env->file_prefix, e->name, ")");
|
|
|
|
}
|
2024-02-17 15:38:29 -08:00
|
|
|
case ArrayType: {
|
|
|
|
type_t *item_t = Match(t, ArrayType)->item_type;
|
2024-02-20 10:06:03 -08:00
|
|
|
return CORD_asprintf("$ArrayInfo(%r)", compile_type_info(env, item_t));
|
2024-02-17 15:38:29 -08:00
|
|
|
}
|
|
|
|
case TableType: {
|
|
|
|
type_t *key_type = Match(t, TableType)->key_type;
|
|
|
|
type_t *value_type = Match(t, TableType)->value_type;
|
2024-02-20 10:06:03 -08:00
|
|
|
return CORD_asprintf("$TableInfo(%r, %r)", compile_type_info(env, key_type), compile_type_info(env, value_type));
|
2024-02-17 15:38:29 -08:00
|
|
|
}
|
|
|
|
case PointerType: {
|
|
|
|
auto ptr = Match(t, PointerType);
|
|
|
|
CORD sigil = ptr->is_stack ? "&" : (ptr->is_optional ? "?" : "@");
|
2024-04-02 10:08:06 -07:00
|
|
|
if (ptr->is_readonly) sigil = CORD_cat(sigil, "%");
|
2024-03-29 09:54:31 -07:00
|
|
|
return CORD_asprintf("$PointerInfo(%r, %r)", Text$quoted(sigil, false), compile_type_info(env, ptr->pointed));
|
2024-02-17 15:38:29 -08:00
|
|
|
}
|
|
|
|
case FunctionType: {
|
2024-03-29 09:54:31 -07:00
|
|
|
return CORD_asprintf("$FunctionInfo(%r)", Text$quoted(type_to_cord(t), false));
|
2024-02-17 15:38:29 -08:00
|
|
|
}
|
|
|
|
case ClosureType: {
|
2024-03-29 09:54:31 -07:00
|
|
|
return CORD_asprintf("$ClosureInfo(%r)", Text$quoted(type_to_cord(t), false));
|
2024-02-17 15:38:29 -08:00
|
|
|
}
|
2024-03-29 09:54:31 -07:00
|
|
|
case TypeInfoType: return "&$TypeInfo";
|
2024-04-21 11:58:33 -07:00
|
|
|
case MemoryType: return "&$Memory";
|
|
|
|
case VoidType: return "&$Void";
|
2024-02-26 20:10:19 -08:00
|
|
|
default:
|
|
|
|
compiler_err(NULL, 0, 0, "I couldn't convert to a type info: %T", t);
|
2024-02-17 15:38:29 -08:00
|
|
|
}
|
|
|
|
}
|
2024-02-17 14:29:56 -08:00
|
|
|
|
2024-04-20 11:55:27 -07:00
|
|
|
CORD compile_cli_arg_call(env_t *env, CORD fn_name, type_t *fn_type)
|
2024-04-12 10:09:31 -07:00
|
|
|
{
|
2024-04-20 11:55:27 -07:00
|
|
|
auto fn_info = Match(fn_type, FunctionType);
|
2024-04-20 12:00:31 -07:00
|
|
|
if (!fn_info->args) {
|
|
|
|
return CORD_all(
|
|
|
|
"if (argc > 1)\n"
|
|
|
|
"errx(1, \"This program doesn't take any arguments.\");\n",
|
|
|
|
fn_name, "();\n");
|
|
|
|
}
|
2024-04-12 10:09:31 -07:00
|
|
|
env_t *main_env = fresh_scope(env);
|
|
|
|
|
|
|
|
CORD usage = CORD_EMPTY;
|
|
|
|
for (arg_t *arg = fn_info->args; arg; arg = arg->next) {
|
|
|
|
usage = CORD_cat(usage, " ");
|
|
|
|
type_t *t = get_arg_type(main_env, arg);
|
|
|
|
CORD flag = Text$replace(arg->name, "_", "-", INT64_MAX);
|
|
|
|
if (arg->default_val) {
|
|
|
|
if (t->tag == BoolType)
|
|
|
|
usage = CORD_all(usage, "[--", flag, "]");
|
|
|
|
else
|
|
|
|
usage = CORD_all(usage, "[--", flag, "=...]");
|
|
|
|
} else {
|
|
|
|
if (t->tag == BoolType)
|
|
|
|
usage = CORD_all(usage, "[--", flag, "|--no-", flag, "]");
|
2024-04-12 10:43:23 -07:00
|
|
|
else if (t->tag == ArrayType)
|
|
|
|
usage = CORD_all(usage, "<", flag, "...>");
|
2024-04-12 10:09:31 -07:00
|
|
|
else
|
|
|
|
usage = CORD_all(usage, "<", flag, ">");
|
|
|
|
}
|
|
|
|
}
|
2024-04-20 11:55:27 -07:00
|
|
|
CORD code = CORD_all("CORD usage = CORD_all(\"Usage: \", argv[0], ", usage ? Text$quoted(usage, false) : "CORD_EMPTY", ");\n",
|
|
|
|
"#define USAGE_ERR(...) errx(1, CORD_to_const_char_star(CORD_all(__VA_ARGS__)))\n"
|
|
|
|
"#define IS_FLAG(str, flag) (strncmp(str, flag, strlen(flag) == 0 && (str[strlen(flag)] == 0 || str[strlen(flag)] == '=')) == 0)\n");
|
2024-04-12 10:09:31 -07:00
|
|
|
|
|
|
|
// Declare args:
|
|
|
|
for (arg_t *arg = fn_info->args; arg; arg = arg->next) {
|
|
|
|
type_t *t = get_arg_type(main_env, arg);
|
|
|
|
assert(arg->name);
|
|
|
|
code = CORD_all(
|
2024-04-16 10:50:07 -07:00
|
|
|
code, compile_declaration(env, t, CORD_cat("$", arg->name)), ";\n",
|
2024-04-12 10:09:31 -07:00
|
|
|
"bool ", arg->name, "$is_set = no;\n");
|
2024-04-16 10:50:07 -07:00
|
|
|
set_binding(env, arg->name, new(binding_t, .type=t, .code=CORD_cat("$", arg->name)));
|
2024-04-12 10:09:31 -07:00
|
|
|
}
|
|
|
|
// Provide --flags:
|
2024-04-16 10:50:07 -07:00
|
|
|
code = CORD_all(code, "CORD flag;\n"
|
2024-04-12 10:09:31 -07:00
|
|
|
"for (int i = 1; i < argc; ) {\n"
|
|
|
|
"if (streq(argv[i], \"--\")) {\n"
|
|
|
|
"argv[i] = NULL;\n"
|
|
|
|
"break;\n"
|
|
|
|
"}\n"
|
|
|
|
"if (strncmp(argv[i], \"--\", 2) != 0) {\n++i;\ncontinue;\n}\n");
|
|
|
|
for (arg_t *arg = fn_info->args; arg; arg = arg->next) {
|
|
|
|
type_t *t = get_arg_type(main_env, arg);
|
|
|
|
CORD flag = Text$replace(arg->name, "_", "-", INT64_MAX);
|
|
|
|
switch (t->tag) {
|
|
|
|
case BoolType: {
|
2024-04-16 10:50:07 -07:00
|
|
|
code = CORD_all(code, "else if (pop_flag(argv, &i, \"", flag, "\", &flag)) {\n"
|
|
|
|
"if (flag) {\n",
|
2024-04-17 10:52:07 -07:00
|
|
|
"$", arg->name, " = Bool$from_text(flag, &", arg->name, "$is_set", ");\n"
|
2024-04-12 10:09:31 -07:00
|
|
|
"if (!", arg->name, "$is_set) \n"
|
2024-04-16 10:50:07 -07:00
|
|
|
"USAGE_ERR(\"Invalid argument for '--", flag, "'\\n\", usage);\n",
|
2024-04-12 10:09:31 -07:00
|
|
|
"} else {\n",
|
2024-04-17 10:52:07 -07:00
|
|
|
"$", arg->name, " = yes;\n",
|
2024-04-12 10:09:31 -07:00
|
|
|
arg->name, "$is_set = yes;\n"
|
|
|
|
"}\n"
|
|
|
|
"}\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case TextType: {
|
2024-04-16 10:50:07 -07:00
|
|
|
code = CORD_all(code, "else if (pop_flag(argv, &i, \"", flag, "\", &flag)) {\n",
|
2024-04-17 10:52:07 -07:00
|
|
|
"$", arg->name, " = CORD_to_const_char_star(flag);\n",
|
2024-04-12 10:09:31 -07:00
|
|
|
arg->name, "$is_set = yes;\n"
|
|
|
|
"}\n");
|
|
|
|
break;
|
|
|
|
}
|
2024-04-12 10:43:23 -07:00
|
|
|
case ArrayType: {
|
|
|
|
if (Match(t, ArrayType)->item_type->tag != TextType)
|
|
|
|
compiler_err(NULL, NULL, NULL, "Main function has unsupported argument type: %T (only arrays of Text are supported)", t);
|
2024-04-16 10:50:07 -07:00
|
|
|
code = CORD_all(code, "else if (pop_flag(argv, &i, \"", flag, "\", &flag)) {\n",
|
2024-04-17 10:52:07 -07:00
|
|
|
"$", arg->name, " = Text$split(CORD_to_const_char_star(flag), \",\");\n",
|
2024-04-12 10:43:23 -07:00
|
|
|
arg->name, "$is_set = yes;\n"
|
|
|
|
"}\n");
|
|
|
|
break;
|
|
|
|
}
|
2024-04-12 10:09:31 -07:00
|
|
|
case IntType: case NumType: {
|
|
|
|
CORD type_name = type_to_cord(t);
|
2024-04-16 10:50:07 -07:00
|
|
|
code = CORD_all(code, "else if (pop_flag(argv, &i, \"", flag, "\", &flag)) {\n",
|
|
|
|
"if (flag == CORD_EMPTY)\n"
|
|
|
|
"USAGE_ERR(\"No value provided for '--", flag, "'\\n\", usage);\n"
|
|
|
|
"CORD invalid = CORD_EMPTY;\n",
|
2024-04-17 10:52:07 -07:00
|
|
|
"$", arg->name, " = ", type_name, "$from_text(flag, &invalid);\n"
|
2024-04-16 10:50:07 -07:00
|
|
|
"if (invalid != CORD_EMPTY)\n"
|
|
|
|
"USAGE_ERR(\"Invalid value provided for '--", flag, "'\\n\", usage);\n",
|
2024-04-12 10:09:31 -07:00
|
|
|
arg->name, "$is_set = yes;\n"
|
|
|
|
"}\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
compiler_err(NULL, NULL, NULL, "Main function has unsupported argument type: %T", t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
code = CORD_all(
|
|
|
|
code, "else {\n"
|
2024-04-16 10:50:07 -07:00
|
|
|
"USAGE_ERR(\"Unrecognized argument: \", argv[i], \"\\n\", usage);\n"
|
2024-04-12 10:09:31 -07:00
|
|
|
"}\n"
|
|
|
|
"}\n"
|
|
|
|
"int i = 1;\n"
|
|
|
|
"while (i < argc && argv[i] == NULL)\n"
|
|
|
|
"++i;\n");
|
|
|
|
|
|
|
|
for (arg_t *arg = fn_info->args; arg; arg = arg->next) {
|
|
|
|
type_t *t = get_arg_type(env, arg);
|
2024-04-12 10:43:23 -07:00
|
|
|
code = CORD_all(code, "if (!", arg->name, "$is_set) {\n");
|
|
|
|
if (t->tag == ArrayType) {
|
|
|
|
code = CORD_all(
|
2024-04-17 10:52:07 -07:00
|
|
|
code, "$", arg->name, " = (array_t){};\n"
|
2024-04-12 10:43:23 -07:00
|
|
|
"for (; i < argc; i++) {\n"
|
|
|
|
"if (argv[i]) {\n"
|
2024-04-16 10:50:07 -07:00
|
|
|
"CORD arg = CORD_from_char_star(argv[i]);\n"
|
2024-04-17 10:52:07 -07:00
|
|
|
"Array$insert(&$", arg->name, ", &arg, 0, $ArrayInfo(&$Text));\n"
|
2024-04-12 10:43:23 -07:00
|
|
|
"argv[i] = NULL;\n"
|
|
|
|
"}\n"
|
|
|
|
"}\n",
|
|
|
|
arg->name, "$is_set = yes;\n");
|
|
|
|
} else if (arg->default_val) {
|
2024-04-17 10:52:07 -07:00
|
|
|
code = CORD_all(code, "$", arg->name, " = ", compile(env, arg->default_val), ";\n");
|
2024-04-12 10:09:31 -07:00
|
|
|
} else {
|
|
|
|
code = CORD_all(
|
|
|
|
code,
|
|
|
|
"if (i < argc) {");
|
|
|
|
if (t->tag == TextType) {
|
2024-04-17 10:52:07 -07:00
|
|
|
code = CORD_all(code, "$", arg->name, " = CORD_from_char_star(argv[i]);\n");
|
2024-04-12 10:09:31 -07:00
|
|
|
} else {
|
|
|
|
code = CORD_all(
|
|
|
|
code,
|
2024-04-16 10:50:07 -07:00
|
|
|
"CORD invalid;\n",
|
2024-04-17 10:52:07 -07:00
|
|
|
"$", arg->name, " = ", type_to_cord(t), "$from_text(argv[i], &invalid)", ";\n"
|
2024-04-16 10:50:07 -07:00
|
|
|
"if (invalid != CORD_EMPTY)\n"
|
|
|
|
"USAGE_ERR(\"Unable to parse this argument as a ", type_to_cord(t), ": \", CORD_from_char_star(argv[i]));\n");
|
2024-04-12 10:09:31 -07:00
|
|
|
}
|
|
|
|
code = CORD_all(
|
|
|
|
code,
|
|
|
|
"argv[i++] = NULL;\n"
|
|
|
|
"while (i < argc && argv[i] == NULL)\n"
|
|
|
|
"++i;\n} else {\n"
|
2024-04-16 10:50:07 -07:00
|
|
|
"USAGE_ERR(\"Required argument '", arg->name, "' was not provided!\\n\", usage);\n",
|
2024-04-12 10:09:31 -07:00
|
|
|
"}\n");
|
|
|
|
}
|
|
|
|
code = CORD_all(code, "}\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
code = CORD_all(code, "for (; i < argc; i++) {\n"
|
2024-04-16 10:50:07 -07:00
|
|
|
"if (argv[i])\nUSAGE_ERR(\"Unexpected argument: \", Text$quoted(argv[i], false), \"\\n\", usage);\n}\n");
|
2024-04-12 10:09:31 -07:00
|
|
|
|
2024-04-20 11:55:27 -07:00
|
|
|
code = CORD_all(code, fn_name, "(");
|
2024-04-12 10:09:31 -07:00
|
|
|
for (arg_t *arg = fn_info->args; arg; arg = arg->next) {
|
2024-04-17 10:52:07 -07:00
|
|
|
code = CORD_all(code, "$", arg->name);
|
2024-04-12 10:09:31 -07:00
|
|
|
if (arg->next) code = CORD_all(code, ", ");
|
|
|
|
}
|
2024-04-20 11:55:27 -07:00
|
|
|
code = CORD_all(code, ");\n");
|
2024-04-12 10:09:31 -07:00
|
|
|
return code;
|
|
|
|
}
|
|
|
|
|
2024-02-17 23:22:31 -08:00
|
|
|
module_code_t compile_file(ast_t *ast)
|
2024-02-17 23:17:44 -08:00
|
|
|
{
|
|
|
|
env_t *env = new_compilation_unit();
|
2024-03-17 17:40:40 -07:00
|
|
|
|
2024-03-19 11:22:03 -07:00
|
|
|
const char *name = file_base_name(ast->file->filename);
|
|
|
|
env->file_prefix = heap_strf("%s$", name);
|
2024-03-29 09:54:31 -07:00
|
|
|
Table$str_set(env->imports, name, env);
|
2024-03-17 17:40:40 -07:00
|
|
|
|
2024-02-17 23:17:44 -08:00
|
|
|
for (ast_list_t *stmt = Match(ast, Block)->statements; stmt; stmt = stmt->next) {
|
2024-04-16 11:02:39 -07:00
|
|
|
// Hack: make sure global is bound as foo$var:
|
2024-04-17 10:44:01 -07:00
|
|
|
if (stmt->ast->tag == Declare && Match(Match(stmt->ast, Declare)->var, Var)->name[0] != '_')
|
2024-04-16 11:02:39 -07:00
|
|
|
env->scope_prefix = heap_strf("%s$", name);
|
2024-02-24 11:29:40 -08:00
|
|
|
bind_statement(env, stmt->ast);
|
2024-04-16 11:02:39 -07:00
|
|
|
env->scope_prefix = NULL;
|
2024-04-10 08:49:43 -07:00
|
|
|
}
|
|
|
|
for (ast_list_t *stmt = Match(ast, Block)->statements; stmt; stmt = stmt->next) {
|
|
|
|
if (stmt->ast->tag == Declare) {
|
|
|
|
auto decl = Match(stmt->ast, Declare);
|
2024-04-17 10:44:01 -07:00
|
|
|
const char *decl_name = Match(decl->var, Var)->name;
|
|
|
|
bool is_private = (decl_name[0] == '_');
|
2024-04-10 08:49:43 -07:00
|
|
|
type_t *t = get_type(env, decl->value);
|
|
|
|
if (t->tag == AbortType || t->tag == VoidType)
|
|
|
|
code_err(stmt->ast, "You can't declare a variable with a %T value", t);
|
2024-04-23 09:50:30 -07:00
|
|
|
if (!is_constant(env, decl->value))
|
2024-04-10 08:49:43 -07:00
|
|
|
code_err(decl->value, "This value is not a valid constant initializer.");
|
2024-04-17 10:44:01 -07:00
|
|
|
|
|
|
|
if (is_private) {
|
|
|
|
env->code->staticdefs = CORD_all(
|
|
|
|
env->code->staticdefs,
|
|
|
|
"static ", compile_type(env, t), " $", decl_name, " = ",
|
|
|
|
compile(env, decl->value), ";\n");
|
2024-04-21 08:22:11 -07:00
|
|
|
} else if (decl->value->tag == Use) {
|
|
|
|
assert(compile_statement(env, stmt->ast) == CORD_EMPTY);
|
2024-04-17 10:44:01 -07:00
|
|
|
} else {
|
|
|
|
env->code->fndefs = CORD_all(
|
|
|
|
env->code->fndefs,
|
|
|
|
compile_declaration(env, t, CORD_cat(env->file_prefix, decl_name)), ";\n");
|
|
|
|
env->code->staticdefs = CORD_all(
|
|
|
|
env->code->staticdefs,
|
|
|
|
"extern ", compile_type(env, t), " ", env->file_prefix, decl_name, " = ",
|
|
|
|
compile(env, decl->value), ";\n");
|
|
|
|
}
|
2024-04-10 08:49:43 -07:00
|
|
|
} else {
|
|
|
|
CORD code = compile_statement(env, stmt->ast);
|
2024-04-12 10:09:31 -07:00
|
|
|
assert(!code);
|
|
|
|
// if (code)
|
|
|
|
// env->code->main = CORD_all(env->code->main, code, "\n");
|
2024-04-10 08:49:43 -07:00
|
|
|
}
|
2024-02-17 23:17:44 -08:00
|
|
|
}
|
|
|
|
|
2024-02-17 23:22:31 -08:00
|
|
|
return (module_code_t){
|
2024-03-19 11:22:03 -07:00
|
|
|
.module_name=name,
|
2024-04-20 11:55:27 -07:00
|
|
|
.env=env,
|
2024-03-19 11:22:03 -07:00
|
|
|
.object_files=env->code->object_files,
|
2024-02-17 23:22:31 -08:00
|
|
|
.header=CORD_all(
|
2024-04-21 11:58:33 -07:00
|
|
|
// "#line 1 ", Text$quoted(ast->file->filename, false), "\n",
|
2024-03-19 20:29:32 -07:00
|
|
|
"#include <tomo/tomo.h>\n",
|
2024-04-21 11:58:33 -07:00
|
|
|
env->code->imports, "\n",
|
2024-02-17 23:22:31 -08:00
|
|
|
env->code->typedefs, "\n",
|
2024-03-03 11:12:40 -08:00
|
|
|
env->code->typecode, "\n",
|
2024-04-12 10:09:31 -07:00
|
|
|
env->code->fndefs, "\n"
|
2024-03-03 12:04:36 -08:00
|
|
|
),
|
2024-02-17 23:22:31 -08:00
|
|
|
.c_file=CORD_all(
|
2024-04-21 11:58:33 -07:00
|
|
|
// "#line 1 ", Text$quoted(ast->file->filename, false), "\n",
|
2024-02-17 23:22:31 -08:00
|
|
|
env->code->staticdefs, "\n",
|
|
|
|
env->code->funcs, "\n",
|
2024-04-12 10:09:31 -07:00
|
|
|
env->code->typeinfos, "\n"
|
2024-02-17 23:22:31 -08:00
|
|
|
),
|
|
|
|
};
|
2024-02-17 23:17:44 -08:00
|
|
|
}
|
|
|
|
|
2024-02-04 12:23:59 -08:00
|
|
|
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
|