Implemented loop iterators

This commit is contained in:
Eduard Urbach 2025-02-19 17:52:15 +01:00
parent 1bc845e6f0
commit 7922cff7ba
Signed by: akyoto
GPG Key ID: C874F672B1AF20C0
17 changed files with 147 additions and 35 deletions

View File

@ -2,9 +2,10 @@ import io
import thread import thread
main() { main() {
thread.create(work) loop 0..3 {
thread.create(work) thread.create(work)
thread.create(work) }
work() work()
} }

View File

@ -1,6 +1,9 @@
package ast package ast
import "git.akyoto.dev/cli/q/src/expression"
// Loop represents a block of repeatable statements. // Loop represents a block of repeatable statements.
type Loop struct { type Loop struct {
Head *expression.Expression
Body AST Body AST
} }

View File

@ -40,8 +40,15 @@ func parseKeyword(tokens token.List, source []byte, nodes AST) (Node, error) {
return nil, err return nil, err
case token.Loop: case token.Loop:
_, _, body, err := block(tokens, source) blockStart, _, body, err := block(tokens, source)
return &Loop{Body: body}, err head := tokens[1:blockStart]
loop := &Loop{
Head: expression.Parse(head),
Body: body,
}
return loop, err
case token.Return: case token.Return:
if len(tokens) == 1 { if len(tokens) == 1 {

View File

@ -46,6 +46,8 @@ func (f *Function) ArrayElementToRegister(node *expression.Expression, register
return nil, errors.New(&errors.UnknownIdentifier{Name: indexName}, f.File, index.Token.Position) return nil, errors.New(&errors.UnknownIdentifier{Name: indexName}, f.File, index.Token.Position)
} }
defer f.UseVariable(indexVariable)
if !types.Is(indexVariable.Type, types.Int) { if !types.Is(indexVariable.Type, types.Int) {
return nil, errors.New(&errors.TypeMismatch{Encountered: indexVariable.Type.Name(), Expected: types.Int.Name()}, f.File, index.Token.Position) return nil, errors.New(&errors.TypeMismatch{Encountered: indexVariable.Type.Name(), Expected: types.Int.Name()}, f.File, index.Token.Position)
} }

View File

@ -5,21 +5,92 @@ import (
"git.akyoto.dev/cli/q/src/asm" "git.akyoto.dev/cli/q/src/asm"
"git.akyoto.dev/cli/q/src/ast" "git.akyoto.dev/cli/q/src/ast"
"git.akyoto.dev/cli/q/src/cpu"
"git.akyoto.dev/cli/q/src/expression"
"git.akyoto.dev/cli/q/src/token"
) )
// CompileLoop compiles a loop instruction. // CompileLoop compiles a loop instruction.
func (f *Function) CompileLoop(loop *ast.Loop) error { func (f *Function) CompileLoop(loop *ast.Loop) error {
if loop.Head == nil {
return f.CompileLoopInfinite(loop)
}
for _, register := range f.CPU.Input { for _, register := range f.CPU.Input {
f.SaveRegister(register) f.SaveRegister(register)
} }
f.count.loop++ f.count.loop++
label := fmt.Sprintf("%s_loop_%d", f.UniqueName, f.count.loop)
f.AddLabel(label) var (
label = fmt.Sprintf("%s_loop_%d", f.UniqueName, f.count.loop)
labelEnd = fmt.Sprintf("%s_loop_%d_end", f.UniqueName, f.count.loop)
counter cpu.Register
from *expression.Expression
to *expression.Expression
)
scope := f.PushScope(loop.Body, f.File.Bytes) scope := f.PushScope(loop.Body, f.File.Bytes)
scope.InLoop = true scope.InLoop = true
err := f.CompileAST(loop.Body)
switch loop.Head.Token.Kind {
case token.Define:
variable, err := f.Define(loop.Head.Children[0])
if err != nil {
return err
}
counter = variable.Register
from = loop.Head.Children[1].Children[0]
to = loop.Head.Children[1].Children[1]
f.AddVariable(variable)
case token.Range:
counter = f.NewRegister()
defer f.FreeRegister(counter)
from = loop.Head.Children[0]
to = loop.Head.Children[1]
default:
panic("could not recognize loop header")
}
_, err := f.ExpressionToRegister(from, counter)
if err != nil {
return err
}
if to.Token.IsNumeric() {
number, err := f.ToNumber(to.Token)
if err != nil {
return err
}
f.AddLabel(label)
f.RegisterNumber(asm.COMPARE, counter, number)
} else {
_, register, isTemporary, err := f.Evaluate(to)
if err != nil {
return err
}
if isTemporary {
defer f.FreeRegister(register)
}
f.AddLabel(label)
f.RegisterRegister(asm.COMPARE, counter, register)
}
f.Jump(asm.JGE, labelEnd)
err = f.CompileAST(loop.Body)
f.RegisterNumber(asm.ADD, counter, 1)
f.Jump(asm.JUMP, label) f.Jump(asm.JUMP, label)
f.AddLabel(labelEnd)
f.PopScope() f.PopScope()
return err return err
} }

View File

@ -0,0 +1,25 @@
package core
import (
"fmt"
"git.akyoto.dev/cli/q/src/asm"
"git.akyoto.dev/cli/q/src/ast"
)
// CompileLoopInfinite compiles an infinite loop.
func (f *Function) CompileLoopInfinite(loop *ast.Loop) error {
for _, register := range f.CPU.Input {
f.SaveRegister(register)
}
f.count.loop++
label := fmt.Sprintf("%s_loop_%d", f.UniqueName, f.count.loop)
f.AddLabel(label)
scope := f.PushScope(loop.Body, f.File.Bytes)
scope.InLoop = true
err := f.CompileAST(loop.Body)
f.Jump(asm.JUMP, label)
f.PopScope()
return err
}

View File

@ -10,7 +10,7 @@ import (
// Define defines a new variable. // Define defines a new variable.
func (f *Function) Define(leaf *expression.Expression) (*scope.Variable, error) { func (f *Function) Define(leaf *expression.Expression) (*scope.Variable, error) {
name := leaf.Token.Text(f.File.Bytes) name := leaf.Token.Text(f.File.Bytes)
variable, _ := f.Identifier(name) variable := f.VariableByName(name)
if variable != nil { if variable != nil {
return nil, errors.New(&errors.VariableAlreadyExists{Name: name}, f.File, leaf.Token.Position) return nil, errors.New(&errors.VariableAlreadyExists{Name: name}, f.File, leaf.Token.Position)

View File

@ -2,6 +2,7 @@ package core
import ( import (
"git.akyoto.dev/cli/q/src/cpu" "git.akyoto.dev/cli/q/src/cpu"
"git.akyoto.dev/cli/q/src/errors"
"git.akyoto.dev/cli/q/src/expression" "git.akyoto.dev/cli/q/src/expression"
"git.akyoto.dev/cli/q/src/token" "git.akyoto.dev/cli/q/src/token"
"git.akyoto.dev/cli/q/src/types" "git.akyoto.dev/cli/q/src/types"
@ -13,6 +14,10 @@ func (f *Function) Evaluate(expr *expression.Expression) (types.Type, cpu.Regist
name := expr.Token.Text(f.File.Bytes) name := expr.Token.Text(f.File.Bytes)
variable := f.VariableByName(name) variable := f.VariableByName(name)
if variable == nil {
return nil, 0, false, errors.New(&errors.UnknownIdentifier{Name: name}, f.File, expr.Token.Position)
}
if variable.Alive == 1 { if variable.Alive == 1 {
f.UseVariable(variable) f.UseVariable(variable)
return variable.Type, variable.Register, false, nil return variable.Type, variable.Register, false, nil

View File

@ -41,6 +41,7 @@ var Operators = [64]Operator{
token.LogicalAnd: {"&&", 2, 2}, token.LogicalAnd: {"&&", 2, 2},
token.LogicalOr: {"||", 1, 2}, token.LogicalOr: {"||", 1, 2},
token.Range: {"..", 0, 2},
token.Separator: {",", 0, 2}, token.Separator: {",", 0, 2},
token.Assign: {"=", math.MinInt8, 2}, token.Assign: {"=", math.MinInt8, 2},

View File

@ -34,6 +34,7 @@ const (
LogicalOr // || LogicalOr // ||
Define // := Define // :=
Period // . Period // .
Range // ..
Call // x() Call // x()
Array // [x] Array // [x]
Separator // , Separator // ,

View File

@ -36,6 +36,8 @@ func operator(tokens List, buffer []byte, i Position) (List, Position) {
kind = AddAssign kind = AddAssign
case ".": case ".":
kind = Period kind = Period
case "..":
kind = Range
case ":=": case ":=":
kind = Define kind = Define
case "<": case "<":

View File

@ -1,23 +0,0 @@
import mem
main() {
a := mem.alloc(4)
i := 0
a[i] = i * 2
i += 1
a[i] = i * 2
i += 1
a[i] = i * 2
i += 1
a[i] = i * 2
i = 0
assert a[i] == i * 2
i += 1
assert a[i] == i * 2
i += 1
assert a[i] == i * 2
i += 1
assert a[i] == i * 2
}

View File

@ -0,0 +1,10 @@
import mem
main() {
a := mem.alloc(4)
loop i := 0..4 {
a[i] = i * 2
assert a[i] == i * 2
}
}

View File

@ -0,0 +1,6 @@
main() {
loop i := 0..10 {
assert i >= 0
assert i < 10
}
}

View File

@ -57,12 +57,13 @@ var programs = []struct {
{"branch-save", "", "", 0}, {"branch-save", "", "", 0},
{"jump-near", "", "", 0}, {"jump-near", "", "", 0},
{"switch", "", "", 0}, {"switch", "", "", 0},
{"loop", "", "", 0}, {"loop-infinite", "", "", 0},
{"loop-lifetime", "", "", 0}, {"loop-lifetime", "", "", 0},
{"loop-limited", "", "", 0},
{"memory-free", "", "", 0}, {"memory-free", "", "", 0},
{"out-of-memory", "", "", 0}, {"out-of-memory", "", "", 0},
{"array-index-static", "", "", 0}, {"index-static", "", "", 0},
{"array-index-dynamic", "", "", 0}, {"index-dynamic", "", "", 0},
{"struct", "", "", 0}, {"struct", "", "", 0},
{"len", "", "", 0}, {"len", "", "", 0},
} }