// Args: // 0 - The string // 1 - The regexp pattern // 2 - (optional) a maximum number of matches to return // // Returns: // An object holding all the matches, or nil if no match. // Each match contains: // n - The nth match group (when n=0, the full text of the match) // Each match group contains: // start - the index of the start of the match // end - the end of the match // text - the string of the match func (s *StringsMod) strings_Matches(args ...runtime.Val) runtime.Val { runtime.ExpectAtLeastNArgs(2, args) src := args[0].String() rx := regexp.MustCompile(args[1].String()) n := -1 // By default, return all matches if len(args) > 2 { n = int(args[2].Int()) } strmtch := rx.FindAllStringSubmatch(src, n) if strmtch == nil { return runtime.Nil } ixmtch := rx.FindAllStringSubmatchIndex(src, n) ob := runtime.NewObject() for i, mtches := range strmtch { obch := runtime.NewObject() for j, mtch := range mtches { leaf := runtime.NewObject() leaf.Set(runtime.String("Text"), runtime.String(mtch)) leaf.Set(runtime.String("Start"), runtime.Number(ixmtch[i][2*j])) leaf.Set(runtime.String("End"), runtime.Number(ixmtch[i][2*j+1])) obch.Set(runtime.Number(j), leaf) } ob.Set(runtime.Number(i), obch) } return ob }
func (jm *JotaModule) Param(vs ...runtime.Val) runtime.Val { jm.dieOnTerminated() jm.paramsMutex.Lock() defer jm.paramsMutex.Unlock() paramName := vs[0].String() value, ok := jm.params[paramName] if !ok { return runtime.Nil } switch t := value.(type) { case string: return runtime.String(t) case bool: return runtime.Bool(t) case int: return runtime.Number(t) case float64: return runtime.Number(t) case linear.Vec2: return jm.newVec(t.X, t.Y) case game.Gid: return jm.newEnt(t) default: base.Error().Printf("Requested parameter of unexpected type: %T", t) return runtime.Nil } }
func TestStringsSplit(t *testing.T) { ctx := runtime.NewCtx(nil, nil) sm := new(StringsMod) sm.SetCtx(ctx) ret := sm.strings_Split(runtime.String("aa:bb::dd"), runtime.String(":")) ob := ret.(runtime.Object) exp := []string{"aa", "bb", "", "dd"} if l := ob.Len().Int(); l != int64(len(exp)) { t.Errorf("expected split length of %d, got %d", len(exp), l) } for i, v := range exp { got := ob.Get(runtime.Number(i)) if got.String() != v { t.Errorf("expected split index %d to be %s, got %s", i, v, got) } } ret = sm.strings_Split(runtime.String("aa:bb::dd:ee:"), runtime.String(":"), runtime.Number(2)) ob = ret.(runtime.Object) exp = []string{"aa", "bb::dd:ee:"} if l := ob.Len().Int(); l != int64(len(exp)) { t.Errorf("expected split length of %d, got %d", len(exp), l) } for i, v := range exp { got := ob.Get(runtime.Number(i)) if got.String() != v { t.Errorf("expected split index %d to be %s, got %s", i, v, got) } } }
func TestMin(t *testing.T) { ctx := runtime.NewCtx(nil, nil) mm := new(MathMod) mm.SetCtx(ctx) cases := []struct { src []runtime.Val exp runtime.Val }{ 0: { src: []runtime.Val{runtime.Number(3), runtime.Number(0), runtime.Number(-12.74), runtime.Number(1)}, exp: runtime.Number(-12.74), }, 1: { src: []runtime.Val{runtime.String("24"), runtime.Bool(true), runtime.Number(12.74)}, exp: runtime.Number(1), }, 2: { src: []runtime.Val{runtime.Number(0), runtime.String("0")}, exp: runtime.Number(0), }, } for i, c := range cases { ret := mm.math_Min(c.src...) if ret != c.exp { t.Errorf("[%d] - expected %f, got %f", i, c.exp.Float(), ret.Float()) } } }
func TestMathIsInf(t *testing.T) { // This is just an interface to Go's function, so just a quick simple test ctx := runtime.NewCtx(nil, nil) mm := new(MathMod) mm.SetCtx(ctx) val := 3.12 val2 := 1 ret := mm.math_IsInf(runtime.Number(val), runtime.Number(val2)) exp := math.IsInf(val, val2) if ret.Bool() != exp { t.Errorf("expected %v, got %v", exp, ret.Bool()) } }
func (jm *JotaModule) newVec(x, y float64) *agoraVec { ob := runtime.NewObject() v := &agoraVec{ Object: ob, jm: jm, } ob.Set(runtime.String("Length"), runtime.NewNativeFunc(jm.ctx, "jota.Vec.Length", v.length)) ob.Set(runtime.String("Sub"), runtime.NewNativeFunc(jm.ctx, "jota.Vec.Sub", v.sub)) ob.Set(runtime.String("Angle"), runtime.NewNativeFunc(jm.ctx, "jota.Vec.Angle", v.angle)) ob.Set(runtime.String("X"), runtime.Number(x)) ob.Set(runtime.String("Y"), runtime.Number(y)) return v }
func (m *MathMod) math_Rand(args ...runtime.Val) runtime.Val { switch len(args) { case 0: return runtime.Number(rand.Int()) case 1: return runtime.Number(rand.Intn(int(args[0].Int()))) default: low := args[0].Int() high := args[1].Int() n := rand.Intn(int(high - low)) return runtime.Number(int64(n) + low) } }
func TestMathPow(t *testing.T) { // This is just an interface to Go's function, so just a quick simple test ctx := runtime.NewCtx(nil, nil) mm := new(MathMod) mm.SetCtx(ctx) val := 1.12 val2 := 3.45 ret := mm.math_Pow(runtime.Number(val), runtime.Number(val2)) exp := math.Pow(val, val2) if ret.Float() != exp { t.Errorf("expected %f, got %f", exp, ret.Float()) } }
func TestStringsSlice(t *testing.T) { ctx := runtime.NewCtx(nil, nil) sm := new(StringsMod) sm.SetCtx(ctx) ret := sm.strings_Slice(runtime.String("agora"), runtime.Number(2)) exp := "ora" if ret.String() != exp { t.Errorf("expected %s, got %s", exp, ret) } ret = sm.strings_Slice(runtime.String("agora"), runtime.Number(2), runtime.Number(4)) exp = "or" if ret.String() != exp { t.Errorf("expected %s, got %s", exp, ret) } }
func TestStringsReplace(t *testing.T) { cases := []struct { args []runtime.Val exp string }{ 0: { args: []runtime.Val{ runtime.String("this is the source"), runtime.String("th"), }, exp: "is is e source", }, 1: { args: []runtime.Val{ runtime.String("this is the source"), runtime.String("th"), runtime.Number(1), }, exp: "is is the source", }, 2: { args: []runtime.Val{ runtime.String("this is the source"), runtime.String("t"), runtime.String("T"), }, exp: "This is The source", }, 3: { args: []runtime.Val{ runtime.String("this is the source"), runtime.String("t"), runtime.String("T"), runtime.Number(1), }, exp: "This is the source", }, } ctx := runtime.NewCtx(nil, nil) sm := new(StringsMod) sm.SetCtx(ctx) for i, c := range cases { ret := sm.strings_Replace(c.args...) if ret.String() != c.exp { t.Errorf("[%d] - expected %s, got %s", i, c.exp, ret) } } }
func (jm *JotaModule) NearbyEnts(vs ...runtime.Val) runtime.Val { jm.dieOnTerminated() jm.engine.Pause() defer jm.engine.Unpause() g := jm.engine.GetState().(*game.Game) me := g.Ents[jm.myGid] obj := runtime.NewObject() if me == nil { return obj } ents := g.EntsInRange(me.Pos(), me.Stats().Vision()) var eds entDistSlice for _, ent := range ents { if ent == me { continue } dist := ent.Pos().Sub(me.Pos()).Mag() if dist > me.Stats().Vision() { continue } if ent.Stats().Cloaking() > 0.9 && ent.Side() != me.Side() { continue } if g.ExistsLos(me.Pos(), ent.Pos()) { eds.ents = append(eds.ents, ent) eds.dist = append(eds.dist, dist) } } sort.Sort(&eds) for i, ent := range eds.ents { obj.Set(runtime.Number(i), jm.newEnt(ent.Id())) } return obj }
func createFileInfo(fi os.FileInfo) runtime.Val { o := runtime.NewObject() o.Set(runtime.String("Name"), runtime.String(fi.Name())) o.Set(runtime.String("Size"), runtime.Number(fi.Size())) o.Set(runtime.String("IsDir"), runtime.Bool(fi.IsDir())) return o }
func (f *FmtMod) fmt_Scanint(args ...runtime.Val) runtime.Val { var i int if _, e := fmt.Fscanf(f.ctx.Stdin, "%d", &i); e != nil { panic(e) } return runtime.Number(i) }
func (m *MathMod) math_Max(args ...runtime.Val) runtime.Val { runtime.ExpectAtLeastNArgs(2, args) max := args[len(args)-1].Float() for i := len(args) - 2; i >= 0; i-- { max = math.Max(max, args[i].Float()) } return runtime.Number(max) }
func (of *file) writeLine(args ...runtime.Val) runtime.Val { n := of.write(args...) m, e := of.f.WriteString("\n") if e != nil { panic(e) } return runtime.Number(int(n.Int()) + m) }
func (m *MathMod) math_Min(args ...runtime.Val) runtime.Val { runtime.ExpectAtLeastNArgs(2, args) min := args[len(args)-1].Float() for i := len(args) - 2; i >= 0; i-- { min = math.Min(min, args[i].Float()) } return runtime.Number(min) }
func (f *FmtMod) fmt_Println(args ...runtime.Val) runtime.Val { ifs := toStringIface(args) n, err := fmt.Fprintln(f.ctx.Stdout, ifs...) if err != nil { panic(err) } return runtime.Number(n) }
func TestTimeConv(t *testing.T) { ctx := runtime.NewCtx(nil, nil) tm := new(TimeMod) tm.SetCtx(ctx) nw := time.Now().UTC() n := tm.time_Date(runtime.Number(nw.Year()), runtime.Number(nw.Month()), runtime.Number(nw.Day()), runtime.Number(nw.Hour()), runtime.Number(nw.Minute()), runtime.Number(nw.Second()), runtime.Number(nw.Nanosecond())) ob := n.(runtime.Object) cnv := ob.Get(runtime.String("__string")) f := cnv.(runtime.Func) ret := f.Call(nil) exp := nw.Format(time.RFC3339) if ret.String() != exp { t.Errorf("expected string to return '%s', got '%s'", exp, ret) } cnv = ob.Get(runtime.String("__int")) f = cnv.(runtime.Func) ret = f.Call(nil) { exp := nw.Unix() if ret.Int() != int64(exp) { t.Errorf("expected int to return %d, got %d", exp, ret.Int()) } } }
func (aEnt *agoraEnt) angle(args ...runtime.Val) runtime.Val { aEnt.jm.engine.Pause() defer aEnt.jm.engine.Unpause() ent := aEnt.jm.engine.GetState().(*game.Game).Ents[aEnt.gid] if ent == nil { return runtime.Nil } return runtime.Number(ent.Angle()) }
func TestTimeSleep(t *testing.T) { ctx := runtime.NewCtx(nil, nil) tm := new(TimeMod) tm.SetCtx(ctx) n := time.Now() tm.time_Sleep(runtime.Number(100)) if diff := time.Now().Sub(n); diff < 100*time.Millisecond { t.Errorf("expected at least 100ms, got %f", diff.Seconds()*1000) } }
// Args: // 0 - The source string // 1 - [Optional] the start index in the source string // 2 (or 1) .. n - The substrings to search for in the source string. // Returns: // The last index of the first found substring in source, if any is found, or -1 func (s *StringsMod) strings_LastIndex(args ...runtime.Val) runtime.Val { runtime.ExpectAtLeastNArgs(2, args) src := args[0].String() start := 0 find := 1 switch v := args[1].(type) { case runtime.Number: runtime.ExpectAtLeastNArgs(3, args) start = int(v.Int()) find = 2 } src = src[start:] for _, v := range args[find:] { if ix := strings.LastIndex(src, v.String()); ix >= 0 { return runtime.Number(ix) } } return runtime.Number(-1) }
func TestStringsConcat(t *testing.T) { ctx := runtime.NewCtx(nil, nil) sm := new(StringsMod) sm.SetCtx(ctx) ret := sm.strings_Concat(runtime.String("hello"), runtime.Number(12), runtime.Bool(true), runtime.String("end")) exp := "hello12trueend" if ret.String() != exp { t.Errorf("expected %s, got %s", exp, ret) } }
func (of *file) write(args ...runtime.Val) runtime.Val { n := 0 for _, v := range args { m, e := of.f.WriteString(v.String()) if e != nil { panic(e) } n += m } return runtime.Number(n) }
func TestStringsByteAt(t *testing.T) { ctx := runtime.NewCtx(nil, nil) sm := new(StringsMod) sm.SetCtx(ctx) src := "some string" ix := 0 ret := sm.strings_ByteAt(runtime.String(src), runtime.Number(ix)) if ret.String() != string(src[ix]) { t.Errorf("expected byte %s at index %d, got %s", string(src[ix]), ix, ret) } ix = 3 ret = sm.strings_ByteAt(runtime.String(src), runtime.Number(ix)) if ret.String() != string(src[ix]) { t.Errorf("expected byte %s at index %d, got %s", string(src[ix]), ix, ret) } ix = 22 ret = sm.strings_ByteAt(runtime.String(src), runtime.Number(ix)) if ret.String() != "" { t.Errorf("expected byte %s at index %d, got %s", "", ix, ret) } }
func (o *OsMod) os_ReadDir(args ...runtime.Val) runtime.Val { runtime.ExpectAtLeastNArgs(1, args) fis, e := ioutil.ReadDir(args[0].String()) if e != nil { panic(e) } ob := runtime.NewObject() for i, fi := range fis { ob.Set(runtime.Number(i), createFileInfo(fi)) } return ob }
func TestMathCosh(t *testing.T) { // This is just an interface to Go's function, so just a quick simple test ctx := runtime.NewCtx(nil, nil) mm := new(MathMod) mm.SetCtx(ctx) val := 0.12 ret := mm.math_Cosh(runtime.Number(val)) exp := math.Cosh(val) if ret.Float() != exp { t.Errorf("expected %f, got %f", exp, ret.Float()) } }
func TestRand(t *testing.T) { ctx := runtime.NewCtx(nil, nil) mm := new(MathMod) mm.SetCtx(ctx) mm.math_RandSeed(runtime.Number(time.Now().UnixNano())) // no-arg form ret := mm.math_Rand() if ret.Int() < 0 { t.Errorf("expected no-arg to procude non-negative value, got %d", ret.Int()) } // one-arg form ret = mm.math_Rand(runtime.Number(10)) if ret.Int() < 0 || ret.Int() >= 10 { t.Errorf("expected one-arg to produce non-negative value lower than 10, got %d", ret.Int()) } // two-args form ret = mm.math_Rand(runtime.Number(3), runtime.Number(9)) if ret.Int() < 3 || ret.Int() >= 9 { t.Errorf("expected two-args to produce value >= 3 and < 9, got %d", ret.Int()) } }
func TestOsWriteFile(t *testing.T) { cases := []struct { src []runtime.Val exp string }{ 0: { exp: "", }, 1: { src: []runtime.Val{runtime.String("hello")}, exp: "hello", }, 2: { src: []runtime.Val{runtime.String("string"), runtime.Number(3), runtime.Bool(true), runtime.Nil, runtime.Number(1.23)}, exp: "string3truenil1.23", }, } fn := "./testdata/writefile.txt" ctx := runtime.NewCtx(nil, nil) om := new(OsMod) om.SetCtx(ctx) for i, c := range cases { args := append([]runtime.Val{runtime.String(fn)}, c.src...) ret := om.os_WriteFile(args...) b, e := ioutil.ReadFile(fn) if e != nil { panic(e) } got := string(b) if ret.Int() != int64(len(c.exp)) { t.Errorf("[%d] - expected %d, got %d", i, len(c.exp), ret.Int()) } if got != c.exp { t.Errorf("[%d] - expected '%s', got '%s'", i, c.exp, got) } } }
// Args: // 0 - the source string // 1 - the separator // 2 [optional] - the maximum number of splits, defaults to all // Returns: // An array-like object with splits as values and indices as keys. func (s *StringsMod) strings_Split(args ...runtime.Val) runtime.Val { runtime.ExpectAtLeastNArgs(2, args) src := args[0].String() sep := args[1].String() cnt := -1 if len(args) > 2 { cnt = int(args[2].Int()) } splits := strings.SplitN(src, sep, cnt) ob := runtime.NewObject() for i, v := range splits { ob.Set(runtime.Number(i), runtime.String(v)) } return ob }
func TestOsWrite(t *testing.T) { ctx := runtime.NewCtx(nil, nil) om := new(OsMod) om.SetCtx(ctx) fn := "./testdata/write.txt" f := om.os_Open(runtime.String(fn), runtime.String("w+")) fl := f.(*file) defer fl.closeFile() // Write the first value ret := fl.writeLine(runtime.Number(1)) if ret.Int() != 2 { t.Errorf("expected 1st written length to be 2, got %d", ret.Int()) } // Move back to start ret = fl.seek() if ret.Int() != 0 { t.Errorf("expected seek to return to start, got offset %d", ret.Int()) } // Write the second value ret = fl.writeLine(runtime.Number(2)) if ret.Int() != 2 { t.Errorf("expected 2nd written length to be 2, got %d", ret.Int()) } }