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:
parent
afac1a0123
commit
09f68c7c33
@ -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)
|
||||||
```
|
```
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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)";
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,7 @@ enum TokenId {
|
|||||||
TokenIdColon,
|
TokenIdColon,
|
||||||
TokenIdArrow,
|
TokenIdArrow,
|
||||||
TokenIdDash,
|
TokenIdDash,
|
||||||
|
TokenIdNumberSign,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Token {
|
struct Token {
|
||||||
|
@ -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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user