From 81b102ed7f318efdad9fb8e778e00e0653caecd9 Mon Sep 17 00:00:00 2001 From: James Shubin Date: Fri, 17 Jan 2025 14:03:48 -0500 Subject: [PATCH] lang: ast: Allow multiple star imports If more than one star import is present in the same scope, allow it. If one star import could overwrite something, ordering is not guaranteed. We allow this for now, but we might create a compiler fix to stop it. This adds a test to notice both of these behaviours. --- lang/ast/structs.go | 20 +++++++++++------- .../TestAstFunc2/import-double-star-bad.txtar | 21 +++++++++++++++++++ .../TestAstFunc2/import-double-star.txtar | 19 +++++++++++++++++ 3 files changed, 52 insertions(+), 8 deletions(-) create mode 100644 lang/interpret_test/TestAstFunc2/import-double-star-bad.txtar create mode 100644 lang/interpret_test/TestAstFunc2/import-double-star.txtar diff --git a/lang/ast/structs.go b/lang/ast/structs.go index b32e0fe8..37be5103 100644 --- a/lang/ast/structs.go +++ b/lang/ast/structs.go @@ -3791,7 +3791,7 @@ func (obj *StmtProg) SetScope(scope *interfaces.Scope) error { } newName = name } - if previous, exists := newVariables[newName]; exists { + if previous, exists := newVariables[newName]; exists && alias != interfaces.BareSymbol { // don't overwrite in same scope return fmt.Errorf("can't squash variable `%s` from `%s` by import of `%s`", newName, previous, imp.Name) } @@ -3806,7 +3806,7 @@ func (obj *StmtProg) SetScope(scope *interfaces.Scope) error { } newName = name } - if previous, exists := newFunctions[newName]; exists { + if previous, exists := newFunctions[newName]; exists && alias != interfaces.BareSymbol { // don't overwrite in same scope return fmt.Errorf("can't squash function `%s` from `%s` by import of `%s`", newName, previous, imp.Name) } @@ -3821,7 +3821,7 @@ func (obj *StmtProg) SetScope(scope *interfaces.Scope) error { } newName = name } - if previous, exists := newClasses[newName]; exists { + if previous, exists := newClasses[newName]; exists && alias != interfaces.BareSymbol { // don't overwrite in same scope return fmt.Errorf("can't squash class `%s` from `%s` by import of `%s`", newName, previous, imp.Name) } @@ -3831,7 +3831,9 @@ func (obj *StmtProg) SetScope(scope *interfaces.Scope) error { // everything has been merged, move on to next import... imports[imp.Name] = struct{}{} // mark as found in scope - aliases[alias] = struct{}{} + if alias != interfaces.BareSymbol { + aliases[alias] = struct{}{} + } } // TODO: this could be called once at the top-level, and then cached... @@ -4105,7 +4107,7 @@ func (obj *StmtProg) SetScope(scope *interfaces.Scope) error { } newName = name } - if previous, exists := newVariables[newName]; exists { + if previous, exists := newVariables[newName]; exists && alias != interfaces.BareSymbol { // don't overwrite in same scope return fmt.Errorf("can't squash variable `%s` from `%s` by include of `%s`", newName, previous, include.Name) } @@ -4120,7 +4122,7 @@ func (obj *StmtProg) SetScope(scope *interfaces.Scope) error { } newName = name } - if previous, exists := newFunctions[newName]; exists { + if previous, exists := newFunctions[newName]; exists && alias != interfaces.BareSymbol { // don't overwrite in same scope return fmt.Errorf("can't squash function `%s` from `%s` by include of `%s`", newName, previous, include.Name) } @@ -4135,7 +4137,7 @@ func (obj *StmtProg) SetScope(scope *interfaces.Scope) error { } newName = name } - if previous, exists := newClasses[newName]; exists { + if previous, exists := newClasses[newName]; exists && alias != interfaces.BareSymbol { // don't overwrite in same scope return fmt.Errorf("can't squash class `%s` from `%s` by include of `%s`", newName, previous, include.Name) } @@ -4145,7 +4147,9 @@ func (obj *StmtProg) SetScope(scope *interfaces.Scope) error { // everything has been merged, move on to next include... //includes[include.Name] = struct{}{} // don't mark as found in scope - aliases[alias] = struct{}{} // do track these as a bonus + if alias != interfaces.BareSymbol { // XXX: check if this one and the above ones in this collection are needed too + aliases[alias] = struct{}{} // do track these as a bonus + } } } diff --git a/lang/interpret_test/TestAstFunc2/import-double-star-bad.txtar b/lang/interpret_test/TestAstFunc2/import-double-star-bad.txtar new file mode 100644 index 00000000..d50e33db --- /dev/null +++ b/lang/interpret_test/TestAstFunc2/import-double-star-bad.txtar @@ -0,0 +1,21 @@ +-- metadata.yaml -- +#files: "files/" # these are some extra files we can use (is the default) +-- main.mcl -- +# XXX: When using multiple star imports, a subsequent one may overwrite the +# earlier one. We should probably make this a compile error. +import "foo.mcl" as * +import "bar.mcl" as * + +include foo("hello") +include foo("world") +-- foo.mcl -- +class foo($s) { + test "foo:${s}" {} +} +-- bar.mcl -- +class foo($s) { + test "bar:${s}" {} +} +-- OUTPUT -- +Vertex: test[bar:hello] +Vertex: test[bar:world] diff --git a/lang/interpret_test/TestAstFunc2/import-double-star.txtar b/lang/interpret_test/TestAstFunc2/import-double-star.txtar new file mode 100644 index 00000000..8a06c43c --- /dev/null +++ b/lang/interpret_test/TestAstFunc2/import-double-star.txtar @@ -0,0 +1,19 @@ +-- metadata.yaml -- +#files: "files/" # these are some extra files we can use (is the default) +-- main.mcl -- +import "foo.mcl" as * +import "bar.mcl" as * + +include foo("hello") +include bar("world") +-- foo.mcl -- +class foo($s) { + test "${s}" {} +} +-- bar.mcl -- +class bar($s) { + test "${s}" {} +} +-- OUTPUT -- +Vertex: test[hello] +Vertex: test[world]