refactor code to prepare for multiple files
verbose compiler output is now behind --verbose flagmaster
parent
ef482ece7c
commit
55b8472374
|
@ -22,16 +22,16 @@ include_directories(
|
|||
)
|
||||
|
||||
set(ZIG_SOURCES
|
||||
"${CMAKE_SOURCE_DIR}/src/tokenizer.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/parser.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/analyze.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/codegen.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/buffer.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/error.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/main.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/parser.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/tokenizer.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/util.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/codegen.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/zig_llvm.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/os.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/util.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/zig_llvm.cpp"
|
||||
)
|
||||
|
||||
set(TEST_SOURCES
|
||||
|
|
|
@ -79,7 +79,9 @@ zig | C equivalent | Description
|
|||
```
|
||||
Root : many(TopLevelDecl) token(EOF)
|
||||
|
||||
TopLevelDecl : FnDef | ExternBlock | RootExportDecl
|
||||
TopLevelDecl : FnDef | ExternBlock | RootExportDecl | Use
|
||||
|
||||
Use : many(Directive) token(Use) token(String) token(Semicolon)
|
||||
|
||||
RootExportDecl : many(Directive) token(Export) token(Symbol) token(String) token(Semicolon)
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ if exists("b:current_syntax")
|
|||
finish
|
||||
endif
|
||||
|
||||
syn keyword zigKeyword fn return mut const extern unreachable export pub as
|
||||
syn keyword zigKeyword fn return mut const extern unreachable export pub as use
|
||||
syn keyword zigType bool i8 u8 i16 u16 i32 u32 i64 u64 isize usize f32 f64 f128 void
|
||||
|
||||
syn region zigCommentLine start="//" end="$" contains=zigTodo,@Spell
|
||||
|
@ -19,6 +19,15 @@ syn region zigCommentBlockDocNest matchgroup=zigCommentBlockDoc start="/\*" end=
|
|||
|
||||
syn keyword zigTodo contained TODO XXX
|
||||
|
||||
syn match zigEscapeError display contained /\\./
|
||||
syn match zigEscape display contained /\\\([nrt0\\'"]\|x\x\{2}\)/
|
||||
syn match zigEscapeUnicode display contained /\\\(u\x\{4}\|U\x\{8}\)/
|
||||
syn match zigEscapeUnicode display contained /\\u{\x\{1,6}}/
|
||||
syn match zigStringContinuation display contained /\\\n\s*/
|
||||
syn region zigString start=+b"+ skip=+\\\\\|\\"+ end=+"+ contains=zigEscape,zigEscapeError,zigStringContinuation
|
||||
syn region zigString start=+"+ skip=+\\\\\|\\"+ end=+"+ contains=zigEscape,zigEscapeUnicode,zigEscapeError,zigStringContinuation,@Spell
|
||||
syn region zigString start='b\?r\z(#*\)"' end='"\z1' contains=@Spell
|
||||
|
||||
let b:current_syntax = "zig"
|
||||
|
||||
hi def link zigKeyword Keyword
|
||||
|
@ -28,4 +37,8 @@ hi def link zigCommentLineDoc SpecialComment
|
|||
hi def link zigCommentBlock zigCommentLine
|
||||
hi def link zigCommentBlockDoc zigCommentLineDoc
|
||||
hi def link zigTodo Todo
|
||||
|
||||
hi def link zigStringContinuation Special
|
||||
hi def link zigString String
|
||||
hi def link zigEscape Special
|
||||
hi def link zigEscapeUnicode zigEscape
|
||||
hi def link zigEscapeError Error
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
use "libc.zig";
|
||||
|
||||
fn print_text() {
|
||||
puts("it works!");
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
#link("c")
|
||||
extern {
|
||||
fn puts(s: *mut u8) -> i32;
|
||||
fn exit(code: i32) -> unreachable;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
export executable "test";
|
||||
|
||||
use "libc.zig";
|
||||
use "foo.zig";
|
||||
|
||||
fn _start() -> unreachable {
|
||||
print_text();
|
||||
exit(0);
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
#include "mathtest.h"
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
printf("%d\n", add(42, 1137));
|
||||
return 0;
|
||||
}
|
105
src/analyze.cpp
105
src/analyze.cpp
|
@ -113,7 +113,7 @@ static void resolve_function_proto(CodeGen *g, AstNode *node) {
|
|||
resolve_type(g, node->data.fn_proto.return_type);
|
||||
}
|
||||
|
||||
static void preview_function_declarations(CodeGen *g, AstNode *node) {
|
||||
static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, AstNode *node) {
|
||||
switch (node->type) {
|
||||
case NodeTypeExternBlock:
|
||||
for (int i = 0; i < node->data.extern_block.directives->length; i += 1) {
|
||||
|
@ -139,6 +139,7 @@ static void preview_function_declarations(CodeGen *g, AstNode *node) {
|
|||
fn_table_entry->proto_node = fn_proto;
|
||||
fn_table_entry->is_extern = true;
|
||||
fn_table_entry->calling_convention = LLVMCCallConv;
|
||||
fn_table_entry->import_entry = import;
|
||||
g->fn_table.put(name, fn_table_entry);
|
||||
}
|
||||
break;
|
||||
|
@ -156,6 +157,7 @@ static void preview_function_declarations(CodeGen *g, AstNode *node) {
|
|||
node->codegen_node->data.fn_def_node.skip = true;
|
||||
} else {
|
||||
FnTableEntry *fn_table_entry = allocate<FnTableEntry>(1);
|
||||
fn_table_entry->import_entry = import;
|
||||
fn_table_entry->proto_node = proto_node;
|
||||
fn_table_entry->fn_def_node = node;
|
||||
fn_table_entry->internal_linkage = proto_node->data.fn_proto.visib_mod != FnProtoVisibModExport;
|
||||
|
@ -190,8 +192,8 @@ static void preview_function_declarations(CodeGen *g, AstNode *node) {
|
|||
} else {
|
||||
g->root_export_decl = node;
|
||||
|
||||
if (!g->out_name)
|
||||
g->out_name = &node->data.root_export_decl.name;
|
||||
if (!g->root_out_name)
|
||||
g->root_out_name = &node->data.root_export_decl.name;
|
||||
|
||||
Buf *out_type = &node->data.root_export_decl.type;
|
||||
OutType export_out_type;
|
||||
|
@ -209,6 +211,9 @@ static void preview_function_declarations(CodeGen *g, AstNode *node) {
|
|||
g->out_type = export_out_type;
|
||||
}
|
||||
break;
|
||||
case NodeTypeUse:
|
||||
zig_panic("TODO use");
|
||||
break;
|
||||
case NodeTypeDirective:
|
||||
case NodeTypeParamDecl:
|
||||
case NodeTypeFnProto:
|
||||
|
@ -346,6 +351,7 @@ static void analyze_expression(CodeGen *g, AstNode *node) {
|
|||
case NodeTypeRootExportDecl:
|
||||
case NodeTypeExternBlock:
|
||||
case NodeTypeFnDef:
|
||||
case NodeTypeUse:
|
||||
zig_unreachable();
|
||||
}
|
||||
}
|
||||
|
@ -377,9 +383,9 @@ static void analyze_top_level_declaration(CodeGen *g, AstNode *node) {
|
|||
|
||||
case NodeTypeRootExportDecl:
|
||||
case NodeTypeExternBlock:
|
||||
case NodeTypeUse:
|
||||
// already looked at these in the preview pass
|
||||
break;
|
||||
|
||||
case NodeTypeDirective:
|
||||
case NodeTypeParamDecl:
|
||||
case NodeTypeFnProto:
|
||||
|
@ -400,13 +406,13 @@ static void analyze_top_level_declaration(CodeGen *g, AstNode *node) {
|
|||
}
|
||||
}
|
||||
|
||||
static void analyze_root(CodeGen *g, AstNode *node) {
|
||||
static void analyze_root(CodeGen *g, ImportTableEntry *import, AstNode *node) {
|
||||
assert(node->type == NodeTypeRoot);
|
||||
|
||||
// find function declarations
|
||||
for (int i = 0; i < node->data.root.top_level_decls.length; i += 1) {
|
||||
AstNode *child = node->data.root.top_level_decls.at(i);
|
||||
preview_function_declarations(g, child);
|
||||
preview_function_declarations(g, import, child);
|
||||
}
|
||||
|
||||
for (int i = 0; i < node->data.root.top_level_decls.length; i += 1) {
|
||||
|
@ -414,7 +420,7 @@ static void analyze_root(CodeGen *g, AstNode *node) {
|
|||
analyze_top_level_declaration(g, child);
|
||||
}
|
||||
|
||||
if (!g->out_name) {
|
||||
if (!g->root_out_name) {
|
||||
add_node_error(g, node,
|
||||
buf_sprintf("missing export declaration and output name not provided"));
|
||||
} else if (g->out_type == OutTypeUnknown) {
|
||||
|
@ -423,88 +429,7 @@ static void analyze_root(CodeGen *g, AstNode *node) {
|
|||
}
|
||||
}
|
||||
|
||||
static void define_primitive_types(CodeGen *g) {
|
||||
{
|
||||
TypeTableEntry *entry = allocate<TypeTableEntry>(1);
|
||||
entry->type_ref = LLVMInt8Type();
|
||||
buf_init_from_str(&entry->name, "u8");
|
||||
entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), 8, 8,
|
||||
LLVMZigEncoding_DW_ATE_unsigned());
|
||||
g->type_table.put(&entry->name, entry);
|
||||
g->builtin_types.entry_u8 = entry;
|
||||
}
|
||||
{
|
||||
TypeTableEntry *entry = allocate<TypeTableEntry>(1);
|
||||
entry->type_ref = LLVMInt32Type();
|
||||
buf_init_from_str(&entry->name, "i32");
|
||||
entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), 32, 32,
|
||||
LLVMZigEncoding_DW_ATE_signed());
|
||||
g->type_table.put(&entry->name, entry);
|
||||
g->builtin_types.entry_i32 = entry;
|
||||
}
|
||||
{
|
||||
TypeTableEntry *entry = allocate<TypeTableEntry>(1);
|
||||
entry->type_ref = LLVMVoidType();
|
||||
buf_init_from_str(&entry->name, "void");
|
||||
entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), 0, 0,
|
||||
LLVMZigEncoding_DW_ATE_unsigned());
|
||||
g->type_table.put(&entry->name, entry);
|
||||
g->builtin_types.entry_void = entry;
|
||||
|
||||
// invalid types are void
|
||||
g->builtin_types.entry_invalid = entry;
|
||||
}
|
||||
{
|
||||
TypeTableEntry *entry = allocate<TypeTableEntry>(1);
|
||||
entry->type_ref = LLVMVoidType();
|
||||
buf_init_from_str(&entry->name, "unreachable");
|
||||
entry->di_type = g->builtin_types.entry_invalid->di_type;
|
||||
g->type_table.put(&entry->name, entry);
|
||||
g->builtin_types.entry_unreachable = entry;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void semantic_analyze(CodeGen *g) {
|
||||
LLVMInitializeAllTargets();
|
||||
LLVMInitializeAllTargetMCs();
|
||||
LLVMInitializeAllAsmPrinters();
|
||||
LLVMInitializeAllAsmParsers();
|
||||
LLVMInitializeNativeTarget();
|
||||
|
||||
g->is_native_target = true;
|
||||
char *native_triple = LLVMGetDefaultTargetTriple();
|
||||
|
||||
LLVMTargetRef target_ref;
|
||||
char *err_msg = nullptr;
|
||||
if (LLVMGetTargetFromTriple(native_triple, &target_ref, &err_msg)) {
|
||||
zig_panic("unable to get target from triple: %s", err_msg);
|
||||
}
|
||||
|
||||
char *native_cpu = LLVMZigGetHostCPUName();
|
||||
char *native_features = LLVMZigGetNativeFeatures();
|
||||
|
||||
LLVMCodeGenOptLevel opt_level = (g->build_type == CodeGenBuildTypeDebug) ?
|
||||
LLVMCodeGenLevelNone : LLVMCodeGenLevelAggressive;
|
||||
|
||||
LLVMRelocMode reloc_mode = g->is_static ? LLVMRelocStatic : LLVMRelocPIC;
|
||||
|
||||
g->target_machine = LLVMCreateTargetMachine(target_ref, native_triple,
|
||||
native_cpu, native_features, opt_level, reloc_mode, LLVMCodeModelDefault);
|
||||
|
||||
g->target_data_ref = LLVMGetTargetMachineData(g->target_machine);
|
||||
|
||||
|
||||
g->module = LLVMModuleCreateWithName("ZigModule");
|
||||
|
||||
g->pointer_size_bytes = LLVMPointerSize(g->target_data_ref);
|
||||
|
||||
g->builder = LLVMCreateBuilder();
|
||||
g->dbuilder = LLVMZigCreateDIBuilder(g->module, true);
|
||||
|
||||
|
||||
define_primitive_types(g);
|
||||
|
||||
analyze_root(g, g->root);
|
||||
void semantic_analyze(CodeGen *g, ImportTableEntry *import_table_entry) {
|
||||
analyze_root(g, import_table_entry, import_table_entry->root);
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
#define ZIG_ANALYZE_HPP
|
||||
|
||||
struct CodeGen;
|
||||
struct ImportTableEntry;
|
||||
|
||||
void semantic_analyze(CodeGen *g);
|
||||
void semantic_analyze(CodeGen *g, ImportTableEntry *entry);
|
||||
|
||||
#endif
|
||||
|
|
255
src/codegen.cpp
255
src/codegen.cpp
|
@ -11,26 +11,21 @@
|
|||
#include "os.hpp"
|
||||
#include "config.h"
|
||||
#include "error.hpp"
|
||||
|
||||
#include "semantic_info.hpp"
|
||||
#include "analyze.hpp"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
CodeGen *create_codegen(AstNode *root, Buf *in_full_path) {
|
||||
CodeGen *codegen_create(Buf *root_source_dir) {
|
||||
CodeGen *g = allocate<CodeGen>(1);
|
||||
g->root = root;
|
||||
g->fn_table.init(32);
|
||||
g->str_table.init(32);
|
||||
g->type_table.init(32);
|
||||
g->link_table.init(32);
|
||||
g->is_static = false;
|
||||
g->import_table.init(32);
|
||||
g->build_type = CodeGenBuildTypeDebug;
|
||||
g->strip_debug_symbols = false;
|
||||
g->out_name = nullptr;
|
||||
g->out_type = OutTypeUnknown;
|
||||
|
||||
os_path_split(in_full_path, &g->in_dir, &g->in_file);
|
||||
g->root_source_dir = root_source_dir;
|
||||
return g;
|
||||
}
|
||||
|
||||
|
@ -42,6 +37,10 @@ void codegen_set_is_static(CodeGen *g, bool is_static) {
|
|||
g->is_static = is_static;
|
||||
}
|
||||
|
||||
void codegen_set_verbose(CodeGen *g, bool verbose) {
|
||||
g->verbose = verbose;
|
||||
}
|
||||
|
||||
void codegen_set_strip(CodeGen *g, bool strip) {
|
||||
g->strip_debug_symbols = strip;
|
||||
}
|
||||
|
@ -51,7 +50,7 @@ void codegen_set_out_type(CodeGen *g, OutType out_type) {
|
|||
}
|
||||
|
||||
void codegen_set_out_name(CodeGen *g, Buf *out_name) {
|
||||
g->out_name = out_name;
|
||||
g->root_out_name = out_name;
|
||||
}
|
||||
|
||||
static LLVMValueRef gen_expr(CodeGen *g, AstNode *expr_node);
|
||||
|
@ -425,16 +424,17 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
|
|||
case NodeTypeBlock:
|
||||
case NodeTypeExternBlock:
|
||||
case NodeTypeDirective:
|
||||
case NodeTypeUse:
|
||||
zig_unreachable();
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
static void gen_block(CodeGen *g, AstNode *block_node, bool add_implicit_return) {
|
||||
static void gen_block(CodeGen *g, ImportTableEntry *import, AstNode *block_node, bool add_implicit_return) {
|
||||
assert(block_node->type == NodeTypeBlock);
|
||||
|
||||
LLVMZigDILexicalBlock *di_block = LLVMZigCreateLexicalBlock(g->dbuilder, g->block_scopes.last(),
|
||||
g->di_file, block_node->line + 1, block_node->column + 1);
|
||||
import->di_file, block_node->line + 1, block_node->column + 1);
|
||||
g->block_scopes.append(LLVMZigLexicalBlockToScope(di_block));
|
||||
|
||||
add_debug_source_node(g, block_node);
|
||||
|
@ -466,22 +466,11 @@ static LLVMZigDISubroutineType *create_di_function_type(CodeGen *g, AstNodeFnPro
|
|||
return LLVMZigCreateSubroutineType(g->dbuilder, di_file, types, types_len, 0);
|
||||
}
|
||||
|
||||
void code_gen(CodeGen *g) {
|
||||
static void do_code_gen(CodeGen *g) {
|
||||
assert(!g->errors.length);
|
||||
|
||||
Buf *producer = buf_sprintf("zig %s", ZIG_VERSION_STRING);
|
||||
bool is_optimized = g->build_type == CodeGenBuildTypeRelease;
|
||||
const char *flags = "";
|
||||
unsigned runtime_version = 0;
|
||||
g->compile_unit = LLVMZigCreateCompileUnit(g->dbuilder, LLVMZigLang_DW_LANG_C99(),
|
||||
buf_ptr(&g->in_file), buf_ptr(&g->in_dir),
|
||||
buf_ptr(producer), is_optimized, flags, runtime_version,
|
||||
"", 0, !g->strip_debug_symbols);
|
||||
|
||||
g->block_scopes.append(LLVMZigCompileUnitToScope(g->compile_unit));
|
||||
|
||||
g->di_file = LLVMZigCreateFile(g->dbuilder, buf_ptr(&g->in_file), buf_ptr(&g->in_dir));
|
||||
|
||||
|
||||
// Generate function prototypes
|
||||
auto it = g->fn_table.entry_iterator();
|
||||
|
@ -523,6 +512,7 @@ void code_gen(CodeGen *g) {
|
|||
// Generate function definitions.
|
||||
for (int i = 0; i < g->fn_defs.length; i += 1) {
|
||||
FnTableEntry *fn_table_entry = g->fn_defs.at(i);
|
||||
ImportTableEntry *import = fn_table_entry->import_entry;
|
||||
AstNode *fn_def_node = fn_table_entry->fn_def_node;
|
||||
LLVMValueRef fn = fn_table_entry->fn_value;
|
||||
g->cur_fn = fn_table_entry;
|
||||
|
@ -532,14 +522,15 @@ void code_gen(CodeGen *g) {
|
|||
AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;
|
||||
|
||||
// Add debug info.
|
||||
LLVMZigDIScope *fn_scope = LLVMZigFileToScope(g->di_file);
|
||||
LLVMZigDIScope *fn_scope = LLVMZigFileToScope(import->di_file);
|
||||
unsigned line_number = fn_def_node->line + 1;
|
||||
unsigned scope_line = line_number;
|
||||
bool is_definition = true;
|
||||
unsigned flags = 0;
|
||||
bool is_optimized = g->build_type == CodeGenBuildTypeRelease;
|
||||
LLVMZigDISubprogram *subprogram = LLVMZigCreateFunction(g->dbuilder,
|
||||
fn_scope, buf_ptr(&fn_proto->name), "", g->di_file, line_number,
|
||||
create_di_function_type(g, fn_proto, g->di_file), fn_table_entry->internal_linkage,
|
||||
fn_scope, buf_ptr(&fn_proto->name), "", import->di_file, line_number,
|
||||
create_di_function_type(g, fn_proto, import->di_file), fn_table_entry->internal_linkage,
|
||||
is_definition, scope_line, flags, is_optimized, fn);
|
||||
|
||||
g->block_scopes.append(LLVMZigSubprogramToScope(subprogram));
|
||||
|
@ -555,7 +546,7 @@ void code_gen(CodeGen *g) {
|
|||
LLVMGetParams(fn, codegen_fn_def->params);
|
||||
|
||||
bool add_implicit_return = codegen_fn_def->add_implicit_return;
|
||||
gen_block(g, fn_def_node->data.fn_def.body, add_implicit_return);
|
||||
gen_block(g, import, fn_def_node->data.fn_def.body, add_implicit_return);
|
||||
|
||||
g->block_scopes.pop();
|
||||
}
|
||||
|
@ -563,7 +554,9 @@ void code_gen(CodeGen *g) {
|
|||
|
||||
LLVMZigDIBuilderFinalize(g->dbuilder);
|
||||
|
||||
LLVMDumpModule(g->module);
|
||||
if (g->verbose) {
|
||||
LLVMDumpModule(g->module);
|
||||
}
|
||||
|
||||
// in release mode, we're sooooo confident that we've generated correct ir,
|
||||
// that we skip the verify module step in order to get better performance.
|
||||
|
@ -573,13 +566,168 @@ void code_gen(CodeGen *g) {
|
|||
#endif
|
||||
}
|
||||
|
||||
void code_gen_optimize(CodeGen *g) {
|
||||
LLVMZigOptimizeModule(g->target_machine, g->module);
|
||||
LLVMDumpModule(g->module);
|
||||
static void define_primitive_types(CodeGen *g) {
|
||||
{
|
||||
TypeTableEntry *entry = allocate<TypeTableEntry>(1);
|
||||
entry->type_ref = LLVMInt8Type();
|
||||
buf_init_from_str(&entry->name, "u8");
|
||||
entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), 8, 8,
|
||||
LLVMZigEncoding_DW_ATE_unsigned());
|
||||
g->type_table.put(&entry->name, entry);
|
||||
g->builtin_types.entry_u8 = entry;
|
||||
}
|
||||
{
|
||||
TypeTableEntry *entry = allocate<TypeTableEntry>(1);
|
||||
entry->type_ref = LLVMInt32Type();
|
||||
buf_init_from_str(&entry->name, "i32");
|
||||
entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), 32, 32,
|
||||
LLVMZigEncoding_DW_ATE_signed());
|
||||
g->type_table.put(&entry->name, entry);
|
||||
g->builtin_types.entry_i32 = entry;
|
||||
}
|
||||
{
|
||||
TypeTableEntry *entry = allocate<TypeTableEntry>(1);
|
||||
entry->type_ref = LLVMVoidType();
|
||||
buf_init_from_str(&entry->name, "void");
|
||||
entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), 0, 0,
|
||||
LLVMZigEncoding_DW_ATE_unsigned());
|
||||
g->type_table.put(&entry->name, entry);
|
||||
g->builtin_types.entry_void = entry;
|
||||
|
||||
// invalid types are void
|
||||
g->builtin_types.entry_invalid = entry;
|
||||
}
|
||||
{
|
||||
TypeTableEntry *entry = allocate<TypeTableEntry>(1);
|
||||
entry->type_ref = LLVMVoidType();
|
||||
buf_init_from_str(&entry->name, "unreachable");
|
||||
entry->di_type = g->builtin_types.entry_invalid->di_type;
|
||||
g->type_table.put(&entry->name, entry);
|
||||
g->builtin_types.entry_unreachable = entry;
|
||||
}
|
||||
}
|
||||
|
||||
ZigList<ErrorMsg> *codegen_error_messages(CodeGen *g) {
|
||||
return &g->errors;
|
||||
|
||||
|
||||
static void init(CodeGen *g, Buf *source_path) {
|
||||
LLVMInitializeAllTargets();
|
||||
LLVMInitializeAllTargetMCs();
|
||||
LLVMInitializeAllAsmPrinters();
|
||||
LLVMInitializeAllAsmParsers();
|
||||
LLVMInitializeNativeTarget();
|
||||
|
||||
g->is_native_target = true;
|
||||
char *native_triple = LLVMGetDefaultTargetTriple();
|
||||
|
||||
LLVMTargetRef target_ref;
|
||||
char *err_msg = nullptr;
|
||||
if (LLVMGetTargetFromTriple(native_triple, &target_ref, &err_msg)) {
|
||||
zig_panic("unable to get target from triple: %s", err_msg);
|
||||
}
|
||||
|
||||
char *native_cpu = LLVMZigGetHostCPUName();
|
||||
char *native_features = LLVMZigGetNativeFeatures();
|
||||
|
||||
LLVMCodeGenOptLevel opt_level = (g->build_type == CodeGenBuildTypeDebug) ?
|
||||
LLVMCodeGenLevelNone : LLVMCodeGenLevelAggressive;
|
||||
|
||||
LLVMRelocMode reloc_mode = g->is_static ? LLVMRelocStatic : LLVMRelocPIC;
|
||||
|
||||
g->target_machine = LLVMCreateTargetMachine(target_ref, native_triple,
|
||||
native_cpu, native_features, opt_level, reloc_mode, LLVMCodeModelDefault);
|
||||
|
||||
g->target_data_ref = LLVMGetTargetMachineData(g->target_machine);
|
||||
|
||||
|
||||
g->module = LLVMModuleCreateWithName("ZigModule");
|
||||
|
||||
g->pointer_size_bytes = LLVMPointerSize(g->target_data_ref);
|
||||
|
||||
g->builder = LLVMCreateBuilder();
|
||||
g->dbuilder = LLVMZigCreateDIBuilder(g->module, true);
|
||||
|
||||
|
||||
define_primitive_types(g);
|
||||
|
||||
Buf *producer = buf_sprintf("zig %s", ZIG_VERSION_STRING);
|
||||
bool is_optimized = g->build_type == CodeGenBuildTypeRelease;
|
||||
const char *flags = "";
|
||||
unsigned runtime_version = 0;
|
||||
g->compile_unit = LLVMZigCreateCompileUnit(g->dbuilder, LLVMZigLang_DW_LANG_C99(),
|
||||
buf_ptr(source_path), buf_ptr(g->root_source_dir),
|
||||
buf_ptr(producer), is_optimized, flags, runtime_version,
|
||||
"", 0, !g->strip_debug_symbols);
|
||||
|
||||
|
||||
}
|
||||
|
||||
void codegen_add_code(CodeGen *g, Buf *source_path, Buf *source_code) {
|
||||
if (!g->initialized) {
|
||||
g->initialized = true;
|
||||
init(g, source_path);
|
||||
}
|
||||
|
||||
Buf full_path = BUF_INIT;
|
||||
os_path_join(g->root_source_dir, source_path, &full_path);
|
||||
|
||||
Buf dirname = BUF_INIT;
|
||||
Buf basename = BUF_INIT;
|
||||
os_path_split(&full_path, &dirname, &basename);
|
||||
|
||||
if (g->verbose) {
|
||||
fprintf(stderr, "\nOriginal Source (%s):\n", buf_ptr(source_path));
|
||||
fprintf(stderr, "----------------\n");
|
||||
fprintf(stderr, "%s\n", buf_ptr(source_code));
|
||||
|
||||
fprintf(stderr, "\nTokens:\n");
|
||||
fprintf(stderr, "---------\n");
|
||||
}
|
||||
|
||||
ZigList<Token> *tokens = tokenize(source_code);
|
||||
|
||||
if (g->verbose) {
|
||||
print_tokens(source_code, tokens);
|
||||
|
||||
fprintf(stderr, "\nAST:\n");
|
||||
fprintf(stderr, "------\n");
|
||||
}
|
||||
|
||||
ImportTableEntry *import_entry = allocate<ImportTableEntry>(1);
|
||||
import_entry->root = ast_parse(source_code, tokens);
|
||||
assert(import_entry->root);
|
||||
if (g->verbose) {
|
||||
ast_print(import_entry->root, 0);
|
||||
|
||||
fprintf(stderr, "\nSemantic Analysis:\n");
|
||||
fprintf(stderr, "--------------------\n");
|
||||
}
|
||||
|
||||
import_entry->path = source_path;
|
||||
import_entry->di_file = LLVMZigCreateFile(g->dbuilder, buf_ptr(&basename), buf_ptr(&dirname));
|
||||
g->import_table.put(source_path, import_entry);
|
||||
|
||||
semantic_analyze(g, import_entry);
|
||||
|
||||
if (g->errors.length == 0) {
|
||||
if (g->verbose) {
|
||||
fprintf(stderr, "OK\n");
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < g->errors.length; i += 1) {
|
||||
ErrorMsg *err = &g->errors.at(i);
|
||||
fprintf(stderr, "Error: Line %d, column %d: %s\n",
|
||||
err->line_start + 1, err->column_start + 1,
|
||||
buf_ptr(err->msg));
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (g->verbose) {
|
||||
fprintf(stderr, "\nCode Generation:\n");
|
||||
fprintf(stderr, "------------------\n");
|
||||
}
|
||||
|
||||
do_code_gen(g);
|
||||
}
|
||||
|
||||
static Buf *to_c_type(CodeGen *g, AstNode *type_node) {
|
||||
|
@ -601,15 +749,15 @@ static Buf *to_c_type(CodeGen *g, AstNode *type_node) {
|
|||
}
|
||||
|
||||
static void generate_h_file(CodeGen *g) {
|
||||
Buf *h_file_out_path = buf_sprintf("%s.h", buf_ptr(g->out_name));
|
||||
Buf *h_file_out_path = buf_sprintf("%s.h", buf_ptr(g->root_out_name));
|
||||
FILE *out_h = fopen(buf_ptr(h_file_out_path), "wb");
|
||||
if (!out_h)
|
||||
zig_panic("unable to open %s: %s", buf_ptr(h_file_out_path), strerror(errno));
|
||||
|
||||
Buf *export_macro = buf_sprintf("%s_EXPORT", buf_ptr(g->out_name));
|
||||
Buf *export_macro = buf_sprintf("%s_EXPORT", buf_ptr(g->root_out_name));
|
||||
buf_upcase(export_macro);
|
||||
|
||||
Buf *extern_c_macro = buf_sprintf("%s_EXTERN_C", buf_ptr(g->out_name));
|
||||
Buf *extern_c_macro = buf_sprintf("%s_EXTERN_C", buf_ptr(g->root_out_name));
|
||||
buf_upcase(extern_c_macro);
|
||||
|
||||
Buf h_buf = BUF_INIT;
|
||||
|
@ -644,7 +792,8 @@ static void generate_h_file(CodeGen *g) {
|
|||
}
|
||||
}
|
||||
|
||||
Buf *ifdef_dance_name = buf_sprintf("%s_%s_H", buf_ptr(g->out_name), buf_ptr(g->out_name));
|
||||
Buf *ifdef_dance_name = buf_sprintf("%s_%s_H",
|
||||
buf_ptr(g->root_out_name), buf_ptr(g->root_out_name));
|
||||
buf_upcase(ifdef_dance_name);
|
||||
|
||||
fprintf(out_h, "#ifndef %s\n", buf_ptr(ifdef_dance_name));
|
||||
|
@ -677,9 +826,27 @@ static void generate_h_file(CodeGen *g) {
|
|||
zig_panic("unable to close h file: %s", strerror(errno));
|
||||
}
|
||||
|
||||
void code_gen_link(CodeGen *g, const char *out_file) {
|
||||
void codegen_link(CodeGen *g, const char *out_file) {
|
||||
bool is_optimized = (g->build_type == CodeGenBuildTypeRelease);
|
||||
if (is_optimized) {
|
||||
if (g->verbose) {
|
||||
fprintf(stderr, "\nOptimization:\n");
|
||||
fprintf(stderr, "---------------\n");
|
||||
}
|
||||
|
||||
LLVMZigOptimizeModule(g->target_machine, g->module);
|
||||
|
||||
if (g->verbose) {
|
||||
LLVMDumpModule(g->module);
|
||||
}
|
||||
}
|
||||
if (g->verbose) {
|
||||
fprintf(stderr, "\nLink:\n");
|
||||
fprintf(stderr, "-------\n");
|
||||
}
|
||||
|
||||
if (!out_file) {
|
||||
out_file = buf_ptr(g->out_name);
|
||||
out_file = buf_ptr(g->root_out_name);
|
||||
}
|
||||
|
||||
Buf out_file_o = BUF_INIT;
|
||||
|
@ -728,8 +895,8 @@ void code_gen_link(CodeGen *g, const char *out_file) {
|
|||
|
||||
if (g->out_type == OutTypeLib) {
|
||||
Buf *out_lib_so = buf_sprintf("lib%s.so.%d.%d.%d",
|
||||
buf_ptr(g->out_name), g->version_major, g->version_minor, g->version_patch);
|
||||
Buf *soname = buf_sprintf("lib%s.so.%d", buf_ptr(g->out_name), g->version_major);
|
||||
buf_ptr(g->root_out_name), g->version_major, g->version_minor, g->version_patch);
|
||||
Buf *soname = buf_sprintf("lib%s.so.%d", buf_ptr(g->root_out_name), g->version_major);
|
||||
args.append("-shared");
|
||||
args.append("-soname");
|
||||
args.append(buf_ptr(soname));
|
||||
|
@ -756,4 +923,8 @@ void code_gen_link(CodeGen *g, const char *out_file) {
|
|||
if (g->out_type == OutTypeLib) {
|
||||
generate_h_file(g);
|
||||
}
|
||||
|
||||
if (g->verbose) {
|
||||
fprintf(stderr, "OK\n");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ struct ErrorMsg {
|
|||
};
|
||||
|
||||
|
||||
CodeGen *create_codegen(AstNode *root, Buf *in_file);
|
||||
CodeGen *codegen_create(Buf *root_source_dir);
|
||||
|
||||
enum CodeGenBuildType {
|
||||
CodeGenBuildTypeDebug,
|
||||
|
@ -38,15 +38,12 @@ enum CodeGenBuildType {
|
|||
void codegen_set_build_type(CodeGen *codegen, CodeGenBuildType build_type);
|
||||
void codegen_set_is_static(CodeGen *codegen, bool is_static);
|
||||
void codegen_set_strip(CodeGen *codegen, bool strip);
|
||||
void codegen_set_verbose(CodeGen *codegen, bool verbose);
|
||||
void codegen_set_out_type(CodeGen *codegen, OutType out_type);
|
||||
void codegen_set_out_name(CodeGen *codegen, Buf *out_name);
|
||||
|
||||
void code_gen_optimize(CodeGen *g);
|
||||
void codegen_add_code(CodeGen *g, Buf *source_path, Buf *source_code);
|
||||
|
||||
void code_gen(CodeGen *g);
|
||||
|
||||
void code_gen_link(CodeGen *g, const char *out_file);
|
||||
|
||||
ZigList<ErrorMsg> *codegen_error_messages(CodeGen *g);
|
||||
void codegen_link(CodeGen *g, const char *out_file);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -5,6 +5,7 @@ const char *err_str(int err) {
|
|||
case ErrorNone: return "(no error)";
|
||||
case ErrorNoMem: return "out of memory";
|
||||
case ErrorInvalidFormat: return "invalid format";
|
||||
case ErrorSemanticAnalyzeFail: return "semantic analyze failed";
|
||||
}
|
||||
return "(invalid error)";
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ enum Error {
|
|||
ErrorNone,
|
||||
ErrorNoMem,
|
||||
ErrorInvalidFormat,
|
||||
ErrorSemanticAnalyzeFail,
|
||||
};
|
||||
|
||||
const char *err_str(int err);
|
||||
|
|
172
src/main.cpp
172
src/main.cpp
|
@ -6,25 +6,11 @@
|
|||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "util.hpp"
|
||||
#include "list.hpp"
|
||||
#include "buffer.hpp"
|
||||
#include "parser.hpp"
|
||||
#include "tokenizer.hpp"
|
||||
#include "error.hpp"
|
||||
#include "codegen.hpp"
|
||||
#include "analyze.hpp"
|
||||
#include "os.hpp"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
static int usage(const char *arg0) {
|
||||
fprintf(stderr, "Usage: %s [command] [options] target\n"
|
||||
|
@ -38,6 +24,7 @@ static int usage(const char *arg0) {
|
|||
" --export [exe|lib|obj] override output type\n"
|
||||
" --name [name] override output name\n"
|
||||
" --output [file] override destination path\n"
|
||||
" --verbose turn on compiler debug output\n"
|
||||
, arg0);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
@ -47,98 +34,47 @@ static int version(void) {
|
|||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
static Buf *fetch_file(FILE *f) {
|
||||
int fd = fileno(f);
|
||||
struct stat st;
|
||||
if (fstat(fd, &st))
|
||||
zig_panic("unable to stat file: %s", strerror(errno));
|
||||
off_t big_size = st.st_size;
|
||||
if (big_size > INT_MAX)
|
||||
zig_panic("file too big");
|
||||
int size = (int)big_size;
|
||||
struct Build {
|
||||
const char *in_file;
|
||||
const char *out_file;
|
||||
bool release;
|
||||
bool strip;
|
||||
bool is_static;
|
||||
OutType out_type;
|
||||
const char *out_name;
|
||||
bool verbose;
|
||||
};
|
||||
|
||||
Buf *buf = buf_alloc_fixed(size);
|
||||
size_t amt_read = fread(buf_ptr(buf), 1, buf_len(buf), f);
|
||||
if (amt_read != (size_t)buf_len(buf))
|
||||
zig_panic("error reading: %s", strerror(errno));
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int build(const char *arg0, const char *in_file, const char *out_file, bool release,
|
||||
bool strip, bool is_static, OutType out_type, char *out_name)
|
||||
{
|
||||
static char cur_dir[1024];
|
||||
|
||||
if (!in_file)
|
||||
static int build(const char *arg0, Build *b) {
|
||||
if (!b->in_file)
|
||||
return usage(arg0);
|
||||
|
||||
FILE *in_f;
|
||||
if (strcmp(in_file, "-") == 0) {
|
||||
in_f = stdin;
|
||||
char *result = getcwd(cur_dir, sizeof(cur_dir));
|
||||
if (!result)
|
||||
zig_panic("unable to get current working directory: %s", strerror(errno));
|
||||
Buf in_file_buf = BUF_INIT;
|
||||
buf_init_from_str(&in_file_buf, b->in_file);
|
||||
|
||||
Buf root_source_dir = BUF_INIT;
|
||||
Buf root_source_code = BUF_INIT;
|
||||
Buf root_source_name = BUF_INIT;
|
||||
if (buf_eql_str(&in_file_buf, "-")) {
|
||||
os_get_cwd(&root_source_dir);
|
||||
os_fetch_file(stdin, &root_source_code);
|
||||
buf_init_from_str(&root_source_name, "");
|
||||
} else {
|
||||
in_f = fopen(in_file, "rb");
|
||||
if (!in_f)
|
||||
zig_panic("unable to open %s for reading: %s\n", in_file, strerror(errno));
|
||||
os_path_split(&in_file_buf, &root_source_dir, &root_source_name);
|
||||
os_fetch_file_path(buf_create_from_str(b->in_file), &root_source_code);
|
||||
}
|
||||
|
||||
fprintf(stderr, "Original source:\n");
|
||||
fprintf(stderr, "----------------\n");
|
||||
Buf *in_data = fetch_file(in_f);
|
||||
fprintf(stderr, "%s\n", buf_ptr(in_data));
|
||||
|
||||
fprintf(stderr, "\nTokens:\n");
|
||||
fprintf(stderr, "---------\n");
|
||||
ZigList<Token> *tokens = tokenize(in_data);
|
||||
print_tokens(in_data, tokens);
|
||||
|
||||
fprintf(stderr, "\nAST:\n");
|
||||
fprintf(stderr, "------\n");
|
||||
AstNode *root = ast_parse(in_data, tokens);
|
||||
assert(root);
|
||||
ast_print(root, 0);
|
||||
|
||||
fprintf(stderr, "\nSemantic Analysis:\n");
|
||||
fprintf(stderr, "--------------------\n");
|
||||
CodeGen *codegen = create_codegen(root, buf_create_from_str(in_file));
|
||||
codegen_set_build_type(codegen, release ? CodeGenBuildTypeRelease : CodeGenBuildTypeDebug);
|
||||
codegen_set_strip(codegen, strip);
|
||||
codegen_set_is_static(codegen, is_static);
|
||||
if (out_type != OutTypeUnknown)
|
||||
codegen_set_out_type(codegen, out_type);
|
||||
if (out_name)
|
||||
codegen_set_out_name(codegen, buf_create_from_str(out_name));
|
||||
semantic_analyze(codegen);
|
||||
ZigList<ErrorMsg> *errors = codegen_error_messages(codegen);
|
||||
if (errors->length == 0) {
|
||||
fprintf(stderr, "OK\n");
|
||||
} else {
|
||||
for (int i = 0; i < errors->length; i += 1) {
|
||||
ErrorMsg *err = &errors->at(i);
|
||||
fprintf(stderr, "Error: Line %d, column %d: %s\n",
|
||||
err->line_start + 1, err->column_start + 1,
|
||||
buf_ptr(err->msg));
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
fprintf(stderr, "\nCode Generation:\n");
|
||||
fprintf(stderr, "------------------\n");
|
||||
code_gen(codegen);
|
||||
|
||||
if (release) {
|
||||
fprintf(stderr, "\nOptimization:\n");
|
||||
fprintf(stderr, "---------------\n");
|
||||
code_gen_optimize(codegen);
|
||||
}
|
||||
|
||||
fprintf(stderr, "\nLink:\n");
|
||||
fprintf(stderr, "-------\n");
|
||||
code_gen_link(codegen, out_file);
|
||||
fprintf(stderr, "OK\n");
|
||||
CodeGen *g = codegen_create(&root_source_dir);
|
||||
codegen_set_build_type(g, b->release ? CodeGenBuildTypeRelease : CodeGenBuildTypeDebug);
|
||||
codegen_set_strip(g, b->strip);
|
||||
codegen_set_is_static(g, b->is_static);
|
||||
if (b->out_type != OutTypeUnknown)
|
||||
codegen_set_out_type(g, b->out_type);
|
||||
if (b->out_name)
|
||||
codegen_set_out_name(g, buf_create_from_str(b->out_name));
|
||||
codegen_set_verbose(g, b->verbose);
|
||||
codegen_add_code(g, &root_source_name, &root_source_code);
|
||||
codegen_link(g, b->out_file);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -151,43 +87,39 @@ enum Cmd {
|
|||
|
||||
int main(int argc, char **argv) {
|
||||
char *arg0 = argv[0];
|
||||
char *in_file = NULL;
|
||||
char *out_file = NULL;
|
||||
bool release = false;
|
||||
bool strip = false;
|
||||
bool is_static = false;
|
||||
|
||||
OutType out_type = OutTypeUnknown;
|
||||
char *out_name = NULL;
|
||||
|
||||
Build b = {0};
|
||||
Cmd cmd = CmdNone;
|
||||
|
||||
for (int i = 1; i < argc; i += 1) {
|
||||
char *arg = argv[i];
|
||||
if (arg[0] == '-' && arg[1] == '-') {
|
||||
if (strcmp(arg, "--release") == 0) {
|
||||
release = true;
|
||||
b.release = true;
|
||||
} else if (strcmp(arg, "--strip") == 0) {
|
||||
strip = true;
|
||||
b.strip = true;
|
||||
} else if (strcmp(arg, "--static") == 0) {
|
||||
is_static = true;
|
||||
b.is_static = true;
|
||||
} else if (strcmp(arg, "--verbose") == 0) {
|
||||
b.verbose = true;
|
||||
} else if (i + 1 >= argc) {
|
||||
return usage(arg0);
|
||||
} else {
|
||||
i += 1;
|
||||
if (strcmp(arg, "--output") == 0) {
|
||||
out_file = argv[i];
|
||||
b.out_file = argv[i];
|
||||
} else if (strcmp(arg, "--export") == 0) {
|
||||
if (strcmp(argv[i], "exe") == 0) {
|
||||
out_type = OutTypeExe;
|
||||
b.out_type = OutTypeExe;
|
||||
} else if (strcmp(argv[i], "lib") == 0) {
|
||||
out_type = OutTypeLib;
|
||||
b.out_type = OutTypeLib;
|
||||
} else if (strcmp(argv[i], "obj") == 0) {
|
||||
out_type = OutTypeObj;
|
||||
b.out_type = OutTypeObj;
|
||||
} else {
|
||||
return usage(arg0);
|
||||
}
|
||||
} else if (strcmp(arg, "--name") == 0) {
|
||||
out_name = argv[i];
|
||||
b.out_name = argv[i];
|
||||
} else {
|
||||
return usage(arg0);
|
||||
}
|
||||
|
@ -206,8 +138,8 @@ int main(int argc, char **argv) {
|
|||
case CmdNone:
|
||||
zig_unreachable();
|
||||
case CmdBuild:
|
||||
if (!in_file) {
|
||||
in_file = arg;
|
||||
if (!b.in_file) {
|
||||
b.in_file = arg;
|
||||
} else {
|
||||
return usage(arg0);
|
||||
}
|
||||
|
@ -222,7 +154,7 @@ int main(int argc, char **argv) {
|
|||
case CmdNone:
|
||||
return usage(arg0);
|
||||
case CmdBuild:
|
||||
return build(arg0, in_file, out_file, release, strip, is_static, out_type, out_name);
|
||||
return build(arg0, &b);
|
||||
case CmdVersion:
|
||||
return version();
|
||||
}
|
||||
|
|
55
src/os.cpp
55
src/os.cpp
|
@ -13,8 +13,8 @@
|
|||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
|
||||
void os_spawn_process(const char *exe, ZigList<const char *> &args, bool detached) {
|
||||
pid_t pid = fork();
|
||||
|
@ -37,7 +37,7 @@ void os_spawn_process(const char *exe, ZigList<const char *> &args, bool detache
|
|||
zig_panic("execvp failed: %s", strerror(errno));
|
||||
}
|
||||
|
||||
static void read_all_fd(int fd, Buf *out_buf) {
|
||||
static void read_all_fd_stream(int fd, Buf *out_buf) {
|
||||
static const ssize_t buf_size = 0x2000;
|
||||
buf_resize(out_buf, buf_size);
|
||||
ssize_t actual_buf_len = 0;
|
||||
|
@ -72,6 +72,12 @@ void os_path_split(Buf *full_path, Buf *out_dirname, Buf *out_basename) {
|
|||
buf_init_from_buf(out_basename, full_path);
|
||||
}
|
||||
|
||||
void os_path_join(Buf *dirname, Buf *basename, Buf *out_full_path) {
|
||||
buf_init_from_buf(out_full_path, dirname);
|
||||
buf_append_char(out_full_path, '/');
|
||||
buf_append_buf(out_full_path, basename);
|
||||
}
|
||||
|
||||
void os_exec_process(const char *exe, ZigList<const char *> &args,
|
||||
int *return_code, Buf *out_stderr, Buf *out_stdout)
|
||||
{
|
||||
|
@ -117,8 +123,8 @@ void os_exec_process(const char *exe, ZigList<const char *> &args,
|
|||
|
||||
waitpid(pid, return_code, 0);
|
||||
|
||||
read_all_fd(stdout_pipe[0], out_stdout);
|
||||
read_all_fd(stderr_pipe[0], out_stderr);
|
||||
read_all_fd_stream(stdout_pipe[0], out_stdout);
|
||||
read_all_fd_stream(stderr_pipe[0], out_stderr);
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -133,3 +139,44 @@ void os_write_file(Buf *full_path, Buf *contents) {
|
|||
if (close(fd) == -1)
|
||||
zig_panic("close failed");
|
||||
}
|
||||
|
||||
int os_fetch_file(FILE *f, Buf *out_contents) {
|
||||
int fd = fileno(f);
|
||||
struct stat st;
|
||||
if (fstat(fd, &st))
|
||||
zig_panic("unable to stat file: %s", strerror(errno));
|
||||
off_t big_size = st.st_size;
|
||||
if (big_size > INT_MAX)
|
||||
zig_panic("file too big");
|
||||
int size = (int)big_size;
|
||||
|
||||
buf_resize(out_contents, size);
|
||||
ssize_t ret = read(fd, buf_ptr(out_contents), size);
|
||||
|
||||
if (ret != size)
|
||||
zig_panic("unable to read file: %s", strerror(errno));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int os_fetch_file_path(Buf *full_path, Buf *out_contents) {
|
||||
FILE *f = fopen(buf_ptr(full_path), "rb");
|
||||
if (!f)
|
||||
zig_panic("unable to open %s: %s\n", buf_ptr(full_path), strerror(errno));
|
||||
int result = os_fetch_file(f, out_contents);
|
||||
fclose(f);
|
||||
return result;
|
||||
}
|
||||
|
||||
int os_get_cwd(Buf *out_cwd) {
|
||||
int err = ERANGE;
|
||||
buf_resize(out_cwd, 512);
|
||||
while (err == ERANGE) {
|
||||
buf_resize(out_cwd, buf_len(out_cwd) * 2);
|
||||
err = getcwd(buf_ptr(out_cwd), buf_len(out_cwd)) ? 0 : errno;
|
||||
}
|
||||
if (err)
|
||||
zig_panic("unable to get cwd: %s", strerror(err));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -11,13 +11,22 @@
|
|||
#include "list.hpp"
|
||||
#include "buffer.hpp"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
void os_spawn_process(const char *exe, ZigList<const char *> &args, bool detached);
|
||||
void os_exec_process(const char *exe, ZigList<const char *> &args,
|
||||
int *return_code, Buf *out_stderr, Buf *out_stdout);
|
||||
|
||||
void os_path_split(Buf *full_path, Buf *out_dirname, Buf *out_basename);
|
||||
void os_path_join(Buf *dirname, Buf *basename, Buf *out_full_path);
|
||||
|
||||
void os_write_file(Buf *full_path, Buf *contents);
|
||||
|
||||
|
||||
int os_fetch_file(FILE *file, Buf *out_contents);
|
||||
int os_fetch_file_path(Buf *full_path, Buf *out_contents);
|
||||
|
||||
int os_get_cwd(Buf *out_cwd);
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
@ -100,6 +100,8 @@ const char *node_type_str(NodeType node_type) {
|
|||
return "Symbol";
|
||||
case NodeTypePrefixOpExpr:
|
||||
return "PrefixOpExpr";
|
||||
case NodeTypeUse:
|
||||
return "Use";
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
@ -241,6 +243,9 @@ void ast_print(AstNode *node, int indent) {
|
|||
fprintf(stderr, "PrimaryExpr Symbol %s\n",
|
||||
buf_ptr(&node->data.symbol));
|
||||
break;
|
||||
case NodeTypeUse:
|
||||
fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(&node->data.use.path));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1231,7 +1236,36 @@ static AstNode *ast_parse_root_export_decl(ParseContext *pc, int *token_index, b
|
|||
}
|
||||
|
||||
/*
|
||||
TopLevelDecl : FnDef | ExternBlock | RootExportDecl
|
||||
Use : many(Directive) token(Use) token(String) token(Semicolon)
|
||||
*/
|
||||
static AstNode *ast_parse_use(ParseContext *pc, int *token_index, bool mandatory) {
|
||||
assert(mandatory == false);
|
||||
|
||||
Token *use_kw = &pc->tokens->at(*token_index);
|
||||
if (use_kw->id != TokenIdKeywordUse)
|
||||
return nullptr;
|
||||
*token_index += 1;
|
||||
|
||||
Token *use_name = &pc->tokens->at(*token_index);
|
||||
*token_index += 1;
|
||||
ast_expect_token(pc, use_name, TokenIdStringLiteral);
|
||||
|
||||
Token *semicolon = &pc->tokens->at(*token_index);
|
||||
*token_index += 1;
|
||||
ast_expect_token(pc, semicolon, TokenIdSemicolon);
|
||||
|
||||
AstNode *node = ast_create_node(NodeTypeUse, use_kw);
|
||||
|
||||
parse_string_literal(pc, use_name, &node->data.use.path);
|
||||
|
||||
node->data.use.directives = pc->directive_list;
|
||||
pc->directive_list = nullptr;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/*
|
||||
TopLevelDecl : FnDef | ExternBlock | RootExportDecl | Use
|
||||
*/
|
||||
static void ast_parse_top_level_decls(ParseContext *pc, int *token_index, ZigList<AstNode *> *top_level_decls) {
|
||||
for (;;) {
|
||||
|
@ -1258,6 +1292,12 @@ static void ast_parse_top_level_decls(ParseContext *pc, int *token_index, ZigLis
|
|||
continue;
|
||||
}
|
||||
|
||||
AstNode *use_node = ast_parse_use(pc, token_index, false);
|
||||
if (use_node) {
|
||||
top_level_decls->append(use_node);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pc->directive_list->length > 0) {
|
||||
ast_error(directive_token, "invalid directive");
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ enum NodeType {
|
|||
NodeTypeSymbol,
|
||||
NodeTypePrefixOpExpr,
|
||||
NodeTypeFnCallExpr,
|
||||
NodeTypeUse,
|
||||
};
|
||||
|
||||
struct AstNodeRoot {
|
||||
|
@ -158,6 +159,11 @@ struct AstNodePrefixOpExpr {
|
|||
AstNode *primary_expr;
|
||||
};
|
||||
|
||||
struct AstNodeUse {
|
||||
Buf path;
|
||||
ZigList<AstNode *> *directives;
|
||||
};
|
||||
|
||||
struct AstNode {
|
||||
enum NodeType type;
|
||||
AstNode *parent;
|
||||
|
@ -180,6 +186,7 @@ struct AstNode {
|
|||
AstNodeCastExpr cast_expr;
|
||||
AstNodePrefixOpExpr prefix_op_expr;
|
||||
AstNodeFnCallExpr fn_call_expr;
|
||||
AstNodeUse use;
|
||||
Buf number;
|
||||
Buf string;
|
||||
Buf symbol;
|
||||
|
|
|
@ -12,15 +12,6 @@
|
|||
#include "hash_map.hpp"
|
||||
#include "zig_llvm.hpp"
|
||||
|
||||
struct FnTableEntry {
|
||||
LLVMValueRef fn_value;
|
||||
AstNode *proto_node;
|
||||
AstNode *fn_def_node;
|
||||
bool is_extern;
|
||||
bool internal_linkage;
|
||||
unsigned calling_convention;
|
||||
};
|
||||
|
||||
struct TypeTableEntry {
|
||||
LLVMTypeRef type_ref;
|
||||
LLVMZigDIType *di_type;
|
||||
|
@ -33,17 +24,35 @@ struct TypeTableEntry {
|
|||
TypeTableEntry *pointer_mut_parent;
|
||||
};
|
||||
|
||||
struct ImportTableEntry {
|
||||
AstNode *root;
|
||||
Buf *path; // relative to root_source_dir
|
||||
LLVMZigDIFile *di_file;
|
||||
};
|
||||
|
||||
struct FnTableEntry {
|
||||
LLVMValueRef fn_value;
|
||||
AstNode *proto_node;
|
||||
AstNode *fn_def_node;
|
||||
bool is_extern;
|
||||
bool internal_linkage;
|
||||
unsigned calling_convention;
|
||||
ImportTableEntry *import_entry;
|
||||
};
|
||||
|
||||
struct CodeGen {
|
||||
LLVMModuleRef module;
|
||||
AstNode *root;
|
||||
ZigList<ErrorMsg> errors;
|
||||
LLVMBuilderRef builder;
|
||||
LLVMZigDIBuilder *dbuilder;
|
||||
LLVMZigDICompileUnit *compile_unit;
|
||||
|
||||
// reminder: hash tables must be initialized before use
|
||||
HashMap<Buf *, FnTableEntry *, buf_hash, buf_eql_buf> fn_table;
|
||||
HashMap<Buf *, LLVMValueRef, buf_hash, buf_eql_buf> str_table;
|
||||
HashMap<Buf *, TypeTableEntry *, buf_hash, buf_eql_buf> type_table;
|
||||
HashMap<Buf *, bool, buf_hash, buf_eql_buf> link_table;
|
||||
HashMap<Buf *, ImportTableEntry *, buf_hash, buf_eql_buf> import_table;
|
||||
|
||||
struct {
|
||||
TypeTableEntry *entry_u8;
|
||||
|
@ -60,12 +69,10 @@ struct CodeGen {
|
|||
CodeGenBuildType build_type;
|
||||
LLVMTargetMachineRef target_machine;
|
||||
bool is_native_target;
|
||||
Buf in_file;
|
||||
Buf in_dir;
|
||||
Buf *root_source_dir;
|
||||
Buf *root_out_name;
|
||||
ZigList<LLVMZigDIScope *> block_scopes;
|
||||
LLVMZigDIFile *di_file;
|
||||
ZigList<FnTableEntry *> fn_defs;
|
||||
Buf *out_name;
|
||||
OutType out_type;
|
||||
FnTableEntry *cur_fn;
|
||||
bool c_stdint_used;
|
||||
|
@ -73,6 +80,8 @@ struct CodeGen {
|
|||
int version_major;
|
||||
int version_minor;
|
||||
int version_patch;
|
||||
bool verbose;
|
||||
bool initialized;
|
||||
};
|
||||
|
||||
struct TypeNode {
|
||||
|
|
|
@ -180,6 +180,8 @@ static void end_token(Tokenize *t) {
|
|||
t->cur_tok->id = TokenIdKeywordExport;
|
||||
} else if (mem_eql_str(token_mem, token_len, "as")) {
|
||||
t->cur_tok->id = TokenIdKeywordAs;
|
||||
} else if (mem_eql_str(token_mem, token_len, "use")) {
|
||||
t->cur_tok->id = TokenIdKeywordUse;
|
||||
}
|
||||
|
||||
t->cur_tok = nullptr;
|
||||
|
@ -562,6 +564,7 @@ static const char * token_name(Token *token) {
|
|||
case TokenIdKeywordPub: return "Pub";
|
||||
case TokenIdKeywordExport: return "Export";
|
||||
case TokenIdKeywordAs: return "As";
|
||||
case TokenIdKeywordUse: return "Use";
|
||||
case TokenIdLParen: return "LParen";
|
||||
case TokenIdRParen: return "RParen";
|
||||
case TokenIdComma: return "Comma";
|
||||
|
|
|
@ -22,6 +22,7 @@ enum TokenId {
|
|||
TokenIdKeywordPub,
|
||||
TokenIdKeywordExport,
|
||||
TokenIdKeywordAs,
|
||||
TokenIdKeywordUse,
|
||||
TokenIdLParen,
|
||||
TokenIdRParen,
|
||||
TokenIdComma,
|
||||
|
|
|
@ -47,6 +47,7 @@ static void add_simple_case(const char *case_name, const char *source, const cha
|
|||
test_case->compiler_args.append(tmp_exe_path);
|
||||
test_case->compiler_args.append("--release");
|
||||
test_case->compiler_args.append("--strip");
|
||||
test_case->compiler_args.append("--verbose");
|
||||
|
||||
test_cases.append(test_case);
|
||||
}
|
||||
|
@ -70,6 +71,7 @@ static void add_compile_fail_case(const char *case_name, const char *source, int
|
|||
test_case->compiler_args.append(tmp_exe_path);
|
||||
test_case->compiler_args.append("--release");
|
||||
test_case->compiler_args.append("--strip");
|
||||
test_case->compiler_args.append("--verbose");
|
||||
|
||||
test_cases.append(test_case);
|
||||
|
||||
|
|
Loading…
Reference in New Issue