feat: add module loading with String.import_ and embedder-provided loadModuleFn

Implement initial module system allowing Wren code to import other modules via a temporary `String.import_` method. The embedder must supply a `WrenLoadModuleFn` callback that returns source code for a given module name; the VM caches loaded modules and returns a fiber to execute the module body. Add `wrenImportModule` to the VM API, wire `loadModuleFn` into `WrenConfiguration`, and update the CLI interpreter to resolve imports relative to the entry script's directory. Include test cases for importing multiple variables, shared imports across modules, and verifying that variable bindings are independent across import sites.
This commit is contained in:
Bob Nystrom
2015-02-06 15:01:15 +00:00
parent 7cba07a322
commit 70191aa022
19 changed files with 311 additions and 2 deletions
@@ -0,0 +1,14 @@
var Module = "module.wren".import_("Module")
var Other = "module.wren".import_("Other")
IO.print(Module) // expect: before
// Reassigning the variable in the other module does not affect this one's
// binding.
Other.change
IO.print(Module) // expect: before
// But it does change there.
Other.show // expect: after
// TODO: Cyclic import.
@@ -0,0 +1,12 @@
// nontest
var Module = "before"
class Other {
static change {
Module = "after"
}
static show {
IO.print(Module)
}
}
@@ -0,0 +1,8 @@
// nontest
var Module1 = "from module one"
var Module2 = "from module two"
var Module3 = "from module three"
var Module4 = "from module four"
var Module5 = "from module five"
IO.print("ran module")
@@ -0,0 +1,14 @@
var Module1 = "module.wren".import_("Module1")
var Module2 = "module.wren".import_("Module2")
var Module3 = "module.wren".import_("Module3")
var Module4 = "module.wren".import_("Module4")
var Module5 = "module.wren".import_("Module5")
// Only execute module body once:
// expect: ran module
IO.print(Module1) // expect: from module one
IO.print(Module2) // expect: from module two
IO.print(Module3) // expect: from module three
IO.print(Module4) // expect: from module four
IO.print(Module5) // expect: from module five
+5
View File
@@ -0,0 +1,5 @@
// nontest
IO.print("a")
var Shared = "shared.wren".import_("Shared")
var A = "a " + Shared
IO.print("a done")
+5
View File
@@ -0,0 +1,5 @@
// nontest
IO.print("b")
var Shared = "shared.wren".import_("Shared")
var B = "b " + Shared
IO.print("b done")
+3
View File
@@ -0,0 +1,3 @@
// nontest
IO.print("shared")
var Shared = "shared"
@@ -0,0 +1,12 @@
var A = "a.wren".import_("A")
var B = "b.wren".import_("B")
// Shared module should only run once:
// expect: a
// expect: shared
// expect: a done
// expect: b
// expect: b done
IO.print(A) // expect: a shared
IO.print(B) // expect: b shared
+3
View File
@@ -0,0 +1,3 @@
// nontest
var Module = "from module"
IO.print("ran module")
@@ -0,0 +1,4 @@
var Module = "module.wren".import_("Module")
// expect: ran module
IO.print(Module) // expect: from module
@@ -0,0 +1 @@
var DoesNotExist = "does_not_exist.wren".import_("DoesNotExist") // expect runtime error: Could not find module 'does_not_exist.wren'.
+3
View File
@@ -0,0 +1,3 @@
// nontest
var Module = "from module"
IO.print("ran module")
@@ -0,0 +1,3 @@
// Should execute the module:
// expect: ran module
var DoesNotExist = "module.wren".import_("DoesNotExist") // expect runtime error: Could not find a variable named 'DoesNotExist' in module 'module.wren'.