| author | |
| committer | |
| log | 8f0f318c393b815d547ea00741d7b1ebf99eaffe |
| tree | f5a80e8fb327f4da94a40cb3ae179dbf2c3ddf9a |
| parent | ac0c5a3707c4a79c27e60242e44231cc05964f0f |
8 files changed, 128 insertions(+), 75 deletions(-)
README.md+3-6| ... | @@ -42,9 +42,6 @@ make | ... | @@ -42,9 +42,6 @@ make |
| 42 | 42 | ||
| 43 | ## Roadmap | 43 | ## Roadmap |
| 44 | 44 | ||
| 45 | * ability to specify version | ||
| 46 | * cli ability to override library export locations | ||
| 47 | * add test for building library | ||
| 48 | * variables and parameters | 45 | * variables and parameters |
| 49 | * Export .so library | 46 | * Export .so library |
| 50 | * Multiple files | 47 | * Multiple files |
| ... | @@ -81,11 +78,11 @@ zig | C equivalent | Description | ... | @@ -81,11 +78,11 @@ zig | C equivalent | Description |
| 81 | ### Grammar | 78 | ### Grammar |
| 82 | 79 | ||
| 83 | ``` | 80 | ``` |
| 84 | Root : RootExportDecl many(TopLevelDecl) token(EOF) | 81 | Root : many(TopLevelDecl) token(EOF) |
| 85 | 82 | ||
| 86 | RootExportDecl : token(Export) token(Symbol) token(String) token(Semicolon) | 83 | TopLevelDecl : FnDef | ExternBlock | RootExportDecl |
| 87 | 84 | ||
| 88 | TopLevelDecl : FnDef | ExternBlock | 85 | RootExportDecl : many(Directive) token(Export) token(Symbol) token(String) token(Semicolon) |
| 89 | 86 | ||
| 90 | ExternBlock : many(Directive) token(Extern) token(LBrace) many(FnDecl) token(RBrace) | 87 | ExternBlock : many(Directive) token(Extern) token(LBrace) many(FnDecl) token(RBrace) |
| 91 | 88 |
example/mathtest.zig+1| ... | @@ -1,3 +1,4 @@ | ... | @@ -1,3 +1,4 @@ |
| 1 | #version("2.0.0") | ||
| 1 | export library "mathtest"; | 2 | export library "mathtest"; |
| 2 | 3 | ||
| 3 | export fn add(a: i32, b: i32) -> i32 { | 4 | export fn add(a: i32, b: i32) -> i32 { |
src/codegen.cpp+76-37| ... | @@ -10,6 +10,7 @@ | ... | @@ -10,6 +10,7 @@ |
| 10 | #include "zig_llvm.hpp" | 10 | #include "zig_llvm.hpp" |
| 11 | #include "os.hpp" | 11 | #include "os.hpp" |
| 12 | #include "config.h" | 12 | #include "config.h" |
| 13 | #include "error.hpp" | ||
| 13 | 14 | ||
| 14 | #include <stdio.h> | 15 | #include <stdio.h> |
| 15 | 16 | ||
| ... | @@ -79,6 +80,10 @@ struct CodeGen { | ... | @@ -79,6 +80,10 @@ struct CodeGen { |
| 79 | OutType out_type; | 80 | OutType out_type; |
| 80 | FnTableEntry *cur_fn; | 81 | FnTableEntry *cur_fn; |
| 81 | bool c_stdint_used; | 82 | bool c_stdint_used; |
| 83 | AstNode *root_export_decl; | ||
| 84 | int version_major; | ||
| 85 | int version_minor; | ||
| 86 | int version_patch; | ||
| 82 | }; | 87 | }; |
| 83 | 88 | ||
| 84 | struct TypeNode { | 89 | struct TypeNode { |
| ... | @@ -169,6 +174,30 @@ static bool type_is_unreachable(AstNode *type_node) { | ... | @@ -169,6 +174,30 @@ static bool type_is_unreachable(AstNode *type_node) { |
| 169 | return type_node->codegen_node->data.type_node.entry->id == TypeIdUnreachable; | 174 | return type_node->codegen_node->data.type_node.entry->id == TypeIdUnreachable; |
| 170 | } | 175 | } |
| 171 | 176 | ||
| 177 | |||
| 178 | static int parse_version_string(Buf *buf, int *major, int *minor, int *patch) { | ||
| 179 | char *dot1 = strstr(buf_ptr(buf), "."); | ||
| 180 | if (!dot1) | ||
| 181 | return ErrorInvalidFormat; | ||
| 182 | char *dot2 = strstr(dot1 + 1, "."); | ||
| 183 | if (!dot2) | ||
| 184 | return ErrorInvalidFormat; | ||
| 185 | |||
| 186 | *major = (int)strtol(buf_ptr(buf), nullptr, 10); | ||
| 187 | *minor = (int)strtol(dot1 + 1, nullptr, 10); | ||
| 188 | *patch = (int)strtol(dot2 + 1, nullptr, 10); | ||
| 189 | |||
| 190 | return ErrorNone; | ||
| 191 | } | ||
| 192 | |||
| 193 | static void set_root_export_version(CodeGen *g, Buf *version_buf, AstNode *node) { | ||
| 194 | int err; | ||
| 195 | if ((err = parse_version_string(version_buf, &g->version_major, &g->version_minor, &g->version_patch))) { | ||
| 196 | add_node_error(g, node, | ||
| 197 | buf_sprintf("invalid version string")); | ||
| 198 | } | ||
| 199 | } | ||
| 200 | |||
| 172 | static void find_declarations(CodeGen *g, AstNode *node); | 201 | static void find_declarations(CodeGen *g, AstNode *node); |
| 173 | 202 | ||
| 174 | static void resolve_type_and_recurse(CodeGen *g, AstNode *node) { | 203 | static void resolve_type_and_recurse(CodeGen *g, AstNode *node) { |
| ... | @@ -303,13 +332,25 @@ static void find_declarations(CodeGen *g, AstNode *node) { | ... | @@ -303,13 +332,25 @@ static void find_declarations(CodeGen *g, AstNode *node) { |
| 303 | case NodeTypeDirective: | 332 | case NodeTypeDirective: |
| 304 | // we handled directives in the parent function | 333 | // we handled directives in the parent function |
| 305 | break; | 334 | break; |
| 335 | case NodeTypeRootExportDecl: | ||
| 336 | for (int i = 0; i < node->data.root_export_decl.directives->length; i += 1) { | ||
| 337 | AstNode *directive_node = node->data.root_export_decl.directives->at(i); | ||
| 338 | Buf *name = &directive_node->data.directive.name; | ||
| 339 | Buf *param = &directive_node->data.directive.param; | ||
| 340 | if (buf_eql_str(name, "version")) { | ||
| 341 | set_root_export_version(g, param, directive_node); | ||
| 342 | } else { | ||
| 343 | add_node_error(g, directive_node, | ||
| 344 | buf_sprintf("invalid directive: '%s'", buf_ptr(name))); | ||
| 345 | } | ||
| 346 | } | ||
| 347 | break; | ||
| 306 | case NodeTypeFnDecl: | 348 | case NodeTypeFnDecl: |
| 307 | case NodeTypeReturnExpr: | 349 | case NodeTypeReturnExpr: |
| 308 | case NodeTypeRoot: | 350 | case NodeTypeRoot: |
| 309 | case NodeTypeBlock: | 351 | case NodeTypeBlock: |
| 310 | case NodeTypeBinOpExpr: | 352 | case NodeTypeBinOpExpr: |
| 311 | case NodeTypeFnCallExpr: | 353 | case NodeTypeFnCallExpr: |
| 312 | case NodeTypeRootExportDecl: | ||
| 313 | case NodeTypeNumberLiteral: | 354 | case NodeTypeNumberLiteral: |
| 314 | case NodeTypeStringLiteral: | 355 | case NodeTypeStringLiteral: |
| 315 | case NodeTypeUnreachable: | 356 | case NodeTypeUnreachable: |
| ... | @@ -385,36 +426,6 @@ static void analyze_node(CodeGen *g, AstNode *node) { | ... | @@ -385,36 +426,6 @@ static void analyze_node(CodeGen *g, AstNode *node) { |
| 385 | switch (node->type) { | 426 | switch (node->type) { |
| 386 | case NodeTypeRoot: | 427 | case NodeTypeRoot: |
| 387 | { | 428 | { |
| 388 | AstNode *root_export_decl_node = node->data.root.root_export_decl; | ||
| 389 | if (root_export_decl_node) { | ||
| 390 | assert(root_export_decl_node->type == NodeTypeRootExportDecl); | ||
| 391 | if (!g->out_name) | ||
| 392 | g->out_name = &root_export_decl_node->data.root_export_decl.name; | ||
| 393 | |||
| 394 | Buf *out_type = &root_export_decl_node->data.root_export_decl.type; | ||
| 395 | OutType export_out_type; | ||
| 396 | if (buf_eql_str(out_type, "executable")) { | ||
| 397 | export_out_type = OutTypeExe; | ||
| 398 | } else if (buf_eql_str(out_type, "library")) { | ||
| 399 | export_out_type = OutTypeLib; | ||
| 400 | } else if (buf_eql_str(out_type, "object")) { | ||
| 401 | export_out_type = OutTypeObj; | ||
| 402 | } else { | ||
| 403 | add_node_error(g, root_export_decl_node, | ||
| 404 | buf_sprintf("invalid export type: '%s'", buf_ptr(out_type))); | ||
| 405 | } | ||
| 406 | if (g->out_type == OutTypeUnknown) | ||
| 407 | g->out_type = export_out_type; | ||
| 408 | } else { | ||
| 409 | if (!g->out_name) { | ||
| 410 | add_node_error(g, node, | ||
| 411 | buf_sprintf("missing export declaration and output name not provided")); | ||
| 412 | } else if (g->out_type == OutTypeUnknown) { | ||
| 413 | add_node_error(g, node, | ||
| 414 | buf_sprintf("missing export declaration and export type not provided")); | ||
| 415 | } | ||
| 416 | } | ||
| 417 | |||
| 418 | // Iterate once over the top level declarations to build the function table | 429 | // Iterate once over the top level declarations to build the function table |
| 419 | for (int i = 0; i < node->data.root.top_level_decls.length; i += 1) { | 430 | for (int i = 0; i < node->data.root.top_level_decls.length; i += 1) { |
| 420 | AstNode *child = node->data.root.top_level_decls.at(i); | 431 | AstNode *child = node->data.root.top_level_decls.at(i); |
| ... | @@ -424,10 +435,40 @@ static void analyze_node(CodeGen *g, AstNode *node) { | ... | @@ -424,10 +435,40 @@ static void analyze_node(CodeGen *g, AstNode *node) { |
| 424 | AstNode *child = node->data.root.top_level_decls.at(i); | 435 | AstNode *child = node->data.root.top_level_decls.at(i); |
| 425 | analyze_node(g, child); | 436 | analyze_node(g, child); |
| 426 | } | 437 | } |
| 438 | if (!g->out_name) { | ||
| 439 | add_node_error(g, node, | ||
| 440 | buf_sprintf("missing export declaration and output name not provided")); | ||
| 441 | } else if (g->out_type == OutTypeUnknown) { | ||
| 442 | add_node_error(g, node, | ||
| 443 | buf_sprintf("missing export declaration and export type not provided")); | ||
| 444 | } | ||
| 427 | break; | 445 | break; |
| 428 | } | 446 | } |
| 429 | case NodeTypeRootExportDecl: | 447 | case NodeTypeRootExportDecl: |
| 430 | // handled in parent | 448 | if (g->root_export_decl) { |
| 449 | add_node_error(g, node, | ||
| 450 | buf_sprintf("only one root export declaration allowed")); | ||
| 451 | } else { | ||
| 452 | g->root_export_decl = node; | ||
| 453 | |||
| 454 | if (!g->out_name) | ||
| 455 | g->out_name = &node->data.root_export_decl.name; | ||
| 456 | |||
| 457 | Buf *out_type = &node->data.root_export_decl.type; | ||
| 458 | OutType export_out_type; | ||
| 459 | if (buf_eql_str(out_type, "executable")) { | ||
| 460 | export_out_type = OutTypeExe; | ||
| 461 | } else if (buf_eql_str(out_type, "library")) { | ||
| 462 | export_out_type = OutTypeLib; | ||
| 463 | } else if (buf_eql_str(out_type, "object")) { | ||
| 464 | export_out_type = OutTypeObj; | ||
| 465 | } else { | ||
| 466 | add_node_error(g, node, | ||
| 467 | buf_sprintf("invalid export type: '%s'", buf_ptr(out_type))); | ||
| 468 | } | ||
| 469 | if (g->out_type == OutTypeUnknown) | ||
| 470 | g->out_type = export_out_type; | ||
| 471 | } | ||
| 431 | break; | 472 | break; |
| 432 | case NodeTypeExternBlock: | 473 | case NodeTypeExternBlock: |
| 433 | for (int fn_decl_i = 0; fn_decl_i < node->data.extern_block.fn_decls.length; fn_decl_i += 1) { | 474 | for (int fn_decl_i = 0; fn_decl_i < node->data.extern_block.fn_decls.length; fn_decl_i += 1) { |
| ... | @@ -1401,11 +1442,9 @@ void code_gen_link(CodeGen *g, const char *out_file) { | ... | @@ -1401,11 +1442,9 @@ void code_gen_link(CodeGen *g, const char *out_file) { |
| 1401 | } | 1442 | } |
| 1402 | 1443 | ||
| 1403 | if (g->out_type == OutTypeLib) { | 1444 | if (g->out_type == OutTypeLib) { |
| 1404 | int major = 1; | 1445 | Buf *out_lib_so = buf_sprintf("lib%s.so.%d.%d.%d", |
| 1405 | int minor = 0; | 1446 | buf_ptr(g->out_name), g->version_major, g->version_minor, g->version_patch); |
| 1406 | int patch = 0; | 1447 | Buf *soname = buf_sprintf("lib%s.so.%d", buf_ptr(g->out_name), g->version_major); |
| 1407 | Buf *out_lib_so = buf_sprintf("lib%s.so.%d.%d.%d", buf_ptr(g->out_name), major, minor, patch); | ||
| 1408 | Buf *soname = buf_sprintf("lib%s.so.%d", buf_ptr(g->out_name), major); | ||
| 1409 | args.append("-shared"); | 1448 | args.append("-shared"); |
| 1410 | args.append("-soname"); | 1449 | args.append("-soname"); |
| 1411 | args.append(buf_ptr(soname)); | 1450 | args.append(buf_ptr(soname)); |
src/error.cpp+1| ... | @@ -2,6 +2,7 @@ | ... | @@ -2,6 +2,7 @@ |
| 2 | 2 | ||
| 3 | const char *err_str(int err) { | 3 | const char *err_str(int err) { |
| 4 | switch ((enum Error)err) { | 4 | switch ((enum Error)err) { |
| 5 | case ErrorNone: return "(no error)"; | ||
| 5 | case ErrorNoMem: return "out of memory"; | 6 | case ErrorNoMem: return "out of memory"; |
| 6 | case ErrorInvalidFormat: return "invalid format"; | 7 | case ErrorInvalidFormat: return "invalid format"; |
| 7 | } | 8 | } |
src/error.hpp+1| ... | @@ -9,6 +9,7 @@ | ... | @@ -9,6 +9,7 @@ |
| 9 | #define ERROR_HPP | 9 | #define ERROR_HPP |
| 10 | 10 | ||
| 11 | enum Error { | 11 | enum Error { |
| 12 | ErrorNone, | ||
| 12 | ErrorNoMem, | 13 | ErrorNoMem, |
| 13 | ErrorInvalidFormat, | 14 | ErrorInvalidFormat, |
| 14 | }; | 15 | }; |
src/parser.cpp+45-30| ... | @@ -1195,6 +1195,44 @@ static AstNode *ast_parse_extern_block(ParseContext *pc, int *token_index, bool | ... | @@ -1195,6 +1195,44 @@ static AstNode *ast_parse_extern_block(ParseContext *pc, int *token_index, bool |
| 1195 | zig_unreachable(); | 1195 | zig_unreachable(); |
| 1196 | } | 1196 | } |
| 1197 | 1197 | ||
| 1198 | /* | ||
| 1199 | RootExportDecl : many(Directive) token(Export) token(Symbol) token(String) token(Semicolon) | ||
| 1200 | */ | ||
| 1201 | static AstNode *ast_parse_root_export_decl(ParseContext *pc, int *token_index, bool mandatory) { | ||
| 1202 | assert(mandatory == false); | ||
| 1203 | |||
| 1204 | Token *export_kw = &pc->tokens->at(*token_index); | ||
| 1205 | if (export_kw->id != TokenIdKeywordExport) | ||
| 1206 | return nullptr; | ||
| 1207 | |||
| 1208 | Token *export_type = &pc->tokens->at(*token_index + 1); | ||
| 1209 | if (export_type->id != TokenIdSymbol) | ||
| 1210 | return nullptr; | ||
| 1211 | |||
| 1212 | *token_index += 2; | ||
| 1213 | |||
| 1214 | AstNode *node = ast_create_node(NodeTypeRootExportDecl, export_kw); | ||
| 1215 | node->data.root_export_decl.directives = pc->directive_list; | ||
| 1216 | pc->directive_list = nullptr; | ||
| 1217 | |||
| 1218 | ast_buf_from_token(pc, export_type, &node->data.root_export_decl.type); | ||
| 1219 | |||
| 1220 | Token *export_name = &pc->tokens->at(*token_index); | ||
| 1221 | *token_index += 1; | ||
| 1222 | ast_expect_token(pc, export_name, TokenIdStringLiteral); | ||
| 1223 | |||
| 1224 | parse_string_literal(pc, export_name, &node->data.root_export_decl.name); | ||
| 1225 | |||
| 1226 | Token *semicolon = &pc->tokens->at(*token_index); | ||
| 1227 | *token_index += 1; | ||
| 1228 | ast_expect_token(pc, semicolon, TokenIdSemicolon); | ||
| 1229 | |||
| 1230 | return node; | ||
| 1231 | } | ||
| 1232 | |||
| 1233 | /* | ||
| 1234 | TopLevelDecl : FnDef | ExternBlock | RootExportDecl | ||
| 1235 | */ | ||
| 1198 | static void ast_parse_top_level_decls(ParseContext *pc, int *token_index, ZigList<AstNode *> *top_level_decls) { | 1236 | static void ast_parse_top_level_decls(ParseContext *pc, int *token_index, ZigList<AstNode *> *top_level_decls) { |
| 1199 | for (;;) { | 1237 | for (;;) { |
| 1200 | Token *directive_token = &pc->tokens->at(*token_index); | 1238 | Token *directive_token = &pc->tokens->at(*token_index); |
| ... | @@ -1202,6 +1240,12 @@ static void ast_parse_top_level_decls(ParseContext *pc, int *token_index, ZigLis | ... | @@ -1202,6 +1240,12 @@ static void ast_parse_top_level_decls(ParseContext *pc, int *token_index, ZigLis |
| 1202 | pc->directive_list = allocate<ZigList<AstNode*>>(1); | 1240 | pc->directive_list = allocate<ZigList<AstNode*>>(1); |
| 1203 | ast_parse_directives(pc, token_index, pc->directive_list); | 1241 | ast_parse_directives(pc, token_index, pc->directive_list); |
| 1204 | 1242 | ||
| 1243 | AstNode *root_export_decl_node = ast_parse_root_export_decl(pc, token_index, false); | ||
| 1244 | if (root_export_decl_node) { | ||
| 1245 | top_level_decls->append(root_export_decl_node); | ||
| 1246 | continue; | ||
| 1247 | } | ||
| 1248 | |||
| 1205 | AstNode *fn_decl_node = ast_parse_fn_def(pc, token_index, false); | 1249 | AstNode *fn_decl_node = ast_parse_fn_def(pc, token_index, false); |
| 1206 | if (fn_decl_node) { | 1250 | if (fn_decl_node) { |
| 1207 | top_level_decls->append(fn_decl_node); | 1251 | top_level_decls->append(fn_decl_node); |
| ... | @@ -1224,41 +1268,12 @@ static void ast_parse_top_level_decls(ParseContext *pc, int *token_index, ZigLis | ... | @@ -1224,41 +1268,12 @@ static void ast_parse_top_level_decls(ParseContext *pc, int *token_index, ZigLis |
| 1224 | zig_unreachable(); | 1268 | zig_unreachable(); |
| 1225 | } | 1269 | } |
| 1226 | 1270 | ||
| 1227 | static AstNode *ast_parse_root_export_decl(ParseContext *pc, int *token_index) { | ||
| 1228 | Token *export_kw = &pc->tokens->at(*token_index); | ||
| 1229 | if (export_kw->id != TokenIdKeywordExport) | ||
| 1230 | return nullptr; | ||
| 1231 | *token_index += 1; | ||
| 1232 | |||
| 1233 | AstNode *node = ast_create_node(NodeTypeRootExportDecl, export_kw); | ||
| 1234 | |||
| 1235 | Token *export_type = &pc->tokens->at(*token_index); | ||
| 1236 | *token_index += 1; | ||
| 1237 | ast_expect_token(pc, export_type, TokenIdSymbol); | ||
| 1238 | |||
| 1239 | ast_buf_from_token(pc, export_type, &node->data.root_export_decl.type); | ||
| 1240 | |||
| 1241 | Token *export_name = &pc->tokens->at(*token_index); | ||
| 1242 | *token_index += 1; | ||
| 1243 | ast_expect_token(pc, export_name, TokenIdStringLiteral); | ||
| 1244 | |||
| 1245 | parse_string_literal(pc, export_name, &node->data.root_export_decl.name); | ||
| 1246 | |||
| 1247 | Token *semicolon = &pc->tokens->at(*token_index); | ||
| 1248 | *token_index += 1; | ||
| 1249 | ast_expect_token(pc, semicolon, TokenIdSemicolon); | ||
| 1250 | |||
| 1251 | return node; | ||
| 1252 | } | ||
| 1253 | |||
| 1254 | /* | 1271 | /* |
| 1255 | Root : RootExportDecl many(TopLevelDecl) token(EOF) | 1272 | Root : many(TopLevelDecl) token(EOF) |
| 1256 | */ | 1273 | */ |
| 1257 | static AstNode *ast_parse_root(ParseContext *pc, int *token_index) { | 1274 | static AstNode *ast_parse_root(ParseContext *pc, int *token_index) { |
| 1258 | AstNode *node = ast_create_node(NodeTypeRoot, &pc->tokens->at(*token_index)); | 1275 | AstNode *node = ast_create_node(NodeTypeRoot, &pc->tokens->at(*token_index)); |
| 1259 | 1276 | ||
| 1260 | node->data.root.root_export_decl = ast_parse_root_export_decl(pc, token_index); | ||
| 1261 | |||
| 1262 | ast_parse_top_level_decls(pc, token_index, &node->data.root.top_level_decls); | 1277 | ast_parse_top_level_decls(pc, token_index, &node->data.root.top_level_decls); |
| 1263 | 1278 | ||
| 1264 | if (*token_index != pc->tokens->length - 1) { | 1279 | if (*token_index != pc->tokens->length - 1) { |
src/parser.hpp+1-1| ... | @@ -38,7 +38,6 @@ enum NodeType { | ... | @@ -38,7 +38,6 @@ enum NodeType { |
| 38 | }; | 38 | }; |
| 39 | 39 | ||
| 40 | struct AstNodeRoot { | 40 | struct AstNodeRoot { |
| 41 | AstNode *root_export_decl; | ||
| 42 | ZigList<AstNode *> top_level_decls; | 41 | ZigList<AstNode *> top_level_decls; |
| 43 | }; | 42 | }; |
| 44 | 43 | ||
| ... | @@ -138,6 +137,7 @@ struct AstNodeDirective { | ... | @@ -138,6 +137,7 @@ struct AstNodeDirective { |
| 138 | struct AstNodeRootExportDecl { | 137 | struct AstNodeRootExportDecl { |
| 139 | Buf type; | 138 | Buf type; |
| 140 | Buf name; | 139 | Buf name; |
| 140 | ZigList<AstNode *> *directives; | ||
| 141 | }; | 141 | }; |
| 142 | 142 | ||
| 143 | struct AstNodeCastExpr { | 143 | struct AstNodeCastExpr { |
src/tokenizer.cpp-1| ... | @@ -608,4 +608,3 @@ void print_tokens(Buf *buf, ZigList<Token> *tokens) { | ... | @@ -608,4 +608,3 @@ void print_tokens(Buf *buf, ZigList<Token> *tokens) { |
| 608 | fprintf(stderr, "\n"); | 608 | fprintf(stderr, "\n"); |
| 609 | } | 609 | } |
| 610 | } | 610 | } |
| 611 |