diff --git a/src/core/compileAssign.go b/src/core/compileAssign.go index 1ce3d1e..24b2be2 100644 --- a/src/core/compileAssign.go +++ b/src/core/compileAssign.go @@ -2,13 +2,13 @@ package core import ( "git.urbach.dev/cli/q/src/ast" - "git.urbach.dev/cli/q/src/errors" "git.urbach.dev/cli/q/src/token" ) // compileAssign compiles an assignment. func (f *Function) compileAssign(node *ast.Assign) error { left := node.Expression.Children[0] + right := node.Expression.Children[1] if left.Token.Kind == token.Array { return f.compileStoreArray(node) @@ -18,11 +18,9 @@ func (f *Function) compileAssign(node *ast.Assign) error { return f.compileStoreField(node) } - right := node.Expression.Children[1] - if left.IsLeaf() { return f.define(left, right, true) } - return errors.New(&NotImplemented{Subject: "multi-assignment"}, f.File, node.Expression.Source().StartPos) + return f.defineMulti(left, right, true) } \ No newline at end of file diff --git a/src/core/compileDefinition.go b/src/core/compileDefinition.go index 2ac8a22..42650f7 100644 --- a/src/core/compileDefinition.go +++ b/src/core/compileDefinition.go @@ -13,11 +13,5 @@ func (f *Function) compileDefinition(node *ast.Define) error { return f.define(left, right, false) } - rightValue, err := f.evaluate(right) - - if err != nil { - return err - } - - return f.compose(left, rightValue) + return f.defineMulti(left, right, false) } \ No newline at end of file diff --git a/src/core/compose.go b/src/core/defineMulti.go similarity index 68% rename from src/core/compose.go rename to src/core/defineMulti.go index 96e544a..ae90fc7 100644 --- a/src/core/compose.go +++ b/src/core/defineMulti.go @@ -7,16 +7,21 @@ import ( "git.urbach.dev/cli/q/src/types" ) -// compose creates SSA values from expressions and composes structs from their individual fields. -func (f *Function) compose(left *expression.Expression, rightValue ssa.Value) error { +// defineMulti creates SSA values from expressions and composes structs from their individual fields. +func (f *Function) defineMulti(left *expression.Expression, right *expression.Expression, isAssign bool) error { + rightValue, err := f.evaluate(right) + + if err != nil { + return err + } + + fn := rightValue.(*ssa.Call).Func leaves := make([]*expression.Expression, 0, 2) for leaf := range left.Leaves() { leaves = append(leaves, leaf) } - fn := rightValue.(*ssa.Call).Func - if len(leaves) != len(fn.Typ.Output) { return errors.New(&DefinitionCountMismatch{Function: fn.String(), Count: len(leaves), ExpectedCount: len(fn.Typ.Output)}, f.File, left.Source().StartPos) } @@ -32,12 +37,28 @@ func (f *Function) compose(left *expression.Expression, rightValue ssa.Value) er continue } - _, exists := f.Block().FindIdentifier(name) + leftValue, exists := f.Block().FindIdentifier(name) - if exists { + if !isAssign && exists { return errors.New(&VariableAlreadyExists{Name: name}, f.File, identifier.Source().StartPos) } + if isAssign { + if !exists { + return errors.New(&UnknownIdentifier{Name: name}, f.File, identifier.Source().StartPos) + } + + phi, isPhi := leftValue.(*ssa.Phi) + + if isPhi && phi.IsPartiallyUndefined() { + return errors.New(&PartiallyUnknownIdentifier{Name: name}, f.File, identifier.Source().StartPos) + } + + if !types.Is(fn.Typ.Output[i], leftValue.Type()) { + return errors.New(&TypeMismatch{Encountered: fn.Typ.Output[i].Name(), Expected: leftValue.Type().Name()}, f.File, right.Source().StartPos) + } + } + structure, isStructType := types.Unwrap(fn.Typ.Output[i]).(*types.Struct) if !isStructType { diff --git a/tests/multi-define-assign.q b/tests/multi-define-assign.q new file mode 100644 index 0000000..38387a5 --- /dev/null +++ b/tests/multi-define-assign.q @@ -0,0 +1,23 @@ +main() { + a, b, c, d := reverse4(1, 2, 3, 4) + assert a == 4 + assert b == 3 + assert c == 2 + assert d == 1 + + a, b, c, d = reverse4(10, 20, 30, 40) + assert a == 40 + assert b == 30 + assert c == 20 + assert d == 10 + + a, _, c, _ = reverse4(100, 200, 300, 400) + assert a == 400 + assert b == 30 + assert c == 200 + assert d == 10 +} + +reverse4(x int, y int, z int, w int) -> (int, int, int, int) { + return w, z, y, x +} \ No newline at end of file diff --git a/tests/tests_test.go b/tests/tests_test.go index 455ae96..51addb3 100644 --- a/tests/tests_test.go +++ b/tests/tests_test.go @@ -81,6 +81,7 @@ var tests = []run{ {"return-3", "", "", 0}, {"return-4", "", "", 0}, {"return-string", "", "Hello\n", 0}, + {"multi-define-assign", "", "", 0}, {"ignore-error", "", "", 0}, {"ignore-unused-field", "", "", 0}, {"slice", "", "Hello World", 0},