package core import ( "fmt" "git.urbach.dev/cli/q/src/asm" "git.urbach.dev/cli/q/src/errors" "git.urbach.dev/cli/q/src/eval" "git.urbach.dev/cli/q/src/expression" "git.urbach.dev/cli/q/src/token" "git.urbach.dev/cli/q/src/types" ) // CompileCondition inserts code to jump to the start label or end label depending on the truth of the condition. func (f *Function) CompileCondition(condition *expression.Expression, successLabel string, failLabel string) error { switch condition.Token.Kind { case token.LogicalOr: f.count.subBranch++ leftFailLabel := f.CreateLabel("false", f.count.subBranch) // Left left := condition.Children[0] err := f.CompileCondition(left, successLabel, leftFailLabel) if err != nil { return err } f.JumpIfTrue(left.Token.Kind, successLabel) // Right f.AddLabel(leftFailLabel) right := condition.Children[1] err = f.CompileCondition(right, successLabel, failLabel) if condition.Parent != nil && condition.Parent.Token.Kind == token.LogicalOr && condition != condition.Parent.LastChild() { f.JumpIfTrue(right.Token.Kind, successLabel) } else { f.JumpIfFalse(right.Token.Kind, failLabel) } return err case token.LogicalAnd: f.count.subBranch++ leftSuccessLabel := f.CreateLabel("true", f.count.subBranch) // Left left := condition.Children[0] err := f.CompileCondition(left, leftSuccessLabel, failLabel) if err != nil { return err } f.JumpIfFalse(left.Token.Kind, failLabel) // Right f.AddLabel(leftSuccessLabel) right := condition.Children[1] err = f.CompileCondition(right, successLabel, failLabel) if condition.Parent != nil && condition.Parent.Token.Kind == token.LogicalOr && condition != condition.Parent.LastChild() { f.JumpIfTrue(right.Token.Kind, successLabel) } else { f.JumpIfFalse(right.Token.Kind, failLabel) } return err case token.Call: value, err := f.Evaluate(condition) if err != nil { return err } if !types.Is(value.Type(), types.Bool) { return errors.New(&errors.TypeMismatch{Encountered: value.Type().Name(), Expected: types.Bool.Name()}, f.File, condition.Token.Position) } switch value := value.(type) { case eval.Number: if value.Number == 0 { f.Jump(asm.JUMP, failLabel) } case eval.Register: f.RegisterNumber(asm.COMPARE, value.Register, 0) f.Jump(asm.JE, failLabel) default: panic(fmt.Errorf("%s: not implemented: %v", f.UniqueName, value)) } return nil case token.Equal, token.NotEqual, token.Greater, token.Less, token.GreaterEqual, token.LessEqual: err := f.Compare(condition) if condition.Parent == nil { f.JumpIfFalse(condition.Token.Kind, failLabel) } return err default: return errors.New(errors.InvalidCondition, f.File, condition.Token.Position) } }