refactor: keep the front end, drop everything downstream of it
The milestone structure had stopped describing the compiler and started
shaping it: m7.c, check_m7.c, tests/m2..m9, and a checker and emitter that had
each grown past 2,500 lines because there was nowhere else to put anything.
Restart from the pipeline instead.
What is left is the front end -- lexer, parser, types, ownership, semantic
analysis -- and the fixtures that describe it. The C backend, the DOSBox-X
runner, the milestone registry and the batch build are removed. The driver now
stops after semantic analysis; a code generator attaches where emit_c did.
Fixtures move from milestone directories to what they check:
parse/ grammar own/ ownership and borrowing
types/ type rules optional/ optionals and error unions
format/ formatting, try units/ units and visibility
generic/ generics pending-backend/
pending-backend/ holds the three fixtures that can only be checked by running
a program -- that the bounds check traps, that --no-checks removes it, and that
drops and defers actually fire, verified through a fake allocator. Those are
not front-end tests and are not pretending to be; they come back first when
there is a code generator.
tests/run.py replaces the DOSBox-X harness. It builds the front end with the
pinned Watcom's Windows-hosted driver and runs every fixture in about two
seconds, and it does something the old runner structurally could not: it reads
the `// ERROR:line:text` marker each fixture carries and checks the diagnostic
against it. Those markers have been in the tree all along, unverified, because
DOS could not redirect the compiler's stderr and only the exit code was ever
compared.
133/188 pass. The 55 failures are not regressions -- they are what was already
true and invisible:
- units (27) and generic (23): `import`, `comptime` and generic declarations
parse and are then dropped on the floor. No pass looks at them. The old
registry did not list these fixtures at all, so nothing said so.
- five in own/, optional/ and generic/: a marker disagrees with the diagnostic
about the line or the wording. Each is either a wrong marker or a wrong
diagnostic and has to be read individually.
Everything removed is in git history.
This commit is contained in:
+6
-19
@@ -1,6 +1,5 @@
|
||||
#include "parser.h"
|
||||
#include "check.h"
|
||||
#include "emit_c.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -37,25 +36,20 @@ static void dump_tokens(const char *src, unsigned long n, const char *file,
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int i,dump=0,dump_tok=0,check_only=0,emit=0,no_checks=0;
|
||||
const char *file=0,*outname=0;
|
||||
int i,dump=0,dump_tok=0,check_only=0,no_checks=0;
|
||||
const char *file=0;
|
||||
unsigned long n;
|
||||
char *src;
|
||||
FeDiags d;
|
||||
FeAst ast;
|
||||
FeParser p;
|
||||
FeCheck check;
|
||||
FeEmitter emitter;
|
||||
FILE *out;
|
||||
unsigned pointer_bits=32;
|
||||
if(argc<2){usage();return 2;}
|
||||
for(i=1;i<argc;i++) {
|
||||
if(strcmp(argv[i],"--dump-ast")==0) dump=1;
|
||||
else if(strcmp(argv[i],"--dump-tokens")==0) dump_tok=1;
|
||||
else if(strcmp(argv[i],"--check")==0) check_only=1;
|
||||
else if(strcmp(argv[i],"--emit-c")==0) emit=1;
|
||||
else if(strcmp(argv[i],"-o")==0){if(i+1>=argc){fprintf(fe_diag_stream(),"fec: -o needs a path\n");return 2;}outname=argv[++i];}
|
||||
else if(strncmp(argv[i],"-o",2)==0 && argv[i][2]) outname=argv[i]+2;
|
||||
else if(strncmp(argv[i],"--target=bits16",15)==0) pointer_bits=16;
|
||||
else if(strncmp(argv[i],"--target=bits32",15)==0) pointer_bits=32;
|
||||
else if(strcmp(argv[i],"--no-checks")==0) no_checks=1;
|
||||
@@ -64,7 +58,7 @@ int main(int argc, char **argv)
|
||||
else if(strcmp(argv[i],"--help")==0){usage();return 0;}
|
||||
else {fprintf(fe_diag_stream(),"fec: unknown option %s\n",argv[i]);return 2;}
|
||||
}
|
||||
if((dump?1:0)+(dump_tok?1:0)+(check_only?1:0)+(emit?1:0)>1){
|
||||
if((dump?1:0)+(dump_tok?1:0)+(check_only?1:0)>1){
|
||||
fprintf(fe_diag_stream(),"fec: choose only one output mode\n");
|
||||
return 2;
|
||||
}
|
||||
@@ -92,16 +86,9 @@ int main(int argc, char **argv)
|
||||
free(src);
|
||||
return 1;
|
||||
}
|
||||
if(check_only){
|
||||
fe_ast_destroy(&ast);
|
||||
free(src);
|
||||
return 0;
|
||||
}
|
||||
out=outname?fopen(outname,"w"):stdout;
|
||||
if(!out){fprintf(fe_diag_stream(),"fec: cannot create %s\n",outname);fe_ast_destroy(&ast);free(src);return 2;}
|
||||
fe_emit_c_init(&emitter,out,&check,pointer_bits,no_checks);
|
||||
fe_emit_c_program(&emitter);
|
||||
if(outname)fclose(out);
|
||||
/* Semantic analysis is the last pass there is. A code generator attaches
|
||||
here; until then --check and the default path are the same thing. */
|
||||
(void)check_only;
|
||||
fe_ast_destroy(&ast);
|
||||
free(src);
|
||||
return d.errors?1:0;
|
||||
|
||||
-2428
File diff suppressed because it is too large
Load Diff
@@ -1,28 +0,0 @@
|
||||
#ifndef FE_EMIT_C_H
|
||||
#define FE_EMIT_C_H
|
||||
|
||||
#include "check.h"
|
||||
#include "own.h"
|
||||
|
||||
typedef struct FeEmitter {
|
||||
FILE *out;
|
||||
FeCheck *check;
|
||||
unsigned pointer_bits;
|
||||
int indent;
|
||||
int no_checks;
|
||||
unsigned temp_serial;
|
||||
FeNode *fallthrough_block;
|
||||
FeNode *block_stack[32];
|
||||
unsigned block_seen[32];
|
||||
unsigned block_depth;
|
||||
unsigned loop_floor[16];
|
||||
unsigned loop_depth;
|
||||
FeType *current_ret;
|
||||
FeNode *current_fn;
|
||||
} FeEmitter;
|
||||
|
||||
void fe_emit_c_init(FeEmitter *e, FILE *out, FeCheck *check,
|
||||
unsigned pointer_bits, int no_checks);
|
||||
void fe_emit_c_program(FeEmitter *e);
|
||||
|
||||
#endif
|
||||
-227
@@ -1,227 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
#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
|
||||
Reference in New Issue
Block a user