diff --git a/pkg/gcs/eval_fn_test.go b/pkg/gcs/eval_fn_test.go index a93188e46..57ad21728 100644 --- a/pkg/gcs/eval_fn_test.go +++ b/pkg/gcs/eval_fn_test.go @@ -259,3 +259,43 @@ func TestNestedActions(t *testing.T) { t.Error(eval.Err()) } } + +func TestIsEven(t *testing.T) { + prog := `is_even(1);` + p := ast.New(prog) + _, gcsl, err := p.Parse() + if err != nil { + t.Fatal(err) + } + fmt.Println("program:") + fmt.Println(gcsl.String()) + eval, _ := NewEvaluator(gcsl, nil) + eval.Log = log.Default() + resultChan := make(chan Obj) + go func() { + res, err := eval.Run() + fmt.Printf("done with result: %v, err: %v\n", res, err) + resultChan <- res + }() + for { + eval.Continue() + a, err := eval.NextAction() + if a == nil { + break + } + if err != nil { + t.Fatal(err) + } + } + result := <-resultChan + if result.Typ() != typNum { + t.Errorf("expecting type to return num, got %v", typStrings[result.Typ()]) + } + if eval.Err() != nil { + t.Error(eval.Err()) + } + val := result.(*number) + if val.ival != 0 { + t.Errorf("expecting result to be 0, got %v", val.ival) + } +} diff --git a/pkg/gcs/sysfunc.go b/pkg/gcs/sysfunc.go index 2434bd3b6..bcb67f5fb 100644 --- a/pkg/gcs/sysfunc.go +++ b/pkg/gcs/sysfunc.go @@ -43,6 +43,7 @@ func (e *Eval) initSysFuncs(env *Env) { e.addSysFunc("cos", e.cos, env) e.addSysFunc("asin", e.asin, env) e.addSysFunc("acos", e.acos, env) + e.addSysFunc("is_even", e.isEven, env) // events e.addSysFunc("set_on_tick", e.setOnTick, env) @@ -495,6 +496,36 @@ func (e *Eval) pickUpCrystallize(c *ast.CallExpr, env *Env) (Obj, error) { }, nil } +func (e *Eval) isEven(c *ast.CallExpr, env *Env) (Obj, error) { + // is_even(number goes in here) + if len(c.Args) != 1 { + return nil, fmt.Errorf("invalid number of params for is_even, expected 1 got %v", len(c.Args)) + } + + // should eval to a number + val, err := e.evalExpr(c.Args[0], env) + if err != nil { + return nil, err + } + + n, ok := val.(*number) + if !ok { + return nil, fmt.Errorf("is_even argument should evaluate to a number, got %v", val.Inspect()) + } + + // if float, floor it + var v int64 + v = n.ival + if n.isFloat { + v = int64(n.fval) + } + + if v%2 == 0 { + return bton(true), nil + } + return bton(false), nil +} + func (e *Eval) sin(c *ast.CallExpr, env *Env) (Obj, error) { // sin(number goes in here) if len(c.Args) != 1 { diff --git a/ui/packages/docs/docs/reference/system_functions.md b/ui/packages/docs/docs/reference/system_functions.md index 63a73be25..0569a209e 100644 --- a/ui/packages/docs/docs/reference/system_functions.md +++ b/ui/packages/docs/docs/reference/system_functions.md @@ -360,6 +360,18 @@ pick_up_crystallize(element); `element` must be a string or an expression that evaluates to a string. ::: +## is_even + +``` +is_even(arg); +``` + +`is_even` evaluates if a given number is even or not. If a number is a floating point, the number if floored first. + +:::danger +`arg` must be a number or an expression that evaluates to a number. +::: + ## sin ```