support linker directives

now you can depend on libc in zig language instead of it being
hardcoded in the compiler.
This commit is contained in:
Andrew Kelley 2015-11-24 23:44:41 -07:00
parent afac1a0123
commit 09f68c7c33
7 changed files with 120 additions and 7 deletions

View File

@ -40,12 +40,13 @@ readable, safe, optimal, and concise code to solve any computing problem.
## Roadmap ## Roadmap
* don't hardcode the link against libc
* C style comments. * C style comments.
* Unit tests. * Unit tests.
* Simple .so library * Simple .so library
* Multiple files * Multiple files
* figure out integers * figure out integers
* inline assembly and syscalls
* running code at compile time
* implement a simple game using SDL2 * implement a simple game using SDL2
* How should the Widget use case be solved? In Genesis I'm using C++ and inheritance. * How should the Widget use case be solved? In Genesis I'm using C++ and inheritance.
@ -74,13 +75,13 @@ Root : many(TopLevelDecl) token(EOF)
TopLevelDecl : FnDef | ExternBlock TopLevelDecl : FnDef | ExternBlock
ExternBlock : token(Extern) token(LBrace) many(FnProtoDecl) token(RBrace) ExternBlock : many(Directive) token(Extern) token(LBrace) many(FnProtoDecl) token(RBrace)
FnProto : token(Fn) token(Symbol) ParamDeclList option(token(Arrow) Type) FnProto : token(Fn) token(Symbol) ParamDeclList option(token(Arrow) Type)
FnDecl : FnProto token(Semicolon) FnDecl : FnProto token(Semicolon)
FnDef : FnProto Block FnDef : many(Directive) FnProto Block
ParamDeclList : token(LParen) list(ParamDecl, token(Comma)) token(RParen) ParamDeclList : token(LParen) list(ParamDecl, token(Comma)) token(RParen)
@ -101,4 +102,6 @@ ReturnStatement : token(Return) Expression token(Semicolon)
Expression : token(Number) | token(String) | token(Unreachable) | FnCall Expression : token(Number) | token(String) | token(Unreachable) | FnCall
FnCall : token(Symbol) token(LParen) list(Expression, token(Comma)) token(RParen) FnCall : token(Symbol) token(LParen) list(Expression, token(Comma)) token(RParen)
Directive : token(NumberSign) token(Symbol) token(LParen) token(String) token(RParen)
``` ```

View File

@ -56,6 +56,7 @@ struct CodeGen {
HashMap<Buf *, FnTableEntry *, buf_hash, buf_eql_buf> fn_table; HashMap<Buf *, FnTableEntry *, buf_hash, buf_eql_buf> fn_table;
HashMap<Buf *, LLVMValueRef, buf_hash, buf_eql_buf> str_table; HashMap<Buf *, LLVMValueRef, buf_hash, buf_eql_buf> str_table;
HashMap<Buf *, TypeTableEntry *, buf_hash, buf_eql_buf> type_table; HashMap<Buf *, TypeTableEntry *, buf_hash, buf_eql_buf> type_table;
HashMap<Buf *, bool, buf_hash, buf_eql_buf> link_table;
TypeTableEntry *invalid_type_entry; TypeTableEntry *invalid_type_entry;
LLVMTargetDataRef target_data_ref; LLVMTargetDataRef target_data_ref;
unsigned pointer_size_bytes; unsigned pointer_size_bytes;
@ -86,6 +87,7 @@ CodeGen *create_codegen(AstNode *root, Buf *in_full_path) {
g->fn_table.init(32); g->fn_table.init(32);
g->str_table.init(32); g->str_table.init(32);
g->type_table.init(32); g->type_table.init(32);
g->link_table.init(32);
g->is_static = false; g->is_static = false;
g->build_type = CodeGenBuildTypeDebug; g->build_type = CodeGenBuildTypeDebug;
g->strip_debug_symbols = false; g->strip_debug_symbols = false;
@ -198,6 +200,18 @@ static void analyze_node(CodeGen *g, AstNode *node) {
} }
break; break;
case NodeTypeExternBlock: case NodeTypeExternBlock:
for (int i = 0; i < node->data.extern_block.directives->length; i += 1) {
AstNode *directive_node = node->data.extern_block.directives->at(i);
Buf *name = &directive_node->data.directive.name;
Buf *param = &directive_node->data.directive.param;
if (buf_eql_str(name, "link")) {
g->link_table.put(param, true);
} else {
add_node_error(g, node,
buf_sprintf("invalid directive: '%s'", buf_ptr(name)));
}
}
for (int fn_decl_i = 0; fn_decl_i < node->data.extern_block.fn_decls.length; fn_decl_i += 1) { 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); AstNode *fn_decl = node->data.extern_block.fn_decls.at(fn_decl_i);
analyze_node(g, fn_decl); analyze_node(g, fn_decl);
@ -306,6 +320,8 @@ static void analyze_node(CodeGen *g, AstNode *node) {
analyze_node(g, child); analyze_node(g, child);
} }
break; break;
case NodeTypeDirective:
break;
} }
} }
@ -639,6 +655,16 @@ void code_gen_link(CodeGen *g, const char *out_file) {
args.append("-o"); args.append("-o");
args.append(out_file); args.append(out_file);
args.append((const char *)buf_ptr(&out_file_o)); args.append((const char *)buf_ptr(&out_file_o));
args.append("-lc");
auto it = g->link_table.entry_iterator();
for (;;) {
auto *entry = it.next();
if (!entry)
break;
Buf *arg = buf_sprintf("-l%s", buf_ptr(entry->key));
args.append(buf_ptr(arg));
}
os_spawn_process("ld", args, false); os_spawn_process("ld", args, false);
} }

