Implemented multiple return values
This commit is contained in:
parent
d4020da6d9
commit
42f0367a94
@ -115,11 +115,11 @@ This is what generates expressions from tokens.
|
||||
- [x] Loops
|
||||
- [x] Hexadecimal, octal and binary literals
|
||||
- [x] Escape sequences
|
||||
- [x] Multiple return values
|
||||
- [ ] Data structures
|
||||
- [ ] Type system
|
||||
- [ ] Type operator: `|` (`User | Error`)
|
||||
- [ ] Error handling
|
||||
- [ ] Multiple return values
|
||||
- [ ] Threading library
|
||||
- [ ] Self-hosted compiler
|
||||
|
||||
|
@ -4,13 +4,14 @@ import sys
|
||||
number(x) {
|
||||
length := 20
|
||||
buffer := mem.alloc(length)
|
||||
tmp := itoa(x, buffer, length)
|
||||
sys.write(1, tmp, buffer + length - tmp)
|
||||
address, count := itoa(x, buffer, length)
|
||||
sys.write(1, address, count)
|
||||
mem.free(buffer, length)
|
||||
}
|
||||
|
||||
itoa(x, buffer, length) {
|
||||
tmp := buffer + length
|
||||
end := buffer + length
|
||||
tmp := end
|
||||
digit := 0
|
||||
|
||||
loop {
|
||||
@ -19,7 +20,7 @@ itoa(x, buffer, length) {
|
||||
tmp[0] = '0' + digit
|
||||
|
||||
if x == 0 {
|
||||
return tmp
|
||||
return tmp, end - tmp
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -6,5 +6,5 @@ import (
|
||||
|
||||
// Return represents a return statement.
|
||||
type Return struct {
|
||||
Value *expression.Expression
|
||||
Values []*expression.Expression
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
register := f.NewRegister()
|
||||
err := f.ExpressionToRegister(right, register)
|
||||
|
||||
f.AddVariable(&scope.Variable{
|
||||
Name: name,
|
||||
Register: register,
|
||||
Alive: uses,
|
||||
})
|
||||
if left.IsLeaf() {
|
||||
variable, err := f.Define(left)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = f.ExpressionToRegister(right, variable.Register)
|
||||
f.AddVariable(variable)
|
||||
return err
|
||||
}
|
||||
|
||||
if !ast.IsFunctionCall(right) {
|
||||
return errors.New(errors.NotImplemented, f.File, node.Expression.Token.Position)
|
||||
}
|
||||
|
||||
count := 0
|
||||
err := f.CompileCall(right)
|
||||
|
||||
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
|
||||
})
|
||||
}
|
||||
|
@ -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
31
src/build/core/Define.go
Normal 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
|
||||
}
|
@ -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 {
|
||||
if node.IsFolded {
|
||||
f.SaveRegister(register)
|
||||
|
||||
if node.IsFolded {
|
||||
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)
|
||||
}
|
||||
|
@ -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 {
|
||||
|
28
tests/programs/return-multi.q
Normal file
28
tests/programs/return-multi.q
Normal file
@ -0,0 +1,28 @@
|
||||
main() {
|
||||
a, b := reverse2(1, 2)
|
||||
assert a == 2
|
||||
assert b == 1
|
||||
|
||||
c, d, e := reverse3(1, 2, 3)
|
||||
assert c == 3
|
||||
assert d == 2
|
||||
assert e == 1
|
||||
|
||||
f, g, h, i := mix4(1, 2, 3, 4)
|
||||
assert f == 4 + 1
|
||||
assert g == 3 + 2
|
||||
assert h == 2 + 3
|
||||
assert i == 1 + 4
|
||||
}
|
||||
|
||||
reverse2(a, b) {
|
||||
return b, a
|
||||
}
|
||||
|
||||
reverse3(a, b, c) {
|
||||
return c, b, a
|
||||
}
|
||||
|
||||
mix4(a, b, c, d) {
|
||||
return d + a, c + b, b + c, a + d
|
||||
}
|
@ -24,6 +24,7 @@ var programs = []struct {
|
||||
{"reassign", "", "", 0},
|
||||
{"reuse", "", "", 0},
|
||||
{"return", "", "", 0},
|
||||
{"return-multi", "", "", 0},
|
||||
{"math", "", "", 0},
|
||||
{"precedence", "", "", 0},
|
||||
{"op-assign", "", "", 0},
|
||||
|
Loading…
Reference in New Issue
Block a user