Implemented loop iterators
This commit is contained in:
parent
1bc845e6f0
commit
7922cff7ba
@ -2,9 +2,10 @@ import io
|
|||||||
import thread
|
import thread
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
|
loop 0..3 {
|
||||||
thread.create(work)
|
thread.create(work)
|
||||||
thread.create(work)
|
}
|
||||||
thread.create(work)
|
|
||||||
work()
|
work()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
25
src/core/CompileLoopInfinite.go
Normal file
25
src/core/CompileLoopInfinite.go
Normal 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
|
||||||
|
}
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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},
|
||||||
|
@ -34,6 +34,7 @@ const (
|
|||||||
LogicalOr // ||
|
LogicalOr // ||
|
||||||
Define // :=
|
Define // :=
|
||||||
Period // .
|
Period // .
|
||||||
|
Range // ..
|
||||||
Call // x()
|
Call // x()
|
||||||
Array // [x]
|
Array // [x]
|
||||||
Separator // ,
|
Separator // ,
|
||||||
|
@ -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 "<":
|
||||||
|
@ -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
|
|
||||||
}
|
|
10
tests/programs/index-dynamic.q
Normal file
10
tests/programs/index-dynamic.q
Normal 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
|
||||||
|
}
|
||||||
|
}
|
6
tests/programs/loop-limited.q
Normal file
6
tests/programs/loop-limited.q
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
main() {
|
||||||
|
loop i := 0..10 {
|
||||||
|
assert i >= 0
|
||||||
|
assert i < 10
|
||||||
|
}
|
||||||
|
}
|
@ -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},
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user