96 lines
2 KiB
Go
96 lines
2 KiB
Go
package core
|
|
|
|
import (
|
|
"git.urbach.dev/cli/q/src/asm"
|
|
"git.urbach.dev/cli/q/src/ast"
|
|
"git.urbach.dev/cli/q/src/cpu"
|
|
"git.urbach.dev/cli/q/src/errors"
|
|
"git.urbach.dev/cli/q/src/expression"
|
|
"git.urbach.dev/cli/q/src/token"
|
|
"git.urbach.dev/cli/q/src/types"
|
|
)
|
|
|
|
// CompileFor compiles a for loop.
|
|
func (f *Function) CompileFor(loop *ast.For) error {
|
|
for _, register := range f.CPU.Input {
|
|
f.SaveRegister(register)
|
|
}
|
|
|
|
f.count.loop++
|
|
|
|
var (
|
|
label = f.CreateLabel("for", f.count.loop)
|
|
labelEnd = f.CreateLabel("for end", f.count.loop)
|
|
counter cpu.Register
|
|
from *expression.Expression
|
|
to *expression.Expression
|
|
)
|
|
|
|
scope := f.PushScope(loop.Body, f.File.Bytes)
|
|
scope.InLoop = true
|
|
|
|
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 {
|
|
value, isTemporary, err := f.Evaluate(to)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !types.Is(value.Type, types.AnyInt) {
|
|
return errors.New(&errors.TypeMismatch{Encountered: value.Type.Name(), Expected: types.AnyInt.Name()}, f.File, to.Token.Position)
|
|
}
|
|
|
|
if isTemporary {
|
|
defer f.FreeRegister(value.Register)
|
|
}
|
|
|
|
f.AddLabel(label)
|
|
f.RegisterRegister(asm.COMPARE, counter, value.Register)
|
|
}
|
|
|
|
f.Jump(asm.JGE, labelEnd)
|
|
err = f.CompileAST(loop.Body)
|
|
f.RegisterNumber(asm.ADD, counter, 1)
|
|
f.Jump(asm.JUMP, label)
|
|
f.AddLabel(labelEnd)
|
|
f.PopScope()
|
|
return err
|
|
}
|