mirror of
https://github.com/ziglang/zig.git
synced 2026-02-15 05:48:31 +00:00
merge conflicts
This commit is contained in:
commit
ab327344b6
@ -31,6 +31,7 @@ set(ZIG_SOURCES
|
||||
"${CMAKE_SOURCE_DIR}/src/main.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/os.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/util.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/errmsg.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/zig_llvm.cpp"
|
||||
)
|
||||
|
||||
|
||||
@ -43,7 +43,6 @@ make
|
||||
## Roadmap
|
||||
|
||||
* variable declarations and assignment expressions
|
||||
* Multiple files
|
||||
* Type checking
|
||||
* inline assembly and syscalls
|
||||
* running code at compile time
|
||||
|
||||
@ -6,6 +6,6 @@ fn private_function() {
|
||||
puts("it works!");
|
||||
}
|
||||
|
||||
fn print_text() {
|
||||
pub fn print_text() {
|
||||
private_function();
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
#link("c")
|
||||
extern {
|
||||
fn puts(s: *mut u8) -> i32;
|
||||
fn exit(code: i32) -> unreachable;
|
||||
pub fn puts(s: *mut u8) -> i32;
|
||||
pub fn exit(code: i32) -> unreachable;
|
||||
}
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
export executable "test";
|
||||
export executable "test-multiple-files";
|
||||
|
||||
use "libc.zig";
|
||||
use "foo.zig";
|
||||
|
||||
fn _start() -> unreachable {
|
||||
export fn _start() -> unreachable {
|
||||
private_function();
|
||||
}
|
||||
|
||||
|
||||
276
src/analyze.cpp
276
src/analyze.cpp
@ -17,14 +17,18 @@ struct BlockContext {
|
||||
BlockContext *parent;
|
||||
};
|
||||
|
||||
static void add_node_error(CodeGen *g, AstNode *node, Buf *msg) {
|
||||
g->errors.add_one();
|
||||
ErrorMsg *last_msg = &g->errors.last();
|
||||
last_msg->line_start = node->line;
|
||||
last_msg->column_start = node->column;
|
||||
last_msg->line_end = -1;
|
||||
last_msg->column_end = -1;
|
||||
last_msg->msg = msg;
|
||||
void add_node_error(CodeGen *g, AstNode *node, Buf *msg) {
|
||||
ErrorMsg *err = allocate<ErrorMsg>(1);
|
||||
err->line_start = node->line;
|
||||
err->column_start = node->column;
|
||||
err->line_end = -1;
|
||||
err->column_end = -1;
|
||||
err->msg = msg;
|
||||
err->path = node->owner->path;
|
||||
err->source = node->owner->source_code;
|
||||
err->line_offsets = node->owner->line_offsets;
|
||||
|
||||
g->errors.append(err);
|
||||
}
|
||||
|
||||
static int parse_version_string(Buf *buf, int *major, int *minor, int *patch) {
|
||||
@ -139,6 +143,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import,
|
||||
AstNode *fn_decl = node->data.extern_block.fn_decls.at(fn_decl_i);
|
||||
assert(fn_decl->type == NodeTypeFnDecl);
|
||||
AstNode *fn_proto = fn_decl->data.fn_decl.fn_proto;
|
||||
bool is_pub = (fn_proto->data.fn_proto.visib_mod == FnProtoVisibModPub);
|
||||
resolve_function_proto(g, fn_proto);
|
||||
Buf *name = &fn_proto->data.fn_proto.name;
|
||||
|
||||
@ -147,7 +152,12 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import,
|
||||
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);
|
||||
|
||||
g->fn_protos.append(fn_table_entry);
|
||||
import->fn_table.put(name, fn_table_entry);
|
||||
if (is_pub) {
|
||||
g->fn_table.put(name, fn_table_entry);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NodeTypeFnDef:
|
||||
@ -155,67 +165,89 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import,
|
||||
AstNode *proto_node = node->data.fn_def.fn_proto;
|
||||
assert(proto_node->type == NodeTypeFnProto);
|
||||
Buf *proto_name = &proto_node->data.fn_proto.name;
|
||||
auto entry = g->fn_table.maybe_get(proto_name);
|
||||
auto entry = import->fn_table.maybe_get(proto_name);
|
||||
bool skip = false;
|
||||
bool is_internal = (proto_node->data.fn_proto.visib_mod != FnProtoVisibModExport);
|
||||
bool is_pub = (proto_node->data.fn_proto.visib_mod == FnProtoVisibModPub);
|
||||
if (entry) {
|
||||
add_node_error(g, node,
|
||||
buf_sprintf("redefinition of '%s'", buf_ptr(proto_name)));
|
||||
assert(!node->codegen_node);
|
||||
node->codegen_node = allocate<CodeGenNode>(1);
|
||||
node->codegen_node->data.fn_def_node.skip = true;
|
||||
} else {
|
||||
skip = true;
|
||||
} else if (is_pub) {
|
||||
auto entry = g->fn_table.maybe_get(proto_name);
|
||||
if (entry) {
|
||||
add_node_error(g, node,
|
||||
buf_sprintf("redefinition of '%s'", buf_ptr(proto_name)));
|
||||
assert(!node->codegen_node);
|
||||
node->codegen_node = allocate<CodeGenNode>(1);
|
||||
node->codegen_node->data.fn_def_node.skip = true;
|
||||
skip = true;
|
||||
}
|
||||
}
|
||||
if (!skip) {
|
||||
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;
|
||||
if (fn_table_entry->internal_linkage) {
|
||||
fn_table_entry->calling_convention = LLVMFastCallConv;
|
||||
} else {
|
||||
fn_table_entry->calling_convention = LLVMCCallConv;
|
||||
}
|
||||
g->fn_table.put(proto_name, fn_table_entry);
|
||||
fn_table_entry->internal_linkage = is_internal;
|
||||
fn_table_entry->calling_convention = is_internal ? LLVMFastCallConv : LLVMCCallConv;
|
||||
|
||||
g->fn_protos.append(fn_table_entry);
|
||||
g->fn_defs.append(fn_table_entry);
|
||||
|
||||
import->fn_table.put(proto_name, fn_table_entry);
|
||||
if (is_pub) {
|
||||
g->fn_table.put(proto_name, fn_table_entry);
|
||||
}
|
||||
|
||||
resolve_function_proto(g, proto_node);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NodeTypeRootExportDecl:
|
||||
for (int i = 0; i < node->data.root_export_decl.directives->length; i += 1) {
|
||||
AstNode *directive_node = node->data.root_export_decl.directives->at(i);
|
||||
Buf *name = &directive_node->data.directive.name;
|
||||
Buf *param = &directive_node->data.directive.param;
|
||||
if (buf_eql_str(name, "version")) {
|
||||
set_root_export_version(g, param, directive_node);
|
||||
} else {
|
||||
add_node_error(g, directive_node,
|
||||
buf_sprintf("invalid directive: '%s'", buf_ptr(name)));
|
||||
if (import == g->root_import) {
|
||||
for (int i = 0; i < node->data.root_export_decl.directives->length; i += 1) {
|
||||
AstNode *directive_node = node->data.root_export_decl.directives->at(i);
|
||||
Buf *name = &directive_node->data.directive.name;
|
||||
Buf *param = &directive_node->data.directive.param;
|
||||
if (buf_eql_str(name, "version")) {
|
||||
set_root_export_version(g, param, directive_node);
|
||||
} else {
|
||||
add_node_error(g, directive_node,
|
||||
buf_sprintf("invalid directive: '%s'", buf_ptr(name)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (g->root_export_decl) {
|
||||
add_node_error(g, node,
|
||||
buf_sprintf("only one root export declaration allowed"));
|
||||
} else {
|
||||
g->root_export_decl = node;
|
||||
|
||||
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;
|
||||
if (buf_eql_str(out_type, "executable")) {
|
||||
export_out_type = OutTypeExe;
|
||||
} else if (buf_eql_str(out_type, "library")) {
|
||||
export_out_type = OutTypeLib;
|
||||
} else if (buf_eql_str(out_type, "object")) {
|
||||
export_out_type = OutTypeObj;
|
||||
} else {
|
||||
if (g->root_export_decl) {
|
||||
add_node_error(g, node,
|
||||
buf_sprintf("invalid export type: '%s'", buf_ptr(out_type)));
|
||||
buf_sprintf("only one root export declaration allowed"));
|
||||
} else {
|
||||
g->root_export_decl = node;
|
||||
|
||||
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;
|
||||
if (buf_eql_str(out_type, "executable")) {
|
||||
export_out_type = OutTypeExe;
|
||||
} else if (buf_eql_str(out_type, "library")) {
|
||||
export_out_type = OutTypeLib;
|
||||
} else if (buf_eql_str(out_type, "object")) {
|
||||
export_out_type = OutTypeObj;
|
||||
} else {
|
||||
add_node_error(g, node,
|
||||
buf_sprintf("invalid export type: '%s'", buf_ptr(out_type)));
|
||||
}
|
||||
if (g->out_type == OutTypeUnknown)
|
||||
g->out_type = export_out_type;
|
||||
}
|
||||
if (g->out_type == OutTypeUnknown)
|
||||
g->out_type = export_out_type;
|
||||
} else {
|
||||
add_node_error(g, node,
|
||||
buf_sprintf("root export declaration only valid in root source file"));
|
||||
}
|
||||
break;
|
||||
case NodeTypeUse:
|
||||
@ -263,7 +295,7 @@ static void check_type_compatibility(CodeGen *g, AstNode *node, TypeTableEntry *
|
||||
add_node_error(g, node, buf_sprintf("type mismatch."));
|
||||
}
|
||||
|
||||
static TypeTableEntry * analyze_expression(CodeGen *g, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) {
|
||||
static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) {
|
||||
switch (node->type) {
|
||||
case NodeTypeBlock:
|
||||
{
|
||||
@ -276,7 +308,7 @@ static TypeTableEntry * analyze_expression(CodeGen *g, BlockContext *context, Ty
|
||||
buf_sprintf("unreachable code"));
|
||||
break;
|
||||
}
|
||||
return_type = analyze_expression(g, context, nullptr, child);
|
||||
return_type = analyze_expression(g, import, context, nullptr, child);
|
||||
}
|
||||
return return_type;
|
||||
}
|
||||
@ -286,7 +318,7 @@ static TypeTableEntry * analyze_expression(CodeGen *g, BlockContext *context, Ty
|
||||
TypeTableEntry *expected_return_type = get_return_type(context);
|
||||
TypeTableEntry *actual_return_type;
|
||||
if (node->data.return_expr.expr) {
|
||||
actual_return_type = analyze_expression(g, context, expected_return_type, node->data.return_expr.expr);
|
||||
actual_return_type = analyze_expression(g, import, context, expected_return_type, node->data.return_expr.expr);
|
||||
} else {
|
||||
actual_return_type = g->builtin_types.entry_void;
|
||||
}
|
||||
@ -304,8 +336,8 @@ static TypeTableEntry * analyze_expression(CodeGen *g, BlockContext *context, Ty
|
||||
case NodeTypeBinOpExpr:
|
||||
{
|
||||
// TODO: think about expected types
|
||||
analyze_expression(g, context, expected_type, node->data.bin_op_expr.op1);
|
||||
analyze_expression(g, context, expected_type, node->data.bin_op_expr.op2);
|
||||
analyze_expression(g, import, context, expected_type, node->data.bin_op_expr.op1);
|
||||
analyze_expression(g, import, context, expected_type, node->data.bin_op_expr.op2);
|
||||
return expected_type;
|
||||
}
|
||||
|
||||
@ -313,14 +345,17 @@ static TypeTableEntry * analyze_expression(CodeGen *g, BlockContext *context, Ty
|
||||
{
|
||||
Buf *name = hack_get_fn_call_name(g, node->data.fn_call_expr.fn_ref_expr);
|
||||
|
||||
auto entry = g->fn_table.maybe_get(name);
|
||||
auto entry = import->fn_table.maybe_get(name);
|
||||
if (!entry)
|
||||
entry = g->fn_table.maybe_get(name);
|
||||
|
||||
if (!entry) {
|
||||
add_node_error(g, node,
|
||||
buf_sprintf("undefined function: '%s'", buf_ptr(name)));
|
||||
// still analyze the parameters, even though we don't know what to expect
|
||||
for (int i = 0; i < node->data.fn_call_expr.params.length; i += 1) {
|
||||
AstNode *child = node->data.fn_call_expr.params.at(i);
|
||||
analyze_expression(g, context, nullptr, child);
|
||||
analyze_expression(g, import, context, nullptr, child);
|
||||
}
|
||||
|
||||
return g->builtin_types.entry_invalid;
|
||||
@ -350,7 +385,7 @@ static TypeTableEntry * analyze_expression(CodeGen *g, BlockContext *context, Ty
|
||||
if (param_type_node->codegen_node)
|
||||
expected_param_type = param_type_node->codegen_node->data.type_node.entry;
|
||||
}
|
||||
analyze_expression(g, context, expected_param_type, child);
|
||||
analyze_expression(g, import, context, expected_param_type, child);
|
||||
}
|
||||
|
||||
TypeTableEntry *return_type = fn_proto->return_type->codegen_node->data.type_node.entry;
|
||||
@ -444,76 +479,7 @@ static void check_fn_def_control_flow(CodeGen *g, AstNode *node) {
|
||||
}
|
||||
}
|
||||
|
||||
static void analyze_expression(CodeGen *g, AstNode *node) {
|
||||
switch (node->type) {
|
||||
case NodeTypeBlock:
|
||||
for (int i = 0; i < node->data.block.statements.length; i += 1) {
|
||||
AstNode *child = node->data.block.statements.at(i);
|
||||
analyze_expression(g, child);
|
||||
}
|
||||
break;
|
||||
case NodeTypeReturnExpr:
|
||||
if (node->data.return_expr.expr) {
|
||||
analyze_expression(g, node->data.return_expr.expr);
|
||||
}
|
||||
break;
|
||||
case NodeTypeBinOpExpr:
|
||||
analyze_expression(g, node->data.bin_op_expr.op1);
|
||||
analyze_expression(g, node->data.bin_op_expr.op2);
|
||||
break;
|
||||
case NodeTypeFnCallExpr:
|
||||
{
|
||||
Buf *name = hack_get_fn_call_name(g, node->data.fn_call_expr.fn_ref_expr);
|
||||
|
||||
auto entry = g->fn_table.maybe_get(name);
|
||||
if (!entry) {
|
||||
add_node_error(g, node,
|
||||
buf_sprintf("undefined function: '%s'", buf_ptr(name)));
|
||||
} else {
|
||||
FnTableEntry *fn_table_entry = entry->value;
|
||||
assert(fn_table_entry->proto_node->type == NodeTypeFnProto);
|
||||
int expected_param_count = fn_table_entry->proto_node->data.fn_proto.params.length;
|
||||
int actual_param_count = node->data.fn_call_expr.params.length;
|
||||
if (expected_param_count != actual_param_count) {
|
||||
add_node_error(g, node,
|
||||
buf_sprintf("wrong number of arguments. Expected %d, got %d.",
|
||||
expected_param_count, actual_param_count));
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < node->data.fn_call_expr.params.length; i += 1) {
|
||||
AstNode *child = node->data.fn_call_expr.params.at(i);
|
||||
analyze_expression(g, child);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NodeTypeCastExpr:
|
||||
zig_panic("TODO");
|
||||
break;
|
||||
case NodeTypePrefixOpExpr:
|
||||
zig_panic("TODO");
|
||||
break;
|
||||
case NodeTypeNumberLiteral:
|
||||
case NodeTypeStringLiteral:
|
||||
case NodeTypeUnreachable:
|
||||
case NodeTypeSymbol:
|
||||
// nothing to do
|
||||
break;
|
||||
case NodeTypeDirective:
|
||||
case NodeTypeFnDecl:
|
||||
case NodeTypeFnProto:
|
||||
case NodeTypeParamDecl:
|
||||
case NodeTypeType:
|
||||
case NodeTypeRoot:
|
||||
case NodeTypeRootExportDecl:
|
||||
case NodeTypeExternBlock:
|
||||
case NodeTypeFnDef:
|
||||
case NodeTypeUse:
|
||||
zig_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
static void analyze_top_level_declaration(CodeGen *g, AstNode *node) {
|
||||
static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import, AstNode *node) {
|
||||
switch (node->type) {
|
||||
case NodeTypeFnDef:
|
||||
{
|
||||
@ -540,7 +506,7 @@ static void analyze_top_level_declaration(CodeGen *g, AstNode *node) {
|
||||
context.root = &context;
|
||||
context.parent = nullptr;
|
||||
TypeTableEntry *expected_type = fn_proto->return_type->codegen_node->data.type_node.entry;
|
||||
analyze_expression(g, &context, expected_type, node->data.fn_def.body);
|
||||
analyze_expression(g, import, &context, expected_type, node->data.fn_def.body);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -576,37 +542,55 @@ static void analyze_top_level_declaration(CodeGen *g, AstNode *node) {
|
||||
}
|
||||
}
|
||||
|
||||
static void analyze_root(CodeGen *g, ImportTableEntry *import, AstNode *node) {
|
||||
static void find_function_declarations_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, import, child);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void analyze_top_level_decls_root(CodeGen *g, ImportTableEntry *import, AstNode *node) {
|
||||
assert(node->type == NodeTypeRoot);
|
||||
|
||||
for (int i = 0; i < node->data.root.top_level_decls.length; i += 1) {
|
||||
AstNode *child = node->data.root.top_level_decls.at(i);
|
||||
analyze_top_level_declaration(g, child);
|
||||
}
|
||||
|
||||
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) {
|
||||
add_node_error(g, node,
|
||||
buf_sprintf("missing export declaration and export type not provided"));
|
||||
analyze_top_level_declaration(g, import, child);
|
||||
}
|
||||
}
|
||||
|
||||
void semantic_analyze(CodeGen *g) {
|
||||
auto it = g->import_table.entry_iterator();
|
||||
for (;;) {
|
||||
auto *entry = it.next();
|
||||
if (!entry)
|
||||
break;
|
||||
{
|
||||
auto it = g->import_table.entry_iterator();
|
||||
for (;;) {
|
||||
auto *entry = it.next();
|
||||
if (!entry)
|
||||
break;
|
||||
|
||||
ImportTableEntry *import = entry->value;
|
||||
analyze_root(g, import, import->root);
|
||||
ImportTableEntry *import = entry->value;
|
||||
find_function_declarations_root(g, import, import->root);
|
||||
}
|
||||
}
|
||||
{
|
||||
auto it = g->import_table.entry_iterator();
|
||||
for (;;) {
|
||||
auto *entry = it.next();
|
||||
if (!entry)
|
||||
break;
|
||||
|
||||
ImportTableEntry *import = entry->value;
|
||||
analyze_top_level_decls_root(g, import, import->root);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!g->root_out_name) {
|
||||
add_node_error(g, g->root_import->root,
|
||||
buf_sprintf("missing export declaration and output name not provided"));
|
||||
} else if (g->out_type == OutTypeUnknown) {
|
||||
add_node_error(g, g->root_import->root,
|
||||
buf_sprintf("missing export declaration and export type not provided"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,7 +9,10 @@
|
||||
#define ZIG_ANALYZE_HPP
|
||||
|
||||
struct CodeGen;
|
||||
struct AstNode;
|
||||
struct Buf;
|
||||
|
||||
void semantic_analyze(CodeGen *g);
|
||||
void add_node_error(CodeGen *g, AstNode *node, Buf *msg);
|
||||
|
||||
#endif
|
||||
|
||||
@ -3,9 +3,8 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
Buf *buf_sprintf(const char *format, ...) {
|
||||
va_list ap, ap2;
|
||||
va_start(ap, format);
|
||||
Buf *buf_vprintf(const char *format, va_list ap) {
|
||||
va_list ap2;
|
||||
va_copy(ap2, ap);
|
||||
|
||||
int len1 = vsnprintf(nullptr, 0, format, ap);
|
||||
@ -19,11 +18,18 @@ Buf *buf_sprintf(const char *format, ...) {
|
||||
assert(len2 == len1);
|
||||
|
||||
va_end(ap2);
|
||||
va_end(ap);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
Buf *buf_sprintf(const char *format, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
Buf *result = buf_vprintf(format, ap);
|
||||
va_end(ap);
|
||||
return result;
|
||||
}
|
||||
|
||||
void buf_appendf(Buf *buf, const char *format, ...) {
|
||||
assert(buf->list.length);
|
||||
va_list ap, ap2;
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <ctype.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#define BUF_INIT {{0}}
|
||||
|
||||
@ -24,6 +25,7 @@ struct Buf {
|
||||
|
||||
Buf *buf_sprintf(const char *format, ...)
|
||||
__attribute__ ((format (printf, 1, 2)));
|
||||
Buf *buf_vprintf(const char *format, va_list ap);
|
||||
|
||||
static inline int buf_len(Buf *buf) {
|
||||
assert(buf->list.length);
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
#include "error.hpp"
|
||||
#include "semantic_info.hpp"
|
||||
#include "analyze.hpp"
|
||||
#include "errmsg.hpp"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
@ -41,6 +42,10 @@ void codegen_set_verbose(CodeGen *g, bool verbose) {
|
||||
g->verbose = verbose;
|
||||
}
|
||||
|
||||
void codegen_set_errmsg_color(CodeGen *g, ErrColor err_color) {
|
||||
g->err_color = err_color;
|
||||
}
|
||||
|
||||
void codegen_set_strip(CodeGen *g, bool strip) {
|
||||
g->strip_debug_symbols = strip;
|
||||
}
|
||||
@ -120,7 +125,13 @@ static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) {
|
||||
|
||||
Buf *name = hack_get_fn_call_name(g, node->data.fn_call_expr.fn_ref_expr);
|
||||
|
||||
FnTableEntry *fn_table_entry = g->fn_table.get(name);
|
||||
FnTableEntry *fn_table_entry;
|
||||
auto entry = g->cur_fn->import_entry->fn_table.maybe_get(name);
|
||||
if (entry)
|
||||
fn_table_entry = entry->value;
|
||||
else
|
||||
fn_table_entry = g->fn_table.get(name);
|
||||
|
||||
assert(fn_table_entry->proto_node->type == NodeTypeFnProto);
|
||||
int expected_param_count = fn_table_entry->proto_node->data.fn_proto.params.length;
|
||||
int actual_param_count = node->data.fn_call_expr.params.length;
|
||||
@ -473,13 +484,8 @@ static void do_code_gen(CodeGen *g) {
|
||||
|
||||
|
||||
// Generate function prototypes
|
||||
auto it = g->fn_table.entry_iterator();
|
||||
for (;;) {
|
||||
auto *entry = it.next();
|
||||
if (!entry)
|
||||
break;
|
||||
|
||||
FnTableEntry *fn_table_entry = entry->value;
|
||||
for (int i = 0; i < g->fn_protos.length; i += 1) {
|
||||
FnTableEntry *fn_table_entry = g->fn_protos.at(i);
|
||||
|
||||
AstNode *proto_node = fn_table_entry->proto_node;
|
||||
assert(proto_node->type == NodeTypeFnProto);
|
||||
@ -542,6 +548,7 @@ static void do_code_gen(CodeGen *g) {
|
||||
assert(codegen_node);
|
||||
|
||||
FnDefNode *codegen_fn_def = &codegen_node->data.fn_def_node;
|
||||
assert(codegen_fn_def);
|
||||
codegen_fn_def->params = allocate<LLVMValueRef>(LLVMCountParams(fn));
|
||||
LLVMGetParams(fn, codegen_fn_def->params);
|
||||
|
||||
@ -664,7 +671,8 @@ static void init(CodeGen *g, Buf *source_path) {
|
||||
|
||||
}
|
||||
|
||||
static void codegen_add_code(CodeGen *g, Buf *source_path, Buf *source_code) {
|
||||
static ImportTableEntry *codegen_add_code(CodeGen *g, Buf *source_path, Buf *source_code) {
|
||||
int err;
|
||||
Buf full_path = BUF_INIT;
|
||||
os_path_join(g->root_source_dir, source_path, &full_path);
|
||||
|
||||
@ -681,24 +689,42 @@ static void codegen_add_code(CodeGen *g, Buf *source_path, Buf *source_code) {
|
||||
fprintf(stderr, "---------\n");
|
||||
}
|
||||
|
||||
ZigList<Token> *tokens = tokenize(source_code);
|
||||
Tokenization tokenization = {0};
|
||||
tokenize(source_code, &tokenization);
|
||||
|
||||
if (tokenization.err) {
|
||||
ErrorMsg *err = allocate<ErrorMsg>(1);
|
||||
err->line_start = tokenization.err_line;
|
||||
err->column_start = tokenization.err_column;
|
||||
err->line_end = -1;
|
||||
err->column_end = -1;
|
||||
err->msg = tokenization.err;
|
||||
err->path = source_path;
|
||||
err->source = source_code;
|
||||
err->line_offsets = tokenization.line_offsets;
|
||||
|
||||
print_err_msg(err, g->err_color);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (g->verbose) {
|
||||
print_tokens(source_code, tokens);
|
||||
print_tokens(source_code, tokenization.tokens);
|
||||
|
||||
fprintf(stderr, "\nAST:\n");
|
||||
fprintf(stderr, "------\n");
|
||||
}
|
||||
|
||||
ImportTableEntry *import_entry = allocate<ImportTableEntry>(1);
|
||||
import_entry->source_code = source_code;
|
||||
import_entry->line_offsets = tokenization.line_offsets;
|
||||
import_entry->path = source_path;
|
||||
import_entry->fn_table.init(32);
|
||||
import_entry->root = ast_parse(source_code, tokens);
|
||||
import_entry->root = ast_parse(source_code, tokenization.tokens, import_entry, g->err_color);
|
||||
assert(import_entry->root);
|
||||
if (g->verbose) {
|
||||
ast_print(import_entry->root, 0);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@ -713,18 +739,23 @@ static void codegen_add_code(CodeGen *g, Buf *source_path, Buf *source_code) {
|
||||
if (!entry) {
|
||||
Buf full_path = BUF_INIT;
|
||||
os_path_join(g->root_source_dir, &top_level_decl->data.use.path, &full_path);
|
||||
Buf import_code = BUF_INIT;
|
||||
os_fetch_file_path(&full_path, &import_code);
|
||||
codegen_add_code(g, &top_level_decl->data.use.path, &import_code);
|
||||
Buf *import_code = buf_alloc();
|
||||
if ((err = os_fetch_file_path(&full_path, import_code))) {
|
||||
add_node_error(g, top_level_decl,
|
||||
buf_sprintf("unable to open \"%s\": %s", buf_ptr(&full_path), err_str(err)));
|
||||
break;
|
||||
}
|
||||
codegen_add_code(g, &top_level_decl->data.use.path, import_code);
|
||||
}
|
||||
}
|
||||
|
||||
return import_entry;
|
||||
}
|
||||
|
||||
void codegen_add_root_code(CodeGen *g, Buf *source_path, Buf *source_code) {
|
||||
init(g, source_path);
|
||||
|
||||
codegen_add_code(g, source_path, source_code);
|
||||
|
||||
g->root_import = codegen_add_code(g, source_path, source_code);
|
||||
|
||||
if (g->verbose) {
|
||||
fprintf(stderr, "\nSemantic Analysis:\n");
|
||||
@ -738,10 +769,8 @@ void codegen_add_root_code(CodeGen *g, Buf *source_path, Buf *source_code) {
|
||||
}
|
||||
} 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));
|
||||
ErrorMsg *err = g->errors.at(i);
|
||||
print_err_msg(err, g->err_color);
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
#define ZIG_CODEGEN_HPP
|
||||
|
||||
#include "parser.hpp"
|
||||
#include "errmsg.hpp"
|
||||
|
||||
struct CodeGen;
|
||||
|
||||
@ -19,16 +20,6 @@ enum OutType {
|
||||
OutTypeObj,
|
||||
};
|
||||
|
||||
|
||||
struct ErrorMsg {
|
||||
int line_start;
|
||||
int column_start;
|
||||
int line_end;
|
||||
int column_end;
|
||||
Buf *msg;
|
||||
};
|
||||
|
||||
|
||||
CodeGen *codegen_create(Buf *root_source_dir);
|
||||
|
||||
enum CodeGenBuildType {
|
||||
@ -39,6 +30,7 @@ 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_errmsg_color(CodeGen *codegen, ErrColor err_color);
|
||||
void codegen_set_out_type(CodeGen *codegen, OutType out_type);
|
||||
void codegen_set_out_name(CodeGen *codegen, Buf *out_name);
|
||||
|
||||
|
||||
38
src/errmsg.cpp
Normal file
38
src/errmsg.cpp
Normal file
@ -0,0 +1,38 @@
|
||||
#include "errmsg.hpp"
|
||||
#include "os.hpp"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#define RED "\x1b[31;1m"
|
||||
#define WHITE "\x1b[37;1m"
|
||||
#define GREEN "\x1b[32;1m"
|
||||
#define RESET "\x1b[0m"
|
||||
|
||||
void print_err_msg(ErrorMsg *err, ErrColor color) {
|
||||
if (color == ErrColorOn || (color == ErrColorAuto && os_stderr_tty())) {
|
||||
fprintf(stderr, WHITE "%s:%d:%d: " RED "error:" WHITE " %s" RESET "\n",
|
||||
buf_ptr(err->path),
|
||||
err->line_start + 1, err->column_start + 1,
|
||||
buf_ptr(err->msg));
|
||||
|
||||
assert(err->source);
|
||||
assert(err->line_offsets);
|
||||
|
||||
int line_start_offset = err->line_offsets->at(err->line_start);
|
||||
int line_end_offset = err->line_offsets->at(err->line_start + 1);
|
||||
|
||||
fwrite(buf_ptr(err->source) + line_start_offset, 1, line_end_offset - line_start_offset - 1, stderr);
|
||||
fprintf(stderr, "\n");
|
||||
for (int i = 0; i < err->column_start; i += 1) {
|
||||
fprintf(stderr, " ");
|
||||
}
|
||||
fprintf(stderr, GREEN "^" RESET "\n");
|
||||
|
||||
} else {
|
||||
fprintf(stderr, "%s:%d:%d: error: %s\n",
|
||||
buf_ptr(err->path),
|
||||
err->line_start + 1, err->column_start + 1,
|
||||
buf_ptr(err->msg));
|
||||
}
|
||||
}
|
||||
|
||||
33
src/errmsg.hpp
Normal file
33
src/errmsg.hpp
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2015 Andrew Kelley
|
||||
*
|
||||
* This file is part of zig, which is MIT licensed.
|
||||
* See http://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#ifndef ZIG_ERRMSG_HPP
|
||||
#define ZIG_ERRMSG_HPP
|
||||
|
||||
#include "buffer.hpp"
|
||||
#include "list.hpp"
|
||||
|
||||
enum ErrColor {
|
||||
ErrColorAuto,
|
||||
ErrColorOff,
|
||||
ErrColorOn,
|
||||
};
|
||||
|
||||
struct ErrorMsg {
|
||||
int line_start;
|
||||
int column_start;
|
||||
int line_end;
|
||||
int column_end;
|
||||
Buf *msg;
|
||||
Buf *path;
|
||||
Buf *source;
|
||||
ZigList<int> *line_offsets;
|
||||
};
|
||||
|
||||
void print_err_msg(ErrorMsg *msg, ErrColor color);
|
||||
|
||||
#endif
|
||||
@ -6,6 +6,12 @@ const char *err_str(int err) {
|
||||
case ErrorNoMem: return "out of memory";
|
||||
case ErrorInvalidFormat: return "invalid format";
|
||||
case ErrorSemanticAnalyzeFail: return "semantic analyze failed";
|
||||
case ErrorAccess: return "access denied";
|
||||
case ErrorInterrupted: return "interrupted";
|
||||
case ErrorSystemResources: return "lack of system resources";
|
||||
case ErrorFileNotFound: return "file not found";
|
||||
case ErrorFileSystem: return "file system error";
|
||||
case ErrorFileTooBig: return "file too big";
|
||||
}
|
||||
return "(invalid error)";
|
||||
}
|
||||
|
||||
@ -13,6 +13,12 @@ enum Error {
|
||||
ErrorNoMem,
|
||||
ErrorInvalidFormat,
|
||||
ErrorSemanticAnalyzeFail,
|
||||
ErrorAccess,
|
||||
ErrorInterrupted,
|
||||
ErrorSystemResources,
|
||||
ErrorFileNotFound,
|
||||
ErrorFileSystem,
|
||||
ErrorFileTooBig,
|
||||
};
|
||||
|
||||
const char *err_str(int err);
|
||||
|
||||
17
src/main.cpp
17
src/main.cpp
@ -25,6 +25,7 @@ static int usage(const char *arg0) {
|
||||
" --name [name] override output name\n"
|
||||
" --output [file] override destination path\n"
|
||||
" --verbose turn on compiler debug output\n"
|
||||
" --color [auto|off|on] enable or disable colored error messages\n"
|
||||
, arg0);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
@ -43,6 +44,7 @@ struct Build {
|
||||
OutType out_type;
|
||||
const char *out_name;
|
||||
bool verbose;
|
||||
ErrColor color;
|
||||
};
|
||||
|
||||
static int build(const char *arg0, Build *b) {
|
||||
@ -73,6 +75,7 @@ static int build(const char *arg0, Build *b) {
|
||||
if (b->out_name)
|
||||
codegen_set_out_name(g, buf_create_from_str(b->out_name));
|
||||
codegen_set_verbose(g, b->verbose);
|
||||
codegen_set_errmsg_color(g, b->color);
|
||||
codegen_add_root_code(g, &root_source_name, &root_source_code);
|
||||
codegen_link(g, b->out_file);
|
||||
|
||||
@ -106,7 +109,9 @@ int main(int argc, char **argv) {
|
||||
return usage(arg0);
|
||||
} else {
|
||||
i += 1;
|
||||
if (strcmp(arg, "--output") == 0) {
|
||||
if (i >= argc) {
|
||||
return usage(arg0);
|
||||
} else if (strcmp(arg, "--output") == 0) {
|
||||
b.out_file = argv[i];
|
||||
} else if (strcmp(arg, "--export") == 0) {
|
||||
if (strcmp(argv[i], "exe") == 0) {
|
||||
@ -118,6 +123,16 @@ int main(int argc, char **argv) {
|
||||
} else {
|
||||
return usage(arg0);
|
||||
}
|
||||
} else if (strcmp(arg, "--color") == 0) {
|
||||
if (strcmp(argv[i], "auto") == 0) {
|
||||
b.color = ErrColorAuto;
|
||||
} else if (strcmp(argv[i], "on") == 0) {
|
||||
b.color = ErrColorOn;
|
||||
} else if (strcmp(argv[i], "off") == 0) {
|
||||
b.color = ErrColorOff;
|
||||
} else {
|
||||
return usage(arg0);
|
||||
}
|
||||
} else if (strcmp(arg, "--name") == 0) {
|
||||
b.out_name = argv[i];
|
||||
} else {
|
||||
|
||||
60
src/os.cpp
60
src/os.cpp
@ -7,6 +7,7 @@
|
||||
|
||||
#include "os.hpp"
|
||||
#include "util.hpp"
|
||||
#include "error.hpp"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
@ -143,26 +144,65 @@ void os_write_file(Buf *full_path, Buf *contents) {
|
||||
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));
|
||||
if (fstat(fd, &st)) {
|
||||
switch (errno) {
|
||||
case EACCES:
|
||||
return ErrorAccess;
|
||||
case ENOENT:
|
||||
return ErrorFileNotFound;
|
||||
case ENOMEM:
|
||||
return ErrorSystemResources;
|
||||
case EINTR:
|
||||
return ErrorInterrupted;
|
||||
case EINVAL:
|
||||
zig_unreachable();
|
||||
default:
|
||||
return ErrorFileSystem;
|
||||
}
|
||||
}
|
||||
off_t big_size = st.st_size;
|
||||
if (big_size > INT_MAX)
|
||||
zig_panic("file too big");
|
||||
if (big_size > INT_MAX) {
|
||||
return ErrorFileTooBig;
|
||||
}
|
||||
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));
|
||||
if (ret != size) {
|
||||
switch (errno) {
|
||||
case EINTR:
|
||||
return ErrorInterrupted;
|
||||
case EINVAL:
|
||||
case EISDIR:
|
||||
zig_unreachable();
|
||||
default:
|
||||
return ErrorFileSystem;
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
if (!f) {
|
||||
switch (errno) {
|
||||
case EACCES:
|
||||
return ErrorAccess;
|
||||
case EINTR:
|
||||
return ErrorInterrupted;
|
||||
case EINVAL:
|
||||
zig_unreachable();
|
||||
case ENFILE:
|
||||
case ENOMEM:
|
||||
return ErrorSystemResources;
|
||||
case ENOENT:
|
||||
return ErrorFileNotFound;
|
||||
default:
|
||||
return ErrorFileSystem;
|
||||
}
|
||||
}
|
||||
int result = os_fetch_file(f, out_contents);
|
||||
fclose(f);
|
||||
return result;
|
||||
@ -180,3 +220,7 @@ int os_get_cwd(Buf *out_cwd) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool os_stderr_tty(void) {
|
||||
return isatty(STDERR_FILENO);
|
||||
}
|
||||
|
||||
@ -29,4 +29,6 @@ int os_fetch_file_path(Buf *full_path, Buf *out_contents);
|
||||
int os_get_cwd(Buf *out_cwd);
|
||||
|
||||
|
||||
bool os_stderr_tty(void);
|
||||
|
||||
#endif
|
||||
|
||||
120
src/parser.cpp
120
src/parser.cpp
@ -6,6 +6,8 @@
|
||||
*/
|
||||
|
||||
#include "parser.hpp"
|
||||
#include "errmsg.hpp"
|
||||
#include "semantic_info.hpp"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
@ -45,21 +47,6 @@ static const char *prefix_op_str(PrefixOp prefix_op) {
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
__attribute__ ((format (printf, 2, 3)))
|
||||
__attribute__ ((noreturn))
|
||||
static void ast_error(Token *token, const char *format, ...) {
|
||||
int line = token->start_line + 1;
|
||||
int column = token->start_column + 1;
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
fprintf(stderr, "Error: Line %d, column %d: ", line, column);
|
||||
vfprintf(stderr, format, ap);
|
||||
fprintf(stderr, "\n");
|
||||
va_end(ap);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
const char *node_type_str(NodeType node_type) {
|
||||
switch (node_type) {
|
||||
case NodeTypeRoot:
|
||||
@ -254,11 +241,36 @@ struct ParseContext {
|
||||
AstNode *root;
|
||||
ZigList<Token> *tokens;
|
||||
ZigList<AstNode *> *directive_list;
|
||||
ImportTableEntry *owner;
|
||||
ErrColor err_color;
|
||||
};
|
||||
|
||||
static AstNode *ast_create_node_no_line_info(NodeType type) {
|
||||
__attribute__ ((format (printf, 3, 4)))
|
||||
__attribute__ ((noreturn))
|
||||
static void ast_error(ParseContext *pc, Token *token, const char *format, ...) {
|
||||
ErrorMsg *err = allocate<ErrorMsg>(1);
|
||||
err->line_start = token->start_line;
|
||||
err->column_start = token->start_column;
|
||||
err->line_end = -1;
|
||||
err->column_end = -1;
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
err->msg = buf_vprintf(format, ap);
|
||||
va_end(ap);
|
||||
|
||||
err->path = pc->owner->path;
|
||||
err->source = pc->owner->source_code;
|
||||
err->line_offsets = pc->owner->line_offsets;
|
||||
|
||||
print_err_msg(err, pc->err_color);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static AstNode *ast_create_node_no_line_info(ParseContext *pc, NodeType type) {
|
||||
AstNode *node = allocate<AstNode>(1);
|
||||
node->type = type;
|
||||
node->owner = pc->owner;
|
||||
return node;
|
||||
}
|
||||
|
||||
@ -267,21 +279,21 @@ static void ast_update_node_line_info(AstNode *node, Token *first_token) {
|
||||
node->column = first_token->start_column;
|
||||
}
|
||||
|
||||
static AstNode *ast_create_node(NodeType type, Token *first_token) {
|
||||
AstNode *node = ast_create_node_no_line_info(type);
|
||||
static AstNode *ast_create_node(ParseContext *pc, NodeType type, Token *first_token) {
|
||||
AstNode *node = ast_create_node_no_line_info(pc, type);
|
||||
ast_update_node_line_info(node, first_token);
|
||||
return node;
|
||||
}
|
||||
|
||||
static AstNode *ast_create_node_with_node(NodeType type, AstNode *other_node) {
|
||||
AstNode *node = ast_create_node_no_line_info(type);
|
||||
static AstNode *ast_create_node_with_node(ParseContext *pc, NodeType type, AstNode *other_node) {
|
||||
AstNode *node = ast_create_node_no_line_info(pc, type);
|
||||
node->line = other_node->line;
|
||||
node->column = other_node->column;
|
||||
return node;
|
||||
}
|
||||
|
||||
static AstNode *ast_create_void_type_node(ParseContext *pc, Token *token) {
|
||||
AstNode *node = ast_create_node(NodeTypeType, token);
|
||||
AstNode *node = ast_create_node(pc, NodeTypeType, token);
|
||||
node->data.type.type = AstNodeTypeTypePrimitive;
|
||||
buf_init_from_str(&node->data.type.primitive_name, "void");
|
||||
return node;
|
||||
@ -331,7 +343,7 @@ __attribute__ ((noreturn))
|
||||
static void ast_invalid_token_error(ParseContext *pc, Token *token) {
|
||||
Buf token_value = BUF_INIT;
|
||||
ast_buf_from_token(pc, token, &token_value);
|
||||
ast_error(token, "invalid token: '%s'", buf_ptr(&token_value));
|
||||
ast_error(pc, token, "invalid token: '%s'", buf_ptr(&token_value));
|
||||
}
|
||||
|
||||
static AstNode *ast_parse_expression(ParseContext *pc, int *token_index, bool mandatory);
|
||||
@ -349,7 +361,7 @@ static AstNode *ast_parse_directive(ParseContext *pc, int token_index, int *new_
|
||||
token_index += 1;
|
||||
ast_expect_token(pc, number_sign, TokenIdNumberSign);
|
||||
|
||||
AstNode *node = ast_create_node(NodeTypeDirective, number_sign);
|
||||
AstNode *node = ast_create_node(pc, NodeTypeDirective, number_sign);
|
||||
|
||||
Token *name_symbol = &pc->tokens->at(token_index);
|
||||
token_index += 1;
|
||||
@ -399,7 +411,7 @@ static AstNode *ast_parse_type(ParseContext *pc, int token_index, int *new_token
|
||||
Token *token = &pc->tokens->at(token_index);
|
||||
token_index += 1;
|
||||
|
||||
AstNode *node = ast_create_node(NodeTypeType, token);
|
||||
AstNode *node = ast_create_node(pc, NodeTypeType, token);
|
||||
|
||||
if (token->id == TokenIdKeywordUnreachable) {
|
||||
node->data.type.type = AstNodeTypeTypePrimitive;
|
||||
@ -437,7 +449,7 @@ static AstNode *ast_parse_param_decl(ParseContext *pc, int token_index, int *new
|
||||
token_index += 1;
|
||||
ast_expect_token(pc, param_name, TokenIdSymbol);
|
||||
|
||||
AstNode *node = ast_create_node(NodeTypeParamDecl, param_name);
|
||||
AstNode *node = ast_create_node(pc, NodeTypeParamDecl, param_name);
|
||||
|
||||
|
||||
ast_buf_from_token(pc, param_name, &node->data.param_decl.name);
|
||||
@ -544,21 +556,21 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool
|
||||
Token *token = &pc->tokens->at(*token_index);
|
||||
|
||||
if (token->id == TokenIdNumberLiteral) {
|
||||
AstNode *node = ast_create_node(NodeTypeNumberLiteral, token);
|
||||
AstNode *node = ast_create_node(pc, NodeTypeNumberLiteral, token);
|
||||
ast_buf_from_token(pc, token, &node->data.number);
|
||||
*token_index += 1;
|
||||
return node;
|
||||
} else if (token->id == TokenIdStringLiteral) {
|
||||
AstNode *node = ast_create_node(NodeTypeStringLiteral, token);
|
||||
AstNode *node = ast_create_node(pc, NodeTypeStringLiteral, token);
|
||||
parse_string_literal(pc, token, &node->data.string);
|
||||
*token_index += 1;
|
||||
return node;
|
||||
} else if (token->id == TokenIdKeywordUnreachable) {
|
||||
AstNode *node = ast_create_node(NodeTypeUnreachable, token);
|
||||
AstNode *node = ast_create_node(pc, NodeTypeUnreachable, token);
|
||||
*token_index += 1;
|
||||
return node;
|
||||
} else if (token->id == TokenIdSymbol) {
|
||||
AstNode *node = ast_create_node(NodeTypeSymbol, token);
|
||||
AstNode *node = ast_create_node(pc, NodeTypeSymbol, token);
|
||||
ast_buf_from_token(pc, token, &node->data.symbol);
|
||||
*token_index += 1;
|
||||
return node;
|
||||
@ -592,7 +604,7 @@ static AstNode *ast_parse_fn_call_expr(ParseContext *pc, int *token_index, bool
|
||||
if (l_paren->id != TokenIdLParen)
|
||||
return primary_expr;
|
||||
|
||||
AstNode *node = ast_create_node_with_node(NodeTypeFnCallExpr, primary_expr);
|
||||
AstNode *node = ast_create_node_with_node(pc, NodeTypeFnCallExpr, primary_expr);
|
||||
node->data.fn_call_expr.fn_ref_expr = primary_expr;
|
||||
ast_parse_fn_call_param_list(pc, *token_index, token_index, &node->data.fn_call_expr.params);
|
||||
|
||||
@ -635,7 +647,7 @@ static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, int *token_index, boo
|
||||
return ast_parse_fn_call_expr(pc, token_index, mandatory);
|
||||
|
||||
AstNode *primary_expr = ast_parse_fn_call_expr(pc, token_index, true);
|
||||
AstNode *node = ast_create_node(NodeTypePrefixOpExpr, token);
|
||||
AstNode *node = ast_create_node(pc, NodeTypePrefixOpExpr, token);
|
||||
node->data.prefix_op_expr.primary_expr = primary_expr;
|
||||
node->data.prefix_op_expr.prefix_op = prefix_op;
|
||||
|
||||
@ -656,7 +668,7 @@ static AstNode *ast_parse_cast_expression(ParseContext *pc, int *token_index, bo
|
||||
return prefix_op_expr;
|
||||
*token_index += 1;
|
||||
|
||||
AstNode *node = ast_create_node(NodeTypeCastExpr, as_kw);
|
||||
AstNode *node = ast_create_node(pc, NodeTypeCastExpr, as_kw);
|
||||
node->data.cast_expr.prefix_op_expr = prefix_op_expr;
|
||||
|
||||
node->data.cast_expr.type = ast_parse_type(pc, *token_index, token_index);
|
||||
@ -705,7 +717,7 @@ static AstNode *ast_parse_mult_expr(ParseContext *pc, int *token_index, bool man
|
||||
|
||||
AstNode *operand_2 = ast_parse_cast_expression(pc, token_index, true);
|
||||
|
||||
AstNode *node = ast_create_node(NodeTypeBinOpExpr, token);
|
||||
AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token);
|
||||
node->data.bin_op_expr.op1 = operand_1;
|
||||
node->data.bin_op_expr.bin_op = mult_op;
|
||||
node->data.bin_op_expr.op2 = operand_2;
|
||||
@ -753,7 +765,7 @@ static AstNode *ast_parse_add_expr(ParseContext *pc, int *token_index, bool mand
|
||||
|
||||
AstNode *operand_2 = ast_parse_mult_expr(pc, token_index, true);
|
||||
|
||||
AstNode *node = ast_create_node(NodeTypeBinOpExpr, token);
|
||||
AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token);
|
||||
node->data.bin_op_expr.op1 = operand_1;
|
||||
node->data.bin_op_expr.bin_op = add_op;
|
||||
node->data.bin_op_expr.op2 = operand_2;
|
||||
@ -801,7 +813,7 @@ static AstNode *ast_parse_bit_shift_expr(ParseContext *pc, int *token_index, boo
|
||||
|
||||
AstNode *operand_2 = ast_parse_add_expr(pc, token_index, true);
|
||||
|
||||
AstNode *node = ast_create_node(NodeTypeBinOpExpr, token);
|
||||
AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token);
|
||||
node->data.bin_op_expr.op1 = operand_1;
|
||||
node->data.bin_op_expr.bin_op = bit_shift_op;
|
||||
node->data.bin_op_expr.op2 = operand_2;
|
||||
@ -825,7 +837,7 @@ static AstNode *ast_parse_bin_and_expr(ParseContext *pc, int *token_index, bool
|
||||
|
||||
AstNode *operand_2 = ast_parse_bit_shift_expr(pc, token_index, true);
|
||||
|
||||
AstNode *node = ast_create_node(NodeTypeBinOpExpr, token);
|
||||
AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token);
|
||||
node->data.bin_op_expr.op1 = operand_1;
|
||||
node->data.bin_op_expr.bin_op = BinOpTypeBinAnd;
|
||||
node->data.bin_op_expr.op2 = operand_2;
|
||||
@ -848,7 +860,7 @@ static AstNode *ast_parse_bin_xor_expr(ParseContext *pc, int *token_index, bool
|
||||
|
||||
AstNode *operand_2 = ast_parse_bin_and_expr(pc, token_index, true);
|
||||
|
||||
AstNode *node = ast_create_node(NodeTypeBinOpExpr, token);
|
||||
AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token);
|
||||
node->data.bin_op_expr.op1 = operand_1;
|
||||
node->data.bin_op_expr.bin_op = BinOpTypeBinXor;
|
||||
node->data.bin_op_expr.op2 = operand_2;
|
||||
@ -871,7 +883,7 @@ static AstNode *ast_parse_bin_or_expr(ParseContext *pc, int *token_index, bool m
|
||||
|
||||
AstNode *operand_2 = ast_parse_bin_xor_expr(pc, token_index, true);
|
||||
|
||||
AstNode *node = ast_create_node(NodeTypeBinOpExpr, token);
|
||||
AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token);
|
||||
node->data.bin_op_expr.op1 = operand_1;
|
||||
node->data.bin_op_expr.bin_op = BinOpTypeBinOr;
|
||||
node->data.bin_op_expr.op2 = operand_2;
|
||||
@ -920,7 +932,7 @@ static AstNode *ast_parse_comparison_expr(ParseContext *pc, int *token_index, bo
|
||||
|
||||
AstNode *operand_2 = ast_parse_bin_or_expr(pc, token_index, true);
|
||||
|
||||
AstNode *node = ast_create_node(NodeTypeBinOpExpr, token);
|
||||
AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token);
|
||||
node->data.bin_op_expr.op1 = operand_1;
|
||||
node->data.bin_op_expr.bin_op = cmp_op;
|
||||
node->data.bin_op_expr.op2 = operand_2;
|
||||
@ -943,7 +955,7 @@ static AstNode *ast_parse_bool_and_expr(ParseContext *pc, int *token_index, bool
|
||||
|
||||
AstNode *operand_2 = ast_parse_comparison_expr(pc, token_index, true);
|
||||
|
||||
AstNode *node = ast_create_node(NodeTypeBinOpExpr, token);
|
||||
AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token);
|
||||
node->data.bin_op_expr.op1 = operand_1;
|
||||
node->data.bin_op_expr.bin_op = BinOpTypeBoolAnd;
|
||||
node->data.bin_op_expr.op2 = operand_2;
|
||||
@ -958,7 +970,7 @@ static AstNode *ast_parse_return_expr(ParseContext *pc, int *token_index, bool m
|
||||
Token *return_tok = &pc->tokens->at(*token_index);
|
||||
if (return_tok->id == TokenIdKeywordReturn) {
|
||||
*token_index += 1;
|
||||
AstNode *node = ast_create_node(NodeTypeReturnExpr, return_tok);
|
||||
AstNode *node = ast_create_node(pc, NodeTypeReturnExpr, return_tok);
|
||||
node->data.return_expr.expr = ast_parse_expression(pc, token_index, false);
|
||||
return node;
|
||||
} else if (mandatory) {
|
||||
@ -983,7 +995,7 @@ static AstNode *ast_parse_bool_or_expr(ParseContext *pc, int *token_index, bool
|
||||
|
||||
AstNode *operand_2 = ast_parse_bool_and_expr(pc, token_index, true);
|
||||
|
||||
AstNode *node = ast_create_node(NodeTypeBinOpExpr, token);
|
||||
AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token);
|
||||
node->data.bin_op_expr.op1 = operand_1;
|
||||
node->data.bin_op_expr.bin_op = BinOpTypeBoolOr;
|
||||
node->data.bin_op_expr.op2 = operand_2;
|
||||
@ -1046,7 +1058,7 @@ static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandato
|
||||
}
|
||||
*token_index += 1;
|
||||
|
||||
AstNode *node = ast_create_node(NodeTypeBlock, l_brace);
|
||||
AstNode *node = ast_create_node(pc, NodeTypeBlock, l_brace);
|
||||
|
||||
for (;;) {
|
||||
Token *token = &pc->tokens->at(*token_index);
|
||||
@ -1092,7 +1104,7 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, int *token_index, bool mand
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AstNode *node = ast_create_node(NodeTypeFnProto, token);
|
||||
AstNode *node = ast_create_node(pc, NodeTypeFnProto, token);
|
||||
node->data.fn_proto.visib_mod = visib_mod;
|
||||
node->data.fn_proto.directives = pc->directive_list;
|
||||
pc->directive_list = nullptr;
|
||||
@ -1125,7 +1137,7 @@ static AstNode *ast_parse_fn_def(ParseContext *pc, int *token_index, bool mandat
|
||||
AstNode *fn_proto = ast_parse_fn_proto(pc, token_index, mandatory);
|
||||
if (!fn_proto)
|
||||
return nullptr;
|
||||
AstNode *node = ast_create_node_with_node(NodeTypeFnDef, fn_proto);
|
||||
AstNode *node = ast_create_node_with_node(pc, NodeTypeFnDef, fn_proto);
|
||||
|
||||
node->data.fn_def.fn_proto = fn_proto;
|
||||
node->data.fn_def.body = ast_parse_block(pc, token_index, true);
|
||||
@ -1138,7 +1150,7 @@ FnDecl : FnProto token(Semicolon)
|
||||
*/
|
||||
static AstNode *ast_parse_fn_decl(ParseContext *pc, int token_index, int *new_token_index) {
|
||||
AstNode *fn_proto = ast_parse_fn_proto(pc, &token_index, true);
|
||||
AstNode *node = ast_create_node_with_node(NodeTypeFnDecl, fn_proto);
|
||||
AstNode *node = ast_create_node_with_node(pc, NodeTypeFnDecl, fn_proto);
|
||||
|
||||
node->data.fn_decl.fn_proto = fn_proto;
|
||||
|
||||
@ -1166,7 +1178,7 @@ static AstNode *ast_parse_extern_block(ParseContext *pc, int *token_index, bool
|
||||
}
|
||||
*token_index += 1;
|
||||
|
||||
AstNode *node = ast_create_node(NodeTypeExternBlock, extern_kw);
|
||||
AstNode *node = ast_create_node(pc, NodeTypeExternBlock, extern_kw);
|
||||
|
||||
node->data.extern_block.directives = pc->directive_list;
|
||||
pc->directive_list = nullptr;
|
||||
@ -1184,7 +1196,7 @@ static AstNode *ast_parse_extern_block(ParseContext *pc, int *token_index, bool
|
||||
Token *token = &pc->tokens->at(*token_index);
|
||||
if (token->id == TokenIdRBrace) {
|
||||
if (pc->directive_list->length > 0) {
|
||||
ast_error(directive_token, "invalid directive");
|
||||
ast_error(pc, directive_token, "invalid directive");
|
||||
}
|
||||
pc->directive_list = nullptr;
|
||||
|
||||
@ -1216,7 +1228,7 @@ static AstNode *ast_parse_root_export_decl(ParseContext *pc, int *token_index, b
|
||||
|
||||
*token_index += 2;
|
||||
|
||||
AstNode *node = ast_create_node(NodeTypeRootExportDecl, export_kw);
|
||||
AstNode *node = ast_create_node(pc, NodeTypeRootExportDecl, export_kw);
|
||||
node->data.root_export_decl.directives = pc->directive_list;
|
||||
pc->directive_list = nullptr;
|
||||
|
||||
@ -1254,7 +1266,7 @@ static AstNode *ast_parse_use(ParseContext *pc, int *token_index, bool mandatory
|
||||
*token_index += 1;
|
||||
ast_expect_token(pc, semicolon, TokenIdSemicolon);
|
||||
|
||||
AstNode *node = ast_create_node(NodeTypeUse, use_kw);
|
||||
AstNode *node = ast_create_node(pc, NodeTypeUse, use_kw);
|
||||
|
||||
parse_string_literal(pc, use_name, &node->data.use.path);
|
||||
|
||||
@ -1299,7 +1311,7 @@ static void ast_parse_top_level_decls(ParseContext *pc, int *token_index, ZigLis
|
||||
}
|
||||
|
||||
if (pc->directive_list->length > 0) {
|
||||
ast_error(directive_token, "invalid directive");
|
||||
ast_error(pc, directive_token, "invalid directive");
|
||||
}
|
||||
pc->directive_list = nullptr;
|
||||
|
||||
@ -1312,7 +1324,7 @@ static void ast_parse_top_level_decls(ParseContext *pc, int *token_index, ZigLis
|
||||
Root : many(TopLevelDecl) token(EOF)
|
||||
*/
|
||||
static AstNode *ast_parse_root(ParseContext *pc, int *token_index) {
|
||||
AstNode *node = ast_create_node(NodeTypeRoot, &pc->tokens->at(*token_index));
|
||||
AstNode *node = ast_create_node(pc, NodeTypeRoot, &pc->tokens->at(*token_index));
|
||||
|
||||
ast_parse_top_level_decls(pc, token_index, &node->data.root.top_level_decls);
|
||||
|
||||
@ -1323,8 +1335,10 @@ static AstNode *ast_parse_root(ParseContext *pc, int *token_index) {
|
||||
return node;
|
||||
}
|
||||
|
||||
AstNode *ast_parse(Buf *buf, ZigList<Token> *tokens) {
|
||||
AstNode *ast_parse(Buf *buf, ZigList<Token> *tokens, ImportTableEntry *owner, ErrColor err_color) {
|
||||
ParseContext pc = {0};
|
||||
pc.err_color = err_color;
|
||||
pc.owner = owner;
|
||||
pc.buf = buf;
|
||||
pc.tokens = tokens;
|
||||
int token_index = 0;
|
||||
|
||||
@ -11,9 +11,11 @@
|
||||
#include "list.hpp"
|
||||
#include "buffer.hpp"
|
||||
#include "tokenizer.hpp"
|
||||
#include "errmsg.hpp"
|
||||
|
||||
struct AstNode;
|
||||
struct CodeGenNode;
|
||||
struct ImportTableEntry;
|
||||
|
||||
enum NodeType {
|
||||
NodeTypeRoot,
|
||||
@ -166,10 +168,10 @@ struct AstNodeUse {
|
||||
|
||||
struct AstNode {
|
||||
enum NodeType type;
|
||||
AstNode *parent;
|
||||
int line;
|
||||
int column;
|
||||
CodeGenNode *codegen_node;
|
||||
ImportTableEntry *owner;
|
||||
union {
|
||||
AstNodeRoot root;
|
||||
AstNodeRootExportDecl root_export_decl;
|
||||
@ -198,7 +200,7 @@ void ast_token_error(Token *token, const char *format, ...);
|
||||
|
||||
|
||||
// This function is provided by generated code, generated by parsergen.cpp
|
||||
AstNode * ast_parse(Buf *buf, ZigList<Token> *tokens);
|
||||
AstNode * ast_parse(Buf *buf, ZigList<Token> *tokens, ImportTableEntry *owner, ErrColor err_color);
|
||||
|
||||
const char *node_type_str(NodeType node_type);
|
||||
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
#include "codegen.hpp"
|
||||
#include "hash_map.hpp"
|
||||
#include "zig_llvm.hpp"
|
||||
#include "errmsg.hpp"
|
||||
|
||||
struct FnTableEntry;
|
||||
|
||||
@ -30,6 +31,8 @@ struct ImportTableEntry {
|
||||
AstNode *root;
|
||||
Buf *path; // relative to root_source_dir
|
||||
LLVMZigDIFile *di_file;
|
||||
Buf *source_code;
|
||||
ZigList<int> *line_offsets;
|
||||
|
||||
// reminder: hash tables must be initialized before use
|
||||
HashMap<Buf *, FnTableEntry *, buf_hash, buf_eql_buf> fn_table;
|
||||
@ -47,7 +50,7 @@ struct FnTableEntry {
|
||||
|
||||
struct CodeGen {
|
||||
LLVMModuleRef module;
|
||||
ZigList<ErrorMsg> errors;
|
||||
ZigList<ErrorMsg*> errors;
|
||||
LLVMBuilderRef builder;
|
||||
LLVMZigDIBuilder *dbuilder;
|
||||
LLVMZigDICompileUnit *compile_unit;
|
||||
@ -77,7 +80,14 @@ struct CodeGen {
|
||||
Buf *root_source_dir;
|
||||
Buf *root_out_name;
|
||||
ZigList<LLVMZigDIScope *> block_scopes;
|
||||
|
||||
// The function definitions this module includes. There must be a corresponding
|
||||
// fn_protos entry.
|
||||
ZigList<FnTableEntry *> fn_defs;
|
||||
// The function prototypes this module includes. In the case of external declarations,
|
||||
// there will not be a corresponding fn_defs entry.
|
||||
ZigList<FnTableEntry *> fn_protos;
|
||||
|
||||
OutType out_type;
|
||||
FnTableEntry *cur_fn;
|
||||
bool c_stdint_used;
|
||||
@ -86,6 +96,8 @@ struct CodeGen {
|
||||
int version_minor;
|
||||
int version_patch;
|
||||
bool verbose;
|
||||
ErrColor err_color;
|
||||
ImportTableEntry *root_import;
|
||||
};
|
||||
|
||||
struct TypeNode {
|
||||
|
||||
@ -104,6 +104,7 @@ enum TokenizeState {
|
||||
TokenizeStateBang,
|
||||
TokenizeStateLessThan,
|
||||
TokenizeStateGreaterThan,
|
||||
TokenizeStateError,
|
||||
};
|
||||
|
||||
|
||||
@ -116,27 +117,25 @@ struct Tokenize {
|
||||
int column;
|
||||
Token *cur_tok;
|
||||
int multi_line_comment_count;
|
||||
Tokenization *out;
|
||||
};
|
||||
|
||||
__attribute__ ((format (printf, 2, 3)))
|
||||
static void tokenize_error(Tokenize *t, const char *format, ...) {
|
||||
int line;
|
||||
int column;
|
||||
t->state = TokenizeStateError;
|
||||
|
||||
if (t->cur_tok) {
|
||||
line = t->cur_tok->start_line + 1;
|
||||
column = t->cur_tok->start_column + 1;
|
||||
t->out->err_line = t->cur_tok->start_line;
|
||||
t->out->err_column = t->cur_tok->start_column;
|
||||
} else {
|
||||
line = t->line + 1;
|
||||
column = t->column + 1;
|
||||
t->out->err_line = t->line;
|
||||
t->out->err_column = t->column;
|
||||
}
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
fprintf(stderr, "Error: Line %d, column %d: ", line, column);
|
||||
vfprintf(stderr, format, ap);
|
||||
fprintf(stderr, "\n");
|
||||
t->out->err = buf_vprintf(format, ap);
|
||||
va_end(ap);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static void begin_token(Tokenize *t, TokenId id) {
|
||||
@ -187,13 +186,20 @@ static void end_token(Tokenize *t) {
|
||||
t->cur_tok = nullptr;
|
||||
}
|
||||
|
||||
ZigList<Token> *tokenize(Buf *buf) {
|
||||
void tokenize(Buf *buf, Tokenization *out) {
|
||||
Tokenize t = {0};
|
||||
t.tokens = allocate<ZigList<Token>>(1);
|
||||
t.out = out;
|
||||
t.tokens = out->tokens = allocate<ZigList<Token>>(1);
|
||||
t.buf = buf;
|
||||
|
||||
out->line_offsets = allocate<ZigList<int>>(1);
|
||||
|
||||
out->line_offsets->append(0);
|
||||
for (t.pos = 0; t.pos < buf_len(t.buf); t.pos += 1) {
|
||||
uint8_t c = buf_ptr(t.buf)[t.pos];
|
||||
switch (t.state) {
|
||||
case TokenizeStateError:
|
||||
break;
|
||||
case TokenizeStateStart:
|
||||
switch (c) {
|
||||
case WHITESPACE:
|
||||
@ -509,6 +515,7 @@ ZigList<Token> *tokenize(Buf *buf) {
|
||||
break;
|
||||
}
|
||||
if (c == '\n') {
|
||||
out->line_offsets->append(t.pos + 1);
|
||||
t.line += 1;
|
||||
t.column = 0;
|
||||
} else {
|
||||
@ -518,6 +525,7 @@ ZigList<Token> *tokenize(Buf *buf) {
|
||||
// EOF
|
||||
switch (t.state) {
|
||||
case TokenizeStateStart:
|
||||
case TokenizeStateError:
|
||||
break;
|
||||
case TokenizeStateString:
|
||||
tokenize_error(&t, "unterminated string");
|
||||
@ -544,11 +552,12 @@ ZigList<Token> *tokenize(Buf *buf) {
|
||||
tokenize_error(&t, "unterminated multi-line comment");
|
||||
break;
|
||||
}
|
||||
t.pos = -1;
|
||||
begin_token(&t, TokenIdEof);
|
||||
end_token(&t);
|
||||
assert(!t.cur_tok);
|
||||
return t.tokens;
|
||||
if (t.state != TokenizeStateError) {
|
||||
t.pos = -1;
|
||||
begin_token(&t, TokenIdEof);
|
||||
end_token(&t);
|
||||
assert(!t.cur_tok);
|
||||
}
|
||||
}
|
||||
|
||||
static const char * token_name(Token *token) {
|
||||
|
||||
@ -65,7 +65,17 @@ struct Token {
|
||||
int start_column;
|
||||
};
|
||||
|
||||
ZigList<Token> *tokenize(Buf *buf);
|
||||
struct Tokenization {
|
||||
ZigList<Token> *tokens;
|
||||
ZigList<int> *line_offsets;
|
||||
|
||||
// if an error occurred
|
||||
Buf *err;
|
||||
int err_line;
|
||||
int err_column;
|
||||
};
|
||||
|
||||
void tokenize(Buf *buf, Tokenization *out_tokenization);
|
||||
|
||||
void print_tokens(Buf *buf, ZigList<Token> *tokens);
|
||||
|
||||
|
||||
@ -16,8 +16,6 @@
|
||||
|
||||
#define BREAKPOINT __asm("int $0x03")
|
||||
|
||||
static const int COMPILE_FAILED_ERR_CODE = 10; // chosen with a random number generator
|
||||
|
||||
void zig_panic(const char *format, ...)
|
||||
__attribute__((cold))
|
||||
__attribute__ ((noreturn))
|
||||
|
||||
@ -14,13 +14,13 @@
|
||||
|
||||
struct TestSourceFile {
|
||||
const char *relative_path;
|
||||
const char *text;
|
||||
const char *source_code;
|
||||
};
|
||||
|
||||
struct TestCase {
|
||||
const char *case_name;
|
||||
const char *output;
|
||||
const char *source;
|
||||
ZigList<TestSourceFile> source_files;
|
||||
ZigList<const char *> compile_errors;
|
||||
ZigList<const char *> compiler_args;
|
||||
ZigList<const char *> program_args;
|
||||
@ -31,11 +31,20 @@ static const char *tmp_source_path = ".tmp_source.zig";
|
||||
static const char *tmp_exe_path = "./.tmp_exe";
|
||||
static const char *zig_exe = "./zig";
|
||||
|
||||
static void add_simple_case(const char *case_name, const char *source, const char *output) {
|
||||
static void add_source_file(TestCase *test_case, const char *path, const char *source) {
|
||||
test_case->source_files.add_one();
|
||||
test_case->source_files.last().relative_path = path;
|
||||
test_case->source_files.last().source_code = source;
|
||||
}
|
||||
|
||||
static TestCase *add_simple_case(const char *case_name, const char *source, const char *output) {
|
||||
TestCase *test_case = allocate<TestCase>(1);
|
||||
test_case->case_name = case_name;
|
||||
test_case->output = output;
|
||||
test_case->source = source;
|
||||
|
||||
test_case->source_files.resize(1);
|
||||
test_case->source_files.at(0).relative_path = tmp_source_path;
|
||||
test_case->source_files.at(0).source_code = source;
|
||||
|
||||
test_case->compiler_args.append("build");
|
||||
test_case->compiler_args.append(tmp_source_path);
|
||||
@ -48,17 +57,23 @@ static void add_simple_case(const char *case_name, const char *source, const cha
|
||||
test_case->compiler_args.append("--release");
|
||||
test_case->compiler_args.append("--strip");
|
||||
test_case->compiler_args.append("--verbose");
|
||||
test_case->compiler_args.append("--color");
|
||||
test_case->compiler_args.append("on");
|
||||
|
||||
test_cases.append(test_case);
|
||||
|
||||
return test_case;
|
||||
}
|
||||
|
||||
static void add_compile_fail_case(const char *case_name, const char *source, int count, ...) {
|
||||
static TestCase *add_compile_fail_case(const char *case_name, const char *source, int count, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, count);
|
||||
|
||||
TestCase *test_case = allocate<TestCase>(1);
|
||||
test_case->case_name = case_name;
|
||||
test_case->source = source;
|
||||
test_case->source_files.resize(1);
|
||||
test_case->source_files.at(0).relative_path = tmp_source_path;
|
||||
test_case->source_files.at(0).source_code = source;
|
||||
|
||||
for (int i = 0; i < count; i += 1) {
|
||||
const char *arg = va_arg(ap, const char *);
|
||||
@ -76,6 +91,8 @@ static void add_compile_fail_case(const char *case_name, const char *source, int
|
||||
test_cases.append(test_case);
|
||||
|
||||
va_end(ap);
|
||||
|
||||
return test_case;
|
||||
}
|
||||
|
||||
static void add_compiling_test_cases(void) {
|
||||
@ -133,13 +150,52 @@ static void add_compiling_test_cases(void) {
|
||||
exit(0);
|
||||
}
|
||||
)SOURCE", "OK\n");
|
||||
|
||||
{
|
||||
TestCase *tc = add_simple_case("multiple files with private function", R"SOURCE(
|
||||
use "libc.zig";
|
||||
use "foo.zig";
|
||||
|
||||
export fn _start() -> unreachable {
|
||||
private_function();
|
||||
}
|
||||
|
||||
fn private_function() -> unreachable {
|
||||
print_text();
|
||||
exit(0);
|
||||
}
|
||||
)SOURCE", "OK\n");
|
||||
|
||||
add_source_file(tc, "libc.zig", R"SOURCE(
|
||||
#link("c")
|
||||
extern {
|
||||
pub fn puts(s: *mut u8) -> i32;
|
||||
pub fn exit(code: i32) -> unreachable;
|
||||
}
|
||||
)SOURCE");
|
||||
|
||||
add_source_file(tc, "foo.zig", R"SOURCE(
|
||||
use "libc.zig";
|
||||
|
||||
// purposefully conflicting function with main source file
|
||||
// but it's private so it should be OK
|
||||
fn private_function() {
|
||||
puts("OK");
|
||||
}
|
||||
|
||||
pub fn print_text() {
|
||||
private_function();
|
||||
}
|
||||
)SOURCE");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void add_compile_failure_test_cases(void) {
|
||||
add_compile_fail_case("multiple function definitions", R"SOURCE(
|
||||
fn a() {}
|
||||
fn a() {}
|
||||
)SOURCE", 1, "Line 3, column 1: redefinition of 'a'");
|
||||
)SOURCE", 1, ".tmp_source.zig:3:1: error: redefinition of 'a'");
|
||||
|
||||
add_compile_fail_case("bad directive", R"SOURCE(
|
||||
#bogus1("")
|
||||
@ -148,37 +204,37 @@ extern {
|
||||
}
|
||||
#bogus2("")
|
||||
fn a() {}
|
||||
)SOURCE", 2, "Line 2, column 1: invalid directive: 'bogus1'",
|
||||
"Line 6, column 1: invalid directive: 'bogus2'");
|
||||
)SOURCE", 2, ".tmp_source.zig:2:1: error: invalid directive: 'bogus1'",
|
||||
".tmp_source.zig:6:1: error: invalid directive: 'bogus2'");
|
||||
|
||||
add_compile_fail_case("unreachable with return", R"SOURCE(
|
||||
fn a() -> unreachable {return;}
|
||||
)SOURCE", 1, "Line 2, column 24: return statement in function with unreachable return type");
|
||||
)SOURCE", 1, ".tmp_source.zig:2:24: error: return statement in function with unreachable return type");
|
||||
|
||||
add_compile_fail_case("control reaches end of non-void function", R"SOURCE(
|
||||
fn a() -> i32 {}
|
||||
)SOURCE", 1, "Line 2, column 1: control reaches end of non-void function");
|
||||
)SOURCE", 1, ".tmp_source.zig:2:1: error: control reaches end of non-void function");
|
||||
|
||||
add_compile_fail_case("undefined function call", R"SOURCE(
|
||||
fn a() {
|
||||
b();
|
||||
}
|
||||
)SOURCE", 1, "Line 3, column 5: undefined function: 'b'");
|
||||
)SOURCE", 1, ".tmp_source.zig:3:5: error: undefined function: 'b'");
|
||||
|
||||
add_compile_fail_case("wrong number of arguments", R"SOURCE(
|
||||
fn a() {
|
||||
b(1);
|
||||
}
|
||||
fn b(a: i32, b: i32, c: i32) { }
|
||||
)SOURCE", 1, "Line 3, column 5: wrong number of arguments. Expected 3, got 1.");
|
||||
)SOURCE", 1, ".tmp_source.zig:3:5: error: wrong number of arguments. Expected 3, got 1.");
|
||||
|
||||
add_compile_fail_case("invalid type", R"SOURCE(
|
||||
fn a() -> bogus {}
|
||||
)SOURCE", 1, "Line 2, column 11: invalid type name: 'bogus'");
|
||||
)SOURCE", 1, ".tmp_source.zig:2:11: error: invalid type name: 'bogus'");
|
||||
|
||||
add_compile_fail_case("pointer to unreachable", R"SOURCE(
|
||||
fn a() -> *mut unreachable {}
|
||||
)SOURCE", 1, "Line 2, column 11: pointer to unreachable not allowed");
|
||||
)SOURCE", 1, ".tmp_source.zig:2:11: error: pointer to unreachable not allowed");
|
||||
|
||||
add_compile_fail_case("unreachable code", R"SOURCE(
|
||||
fn a() {
|
||||
@ -187,12 +243,16 @@ fn a() {
|
||||
}
|
||||
|
||||
fn b() {}
|
||||
)SOURCE", 1, "Line 4, column 5: unreachable code");
|
||||
)SOURCE", 1, ".tmp_source.zig:4:5: error: unreachable code");
|
||||
|
||||
add_compile_fail_case("bad version string", R"SOURCE(
|
||||
#version("aoeu")
|
||||
export executable "test";
|
||||
)SOURCE", 1, "Line 2, column 1: invalid version string");
|
||||
)SOURCE", 1, ".tmp_source.zig:2:1: error: invalid version string");
|
||||
|
||||
add_compile_fail_case("bad import", R"SOURCE(
|
||||
use "bogus-does-not-exist.zig";
|
||||
)SOURCE", 1, ".tmp_source.zig:2:1: error: unable to open \"./bogus-does-not-exist.zig\": file not found");
|
||||
}
|
||||
|
||||
static void print_compiler_invokation(TestCase *test_case, Buf *zig_stderr) {
|
||||
@ -205,7 +265,12 @@ static void print_compiler_invokation(TestCase *test_case, Buf *zig_stderr) {
|
||||
}
|
||||
|
||||
static void run_test(TestCase *test_case) {
|
||||
os_write_file(buf_create_from_str(tmp_source_path), buf_create_from_str(test_case->source));
|
||||
for (int i = 0; i < test_case->source_files.length; i += 1) {
|
||||
TestSourceFile *test_source = &test_case->source_files.at(i);
|
||||
os_write_file(
|
||||
buf_create_from_str(test_source->relative_path),
|
||||
buf_create_from_str(test_source->source_code));
|
||||
}
|
||||
|
||||
Buf zig_stderr = BUF_INIT;
|
||||
Buf zig_stdout = BUF_INIT;
|
||||
@ -263,6 +328,11 @@ static void run_test(TestCase *test_case) {
|
||||
printf("=======================================\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (int i = 0; i < test_case->source_files.length; i += 1) {
|
||||
TestSourceFile *test_source = &test_case->source_files.at(i);
|
||||
remove(test_source->relative_path);
|
||||
}
|
||||
}
|
||||
|
||||
static void run_all_tests(void) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user