diff --git a/README.md b/README.md index 389b5ada0..1b5d0839d 100644 --- a/README.md +++ b/README.md @@ -40,12 +40,13 @@ readable, safe, optimal, and concise code to solve any computing problem. ## Roadmap - * don't hardcode the link against libc * C style comments. * Unit tests. * Simple .so library * Multiple files * figure out integers + * inline assembly and syscalls + * running code at compile time * implement a simple game using SDL2 * 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 -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) FnDecl : FnProto token(Semicolon) -FnDef : FnProto Block +FnDef : many(Directive) FnProto Block 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 FnCall : token(Symbol) token(LParen) list(Expression, token(Comma)) token(RParen) + +Directive : token(NumberSign) token(Symbol) token(LParen) token(String) token(RParen) ``` diff --git a/src/codegen.cpp b/src/codegen.cpp index 5cd823626..3df9ed876 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -56,6 +56,7 @@ struct CodeGen { HashMap fn_table; HashMap str_table; HashMap type_table; + HashMap link_table; TypeTableEntry *invalid_type_entry; LLVMTargetDataRef target_data_ref; unsigned pointer_size_bytes; @@ -86,6 +87,7 @@ CodeGen *create_codegen(AstNode *root, Buf *in_full_path) { g->fn_table.init(32); g->str_table.init(32); g->type_table.init(32); + g->link_table.init(32); g->is_static = false; g->build_type = CodeGenBuildTypeDebug; g->strip_debug_symbols = false; @@ -198,6 +200,18 @@ static void analyze_node(CodeGen *g, AstNode *node) { } break; 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) { AstNode *fn_decl = node->data.extern_block.fn_decls.at(fn_decl_i); analyze_node(g, fn_decl); @@ -306,6 +320,8 @@ static void analyze_node(CodeGen *g, AstNode *node) { analyze_node(g, child); } break; + case NodeTypeDirective: + break; } } @@ -639,6 +655,16 @@ void code_gen_link(CodeGen *g, const char *out_file) { args.append("-o"); args.append(out_file); 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); } diff --git a/src/parser.cpp b/src/parser.cpp index 403cd9d9b..1b256c851 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -48,6 +48,8 @@ const char *node_type_str(NodeType node_type) { return "FnCall"; case NodeTypeExternBlock: return "ExternBlock"; + case NodeTypeDirective: + return "Directive"; } zig_unreachable(); } @@ -158,13 +160,23 @@ struct ParseContext { Buf *buf; AstNode *root; ZigList *tokens; + ZigList *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(1); node->type = type; + return node; +} + +static void ast_update_node_line_info(AstNode *node, Token *first_token) { node->line = first_token->start_line; 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; } @@ -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 *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) { 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); + node->data.extern_block.directives = pc->directive_list; + pc->directive_list = nullptr; + Token *l_brace = &pc->tokens->at(token_index); token_index += 1; 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 (;;) { Token *token = &pc->tokens->at(token_index); - if (token->id == TokenIdKeywordFn) { + if (token->id == TokenIdNumberSign) { + assert(!pc->directive_list); + pc->directive_list = allocate>(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); top_level_decls->append(fn_decl_node); } else if (token->id == TokenIdKeywordExtern) { diff --git a/src/parser.hpp b/src/parser.hpp index b82c0b6ec..74627306a 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -27,6 +27,7 @@ enum NodeType { NodeTypeExpression, NodeTypeFnCall, NodeTypeExternBlock, + NodeTypeDirective, }; struct AstNodeRoot { @@ -112,9 +113,15 @@ struct AstNodeFnCall { }; struct AstNodeExternBlock { + ZigList *directives; ZigList fn_decls; }; +struct AstNodeDirective { + Buf name; + Buf param; +}; + struct AstNode { enum NodeType type; AstNode *parent; @@ -133,6 +140,7 @@ struct AstNode { AstNodeExpression expression; AstNodeFnCall fn_call; AstNodeExternBlock extern_block; + AstNodeDirective directive; } data; }; diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 9e825535e..0e83f9a5c 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -218,6 +218,10 @@ ZigList *tokenize(Buf *buf) { begin_token(&t, TokenIdDash); t.state = TokenizeStateSawDash; break; + case '#': + begin_token(&t, TokenIdNumberSign); + end_token(&t); + break; default: tokenize_error(&t, "invalid character: '%c'", c); } @@ -321,6 +325,7 @@ static const char * token_name(Token *token) { case TokenIdColon: return "Colon"; case TokenIdArrow: return "Arrow"; case TokenIdDash: return "Dash"; + case TokenIdNumberSign: return "NumberSign"; } return "(invalid token)"; } diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp index 427b44a91..e2337aea8 100644 --- a/src/tokenizer.hpp +++ b/src/tokenizer.hpp @@ -32,6 +32,7 @@ enum TokenId { TokenIdColon, TokenIdArrow, TokenIdDash, + TokenIdNumberSign, }; struct Token { diff --git a/test/hello.zig b/test/hello.zig index 6c52f8110..a7098aa16 100644 --- a/test/hello.zig +++ b/test/hello.zig @@ -1,3 +1,4 @@ +#link("c") extern { fn puts(s: *mut u8) -> i32; fn exit(code: i32) -> unreachable;