add root export declaration which is overridable by command line options

master
Andrew Kelley 2015-11-27 21:24:11 -07:00
parent 4cc95174a7
commit cb4773ce29
10 changed files with 206 additions and 30 deletions

View File

@ -32,7 +32,9 @@ readable, safe, optimal, and concise code to solve any computing problem.
## Roadmap
* Simple .so library
* Math expression
* Export .so library
* Export .o file
* Multiple files
* inline assembly and syscalls
* running code at compile time
@ -66,7 +68,9 @@ zig | C equivalent | Description
### Grammar
```
Root : many(TopLevelDecl) token(EOF)
Root : RootExportDecl many(TopLevelDecl) token(EOF)
RootExportDecl : token(Export) token(Symbol) token(String) token(Semicolon)
TopLevelDecl : FnDef | ExternBlock

View File

@ -10,7 +10,22 @@ endif
syn keyword zigKeyword fn return mut const extern unreachable export pub
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
syn region zigCommentLineDoc start="//\%(//\@!\|!\)" end="$" contains=zigTodo,@Spell
syn region zigCommentBlock matchgroup=zigCommentBlock start="/\*\%(!\|\*[*/]\@!\)\@!" end="\*/" contains=zigTodo,zigCommentBlockNest,@Spell
syn region zigCommentBlockDoc matchgroup=zigCommentBlockDoc start="/\*\%(!\|\*[*/]\@!\)" end="\*/" contains=zigTodo,zigCommentBlockDocNest,@Spell
syn region zigCommentBlockNest matchgroup=zigCommentBlock start="/\*" end="\*/" contains=zigTodo,zigCommentBlockNest,@Spell contained transparent
syn region zigCommentBlockDocNest matchgroup=zigCommentBlockDoc start="/\*" end="\*/" contains=zigTodo,zigCommentBlockDocNest,@Spell contained transparent
syn keyword zigTodo contained TODO XXX
let b:current_syntax = "zig"
hi def link zigKeyword Keyword
hi def link zigType Type
hi def link zigCommentLine Comment
hi def link zigCommentLineDoc SpecialComment
hi def link zigCommentBlock zigCommentLine
hi def link zigCommentBlockDoc zigCommentLineDoc
hi def link zigTodo Todo

View File

@ -1,3 +1,5 @@
export executable "hello";
#link("c")
extern {
fn puts(s: *mut u8) -> i32;

6
example/math.zig Normal file
View File

@ -0,0 +1,6 @@
export library "math";
export fn add(a: i32, b: i32) -> i32 {
return a + b;
}

View File

@ -75,6 +75,8 @@ struct CodeGen {
ZigList<llvm::DIScope *> block_scopes;
llvm::DIFile *di_file;
ZigList<FnTableEntry *> fn_defs;
Buf *out_name;
OutType out_type;
};
struct TypeNode {
@ -103,6 +105,8 @@ CodeGen *create_codegen(AstNode *root, Buf *in_full_path) {
g->is_static = false;
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);
return g;
@ -120,6 +124,14 @@ void codegen_set_strip(CodeGen *g, bool strip) {
g->strip_debug_symbols = strip;
}
void codegen_set_out_type(CodeGen *g, OutType out_type) {
g->out_type = out_type;
}
void codegen_set_out_name(CodeGen *g, Buf *out_name) {
g->out_name = out_name;
}
static void add_node_error(CodeGen *g, AstNode *node, Buf *msg) {
g->errors.add_one();
ErrorMsg *last_msg = &g->errors.last();
@ -294,6 +306,7 @@ static void find_declarations(CodeGen *g, AstNode *node) {
case NodeTypeBlock:
case NodeTypeExpression:
case NodeTypeFnCall:
case NodeTypeRootExportDecl:
zig_unreachable();
}
}
@ -355,15 +368,50 @@ static void check_fn_def_control_flow(CodeGen *g, AstNode *node) {
static void analyze_node(CodeGen *g, AstNode *node) {
switch (node->type) {
case NodeTypeRoot:
// Iterate once over the top level declarations to build the function table
for (int i = 0; i < node->data.root.top_level_decls.length; i += 1) {
AstNode *child = node->data.root.top_level_decls.at(i);
find_declarations(g, child);
}
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_node(g, child);
{
AstNode *root_export_decl_node = node->data.root.root_export_decl;
if (root_export_decl_node) {
assert(root_export_decl_node->type == NodeTypeRootExportDecl);
if (!g->out_name)
g->out_name = &root_export_decl_node->data.root_export_decl.name;
Buf *out_type = &root_export_decl_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, root_export_decl_node,
buf_sprintf("invalid export type: '%s'", buf_ptr(out_type)));
}
if (g->out_type == OutTypeUnknown)
g->out_type = export_out_type;
} else {
if (!g->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"));
}
}
// Iterate once over the top level declarations to build the function table
for (int i = 0; i < node->data.root.top_level_decls.length; i += 1) {
AstNode *child = node->data.root.top_level_decls.at(i);
find_declarations(g, child);
}
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_node(g, child);
}
break;
}
case NodeTypeRootExportDecl:
// handled in parent
break;
case NodeTypeExternBlock:
for (int fn_decl_i = 0; fn_decl_i < node->data.extern_block.fn_decls.length; fn_decl_i += 1) {
@ -674,6 +722,7 @@ static void gen_block(CodeGen *g, AstNode *block_node, bool add_implicit_return)
case NodeTypeFnCall:
case NodeTypeExternBlock:
case NodeTypeDirective:
case NodeTypeRootExportDecl:
zig_unreachable();
}
}
@ -929,6 +978,15 @@ static Buf *get_dynamic_linker(CodeGen *g) {
}
}
/*
# static link into libfoo.a
ar cq libfoo.a foo1.o foo2.o
# dynamic link into libfoo.so
gcc -fPIC -g -Werror -pedantic -shared -Wl,-soname,libsoundio.so.1 -o libsoundio.so.1.0.3 foo1.o foo2.o -ljack -lpulse -lasound -lpthread
*/
void code_gen_link(CodeGen *g, const char *out_file) {
LLVMPassRegistryRef registry = LLVMGetGlobalPassRegistry();
LLVMInitializeCore(registry);
@ -937,6 +995,10 @@ void code_gen_link(CodeGen *g, const char *out_file) {
LLVMZigInitializeLowerIntrinsicsPass(registry);
LLVMZigInitializeUnreachableBlockElimPass(registry);
if (!out_file) {
out_file = buf_ptr(g->out_name);
}
Buf out_file_o = BUF_INIT;
buf_init_from_str(&out_file_o, out_file);
buf_append_str(&out_file_o, ".o");

View File

@ -12,6 +12,14 @@
struct CodeGen;
enum OutType {
OutTypeUnknown,
OutTypeExe,
OutTypeLib,
OutTypeObj,
};
struct ErrorMsg {
int line_start;
int column_start;
@ -30,6 +38,8 @@ 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_out_type(CodeGen *codegen, OutType out_type);
void codegen_set_out_name(CodeGen *codegen, Buf *out_name);
void semantic_analyze(CodeGen *g);

View File

@ -28,18 +28,24 @@
static int usage(const char *arg0) {
fprintf(stderr, "Usage: %s [command] [options] target\n"
"Commands:\n"
" build create an executable from target\n"
"Options:\n"
" --output output file\n"
" --version print version number and exit\n"
" -Ipath add path to header include path\n"
" --release build with optimizations on\n"
" --strip exclude debug symbols\n"
" --static build a static executable\n"
" build create executable, object, or library from target\n"
" version print version number and exit\n"
"Optional Options:\n"
" --release build with optimizations on and debug protection off\n"
" --static output will be statically linked\n"
" --strip exclude debug symbols\n"
" --export [exe|lib|obj] override output type\n"
" --name [name] override output name\n"
" --output [file] override destination path\n"
, arg0);
return EXIT_FAILURE;
}
static int version(void) {
printf("%s\n", ZIG_VERSION_STRING);
return EXIT_SUCCESS;
}
static Buf *fetch_file(FILE *f) {
int fd = fileno(f);
struct stat st;
@ -58,12 +64,12 @@ static Buf *fetch_file(FILE *f) {
return buf;
}
static int build(const char *arg0, const char *in_file, const char *out_file,
ZigList<char *> *include_paths, bool release, bool strip, bool is_static)
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 || !out_file)
if (!in_file)
return usage(arg0);
FILE *in_f;
@ -100,6 +106,10 @@ static int build(const char *arg0, const char *in_file, const char *out_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) {
@ -135,25 +145,25 @@ static int build(const char *arg0, const char *in_file, const char *out_file,
enum Cmd {
CmdNone,
CmdBuild,
CmdVersion,
};
int main(int argc, char **argv) {
char *arg0 = argv[0];
char *in_file = NULL;
char *out_file = NULL;
ZigList<char *> include_paths = {0};
bool release = false;
bool strip = false;
bool is_static = false;
OutType out_type = OutTypeUnknown;
char *out_name = NULL;
Cmd cmd = CmdNone;
for (int i = 1; i < argc; i += 1) {
char *arg = argv[i];
if (arg[0] == '-' && arg[1] == '-') {
if (strcmp(arg, "--version") == 0) {
printf("%s\n", ZIG_VERSION_STRING);
return EXIT_SUCCESS;
} else if (strcmp(arg, "--release") == 0) {
if (strcmp(arg, "--release") == 0) {
release = true;
} else if (strcmp(arg, "--strip") == 0) {
strip = true;
@ -165,15 +175,27 @@ int main(int argc, char **argv) {
i += 1;
if (strcmp(arg, "--output") == 0) {
out_file = argv[i];
} else if (strcmp(arg, "--export") == 0) {
if (strcmp(argv[i], "exe") == 0) {
out_type = OutTypeExe;
} else if (strcmp(argv[i], "lib") == 0) {
out_type = OutTypeLib;
} else if (strcmp(argv[i], "obj") == 0) {
out_type = OutTypeObj;
} else {
return usage(arg0);
}
} else if (strcmp(arg, "--name") == 0) {
out_name = argv[i];
} else {
return usage(arg0);
}
}
} else if (arg[0] == '-' && arg[1] == 'I') {
include_paths.append(arg + 2);
} else if (cmd == CmdNone) {
if (strcmp(arg, "build") == 0) {
cmd = CmdBuild;
} else if (strcmp(arg, "version") == 0) {
cmd = CmdVersion;
} else {
fprintf(stderr, "Unrecognized command: %s\n", arg);
return usage(arg0);
@ -189,6 +211,8 @@ int main(int argc, char **argv) {
return usage(arg0);
}
break;
case CmdVersion:
return usage(arg0);
}
}
}
@ -197,9 +221,10 @@ int main(int argc, char **argv) {
case CmdNone:
return usage(arg0);
case CmdBuild:
return build(arg0, in_file, out_file, &include_paths, release, strip, is_static);
return build(arg0, in_file, out_file, release, strip, is_static, out_type, out_name);
case CmdVersion:
return version();
}
zig_unreachable();
}

View File

@ -29,6 +29,8 @@ const char *node_type_str(NodeType node_type) {
switch (node_type) {
case NodeTypeRoot:
return "Root";
case NodeTypeRootExportDecl:
return "RootExportDecl";
case NodeTypeFnDef:
return "FnDef";
case NodeTypeFnDecl:
@ -68,6 +70,11 @@ void ast_print(AstNode *node, int indent) {
ast_print(child, indent + 2);
}
break;
case NodeTypeRootExportDecl:
fprintf(stderr, "%s %s '%s'\n", node_type_str(node->type),
buf_ptr(&node->data.root_export_decl.type),
buf_ptr(&node->data.root_export_decl.name));
break;
case NodeTypeFnDef:
{
fprintf(stderr, "%s\n", node_type_str(node->type));
@ -714,6 +721,36 @@ static void ast_parse_top_level_decls(ParseContext *pc, int *token_index, ZigLis
zig_unreachable();
}
static AstNode *ast_parse_root_export_decl(ParseContext *pc, int *token_index) {
Token *export_kw = &pc->tokens->at(*token_index);
if (export_kw->id != TokenIdKeywordExport)
return nullptr;
*token_index += 1;
AstNode *node = ast_create_node(NodeTypeRootExportDecl, export_kw);
Token *export_type = &pc->tokens->at(*token_index);
*token_index += 1;
ast_expect_token(pc, export_type, TokenIdSymbol);
ast_buf_from_token(pc, export_type, &node->data.root_export_decl.type);
Token *export_name = &pc->tokens->at(*token_index);
*token_index += 1;
ast_expect_token(pc, export_name, TokenIdStringLiteral);
parse_string_literal(pc, export_name, &node->data.root_export_decl.name);
Token *semicolon = &pc->tokens->at(*token_index);
*token_index += 1;
ast_expect_token(pc, semicolon, TokenIdSemicolon);
return node;
}
/*
Root : RootExportDecl many(TopLevelDecl) token(EOF)
*/
AstNode *ast_parse(Buf *buf, ZigList<Token> *tokens) {
ParseContext pc = {0};
pc.buf = buf;
@ -721,6 +758,9 @@ AstNode *ast_parse(Buf *buf, ZigList<Token> *tokens) {
pc.tokens = tokens;
int token_index = 0;
pc.root->data.root.root_export_decl = ast_parse_root_export_decl(&pc, &token_index);
ast_parse_top_level_decls(&pc, &token_index, &pc.root->data.root.top_level_decls);
if (token_index != tokens->length - 1) {

View File

@ -17,6 +17,7 @@ struct CodeGenNode;
enum NodeType {
NodeTypeRoot,
NodeTypeRootExportDecl,
NodeTypeFnProto,
NodeTypeFnDef,
NodeTypeFnDecl,
@ -31,6 +32,7 @@ enum NodeType {
};
struct AstNodeRoot {
AstNode *root_export_decl;
ZigList<AstNode *> top_level_decls;
};
@ -113,6 +115,11 @@ struct AstNodeDirective {
Buf param;
};
struct AstNodeRootExportDecl {
Buf type;
Buf name;
};
struct AstNode {
enum NodeType type;
AstNode *parent;
@ -121,6 +128,7 @@ struct AstNode {
CodeGenNode *codegen_node;
union {
AstNodeRoot root;
AstNodeRootExportDecl root_export_decl;
AstNodeFnDef fn_def;
AstNodeFnDecl fn_decl;
AstNodeFnProto fn_proto;

View File

@ -39,6 +39,10 @@ static void add_simple_case(const char *case_name, const char *source, const cha
test_case->compiler_args.append("build");
test_case->compiler_args.append(tmp_source_path);
test_case->compiler_args.append("--export");
test_case->compiler_args.append("exe");
test_case->compiler_args.append("--name");
test_case->compiler_args.append("test");
test_case->compiler_args.append("--output");
test_case->compiler_args.append(tmp_exe_path);
test_case->compiler_args.append("--release");