diff --git a/docs/grammar.md b/docs/grammar.md index e09d853..d26f8db 100644 --- a/docs/grammar.md +++ b/docs/grammar.md @@ -198,7 +198,8 @@ statement ::= import_statement | block import_statement ::= 'import' name ( 'as' name )? ';' - | 'import' '{' name ( ',' name )* '}' 'from' name ';' + | 'import' STRING 'as' name ';' + | 'import' '{' name ( ',' name )* '}' 'from' ( name | STRING ) ';' variable_declaration ::= 'let' name '=' expression | 'mut' name ( '=' expression )? diff --git a/docs/grammar.txt b/docs/grammar.txt index 267d993..0bc1f63 100644 --- a/docs/grammar.txt +++ b/docs/grammar.txt @@ -2,7 +2,8 @@ chunk ::= stmt* EOF stmt ::= 'import' NAME ( 'as' NAME )? ';' - | 'import' '{' NAME ( ',' NAME )* '}' 'from' NAME ';' + | 'import' STRING 'as' NAME ';' + | 'import' '{' NAME ( ',' NAME )* '}' 'from' ( NAME | STRING ) ';' | var_decl ';' | assign_call ';' | 'struct' NAME '{' ( STRING | NAME ( ',' STRING | NAME )* )? '}' diff --git a/examples/import.hk b/examples/import.hk index 80f638e..4fc5cbe 100644 --- a/examples/import.hk +++ b/examples/import.hk @@ -3,7 +3,7 @@ // import os; -import { foo, bar } from module; +import { foo, bar } from "module.hk"; println(os.clock()); println(foo()); diff --git a/src/builtin.c b/src/builtin.c index bf7affb..3887c36 100644 --- a/src/builtin.c +++ b/src/builtin.c @@ -133,13 +133,16 @@ static inline void string_to_double(HkState *state, HkString *str, double *resul static inline HkArray *split(HkString *str, HkString *sep) { HkArray *arr = hk_array_new(); - char *cur = str->chars; + // TODO: Do not use strtok_r and do not copy the string + HkString *_str = hk_string_copy(str); + char *cur = _str->chars; char *tk; while ((tk = strtok_r(cur, sep->chars, &cur))) { HkValue elem = hk_string_value(hk_string_from_chars(-1, tk)); hk_array_inplace_add_element(arr, elem); } + hk_string_free(_str); return arr; } diff --git a/src/compiler.c b/src/compiler.c index aea69e4..cb04d8b 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -526,6 +526,23 @@ static void compile_import_statement(Compiler *comp) hk_chunk_emit_opcode(chunk, HK_OP_LOAD_MODULE); return; } + if (match(scan, TOKEN_STRING)) + { + Token tk = scan->token; + scanner_next_token(scan); + uint8_t index = add_string_constant(comp, &tk); + hk_chunk_emit_opcode(chunk, HK_OP_CONSTANT); + hk_chunk_emit_byte(chunk, index); + consume(comp, TOKEN_AS); + if (!match(scan, TOKEN_NAME)) + syntax_error_unexpected(comp); + tk = scan->token; + scanner_next_token(scan); + define_local(comp, &tk, false); + consume(comp, TOKEN_SEMICOLON); + hk_chunk_emit_opcode(chunk, HK_OP_LOAD_MODULE); + return; + } if (match(scan, TOKEN_LBRACE)) { scanner_next_token(scan); @@ -553,7 +570,7 @@ static void compile_import_statement(Compiler *comp) } consume(comp, TOKEN_RBRACE); consume(comp, TOKEN_FROM); - if (!match(scan, TOKEN_NAME)) + if (!match(scan, TOKEN_NAME) && !match(scan, TOKEN_STRING)) syntax_error_unexpected(comp); tk = scan->token; scanner_next_token(scan); @@ -605,7 +622,7 @@ static void compile_constant_declaration(Compiler *comp) { if (!match(scan, TOKEN_NAME)) syntax_error_unexpected(comp); - // FIX: This is a bug, we should not define the local here + // FIXME: This is a bug, we should not define the local here define_local(comp, &scan->token, false); } scanner_next_token(scan); @@ -625,7 +642,7 @@ static void compile_constant_declaration(Compiler *comp) syntax_error_unexpected(comp); Token tk = scan->token; scanner_next_token(scan); - // FIX: This is a bug, we should not define the local here + // FIXME: This is a bug, we should not define the local here define_local(comp, &tk, false); uint8_t index = add_string_constant(comp, &tk); hk_chunk_emit_opcode(chunk, HK_OP_CONSTANT); @@ -638,7 +655,7 @@ static void compile_constant_declaration(Compiler *comp) syntax_error_unexpected(comp); Token tk = scan->token; scanner_next_token(scan); - // FIX: This is a bug, we should not define the local here + // FIXME: This is a bug, we should not define the local here define_local(comp, &tk, false); uint8_t index = add_string_constant(comp, &tk); hk_chunk_emit_opcode(chunk, HK_OP_CONSTANT); @@ -684,7 +701,7 @@ static void compile_variable_declaration(Compiler *comp) { if (!match(scan, TOKEN_NAME)) syntax_error_unexpected(comp); - // FIX: This is a bug, we should not define the local here + // FIXME: This is a bug, we should not define the local here define_local(comp, &scan->token, false); } scanner_next_token(scan); @@ -698,7 +715,7 @@ static void compile_variable_declaration(Compiler *comp) { if (!match(scan, TOKEN_NAME)) syntax_error_unexpected(comp); - // FIX: This is a bug, we should not define the local here + // FIXME: This is a bug, we should not define the local here define_local(comp, &scan->token, false); } scanner_next_token(scan); @@ -718,7 +735,7 @@ static void compile_variable_declaration(Compiler *comp) syntax_error_unexpected(comp); Token tk = scan->token; scanner_next_token(scan); - // FIX: This is a bug, we should not define the local here + // FIXME: This is a bug, we should not define the local here define_local(comp, &tk, true); uint8_t index = add_string_constant(comp, &tk); hk_chunk_emit_opcode(chunk, HK_OP_CONSTANT); @@ -731,7 +748,7 @@ static void compile_variable_declaration(Compiler *comp) syntax_error_unexpected(comp); Token tk = scan->token; scanner_next_token(scan); - // FIX: This is a bug, we should not define the local here + // FIXME: This is a bug, we should not define the local here define_local(comp, &tk, true); uint8_t index = add_string_constant(comp, &tk); hk_chunk_emit_opcode(chunk, HK_OP_CONSTANT); diff --git a/src/module.c b/src/module.c index 5329b17..be858a9 100644 --- a/src/module.c +++ b/src/module.c @@ -39,7 +39,6 @@ #define WILDCARD "?" #define LIB_DIR "lib" -#define LIB_PREFIX "lib" #define LIB_POSTFIX "_mod" #define SRC_EXT ".hk" @@ -62,13 +61,16 @@ #endif static StringMap moduleCache; +static HkString *path = NULL; static inline const char *get_home_dir(void); static inline const char *get_default_home_dir(void); static inline HkString *get_path(void); static inline HkString *get_default_path(void); -static inline HkString *path_match(HkString *path, HkString *name); -static inline void load_module(HkState *state, HkString *name); +static inline HkString *path_match(HkString *path, HkString *name, HkString *currFile); +static inline bool is_relative(char *filename); +static inline HkString *get_module_file(HkString *relFile, HkString *currFile); +static inline void load_module(HkState *state, HkString *name, HkString *currFile); static inline bool is_source_module(char *filename); static inline void load_source_module(HkState *state, HkString *file, HkString *name); static inline void load_native_module(HkState *state, HkString *file, HkString *name); @@ -100,20 +102,24 @@ static inline const char *get_default_home_dir(void) static inline HkString *get_path(void) { - const char *_path = getenv(PATH_ENV_VAR); - if (_path) - return hk_string_from_chars(-1, _path); - return get_default_path(); + if (!path) + { + const char *_path = getenv(PATH_ENV_VAR); + path = _path ? hk_string_from_chars(-1, _path) : get_default_path(); + } + return path; } static inline HkString *get_default_path(void) { HkString *path = hk_string_new(); + hk_string_inplace_concat_chars(path, -1, WILDCARD); + hk_string_inplace_concat_chars(path, -1, PATH_SEP); + hk_string_inplace_concat_chars(path, -1, get_home_dir()); hk_string_inplace_concat_chars(path, -1, DIR_SEP); hk_string_inplace_concat_chars(path, -1, LIB_DIR); hk_string_inplace_concat_chars(path, -1, DIR_SEP); - hk_string_inplace_concat_chars(path, -1, LIB_PREFIX); hk_string_inplace_concat_chars(path, -1, WILDCARD); hk_string_inplace_concat_chars(path, -1, LIB_POSTFIX); hk_string_inplace_concat_chars(path, -1, LIB_EXT); @@ -130,36 +136,61 @@ static inline HkString *get_default_path(void) return path; } -static inline HkString *path_match(HkString *path, HkString *name) +static inline HkString *path_match(HkString *path, HkString *name, HkString *currFile) { HkString *sep = hk_string_from_chars(-1, PATH_SEP); - HkArray *entries = hk_string_split(path, sep); + HkArray *patterns = hk_string_split(path, sep); hk_string_free(sep); HkString *wc = hk_string_from_chars(-1, WILDCARD); - int n = entries->length; + int n = patterns->length; for (int i = 0; i < n; ++i) { - HkValue elem = hk_array_get_element(entries, i); - HkString *entry = hk_as_string(elem); - HkString *file = hk_string_replace_all(entry, wc, name); + HkValue elem = hk_array_get_element(patterns, i); + HkString *pattern = hk_as_string(elem); + HkString *file = hk_string_replace_all(pattern, wc, name); + if (is_relative(file->chars)) + { + HkString *_file = get_module_file(file, currFile); + hk_string_free(file); + file = _file; + } if (file_exists(file->chars)) { hk_string_free(wc); - hk_array_free(entries); + hk_array_free(patterns); return file; } hk_string_free(file); } hk_string_free(wc); - hk_array_free(entries); + hk_array_free(patterns); return NULL; } -static inline void load_module(HkState *state, HkString *name) +static inline bool is_relative(char *filename) +{ + return filename[0] != DIR_SEP[0]; +} + +static inline HkString *get_module_file(HkString *relFile, HkString *currFile) +{ + char *chars = currFile->chars; + char *end = strrchr(chars, DIR_SEP[0]); + if (end) + { + int length = (int) (end - chars); + HkString *file = hk_string_from_chars(length, chars); + hk_string_inplace_concat_chars(file, -1, DIR_SEP); + hk_string_inplace_concat(file, relFile); + return file; + } + return hk_string_copy(relFile); +} + +static inline void load_module(HkState *state, HkString *name, HkString *currFile) { HkString *path = get_path(); - HkString *file = path_match(path, name); - hk_string_free(path); + HkString *file = path_match(path, name, currFile); if (!file) { hk_state_runtime_error(state, "cannot find module `%.*s`", @@ -265,24 +296,26 @@ void module_cache_init(void) void module_cache_deinit(void) { string_map_deinit(&moduleCache); + if (path) + hk_string_free(path); } -void module_load(HkState *state) +void module_load(HkState *state, HkString *currFile) { HkValue *slots = &state->stackSlots[state->stackTop]; HkValue val = slots[0]; hk_assert(hk_is_string(val), "module name must be a string"); HkString *name = hk_as_string(val); HkValue module; + // FIXME: Do cache using absolute file path instead of module name if (module_cache_get(name, &module)) { hk_value_incr_ref(module); slots[0] = module; - --state->stackTop; hk_string_release(name); return; } - load_module(state, name); + load_module(state, name, currFile); hk_return_if_not_ok(state); module_cache_put(name, state->stackSlots[state->stackTop]); slots[0] = state->stackSlots[state->stackTop]; diff --git a/src/module.h b/src/module.h index cfa84f0..d5fef3a 100644 --- a/src/module.h +++ b/src/module.h @@ -10,6 +10,6 @@ void module_cache_init(void); void module_cache_deinit(void); -void module_load(HkState *state); +void module_load(HkState *state, HkString *currFile); #endif // MODULE_H diff --git a/src/state.c b/src/state.c index 9073bc4..4a48c25 100644 --- a/src/state.c +++ b/src/state.c @@ -1772,7 +1772,7 @@ static inline void call_function(HkState *state, HkValue *locals, HkClosure *cl, goto end; break; case HK_OP_LOAD_MODULE: - module_load(state); + module_load(state, fn->file); if (!hk_state_is_ok(state)) goto end; break; diff --git a/src/string.c b/src/string.c index 9073c1d..16855ff 100644 --- a/src/string.c +++ b/src/string.c @@ -18,7 +18,7 @@ static inline HkString *string_allocate(int minCapacity); static inline void add_char(HkString *str, char c); static inline uint32_t hash(int length, char *chars); -static inline int index_of(char *chars, int length, char *sub); +static inline int index_of(char *chars, int subLength, char *sub); static inline HkString *string_allocate(int minCapacity) { @@ -50,11 +50,16 @@ static inline uint32_t hash(int length, char *chars) return hash; } -static inline int index_of(char *chars, int length, char *sub) +static inline int index_of(char *chars, int subLength, char *sub) { - for (int i = 0; chars[i + length]; ++i) - if (!memcmp(&chars[i], sub, length)) + int i = 0; + for (; chars[i + subLength]; ++i) + { + if (!memcmp(&chars[i], sub, subLength)) return i; + } + if (!memcmp(&chars[i], sub, subLength)) + return i; return -1; } @@ -204,18 +209,22 @@ HkString *hk_string_replace_all(HkString *str, HkString *sub1, HkString *sub2) return hk_string_copy(str); HkString *result = string_allocate(0); result->length = 0; - char *chars = str->chars; + char *strChars = str->chars; char *subChars = sub1->chars; for (;;) { - int index = index_of(chars, subLength, subChars); + if (!strLength || strLength > str->length) + break; + int index = index_of(strChars, subLength, subChars); if (index == -1) break; - hk_string_inplace_concat_chars(result, index, chars); + hk_string_inplace_concat_chars(result, index, strChars); hk_string_inplace_concat(result, sub2); - chars += index + subLength; + int count = index + subLength; + strLength -= count; + strChars += count; } - hk_string_inplace_concat_chars(result, -1, chars); + hk_string_inplace_concat_chars(result, -1, strChars); return result; } @@ -238,13 +247,16 @@ HkString *hk_string_slice(HkString *str, int start, int stop) HkArray *hk_string_split(HkString *str, HkString *sep) { HkArray *arr = hk_array_new(); - char *cur = str->chars; + // TODO: Do not use strtok_r and do not copy the string + HkString *_str = hk_string_copy(str); + char *cur = _str->chars; char *tk; while ((tk = strtok_r(cur, sep->chars, &cur))) { HkValue elem = hk_string_value(hk_string_from_chars(-1, tk)); hk_array_inplace_add_element(arr, elem); } + hk_string_free(_str); return arr; } diff --git a/tests/import_statement_test.hk b/tests/import_statement_test.hk new file mode 100644 index 0000000..6e79165 --- /dev/null +++ b/tests/import_statement_test.hk @@ -0,0 +1,15 @@ + +import os; +println(os.clock()); + +import "numbers" as n; +println(n.PI); + +import { clock } from "os"; +println(clock()); + +import "../examples/module.hk" as m; +println(m.foo()); + +import { bar } from "../examples/module"; +println(bar());