summaryrefslogtreecommitdiffhomepage
path: root/tests/custom/04_modules
diff options
context:
space:
mode:
Diffstat (limited to 'tests/custom/04_modules')
-rw-r--r--tests/custom/04_modules/01_export_variable_declaration29
-rw-r--r--tests/custom/04_modules/02_export_function_declaration22
-rw-r--r--tests/custom/04_modules/03_export_list27
-rw-r--r--tests/custom/04_modules/04_export_rename28
-rw-r--r--tests/custom/04_modules/05_export_default38
-rw-r--r--tests/custom/04_modules/06_export_errors89
-rw-r--r--tests/custom/04_modules/07_import_default99
-rw-r--r--tests/custom/04_modules/08_import_list105
-rw-r--r--tests/custom/04_modules/09_import_wildcard73
-rw-r--r--tests/custom/04_modules/10_import_none18
-rw-r--r--tests/custom/04_modules/11_import_many_exec_once28
-rw-r--r--tests/custom/04_modules/12_import_immutability52
-rw-r--r--tests/custom/04_modules/13_import_liveness29
13 files changed, 637 insertions, 0 deletions
diff --git a/tests/custom/04_modules/01_export_variable_declaration b/tests/custom/04_modules/01_export_variable_declaration
new file mode 100644
index 0000000..19a1c11
--- /dev/null
+++ b/tests/custom/04_modules/01_export_variable_declaration
@@ -0,0 +1,29 @@
+Variable declarations can be prepended with `export` to automatically
+export each variable using the same name as the variable itself.
+
+Updates to the variable after the export are reflected properly in
+the including scope.
+
+-- File test-var-decl.uc --
+export let a, b, c;
+export let d = 4, e = 5, f = 6;
+export const g = 7, h = 8, i = 9;
+
+a = 1;
+b = 2;
+c = 3;
+-- End --
+
+-- Testcase --
+import { a, b, c, d, e, f, g, h, i } from "./files/test-var-decl.uc";
+
+print([ a, b, c, d, e, f, g, h, i ], "\n");
+-- End --
+
+-- Args --
+-R
+-- End --
+
+-- Expect stdout --
+[ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
+-- End --
diff --git a/tests/custom/04_modules/02_export_function_declaration b/tests/custom/04_modules/02_export_function_declaration
new file mode 100644
index 0000000..4067da9
--- /dev/null
+++ b/tests/custom/04_modules/02_export_function_declaration
@@ -0,0 +1,22 @@
+A named function declaration can be prepended with `export` to
+automatically export the function.
+
+-- File test-func-decl.uc --
+export function func() {
+ print("Hello, world!\n");
+};
+-- End --
+
+-- Testcase --
+import { func } from "./files/test-func-decl.uc";
+
+func();
+-- End --
+
+-- Args --
+-R
+-- End --
+
+-- Expect stdout --
+Hello, world!
+-- End --
diff --git a/tests/custom/04_modules/03_export_list b/tests/custom/04_modules/03_export_list
new file mode 100644
index 0000000..8f93f08
--- /dev/null
+++ b/tests/custom/04_modules/03_export_list
@@ -0,0 +1,27 @@
+Already declared local variables and functions may be exported using the
+curly brace export list syntax.
+
+-- File test-var-decl.uc --
+let testvar = 123;
+const testconst = "Test";
+
+function testfunc() {
+ print("Hello, world!\n");
+}
+
+export { testvar, testconst, testfunc };
+-- End --
+
+-- Testcase --
+import { testvar, testconst, testfunc } from "./files/test-var-decl.uc";
+
+print([ testvar, testconst, testfunc ], "\n");
+-- End --
+
+-- Args --
+-R
+-- End --
+
+-- Expect stdout --
+[ 123, "Test", "function testfunc() { ... }" ]
+-- End --
diff --git a/tests/custom/04_modules/04_export_rename b/tests/custom/04_modules/04_export_rename
new file mode 100644
index 0000000..49057fd
--- /dev/null
+++ b/tests/custom/04_modules/04_export_rename
@@ -0,0 +1,28 @@
+By using the `as` keyword, exports may be renamed when using the export
+list syntax. It is also possible to specify string aliases which are not
+valid variable names, in this case a rename on import is mandatory.
+
+-- File test.uc --
+let testvar = 123;
+const testconst = "Test";
+
+function testfunc() {
+ print("Hello, world!\n");
+}
+
+export { testvar as modvar, testconst as 'define', testfunc as "module-function" };
+-- End --
+
+-- Testcase --
+import { modvar, define, "module-function" as func } from "./files/test.uc";
+
+print([ modvar, define, func ], "\n");
+-- End --
+
+-- Args --
+-R
+-- End --
+
+-- Expect stdout --
+[ 123, "Test", "function testfunc() { ... }" ]
+-- End --
diff --git a/tests/custom/04_modules/05_export_default b/tests/custom/04_modules/05_export_default
new file mode 100644
index 0000000..a4c8a43
--- /dev/null
+++ b/tests/custom/04_modules/05_export_default
@@ -0,0 +1,38 @@
+The `export default` statement can be used to declare a default export
+value for a module. The value for `export default` can be an arbitrary
+expression, it must not refer to a local variable.
+
+When using the export list syntax, the alias "default" can be used to
+designate the default export.
+
+-- File test-default-expr.uc --
+export default 7 * 21;
+-- End --
+
+-- File test-default-func.uc --
+export default function() {
+ return "Hello, world!";
+};
+-- End --
+
+-- File test-default-alias.uc --
+let a = 1, b = 2, c = 3;
+
+export { a, b as default, c };
+-- End --
+
+-- Testcase --
+import def1 from "./files/test-default-expr.uc";
+import def2 from "./files/test-default-func.uc";
+import def3 from "./files/test-default-alias.uc";
+
+print([ def1, def2(), def3 ], "\n");
+-- End --
+
+-- Args --
+-R
+-- End --
+
+-- Expect stdout --
+[ 147, "Hello, world!", 2 ]
+-- End --
diff --git a/tests/custom/04_modules/06_export_errors b/tests/custom/04_modules/06_export_errors
new file mode 100644
index 0000000..c02a547
--- /dev/null
+++ b/tests/custom/04_modules/06_export_errors
@@ -0,0 +1,89 @@
+Export statements are only allowed at the toplevel of a module.
+
+-- Testcase --
+export let x = 1;
+-- End --
+
+-- Args --
+-R
+-- End --
+
+-- Expect stderr --
+Syntax error: Exports may only appear at top level of a module
+
+ `export let x = 1;`
+ ^-- Near here
+
+
+-- End --
+
+
+Export statements are not allowed within functions or nested blocks.
+
+-- Testcase --
+import "./files/test.uc";
+-- End --
+
+-- File test.uc --
+{
+ export let x = 1;
+}
+-- End --
+
+-- Args --
+-R
+-- End --
+
+-- Expect stderr --
+Syntax error: Unable to compile module './files/test.uc':
+Syntax error: Exports may only appear at top level of a module
+In line 2, byte 2:
+
+ ` export let x = 1;`
+ ^-- Near here
+
+
+
+In line 1, byte 25:
+
+ `import "./files/test.uc";`
+ Near here --------------^
+
+
+-- End --
+
+
+Duplicate export names should result in an error.
+
+-- Testcase --
+import "./files/test-duplicate.uc";
+-- End --
+
+-- File test-duplicate.uc --
+let x = 1, y = 2;
+
+export { x };
+export { y as x };
+-- End --
+
+-- Args --
+-R
+-- End --
+
+-- Expect stderr --
+Syntax error: Unable to compile module './files/test-duplicate.uc':
+Syntax error: Duplicate export 'x' for module './files/test-duplicate.uc'
+In line 4, byte 15:
+
+ `export { y as x };`
+ Near here ----^
+
+
+
+In line 1, byte 35:
+
+ `import "./files/test-duplicate.uc";`
+ Near here ------------------------^
+
+
+-- End --
diff --git a/tests/custom/04_modules/07_import_default b/tests/custom/04_modules/07_import_default
new file mode 100644
index 0000000..7190a22
--- /dev/null
+++ b/tests/custom/04_modules/07_import_default
@@ -0,0 +1,99 @@
+An `import` statement with a sole label will import the modules default
+export and bind it to a local variable named after the label.
+
+-- Testcase --
+import defVal from "./files/test1.uc";
+
+print(defVal, "\n");
+-- End --
+
+-- File test1.uc --
+export default "This is the default export";
+-- End --
+
+-- Args --
+-R
+-- End --
+
+-- Expect stdout --
+This is the default export
+-- End --
+
+
+Attemping to import a default export from a module without default
+export will raise an error.
+
+-- Testcase --
+import defVal from "./files/test2.uc";
+
+print(defVal, "\n");
+-- End --
+
+-- File test2.uc --
+export const x = "This is a non-default export";
+-- End --
+
+-- Args --
+-R
+-- End --
+
+-- Expect stderr --
+Syntax error: Module ./files/test2.uc has no default export
+In line 1, byte 20:
+
+ `import defVal from "./files/test2.uc";`
+ Near here ---------^
+
+
+-- End --
+
+
+In import statements usign the list syntax, the `default` keyword can be
+used to refer to default exports.
+
+-- Testcase --
+import { default as defVal } from "./files/test3.uc";
+
+print(defVal, "\n");
+-- End --
+
+-- File test3.uc --
+export default "This is the default export";
+-- End --
+
+-- Args --
+-R
+-- End --
+
+-- Expect stdout --
+This is the default export
+-- End --
+
+
+When using the default keyword within the list syntax, the `as` keyword is
+mandatory to assign a non-reserved keyword as name.
+
+-- Testcase --
+import { default } from "./files/test4.uc";
+
+print(defVal, "\n");
+-- End --
+
+-- File test4.uc --
+export default "This is the default export";
+-- End --
+
+-- Args --
+-R
+-- End --
+
+-- Expect stderr --
+Syntax error: Unexpected token
+Expecting 'as'
+In line 1, byte 18:
+
+ `import { default } from "./files/test4.uc";`
+ Near here -------^
+
+
+-- End --
diff --git a/tests/custom/04_modules/08_import_list b/tests/custom/04_modules/08_import_list
new file mode 100644
index 0000000..1a4f116
--- /dev/null
+++ b/tests/custom/04_modules/08_import_list
@@ -0,0 +1,105 @@
+An `import` statement followed by a curly brace enclosed list of names
+will import the corresponding exports from the module.
+
+-- Testcase --
+import { a, b, c } from "./files/test1.uc";
+
+print([ a, b, c ], "\n");
+-- End --
+
+-- File test1.uc --
+export const a = 1, b = 2, c = 3;
+-- End --
+
+-- Args --
+-R
+-- End --
+
+-- Expect stdout --
+[ 1, 2, 3 ]
+-- End --
+
+
+Attemping to import a not exported name will raise an error.
+
+-- Testcase --
+import y from "./files/test2.uc";
+
+print(y, "\n");
+-- End --
+
+-- File test2.uc --
+export const x = "This is a test";
+-- End --
+
+-- Args --
+-R
+-- End --
+
+-- Expect stderr --
+Syntax error: Module ./files/test2.uc has no default export
+In line 1, byte 15:
+
+ `import y from "./files/test2.uc";`
+ Near here ----^
+
+
+-- End --
+
+
+Imports may be renamed to assign an alternative local name to the
+exported module symbols. Renaming is also required for string export
+names which are no valid variable identifiers.
+
+-- Testcase --
+import { a as var1, bool as var2, "my function" as var3 } from "./files/test3.uc";
+
+print([ var1, var2, var3 ], "\n");
+-- End --
+
+-- File test3.uc --
+const a = "A string";
+
+let b = 123;
+
+function c() {
+ return "A function"
+}
+
+export {
+ a,
+ b as bool,
+ c as "my function"
+};
+-- End --
+
+-- Args --
+-R
+-- End --
+
+-- Expect stdout --
+[ "A string", 123, "function c() { ... }" ]
+-- End --
+
+
+A list expression may follow a default import expression in an `import`
+statment.
+
+-- Testcase --
+import defVal, { a as x, b as y, c as z } from "./files/test4.uc";
+
+print([defVal, x, y, z], "\n");
+-- End --
+
+-- File test4.uc --
+export const a = 1, b = 2, c = 3;
+export default a + b + c;
+-- End --
+
+-- Args --
+-R
+-- End --
+
+-- Expect stdout --
+[ 6, 1, 2, 3 ]
+-- End --
diff --git a/tests/custom/04_modules/09_import_wildcard b/tests/custom/04_modules/09_import_wildcard
new file mode 100644
index 0000000..aa3dc82
--- /dev/null
+++ b/tests/custom/04_modules/09_import_wildcard
@@ -0,0 +1,73 @@
+By specifying `*` instead of a label or an import list after an `import`
+keyword, all of the modules exports are aggregated into an object whose
+keys and values refer to the exported names and their corresponding
+values respectively.
+
+-- Testcase --
+import * as mod from "./files/test1.uc";
+
+print(mod, "\n");
+-- End --
+
+-- File test1.uc --
+export const a = 1, b = 2, c = 3;
+export default a + b + c;
+-- End --
+
+-- Args --
+-R
+-- End --
+
+-- Expect stdout --
+{ "a": 1, "b": 2, "c": 3, "default": 6 }
+-- End --
+
+
+When using the wildcard import syntax, assigning a name using the `as`
+expression is mandatory.
+
+-- Testcase --
+import * from "./files/test2.uc";
+-- End --
+
+-- File test2.uc --
+export const x = "This is a test";
+-- End --
+
+-- Args --
+-R
+-- End --
+
+-- Expect stderr --
+Syntax error: Unexpected token
+Expecting 'as'
+In line 1, byte 10:
+
+ `import * from "./files/test2.uc";`
+ ^-- Near here
+
+
+-- End --
+
+
+A wildcard expression may follow a default import expression in an `import`
+statment.
+
+-- Testcase --
+import defVal, * as mod from "./files/test3.uc";
+
+print([defVal, mod], "\n");
+-- End --
+
+-- File test3.uc --
+export const a = 1, b = 2, c = 3;
+export default a + b + c;
+-- End --
+
+-- Args --
+-R
+-- End --
+
+-- Expect stdout --
+[ 6, { "a": 1, "b": 2, "c": 3, "default": 6 } ]
+-- End --
diff --git a/tests/custom/04_modules/10_import_none b/tests/custom/04_modules/10_import_none
new file mode 100644
index 0000000..be30106
--- /dev/null
+++ b/tests/custom/04_modules/10_import_none
@@ -0,0 +1,18 @@
+An `import` statement may omit a default name, wildcard expression or name
+lsit entirely to execute a module code solely for its side effects.
+
+-- Testcase --
+import "./files/test.uc";
+-- End --
+
+-- File test.uc --
+print("This is the test module running\n");
+-- End --
+
+-- Args --
+-R
+-- End --
+
+-- Expect stdout --
+This is the test module running
+-- End --
diff --git a/tests/custom/04_modules/11_import_many_exec_once b/tests/custom/04_modules/11_import_many_exec_once
new file mode 100644
index 0000000..f469c7f
--- /dev/null
+++ b/tests/custom/04_modules/11_import_many_exec_once
@@ -0,0 +1,28 @@
+When multiple imports refer to the same module, the module will only be
+executed once. The equivalence of module paths is tested after canonicalizing
+the requested path.
+
+-- Testcase --
+import { counter as counter1 } from "./files/test/example.uc";
+import { counter as counter2 } from "files/test/example.uc";
+import { counter as counter3 } from "test.example";
+
+print([ counter1, counter2, counter3 ], "\n");
+-- End --
+
+-- File test/example.uc --
+print("This is the test module running\n");
+
+export let counter = 0;
+
+counter++;
+-- End --
+
+-- Args --
+-R -L ./files
+-- End --
+
+-- Expect stdout --
+This is the test module running
+[ 1, 1, 1 ]
+-- End --
diff --git a/tests/custom/04_modules/12_import_immutability b/tests/custom/04_modules/12_import_immutability
new file mode 100644
index 0000000..37c0bc6
--- /dev/null
+++ b/tests/custom/04_modules/12_import_immutability
@@ -0,0 +1,52 @@
+Module imports are read-only bindings to the exported module variables.
+
+-- Testcase --
+import { a } from "./files/test.uc";
+
+a = 2;
+-- End --
+
+-- File test.uc --
+export let a = 1;
+-- End --
+
+-- Args --
+-R
+-- End --
+
+-- Expect stderr --
+Syntax error: Invalid assignment to constant 'a'
+In line 3, byte 5:
+
+ `a = 2;`
+ ^-- Near here
+
+
+-- End --
+
+
+Aggregated module objects are read-only as well.
+
+-- Testcase --
+import * as mod from "./files/test.uc";
+
+mod.a = 2;
+-- End --
+
+-- File test.uc --
+export let a = 1;
+-- End --
+
+-- Args --
+-R
+-- End --
+
+-- Expect stderr --
+Type error: object value is immutable
+In line 3, byte 9:
+
+ `mod.a = 2;`
+ ^-- Near here
+
+
+-- End --
diff --git a/tests/custom/04_modules/13_import_liveness b/tests/custom/04_modules/13_import_liveness
new file mode 100644
index 0000000..ca7ff35
--- /dev/null
+++ b/tests/custom/04_modules/13_import_liveness
@@ -0,0 +1,29 @@
+Imported bindings to exported module variables are live, they'll reflect
+every change to the exported variable values.
+
+-- Testcase --
+import { counter, count } from "./files/test.uc";
+
+print(counter, "\n");
+count();
+print(counter, "\n");
+-- End --
+
+-- File test.uc --
+let counter = 1;
+
+function count() {
+ counter++;
+}
+
+export { counter, count };
+-- End --
+
+-- Args --
+-R
+-- End --
+
+-- Expect stdout --
+1
+2
+-- End --