add root export declaration which is overridable by command line options
parent
4cc95174a7
commit
cb4773ce29
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
export executable "hello";
|
||||
|
||||
#link("c")
|
||||
extern {
|
||||
fn puts(s: *mut u8) -> i32;
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
export library "math";
|
||||
|
||||
export fn add(a: i32, b: i32) -> i32 {
|
||||
return a + b;
|
||||
}
|
||||
|
|
@ -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,6 +368,37 @@ static void check_fn_def_control_flow(CodeGen *g, AstNode *node) {
|
|||
static void analyze_node(CodeGen *g, AstNode *node) {
|
||||
switch (node->type) {
|
||||
case NodeTypeRoot:
|
||||
{
|
||||
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);
|
||||
|
@ -365,6 +409,10 @@ static void analyze_node(CodeGen *g, AstNode *node) {
|
|||
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) {
|
||||
AstNode *fn_decl = node->data.extern_block.fn_decls.at(fn_decl_i);
|
||||
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
63
src/main.cpp
63
src/main.cpp
|
@ -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"
|
||||
" 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"
|
||||
" --static build a static executable\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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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");
|
||||
|
|
Loading…
Reference in New Issue