| 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 | 42 | |
| 43 | 43 | ## Roadmap |
| 44 | 44 | |
| 45 | * ability to specify version | |
| 46 | * cli ability to override library export locations | |
| 47 | * add test for building library | |
| 48 | 45 | * variables and parameters |
| 49 | 46 | * Export .so library |
| 50 | 47 | * Multiple files |
| ... | ... | @@ -81,11 +78,11 @@ zig | C equivalent | Description |
| 81 | 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 | 87 | ExternBlock : many(Directive) token(Extern) token(LBrace) many(FnDecl) token(RBrace) |
| 91 | 88 |
example/mathtest.zig+1| ... | ... | @@ -1,3 +1,4 @@ |
| 1 | #version("2.0.0") | |
| 1 | 2 | export library "mathtest"; |
| 2 | 3 | |
| 3 | 4 | export fn add(a: i32, b: i32) -> i32 { |
src/codegen.cpp+76-37| ... | ... | @@ -10,6 +10,7 @@ |
| 10 | 10 | #include "zig_llvm.hpp" |
| 11 | 11 | #include "os.hpp" |
| 12 | 12 | #include "config.h" |
| 13 | #include "error.hpp" | |
| 13 | 14 | |
| 14 | 15 | #include <stdio.h> |
| 15 | 16 | |
| ... | ... | @@ -79,6 +80,10 @@ struct CodeGen { |
| 79 | 80 | OutType out_type; |
| 80 | 81 | FnTableEntry *cur_fn; |
| 81 | 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 | 89 | struct TypeNode { |
| ... | ... | @@ -169,6 +174,30 @@ static bool type_is_unreachable(AstNode *type_node) { |
| 169 | 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 | 201 | static void find_declarations(CodeGen *g, AstNode *node); |
| 173 | 202 | |
| 174 | 203 | static void resolve_type_and_recurse(CodeGen *g, AstNode *node) { |
| ... | ... | @@ -303,13 +332,25 @@ static void find_declarations(CodeGen *g, AstNode *node) { |
| 303 | 332 | case NodeTypeDirective: |
| 304 | 333 | // we handled directives in the parent function |
| 305 | 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 | 348 | case NodeTypeFnDecl: |
| 307 | 349 | case NodeTypeReturnExpr: |
| 308 | 350 | case NodeTypeRoot: |
| 309 | 351 | case NodeTypeBlock: |
| 310 | 352 | case NodeTypeBinOpExpr: |
| 311 | 353 | case NodeTypeFnCallExpr: |
| 312 | case NodeTypeRootExportDecl: | |
| 313 | 354 | case NodeTypeNumberLiteral: |
| 314 | 355 | case NodeTypeStringLiteral: |
| 315 | 356 | case NodeTypeUnreachable: |
| ... | ... | @@ -385,36 +426,6 @@ static void analyze_node(CodeGen *g, AstNode *node) { |
| 385 | 426 | switch (node->type) { |
| 386 | 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 | 429 | // Iterate once over the top level declarations to build the function table |
| 419 | 430 | for (int i = 0; i < node->data.root.top_level_decls.length; i += 1) { |
| 420 | 431 | AstNode *child = node->data.root.top_level_decls.at(i); |
| ... | ... | @@ -424,10 +435,40 @@ static void analyze_node(CodeGen *g, AstNode *node) { |
| 424 | 435 | AstNode *child = node->data.root.top_level_decls.at(i); |
| 425 | 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 | 445 | break; |
| 428 | 446 | } |
| 429 | 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 | 472 | break; |
| 432 | 473 | case NodeTypeExternBlock: |
| 433 | 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 | 1442 | } |
| 1402 | 1443 | |
| 1403 | 1444 | if (g->out_type == OutTypeLib) { |
| 1404 | int major = 1; | |
| 1405 | int minor = 0; | |
| 1406 | int patch = 0; | |
| 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); | |
| 1445 | Buf *out_lib_so = buf_sprintf("lib%s.so.%d.%d.%d", | |
| 1446 | buf_ptr(g->out_name), g->version_major, g->version_minor, g->version_patch); | |
| 1447 | Buf *soname = buf_sprintf("lib%s.so.%d", buf_ptr(g->out_name), g->version_major); | |
| 1409 | 1448 | args.append("-shared"); |
| 1410 | 1449 | args.append("-soname"); |
| 1411 | 1450 | args.append(buf_ptr(soname)); |
src/error.cpp+1| ... | ... | @@ -2,6 +2,7 @@ |
| 2 | 2 | |
| 3 | 3 | const char *err_str(int err) { |
| 4 | 4 | switch ((enum Error)err) { |
| 5 | case ErrorNone: return "(no error)"; | |
| 5 | 6 | case ErrorNoMem: return "out of memory"; |
| 6 | 7 | case ErrorInvalidFormat: return "invalid format"; |
| 7 | 8 | } |
src/error.hpp+1| ... | ... | @@ -9,6 +9,7 @@ |
| 9 | 9 | #define ERROR_HPP |
| 10 | 10 | |
| 11 | 11 | enum Error { |
| 12 | ErrorNone, | |
| 12 | 13 | ErrorNoMem, |
| 13 | 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 | 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 | 1236 | static void ast_parse_top_level_decls(ParseContext *pc, int *token_index, ZigList<AstNode *> *top_level_decls) { |
| 1199 | 1237 | for (;;) { |
| 1200 | 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 | 1240 | pc->directive_list = allocate<ZigList<AstNode*>>(1); |
| 1203 | 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 | 1249 | AstNode *fn_decl_node = ast_parse_fn_def(pc, token_index, false); |
| 1206 | 1250 | if (fn_decl_node) { |
| 1207 | 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 | 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 | 1274 | static AstNode *ast_parse_root(ParseContext *pc, int *token_index) { |
| 1258 | 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 | 1277 | ast_parse_top_level_decls(pc, token_index, &node->data.root.top_level_decls); |
| 1263 | 1278 | |
| 1264 | 1279 | if (*token_index != pc->tokens->length - 1) { |
src/parser.hpp+1-1| ... | ... | @@ -38,7 +38,6 @@ enum NodeType { |
| 38 | 38 | }; |
| 39 | 39 | |
| 40 | 40 | struct AstNodeRoot { |
| 41 | AstNode *root_export_decl; | |
| 42 | 41 | ZigList<AstNode *> top_level_decls; |
| 43 | 42 | }; |
| 44 | 43 | |
| ... | ... | @@ -138,6 +137,7 @@ struct AstNodeDirective { |
| 138 | 137 | struct AstNodeRootExportDecl { |
| 139 | 138 | Buf type; |
| 140 | 139 | Buf name; |
| 140 | ZigList<AstNode *> *directives; | |
| 141 | 141 | }; |
| 142 | 142 | |
| 143 | 143 | struct AstNodeCastExpr { |
src/tokenizer.cpp-1| ... | ... | @@ -608,4 +608,3 @@ void print_tokens(Buf *buf, ZigList<Token> *tokens) { |
| 608 | 608 | fprintf(stderr, "\n"); |
| 609 | 609 | } |
| 610 | 610 | } |
| 611 |