diff --git a/compiler/src/formatting/format.re b/compiler/src/formatting/format.re index 0ef65ec721..91327cd346 100644 --- a/compiler/src/formatting/format.re +++ b/compiler/src/formatting/format.re @@ -3956,8 +3956,9 @@ and print_expression_inner = }; switch (item) { - | PUseValue({name, alias}) - | PUseModule({name, alias}) => item_name(name, alias) + | PUseValue({name, alias}) => item_name(name, alias) + | PUseModule({name, alias}) => + Doc.concat([Doc.text("module "), item_name(name, alias)]) | PUseType({name, alias}) => Doc.concat([Doc.text("type "), item_name(name, alias)]) }; @@ -5106,8 +5107,9 @@ let rec toplevel_print = }; switch (item) { - | PProvideValue({name, alias}) - | PProvideModule({name, alias}) => item_name(name, alias) + | PProvideValue({name, alias}) => item_name(name, alias) + | PProvideModule({name, alias}) => + Doc.concat([Doc.text("module "), item_name(name, alias)]) | PProvideType({name, alias}) => Doc.concat([Doc.text("type "), item_name(name, alias)]) }; diff --git a/compiler/src/parsing/parser.messages b/compiler/src/parsing/parser.messages index ef5c53987c..47d29ac21d 100644 --- a/compiler/src/parsing/parser.messages +++ b/compiler/src/parsing/parser.messages @@ -63,6 +63,15 @@ program: MODULE UIDENT EOL INCLUDE STRING AS YIELD ## The known suffix of the stack is as follows: ## AS ## +program: MODULE UIDENT EOL PROVIDE LBRACE MODULE YIELD +## +## Ends in an error in state: 829. +## +## provide_item -> MODULE . aliasable(uid) [ RBRACE EOL COMMA ] +## +## The known suffix of the stack is as follows: +## MODULE +## Expected an uppercase module identifier. @@ -269,14 +278,14 @@ program: MODULE UIDENT EOL FOREIGN WASM LIDENT COLON UIDENT AS YIELD ## The known suffix of the stack is as follows: ## AS ## -program: MODULE UIDENT EOL PROVIDE LBRACE UIDENT AS EOL YIELD +program: MODULE UIDENT EOL PROVIDE LBRACE MODULE UIDENT AS EOL YIELD ## -## Ends in an error in state: 51. +## Ends in an error in state: 52. ## -## as_prefix(uid) -> AS option(eols) . uid [ RBRACE EOL COMMA ] +## as_prefix(uid) -> AS eols . uid [ RBRACE EOL COMMA ] ## ## The known suffix of the stack is as follows: -## AS option(eols) +## AS eols ## ## WARNING: This example involves spurious reductions. ## This implies that, although the LR(1) items shown above provide an @@ -284,7 +293,6 @@ program: MODULE UIDENT EOL PROVIDE LBRACE UIDENT AS EOL YIELD ## may provide an INCOMPLETE view of the future (what was expected next). ## In state 3, spurious reduction of production nonempty_list(eol) -> EOL ## In state 6, spurious reduction of production eols -> nonempty_list(eol) -## In state 53, spurious reduction of production option(eols) -> eols ## program: MODULE UIDENT EOL PROVIDE LBRACE LIDENT AS EOL YIELD ## @@ -424,7 +432,7 @@ program: MODULE UIDENT EOL FROM UIDENT USE LBRACE YIELD ## In state 31, spurious reduction of production lbrace -> LBRACE ## -Expected a lowercase identifier to use a value, an uppercase identifier to use a module, or the keyword `type` followed by an uppercase identifier to use a type. +Expected a lowercase identifier to use a value, the keyword `module` followed by an uppercase identifier to use a module, or the keyword `type` followed by an uppercase identifier to use a type. program: MODULE UIDENT EOL FROM UIDENT USE LBRACE TYPE YIELD ## @@ -435,10 +443,19 @@ program: MODULE UIDENT EOL FROM UIDENT USE LBRACE TYPE YIELD ## The known suffix of the stack is as follows: ## TYPE ## +program: MODULE UIDENT EOL FROM UIDENT USE LBRACE MODULE YIELD +## +## Ends in an error in state: 57. +## +## use_item -> MODULE . aliasable(uid) [ RBRACE EOL COMMA ] +## +## The known suffix of the stack is as follows: +## MODULE +## Expected an uppercase type identifier. -program: MODULE UIDENT EOL PROVIDE LBRACE UIDENT YIELD +program: MODULE UIDENT EOL PROVIDE LBRACE MODULE UIDENT YIELD ## ## Ends in an error in state: 49. ## @@ -450,11 +467,12 @@ program: MODULE UIDENT EOL PROVIDE LBRACE UIDENT YIELD Expected the keyword `as` followed by a module alias, a comma followed by more items to use, or `}` to end the statement. -program: MODULE UIDENT EOL PROVIDE LBRACE UIDENT AS YIELD +program: MODULE UIDENT EOL PROVIDE LBRACE MODULE UIDENT AS YIELD ## ## Ends in an error in state: 50. ## ## as_prefix(uid) -> AS . uid [ RBRACE EOL COMMA ] +## as_prefix(uid) -> AS . eols uid [ RBRACE EOL COMMA ] ## ## The known suffix of the stack is as follows: ## AS @@ -508,7 +526,7 @@ program: MODULE UIDENT EOL PROVIDE LBRACE LIDENT COMMA YIELD ## In state 63, spurious reduction of production comma -> COMMA ## -Expected more items to provide or `}` to end the provide statement. +Expected a lowercase identifier to provide a value, the keyword `module` followed by an uppercase identifier to provide a module, the keyword `type` followed by an uppercase identifier to provide a type, or `}` to end the provide statement. program: MODULE UIDENT EOL PROVIDE LBRACE YIELD ## @@ -526,7 +544,7 @@ program: MODULE UIDENT EOL PROVIDE LBRACE YIELD ## In state 31, spurious reduction of production lbrace -> LBRACE ## -Expected a comma-separated list of items to provide. +Expected a lowercase identifier to provide a value, the keyword `module` followed by an uppercase identifier to provide a module, or the keyword `type` followed by an uppercase identifier to provide a type. program: MODULE UIDENT EOL PROVIDE LBRACE TYPE YIELD ## @@ -594,7 +612,7 @@ program: MODULE UIDENT EOL FROM UIDENT USE LBRACE LIDENT COMMA YIELD ## In state 63, spurious reduction of production comma -> COMMA ## -Expected more items to use or `}` to end the statement. +Expected a lowercase identifier to use a value, the keyword `module` followed by an uppercase identifier to use a module, the keyword `type` followed by an uppercase identifier to use a type, or `}` to end the use statement. program: MODULE UIDENT EOL ASSERT WHEN ## diff --git a/compiler/src/parsing/parser.mly b/compiler/src/parsing/parser.mly index f1c3110201..47abbf9223 100644 --- a/compiler/src/parsing/parser.mly +++ b/compiler/src/parsing/parser.mly @@ -342,7 +342,7 @@ aliasable(X): use_item: | TYPE aliasable(uid) { PUseType { name=fst $2; alias = snd $2; loc=to_loc $loc} } - | aliasable(uid) { PUseModule { name=fst $1; alias = snd $1; loc=to_loc $loc} } + | MODULE aliasable(uid) { PUseModule { name=fst $2; alias = snd $2; loc=to_loc $loc} } | aliasable(lid) { PUseValue { name=fst $1; alias = snd $1; loc=to_loc $loc} } use_items: @@ -371,7 +371,7 @@ data_declaration_stmts: provide_item: | TYPE aliasable(uid) { PProvideType { name=fst $2; alias = snd $2; loc=to_loc $loc} } - | aliasable(uid) { PProvideModule { name=fst $1; alias = snd $1; loc=to_loc $loc} } + | MODULE aliasable(uid) { PProvideModule { name=fst $2; alias = snd $2; loc=to_loc $loc} } | aliasable(lid) { PProvideValue { name=fst $1; alias = snd $1; loc=to_loc $loc} } provide_items: diff --git a/compiler/test/grainfmt/includes.expected.gr b/compiler/test/grainfmt/includes.expected.gr index d3ee078019..817e54794f 100644 --- a/compiler/test/grainfmt/includes.expected.gr +++ b/compiler/test/grainfmt/includes.expected.gr @@ -9,10 +9,15 @@ include "option" as Opt include "array" include "array" as Foo from List use { length, map, forEach as each } -from Opt use { MutableOpt, ImmutableOpt as Imm, type Opt, type Opt as OptAlias } +from Opt use { + module MutableOpt, + module ImmutableOpt as Imm, + type Opt, + type Opt as OptAlias, +} from Opt use { /* comment1 */ /* comment2 */ /* comment3 */ /* comment4 */ /* comment5 */ /* comment6 */ /* comment7 */ - MutableOpt, - ImmutableOpt as Imm, + module MutableOpt, + module ImmutableOpt as Imm, type Opt, type Opt as OptAlias, } diff --git a/compiler/test/grainfmt/includes.input.gr b/compiler/test/grainfmt/includes.input.gr index 286014ee69..b4b71bdc47 100644 --- a/compiler/test/grainfmt/includes.input.gr +++ b/compiler/test/grainfmt/includes.input.gr @@ -10,8 +10,8 @@ include "option" as include /* special include */ "array" include "array" as /* special include */ Foo from List use { length, map, forEach as each } -from Opt use { MutableOpt, ImmutableOpt as Imm, type Opt, type Opt as OptAlias } -from Opt use { MutableOpt, /* comment1 */ ImmutableOpt /* comment2 */ as /* comment3 */ Imm /* comment4 */, /* comment5 */ type /* comment6 */ Opt, type Opt as /* comment7 */ OptAlias } +from Opt use { module MutableOpt, module ImmutableOpt as Imm, type Opt, type Opt as OptAlias } +from Opt use { module MutableOpt, /* comment1 */ module ImmutableOpt /* comment2 */ as /* comment3 */ Imm /* comment4 */, /* comment5 */ type /* comment6 */ Opt, type Opt as /* comment7 */ OptAlias } include "runtime/unsafe/wasmi32" from WasmI32 use { diff --git a/compiler/test/input/modules/provideIncludedModule.gr b/compiler/test/input/modules/provideIncludedModule.gr index 3fcbce151a..d601a46d5c 100644 --- a/compiler/test/input/modules/provideIncludedModule.gr +++ b/compiler/test/input/modules/provideIncludedModule.gr @@ -3,13 +3,13 @@ module ProvideIncludedModule include "option" as Option include "array" as Array -provide { Option as Smoption } +provide { module Option as Smoption } module Qux { provide let val = 9 provide module Quux { - provide { Array } + provide { module Array } } } -provide { Qux } +provide { module Qux } diff --git a/compiler/test/input/nestedModules.gr b/compiler/test/input/nestedModules.gr index 463d2769c7..930fe8178b 100644 --- a/compiler/test/input/nestedModules.gr +++ b/compiler/test/input/nestedModules.gr @@ -7,7 +7,7 @@ module Foo { provide let foo = "hello from foo" provide module Bar { provide let bar = "hello from bar" - provide { List } + provide { module List } } } diff --git a/compiler/test/stdlib/array.test.gr b/compiler/test/stdlib/array.test.gr index 38b7b66b5c..b0177050ef 100644 --- a/compiler/test/stdlib/array.test.gr +++ b/compiler/test/stdlib/array.test.gr @@ -437,7 +437,7 @@ assert Array.chunk(3, [> 1, 2, 3, 4, 5, 6, 7, 8]) == assert Array.chunk(10, [> 1, 2, 3, 4, 5]) == [> [> 1, 2, 3, 4, 5]] module Immutable { - from Array use { Immutable as Array } + from Array use { module Immutable as Array } let fromList = Array.fromList let arr = fromList([1, 2, 3]) diff --git a/compiler/test/stdlib/map.test.gr b/compiler/test/stdlib/map.test.gr index 49059cd4fb..cab66b7833 100644 --- a/compiler/test/stdlib/map.test.gr +++ b/compiler/test/stdlib/map.test.gr @@ -413,7 +413,7 @@ Map.update( assert Map.contains("c", toUpdate) == false module Immutable { - from Map use { Immutable as Map } + from Map use { module Immutable as Map } let strKeys = Map.fromList([("🌾", 1), ("🐑", 2), ("🧱", 3)]) let numKeys = Map.fromList([(1, "🌾"), (2, "🐑"), (3, "🧱")]) diff --git a/compiler/test/stdlib/priorityqueue.test.gr b/compiler/test/stdlib/priorityqueue.test.gr index 727f8ef3d2..469924ad22 100644 --- a/compiler/test/stdlib/priorityqueue.test.gr +++ b/compiler/test/stdlib/priorityqueue.test.gr @@ -85,7 +85,7 @@ assert PriorityQueue.fromList(Array.toList(lotsOfVals), compare) == PriorityQueue.fromArray(lotsOfVals, compare) module Immutable { - from PriorityQueue use { Immutable as PriorityQueue } + from PriorityQueue use { module Immutable as PriorityQueue } // formatter-ignore let lotsOfVals = [> diff --git a/compiler/test/stdlib/queue.test.gr b/compiler/test/stdlib/queue.test.gr index 0d993f721f..c7fb80a367 100644 --- a/compiler/test/stdlib/queue.test.gr +++ b/compiler/test/stdlib/queue.test.gr @@ -88,7 +88,7 @@ assert Queue.pop(queue) == Some(10) assert Queue.pop(queue) == None module Immutable { - from Queue use { Immutable as Queue } + from Queue use { module Immutable as Queue } // 1 <- 2 <- 3 let sampleQueue = Queue.push(3, Queue.push(2, Queue.push(1, Queue.empty))) diff --git a/compiler/test/stdlib/range.test.gr b/compiler/test/stdlib/range.test.gr index d71c1c3f47..e381306375 100644 --- a/compiler/test/stdlib/range.test.gr +++ b/compiler/test/stdlib/range.test.gr @@ -47,7 +47,7 @@ assert Range.map(toString, exclusiveDescendingRange) == ["5", "4", "3", "2"] assert Range.map(toString, exclusiveSameRange) == [] module Inclusive { - from Range use { Inclusive as Range } + from Range use { module Inclusive as Range } let inclusiveAscendingRange = { rangeStart: 1, rangeEnd: 5 } let inclusiveDescendingRange = { rangeStart: 5, rangeEnd: 1 } diff --git a/compiler/test/stdlib/set.test.gr b/compiler/test/stdlib/set.test.gr index d9df834df1..8b30859963 100644 --- a/compiler/test/stdlib/set.test.gr +++ b/compiler/test/stdlib/set.test.gr @@ -261,7 +261,7 @@ let largeSet = Set.fromArray(Array.init(128, i => i)) assert Set.getInternalStats(largeSet) == (128, 64) module Immutable { - from Set use { Immutable as Set } + from Set use { module Immutable as Set } // Set.isEmpty diff --git a/compiler/test/stdlib/stack.test.gr b/compiler/test/stdlib/stack.test.gr index 0fb0b09509..57f460d26b 100644 --- a/compiler/test/stdlib/stack.test.gr +++ b/compiler/test/stdlib/stack.test.gr @@ -57,7 +57,7 @@ Stack.push(0, stack2) assert stack == stack2 module Immutable { - from Stack use { Immutable as Stack } + from Stack use { module Immutable as Stack } // 1 <- 2 <- 3 let sampleStack = Stack.push(3, Stack.push(2, Stack.push(1, Stack.empty))) diff --git a/compiler/test/suites/includes.re b/compiler/test/suites/includes.re index 0121ef19bb..d9ed2d6aa4 100644 --- a/compiler/test/suites/includes.re +++ b/compiler/test/suites/includes.re @@ -71,14 +71,24 @@ describe("includes", ({test, testSkip}) => { ); assertCompileError( "include_some_error3", - "include \"provideAll\" as ProvideAll; from ProvideAll use {Foo}; a", + "include \"provideAll\" as ProvideAll; from ProvideAll use {module Foo}", "Unbound module Foo in module ProvideAll", ); assertCompileError( - "include_some_error3", - "include \"provideAll\" as ProvideAll; from ProvideAll use {x, Foo}; a", + "include_some_error4", + "include \"provideAll\" as ProvideAll; from ProvideAll use {x, module Foo}", "Unbound module Foo in module ProvideAll", ); + assertCompileError( + "include_some_error5", + "include \"provideAll\" as ProvideAll; from ProvideAll use {Foo}", + "Expected a lowercase identifier to use a value, the keyword `module` followed by an uppercase identifier to use a module, or the keyword `type` followed by an uppercase identifier to use a type.", + ); + assertCompileError( + "include_some_error6", + "include \"provideAll\" as ProvideAll; from ProvideAll use {a, Foo}", + "Expected a lowercase identifier to use a value, the keyword `module` followed by an uppercase identifier to use a module, the keyword `type` followed by an uppercase identifier to use a type, or `}` to end the use statement.", + ); /* include module tests */ assertSnapshot("include_module", "include \"provideAll\" as Foo; Foo.x"); assertSnapshot( @@ -94,12 +104,12 @@ describe("includes", ({test, testSkip}) => { /* use well-formedness errors */ assertCompileError( "include_alias_illegal_renaming", - "include \"list\" as List; from List use {Cons as cons, Empty}; cons(3, Empty)", + "include \"list\" as List; from List use {module Cons as cons, module Empty}; cons(3, Empty)", "Expected an uppercase module alias", ); assertCompileError( "include_alias_illegal_renaming2", - "include \"list\" as List; from List use {sum as Sum, Empty}; sum(Empty)", + "include \"list\" as List; from List use {sum as Sum, module Empty}; sum(Empty)", "Expected a lowercase alias", ); assertCompileError( diff --git a/compiler/test/suites/modules.re b/compiler/test/suites/modules.re index ea2e57ec82..06b06b1cf3 100644 --- a/compiler/test/suites/modules.re +++ b/compiler/test/suites/modules.re @@ -92,7 +92,7 @@ describe("modules", ({test, testSkip}) => { provide let foo = "foo2" } } - from Foo use { foo, type Foo, Foo } + from Foo use { foo, type Foo, module Foo } print(foo) print(Foo.foo) print(Foo) @@ -137,7 +137,7 @@ describe("modules", ({test, testSkip}) => { module ReprovidedSimple include "simpleModule" - provide { Simple } + provide { module Simple } |}, ); let ic = open_in_bin(outfile); diff --git a/compiler/test/suites/provides.re b/compiler/test/suites/provides.re index 01f22bd31a..1e57d84406 100644 --- a/compiler/test/suites/provides.re +++ b/compiler/test/suites/provides.re @@ -89,7 +89,7 @@ describe("provides", ({test, testSkip}) => { ); assertCompileError( "provide11", - "enum Foo { Bar }; provide { Bar }", + "enum Foo { Bar }; provide { module Bar }", "Unbound module Bar", ); assertSnapshot( @@ -99,6 +99,16 @@ describe("provides", ({test, testSkip}) => { ProvidedType.apply((arg) => print("ok")) |}, ); + assertCompileError( + "provide13", + "module Nested { let val = 1 }; provide { Nested }", + "Expected a lowercase identifier to provide a value, the keyword `module` followed by an uppercase identifier to provide a module, or the keyword `type` followed by an uppercase identifier to provide a type.", + ); + assertCompileError( + "provide13", + "let a = 1; module Nested { let val = 1 }; provide { a, Nested }", + "Expected a lowercase identifier to provide a value, the keyword `module` followed by an uppercase identifier to provide a module, the keyword `type` followed by an uppercase identifier to provide a type, or `}` to end the provide statement.", + ); assertCompileError( "regression_issue_1489", "provide { foo }", @@ -131,7 +141,7 @@ describe("provides", ({test, testSkip}) => { ); assertCompileError( "multiple_provides_6", - "provide module Foo {void}; provide {Foo}", + "provide module Foo {void}; provide {module Foo}", "provided multiple times", ); assertCompileError(