Merge pull request #8 from sebastianrcnt/agent/m7-core-foundation
add M7 semantic and cleanup foundation
This commit is contained in:
+1
-1
@@ -1,7 +1,7 @@
|
||||
CC ?= cc
|
||||
CFLAGS ?= -O2 -Wall -Wextra -std=c89
|
||||
CPPFLAGS ?= -Isrc
|
||||
SRC = src/arena.c src/diag.c src/lexer.c src/ast.c src/parser.c src/types.c src/own.c src/check.c src/emit_c.c src/driver.c
|
||||
SRC = src/arena.c src/diag.c src/lexer.c src/ast.c src/parser.c src/types.c src/m7.c src/own.c src/check.c src/lower.c src/emit_c.c src/driver.c
|
||||
OBJ = $(SRC:.c=.o)
|
||||
|
||||
.PHONY: all clean dos-build
|
||||
|
||||
@@ -26,10 +26,14 @@ wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=parser.obj src\parser.c
|
||||
if errorlevel 1 goto build_fail
|
||||
wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=types.obj src\types.c
|
||||
if errorlevel 1 goto build_fail
|
||||
wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=m7.obj src\m7.c
|
||||
if errorlevel 1 goto build_fail
|
||||
wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=own.obj src\own.c
|
||||
if errorlevel 1 goto build_fail
|
||||
wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=check.obj src\check.c
|
||||
if errorlevel 1 goto build_fail
|
||||
wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=lower.obj src\lower.c
|
||||
if errorlevel 1 goto build_fail
|
||||
rem Use an unambiguous short object name for the emit_c source.
|
||||
wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=emitc.obj src\emit_c.c
|
||||
if errorlevel 1 goto build_fail
|
||||
|
||||
+227
@@ -0,0 +1,227 @@
|
||||
#include "lower.h"
|
||||
#include <string.h>
|
||||
|
||||
static int lower_grow_scopes(FeLowerPlan *plan)
|
||||
{
|
||||
FeLowerScope *items;
|
||||
unsigned capacity;
|
||||
if (plan->scope_count < plan->scope_capacity) return 1;
|
||||
capacity = plan->scope_capacity ? plan->scope_capacity * 2U : 8U;
|
||||
items = (FeLowerScope *)fe_arena_alloc(plan->arena,
|
||||
capacity * sizeof(FeLowerScope));
|
||||
if (!items) return 0;
|
||||
if (plan->scopes)
|
||||
memcpy(items, plan->scopes,
|
||||
plan->scope_count * sizeof(FeLowerScope));
|
||||
plan->scopes = items;
|
||||
plan->scope_capacity = capacity;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int lower_grow_cleanups(FeLowerPlan *plan)
|
||||
{
|
||||
FeLowerCleanup *items;
|
||||
unsigned capacity;
|
||||
if (plan->cleanup_count < plan->cleanup_capacity) return 1;
|
||||
capacity = plan->cleanup_capacity ? plan->cleanup_capacity * 2U : 16U;
|
||||
items = (FeLowerCleanup *)fe_arena_alloc(plan->arena,
|
||||
capacity * sizeof(FeLowerCleanup));
|
||||
if (!items) return 0;
|
||||
if (plan->cleanups)
|
||||
memcpy(items, plan->cleanups,
|
||||
plan->cleanup_count * sizeof(FeLowerCleanup));
|
||||
plan->cleanups = items;
|
||||
plan->cleanup_capacity = capacity;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static unsigned lower_add_scope(FeLowerPlan *plan, FeNode *block,
|
||||
unsigned parent)
|
||||
{
|
||||
FeLowerScope *scope;
|
||||
unsigned index;
|
||||
if (!lower_grow_scopes(plan)) return FE_LOWER_NO_SCOPE;
|
||||
index = plan->scope_count++;
|
||||
scope = &plan->scopes[index];
|
||||
scope->block = block;
|
||||
scope->parent = parent;
|
||||
scope->ordinal = plan->next_ordinal++;
|
||||
return index;
|
||||
}
|
||||
|
||||
static int lower_add_cleanup(FeLowerPlan *plan, unsigned scope,
|
||||
FeLowerCleanupKind kind, FeNode *node,
|
||||
FeNode *decl, FeType *type)
|
||||
{
|
||||
FeLowerCleanup *cleanup;
|
||||
if (!lower_grow_cleanups(plan)) return 0;
|
||||
cleanup = &plan->cleanups[plan->cleanup_count++];
|
||||
cleanup->kind = kind;
|
||||
cleanup->scope = scope;
|
||||
cleanup->ordinal = plan->next_ordinal++;
|
||||
cleanup->node = node;
|
||||
cleanup->decl = decl;
|
||||
cleanup->type = type;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int fe_lower_type_needs_drop(const FeType *type)
|
||||
{
|
||||
unsigned i;
|
||||
unsigned j;
|
||||
if (!type) return 0;
|
||||
if (type->kind == FE_TYPE_OWNED) return 1;
|
||||
if (type->kind == FE_TYPE_OPTIONAL)
|
||||
return fe_lower_type_needs_drop(type->elem);
|
||||
if (type->kind == FE_TYPE_ERROR_UNION)
|
||||
return fe_lower_type_needs_drop(type->error_value);
|
||||
if (type->kind == FE_TYPE_ARRAY)
|
||||
return fe_lower_type_needs_drop(type->elem);
|
||||
if (type->kind == FE_TYPE_STRUCT) {
|
||||
if (type->has_drop) return 1;
|
||||
for (i = 0; i < type->field_count; ++i)
|
||||
if (fe_lower_type_needs_drop(type->fields[i].type)) return 1;
|
||||
return 0;
|
||||
}
|
||||
if (type->kind == FE_TYPE_ENUM) {
|
||||
for (i = 0; i < type->variant_count; ++i)
|
||||
for (j = 0; j < type->variants[i].field_count; ++j)
|
||||
if (fe_lower_type_needs_drop(type->variants[i].fields[j].type))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lower_build_node(FeLowerPlan *plan, FeNode *node,
|
||||
unsigned scope);
|
||||
|
||||
static int lower_build_list(FeLowerPlan *plan, FeNode *node,
|
||||
unsigned scope)
|
||||
{
|
||||
while (node) {
|
||||
if (!lower_build_node(plan, node, scope)) return 0;
|
||||
node = node->next;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int lower_build_block(FeLowerPlan *plan, FeNode *block,
|
||||
unsigned parent)
|
||||
{
|
||||
unsigned scope;
|
||||
if (!block || block->kind != FE_N_BLOCK) return 1;
|
||||
scope = lower_add_scope(plan, block, parent);
|
||||
if (scope == FE_LOWER_NO_SCOPE) return 0;
|
||||
return lower_build_list(plan, block->children, scope);
|
||||
}
|
||||
|
||||
static int lower_build_node(FeLowerPlan *plan, FeNode *node,
|
||||
unsigned scope)
|
||||
{
|
||||
FeNode *child;
|
||||
FeType *type;
|
||||
if (!node) return 1;
|
||||
if (node->kind == FE_N_BLOCK)
|
||||
return lower_build_block(plan, node, scope);
|
||||
if (node->kind == FE_N_DEFER) {
|
||||
if (!lower_add_cleanup(plan, scope, FE_LOWER_CLEANUP_DEFER,
|
||||
node->a, node, 0))
|
||||
return 0;
|
||||
return lower_build_node(plan, node->a, scope);
|
||||
}
|
||||
if (node->kind == FE_N_LET || node->kind == FE_N_VAR ||
|
||||
node->kind == FE_N_CONST) {
|
||||
type = node->sem_type;
|
||||
if (fe_lower_type_needs_drop(type))
|
||||
if (!lower_add_cleanup(plan, scope, FE_LOWER_CLEANUP_DROP,
|
||||
node, node, type))
|
||||
return 0;
|
||||
}
|
||||
if (!lower_build_node(plan, node->a, scope)) return 0;
|
||||
if (!lower_build_node(plan, node->b, scope)) return 0;
|
||||
if (!lower_build_node(plan, node->c, scope)) return 0;
|
||||
for (child = node->children; child; child = child->next)
|
||||
if (!lower_build_node(plan, child, scope)) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void fe_lower_plan_init(FeLowerPlan *plan, FeArena *arena)
|
||||
{
|
||||
if (!plan) return;
|
||||
plan->arena = arena;
|
||||
plan->fn = 0;
|
||||
plan->scopes = 0;
|
||||
plan->scope_count = 0;
|
||||
plan->scope_capacity = 0;
|
||||
plan->cleanups = 0;
|
||||
plan->cleanup_count = 0;
|
||||
plan->cleanup_capacity = 0;
|
||||
plan->next_ordinal = 0;
|
||||
}
|
||||
|
||||
int fe_lower_plan_build(FeLowerPlan *plan, FeNode *fn)
|
||||
{
|
||||
if (!plan || !plan->arena || !fn || fn->kind != FE_N_FN) return 0;
|
||||
plan->fn = fn;
|
||||
plan->scopes = 0;
|
||||
plan->scope_count = 0;
|
||||
plan->scope_capacity = 0;
|
||||
plan->cleanups = 0;
|
||||
plan->cleanup_count = 0;
|
||||
plan->cleanup_capacity = 0;
|
||||
plan->next_ordinal = 0;
|
||||
if (!fn->c) return 1;
|
||||
return lower_build_block(plan, fn->c, FE_LOWER_NO_SCOPE);
|
||||
}
|
||||
|
||||
unsigned fe_lower_scope_for_block(const FeLowerPlan *plan,
|
||||
const FeNode *block)
|
||||
{
|
||||
unsigned i;
|
||||
if (!plan || !block) return FE_LOWER_NO_SCOPE;
|
||||
for (i = 0; i < plan->scope_count; ++i)
|
||||
if (plan->scopes[i].block == block) return i;
|
||||
return FE_LOWER_NO_SCOPE;
|
||||
}
|
||||
|
||||
unsigned fe_lower_collect_cleanups(const FeLowerPlan *plan,
|
||||
const FeNode *from_block,
|
||||
const FeNode *stop_block,
|
||||
const FeLowerCleanup **out,
|
||||
unsigned out_capacity)
|
||||
{
|
||||
unsigned scope;
|
||||
unsigned stop;
|
||||
unsigned i;
|
||||
unsigned count;
|
||||
if (!plan || !from_block) return 0;
|
||||
scope = fe_lower_scope_for_block(plan, from_block);
|
||||
stop = stop_block ? fe_lower_scope_for_block(plan, stop_block) :
|
||||
FE_LOWER_NO_SCOPE;
|
||||
count = 0;
|
||||
while (scope != FE_LOWER_NO_SCOPE && scope != stop) {
|
||||
for (i = plan->cleanup_count; i > 0; --i) {
|
||||
if (plan->cleanups[i - 1U].scope != scope) continue;
|
||||
if (out && count < out_capacity)
|
||||
out[count] = &plan->cleanups[i - 1U];
|
||||
++count;
|
||||
}
|
||||
scope = plan->scopes[scope].parent;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
int fe_lower_exit_runs_cleanup(FeLowerExitKind kind)
|
||||
{
|
||||
return kind == FE_LOWER_EXIT_FALLTHROUGH ||
|
||||
kind == FE_LOWER_EXIT_RETURN ||
|
||||
kind == FE_LOWER_EXIT_ERROR_RETURN ||
|
||||
kind == FE_LOWER_EXIT_BREAK ||
|
||||
kind == FE_LOWER_EXIT_CONTINUE;
|
||||
}
|
||||
|
||||
int fe_lower_exit_leaves_function(FeLowerExitKind kind)
|
||||
{
|
||||
return kind == FE_LOWER_EXIT_RETURN ||
|
||||
kind == FE_LOWER_EXIT_ERROR_RETURN;
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
#ifndef FE_LOWER_H
|
||||
#define FE_LOWER_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define FE_LOWER_NO_SCOPE ((unsigned)~0U)
|
||||
|
||||
typedef enum FeLowerExitKind {
|
||||
FE_LOWER_EXIT_FALLTHROUGH = 0,
|
||||
FE_LOWER_EXIT_RETURN,
|
||||
FE_LOWER_EXIT_ERROR_RETURN,
|
||||
FE_LOWER_EXIT_BREAK,
|
||||
FE_LOWER_EXIT_CONTINUE
|
||||
} FeLowerExitKind;
|
||||
|
||||
typedef enum FeLowerCleanupKind {
|
||||
FE_LOWER_CLEANUP_DROP = 0,
|
||||
FE_LOWER_CLEANUP_DEFER
|
||||
} FeLowerCleanupKind;
|
||||
|
||||
typedef struct FeLowerScope {
|
||||
FeNode *block;
|
||||
unsigned parent;
|
||||
unsigned ordinal;
|
||||
} FeLowerScope;
|
||||
|
||||
typedef struct FeLowerCleanup {
|
||||
FeLowerCleanupKind kind;
|
||||
unsigned scope;
|
||||
unsigned ordinal;
|
||||
FeNode *node;
|
||||
FeNode *decl;
|
||||
FeType *type;
|
||||
} FeLowerCleanup;
|
||||
|
||||
typedef struct FeLowerPlan {
|
||||
FeArena *arena;
|
||||
FeNode *fn;
|
||||
FeLowerScope *scopes;
|
||||
unsigned scope_count;
|
||||
unsigned scope_capacity;
|
||||
FeLowerCleanup *cleanups;
|
||||
unsigned cleanup_count;
|
||||
unsigned cleanup_capacity;
|
||||
unsigned next_ordinal;
|
||||
} FeLowerPlan;
|
||||
|
||||
void fe_lower_plan_init(FeLowerPlan *plan, FeArena *arena);
|
||||
int fe_lower_plan_build(FeLowerPlan *plan, FeNode *fn);
|
||||
unsigned fe_lower_scope_for_block(const FeLowerPlan *plan,
|
||||
const FeNode *block);
|
||||
unsigned fe_lower_collect_cleanups(const FeLowerPlan *plan,
|
||||
const FeNode *from_block,
|
||||
const FeNode *stop_block,
|
||||
const FeLowerCleanup **out,
|
||||
unsigned out_capacity);
|
||||
int fe_lower_type_needs_drop(const FeType *type);
|
||||
int fe_lower_exit_runs_cleanup(FeLowerExitKind kind);
|
||||
int fe_lower_exit_leaves_function(FeLowerExitKind kind);
|
||||
|
||||
#endif
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
#include "m7.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
static char *m7_generated_name(FeTypeCtx *ctx, const char *prefix)
|
||||
{
|
||||
char number[24];
|
||||
char *p;
|
||||
unsigned long n;
|
||||
sprintf(number, "%u", ctx->generated_serial++);
|
||||
n = (unsigned long)strlen(prefix) + (unsigned long)strlen(number) + 1UL;
|
||||
p = (char *)fe_arena_alloc(ctx->arena, n);
|
||||
if (!p) return 0;
|
||||
strcpy(p, prefix);
|
||||
strcat(p, number);
|
||||
return p;
|
||||
}
|
||||
|
||||
int fe_m7_optional_uses_niche(const FeType *payload)
|
||||
{
|
||||
if (!payload) return 0;
|
||||
if (payload->kind == FE_TYPE_REF) return 1;
|
||||
if (payload->kind == FE_TYPE_OWNED &&
|
||||
!(payload->elem && payload->elem->kind == FE_TYPE_SLICE))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
FeType *fe_m7_optional_type(FeTypeCtx *ctx, FeType *payload)
|
||||
{
|
||||
char key[128];
|
||||
FeType *t;
|
||||
if (!ctx || !payload) return 0;
|
||||
sprintf(key, "?%s", payload->name);
|
||||
t = fe_type_intern(ctx, key);
|
||||
if (!t) return 0;
|
||||
if (t->kind == FE_TYPE_UNKNOWN) {
|
||||
t->kind = FE_TYPE_OPTIONAL;
|
||||
t->elem = payload;
|
||||
if (!fe_m7_optional_uses_niche(payload)) {
|
||||
t->cname = m7_generated_name(ctx, "struct fe_option_");
|
||||
t->maker = m7_generated_name(ctx, "fe_make_option_");
|
||||
}
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
int fe_m7_can_contextual_null(const FeType *expected)
|
||||
{
|
||||
return expected && expected->kind == FE_TYPE_OPTIONAL;
|
||||
}
|
||||
|
||||
FeType *fe_m7_error_union_type(FeTypeCtx *ctx, FeType *error_type,
|
||||
FeType *value_type)
|
||||
{
|
||||
char key[160];
|
||||
FeType *t;
|
||||
if (!ctx || !value_type) return 0;
|
||||
if (!error_type || strcmp(error_type->name, "core.Error") == 0)
|
||||
return fe_type_error_union(ctx, value_type);
|
||||
sprintf(key, "%s!%s", error_type->name, value_type->name);
|
||||
t = fe_type_intern(ctx, key);
|
||||
if (!t) return 0;
|
||||
if (t->kind == FE_TYPE_UNKNOWN) {
|
||||
t->kind = FE_TYPE_ERROR_UNION;
|
||||
/* For FE_TYPE_ERROR_UNION, elem is the nominal error identity.
|
||||
A null elem denotes the built-in core.Error shorthand !T. */
|
||||
t->elem = error_type;
|
||||
t->error_value = value_type;
|
||||
if (value_type->kind != FE_TYPE_VOID) {
|
||||
t->cname = m7_generated_name(ctx, "struct fe_result_");
|
||||
t->maker = m7_generated_name(ctx, "fe_make_result_");
|
||||
t->alloc_cname = m7_generated_name(ctx, "fe_alloc_result_");
|
||||
}
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
FeType *fe_m7_error_type(FeTypeCtx *ctx, const FeType *error_union)
|
||||
{
|
||||
if (!ctx || !error_union || error_union->kind != FE_TYPE_ERROR_UNION)
|
||||
return 0;
|
||||
if (error_union->elem) return error_union->elem;
|
||||
return fe_type_intern(ctx, "core.Error");
|
||||
}
|
||||
|
||||
FeM7ContextKind fe_m7_error_context(FeTypeCtx *ctx, const FeType *expected,
|
||||
const FeType *actual)
|
||||
{
|
||||
FeType *error_type;
|
||||
if (!ctx || !expected || !actual ||
|
||||
expected->kind != FE_TYPE_ERROR_UNION)
|
||||
return FE_M7_CONTEXT_NONE;
|
||||
if (expected->error_value && fe_type_equal(expected->error_value, actual))
|
||||
return FE_M7_CONTEXT_SUCCESS;
|
||||
error_type = fe_m7_error_type(ctx, expected);
|
||||
if (error_type && fe_type_equal(error_type, actual))
|
||||
return FE_M7_CONTEXT_FAILURE;
|
||||
return FE_M7_CONTEXT_NONE;
|
||||
}
|
||||
|
||||
FeM7LazyKind fe_m7_lazy_kind(const FeNode *node)
|
||||
{
|
||||
if (!node || !node->text) return FE_M7_LAZY_NONE;
|
||||
if (strcmp(node->text, "orelse") == 0) return FE_M7_LAZY_ORELSE;
|
||||
if (strcmp(node->text, "catch") == 0) return FE_M7_LAZY_CATCH;
|
||||
return FE_M7_LAZY_NONE;
|
||||
}
|
||||
|
||||
int fe_m7_is_try(const FeNode *node)
|
||||
{
|
||||
return node && node->kind == FE_N_UNARY && node->text &&
|
||||
strcmp(node->text, "try") == 0;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#ifndef FE_M7_H
|
||||
#define FE_M7_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
typedef enum FeM7ContextKind {
|
||||
FE_M7_CONTEXT_NONE = 0,
|
||||
FE_M7_CONTEXT_SUCCESS,
|
||||
FE_M7_CONTEXT_FAILURE
|
||||
} FeM7ContextKind;
|
||||
|
||||
typedef enum FeM7LazyKind {
|
||||
FE_M7_LAZY_NONE = 0,
|
||||
FE_M7_LAZY_ORELSE,
|
||||
FE_M7_LAZY_CATCH
|
||||
} FeM7LazyKind;
|
||||
|
||||
FeType *fe_m7_optional_type(FeTypeCtx *ctx, FeType *payload);
|
||||
int fe_m7_optional_uses_niche(const FeType *payload);
|
||||
int fe_m7_can_contextual_null(const FeType *expected);
|
||||
|
||||
FeType *fe_m7_error_union_type(FeTypeCtx *ctx, FeType *error_type,
|
||||
FeType *value_type);
|
||||
FeType *fe_m7_error_type(FeTypeCtx *ctx, const FeType *error_union);
|
||||
FeM7ContextKind fe_m7_error_context(FeTypeCtx *ctx, const FeType *expected,
|
||||
const FeType *actual);
|
||||
|
||||
FeM7LazyKind fe_m7_lazy_kind(const FeNode *node);
|
||||
int fe_m7_is_try(const FeNode *node);
|
||||
|
||||
#endif
|
||||
+4
-2
@@ -6,7 +6,7 @@
|
||||
typedef enum FeTypeKind {
|
||||
FE_TYPE_ERROR, FE_TYPE_ERROR_UNION, FE_TYPE_VOID, FE_TYPE_BOOL, FE_TYPE_CHAR, FE_TYPE_INT,
|
||||
FE_TYPE_STRUCT, FE_TYPE_ENUM, FE_TYPE_ARRAY, FE_TYPE_SLICE, FE_TYPE_STR,
|
||||
FE_TYPE_REF, FE_TYPE_OWNED, FE_TYPE_UNKNOWN
|
||||
FE_TYPE_REF, FE_TYPE_OWNED, FE_TYPE_OPTIONAL, FE_TYPE_UNKNOWN
|
||||
} FeTypeKind;
|
||||
|
||||
typedef struct FeFieldType FeFieldType;
|
||||
@@ -49,7 +49,9 @@ struct FeType {
|
||||
unsigned long size;
|
||||
unsigned align;
|
||||
FeType *elem;
|
||||
/* Success value for an error union; !void is represented directly. */
|
||||
/* Success value for an error union; !void is represented directly.
|
||||
For a nominal E!T created by M7, elem holds E. A null elem denotes
|
||||
the built-in core.Error shorthand !T. */
|
||||
FeType *error_value;
|
||||
int ref_mut;
|
||||
FeFieldType *fields;
|
||||
|
||||
Reference in New Issue
Block a user