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.
This commit is contained in:
James Shubin
2025-01-17 14:03:48 -05:00
parent c8f911ec5d
commit 81b102ed7f
3 changed files with 52 additions and 8 deletions

View File

@@ -3791,7 +3791,7 @@ func (obj *StmtProg) SetScope(scope *interfaces.Scope) error {
} }
newName = name newName = name
} }
if previous, exists := newVariables[newName]; exists { if previous, exists := newVariables[newName]; exists && alias != interfaces.BareSymbol {
// don't overwrite in same scope // don't overwrite in same scope
return fmt.Errorf("can't squash variable `%s` from `%s` by import of `%s`", newName, previous, imp.Name) 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 newName = name
} }
if previous, exists := newFunctions[newName]; exists { if previous, exists := newFunctions[newName]; exists && alias != interfaces.BareSymbol {
// don't overwrite in same scope // don't overwrite in same scope
return fmt.Errorf("can't squash function `%s` from `%s` by import of `%s`", newName, previous, imp.Name) 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 newName = name
} }
if previous, exists := newClasses[newName]; exists { if previous, exists := newClasses[newName]; exists && alias != interfaces.BareSymbol {
// don't overwrite in same scope // don't overwrite in same scope
return fmt.Errorf("can't squash class `%s` from `%s` by import of `%s`", newName, previous, imp.Name) 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... // everything has been merged, move on to next import...
imports[imp.Name] = struct{}{} // mark as found in scope 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... // 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 newName = name
} }
if previous, exists := newVariables[newName]; exists { if previous, exists := newVariables[newName]; exists && alias != interfaces.BareSymbol {
// don't overwrite in same scope // don't overwrite in same scope
return fmt.Errorf("can't squash variable `%s` from `%s` by include of `%s`", newName, previous, include.Name) 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 newName = name
} }
if previous, exists := newFunctions[newName]; exists { if previous, exists := newFunctions[newName]; exists && alias != interfaces.BareSymbol {
// don't overwrite in same scope // don't overwrite in same scope
return fmt.Errorf("can't squash function `%s` from `%s` by include of `%s`", newName, previous, include.Name) 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 newName = name
} }
if previous, exists := newClasses[newName]; exists { if previous, exists := newClasses[newName]; exists && alias != interfaces.BareSymbol {
// don't overwrite in same scope // don't overwrite in same scope
return fmt.Errorf("can't squash class `%s` from `%s` by include of `%s`", newName, previous, include.Name) 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... // everything has been merged, move on to next include...
//includes[include.Name] = struct{}{} // don't mark as found in scope //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
}
} }
} }

View File

@@ -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]

View File

@@ -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]