View File

@ -48,6 +48,8 @@ const char *node_type_str(NodeType node_type) {
return "FnCall"; return "FnCall";
case NodeTypeExternBlock: case NodeTypeExternBlock:
return "ExternBlock"; return "ExternBlock";
case NodeTypeDirective:
return "Directive";
} }
zig_unreachable(); zig_unreachable();
} }
@ -158,13 +160,23 @@ struct ParseContext {
Buf *buf; Buf *buf;
AstNode *root; AstNode *root;
ZigList<Token> *tokens; ZigList<Token> *tokens;
ZigList<AstNode *> *directive_list;
}; };
static AstNode *ast_create_node(NodeType type, Token *first_token) { static AstNode *ast_create_node_no_line_info(NodeType type) {
AstNode *node = allocate<AstNode>(1); AstNode *node = allocate<AstNode>(1);
node->type = type; node->type = type;
return node;
}
static void ast_update_node_line_info(AstNode *node, Token *first_token) {
node->line = first_token->start_line; node->line = first_token->start_line;
node->column = first_token->start_column; 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);
ast_update_node_line_info(node, first_token);
return node; return node;
} }
@ -536,7 +548,57 @@ static AstNode *ast_parse_fn_decl(ParseContext *pc, int token_index, int *new_to
} }
/* /*
ExternBlock : token(Extern) token(LBrace) many(FnProtoDecl) token(RBrace) Directive : token(NumberSign) token(Symbol) token(LParen) token(String) token(RParen)
*/
static AstNode *ast_parse_directive(ParseContext *pc, int token_index, int *new_token_index) {
Token *number_sign = &pc->tokens->at(token_index);
token_index += 1;
ast_expect_token(pc, number_sign, TokenIdNumberSign);
AstNode *node = ast_create_node(NodeTypeDirective, number_sign);
Token *name_symbol = &pc->tokens->at(token_index);
token_index += 1;
ast_expect_token(pc, name_symbol, TokenIdSymbol);
ast_buf_from_token(pc, name_symbol, &node->data.directive.name);
Token *l_paren = &pc->tokens->at(token_index);
token_index += 1;
ast_expect_token(pc, l_paren, TokenIdLParen);
Token *param_str = &pc->tokens->at(token_index);
token_index += 1;
ast_expect_token(pc, param_str, TokenIdStringLiteral);
parse_string_literal(pc, param_str, &node->data.directive.param);
Token *r_paren = &pc->tokens->at(token_index);
token_index += 1;
ast_expect_token(pc, r_paren, TokenIdRParen);
*new_token_index = token_index;
return node;
}
static void ast_parse_directives(ParseContext *pc, int token_index, int *new_token_index,
ZigList<AstNode *> *directives)
{
for (;;) {
Token *token = &pc->tokens->at(token_index);
if (token->id == TokenIdNumberSign) {
AstNode *directive_node = ast_parse_directive(pc, token_index, &token_index);
directives->append(directive_node);
} else {
*new_token_index = token_index;
return;
}
}
zig_unreachable();
}
/*
ExternBlock : many(Directive) token(Extern) token(LBrace) many(FnProtoDecl) token(RBrace)
*/ */
static AstNode *ast_parse_extern_block(ParseContext *pc, int token_index, int *new_token_index) { static AstNode *ast_parse_extern_block(ParseContext *pc, int token_index, int *new_token_index) {
Token *extern_kw = &pc->tokens->at(token_index); Token *extern_kw = &pc->tokens->at(token_index);
@ -545,6 +607,9 @@ static AstNode *ast_parse_extern_block(ParseContext *pc, int token_index, int *n
AstNode *node = ast_create_node(NodeTypeExternBlock, extern_kw); AstNode *node = ast_create_node(NodeTypeExternBlock, extern_kw);
node->data.extern_block.directives = pc->directive_list;
pc->directive_list = nullptr;
Token *l_brace = &pc->tokens->at(token_index); Token *l_brace = &pc->tokens->at(token_index);
token_index += 1; token_index += 1;
ast_expect_token(pc, l_brace, TokenIdLBrace); ast_expect_token(pc, l_brace, TokenIdLBrace);
@ -570,7 +635,11 @@ static void ast_parse_top_level_decls(ParseContext *pc, int token_index, int *ne
{ {
for (;;) { for (;;) {
Token *token = &pc->tokens->at(token_index); Token *token = &pc->tokens->at(token_index);
if (token->id == TokenIdKeywordFn) { if (token->id == TokenIdNumberSign) {
assert(!pc->directive_list);
pc->directive_list = allocate<ZigList<AstNode*>>(1);
ast_parse_directives(pc, token_index, &token_index, pc->directive_list);
} else if (token->id == TokenIdKeywordFn) {
AstNode *fn_decl_node = ast_parse_fn_def(pc, token_index, &token_index); AstNode *fn_decl_node = ast_parse_fn_def(pc, token_index, &token_index);
top_level_decls->append(fn_decl_node); top_level_decls->append(fn_decl_node);
} else if (token->id == TokenIdKeywordExtern) { } else if (token->id == TokenIdKeywordExtern) {

View File

@ -27,6 +27,7 @@ enum NodeType {
NodeTypeExpression, NodeTypeExpression,
NodeTypeFnCall, NodeTypeFnCall,
NodeTypeExternBlock, NodeTypeExternBlock,
NodeTypeDirective,
}; };
struct AstNodeRoot { struct AstNodeRoot {
@ -112,9 +113,15 @@ struct AstNodeFnCall {
}; };
struct AstNodeExternBlock { struct AstNodeExternBlock {
ZigList<AstNode *> *directives;
ZigList<AstNode *> fn_decls; ZigList<AstNode *> fn_decls;
}; };
struct AstNodeDirective {
Buf name;
Buf param;
};
struct AstNode { struct AstNode {
enum NodeType type; enum NodeType type;
AstNode *parent; AstNode *parent;
@ -133,6 +140,7 @@ struct AstNode {
AstNodeExpression expression; AstNodeExpression expression;
AstNodeFnCall fn_call; AstNodeFnCall fn_call;
AstNodeExternBlock extern_block; AstNodeExternBlock extern_block;
AstNodeDirective directive;
} data; } data;
}; };

View File

@ -218,6 +218,10 @@ ZigList<Token> *tokenize(Buf *buf) {
begin_token(&t, TokenIdDash); begin_token(&t, TokenIdDash);
t.state = TokenizeStateSawDash; t.state = TokenizeStateSawDash;
break; break;
case '#':
begin_token(&t, TokenIdNumberSign);
end_token(&t);
break;
default: default:
tokenize_error(&t, "invalid character: '%c'", c); tokenize_error(&t, "invalid character: '%c'", c);
} }
@ -321,6 +325,7 @@ static const char * token_name(Token *token) {
case TokenIdColon: return "Colon"; case TokenIdColon: return "Colon";
case TokenIdArrow: return "Arrow"; case TokenIdArrow: return "Arrow";
case TokenIdDash: return "Dash"; case TokenIdDash: return "Dash";
case TokenIdNumberSign: return "NumberSign";
} }
return "(invalid token)"; return "(invalid token)";
} }

View File

@ -32,6 +32,7 @@ enum TokenId {
TokenIdColon, TokenIdColon,
TokenIdArrow, TokenIdArrow,
TokenIdDash, TokenIdDash,
TokenIdNumberSign,
}; };
struct Token { struct Token {

View File

@ -1,3 +1,4 @@
#link("c")
extern { extern {
fn puts(s: *mut u8) -> i32; fn puts(s: *mut u8) -> i32;
fn exit(code: i32) -> unreachable; fn exit(code: i32) -> unreachable;