package core import ( "git.urbach.dev/cli/q/src/errors" "git.urbach.dev/cli/q/src/expression" "git.urbach.dev/cli/q/src/ssa" "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, thenBlock *ssa.Block, elseBlock *ssa.Block) error { f.Block().AddSuccessor(thenBlock) f.Block().AddSuccessor(elseBlock) switch condition.Token.Kind { case token.LogicalOr: f.Count.SubBranch++ leftFailLabel := f.CreateLabel("or", f.Count.SubBranch) leftFail := ssa.NewBlock(leftFailLabel) // Left left := condition.Children[0] err := f.compileCondition(left, thenBlock, leftFail) if err != nil { return err } f.AddBlock(leftFail) // Right right := condition.Children[1] err = f.compileCondition(right, thenBlock, elseBlock) return err case token.LogicalAnd: f.Count.SubBranch++ leftSuccessLabel := f.CreateLabel("and", f.Count.SubBranch) leftSuccess := ssa.NewBlock(leftSuccessLabel) // Left left := condition.Children[0] err := f.compileCondition(left, leftSuccess, elseBlock) if err != nil { return err } f.AddBlock(leftSuccess) // Right right := condition.Children[1] err = f.compileCondition(right, thenBlock, elseBlock) return err case token.Equal, token.NotEqual, token.Greater, token.Less, token.GreaterEqual, token.LessEqual: conditionValue, err := f.evaluateRight(condition) if err != nil { return err } binaryOp, isBinaryOp := conditionValue.(*ssa.BinaryOp) if isBinaryOp && binaryOp.Op.IsComparison() { left := binaryOp.Left if left.Type() == types.Error { right := binaryOp.Right.(*ssa.Int) switch { case condition.Token.Kind == token.NotEqual && right.Int == 0: for _, protected := range thenBlock.Protected[left] { thenBlock.Unidentify(protected) } thenBlock.Unprotect(left) elseBlock.Unidentify(left) elseBlock.Unprotect(left) case condition.Token.Kind == token.Equal && right.Int == 0: for _, protected := range elseBlock.Protected[left] { elseBlock.Unidentify(protected) } elseBlock.Unprotect(left) thenBlock.Unidentify(left) thenBlock.Unprotect(left) } } } branch := &ssa.Branch{ Condition: conditionValue, Then: thenBlock, Else: elseBlock, } f.Append(branch) return nil case token.Not: return f.compileCondition(condition.Children[0], elseBlock, thenBlock) default: if condition.Token.Kind.IsAssignment() { return errors.New(InvalidCondition, f.File, condition.Token) } value, err := f.evaluateRight(condition) if err != nil { return err } if !types.Is(value.Type(), types.Bool) { return errors.New(&TypeMismatch{Encountered: value.Type().Name(), Expected: types.Bool.Name()}, f.File, condition.Source()) } branch := &ssa.Branch{ Condition: value, Then: thenBlock, Else: elseBlock, } f.Append(branch) return nil } }