Files
spl/stage1/splc0.c
2026-08-06 19:26:30 +08:00

302 lines
8.9 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* splc0.c — SPL compiler CLI (stage 1, 引导用)
*
* splc0 --dump tokens|ast|all <file> dump 前端产物
* splc0 <in> <out> 编译 (阶段 B 实现)
*/
#define __SCC_LOG_IMPL_IMPORT_SRC__
#include "../stage0/include/utils.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "spl_ast.h"
#include "spl_ast2ir.h"
#include "spl_ir2vm.h"
#include "spl_lexer.h"
#include "spl_sema.h"
#include "spl_tok.h"
static char *read_file(const char *path, long *out_len) {
FILE *f = fopen(path, "rb");
if (!f) {
perror("fopen");
return NULL;
}
fseek(f, 0, SEEK_END);
long len = ftell(f);
fseek(f, 0, SEEK_SET);
char *buf = malloc((size_t)len + 1);
if (!buf) {
fclose(f);
return NULL;
}
fread(buf, 1, (size_t)len, f);
fclose(f);
buf[len] = '\0';
*out_len = len;
return buf;
}
static const char *const tok_type_names[] = {
#define X(name, enum_name, dummy) #enum_name,
KEYWORD_TABLE
#undef X
#define X(name, enum_name, dummy) #enum_name,
TOKEN_TABLE
#undef X
};
static void dump_tokens(const char *src, const char *fname) {
spl_tok_vec_t toks = spl_lex(src, fname);
printf("tokens got (%zu)\n", toks.size);
for (usize i = 0; i < toks.size; i++) {
const spl_tok_t *t = &toks.data[i];
printf("[%s] %.*s (%zu:%zu)\n", tok_type_names[t->type], (int)t->len, t->lexeme, t->line,
t->col);
}
vec_free(toks);
}
static void dump_ast(const char *src, const char *fname) {
spl_tok_vec_t toks = spl_lex(src, fname);
spl_ast_t ast;
spl_ast_init(&ast, &toks);
spl_ast_prase(&ast);
if (ast.parsed < 0) {
printf("parse failed, skip AST dump\n");
spl_ast_drop(&ast);
return;
}
spl_ast_valid(&ast);
spl_ast_dump(&ast, ast.root);
spl_ast_drop(&ast);
}
static void dump_sema(const char *src, const char *fname) {
spl_tok_vec_t toks = spl_lex(src, fname);
spl_ast_t ast;
spl_ast_init(&ast, &toks);
spl_ast_prase(&ast);
if (ast.parsed < 0) {
printf("parse failed, skip sema dump\n");
spl_ast_drop(&ast);
return;
}
spl_ast_valid(&ast);
spl_sema_t sema;
spl_sema_init(&sema);
sema.ast = &ast;
spl_sema_run(&sema);
spl_sema_check(&sema);
printf("Sema root_scope=%zu scopes=%zu errors=%d\n", sema.root_scope, sema.scopes.size,
sema.error_count);
for (usize i = 0; i < sema.scopes.size; i++) {
printf("scope[%zu] parent=%zu\n", i, sema.scopes.data[i].parent);
map_for(sema.scopes.data[i].symbols, mi) {
printf(" %s -> def#%zu\n", sema.scopes.data[i].symbols.data[mi].key,
sema.scopes.data[i].symbols.data[mi].val);
}
}
printf("TypeTable:\n");
for (usize i = 0; i < sema.type.type_table.size; i++) {
printf(" id#%zu type=", i);
spl_type_pure_dump(&sema.type, i);
printf("\n");
}
printf("DefTable:\n");
for (usize i = 0; i < sema.type.def_table.size; i++) {
printf(" def#%zu ", i);
spl_type_def_dump(&sema.type, i);
printf("\n");
}
spl_sema_drop(&sema);
spl_ast_drop(&ast);
}
static void dump_ir(const char *src, const char *fname) {
spl_tok_vec_t toks = spl_lex(src, fname);
spl_ast_t ast;
spl_ast_init(&ast, &toks);
spl_ast_prase(&ast);
if (ast.parsed < 0) {
printf("parse failed, skip IR\n");
spl_ast_drop(&ast);
return;
}
spl_ast_valid(&ast);
spl_sema_t sema;
spl_sema_init(&sema);
sema.ast = &ast;
spl_sema_run(&sema);
spl_sema_check(&sema);
if (sema.error_count) {
printf("sema errors=%d, skip IR\n", sema.error_count);
spl_sema_drop(&sema);
spl_ast_drop(&ast);
return;
}
spl_ast2ir_t a2ir;
spl_ast2ir_init(&a2ir, &sema);
spl_ast2ir_run(&a2ir);
if (a2ir.err_count)
printf("ast2ir errors=%d\n", a2ir.err_count);
spl_ir_dump(&a2ir.ir, &sema.type);
spl_ast2ir_drop(&a2ir);
spl_sema_drop(&sema);
spl_ast_drop(&ast);
}
static int cmd_dump(const char *flags, const char *path) {
long len;
char *src = read_file(path, &len);
if (!src)
return 1;
int do_tokens = strstr(flags, "tokens") != NULL || strcmp(flags, "all") == 0;
int do_ast = strstr(flags, "ast") != NULL || strcmp(flags, "all") == 0;
int do_sema = strstr(flags, "sema") != NULL || strcmp(flags, "all") == 0;
int do_ir = strstr(flags, "ir") != NULL || strcmp(flags, "all") == 0;
if (do_tokens)
dump_tokens(src, path);
if (do_ast)
dump_ast(src, path);
if (do_sema)
dump_sema(src, path);
if (do_ir)
dump_ir(src, path);
free(src);
return 0;
}
static spl_ast_node_t *ast_node_at(spl_ast_t *ast, spl_ast_node_ref_t ref) {
if (ref == 0 || ref >= ast->buckets.size)
return NULL;
return &ast->buckets.data[ref];
}
static int ast_line(spl_ast_t *ast, spl_ast_node_ref_t ref) {
spl_ast_node_t *n = ast_node_at(ast, ref);
return n ? n->loc.line : 0;
}
/* -g在 .sir 尾部追加 debug 段文本IR 行 + VAR 行spl_cli -g 读取 */
static void gen_debug_map(const char *outpath, spl_ast_t *ast, const spl_ir_t *ir,
const spl_ir2vm_t *ir2vm) {
FILE *f = fopen(outpath, "ab");
if (!f)
return;
fprintf(f, "SPLDBG\n");
for (usize i = 0; i < ir2vm->fdbg.size; i++) {
const spl_ir2vm_fdbg_t *fd = &ir2vm->fdbg.data[i];
if (fd->fid >= ir->funcs.size)
continue;
const spl_ir_func_t *fn = &ir->funcs.data[fd->fid];
const char *fname = fn->name ? fn->name : "?";
for (usize ref = 1; ref < fd->node_first_ip.size; ref++) {
usize ip = fd->node_first_ip.data[ref];
if (ip == (usize)-1)
continue;
const spl_ir_node_t *n = &fn->nodes.data[ref];
fprintf(f, "IR %zu %zu %d %s\n", ip, ref, ast_line(ast, n->src_ref),
spl_ir_kind_name(n->kind));
}
for (usize j = 0; j < fn->dbg_vars.size; j++) {
const spl_ir_dbg_var_t *dv = &fn->dbg_vars.data[j];
if (!dv->name)
continue;
fprintf(f, "VAR %s %s %zu %zu %d\n", fname, dv->name, dv->offset, dv->tid,
dv->is_param);
}
}
fclose(f);
}
static int compile_spl(const char *src, const char *fname, const char *outpath, int gen_debug) {
spl_tok_vec_t toks = spl_lex(src, fname);
spl_ast_t ast;
spl_ast_init(&ast, &toks);
spl_ast_prase(&ast);
if (ast.parsed < 0) {
printf("parse failed, no output\n");
spl_ast_drop(&ast);
return 1;
}
spl_ast_valid(&ast);
spl_sema_t sema;
spl_sema_init(&sema);
sema.ast = &ast;
spl_sema_run(&sema);
spl_sema_check(&sema);
if (sema.error_count) {
printf("sema errors=%d, no output\n", sema.error_count);
spl_sema_drop(&sema);
spl_ast_drop(&ast);
return 1;
}
spl_ast2ir_t a2ir;
spl_ast2ir_init(&a2ir, &sema);
spl_ast2ir_run(&a2ir);
if (a2ir.err_count) {
printf("ast2ir errors=%d, no output\n", a2ir.err_count);
spl_ast2ir_drop(&a2ir);
spl_sema_drop(&sema);
spl_ast_drop(&ast);
return 1;
}
spl_ir2vm_t ir2vm;
spl_ir2vm_init(&ir2vm, &a2ir.ir, &sema.type);
int rc = spl_ir2vm_run(&ir2vm, outpath);
if (gen_debug && rc == 0)
gen_debug_map(outpath, &ast, &a2ir.ir, &ir2vm);
spl_ir2vm_drop(&ir2vm);
spl_ast2ir_drop(&a2ir);
spl_sema_drop(&sema);
spl_ast_drop(&ast);
return rc ? 1 : 0;
}
int main(int argc, char **argv) {
if (argc < 2) {
LOG_FATAL("Usage: splc0 [--dump <flags>] <in> [out]");
return 1;
}
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0) {
LOG_INFO("splc0 <in> <out> compile (.spl -> .sir, stage B)");
LOG_INFO("splc0 --dump <flags> <file> dump: tokens,ast,sema,ir,all");
return 0;
}
int argi = 1;
if (argi >= argc) {
LOG_FATAL("Usage: splc0 [--dump <flags>] <in> [out]");
return 1;
}
if (strcmp(argv[argi], "--dump") == 0) {
if (argc < argi + 3) {
LOG_INFO("splc0: --dump need <flags> <file>");
return 1;
}
return cmd_dump(argv[argi + 1], argv[argi + 2]);
}
/* splc0 [-g] <in> <out> */
if (argc < argi + 2) {
LOG_FATAL("Usage: splc0 <in> <out> [-g] or splc0 --dump <flags> <file>\n");
return 1;
}
int gen_debug = 0;
if (strcmp(argv[argi], "-g") == 0) {
gen_debug = 1;
argi++;
}
if (argc < argi + 2) {
LOG_FATAL("Usage: splc0 <in> <out> [-g] or splc0 --dump <flags> <file>\n");
return 1;
}
long len;
char *src = read_file(argv[argi], &len);
if (!src)
return 1;
int rc = compile_spl(src, argv[argi], argv[argi + 1], gen_debug);
free(src);
return rc;
}