diff --git a/docs/c-integration.md b/docs/c-integration.md index 5cd05c92..c38cdd0a 100644 --- a/docs/c-integration.md +++ b/docs/c-integration.md @@ -21,6 +21,8 @@ and in Skull: ```python # hello.sk +external hello + hello[] return 0 @@ -31,6 +33,7 @@ Now, to compile these files together: ``` $ skull hello.sk -- hello.c $ ./hello +hello from hello.c! ``` Everything after `--` is passed as arguments to `cc`, in this case, `hello.c`. @@ -41,6 +44,7 @@ Skull can also compile `.o` and `.so` files, but you will need to compile them y $ cc -c hello.c $ skull hello.sk -- hello.o $ ./hello +hello from hello.c! ``` For the time being, Skull can only run C functions prototypes like: `void f(void)` diff --git a/docs/skull/llvm/ast.md b/docs/skull/llvm/ast.md index 3e442312..be2e06c0 100644 --- a/docs/skull/llvm/ast.md +++ b/docs/skull/llvm/ast.md @@ -60,6 +60,12 @@ LLVMValueRef llvm_make_div(Variable *var, const Token *lhs, const Token *rhs) > Build LLVM for assining division of `lhs` and `rhs` to `var`. +```c +void declare_external_function(AstNode *node) +``` + +> Store function name of externaly declared function in `node`. + ```c void llvm_make_function(AstNode *node) ``` diff --git a/skull/llvm/ast.c b/skull/llvm/ast.c index be1eecb3..7ac526a5 100644 --- a/skull/llvm/ast.c +++ b/skull/llvm/ast.c @@ -38,6 +38,10 @@ static LLVMModuleRef module; extern LLVMBuilderRef builder; +#define EXTERNAL_FUNCTIONS_MAX 256 +unsigned external_functions = 0; +char *external_function[EXTERNAL_FUNCTIONS_MAX] = {0}; + void llvm_assign_identifier(Variable *const var, const AstNode *const node); /* @@ -61,6 +65,12 @@ void str_to_llvm_ir(char *const str_, LLVMValueRef func_, LLVMModuleRef module_) node_to_llvm_ir(node); free_ast_tree(node); free(str); + + char **current_function = external_function; + while (*current_function) { + free(*current_function); + current_function++; + } } /* @@ -82,6 +92,10 @@ void node_to_llvm_ir(AstNode *node) { llvm_make_if(node); } + else if (node->node_type == AST_NODE_EXTERNAL) { + declare_external_function(node); + } + else if ( node->node_type == AST_NODE_IDENTIFIER && node->token->next && @@ -375,6 +389,18 @@ LLVMValueRef llvm_make_div(Variable *var, const Token *lhs, const Token *rhs) { return div; } +/* +Store function name of externaly declared function in `node`. +*/ +void declare_external_function(AstNode *node) { + char32_t *const tmp = token_str(node->token->next); + char *const func_name = c32stombs(tmp); + free(tmp); + + external_function[external_functions] = func_name; + external_functions++; +} + /* Builds a function declaration from `node`. */ @@ -390,6 +416,19 @@ void llvm_make_function(AstNode *node) { char32_t *const tmp = token_str(node->token); char *const func_name = c32stombs(tmp); + + char **current_function = external_function; + while (*current_function) { + if (strcmp(*current_function, func_name) == 0) { + break; + } + current_function++; + } + + if (!*current_function) { + PANIC(FMT_ERROR(U"external function \"%\" missing external declaration", { .real = tmp })); + } + free(tmp); LLVMValueRef function = LLVMAddFunction( diff --git a/skull/llvm/ast.h b/skull/llvm/ast.h index cf0f63c5..978c4edf 100644 --- a/skull/llvm/ast.h +++ b/skull/llvm/ast.h @@ -8,6 +8,7 @@ void str_to_llvm_ir(char *const, LLVMValueRef, LLVMModuleRef); void node_to_llvm_ir(AstNode *); +void declare_external_function(AstNode *); void llvm_make_return(AstNode *); void llvm_make_var_def(AstNode **); void llvm_make_if(AstNode *); diff --git a/test/docs/c-integration/hello.sk b/test/docs/c-integration/hello.sk index 19b564ee..2793ed8d 100644 --- a/test/docs/c-integration/hello.sk +++ b/test/docs/c-integration/hello.sk @@ -1,3 +1,5 @@ +external hello + hello[] return 0 \ No newline at end of file diff --git a/test/sh/_multiple_externals.sk.ll b/test/sh/_multiple_externals.sk.ll new file mode 100644 index 00000000..3f80ccc3 --- /dev/null +++ b/test/sh/_multiple_externals.sk.ll @@ -0,0 +1,6 @@ +; ModuleID = './test/sh/multiple_externals.sk' +source_filename = "./test/sh/multiple_externals.sk" + +define i64 @main() { +entry: +} diff --git a/test/sh/error/missing_external.sk b/test/sh/error/missing_external.sk new file mode 100644 index 00000000..f41dd506 --- /dev/null +++ b/test/sh/error/missing_external.sk @@ -0,0 +1 @@ +x[] \ No newline at end of file diff --git a/test/sh/main.sh b/test/sh/main.sh index 4b541a6b..d10914a2 100755 --- a/test/sh/main.sh +++ b/test/sh/main.sh @@ -71,6 +71,7 @@ test "mult_int_consts.sk" test "mult_float_consts.sk" test "div_int_consts.sk" test "div_float_consts.sk" +test "multiple_externals.sk" test_err "missing_file_extension_fails" "missing required \".sk\" extension, exiting" test_err ".sk" "\".sk\" is not a valid name, exiting" @@ -102,6 +103,7 @@ test_err "error/sub_mismatched_consts.sk" "Compilation error: cannot subtract \" test_err "error/mult_mismatched_consts.sk" "Compilation error: cannot multiply \"1\" and \"\"fail\"\"" test_err "error/div_mismatched_consts.sk" "Compilation error: cannot divide \"1\" and \"\"fail\"\"" test_err "error/assign_mismatch_var_type.sk" "Compilation error: type mismatch: expected type \"bool\"" +test_err "error/missing_external.sk" "Compilation error: external function \"x\" missing external declaration" touch test/sh/error/read_protected.sk chmod 200 test/sh/error/read_protected.sk diff --git a/test/sh/multiple_externals.sk b/test/sh/multiple_externals.sk new file mode 100644 index 00000000..8cd7f1b1 --- /dev/null +++ b/test/sh/multiple_externals.sk @@ -0,0 +1,2 @@ +external a +external b \ No newline at end of file diff --git a/test/sh/nodes_after_call_not_ignored.sk b/test/sh/nodes_after_call_not_ignored.sk index 9a1d3bf8..8bdee200 100644 --- a/test/sh/nodes_after_call_not_ignored.sk +++ b/test/sh/nodes_after_call_not_ignored.sk @@ -1,3 +1,5 @@ +external x + x[] return 1 \ No newline at end of file diff --git a/test/sh/simple_function_call.sk b/test/sh/simple_function_call.sk index f41dd506..974b48b2 100644 --- a/test/sh/simple_function_call.sk +++ b/test/sh/simple_function_call.sk @@ -1 +1,3 @@ +external x + x[] \ No newline at end of file