allow destructuring tuples when assigning

This commit is contained in:
Neemek 2026-07-13 22:06:09 +02:00
parent 7cb2be8415
commit 91baf28fdf
Signed by: neemek
GPG key ID: 84FFE4D7D40AB25E
2 changed files with 73 additions and 26 deletions

View file

@ -569,23 +569,13 @@ func (c *Compiler) compile(tree Node) (TypeSignature, error) {
case AssignNodeType:
n := tree.(*AssignNode)
switch n.dest.Type() {
case ReferenceNodeType:
d := n.dest.(*ReferenceNode)
if d.name == "_" {
return c.compile(n.value)
}
if n.declare && c.isVarDeclaredHere(d.name) {
return nil, c.error(fmt.Sprintf("%s is already declared in this scope", d.name), n)
}
return c.addSetVar(d.name, n.value, n.declare)
default:
return nil, c.error(fmt.Sprintf("cannot assign to %s", n.dest.Type()), n.dest)
t, err := c.compile(n.value)
if err != nil {
return nil, err
}
return c.compileAssignFromStack(n.dest, t, n.declare)
case InvokeNodeType:
n := tree.(*InvokeNode)
@ -635,6 +625,7 @@ func (c *Compiler) compile(tree Node) (TypeSignature, error) {
// allow self-referencing
sig := n.Signature()
c.descend()
c.registerVar(n.name, sig)
// keep track of main chunk
@ -672,6 +663,7 @@ func (c *Compiler) compile(tree Node) (TypeSignature, error) {
return nil, c.error(fmt.Sprintf("yield does not match expected return; got %s, expected %s", yield, c.expectedReturn), causer)
}
c.ascend()
c.ascend()
mc.Constants[fi] = &FunctionValue{
@ -907,6 +899,51 @@ func (c *Compiler) compileBinary(binary *BinaryNode) (TypeSignature, error) {
return res, nil
}
// compileAssignFromStack assign the value of type sig which is expected to be on top of the stack.
func (c *Compiler) compileAssignFromStack(to Node, sig TypeSignature, declare bool) (TypeSignature, error) {
switch to.Type() {
case ReferenceNodeType:
d := to.(*ReferenceNode)
if d.name == "_" {
return sig, nil
}
if declare && c.isVarDeclaredHere(d.name) {
return nil, c.error(fmt.Sprintf("%s is already declared in this scope", d.name), to)
}
return c.addSetVar(d.name, sig, declare, to)
case TupleNodeType:
t := to.(*TupleNode)
tsig, ok := sig.(*TupleSignature)
if !ok {
return nil, c.error(fmt.Sprintf("cannot destructure non-tuple %s", sig), to)
}
if len(t.items) != len(tsig.Contents) {
return nil, c.error(fmt.Sprintf("not same amount of items; must be %d", len(tsig.Contents)), to)
}
c.add(InstructionDuplicate)
c.add(InstructionDestructureTuple)
// iterate from top to bottom
for i := len(t.items) - 1; i >= 0; i-- {
_, err := c.compileAssignFromStack(t.items[i], tsig.Contents[i], declare)
if err != nil {
return nil, err
}
c.add(InstructionPop)
}
return sig, nil
default:
return nil, c.error(fmt.Sprintf("cannot assign to %s", to.Type()), to)
}
}
func (c *Compiler) getVarSignature(name string, causer Node) (TypeSignature, error) {
if c.isGlobal(name) {
return SignatureOf(DefaultGlobals[name]), nil
@ -950,23 +987,20 @@ func (c *Compiler) addGetVar(name string, causer Node) (TypeSignature, error) {
return c.getVarSignature(name, causer)
}
func (c *Compiler) addSetVar(name string, value Node, declare bool) (TypeSignature, error) {
t, err := c.compile(value)
if err != nil {
return nil, err
}
// addSetVar add instructions for setting a variable of specified type which is ON TOP OF THE STACK
// does also register the variable with the correct type.
func (c *Compiler) addSetVar(name string, t TypeSignature, declare bool, causer Node) (TypeSignature, error) {
if declare {
c.add(InstructionDeclareLocal)
c.registerVar(name, t)
} else {
vt, err := c.getVarSignature(name, value)
vt, err := c.getVarSignature(name, causer) // it needs someone to blame >:3
if err != nil {
return nil, err
}
if !vt.Contains(t) {
return nil, c.error(fmt.Sprintf("cannot assign value of type %s to variable %s of type %s", t, name, vt), value)
return nil, c.error(fmt.Sprintf("cannot assign value of type %s to variable %s of type %s", t, name, vt), causer)
}
c.add(InstructionSetLocal)

View file

@ -1,8 +1,21 @@
# calculate fibonacci numbers with a loop
(a, b) := (1, 0)
for _ in 0..100 {
(a, b) = (a + b, b)
fn range(from: int, to: int) -> (fn() -> (int, bool)) {
i := from - 1
end := to - 1
fn() -> (int, bool) {
if i < end {
(i = i+1, true)
} else {
(-1, false)
}
}
}
(a, b) := (0, 1)
for _ in range(0, 100) {
(a, b) = (a + b, a)
println(a)
}