Implemented multiple return values

This commit is contained in:
2024-08-05 12:39:07 +02:00
parent d4020da6d9
commit 42f0367a94
15 changed files with 113 additions and 35 deletions

View File

@ -3,5 +3,5 @@ package x64
// Return transfers program control to a return address located on the top of the stack.
// The address is usually placed on the stack by a Call instruction.
func Return(code []byte) []byte {
return append(code, 0xc3)
return append(code, 0xC3)
}

View File

@ -29,8 +29,8 @@ func Count(body AST, buffer []byte, kind token.Kind, name string) uint8 {
count += Count(node.Body, buffer, kind, name)
case *Return:
if node.Value != nil {
count += node.Value.Count(buffer, kind, name)
for _, value := range node.Values {
count += value.Count(buffer, kind, name)
}
case *Switch:

View File

@ -6,5 +6,5 @@ import (
// Return represents a return statement.
type Return struct {
Value *expression.Expression
Values []*expression.Expression
}

View File

@ -48,8 +48,8 @@ func parseKeyword(tokens token.List, source []byte, nodes AST) (Node, error) {
return &Return{}, nil
}
value := expression.Parse(tokens[1:])
return &Return{Value: value}, nil
values := expression.NewList(tokens[1:])
return &Return{Values: values}, nil
case token.Switch:
blockStart := tokens.IndexKind(token.BlockStart)

View File

@ -31,7 +31,10 @@ func (f *Function) CompileASTNode(node ast.Node) error {
return f.CompileLoop(node)
case *ast.Return:
f.Fold(node.Value)
for _, value := range node.Values {
f.Fold(value)
}
return f.CompileReturn(node)
case *ast.Switch:

View File

@ -69,6 +69,7 @@ func (f *Function) CompileCall(root *expression.Expression) error {
return err
}
// TODO: Save all return value registers of the function
f.SaveRegister(f.CPU.Output[0])
// Push

View File

@ -1,36 +1,50 @@
package core
import (
"git.akyoto.dev/cli/q/src/build/asm"
"git.akyoto.dev/cli/q/src/build/ast"
"git.akyoto.dev/cli/q/src/build/errors"
"git.akyoto.dev/cli/q/src/build/scope"
"git.akyoto.dev/cli/q/src/build/token"
"git.akyoto.dev/cli/q/src/build/expression"
)
// CompileDefinition compiles a variable definition.
func (f *Function) CompileDefinition(node *ast.Define) error {
left := node.Expression.Children[0]
right := node.Expression.Children[1]
name := left.Token.Text(f.File.Bytes)
if f.IdentifierExists(name) {
return errors.New(&errors.VariableAlreadyExists{Name: name}, f.File, left.Token.Position)
if left.IsLeaf() {
variable, err := f.Define(left)
if err != nil {
return err
}
err = f.ExpressionToRegister(right, variable.Register)
f.AddVariable(variable)
return err
}
uses := token.Count(f.Body, f.File.Bytes, token.Identifier, name) - 1
if uses == 0 {
return errors.New(&errors.UnusedVariable{Name: name}, f.File, left.Token.Position)
if !ast.IsFunctionCall(right) {
return errors.New(errors.NotImplemented, f.File, node.Expression.Token.Position)
}
register := f.NewRegister()
err := f.ExpressionToRegister(right, register)
count := 0
err := f.CompileCall(right)
f.AddVariable(&scope.Variable{
Name: name,
Register: register,
Alive: uses,
if err != nil {
return err
}
return left.EachLeaf(func(leaf *expression.Expression) error {
variable, err := f.Define(leaf)
if err != nil {
return err
}
f.RegisterRegister(asm.MOVE, variable.Register, f.CPU.Output[count])
f.AddVariable(variable)
count++
return nil
})
return err
}

View File

@ -8,9 +8,9 @@ import (
func (f *Function) CompileReturn(node *ast.Return) error {
defer f.Return()
if node.Value == nil {
if len(node.Values) == 0 {
return nil
}
return f.ExpressionToRegister(node.Value, f.CPU.Output[0])
return f.ExpressionsToRegisters(node.Values, f.CPU.Output)
}

31
src/build/core/Define.go Normal file
View File

@ -0,0 +1,31 @@
package core
import (
"git.akyoto.dev/cli/q/src/build/errors"
"git.akyoto.dev/cli/q/src/build/expression"
"git.akyoto.dev/cli/q/src/build/scope"
"git.akyoto.dev/cli/q/src/build/token"
)
// Define defines a new variable.
func (f *Function) Define(leaf *expression.Expression) (*scope.Variable, error) {
name := leaf.Token.Text(f.File.Bytes)
if f.IdentifierExists(name) {
return nil, errors.New(&errors.VariableAlreadyExists{Name: name}, f.File, leaf.Token.Position)
}
uses := token.Count(f.Body, f.File.Bytes, token.Identifier, name) - 1
if uses == 0 {
return nil, errors.New(&errors.UnusedVariable{Name: name}, f.File, leaf.Token.Position)
}
variable := &scope.Variable{
Name: name,
Register: f.NewRegister(),
Alive: uses,
}
return variable, nil
}

View File

@ -11,8 +11,9 @@ import (
// ExpressionToRegister puts the result of an expression into the specified register.
func (f *Function) ExpressionToRegister(node *expression.Expression, register cpu.Register) error {
f.SaveRegister(register)
if node.IsFolded {
f.SaveRegister(register)
f.RegisterNumber(asm.MOVE, register, node.Value)
return nil
}
@ -25,7 +26,6 @@ func (f *Function) ExpressionToRegister(node *expression.Expression, register cp
err := f.CompileCall(node)
if register != f.CPU.Output[0] {
f.SaveRegister(register)
f.RegisterRegister(asm.MOVE, register, f.CPU.Output[0])
}
@ -70,7 +70,6 @@ func (f *Function) ExpressionToRegister(node *expression.Expression, register cp
err = f.Execute(node.Token, register, right)
if register != final {
f.SaveRegister(final)
f.RegisterRegister(asm.MOVE, final, register)
f.FreeRegister(register)
}

View File

@ -7,7 +7,7 @@ import (
// ExpressionsToRegisters moves multiple expressions into the specified registers.
func (f *Function) ExpressionsToRegisters(expressions []*expression.Expression, registers []cpu.Register) error {
for i := len(registers) - 1; i >= 0; i-- {
for i := len(expressions) - 1; i >= 0; i-- {
err := f.ExpressionToRegister(expressions[i], registers[i])
if err != nil {